From beb08eb751fa8e1f72042f263316ab5e5ddb596d Mon Sep 17 00:00:00 2001 From: Unit 193 Date: Wed, 10 Nov 2021 00:54:13 -0500 Subject: New upstream version 2.4.0+dfsg. --- .editorconfig | 9 + .github/ISSUE_TEMPLATE.md | 30 - .github/ISSUE_TEMPLATE/bug_report.yml | 79 + .github/ISSUE_TEMPLATE/config.yml | 1 + .gitmodules | 3 + Build.properties | 6 +- CMakeLists.txt | 52 +- LICENSE | 61 +- README.md | 45 +- RELEASING.md | 59 + _config.yml | 2 +- azure-pipelines.yml | 29 +- .../download_install_bonjour_sdk_like.ps1 | 3 +- azure-pipelines/download_install_qt.ps1 | 5 +- clean_build.bat | 5 +- clean_build.sh | 6 +- cmake/Version.cmake | 7 +- cmake/gtest.cmake | 44 + doc/QtCodeStyle.xml | 234 - doc/barrier.conf.example-advanced | 6 +- doc/barrier.conf.example-barebones | 17 + doc/barrier.conf.example-basic | 4 +- doc/barrierc.1 | 2 +- doc/barriers.1 | 2 +- doc/newsfragments/README.md | 13 + doc/org.barrier-foss.org.barrierc.plist | 18 +- doc/org.barrier-foss.org.barriers.plist | 12 +- doc/release_notes/index.md | 94 + doc/release_notes/index.template.jinja | 37 + gulrak-filesystem/.appveyor.yml | 85 + gulrak-filesystem/.ci/unix-build.sh | 4 + gulrak-filesystem/.ci/unix-test.sh | 7 + gulrak-filesystem/.cirrus.yml | 35 + gulrak-filesystem/.clang-format | 25 + gulrak-filesystem/.clang-tidy | 3 + gulrak-filesystem/.drone.yml | 43 + .../.github/ISSUE_TEMPLATE/bug_report.md | 17 + .../.github/ISSUE_TEMPLATE/feature_request.md | 17 + .../.github/workflows/build_cmake.yml | 197 + gulrak-filesystem/.gitignore | 7 + gulrak-filesystem/CMakeLists.txt | 74 + gulrak-filesystem/LICENSE | 19 + gulrak-filesystem/README.md | 1062 ++ gulrak-filesystem/cmake/GhcHelper.cmake | 64 + gulrak-filesystem/cmake/config.cmake.in | 6 + gulrak-filesystem/examples/CMakeLists.txt | 17 + gulrak-filesystem/examples/dir.cpp | 60 + gulrak-filesystem/examples/du.cpp | 61 + gulrak-filesystem/include/ghc/filesystem.hpp | 6017 ++++++++ gulrak-filesystem/include/ghc/fs_fwd.hpp | 38 + gulrak-filesystem/include/ghc/fs_impl.hpp | 35 + gulrak-filesystem/include/ghc/fs_std.hpp | 60 + gulrak-filesystem/include/ghc/fs_std_fwd.hpp | 63 + gulrak-filesystem/include/ghc/fs_std_impl.hpp | 46 + gulrak-filesystem/test/CMakeLists.txt | 91 + gulrak-filesystem/test/catch.hpp | 13922 +++++++++++++++++++ .../test/cmake/ParseAndAddCatchTests.cmake | 230 + gulrak-filesystem/test/exception.cpp | 5 + gulrak-filesystem/test/filesystem_test.cpp | 2956 ++++ gulrak-filesystem/test/fwd_test.cpp | 7 + gulrak-filesystem/test/impl_test.cpp | 8 + gulrak-filesystem/test/multi1.cpp | 42 + gulrak-filesystem/test/multi2.cpp | 40 + osx_environment.sh | 19 +- res/Readme.txt | 2 +- res/barrier.desktop | 3 +- res/config.h.in | 12 - res/makeicon.sh | 8 +- res/openssl/barrier.conf | 65 - snap/snapcraft.yaml | 2 +- src/CMakeLists.txt | 12 +- src/cmd/CMakeLists.txt | 7 +- src/cmd/barrierc/CMakeLists.txt | 16 +- .../barrierc/MSWindowsClientTaskBarReceiver.cpp | 4 +- src/cmd/barrierc/MSWindowsClientTaskBarReceiver.h | 4 +- src/cmd/barrierc/OSXClientTaskBarReceiver.cpp | 5 +- src/cmd/barrierc/OSXClientTaskBarReceiver.h | 4 +- src/cmd/barrierc/XWindowsClientTaskBarReceiver.cpp | 4 +- src/cmd/barrierc/XWindowsClientTaskBarReceiver.h | 4 +- src/cmd/barrierc/barrierc.cpp | 8 +- src/cmd/barrierc/barrierc.exe.manifest | 2 - src/cmd/barrierc/barrierc.rc | 97 +- src/cmd/barrierc/resource.h | 2 +- src/cmd/barrierd/CMakeLists.txt | 3 + src/cmd/barrierd/barrierd.cpp | 4 +- src/cmd/barrierd/barrierd.ico | Bin 0 -> 121502 bytes src/cmd/barrierd/barrierd.rc | 111 + src/cmd/barriers/CMakeLists.txt | 17 +- .../barriers/MSWindowsServerTaskBarReceiver.cpp | 4 +- src/cmd/barriers/MSWindowsServerTaskBarReceiver.h | 4 +- src/cmd/barriers/OSXServerTaskBarReceiver.cpp | 4 +- src/cmd/barriers/OSXServerTaskBarReceiver.h | 4 +- src/cmd/barriers/XWindowsServerTaskBarReceiver.cpp | 4 +- src/cmd/barriers/XWindowsServerTaskBarReceiver.h | 4 +- src/cmd/barriers/barriers.cpp | 8 +- src/cmd/barriers/barriers.exe.manifest | 2 - src/cmd/barriers/barriers.rc | 61 +- src/cmd/barriers/resource.h | 2 +- src/gui/CMakeLists.txt | 136 +- src/gui/gui.pro | 162 - src/gui/gui.ts | 9 +- src/gui/lang.cmd | 2 +- src/gui/langbuild.cmd | 4 +- src/gui/res/lang/Languages.xml | 92 +- src/gui/res/lang/gui_af-ZA.ts | 9 +- src/gui/res/lang/gui_ar.ts | 9 +- src/gui/res/lang/gui_bg-BG.qm | Bin 21889 -> 21854 bytes src/gui/res/lang/gui_bg-BG.ts | 13 +- src/gui/res/lang/gui_ca-AD.qm | Bin 23086 -> 23050 bytes src/gui/res/lang/gui_ca-AD.ts | 19 +- src/gui/res/lang/gui_cs-CZ.qm | Bin 22897 -> 22864 bytes src/gui/res/lang/gui_cs-CZ.ts | 13 +- src/gui/res/lang/gui_cy.qm | Bin 15069 -> 15048 bytes src/gui/res/lang/gui_cy.ts | 13 +- src/gui/res/lang/gui_da.qm | Bin 22065 -> 22042 bytes src/gui/res/lang/gui_da.ts | 15 +- src/gui/res/lang/gui_de.qm | Bin 23631 -> 40389 bytes src/gui/res/lang/gui_de.ts | 589 +- src/gui/res/lang/gui_es.qm | Bin 23609 -> 23570 bytes src/gui/res/lang/gui_es.ts | 13 +- src/gui/res/lang/gui_et-EE.qm | Bin 21531 -> 21506 bytes src/gui/res/lang/gui_et-EE.ts | 13 +- src/gui/res/lang/gui_fi.qm | Bin 21785 -> 21750 bytes src/gui/res/lang/gui_fi.ts | 13 +- src/gui/res/lang/gui_fr.qm | Bin 23635 -> 23610 bytes src/gui/res/lang/gui_fr.ts | 13 +- src/gui/res/lang/gui_gl.ts | 9 +- src/gui/res/lang/gui_grk.ts | 9 +- src/gui/res/lang/gui_he.qm | Bin 13576 -> 13555 bytes src/gui/res/lang/gui_he.ts | 13 +- src/gui/res/lang/gui_hi.ts | 9 +- src/gui/res/lang/gui_hr-HR.qm | Bin 20504 -> 20477 bytes src/gui/res/lang/gui_hr-HR.ts | 13 +- src/gui/res/lang/gui_hu-HU.qm | Bin 18574 -> 18549 bytes src/gui/res/lang/gui_hu-HU.ts | 13 +- src/gui/res/lang/gui_id.ts | 9 +- src/gui/res/lang/gui_is-IS.ts | 9 +- src/gui/res/lang/gui_it.qm | Bin 21856 -> 21827 bytes src/gui/res/lang/gui_it.ts | 13 +- src/gui/res/lang/gui_ja-JP.qm | Bin 17380 -> 33285 bytes src/gui/res/lang/gui_ja-JP.ts | 692 +- src/gui/res/lang/gui_ko.qm | Bin 18396 -> 18383 bytes src/gui/res/lang/gui_ko.ts | 23 +- src/gui/res/lang/gui_lt.ts | 9 +- src/gui/res/lang/gui_lv.ts | 9 +- src/gui/res/lang/gui_mr.ts | 9 +- src/gui/res/lang/gui_nl-NL.qm | Bin 22485 -> 22454 bytes src/gui/res/lang/gui_nl-NL.ts | 15 +- src/gui/res/lang/gui_no.qm | Bin 21717 -> 21694 bytes src/gui/res/lang/gui_no.ts | 13 +- src/gui/res/lang/gui_pes-IR.ts | 9 +- src/gui/res/lang/gui_pl-PL.qm | Bin 22481 -> 22448 bytes src/gui/res/lang/gui_pl-PL.ts | 17 +- src/gui/res/lang/gui_pt-BR.qm | Bin 22451 -> 22414 bytes src/gui/res/lang/gui_pt-BR.ts | 13 +- src/gui/res/lang/gui_pt-PT.qm | Bin 21266 -> 21229 bytes src/gui/res/lang/gui_pt-PT.ts | 13 +- src/gui/res/lang/gui_ro.qm | Bin 19807 -> 19774 bytes src/gui/res/lang/gui_ro.ts | 13 +- src/gui/res/lang/gui_ru.qm | Bin 22330 -> 22309 bytes src/gui/res/lang/gui_ru.ts | 17 +- src/gui/res/lang/gui_si.ts | 9 +- src/gui/res/lang/gui_sk-SK.qm | Bin 1198 -> 39331 bytes src/gui/res/lang/gui_sk-SK.ts | 586 +- src/gui/res/lang/gui_sl-SI.ts | 9 +- src/gui/res/lang/gui_sq-AL.qm | Bin 20259 -> 20230 bytes src/gui/res/lang/gui_sq-AL.ts | 13 +- src/gui/res/lang/gui_sr.ts | 9 +- src/gui/res/lang/gui_sv.qm | Bin 22157 -> 37805 bytes src/gui/res/lang/gui_sv.ts | 13 +- src/gui/res/lang/gui_th-TH.ts | 9 +- src/gui/res/lang/gui_tr-TR.ts | 13 +- src/gui/res/lang/gui_uk.qm | Bin 22694 -> 22673 bytes src/gui/res/lang/gui_uk.ts | 13 +- src/gui/res/lang/gui_ur.ts | 9 +- src/gui/res/lang/gui_vi.ts | 9 +- src/gui/res/lang/gui_zh-CN.qm | Bin 16198 -> 25228 bytes src/gui/res/lang/gui_zh-CN.ts | 1352 +- src/gui/res/lang/gui_zh-TW.qm | Bin 16332 -> 16321 bytes src/gui/res/lang/gui_zh-TW.ts | 13 +- src/gui/res/win/Barrier.rc | 113 +- src/gui/src/AboutDialog.cpp | 4 +- src/gui/src/AboutDialog.h | 5 +- src/gui/src/Action.cpp | 19 +- src/gui/src/Action.h | 35 +- src/gui/src/ActionDialog.cpp | 14 +- src/gui/src/ActionDialog.h | 4 +- src/gui/src/AppConfig.cpp | 16 +- src/gui/src/AppConfig.h | 8 + src/gui/src/BaseConfig.cpp | 5 +- src/gui/src/BaseConfig.h | 4 +- src/gui/src/Fingerprint.cpp | 148 - src/gui/src/Fingerprint.h | 42 - src/gui/src/FingerprintAcceptDialog.cpp | 65 + src/gui/src/FingerprintAcceptDialog.h | 45 + src/gui/src/FingerprintAcceptDialog.ui | 174 + src/gui/src/Hotkey.cpp | 36 +- src/gui/src/Hotkey.h | 27 +- src/gui/src/HotkeyDialog.cpp | 4 +- src/gui/src/HotkeyDialog.h | 4 +- src/gui/src/IpcClient.h | 2 +- src/gui/src/KeySequence.cpp | 33 +- src/gui/src/KeySequence.h | 8 +- src/gui/src/KeySequenceWidget.cpp | 4 +- src/gui/src/KeySequenceWidget.h | 5 +- src/gui/src/MainWindow.cpp | 192 +- src/gui/src/MainWindow.h | 14 +- src/gui/src/MainWindowBase.ui | 148 +- src/gui/src/NewScreenWidget.cpp | 7 +- src/gui/src/NewScreenWidget.h | 5 +- src/gui/src/QBarrierApplication.cpp | 7 +- src/gui/src/QBarrierApplication.h | 5 +- src/gui/src/QUtility.cpp | 3 +- src/gui/src/Screen.cpp | 7 +- src/gui/src/Screen.h | 7 +- src/gui/src/ScreenSettingsDialog.cpp | 5 +- src/gui/src/ScreenSettingsDialog.h | 5 +- src/gui/src/ScreenSetupModel.cpp | 14 +- src/gui/src/ScreenSetupModel.h | 9 +- src/gui/src/ScreenSetupView.cpp | 83 +- src/gui/src/ScreenSetupView.h | 9 +- src/gui/src/ServerConfig.cpp | 18 +- src/gui/src/ServerConfig.h | 21 +- src/gui/src/ServerConfigDialog.cpp | 26 +- src/gui/src/ServerConfigDialog.h | 5 +- src/gui/src/SettingsDialog.cpp | 8 +- src/gui/src/SettingsDialog.h | 4 +- src/gui/src/SettingsDialogBase.ui | 87 +- src/gui/src/SetupWizard.cpp | 10 +- src/gui/src/SetupWizard.h | 4 +- src/gui/src/ShutdownCh.h | 1 - src/gui/src/SslCertificate.cpp | 208 +- src/gui/src/SslCertificate.h | 11 +- src/gui/src/TrashScreenWidget.cpp | 5 +- src/gui/src/TrashScreenWidget.h | 5 +- src/gui/src/ZeroconfService.cpp | 12 +- src/gui/src/main.cpp | 6 + src/gui/test/HotkeyTests.cpp | 320 + src/gui/test/KeySequenceTests.cpp | 145 + src/gui/test/Utils.h | 38 + src/gui/test/main.cpp | 23 + src/lib/CMakeLists.txt | 4 +- src/lib/arch/Arch.cpp | 4 +- src/lib/arch/Arch.h | 4 +- src/lib/arch/ArchConsoleStd.cpp | 6 +- src/lib/arch/ArchConsoleStd.h | 4 +- src/lib/arch/ArchDaemonNone.cpp | 4 +- src/lib/arch/ArchDaemonNone.h | 4 +- src/lib/arch/CMakeLists.txt | 4 +- src/lib/arch/IArchConsole.h | 4 +- src/lib/arch/IArchDaemon.h | 8 +- src/lib/arch/IArchLog.h | 4 +- src/lib/arch/IArchMultithread.h | 30 +- src/lib/arch/IArchNetwork.h | 12 +- src/lib/arch/IArchSleep.h | 4 +- src/lib/arch/IArchString.cpp | 4 +- src/lib/arch/IArchString.h | 14 +- src/lib/arch/IArchSystem.h | 4 +- src/lib/arch/IArchTaskBar.h | 4 +- src/lib/arch/IArchTaskBarReceiver.h | 4 +- src/lib/arch/IArchTime.h | 4 +- src/lib/arch/XArch.h | 6 +- src/lib/arch/multibyte.h | 4 +- src/lib/arch/unix/ArchConsoleUnix.cpp | 4 +- src/lib/arch/unix/ArchConsoleUnix.h | 4 +- src/lib/arch/unix/ArchDaemonUnix.cpp | 20 +- src/lib/arch/unix/ArchDaemonUnix.h | 4 +- src/lib/arch/unix/ArchInternetUnix.cpp | 8 +- src/lib/arch/unix/ArchLogUnix.cpp | 4 +- src/lib/arch/unix/ArchLogUnix.h | 4 +- src/lib/arch/unix/ArchMultithreadPosix.cpp | 35 +- src/lib/arch/unix/ArchMultithreadPosix.h | 7 +- src/lib/arch/unix/ArchNetworkBSD.cpp | 4 +- src/lib/arch/unix/ArchNetworkBSD.h | 4 +- src/lib/arch/unix/ArchSleepUnix.cpp | 4 +- src/lib/arch/unix/ArchSleepUnix.h | 4 +- src/lib/arch/unix/ArchStringUnix.cpp | 5 +- src/lib/arch/unix/ArchStringUnix.h | 4 +- src/lib/arch/unix/ArchSystemUnix.cpp | 4 +- src/lib/arch/unix/ArchSystemUnix.h | 4 +- src/lib/arch/unix/ArchTaskBarXWindows.cpp | 4 +- src/lib/arch/unix/ArchTaskBarXWindows.h | 4 +- src/lib/arch/unix/ArchTimeUnix.cpp | 4 +- src/lib/arch/unix/ArchTimeUnix.h | 4 +- src/lib/arch/unix/XArchUnix.cpp | 4 +- src/lib/arch/unix/XArchUnix.h | 4 +- src/lib/arch/vsnprintf.h | 67 - src/lib/arch/win32/ArchConsoleWindows.cpp | 4 +- src/lib/arch/win32/ArchConsoleWindows.h | 4 +- src/lib/arch/win32/ArchDaemonWindows.cpp | 10 +- src/lib/arch/win32/ArchDaemonWindows.h | 4 +- src/lib/arch/win32/ArchInternetWindows.cpp | 8 +- src/lib/arch/win32/ArchLogWindows.cpp | 4 +- src/lib/arch/win32/ArchLogWindows.h | 4 +- src/lib/arch/win32/ArchMiscWindows.cpp | 22 +- src/lib/arch/win32/ArchMiscWindows.h | 6 +- src/lib/arch/win32/ArchMultithreadWindows.cpp | 33 +- src/lib/arch/win32/ArchMultithreadWindows.h | 7 +- src/lib/arch/win32/ArchNetworkWinsock.cpp | 4 +- src/lib/arch/win32/ArchNetworkWinsock.h | 4 +- src/lib/arch/win32/ArchSleepWindows.cpp | 6 +- src/lib/arch/win32/ArchSleepWindows.h | 4 +- src/lib/arch/win32/ArchStringWindows.cpp | 9 +- src/lib/arch/win32/ArchStringWindows.h | 4 +- src/lib/arch/win32/ArchSystemWindows.cpp | 4 +- src/lib/arch/win32/ArchSystemWindows.h | 4 +- src/lib/arch/win32/ArchTaskBarWindows.cpp | 15 +- src/lib/arch/win32/ArchTaskBarWindows.h | 7 +- src/lib/arch/win32/ArchTimeWindows.cpp | 4 +- src/lib/arch/win32/ArchTimeWindows.h | 4 +- src/lib/arch/win32/XArchWindows.cpp | 80 +- src/lib/arch/win32/XArchWindows.h | 4 +- src/lib/barrier/App.cpp | 47 +- src/lib/barrier/App.h | 24 +- src/lib/barrier/AppUtil.cpp | 8 +- src/lib/barrier/AppUtil.h | 8 +- src/lib/barrier/ArgParser.cpp | 67 +- src/lib/barrier/ArgParser.h | 20 +- src/lib/barrier/ArgsBase.cpp | 9 +- src/lib/barrier/ArgsBase.h | 10 +- src/lib/barrier/BarrierType.h | 26 + src/lib/barrier/CMakeLists.txt | 4 +- src/lib/barrier/Chunk.cpp | 4 +- src/lib/barrier/Chunk.h | 6 +- src/lib/barrier/ClientApp.cpp | 58 +- src/lib/barrier/ClientApp.h | 7 +- src/lib/barrier/ClientArgs.cpp | 4 +- src/lib/barrier/ClientArgs.h | 4 +- src/lib/barrier/ClientTaskBarReceiver.cpp | 4 +- src/lib/barrier/ClientTaskBarReceiver.h | 4 +- src/lib/barrier/Clipboard.cpp | 4 +- src/lib/barrier/Clipboard.h | 4 +- src/lib/barrier/ClipboardChunk.cpp | 8 +- src/lib/barrier/ClipboardChunk.h | 4 +- src/lib/barrier/DragInformation.cpp | 16 +- src/lib/barrier/DragInformation.h | 8 +- src/lib/barrier/DropHelper.cpp | 11 +- src/lib/barrier/DropHelper.h | 4 +- src/lib/barrier/FileChunk.cpp | 4 +- src/lib/barrier/FileChunk.h | 4 +- src/lib/barrier/IApp.h | 4 +- src/lib/barrier/IAppUtil.h | 6 +- src/lib/barrier/IClient.h | 4 +- src/lib/barrier/IClipboard.cpp | 8 +- src/lib/barrier/IClipboard.h | 4 +- src/lib/barrier/IKeyState.cpp | 4 +- src/lib/barrier/IKeyState.h | 8 +- src/lib/barrier/INode.h | 6 +- src/lib/barrier/IPlatformScreen.cpp | 4 +- src/lib/barrier/IPlatformScreen.h | 7 +- src/lib/barrier/IPrimaryScreen.cpp | 4 +- src/lib/barrier/IPrimaryScreen.h | 4 +- src/lib/barrier/IScreen.h | 6 +- src/lib/barrier/IScreenSaver.h | 6 +- src/lib/barrier/ISecondaryScreen.h | 4 +- src/lib/barrier/KeyMap.cpp | 14 +- src/lib/barrier/KeyMap.h | 12 +- src/lib/barrier/KeyState.cpp | 10 +- src/lib/barrier/KeyState.h | 8 +- src/lib/barrier/PacketStreamFilter.cpp | 26 +- src/lib/barrier/PacketStreamFilter.h | 10 +- src/lib/barrier/PlatformScreen.cpp | 4 +- src/lib/barrier/PlatformScreen.h | 1 + src/lib/barrier/PortableTaskBarReceiver.cpp | 6 +- src/lib/barrier/PortableTaskBarReceiver.h | 4 +- src/lib/barrier/ProtocolUtil.cpp | 18 +- src/lib/barrier/ProtocolUtil.h | 10 +- src/lib/barrier/Screen.cpp | 12 +- src/lib/barrier/Screen.h | 14 +- src/lib/barrier/ServerApp.cpp | 92 +- src/lib/barrier/ServerApp.h | 8 +- src/lib/barrier/ServerArgs.h | 1 + src/lib/barrier/ServerTaskBarReceiver.cpp | 6 +- src/lib/barrier/ServerTaskBarReceiver.h | 4 +- src/lib/barrier/StreamChunker.cpp | 27 +- src/lib/barrier/StreamChunker.h | 11 +- src/lib/barrier/XBarrier.cpp | 6 +- src/lib/barrier/XBarrier.h | 10 +- src/lib/barrier/XScreen.cpp | 4 +- src/lib/barrier/XScreen.h | 4 +- src/lib/barrier/clipboard_types.h | 4 +- src/lib/barrier/key_types.cpp | 14 +- src/lib/barrier/key_types.h | 17 +- src/lib/barrier/mouse_types.h | 4 +- src/lib/barrier/option_types.h | 4 +- src/lib/barrier/protocol_types.cpp | 4 +- src/lib/barrier/protocol_types.h | 16 +- src/lib/barrier/unix/AppUtilUnix.cpp | 4 +- src/lib/barrier/unix/AppUtilUnix.h | 6 +- src/lib/barrier/win32/AppUtilWindows.cpp | 28 +- src/lib/barrier/win32/AppUtilWindows.h | 6 +- src/lib/barrier/win32/DaemonApp.cpp | 25 +- src/lib/barrier/win32/DaemonApp.h | 6 +- src/lib/base/CMakeLists.txt | 4 +- src/lib/base/ELevel.h | 4 +- src/lib/base/Event.cpp | 4 +- src/lib/base/Event.h | 8 +- src/lib/base/EventQueue.cpp | 16 +- src/lib/base/EventQueue.h | 6 +- src/lib/base/EventTypes.cpp | 5 +- src/lib/base/EventTypes.h | 44 +- src/lib/base/FunctionEventJob.cpp | 4 +- src/lib/base/FunctionEventJob.h | 4 +- src/lib/base/FunctionJob.cpp | 43 - src/lib/base/FunctionJob.h | 39 - src/lib/base/IEventJob.h | 4 +- src/lib/base/IEventQueue.h | 8 +- src/lib/base/IEventQueueBuffer.h | 6 +- src/lib/base/IJob.h | 4 +- src/lib/base/ILogOutputter.h | 4 +- src/lib/base/Log.cpp | 10 +- src/lib/base/Log.h | 12 +- src/lib/base/PriorityQueue.h | 4 +- src/lib/base/SimpleEventQueueBuffer.cpp | 4 +- src/lib/base/SimpleEventQueueBuffer.h | 6 +- src/lib/base/Stopwatch.cpp | 4 +- src/lib/base/Stopwatch.h | 6 +- src/lib/base/String.cpp | 78 +- src/lib/base/String.h | 9 +- src/lib/base/TMethodEventJob.h | 4 +- src/lib/base/TMethodJob.h | 68 - src/lib/base/Unicode.cpp | 4 +- src/lib/base/Unicode.h | 4 +- src/lib/base/XBase.cpp | 4 +- src/lib/base/XBase.h | 4 +- src/lib/base/finally.h | 61 + src/lib/base/log_outputters.cpp | 11 +- src/lib/base/log_outputters.h | 4 +- src/lib/client/CMakeLists.txt | 4 +- src/lib/client/Client.cpp | 46 +- src/lib/client/Client.h | 16 +- src/lib/client/ServerProxy.cpp | 37 +- src/lib/client/ServerProxy.h | 8 +- src/lib/common/CMakeLists.txt | 4 +- src/lib/common/DataDirectories.h | 37 +- src/lib/common/DataDirectories_static.cpp | 41 +- src/lib/common/IInterface.h | 4 +- src/lib/common/MacOSXPrecomp.h | 23 - src/lib/common/PathUtilities.cpp | 75 - src/lib/common/PathUtilities.h | 31 - src/lib/common/Version.cpp | 4 +- src/lib/common/Version.h | 4 +- src/lib/common/basic_types.h | 67 +- src/lib/common/common.h | 24 +- src/lib/common/stdbitset.h | 4 +- src/lib/common/stddeque.h | 4 +- src/lib/common/stdfstream.h | 4 +- src/lib/common/stdistream.h | 4 +- src/lib/common/stdlist.h | 4 +- src/lib/common/stdmap.h | 4 +- src/lib/common/stdostream.h | 4 +- src/lib/common/stdpost.h | 4 +- src/lib/common/stdpre.h | 4 +- src/lib/common/stdset.h | 4 +- src/lib/common/stdsstream.h | 4 +- src/lib/common/stdstring.h | 4 +- src/lib/common/stdvector.h | 4 +- src/lib/common/unix/DataDirectories.cpp | 38 +- src/lib/common/win32/DataDirectories.cpp | 36 +- src/lib/common/win32/encoding_utilities.cpp | 37 + src/lib/common/win32/encoding_utilities.h | 28 + src/lib/io/CMakeLists.txt | 4 +- src/lib/io/IStream.h | 4 +- src/lib/io/StreamBuffer.cpp | 6 +- src/lib/io/StreamBuffer.h | 4 +- src/lib/io/StreamFilter.cpp | 4 +- src/lib/io/StreamFilter.h | 4 +- src/lib/io/XIO.cpp | 4 +- src/lib/io/XIO.h | 4 +- src/lib/io/filesystem.cpp | 71 + src/lib/io/filesystem.h | 41 + src/lib/ipc/CMakeLists.txt | 4 +- src/lib/ipc/Ipc.h | 4 +- src/lib/ipc/IpcClient.cpp | 4 +- src/lib/ipc/IpcClient.h | 6 +- src/lib/ipc/IpcClientProxy.cpp | 8 +- src/lib/ipc/IpcClientProxy.h | 6 +- src/lib/ipc/IpcLogOutputter.cpp | 11 +- src/lib/ipc/IpcLogOutputter.h | 16 +- src/lib/ipc/IpcMessage.cpp | 4 +- src/lib/ipc/IpcMessage.h | 4 +- src/lib/ipc/IpcServer.cpp | 4 +- src/lib/ipc/IpcServer.h | 8 +- src/lib/ipc/IpcServerProxy.cpp | 12 +- src/lib/ipc/IpcServerProxy.h | 4 +- src/lib/mt/CMakeLists.txt | 4 +- src/lib/mt/CondVar.cpp | 6 +- src/lib/mt/CondVar.h | 6 +- src/lib/mt/Lock.cpp | 4 +- src/lib/mt/Lock.h | 4 +- src/lib/mt/Mutex.cpp | 4 +- src/lib/mt/Mutex.h | 4 +- src/lib/mt/Thread.cpp | 46 +- src/lib/mt/Thread.h | 35 +- src/lib/mt/XMT.cpp | 4 +- src/lib/mt/XMT.h | 4 +- src/lib/mt/XThread.h | 14 +- src/lib/net/CMakeLists.txt | 4 +- src/lib/net/ConnectionSecurityLevel.h | 27 + src/lib/net/FingerprintData.cpp | 52 + src/lib/net/FingerprintData.h | 46 + src/lib/net/FingerprintDatabase.cpp | 135 + src/lib/net/FingerprintDatabase.h | 53 + src/lib/net/IDataSocket.cpp | 4 +- src/lib/net/IDataSocket.h | 4 +- src/lib/net/IListenSocket.h | 6 +- src/lib/net/ISocket.h | 4 +- src/lib/net/ISocketFactory.h | 15 +- src/lib/net/ISocketMultiplexerJob.h | 4 +- src/lib/net/NetworkAddress.cpp | 4 +- src/lib/net/NetworkAddress.h | 6 +- src/lib/net/SecureListenSocket.cpp | 36 +- src/lib/net/SecureListenSocket.h | 15 +- src/lib/net/SecureSocket.cpp | 356 +- src/lib/net/SecureSocket.h | 62 +- src/lib/net/SecureUtils.cpp | 312 + src/lib/net/SecureUtils.h | 43 + src/lib/net/SocketMultiplexer.cpp | 17 +- src/lib/net/SocketMultiplexer.h | 6 +- src/lib/net/TCPListenSocket.cpp | 16 +- src/lib/net/TCPListenSocket.h | 4 +- src/lib/net/TCPSocket.cpp | 42 +- src/lib/net/TCPSocket.h | 10 +- src/lib/net/TCPSocketFactory.cpp | 21 +- src/lib/net/TCPSocketFactory.h | 15 +- src/lib/net/TSocketMultiplexerMethodJob.h | 94 +- src/lib/net/XSocket.cpp | 6 +- src/lib/net/XSocket.h | 4 +- src/lib/platform/CMakeLists.txt | 4 +- src/lib/platform/IMSWindowsClipboardFacade.h | 6 +- src/lib/platform/IOSXKeyResource.cpp | 6 +- src/lib/platform/IOSXKeyResource.h | 8 +- src/lib/platform/IXWindowsImpl.h | 58 +- src/lib/platform/ImmuneKeysReader.cpp | 2 +- src/lib/platform/ImmuneKeysReader.h | 4 - src/lib/platform/MSWindowsClipboard.cpp | 6 +- src/lib/platform/MSWindowsClipboard.h | 4 +- .../MSWindowsClipboardAnyTextConverter.cpp | 4 +- .../platform/MSWindowsClipboardAnyTextConverter.h | 4 +- .../platform/MSWindowsClipboardBitmapConverter.cpp | 4 +- .../platform/MSWindowsClipboardBitmapConverter.h | 4 +- src/lib/platform/MSWindowsClipboardFacade.cpp | 4 +- src/lib/platform/MSWindowsClipboardFacade.h | 4 +- .../platform/MSWindowsClipboardHTMLConverter.cpp | 4 +- src/lib/platform/MSWindowsClipboardHTMLConverter.h | 4 +- .../platform/MSWindowsClipboardTextConverter.cpp | 4 +- src/lib/platform/MSWindowsClipboardTextConverter.h | 4 +- .../platform/MSWindowsClipboardUTF16Converter.cpp | 4 +- .../platform/MSWindowsClipboardUTF16Converter.h | 4 +- src/lib/platform/MSWindowsDebugOutputter.cpp | 4 +- src/lib/platform/MSWindowsDebugOutputter.h | 6 +- src/lib/platform/MSWindowsDesks.cpp | 25 +- src/lib/platform/MSWindowsDesks.h | 13 +- src/lib/platform/MSWindowsDropTarget.cpp | 10 +- src/lib/platform/MSWindowsDropTarget.h | 8 +- src/lib/platform/MSWindowsEventQueueBuffer.cpp | 4 +- src/lib/platform/MSWindowsEventQueueBuffer.h | 4 +- src/lib/platform/MSWindowsHook.cpp | 66 +- src/lib/platform/MSWindowsHook.h | 4 +- src/lib/platform/MSWindowsHookResource.cpp | 2 +- src/lib/platform/MSWindowsHookResource.h | 2 +- src/lib/platform/MSWindowsKeyState.cpp | 105 +- src/lib/platform/MSWindowsKeyState.h | 6 +- src/lib/platform/MSWindowsScreen.cpp | 92 +- src/lib/platform/MSWindowsScreen.h | 19 +- src/lib/platform/MSWindowsScreenSaver.cpp | 17 +- src/lib/platform/MSWindowsScreenSaver.h | 8 +- src/lib/platform/MSWindowsSession.cpp | 14 +- src/lib/platform/MSWindowsSession.h | 6 +- src/lib/platform/MSWindowsUtil.cpp | 4 +- src/lib/platform/MSWindowsUtil.h | 4 +- src/lib/platform/MSWindowsWatchdog.cpp | 60 +- src/lib/platform/MSWindowsWatchdog.h | 8 +- src/lib/platform/OSXClipboard.cpp | 14 +- src/lib/platform/OSXClipboard.h | 4 +- src/lib/platform/OSXClipboardAnyTextConverter.cpp | 4 +- src/lib/platform/OSXClipboardAnyTextConverter.h | 4 +- src/lib/platform/OSXClipboardHTMLConverter.cpp | 2 +- src/lib/platform/OSXClipboardTextConverter.cpp | 12 +- src/lib/platform/OSXClipboardTextConverter.h | 4 +- src/lib/platform/OSXClipboardUTF16Converter.cpp | 4 +- src/lib/platform/OSXClipboardUTF16Converter.h | 4 +- src/lib/platform/OSXDragSimulator.h | 2 +- src/lib/platform/OSXDragSimulator.m | 102 - src/lib/platform/OSXDragSimulator.mm | 102 + src/lib/platform/OSXDragView.m | 177 - src/lib/platform/OSXDragView.mm | 177 + src/lib/platform/OSXEventQueueBuffer.cpp | 18 +- src/lib/platform/OSXEventQueueBuffer.h | 4 +- src/lib/platform/OSXKeyState.cpp | 74 +- src/lib/platform/OSXKeyState.h | 8 +- src/lib/platform/OSXMediaKeySimulator.m | 92 - src/lib/platform/OSXMediaKeySimulator.mm | 92 + src/lib/platform/OSXMediaKeySupport.m | 154 - src/lib/platform/OSXMediaKeySupport.mm | 154 + src/lib/platform/OSXPasteboardPeeker.h | 2 +- src/lib/platform/OSXPasteboardPeeker.m | 37 - src/lib/platform/OSXPasteboardPeeker.mm | 37 + src/lib/platform/OSXScreen.h | 48 +- src/lib/platform/OSXScreen.mm | 218 +- src/lib/platform/OSXScreenSaver.cpp | 14 +- src/lib/platform/OSXScreenSaver.h | 8 +- src/lib/platform/OSXScreenSaverControl.h | 2 +- src/lib/platform/OSXScreenSaverUtil.h | 4 +- src/lib/platform/OSXScreenSaverUtil.m | 83 - src/lib/platform/OSXScreenSaverUtil.mm | 83 + src/lib/platform/OSXUchrKeyResource.cpp | 4 +- src/lib/platform/OSXUchrKeyResource.h | 12 +- src/lib/platform/XWindowsClipboard.cpp | 8 +- src/lib/platform/XWindowsClipboard.h | 10 +- .../XWindowsClipboardAnyBitmapConverter.cpp | 4 +- .../platform/XWindowsClipboardAnyBitmapConverter.h | 4 +- src/lib/platform/XWindowsClipboardBMPConverter.cpp | 4 +- src/lib/platform/XWindowsClipboardBMPConverter.h | 4 +- .../platform/XWindowsClipboardHTMLConverter.cpp | 4 +- src/lib/platform/XWindowsClipboardHTMLConverter.h | 4 +- .../platform/XWindowsClipboardTextConverter.cpp | 4 +- src/lib/platform/XWindowsClipboardTextConverter.h | 4 +- .../platform/XWindowsClipboardUCS2Converter.cpp | 4 +- src/lib/platform/XWindowsClipboardUCS2Converter.h | 4 +- .../platform/XWindowsClipboardUTF8Converter.cpp | 4 +- src/lib/platform/XWindowsClipboardUTF8Converter.h | 4 +- src/lib/platform/XWindowsEventQueueBuffer.cpp | 8 +- src/lib/platform/XWindowsEventQueueBuffer.h | 10 +- src/lib/platform/XWindowsKeyState.cpp | 20 +- src/lib/platform/XWindowsKeyState.h | 24 +- src/lib/platform/XWindowsScreen.cpp | 12 +- src/lib/platform/XWindowsScreen.h | 10 +- src/lib/platform/XWindowsScreenSaver.cpp | 28 +- src/lib/platform/XWindowsScreenSaver.h | 12 +- src/lib/platform/XWindowsUtil.cpp | 49 +- src/lib/platform/XWindowsUtil.h | 12 +- src/lib/platform/synwinhk.h | 4 +- src/lib/server/BaseClientProxy.cpp | 4 +- src/lib/server/BaseClientProxy.h | 4 +- src/lib/server/CMakeLists.txt | 4 +- src/lib/server/ClientListener.cpp | 37 +- src/lib/server/ClientListener.h | 13 +- src/lib/server/ClientProxy.cpp | 4 +- src/lib/server/ClientProxy.h | 4 +- src/lib/server/ClientProxy1_0.cpp | 27 +- src/lib/server/ClientProxy1_0.h | 4 +- src/lib/server/ClientProxy1_1.cpp | 4 +- src/lib/server/ClientProxy1_1.h | 4 +- src/lib/server/ClientProxy1_2.cpp | 4 +- src/lib/server/ClientProxy1_2.h | 4 +- src/lib/server/ClientProxy1_3.cpp | 4 +- src/lib/server/ClientProxy1_3.h | 4 +- src/lib/server/ClientProxy1_4.cpp | 4 +- src/lib/server/ClientProxy1_4.h | 4 +- src/lib/server/ClientProxy1_5.cpp | 8 +- src/lib/server/ClientProxy1_5.h | 4 +- src/lib/server/ClientProxy1_6.cpp | 6 +- src/lib/server/ClientProxy1_6.h | 4 +- src/lib/server/ClientProxyUnknown.cpp | 10 +- src/lib/server/ClientProxyUnknown.h | 4 +- src/lib/server/Config.cpp | 38 +- src/lib/server/Config.h | 6 +- src/lib/server/InputFilter.cpp | 16 +- src/lib/server/InputFilter.h | 18 +- src/lib/server/PrimaryClient.cpp | 4 +- src/lib/server/PrimaryClient.h | 10 +- src/lib/server/Server.cpp | 32 +- src/lib/server/Server.h | 26 +- src/test/CMakeLists.txt | 33 - src/test/global/TestEventQueue.cpp | 6 +- src/test/global/TestEventQueue.h | 4 +- src/test/global/TestUtils.cpp | 37 + src/test/global/TestUtils.h | 30 + src/test/global/gmock.h | 4 +- src/test/global/gtest.h | 4 +- src/test/guitests/src/VersionCheckerTests.cpp | 4 +- src/test/guitests/src/VersionCheckerTests.h | 4 +- src/test/guitests/src/main.cpp | 4 +- src/test/integtests/CMakeLists.txt | 8 +- src/test/integtests/Main.cpp | 12 +- src/test/integtests/ipc/IpcTests.cpp | 21 +- src/test/integtests/net/NetworkTests.cpp | 79 +- .../platform/MSWindowsClipboardTests.cpp | 6 +- .../integtests/platform/MSWindowsKeyStateTests.cpp | 16 +- src/test/integtests/platform/OSXClipboardTests.cpp | 56 +- src/test/integtests/platform/OSXScreenTests.cpp | 4 +- .../integtests/platform/XWindowsClipboardTests.cpp | 44 +- .../integtests/platform/XWindowsKeyStateTests.cpp | 2 +- .../platform/XWindowsScreenSaverTests.cpp | 8 +- .../integtests/platform/XWindowsScreenTests.cpp | 8 +- src/test/mock/barrier/MockApp.h | 2 +- src/test/mock/barrier/MockArgParser.h | 2 +- src/test/mock/barrier/MockEventQueue.h | 4 +- src/test/mock/barrier/MockKeyState.h | 4 +- src/test/mock/barrier/MockScreen.h | 2 +- src/test/mock/ipc/MockIpcServer.h | 2 +- src/test/mock/server/MockConfig.h | 2 +- src/test/mock/server/MockInputFilter.h | 2 +- src/test/mock/server/MockPrimaryClient.h | 2 +- src/test/mock/server/MockServer.h | 2 +- src/test/unittests/CMakeLists.txt | 8 +- src/test/unittests/Main.cpp | 8 +- src/test/unittests/barrier/ArgParserTests.cpp | 4 +- .../unittests/barrier/ClientArgsParsingTests.cpp | 4 +- src/test/unittests/barrier/ClipboardChunkTests.cpp | 4 +- src/test/unittests/barrier/ClipboardTests.cpp | 16 +- .../unittests/barrier/GenericArgsParsingTests.cpp | 36 +- src/test/unittests/barrier/KeyMapTests.cpp | 42 +- src/test/unittests/barrier/KeyStateTests.cpp | 12 +- src/test/unittests/base/StringTests.cpp | 36 +- src/test/unittests/ipc/IpcLogOutputterTests.cpp | 20 +- .../unittests/net/FingerprintDatabaseTests.cpp | 95 + src/test/unittests/net/SecureUtilsTests.cpp | 73 + src/test/unittests/platform/OSXKeyStateTests.cpp | 12 +- towncrier.toml | 39 + 711 files changed, 34644 insertions(+), 6810 deletions(-) create mode 100644 .editorconfig delete mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 RELEASING.md create mode 100644 cmake/gtest.cmake delete mode 100644 doc/QtCodeStyle.xml create mode 100644 doc/barrier.conf.example-barebones create mode 100644 doc/newsfragments/README.md create mode 100644 doc/release_notes/index.md create mode 100644 doc/release_notes/index.template.jinja create mode 100644 gulrak-filesystem/.appveyor.yml create mode 100755 gulrak-filesystem/.ci/unix-build.sh create mode 100755 gulrak-filesystem/.ci/unix-test.sh create mode 100644 gulrak-filesystem/.cirrus.yml create mode 100644 gulrak-filesystem/.clang-format create mode 100644 gulrak-filesystem/.clang-tidy create mode 100644 gulrak-filesystem/.drone.yml create mode 100644 gulrak-filesystem/.github/ISSUE_TEMPLATE/bug_report.md create mode 100644 gulrak-filesystem/.github/ISSUE_TEMPLATE/feature_request.md create mode 100644 gulrak-filesystem/.github/workflows/build_cmake.yml create mode 100644 gulrak-filesystem/.gitignore create mode 100644 gulrak-filesystem/CMakeLists.txt create mode 100644 gulrak-filesystem/LICENSE create mode 100644 gulrak-filesystem/README.md create mode 100644 gulrak-filesystem/cmake/GhcHelper.cmake create mode 100644 gulrak-filesystem/cmake/config.cmake.in create mode 100644 gulrak-filesystem/examples/CMakeLists.txt create mode 100644 gulrak-filesystem/examples/dir.cpp create mode 100644 gulrak-filesystem/examples/du.cpp create mode 100644 gulrak-filesystem/include/ghc/filesystem.hpp create mode 100644 gulrak-filesystem/include/ghc/fs_fwd.hpp create mode 100644 gulrak-filesystem/include/ghc/fs_impl.hpp create mode 100644 gulrak-filesystem/include/ghc/fs_std.hpp create mode 100644 gulrak-filesystem/include/ghc/fs_std_fwd.hpp create mode 100644 gulrak-filesystem/include/ghc/fs_std_impl.hpp create mode 100644 gulrak-filesystem/test/CMakeLists.txt create mode 100644 gulrak-filesystem/test/catch.hpp create mode 100644 gulrak-filesystem/test/cmake/ParseAndAddCatchTests.cmake create mode 100644 gulrak-filesystem/test/exception.cpp create mode 100644 gulrak-filesystem/test/filesystem_test.cpp create mode 100644 gulrak-filesystem/test/fwd_test.cpp create mode 100644 gulrak-filesystem/test/impl_test.cpp create mode 100644 gulrak-filesystem/test/multi1.cpp create mode 100644 gulrak-filesystem/test/multi2.cpp delete mode 100644 res/openssl/barrier.conf delete mode 100644 src/cmd/barrierc/barrierc.exe.manifest create mode 100644 src/cmd/barrierd/barrierd.ico create mode 100644 src/cmd/barrierd/barrierd.rc delete mode 100644 src/cmd/barriers/barriers.exe.manifest delete mode 100644 src/gui/gui.pro mode change 100644 => 100755 src/gui/langbuild.cmd delete mode 100644 src/gui/src/Fingerprint.cpp delete mode 100644 src/gui/src/Fingerprint.h create mode 100644 src/gui/src/FingerprintAcceptDialog.cpp create mode 100644 src/gui/src/FingerprintAcceptDialog.h create mode 100644 src/gui/src/FingerprintAcceptDialog.ui create mode 100644 src/gui/test/HotkeyTests.cpp create mode 100644 src/gui/test/KeySequenceTests.cpp create mode 100644 src/gui/test/Utils.h create mode 100644 src/gui/test/main.cpp delete mode 100644 src/lib/arch/vsnprintf.h create mode 100644 src/lib/barrier/BarrierType.h delete mode 100644 src/lib/base/FunctionJob.cpp delete mode 100644 src/lib/base/FunctionJob.h delete mode 100644 src/lib/base/TMethodJob.h create mode 100644 src/lib/base/finally.h delete mode 100644 src/lib/common/MacOSXPrecomp.h delete mode 100644 src/lib/common/PathUtilities.cpp delete mode 100644 src/lib/common/PathUtilities.h create mode 100644 src/lib/common/win32/encoding_utilities.cpp create mode 100644 src/lib/common/win32/encoding_utilities.h create mode 100644 src/lib/io/filesystem.cpp create mode 100644 src/lib/io/filesystem.h create mode 100644 src/lib/net/ConnectionSecurityLevel.h create mode 100644 src/lib/net/FingerprintData.cpp create mode 100644 src/lib/net/FingerprintData.h create mode 100644 src/lib/net/FingerprintDatabase.cpp create mode 100644 src/lib/net/FingerprintDatabase.h create mode 100644 src/lib/net/SecureUtils.cpp create mode 100644 src/lib/net/SecureUtils.h delete mode 100644 src/lib/platform/OSXDragSimulator.m create mode 100644 src/lib/platform/OSXDragSimulator.mm delete mode 100644 src/lib/platform/OSXDragView.m create mode 100644 src/lib/platform/OSXDragView.mm delete mode 100644 src/lib/platform/OSXMediaKeySimulator.m create mode 100644 src/lib/platform/OSXMediaKeySimulator.mm delete mode 100644 src/lib/platform/OSXMediaKeySupport.m create mode 100644 src/lib/platform/OSXMediaKeySupport.mm delete mode 100644 src/lib/platform/OSXPasteboardPeeker.m create mode 100644 src/lib/platform/OSXPasteboardPeeker.mm delete mode 100644 src/lib/platform/OSXScreenSaverUtil.m create mode 100644 src/lib/platform/OSXScreenSaverUtil.mm delete mode 100644 src/test/CMakeLists.txt create mode 100644 src/test/global/TestUtils.cpp create mode 100644 src/test/global/TestUtils.h create mode 100644 src/test/unittests/net/FingerprintDatabaseTests.cpp create mode 100644 src/test/unittests/net/SecureUtilsTests.cpp create mode 100644 towncrier.toml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d741e40 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 4 diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 1411b05..0000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,30 +0,0 @@ -### Operating Systems ### - -Server: microOS Tiara - -Client: Applesoft Windy OS 10 - -**READ ME, DELETE ME**: On Windows, hold the Windows key and press 'r', type 'winver' and hit return to get your OS version. On Mac, hit the Apple menu (top left of the screen) and check 'About this Mac'. Linux users... you know what you're using ;) - -### Barrier Version ### - -1.9.π - -**READ ME, DELETE ME**: Go to the 'Help' (on Windows) or 'Barrier' (on macOS) menu and then 'About Barrier' to check your version. Verify that you are using the same version across all of your machines, and that your issue still occurs with the latest release available at https://github.com/debauchee/barrier/ - -### Steps to reproduce bug ### - -**READ ME, DELETE ME**: Try to be succinct. If your bug is intermittent, try and describe what you're doing when it happens most. - -1. Click things. -2. Type things. -3. Bug occurs. -4. ... - -### Other info ### - -* When did the problem start to occur? When I... -* Is there a way to work around it? No/Yes, you can... -* Does this bug prevent you from using Barrier entirely? Yes/No - -Put anything else you can think of here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..208e705 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,79 @@ +name: Bug Report +description: File a bug report (for questions, ideas & support, use the Discussions tab, or IRC for quick answers, but make sure to stay on the channel!) +labels: [bug, triage] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Also tell us, what did you expect to happen? + placeholder: Tell us what you see! Screenshots are also helpful - please attach to the issue (when created), rather than linking to image hosting sites. + validations: + required: true + - type: dropdown + id: version + attributes: + label: Version + description: What version of Barrier are you running? + options: + - v2.0.0-RC1 + - v2.0.0-RC2 + - v2.0.0 + - v2.1.1 + - v2.1.2 + - v2.3.0 + - v2.3.1 + - v2.3.2-alpha + - v2.3.2 + - v2.3.3 + - From Git HEAD or commit (specify below) + validations: + required: true + - type: input + id: git-commit-if + attributes: + label: Git commit hash (if applicable) + description: "When building Barrier from Git, what commit hash did you checkout from?" + placeholder: b0c0b42b + validations: + required: false + - type: textarea + id: pkg-mgr-origin + attributes: + label: If applicable, where did you install Barrier from? + description: This includes Snaps, Flatpaks, and self-built executables. + validations: + required: false + - type: dropdown + id: os + attributes: + label: What OSes are you seeing the problem on? (Check all that apply) + multiple: true + options: + - Linux + - Windows + - macOS + validations: + required: true + - type: textarea + id: os-version + attributes: + label: What OS versions are you using? + description: This applies to both client(s) and the server. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: shell + - type: textarea + id: misc-info + attributes: + label: Any other information + description: Please enter any other information we should know, if applicable. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3ba13e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.gitmodules b/.gitmodules index ad9a302..9a58a7e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "ext/gmock"] path = ext/gmock url = https://github.com/google/googlemock.git +[submodule "ext/gulrak-filesystem"] + path = ext/gulrak-filesystem + url = https://github.com/gulrak/filesystem diff --git a/Build.properties b/Build.properties index 437beb7..07192e0 100644 --- a/Build.properties +++ b/Build.properties @@ -2,6 +2,6 @@ # Barrier build parameters # BARRIER_VERSION_MAJOR = 2 -BARRIER_VERSION_MINOR = 3 -BARRIER_VERSION_PATCH = 2 -BARRIER_VERSION_STAGE = snapshot +BARRIER_VERSION_MINOR = 4 +BARRIER_VERSION_PATCH = 0 +BARRIER_VERSION_STAGE = release diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a377c0..86deb4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,8 @@ project (barrier C CXX) option (BARRIER_BUILD_GUI "Build the GUI" ON) option (BARRIER_BUILD_INSTALLER "Build the installer" ON) +option (BARRIER_BUILD_TESTS "Build the tests" ON) +option (BARRIER_USE_EXTERNAL_GTEST "Use external installation of Google Test framework" OFF) set (CMAKE_EXPORT_COMPILE_COMMANDS ON) set (CMAKE_CXX_STANDARD 14) @@ -49,7 +51,6 @@ else() endif() set (libs) -include_directories (BEFORE SYSTEM ./ext/gtest/include) if (UNIX) if (NOT APPLE) @@ -90,7 +91,6 @@ if (UNIX) check_function_exists (poll HAVE_POLL) check_function_exists (sigwait HAVE_POSIX_SIGWAIT) check_function_exists (strftime HAVE_STRFTIME) - check_function_exists (vsnprintf HAVE_VSNPRINTF) check_function_exists (inet_aton HAVE_INET_ATON) # For some reason, the check_function_exists macro doesn't detect @@ -165,10 +165,10 @@ if (UNIX) link_directories("/usr/local/lib") endif() - if (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") - set (CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};/usr/X11R6/include;/usr/local/include;/usr/local/include/avahi-compat-libdns_sd") + if (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") + set (CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};/usr/X11R6/include;/usr/local/include;/usr/local/include/avahi-compat-libdns_sd") set (CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -L/usr/local/lib -L/usr/X11R6/lib") - include_directories("/usr/local/include" "/usr/X11R6/include" "/usr/local/include/avahi-compat-libdns_sd") + include_directories("/usr/local/include" "/usr/X11R6/include" "/usr/local/include/avahi-compat-libdns_sd") link_directories("/usr/local/lib") link_directories("/usr/X11R6/lib") endif() @@ -194,9 +194,8 @@ if (UNIX) check_include_files ("X11/extensions/XInput2.h" HAVE_XI2) check_include_files ("dns_sd.h" HAVE_DNSSD) - if (HAVE_X11_EXTENSIONS_DPMS_H) - # Assume that function prototypes declared, when include exists. - set (HAVE_DPMS_PROTOTYPES 1) + if (NOT HAVE_X11_EXTENSIONS_XTEST_H) + message (FATAL_ERROR "Missing header: X11/extensions/XTest.h") endif() if (NOT HAVE_X11_XKBLIB_H) @@ -292,6 +291,9 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows") list (APPEND libs Wtsapi32 Userenv Wininet comsuppw Shlwapi) add_definitions ( + /DSYSAPI_WIN32=1 + /DWINAPI_MSWINDOWS=1 + /D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=1 # tr1 is used from gtest and gmock /DWIN32 /D_WINDOWS /D_CRT_SECURE_NO_WARNINGS @@ -300,6 +302,8 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows") ) endif() +include_directories("${CMAKE_SOURCE_DIR}/ext/gulrak-filesystem/include") + # # OpenSSL # @@ -317,10 +321,21 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") ${OPENSSL_ROOT}/lib/ssleay32.lib ) elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + find_program(APT_PROGRAM "apt") find_program(BREW_PROGRAM "brew") find_program(PORT_PROGRAM "port") - - if (IS_DIRECTORY /opt/local AND PORT_PROGRAM) + + if (IS_DIRECTORY /opt/procursus AND APT_PROGRAM) + # procursus/apt + set (OPENSSL_ROOT /opt/procursus) + + include_directories (BEFORE SYSTEM ${OPENSSL_ROOT}/include) + + set (OPENSSL_LIBS + ${OPENSSL_ROOT}/lib/libssl.a + ${OPENSSL_ROOT}/lib/libcrypto.a + ) + elseif (IS_DIRECTORY /opt/local AND PORT_PROGRAM) # macports set (OPENSSL_ROOT /opt/local) @@ -335,6 +350,16 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") include_directories (BEFORE SYSTEM ${OPENSSL_ROOT}/include) + set (OPENSSL_LIBS + ${OPENSSL_ROOT}/lib/libssl.a + ${OPENSSL_ROOT}/lib/libcrypto.a + ) + elseif (IS_DIRECTORY /opt/homebrew/opt/openssl AND BREW_PROGRAM) + # brew + set (OPENSSL_ROOT /opt/homebrew/opt/openssl) + + include_directories (BEFORE SYSTEM ${OPENSSL_ROOT}/include) + set (OPENSSL_LIBS ${OPENSSL_ROOT}/lib/libssl.a ${OPENSSL_ROOT}/lib/libcrypto.a @@ -369,7 +394,7 @@ macro (configure_files srcDir destDir) set (sourceFilePath ${srcDir}/${sourceFile}) if (IS_DIRECTORY ${sourceFilePath}) message (STATUS "Copying directory ${sourceFile}") - make_directory (${destDir/${sourceFile}) + make_directory (${destDir}/${sourceFile}) else() message (STATUS "Copying file ${sourceFile}") configure_file (${sourceFilePath} ${destDir}/${sourceFile} COPYONLY) @@ -420,7 +445,7 @@ endif() if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") configure_files (${CMAKE_CURRENT_SOURCE_DIR}/dist/rpm ${CMAKE_BINARY_DIR}/rpm) install(FILES res/barrier.svg DESTINATION share/icons/hicolor/scalable/apps) - if("${VERSION_MAJOR}" STREQUAL "2") + if("${VERSION_MAJOR}" STREQUAL "2") install(FILES res/barrier2.desktop DESTINATION share/applications) else() install(FILES res/barrier.desktop DESTINATION share/applications) @@ -430,4 +455,7 @@ endif() else() message (STATUS "NOT configuring the installer") endif() + +enable_testing() + add_subdirectory (src) diff --git a/LICENSE b/LICENSE index acfef1b..42498a4 100644 --- a/LICENSE +++ b/LICENSE @@ -3,7 +3,7 @@ Copyright (C) 2012-2016 Symless Ltd. Copyright (C) 2008-2014 Nick Bolton Copyright (C) 2002-2014 Chris Schoeneman -This program is released under the GPL with the additional exemption +This program is released under the GPL with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. GNU GENERAL PUBLIC LICENSE @@ -286,3 +286,62 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.md b/README.md index 7f380b1..3c545e9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Barrier -Eliminate the barrier between your machines. +Eliminate the barrier between your machines. Find [releases for windows and macOS here](https://github.com/debauchee/barrier/releases). Your distro probably already has barrier packaged for it, see [distro specific packages](#distro-specific-packages) below for a list. Alternatively, we also provide a [flatpak](https://github.com/flathub/com.github.debauchee.barrier) @@ -8,7 +8,7 @@ and a [snap](https://snapcraft.io/barrier). ### Contact info: -- `#barrier` on freenode +- `#barrier` on LiberaChat IRC network #### CI Build Status @@ -30,9 +30,14 @@ Barrier is software that mimics the functionality of a KVM switch, which histori Barrier was forked from Symless's Synergy 1.9 codebase. Synergy was a commercialized reimplementation of the original CosmoSynergy written by Chris Schoeneman. +At the moment, barrier is not compatible with synergy. Barrier needs to be installed on all machines that will share keyboard and mouse. + ### What's different? -Whereas Synergy has moved beyond its goals from the 1.x era, Barrier aims to maintain that simplicity. Barrier will let you use your keyboard and mouse from machine A to control machine B (or more). It's that simple. +Whereas Synergy has moved beyond its goals from the 1.x era, Barrier aims to maintain that simplicity. +Barrier will let you use your keyboard and mouse from one computer to control one or more other computers. +Clipboard sharing is supported. +That's it. ### Project goals @@ -42,15 +47,33 @@ Compatibility. We use more than one operating system and you probably do, too. W Communication. Everything we do is in the open. Our issue tracker will let you see if others are having the same problem you're having and will allow you to add additional information. You will also be able to see when progress is made and how the issue gets resolved. +### Usage + +Install and run barrier on each machine that will be sharing. +On the machine with the keyboard and mouse, make it the server. + +Click the "Configure server" button and drag a new screen onto the grid for each client machine. +Ensure the "screen name" matches exactly (case-sensitive) for each configured screen -- the clients' barrier windows will tell you their screen names (just above the server IP). + +On the client(s), put in the server machine's IP address (or use Bonjour/auto configuration when prompted) and "start" them. +You should see `Barrier is running` on both server and clients. +You should now be able to move the mouse between all the screens as if they were the same machine. + +Note that if the keyboard's Scroll Lock is active then this will prevent the mouse from switching screens. + ### Contact & support Please be aware that the *only* way to draw our attention to a bug is to create a new issue in [the issue tracker](https://github.com/debauchee/barrier/issues). Write a clear, concise, detailed report and you will get a clear, concise, detailed response. Priority is always given to issues that affect a wider range of users. -For short and simple questions or to just say hello find us on the Freenode IRC network in the #barrier channel. +For short and simple questions or to just say hello find us on the LiberaChat IRC network in the #barrier channel. ### Contributions -At this time we are looking for developers to help fix the issues found in the issue tracker. Submit pull requests once you've polished up your patch and we'll review and possibly merge it. +At this time we are looking for developers to help fix the issues found in the issue tracker. +Submit pull requests once you've polished up your patch and we'll review and possibly merge it. + +Most pull requests will need to include a release note. +See docs/newsfragments/README.md for documentation of how to do that. ## Distro specific packages @@ -77,3 +100,15 @@ A: Q: Are 32-bit versions of Windows supported? A: No + +Q: How do I load my configuration on startup? + +A: Start the binary with the argument `--config ` + +Q: After loading my configuration on the client the field 'Server IP' is still empty! + +A: Edit your configuration to include the server's ip address manually with + + (...) + section: options + serverhostname= diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..741f54a --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,59 @@ +Creating a release +================== + +This document is documentation intednded for maintainers of Barrier. +It documents the release process of Barrier. + +Step 1: Setup environment variables +----------------------------------- + +Setup the following environment variable that will be used throughout the rest of the steps. + + export VERSION=X.Y.Z + +Step 2: Release notes PR +------------------------ + +Open a new branch (e.g. `release`) and run the following: + + towncrier --version ${VERSION} --date `date -u +%F` + +This collects the release notes using the `towncrier` tool. Please commit the collected release +notes afterwards. + +Certain file names are not properly supported by the `towncrier` tool and it ignores them. +Check `newsfragments` directory for any forgotten release notes + +Step 3: Merge the release notes PR +---------------------------------- + +Step 4: Push git tag +-------------------- + +Pull the merge commit created on the `master` branch during the step 2. + +Create a tag: + + git tag -s v${VERSION} -m v${VERSION} + +Push the tag: + + git push origin master --tags + + +Step 5: Draft a new release on Github +------------------------------------- + +Go to https://github.com/buildbot/buildbot/releases and draft a new release. + +Use git tag as the title of the release: `vX.Y.Z`. + +Use the release notes generated by the `towncrier` tool as the description of the releases. + +Upload the artifacts created by Azure pipelines as the binaries of the release. The following +artifacts should be uploaded to Github: + + - the Barrier-X.Y.Z-release.dmg created by the oldest Mac OS task (artifact name is + "Mac Release Disk Image and App XYZ"). + + - the BarrierSetup-X.Y.Z-release.exe (artifact name is Windows Release Installer). diff --git a/_config.yml b/_config.yml index c741881..f980e76 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-slate \ No newline at end of file +theme: jekyll-theme-slate diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e12cdd6..e34a3bc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -63,9 +63,15 @@ jobs: artifactName: Windows Release Installer - job: LinuxBuild + strategy: + matrix: + ubuntu-18.04: + imageName: 'ubuntu-18.04' + ubuntu-20.04: + imageName: 'ubuntu-20.04' displayName: Linux Build pool: - vmImage: 'ubuntu-16.04' + vmImage: $(imageName) steps: - script: sudo apt-get update -y - script: sudo apt-get install -y libxtst-dev qtdeclarative5-dev libavahi-compat-libdnssd-dev libcurl4-openssl-dev @@ -75,13 +81,22 @@ jobs: - job: MacBuild displayName: Mac Build - pool: - vmImage: 'macOS-10.14' strategy: matrix: - Release: - B_BUILD_TYPE: Release - BARRIER_VERSION_STAGE: Release + big-sur-Release: + imageName: "macOS-11" + B_BUILD_TYPE: Release + BARRIER_VERSION_STAGE: Release + catalina-Release: + imageName: "macOS-10.15" + B_BUILD_TYPE: Release + BARRIER_VERSION_STAGE: Release + mojave-Release: + imageName: "macOS-10.14" + B_BUILD_TYPE: Release + BARRIER_VERSION_STAGE: Release + pool: + vmImage: $(imageName) variables: VERBOSE: 1 TERM: xterm-256color @@ -99,4 +114,4 @@ jobs: condition: eq(variables['B_BUILD_TYPE'], 'Release') inputs: pathtoPublish: build/bundle - artifactName: Mac Release Disk Image and App + artifactName: Mac Release Disk Image and App $(imageName) diff --git a/azure-pipelines/download_install_bonjour_sdk_like.ps1 b/azure-pipelines/download_install_bonjour_sdk_like.ps1 index 97bfa83..7f5fcb9 100644 --- a/azure-pipelines/download_install_bonjour_sdk_like.ps1 +++ b/azure-pipelines/download_install_bonjour_sdk_like.ps1 @@ -1,8 +1,7 @@ $ErrorActionPreference = "Stop" New-Item -Force -ItemType Directory -Path ".\deps\" -$Wc = New-Object System.Net.WebClient -$Wc.DownloadFile('https://github.com/nelsonjchen/mDNSResponder/releases/download/v2019.05.08.1/x64_RelWithDebInfo.zip', 'deps\BonjourSDKLike.zip') ; +Invoke-WebRequest 'https://github.com/nelsonjchen/mDNSResponder/releases/download/v2019.05.08.1/x64_RelWithDebInfo.zip' -OutFile 'deps\BonjourSDKLike.zip' ; Write-Output 'Downloaded BonjourSDKLike Zip' Write-Output 'Unzipping BonjourSDKLike Zip' Remove-Item -Recurse -Force -ErrorAction Ignore .\deps\BonjourSDKLike diff --git a/azure-pipelines/download_install_qt.ps1 b/azure-pipelines/download_install_qt.ps1 index c2c2322..a4b51d2 100644 --- a/azure-pipelines/download_install_qt.ps1 +++ b/azure-pipelines/download_install_qt.ps1 @@ -6,8 +6,7 @@ $qt_version = '5.13.0' New-Item -Force -ItemType Directory -Path ".\deps\" Write-Output 'Downloading QLI Installer' -$Wc = New-Object System.Net.WebClient -$Wc.DownloadFile("https://github.com/nelsonjchen/qli-installer/archive/v$qli_install_version.zip", '.\deps\qli-installer.zip') ; +Invoke-WebRequest "https://github.com/nelsonjchen/qli-installer/archive/v$qli_install_version.zip" -OutFile '.\deps\qli-installer.zip' ; Write-Output 'Downloaded QLI Installer' Write-Output 'Extracting QLI Installer' @@ -21,6 +20,6 @@ Write-Output 'Installed QLI Installer Dependencies' Write-Output 'Starting QT Installer' $Env:QLI_OUT_DIR = ".\deps\Qt\Qt$qt_version" -$Env:QLI_BASE_URL = "http://mirrors.ocf.berkeley.edu/qt/online/qtsdkrepository/" +$Env:QLI_BASE_URL = "https://download.qt.io/online/qtsdkrepository/" python .\deps\qli-installer\qli-installer.py $qt_version windows desktop win64_msvc2017_64 Write-Output 'Installed QT Installer' diff --git a/clean_build.bat b/clean_build.bat index 4416a7e..6f04232 100644 --- a/clean_build.bat +++ b/clean_build.bat @@ -41,8 +41,7 @@ if ERRORLEVEL 1 goto failed cd build cmake -G "%cmake_gen%" -A x64 -D CMAKE_BUILD_TYPE=%B_BUILD_TYPE% -D CMAKE_PREFIX_PATH="%B_QT_FULLPATH%" -D DNSSD_LIB="%B_BONJOUR%\Lib\x64\dnssd.lib" -D QT_VERSION=%B_QT_VER% .. if ERRORLEVEL 1 goto failed -echo @msbuild barrier.sln /p:Platform="x64" /p:Configuration=%B_BUILD_TYPE% /m %B_BUILD_OPTIONS% > make.bat -call make.bat +cmake --build . --config %B_BUILD_TYPE% if ERRORLEVEL 1 goto failed if exist bin\Debug ( copy %B_QT_FULLPATH%\bin\Qt5Cored.dll bin\Debug\ > NUL @@ -65,7 +64,7 @@ if exist bin\Debug ( mkdir bin\Release\platforms copy %B_QT_FULLPATH%\plugins\platforms\qwindows.dll bin\Release\platforms\ > NUL ) else ( - echo Remember to copy supporting binaries and confiuration files! + echo Remember to copy supporting binaries and configuration files! ) echo Build completed successfully diff --git a/clean_build.sh b/clean_build.sh index 3a92d8b..585ca88 100755 --- a/clean_build.sh +++ b/clean_build.sh @@ -1,10 +1,10 @@ #!/bin/sh -cd "$(dirname $0)" || exit 1 +cd "$(dirname "$0")" || exit 1 # some environments have cmake v2 as 'cmake' and v3 as 'cmake3' # check for cmake3 first then fallback to just cmake B_CMAKE=`type cmake3 2>/dev/null` if [ $? -eq 0 ]; then - B_CMAKE=`echo $B_CMAKE | cut -d' ' -f3` + B_CMAKE=`echo "$B_CMAKE" | cut -d' ' -f3` else B_CMAKE=cmake fi @@ -26,7 +26,7 @@ B_CMAKE_FLAGS="-DCMAKE_BUILD_TYPE=$B_BUILD_TYPE $B_CMAKE_FLAGS" rm -rf build mkdir build || exit 1 cd build || exit 1 -echo Starting Barrier $B_BUILD_TYPE build... +echo "Starting Barrier $B_BUILD_TYPE build..." $B_CMAKE $B_CMAKE_FLAGS .. || exit 1 make || exit 1 echo "Build completed successfully" diff --git a/cmake/Version.cmake b/cmake/Version.cmake index 9683649..73524bf 100644 --- a/cmake/Version.cmake +++ b/cmake/Version.cmake @@ -1,8 +1,8 @@ cmake_minimum_required (VERSION 3.4) set (BARRIER_VERSION_MAJOR 2) -set (BARRIER_VERSION_MINOR 3) -set (BARRIER_VERSION_PATCH 3) +set (BARRIER_VERSION_MINOR 4) +set (BARRIER_VERSION_PATCH 0) set (BARRIER_VERSION_STAGE "release") # @@ -85,6 +85,9 @@ message (STATUS "Full Barrier version string is '" ${BARRIER_VERSION_STRING} "'" add_definitions (-DBARRIER_VERSION="${BARRIER_VERSION}") add_definitions (-DBARRIER_VERSION_STRING="${BARRIER_VERSION_STRING}") +add_definitions (-DBARRIER_VERSION_MAJOR=${BARRIER_VERSION_MAJOR}) +add_definitions (-DBARRIER_VERSION_MINOR=${BARRIER_VERSION_MINOR}) +add_definitions (-DBARRIER_VERSION_PATCH=${BARRIER_VERSION_PATCH}) add_definitions (-DBARRIER_REVISION="${BARRIER_REVISION}") add_definitions (-DBARRIER_BUILD_DATE="${BARRIER_BUILD_DATE}") add_definitions (-DBARRIER_BUILD_NUMBER=${BARRIER_BUILD_NUMBER}) diff --git a/cmake/gtest.cmake b/cmake/gtest.cmake new file mode 100644 index 0000000..e83491c --- /dev/null +++ b/cmake/gtest.cmake @@ -0,0 +1,44 @@ +# barrier -- mouse and keyboard sharing utility +# Copyright (C) 2012-2016 Symless Ltd. +# Copyright (C) 2011 Nick Bolton +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file LICENSE that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if (BARRIER_USE_EXTERNAL_GTEST) + include (FindPkgConfig) + find_package(GTest REQUIRED) + pkg_check_modules(GMOCK REQUIRED gmock) + include_directories( + ${GTEST_INCLUDE_DIRS} + ${GMOCK_INCLUDE_DIRS} + ) +else() + include_directories( + ../ext/gtest + ../ext/gtest/include + ../ext/gmock + ../ext/gmock/include + ) + + add_library(gtest STATIC ../ext/gtest/src/gtest-all.cc) + add_library(gmock STATIC ../ext/gmock/src/gmock-all.cc) + + set(GTEST_LIBRARIES gtest) + set(GMOCK_LIBRARIES gmock) + + if (UNIX) + # ignore warnings in gtest and gmock + set_target_properties(gtest PROPERTIES COMPILE_FLAGS "-w") + set_target_properties(gmock PROPERTIES COMPILE_FLAGS "-w") + endif() +endif() diff --git a/doc/QtCodeStyle.xml b/doc/QtCodeStyle.xml deleted file mode 100644 index e621c4f..0000000 --- a/doc/QtCodeStyle.xml +++ /dev/null @@ -1,234 +0,0 @@ - - - - - - CodeStyleData - - false - false - true - false - false - false - true - false - true - false - false - false - true - true - false - true - false - false - false - 4 - true - false - 2 - false - 4 - - - - DisplayName - Barrier - - - - - - - - CodeStyleData - - false - false - true - false - false - false - true - false - true - false - false - false - true - true - false - true - false - false - false - 4 - true - false - 2 - false - 4 - - - - DisplayName - Barrier - - - - - - - - CodeStyleData - - false - false - true - false - false - false - true - false - true - false - false - false - true - true - false - true - false - false - false - 4 - true - false - 2 - false - 4 - - - - DisplayName - Barrier - - - - - - - - CodeStyleData - - false - false - true - false - false - false - true - false - true - false - false - false - true - true - false - true - false - false - false - 4 - true - false - 2 - false - 4 - - - - DisplayName - Barrier - - - - - - - - CodeStyleData - - false - false - true - false - false - false - true - false - true - false - false - false - true - true - false - true - false - false - false - 4 - true - false - 2 - false - 4 - - - - DisplayName - Barrier - - - - - - - - CodeStyleData - - false - false - true - false - false - false - true - false - true - false - false - false - true - true - false - true - false - false - false - 4 - true - false - 2 - false - 4 - - - - DisplayName - Barrier - - diff --git a/doc/barrier.conf.example-advanced b/doc/barrier.conf.example-advanced index ad9df29..e1b2392 100644 --- a/doc/barrier.conf.example-advanced +++ b/doc/barrier.conf.example-advanced @@ -47,9 +47,9 @@ section: links end # The aliases section is to map the full names of the computers to their logical names used in the screens section -# One way to find the actual name of a comptuer is to run hostname from a command window +# One way to find the actual name of a computer is to run hostname from a command window section: aliases # Laptop is actually known as John-Smiths-MacBook-3.local - desktop2: - John-Smiths-MacBook-3.local + John-Smiths-MacBook-3.local: + desktop2 end diff --git a/doc/barrier.conf.example-barebones b/doc/barrier.conf.example-barebones new file mode 100644 index 0000000..1a4558d --- /dev/null +++ b/doc/barrier.conf.example-barebones @@ -0,0 +1,17 @@ +# barebones barrier.conf example +# for two computers side by side +# replace screen.name.server and screen.name.client with your screen names from gui +# there's a 50/50 chance you'll want to swap left and right +# save this as barrier.conf in your home folder and "Use existing configuration" on server + +section: screens + screen.name.server: + screen.name.client: +end + +section: links + screen.name.server: + left = screen.name.client + screen.name.client: + right = screen.name.server +end diff --git a/doc/barrier.conf.example-basic b/doc/barrier.conf.example-basic index 39ff6d6..57d71d9 100644 --- a/doc/barrier.conf.example-basic +++ b/doc/barrier.conf.example-basic @@ -34,6 +34,6 @@ end section: aliases # The "real" name of iMac is John-Smiths-iMac-3.local. If we wanted we could remove this alias and instead use John-Smiths-iMac-3.local everywhere iMac is above. Hopefully it should be easy to see why using an alias is nicer - iMac: - John-Smiths-iMac-3.local + John-Smiths-iMac-3.local: + iMac end diff --git a/doc/barrierc.1 b/doc/barrierc.1 index 2fd882a..d470447 100644 --- a/doc/barrierc.1 +++ b/doc/barrierc.1 @@ -1,5 +1,5 @@ .\" See UpdateManpages.txt about modification of this file. Most of it was generated by help2man 1.47.8. -.TH BARRIERC "1" "November 2019" "barrierc 2.3.3-release" "User Commands" +.TH BARRIERC "1" "November 2019" "barrierc 2.4.0-release" "User Commands" .SH NAME barrierc \- Barrier Keyboard/Mouse Client .SH SYNOPSIS diff --git a/doc/barriers.1 b/doc/barriers.1 index 098ea84..f4a460e 100644 --- a/doc/barriers.1 +++ b/doc/barriers.1 @@ -1,5 +1,5 @@ .\" See UpdateManpages.txt about modification of this file. Most of it was generated by help2man 1.47.8. -.TH BARRIERS "1" "November 2019" "barriers 2.3.3-release" "User Commands" +.TH BARRIERS "1" "November 2019" "barriers 2.4.0-release" "User Commands" .SH NAME barriers \- Barrier Keyboard/Mouse Server .SH SYNOPSIS diff --git a/doc/newsfragments/README.md b/doc/newsfragments/README.md new file mode 100644 index 0000000..0338cf8 --- /dev/null +++ b/doc/newsfragments/README.md @@ -0,0 +1,13 @@ +This is the directory for release note fragments processed by +[towncrier](https://github.com/hawkowl/towncrier). + +When making a user-visible change create a file in this directory and it will be automatically be +included into the release note document when the next release is published. + +The file extension specifies the type of a change. The following are currently supported: + + - .feature: a new feature. + - .bugfix: a bug fix. + - .security: a fix for security issue. + - .doc: a documentation improvement. + - .removal: a deprecation or removal of functionality. diff --git a/doc/org.barrier-foss.org.barrierc.plist b/doc/org.barrier-foss.org.barrierc.plist index 31e10ba..90345c8 100644 --- a/doc/org.barrier-foss.org.barrierc.plist +++ b/doc/org.barrier-foss.org.barrierc.plist @@ -4,17 +4,17 @@ - Label - org.debauchee.com.barrierc.plist - OnDemand - - ProgramArguments - + Label + org.debauchee.com.barrierc.plist + OnDemand + + ProgramArguments + /usr/bin/barrierc 192.168.0.2 - - RunAtLoad - + + RunAtLoad + diff --git a/doc/org.barrier-foss.org.barriers.plist b/doc/org.barrier-foss.org.barriers.plist index f1ab5bf..fed7b47 100644 --- a/doc/org.barrier-foss.org.barriers.plist +++ b/doc/org.barrier-foss.org.barriers.plist @@ -4,12 +4,12 @@ - Label - org.debauchee.com.barriers.plist - OnDemand - - ProgramArguments - + Label + org.debauchee.com.barriers.plist + OnDemand + + ProgramArguments + /usr/bin/barriers --no-daemon --config diff --git a/doc/release_notes/index.md b/doc/release_notes/index.md new file mode 100644 index 0000000..72d3a43 --- /dev/null +++ b/doc/release_notes/index.md @@ -0,0 +1,94 @@ +Release notes +============= + +[comment]: <> (towncrier release notes start) + +Barrier `2.4.0` ( `2021-11-01` ) +================================ + +Security fixes +-------------- + +- Barrier now supports client identity verification (fixes CVE-2021-42072, CVE-2021-42073). + + Previously a malicious client could connect to Barrier server without any authentication and + send application-level messages. This made the attack surface of Barrier significantly larger. + Additionally, in case the malicious client got possession of a valid screen name by brute forcing + or other means it could modify the clipboard contents of the server. + + To support seamless upgrades from older versions of Barrier this is currently disabled by default. + The feature can be enabled in the settings dialog. If enabled, older clients of Barrier will be + rejected. + +- Barrier now uses SHA256 fingerprints for establishing security of encrypted SSL connections. + After upgrading client to new version the existing server fingerprint will need to be approved + again. Client and server will show both SHA1 and SHA256 server fingerprints to allow + interoperability with older versions of Barrier. + +Bug fixes +--------- + +- Fixed build failure on mips*el and riscv64 architecture. +- Fixed reading of configuration on Windows when the paths contain non-ASCII characters +(https://github.com/debauchee/barrier/issues/976, https://github.com/debauchee/barrier/issues/974, + https://github.com/debauchee/barrier/issues/444). +- Barrier no longer uses openssl CLI tool for any operations and hooks into the openssl library directly. +- More X11 clipboard MIME types have been mapped to corresponding converters (https://github.com/debauchee/barrier/issues/344). +- Fixed setup of multiple actions associated with a hotkey. +- Fixed setup of hotkeys with special characters such as comma and semicolon + (https://github.com/debauchee/barrier/issues/778). +- Fixed transfer of non-ASCII characters coming from a Windows server in certain cases + (https://github.com/debauchee/barrier/issues/527). +- Barrier will now regenerate server certificate if it's invalid instead of failing to launch + (https://github.com/debauchee/barrier/issues/802) +- Added support for additional keys on Sun Microsystems USB keyboards + (https://github.com/debauchee/barrier/issues/784). +- Updated Chinese translation. +- Updated Slovak translation. +- Theme icons are now preferred to icons distributed together with Barrier + (https://github.com/debauchee/barrier/issues/471). +- Fixed incorrect setup of Barrier service path on Windows. + +Features +-------- + +- Added `--drop-target` option that improves drag and drop support on Windows when Barrier is + being run as a portable app. +- The `--enable-crypto` command line option has been made the default to reduce chances of + accidental security mishaps when configuring Barrier from command line. + A new `--disable-crypto` command line option has been added to explicitly disable encryption. +- Added support for randomart images for easier comparison of SSL certificate fingerprints. + The algorithm is identical to what OpenSSH uses. +- Implemented a configuration option for Server GUI auto-start. +- Made it possible to use keyboard instead of mouse to modify screen layout. +- Added support for keyboard backlight media keys +- Added support for Eisu_toggle and Muhenkan keys +- Added `--profile-dir` option that allows to select custom profile directory. + +Barrier `2.3.4` ( `2021-11-01` ) +================================ + +Security fixes +-------------- + +- Barrier will now correctly close connections when the app-level handshake fails (fixes CVE-2021-42075). + + Previously repeated failing connections would leak file descriptors leading to Barrier being unable + to receive new connections from clients. + +- Barrier will now enforce a maximum length of input messages (fixes CVE-2021-42076). + + Previously it was possible for a malicious client or server to send excessive length messages + leading to denial of service by resource exhaustion. + +- Fixed a bug which caused Barrier to crash when disconnecting a TCP session just after sending Hello message. + This bug allowed an unauthenticated attacker to crash Barrier with only network access. + +All of the above security issues have been reported by Matthias Gerstner who was really helpful +resolving them. + +Bug fixes +--------- + +- Fixed a bug in SSL implementation that caused invalid data occasionally being sent to clients + under heavy load. diff --git a/doc/release_notes/index.template.jinja b/doc/release_notes/index.template.jinja new file mode 100644 index 0000000..418a1d3 --- /dev/null +++ b/doc/release_notes/index.template.jinja @@ -0,0 +1,37 @@ +{% for section, _ in sections|dictsort(by='key') %} +{% set underline = "-" %} +{% if section %} +{{section}} +{{ underline * section|length }}{% set underline = "-" %} + +{% endif %} +{% if sections[section] %} +{% for category, val in definitions|dictsort if category in sections[section]%} + +{{ definitions[category]['name'] }} +{{ underline * definitions[category]['name']|length }} + +{% if definitions[category]['showcontent'] %} +{% for text, values in sections[section][category]|dictsort(by='value') %} +- {{ text }} +{% endfor %} +{% else %} +- {{ sections[section][category]['']|sort|join(', ') }} + + +{% endif %} +{% if sections[section][category]|length == 0 %} + +No significant changes. + + +{% else %} +{% endif %} +{% endfor %} +{% else %} + +No significant changes. + + +{% endif %} +{% endfor %} diff --git a/gulrak-filesystem/.appveyor.yml b/gulrak-filesystem/.appveyor.yml new file mode 100644 index 0000000..cf986f7 --- /dev/null +++ b/gulrak-filesystem/.appveyor.yml @@ -0,0 +1,85 @@ +environment: + matrix: + - platform: x86 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + generator: "Visual Studio 14 2015" + compiler: msvc + configuration: Release + + - platform: x64 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + generator: "Visual Studio 14 2015 Win64" + compiler: msvc + configuration: Release + + - platform: x86 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + generator: "Visual Studio 15 2017" + compiler: msvc + configuration: Release + + - platform: x64 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + generator: "Visual Studio 15 2017 Win64" + compiler: msvc + configuration: Release + + - platform: x86 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + generator: "Visual Studio 16 2019" + compiler: msvc19 + configuration: Release + arch: Win32 + + - platform: x64 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + generator: "Visual Studio 16 2019" + compiler: msvc19 + configuration: Release + arch: x64 + + - platform: x86 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + generator: "MinGW Makefiles" + compiler: mingw + TOOLCHAIN_PATH: C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1/mingw32\bin + CC: C:/mingw-w64/i686-6.3.0-posix-dwarf-rt_v5-rev1/mingw32/bin/gcc.exe + CXX: C:/mingw-w64/i686-6.3.0-posix-dwarf-rt_v5-rev1/mingw32/bin/g++.exe + configuration: Release + + - platform: x64 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + generator: "MinGW Makefiles" + compiler: mingw + TOOLCHAIN_PATH: C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin + CC: C:/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/mingw64/bin/gcc.exe + CXX: C:/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/mingw64/bin/g++.exe + configuration: Release + +matrix: + fast_finish: false + +init: + - cmd: cmake --version + - cmd: msbuild /version + +install: + - cmd: reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1" + - cmd: ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe + +build_script: + - mkdir build + - cd build + - if [%compiler%]==[msvc] cmake -G"%generator%" .. + - if [%compiler%]==[msvc19] cmake -G"%generator%" -A "%arch%" .. + - if [%compiler%]==[mingw] set PATH=%TOOLCHAIN_PATH%;%PATH% + - if [%compiler%]==[mingw] cmake -G"%generator%" -DCMAKE_C_COMPILER=%CC% -DCMAKE_CXX_COMPILER=%CXX% -DCMAKE_SH=CMAKE_SH-NOTFOUND -DCMAKE_BUILD_TYPE=%configuration% .. + - cmake --build . --config %configuration% + +test_script: + - cd %APPVEYOR_BUILD_FOLDER%\build + - set CTEST_OUTPUT_ON_FAILURE=1 + - ctest -C %configuration% + - if exist "test\Release\std_filesystem_test.exe" test\Release\std_filesystem_test.exe & exit 0 + - cd .. + diff --git a/gulrak-filesystem/.ci/unix-build.sh b/gulrak-filesystem/.ci/unix-build.sh new file mode 100755 index 0000000..914befa --- /dev/null +++ b/gulrak-filesystem/.ci/unix-build.sh @@ -0,0 +1,4 @@ +#!/bin/sh +mkdir build && cd build +cmake -DCMAKE_BUILD_TYPE=Release .. +cmake --build . diff --git a/gulrak-filesystem/.ci/unix-test.sh b/gulrak-filesystem/.ci/unix-test.sh new file mode 100755 index 0000000..23b511f --- /dev/null +++ b/gulrak-filesystem/.ci/unix-test.sh @@ -0,0 +1,7 @@ +#!/bin/sh +cd build +echo "Tests run as user: $USER" +ctest -E Windows +if [ -f "test/std_filesystem_test" ]; then + test/std_filesystem_test || true +fi diff --git a/gulrak-filesystem/.cirrus.yml b/gulrak-filesystem/.cirrus.yml new file mode 100644 index 0000000..9766476 --- /dev/null +++ b/gulrak-filesystem/.cirrus.yml @@ -0,0 +1,35 @@ +freebsd_task: + freebsd_instance: + image_family: freebsd-12-1 + install_script: | + pkg install -y cmake + pw groupadd testgrp + pw useradd testuser -g testgrp -w none -m + chown -R testuser:testgrp . + build_script: | + sudo -u testuser .ci/unix-build.sh + test_script: | + sudo -u testuser .ci/unix-test.sh + +centos7_task: + container: + image: centos:7 + install_script: | + yum install -y centos-release-scl + yum install -y devtoolset-9 + curl -L https://github.com/Kitware/CMake/releases/download/v3.16.4/cmake-3.16.4-Linux-x86_64.tar.gz | tar xzvf - -C /usr/local --strip-components 1 + build_script: | + source /opt/rh/devtoolset-9/enable && PATH=$PATH:/usr/local/bin .ci/unix-build.sh + test_script: | + PATH=$PATH:/usr/local/bin .ci/unix-test.sh + +centos8_task: + container: + image: centos:8 + install_script: | + yum group install -y "Development Tools" + curl -L https://github.com/Kitware/CMake/releases/download/v3.16.4/cmake-3.16.4-Linux-x86_64.tar.gz | tar xzvf - -C /usr/local --strip-components 1 + build_script: | + PATH=$PATH:/usr/local/bin .ci/unix-build.sh + test_script: | + PATH=$PATH:/usr/local/bin .ci/unix-test.sh \ No newline at end of file diff --git a/gulrak-filesystem/.clang-format b/gulrak-filesystem/.clang-format new file mode 100644 index 0000000..b24fe34 --- /dev/null +++ b/gulrak-filesystem/.clang-format @@ -0,0 +1,25 @@ +--- +Language: Cpp +BasedOnStyle: Chromium +AccessModifierOffset: '-4' +IndentWidth: '4' +ColumnLimit: 256 +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakConstructorInitializers: BeforeComma +ConstructorInitializerAllOnOneLineOrOnePerLine: false +IndentPPDirectives: None +... diff --git a/gulrak-filesystem/.clang-tidy b/gulrak-filesystem/.clang-tidy new file mode 100644 index 0000000..203d506 --- /dev/null +++ b/gulrak-filesystem/.clang-tidy @@ -0,0 +1,3 @@ +--- +Checks: -modernize-use-nodiscard +... diff --git a/gulrak-filesystem/.drone.yml b/gulrak-filesystem/.drone.yml new file mode 100644 index 0000000..da967a4 --- /dev/null +++ b/gulrak-filesystem/.drone.yml @@ -0,0 +1,43 @@ +kind: pipeline +name: arm + +platform: + os: linux + arch: arm + +steps: +- name: build + image: alpine + failure: ignore + commands: + - apk update + - apk add --no-cache build-base cmake sudo + - addgroup testgrp + - adduser --disabled-password testuser testgrp + - passwd testuser -u -d + - chown -R testuser:testgrp . + - sudo -u testuser .ci/unix-build.sh + - sudo -u testuser .ci/unix-test.sh + +--- + +kind: pipeline +name: arm64 + +platform: + os: linux + arch: arm64 + +steps: +- name: build + image: alpine + failure: ignore + commands: + - apk update + - apk add --no-cache build-base cmake + - addgroup testgrp + - adduser --disabled-password testuser testgrp + - passwd testuser -u -d + - chown -R testuser:testgrp . + - su -c "./.ci/unix-build.sh" testuser + - su -c "./.ci/unix-test.sh" testuser diff --git a/gulrak-filesystem/.github/ISSUE_TEMPLATE/bug_report.md b/gulrak-filesystem/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..fc25aa4 --- /dev/null +++ b/gulrak-filesystem/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,17 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Detailed steps to reproduce the behavior. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Additional context** +Add any other context about the problem here. diff --git a/gulrak-filesystem/.github/ISSUE_TEMPLATE/feature_request.md b/gulrak-filesystem/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..066b2d9 --- /dev/null +++ b/gulrak-filesystem/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/gulrak-filesystem/.github/workflows/build_cmake.yml b/gulrak-filesystem/.github/workflows/build_cmake.yml new file mode 100644 index 0000000..783508b --- /dev/null +++ b/gulrak-filesystem/.github/workflows/build_cmake.yml @@ -0,0 +1,197 @@ +name: CMake Build Matrix + +on: [ push, pull_request ] + +jobs: + build: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + config: + - name: "Ubuntu 20.04 GCC 9.3" + os: ubuntu-20.04 + build_type: Release + packages: ninja-build + generator: Ninja + compatibility: "cxx_std_11;cxx_std_17;cxx_std_20" + cc: gcc + cxx: g++ + + - name: "Ubuntu 20.04 Clang 10.0" + os: ubuntu-20.04 + build_type: Release + packages: ninja-build + generator: Ninja + compatibility: "cxx_std_11;cxx_std_17;cxx_std_20" + cc: clang-10 + cxx: clang++-10 + + - name: "Ubuntu 20.04 Clang 11.0" + os: ubuntu-20.04 + build_type: Release + packages: ninja-build clang-11 libc++-11-dev libc++abi-11-dev + generator: Ninja + compatibility: "cxx_std_11;cxx_std_17;cxx_std_20" + cc: clang-11 + cxx: clang++-11 + + - name: "Ubuntu 20.04 GCC 9.3 coverage" + os: ubuntu-20.04 + build_type: Debug + packages: ninja-build lcov + generator: Ninja + compatibility: "cxx_std_11;cxx_std_17;cxx_std_20" + cc: gcc + cxx: g++ + + - name: "Ubuntu 18.04 GCC 8.4" + os: ubuntu-18.04 + build_type: Release + packages: ninja-build gcc-8 g++-8 + generator: Ninja + compatibility: "cxx_std_11;cxx_std_17" + cc: gcc-8 + cxx: g++-8 + + - name: "Ubuntu 18.04 GCC 7.5" + os: ubuntu-18.04 + build_type: Release + packages: ninja-build + generator: Ninja + compatibility: "cxx_std_11;cxx_std_17" + cc: gcc-7 + cxx: g++-7 + + - name: "Ubuntu 18.04 GCC 6.5" + os: ubuntu-18.04 + build_type: Release + packages: ninja-build gcc-6 g++-6 + generator: Ninja + compatibility: "cxx_std_11;cxx_std_17" + cc: gcc-6 + cxx: g++-6 + + - name: "Ubuntu 18.04 GCC 5.5" + os: ubuntu-18.04 + build_type: Release + packages: ninja-build gcc-5 g++-5 + generator: Ninja + compatibility: "cxx_std_11;cxx_std_17" + cc: gcc-5 + cxx: g++-5 + + - name: "Ubuntu 18.04 Clang 9.0" + os: ubuntu-18.04 + build_type: Release + packages: ninja-build libc++-9-dev libc++abi-9-dev + generator: Ninja + compatibility: "cxx_std_11;cxx_std_17;cxx_std_20" + cc: clang + cxx: clang++ + + - name: "Ubuntu 18.04 Clang 6.0" + os: ubuntu-18.04 + build_type: Release + packages: ninja-build clang-6.0 + generator: Ninja + compatibility: "cxx_std_11;cxx_std_17" + cc: clang-6.0 + cxx: clang++-6.0 + + - name: "Ubuntu 18.04 Clang 5.0" + os: ubuntu-18.04 + build_type: Release + packages: ninja-build clang-5.0 + generator: Ninja + compatibility: "cxx_std_11;cxx_std_17" + cc: clang-5.0 + cxx: clang++-5.0 + + - name: "Windows MSVC 2019" + os: windows-latest + build_type: Release + packages: ninja + generator: "Visual Studio 16 2019" + compatibility: "cxx_std_11;cxx_std_17;cxx_std_20" + cc: cl + cxx: cl + + - name: "macOS 10.15 AppleClang" + os: macos-10.15 + build_type: Release + packages: ninja + generator: Ninja + compatibility: "cxx_std_11;cxx_std_17" + cc: clang + cxx: clang++ + + steps: + - uses: actions/checkout@v2 + + - name: print environment + run: | + echo github.event.action: ${{ github.event.action }} + echo github.event_name: ${{ github.event_name }} + + - name: Install dependencies on Ubuntu + if: startsWith(matrix.config.os, 'ubuntu') + shell: bash + run: | + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt update + sudo apt install ${{ matrix.config.packages }} + + - name: Install dependencies on windows + if: startsWith(matrix.config.os, 'windows') + run: | + choco install ${{ matrix.config.packages }} + + - name: Install dependencies on macOS + if: startsWith(matrix.config.os, 'macos') + run: | + brew install ${{ matrix.config.packages }} + + - name: Configure project + shell: bash + run: | + export CC=${{ matrix.config.cc }} + export CXX=${{ matrix.config.cxx }} + ninja --version + cmake --version + mkdir build + mkdir install + if [[ "${{ matrix.config.build_type }}" == "Debug" ]]; then + cmake -G "${{ matrix.config.generator }}" -S . -B build -DCMAKE_BUILD_TYPE=Debug -DGHC_COVERAGE=ON -DGHC_FILESYSTEM_TEST_COMPILE_FEATURES="${{ matrix.config.compatibility }}" -DCMAKE_INSTALL_PREFIX:PATH=install + else + cmake -G "${{ matrix.config.generator }}" -S . -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} -DGHC_FILESYSTEM_TEST_COMPILE_FEATURES="${{ matrix.config.compatibility }}" -DCMAKE_INSTALL_PREFIX:PATH=install + fi + + - name: Build project + shell: bash + run: | + cmake --build build --config ${{ matrix.config.build_type }} + + - name: Run tests + run: | + cd build && ctest -C ${{ matrix.config.build_type }} + + - name: Collect coverage info + if: startsWith(matrix.config.build_type, 'Debug') + run: | + cd build + lcov --compat-libtool --directory . --capture --output-file coverage_output.info + lcov --remove coverage_output.info '/usr/*' '*/c++/*' '*.h' '*/catch.hpp' -o coverage.info + # sed -i 's|SF:/.*/filesystem/|SF:../|g' coverage.info + + - name: Upload coverage info + if: startsWith(matrix.config.build_type, 'Debug') + env: + COVERALLS_DEBUG: true + NODE_COVERALLS_DEBUG: 1 + uses: coverallsapp/github-action@master + with: + path-to-lcov: ${{ github.workspace }}/build/coverage.info + github-token: ${{ secrets.GITHUB_TOKEN }} + diff --git a/gulrak-filesystem/.gitignore b/gulrak-filesystem/.gitignore new file mode 100644 index 0000000..ac7a37a --- /dev/null +++ b/gulrak-filesystem/.gitignore @@ -0,0 +1,7 @@ +/*build*/ +.vs/ +.vscode/ +.idea/ +*.swp +*~ +.DS_Store diff --git a/gulrak-filesystem/CMakeLists.txt b/gulrak-filesystem/CMakeLists.txt new file mode 100644 index 0000000..f6b7f54 --- /dev/null +++ b/gulrak-filesystem/CMakeLists.txt @@ -0,0 +1,74 @@ +cmake_minimum_required(VERSION 3.7.2) +project(ghcfilesystem) + +if (POLICY CMP0077) + cmake_policy(SET CMP0077 NEW) +endif() +if(POLICY CMP0110) + cmake_policy(SET CMP0110 NEW) +endif() + +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + option(GHC_FILESYSTEM_BUILD_TESTING "Enable tests" ON) + option(GHC_FILESYSTEM_BUILD_EXAMPLES "Build examples" ON) + option(GHC_FILESYSTEM_WITH_INSTALL "With install target" ON) +else() + option(GHC_FILESYSTEM_BUILD_EXAMPLES "Build examples" OFF) + option(GHC_FILESYSTEM_BUILD_TESTING "Enable tests" OFF) + option(GHC_FILESYSTEM_WITH_INSTALL "With install target" ON) +endif() +option(GHC_FILESYSTEM_BUILD_STD_TESTING "Enable STD tests" ${GHC_FILESYSTEM_BUILD_TESTING}) +if(NOT DEFINED GHC_FILESYSTEM_TEST_COMPILE_FEATURES) + set(GHC_FILESYSTEM_TEST_COMPILE_FEATURES ${CMAKE_CXX_COMPILE_FEATURES}) +endif() + +if(NOT DEFINED CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + if(NOT CYGWIN) + set(CMAKE_CXX_EXTENSIONS OFF) + endif() +endif() +if(CMAKE_CXX_STANDARD LESS 11) + message(FATAL_ERROR "CMAKE_CXX_STANDARD is less than 11, ghc::filesystem only works with C++11 and above.") +endif() +message(STATUS "CMAKE_CXX_COMPILE_FEATURES: ${CMAKE_CXX_COMPILE_FEATURES}") + +add_library(ghc_filesystem INTERFACE) +target_include_directories(ghc_filesystem INTERFACE + $ + $) +target_compile_options(ghc_filesystem INTERFACE "$<$:/utf-8>") +target_compile_options(ghc_filesystem INTERFACE "$<$:/utf-8>") + +if(GHC_FILESYSTEM_BUILD_TESTING OR GHC_FILESYSTEM_BUILD_EXAMPLES) + set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") + include(GhcHelper) + + if(GHC_FILESYSTEM_BUILD_TESTING) + enable_testing() + add_subdirectory(test) + endif() + + if(GHC_FILESYSTEM_BUILD_EXAMPLES) + add_subdirectory(examples) + endif() +endif() + +if(GHC_FILESYSTEM_WITH_INSTALL) + include(CMakePackageConfigHelpers) + include(GNUInstallDirs) + install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + install(TARGETS ghc_filesystem EXPORT ghc_filesystem-targets) + install(EXPORT ghc_filesystem-targets NAMESPACE ghcFilesystem:: DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ghc_filesystem") + export(EXPORT ghc_filesystem-targets NAMESPACE ghcFilesystem:: FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/ghc_filesystem-targets.cmake") + configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.cmake.in" + "${PROJECT_BINARY_DIR}/cmake/ghc_filesystem-config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ghc_filesystem" + PATH_VARS CMAKE_INSTALL_INCLUDEDIR) + install(FILES "${PROJECT_BINARY_DIR}/cmake/ghc_filesystem-config.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ghc_filesystem") + add_library(ghcFilesystem::ghc_filesystem ALIAS ghc_filesystem) +endif() diff --git a/gulrak-filesystem/LICENSE b/gulrak-filesystem/LICENSE new file mode 100644 index 0000000..8b24faa --- /dev/null +++ b/gulrak-filesystem/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018, Steffen Schümann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/gulrak-filesystem/README.md b/gulrak-filesystem/README.md new file mode 100644 index 0000000..0e8901a --- /dev/null +++ b/gulrak-filesystem/README.md @@ -0,0 +1,1062 @@ +![Supported Platforms](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows%20%7C%20FreeBSD-blue.svg) +![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg) +[![CMake Build Matrix](https://github.com/gulrak/filesystem/actions/workflows/build_cmake.yml/badge.svg?branch=master)](https://github.com/gulrak/filesystem/actions/workflows/build_cmake.yml) +[![Build Status](https://ci.appveyor.com/api/projects/status/t07wp3k2cddo0hpo/branch/master?svg=true)](https://ci.appveyor.com/project/gulrak/filesystem) +[![Build Status](https://api.cirrus-ci.com/github/gulrak/filesystem.svg?branch=master)](https://cirrus-ci.com/github/gulrak/filesystem) +[![Build Status](https://cloud.drone.io/api/badges/gulrak/filesystem/status.svg?ref=refs/heads/master)](https://cloud.drone.io/gulrak/filesystem) +[![Coverage Status](https://coveralls.io/repos/github/gulrak/filesystem/badge.svg?branch=master)](https://coveralls.io/github/gulrak/filesystem?branch=master) +[![Latest Release Tag](https://img.shields.io/github/tag/gulrak/filesystem.svg)](https://github.com/gulrak/filesystem/tree/v1.5.10) + +- [Filesystem](#filesystem) + - [Motivation](#motivation) + - [Why the namespace GHC?](#why-the-namespace-ghc) + - [Platforms](#platforms) + - [Tests](#tests) + - [Usage](#usage) + - [Downloads](#downloads) + - [Using it as Single-File-Header](#using-it-as-single-file-header) + - [Using it as Forwarding-/Implementation-Header](#using-it-as-forwarding-implementation-header) + - [Git Submodule and CMake](#git-submodule-and-cmake) + - [Versioning](#versioning) + - [Documentation](#documentation) + - [`ghc::filesystem::ifstream`, `ghc::filesystem::ofstream`, `ghc::filesystem::fstream`](#ghcfilesystemifstream-ghcfilesystemofstream-ghcfilesystemfstream) + - [`ghc::filesystem::u8arguments`](#ghcfilesystemu8arguments) + - [Differences](#differences) + - [LWG Defects](#lwg-defects) + - [Not Implemented on C++ before C++17](#not-implemented-on-c-before-c17) + - [Differences in API](#differences-in-api) + - [Differences of Specific Interfaces](#differences-of-specific-interfaces) + - [Differences in Behavior](#differences-in-behavior) + - [fs.path](#fspath-refhttpsencppreferencecomwcppfilesystempath) + - [Open Issues](#open-issues) + - [Windows](#windows) + - [Symbolic Links on Windows](#symbolic-links-on-windows) + - [Permissions](#permissions) + - [Release Notes](#release-notes) + +# Filesystem + +This is a header-only single-file `std::filesystem` compatible helper library, +based on the C++17 and C++20 specs, but implemented for C++11, C++14, C++17 or C++20 +(tightly following the C++17 standard with very few documented exceptions). It is currently tested on +macOS 10.12/10.14/10.15, Windows 10, Ubuntu 18.04, Ubuntu 20.04, CentOS 7, CentOS 8, FreeBSD 12 +and Alpine ARM/ARM64 Linux but should work on other systems too, as long as you have +at least a C++11 compatible compiler. It should work with Android NDK, Emscripten and I even +had reports of it being used on iOS (within sandboxing constraints) and with v1.5.6 there +is experimental support for QNX. The support of Android NDK, Emscripten and QNX is not +backed up by automated testing but PRs and bug reports are welcome for those too. +It is of course in its own namespace `ghc::filesystem` to not interfere with a regular `std::filesystem` +should you use it in a mixed C++17 environment (which is possible). + +*Test coverage is well above 90%, and starting with v1.3.6 and in v1.5.0 +more time was invested in benchmarking and optimizing parts of the library. I'll try +to continue to optimize some parts and refactor others, striving +to improve it as long as it doesn't introduce additional C++17/C++20 compatibility +issues. Feedback is always welcome. Simply open an issue if you see something missing +or wrong or not behaving as expected and I'll comment.* + + +## Motivation + +I'm often in need of filesystem functionality, mostly `fs::path`, but directory +access too, and when beginning to use C++11, I used that language update +to try to reduce my third-party dependencies. I could drop most of what +I used, but still missed some stuff that I started implementing for the +fun of it. Originally I based these helpers on my own coding- and naming +conventions. When C++17 was finalized, I wanted to use that interface, +but it took a while, to push myself to convert my classes. + +The implementation is closely based on chapter 30.10 from the C++17 standard +and a draft close to that version is +[Working Draft N4687](https://github.com/cplusplus/draft/raw/master/papers/n4687.pdf). +It is from after the standardization of C++17 but it contains the latest filesystem +interface changes compared to the +[Working Draft N4659](https://github.com/cplusplus/draft/raw/master/papers/n4659.pdf). +Staring with v1.4.0, when compiled using C++20, it adapts to the changes according to path sorting order +and `std::u8string` handling from [Working Draft N4860](https://isocpp.org/files/papers/N4860.pdf). + +I want to thank the people working on improving C++, I really liked how the language +evolved with C++11 and the following standards. Keep on the good work! + +## Why the namespace GHC? +If you ask yourself, what `ghc` is standing for, it is simply +`gulraks helper classes`, yeah, I know, not very imaginative, but I wanted a +short namespace and I use it in some of my private classes (so **it has nothing +to do with Haskell**, sorry for the name clash). + +## Platforms + +`ghc::filesystem` is developed on macOS but CI tested on macOS, Windows, +various Linux Distributions and FreeBSD. It should work on any of these with a C++11-capable +compiler. Also there are some checks to hopefully better work on Android, but +as I currently don't test with the Android NDK, I wouldn't call it a +supported platform yet, same is valid for using it with Emscripten. It is now +part of the detected platforms, I fixed the obvious issues and ran some tests with +it, so it should be fine. All in all, I don't see it replacing `std::filesystem` +where full C++17 or C++20 is available, it doesn't try to be a "better" +`std::filesystem`, just an almost drop-in if you can't use it (with the exception +of the UTF-8 preference). + +:information_source: **Important:** _This implementation is following the ["UTF-8 Everywhere" philosophy](https://utf8everywhere.org/) in that all +`std::string` instances will be interpreted the same as `std::u8string` encoding +wise and as being in UTF-8. The `std::u16string` will be seen as UTF-16. See *Differences in API* +for more information._ + +Unit tests are currently run with: + +* macOS 10.12: Xcode 9.2 (clang-900.0.39.2), GCC 9.2, Clang 9.0, macOS 10.13: Xcode 10.1, macOS 10.14: Xcode 11.2, macOS 10.15: Xcode 11.6, Xcode 12.4 +* Windows: Visual Studio 2017, Visual Studio 2015, Visual Studio 2019, MinGW GCC 6.3 (Win32), GCC 7.2 (Win64), Cygwin GCC 10.2 (no CI yet) +* Linux (Ubuntu): GCC (5.5, 6.5, 7.4, 8.3, 9.2), Clang (5.0, 6.0, 7.1, 8.0, 9.0) +* Linux (Alpine ARM/ARM64): GCC 9.2.0 +* FreeBSD: Clang 8.0 + + +## Tests + +The header comes with a set of unit-tests and uses [CMake](https://cmake.org/) +as a build tool and [Catch2](https://github.com/catchorg/Catch2) as test framework. +All tests are registered with in CMake, so the [ctest](https://cmake.org/cmake/help/latest/manual/ctest.1.html) +commando can be used to run the tests. + +All tests against this implementation should succeed, depending on your environment +it might be that there are some warnings, e.g. if you have no rights to create +Symlinks on Windows or at least the test thinks so, but these are just informative. + +To build the tests from inside the project directory under macOS or Linux just: + +```cpp +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Debug .. +make +ctest +``` + +This generates the test binaries that run the tests and the last command executes +them. + +If the default compiler is a GCC 8 or newer, or Clang 7 or newer, it +additionally tries to build a version of the test binary compiled against GCCs/Clangs +`std::filesystem` implementation, named `std_filesystem_test` +as an additional test of conformance. Ideally all tests should compile and +succeed with all filesystem implementations, but in reality, there are +some differences in behavior, sometimes due to room for interpretation in +in the standard, and there might be issues in these implementations too. + + +## Usage + +### Downloads + +The latest release version is [v1.5.10](https://github.com/gulrak/filesystem/tree/v1.5.10) and +source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.5.10). + +The latest pre-native-backend version is [v1.4.0](https://github.com/gulrak/filesystem/tree/v1.4.0) and +source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.4.0). + +The latest pre-C++20-support release version is [v1.3.10](https://github.com/gulrak/filesystem/tree/v1.3.10) and +source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.3.10). + +Currently only the latest minor release version receives bugfixes, so if possible, +you should use the latest release. + +### Using it as Single-File-Header + +As `ghc::filesystem` is at first a header-only library, it should be enough to copy the header +or the `include/ghc` directory into your project folder or point your include path to this place and +simply include the `filesystem.hpp` header (or `ghc/filesystem.hpp` if you use the subdirectory). + +Everything is in the namespace `ghc::filesystem`, so one way to use it only as +a fallback could be: + +```cpp +#ifdef __APPLE__ +#include // for deployment target to support pre-catalina targets without std::fs +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include +namespace fs = std::filesystem; +#endif +#endif +#ifndef GHC_USE_STD_FS +#include +namespace fs = ghc::filesystem; +#endif +``` + +**Note that this code uses a two-stage preprocessor condition because Visual Studio 2015 +doesn't like the `(<...>)` syntax, even if it could cut evaluation early before. This code also +used the minimum deployment target to detect if `std::filesystem` really is available on macOS +compilation.** + +**Note also, this detection now works on MSVC versions prior to 15.7 on, or without setting +the `/Zc:__cplusplus` compile switch that would fix `__cplusplus` on MSVC. (Without the switch +the compiler always reports `199711L` +([see](https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/)), +but `_MSVC_LANG` works without it.** + +If you want to also use the `fstream` wrapper with `path` support as fallback, +you might use: + +```cpp +#ifdef __APPLE__ +#include // for deployment target to support pre-catalina targets without std::fs +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include +namespace fs { +using namespace std::filesystem; +using ifstream = std::ifstream; +using ofstream = std::ofstream; +using fstream = std::fstream; +} +#endif +#endif +#ifndef GHC_USE_STD_FS +#include +namespace fs { +using namespace ghc::filesystem; +using ifstream = ghc::filesystem::ifstream; +using ofstream = ghc::filesystem::ofstream; +using fstream = ghc::filesystem::fstream; +} +#endif +``` + +Now you have e.g. `fs::ofstream out(somePath);` and it is either the wrapper or +the C++17 `std::ofstream`. + +:information_source: **Be aware, as a header-only library, it is not hiding the fact, that it +uses system includes, so they "pollute" your global namespace. Use the +forwarding-/implementation-header based approach (see below) to avoid this. +For Windows it needs `Windows.h` and it might be a good idea to define +`WIN32_LEAN_AND_MEAN` or `NOMINMAX` prior to including `filesystem.hpp` or +`fs_std.hpp` headers to reduce pollution of your global namespace and compile +time. They are not defined by `ghc::filesystem` to allow combination with contexts +where the full `Windows.h`is needed, e.g. for UI elements.** + +:information_source: **Hint:** There is an additional header named `ghc/fs_std.hpp` that implements this +dynamic selection of a filesystem implementation, that you can include +instead of `ghc/filesystem.hpp` when you want `std::filesystem` where +available and `ghc::filesystem` where not. + + +### Using it as Forwarding-/Implementation-Header + +Alternatively, starting from v1.1.0 `ghc::filesystem` can also be used by +including one of two additional wrapper headers. These allow to include +a forwarded version in most places (`ghc/fs_fwd.hpp`) while hiding the +implementation details in a single cpp file that includes `ghc/fs_impl.hpp` to +implement the needed code. Using `ghc::filesystem` this way makes sure +system includes are only visible from inside the cpp file, all other places are clean. + +Be aware, that it is currently not supported to hide the implementation +into a Windows-DLL, as a DLL interface with C++ standard templates in interfaces +is a different beast. If someone is willing to give it a try, I might integrate +a PR but currently working on that myself is not a priority. + +If you use the forwarding/implementation approach, you can still use the dynamic +switching like this: + +```cpp +#ifdef __APPLE__ +#include // for deployment target to support pre-catalina targets without std::fs +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include +namespace fs { +using namespace std::filesystem; +using ifstream = std::ifstream; +using ofstream = std::ofstream; +using fstream = std::fstream; +} +#endif +#endif +#ifndef GHC_USE_STD_FS +#include +namespace fs { +using namespace ghc::filesystem; +using ifstream = ghc::filesystem::ifstream; +using ofstream = ghc::filesystem::ofstream; +using fstream = ghc::filesystem::fstream; +} +#endif +``` + +and in the implementation hiding cpp, you might use (before any include that includes `ghc/fs_fwd.hpp` +to take precedence: + +```cpp +#ifdef __APPLE__ // for deployment target to support pre-catalina targets without std::fs +#include +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#endif +#endif +#ifndef GHC_USE_STD_FS +#define GHC_FILESYSTEM_IMPLEMENTATION +#include +#endif +``` + +:information_source: **Hint:** There are additional helper headers, named `ghc/fs_std_fwd.hpp` and +`ghc/fs_std_impl.hpp` that use this technique, so you can simply include them +if you want to dynamically select the filesystem implementation. they also +enable the `wchar_t` support on `ghc::filesystem` on Windows, so the resulting +implementation in the `fs` namespace will be compatible. + + + +### Git Submodule and CMake + +Starting from v1.1.0, it is possible to add `ghc::filesystem` +as a git submodule, add the directory to your `CMakeLists.txt` with +`add_subdirectory()` and then simply use `target_link_libraries(your-target ghc_filesystem)` +to ensure correct include path that allow `#include ` +to work. + +The `CMakeLists.txt` offers a few options to customize its behavior: + +* `GHC_FILESYSTEM_BUILD_TESTING` - Compile tests, default is `OFF` when used as + a submodule, else `ON`. +* `GHC_FILESYSTEM_BUILD_EXAMPLES` - Compile the examples, default is `OFF` when used as + a submodule, else `ON`. +* `GHC_FILESYSTEM_WITH_INSTALL` - Add install target to build, default is `OFF` when used as + a submodule, else `ON`. +* `GHC_FILESYSTEM_BUILD_STD_TESTING` - Compile `std_filesystem_test`, the variant of + the test suite running against `std::filesystem`, defaulting to `GHC_FILESYSTEM_BUILD_TESTING`. + This is only done if the compiler is detected as being able to do it. +* `GHC_FILESYSTEM_TEST_COMPILE_FEATURES` can be set to a list of features to override + `CMAKE_CXX_COMPILE_FEATURES` when the detection of C++17 or C++20 for additional tests + is not working (e.g. `cxx_std_20` to enforce building a `filesystem_test_cpp20` with C++20). + +### Versioning + +There is a version macro `GHC_FILESYSTEM_VERSION` defined in case future changes +might make it needed to react on the version, but I don't plan to break anything. +It's the version as decimal number `(major * 10000 + minor * 100 + patch)`. + +:information_source: **Note:** Only even patch versions will be used for releases +and odd patch version will only be used for in between commits while working on +the next version. + + +## Documentation + +There is almost no documentation in this release, as any `std::filesystem` +documentation would work, besides the few differences explained in the next +section. So you might head over to https://en.cppreference.com/w/cpp/filesystem +for a description of the components of this library. + +When compiling with C++11, C++14 or C++17, the API is following the C++17 +standard, where possible, with the exception that `std::string_view` parameters +are only supported on C++17. When Compiling with C++20, `ghc::filesysytem` +defaults to the C++20 API, with the `char8_t` and `std::u8string` interfaces +and the deprecated `fs::u8path` factory method. + +:information_source: **Note:** If the C++17 API should be enforced even in C++20 mode, +use the define `GHC_FILESYSTEM_ENFORCE_CPP17_API`. +Even then it is possible to create `fws::path` from `std::u8string` but +`fs::path::u8string()` and `fs::path::generic_u8string()` return normal +UTF-8 encoded `std::string` instances, so code written for C++17 could +still work with `ghc::filesystem` when compiled with C++20. + +The only additions to the standard are documented here: + + +### `ghc::filesystem::ifstream`, `ghc::filesystem::ofstream`, `ghc::filesystem::fstream` + +These are simple wrappers around `std::ifstream`, `std::ofstream` and `std::fstream`. +They simply add an `open()` method and a constructor with an `ghc::filesystem::path` +argument as the `fstream` variants in C++17 have them. + +### `ghc::filesystem::u8arguments` + +This is a helper class that currently checks for UTF-8 encoding on non-Windows platforms but on Windows it +fetches the command line arguments as Unicode strings from the OS with + +```cpp +::CommandLineToArgvW(::GetCommandLineW(), &argc) +``` + +and then converts them to UTF-8, and replaces `argc` and `argv`. It is a guard-like +class that reverts its changes when going out of scope. + +So basic usage is: + +```cpp +namespace fs = ghc::filesystem; + +int main(int argc, char* argv[]) +{ + fs::u8arguments u8guard(argc, argv); + if(!u8guard.valid()) { + std::cerr << "Bad encoding, needs UTF-8." << std::endl; + exit(EXIT_FAILURE); + } + + // now use argc/argv as usual, they have utf-8 enconding on windows + // ... + + return 0; +} +``` + +That way `argv` is UTF-8 encoded as long as the scope from `main` is valid. + +**Note:** On macOS, while debugging under Xcode the code currently will return +`false` as Xcode starts the application with `US-ASCII` as encoding, no matter what +encoding is actually used and even setting `LC_ALL` in the product scheme doesn't +change anything. I still need to investigate this. + + +## Differences + +As this implementation is based on existing code from my private helper +classes, it derived some constraints of it. Starting from v1.5.0 most of the +differences between this and the standard C++17/C++20 API where removed. + + +### LWG Defects + +This implementation has switchable behavior for the LWG defects +[#2682](https://wg21.cmeerw.net/lwg/issue2682), +[#2935](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2935), +[#2936](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2936) and +[#2937](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2937). +The currently selected behavior (starting from v1.4.0) is following +[#2682](https://wg21.cmeerw.net/lwg/issue2682), [#2936](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2936), +[#2937](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2937) but +not following [#2935](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2935), +as I feel it is a bug to report no error on a `create_directory()` or `create_directories()` +where a regular file of the same name prohibits the creation of a directory and forces +the user of those functions to double-check via `fs::is_directory` if it really worked. +The more intuitive approach to directory creation of treating a file with that name as an +error is also advocated by the newer paper +[WG21 P1164R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1164r0.pdf), the revision +P1161R1 was agreed upon on Kona 2019 meeting [see merge](https://github.com/cplusplus/draft/issues/2703) +and GCC by now switched to following its proposal +([GCC #86910](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86910)). + +### Not Implemented on C++ before C++17 + +```cpp +// methods in ghc::filesystem::path: +path& operator+=(basic_string_view x); +int compare(basic_string_view s) const; +``` + +These are not implemented under C++11 and C++14, as there is no +`std::basic_string_view` available and I did want to keep this +implementation self-contained and not write a full C++17-upgrade for +C++11/14. Starting with v1.1.0 these are supported when compiling +`ghc::filesystem` under C++17 of C++20. + +Starting with v1.5.2 `ghc::filesystem` will try to allow the use of +`std::experimental::basic_string_view` where it detects is availability. +Additionally if you have a `basic_string_view` compatible c++11 +implementation it can be used instead of `std::basic_string_view` +by defining `GHC_HAS_CUSTOM_STRING_VIEW` and importing the +implementation into the `ghc::filesystem` namespace with: + +```cpp +namespace ghc { + namespace filesystem { + using my::basic_string_view; + } +} +``` + +before including the filesystem header. + +### Differences in API + +To not depend on any external third party libraries and still stay portable and +compact, this implementation is following the ["UTF-8 Everywhere" philosophy](https://utf8everywhere.org/) in that all +`std::string` instances will be interpreted the same as `std::u8string` encoding +wise and as being in UTF-8. The `std::u16string` will be seen as UTF-16 and `std::u32string` will be +seen as Unicode codepoints. Depending on the size of `std::wstring` characters, it will handle +`std::wstring` as being UTF-16 (e.g. Windows) or `char32_t` Unicode codepoints +(currently all other platforms). + +#### Differences of Specific Interfaces + +Starting with v1.5.0 `ghc::filesystem` is following the C++17 standard in +using `wchar_t` and `std::wstring` on Windows as the types internally used +for path representation. It is still possible to get the old behavior by defining +`GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE` and get `filesystem::path::string_type` as +`std::string` and `filesystem::path::value_type` as `wchar_t`. + +If you need to call some Windows API, with v1.5.0 and above, simply +use the W-variant of the Windows-API call (e.g. `GetFileAttributesW(p.c_str())`). + +:information_source: **Note:** _When using the old behavior by defining +`GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE`, use the `path::wstring()` member +(e.g. `GetFileAttributesW(p.wstring().c_str())`). This gives you the +Unicode variant independent of the `UNICODE` macro and makes sharing code +between Windows, Linux and macOS easier and works with `std::filesystem` and +`ghc::filesystem`._ + +```cpp +std::string path::u8string() const; +std::string path::generic_u8string() const; +vs. +std::u8string path::u8string() const; +std::u8string path::generic_u8string() const; +``` + +The return type of these two methods is depending on the used C++ standard +and if `GHC_FILESYSTEM_ENFORCE_CPP17_API` is defined. On C++11, C++14 and +C++17 or when `GHC_FILESYSTEM_ENFORCE_CPP17_API` is defined, the return +type is `std::string`, and on C++20 without the define it is `std::u8string`. + +### Differences in Behavior + +I created a wiki entry about quite a lot of [behavioral differences](https://github.com/gulrak/filesystem/wiki/Differences-to-Standard-Filesystem-Implementations) +between different `std::filesystem` implementations that could result in a +mention here, but this readme only tries to address the design choice +differences between `ghc::filesystem` and those. I try to update the wiki page +from time to time. + +Any additional observations are welcome! + +#### fs.path ([ref](https://en.cppreference.com/w/cpp/filesystem/path)) + +Since v1.5.0 the complete inner mechanics of this implementations `fs::path` +where changed to the _native_ format as the internal representation. +Creating any mixed slash `fs::path` object under Windows (e.g. with `"C:\foo/bar"`) +will lead clean path with `"C:\foo\bar"` via `native()` and `"C:/foo/bar"` via +`generic_string()` API. On all platforms redundant additional separators are +removed, even if this is not enforced by the standard and other implementations +mostly not do this. + +Additionally this implementation follows the standards suggestion to handle +posix paths of the form `"//host/path"` and USC path on windows also as having +a root-name (e.g. `"//host"`). The GCC implementation didn't choose to do that +while testing on Ubuntu 18.04 and macOS with GCC 8.1.0 or Clang 7.0.0. This difference +will show as warnings under `std::filesystem`. This leads to a change in the +algorithm described in the standard for `operator/=(path& p)` where any path +`p` with `p.is_absolute()` will degrade to an assignment, while this implementation +has the exception where `*this == *this.root_name()` and `p == preferred_separator` +a normal append will be done, to allow: + +```cpp +fs::path p1 = "//host/foo/bar/file.txt"; +fs::path p2; +for (auto p : p1) p2 /= p; +ASSERT(p1 == p2); +``` + +For all non-host-leading paths the behavior will match the one described by +the standard. + + +## Open Issues + +### Windows + +#### Symbolic Links on Windows + +As symbolic links on Windows, while being supported more or less since +Windows Vista (with some strict security constraints) and fully since some earlier +build of Windows 10, when "Developer Mode" is activated, are at time of writing +(2018) rarely used, still they are supported wiit th this implementation. + +#### Permissions + +The Windows ACL permission feature translates badly to the POSIX permission +bit mask used in the interface of C++17 filesystem. The permissions returned +in the `file_status` are therefore currently synthesized for the `user`-level +and copied to the `group`- and `other`-level. There is still some potential +for more interaction with the Windows permission system, but currently setting +or reading permissions with this implementation will most certainly not lead +to the expected behavior. + + +## Release Notes + +### [v1.5.10](https://github.com/gulrak/filesystem/releases/tag/v1.5.10) + +* Pull request [#136](https://github.com/gulrak/filesystem/pull/136), the Windows + implementation used some unnecessary expensive shared pointer for resource + management and these where replaced by a dedicated code. +* Fix for [#132](https://github.com/gulrak/filesystem/issues/132), pull request + [#135](https://github.com/gulrak/filesystem/pull/135), `fs::remove_all` now + just deletes symbolic links instead of following them. +* Pull request [#133](https://github.com/gulrak/filesystem/pull/133), fix for + `fs::space` where a numerical overflow could happen in a multiplication. +* Replaced _travis-ci.org_ with GitHub Workflow for the configurations: + Ubuntu 20.04: GCC 9.3, Ubuntu 18.04: GCC 7.5, GCC 8.4, macOS 10.15: Xcode 12.4, + Windows 10: Visual Studio 2019 + +### [v1.5.8](https://github.com/gulrak/filesystem/releases/tag/v1.5.8) + +* Fix for [#125]((https://github.com/gulrak/filesystem/issues/124), where + `fs::create_directories` on Windows no longer breaks on long filenames. + +### [v1.5.6](https://github.com/gulrak/filesystem/releases/tag/v1.5.6) + +* Fix for [#124](https://github.com/gulrak/filesystem/issues/124), + `ghc::filesystem` treated mounted folder/volumes erroneously as symlinks, + leading `fs::canonical` to fail on paths containing those. +* Fix for [#122](https://github.com/gulrak/filesystem/issues/122), incrementing + the `recursive_directory_iterator` will not try to enter dead symlinks. +* Fix for [#121](https://github.com/gulrak/filesystem/issues/121), on Windows + backend the `fs::remove` failed when the path pointed to a read-only entry, + see also ([microsoft/STL#1511](https://github.com/microsoft/STL/issues/1511)) + for the corresponding issue in `std::fs` on windows. +* Fix for [#119](https://github.com/gulrak/filesystem/issues/119), added missing + support for char16_t and char32_t and on C++20 char8_t literals. +* Pull request [#118](https://github.com/gulrak/filesystem/pull/118), when + running tests as root, disable tests that would not work. +* Pull request [#117](https://github.com/gulrak/filesystem/pull/117), added + checks to tests to detect the clang/libstdc++ combination. +* Fix for [#116](https://github.com/gulrak/filesystem/issues/116), internal + macro `GHC_NO_DIRENT_D_TYPE` allows os detection to support systems without + the `dirent.d_type` member, experimental first QNX compile support as + initial use case, fixed issue with filesystems returning DT_UNKNOWN + (e.g. reiserfs). +* Pull request [#115](https://github.com/gulrak/filesystem/pull/115), added + `string_view` support when clang with libstdc++ is detected. +* Fix for [#114](https://github.com/gulrak/filesystem/issues/114), for macOS + the pre-Catalina deployment target detection worked only if `` + was included before `` or ``/``. +* Fix for [#113](https://github.com/gulrak/filesystem/issues/113), the use of + standard chapter numbers was misleading since C++17 and C++20 `std::filesystem` + features are supported, and was replaced by the tag-like chapter names that + stay (mostly) consistent over the versions. + +### [v1.5.4](https://github.com/gulrak/filesystem/releases/tag/v1.5.4) + +* Pull request [#112](https://github.com/gulrak/filesystem/pull/112), lots + of cleanup work on the readme, thanks! +* Enhancement for [#111](https://github.com/gulrak/filesystem/issues/111), + further optimization of directory iteration, performance for + `recursive_directory_iterator` over large trees now somewhere between + libc++ and libstdc++. +* Enhancement for [#110](https://github.com/gulrak/filesystem/issues/110), + `ghc::filesystem` now has preliminary support for Cygwin. Changes where + made to allow the tests to compile and run successfully (tested with GCC + 10.2.0), feedback and additional PRs welcome as it is currently not + part of the CI configuration. +* Pull request [#109](https://github.com/gulrak/filesystem/pull/109), various + spelling errors in error messages and comments fixed. +* Pull request [#108](https://github.com/gulrak/filesystem/pull/108), old + style casts removed. +* Fix for [#107](https://github.com/gulrak/filesystem/issues/107), the error + handling for status calls was suppressing errors on symlink targets. +* Pull request [#106](https://github.com/gulrak/filesystem/pull/106), fixed + detection of AppleClang for compile options. +* Pull request [#105](https://github.com/gulrak/filesystem/pull/105), added + option `GHC_FILESYSTEM_BUILD_STD_TESTING` to override additional build of + `std::filesystem` versions of the tests for comparison and the possibility + to use `GHC_FILESYSTEM_TEST_COMPILE_FEATURES` to prefill the used compile + features defaulting to `CMAKE_CXX_COMPILE_FEATURES` when not given. + +### [v1.5.2](https://github.com/gulrak/filesystem/releases/tag/v1.5.2) + +* Enhancement [#104](https://github.com/gulrak/filesystem/issues/104), + on POSIX backend: optimized reuse of status information and reduced + `directory_entry` creation leads to about 20%-25% in tests with + `recursive_directory_iterator` over a larger directory tree. +* Pull request [#103](https://github.com/gulrak/filesystem/pull/103), `wchar_t` + was not in the list of supported char types on non-Windows backends. +* Pull request [#102](https://github.com/gulrak/filesystem/pull/102), improved + `string_view` support makes use of `` or `` + when available, and allows use of custom `basic_string_view` implementation + when defining `GHC_HAS_CUSTOM_STRING_VIEW` and importing the string view + into the `ghc::filesystem` namespace before including filesystem header. +* Pull request [#101](https://github.com/gulrak/filesystem/pull/101), fix for + [#100](https://github.com/gulrak/filesystem/issues/100), append and concat + type of operations on path called redundant conversions. +* Pull request [#98](https://github.com/gulrak/filesystem/pull/98), on older + linux variants (GCC 7/8), the comparison `std::filesystem` tests now link + with `-lrt` to avoid issues. +* Fix for [#97](https://github.com/gulrak/filesystem/issues/97), on BTRFS the + test case for `fs::hard_link_count` failed due to the filesystems behavior, + the test case was adapted to take that into account. +* Pull request [#96](https://github.com/gulrak/filesystem/pull/96), the export + attribute defines `GHC_FS_API` and `GHC_FS_API_CLASS` are now honored when when + set from outside to allow override of behavior. +* Fix for [#95](https://github.com/gulrak/filesystem/issues/95), the syntax for + disabling the deprecated warning in tests in MSVC was wrong. +* Pull request [#93](https://github.com/gulrak/filesystem/pull/93), now the + CMake configuration file is configured and part of the `make install` files. + +### [v1.5.0](https://github.com/gulrak/filesystem/releases/tag/v1.5.0) + +* Fix for [#91](https://github.com/gulrak/filesystem/issues/91), the way + the CMake build options `GHC_FILESYSTEM_BUILD_TESTING`, `GHC_FILESYSTEM_BUILD_EXAMPLES` + and `GHC_FILESYSTEM_WITH_INSTALL` where implemented, prohibited setting them + from a parent project when using this via `add_subdirectory`, this fix + allows to set them again. +* Major refactoring for [#90](https://github.com/gulrak/filesystem/issues/90), + the way, the Windows version of `fs::path` was originally created from the + POSIX based implementation was, by adaption of the incoming and outgoing + strings. This resulted in a mutable cache inside `fs::path`on Windows, that + was inherently not thread-safe, even for `const` methods. + To not add additional patches to a suboptimal solution, this time I reworked + the `path` code to now store _native_ path-representation. This changed a + lot of code, but when combined with `wchar_t` as `value_type` helped to avoid + lots of conversion for calls to Win-API.
+ As interfaces where changed, it had to be released in a new minor version. + The set of refactorings resulted in the following changes: + * `fs::path::native()` and `fs::path::c_str()` can now be `noexcept` as the + standard mandates + * On Windows `wchar_t` is now the default for `fs::path::value_type` and + `std::wstring` is the default for `fs::path::string_type`. + * This allows the implementation to call Win-API without allocating + conversions + * Thread-safety on `const` methods of `fs::path` is no longer an issue + * Some code could be simplified during this refactoring + * Automatic prefixing of long path on Windows can now be disabled with + defining `GHC_WIN_DISABLE_AUTO_PREFIXES`, for all other types of prefixes + or namespaces the behavior follows that of MSVC `std::filesystem::path` + * In case the old `char`/`std::string` based approach for Windows is still + needed, it can be activated with `GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE` +* Enhancement for [#89](https://github.com/gulrak/filesystem/issues/89), `fs::file_status` + now supports `operator==` introduced in `std::filesystem` with C++20. +* Refactoring for [#88](https://github.com/gulrak/filesystem/issues/88), `fs::path::parent_path()` + had a performance issue, as it was still using a loop based approach to recreate + the parent from elements. This created lots of temporaries and was too slow + especially on long paths. + +### [v1.4.0](https://github.com/gulrak/filesystem/releases/tag/v1.4.0) + +* Enhancements for [#71](https://github.com/gulrak/filesystem/issues/71), when compiled with C++20: + * `char8_t` and `std::u8string` are supported where `Source` is the parameter type + * `fs::path::u8string()` and `fs::path::generic_u8string()` now return a `std::u8string` + * The _spaceship operator_ `<=>` is now supported for `fs::path` + * With the define `GHC_FILESYSTEM_ENFORCE_CPP17_API` `ghc::filesystem` will fall back + to the old `fs::path::u8string()` and `fs::path::generic_u8string()` API if preferred +* Bugfix for `fs::proximate(p, ec)` where the internal call to `fs::current_path()` was not + using the `error_code` variant, throwing possible exceptions instead of setting `ec`. +* Enhancement `LWG_2936_BEHAVIOUR` is now on by default. +* Some cleanup work to reduce preprocessor directives for better readability and remove unneeded + template specializations. + +### [v1.3.10](https://github.com/gulrak/filesystem/releases/tag/v1.3.10) + +* Fix for [#81](https://github.com/gulrak/filesystem/issues/81), fixed issues with + handling `Source` parameters that are string views. +* Fix for [#79](https://github.com/gulrak/filesystem/issues/79), the bit operations + for filesystem bitmasks that should be are now `constexpr`. + +### [v1.3.8](https://github.com/gulrak/filesystem/releases/tag/v1.3.8) + +* Refactoring for [#78](https://github.com/gulrak/filesystem/issues/78), the dynamic + switching helper includes are now using `__MAC_OS_X_VERSION_MIN_REQUIRED` to + ensure that `std::filesystem` is only selected on macOS if the deployment target is + at least Catalina. +* Bugfix for [#77](https://github.com/gulrak/filesystem/issues/77), the `directory_iterator` + and the `recursive_directory_iterator` had an issue with the `skip_permission_denied` + option, that leads to the inability to skip SIP protected folders on macOS. +* Enhancement for [#76](https://github.com/gulrak/filesystem/issues/76), `_MSVC_LANG` is + now used when available, additionally to `__cplusplus`, in the helping headers to + allow them to work even when `/Zc:__cplusplus` is not used. +* Bugfix for [#75](https://github.com/gulrak/filesystem/issues/75), NTFS reparse points + to mapped volumes where handled incorrect, leading to `false` on `fs::exists` or + not-found-errors on `fs::status`. Namespaced paths are not filtered anymore. + +### [v1.3.6](https://github.com/gulrak/filesystem/releases/tag/v1.3.6) + +* Pull request [#74](https://github.com/gulrak/filesystem/pull/74), on Windows symlink + evaluation used the wrong reparse struct information and was not handling the case + of relative paths well, thanks for the contribution. +* Refactoring for [#73](https://github.com/gulrak/filesystem/issues/73), enhanced performance + in path handling. the changes lead to much fewer path/string creations or copies, speeding + up large directory iteration or operations on many path instances. +* Bugfix for [#72](https://github.com/gulrak/filesystem/issues/72), the `TestAllocator` in + `filesystem_test.cpp` was completed to fulfill the requirements to build on CentOS 7 with + `devtoolset-9`. CentOS 7 and CentOS 8 are now part of the CI builds. +* Bugfix for [#70](https://github.com/gulrak/filesystem/issues/70), root names are now case + insensitive on Windows. This fix also adds the new behavior switch `LWG_2936_BEHAVIOUR` + that allows to enable post C++17 `fs::path::compare` behavior, where the comparison is as + if it was an element wise path comparison as described in + [LWG 2936](https://cplusplus.github.io/LWG/issue2936) and C++20 `[fs.path.compare]`. + It is default off in v1.3.6 and will be default starting from v1.4.0 as it changes ordering. + +### [v1.3.4](https://github.com/gulrak/filesystem/releases/tag/v1.3.4) + +* Pull request [#69](https://github.com/gulrak/filesystem/pull/69), use `wchar_t` versions of + `std::fstream` from `ghc::filesystem::fstream` wrappers on Windows if using GCC with libc++. +* Bugfix for [#68](https://github.com/gulrak/filesystem/issues/68), better handling of + permission issues for directory iterators when using `fs::directory_options::skip_permission_denied` + and initial support for compilation with emscripten. +* Refactoring for [#66](https://github.com/gulrak/filesystem/issues/63), unneeded shared_ptr guards + where removed and the file handles closed where needed to avoid unnecessary allocations. +* Bugfix for [#63](https://github.com/gulrak/filesystem/issues/63), fixed issues on Windows + with clang++ and C++17. +* Pull request [#62](https://github.com/gulrak/filesystem/pull/62), various fixes for + better Android support, thanks for the PR +* Pull request [#61](https://github.com/gulrak/filesystem/pull/61), `ghc::filesystem` now + supports use in projects with disabled exceptions. API signatures using exceptions for + error handling are not available in this mode, thanks for the PR (this resolves + [#60](https://github.com/gulrak/filesystem/issues/60) and + [#43](https://github.com/gulrak/filesystem/issues/43)) + +### [v1.3.2](https://github.com/gulrak/filesystem/releases/tag/v1.3.2) + +* Bugfix for [#58](https://github.com/gulrak/filesystem/issues/58), on MinGW the + compilation could fail with an error about an undefined `ERROR_FILE_TOO_LARGE` + constant. +* Bugfix for [#56](https://github.com/gulrak/filesystem/issues/58), `fs::lexically_relative` + didn't ignore trailing slash on the base parameter, thanks for PR + [#57](https://github.com/gulrak/filesystem/pull/57). +* Bugfix for [#55](https://github.com/gulrak/filesystem/issues/55), `fs::create_directories` + returned `true` when nothing needed to be created, because the directory already existed. +* Bugfix for [#54](https://github.com/gulrak/filesystem/issues/54), `error_code` + was not reset, if cached result was returned. +* Pull request [#53](https://github.com/gulrak/filesystem/pull/53), fix for wrong + handling of leading whitespace when reading `fs::path` from a stream. +* Pull request [#52](https://github.com/gulrak/filesystem/pull/52), an ARM Linux + target is now part of the CI infrastructure with the service of Drone CI. +* Pull request [#51](https://github.com/gulrak/filesystem/pull/51), FreeBSD is now + part of the CI infrastructure with the service of Cirrus CI. +* Pull request [#50](https://github.com/gulrak/filesystem/pull/50), adaptive cast to + `timespec` fields to avoid warnings. + +### [v1.3.0](https://github.com/gulrak/filesystem/releases/tag/v1.3.0) + +* **Important: `ghc::filesystem` is re-licensed from BSD-3-Clause to MIT license.** (see + [#47](https://github.com/gulrak/filesystem/issues/47)) +* Pull request [#46](https://github.com/gulrak/filesystem/pull/46), suppresses + unused parameter warning on Android. +* Bugfix for [#44](https://github.com/gulrak/filesystem/issues/44), fixes + for warnings from newer Xcode versions. + +### [v1.2.10](https://github.com/gulrak/filesystem/releases/tag/v1.2.10) + +* The Visual Studio 2019 compiler, GCC 9.2 and Clang 9.0 where added to the + CI configuration. +* Bugfix for [#41](https://github.com/gulrak/filesystem/issues/41), `fs::rename` + on Windows didn't replace an existing regular file as required by the standard, + but gave an error. New tests and a fix as provided in the issue was implemented. +* Bugfix for [#39](https://github.com/gulrak/filesystem/issues/39), for the + forwarding use via `fs_fwd.hpp` or `fs_std_fwd.hpp` there was a use of + `DWORD` in the forwarding part leading to an error if `Windows.h` was not + included before the header. The tests were changed to give an error in that + case too and the useage of `DWORD` was removed. +* Bugfix for [#38](https://github.com/gulrak/filesystem/issues/38), casting the + return value of `GetProcAddress` gave a warning with `-Wcast-function-type` + on MSYS2 and MinGW GCC 9 builds. + +### [v1.2.8](https://github.com/gulrak/filesystem/releases/tag/v1.2.8) + +* Pull request [#30](https://github.com/gulrak/filesystem/pull/30), the + `CMakeLists.txt` will automatically exclude building examples and tests when + used as submodule, the configuration options now use a prefixed name to + reduce risk of conflicts. +* Pull request [#24](https://github.com/gulrak/filesystem/pull/24), install + target now creates a `ghcFilesystemConfig.cmake` in + `${CMAKE_INSTALL_LIBDIR}/cmake/ghcFilesystem` for `find_package` that + exports a target as `ghcFilesystem::ghc_filesystem`. +* Pull request [#31](https://github.com/gulrak/filesystem/pull/31), fixes + `error: redundant redeclaration of 'constexpr' static data member` deprecation + warning in C++17 mode. +* Pull request [#32](https://github.com/gulrak/filesystem/pull/32), fixes + old-style-cast warnings. +* Pull request [#34](https://github.com/gulrak/filesystem/pull/34), fixes + [TOCTOU](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use) situation + on `fs::create_directories`, thanks for the PR! +* Feature [#35](https://github.com/gulrak/filesystem/issues/35), new CMake + option to add an install target `GHC_FILESYSTEM_WITH_INSTALL` that is + defaulted to OFF if `ghc::filesystem` is used via `add_subdirectory`. +* Bugfix for [#33](https://github.com/gulrak/filesystem/issues/33), fixes + an issue with `fs::path::lexically_normal()` that leaves a trailing separator + in case of a resulting path ending with `..` as last element. +* Bugfix for [#36](https://github.com/gulrak/filesystem/issues/36), warnings + on Xcode 11.2 due to unhelpful references in path element iteration. + +### [v1.2.6](https://github.com/gulrak/filesystem/releases/tag/v1.2.6) + +* Pull request [#23](https://github.com/gulrak/filesystem/pull/23), tests and + examples can now be disabled in CMake via setting `BUILD_TESTING` and + `BUILD_EXAMPLES` to `NO`, `OFF` or `FALSE`. +* Pull request [#25](https://github.com/gulrak/filesystem/pull/25), + missing specialization for construction from `std::string_view` when + available was added. +* Additional test case when `std::string_view` is available. +* Bugfix for [#27](https://github.com/gulrak/filesystem/issues/27), the + `fs::path::preferred_separator` declaration was not compiling on pre + C++17 compilers and no test accessed it, to show the problem. Fixed + it to an construction C++11 compiler should accept and added a test that + is successful on all combinations tested. +* Bugfix for [#29](https://github.com/gulrak/filesystem/issues/29), stricter + warning settings where chosen and resulting warnings where fixed. + +### [v1.2.4](https://github.com/gulrak/filesystem/releases/tag/v1.2.4) + +* Enabled stronger warning switches and resulting fixed issues on GCC and MinGW +* Bugfix for #22, the `fs::copy_options` where not forwarded from `fs::copy` to + `fs::copy_file` in one of the cases. + +### [v1.2.2](https://github.com/gulrak/filesystem/releases/tag/v1.2.2) + +* Fix for ([#21](https://github.com/gulrak/filesystem/pull/21)), when compiling + on Alpine Linux with musl instead of glibc, the wrong `strerror_r` signature + was expected. The complex preprocessor define mix was dropped in favor of + the usual dispatch by overloading a unifying wrapper. + +### [v1.2.0](https://github.com/gulrak/filesystem/releases/tag/v1.2.0) + +* Added MinGW 32/64 and Visual Studio 2015 builds to the CI configuration. +* Fixed additional compilation issues on MinGW. +* Pull request ([#13](https://github.com/gulrak/filesystem/pull/13)), set + minimum required CMake version to 3.7.2 (as in Debian 8). +* Pull request ([#14](https://github.com/gulrak/filesystem/pull/14)), added + support for a make install target. +* Bugfix for ([#15](https://github.com/gulrak/filesystem/issues/15)), the + forward/impl way of using `ghc::filesystem` missed a `` include + in the windows case. +* Bugfix for ([#16](https://github.com/gulrak/filesystem/issues/16)), + VS2019 didn't like the old size dispatching in the utf8 decoder, so it + was changed to a sfinae based approach. +* New feature ([#17](https://github.com/gulrak/filesystem/issues/17)), optional + support for standard conforming `wchar_t/std::wstring` interface when + compiling on Windows with defined `GHC_WIN_WSTRING_STRING_TYPE`, this is + default when using the `ghc/fs_std*.hpp` header, to enhance compatibility. +* New feature ([#18](https://github.com/gulrak/filesystem/issues/18)), optional + filesystem exceptions/errors on Unicode errors with defined + `GHC_RAISE_UNICODE_ERRORS` (instead of replacing invalid code points or + UTF-8 encoding errors with the replacement character `U+FFFD`). +* Pull request ([#20](https://github.com/gulrak/filesystem/pull/20)), fix for + file handle leak in `fs::copy_file`. +* Coverage now checked in CI (~95% line coverage). + +### [v1.1.4](https://github.com/gulrak/filesystem/releases/tag/v1.1.4) + +* Additional Bugfix for ([#12](https://github.com/gulrak/filesystem/issues/12)), + error in old unified `readdir/readdir_r` code of `fs::directory_iterator`; + as `readdir_r` is now deprecated, I decided to drop it and the resulting + code is much easier, shorter and due to more refactoring faster +* Fix for crashing unit tests against MSVC C++17 `std::filesystem` +* Travis-CI now additionally test with Xcode 10.2 on macOS +* Some minor refactorings + +### [v1.1.2](https://github.com/gulrak/filesystem/releases/tag/v1.1.2) + +* Bugfix for ([#11](https://github.com/gulrak/filesystem/issues/11)), + `fs::path::lexically_normal()` had some issues with `".."`-sequences. +* Bugfix for ([#12](https://github.com/gulrak/filesystem/issues/12)), + `fs::recursive_directory_iterator` could run into endless loops, + the methods depth() and pop() had issues and the copy behavior and + `input_iterator_tag` conformance was broken, added tests +* Restructured some CMake code into a macro to ease the support for + C++17 `std::filesystem` builds of tests and examples for interoperability + checks. +* Some fixes on Windows tests to ease interoperability test runs. +* Reduced noise on `fs::weakly_canonical()` tests against `std::fs` +* Added simple `du` example showing the `recursive_directory_iterator` + used to add the sizes of files in a directory tree. +* Added error checking in `fs::file_time_type` test helpers +* `fs::copy()` now conforms LWG #2682, disallowing the use of + `copy_option::create_symlinks' to be used on directories + +### [v1.1.0](https://github.com/gulrak/filesystem/releases/tag/v1.1.0) + +* Restructuring of the project directory. The header files are now using + `hpp` as extension to be marked as c++ and they where moved to + `include/ghc/` to be able to include by `` as the + former include name might have been to generic and conflict with other + files. +* Better CMake support: `ghc::filesystem` now can be used as a submodul + and added with `add_subdirectory` and will export itself as `ghc_filesystem` + target. To use it, only `target_link_libraries(your-target ghc_filesystem)` + is needed and the include directories will be set so `#include ` + will be a valid directive. + Still you can simply only add the header file to you project and include it + from there. +* Enhancement ([#10](https://github.com/gulrak/filesystem/issues/10)), + support for separation of implementation and forwarded api: Two + additional simple includes are added, that can be used to forward + `ghc::filesystem` declarations (`fs_fwd.hpp`) and to wrap the + implementation into a single cpp (`fs_impl.hpp`) +* The `std::basic_string_view` variants of the `fs::path` api are + now supported when compiling with C++17. +* Added CI integration for Travis-CI and Appveyor. +* Fixed MinGW compilation issues. +* Added long filename support for Windows. + +### [v1.0.10](https://github.com/gulrak/filesystem/releases/tag/v1.0.10) + +* Bugfix for ([#9](https://github.com/gulrak/filesystem/issues/9)), added + missing return statement to `ghc::filesystem::path::generic_string()` +* Added checks to hopefully better compile against Android NDK. There where + no tests run yet, so feedback is needed to actually call this supported. +* `filesystem.h` was renamed `filesystem.hpp` to better reflect that it is + a c++ language header. + +### [v1.0.8](https://github.com/gulrak/filesystem/releases/tag/v1.0.8) + +* Bugfix for ([#6](https://github.com/gulrak/filesystem/issues/6)), where + `ghc::filesystem::remove()` and `ghc::filesystem::remove_all()` both are + now able to remove a single file and both will not raise an error if the + path doesn't exist. +* Merged pull request ([#7](https://github.com/gulrak/filesystem/pull/7)), + a typo leading to setting error code instead of comparing it in + `ghc::filesystem::remove()` under Windows. +* Bugfix for (([#8](https://github.com/gulrak/filesystem/issues/8)), the + Windows version of `ghc::filesystem::directory_iterator` now releases + resources when reaching `end()` like the POSIX one does. + + +### [v1.0.6](https://github.com/gulrak/filesystem/releases/tag/v1.0.6) + +* Bugfix for ([#4](https://github.com/gulrak/filesystem/issues/4)), missing error_code + propagation in `ghc::filesystem::copy()` and `ghc::filesystem::remove_all` fixed. +* Bugfix for ([#5](https://github.com/gulrak/filesystem/issues/5)), added missing std + namespace in `ghc::filesystem::recursive_directory_iterator::difference_type`. + +### [v1.0.4](https://github.com/gulrak/filesystem/releases/tag/v1.0.4) + +* Bugfix for ([#3](https://github.com/gulrak/filesystem/issues/3)), fixed missing inlines + and added test to ensure including into multiple implementation files works as expected. +* Building tests with `-Wall -Wextra -Werror` and fixed resulting issues. + +### [v1.0.2](https://github.com/gulrak/filesystem/releases/tag/v1.0.2) + +* Updated catch2 to v2.4.0. +* Refactored `fs.op.permissions` test to work with all tested `std::filesystem` + implementations (gcc, clang, msvc++). +* Added helper class `ghc::filesystem::u8arguments` as `argv` converter, to + help follow the UTF-8 path on windows. Simply instantiate it with `argc` and + `argv` and it will fetch the Unicode version of the command line and convert + it to UTF-8. The destructor reverts the change. +* Added `examples` folder with hopefully some usefull example usage. Examples are + tested (and build) with `ghc::filesystem` and C++17 `std::filesystem` when + available. +* Starting with this version, only even patch level versions will be tagged and + odd patch levels mark in-between non-stable wip states. +* Tests can now also be run against MS version of `std::filesystem` for comparison. +* Added missing `fstream` include. +* Removed non-conforming C99 `timespec`/`timeval` usage. +* Fixed some integer type mismatches that could lead to warnings. +* Fixed `chrono` conversion issues in test and example on clang 7.0.0. + +### [v1.0.1](https://github.com/gulrak/filesystem/releases/tag/v1.0.1) + +* Bugfix: `ghc::filesystem::canonical` now sees empty path as non-existant and reports + an error. Due to this `ghc::filesystem::weakly_canonical` now returns relative + paths for non-existant argument paths. ([#1](https://github.com/gulrak/filesystem/issues/1)) +* Bugfix: `ghc::filesystem::remove_all` now also counts directories removed ([#2](https://github.com/gulrak/filesystem/issues/2)) +* Bugfix: `recursive_directory_iterator` tests didn't respect equality domain issues + and dereferencapable constraints, leading to fails on `std::filesystem` tests. +* Bugfix: Some `noexcept` tagged methods and functions could indirectly throw exceptions + due to UFT-8 decoding issues. +* `std_filesystem_test` is now also generated if LLVM/clang 7.0.0 is found. + + +### [v1.0.0](https://github.com/gulrak/filesystem/releases/tag/v1.0.0) + +This was the first public release version. It implements the full range of +C++17 `std::filesystem`, as far as possible without other C++17 dependencies. + diff --git a/gulrak-filesystem/cmake/GhcHelper.cmake b/gulrak-filesystem/cmake/GhcHelper.cmake new file mode 100644 index 0000000..8fffae9 --- /dev/null +++ b/gulrak-filesystem/cmake/GhcHelper.cmake @@ -0,0 +1,64 @@ +macro(AddExecutableWithStdFS targetName) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 7.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0)) + if(APPLE) + include_directories(/usr/local/opt/llvm/include) + link_directories(/usr/local/opt/llvm/lib) + endif() + add_executable(${targetName} ${ARGN}) + set_property(TARGET ${targetName} PROPERTY CXX_STANDARD 17) + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) + if(APPLE) + target_link_libraries(${targetName} -lc++fs) + else() + target_compile_options(${targetName} PRIVATE "-stdlib=libc++") + target_link_libraries(${targetName} -stdlib=libc++ -lc++fs $<$:rt>) + endif() + else() + if(NOT APPLE) + target_compile_options(${targetName} PRIVATE "-stdlib=libc++") + target_link_libraries(${targetName} -stdlib=libc++) + endif() + endif() + target_compile_definitions(${targetName} PRIVATE USE_STD_FS) +endif() + +if (CMAKE_COMPILER_IS_GNUCXX AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 8.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0)) + add_executable(${targetName} ${ARGN}) + set_property(TARGET ${targetName} PROPERTY CXX_STANDARD 17) + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) + target_link_libraries(${targetName} -lstdc++fs) + endif() + target_compile_options(${targetName} PRIVATE $<$:-Wa,-mbig-obj>) + target_compile_definitions(${targetName} PRIVATE USE_STD_FS) +endif() + +if(CMAKE_CXX_COMPILER_ID MATCHES MSVC AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 19.15 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.15)) + add_executable(${targetName} ${ARGN}) + set_property(TARGET ${targetName} PROPERTY CXX_STANDARD 17) + set_property(TARGET ${targetName} PROPERTY CXX_STANDARD_REQUIRED ON) + target_compile_options(${targetName} PRIVATE "/Zc:__cplusplus") + target_compile_definitions(${targetName} PRIVATE USE_STD_FS _CRT_SECURE_NO_WARNINGS) +endif() + +endmacro() + +macro(AddTestExecutableWithStdCpp cppStd) + add_executable(filesystem_test_cpp${cppStd} ${ARGN}) + set_property(TARGET filesystem_test_cpp${cppStd} PROPERTY CXX_STANDARD ${cppStd}) + target_link_libraries(filesystem_test_cpp${cppStd} ghc_filesystem) + target_compile_options(filesystem_test_cpp${cppStd} PRIVATE + $<$:-s DISABLE_EXCEPTION_CATCHING=0> + $<$,$>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Werror -Wno-error=deprecated-declarations> + $<$:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Wno-psabi -Werror -Wno-error=deprecated-declarations> + $<$:/WX /wd4996> + $<$:-Wa,-mbig-obj> + $<$:--coverage>) + if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) + target_compile_definitions(filesystem_test_cpp${cppStd} PRIVATE _CRT_SECURE_NO_WARNINGS) + endif() + if(EMSCRIPTEN) + set_target_properties(filesystem_test_cpp${cppStd} PROPERTIES LINK_FLAGS "-g4 -s DISABLE_EXCEPTION_CATCHING=0 -s ALLOW_MEMORY_GROWTH=1") + endif() + ParseAndAddCatchTests(filesystem_test_cpp${cppStd}) +endmacro() diff --git a/gulrak-filesystem/cmake/config.cmake.in b/gulrak-filesystem/cmake/config.cmake.in new file mode 100644 index 0000000..ace9761 --- /dev/null +++ b/gulrak-filesystem/cmake/config.cmake.in @@ -0,0 +1,6 @@ +@PACKAGE_INIT@ + +# import targets +include("${CMAKE_CURRENT_LIST_DIR}/ghc_filesystem-targets.cmake") + +check_required_components(ghcfilesystem) \ No newline at end of file diff --git a/gulrak-filesystem/examples/CMakeLists.txt b/gulrak-filesystem/examples/CMakeLists.txt new file mode 100644 index 0000000..17bb3d6 --- /dev/null +++ b/gulrak-filesystem/examples/CMakeLists.txt @@ -0,0 +1,17 @@ + +add_executable(fs_dir dir.cpp) +target_link_libraries(fs_dir ghc_filesystem) +if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) + target_compile_definitions(fs_dir PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() +AddExecutableWithStdFS(std_fs_dir dir.cpp) + +add_executable(fs_du du.cpp) +target_link_libraries(fs_du ghc_filesystem) +AddExecutableWithStdFS(std_fs_du du.cpp) + +if(EXISTS "${PROJECT_SOURCE_DIR}/examples/benchmark.cpp") + add_executable(fs_benchmark benchmark.cpp) + set_property(TARGET fs_benchmark PROPERTY CXX_STANDARD 17) + target_link_libraries(fs_benchmark ghc_filesystem) +endif() \ No newline at end of file diff --git a/gulrak-filesystem/examples/dir.cpp b/gulrak-filesystem/examples/dir.cpp new file mode 100644 index 0000000..abd4cc6 --- /dev/null +++ b/gulrak-filesystem/examples/dir.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) +#if __has_include() +#define GHC_USE_STD_FS +#include +namespace fs = std::filesystem; +#endif +#endif +#ifndef GHC_USE_STD_FS +#include +namespace fs = ghc::filesystem; +#endif + +template +std::time_t to_time_t(TP tp) +{ + // Based on trick from: Nico Josuttis, C++17 - The Complete Guide + std::chrono::system_clock::duration dt = std::chrono::duration_cast(tp - TP::clock::now()); + return std::chrono::system_clock::to_time_t(std::chrono::system_clock::now() + dt); +} + +static std::string perm_to_str(fs::perms prms) +{ + std::string result; + result.reserve(9); + for (int i = 0; i < 9; ++i) { + result = ((static_cast(prms) & (1 << i)) ? "xwrxwrxwr"[i] : '-') + result; + } + return result; +} + +int main(int argc, char* argv[]) +{ +#ifdef GHC_FILESYSTEM_VERSION + fs::u8arguments u8guard(argc, argv); + if (!u8guard.valid()) { + std::cerr << "Invalid character encoding, UTF-8 based encoding needed." << std::endl; + std::exit(EXIT_FAILURE); + } +#endif + if (argc > 2) { + std::cerr << "USAGE: dir " << std::endl; + exit(1); + } + fs::path dir{"."}; + if (argc == 2) { + dir = fs::u8path(argv[1]); + } + for (auto de : fs::directory_iterator(dir)) { + auto ft = to_time_t(de.last_write_time()); + auto ftm = *std::localtime(&ft); + std::cout << (de.is_directory() ? "d" : "-") << perm_to_str(de.symlink_status().permissions()) << " " << std::setw(8) << (de.is_directory() ? "-" : std::to_string(de.file_size())) << " " << std::put_time(&ftm, "%Y-%m-%d %H:%M:%S") << " " + << de.path().filename().string() << std::endl; + } + return 0; +} diff --git a/gulrak-filesystem/examples/du.cpp b/gulrak-filesystem/examples/du.cpp new file mode 100644 index 0000000..929b809 --- /dev/null +++ b/gulrak-filesystem/examples/du.cpp @@ -0,0 +1,61 @@ +#include +#include +#include + +#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) +#if __has_include() +#define GHC_USE_STD_FS +#include +namespace fs = std::filesystem; +#endif +#endif +#ifndef GHC_USE_STD_FS +#include +namespace fs = ghc::filesystem; +#endif + +int main(int argc, char* argv[]) +{ +#ifdef GHC_FILESYSTEM_VERSION + fs::u8arguments u8guard(argc, argv); + if(!u8guard.valid()) { + std::cerr << "Invalid character encoding, UTF-8 based encoding needed." << std::endl; + std::exit(EXIT_FAILURE); + } +#endif + if(argc > 2) { + std::cerr << "USAGE: du " << std::endl; + exit(1); + } + fs::path dir{"."}; + if(argc == 2) { + dir = fs::u8path(argv[1]); + } + + uint64_t totalSize = 0; + int totalDirs = 0; + int totalFiles = 0; + int maxDepth = 0; + + try { + auto rdi = fs::recursive_directory_iterator(dir); + for(auto de : rdi) { + if(rdi.depth() > maxDepth) { + maxDepth = rdi.depth(); + } + if(de.is_regular_file()) { + totalSize += de.file_size(); + ++totalFiles; + } + else if(de.is_directory()) { + ++totalDirs; + } + } + } + catch(fs::filesystem_error fe) { + std::cerr << "Error: " << fe.what() << std::endl; + exit(1); + } + std::cout << totalSize << " bytes in " << totalFiles << " files and " << totalDirs << " directories, maximum depth: " << maxDepth << std::endl; + return 0; +} diff --git a/gulrak-filesystem/include/ghc/filesystem.hpp b/gulrak-filesystem/include/ghc/filesystem.hpp new file mode 100644 index 0000000..a319def --- /dev/null +++ b/gulrak-filesystem/include/ghc/filesystem.hpp @@ -0,0 +1,6017 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14/C++17/C++20 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// +// To dynamically select std::filesystem where available on most platforms, +// you could use: +// +// #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +// #if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +// #define GHC_USE_STD_FS +// #include +// namespace fs = std::filesystem; +// #endif +// #endif +// #ifndef GHC_USE_STD_FS +// #include +// namespace fs = ghc::filesystem; +// #endif +// +//--------------------------------------------------------------------------------------- +#ifndef GHC_FILESYSTEM_H +#define GHC_FILESYSTEM_H + +// #define BSD manifest constant only in +// sys/param.h +#ifndef _WIN32 +#include +#endif + +#ifndef GHC_OS_DETECTED +#if defined(__APPLE__) && defined(__MACH__) +#define GHC_OS_MACOS +#elif defined(__linux__) +#define GHC_OS_LINUX +#if defined(__ANDROID__) +#define GHC_OS_ANDROID +#endif +#elif defined(_WIN64) +#define GHC_OS_WINDOWS +#define GHC_OS_WIN64 +#elif defined(_WIN32) +#define GHC_OS_WINDOWS +#define GHC_OS_WIN32 +#elif defined(__CYGWIN__) +#define GHC_OS_CYGWIN +#elif defined(__svr4__) +#define GHC_OS_SYS5R4 +#elif defined(BSD) +#define GHC_OS_BSD +#elif defined(__EMSCRIPTEN__) +#define GHC_OS_WEB +#include +#elif defined(__QNX__) +#define GHC_OS_QNX +#define GHC_NO_DIRENT_D_TYPE +#else +#error "Operating system currently not supported!" +#endif +#define GHC_OS_DETECTED +#if (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#if _MSVC_LANG == 201703L +#define GHC_FILESYSTEM_RUNNING_CPP17 +#else +#define GHC_FILESYSTEM_RUNNING_CPP20 +#endif +#elif (defined(__cplusplus) && __cplusplus >= 201703L) +#if __cplusplus == 201703L +#define GHC_FILESYSTEM_RUNNING_CPP17 +#else +#define GHC_FILESYSTEM_RUNNING_CPP20 +#endif +#endif +#endif + +#if defined(GHC_FILESYSTEM_IMPLEMENTATION) +#define GHC_EXPAND_IMPL +#define GHC_INLINE +#ifdef GHC_OS_WINDOWS +#ifndef GHC_FS_API +#define GHC_FS_API +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS +#endif +#else +#ifndef GHC_FS_API +#define GHC_FS_API __attribute__((visibility("default"))) +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS __attribute__((visibility("default"))) +#endif +#endif +#elif defined(GHC_FILESYSTEM_FWD) +#define GHC_INLINE +#ifdef GHC_OS_WINDOWS +#ifndef GHC_FS_API +#define GHC_FS_API extern +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS +#endif +#else +#ifndef GHC_FS_API +#define GHC_FS_API extern +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS +#endif +#endif +#else +#define GHC_EXPAND_IMPL +#define GHC_INLINE inline +#ifndef GHC_FS_API +#define GHC_FS_API +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS +#endif +#endif + +#ifdef GHC_EXPAND_IMPL + +#ifdef GHC_OS_WINDOWS +#include +// additional includes +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef GHC_OS_ANDROID +#include +#if __ANDROID_API__ < 12 +#include +#endif +#include +#define statvfs statfs +#else +#include +#endif +#ifdef GHC_OS_CYGWIN +#include +#endif +#if !defined(__ANDROID__) || __ANDROID_API__ >= 26 +#include +#endif +#endif +#ifdef GHC_OS_MACOS +#include +#endif + +#if defined(__cpp_impl_three_way_comparison) && defined(__has_include) +#if __has_include() +#define GHC_HAS_THREEWAY_COMP +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else // GHC_EXPAND_IMPL + +#if defined(__cpp_impl_three_way_comparison) && defined(__has_include) +#if __has_include() +#define GHC_HAS_THREEWAY_COMP +#include +#endif +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef GHC_OS_WINDOWS +#include +#endif +#endif // GHC_EXPAND_IMPL + +// After standard library includes. +// Standard library support for std::string_view. +#if defined(__cpp_lib_string_view) +#define GHC_HAS_STD_STRING_VIEW +#elif defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 4000) && (__cplusplus >= 201402) +#define GHC_HAS_STD_STRING_VIEW +#elif defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 7) && (__cplusplus >= 201703) +#define GHC_HAS_STD_STRING_VIEW +#elif defined(_MSC_VER) && (_MSC_VER >= 1910 && _MSVC_LANG >= 201703) +#define GHC_HAS_STD_STRING_VIEW +#endif + +// Standard library support for std::experimental::string_view. +#if defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 3700 && _LIBCPP_VERSION < 7000) && (__cplusplus >= 201402) +#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW +#elif defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4)) && (__cplusplus >= 201402) +#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW +#elif defined(__GLIBCXX__) && defined(_GLIBCXX_USE_DUAL_ABI) && (__cplusplus >= 201402) +// macro _GLIBCXX_USE_DUAL_ABI is always defined in libstdc++ from gcc-5 and newer +#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW +#endif + +#if defined(GHC_HAS_STD_STRING_VIEW) +#include +#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW) +#include +#endif + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp): +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Enforce C++17 API where possible when compiling for C++20, handles the following cases: +// * fs::path::u8string() returns std::string instead of std::u8string +// #define GHC_FILESYSTEM_ENFORCE_CPP17_API +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories +// configure LWG conformance () +#define LWG_2682_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular +// file with that name, it is superseded by P1164R1, so only activate if really needed +// #define LWG_2935_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2936 enables new element wise (more expensive) path comparison +// * if this->root_name().native().compare(p.root_name().native()) != 0 return result +// * if this->has_root_directory() and !p.has_root_directory() return -1 +// * if !this->has_root_directory() and p.has_root_directory() return -1 +// * else result of element wise comparison of path iteration where first comparison is != 0 or 0 +// if all comparisons are 0 (on Windows this implementation does case insensitive root_name() +// comparison) +#define LWG_2936_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2) +#define LWG_2937_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// UTF8-Everywhere is the original behaviour of ghc::filesystem. But since v1.5 the windows +// version defaults to std::wstring storage backend. Still all std::string will be interpreted +// as UTF-8 encoded. With this define you can enfoce the old behavior on Windows, using +// std::string as backend and for fs::path::native() and char for fs::path::c_str(). This +// needs more conversions so it is (an was before v1.5) slower, bot might help keeping source +// homogeneous in a multi platform project. +// #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Raise errors/exceptions when invalid unicode codepoints or UTF-8 sequences are found, +// instead of replacing them with the unicode replacement character (U+FFFD). +// #define GHC_RAISE_UNICODE_ERRORS +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Automatic prefix windows path with "\\?\" if they would break the MAX_PATH length. +// instead of replacing them with the unicode replacement character (U+FFFD). +#ifndef GHC_WIN_DISABLE_AUTO_PREFIXES +#define GHC_WIN_AUTO_PREFIX_LONG_PATH +#endif // GHC_WIN_DISABLE_AUTO_PREFIXES +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +// ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) +#define GHC_FILESYSTEM_VERSION 10510L + +#if !defined(GHC_WITH_EXCEPTIONS) && (defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)) +#define GHC_WITH_EXCEPTIONS +#endif +#if !defined(GHC_WITH_EXCEPTIONS) && defined(GHC_RAISE_UNICODE_ERRORS) +#error "Can't raise unicode errors with exception support disabled" +#endif + +namespace ghc { +namespace filesystem { + +#if defined(GHC_HAS_CUSTOM_STRING_VIEW) +#define GHC_WITH_STRING_VIEW +#elif defined(GHC_HAS_STD_STRING_VIEW) +#define GHC_WITH_STRING_VIEW +using std::basic_string_view; +#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW) +#define GHC_WITH_STRING_VIEW +using std::experimental::basic_string_view; +#endif + +// temporary existing exception type for yet unimplemented parts +class GHC_FS_API_CLASS not_implemented_exception : public std::logic_error +{ +public: + not_implemented_exception() + : std::logic_error("function not implemented yet.") + { + } +}; + +template +class path_helper_base +{ +public: + using value_type = char_type; +#ifdef GHC_OS_WINDOWS + static constexpr value_type preferred_separator = '\\'; +#else + static constexpr value_type preferred_separator = '/'; +#endif +}; + +#if __cplusplus < 201703L +template +constexpr char_type path_helper_base::preferred_separator; +#endif + +#ifdef GHC_OS_WINDOWS +class path; +namespace detail { +bool has_executable_extension(const path& p); +} +#endif + +// [fs.class.path] class path +class GHC_FS_API_CLASS path +#if defined(GHC_OS_WINDOWS) && !defined(GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE) +#define GHC_USE_WCHAR_T +#define GHC_NATIVEWP(p) p.c_str() +#define GHC_PLATFORM_LITERAL(str) L##str + : private path_helper_base +{ +public: + using path_helper_base::value_type; +#else +#define GHC_NATIVEWP(p) p.wstring().c_str() +#define GHC_PLATFORM_LITERAL(str) str + : private path_helper_base +{ +public: + using path_helper_base::value_type; +#endif + using string_type = std::basic_string; + using path_helper_base::preferred_separator; + + // [fs.enum.path.format] enumeration format + /// The path format in which the constructor argument is given. + enum format { + generic_format, ///< The generic format, internally used by + ///< ghc::filesystem::path with slashes + native_format, ///< The format native to the current platform this code + ///< is build for + auto_format, ///< Try to auto-detect the format, fallback to native + }; + + template + struct _is_basic_string : std::false_type + { + }; + template + struct _is_basic_string> : std::true_type + { + }; + template + struct _is_basic_string, std::allocator>> : std::true_type + { + }; +#ifdef GHC_WITH_STRING_VIEW + template + struct _is_basic_string> : std::true_type + { + }; + template + struct _is_basic_string>> : std::true_type + { + }; +#endif + + template + using path_type = typename std::enable_if::value, path>::type; + template +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + using path_from_string = + typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value, + path>::type; + template + using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; +#else + using path_from_string = + typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value, + path>::type; + template + using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; +#endif + // [fs.path.construct] constructors and destructor + path() noexcept; + path(const path& p); + path(path&& p) noexcept; + path(string_type&& source, format fmt = auto_format); + template > + path(const Source& source, format fmt = auto_format); + template + path(InputIterator first, InputIterator last, format fmt = auto_format); +#ifdef GHC_WITH_EXCEPTIONS + template > + path(const Source& source, const std::locale& loc, format fmt = auto_format); + template + path(InputIterator first, InputIterator last, const std::locale& loc, format fmt = auto_format); +#endif + ~path(); + + // [fs.path.assign] assignments + path& operator=(const path& p); + path& operator=(path&& p) noexcept; + path& operator=(string_type&& source); + path& assign(string_type&& source); + template + path& operator=(const Source& source); + template + path& assign(const Source& source); + template + path& assign(InputIterator first, InputIterator last); + + // [fs.path.append] appends + path& operator/=(const path& p); + template + path& operator/=(const Source& source); + template + path& append(const Source& source); + template + path& append(InputIterator first, InputIterator last); + + // [fs.path.concat] concatenation + path& operator+=(const path& x); + path& operator+=(const string_type& x); +#ifdef GHC_WITH_STRING_VIEW + path& operator+=(basic_string_view x); +#endif + path& operator+=(const value_type* x); + path& operator+=(value_type x); + template + path_from_string& operator+=(const Source& x); + template + path_type_EcharT& operator+=(EcharT x); + template + path& concat(const Source& x); + template + path& concat(InputIterator first, InputIterator last); + + // [fs.path.modifiers] modifiers + void clear() noexcept; + path& make_preferred(); + path& remove_filename(); + path& replace_filename(const path& replacement); + path& replace_extension(const path& replacement = path()); + void swap(path& rhs) noexcept; + + // [fs.path.native.obs] native format observers + const string_type& native() const noexcept; + const value_type* c_str() const noexcept; + operator string_type() const; + template , class Allocator = std::allocator> + std::basic_string string(const Allocator& a = Allocator()) const; + std::string string() const; + std::wstring wstring() const; +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + std::u8string u8string() const; +#else + std::string u8string() const; +#endif + std::u16string u16string() const; + std::u32string u32string() const; + + // [fs.path.generic.obs] generic format observers + template , class Allocator = std::allocator> + std::basic_string generic_string(const Allocator& a = Allocator()) const; + std::string generic_string() const; + std::wstring generic_wstring() const; +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + std::u8string generic_u8string() const; +#else + std::string generic_u8string() const; +#endif + std::u16string generic_u16string() const; + std::u32string generic_u32string() const; + + // [fs.path.compare] compare + int compare(const path& p) const noexcept; + int compare(const string_type& s) const; +#ifdef GHC_WITH_STRING_VIEW + int compare(basic_string_view s) const; +#endif + int compare(const value_type* s) const; + + // [fs.path.decompose] decomposition + path root_name() const; + path root_directory() const; + path root_path() const; + path relative_path() const; + path parent_path() const; + path filename() const; + path stem() const; + path extension() const; + + // [fs.path.query] query + bool empty() const noexcept; + bool has_root_name() const; + bool has_root_directory() const; + bool has_root_path() const; + bool has_relative_path() const; + bool has_parent_path() const; + bool has_filename() const; + bool has_stem() const; + bool has_extension() const; + bool is_absolute() const; + bool is_relative() const; + + // [fs.path.gen] generation + path lexically_normal() const; + path lexically_relative(const path& base) const; + path lexically_proximate(const path& base) const; + + // [fs.path.itr] iterators + class iterator; + using const_iterator = iterator; + iterator begin() const; + iterator end() const; + +private: + using impl_value_type = value_type; + using impl_string_type = std::basic_string; + friend class directory_iterator; + void append_name(const value_type* name); + static constexpr impl_value_type generic_separator = '/'; + template + class input_iterator_range + { + public: + typedef InputIterator iterator; + typedef InputIterator const_iterator; + typedef typename InputIterator::difference_type difference_type; + + input_iterator_range(const InputIterator& first, const InputIterator& last) + : _first(first) + , _last(last) + { + } + + InputIterator begin() const { return _first; } + InputIterator end() const { return _last; } + + private: + InputIterator _first; + InputIterator _last; + }; + friend void swap(path& lhs, path& rhs) noexcept; + friend size_t hash_value(const path& p) noexcept; + friend path canonical(const path& p, std::error_code& ec); + friend bool create_directories(const path& p, std::error_code& ec) noexcept; + string_type::size_type root_name_length() const noexcept; + void postprocess_path_with_format(format fmt); + void check_long_path(); + impl_string_type _path; +#ifdef GHC_OS_WINDOWS + void handle_prefixes(); + friend bool detail::has_executable_extension(const path& p); +#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH + string_type::size_type _prefixLength{0}; +#else // GHC_WIN_AUTO_PREFIX_LONG_PATH + static const string_type::size_type _prefixLength{0}; +#endif // GHC_WIN_AUTO_PREFIX_LONG_PATH +#else + static const string_type::size_type _prefixLength{0}; +#endif +}; + +// [fs.path.nonmember] path non-member functions +GHC_FS_API void swap(path& lhs, path& rhs) noexcept; +GHC_FS_API size_t hash_value(const path& p) noexcept; +#ifdef GHC_HAS_THREEWAY_COMP +GHC_FS_API std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept; +#endif +GHC_FS_API bool operator==(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator!=(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator<(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator<=(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator>(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator>=(const path& lhs, const path& rhs) noexcept; +GHC_FS_API path operator/(const path& lhs, const path& rhs); + +// [fs.path.io] path inserter and extractor +template +std::basic_ostream& operator<<(std::basic_ostream& os, const path& p); +template +std::basic_istream& operator>>(std::basic_istream& is, path& p); + +// [pfs.path.factory] path factory functions +template > +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +[[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]] +#endif +path u8path(const Source& source); +template +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +[[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]] +#endif +path u8path(InputIterator first, InputIterator last); + +// [fs.class.filesystem_error] class filesystem_error +class GHC_FS_API_CLASS filesystem_error : public std::system_error +{ +public: + filesystem_error(const std::string& what_arg, std::error_code ec); + filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec); + filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec); + const path& path1() const noexcept; + const path& path2() const noexcept; + const char* what() const noexcept override; + +private: + std::string _what_arg; + std::error_code _ec; + path _p1, _p2; +}; + +class GHC_FS_API_CLASS path::iterator +{ +public: + using value_type = const path; + using difference_type = std::ptrdiff_t; + using pointer = const path*; + using reference = const path&; + using iterator_category = std::bidirectional_iterator_tag; + + iterator(); + iterator(const path& p, const impl_string_type::const_iterator& pos); + iterator& operator++(); + iterator operator++(int); + iterator& operator--(); + iterator operator--(int); + bool operator==(const iterator& other) const; + bool operator!=(const iterator& other) const; + reference operator*() const; + pointer operator->() const; + +private: + friend class path; + impl_string_type::const_iterator increment(const impl_string_type::const_iterator& pos) const; + impl_string_type::const_iterator decrement(const impl_string_type::const_iterator& pos) const; + void updateCurrent(); + impl_string_type::const_iterator _first; + impl_string_type::const_iterator _last; + impl_string_type::const_iterator _prefix; + impl_string_type::const_iterator _root; + impl_string_type::const_iterator _iter; + path _current; +}; + +struct space_info +{ + uintmax_t capacity; + uintmax_t free; + uintmax_t available; +}; + +// [fs.enum] enumerations +// [fs.enum.file_type] +enum class file_type { + none, + not_found, + regular, + directory, + symlink, + block, + character, + fifo, + socket, + unknown, +}; + +// [fs.enum.perms] +enum class perms : uint16_t { + none = 0, + + owner_read = 0400, + owner_write = 0200, + owner_exec = 0100, + owner_all = 0700, + + group_read = 040, + group_write = 020, + group_exec = 010, + group_all = 070, + + others_read = 04, + others_write = 02, + others_exec = 01, + others_all = 07, + + all = 0777, + set_uid = 04000, + set_gid = 02000, + sticky_bit = 01000, + + mask = 07777, + unknown = 0xffff +}; + +// [fs.enum.perm.opts] +enum class perm_options : uint16_t { + replace = 3, + add = 1, + remove = 2, + nofollow = 4, +}; + +// [fs.enum.copy.opts] +enum class copy_options : uint16_t { + none = 0, + + skip_existing = 1, + overwrite_existing = 2, + update_existing = 4, + + recursive = 8, + + copy_symlinks = 0x10, + skip_symlinks = 0x20, + + directories_only = 0x40, + create_symlinks = 0x80, +#ifndef GHC_OS_WEB + create_hard_links = 0x100 +#endif +}; + +// [fs.enum.dir.opts] +enum class directory_options : uint16_t { + none = 0, + follow_directory_symlink = 1, + skip_permission_denied = 2, +}; + +// [fs.class.file_status] class file_status +class GHC_FS_API_CLASS file_status +{ +public: + // [fs.file_status.cons] constructors and destructor + file_status() noexcept; + explicit file_status(file_type ft, perms prms = perms::unknown) noexcept; + file_status(const file_status&) noexcept; + file_status(file_status&&) noexcept; + ~file_status(); + // assignments: + file_status& operator=(const file_status&) noexcept; + file_status& operator=(file_status&&) noexcept; + // [fs.file_status.mods] modifiers + void type(file_type ft) noexcept; + void permissions(perms prms) noexcept; + // [fs.file_status.obs] observers + file_type type() const noexcept; + perms permissions() const noexcept; + friend bool operator==(const file_status& lhs, const file_status& rhs) noexcept { return lhs.type() == rhs.type() && lhs.permissions() == rhs.permissions(); } + +private: + file_type _type; + perms _perms; +}; + +using file_time_type = std::chrono::time_point; + +// [fs.class.directory_entry] Class directory_entry +class GHC_FS_API_CLASS directory_entry +{ +public: + // [fs.dir.entry.cons] constructors and destructor + directory_entry() noexcept = default; + directory_entry(const directory_entry&) = default; + directory_entry(directory_entry&&) noexcept = default; +#ifdef GHC_WITH_EXCEPTIONS + explicit directory_entry(const path& p); +#endif + directory_entry(const path& p, std::error_code& ec); + ~directory_entry(); + + // assignments: + directory_entry& operator=(const directory_entry&) = default; + directory_entry& operator=(directory_entry&&) noexcept = default; + + // [fs.dir.entry.mods] modifiers +#ifdef GHC_WITH_EXCEPTIONS + void assign(const path& p); + void replace_filename(const path& p); + void refresh(); +#endif + void assign(const path& p, std::error_code& ec); + void replace_filename(const path& p, std::error_code& ec); + void refresh(std::error_code& ec) noexcept; + + // [fs.dir.entry.obs] observers + const filesystem::path& path() const noexcept; + operator const filesystem::path&() const noexcept; +#ifdef GHC_WITH_EXCEPTIONS + bool exists() const; + bool is_block_file() const; + bool is_character_file() const; + bool is_directory() const; + bool is_fifo() const; + bool is_other() const; + bool is_regular_file() const; + bool is_socket() const; + bool is_symlink() const; + uintmax_t file_size() const; + file_time_type last_write_time() const; + file_status status() const; + file_status symlink_status() const; +#endif + bool exists(std::error_code& ec) const noexcept; + bool is_block_file(std::error_code& ec) const noexcept; + bool is_character_file(std::error_code& ec) const noexcept; + bool is_directory(std::error_code& ec) const noexcept; + bool is_fifo(std::error_code& ec) const noexcept; + bool is_other(std::error_code& ec) const noexcept; + bool is_regular_file(std::error_code& ec) const noexcept; + bool is_socket(std::error_code& ec) const noexcept; + bool is_symlink(std::error_code& ec) const noexcept; + uintmax_t file_size(std::error_code& ec) const noexcept; + file_time_type last_write_time(std::error_code& ec) const noexcept; + file_status status(std::error_code& ec) const noexcept; + file_status symlink_status(std::error_code& ec) const noexcept; + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS + uintmax_t hard_link_count() const; +#endif + uintmax_t hard_link_count(std::error_code& ec) const noexcept; +#endif + +#ifdef GHC_HAS_THREEWAY_COMP + std::strong_ordering operator<=>(const directory_entry& rhs) const noexcept; +#endif + bool operator<(const directory_entry& rhs) const noexcept; + bool operator==(const directory_entry& rhs) const noexcept; + bool operator!=(const directory_entry& rhs) const noexcept; + bool operator<=(const directory_entry& rhs) const noexcept; + bool operator>(const directory_entry& rhs) const noexcept; + bool operator>=(const directory_entry& rhs) const noexcept; + +private: + friend class directory_iterator; +#ifdef GHC_WITH_EXCEPTIONS + file_type status_file_type() const; +#endif + file_type status_file_type(std::error_code& ec) const noexcept; + filesystem::path _path; + file_status _status; + file_status _symlink_status; + uintmax_t _file_size = static_cast(-1); +#ifndef GHC_OS_WINDOWS + uintmax_t _hard_link_count = static_cast(-1); +#endif + time_t _last_write_time = 0; +}; + +// [fs.class.directory.iterator] Class directory_iterator +class GHC_FS_API_CLASS directory_iterator +{ +public: + class GHC_FS_API_CLASS proxy + { + public: + const directory_entry& operator*() const& noexcept { return _dir_entry; } + directory_entry operator*() && noexcept { return std::move(_dir_entry); } + + private: + explicit proxy(const directory_entry& dir_entry) + : _dir_entry(dir_entry) + { + } + friend class directory_iterator; + friend class recursive_directory_iterator; + directory_entry _dir_entry; + }; + using iterator_category = std::input_iterator_tag; + using value_type = directory_entry; + using difference_type = std::ptrdiff_t; + using pointer = const directory_entry*; + using reference = const directory_entry&; + + // [fs.dir.itr.members] member functions + directory_iterator() noexcept; +#ifdef GHC_WITH_EXCEPTIONS + explicit directory_iterator(const path& p); + directory_iterator(const path& p, directory_options options); +#endif + directory_iterator(const path& p, std::error_code& ec) noexcept; + directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept; + directory_iterator(const directory_iterator& rhs); + directory_iterator(directory_iterator&& rhs) noexcept; + ~directory_iterator(); + directory_iterator& operator=(const directory_iterator& rhs); + directory_iterator& operator=(directory_iterator&& rhs) noexcept; + const directory_entry& operator*() const; + const directory_entry* operator->() const; +#ifdef GHC_WITH_EXCEPTIONS + directory_iterator& operator++(); +#endif + directory_iterator& increment(std::error_code& ec) noexcept; + + // other members as required by [input.iterators] +#ifdef GHC_WITH_EXCEPTIONS + proxy operator++(int) + { + proxy p{**this}; + ++*this; + return p; + } +#endif + bool operator==(const directory_iterator& rhs) const; + bool operator!=(const directory_iterator& rhs) const; + +private: + friend class recursive_directory_iterator; + class impl; + std::shared_ptr _impl; +}; + +// [fs.dir.itr.nonmembers] directory_iterator non-member functions +GHC_FS_API directory_iterator begin(directory_iterator iter) noexcept; +GHC_FS_API directory_iterator end(const directory_iterator&) noexcept; + +// [fs.class.re.dir.itr] class recursive_directory_iterator +class GHC_FS_API_CLASS recursive_directory_iterator +{ +public: + using iterator_category = std::input_iterator_tag; + using value_type = directory_entry; + using difference_type = std::ptrdiff_t; + using pointer = const directory_entry*; + using reference = const directory_entry&; + + // [fs.rec.dir.itr.members] constructors and destructor + recursive_directory_iterator() noexcept; +#ifdef GHC_WITH_EXCEPTIONS + explicit recursive_directory_iterator(const path& p); + recursive_directory_iterator(const path& p, directory_options options); +#endif + recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept; + recursive_directory_iterator(const path& p, std::error_code& ec) noexcept; + recursive_directory_iterator(const recursive_directory_iterator& rhs); + recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept; + ~recursive_directory_iterator(); + + // [fs.rec.dir.itr.members] observers + directory_options options() const; + int depth() const; + bool recursion_pending() const; + + const directory_entry& operator*() const; + const directory_entry* operator->() const; + + // [fs.rec.dir.itr.members] modifiers recursive_directory_iterator& + recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs); + recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept; +#ifdef GHC_WITH_EXCEPTIONS + recursive_directory_iterator& operator++(); +#endif + recursive_directory_iterator& increment(std::error_code& ec) noexcept; + +#ifdef GHC_WITH_EXCEPTIONS + void pop(); +#endif + void pop(std::error_code& ec); + void disable_recursion_pending(); + + // other members as required by [input.iterators] +#ifdef GHC_WITH_EXCEPTIONS + directory_iterator::proxy operator++(int) + { + directory_iterator::proxy proxy{**this}; + ++*this; + return proxy; + } +#endif + bool operator==(const recursive_directory_iterator& rhs) const; + bool operator!=(const recursive_directory_iterator& rhs) const; + +private: + struct recursive_directory_iterator_impl + { + directory_options _options; + bool _recursion_pending; + std::stack _dir_iter_stack; + recursive_directory_iterator_impl(directory_options options, bool recursion_pending) + : _options(options) + , _recursion_pending(recursion_pending) + { + } + }; + std::shared_ptr _impl; +}; + +// [fs.rec.dir.itr.nonmembers] directory_iterator non-member functions +GHC_FS_API recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept; +GHC_FS_API recursive_directory_iterator end(const recursive_directory_iterator&) noexcept; + +// [fs.op.funcs] filesystem operations +#ifdef GHC_WITH_EXCEPTIONS +GHC_FS_API path absolute(const path& p); +GHC_FS_API path canonical(const path& p); +GHC_FS_API void copy(const path& from, const path& to); +GHC_FS_API void copy(const path& from, const path& to, copy_options options); +GHC_FS_API bool copy_file(const path& from, const path& to); +GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option); +GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink); +GHC_FS_API bool create_directories(const path& p); +GHC_FS_API bool create_directory(const path& p); +GHC_FS_API bool create_directory(const path& p, const path& attributes); +GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink); +GHC_FS_API void create_symlink(const path& to, const path& new_symlink); +GHC_FS_API path current_path(); +GHC_FS_API void current_path(const path& p); +GHC_FS_API bool exists(const path& p); +GHC_FS_API bool equivalent(const path& p1, const path& p2); +GHC_FS_API uintmax_t file_size(const path& p); +GHC_FS_API bool is_block_file(const path& p); +GHC_FS_API bool is_character_file(const path& p); +GHC_FS_API bool is_directory(const path& p); +GHC_FS_API bool is_empty(const path& p); +GHC_FS_API bool is_fifo(const path& p); +GHC_FS_API bool is_other(const path& p); +GHC_FS_API bool is_regular_file(const path& p); +GHC_FS_API bool is_socket(const path& p); +GHC_FS_API bool is_symlink(const path& p); +GHC_FS_API file_time_type last_write_time(const path& p); +GHC_FS_API void last_write_time(const path& p, file_time_type new_time); +GHC_FS_API void permissions(const path& p, perms prms, perm_options opts = perm_options::replace); +GHC_FS_API path proximate(const path& p, const path& base = current_path()); +GHC_FS_API path read_symlink(const path& p); +GHC_FS_API path relative(const path& p, const path& base = current_path()); +GHC_FS_API bool remove(const path& p); +GHC_FS_API uintmax_t remove_all(const path& p); +GHC_FS_API void rename(const path& from, const path& to); +GHC_FS_API void resize_file(const path& p, uintmax_t size); +GHC_FS_API space_info space(const path& p); +GHC_FS_API file_status status(const path& p); +GHC_FS_API file_status symlink_status(const path& p); +GHC_FS_API path temp_directory_path(); +GHC_FS_API path weakly_canonical(const path& p); +#endif +GHC_FS_API path absolute(const path& p, std::error_code& ec); +GHC_FS_API path canonical(const path& p, std::error_code& ec); +GHC_FS_API void copy(const path& from, const path& to, std::error_code& ec) noexcept; +GHC_FS_API void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept; +GHC_FS_API bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept; +GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option, std::error_code& ec) noexcept; +GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept; +GHC_FS_API bool create_directories(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool create_directory(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept; +GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; +GHC_FS_API void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; +GHC_FS_API path current_path(std::error_code& ec); +GHC_FS_API void current_path(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool exists(file_status s) noexcept; +GHC_FS_API bool exists(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept; +GHC_FS_API uintmax_t file_size(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_block_file(file_status s) noexcept; +GHC_FS_API bool is_block_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_character_file(file_status s) noexcept; +GHC_FS_API bool is_character_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_directory(file_status s) noexcept; +GHC_FS_API bool is_directory(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_empty(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_fifo(file_status s) noexcept; +GHC_FS_API bool is_fifo(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_other(file_status s) noexcept; +GHC_FS_API bool is_other(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_regular_file(file_status s) noexcept; +GHC_FS_API bool is_regular_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_socket(file_status s) noexcept; +GHC_FS_API bool is_socket(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_symlink(file_status s) noexcept; +GHC_FS_API bool is_symlink(const path& p, std::error_code& ec) noexcept; +GHC_FS_API file_time_type last_write_time(const path& p, std::error_code& ec) noexcept; +GHC_FS_API void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept; +GHC_FS_API void permissions(const path& p, perms prms, std::error_code& ec) noexcept; +GHC_FS_API void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept; +GHC_FS_API path proximate(const path& p, std::error_code& ec); +GHC_FS_API path proximate(const path& p, const path& base, std::error_code& ec); +GHC_FS_API path read_symlink(const path& p, std::error_code& ec); +GHC_FS_API path relative(const path& p, std::error_code& ec); +GHC_FS_API path relative(const path& p, const path& base, std::error_code& ec); +GHC_FS_API bool remove(const path& p, std::error_code& ec) noexcept; +GHC_FS_API uintmax_t remove_all(const path& p, std::error_code& ec) noexcept; +GHC_FS_API void rename(const path& from, const path& to, std::error_code& ec) noexcept; +GHC_FS_API void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept; +GHC_FS_API space_info space(const path& p, std::error_code& ec) noexcept; +GHC_FS_API file_status status(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool status_known(file_status s) noexcept; +GHC_FS_API file_status symlink_status(const path& p, std::error_code& ec) noexcept; +GHC_FS_API path temp_directory_path(std::error_code& ec) noexcept; +GHC_FS_API path weakly_canonical(const path& p, std::error_code& ec) noexcept; + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS +GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link); +GHC_FS_API uintmax_t hard_link_count(const path& p); +#endif +GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept; +GHC_FS_API uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept; +#endif + +// Non-C++17 add-on std::fstream wrappers with path +template > +class basic_filebuf : public std::basic_filebuf +{ +public: + basic_filebuf() {} + ~basic_filebuf() override {} + basic_filebuf(const basic_filebuf&) = delete; + const basic_filebuf& operator=(const basic_filebuf&) = delete; + basic_filebuf* open(const path& p, std::ios_base::openmode mode) + { +#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) + return std::basic_filebuf::open(p.wstring().c_str(), mode) ? this : 0; +#else + return std::basic_filebuf::open(p.string().c_str(), mode) ? this : 0; +#endif + } +}; + +template > +class basic_ifstream : public std::basic_ifstream +{ +public: + basic_ifstream() {} +#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) + explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) + : std::basic_ifstream(p.wstring().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream::open(p.wstring().c_str(), mode); } +#else + explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) + : std::basic_ifstream(p.string().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream::open(p.string().c_str(), mode); } +#endif + basic_ifstream(const basic_ifstream&) = delete; + const basic_ifstream& operator=(const basic_ifstream&) = delete; + ~basic_ifstream() override {} +}; + +template > +class basic_ofstream : public std::basic_ofstream +{ +public: + basic_ofstream() {} +#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) + explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) + : std::basic_ofstream(p.wstring().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream::open(p.wstring().c_str(), mode); } +#else + explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) + : std::basic_ofstream(p.string().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream::open(p.string().c_str(), mode); } +#endif + basic_ofstream(const basic_ofstream&) = delete; + const basic_ofstream& operator=(const basic_ofstream&) = delete; + ~basic_ofstream() override {} +}; + +template > +class basic_fstream : public std::basic_fstream +{ +public: + basic_fstream() {} +#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) + explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + : std::basic_fstream(p.wstring().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream::open(p.wstring().c_str(), mode); } +#else + explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + : std::basic_fstream(p.string().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream::open(p.string().c_str(), mode); } +#endif + basic_fstream(const basic_fstream&) = delete; + const basic_fstream& operator=(const basic_fstream&) = delete; + ~basic_fstream() override {} +}; + +typedef basic_filebuf filebuf; +typedef basic_filebuf wfilebuf; +typedef basic_ifstream ifstream; +typedef basic_ifstream wifstream; +typedef basic_ofstream ofstream; +typedef basic_ofstream wofstream; +typedef basic_fstream fstream; +typedef basic_fstream wfstream; + +class GHC_FS_API_CLASS u8arguments +{ +public: + u8arguments(int& argc, char**& argv); + ~u8arguments() + { + _refargc = _argc; + _refargv = _argv; + } + + bool valid() const { return _isvalid; } + +private: + int _argc; + char** _argv; + int& _refargc; + char**& _refargv; + bool _isvalid; +#ifdef GHC_OS_WINDOWS + std::vector _args; + std::vector _argp; +#endif +}; + +//------------------------------------------------------------------------------------------------- +// Implementation +//------------------------------------------------------------------------------------------------- + +namespace detail { +enum utf8_states_t { S_STRT = 0, S_RJCT = 8 }; +GHC_FS_API void appendUTF8(std::string& str, uint32_t unicode); +GHC_FS_API bool is_surrogate(uint32_t c); +GHC_FS_API bool is_high_surrogate(uint32_t c); +GHC_FS_API bool is_low_surrogate(uint32_t c); +GHC_FS_API unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint); +enum class portable_error { + none = 0, + exists, + not_found, + not_supported, + not_implemented, + invalid_argument, + is_a_directory, +}; +GHC_FS_API std::error_code make_error_code(portable_error err); +#ifdef GHC_OS_WINDOWS +GHC_FS_API std::error_code make_system_error(uint32_t err = 0); +#else +GHC_FS_API std::error_code make_system_error(int err = 0); +#endif +} // namespace detail + +namespace detail { + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE std::error_code make_error_code(portable_error err) +{ +#ifdef GHC_OS_WINDOWS + switch (err) { + case portable_error::none: + return std::error_code(); + case portable_error::exists: + return std::error_code(ERROR_ALREADY_EXISTS, std::system_category()); + case portable_error::not_found: + return std::error_code(ERROR_PATH_NOT_FOUND, std::system_category()); + case portable_error::not_supported: + return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); + case portable_error::not_implemented: + return std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category()); + case portable_error::invalid_argument: + return std::error_code(ERROR_INVALID_PARAMETER, std::system_category()); + case portable_error::is_a_directory: +#ifdef ERROR_DIRECTORY_NOT_SUPPORTED + return std::error_code(ERROR_DIRECTORY_NOT_SUPPORTED, std::system_category()); +#else + return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); +#endif + } +#else + switch (err) { + case portable_error::none: + return std::error_code(); + case portable_error::exists: + return std::error_code(EEXIST, std::system_category()); + case portable_error::not_found: + return std::error_code(ENOENT, std::system_category()); + case portable_error::not_supported: + return std::error_code(ENOTSUP, std::system_category()); + case portable_error::not_implemented: + return std::error_code(ENOSYS, std::system_category()); + case portable_error::invalid_argument: + return std::error_code(EINVAL, std::system_category()); + case portable_error::is_a_directory: + return std::error_code(EISDIR, std::system_category()); + } +#endif + return std::error_code(); +} + +#ifdef GHC_OS_WINDOWS +GHC_INLINE std::error_code make_system_error(uint32_t err) +{ + return std::error_code(err ? static_cast(err) : static_cast(::GetLastError()), std::system_category()); +} +#else +GHC_INLINE std::error_code make_system_error(int err) +{ + return std::error_code(err ? err : errno, std::system_category()); +} +#endif + +#endif // GHC_EXPAND_IMPL + +template +using EnableBitmask = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, Enum>::type; +} // namespace detail + +template +constexpr detail::EnableBitmask operator&(Enum X, Enum Y) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(X) & static_cast(Y)); +} + +template +constexpr detail::EnableBitmask operator|(Enum X, Enum Y) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(X) | static_cast(Y)); +} + +template +constexpr detail::EnableBitmask operator^(Enum X, Enum Y) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(X) ^ static_cast(Y)); +} + +template +constexpr detail::EnableBitmask operator~(Enum X) +{ + using underlying = typename std::underlying_type::type; + return static_cast(~static_cast(X)); +} + +template +detail::EnableBitmask& operator&=(Enum& X, Enum Y) +{ + X = X & Y; + return X; +} + +template +detail::EnableBitmask& operator|=(Enum& X, Enum Y) +{ + X = X | Y; + return X; +} + +template +detail::EnableBitmask& operator^=(Enum& X, Enum Y) +{ + X = X ^ Y; + return X; +} + +#ifdef GHC_EXPAND_IMPL + +namespace detail { + +GHC_INLINE bool in_range(uint32_t c, uint32_t lo, uint32_t hi) +{ + return (static_cast(c - lo) < (hi - lo + 1)); +} + +GHC_INLINE bool is_surrogate(uint32_t c) +{ + return in_range(c, 0xd800, 0xdfff); +} + +GHC_INLINE bool is_high_surrogate(uint32_t c) +{ + return (c & 0xfffffc00) == 0xd800; +} + +GHC_INLINE bool is_low_surrogate(uint32_t c) +{ + return (c & 0xfffffc00) == 0xdc00; +} + +GHC_INLINE void appendUTF8(std::string& str, uint32_t unicode) +{ + if (unicode <= 0x7f) { + str.push_back(static_cast(unicode)); + } + else if (unicode >= 0x80 && unicode <= 0x7ff) { + str.push_back(static_cast((unicode >> 6) + 192)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else if ((unicode >= 0x800 && unicode <= 0xd7ff) || (unicode >= 0xe000 && unicode <= 0xffff)) { + str.push_back(static_cast((unicode >> 12) + 224)); + str.push_back(static_cast(((unicode & 0xfff) >> 6) + 128)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else if (unicode >= 0x10000 && unicode <= 0x10ffff) { + str.push_back(static_cast((unicode >> 18) + 240)); + str.push_back(static_cast(((unicode & 0x3ffff) >> 12) + 128)); + str.push_back(static_cast(((unicode & 0xfff) >> 6) + 128)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal code point for unicode character.", str, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + appendUTF8(str, 0xfffd); +#endif + } +} + +// Thanks to Bjoern Hoehrmann (https://bjoern.hoehrmann.de/utf-8/decoder/dfa/) +// and Taylor R Campbell for the ideas to this DFA approach of UTF-8 decoding; +// Generating debugging and shrinking my own DFA from scratch was a day of fun! +GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint) +{ + static const uint32_t utf8_state_info[] = { + // encoded states + 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, 0x99999999u, + 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, 0u, 0u, + }; + uint8_t category = fragment < 128 ? 0 : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf; + codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) : (0xffu >> category) & fragment); + return state == S_RJCT ? static_cast(S_RJCT) : static_cast((utf8_state_info[category + 16] >> (state << 2)) & 0xf); +} + +GHC_INLINE bool validUtf8(const std::string& utf8String) +{ + std::string::const_iterator iter = utf8String.begin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.end()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_RJCT) { + return false; + } + } + if (utf8_state) { + return false; + } + return true; +} + +} // namespace detail + +#endif + +namespace detail { + +template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 1)>::type* = nullptr> +inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + return StringType(utf8String.begin(), utf8String.end(), alloc); +} + +template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 2)>::type* = nullptr> +inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + StringType result(alloc); + result.reserve(utf8String.length()); + auto iter = utf8String.cbegin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.cend()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_STRT) { + if (codepoint <= 0xffff) { + result += static_cast(codepoint); + } + else { + codepoint -= 0x10000; + result += static_cast((codepoint >> 10) + 0xd800); + result += static_cast((codepoint & 0x3ff) + 0xdc00); + } + codepoint = 0; + } + else if (utf8_state == S_RJCT) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); + utf8_state = S_STRT; + codepoint = 0; +#endif + } + } + if (utf8_state) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); +#endif + } + return result; +} + +template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 4)>::type* = nullptr> +inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + StringType result(alloc); + result.reserve(utf8String.length()); + auto iter = utf8String.cbegin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.cend()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_STRT) { + result += static_cast(codepoint); + codepoint = 0; + } + else if (utf8_state == S_RJCT) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); + utf8_state = S_STRT; + codepoint = 0; +#endif + } + } + if (utf8_state) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); +#endif + } + return result; +} + +template +inline StringType fromUtf8(const charT (&utf8String)[N]) +{ +#ifdef GHC_WITH_STRING_VIEW + return fromUtf8(basic_string_view(utf8String, N - 1)); +#else + return fromUtf8(std::basic_string(utf8String, N - 1)); +#endif +} + +template ::value && (sizeof(typename strT::value_type) == 1), int>::type size = 1> +inline std::string toUtf8(const strT& unicodeString) +{ + return std::string(unicodeString.begin(), unicodeString.end()); +} + +template ::value && (sizeof(typename strT::value_type) == 2), int>::type size = 2> +inline std::string toUtf8(const strT& unicodeString) +{ + std::string result; + for (auto iter = unicodeString.begin(); iter != unicodeString.end(); ++iter) { + char32_t c = *iter; + if (is_surrogate(c)) { + ++iter; + if (iter != unicodeString.end() && is_high_surrogate(c) && is_low_surrogate(*iter)) { + appendUTF8(result, (char32_t(c) << 10) + *iter - 0x35fdc00); + } + else { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal code point for unicode character.", result, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + appendUTF8(result, 0xfffd); + if (iter == unicodeString.end()) { + break; + } +#endif + } + } + else { + appendUTF8(result, c); + } + } + return result; +} + +template ::value && (sizeof(typename strT::value_type) == 4), int>::type size = 4> +inline std::string toUtf8(const strT& unicodeString) +{ + std::string result; + for (auto c : unicodeString) { + appendUTF8(result, static_cast(c)); + } + return result; +} + +template +inline std::string toUtf8(const charT* unicodeString) +{ +#ifdef GHC_WITH_STRING_VIEW + return toUtf8(basic_string_view>(unicodeString)); +#else + return toUtf8(std::basic_string>(unicodeString)); +#endif +} + +#ifdef GHC_USE_WCHAR_T +template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 1), bool>::type = false> +inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + auto temp = toUtf8(wString); + return StringType(temp.begin(), temp.end(), alloc); +} + +template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 2), bool>::type = false> +inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + return StringType(wString.begin(), wString.end(), alloc); +} + +template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 4), bool>::type = false> +inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + auto temp = toUtf8(wString); + return fromUtf8(temp, alloc); +} + +template ::value && (sizeof(typename strT::value_type) == 1), bool>::type = false> +inline std::wstring toWChar(const strT& unicodeString) +{ + return fromUtf8(unicodeString); +} + +template ::value && (sizeof(typename strT::value_type) == 2), bool>::type = false> +inline std::wstring toWChar(const strT& unicodeString) +{ + return std::wstring(unicodeString.begin(), unicodeString.end()); +} + +template ::value && (sizeof(typename strT::value_type) == 4), bool>::type = false> +inline std::wstring toWChar(const strT& unicodeString) +{ + auto temp = toUtf8(unicodeString); + return fromUtf8(temp); +} + +template +inline std::wstring toWChar(const charT* unicodeString) +{ +#ifdef GHC_WITH_STRING_VIEW + return toWChar(basic_string_view>(unicodeString)); +#else + return toWChar(std::basic_string>(unicodeString)); +#endif +} +#endif // GHC_USE_WCHAR_T + +} // namespace detail + +#ifdef GHC_EXPAND_IMPL + +namespace detail { + +template ::value, bool>::type = true> +GHC_INLINE bool startsWith(const strT& what, const strT& with) +{ + return with.length() <= what.length() && equal(with.begin(), with.end(), what.begin()); +} + +template ::value, bool>::type = true> +GHC_INLINE bool endsWith(const strT& what, const strT& with) +{ + return with.length() <= what.length() && what.compare(what.length() - with.length(), with.size(), with) == 0; +} + +} // namespace detail + +GHC_INLINE void path::check_long_path() +{ +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) { + postprocess_path_with_format(native_format); + } +#endif +} + +GHC_INLINE void path::postprocess_path_with_format(path::format fmt) +{ +#ifdef GHC_RAISE_UNICODE_ERRORS + if (!detail::validUtf8(_path)) { + path t; + t._path = _path; + throw filesystem_error("Illegal byte sequence for unicode character.", t, std::make_error_code(std::errc::illegal_byte_sequence)); + } +#endif + switch (fmt) { +#ifdef GHC_OS_WINDOWS + case path::native_format: + case path::auto_format: + case path::generic_format: + for (auto& c : _path) { + if (c == generic_separator) { + c = preferred_separator; + } + } +#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH + if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) { + _path = GHC_PLATFORM_LITERAL("\\\\?\\") + _path; + } +#endif + handle_prefixes(); + break; +#else + case path::auto_format: + case path::native_format: + case path::generic_format: + // nothing to do + break; +#endif + } + if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator) { + impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast(_prefixLength) + 2, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); + _path.erase(new_end, _path.end()); + } + else { + impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast(_prefixLength), _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); + _path.erase(new_end, _path.end()); + } +} + +#endif // GHC_EXPAND_IMPL + +template +inline path::path(const Source& source, format fmt) +#ifdef GHC_USE_WCHAR_T + : _path(detail::toWChar(source)) +#else + : _path(detail::toUtf8(source)) +#endif +{ + postprocess_path_with_format(fmt); +} + +template +inline path u8path(const Source& source) +{ + return path(source); +} +template +inline path u8path(InputIterator first, InputIterator last) +{ + return path(first, last); +} + +template +inline path::path(InputIterator first, InputIterator last, format fmt) + : path(std::basic_string::value_type>(first, last), fmt) +{ + // delegated +} + +#ifdef GHC_EXPAND_IMPL + +namespace detail { + +GHC_INLINE bool equals_simple_insensitive(const path::value_type* str1, const path::value_type* str2) +{ +#ifdef GHC_OS_WINDOWS +#ifdef __GNUC__ + while (::tolower((unsigned char)*str1) == ::tolower((unsigned char)*str2++)) { + if (*str1++ == 0) + return true; + } + return false; +#else // __GNUC__ +#ifdef GHC_USE_WCHAR_T + return 0 == ::_wcsicmp(str1, str2); +#else // GHC_USE_WCHAR_T + return 0 == ::_stricmp(str1, str2); +#endif // GHC_USE_WCHAR_T +#endif // __GNUC__ +#else // GHC_OS_WINDOWS + return 0 == ::strcasecmp(str1, str2); +#endif // GHC_OS_WINDOWS +} + +GHC_INLINE int compare_simple_insensitive(const path::value_type* str1, size_t len1, const path::value_type* str2, size_t len2) +{ + while (len1 > 0 && len2 > 0 && ::tolower(static_cast(*str1)) == ::tolower(static_cast(*str2))) { + --len1; + --len2; + ++str1; + ++str2; + } + if (len1 && len2) { + return *str1 < *str2 ? -1 : 1; + } + if (len1 == 0 && len2 == 0) { + return 0; + } + return len1 == 0 ? -1 : 1; +} + +GHC_INLINE const char* strerror_adapter(char* gnu, char*) +{ + return gnu; +} + +GHC_INLINE const char* strerror_adapter(int posix, char* buffer) +{ + if (posix) { + return "Error in strerror_r!"; + } + return buffer; +} + +template +GHC_INLINE std::string systemErrorText(ErrorNumber code = 0) +{ +#if defined(GHC_OS_WINDOWS) + LPVOID msgBuf; + DWORD dw = code ? static_cast(code) : ::GetLastError(); + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msgBuf, 0, NULL); + std::string msg = toUtf8(std::wstring((LPWSTR)msgBuf)); + LocalFree(msgBuf); + return msg; +#else + char buffer[512]; + return strerror_adapter(strerror_r(code ? code : errno, buffer, sizeof(buffer)), buffer); +#endif +} + +#ifdef GHC_OS_WINDOWS +using CreateSymbolicLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, DWORD); +using CreateHardLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); + +GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool to_directory, std::error_code& ec) +{ + std::error_code tec; + auto fs = status(target_name, tec); + if ((fs.type() == file_type::directory && !to_directory) || (fs.type() == file_type::regular && to_directory)) { + ec = detail::make_error_code(detail::portable_error::not_supported); + return; + } +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + static CreateSymbolicLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW")); +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif + if (api_call) { + if (api_call(GHC_NATIVEWP(new_symlink), GHC_NATIVEWP(target_name), to_directory ? 1 : 0) == 0) { + auto result = ::GetLastError(); + if (result == ERROR_PRIVILEGE_NOT_HELD && api_call(GHC_NATIVEWP(new_symlink), GHC_NATIVEWP(target_name), to_directory ? 3 : 2) != 0) { + return; + } + ec = detail::make_system_error(result); + } + } + else { + ec = detail::make_system_error(ERROR_NOT_SUPPORTED); + } +} + +GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec) +{ +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + static CreateHardLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW")); +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif + if (api_call) { + if (api_call(GHC_NATIVEWP(new_hardlink), GHC_NATIVEWP(target_name), NULL) == 0) { + ec = detail::make_system_error(); + } + } + else { + ec = detail::make_system_error(ERROR_NOT_SUPPORTED); + } +} + +GHC_INLINE path getFullPathName(const wchar_t* p, std::error_code& ec) +{ + ULONG size = ::GetFullPathNameW(p, 0, 0, 0); + if (size) { + std::vector buf(size, 0); + ULONG s2 = GetFullPathNameW(p, size, buf.data(), nullptr); + if (s2 && s2 < size) { + return path(std::wstring(buf.data(), s2)); + } + } + ec = detail::make_system_error(); + return path(); +} + +#else +GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool, std::error_code& ec) +{ + if (::symlink(target_name.c_str(), new_symlink.c_str()) != 0) { + ec = detail::make_system_error(); + } +} + +#ifndef GHC_OS_WEB +GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec) +{ + if (::link(target_name.c_str(), new_hardlink.c_str()) != 0) { + ec = detail::make_system_error(); + } +} +#endif +#endif + +template +GHC_INLINE file_status file_status_from_st_mode(T mode) +{ +#ifdef GHC_OS_WINDOWS + file_type ft = file_type::unknown; + if ((mode & _S_IFDIR) == _S_IFDIR) { + ft = file_type::directory; + } + else if ((mode & _S_IFREG) == _S_IFREG) { + ft = file_type::regular; + } + else if ((mode & _S_IFCHR) == _S_IFCHR) { + ft = file_type::character; + } + perms prms = static_cast(mode & 0xfff); + return file_status(ft, prms); +#else + file_type ft = file_type::unknown; + if (S_ISDIR(mode)) { + ft = file_type::directory; + } + else if (S_ISREG(mode)) { + ft = file_type::regular; + } + else if (S_ISCHR(mode)) { + ft = file_type::character; + } + else if (S_ISBLK(mode)) { + ft = file_type::block; + } + else if (S_ISFIFO(mode)) { + ft = file_type::fifo; + } + else if (S_ISLNK(mode)) { + ft = file_type::symlink; + } + else if (S_ISSOCK(mode)) { + ft = file_type::socket; + } + perms prms = static_cast(mode & 0xfff); + return file_status(ft, prms); +#endif +} + +#ifdef GHC_OS_WINDOWS + +class unique_handle +{ +public: + typedef HANDLE element_type; + + unique_handle() noexcept + : _handle(INVALID_HANDLE_VALUE) + { + } + explicit unique_handle(element_type h) noexcept + : _handle(h) + { + } + unique_handle(unique_handle&& u) noexcept + : _handle(u.release()) + { + } + ~unique_handle() { reset(); } + unique_handle& operator=(unique_handle&& u) noexcept + { + reset(u.release()); + return *this; + } + element_type get() const noexcept { return _handle; } + explicit operator bool() const noexcept { return _handle != INVALID_HANDLE_VALUE; } + element_type release() noexcept + { + element_type tmp = _handle; + _handle = INVALID_HANDLE_VALUE; + return tmp; + } + void reset(element_type h = INVALID_HANDLE_VALUE) noexcept + { + element_type tmp = _handle; + _handle = h; + if (tmp != INVALID_HANDLE_VALUE) { + CloseHandle(tmp); + } + } + void swap(unique_handle& u) noexcept { std::swap(_handle, u._handle); } + +private: + element_type _handle; +}; + +#ifndef REPARSE_DATA_BUFFER_HEADER_SIZE +typedef struct _REPARSE_DATA_BUFFER +{ + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union + { + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct + { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; +} REPARSE_DATA_BUFFER; +#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE +#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024) +#endif +#endif + +template +struct free_deleter +{ + void operator()(T* p) const { std::free(p); } +}; + +GHC_INLINE std::unique_ptr> getReparseData(const path& p, std::error_code& ec) +{ + unique_handle file(CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0)); + if (!file) { + ec = detail::make_system_error(); + return nullptr; + } + + std::unique_ptr> reparseData(reinterpret_cast(std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE))); + ULONG bufferUsed; + if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) { + return reparseData; + } + else { + ec = detail::make_system_error(); + } + return nullptr; +} +#endif + +GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) +{ +#ifdef GHC_OS_WINDOWS + path result; + auto reparseData = detail::getReparseData(p, ec); + if (!ec) { + if (reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag)) { + switch (reparseData->ReparseTag) { + case IO_REPARSE_TAG_SYMLINK: { + auto printName = std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR)); + auto substituteName = + std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); + if (detail::endsWith(substituteName, printName) && detail::startsWith(substituteName, std::wstring(L"\\??\\"))) { + result = printName; + } + else { + result = substituteName; + } + if (reparseData->SymbolicLinkReparseBuffer.Flags & 0x1 /*SYMLINK_FLAG_RELATIVE*/) { + result = p.parent_path() / result; + } + break; + } + case IO_REPARSE_TAG_MOUNT_POINT: + result = detail::getFullPathName(GHC_NATIVEWP(p), ec); + // result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); + break; + default: + break; + } + } + } + return result; +#else + size_t bufferSize = 256; + while (true) { + std::vector buffer(bufferSize, static_cast(0)); + auto rc = ::readlink(p.c_str(), buffer.data(), buffer.size()); + if (rc < 0) { + ec = detail::make_system_error(); + return path(); + } + else if (rc < static_cast(bufferSize)) { + return path(std::string(buffer.data(), static_cast(rc))); + } + bufferSize *= 2; + } + return path(); +#endif +} + +#ifdef GHC_OS_WINDOWS +GHC_INLINE time_t timeFromFILETIME(const FILETIME& ft) +{ + ULARGE_INTEGER ull; + ull.LowPart = ft.dwLowDateTime; + ull.HighPart = ft.dwHighDateTime; + return static_cast(ull.QuadPart / 10000000ULL - 11644473600ULL); +} + +GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft) +{ + LONGLONG ll; + ll = Int32x32To64(t, 10000000) + 116444736000000000; + ft.dwLowDateTime = static_cast(ll); + ft.dwHighDateTime = static_cast(ll >> 32); +} + +template +GHC_INLINE uintmax_t hard_links_from_INFO(const INFO* info) +{ + return static_cast(-1); +} + +template <> +GHC_INLINE uintmax_t hard_links_from_INFO(const BY_HANDLE_FILE_INFORMATION* info) +{ + return info->nNumberOfLinks; +} + +template +GHC_INLINE DWORD reparse_tag_from_INFO(const INFO*) +{ + return 0; +} + +template <> +GHC_INLINE DWORD reparse_tag_from_INFO(const WIN32_FIND_DATAW* info) +{ + return info->dwReserved0; +} + +template +GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code& ec, uintmax_t* sz = nullptr, time_t* lwt = nullptr) +{ + file_type ft = file_type::unknown; + if (sizeof(INFO) == sizeof(WIN32_FIND_DATAW)) { + if (detail::reparse_tag_from_INFO(info) == IO_REPARSE_TAG_SYMLINK) { + ft = file_type::symlink; + } + } + else { + if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + auto reparseData = detail::getReparseData(p, ec); + if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + ft = file_type::symlink; + } + } + } + if (ft == file_type::unknown) { + if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + ft = file_type::directory; + } + else { + ft = file_type::regular; + } + } + perms prms = perms::owner_read | perms::group_read | perms::others_read; + if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { + prms = prms | perms::owner_write | perms::group_write | perms::others_write; + } + if (has_executable_extension(p)) { + prms = prms | perms::owner_exec | perms::group_exec | perms::others_exec; + } + if (sz) { + *sz = static_cast(info->nFileSizeHigh) << (sizeof(info->nFileSizeHigh) * 8) | info->nFileSizeLow; + } + if (lwt) { + *lwt = detail::timeFromFILETIME(info->ftLastWriteTime); + } + return file_status(ft, prms); +} + +#endif + +GHC_INLINE bool is_not_found_error(std::error_code& ec) +{ +#ifdef GHC_OS_WINDOWS + return ec.value() == ERROR_FILE_NOT_FOUND || ec.value() == ERROR_PATH_NOT_FOUND || ec.value() == ERROR_INVALID_NAME; +#else + return ec.value() == ENOENT || ec.value() == ENOTDIR; +#endif +} + +GHC_INLINE file_status symlink_status_ex(const path& p, std::error_code& ec, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr) noexcept +{ +#ifdef GHC_OS_WINDOWS + file_status fs; + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { + ec = detail::make_system_error(); + } + else { + ec.clear(); + fs = detail::status_from_INFO(p, &attr, ec, sz, lwt); + if (nhl) { + *nhl = 0; + } + } + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found); + } + return ec ? file_status(file_type::none) : fs; +#else + (void)sz; + (void)nhl; + (void)lwt; + struct ::stat fs; + auto result = ::lstat(p.c_str(), &fs); + if (result == 0) { + ec.clear(); + file_status f_s = detail::file_status_from_st_mode(fs.st_mode); + return f_s; + } + ec = detail::make_system_error(); + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found, perms::unknown); + } + return file_status(file_type::none); +#endif +} + +GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status* sls = nullptr, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr, int recurse_count = 0) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (recurse_count > 16) { + ec = detail::make_system_error(0x2A9 /*ERROR_STOPPED_ON_SYMLINK*/); + return file_status(file_type::unknown); + } + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!::GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { + ec = detail::make_system_error(); + } + else if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + auto reparseData = detail::getReparseData(p, ec); + if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + path target = resolveSymlink(p, ec); + file_status result; + if (!ec && !target.empty()) { + if (sls) { + *sls = status_from_INFO(p, &attr, ec); + } + return detail::status_ex(target, ec, nullptr, sz, nhl, lwt, recurse_count + 1); + } + return file_status(file_type::unknown); + } + } + if (ec) { + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found); + } + return file_status(file_type::none); + } + if (nhl) { + *nhl = 0; + } + return detail::status_from_INFO(p, &attr, ec, sz, lwt); +#else + (void)recurse_count; + struct ::stat st; + auto result = ::lstat(p.c_str(), &st); + if (result == 0) { + ec.clear(); + file_status fs = detail::file_status_from_st_mode(st.st_mode); + if (sls) { + *sls = fs; + } + if (fs.type() == file_type::symlink) { + result = ::stat(p.c_str(), &st); + if (result == 0) { + fs = detail::file_status_from_st_mode(st.st_mode); + } + else { + ec = detail::make_system_error(); + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found, perms::unknown); + } + return file_status(file_type::none); + } + } + if (sz) { + *sz = static_cast(st.st_size); + } + if (nhl) { + *nhl = st.st_nlink; + } + if (lwt) { + *lwt = st.st_mtime; + } + return fs; + } + else { + ec = detail::make_system_error(); + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found, perms::unknown); + } + return file_status(file_type::none); + } +#endif +} + +} // namespace detail + +GHC_INLINE u8arguments::u8arguments(int& argc, char**& argv) + : _argc(argc) + , _argv(argv) + , _refargc(argc) + , _refargv(argv) + , _isvalid(false) +{ +#ifdef GHC_OS_WINDOWS + LPWSTR* p; + p = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + _args.reserve(static_cast(argc)); + _argp.reserve(static_cast(argc)); + for (size_t i = 0; i < static_cast(argc); ++i) { + _args.push_back(detail::toUtf8(std::wstring(p[i]))); + _argp.push_back((char*)_args[i].data()); + } + argv = _argp.data(); + ::LocalFree(p); + _isvalid = true; +#else + std::setlocale(LC_ALL, ""); +#if defined(__ANDROID__) && __ANDROID_API__ < 26 + _isvalid = true; +#else + if (detail::equals_simple_insensitive(::nl_langinfo(CODESET), "UTF-8")) { + _isvalid = true; + } +#endif +#endif +} + +//----------------------------------------------------------------------------- +// [fs.path.construct] constructors and destructor + +GHC_INLINE path::path() noexcept {} + +GHC_INLINE path::path(const path& p) + : _path(p._path) +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + , _prefixLength(p._prefixLength) +#endif +{ +} + +GHC_INLINE path::path(path&& p) noexcept + : _path(std::move(p._path)) +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + , _prefixLength(p._prefixLength) +#endif +{ +} + +GHC_INLINE path::path(string_type&& source, format fmt) + : _path(std::move(source)) +{ + postprocess_path_with_format(fmt); +} + +#endif // GHC_EXPAND_IMPL + +#ifdef GHC_WITH_EXCEPTIONS +template +inline path::path(const Source& source, const std::locale& loc, format fmt) + : path(source, fmt) +{ + std::string locName = loc.name(); + if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) { + throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported)); + } +} + +template +inline path::path(InputIterator first, InputIterator last, const std::locale& loc, format fmt) + : path(std::basic_string::value_type>(first, last), fmt) +{ + std::string locName = loc.name(); + if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) { + throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported)); + } +} +#endif + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE path::~path() {} + +//----------------------------------------------------------------------------- +// [fs.path.assign] assignments + +GHC_INLINE path& path::operator=(const path& p) +{ + _path = p._path; +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = p._prefixLength; +#endif + return *this; +} + +GHC_INLINE path& path::operator=(path&& p) noexcept +{ + _path = std::move(p._path); +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = p._prefixLength; +#endif + return *this; +} + +GHC_INLINE path& path::operator=(path::string_type&& source) +{ + return assign(source); +} + +GHC_INLINE path& path::assign(path::string_type&& source) +{ + _path = std::move(source); + postprocess_path_with_format(native_format); + return *this; +} + +#endif // GHC_EXPAND_IMPL + +template +inline path& path::operator=(const Source& source) +{ + return assign(source); +} + +template +inline path& path::assign(const Source& source) +{ +#ifdef GHC_USE_WCHAR_T + _path.assign(detail::toWChar(source)); +#else + _path.assign(detail::toUtf8(source)); +#endif + postprocess_path_with_format(native_format); + return *this; +} + +template <> +inline path& path::assign(const path& source) +{ + _path = source._path; +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = source._prefixLength; +#endif + return *this; +} + +template +inline path& path::assign(InputIterator first, InputIterator last) +{ + _path.assign(first, last); + postprocess_path_with_format(native_format); + return *this; +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.append] appends + +GHC_INLINE path& path::operator/=(const path& p) +{ + if (p.empty()) { + // was: if ((!has_root_directory() && is_absolute()) || has_filename()) + if (!_path.empty() && _path[_path.length() - 1] != preferred_separator && _path[_path.length() - 1] != ':') { + _path += preferred_separator; + } + return *this; + } + if ((p.is_absolute() && (_path != root_name()._path || p._path != "/")) || (p.has_root_name() && p.root_name() != root_name())) { + assign(p); + return *this; + } + if (p.has_root_directory()) { + assign(root_name()); + } + else if ((!has_root_directory() && is_absolute()) || has_filename()) { + _path += preferred_separator; + } + auto iter = p.begin(); + bool first = true; + if (p.has_root_name()) { + ++iter; + } + while (iter != p.end()) { + if (!first && !(!_path.empty() && _path[_path.length() - 1] == preferred_separator)) { + _path += preferred_separator; + } + first = false; + _path += (*iter++).native(); + } + check_long_path(); + return *this; +} + +GHC_INLINE void path::append_name(const value_type* name) +{ + if (_path.empty()) { + this->operator/=(path(name)); + } + else { + if (_path.back() != path::preferred_separator) { + _path.push_back(path::preferred_separator); + } + _path += name; + check_long_path(); + } +} + +#endif // GHC_EXPAND_IMPL + +template +inline path& path::operator/=(const Source& source) +{ + return append(source); +} + +template +inline path& path::append(const Source& source) +{ + return this->operator/=(path(source)); +} + +template <> +inline path& path::append(const path& p) +{ + return this->operator/=(p); +} + +template +inline path& path::append(InputIterator first, InputIterator last) +{ + std::basic_string::value_type> part(first, last); + return append(part); +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.concat] concatenation + +GHC_INLINE path& path::operator+=(const path& x) +{ + return concat(x._path); +} + +GHC_INLINE path& path::operator+=(const string_type& x) +{ + return concat(x); +} + +#ifdef GHC_WITH_STRING_VIEW +GHC_INLINE path& path::operator+=(basic_string_view x) +{ + return concat(x); +} +#endif + +GHC_INLINE path& path::operator+=(const value_type* x) +{ +#ifdef GHC_WITH_STRING_VIEW + basic_string_view part(x); +#else + string_type part(x); +#endif + return concat(part); +} + +GHC_INLINE path& path::operator+=(value_type x) +{ +#ifdef GHC_OS_WINDOWS + if (x == generic_separator) { + x = preferred_separator; + } +#endif + if (_path.empty() || _path.back() != preferred_separator) { + _path += x; + } + check_long_path(); + return *this; +} + +#endif // GHC_EXPAND_IMPL + +template +inline path::path_from_string& path::operator+=(const Source& x) +{ + return concat(x); +} + +template +inline path::path_type_EcharT& path::operator+=(EcharT x) +{ +#ifdef GHC_WITH_STRING_VIEW + basic_string_view part(&x, 1); +#else + std::basic_string part(1, x); +#endif + concat(part); + return *this; +} + +template +inline path& path::concat(const Source& x) +{ + path p(x); + _path += p._path; + postprocess_path_with_format(native_format); + return *this; +} +template +inline path& path::concat(InputIterator first, InputIterator last) +{ + _path.append(first, last); + postprocess_path_with_format(native_format); + return *this; +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.modifiers] modifiers +GHC_INLINE void path::clear() noexcept +{ + _path.clear(); +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = 0; +#endif +} + +GHC_INLINE path& path::make_preferred() +{ + // as this filesystem implementation only uses generic_format + // internally, this must be a no-op + return *this; +} + +GHC_INLINE path& path::remove_filename() +{ + if (has_filename()) { + _path.erase(_path.size() - filename()._path.size()); + } + return *this; +} + +GHC_INLINE path& path::replace_filename(const path& replacement) +{ + remove_filename(); + return append(replacement); +} + +GHC_INLINE path& path::replace_extension(const path& replacement) +{ + if (has_extension()) { + _path.erase(_path.size() - extension()._path.size()); + } + if (!replacement.empty() && replacement._path[0] != '.') { + _path += '.'; + } + return concat(replacement); +} + +GHC_INLINE void path::swap(path& rhs) noexcept +{ + _path.swap(rhs._path); +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + std::swap(_prefixLength, rhs._prefixLength); +#endif +} + +//----------------------------------------------------------------------------- +// [fs.path.native.obs] native format observers +GHC_INLINE const path::string_type& path::native() const noexcept +{ + return _path; +} + +GHC_INLINE const path::value_type* path::c_str() const noexcept +{ + return native().c_str(); +} + +GHC_INLINE path::operator path::string_type() const +{ + return native(); +} + +#endif // GHC_EXPAND_IMPL + +template +inline std::basic_string path::string(const Allocator& a) const +{ +#ifdef GHC_USE_WCHAR_T + return detail::fromWChar>(_path, a); +#else + return detail::fromUtf8>(_path, a); +#endif +} + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE std::string path::string() const +{ +#ifdef GHC_USE_WCHAR_T + return detail::toUtf8(native()); +#else + return native(); +#endif +} + +GHC_INLINE std::wstring path::wstring() const +{ +#ifdef GHC_USE_WCHAR_T + return native(); +#else + return detail::fromUtf8(native()); +#endif +} + +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +GHC_INLINE std::u8string path::u8string() const +{ +#ifdef GHC_USE_WCHAR_T + return std::u8string(reinterpret_cast(detail::toUtf8(native()).c_str())); +#else + return std::u8string(reinterpret_cast(c_str())); +#endif +} +#else +GHC_INLINE std::string path::u8string() const +{ +#ifdef GHC_USE_WCHAR_T + return detail::toUtf8(native()); +#else + return native(); +#endif +} +#endif + +GHC_INLINE std::u16string path::u16string() const +{ + // TODO: optimize + return detail::fromUtf8(string()); +} + +GHC_INLINE std::u32string path::u32string() const +{ + // TODO: optimize + return detail::fromUtf8(string()); +} + +#endif // GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.generic.obs] generic format observers +template +inline std::basic_string path::generic_string(const Allocator& a) const +{ +#ifdef GHC_OS_WINDOWS +#ifdef GHC_USE_WCHAR_T + auto result = detail::fromWChar, path::string_type>(_path, a); +#else + auto result = detail::fromUtf8>(_path, a); +#endif + for (auto& c : result) { + if (c == preferred_separator) { + c = generic_separator; + } + } + return result; +#else + return detail::fromUtf8>(_path, a); +#endif +} + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE std::string path::generic_string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return _path; +#endif +} + +GHC_INLINE std::wstring path::generic_wstring() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return detail::fromUtf8(_path); +#endif +} // namespace filesystem + +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +GHC_INLINE std::u8string path::generic_u8string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return std::u8string(reinterpret_cast(_path.c_str())); +#endif +} +#else +GHC_INLINE std::string path::generic_u8string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return _path; +#endif +} +#endif + +GHC_INLINE std::u16string path::generic_u16string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return detail::fromUtf8(_path); +#endif +} + +GHC_INLINE std::u32string path::generic_u32string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return detail::fromUtf8(_path); +#endif +} + +//----------------------------------------------------------------------------- +// [fs.path.compare] compare +GHC_INLINE int path::compare(const path& p) const noexcept +{ +#ifdef LWG_2936_BEHAVIOUR + auto rnl1 = root_name_length(); + auto rnl2 = p.root_name_length(); +#ifdef GHC_OS_WINDOWS + auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2); +#else + auto rnc = _path.compare(0, rnl1, p._path, 0, (std::min(rnl1, rnl2))); +#endif + if (rnc) { + return rnc; + } + bool hrd1 = has_root_directory(), hrd2 = p.has_root_directory(); + if (hrd1 != hrd2) { + return hrd1 ? 1 : -1; + } + if (hrd1) { + ++rnl1; + ++rnl2; + } + auto iter1 = _path.begin() + static_cast(rnl1); + auto iter2 = p._path.begin() + static_cast(rnl2); + while (iter1 != _path.end() && iter2 != p._path.end() && *iter1 == *iter2) { + ++iter1; + ++iter2; + } + if (iter1 == _path.end()) { + return iter2 == p._path.end() ? 0 : -1; + } + if (iter2 == p._path.end()) { + return 1; + } + if (*iter1 == preferred_separator) { + return -1; + } + if (*iter2 == preferred_separator) { + return 1; + } + return *iter1 < *iter2 ? -1 : 1; +#else // LWG_2936_BEHAVIOUR +#ifdef GHC_OS_WINDOWS + auto rnl1 = root_name_length(); + auto rnl2 = p.root_name_length(); + auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2); + if (rnc) { + return rnc; + } + return _path.compare(rnl1, std::string::npos, p._path, rnl2, std::string::npos); +#else + return _path.compare(p._path); +#endif +#endif +} + +GHC_INLINE int path::compare(const string_type& s) const +{ + return compare(path(s)); +} + +#ifdef GHC_WITH_STRING_VIEW +GHC_INLINE int path::compare(basic_string_view s) const +{ + return compare(path(s)); +} +#endif + +GHC_INLINE int path::compare(const value_type* s) const +{ + return compare(path(s)); +} + +//----------------------------------------------------------------------------- +// [fs.path.decompose] decomposition +#ifdef GHC_OS_WINDOWS +GHC_INLINE void path::handle_prefixes() +{ +#if defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = 0; + if (_path.length() >= 6 && _path[2] == '?' && std::toupper(static_cast(_path[4])) >= 'A' && std::toupper(static_cast(_path[4])) <= 'Z' && _path[5] == ':') { + if (detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\"))) || detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\??\\")))) { + _prefixLength = 4; + } + } +#endif // GHC_WIN_AUTO_PREFIX_LONG_PATH +} +#endif + +GHC_INLINE path::string_type::size_type path::root_name_length() const noexcept +{ +#ifdef GHC_OS_WINDOWS + if (_path.length() >= _prefixLength + 2 && std::toupper(static_cast(_path[_prefixLength])) >= 'A' && std::toupper(static_cast(_path[_prefixLength])) <= 'Z' && _path[_prefixLength + 1] == ':') { + return 2; + } +#endif + if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator && std::isprint(_path[_prefixLength + 2])) { + impl_string_type::size_type pos = _path.find(preferred_separator, _prefixLength + 3); + if (pos == impl_string_type::npos) { + return _path.length(); + } + else { + return pos; + } + } + return 0; +} + +GHC_INLINE path path::root_name() const +{ + return path(_path.substr(_prefixLength, root_name_length()), native_format); +} + +GHC_INLINE path path::root_directory() const +{ + if (has_root_directory()) { + static const path _root_dir(std::string(1, preferred_separator), native_format); + return _root_dir; + } + return path(); +} + +GHC_INLINE path path::root_path() const +{ + return path(root_name().string() + root_directory().string(), native_format); +} + +GHC_INLINE path path::relative_path() const +{ + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); + return path(_path.substr((std::min)(rootPathLen, _path.length())), generic_format); +} + +GHC_INLINE path path::parent_path() const +{ + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); + if (rootPathLen < _path.length()) { + if (empty()) { + return path(); + } + else { + auto piter = end(); + auto iter = piter.decrement(_path.end()); + if (iter > _path.begin() + static_cast(rootPathLen) && *iter != preferred_separator) { + --iter; + } + return path(_path.begin(), iter, native_format); + } + } + else { + return *this; + } +} + +GHC_INLINE path path::filename() const +{ + return !has_relative_path() ? path() : path(*--end()); +} + +GHC_INLINE path path::stem() const +{ + impl_string_type fn = filename().native(); + if (fn != "." && fn != "..") { + impl_string_type::size_type pos = fn.rfind('.'); + if (pos != impl_string_type::npos && pos > 0) { + return path{fn.substr(0, pos), native_format}; + } + } + return path{fn, native_format}; +} + +GHC_INLINE path path::extension() const +{ + if (has_relative_path()) { + auto iter = end(); + const auto& fn = *--iter; + impl_string_type::size_type pos = fn._path.rfind('.'); + if (pos != std::string::npos && pos > 0) { + return path(fn._path.substr(pos), native_format); + } + } + return path(); +} + +#ifdef GHC_OS_WINDOWS +namespace detail { +GHC_INLINE bool has_executable_extension(const path& p) +{ + if (p.has_relative_path()) { + auto iter = p.end(); + const auto& fn = *--iter; + auto pos = fn._path.find_last_of('.'); + if (pos == std::string::npos || pos == 0 || fn._path.length() - pos != 3) { + return false; + } + const path::value_type* ext = fn._path.c_str() + pos + 1; + if (detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("exe")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("cmd")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("bat")) || + detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("com"))) { + return true; + } + } + return false; +} +} // namespace detail +#endif + +//----------------------------------------------------------------------------- +// [fs.path.query] query +GHC_INLINE bool path::empty() const noexcept +{ + return _path.empty(); +} + +GHC_INLINE bool path::has_root_name() const +{ + return root_name_length() > 0; +} + +GHC_INLINE bool path::has_root_directory() const +{ + auto rootLen = _prefixLength + root_name_length(); + return (_path.length() > rootLen && _path[rootLen] == preferred_separator); +} + +GHC_INLINE bool path::has_root_path() const +{ + return has_root_name() || has_root_directory(); +} + +GHC_INLINE bool path::has_relative_path() const +{ + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); + return rootPathLen < _path.length(); +} + +GHC_INLINE bool path::has_parent_path() const +{ + return !parent_path().empty(); +} + +GHC_INLINE bool path::has_filename() const +{ + return has_relative_path() && !filename().empty(); +} + +GHC_INLINE bool path::has_stem() const +{ + return !stem().empty(); +} + +GHC_INLINE bool path::has_extension() const +{ + return !extension().empty(); +} + +GHC_INLINE bool path::is_absolute() const +{ +#ifdef GHC_OS_WINDOWS + return has_root_name() && has_root_directory(); +#else + return has_root_directory(); +#endif +} + +GHC_INLINE bool path::is_relative() const +{ + return !is_absolute(); +} + +//----------------------------------------------------------------------------- +// [fs.path.gen] generation +GHC_INLINE path path::lexically_normal() const +{ + path dest; + bool lastDotDot = false; + for (string_type s : *this) { + if (s == ".") { + dest /= ""; + continue; + } + else if (s == ".." && !dest.empty()) { + auto root = root_path(); + if (dest == root) { + continue; + } + else if (*(--dest.end()) != "..") { + if (dest._path.back() == preferred_separator) { + dest._path.pop_back(); + } + dest.remove_filename(); + continue; + } + } + if (!(s.empty() && lastDotDot)) { + dest /= s; + } + lastDotDot = s == ".."; + } + if (dest.empty()) { + dest = "."; + } + return dest; +} + +GHC_INLINE path path::lexically_relative(const path& base) const +{ + if (root_name() != base.root_name() || is_absolute() != base.is_absolute() || (!has_root_directory() && base.has_root_directory())) { + return path(); + } + const_iterator a = begin(), b = base.begin(); + while (a != end() && b != base.end() && *a == *b) { + ++a; + ++b; + } + if (a == end() && b == base.end()) { + return path("."); + } + int count = 0; + for (const auto& element : input_iterator_range(b, base.end())) { + if (element != "." && element != "" && element != "..") { + ++count; + } + else if (element == "..") { + --count; + } + } + if (count < 0) { + return path(); + } + path result; + for (int i = 0; i < count; ++i) { + result /= ".."; + } + for (const auto& element : input_iterator_range(a, end())) { + result /= element; + } + return result; +} + +GHC_INLINE path path::lexically_proximate(const path& base) const +{ + path result = lexically_relative(base); + return result.empty() ? *this : result; +} + +//----------------------------------------------------------------------------- +// [fs.path.itr] iterators +GHC_INLINE path::iterator::iterator() {} + +GHC_INLINE path::iterator::iterator(const path& p, const impl_string_type::const_iterator& pos) + : _first(p._path.begin()) + , _last(p._path.end()) + , _prefix(_first + static_cast(p._prefixLength)) + , _root(p.has_root_directory() ? _first + static_cast(p._prefixLength + p.root_name_length()) : _last) + , _iter(pos) +{ + if (pos != _last) { + updateCurrent(); + } +} + +GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(const path::impl_string_type::const_iterator& pos) const +{ + path::impl_string_type::const_iterator i = pos; + bool fromStart = i == _first || i == _prefix; + if (i != _last) { + if (fromStart && i == _first && _prefix > _first) { + i = _prefix; + } + else if (*i++ == preferred_separator) { + // we can only sit on a slash if it is a network name or a root + if (i != _last && *i == preferred_separator) { + if (fromStart && !(i + 1 != _last && *(i + 1) == preferred_separator)) { + // leadind double slashes detected, treat this and the + // following until a slash as one unit + i = std::find(++i, _last, preferred_separator); + } + else { + // skip redundant slashes + while (i != _last && *i == preferred_separator) { + ++i; + } + } + } + } + else { + if (fromStart && i != _last && *i == ':') { + ++i; + } + else { + i = std::find(i, _last, preferred_separator); + } + } + } + return i; +} + +GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(const path::impl_string_type::const_iterator& pos) const +{ + path::impl_string_type::const_iterator i = pos; + if (i != _first) { + --i; + // if this is now the root slash or the trailing slash, we are done, + // else check for network name + if (i != _root && (pos != _last || *i != preferred_separator)) { +#ifdef GHC_OS_WINDOWS + static const impl_string_type seps = GHC_PLATFORM_LITERAL("\\:"); + i = std::find_first_of(std::reverse_iterator(i), std::reverse_iterator(_first), seps.begin(), seps.end()).base(); + if (i > _first && *i == ':') { + i++; + } +#else + i = std::find(std::reverse_iterator(i), std::reverse_iterator(_first), preferred_separator).base(); +#endif + // Now we have to check if this is a network name + if (i - _first == 2 && *_first == preferred_separator && *(_first + 1) == preferred_separator) { + i -= 2; + } + } + } + return i; +} + +GHC_INLINE void path::iterator::updateCurrent() +{ + if ((_iter == _last) || (_iter != _first && _iter != _last && (*_iter == preferred_separator && _iter != _root) && (_iter + 1 == _last))) { + _current.clear(); + } + else { + _current.assign(_iter, increment(_iter)); + } +} + +GHC_INLINE path::iterator& path::iterator::operator++() +{ + _iter = increment(_iter); + while (_iter != _last && // we didn't reach the end + _iter != _root && // this is not a root position + *_iter == preferred_separator && // we are on a separator + (_iter + 1) != _last // the slash is not the last char + ) { + ++_iter; + } + updateCurrent(); + return *this; +} + +GHC_INLINE path::iterator path::iterator::operator++(int) +{ + path::iterator i{*this}; + ++(*this); + return i; +} + +GHC_INLINE path::iterator& path::iterator::operator--() +{ + _iter = decrement(_iter); + updateCurrent(); + return *this; +} + +GHC_INLINE path::iterator path::iterator::operator--(int) +{ + auto i = *this; + --(*this); + return i; +} + +GHC_INLINE bool path::iterator::operator==(const path::iterator& other) const +{ + return _iter == other._iter; +} + +GHC_INLINE bool path::iterator::operator!=(const path::iterator& other) const +{ + return _iter != other._iter; +} + +GHC_INLINE path::iterator::reference path::iterator::operator*() const +{ + return _current; +} + +GHC_INLINE path::iterator::pointer path::iterator::operator->() const +{ + return &_current; +} + +GHC_INLINE path::iterator path::begin() const +{ + return iterator(*this, _path.begin()); +} + +GHC_INLINE path::iterator path::end() const +{ + return iterator(*this, _path.end()); +} + +//----------------------------------------------------------------------------- +// [fs.path.nonmember] path non-member functions +GHC_INLINE void swap(path& lhs, path& rhs) noexcept +{ + swap(lhs._path, rhs._path); +} + +GHC_INLINE size_t hash_value(const path& p) noexcept +{ + return std::hash()(p.generic_string()); +} + +#ifdef GHC_HAS_THREEWAY_COMP +GHC_INLINE std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) <=> 0; +} +#endif + +GHC_INLINE bool operator==(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) == 0; +} + +GHC_INLINE bool operator!=(const path& lhs, const path& rhs) noexcept +{ + return !(lhs == rhs); +} + +GHC_INLINE bool operator<(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) < 0; +} + +GHC_INLINE bool operator<=(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) <= 0; +} + +GHC_INLINE bool operator>(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) > 0; +} + +GHC_INLINE bool operator>=(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) >= 0; +} + +GHC_INLINE path operator/(const path& lhs, const path& rhs) +{ + path result(lhs); + result /= rhs; + return result; +} + +#endif // GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.io] path inserter and extractor +template +inline std::basic_ostream& operator<<(std::basic_ostream& os, const path& p) +{ + os << "\""; + auto ps = p.string(); + for (auto c : ps) { + if (c == '"' || c == '\\') { + os << '\\'; + } + os << c; + } + os << "\""; + return os; +} + +template +inline std::basic_istream& operator>>(std::basic_istream& is, path& p) +{ + std::basic_string tmp; + charT c; + is >> c; + if (c == '"') { + auto sf = is.flags(); + is >> std::noskipws; + while (is) { + auto c2 = is.get(); + if (is) { + if (c2 == '\\') { + c2 = is.get(); + if (is) { + tmp += static_cast(c2); + } + } + else if (c2 == '"') { + break; + } + else { + tmp += static_cast(c2); + } + } + } + if ((sf & std::ios_base::skipws) == std::ios_base::skipws) { + is >> std::skipws; + } + p = path(tmp); + } + else { + is >> tmp; + p = path(static_cast(c) + tmp); + } + return is; +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.class.filesystem_error] Class filesystem_error +GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, std::error_code ec) + : std::system_error(ec, what_arg) + , _what_arg(what_arg) + , _ec(ec) +{ +} + +GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec) + : std::system_error(ec, what_arg) + , _what_arg(what_arg) + , _ec(ec) + , _p1(p1) +{ + if (!_p1.empty()) { + _what_arg += ": '" + _p1.string() + "'"; + } +} + +GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec) + : std::system_error(ec, what_arg) + , _what_arg(what_arg) + , _ec(ec) + , _p1(p1) + , _p2(p2) +{ + if (!_p1.empty()) { + _what_arg += ": '" + _p1.string() + "'"; + } + if (!_p2.empty()) { + _what_arg += ", '" + _p2.string() + "'"; + } +} + +GHC_INLINE const path& filesystem_error::path1() const noexcept +{ + return _p1; +} + +GHC_INLINE const path& filesystem_error::path2() const noexcept +{ + return _p2; +} + +GHC_INLINE const char* filesystem_error::what() const noexcept +{ + return _what_arg.c_str(); +} + +//----------------------------------------------------------------------------- +// [fs.op.funcs] filesystem operations +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path absolute(const path& p) +{ + std::error_code ec; + path result = absolute(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE path absolute(const path& p, std::error_code& ec) +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (p.empty()) { + return absolute(current_path(ec), ec) / ""; + } + ULONG size = ::GetFullPathNameW(GHC_NATIVEWP(p), 0, 0, 0); + if (size) { + std::vector buf(size, 0); + ULONG s2 = GetFullPathNameW(GHC_NATIVEWP(p), size, buf.data(), nullptr); + if (s2 && s2 < size) { + path result = path(std::wstring(buf.data(), s2)); + if (p.filename() == ".") { + result /= "."; + } + return result; + } + } + ec = detail::make_system_error(); + return path(); +#else + path base = current_path(ec); + if (!ec) { + if (p.empty()) { + return base / p; + } + if (p.has_root_name()) { + if (p.has_root_directory()) { + return p; + } + else { + return p.root_name() / base.root_directory() / base.relative_path() / p.relative_path(); + } + } + else { + if (p.has_root_directory()) { + return base.root_name() / p; + } + else { + return base / p; + } + } + } + ec = detail::make_system_error(); + return path(); +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path canonical(const path& p) +{ + std::error_code ec; + auto result = canonical(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE path canonical(const path& p, std::error_code& ec) +{ + if (p.empty()) { + ec = detail::make_error_code(detail::portable_error::not_found); + return path(); + } + path work = p.is_absolute() ? p : absolute(p, ec); + path result; + + auto fs = status(work, ec); + if (ec) { + return path(); + } + if (fs.type() == file_type::not_found) { + ec = detail::make_error_code(detail::portable_error::not_found); + return path(); + } + bool redo; + do { + auto rootPathLen = work._prefixLength + work.root_name_length() + (work.has_root_directory() ? 1 : 0); + redo = false; + result.clear(); + for (auto pe : work) { + if (pe.empty() || pe == ".") { + continue; + } + else if (pe == "..") { + result = result.parent_path(); + continue; + } + else if ((result / pe).string().length() <= rootPathLen) { + result /= pe; + continue; + } + auto sls = symlink_status(result / pe, ec); + if (ec) { + return path(); + } + if (is_symlink(sls)) { + redo = true; + auto target = read_symlink(result / pe, ec); + if (ec) { + return path(); + } + if (target.is_absolute()) { + result = target; + continue; + } + else { + result /= target; + continue; + } + } + else { + result /= pe; + } + } + work = result; + } while (redo); + ec.clear(); + return result; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void copy(const path& from, const path& to) +{ + copy(from, to, copy_options::none); +} + +GHC_INLINE void copy(const path& from, const path& to, copy_options options) +{ + std::error_code ec; + copy(from, to, options, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); + } +} +#endif + +GHC_INLINE void copy(const path& from, const path& to, std::error_code& ec) noexcept +{ + copy(from, to, copy_options::none, ec); +} + +GHC_INLINE void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept +{ + std::error_code tec; + file_status fs_from, fs_to; + ec.clear(); + if ((options & (copy_options::skip_symlinks | copy_options::copy_symlinks | copy_options::create_symlinks)) != copy_options::none) { + fs_from = symlink_status(from, ec); + } + else { + fs_from = status(from, ec); + } + if (!exists(fs_from)) { + if (!ec) { + ec = detail::make_error_code(detail::portable_error::not_found); + } + return; + } + if ((options & (copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none) { + fs_to = symlink_status(to, tec); + } + else { + fs_to = status(to, tec); + } + if (is_other(fs_from) || is_other(fs_to) || (is_directory(fs_from) && is_regular_file(fs_to)) || (exists(fs_to) && equivalent(from, to, ec))) { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + } + else if (is_symlink(fs_from)) { + if ((options & copy_options::skip_symlinks) == copy_options::none) { + if (!exists(fs_to) && (options & copy_options::copy_symlinks) != copy_options::none) { + copy_symlink(from, to, ec); + } + else { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + } + } + } + else if (is_regular_file(fs_from)) { + if ((options & copy_options::directories_only) == copy_options::none) { + if ((options & copy_options::create_symlinks) != copy_options::none) { + create_symlink(from.is_absolute() ? from : canonical(from, ec), to, ec); + } +#ifndef GHC_OS_WEB + else if ((options & copy_options::create_hard_links) != copy_options::none) { + create_hard_link(from, to, ec); + } +#endif + else if (is_directory(fs_to)) { + copy_file(from, to / from.filename(), options, ec); + } + else { + copy_file(from, to, options, ec); + } + } + } +#ifdef LWG_2682_BEHAVIOUR + else if (is_directory(fs_from) && (options & copy_options::create_symlinks) != copy_options::none) { + ec = detail::make_error_code(detail::portable_error::is_a_directory); + } +#endif + else if (is_directory(fs_from) && (options == copy_options::none || (options & copy_options::recursive) != copy_options::none)) { + if (!exists(fs_to)) { + create_directory(to, from, ec); + if (ec) { + return; + } + } + for (auto iter = directory_iterator(from, ec); iter != directory_iterator(); iter.increment(ec)) { + if (!ec) { + copy(iter->path(), to / iter->path().filename(), options | static_cast(0x8000), ec); + } + if (ec) { + return; + } + } + } + return; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool copy_file(const path& from, const path& to) +{ + return copy_file(from, to, copy_options::none); +} + +GHC_INLINE bool copy_file(const path& from, const path& to, copy_options option) +{ + std::error_code ec; + auto result = copy_file(from, to, option, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); + } + return result; +} +#endif + +GHC_INLINE bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept +{ + return copy_file(from, to, copy_options::none, ec); +} + +GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept +{ + std::error_code tecf, tect; + auto sf = status(from, tecf); + auto st = status(to, tect); + bool overwrite = false; + ec.clear(); + if (!is_regular_file(sf)) { + ec = tecf; + return false; + } + if (exists(st) && (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::skip_existing | copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none)) { + ec = tect ? tect : detail::make_error_code(detail::portable_error::exists); + return false; + } + if (exists(st)) { + if ((options & copy_options::update_existing) == copy_options::update_existing) { + auto from_time = last_write_time(from, ec); + if (ec) { + ec = detail::make_system_error(); + return false; + } + auto to_time = last_write_time(to, ec); + if (ec) { + ec = detail::make_system_error(); + return false; + } + if (from_time <= to_time) { + return false; + } + } + overwrite = true; + } +#ifdef GHC_OS_WINDOWS + if (!::CopyFileW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), !overwrite)) { + ec = detail::make_system_error(); + return false; + } + return true; +#else + std::vector buffer(16384, '\0'); + int in = -1, out = -1; + if ((in = ::open(from.c_str(), O_RDONLY)) < 0) { + ec = detail::make_system_error(); + return false; + } + int mode = O_CREAT | O_WRONLY | O_TRUNC; + if (!overwrite) { + mode |= O_EXCL; + } + if ((out = ::open(to.c_str(), mode, static_cast(sf.permissions() & perms::all))) < 0) { + ec = detail::make_system_error(); + ::close(in); + return false; + } + ssize_t br, bw; + while ((br = ::read(in, buffer.data(), buffer.size())) > 0) { + ssize_t offset = 0; + do { + if ((bw = ::write(out, buffer.data() + offset, static_cast(br))) > 0) { + br -= bw; + offset += bw; + } + else if (bw < 0) { + ec = detail::make_system_error(); + ::close(in); + ::close(out); + return false; + } + } while (br); + } + ::close(in); + ::close(out); + return true; +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink) +{ + std::error_code ec; + copy_symlink(existing_symlink, new_symlink, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), existing_symlink, new_symlink, ec); + } +} +#endif + +GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept +{ + ec.clear(); + auto to = read_symlink(existing_symlink, ec); + if (!ec) { + if (exists(to, ec) && is_directory(to, ec)) { + create_directory_symlink(to, new_symlink, ec); + } + else { + create_symlink(to, new_symlink, ec); + } + } +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool create_directories(const path& p) +{ + std::error_code ec; + auto result = create_directories(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept +{ + path current; + ec.clear(); + bool didCreate = false; + auto rootPathLen = p._prefixLength + p.root_name_length() + (p.has_root_directory() ? 1 : 0); + current = p.native().substr(0, rootPathLen); + path folders(p._path.substr(rootPathLen)); + for (path::string_type part : folders) { + current /= part; + std::error_code tec; + auto fs = status(current, tec); + if (tec && fs.type() != file_type::not_found) { + ec = tec; + return false; + } + if (!exists(fs)) { + create_directory(current, ec); + if (ec) { + std::error_code tmp_ec; + if (is_directory(current, tmp_ec)) { + ec.clear(); + } + else { + return false; + } + } + didCreate = true; + } +#ifndef LWG_2935_BEHAVIOUR + else if (!is_directory(fs)) { + ec = detail::make_error_code(detail::portable_error::exists); + return false; + } +#endif + } + return didCreate; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool create_directory(const path& p) +{ + std::error_code ec; + auto result = create_directory(p, path(), ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE bool create_directory(const path& p, std::error_code& ec) noexcept +{ + return create_directory(p, path(), ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool create_directory(const path& p, const path& attributes) +{ + std::error_code ec; + auto result = create_directory(p, attributes, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept +{ + std::error_code tec; + ec.clear(); + auto fs = status(p, tec); +#ifdef LWG_2935_BEHAVIOUR + if (status_known(fs) && exists(fs)) { + return false; + } +#else + if (status_known(fs) && exists(fs) && is_directory(fs)) { + return false; + } +#endif +#ifdef GHC_OS_WINDOWS + if (!attributes.empty()) { + if (!::CreateDirectoryExW(GHC_NATIVEWP(attributes), GHC_NATIVEWP(p), NULL)) { + ec = detail::make_system_error(); + return false; + } + } + else if (!::CreateDirectoryW(GHC_NATIVEWP(p), NULL)) { + ec = detail::make_system_error(); + return false; + } +#else + ::mode_t attribs = static_cast(perms::all); + if (!attributes.empty()) { + struct ::stat fileStat; + if (::stat(attributes.c_str(), &fileStat) != 0) { + ec = detail::make_system_error(); + return false; + } + attribs = fileStat.st_mode; + } + if (::mkdir(p.c_str(), attribs) != 0) { + ec = detail::make_system_error(); + return false; + } +#endif + return true; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink) +{ + std::error_code ec; + create_directory_symlink(to, new_symlink, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec); + } +} +#endif + +GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept +{ + detail::create_symlink(to, new_symlink, true, ec); +} + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link) +{ + std::error_code ec; + create_hard_link(to, new_hard_link, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), to, new_hard_link, ec); + } +} +#endif + +GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept +{ + detail::create_hardlink(to, new_hard_link, ec); +} +#endif + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void create_symlink(const path& to, const path& new_symlink) +{ + std::error_code ec; + create_symlink(to, new_symlink, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec); + } +} +#endif + +GHC_INLINE void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept +{ + detail::create_symlink(to, new_symlink, false, ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path current_path() +{ + std::error_code ec; + auto result = current_path(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), ec); + } + return result; +} +#endif + +GHC_INLINE path current_path(std::error_code& ec) +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + DWORD pathlen = ::GetCurrentDirectoryW(0, 0); + std::unique_ptr buffer(new wchar_t[size_t(pathlen) + 1]); + if (::GetCurrentDirectoryW(pathlen, buffer.get()) == 0) { + ec = detail::make_system_error(); + return path(); + } + return path(std::wstring(buffer.get()), path::native_format); +#else + size_t pathlen = static_cast(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX))); + std::unique_ptr buffer(new char[pathlen + 1]); + if (::getcwd(buffer.get(), pathlen) == nullptr) { + ec = detail::make_system_error(); + return path(); + } + return path(buffer.get()); +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void current_path(const path& p) +{ + std::error_code ec; + current_path(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} +#endif + +GHC_INLINE void current_path(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (!::SetCurrentDirectoryW(GHC_NATIVEWP(p))) { + ec = detail::make_system_error(); + } +#else + if (::chdir(p.string().c_str()) == -1) { + ec = detail::make_system_error(); + } +#endif +} + +GHC_INLINE bool exists(file_status s) noexcept +{ + return status_known(s) && s.type() != file_type::not_found; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool exists(const path& p) +{ + return exists(status(p)); +} +#endif + +GHC_INLINE bool exists(const path& p, std::error_code& ec) noexcept +{ + file_status s = status(p, ec); + if (status_known(s)) { + ec.clear(); + } + return exists(s); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool equivalent(const path& p1, const path& p2) +{ + std::error_code ec; + bool result = equivalent(p1, p2, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p1, p2, ec); + } + return result; +} +#endif + +GHC_INLINE bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + detail::unique_handle file1(::CreateFileW(GHC_NATIVEWP(p1), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); + auto e1 = ::GetLastError(); + detail::unique_handle file2(::CreateFileW(GHC_NATIVEWP(p2), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); + if (!file1 || !file2) { +#ifdef LWG_2937_BEHAVIOUR + ec = detail::make_system_error(e1 ? e1 : ::GetLastError()); +#else + if (file1 == file2) { + ec = detail::make_system_error(e1 ? e1 : ::GetLastError()); + } +#endif + return false; + } + BY_HANDLE_FILE_INFORMATION inf1, inf2; + if (!::GetFileInformationByHandle(file1.get(), &inf1)) { + ec = detail::make_system_error(); + return false; + } + if (!::GetFileInformationByHandle(file2.get(), &inf2)) { + ec = detail::make_system_error(); + return false; + } + return inf1.ftLastWriteTime.dwLowDateTime == inf2.ftLastWriteTime.dwLowDateTime && inf1.ftLastWriteTime.dwHighDateTime == inf2.ftLastWriteTime.dwHighDateTime && inf1.nFileIndexHigh == inf2.nFileIndexHigh && inf1.nFileIndexLow == inf2.nFileIndexLow && + inf1.nFileSizeHigh == inf2.nFileSizeHigh && inf1.nFileSizeLow == inf2.nFileSizeLow && inf1.dwVolumeSerialNumber == inf2.dwVolumeSerialNumber; +#else + struct ::stat s1, s2; + auto rc1 = ::stat(p1.c_str(), &s1); + auto e1 = errno; + auto rc2 = ::stat(p2.c_str(), &s2); + if (rc1 || rc2) { +#ifdef LWG_2937_BEHAVIOUR + ec = detail::make_system_error(e1 ? e1 : errno); +#else + if (rc1 && rc2) { + ec = detail::make_system_error(e1 ? e1 : errno); + } +#endif + return false; + } + return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino && s1.st_size == s2.st_size && s1.st_mtime == s2.st_mtime; +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t file_size(const path& p) +{ + std::error_code ec; + auto result = file_size(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE uintmax_t file_size(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { + ec = detail::make_system_error(); + return static_cast(-1); + } + return static_cast(attr.nFileSizeHigh) << (sizeof(attr.nFileSizeHigh) * 8) | attr.nFileSizeLow; +#else + struct ::stat fileStat; + if (::stat(p.c_str(), &fileStat) == -1) { + ec = detail::make_system_error(); + return static_cast(-1); + } + return static_cast(fileStat.st_size); +#endif +} + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t hard_link_count(const path& p) +{ + std::error_code ec; + auto result = hard_link_count(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + uintmax_t result = static_cast(-1); + detail::unique_handle file(::CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); + BY_HANDLE_FILE_INFORMATION inf; + if (!file) { + ec = detail::make_system_error(); + } + else { + if (!::GetFileInformationByHandle(file.get(), &inf)) { + ec = detail::make_system_error(); + } + else { + result = inf.nNumberOfLinks; + } + } + return result; +#else + uintmax_t result = 0; + file_status fs = detail::status_ex(p, ec, nullptr, nullptr, &result, nullptr); + if (fs.type() == file_type::not_found) { + ec = detail::make_error_code(detail::portable_error::not_found); + } + return ec ? static_cast(-1) : result; +#endif +} +#endif + +GHC_INLINE bool is_block_file(file_status s) noexcept +{ + return s.type() == file_type::block; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_block_file(const path& p) +{ + return is_block_file(status(p)); +} +#endif + +GHC_INLINE bool is_block_file(const path& p, std::error_code& ec) noexcept +{ + return is_block_file(status(p, ec)); +} + +GHC_INLINE bool is_character_file(file_status s) noexcept +{ + return s.type() == file_type::character; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_character_file(const path& p) +{ + return is_character_file(status(p)); +} +#endif + +GHC_INLINE bool is_character_file(const path& p, std::error_code& ec) noexcept +{ + return is_character_file(status(p, ec)); +} + +GHC_INLINE bool is_directory(file_status s) noexcept +{ + return s.type() == file_type::directory; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_directory(const path& p) +{ + return is_directory(status(p)); +} +#endif + +GHC_INLINE bool is_directory(const path& p, std::error_code& ec) noexcept +{ + return is_directory(status(p, ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_empty(const path& p) +{ + if (is_directory(p)) { + return directory_iterator(p) == directory_iterator(); + } + else { + return file_size(p) == 0; + } +} +#endif + +GHC_INLINE bool is_empty(const path& p, std::error_code& ec) noexcept +{ + auto fs = status(p, ec); + if (ec) { + return false; + } + if (is_directory(fs)) { + directory_iterator iter(p, ec); + if (ec) { + return false; + } + return iter == directory_iterator(); + } + else { + auto sz = file_size(p, ec); + if (ec) { + return false; + } + return sz == 0; + } +} + +GHC_INLINE bool is_fifo(file_status s) noexcept +{ + return s.type() == file_type::fifo; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_fifo(const path& p) +{ + return is_fifo(status(p)); +} +#endif + +GHC_INLINE bool is_fifo(const path& p, std::error_code& ec) noexcept +{ + return is_fifo(status(p, ec)); +} + +GHC_INLINE bool is_other(file_status s) noexcept +{ + return exists(s) && !is_regular_file(s) && !is_directory(s) && !is_symlink(s); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_other(const path& p) +{ + return is_other(status(p)); +} +#endif + +GHC_INLINE bool is_other(const path& p, std::error_code& ec) noexcept +{ + return is_other(status(p, ec)); +} + +GHC_INLINE bool is_regular_file(file_status s) noexcept +{ + return s.type() == file_type::regular; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_regular_file(const path& p) +{ + return is_regular_file(status(p)); +} +#endif + +GHC_INLINE bool is_regular_file(const path& p, std::error_code& ec) noexcept +{ + return is_regular_file(status(p, ec)); +} + +GHC_INLINE bool is_socket(file_status s) noexcept +{ + return s.type() == file_type::socket; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_socket(const path& p) +{ + return is_socket(status(p)); +} +#endif + +GHC_INLINE bool is_socket(const path& p, std::error_code& ec) noexcept +{ + return is_socket(status(p, ec)); +} + +GHC_INLINE bool is_symlink(file_status s) noexcept +{ + return s.type() == file_type::symlink; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_symlink(const path& p) +{ + return is_symlink(symlink_status(p)); +} +#endif + +GHC_INLINE bool is_symlink(const path& p, std::error_code& ec) noexcept +{ + return is_symlink(symlink_status(p, ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_time_type last_write_time(const path& p) +{ + std::error_code ec; + auto result = last_write_time(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE file_time_type last_write_time(const path& p, std::error_code& ec) noexcept +{ + time_t result = 0; + ec.clear(); + file_status fs = detail::status_ex(p, ec, nullptr, nullptr, nullptr, &result); + return ec ? (file_time_type::min)() : std::chrono::system_clock::from_time_t(result); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void last_write_time(const path& p, file_time_type new_time) +{ + std::error_code ec; + last_write_time(p, new_time, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} +#endif + +GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept +{ + ec.clear(); + auto d = new_time.time_since_epoch(); +#ifdef GHC_OS_WINDOWS + detail::unique_handle file(::CreateFileW(GHC_NATIVEWP(p), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)); + FILETIME ft; + auto tt = std::chrono::duration_cast(d).count() * 10 + 116444736000000000; + ft.dwLowDateTime = static_cast(tt); + ft.dwHighDateTime = static_cast(tt >> 32); + if (!::SetFileTime(file.get(), 0, 0, &ft)) { + ec = detail::make_system_error(); + } +#elif defined(GHC_OS_MACOS) +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED +#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300 + struct ::stat fs; + if (::stat(p.c_str(), &fs) == 0) { + struct ::timeval tv[2]; + tv[0].tv_sec = fs.st_atimespec.tv_sec; + tv[0].tv_usec = static_cast(fs.st_atimespec.tv_nsec / 1000); + tv[1].tv_sec = std::chrono::duration_cast(d).count(); + tv[1].tv_usec = static_cast(std::chrono::duration_cast(d).count() % 1000000); + if (::utimes(p.c_str(), tv) == 0) { + return; + } + } + ec = detail::make_system_error(); + return; +#else + struct ::timespec times[2]; + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_sec = std::chrono::duration_cast(d).count(); + times[1].tv_nsec = 0; // std::chrono::duration_cast(d).count() % 1000000000; + if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { + ec = detail::make_system_error(); + } + return; +#endif +#endif +#else +#ifndef UTIME_OMIT +#define UTIME_OMIT ((1l << 30) - 2l) +#endif + struct ::timespec times[2]; + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_sec = static_cast(std::chrono::duration_cast(d).count()); + times[1].tv_nsec = static_cast(std::chrono::duration_cast(d).count() % 1000000000); +#if defined(__ANDROID_API__) && __ANDROID_API__ < 12 + if (syscall(__NR_utimensat, AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { +#else + if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { +#endif + ec = detail::make_system_error(); + } + return; +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void permissions(const path& p, perms prms, perm_options opts) +{ + std::error_code ec; + permissions(p, prms, opts, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} +#endif + +GHC_INLINE void permissions(const path& p, perms prms, std::error_code& ec) noexcept +{ + permissions(p, prms, perm_options::replace, ec); +} + +GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept +{ + if (static_cast(opts & (perm_options::replace | perm_options::add | perm_options::remove)) == 0) { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + return; + } + auto fs = symlink_status(p, ec); + if ((opts & perm_options::replace) != perm_options::replace) { + if ((opts & perm_options::add) == perm_options::add) { + prms = fs.permissions() | prms; + } + else { + prms = fs.permissions() & ~prms; + } + } +#ifdef GHC_OS_WINDOWS +#ifdef __GNUC__ + auto oldAttr = GetFileAttributesW(GHC_NATIVEWP(p)); + if (oldAttr != INVALID_FILE_ATTRIBUTES) { + DWORD newAttr = ((prms & perms::owner_write) == perms::owner_write) ? oldAttr & ~(static_cast(FILE_ATTRIBUTE_READONLY)) : oldAttr | FILE_ATTRIBUTE_READONLY; + if (oldAttr == newAttr || SetFileAttributesW(GHC_NATIVEWP(p), newAttr)) { + return; + } + } + ec = detail::make_system_error(); +#else + int mode = 0; + if ((prms & perms::owner_read) == perms::owner_read) { + mode |= _S_IREAD; + } + if ((prms & perms::owner_write) == perms::owner_write) { + mode |= _S_IWRITE; + } + if (::_wchmod(p.wstring().c_str(), mode) != 0) { + ec = detail::make_system_error(); + } +#endif +#else + if ((opts & perm_options::nofollow) != perm_options::nofollow) { + if (::chmod(p.c_str(), static_cast(prms)) != 0) { + ec = detail::make_system_error(); + } + } +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path proximate(const path& p, std::error_code& ec) +{ + auto cp = current_path(ec); + if (!ec) { + return proximate(p, cp, ec); + } + return path(); +} +#endif + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path proximate(const path& p, const path& base) +{ + return weakly_canonical(p).lexically_proximate(weakly_canonical(base)); +} +#endif + +GHC_INLINE path proximate(const path& p, const path& base, std::error_code& ec) +{ + return weakly_canonical(p, ec).lexically_proximate(weakly_canonical(base, ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path read_symlink(const path& p) +{ + std::error_code ec; + auto result = read_symlink(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE path read_symlink(const path& p, std::error_code& ec) +{ + file_status fs = symlink_status(p, ec); + if (fs.type() != file_type::symlink) { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + return path(); + } + auto result = detail::resolveSymlink(p, ec); + return ec ? path() : result; +} + +GHC_INLINE path relative(const path& p, std::error_code& ec) +{ + return relative(p, current_path(ec), ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path relative(const path& p, const path& base) +{ + return weakly_canonical(p).lexically_relative(weakly_canonical(base)); +} +#endif + +GHC_INLINE path relative(const path& p, const path& base, std::error_code& ec) +{ + return weakly_canonical(p, ec).lexically_relative(weakly_canonical(base, ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool remove(const path& p) +{ + std::error_code ec; + auto result = remove(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS +#ifdef GHC_USE_WCHAR_T + auto cstr = p.c_str(); +#else + std::wstring np = detail::fromUtf8(p.u8string()); + auto cstr = np.c_str(); +#endif + DWORD attr = GetFileAttributesW(cstr); + if (attr == INVALID_FILE_ATTRIBUTES) { + auto error = ::GetLastError(); + if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND) { + return false; + } + ec = detail::make_system_error(error); + } + else if (attr & FILE_ATTRIBUTE_READONLY) { + auto new_attr = attr & ~static_cast(FILE_ATTRIBUTE_READONLY); + if (!SetFileAttributesW(cstr, new_attr)) { + auto error = ::GetLastError(); + ec = detail::make_system_error(error); + } + } + if (!ec) { + if (attr & FILE_ATTRIBUTE_DIRECTORY) { + if (!RemoveDirectoryW(cstr)) { + ec = detail::make_system_error(); + } + } + else { + if (!DeleteFileW(cstr)) { + ec = detail::make_system_error(); + } + } + } +#else + if (::remove(p.c_str()) == -1) { + auto error = errno; + if (error == ENOENT) { + return false; + } + ec = detail::make_system_error(); + } +#endif + return ec ? false : true; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t remove_all(const path& p) +{ + std::error_code ec; + auto result = remove_all(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE uintmax_t remove_all(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); + uintmax_t count = 0; + if (p == "/") { + ec = detail::make_error_code(detail::portable_error::not_supported); + return static_cast(-1); + } + std::error_code tec; + auto fs = symlink_status(p, tec); + if (exists(fs) && is_directory(fs)) { + for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) { + if (ec && !detail::is_not_found_error(ec)) { + break; + } + bool is_symlink_result = iter->is_symlink(ec); + if (ec) + return static_cast(-1); + if (!is_symlink_result && iter->is_directory(ec)) { + count += remove_all(iter->path(), ec); + if (ec) { + return static_cast(-1); + } + } + else { + if (!ec) { + remove(iter->path(), ec); + } + if (ec) { + return static_cast(-1); + } + ++count; + } + } + } + if (!ec) { + if (remove(p, ec)) { + ++count; + } + } + if (ec) { + return static_cast(-1); + } + return count; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void rename(const path& from, const path& to) +{ + std::error_code ec; + rename(from, to, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); + } +} +#endif + +GHC_INLINE void rename(const path& from, const path& to, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (from != to) { + if (!MoveFileExW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), (DWORD)MOVEFILE_REPLACE_EXISTING)) { + ec = detail::make_system_error(); + } + } +#else + if (from != to) { + if (::rename(from.c_str(), to.c_str()) != 0) { + ec = detail::make_system_error(); + } + } +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void resize_file(const path& p, uintmax_t size) +{ + std::error_code ec; + resize_file(p, size, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} +#endif + +GHC_INLINE void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + LARGE_INTEGER lisize; + lisize.QuadPart = static_cast(size); + if (lisize.QuadPart < 0) { +#ifdef ERROR_FILE_TOO_LARGE + ec = detail::make_system_error(ERROR_FILE_TOO_LARGE); +#else + ec = detail::make_system_error(223); +#endif + return; + } + detail::unique_handle file(CreateFileW(GHC_NATIVEWP(p), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)); + if (!file) { + ec = detail::make_system_error(); + } + else if (SetFilePointerEx(file.get(), lisize, NULL, FILE_BEGIN) == 0 || SetEndOfFile(file.get()) == 0) { + ec = detail::make_system_error(); + } +#else + if (::truncate(p.c_str(), static_cast(size)) != 0) { + ec = detail::make_system_error(); + } +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE space_info space(const path& p) +{ + std::error_code ec; + auto result = space(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE space_info space(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + ULARGE_INTEGER freeBytesAvailableToCaller = {{ 0, 0 }}; + ULARGE_INTEGER totalNumberOfBytes = {{ 0, 0 }}; + ULARGE_INTEGER totalNumberOfFreeBytes = {{ 0, 0 }}; + if (!GetDiskFreeSpaceExW(GHC_NATIVEWP(p), &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) { + ec = detail::make_system_error(); + return {static_cast(-1), static_cast(-1), static_cast(-1)}; + } + return {static_cast(totalNumberOfBytes.QuadPart), static_cast(totalNumberOfFreeBytes.QuadPart), static_cast(freeBytesAvailableToCaller.QuadPart)}; +#else + struct ::statvfs sfs; + if (::statvfs(p.c_str(), &sfs) != 0) { + ec = detail::make_system_error(); + return {static_cast(-1), static_cast(-1), static_cast(-1)}; + } + return {static_cast(sfs.f_blocks) * static_cast(sfs.f_frsize), static_cast(sfs.f_bfree) * static_cast(sfs.f_frsize), static_cast(sfs.f_bavail) * static_cast(sfs.f_frsize)}; +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_status status(const path& p) +{ + std::error_code ec; + auto result = status(p, ec); + if (result.type() == file_type::none) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE file_status status(const path& p, std::error_code& ec) noexcept +{ + return detail::status_ex(p, ec); +} + +GHC_INLINE bool status_known(file_status s) noexcept +{ + return s.type() != file_type::none; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_status symlink_status(const path& p) +{ + std::error_code ec; + auto result = symlink_status(p, ec); + if (result.type() == file_type::none) { + throw filesystem_error(detail::systemErrorText(ec.value()), ec); + } + return result; +} +#endif + +GHC_INLINE file_status symlink_status(const path& p, std::error_code& ec) noexcept +{ + return detail::symlink_status_ex(p, ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path temp_directory_path() +{ + std::error_code ec; + path result = temp_directory_path(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), ec); + } + return result; +} +#endif + +GHC_INLINE path temp_directory_path(std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + wchar_t buffer[512]; + auto rc = GetTempPathW(511, buffer); + if (!rc || rc > 511) { + ec = detail::make_system_error(); + return path(); + } + return path(std::wstring(buffer)); +#else + static const char* temp_vars[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr}; + const char* temp_path = nullptr; + for (auto temp_name = temp_vars; *temp_name != nullptr; ++temp_name) { + temp_path = std::getenv(*temp_name); + if (temp_path) { + return path(temp_path); + } + } + return path("/tmp"); +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path weakly_canonical(const path& p) +{ + std::error_code ec; + auto result = weakly_canonical(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE path weakly_canonical(const path& p, std::error_code& ec) noexcept +{ + path result; + ec.clear(); + bool scan = true; + for (auto pe : p) { + if (scan) { + std::error_code tec; + if (exists(result / pe, tec)) { + result /= pe; + } + else { + if (ec) { + return path(); + } + scan = false; + if (!result.empty()) { + result = canonical(result, ec) / pe; + if (ec) { + break; + } + } + else { + result /= pe; + } + } + } + else { + result /= pe; + } + } + if (scan) { + if (!result.empty()) { + result = canonical(result, ec); + } + } + return ec ? path() : result.lexically_normal(); +} + +//----------------------------------------------------------------------------- +// [fs.class.file_status] class file_status +// [fs.file_status.cons] constructors and destructor +GHC_INLINE file_status::file_status() noexcept + : file_status(file_type::none) +{ +} + +GHC_INLINE file_status::file_status(file_type ft, perms prms) noexcept + : _type(ft) + , _perms(prms) +{ +} + +GHC_INLINE file_status::file_status(const file_status& other) noexcept + : _type(other._type) + , _perms(other._perms) +{ +} + +GHC_INLINE file_status::file_status(file_status&& other) noexcept + : _type(other._type) + , _perms(other._perms) +{ +} + +GHC_INLINE file_status::~file_status() {} + +// assignments: +GHC_INLINE file_status& file_status::operator=(const file_status& rhs) noexcept +{ + _type = rhs._type; + _perms = rhs._perms; + return *this; +} + +GHC_INLINE file_status& file_status::operator=(file_status&& rhs) noexcept +{ + _type = rhs._type; + _perms = rhs._perms; + return *this; +} + +// [fs.file_status.mods] modifiers +GHC_INLINE void file_status::type(file_type ft) noexcept +{ + _type = ft; +} + +GHC_INLINE void file_status::permissions(perms prms) noexcept +{ + _perms = prms; +} + +// [fs.file_status.obs] observers +GHC_INLINE file_type file_status::type() const noexcept +{ + return _type; +} + +GHC_INLINE perms file_status::permissions() const noexcept +{ + return _perms; +} + +//----------------------------------------------------------------------------- +// [fs.class.directory_entry] class directory_entry +// [fs.dir.entry.cons] constructors and destructor +// directory_entry::directory_entry() noexcept = default; +// directory_entry::directory_entry(const directory_entry&) = default; +// directory_entry::directory_entry(directory_entry&&) noexcept = default; +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE directory_entry::directory_entry(const filesystem::path& p) + : _path(p) + , _file_size(static_cast(-1)) +#ifndef GHC_OS_WINDOWS + , _hard_link_count(static_cast(-1)) +#endif + , _last_write_time(0) +{ + refresh(); +} +#endif + +GHC_INLINE directory_entry::directory_entry(const filesystem::path& p, std::error_code& ec) + : _path(p) + , _file_size(static_cast(-1)) +#ifndef GHC_OS_WINDOWS + , _hard_link_count(static_cast(-1)) +#endif + , _last_write_time(0) +{ + refresh(ec); +} + +GHC_INLINE directory_entry::~directory_entry() {} + +// assignments: +// directory_entry& directory_entry::operator=(const directory_entry&) = default; +// directory_entry& directory_entry::operator=(directory_entry&&) noexcept = default; + +// [fs.dir.entry.mods] directory_entry modifiers +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void directory_entry::assign(const filesystem::path& p) +{ + _path = p; + refresh(); +} +#endif + +GHC_INLINE void directory_entry::assign(const filesystem::path& p, std::error_code& ec) +{ + _path = p; + refresh(ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p) +{ + _path.replace_filename(p); + refresh(); +} +#endif + +GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p, std::error_code& ec) +{ + _path.replace_filename(p); + refresh(ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void directory_entry::refresh() +{ + std::error_code ec; + refresh(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec); + } +} +#endif + +GHC_INLINE void directory_entry::refresh(std::error_code& ec) noexcept +{ +#ifdef GHC_OS_WINDOWS + _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, nullptr, &_last_write_time); +#else + _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, &_hard_link_count, &_last_write_time); +#endif +} + +// [fs.dir.entry.obs] directory_entry observers +GHC_INLINE const filesystem::path& directory_entry::path() const noexcept +{ + return _path; +} + +GHC_INLINE directory_entry::operator const filesystem::path&() const noexcept +{ + return _path; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_type directory_entry::status_file_type() const +{ + return _status.type() != file_type::none ? _status.type() : filesystem::status(path()).type(); +} +#endif + +GHC_INLINE file_type directory_entry::status_file_type(std::error_code& ec) const noexcept +{ + if (_status.type() != file_type::none) { + ec.clear(); + return _status.type(); + } + return filesystem::status(path(), ec).type(); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::exists() const +{ + return status_file_type() != file_type::not_found; +} +#endif + +GHC_INLINE bool directory_entry::exists(std::error_code& ec) const noexcept +{ + return status_file_type(ec) != file_type::not_found; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_block_file() const +{ + return status_file_type() == file_type::block; +} +#endif +GHC_INLINE bool directory_entry::is_block_file(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::block; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_character_file() const +{ + return status_file_type() == file_type::character; +} +#endif + +GHC_INLINE bool directory_entry::is_character_file(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::character; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_directory() const +{ + return status_file_type() == file_type::directory; +} +#endif + +GHC_INLINE bool directory_entry::is_directory(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::directory; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_fifo() const +{ + return status_file_type() == file_type::fifo; +} +#endif + +GHC_INLINE bool directory_entry::is_fifo(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::fifo; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_other() const +{ + auto ft = status_file_type(); + return ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink(); +} +#endif + +GHC_INLINE bool directory_entry::is_other(std::error_code& ec) const noexcept +{ + auto ft = status_file_type(ec); + bool other = ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink(ec); + return !ec && other; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_regular_file() const +{ + return status_file_type() == file_type::regular; +} +#endif + +GHC_INLINE bool directory_entry::is_regular_file(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::regular; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_socket() const +{ + return status_file_type() == file_type::socket; +} +#endif + +GHC_INLINE bool directory_entry::is_socket(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::socket; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_symlink() const +{ + return _symlink_status.type() != file_type::none ? _symlink_status.type() == file_type::symlink : filesystem::is_symlink(symlink_status()); +} +#endif + +GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept +{ + if (_symlink_status.type() != file_type::none) { + ec.clear(); + return _symlink_status.type() == file_type::symlink; + } + return filesystem::is_symlink(symlink_status(ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t directory_entry::file_size() const +{ + if (_file_size != static_cast(-1)) { + return _file_size; + } + return filesystem::file_size(path()); +} +#endif + +GHC_INLINE uintmax_t directory_entry::file_size(std::error_code& ec) const noexcept +{ + if (_file_size != static_cast(-1)) { + ec.clear(); + return _file_size; + } + return filesystem::file_size(path(), ec); +} + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t directory_entry::hard_link_count() const +{ +#ifndef GHC_OS_WINDOWS + if (_hard_link_count != static_cast(-1)) { + return _hard_link_count; + } +#endif + return filesystem::hard_link_count(path()); +} +#endif + +GHC_INLINE uintmax_t directory_entry::hard_link_count(std::error_code& ec) const noexcept +{ +#ifndef GHC_OS_WINDOWS + if (_hard_link_count != static_cast(-1)) { + ec.clear(); + return _hard_link_count; + } +#endif + return filesystem::hard_link_count(path(), ec); +} +#endif + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_time_type directory_entry::last_write_time() const +{ + if (_last_write_time != 0) { + return std::chrono::system_clock::from_time_t(_last_write_time); + } + return filesystem::last_write_time(path()); +} +#endif + +GHC_INLINE file_time_type directory_entry::last_write_time(std::error_code& ec) const noexcept +{ + if (_last_write_time != 0) { + ec.clear(); + return std::chrono::system_clock::from_time_t(_last_write_time); + } + return filesystem::last_write_time(path(), ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_status directory_entry::status() const +{ + if (_status.type() != file_type::none && _status.permissions() != perms::unknown) { + return _status; + } + return filesystem::status(path()); +} +#endif + +GHC_INLINE file_status directory_entry::status(std::error_code& ec) const noexcept +{ + if (_status.type() != file_type::none && _status.permissions() != perms::unknown) { + ec.clear(); + return _status; + } + return filesystem::status(path(), ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_status directory_entry::symlink_status() const +{ + if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) { + return _symlink_status; + } + return filesystem::symlink_status(path()); +} +#endif + +GHC_INLINE file_status directory_entry::symlink_status(std::error_code& ec) const noexcept +{ + if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) { + ec.clear(); + return _symlink_status; + } + return filesystem::symlink_status(path(), ec); +} + +#ifdef GHC_HAS_THREEWAY_COMP +GHC_INLINE std::strong_ordering directory_entry::operator<=>(const directory_entry& rhs) const noexcept +{ + return _path <=> rhs._path; +} +#endif + +GHC_INLINE bool directory_entry::operator<(const directory_entry& rhs) const noexcept +{ + return _path < rhs._path; +} + +GHC_INLINE bool directory_entry::operator==(const directory_entry& rhs) const noexcept +{ + return _path == rhs._path; +} + +GHC_INLINE bool directory_entry::operator!=(const directory_entry& rhs) const noexcept +{ + return _path != rhs._path; +} + +GHC_INLINE bool directory_entry::operator<=(const directory_entry& rhs) const noexcept +{ + return _path <= rhs._path; +} + +GHC_INLINE bool directory_entry::operator>(const directory_entry& rhs) const noexcept +{ + return _path > rhs._path; +} + +GHC_INLINE bool directory_entry::operator>=(const directory_entry& rhs) const noexcept +{ + return _path >= rhs._path; +} + +//----------------------------------------------------------------------------- +// [fs.class.directory_iterator] class directory_iterator + +#ifdef GHC_OS_WINDOWS +class directory_iterator::impl +{ +public: + impl(const path& p, directory_options options) + : _base(p) + , _options(options) + , _dirHandle(INVALID_HANDLE_VALUE) + { + if (!_base.empty()) { + ZeroMemory(&_findData, sizeof(WIN32_FIND_DATAW)); + if ((_dirHandle = FindFirstFileW(GHC_NATIVEWP((_base / "*")), &_findData)) != INVALID_HANDLE_VALUE) { + if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") { + increment(_ec); + } + else { + _dir_entry._path = _base / std::wstring(_findData.cFileName); + copyToDirEntry(_ec); + } + } + else { + auto error = ::GetLastError(); + _base = filesystem::path(); + if (error != ERROR_ACCESS_DENIED || (options & directory_options::skip_permission_denied) == directory_options::none) { + _ec = detail::make_system_error(); + } + } + } + } + impl(const impl& other) = delete; + ~impl() + { + if (_dirHandle != INVALID_HANDLE_VALUE) { + FindClose(_dirHandle); + _dirHandle = INVALID_HANDLE_VALUE; + } + } + void increment(std::error_code& ec) + { + if (_dirHandle != INVALID_HANDLE_VALUE) { + do { + if (FindNextFileW(_dirHandle, &_findData)) { + _dir_entry._path = _base; +#ifdef GHC_USE_WCHAR_T + _dir_entry._path.append_name(_findData.cFileName); +#else +#ifdef GHC_RAISE_UNICODE_ERRORS + try { + _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str()); + } + catch (filesystem_error& fe) { + ec = fe.code(); + return; + } +#else + _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str()); +#endif +#endif + copyToDirEntry(ec); + } + else { + auto err = ::GetLastError(); + if (err != ERROR_NO_MORE_FILES) { + _ec = ec = detail::make_system_error(err); + } + FindClose(_dirHandle); + _dirHandle = INVALID_HANDLE_VALUE; + _dir_entry._path.clear(); + break; + } + } while (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L".."); + } + else { + ec = _ec; + } + } + void copyToDirEntry(std::error_code& ec) + { + if (_findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + _dir_entry._status = detail::status_ex(_dir_entry._path, ec, &_dir_entry._symlink_status, &_dir_entry._file_size, nullptr, &_dir_entry._last_write_time); + } + else { + _dir_entry._status = detail::status_from_INFO(_dir_entry._path, &_findData, ec, &_dir_entry._file_size, &_dir_entry._last_write_time); + _dir_entry._symlink_status = _dir_entry._status; + } + if (ec) { + if (_dir_entry._status.type() != file_type::none && _dir_entry._symlink_status.type() != file_type::none) { + ec.clear(); + } + else { + _dir_entry._file_size = static_cast(-1); + _dir_entry._last_write_time = 0; + } + } + } + path _base; + directory_options _options; + WIN32_FIND_DATAW _findData; + HANDLE _dirHandle; + directory_entry _dir_entry; + std::error_code _ec; +}; +#else +// POSIX implementation +class directory_iterator::impl +{ +public: + impl(const path& path, directory_options options) + : _base(path) + , _options(options) + , _dir(nullptr) + , _entry(nullptr) + { + if (!path.empty()) { + _dir = ::opendir(path.native().c_str()); + if (!_dir) { + auto error = errno; + _base = filesystem::path(); + if ((error != EACCES && error != EPERM) || (options & directory_options::skip_permission_denied) == directory_options::none) { + _ec = detail::make_system_error(); + } + } + else { + increment(_ec); + } + } + } + impl(const impl& other) = delete; + ~impl() + { + if (_dir) { + ::closedir(_dir); + } + } + void increment(std::error_code& ec) + { + if (_dir) { + bool skip; + do { + skip = false; + errno = 0; + _entry = ::readdir(_dir); + if (_entry) { + _dir_entry._path = _base; + _dir_entry._path.append_name(_entry->d_name); + copyToDirEntry(); + if (ec && (ec.value() == EACCES || ec.value() == EPERM) && (_options & directory_options::skip_permission_denied) == directory_options::skip_permission_denied) { + ec.clear(); + skip = true; + } + } + else { + ::closedir(_dir); + _dir = nullptr; + _dir_entry._path.clear(); + if (errno) { + ec = detail::make_system_error(); + } + break; + } + } while (skip || std::strcmp(_entry->d_name, ".") == 0 || std::strcmp(_entry->d_name, "..") == 0); + } + } + + void copyToDirEntry() + { +#ifdef GHC_NO_DIRENT_D_TYPE + _dir_entry._symlink_status = file_status(); + _dir_entry._status = file_status(); +#else + _dir_entry._symlink_status.permissions(perms::unknown); + switch (_entry->d_type) { + case DT_BLK: + _dir_entry._symlink_status.type(file_type::block); + break; + case DT_CHR: + _dir_entry._symlink_status.type(file_type::character); + break; + case DT_DIR: + _dir_entry._symlink_status.type(file_type::directory); + break; + case DT_FIFO: + _dir_entry._symlink_status.type(file_type::fifo); + break; + case DT_LNK: + _dir_entry._symlink_status.type(file_type::symlink); + break; + case DT_REG: + _dir_entry._symlink_status.type(file_type::regular); + break; + case DT_SOCK: + _dir_entry._symlink_status.type(file_type::socket); + break; + case DT_UNKNOWN: + _dir_entry._symlink_status.type(file_type::none); + break; + default: + _dir_entry._symlink_status.type(file_type::unknown); + break; + } + if (_entry->d_type != DT_LNK) { + _dir_entry._status = _dir_entry._symlink_status; + } + else { + _dir_entry._status.type(file_type::none); + _dir_entry._status.permissions(perms::unknown); + } +#endif + _dir_entry._file_size = static_cast(-1); + _dir_entry._hard_link_count = static_cast(-1); + _dir_entry._last_write_time = 0; + } + path _base; + directory_options _options; + DIR* _dir; + struct ::dirent* _entry; + directory_entry _dir_entry; + std::error_code _ec; +}; +#endif + +// [fs.dir.itr.members] member functions +GHC_INLINE directory_iterator::directory_iterator() noexcept + : _impl(new impl(path(), directory_options::none)) +{ +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE directory_iterator::directory_iterator(const path& p) + : _impl(new impl(p, directory_options::none)) +{ + if (_impl->_ec) { + throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec); + } + _impl->_ec.clear(); +} + +GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options) + : _impl(new impl(p, options)) +{ + if (_impl->_ec) { + throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec); + } +} +#endif + +GHC_INLINE directory_iterator::directory_iterator(const path& p, std::error_code& ec) noexcept + : _impl(new impl(p, directory_options::none)) +{ + if (_impl->_ec) { + ec = _impl->_ec; + } +} + +GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept + : _impl(new impl(p, options)) +{ + if (_impl->_ec) { + ec = _impl->_ec; + } +} + +GHC_INLINE directory_iterator::directory_iterator(const directory_iterator& rhs) + : _impl(rhs._impl) +{ +} + +GHC_INLINE directory_iterator::directory_iterator(directory_iterator&& rhs) noexcept + : _impl(std::move(rhs._impl)) +{ +} + +GHC_INLINE directory_iterator::~directory_iterator() {} + +GHC_INLINE directory_iterator& directory_iterator::operator=(const directory_iterator& rhs) +{ + _impl = rhs._impl; + return *this; +} + +GHC_INLINE directory_iterator& directory_iterator::operator=(directory_iterator&& rhs) noexcept +{ + _impl = std::move(rhs._impl); + return *this; +} + +GHC_INLINE const directory_entry& directory_iterator::operator*() const +{ + return _impl->_dir_entry; +} + +GHC_INLINE const directory_entry* directory_iterator::operator->() const +{ + return &_impl->_dir_entry; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE directory_iterator& directory_iterator::operator++() +{ + std::error_code ec; + _impl->increment(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_entry._path, ec); + } + return *this; +} +#endif + +GHC_INLINE directory_iterator& directory_iterator::increment(std::error_code& ec) noexcept +{ + _impl->increment(ec); + return *this; +} + +GHC_INLINE bool directory_iterator::operator==(const directory_iterator& rhs) const +{ + return _impl->_dir_entry._path == rhs._impl->_dir_entry._path; +} + +GHC_INLINE bool directory_iterator::operator!=(const directory_iterator& rhs) const +{ + return _impl->_dir_entry._path != rhs._impl->_dir_entry._path; +} + +// [fs.dir.itr.nonmembers] directory_iterator non-member functions + +GHC_INLINE directory_iterator begin(directory_iterator iter) noexcept +{ + return iter; +} + +GHC_INLINE directory_iterator end(const directory_iterator&) noexcept +{ + return directory_iterator(); +} + +//----------------------------------------------------------------------------- +// [fs.class.rec.dir.itr] class recursive_directory_iterator + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator() noexcept + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator()); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p) + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options) + : _impl(new recursive_directory_iterator_impl(options, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p, options)); +} +#endif + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept + : _impl(new recursive_directory_iterator_impl(options, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p, options, ec)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, std::error_code& ec) noexcept + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p, ec)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const recursive_directory_iterator& rhs) + : _impl(rhs._impl) +{ +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept + : _impl(std::move(rhs._impl)) +{ +} + +GHC_INLINE recursive_directory_iterator::~recursive_directory_iterator() {} + +// [fs.rec.dir.itr.members] observers +GHC_INLINE directory_options recursive_directory_iterator::options() const +{ + return _impl->_options; +} + +GHC_INLINE int recursive_directory_iterator::depth() const +{ + return static_cast(_impl->_dir_iter_stack.size() - 1); +} + +GHC_INLINE bool recursive_directory_iterator::recursion_pending() const +{ + return _impl->_recursion_pending; +} + +GHC_INLINE const directory_entry& recursive_directory_iterator::operator*() const +{ + return *(_impl->_dir_iter_stack.top()); +} + +GHC_INLINE const directory_entry* recursive_directory_iterator::operator->() const +{ + return &(*(_impl->_dir_iter_stack.top())); +} + +// [fs.rec.dir.itr.members] modifiers recursive_directory_iterator& +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(const recursive_directory_iterator& rhs) +{ + _impl = rhs._impl; + return *this; +} + +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(recursive_directory_iterator&& rhs) noexcept +{ + _impl = std::move(rhs._impl); + return *this; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator++() +{ + std::error_code ec; + increment(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); + } + return *this; +} +#endif + +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment(std::error_code& ec) noexcept +{ + bool isSymLink = (*this)->is_symlink(ec); + bool isDir = !ec && (*this)->is_directory(ec); + if (isSymLink && detail::is_not_found_error(ec)) { + ec.clear(); + } + if (!ec) { + if (recursion_pending() && isDir && (!isSymLink || (options() & directory_options::follow_directory_symlink) != directory_options::none)) { + _impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec)); + } + else { + _impl->_dir_iter_stack.top().increment(ec); + } + if (!ec) { + while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { + _impl->_dir_iter_stack.pop(); + _impl->_dir_iter_stack.top().increment(ec); + } + } + else if (!_impl->_dir_iter_stack.empty()) { + _impl->_dir_iter_stack.pop(); + } + _impl->_recursion_pending = true; + } + return *this; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void recursive_directory_iterator::pop() +{ + std::error_code ec; + pop(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); + } +} +#endif + +GHC_INLINE void recursive_directory_iterator::pop(std::error_code& ec) +{ + if (depth() == 0) { + *this = recursive_directory_iterator(); + } + else { + do { + _impl->_dir_iter_stack.pop(); + _impl->_dir_iter_stack.top().increment(ec); + } while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()); + } +} + +GHC_INLINE void recursive_directory_iterator::disable_recursion_pending() +{ + _impl->_recursion_pending = false; +} + +// other members as required by [input.iterators] +GHC_INLINE bool recursive_directory_iterator::operator==(const recursive_directory_iterator& rhs) const +{ + return _impl->_dir_iter_stack.top() == rhs._impl->_dir_iter_stack.top(); +} + +GHC_INLINE bool recursive_directory_iterator::operator!=(const recursive_directory_iterator& rhs) const +{ + return _impl->_dir_iter_stack.top() != rhs._impl->_dir_iter_stack.top(); +} + +// [fs.rec.dir.itr.nonmembers] directory_iterator non-member functions +GHC_INLINE recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept +{ + return iter; +} + +GHC_INLINE recursive_directory_iterator end(const recursive_directory_iterator&) noexcept +{ + return recursive_directory_iterator(); +} + +#endif // GHC_EXPAND_IMPL + +} // namespace filesystem +} // namespace ghc + +// cleanup some macros +#undef GHC_INLINE +#undef GHC_EXPAND_IMPL + +#endif // GHC_FILESYSTEM_H diff --git a/gulrak-filesystem/include/ghc/fs_fwd.hpp b/gulrak-filesystem/include/ghc/fs_fwd.hpp new file mode 100644 index 0000000..31188d1 --- /dev/null +++ b/gulrak-filesystem/include/ghc/fs_fwd.hpp @@ -0,0 +1,38 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// fs_fwd.hpp - The forwarding header for the header/implementation seperated usage of +// ghc::filesystem. +// This file can be include at any place, where ghc::filesystem api is needed while +// not bleeding implementation details (e.g. system includes) into the global namespace, +// as long as one cpp includes fs_impl.hpp to deliver the matching implementations. +//--------------------------------------------------------------------------------------- +#ifndef GHC_FILESYSTEM_FWD_H +#define GHC_FILESYSTEM_FWD_H +#define GHC_FILESYSTEM_FWD +#include +#endif // GHC_FILESYSTEM_FWD_H diff --git a/gulrak-filesystem/include/ghc/fs_impl.hpp b/gulrak-filesystem/include/ghc/fs_impl.hpp new file mode 100644 index 0000000..92e3eae --- /dev/null +++ b/gulrak-filesystem/include/ghc/fs_impl.hpp @@ -0,0 +1,35 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// fs_impl.hpp - The implementation header for the header/implementation seperated usage of +// ghc::filesystem. +// This file can be used to hide the implementation of ghc::filesystem into a single cpp. +// The cpp has to include this before including fs_fwd.hpp directly or via a different +// header to work. +//--------------------------------------------------------------------------------------- +#define GHC_FILESYSTEM_IMPLEMENTATION +#include diff --git a/gulrak-filesystem/include/ghc/fs_std.hpp b/gulrak-filesystem/include/ghc/fs_std.hpp new file mode 100644 index 0000000..c9492fd --- /dev/null +++ b/gulrak-filesystem/include/ghc/fs_std.hpp @@ -0,0 +1,60 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// fs_std.hpp - The dynamic switching header that includes std::filesystem if detected +// or ghc::filesystem if not, and makes the resulting API available in the +// namespace fs. +//--------------------------------------------------------------------------------------- +#ifndef GHC_FILESYSTEM_STD_H +#define GHC_FILESYSTEM_STD_H +#if defined(__APPLE__) +#include +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include +namespace fs { +using namespace std::filesystem; +using ifstream = std::ifstream; +using ofstream = std::ofstream; +using fstream = std::fstream; +} +#endif +#endif +#ifndef GHC_USE_STD_FS +//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE +#include +namespace fs { +using namespace ghc::filesystem; +using ifstream = ghc::filesystem::ifstream; +using ofstream = ghc::filesystem::ofstream; +using fstream = ghc::filesystem::fstream; +} +#endif +#endif // GHC_FILESYSTEM_STD_H + diff --git a/gulrak-filesystem/include/ghc/fs_std_fwd.hpp b/gulrak-filesystem/include/ghc/fs_std_fwd.hpp new file mode 100644 index 0000000..163c956 --- /dev/null +++ b/gulrak-filesystem/include/ghc/fs_std_fwd.hpp @@ -0,0 +1,63 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// fs_std_fwd.hpp - The forwarding header for the header/implementation seperated usage of +// ghc::filesystem that uses std::filesystem if it detects it. +// This file can be include at any place, where fs::filesystem api is needed while +// not bleeding implementation details (e.g. system includes) into the global namespace, +// as long as one cpp includes fs_std_impl.hpp to deliver the matching implementations. +//--------------------------------------------------------------------------------------- +#ifndef GHC_FILESYSTEM_STD_FWD_H +#define GHC_FILESYSTEM_STD_FWD_H +#if defined(__APPLE__) +#include +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include +namespace fs { +using namespace std::filesystem; +using ifstream = std::ifstream; +using ofstream = std::ofstream; +using fstream = std::fstream; +} +#endif +#endif +#ifndef GHC_USE_STD_FS +//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE +#define GHC_FILESYSTEM_FWD +#include +namespace fs { +using namespace ghc::filesystem; +using ifstream = ghc::filesystem::ifstream; +using ofstream = ghc::filesystem::ofstream; +using fstream = ghc::filesystem::fstream; +} +#endif +#endif // GHC_FILESYSTEM_STD_FWD_H + diff --git a/gulrak-filesystem/include/ghc/fs_std_impl.hpp b/gulrak-filesystem/include/ghc/fs_std_impl.hpp new file mode 100644 index 0000000..7042edc --- /dev/null +++ b/gulrak-filesystem/include/ghc/fs_std_impl.hpp @@ -0,0 +1,46 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// fs_std_impl.hpp - The implementation header for the header/implementation seperated usage of +// ghc::filesystem that does nothing if std::filesystem is detected. +// This file can be used to hide the implementation of ghc::filesystem into a single cpp. +// The cpp has to include this before including fs_std_fwd.hpp directly or via a different +// header to work. +//--------------------------------------------------------------------------------------- +#if defined(__APPLE__) +#include +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#endif +#endif +#ifndef GHC_USE_STD_FS +//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE +#define GHC_FILESYSTEM_IMPLEMENTATION +#include +#endif diff --git a/gulrak-filesystem/test/CMakeLists.txt b/gulrak-filesystem/test/CMakeLists.txt new file mode 100644 index 0000000..f9fdd18 --- /dev/null +++ b/gulrak-filesystem/test/CMakeLists.txt @@ -0,0 +1,91 @@ + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") +set(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS ON) +include(ParseAndAddCatchTests) + +if(GHC_COVERAGE) + message("Generating test runner for coverage run...") + set(CMAKE_EXE_LINKER_FLAGS "${CMCMAKE_EXE_LINKER_FLAGS} --coverage") + add_executable(filesystem_test filesystem_test.cpp catch.hpp) + if(MINGW) + target_compile_options(filesystem_test PUBLIC --coverage "-Wa,-mbig-obj") + else() + target_compile_options(filesystem_test PUBLIC --coverage) + endif() + target_link_libraries(filesystem_test PUBLIC ghc_filesystem --coverage) + if("cxx_std_17" IN_LIST GHC_FILESYSTEM_TEST_COMPILE_FEATURES) + AddTestExecutableWithStdCpp(17 filesystem_test.cpp catch.hpp) + endif() + if("cxx_std_20" IN_LIST GHC_FILESYSTEM_TEST_COMPILE_FEATURES) + AddTestExecutableWithStdCpp(20 filesystem_test.cpp catch.hpp) + endif() +else() + message("Generating test runner for normal test...") + add_executable(filesystem_test filesystem_test.cpp catch.hpp) + target_link_libraries(filesystem_test ghc_filesystem) + target_compile_options(filesystem_test PRIVATE + $<$:-s DISABLE_EXCEPTION_CATCHING=0> + $<$,$>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Werror> + $<$:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Wno-psabi -Werror> + $<$:/WX> + $<$:-Wa,-mbig-obj>) + if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) + target_compile_definitions(filesystem_test PRIVATE _CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN NOMINMAX) + endif() + if(EMSCRIPTEN) + set_target_properties(filesystem_test PROPERTIES LINK_FLAGS "-g4 -s DISABLE_EXCEPTION_CATCHING=0 -s ALLOW_MEMORY_GROWTH=1") + endif() + ParseAndAddCatchTests(filesystem_test) + if(GHC_FILESYSTEM_BUILD_STD_TESTING) + AddExecutableWithStdFS(std_filesystem_test filesystem_test.cpp catch.hpp) + endif() + if(WIN32) + add_executable(filesystem_test_char filesystem_test.cpp catch.hpp) + target_link_libraries(filesystem_test_char ghc_filesystem) + target_compile_options(filesystem_test_char PRIVATE + $<$:-Wall -Wextra -Werror> + $<$:-Wall -Werror> + $<$:/WX>) + if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) + target_compile_definitions(filesystem_test_char PRIVATE _CRT_SECURE_NO_WARNINGS GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE) + else() + target_compile_definitions(filesystem_test_char PRIVATE GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE) + endif() + ParseAndAddCatchTests(filesystem_test_char) + endif() + if("cxx_std_17" IN_LIST GHC_FILESYSTEM_TEST_COMPILE_FEATURES) + AddTestExecutableWithStdCpp(17 filesystem_test.cpp catch.hpp) + endif() + if("cxx_std_20" IN_LIST GHC_FILESYSTEM_TEST_COMPILE_FEATURES) + AddTestExecutableWithStdCpp(20 filesystem_test.cpp catch.hpp) + endif() +endif() + +add_executable(multifile_test multi1.cpp multi2.cpp catch.hpp) +target_link_libraries(multifile_test ghc_filesystem) +add_test(multifile_test multifile_test) + +add_executable(fwd_impl_test fwd_test.cpp impl_test.cpp) +target_link_libraries(fwd_impl_test ghc_filesystem) +target_compile_options(fwd_impl_test PRIVATE + $<$:-s DISABLE_EXCEPTION_CATCHING=0> + $<$,$>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Werror> + $<$:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Wno-psabi -Werror> + $<$:/WX> + $<$:-Wa,-mbig-obj>) +if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) + target_compile_definitions(fwd_impl_test PRIVATE _CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN NOMINMAX) +endif() +add_test(fwd_impl_test fwd_impl_test) + +add_executable(exception exception.cpp) +if(NOT MSVC) + target_compile_options(exception PRIVATE -fno-exceptions) +endif() +target_include_directories(exception PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include) +target_compile_options(exception PRIVATE + $<$:-s DISABLE_EXCEPTION_CATCHING=0> + $<$,$>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Werror> + $<$:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Wno-psabi -Werror> + $<$:/WX> + $<$:-Wa,-mbig-obj>) diff --git a/gulrak-filesystem/test/catch.hpp b/gulrak-filesystem/test/catch.hpp new file mode 100644 index 0000000..b0fa641 --- /dev/null +++ b/gulrak-filesystem/test/catch.hpp @@ -0,0 +1,13922 @@ +/* + * Catch v2.4.0 + * Generated: 2018-09-04 11:55:01.682061 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 4 +#define CATCH_VERSION_PATCH 0 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // GCC likes to warn on REQUIREs, and we cannot suppress them + // locally because g++'s support for _Pragma is lacking in older, + // still supported, versions +# pragma GCC diagnostic ignored "-Wparentheses" +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if __cplusplus >= 201402L +# define CATCH_CPP14_OR_GREATER +# endif + +# if __cplusplus >= 201703L +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +#if defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#ifdef __clang__ + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE + +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo( SourceLineInfo && ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo& operator = ( SourceLineInfo && ) = default; + + bool empty() const noexcept; + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + using ITestCasePtr = std::shared_ptr; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include + +namespace Catch { + + class StringData; + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. c_str() must return a null terminated + /// string, however, and so the StringRef will internally take ownership + /// (taking a copy), if necessary. In theory this ownership is not externally + /// visible - but it does mean (substring) StringRefs should not be shared between + /// threads. + class StringRef { + public: + using size_type = std::size_t; + + private: + friend struct StringRefTestAccess; + + char const* m_start; + size_type m_size; + + char* m_data = nullptr; + + void takeOwnership(); + + static constexpr char const* const s_empty = ""; + + public: // construction/ assignment + StringRef() noexcept + : StringRef( s_empty, 0 ) + {} + + StringRef( StringRef const& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ) + {} + + StringRef( StringRef&& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ), + m_data( other.m_data ) + { + other.m_data = nullptr; + } + + StringRef( char const* rawChars ) noexcept; + + StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + ~StringRef() noexcept { + delete[] m_data; + } + + auto operator = ( StringRef const &other ) noexcept -> StringRef& { + delete[] m_data; + m_data = nullptr; + m_start = other.m_start; + m_size = other.m_size; + return *this; + } + + operator std::string() const; + + void swap( StringRef& other ) noexcept; + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != ( StringRef const& other ) const noexcept -> bool; + + auto operator[] ( size_type index ) const noexcept -> char; + + public: // named queries + auto empty() const noexcept -> bool { + return m_size == 0; + } + auto size() const noexcept -> size_type { + return m_size; + } + + auto numberOfCharacters() const noexcept -> size_type; + auto c_str() const -> char const*; + + public: // substrings and searches + auto substr( size_type start, size_type size ) const noexcept -> StringRef; + + // Returns the current start pointer. + // Note that the pointer can change when if the StringRef is a substring + auto currentData() const noexcept -> char const*; + + private: // ownership queries - may not be consistent between calls + auto isOwned() const noexcept -> bool; + auto isSubstring() const noexcept -> bool; + }; + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; + auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } + +} // namespace Catch + +inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +namespace Catch { + +template +class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); +public: + TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} + + void invoke() const override { + C obj; + (obj.*m_testAsMethod)(); + } +}; + +auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; + +template +auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsMethod( testAsMethod ); +} + +struct NameAndTags { + NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept; + StringRef name; + StringRef tags; +}; + +struct AutoReg : NonCopyable { + AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept; + ~AutoReg(); +}; + +} // end namespace Catch + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF + +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + struct TestName : INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF ClassName) { \ + void test(); \ + }; \ + } \ + void TestName::test() + +#endif + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ \ + struct TestName : INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF ClassName) { \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + void TestName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +// end catch_test_registry.h +// start catch_capture.hpp + +// start catch_assertionhandler.h + +// start catch_assertioninfo.h + +// start catch_result_type.h + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; }; + + bool isOk( ResultWas::OfType resultType ); + bool isJustInfo( int flags ); + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; }; + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); + + bool shouldContinueOnFailure( int flags ); + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + bool shouldSuppressFailure( int flags ); + +} // end namespace Catch + +// end catch_result_type.h +namespace Catch { + + struct AssertionInfo + { + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; + }; + +} // end namespace Catch + +// end catch_assertioninfo.h +// start catch_decomposer.h + +// start catch_tostring.h + +#include +#include +#include +#include +// start catch_stream.h + +#include +#include +#include + +namespace Catch { + + std::ostream& cout(); + std::ostream& cerr(); + std::ostream& clog(); + + class StringRef; + + struct IStream { + virtual ~IStream(); + virtual std::ostream& stream() const = 0; + }; + + auto makeStream( StringRef const &filename ) -> IStream const*; + + class ReusableStringStream { + std::size_t m_index; + std::ostream* m_oss; + public: + ReusableStringStream(); + ~ReusableStringStream(); + + auto str() const -> std::string; + + template + auto operator << ( T const& value ) -> ReusableStringStream& { + *m_oss << value; + return *this; + } + auto get() -> std::ostream& { return *m_oss; } + }; +} + +// end catch_stream.h + +#ifdef __OBJC__ +// start catch_objc_arc.hpp + +#import + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) { + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +// end catch_objc_arc.hpp +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless +#endif + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + // Bring in operator<< from global namespace into Catch namespace + using ::operator<<; + + namespace Detail { + + extern const std::string unprintableString; + + std::string rawMemoryToString( const void *object, std::size_t size ); + + template + std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } + + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype(std::declval() << std::declval(), std::true_type()); + + template + static auto test(...)->std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; + + template + std::string convertUnknownEnumToString( E e ); + + template + typename std::enable_if< + !std::is_enum::value && !std::is_base_of::value, + std::string>::type convertUnstreamable( T const& ) { + return Detail::unprintableString; + } + template + typename std::enable_if< + !std::is_enum::value && std::is_base_of::value, + std::string>::type convertUnstreamable(T const& ex) { + return ex.what(); + } + + template + typename std::enable_if< + std::is_enum::value + , std::string>::type convertUnstreamable( T const& value ) { + return convertUnknownEnumToString( value ); + } + +#if defined(_MANAGED) + //! Convert a CLR string to a utf8 std::string + template + std::string clrReferenceToString( T^ ref ) { + if (ref == nullptr) + return std::string("null"); + auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); + cli::pin_ptr p = &bytes[0]; + return std::string(reinterpret_cast(p), bytes->Length); + } +#endif + + } // namespace Detail + + // If we decide for C++14, change these to enable_if_ts + template + struct StringMaker { + template + static + typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type + convert(const Fake& value) { + ReusableStringStream rss; + // NB: call using the function-like syntax to avoid ambiguity with + // user-defined templated operator<< under clang. + rss.operator<<(value); + return rss.str(); + } + + template + static + typename std::enable_if::value, std::string>::type + convert( const Fake& value ) { +#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) + return Detail::convertUnstreamable(value); +#else + return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); +#endif + } + }; + + namespace Detail { + + // This function dispatches all stringification requests inside of Catch. + // Should be preferably called fully qualified, like ::Catch::Detail::stringify + template + std::string stringify(const T& e) { + return ::Catch::StringMaker::type>::type>::convert(e); + } + + template + std::string convertUnknownEnumToString( E e ) { + return ::Catch::Detail::stringify(static_cast::type>(e)); + } + +#if defined(_MANAGED) + template + std::string stringify( T^ e ) { + return ::Catch::StringMaker::convert(e); + } +#endif + + } // namespace Detail + + // Some predefined specializations + + template<> + struct StringMaker { + static std::string convert(const std::string& str); + }; +#ifdef CATCH_CONFIG_WCHAR + template<> + struct StringMaker { + static std::string convert(const std::wstring& wstr); + }; +#endif + + template<> + struct StringMaker { + static std::string convert(char const * str); + }; + template<> + struct StringMaker { + static std::string convert(char * str); + }; + +#ifdef CATCH_CONFIG_WCHAR + template<> + struct StringMaker { + static std::string convert(wchar_t const * str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t * str); + }; +#endif + + // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, + // while keeping string semantics? + template + struct StringMaker { + static std::string convert(char const* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template + struct StringMaker { + static std::string convert(signed char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); + } + }; + template + struct StringMaker { + static std::string convert(unsigned char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); + } + }; + + template<> + struct StringMaker { + static std::string convert(int value); + }; + template<> + struct StringMaker { + static std::string convert(long value); + }; + template<> + struct StringMaker { + static std::string convert(long long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long long value); + }; + + template<> + struct StringMaker { + static std::string convert(bool b); + }; + + template<> + struct StringMaker { + static std::string convert(char c); + }; + template<> + struct StringMaker { + static std::string convert(signed char c); + }; + template<> + struct StringMaker { + static std::string convert(unsigned char c); + }; + + template<> + struct StringMaker { + static std::string convert(std::nullptr_t); + }; + + template<> + struct StringMaker { + static std::string convert(float value); + }; + template<> + struct StringMaker { + static std::string convert(double value); + }; + + template + struct StringMaker { + template + static std::string convert(U* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template + struct StringMaker { + static std::string convert(R C::* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + +#if defined(_MANAGED) + template + struct StringMaker { + static std::string convert( T^ ref ) { + return ::Catch::Detail::clrReferenceToString(ref); + } + }; +#endif + + namespace Detail { + template + std::string rangeToString(InputIterator first, InputIterator last) { + ReusableStringStream rss; + rss << "{ "; + if (first != last) { + rss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + rss << ", " << ::Catch::Detail::stringify(*first); + } + rss << " }"; + return rss.str(); + } + } + +#ifdef __OBJC__ + template<> + struct StringMaker { + static std::string convert(NSString * nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker { + static std::string convert(NSObject* nsObject) { + return ::Catch::Detail::stringify([nsObject description]); + } + + }; + namespace Detail { + inline std::string stringify( NSString* nsstring ) { + return StringMaker::convert( nsstring ); + } + + } // namespace Detail +#endif // __OBJC__ + +} // namespace Catch + +////////////////////////////////////////////////////// +// Separate std-lib types stringification, so it can be selectively enabled +// This means that we do not bring in + +#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::pair& pair) { + ReusableStringStream rss; + rss << "{ " + << ::Catch::Detail::stringify(pair.first) + << ", " + << ::Catch::Detail::stringify(pair.second) + << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +// Separate std::tuple specialization +#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include +namespace Catch { + namespace Detail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct TupleElementPrinter { + static void print(const Tuple& tuple, std::ostream& os) { + os << (N ? ", " : " ") + << ::Catch::Detail::stringify(std::get(tuple)); + TupleElementPrinter::print(tuple, os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct TupleElementPrinter { + static void print(const Tuple&, std::ostream&) {} + }; + + } + + template + struct StringMaker> { + static std::string convert(const std::tuple& tuple) { + ReusableStringStream rss; + rss << '{'; + Detail::TupleElementPrinter>::print(tuple, rss.get()); + rss << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER + +namespace Catch { + struct not_this_one {}; // Tag type for detecting which begin/ end are being selected + + // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace + using std::begin; + using std::end; + + not_this_one begin( ... ); + not_this_one end( ... ); + + template + struct is_range { + static const bool value = + !std::is_same())), not_this_one>::value && + !std::is_same())), not_this_one>::value; + }; + +#if defined(_MANAGED) // Managed types are never ranges + template + struct is_range { + static const bool value = false; + }; +#endif + + template + std::string rangeToString( Range const& range ) { + return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); + } + + // Handle vector specially + template + std::string rangeToString( std::vector const& v ) { + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for( bool b : v ) { + if( first ) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify( b ); + } + rss << " }"; + return rss.str(); + } + + template + struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { + static std::string convert( R const& range ) { + return rangeToString( range ); + } + }; + + template + struct StringMaker { + static std::string convert(T const(&arr)[SZ]) { + return rangeToString(arr); + } + }; + +} // namespace Catch + +// Separate std::chrono::duration specialization +#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include +#include +#include + +namespace Catch { + +template +struct ratio_string { + static std::string symbol(); +}; + +template +std::string ratio_string::symbol() { + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return rss.str(); +} +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; + + //////////// + // std::chrono::duration specializations + template + struct StringMaker> { + static std::string convert(std::chrono::duration const& duration) { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string::symbol() << 's'; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); + } + }; + + //////////// + // std::chrono::time_point specialization + // Generic time_point cannot be specialized, only std::chrono::time_point + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; + } + }; + // std::chrono::time_point specialization + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); +#else + std::tm* timeInfo = std::gmtime(&converted); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_tostring.h +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning(disable:4180) // qualifier applied to function type has no meaning +#endif + +namespace Catch { + + struct ITransientExpression { + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + auto getResult() const -> bool { return m_result; } + virtual void streamReconstructedExpression( std::ostream &os ) const = 0; + + ITransientExpression( bool isBinaryExpression, bool result ) + : m_isBinaryExpression( isBinaryExpression ), + m_result( result ) + {} + + // We don't actually need a virtual destructor, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); + + bool m_isBinaryExpression; + bool m_result; + + }; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); + + template + class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + formatReconstructedExpression + ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); + } + + public: + BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + : ITransientExpression{ true, comparisonResult }, + m_lhs( lhs ), + m_op( op ), + m_rhs( rhs ) + {} + }; + + template + class UnaryExpr : public ITransientExpression { + LhsT m_lhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + os << Catch::Detail::stringify( m_lhs ); + } + + public: + explicit UnaryExpr( LhsT lhs ) + : ITransientExpression{ false, lhs ? true : false }, + m_lhs( lhs ) + {} + }; + + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); } + template + auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + template + auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + + template + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); } + template + auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + template + auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + + template + class ExprLhs { + LhsT m_lhs; + public: + explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + + template + auto operator == ( RhsT const& rhs ) -> BinaryExpr const { + return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; + } + auto operator == ( bool rhs ) -> BinaryExpr const { + return { m_lhs == rhs, m_lhs, "==", rhs }; + } + + template + auto operator != ( RhsT const& rhs ) -> BinaryExpr const { + return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; + } + auto operator != ( bool rhs ) -> BinaryExpr const { + return { m_lhs != rhs, m_lhs, "!=", rhs }; + } + + template + auto operator > ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs > rhs), m_lhs, ">", rhs }; + } + template + auto operator < ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs < rhs), m_lhs, "<", rhs }; + } + template + auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs >= rhs), m_lhs, ">=", rhs }; + } + template + auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs <= rhs), m_lhs, "<=", rhs }; + } + + auto makeUnaryExpr() const -> UnaryExpr { + return UnaryExpr{ m_lhs }; + } + }; + + void handleExpression( ITransientExpression const& expr ); + + template + void handleExpression( ExprLhs const& expr ) { + handleExpression( expr.makeUnaryExpr() ); + } + + struct Decomposer { + template + auto operator <= ( T const& lhs ) -> ExprLhs { + return ExprLhs{ lhs }; + } + + auto operator <=( bool value ) -> ExprLhs { + return ExprLhs{ value }; + } + }; + +} // end namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_decomposer.h +// start catch_interfaces_capture.h + +#include + +namespace Catch { + + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + struct Counts; + struct BenchmarkInfo; + struct BenchmarkStats; + struct AssertionReaction; + struct SourceLineInfo; + + struct ITransientExpression; + struct IGeneratorTracker; + + struct IResultCapture { + + virtual ~IResultCapture(); + + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + + virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; + + virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; + virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0; + + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; + + virtual void handleFatalErrorCondition( StringRef message ) = 0; + + virtual void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) = 0; + virtual void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) = 0; + virtual void handleIncomplete + ( AssertionInfo const& info ) = 0; + virtual void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) = 0; + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + }; + + IResultCapture& getResultCapture(); +} + +// end catch_interfaces_capture.h +namespace Catch { + + struct TestFailureException{}; + struct AssertionResultData; + struct IResultCapture; + class RunContext; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ); + LazyExpression( LazyExpression const& other ); + LazyExpression& operator = ( LazyExpression const& ) = delete; + + explicit operator bool() const; + + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; + + struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; + }; + + class AssertionHandler { + AssertionInfo m_assertionInfo; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture& m_resultCapture; + + public: + AssertionHandler + ( StringRef const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ); + ~AssertionHandler() { + if ( !m_completed ) { + m_resultCapture.handleIncomplete( m_assertionInfo ); + } + } + + template + void handleExpr( ExprLhs const& expr ) { + handleExpr( expr.makeUnaryExpr() ); + } + void handleExpr( ITransientExpression const& expr ); + + void handleMessage(ResultWas::OfType resultType, StringRef const& message); + + void handleExceptionThrownAsExpected(); + void handleUnexpectedExceptionNotThrown(); + void handleExceptionNotThrownAsExpected(); + void handleThrowingCallSkipped(); + void handleUnexpectedInflightException(); + + void complete(); + void setCompleted(); + + // query + auto allowThrows() const -> bool; + }; + + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ); + +} // namespace Catch + +// end catch_assertionhandler.h +// start catch_message.h + +#include +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo( StringRef const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + unsigned int sequence; + + bool operator == ( MessageInfo const& other ) const; + bool operator < ( MessageInfo const& other ) const; + private: + static unsigned int globalCount; + }; + + struct MessageStream { + + template + MessageStream& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + ReusableStringStream m_stream; + }; + + struct MessageBuilder : MessageStream { + MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ); + + template + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + }; + + class ScopedMessage { + public: + explicit ScopedMessage( MessageBuilder const& builder ); + ~ScopedMessage(); + + MessageInfo m_info; + }; + + class Capturer { + std::vector m_messages; + IResultCapture& m_resultCapture = getResultCapture(); + size_t m_captured = 0; + public: + Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); + ~Capturer(); + + void captureValue( size_t index, StringRef value ); + + template + void captureValues( size_t index, T&& value ) { + captureValue( index, Catch::Detail::stringify( value ) ); + } + + template + void captureValues( size_t index, T&& value, Ts&&... values ) { + captureValues( index, value ); + captureValues( index+1, values... ); + } + }; + +} // end namespace Catch + +// end catch_message.h +#if !defined(CATCH_CONFIG_DISABLE) + +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ +#else + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#endif + +#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + +/////////////////////////////////////////////////////////////////////////////// +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +#define INTERNAL_CATCH_TRY +#define INTERNAL_CATCH_CATCH( capturer ) + +#else // CATCH_CONFIG_FAST_COMPILE + +#define INTERNAL_CATCH_TRY try +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } + +#endif + +#define INTERNAL_CATCH_REACT( handler ) handler.complete(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( (void)0, false && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( !Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(expr); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ + catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ + auto varName = Catch::Capturer( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \ + varName.captureValues( 0, __VA_ARGS__ ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( macroName, log ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ); + +/////////////////////////////////////////////////////////////////////////////// +// Although this is matcher-based, it can be used with just a string +#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_capture.hpp +// start catch_section.h + +// start catch_section_info.h + +// start catch_totals.h + +#include + +namespace Catch { + + struct Counts { + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); + + std::size_t total() const; + bool allPassed() const; + bool allOk() const; + + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); + + Totals delta( Totals const& prevTotals ) const; + + int error = 0; + Counts assertions; + Counts testCases; + }; +} + +// end catch_totals.h +#include + +namespace Catch { + + struct SectionInfo { + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ); + + // Deprecated + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& ) : SectionInfo( _lineInfo, _name ) {} + + std::string name; + std::string description; // !Deprecated: this will always be empty + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +// end catch_section_info.h +// start catch_timer.h + +#include + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t; + auto getEstimatedClockResolution() -> uint64_t; + + class Timer { + uint64_t m_nanoseconds = 0; + public: + void start(); + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; + }; + +} // namespace Catch + +// end catch_timer.h +#include + +namespace Catch { + + class Section : NonCopyable { + public: + Section( SectionInfo const& info ); + ~Section(); + + // This indicates whether the section should be executed or not + explicit operator bool() const; + + private: + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; + +} // end namespace Catch + +#define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS + +#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS + +// end catch_section.h +// start catch_benchmark.h + +#include +#include + +namespace Catch { + + class BenchmarkLooper { + + std::string m_name; + std::size_t m_count = 0; + std::size_t m_iterationsToRun = 1; + uint64_t m_resolution; + Timer m_timer; + + static auto getResolution() -> uint64_t; + public: + // Keep most of this inline as it's on the code path that is being timed + BenchmarkLooper( StringRef name ) + : m_name( name ), + m_resolution( getResolution() ) + { + reportStart(); + m_timer.start(); + } + + explicit operator bool() { + if( m_count < m_iterationsToRun ) + return true; + return needsMoreIterations(); + } + + void increment() { + ++m_count; + } + + void reportStart(); + auto needsMoreIterations() -> bool; + }; + +} // end namespace Catch + +#define BENCHMARK( name ) \ + for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() ) + +// end catch_benchmark.h +// start catch_interfaces_exception.h + +// start catch_interfaces_registry_hub.h + +#include +#include + +namespace Catch { + + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + struct ITagAliasRegistry; + class StartupExceptionRegistry; + + using IReporterFactoryPtr = std::shared_ptr; + + struct IRegistryHub { + virtual ~IRegistryHub(); + + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + + virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; + virtual void registerListener( IReporterFactoryPtr const& factory ) = 0; + virtual void registerTest( TestCase const& testInfo ) = 0; + virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; + }; + + IRegistryHub const& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +// end catch_interfaces_registry_hub.h +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ + static std::string translatorName( signature ) +#endif + +#include +#include +#include + +namespace Catch { + using exceptionTranslateFunction = std::string(*)(); + + struct IExceptionTranslator; + using ExceptionTranslators = std::vector>; + + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; + }; + + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; + }; + + class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { + public: + + ExceptionTranslator( std::string(*translateFunction)( T& ) ) + : m_translateFunction( translateFunction ) + {} + + std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { + try { + if( it == itEnd ) + std::rethrow_exception(std::current_exception()); + else + return (*it)->translate( it+1, itEnd ); + } + catch( T& ex ) { + return m_translateFunction( ex ); + } + } + + protected: + std::string(*m_translateFunction)( T& ); + }; + + public: + template + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator( translateFunction ) ); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ + static std::string translatorName( signature ); \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + static std::string translatorName( signature ) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// end catch_interfaces_exception.h +// start catch_approx.h + +#include + +namespace Catch { +namespace Detail { + + class Approx { + private: + bool equalityComparisonImpl(double other) const; + // Validates the new margin (margin >= 0) + // out-of-line to avoid including stdexcept in the header + void setMargin(double margin); + // Validates the new epsilon (0 < epsilon < 1) + // out-of-line to avoid including stdexcept in the header + void setEpsilon(double epsilon); + + public: + explicit Approx ( double value ); + + static Approx custom(); + + Approx operator-() const; + + template ::value>::type> + Approx operator()( T const& value ) { + Approx approx( static_cast(value) ); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; + return approx; + } + + template ::value>::type> + explicit Approx( T const& value ): Approx(static_cast(value)) + {} + + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } + + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator != ( T const& lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template ::value>::type> + friend bool operator != ( Approx const& lhs, T const& rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + Approx& epsilon( T const& newEpsilon ) { + double epsilonAsDouble = static_cast(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } + + template ::value>::type> + Approx& margin( T const& newMargin ) { + double marginAsDouble = static_cast(newMargin); + setMargin(marginAsDouble); + return *this; + } + + template ::value>::type> + Approx& scale( T const& newScale ) { + m_scale = static_cast(newScale); + return *this; + } + + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val); + Detail::Approx operator "" _a(unsigned long long val); +} // end namespace literals + +template<> +struct StringMaker { + static std::string convert(Catch::Detail::Approx const& value); +}; + +} // end namespace Catch + +// end catch_approx.h +// start catch_string_manip.h + +#include +#include + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; +} + +// end catch_string_manip.h +#ifndef CATCH_CONFIG_DISABLE_MATCHERS +// start catch_capture_matchers.h + +// start catch_matchers.h + +#include +#include + +namespace Catch { +namespace Matchers { + namespace Impl { + + template struct MatchAllOf; + template struct MatchAnyOf; + template struct MatchNotOf; + + class MatcherUntypedBase { + public: + MatcherUntypedBase() = default; + MatcherUntypedBase ( MatcherUntypedBase const& ) = default; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; + std::string toString() const; + + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; + }; + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#endif + + template + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + template + struct MatcherMethod { + virtual bool match( PtrT* arg ) const = 0; + }; + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { + + MatchAllOf operator && ( MatcherBase const& other ) const; + MatchAnyOf operator || ( MatcherBase const& other ) const; + MatchNotOf operator ! () const; + }; + + template + struct MatchAllOf : MatcherBase { + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (!matcher->match(arg)) + return false; + } + return true; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " and "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAllOf& operator && ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + template + struct MatchAnyOf : MatcherBase { + + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (matcher->match(arg)) + return true; + } + return false; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " or "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAnyOf& operator || ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + + template + struct MatchNotOf : MatcherBase { + + MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} + + bool match( ArgT const& arg ) const override { + return !m_underlyingMatcher.match( arg ); + } + + std::string describe() const override { + return "not " + m_underlyingMatcher.toString(); + } + MatcherBase const& m_underlyingMatcher; + }; + + template + MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { + return MatchAllOf() && *this && other; + } + template + MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator ! () const { + return MatchNotOf( *this ); + } + + } // namespace Impl + +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_floating.h + +#include +#include + +namespace Catch { +namespace Matchers { + + namespace Floating { + + enum class FloatingPointKind : uint8_t; + + struct WithinAbsMatcher : MatcherBase { + WithinAbsMatcher(double target, double margin); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + double m_margin; + }; + + struct WithinUlpsMatcher : MatcherBase { + WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + int m_ulps; + FloatingPointKind m_type; + }; + + } // namespace Floating + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff); + Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff); + Floating::WithinAbsMatcher WithinAbs(double target, double margin); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.h +// start catch_matchers_generic.hpp + +#include +#include + +namespace Catch { +namespace Matchers { +namespace Generic { + +namespace Detail { + std::string finalizeDescription(const std::string& desc); +} + +template +class PredicateMatcher : public MatcherBase { + std::function m_predicate; + std::string m_description; +public: + + PredicateMatcher(std::function const& elem, std::string const& descr) + :m_predicate(std::move(elem)), + m_description(Detail::finalizeDescription(descr)) + {} + + bool match( T const& item ) const override { + return m_predicate(item); + } + + std::string describe() const override { + return m_description; + } +}; + +} // namespace Generic + + // The following functions create the actual matcher objects. + // The user has to explicitly specify type to the function, because + // infering std::function is hard (but possible) and + // requires a lot of TMP. + template + Generic::PredicateMatcher Predicate(std::function const& predicate, std::string const& description = "") { + return Generic::PredicateMatcher(predicate, description); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_generic.hpp +// start catch_matchers_string.h + +#include + +namespace Catch { +namespace Matchers { + + namespace StdString { + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct StringMatcherBase : MatcherBase { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + std::string describe() const override; + + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + + struct RegexMatcher : MatcherBase { + RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity ); + bool match( std::string const& matchee ) const override; + std::string describe() const override; + + private: + std::string m_regex; + CaseSensitive::Choice m_caseSensitivity; + }; + + } // namespace StdString + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_string.h +// start catch_matchers_vector.h + +#include + +namespace Catch { +namespace Matchers { + + namespace Vector { + namespace Detail { + template + size_t count(InputIterator first, InputIterator last, T const& item) { + size_t cnt = 0; + for (; first != last; ++first) { + if (*first == item) { + ++cnt; + } + } + return cnt; + } + template + bool contains(InputIterator first, InputIterator last, T const& item) { + for (; first != last; ++first) { + if (*first == item) { + return true; + } + } + return false; + } + } + + template + struct ContainsElementMatcher : MatcherBase> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector const &v) const override { + for (auto const& el : v) { + if (el == m_comparator) { + return true; + } + } + return false; + } + + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + T const& m_comparator; + }; + + template + struct ContainsMatcher : MatcherBase> { + + ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const override { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (auto const& comparator : m_comparator) { + auto present = false; + for (const auto& el : v) { + if (el == comparator) { + present = true; + break; + } + } + if (!present) { + return false; + } + } + return true; + } + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + std::vector const& m_comparator; + }; + + template + struct EqualsMatcher : MatcherBase> { + + EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const override { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + std::string describe() const override { + return "Equals: " + ::Catch::Detail::stringify( m_comparator ); + } + std::vector const& m_comparator; + }; + + template + struct UnorderedEqualsMatcher : MatcherBase> { + UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} + bool match(std::vector const& vec) const override { + // Note: This is a reimplementation of std::is_permutation, + // because I don't want to include inside the common path + if (m_target.size() != vec.size()) { + return false; + } + auto lfirst = m_target.begin(), llast = m_target.end(); + auto rfirst = vec.begin(), rlast = vec.end(); + // Cut common prefix to optimize checking of permuted parts + while (lfirst != llast && *lfirst != *rfirst) { + ++lfirst; ++rfirst; + } + if (lfirst == llast) { + return true; + } + + for (auto mid = lfirst; mid != llast; ++mid) { + // Skip already counted items + if (Detail::contains(lfirst, mid, *mid)) { + continue; + } + size_t num_vec = Detail::count(rfirst, rlast, *mid); + if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) { + return false; + } + } + + return true; + } + + std::string describe() const override { + return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); + } + private: + std::vector const& m_target; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template + Vector::ContainsMatcher Contains( std::vector const& comparator ) { + return Vector::ContainsMatcher( comparator ); + } + + template + Vector::ContainsElementMatcher VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher( comparator ); + } + + template + Vector::EqualsMatcher Equals( std::vector const& comparator ) { + return Vector::EqualsMatcher( comparator ); + } + + template + Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { + return Vector::UnorderedEqualsMatcher(target); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_vector.h +namespace Catch { + + template + class MatchExpr : public ITransientExpression { + ArgT const& m_arg; + MatcherT m_matcher; + StringRef m_matcherString; + public: + MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) + : ITransientExpression{ true, matcher.match( arg ) }, + m_arg( arg ), + m_matcher( matcher ), + m_matcherString( matcherString ) + {} + + void streamReconstructedExpression( std::ostream &os ) const override { + auto matcherAsString = m_matcher.toString(); + os << Catch::Detail::stringify( m_arg ) << ' '; + if( matcherAsString == Detail::unprintableString ) + os << m_matcherString; + else + os << matcherAsString; + } + }; + + using StringMatcher = Matchers::Impl::MatcherBase; + + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ); + + template + auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr { + return MatchExpr( arg, matcher, matcherString ); + } + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__ ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ex ) { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +// end catch_capture_matchers.h +#endif +// start catch_generators.hpp + +// start catch_interfaces_generatortracker.h + + +#include + +namespace Catch { + + namespace Generators { + class GeneratorBase { + protected: + size_t m_size = 0; + + public: + GeneratorBase( size_t size ) : m_size( size ) {} + virtual ~GeneratorBase(); + auto size() const -> size_t { return m_size; } + }; + using GeneratorBasePtr = std::unique_ptr; + + } // namespace Generators + + struct IGeneratorTracker { + virtual ~IGeneratorTracker(); + virtual auto hasGenerator() const -> bool = 0; + virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; + virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; + virtual auto getIndex() const -> std::size_t = 0; + }; + +} // namespace Catch + +// end catch_interfaces_generatortracker.h +// start catch_enforce.h + +#include + +namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + template + [[noreturn]] + void throw_exception(Ex const& e) { + throw e; + } +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + [[noreturn]] + void throw_exception(std::exception const& e); +#endif +} // namespace Catch; + +#define CATCH_PREPARE_EXCEPTION( type, msg ) \ + type( ( Catch::ReusableStringStream() << msg ).str() ) +#define CATCH_INTERNAL_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg)) +#define CATCH_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg )) +#define CATCH_RUNTIME_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg )) +#define CATCH_ENFORCE( condition, msg ) \ + do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) + +// end catch_enforce.h +#include +#include +#include + +#include + +namespace Catch { +namespace Generators { + + // !TBD move this into its own location? + namespace pf{ + template + std::unique_ptr make_unique( Args&&... args ) { + return std::unique_ptr(new T(std::forward(args)...)); + } + } + + template + struct IGenerator { + virtual ~IGenerator() {} + virtual auto get( size_t index ) const -> T = 0; + }; + + template + class SingleValueGenerator : public IGenerator { + T m_value; + public: + SingleValueGenerator( T const& value ) : m_value( value ) {} + + auto get( size_t ) const -> T override { + return m_value; + } + }; + + template + class FixedValuesGenerator : public IGenerator { + std::vector m_values; + + public: + FixedValuesGenerator( std::initializer_list values ) : m_values( values ) {} + + auto get( size_t index ) const -> T override { + return m_values[index]; + } + }; + + template + class RangeGenerator : public IGenerator { + T const m_first; + T const m_last; + + public: + RangeGenerator( T const& first, T const& last ) : m_first( first ), m_last( last ) { + assert( m_last > m_first ); + } + + auto get( size_t index ) const -> T override { + // ToDo:: introduce a safe cast to catch potential overflows + return static_cast(m_first+index); + } + }; + + template + struct NullGenerator : IGenerator { + auto get( size_t ) const -> T override { + CATCH_INTERNAL_ERROR("A Null Generator is always empty"); + } + }; + + template + class Generator { + std::unique_ptr> m_generator; + size_t m_size; + + public: + Generator( size_t size, std::unique_ptr> generator ) + : m_generator( std::move( generator ) ), + m_size( size ) + {} + + auto size() const -> size_t { return m_size; } + auto operator[]( size_t index ) const -> T { + assert( index < m_size ); + return m_generator->get( index ); + } + }; + + std::vector randomiseIndices( size_t selectionSize, size_t sourceSize ); + + template + class GeneratorRandomiser : public IGenerator { + Generator m_baseGenerator; + + std::vector m_indices; + public: + GeneratorRandomiser( Generator&& baseGenerator, size_t numberOfItems ) + : m_baseGenerator( std::move( baseGenerator ) ), + m_indices( randomiseIndices( numberOfItems, m_baseGenerator.size() ) ) + {} + + auto get( size_t index ) const -> T override { + return m_baseGenerator[m_indices[index]]; + } + }; + + template + struct RequiresASpecialisationFor; + + template + auto all() -> Generator { return RequiresASpecialisationFor(); } + + template<> + auto all() -> Generator; + + template + auto range( T const& first, T const& last ) -> Generator { + return Generator( (last-first), pf::make_unique>( first, last ) ); + } + + template + auto random( T const& first, T const& last ) -> Generator { + auto gen = range( first, last ); + auto size = gen.size(); + + return Generator( size, pf::make_unique>( std::move( gen ), size ) ); + } + template + auto random( size_t size ) -> Generator { + return Generator( size, pf::make_unique>( all(), size ) ); + } + + template + auto values( std::initializer_list values ) -> Generator { + return Generator( values.size(), pf::make_unique>( values ) ); + } + template + auto value( T const& val ) -> Generator { + return Generator( 1, pf::make_unique>( val ) ); + } + + template + auto as() -> Generator { + return Generator( 0, pf::make_unique>() ); + } + + template + auto table( std::initializer_list>&& tuples ) -> Generator> { + return values>( std::forward>>( tuples ) ); + } + + template + struct Generators : GeneratorBase { + std::vector> m_generators; + + using type = T; + + Generators() : GeneratorBase( 0 ) {} + + void populate( T&& val ) { + m_size += 1; + m_generators.emplace_back( value( std::move( val ) ) ); + } + template + void populate( U&& val ) { + populate( T( std::move( val ) ) ); + } + void populate( Generator&& generator ) { + m_size += generator.size(); + m_generators.emplace_back( std::move( generator ) ); + } + + template + void populate( U&& valueOrGenerator, Gs... moreGenerators ) { + populate( std::forward( valueOrGenerator ) ); + populate( std::forward( moreGenerators )... ); + } + + auto operator[]( size_t index ) const -> T { + size_t sizes = 0; + for( auto const& gen : m_generators ) { + auto localIndex = index-sizes; + sizes += gen.size(); + if( index < sizes ) + return gen[localIndex]; + } + CATCH_INTERNAL_ERROR("Index '" << index << "' is out of range (" << sizes << ')'); + } + }; + + template + auto makeGenerators( Generator&& generator, Gs... moreGenerators ) -> Generators { + Generators generators; + generators.m_generators.reserve( 1+sizeof...(Gs) ); + generators.populate( std::move( generator ), std::forward( moreGenerators )... ); + return generators; + } + template + auto makeGenerators( Generator&& generator ) -> Generators { + Generators generators; + generators.populate( std::move( generator ) ); + return generators; + } + template + auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators { + return makeGenerators( value( std::forward( val ) ), std::forward( moreGenerators )... ); + } + template + auto makeGenerators( U&& val, Gs... moreGenerators ) -> Generators { + return makeGenerators( value( T( std::forward( val ) ) ), std::forward( moreGenerators )... ); + } + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + + template + // Note: The type after -> is weird, because VS2015 cannot parse + // the expression used in the typedef inside, when it is in + // return type. Yeah, ¯\_(ツ)_/¯ + auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval()[0]) { + using UnderlyingType = typename decltype(generatorExpression())::type; + + IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); + if( !tracker.hasGenerator() ) + tracker.setGenerator( pf::make_unique>( generatorExpression() ) ); + + auto const& generator = static_cast const&>( *tracker.getGenerator() ); + return generator[tracker.getIndex()]; + } + +} // namespace Generators +} // namespace Catch + +#define GENERATE( ... ) \ + Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) + +// end catch_generators.hpp + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// start catch_test_case_info.h + +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + struct ITestInvoker; + + struct TestCaseInfo { + enum SpecialProperties{ + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4, + NonPortable = 1 << 5, + Benchmark = 1 << 6 + }; + + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector const& _tags, + SourceLineInfo const& _lineInfo ); + + friend void setTags( TestCaseInfo& testCaseInfo, std::vector tags ); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string tagsAsString() const; + + std::string name; + std::string className; + std::string description; + std::vector tags; + std::vector lcaseTags; + SourceLineInfo lineInfo; + SpecialProperties properties; + }; + + class TestCase : public TestCaseInfo { + public: + + TestCase( ITestInvoker* testCase, TestCaseInfo&& info ); + + TestCase withName( std::string const& _newName ) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + + private: + std::shared_ptr test; + }; + + TestCase makeTestCase( ITestInvoker* testCase, + std::string const& className, + NameAndTags const& nameAndTags, + SourceLineInfo const& lineInfo ); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_case_info.h +// start catch_interfaces_runner.h + +namespace Catch { + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +// end catch_interfaces_runner.h + +#ifdef __OBJC__ +// start catch_objc.hpp + +#import + +#include + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +-(void) setUp; +-(void) tearDown; + +@end + +namespace Catch { + + class OcMethod : public ITestInvoker { + + public: + OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} + + virtual void invoke() const { + id obj = [[m_cls alloc] init]; + + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); + + arcSafeRelease( obj ); + } + private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; + }; + + namespace Detail{ + + inline std::string getAnnotation( Class cls, + std::string const& annotationName, + std::string const& testCaseName ) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString( selStr ); + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; + return ""; + } + } + + inline std::size_t registerTestMethods() { + std::size_t noTestMethods = 0; + int noClasses = objc_getClassList( nullptr, 0 ); + + Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); + + for( int c = 0; c < noClasses; c++ ) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList( cls, &count ); + for( u_int m = 0; m < count ; m++ ) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if( startsWith( methodName, "Catch_TestCase_" ) ) { + std::string testCaseName = methodName.substr( 15 ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); + const char* className = class_getName( cls ); + + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) ); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; + } + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) + + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + + struct StringHolder : MatcherBase{ + StringHolder( NSString* substr ) : m_substr( [substr copy] ){} + StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} + StringHolder() { + arcSafeRelease( m_substr ); + } + + bool match( NSString* arg ) const override { + return false; + } + + NSString* CATCH_ARC_STRONG m_substr; + }; + + struct Equals : StringHolder { + Equals( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str isEqualToString:m_substr]; + } + + std::string describe() const override { + return "equals string: " + Catch::Detail::stringify( m_substr ); + } + }; + + struct Contains : StringHolder { + Contains( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + std::string describe() const override { + return "contains string: " + Catch::Detail::stringify( m_substr ); + } + }; + + struct StartsWith : StringHolder { + StartsWith( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == 0; + } + + std::string describe() const override { + return "starts with: " + Catch::Detail::stringify( m_substr ); + } + }; + struct EndsWith : StringHolder { + EndsWith( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } + + std::string describe() const override { + return "ends with: " + Catch::Detail::stringify( m_substr ); + } + }; + + } // namespace NSStringMatchers + } // namespace Impl + + inline Impl::NSStringMatchers::Equals + Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } + + inline Impl::NSStringMatchers::Contains + Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } + + inline Impl::NSStringMatchers::StartsWith + StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + + } // namespace Matchers + + using namespace Matchers; + +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix +#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ +{ \ +return @ name; \ +} \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ +{ \ +return @ desc; \ +} \ +-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) + +#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) + +// end catch_objc.hpp +#endif + +#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES +// start catch_external_interfaces.h + +// start catch_reporter_bases.hpp + +// start catch_interfaces_reporter.h + +// start catch_config.hpp + +// start catch_test_spec_parser.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_test_spec.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_wildcard_pattern.h + +namespace Catch +{ + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); + virtual ~WildcardPattern() = default; + virtual bool matches( std::string const& str ) const; + + private: + std::string adjustCase( std::string const& str ) const; + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard = NoWildcard; + std::string m_pattern; + }; +} + +// end catch_wildcard_pattern.h +#include +#include +#include + +namespace Catch { + + class TestSpec { + struct Pattern { + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + }; + using PatternPtr = std::shared_ptr; + + class NamePattern : public Pattern { + public: + NamePattern( std::string const& name ); + virtual ~NamePattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + TagPattern( std::string const& tag ); + virtual ~TagPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + ExcludedPattern( PatternPtr const& underlyingPattern ); + virtual ~ExcludedPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + PatternPtr m_underlyingPattern; + }; + + struct Filter { + std::vector m_patterns; + + bool matches( TestCaseInfo const& testCase ) const; + }; + + public: + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; + + private: + std::vector m_filters; + + friend class TestSpecParser; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec.h +// start catch_interfaces_tag_alias_registry.h + +#include + +namespace Catch { + + struct TagAlias; + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// end catch_interfaces_tag_alias_registry.h +namespace Catch { + + class TestSpecParser { + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; + Mode m_mode = None; + bool m_exclusion = false; + std::size_t m_start = std::string::npos, m_pos = 0; + std::string m_arg; + std::vector m_escapeChars; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases = nullptr; + + public: + TestSpecParser( ITagAliasRegistry const& tagAliases ); + + TestSpecParser& parse( std::string const& arg ); + TestSpec testSpec(); + + private: + void visitChar( char c ); + void startNewMode( Mode mode, std::size_t start ); + void escape(); + std::string subString() const; + + template + void addPattern() { + std::string token = subString(); + for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) + token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); + m_escapeChars.clear(); + if( startsWith( token, "exclude:" ) ) { + m_exclusion = true; + token = token.substr( 8 ); + } + if( !token.empty() ) { + TestSpec::PatternPtr pattern = std::make_shared( token ); + if( m_exclusion ) + pattern = std::make_shared( pattern ); + m_currentFilter.m_patterns.push_back( pattern ); + } + m_exclusion = false; + m_mode = None; + } + + void addFilter(); + }; + TestSpec parseTestSpec( std::string const& arg ); + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec_parser.h +// start catch_interfaces_config.h + +#include +#include +#include +#include + +namespace Catch { + + enum class Verbosity { + Quiet = 0, + Normal, + High + }; + + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01, + NoTests = 0x02 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + struct RunTests { enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; + + class TestSpec; + + struct IConfig : NonCopyable { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutNoTests() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual int benchmarkResolutionMultiple() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + }; + + using IConfigPtr = std::shared_ptr; +} + +// end catch_interfaces_config.h +// Libstdc++ doesn't like incomplete classes for unique_ptr + +#include +#include +#include + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + + struct IStream; + + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; + + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; + + int abortAfter = -1; + unsigned int rngSeed = 0; + int benchmarkResolutionMultiple = 100; + + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; + + std::string outputFilename; + std::string name; + std::string processName; +#ifndef CATCH_CONFIG_DEFAULT_REPORTER +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; +#undef CATCH_CONFIG_DEFAULT_REPORTER + + std::vector testsOrTags; + std::vector sectionsToRun; + }; + + class Config : public IConfig { + public: + + Config() = default; + Config( ConfigData const& data ); + virtual ~Config() = default; + + std::string const& getFilename() const; + + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; + + std::string getProcessName() const; + std::string const& getReporterName() const; + + std::vector const& getTestsOrTags() const; + std::vector const& getSectionsToRun() const override; + + virtual TestSpec const& testSpec() const override; + bool hasTestFilters() const override; + + bool showHelp() const; + + // IConfig interface + bool allowThrows() const override; + std::ostream& stream() const override; + std::string name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + bool warnAboutNoTests() const override; + ShowDurations::OrNot showDurations() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + int benchmarkResolutionMultiple() const override; + UseColour::YesOrNo useColour() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; + + private: + + IStream const* openStream(); + ConfigData m_data; + + std::unique_ptr m_stream; + TestSpec m_testSpec; + bool m_hasTestFilters = false; + }; + +} // end namespace Catch + +// end catch_config.hpp +// start catch_assertionresult.h + +#include + +namespace Catch { + + struct AssertionResultData + { + AssertionResultData() = delete; + + AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); + + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; + + std::string reconstructExpression() const; + }; + + class AssertionResult { + public: + AssertionResult() = delete; + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +// end catch_assertionresult.h +// start catch_option.hpp + +namespace Catch { + + // An optional type + template + class Option { + public: + Option() : nullableValue( nullptr ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) + {} + + ~Option() { + reset(); + } + + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = nullptr; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } + + bool operator !() const { return nullableValue == nullptr; } + explicit operator bool() const { + return some(); + } + + private: + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; + }; + +} // end namespace Catch + +// end catch_option.hpp +#include +#include +#include +#include +#include + +namespace Catch { + + struct ReporterConfig { + explicit ReporterConfig( IConfigPtr const& _fullConfig ); + + ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); + + std::ostream& stream() const; + IConfigPtr fullConfig() const; + + private: + std::ostream* m_stream; + IConfigPtr m_fullConfig; + }; + + struct ReporterPreferences { + bool shouldRedirectStdOut = false; + bool shouldReportAllAssertions = false; + }; + + template + struct LazyStat : Option { + LazyStat& operator=( T const& _value ) { + Option::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option::reset(); + used = false; + } + bool used = false; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ); + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ); + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ); + + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = default; + AssertionStats& operator = ( AssertionStats && ) = default; + virtual ~AssertionStats(); + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; + virtual ~SectionStats(); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ); + + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; + virtual ~TestCaseStats(); + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ); + TestGroupStats( GroupInfo const& _groupInfo ); + + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; + virtual ~TestGroupStats(); + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); + + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; + virtual ~TestRunStats(); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + struct BenchmarkInfo { + std::string name; + }; + struct BenchmarkStats { + BenchmarkInfo info; + std::size_t iterations; + uint64_t elapsedTimeInNanoseconds; + }; + + struct IStreamingReporter { + virtual ~IStreamingReporter() = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + // static std::set getSupportedVerbosities() + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + // *** experimental *** + virtual void benchmarkStarting( BenchmarkInfo const& ) {} + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + + // *** experimental *** + virtual void benchmarkEnded( BenchmarkStats const& ) {} + + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + // Default empty implementation provided + virtual void fatalErrorEncountered( StringRef name ); + + virtual bool isMulti() const; + }; + using IStreamingReporterPtr = std::unique_ptr; + + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + using IReporterFactoryPtr = std::shared_ptr; + + struct IReporterRegistry { + using FactoryMap = std::map; + using Listeners = std::vector; + + virtual ~IReporterRegistry(); + virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; + }; + +} // end namespace Catch + +// end catch_interfaces_reporter.h +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result); + + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration( double duration ); + + template + struct StreamingReporterBase : IStreamingReporter { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + ~StreamingReporterBase() override = default; + + void noMatchingTestCases(std::string const&) override {} + + void testRunStarting(TestRunInfo const& _testRunInfo) override { + currentTestRunInfo = _testRunInfo; + } + void testGroupStarting(GroupInfo const& _groupInfo) override { + currentGroupInfo = _groupInfo; + } + + void testCaseStarting(TestCaseInfo const& _testInfo) override { + currentTestCaseInfo = _testInfo; + } + void sectionStarting(SectionInfo const& _sectionInfo) override { + m_sectionStack.push_back(_sectionInfo); + } + + void sectionEnded(SectionStats const& /* _sectionStats */) override { + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { + currentTestCaseInfo.reset(); + } + void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { + currentGroupInfo.reset(); + } + void testRunEnded(TestRunStats const& /* _testRunStats */) override { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + void skipTest(TestCaseInfo const&) override { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + IConfigPtr m_config; + std::ostream& stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + struct CumulativeReporterBase : IStreamingReporter { + template + struct Node { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + using ChildNodes = std::vector>; + T value; + ChildNodes children; + }; + struct SectionNode { + explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} + virtual ~SectionNode() = default; + + bool operator == (SectionNode const& other) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == (std::shared_ptr const& other) const { + return operator==(*other); + } + + SectionStats stats; + using ChildSections = std::vector>; + using Assertions = std::vector; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() (std::shared_ptr const& node) const { + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); + } + void operator=(BySectionInfo const&) = delete; + + private: + SectionInfo const& m_other; + }; + + using TestCaseNode = Node; + using TestGroupNode = Node; + using TestRunNode = Node; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } + ~CumulativeReporterBase() override = default; + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + void testRunStarting( TestRunInfo const& ) override {} + void testGroupStarting( GroupInfo const& ) override {} + + void testCaseStarting( TestCaseInfo const& ) override {} + + void sectionStarting( SectionInfo const& sectionInfo ) override { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + std::shared_ptr node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = std::make_shared( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + auto it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = std::make_shared( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = std::move(node); + } + + void assertionStarting(AssertionInfo const&) override {} + + bool assertionEnded(AssertionStats const& assertionStats) override { + assert(!m_sectionStack.empty()); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression(const_cast( assertionStats.assertionResult ) ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } + void sectionEnded(SectionStats const& sectionStats) override { + assert(!m_sectionStack.empty()); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& testCaseStats) override { + auto node = std::make_shared(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); + + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + void testGroupEnded(TestGroupStats const& testGroupStats) override { + auto node = std::make_shared(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + void testRunEnded(TestRunStats const& testRunStats) override { + auto node = std::make_shared(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + void skipTest(TestCaseInfo const&) override {} + + IConfigPtr m_config; + std::ostream& stream; + std::vector m_assertions; + std::vector>> m_sections; + std::vector> m_testCases; + std::vector> m_testGroups; + + std::vector> m_testRuns; + + std::shared_ptr m_rootSection; + std::shared_ptr m_deepestSection; + std::vector> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase( ReporterConfig const& _config ); + + void assertionStarting(AssertionInfo const&) override; + bool assertionEnded(AssertionStats const&) override; + }; + +} // end namespace Catch + +// end catch_reporter_bases.hpp +// start catch_console_colour.h + +namespace Catch { + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + BrightYellow = Bright | Yellow, + + // By intention + FileName = LightGrey, + Warning = BrightYellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = BrightYellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour( Code _colourCode ); + Colour( Colour&& other ) noexcept; + Colour& operator=( Colour&& other ) noexcept; + ~Colour(); + + // Use static method for one-shot changes + static void use( Code _colourCode ); + + private: + bool m_moved = false; + }; + + std::ostream& operator << ( std::ostream& os, Colour const& ); + +} // end namespace Catch + +// end catch_console_colour.h +// start catch_reporter_registrars.hpp + + +namespace Catch { + + template + class ReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); + } + + virtual std::string getDescription() const override { + return T::getDescription(); + } + }; + + public: + + explicit ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, std::make_shared() ); + } + }; + + template + class ListenerRegistrar { + + class ListenerFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); + } + virtual std::string getDescription() const override { + return std::string(); + } + }; + + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( std::make_shared() ); + } + }; +} + +#if !defined(CATCH_CONFIG_DISABLE) + +#define CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +#define CATCH_REGISTER_LISTENER( listenerType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#else // CATCH_CONFIG_DISABLE + +#define CATCH_REGISTER_REPORTER(name, reporterType) +#define CATCH_REGISTER_LISTENER(listenerType) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_reporter_registrars.hpp +// Allow users to base their work off existing reporters +// start catch_reporter_compact.h + +namespace Catch { + + struct CompactReporter : StreamingReporterBase { + + using StreamingReporterBase::StreamingReporterBase; + + ~CompactReporter() override; + + static std::string getDescription(); + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionEnded(SectionStats const& _sectionStats) override; + + void testRunEnded(TestRunStats const& _testRunStats) override; + + }; + +} // end namespace Catch + +// end catch_reporter_compact.h +// start catch_reporter_console.h + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + // Fwd decls + struct SummaryColumn; + class TablePrinter; + + struct ConsoleReporter : StreamingReporterBase { + std::unique_ptr m_tablePrinter; + + ConsoleReporter(ReporterConfig const& config); + ~ConsoleReporter() override; + static std::string getDescription(); + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionStarting(SectionInfo const& _sectionInfo) override; + void sectionEnded(SectionStats const& _sectionStats) override; + + void benchmarkStarting(BenchmarkInfo const& info) override; + void benchmarkEnded(BenchmarkStats const& stats) override; + + void testCaseEnded(TestCaseStats const& _testCaseStats) override; + void testGroupEnded(TestGroupStats const& _testGroupStats) override; + void testRunEnded(TestRunStats const& _testRunStats) override; + + private: + + void lazyPrint(); + + void lazyPrintWithoutClosingBenchmarkTable(); + void lazyPrintRunInfo(); + void lazyPrintGroupInfo(); + void printTestCaseAndSectionHeader(); + + void printClosedHeader(std::string const& _name); + void printOpenHeader(std::string const& _name); + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::string const& _string, std::size_t indent = 0); + + void printTotals(Totals const& totals); + void printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row); + + void printTotalsDivider(Totals const& totals); + void printSummaryDivider(); + + private: + bool m_headerPrinted = false; + }; + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +// end catch_reporter_console.h +// start catch_reporter_junit.h + +// start catch_xmlwriter.h + +#include + +namespace Catch { + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); + + void encodeTo( std::ostream& os ) const; + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ); + + ScopedElement( ScopedElement&& other ) noexcept; + ScopedElement& operator=( ScopedElement&& other ) noexcept; + + ~ScopedElement(); + + ScopedElement& writeText( std::string const& text, bool indent = true ); + + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer = nullptr; + }; + + XmlWriter( std::ostream& os = Catch::cout() ); + ~XmlWriter(); + + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; + + XmlWriter& startElement( std::string const& name ); + + ScopedElement scopedElement( std::string const& name ); + + XmlWriter& endElement(); + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); + + XmlWriter& writeAttribute( std::string const& name, bool attribute ); + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + ReusableStringStream rss; + rss << attribute; + return writeAttribute( name, rss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ); + + XmlWriter& writeComment( std::string const& text ); + + void writeStylesheetRef( std::string const& url ); + + XmlWriter& writeBlankLine(); + + void ensureTagClosed(); + + private: + + void writeDeclaration(); + + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector m_tags; + std::string m_indent; + std::ostream& m_os; + }; + +} + +// end catch_xmlwriter.h +namespace Catch { + + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter(ReporterConfig const& _config); + + ~JunitReporter() override; + + static std::string getDescription(); + + void noMatchingTestCases(std::string const& /*spec*/) override; + + void testRunStarting(TestRunInfo const& runInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testCaseInfo) override; + bool assertionEnded(AssertionStats const& assertionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEndedCumulative() override; + + void writeGroup(TestGroupNode const& groupNode, double suiteTime); + + void writeTestCase(TestCaseNode const& testCaseNode); + + void writeSection(std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode); + + void writeAssertions(SectionNode const& sectionNode); + void writeAssertion(AssertionStats const& stats); + + XmlWriter xml; + Timer suiteTimer; + std::string stdOutForSuite; + std::string stdErrForSuite; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; + }; + +} // end namespace Catch + +// end catch_reporter_junit.h +// start catch_reporter_xml.h + +namespace Catch { + class XmlReporter : public StreamingReporterBase { + public: + XmlReporter(ReporterConfig const& _config); + + ~XmlReporter() override; + + static std::string getDescription(); + + virtual std::string getStylesheetRef() const; + + void writeSourceInfo(SourceLineInfo const& sourceInfo); + + public: // StreamingReporterBase + + void noMatchingTestCases(std::string const& s) override; + + void testRunStarting(TestRunInfo const& testInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testInfo) override; + + void sectionStarting(SectionInfo const& sectionInfo) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& assertionStats) override; + + void sectionEnded(SectionStats const& sectionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEnded(TestRunStats const& testRunStats) override; + + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth = 0; + }; + +} // end namespace Catch + +// end catch_reporter_xml.h + +// end catch_external_interfaces.h +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +#ifdef CATCH_IMPL +// start catch_impl.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// Keep these here for external reporters +// start catch_test_case_tracker.h + +#include +#include +#include + +namespace Catch { +namespace TestCaseTracking { + + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); + }; + + struct ITracker; + + using ITrackerPtr = std::shared_ptr; + + struct ITracker { + virtual ~ITracker(); + + // static queries + virtual NameAndLocation const& nameAndLocation() const = 0; + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + + virtual ITracker& parent() = 0; + + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild( ITrackerPtr const& child ) = 0; + virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; + virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isIndexTracker() const = 0; + }; + + class TrackerContext { + + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; + + ITrackerPtr m_rootTracker; + ITracker* m_currentTracker = nullptr; + RunState m_runState = NotStarted; + + public: + + static TrackerContext& instance(); + + ITracker& startRun(); + void endRun(); + + void startCycle(); + void completeCycle(); + + bool completedCycle() const; + ITracker& currentTracker(); + void setCurrentTracker( ITracker* tracker ); + }; + + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + + using Children = std::vector; + NameAndLocation m_nameAndLocation; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState = NotStarted; + + public: + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + NameAndLocation const& nameAndLocation() const override; + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; + + void addChild( ITrackerPtr const& child ) override; + + ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; + ITracker& parent() override; + + void openChild() override; + + bool isSectionTracker() const override; + bool isIndexTracker() const override; + + void open(); + + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; + + private: + void moveToParent(); + void moveToThis(); + }; + + class SectionTracker : public TrackerBase { + std::vector m_filters; + public: + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + bool isSectionTracker() const override; + + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); + + void tryOpen(); + + void addInitialFilters( std::vector const& filters ); + void addNextFilters( std::vector const& filters ); + }; + + class IndexTracker : public TrackerBase { + int m_size; + int m_index = -1; + public: + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ); + + bool isIndexTracker() const override; + void close() override; + + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ); + + int index() const; + + void moveNext(); + }; + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +// end catch_test_case_tracker.h + +// start catch_leak_detector.h + +namespace Catch { + + struct LeakDetector { + LeakDetector(); + }; + +} +// end catch_leak_detector.h +// Cpp files will be included in the single-header file here +// start catch_approx.cpp + +#include +#include + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} + +namespace Catch { +namespace Detail { + + Approx::Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_margin( 0.0 ), + m_scale( 0.0 ), + m_value( value ) + {} + + Approx Approx::custom() { + return Approx( 0 ); + } + + Approx Approx::operator-() const { + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; + } + + std::string Approx::toString() const { + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; + return rss.str(); + } + + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value))); + } + + void Approx::setMargin(double margin) { + CATCH_ENFORCE(margin >= 0, + "Invalid Approx::margin: " << margin << '.' + << " Approx::Margin has to be non-negative."); + m_margin = margin; + } + + void Approx::setEpsilon(double epsilon) { + CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0, + "Invalid Approx::epsilon: " << epsilon << '.' + << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = epsilon; + } + +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val) { + return Detail::Approx(val); + } + Detail::Approx operator "" _a(unsigned long long val) { + return Detail::Approx(val); + } +} // end namespace literals + +std::string StringMaker::convert(Catch::Detail::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch +// end catch_approx.cpp +// start catch_assertionhandler.cpp + +// start catch_context.h + +#include + +namespace Catch { + + struct IResultCapture; + struct IRunner; + struct IConfig; + struct IMutableContext; + + using IConfigPtr = std::shared_ptr; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual IConfigPtr const& getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( IConfigPtr const& config ) = 0; + + private: + static IMutableContext *currentContext; + friend IMutableContext& getCurrentMutableContext(); + friend void cleanUpContext(); + static void createContext(); + }; + + inline IMutableContext& getCurrentMutableContext() + { + if( !IMutableContext::currentContext ) + IMutableContext::createContext(); + return *IMutableContext::currentContext; + } + + inline IContext& getCurrentContext() + { + return getCurrentMutableContext(); + } + + void cleanUpContext(); +} + +// end catch_context.h +// start catch_debugger.h + +namespace Catch { + bool isDebuggerActive(); +} + +#ifdef CATCH_PLATFORM_MAC + + #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ + +#elif defined(CATCH_PLATFORM_LINUX) + // If we can use inline assembler, do it because this allows us to break + // directly at the location of the failing check instead of breaking inside + // raise() called from it, i.e. one stack frame below. + #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) + #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ + #else // Fall back to the generic way. + #include + + #define CATCH_TRAP() raise(SIGTRAP) + #endif +#elif defined(_MSC_VER) + #define CATCH_TRAP() __debugbreak() +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_TRAP() DebugBreak() +#endif + +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } +#else + namespace Catch { + inline void doNothing() {} + } + #define CATCH_BREAK_INTO_DEBUGGER() Catch::doNothing() +#endif + +// end catch_debugger.h +// start catch_run_context.h + +// start catch_fatal_condition.h + +// start catch_windows_h_proxy.h + + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINED_NOMINMAX +# define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + + struct FatalConditionHandler { + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); + FatalConditionHandler(); + static void reset(); + ~FatalConditionHandler(); + + private: + static bool isSet; + static ULONG guaranteeSize; + static PVOID exceptionHandlerHandle; + }; + +} // namespace Catch + +#elif defined ( CATCH_CONFIG_POSIX_SIGNALS ) + +#include + +namespace Catch { + + struct FatalConditionHandler { + + static bool isSet; + static struct sigaction oldSigActions[]; + static stack_t oldSigStack; + static char altStackMem[]; + + static void handleSignal( int sig ); + + FatalConditionHandler(); + ~FatalConditionHandler(); + static void reset(); + }; + +} // namespace Catch + +#else + +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + +#endif + +// end catch_fatal_condition.h +#include + +namespace Catch { + + struct IMutableContext; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + public: + RunContext( RunContext const& ) = delete; + RunContext& operator =( RunContext const& ) = delete; + + explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter ); + + ~RunContext() override; + + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ); + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ); + + Totals runTest(TestCase const& testCase); + + IConfigPtr config() const; + IStreamingReporter& reporter() const; + + public: // IResultCapture + + // Assertion handlers + void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) override; + void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) override; + void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) override; + void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) override; + void handleIncomplete + ( AssertionInfo const& info ) override; + void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) override; + + bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; + + void sectionEnded( SectionEndInfo const& endInfo ) override; + void sectionEndedEarly( SectionEndInfo const& endInfo ) override; + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; + + void benchmarkStarting( BenchmarkInfo const& info ) override; + void benchmarkEnded( BenchmarkStats const& stats ) override; + + void pushScopedMessage( MessageInfo const& message ) override; + void popScopedMessage( MessageInfo const& message ) override; + + std::string getCurrentTestName() const override; + + const AssertionResult* getLastResult() const override; + + void exceptionEarlyReported() override; + + void handleFatalErrorCondition( StringRef message ) override; + + bool lastAssertionPassed() override; + + void assertionPassed() override; + + public: + // !TBD We need to do this another way! + bool aborting() const final; + + private: + + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); + void invokeActiveTestCase(); + + void resetAssertionInfo(); + bool testForMissingAssertions( Counts& assertions ); + + void assertionEnded( AssertionResult const& result ); + void reportExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ); + + void populateReaction( AssertionReaction& reaction ); + + private: + + void handleUnfinishedSections(); + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase = nullptr; + ITracker* m_testCaseTracker; + Option m_lastResult; + + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr m_reporter; + std::vector m_messages; + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + bool m_lastAssertionPassed = false; + bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; + }; + +} // end namespace Catch + +// end catch_run_context.h +namespace Catch { + + namespace { + auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { + expr.streamReconstructedExpression( os ); + return os; + } + } + + LazyExpression::LazyExpression( bool isNegated ) + : m_isNegated( isNegated ) + {} + + LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} + + LazyExpression::operator bool() const { + return m_transientExpression != nullptr; + } + + auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { + if( lazyExpr.m_isNegated ) + os << "!"; + + if( lazyExpr ) { + if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) + os << "(" << *lazyExpr.m_transientExpression << ")"; + else + os << *lazyExpr.m_transientExpression; + } + else { + os << "{** error - unchecked empty expression requested **}"; + } + return os; + } + + AssertionHandler::AssertionHandler + ( StringRef const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ) + : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, + m_resultCapture( getResultCapture() ) + {} + + void AssertionHandler::handleExpr( ITransientExpression const& expr ) { + m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); + } + void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) { + m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); + } + + auto AssertionHandler::allowThrows() const -> bool { + return getCurrentContext().getConfig()->allowThrows(); + } + + void AssertionHandler::complete() { + setCompleted(); + if( m_reaction.shouldDebugBreak ) { + + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) + + // (To go back to the test and change execution, jump over the throw, next) + CATCH_BREAK_INTO_DEBUGGER(); + } + if (m_reaction.shouldThrow) { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + throw Catch::TestFailureException(); +#else + CATCH_ERROR( "Test failure requires aborting test!" ); +#endif + } + } + void AssertionHandler::setCompleted() { + m_completed = true; + } + + void AssertionHandler::handleUnexpectedInflightException() { + m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); + } + + void AssertionHandler::handleExceptionThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + void AssertionHandler::handleExceptionNotThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + void AssertionHandler::handleUnexpectedExceptionNotThrown() { + m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction ); + } + + void AssertionHandler::handleThrowingCallSkipped() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + // This is the overload that takes a string and infers the Equals matcher from it + // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ) { + handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); + } + +} // namespace Catch +// end catch_assertionhandler.cpp +// start catch_assertionresult.cpp + +namespace Catch { + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): + lazyExpression(_lazyExpression), + resultType(_resultType) {} + + std::string AssertionResultData::reconstructExpression() const { + + if( reconstructedExpression.empty() ) { + if( lazyExpression ) { + ReusableStringStream rss; + rss << lazyExpression; + reconstructedExpression = rss.str(); + } + } + return reconstructedExpression; + } + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return m_info.capturedExpression[0] != 0; + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return "!(" + m_info.capturedExpression + ")"; + else + return m_info.capturedExpression; + } + + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if( m_info.macroName[0] == 0 ) + expr = m_info.capturedExpression; + else { + expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; + } + return expr; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() + ? getExpression() + : expr; + } + + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + StringRef AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch +// end catch_assertionresult.cpp +// start catch_benchmark.cpp + +namespace Catch { + + auto BenchmarkLooper::getResolution() -> uint64_t { + return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple(); + } + + void BenchmarkLooper::reportStart() { + getResultCapture().benchmarkStarting( { m_name } ); + } + auto BenchmarkLooper::needsMoreIterations() -> bool { + auto elapsed = m_timer.getElapsedNanoseconds(); + + // Exponentially increasing iterations until we're confident in our timer resolution + if( elapsed < m_resolution ) { + m_iterationsToRun *= 10; + return true; + } + + getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } ); + return false; + } + +} // end namespace Catch +// end catch_benchmark.cpp +// start catch_capture_matchers.cpp + +namespace Catch { + + using StringMatcher = Matchers::Impl::MatcherBase; + + // This is the general overload that takes a any string matcher + // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers + // the Equals matcher (so the header does not mention matchers) + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr( exceptionMessage, matcher, matcherString ); + handler.handleExpr( expr ); + } + +} // namespace Catch +// end catch_capture_matchers.cpp +// start catch_commandline.cpp + +// start catch_commandline.h + +// start catch_clara.h + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#endif +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wshadow" +#endif + +// start clara.hpp +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See https://github.com/philsquared/Clara for more details + +// Clara v1.1.4 + + +#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#ifndef CLARA_CONFIG_OPTIONAL_TYPE +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +#include +#define CLARA_CONFIG_OPTIONAL_TYPE std::optional +#endif +#endif +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// This work is licensed under the BSD 2-Clause license. +// See the accompanying LICENSE file, or the one at https://opensource.org/licenses/BSD-2-Clause +// +// This project is hosted at https://github.com/philsquared/textflowcpp + + +#include +#include +#include +#include + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { namespace clara { namespace TextFlow { + + inline auto isWhitespace( char c ) -> bool { + static std::string chars = " \t\n\r"; + return chars.find( c ) != std::string::npos; + } + inline auto isBreakableBefore( char c ) -> bool { + static std::string chars = "[({<|"; + return chars.find( c ) != std::string::npos; + } + inline auto isBreakableAfter( char c ) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find( c ) != std::string::npos; + } + + class Columns; + + class Column { + std::vector m_strings; + size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + + public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator( Column const& column, size_t stringIndex ) + : m_column( column ), + m_stringIndex( stringIndex ) + {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary( size_t at ) const -> bool { + assert( at > 0 ); + assert( at <= line().size() ); + + return at == line().size() || + ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || + isBreakableBefore( line()[at] ) || + isBreakableAfter( line()[at-1] ); + } + + void calcLength() { + assert( m_stringIndex < m_column.m_strings.size() ); + + m_suffix = false; + auto width = m_column.m_width-indent(); + m_end = m_pos; + while( m_end < line().size() && line()[m_end] != '\n' ) + ++m_end; + + if( m_end < m_pos + width ) { + m_len = m_end - m_pos; + } + else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); + } + + public: + explicit iterator( Column const& column ) : m_column( column ) { + assert( m_column.m_width > m_column.m_indent ); + assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); + calcLength(); + if( m_len == 0 ) + m_stringIndex++; // Empty string + } + + auto operator *() const -> std::string { + assert( m_stringIndex < m_column.m_strings.size() ); + assert( m_pos <= m_end ); + if( m_pos + m_column.m_width < m_end ) + return addIndentAndSuffix(line().substr(m_pos, m_len)); + else + return addIndentAndSuffix(line().substr(m_pos, m_end - m_pos)); + } + + auto operator ++() -> iterator& { + m_pos += m_len; + if( m_pos < line().size() && line()[m_pos] == '\n' ) + m_pos += 1; + else + while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) + ++m_pos; + + if( m_pos == line().size() ) { + m_pos = 0; + ++m_stringIndex; + } + if( m_stringIndex < m_column.m_strings.size() ) + calcLength(); + return *this; + } + auto operator ++(int) -> iterator { + iterator prev( *this ); + operator++(); + return prev; + } + + auto operator ==( iterator const& other ) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator !=( iterator const& other ) const -> bool { + return !operator==( other ); + } + }; + using const_iterator = iterator; + + explicit Column( std::string const& text ) { m_strings.push_back( text ); } + + auto width( size_t newWidth ) -> Column& { + assert( newWidth > 0 ); + m_width = newWidth; + return *this; + } + auto indent( size_t newIndent ) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent( size_t newIndent ) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator( *this ); } + auto end() const -> iterator { return { *this, m_strings.size() }; } + + inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { + bool first = true; + for( auto line : col ) { + if( first ) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator + ( Column const& other ) -> Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + }; + + class Spacer : public Column { + + public: + explicit Spacer( size_t spaceWidth ) : Column( "" ) { + width( spaceWidth ); + } + }; + + class Columns { + std::vector m_columns; + + public: + + class iterator { + friend Columns; + struct EndTag {}; + + std::vector const& m_columns; + std::vector m_iterators; + size_t m_activeIterators; + + iterator( Columns const& columns, EndTag ) + : m_columns( columns.m_columns ), + m_activeIterators( 0 ) + { + m_iterators.reserve( m_columns.size() ); + + for( auto const& col : m_columns ) + m_iterators.push_back( col.end() ); + } + + public: + explicit iterator( Columns const& columns ) + : m_columns( columns.m_columns ), + m_activeIterators( m_columns.size() ) + { + m_iterators.reserve( m_columns.size() ); + + for( auto const& col : m_columns ) + m_iterators.push_back( col.begin() ); + } + + auto operator ==( iterator const& other ) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator !=( iterator const& other ) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator *() const -> std::string { + std::string row, padding; + + for( size_t i = 0; i < m_columns.size(); ++i ) { + auto width = m_columns[i].width(); + if( m_iterators[i] != m_columns[i].end() ) { + std::string col = *m_iterators[i]; + row += padding + col; + if( col.size() < width ) + padding = std::string( width - col.size(), ' ' ); + else + padding = ""; + } + else { + padding += std::string( width, ' ' ); + } + } + return row; + } + auto operator ++() -> iterator& { + for( size_t i = 0; i < m_columns.size(); ++i ) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator ++(int) -> iterator { + iterator prev( *this ); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator( *this ); } + auto end() const -> iterator { return { *this, iterator::EndTag() }; } + + auto operator += ( Column const& col ) -> Columns& { + m_columns.push_back( col ); + return *this; + } + auto operator + ( Column const& col ) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { + + bool first = true; + for( auto line : cols ) { + if( first ) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + }; + + inline auto Column::operator + ( Column const& other ) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; + } +}}} // namespace Catch::clara::TextFlow + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + +#include +#include +#include + +#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CATCH_PLATFORM_WINDOWS +#endif + +namespace Catch { namespace clara { +namespace detail { + + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template + struct UnaryLambdaTraits : UnaryLambdaTraits {}; + + template + struct UnaryLambdaTraits { + static const bool isValid = false; + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const::type>::type; + using ReturnType = ReturnT; + }; + + class TokenStream; + + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector m_args; + + public: + Args( int argc, char const* const* argv ) + : m_exeName(argv[0]), + m_args(argv + 1, argv + argc) {} + + Args( std::initializer_list args ) + : m_exeName( *args.begin() ), + m_args( args.begin()+1, args.end() ) + {} + + auto exeName() const -> std::string { + return m_exeName; + } + }; + + // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string + // may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, Argument + }; + struct Token { + TokenType type; + std::string token; + }; + + inline auto isOptPrefix( char c ) -> bool { + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize( 0 ); + + // Skip any empty strings + while( it != itEnd && it->empty() ) + ++it; + + if( it != itEnd ) { + auto const &next = *it; + if( isOptPrefix( next[0] ) ) { + auto delimiterPos = next.find_first_of( " :=" ); + if( delimiterPos != std::string::npos ) { + m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); + m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); + } else { + if( next[1] != '-' && next.size() > 2 ) { + std::string opt = "- "; + for( size_t i = 1; i < next.size(); ++i ) { + opt[1] = next[i]; + m_tokenBuffer.push_back( { TokenType::Option, opt } ); + } + } else { + m_tokenBuffer.push_back( { TokenType::Option, next } ); + } + } + } else { + m_tokenBuffer.push_back( { TokenType::Argument, next } ); + } + } + } + + public: + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { + loadBuffer(); + } + + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } + + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); + } + + auto operator->() const -> Token const * { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); + } + + auto operator++() -> TokenStream & { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } + }; + + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError + }; + + protected: + ResultBase( Type type ) : m_type( type ) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; + }; + + template + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; + } + + protected: + ResultValueBase( Type type ) : ResultBase( type ) {} + + ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + } + + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); + } + + auto operator=( ResultValueBase const &other ) -> ResultValueBase & { + if( m_type == ResultBase::Ok ) + m_value.~T(); + ResultBase::operator=(other); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + return *this; + } + + ~ResultValueBase() override { + if( m_type == Ok ) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template<> + class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult( BasicResult const &other ) + : ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); + } + + template + static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } + static auto ok() -> BasicResult { return { ResultBase::Ok }; } + static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } + static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } + + protected: + void enforceOk() const override { + + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert( m_type != ResultBase::LogicError ); + assert( m_type != ResultBase::RuntimeError ); + if( m_type != ResultBase::Ok ) + std::abort(); + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; + }; + + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; + + class ParseState { + public: + + ParseState( ParseResultType type, TokenStream const &remainingTokens ) + : m_type(type), + m_remainingTokens( remainingTokens ) + {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template + inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + std::string srcLC = source; + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + } +#ifdef CLARA_CONFIG_OPTIONAL_TYPE + template + inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& target ) -> ParserResult { + T temp; + auto result = convertInto( source, temp ); + if( result ) + target = std::move(temp); + return result; + } +#endif // CLARA_CONFIG_OPTIONAL_TYPE + + struct NonCopyable { + NonCopyable() = default; + NonCopyable( NonCopyable const & ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable &operator=( NonCopyable const & ) = delete; + NonCopyable &operator=( NonCopyable && ) = delete; + }; + + struct BoundRef : NonCopyable { + virtual ~BoundRef() = default; + virtual auto isContainer() const -> bool { return false; } + virtual auto isFlag() const -> bool { return false; } + }; + struct BoundValueRefBase : BoundRef { + virtual auto setValue( std::string const &arg ) -> ParserResult = 0; + }; + struct BoundFlagRefBase : BoundRef { + virtual auto setFlag( bool flag ) -> ParserResult = 0; + virtual auto isFlag() const -> bool { return true; } + }; + + template + struct BoundValueRef : BoundValueRefBase { + T &m_ref; + + explicit BoundValueRef( T &ref ) : m_ref( ref ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); + } + }; + + template + struct BoundValueRef> : BoundValueRefBase { + std::vector &m_ref; + + explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + T temp; + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} + + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + struct LambdaInvoker { + static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); + + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); + } + }; + + template<> + struct LambdaInvoker { + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + ArgType temp{}; + auto result = convertInto( arg, temp ); + return !result + ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); + } + + template + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda::ArgType>( m_lambda, arg ); + } + }; + + template + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + struct Parser; + + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); + } + }; + + template + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|( T const &other ) const -> Parser; + + template + auto operator+( T const &other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} + + public: + template + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint( hint ) + {} + + template + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint(hint) + {} + + auto operator()( std::string const &description ) -> DerivedT & { + m_description = description; + return static_cast( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast( *this ); + }; + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast( *this ); + }; + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if( m_ref->isContainer() ) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + template + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { + return std::make_shared>( lambda) ; + } + + public: + ExeName() : m_name( std::make_shared( "" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared>( ref ); + } + + template + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared>( lambda ); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + } + + auto name() const -> std::string { return *m_name; } + auto set( std::string const& newName ) -> ParserResult { + + auto lastSlash = newName.find_last_of( "\\/" ); + auto filename = ( lastSlash == std::string::npos ) + ? newName + : newName.substr( lastSlash+1 ); + + *m_name = filename; + if( m_ref ) + return m_ref->setValue( filename ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + class Arg : public ParserRefImpl { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if( token.type != TokenType::Argument ) + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + + assert( !m_ref->isFlag() ); + auto valueRef = static_cast( m_ref.get() ); + + auto result = valueRef->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); + else + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + }; + + inline auto normaliseOpt( std::string const &optName ) -> std::string { +#ifdef CATCH_PLATFORM_WINDOWS + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else +#endif + return optName; + } + + class Opt : public ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} + + template + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template + Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); + return *this; + } + + auto getHelpColumns() const -> std::vector { + std::ostringstream oss; + bool first = true; + for( auto const &opt : m_optNames ) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if( !m_hint.empty() ) + oss << " <" << m_hint << ">"; + return { { oss.str(), m_description } }; + } + + auto isMatch( std::string const &optToken ) const -> bool { + auto normalisedToken = normaliseOpt( optToken ); + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + if( remainingTokens && remainingTokens->type == TokenType::Option ) { + auto const &token = *remainingTokens; + if( isMatch(token.token ) ) { + if( m_ref->isFlag() ) { + auto flagRef = static_cast( m_ref.get() ); + auto result = flagRef->setFlag( true ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } else { + auto valueRef = static_cast( m_ref.get() ); + ++remainingTokens; + if( !remainingTokens ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto const &argToken = *remainingTokens; + if( argToken.type != TokenType::Argument ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto result = valueRef->setValue( argToken.token ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + } + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + + auto validate() const -> Result override { + if( m_optNames.empty() ) + return Result::logicError( "No options supplied to Opt" ); + for( auto const &name : m_optNames ) { + if( name.empty() ) + return Result::logicError( "Option name cannot be empty" ); +#ifdef CATCH_PLATFORM_WINDOWS + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); +#else + if( name[0] != '-' ) + return Result::logicError( "Option name must begin with '-'" ); +#endif + } + return ParserRefImpl::validate(); + } + }; + + struct Help : Opt { + Help( bool &showHelpFlag ) + : Opt([&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + }) + { + static_cast( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; + + struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator|=( ExeName const &exeName ) -> Parser & { + m_exeName = exeName; + return *this; + } + + auto operator|=( Arg const &arg ) -> Parser & { + m_args.push_back(arg); + return *this; + } + + auto operator|=( Opt const &opt ) -> Parser & { + m_options.push_back(opt); + return *this; + } + + auto operator|=( Parser const &other ) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + // Forward deprecated interface with '+' instead of '|' + template + auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } + template + auto operator+( T const &other ) const -> Parser { return operator|( other ); } + + auto getHelpColumns() const -> std::vector { + std::vector cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); + } + return cols; + } + + void writeToStream( std::ostream &os ) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for( auto const &arg : m_args ) { + if (first) + first = false; + else + os << " "; + if( arg.isOptional() && required ) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if( arg.cardinality() == 0 ) + os << " ... "; + } + if( !required ) + os << "]"; + if( !m_options.empty() ) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for( auto const &cols : rows ) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + optWidth = (std::min)(optWidth, consoleWidth/2); + + for( auto const &cols : rows ) { + auto row = + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + + TextFlow::Spacer(4) + + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); + os << row << std::endl; + } + } + + friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { + parser.writeToStream( os ); + return os; + } + + auto validate() const -> Result override { + for( auto const &opt : m_options ) { + auto result = opt.validate(); + if( !result ) + return result; + } + for( auto const &arg : m_args ) { + auto result = arg.validate(); + if( !result ) + return result; + } + return Result::ok(); + } + + using ParserBase::parse; + + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert( totalParsers < 512 ); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) parseInfos[i++].parser = &arg; + } + + m_exeName.set( exeName ); + + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( size_t i = 0; i < totalParsers; ++i ) { + auto& parseInfo = parseInfos[i]; + if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if( result.value().type() == ParseResultType::ShortCircuitAll ) + return result; + if( !tokenParsed ) + return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; + } + }; + + template + template + auto ComposableParserImpl::operator|( T const &other ) const -> Parser { + return Parser() | static_cast( *this ) | other; + } +} // namespace detail + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + +}} // namespace Catch::clara + +// end clara.hpp +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// end catch_clara.h +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ); + +} // end namespace Catch + +// end catch_commandline.h +#include +#include + +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ) { + + using namespace clara; + + auto const setWarning = [&]( std::string const& warning ) { + auto warningSet = [&]() { + if( warning == "NoAssertions" ) + return WarnAbout::NoAssertions; + + if ( warning == "NoTests" ) + return WarnAbout::NoTests; + + return WarnAbout::Nothing; + }(); + + if (warningSet == WarnAbout::Nothing) + return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); + config.warnings = static_cast( config.warnings | warningSet ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const loadTestNamesFromFile = [&]( std::string const& filename ) { + std::ifstream f( filename.c_str() ); + if( !f.is_open() ) + return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + config.testsOrTags.push_back( line + ',' ); + } + } + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setTestOrder = [&]( std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setRngSeed = [&]( std::string const& seed ) { + if( seed != "time" ) + return clara::detail::convertInto( seed, config.rngSeed ); + config.rngSeed = static_cast( std::time(nullptr) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setColourUsage = [&]( std::string const& useColour ) { + auto mode = toLower( useColour ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setWaitForKeypress = [&]( std::string const& keypress ) { + auto keypressLc = toLower( keypress ); + if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setVerbosity = [&]( std::string const& verbosity ) { + auto lcVerbosity = toLower( verbosity ); + if( lcVerbosity == "quiet" ) + config.verbosity = Verbosity::Quiet; + else if( lcVerbosity == "normal" ) + config.verbosity = Verbosity::Normal; + else if( lcVerbosity == "high" ) + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + + auto cli + = ExeName( config.processName ) + | Help( config.showHelp ) + | Opt( config.listTests ) + ["-l"]["--list-tests"] + ( "list all/matching test cases" ) + | Opt( config.listTags ) + ["-t"]["--list-tags"] + ( "list all/matching tags" ) + | Opt( config.showSuccessfulTests ) + ["-s"]["--success"] + ( "include successful tests in output" ) + | Opt( config.shouldDebugBreak ) + ["-b"]["--break"] + ( "break into debugger on failure" ) + | Opt( config.noThrow ) + ["-e"]["--nothrow"] + ( "skip exception tests" ) + | Opt( config.showInvisibles ) + ["-i"]["--invisibles"] + ( "show invisibles (tabs, newlines)" ) + | Opt( config.outputFilename, "filename" ) + ["-o"]["--out"] + ( "output filename" ) + | Opt( config.reporterName, "name" ) + ["-r"]["--reporter"] + ( "reporter to use (defaults to console)" ) + | Opt( config.name, "name" ) + ["-n"]["--name"] + ( "suite name" ) + | Opt( [&]( bool ){ config.abortAfter = 1; } ) + ["-a"]["--abort"] + ( "abort at first failure" ) + | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) + ["-x"]["--abortx"] + ( "abort after x failures" ) + | Opt( setWarning, "warning name" ) + ["-w"]["--warn"] + ( "enable warnings" ) + | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) + ["-d"]["--durations"] + ( "show test durations" ) + | Opt( loadTestNamesFromFile, "filename" ) + ["-f"]["--input-file"] + ( "load test names to run from a file" ) + | Opt( config.filenamesAsTags ) + ["-#"]["--filenames-as-tags"] + ( "adds a tag for the filename" ) + | Opt( config.sectionsToRun, "section name" ) + ["-c"]["--section"] + ( "specify section to run" ) + | Opt( setVerbosity, "quiet|normal|high" ) + ["-v"]["--verbosity"] + ( "set output verbosity" ) + | Opt( config.listTestNamesOnly ) + ["--list-test-names-only"] + ( "list all/matching test cases names only" ) + | Opt( config.listReporters ) + ["--list-reporters"] + ( "list all reporters" ) + | Opt( setTestOrder, "decl|lex|rand" ) + ["--order"] + ( "test case order (defaults to decl)" ) + | Opt( setRngSeed, "'time'|number" ) + ["--rng-seed"] + ( "set a specific seed for random numbers" ) + | Opt( setColourUsage, "yes|no" ) + ["--use-colour"] + ( "should output be colourised" ) + | Opt( config.libIdentify ) + ["--libidentify"] + ( "report name and version according to libidentify standard" ) + | Opt( setWaitForKeypress, "start|exit|both" ) + ["--wait-for-keypress"] + ( "waits for a keypress before exiting" ) + | Opt( config.benchmarkResolutionMultiple, "multiplier" ) + ["--benchmark-resolution-multiple"] + ( "multiple of clock resolution to run benchmarks" ) + + | Arg( config.testsOrTags, "test name|pattern|tags" ) + ( "which test or tests to use" ); + + return cli; + } + +} // end namespace Catch +// end catch_commandline.cpp +// start catch_common.cpp + +#include +#include + +namespace Catch { + + bool SourceLineInfo::empty() const noexcept { + return file[0] == '\0'; + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { + // We can assume that the same file will usually have the same pointer. + // Thus, if the pointers are the same, there is no point in calling the strcmp + return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << '(' << info.line << ')'; +#else + os << info.file << ':' << info.line; +#endif + return os; + } + + std::string StreamEndStop::operator+() const { + return std::string(); + } + + NonCopyable::NonCopyable() = default; + NonCopyable::~NonCopyable() = default; + +} +// end catch_common.cpp +// start catch_config.cpp + +namespace Catch { + + Config::Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + TestSpecParser parser(ITagAliasRegistry::get()); + if (data.testsOrTags.empty()) { + parser.parse("~[.]"); // All not hidden tests + } + else { + m_hasTestFilters = true; + for( auto const& testOrTags : data.testsOrTags ) + parser.parse( testOrTags ); + } + m_testSpec = parser.testSpec(); + } + + std::string const& Config::getFilename() const { + return m_data.outputFilename ; + } + + bool Config::listTests() const { return m_data.listTests; } + bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool Config::listTags() const { return m_data.listTags; } + bool Config::listReporters() const { return m_data.listReporters; } + + std::string Config::getProcessName() const { return m_data.processName; } + std::string const& Config::getReporterName() const { return m_data.reporterName; } + + std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; } + std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + + TestSpec const& Config::testSpec() const { return m_testSpec; } + bool Config::hasTestFilters() const { return m_hasTestFilters; } + + bool Config::showHelp() const { return m_data.showHelp; } + + // IConfig interface + bool Config::allowThrows() const { return !m_data.noThrow; } + std::ostream& Config::stream() const { return m_stream->stream(); } + std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); } + bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } + ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } + unsigned int Config::rngSeed() const { return m_data.rngSeed; } + int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; } + UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } + bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } + int Config::abortAfter() const { return m_data.abortAfter; } + bool Config::showInvisibles() const { return m_data.showInvisibles; } + Verbosity Config::verbosity() const { return m_data.verbosity; } + + IStream const* Config::openStream() { + return Catch::makeStream(m_data.outputFilename); + } + +} // end namespace Catch +// end catch_config.cpp +// start catch_console_colour.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +// start catch_errno_guard.h + +namespace Catch { + + class ErrnoGuard { + public: + ErrnoGuard(); + ~ErrnoGuard(); + private: + int m_oldErrno; + }; + +} + +// end catch_errno_guard.h +#include + +namespace Catch { + namespace { + + struct IColourImpl { + virtual ~IColourImpl() = default; + virtual void use( Colour::Code _colourCode ) = 0; + }; + + struct NoColourImpl : IColourImpl { + void use( Colour::Code ) {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + + } // anon namespace +} // namespace Catch + +#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) +# ifdef CATCH_PLATFORM_WINDOWS +# define CATCH_CONFIG_COLOUR_WINDOWS +# else +# define CATCH_CONFIG_COLOUR_ANSI +# endif +#endif + +#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// + +namespace Catch { +namespace { + + class Win32ColourImpl : public IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) + { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); + originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); + } + + virtual void use( Colour::Code _colourCode ) override { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalForegroundAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + + default: + CATCH_ERROR( "Unknown colour requested" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); + } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + + IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = UseColour::Yes; + return colourMode == UseColour::Yes + ? &s_instance + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// + +#include + +namespace Catch { +namespace { + + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public IColourImpl { + public: + virtual void use( Colour::Code _colourCode ) override { + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0;34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + case Colour::BrightYellow: return setColour( "[1;33m" ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); + } + } + static IColourImpl* instance() { + static PosixColourImpl s_instance; + return &s_instance; + } + + private: + void setColour( const char* _escapeCode ) { + Catch::cout() << '\033' << _escapeCode; + } + }; + + bool useColourOnPlatform() { + return +#ifdef CATCH_PLATFORM_MAC + !isDebuggerActive() && +#endif +#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) + isatty(STDOUT_FILENO) +#else + false +#endif + ; + } + IColourImpl* platformColourInstance() { + ErrnoGuard guard; + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = useColourOnPlatform() + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? PosixColourImpl::instance() + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#else // not Windows or ANSI /////////////////////////////////////////////// + +namespace Catch { + + static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + +} // end namespace Catch + +#endif // Windows/ ANSI/ None + +namespace Catch { + + Colour::Colour( Code _colourCode ) { use( _colourCode ); } + Colour::Colour( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + } + Colour& Colour::operator=( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + return *this; + } + + Colour::~Colour(){ if( !m_moved ) use( None ); } + + void Colour::use( Code _colourCode ) { + static IColourImpl* impl = platformColourInstance(); + impl->use( _colourCode ); + } + + std::ostream& operator << ( std::ostream& os, Colour const& ) { + return os; + } + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_console_colour.cpp +// start catch_context.cpp + +namespace Catch { + + class Context : public IMutableContext, NonCopyable { + + public: // IContext + virtual IResultCapture* getResultCapture() override { + return m_resultCapture; + } + virtual IRunner* getRunner() override { + return m_runner; + } + + virtual IConfigPtr const& getConfig() const override { + return m_config; + } + + virtual ~Context() override; + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) override { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) override { + m_runner = runner; + } + virtual void setConfig( IConfigPtr const& config ) override { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IConfigPtr m_config; + IRunner* m_runner = nullptr; + IResultCapture* m_resultCapture = nullptr; + }; + + IMutableContext *IMutableContext::currentContext = nullptr; + + void IMutableContext::createContext() + { + currentContext = new Context(); + } + + void cleanUpContext() { + delete IMutableContext::currentContext; + IMutableContext::currentContext = nullptr; + } + IContext::~IContext() = default; + IMutableContext::~IMutableContext() = default; + Context::~Context() = default; +} +// end catch_context.cpp +// start catch_debug_console.cpp + +// start catch_debug_console.h + +#include + +namespace Catch { + void writeToDebugConsole( std::string const& text ); +} + +// end catch_debug_console.h +#ifdef CATCH_PLATFORM_WINDOWS + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } + +#else + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } + } + +#endif // Platform +// end catch_debug_console.cpp +// start catch_debugger.cpp + +#ifdef CATCH_PLATFORM_MAC + +# include +# include +# include +# include +# include +# include +# include + +namespace Catch { + + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + + int mib[4]; + struct kinfo_proc info; + std::size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch + +#elif defined(CATCH_PLATFORM_LINUX) + #include + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + bool isDebuggerActive() { return false; } + } +#endif // Platform +// end catch_debugger.cpp +// start catch_decomposer.cpp + +namespace Catch { + + ITransientExpression::~ITransientExpression() = default; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { + if( lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ) + os << lhs << " " << op << " " << rhs; + else + os << lhs << "\n" << op << "\n" << rhs; + } +} +// end catch_decomposer.cpp +// start catch_enforce.cpp + +namespace Catch { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) + [[noreturn]] + void throw_exception(std::exception const& e) { + Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; + std::terminate(); + } +#endif +} // namespace Catch; +// end catch_enforce.cpp +// start catch_errno_guard.cpp + +#include + +namespace Catch { + ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } +} +// end catch_errno_guard.cpp +// start catch_exception_translator_registry.cpp + +// start catch_exception_translator_registry.h + +#include +#include +#include + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry(); + virtual void registerTranslator( const IExceptionTranslator* translator ); + virtual std::string translateActiveException() const override; + std::string tryTranslators() const; + + private: + std::vector> m_translators; + }; +} + +// end catch_exception_translator_registry.h +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { + } + + void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( std::unique_ptr( translator ) ); + } + +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + std::string ExceptionTranslatorRegistry::translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::Detail::stringify( [exception description] ); + } +#else + // Compiling a mixed mode project with MSVC means that CLR + // exceptions will be caught in (...) as well. However, these + // do not fill-in std::current_exception and thus lead to crash + // when attempting rethrow. + // /EHa switch also causes structured exceptions to be caught + // here, but they fill-in current_exception properly, so + // at worst the output should be a little weird, instead of + // causing a crash. + if (std::current_exception() == nullptr) { + return "Non C++ exception. Possibly a CLR exception."; + } + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + std::rethrow_exception(std::current_exception()); + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + std::string ExceptionTranslatorRegistry::translateActiveException() const { + CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } +#endif + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if( m_translators.empty() ) + std::rethrow_exception(std::current_exception()); + else + return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); + } +} +// end catch_exception_translator_registry.cpp +// start catch_fatal_condition.cpp + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace { + // Report the error condition + void reportFatal( char const * const message ) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); + } +} + +#endif // signals/SEH handling + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; + + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + static SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + + LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const& def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + exceptionHandlerHandle = nullptr; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + void FatalConditionHandler::reset() { + if (isSet) { + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = nullptr; + isSet = false; + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + +bool FatalConditionHandler::isSet = false; +ULONG FatalConditionHandler::guaranteeSize = 0; +PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; + +} // namespace Catch + +#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace Catch { + + struct SignalDefs { + int id; + const char* name; + }; + + // 32kb for the alternate stack seems to be sufficient. However, this value + // is experimentally determined, so that's not guaranteed. + constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; + + static SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + + void FatalConditionHandler::handleSignal( int sig ) { + char const * name = ""; + for (auto const& def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = sigStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + + void FatalConditionHandler::reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + isSet = false; + } + } + + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[sigStackSize] = {}; + +} // namespace Catch + +#else + +namespace Catch { + void FatalConditionHandler::reset() {} +} + +#endif // signals/SEH handling + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +// end catch_fatal_condition.cpp +// start catch_generators.cpp + +// start catch_random_number_generator.h + +#include +#include + +namespace Catch { + + struct IConfig; + + std::mt19937& rng(); + void seedRng( IConfig const& config ); + unsigned int rngSeed(); + +} + +// end catch_random_number_generator.h +#include +#include + +namespace Catch { + +IGeneratorTracker::~IGeneratorTracker() {} + +namespace Generators { + + GeneratorBase::~GeneratorBase() {} + + std::vector randomiseIndices( size_t selectionSize, size_t sourceSize ) { + + assert( selectionSize <= sourceSize ); + std::vector indices; + indices.reserve( selectionSize ); + std::uniform_int_distribution uid( 0, sourceSize-1 ); + + std::set seen; + // !TBD: improve this algorithm + while( indices.size() < selectionSize ) { + auto index = uid( rng() ); + if( seen.insert( index ).second ) + indices.push_back( index ); + } + return indices; + } + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + return getResultCapture().acquireGeneratorTracker( lineInfo ); + } + + template<> + auto all() -> Generator { + return range( std::numeric_limits::min(), std::numeric_limits::max() ); + } + +} // namespace Generators +} // namespace Catch +// end catch_generators.cpp +// start catch_interfaces_capture.cpp + +namespace Catch { + IResultCapture::~IResultCapture() = default; +} +// end catch_interfaces_capture.cpp +// start catch_interfaces_config.cpp + +namespace Catch { + IConfig::~IConfig() = default; +} +// end catch_interfaces_config.cpp +// start catch_interfaces_exception.cpp + +namespace Catch { + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} +// end catch_interfaces_exception.cpp +// start catch_interfaces_registry_hub.cpp + +namespace Catch { + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} +// end catch_interfaces_registry_hub.cpp +// start catch_interfaces_reporter.cpp + +// start catch_reporter_listening.h + +namespace Catch { + + class ListeningReporter : public IStreamingReporter { + using Reporters = std::vector; + Reporters m_listeners; + IStreamingReporterPtr m_reporter = nullptr; + ReporterPreferences m_preferences; + + public: + ListeningReporter(); + + void addListener( IStreamingReporterPtr&& listener ); + void addReporter( IStreamingReporterPtr&& reporter ); + + public: // IStreamingReporter + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases( std::string const& spec ) override; + + static std::set getSupportedVerbosities(); + + void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; + void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override; + + void testRunStarting( TestRunInfo const& testRunInfo ) override; + void testGroupStarting( GroupInfo const& groupInfo ) override; + void testCaseStarting( TestCaseInfo const& testInfo ) override; + void sectionStarting( SectionInfo const& sectionInfo ) override; + void assertionStarting( AssertionInfo const& assertionInfo ) override; + + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded( AssertionStats const& assertionStats ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + void testCaseEnded( TestCaseStats const& testCaseStats ) override; + void testGroupEnded( TestGroupStats const& testGroupStats ) override; + void testRunEnded( TestRunStats const& testRunStats ) override; + + void skipTest( TestCaseInfo const& testInfo ) override; + bool isMulti() const override; + + }; + +} // end namespace Catch + +// end catch_reporter_listening.h +namespace Catch { + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& ReporterConfig::stream() const { return *m_stream; } + IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } + + TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} + + GroupInfo::GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + AssertionStats::AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; + + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + + AssertionStats::~AssertionStats() = default; + + SectionStats::SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + + SectionStats::~SectionStats() = default; + + TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + + TestCaseStats::~TestCaseStats() = default; + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + + TestGroupStats::~TestGroupStats() = default; + + TestRunStats::TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestRunStats::~TestRunStats() = default; + + void IStreamingReporter::fatalErrorEncountered( StringRef ) {} + bool IStreamingReporter::isMulti() const { return false; } + + IReporterFactory::~IReporterFactory() = default; + IReporterRegistry::~IReporterRegistry() = default; + +} // end namespace Catch +// end catch_interfaces_reporter.cpp +// start catch_interfaces_runner.cpp + +namespace Catch { + IRunner::~IRunner() = default; +} +// end catch_interfaces_runner.cpp +// start catch_interfaces_testcase.cpp + +namespace Catch { + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} +// end catch_interfaces_testcase.cpp +// start catch_leak_detector.cpp + +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include + +namespace Catch { + + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +} + +#else + + Catch::LeakDetector::LeakDetector() {} + +#endif +// end catch_leak_detector.cpp +// start catch_list.cpp + +// start catch_list.h + +#include + +namespace Catch { + + std::size_t listTests( Config const& config ); + + std::size_t listTestsNamesOnly( Config const& config ); + + struct TagInfo { + void add( std::string const& spelling ); + std::string all() const; + + std::set spellings; + std::size_t count = 0; + }; + + std::size_t listTags( Config const& config ); + + std::size_t listReporters( Config const& /*config*/ ); + + Option list( Config const& config ); + +} // end namespace Catch + +// end catch_list.h +// start catch_text.h + +namespace Catch { + using namespace clara::TextFlow; +} + +// end catch_text.h +#include +#include +#include + +namespace Catch { + + std::size_t listTests( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + } + + auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; + if( config.verbosity() >= Verbosity::High ) { + Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column( description ).indent(4) << std::endl; + } + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; + } + + if( !config.hasTestFilters() ) + Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; + else + Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; + return matchedTestCases.size(); + } + + std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + matchedTests++; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if ( config.verbosity() >= Verbosity::High ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; + } + return matchedTests; + } + + void TagInfo::add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + + std::string TagInfo::all() const { + std::string out; + for( auto const& spelling : spellings ) + out += "[" + spelling + "]"; + return out; + } + + std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCase : matchedTestCases ) { + for( auto const& tagName : testCase.getTestCaseInfo().tags ) { + std::string lcaseTagName = toLower( tagName ); + auto countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( auto const& tagCount : tagCounts ) { + ReusableStringStream rss; + rss << " " << std::setw(2) << tagCount.second.count << " "; + auto str = rss.str(); + auto wrapper = Column( tagCount.second.all() ) + .initialIndent( 0 ) + .indent( str.size() ) + .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); + Catch::cout() << str << wrapper << '\n'; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; + return tagCounts.size(); + } + + std::size_t listReporters( Config const& /*config*/ ) { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for( auto const& factoryKvp : factories ) + maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); + + for( auto const& factoryKvp : factories ) { + Catch::cout() + << Column( factoryKvp.first + ":" ) + .indent(2) + .width( 5+maxNameLen ) + + Column( factoryKvp.second->getDescription() ) + .initialIndent(0) + .indent(2) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) + << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); + } + + Option list( Config const& config ) { + Option listedCount; + if( config.listTests() ) + listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); + if( config.listTags() ) + listedCount = listedCount.valueOr(0) + listTags( config ); + if( config.listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters( config ); + return listedCount; + } + +} // end namespace Catch +// end catch_list.cpp +// start catch_matchers.cpp + +namespace Catch { +namespace Matchers { + namespace Impl { + + std::string MatcherUntypedBase::toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; + } + + MatcherUntypedBase::~MatcherUntypedBase() = default; + + } // namespace Impl +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch +// end catch_matchers.cpp +// start catch_matchers_floating.cpp + +// start catch_to_string.hpp + +#include + +namespace Catch { + template + std::string to_string(T const& t) { +#if defined(CATCH_CONFIG_CPP11_TO_STRING) + return std::to_string(t); +#else + ReusableStringStream rss; + rss << t; + return rss.str(); +#endif + } +} // end namespace Catch + +// end catch_to_string.hpp +#include +#include +#include + +namespace Catch { +namespace Matchers { +namespace Floating { +enum class FloatingPointKind : uint8_t { + Float, + Double +}; +} +} +} + +namespace { + +template +struct Converter; + +template <> +struct Converter { + static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); + Converter(float f) { + std::memcpy(&i, &f, sizeof(f)); + } + int32_t i; +}; + +template <> +struct Converter { + static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); + Converter(double d) { + std::memcpy(&i, &d, sizeof(d)); + } + int64_t i; +}; + +template +auto convert(T t) -> Converter { + return Converter(t); +} + +template +bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) { + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (std::isnan(lhs) || std::isnan(rhs)) { + return false; + } + + auto lc = convert(lhs); + auto rc = convert(rhs); + + if ((lc.i < 0) != (rc.i < 0)) { + // Potentially we can have +0 and -0 + return lhs == rhs; + } + + auto ulpDiff = std::abs(lc.i - rc.i); + return ulpDiff <= maxUlpDiff; +} + +} + +namespace Catch { +namespace Matchers { +namespace Floating { + WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + :m_target{ target }, m_margin{ margin } { + CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' + << " Margin has to be non-negative."); + } + + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool WithinAbsMatcher::match(double const& matchee) const { + return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); + } + + std::string WithinAbsMatcher::describe() const { + return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); + } + + WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType) + :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { + CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.' + << " ULPs have to be non-negative."); + } + +#if defined(__clang__) +#pragma clang diagnostic push +// Clang <3.5 reports on the default branch in the switch below +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + + bool WithinUlpsMatcher::match(double const& matchee) const { + switch (m_type) { + case FloatingPointKind::Float: + return almostEqualUlps(static_cast(matchee), static_cast(m_target), m_ulps); + case FloatingPointKind::Double: + return almostEqualUlps(matchee, m_target, m_ulps); + default: + CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" ); + } + } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + std::string WithinUlpsMatcher::describe() const { + return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); + } + +}// namespace Floating + +Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); +} + +Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); +} + +Floating::WithinAbsMatcher WithinAbs(double target, double margin) { + return Floating::WithinAbsMatcher(target, margin); +} + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.cpp +// start catch_matchers_generic.cpp + +std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) { + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } +} +// end catch_matchers_generic.cpp +// start catch_matchers_string.cpp + +#include + +namespace Catch { +namespace Matchers { + + namespace StdString { + + CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + } + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); + } + + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} + + bool EqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} + + bool ContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {} + + bool RegexMatcher::match(std::string const& matchee) const { + auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway + if (m_caseSensitivity == CaseSensitive::Choice::No) { + flags |= std::regex::icase; + } + auto reg = std::regex(m_regex, flags); + return std::regex_match(matchee, reg); + } + + std::string RegexMatcher::describe() const { + return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively"); + } + + } // namespace StdString + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + + StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) { + return StdString::RegexMatcher(regex, caseSensitivity); + } + +} // namespace Matchers +} // namespace Catch +// end catch_matchers_string.cpp +// start catch_message.cpp + +// start catch_uncaught_exceptions.h + +namespace Catch { + bool uncaught_exceptions(); +} // end namespace Catch + +// end catch_uncaught_exceptions.h +#include + +namespace Catch { + + MessageInfo::MessageInfo( StringRef const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + bool MessageInfo::operator==( MessageInfo const& other ) const { + return sequence == other.sequence; + } + + bool MessageInfo::operator<( MessageInfo const& other ) const { + return sequence < other.sequence; + } + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + + //////////////////////////////////////////////////////////////////////////// + + Catch::MessageBuilder::MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + :m_info(macroName, lineInfo, type) {} + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ) + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + + ScopedMessage::~ScopedMessage() { + if ( !uncaught_exceptions() ){ + getResultCapture().popScopedMessage(m_info); + } + } + + Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { + auto start = std::string::npos; + for( size_t pos = 0; pos <= names.size(); ++pos ) { + char c = names[pos]; + if( pos == names.size() || c == ' ' || c == '\t' || c == ',' || c == ']' ) { + if( start != std::string::npos ) { + m_messages.push_back( MessageInfo( macroName, lineInfo, resultType ) ); + m_messages.back().message = names.substr( start, pos-start) + " := "; + start = std::string::npos; + } + } + else if( c != '[' && c != ']' && start == std::string::npos ) + start = pos; + } + } + Capturer::~Capturer() { + if ( !uncaught_exceptions() ){ + assert( m_captured == m_messages.size() ); + for( size_t i = 0; i < m_captured; ++i ) + m_resultCapture.popScopedMessage( m_messages[i] ); + } + } + + void Capturer::captureValue( size_t index, StringRef value ) { + assert( index < m_messages.size() ); + m_messages[index].message += value; + m_resultCapture.pushScopedMessage( m_messages[index] ); + m_captured++; + } + +} // end namespace Catch +// end catch_message.cpp +// start catch_output_redirect.cpp + +// start catch_output_redirect.h +#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H + +#include +#include +#include + +namespace Catch { + + class RedirectedStream { + std::ostream& m_originalStream; + std::ostream& m_redirectionStream; + std::streambuf* m_prevBuf; + + public: + RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ); + ~RedirectedStream(); + }; + + class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + public: + RedirectedStdOut(); + auto str() const -> std::string; + }; + + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes + class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + public: + RedirectedStdErr(); + auto str() const -> std::string; + }; + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + + // Windows's implementation of std::tmpfile is terrible (it tries + // to create a file inside system folder, thus requiring elevated + // privileges for the binary), so we have to use tmpnam(_s) and + // create the file ourselves there. + class TempFile { + public: + TempFile(TempFile const&) = delete; + TempFile& operator=(TempFile const&) = delete; + TempFile(TempFile&&) = delete; + TempFile& operator=(TempFile&&) = delete; + + TempFile(); + ~TempFile(); + + std::FILE* getFile(); + std::string getContents(); + + private: + std::FILE* m_file = nullptr; + #if defined(_MSC_VER) + char m_buffer[L_tmpnam] = { 0 }; + #endif + }; + + class OutputRedirect { + public: + OutputRedirect(OutputRedirect const&) = delete; + OutputRedirect& operator=(OutputRedirect const&) = delete; + OutputRedirect(OutputRedirect&&) = delete; + OutputRedirect& operator=(OutputRedirect&&) = delete; + + OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); + ~OutputRedirect(); + + private: + int m_originalStdout = -1; + int m_originalStderr = -1; + TempFile m_stdoutFile; + TempFile m_stderrFile; + std::string& m_stdoutDest; + std::string& m_stderrDest; + }; + +#endif + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +// end catch_output_redirect.h +#include +#include +#include +#include +#include + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #include //_dup and _dup2 + #define dup _dup + #define dup2 _dup2 + #define fileno _fileno + #else + #include // dup and dup2 + #endif +#endif + +namespace Catch { + + RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) + : m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) + { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + + RedirectedStream::~RedirectedStream() { + m_originalStream.rdbuf( m_prevBuf ); + } + + RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} + auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + + RedirectedStdErr::RedirectedStdErr() + : m_cerr( Catch::cerr(), m_rss.get() ), + m_clog( Catch::clog(), m_rss.get() ) + {} + auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + +#if defined(_MSC_VER) + TempFile::TempFile() { + if (tmpnam_s(m_buffer)) { + CATCH_RUNTIME_ERROR("Could not get a temp filename"); + } + if (fopen_s(&m_file, m_buffer, "w")) { + char buffer[100]; + if (strerror_s(buffer, errno)) { + CATCH_RUNTIME_ERROR("Could not translate errno to a string"); + } + CATCH_RUNTIME_ERROR("Coul dnot open the temp file: '" << m_buffer << "' because: " << buffer); + } + } +#else + TempFile::TempFile() { + m_file = std::tmpfile(); + if (!m_file) { + CATCH_RUNTIME_ERROR("Could not create a temp file."); + } + } + +#endif + + TempFile::~TempFile() { + // TBD: What to do about errors here? + std::fclose(m_file); + // We manually create the file on Windows only, on Linux + // it will be autodeleted +#if defined(_MSC_VER) + std::remove(m_buffer); +#endif + } + + FILE* TempFile::getFile() { + return m_file; + } + + std::string TempFile::getContents() { + std::stringstream sstr; + char buffer[100] = {}; + std::rewind(m_file); + while (std::fgets(buffer, sizeof(buffer), m_file)) { + sstr << buffer; + } + return sstr.str(); + } + + OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : + m_originalStdout(dup(1)), + m_originalStderr(dup(2)), + m_stdoutDest(stdout_dest), + m_stderrDest(stderr_dest) { + dup2(fileno(m_stdoutFile.getFile()), 1); + dup2(fileno(m_stderrFile.getFile()), 2); + } + + OutputRedirect::~OutputRedirect() { + Catch::cout() << std::flush; + fflush(stdout); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush(stderr); + + dup2(m_originalStdout, 1); + dup2(m_originalStderr, 2); + + m_stdoutDest += m_stdoutFile.getContents(); + m_stderrDest += m_stderrFile.getContents(); + } + +#endif // CATCH_CONFIG_NEW_CAPTURE + +} // namespace Catch + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #undef dup + #undef dup2 + #undef fileno + #endif +#endif +// end catch_output_redirect.cpp +// start catch_random_number_generator.cpp + +namespace Catch { + + std::mt19937& rng() { + static std::mt19937 s_rng; + return s_rng; + } + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) { + std::srand( config.rngSeed() ); + rng().seed( config.rngSeed() ); + } + } + + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } +} +// end catch_random_number_generator.cpp +// start catch_registry_hub.cpp + +// start catch_test_case_registry_impl.h + +#include +#include +#include +#include + +namespace Catch { + + class TestCase; + struct IConfig; + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + + void enforceNoDuplicateTestCases( std::vector const& functions ); + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; + + virtual void registerTest( TestCase const& testCase ); + + std::vector const& getAllTests() const override; + std::vector const& getAllTestsSorted( IConfig const& config ) const override; + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class TestInvokerAsFunction : public ITestInvoker { + void(*m_testAsFunction)(); + public: + TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; + + void invoke() const override; + }; + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ); + + /////////////////////////////////////////////////////////////////////////// + +} // end namespace Catch + +// end catch_test_case_registry_impl.h +// start catch_reporter_registry.h + +#include + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + + public: + + ~ReporterRegistry() override; + + IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; + + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); + void registerListener( IReporterFactoryPtr const& factory ); + + FactoryMap const& getFactories() const override; + Listeners const& getListeners() const override; + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; +} + +// end catch_reporter_registry.h +// start catch_tag_alias_registry.h + +// start catch_tag_alias.h + +#include + +namespace Catch { + + struct TagAlias { + TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); + + std::string tag; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +// end catch_tag_alias.h +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + ~TagAliasRegistry() override; + TagAlias const* find( std::string const& alias ) const override; + std::string expandAliases( std::string const& unexpandedTestSpec ) const override; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map m_registry; + }; + +} // end namespace Catch + +// end catch_tag_alias_registry.h +// start catch_startup_exception_registry.h + +#include +#include + +namespace Catch { + + class StartupExceptionRegistry { + public: + void add(std::exception_ptr const& exception) noexcept; + std::vector const& getExceptions() const noexcept; + private: + std::vector m_exceptions; + }; + +} // end namespace Catch + +// end catch_startup_exception_registry.h +// start catch_singletons.hpp + +namespace Catch { + + struct ISingleton { + virtual ~ISingleton(); + }; + + void addSingleton( ISingleton* singleton ); + void cleanupSingletons(); + + template + class Singleton : SingletonImplT, public ISingleton { + + static auto getInternal() -> Singleton* { + static Singleton* s_instance = nullptr; + if( !s_instance ) { + s_instance = new Singleton; + addSingleton( s_instance ); + } + return s_instance; + } + + public: + static auto get() -> InterfaceT const& { + return *getInternal(); + } + static auto getMutable() -> MutableInterfaceT& { + return *getInternal(); + } + }; + +} // namespace Catch + +// end catch_singletons.hpp +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub, + private NonCopyable { + + public: // IRegistryHub + RegistryHub() = default; + IReporterRegistry const& getReporterRegistry() const override { + return m_reporterRegistry; + } + ITestCaseRegistry const& getTestCaseRegistry() const override { + return m_testCaseRegistry; + } + IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override { + return m_exceptionTranslatorRegistry; + } + ITagAliasRegistry const& getTagAliasRegistry() const override { + return m_tagAliasRegistry; + } + StartupExceptionRegistry const& getStartupExceptionRegistry() const override { + return m_exceptionRegistry; + } + + public: // IMutableRegistryHub + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerReporter( name, factory ); + } + void registerListener( IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerListener( factory ); + } + void registerTest( TestCase const& testInfo ) override { + m_testCaseRegistry.registerTest( testInfo ); + } + void registerTranslator( const IExceptionTranslator* translator ) override { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } + void registerStartupException() noexcept override { + m_exceptionRegistry.add(std::current_exception()); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + }; + } + + using RegistryHubSingleton = Singleton; + + IRegistryHub const& getRegistryHub() { + return RegistryHubSingleton::get(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return RegistryHubSingleton::getMutable(); + } + void cleanUp() { + cleanupSingletons(); + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } + +} // end namespace Catch +// end catch_registry_hub.cpp +// start catch_reporter_registry.cpp + +namespace Catch { + + ReporterRegistry::~ReporterRegistry() = default; + + IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { + auto it = m_factories.find( name ); + if( it == m_factories.end() ) + return nullptr; + return it->second->create( ReporterConfig( config ) ); + } + + void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { + m_factories.emplace(name, factory); + } + void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { + m_listeners.push_back( factory ); + } + + IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { + return m_factories; + } + IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { + return m_listeners; + } + +} +// end catch_reporter_registry.cpp +// start catch_result_type.cpp + +namespace Catch { + + bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } + + bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch +// end catch_result_type.cpp +// start catch_run_context.cpp + +#include +#include +#include + +namespace Catch { + + namespace Generators { + struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { + size_t m_index = static_cast( -1 ); + GeneratorBasePtr m_generator; + + GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + {} + ~GeneratorTracker(); + + static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { + std::shared_ptr tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = std::static_pointer_cast( childTracker ); + } + else { + tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + void moveNext() { + m_index++; + m_children.clear(); + } + + // TrackerBase interface + bool isIndexTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_generator->size()-1 ) + m_runState = Executing; + } + + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = std::move( generator ); + } + auto getIndex() const -> size_t override { + return m_index; + } + }; + GeneratorTracker::~GeneratorTracker() {} + } + + RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) + : m_runInfo(_config->name()), + m_context(getCurrentMutableContext()), + m_config(_config), + m_reporter(std::move(reporter)), + m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, + m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) + { + m_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } + + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } + + void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } + + Totals RunContext::runTest(TestCase const& testCase) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + auto const& testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + ITracker& rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting())); + + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; + } + + IConfigPtr RunContext::config() const { + return m_config; + } + + IStreamingReporter& RunContext::reporter() const { + return *m_reporter; + } + + void RunContext::assertionEnded(AssertionResult const & result) { + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + m_lastAssertionPassed = true; + } else if (!result.isOk()) { + m_lastAssertionPassed = false; + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; + } + else { + m_lastAssertionPassed = true; + } + + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + + // Reset working state + resetAssertionInfo(); + m_lastResult = result; + } + void RunContext::resetAssertionInfo() { + m_lastAssertionInfo.macroName = StringRef(); + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + } + + bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { + ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + + return true; + } + auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + using namespace Generators; + GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) ); + assert( tracker.isOpen() ); + m_lastAssertionInfo.lineInfo = lineInfo; + return tracker; + } + + bool RunContext::testForMissingAssertions(Counts& assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + void RunContext::sectionEnded(SectionEndInfo const & endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); + } + + void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); + } + void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); + } + void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { + m_reporter->benchmarkEnded( stats ); + } + + void RunContext::pushScopedMessage(MessageInfo const & message) { + m_messages.push_back(message); + } + + void RunContext::popScopedMessage(MessageInfo const & message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } + + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + const AssertionResult * RunContext::getLastResult() const { + return &(*m_lastResult); + } + + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + void RunContext::handleFatalErrorCondition( StringRef message ) { + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const& testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + std::string(), + std::string(), + false)); + m_totals.testCases.failed++; + testGroupEnded(std::string(), m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } + + bool RunContext::lastAssertionPassed() { + return m_lastAssertionPassed; + } + + void RunContext::assertionPassed() { + m_lastAssertionPassed = true; + ++m_totals.assertions.passed; + resetAssertionInfo(); + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed == static_cast(m_config->abortAfter()); + } + + void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal }; + + seedRng(*m_config); + + Timer timer; + CATCH_TRY { + if (m_reporter->getPreferences().shouldRedirectStdOut) { +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) + RedirectedStdOut redirectedStdOut; + RedirectedStdErr redirectedStdErr; + + timer.start(); + invokeActiveTestCase(); + redirectedCout += redirectedStdOut.str(); + redirectedCerr += redirectedStdErr.str(); +#else + OutputRedirect r(redirectedCout, redirectedCerr); + timer.start(); + invokeActiveTestCase(); +#endif + } else { + timer.start(); + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } CATCH_CATCH_ANON (TestFailureException&) { + // This just means the test was aborted due to failure + } CATCH_CATCH_ALL { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if( m_shouldReportUnexpected ) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction ); + } + } + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } + + void RunContext::handleExpr( + AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + bool negated = isFalseTest( info.resultDisposition ); + bool result = expr.getResult() != negated; + + if( result ) { + if (!m_includeSuccessfulResults) { + assertionPassed(); + } + else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } + else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); + populateReaction( reaction ); + } + } + void RunContext::reportExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ) { + + m_lastAssertionInfo = info; + AssertionResultData data( resultType, LazyExpression( negated ) ); + + AssertionResult assertionResult{ info, data }; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + assertionEnded( assertionResult ); + } + + void RunContext::handleMessage( + AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ m_lastAssertionInfo, data }; + assertionEnded( assertionResult ); + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + void RunContext::handleUnexpectedExceptionNotThrown( + AssertionInfo const& info, + AssertionReaction& reaction + ) { + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); + } + + void RunContext::handleUnexpectedInflightException( + AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + populateReaction( reaction ); + } + + void RunContext::populateReaction( AssertionReaction& reaction ) { + reaction.shouldDebugBreak = m_config->shouldDebugBreak(); + reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); + } + + void RunContext::handleIncomplete( + AssertionInfo const& info + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + } + void RunContext::handleNonExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + + IResultCapture& getResultCapture() { + if (auto* capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); + } +} +// end catch_run_context.cpp +// start catch_section.cpp + +namespace Catch { + + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; + if( uncaught_exceptions() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + +} // end namespace Catch +// end catch_section.cpp +// start catch_section_info.cpp + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ) + : name( _name ), + lineInfo( _lineInfo ) + {} + +} // end namespace Catch +// end catch_section_info.cpp +// start catch_session.cpp + +// start catch_session.h + +#include + +namespace Catch { + + class Session : NonCopyable { + public: + + Session(); + ~Session() override; + + void showHelp() const; + void libIdentify(); + + int applyCommandLine( int argc, char const * const * argv ); + + void useConfigData( ConfigData const& configData ); + + int run( int argc, char* argv[] ); + #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) + int run( int argc, wchar_t* const argv[] ); + #endif + int run(); + + clara::Parser const& cli() const; + void cli( clara::Parser const& newParser ); + ConfigData& configData(); + Config& config(); + private: + int runInternal(); + + clara::Parser m_cli; + ConfigData m_configData; + std::shared_ptr m_config; + bool m_startupExceptions = false; + }; + +} // end namespace Catch + +// end catch_session.h +// start catch_version.h + +#include + +namespace Catch { + + // Versioning information + struct Version { + Version( Version const& ) = delete; + Version& operator=( Version const& ) = delete; + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; + + // buildNumber is only used if branchName is not null + char const * const branchName; + unsigned int const buildNumber; + + friend std::ostream& operator << ( std::ostream& os, Version const& version ); + }; + + Version const& libraryVersion(); +} + +// end catch_version.h +#include +#include + +namespace Catch { + + namespace { + const int MaxExitCode = 255; + + IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); + + return reporter; + } + + IStreamingReporterPtr makeReporter(std::shared_ptr const& config) { + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { + return createReporter(config->getReporterName(), config); + } + + auto multi = std::unique_ptr(new ListeningReporter); + + auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const& listener : listeners) { + multi->addListener(listener->create(Catch::ReporterConfig(config))); + } + multi->addReporter(createReporter(config->getReporterName(), config)); + return std::move(multi); + } + + Catch::Totals runTests(std::shared_ptr const& config) { + // FixMe: Add listeners in order first, then add reporters. + + auto reporter = makeReporter(config); + + RunContext context(config, std::move(reporter)); + + Totals totals; + + context.testGroupStarting(config->name(), 1, 1); + + TestSpec testSpec = config->testSpec(); + + auto const& allTestCases = getAllTestCasesSorted(*config); + for (auto const& testCase : allTestCases) { + if (!context.aborting() && matchTest(testCase, testSpec, *config)) + totals += context.runTest(testCase); + else + context.reporter().skipTest(testCase); + } + + if (config->warnAboutNoTests() && totals.testCases.total() == 0) { + ReusableStringStream testConfig; + + bool first = true; + for (const auto& input : config->getTestsOrTags()) { + if (!first) { testConfig << ' '; } + first = false; + testConfig << input; + } + + context.reporter().noMatchingTestCases(testConfig.str()); + totals.error = -1; + } + + context.testGroupEnded(config->name(), totals, 1, 1); + return totals; + } + + void applyFilenamesAsTags(Catch::IConfig const& config) { + auto& tests = const_cast&>(getAllTestCasesSorted(config)); + for (auto& testCase : tests) { + auto tags = testCase.tags; + + std::string filename = testCase.lineInfo.file; + auto lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) { + filename.erase(0, lastSlash); + filename[0] = '#'; + } + + auto lastDot = filename.find_last_of('.'); + if (lastDot != std::string::npos) { + filename.erase(lastDot); + } + + tags.push_back(std::move(filename)); + setTags(testCase, tags); + } + } + + } // anon namespace + + Session::Session() { + static bool alreadyInstantiated = false; + if( alreadyInstantiated ) { + CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } + CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); } + } + + // There cannot be exceptions at startup in no-exception mode. +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if ( !exceptions.empty() ) { + m_startupExceptions = true; + Colour colourGuard( Colour::Red ); + Catch::cerr() << "Errors occurred during startup!" << '\n'; + // iterate over all exceptions and notify user + for ( const auto& ex_ptr : exceptions ) { + try { + std::rethrow_exception(ex_ptr); + } catch ( std::exception const& ex ) { + Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; + } + } + } +#endif + + alreadyInstantiated = true; + m_cli = makeCommandLineParser( m_configData ); + } + Session::~Session() { + Catch::cleanUp(); + } + + void Session::showHelp() const { + Catch::cout() + << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" << std::endl; + } + void Session::libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } + + int Session::applyCommandLine( int argc, char const * const * argv ) { + if( m_startupExceptions ) + return 1; + + auto result = m_cli.parse( clara::Args( argc, argv ) ); + if( !result ) { + Catch::cerr() + << Colour( Colour::Red ) + << "\nError(s) in input:\n" + << Column( result.errorMessage() ).indent( 2 ) + << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" << std::endl; + return MaxExitCode; + } + + if( m_configData.showHelp ) + showHelp(); + if( m_configData.libIdentify ) + libIdentify(); + m_config.reset(); + return 0; + } + + void Session::useConfigData( ConfigData const& configData ) { + m_configData = configData; + m_config.reset(); + } + + int Session::run( int argc, char* argv[] ) { + if( m_startupExceptions ) + return 1; + int returnCode = applyCommandLine( argc, argv ); + if( returnCode == 0 ) + returnCode = run(); + return returnCode; + } + +#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) + int Session::run( int argc, wchar_t* const argv[] ) { + + char **utf8Argv = new char *[ argc ]; + + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); + + utf8Argv[ i ] = new char[ bufSize ]; + + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + } + + int returnCode = run( argc, utf8Argv ); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } +#endif + int Session::run() { + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(std::getchar()); + } + return exitCode; + } + + clara::Parser const& Session::cli() const { + return m_cli; + } + void Session::cli( clara::Parser const& newParser ) { + m_cli = newParser; + } + ConfigData& Session::configData() { + return m_configData; + } + Config& Session::config() { + if( !m_config ) + m_config = std::make_shared( m_configData ); + return *m_config; + } + + int Session::runInternal() { + if( m_startupExceptions ) + return 1; + + if (m_configData.showHelp || m_configData.libIdentify) { + return 0; + } + + CATCH_TRY { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( config() ) ) + return static_cast( *listed ); + + auto totals = runTests( m_config ); + // Note that on unices only the lower 8 bits are usually used, clamping + // the return value to 255 prevents false negative when some multiple + // of 256 tests has failed + return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast(totals.assertions.failed))); + } +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; + } +#endif + } + +} // end namespace Catch +// end catch_session.cpp +// start catch_singletons.cpp + +#include + +namespace Catch { + + namespace { + static auto getSingletons() -> std::vector*& { + static std::vector* g_singletons = nullptr; + if( !g_singletons ) + g_singletons = new std::vector(); + return g_singletons; + } + } + + ISingleton::~ISingleton() {} + + void addSingleton(ISingleton* singleton ) { + getSingletons()->push_back( singleton ); + } + void cleanupSingletons() { + auto& singletons = getSingletons(); + for( auto singleton : *singletons ) + delete singleton; + delete singletons; + singletons = nullptr; + } + +} // namespace Catch +// end catch_singletons.cpp +// start catch_startup_exception_registry.cpp + +namespace Catch { +void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + CATCH_TRY { + m_exceptions.push_back(exception); + } CATCH_CATCH_ALL { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } + + std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } + +} // end namespace Catch +// end catch_startup_exception_registry.cpp +// start catch_stream.cpp + +#include +#include +#include +#include +#include +#include + +namespace Catch { + + Catch::IStream::~IStream() = default; + + namespace detail { namespace { + template + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); + } + + private: + int overflow( int c ) override { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() override { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( StringRef filename ) { + m_ofs.open( filename.c_str() ); + CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); + } + ~FileStream() override = default; + public: // IStream + std::ostream& stream() const override { + return m_ofs; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream() : m_os( Catch::cout().rdbuf() ) {} + ~CoutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + /////////////////////////////////////////////////////////////////////////// + + class DebugOutStream : public IStream { + std::unique_ptr> m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + m_os( m_streamBuf.get() ) + {} + + ~DebugOutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + }} // namespace anon::detail + + /////////////////////////////////////////////////////////////////////////// + + auto makeStream( StringRef const &filename ) -> IStream const* { + if( filename.empty() ) + return new detail::CoutStream(); + else if( filename[0] == '%' ) { + if( filename == "%debug" ) + return new detail::DebugOutStream(); + else + CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); + } + else + return new detail::FileStream( filename ); + } + + // This class encapsulates the idea of a pool of ostringstreams that can be reused. + struct StringStreams { + std::vector> m_streams; + std::vector m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + + auto add() -> std::size_t { + if( m_unused.empty() ) { + m_streams.push_back( std::unique_ptr( new std::ostringstream ) ); + return m_streams.size()-1; + } + else { + auto index = m_unused.back(); + m_unused.pop_back(); + return index; + } + } + + void release( std::size_t index ) { + m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state + m_unused.push_back(index); + } + }; + + ReusableStringStream::ReusableStringStream() + : m_index( Singleton::getMutable().add() ), + m_oss( Singleton::getMutable().m_streams[m_index].get() ) + {} + + ReusableStringStream::~ReusableStringStream() { + static_cast( m_oss )->str(""); + m_oss->clear(); + Singleton::getMutable().release( m_index ); + } + + auto ReusableStringStream::str() const -> std::string { + return static_cast( m_oss )->str(); + } + + /////////////////////////////////////////////////////////////////////////// + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions + std::ostream& cout() { return std::cout; } + std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { return std::clog; } +#endif +} +// end catch_stream.cpp +// start catch_string_manip.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + char toLowerCh(char c) { + return static_cast( std::tolower( c ) ); + } + } + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); + + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); + } + + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + bool replaced = false; + std::size_t i = str.find( replaceThis ); + while( i != std::string::npos ) { + replaced = true; + str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); + if( i < str.size()-withThis.size() ) + i = str.find( replaceThis, i+withThis.size() ); + else + i = std::string::npos; + } + return replaced; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << 's'; + return os; + } + +} +// end catch_string_manip.cpp +// start catch_stringref.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +#include +#include +#include + +namespace { + const uint32_t byte_2_lead = 0xC0; + const uint32_t byte_3_lead = 0xE0; + const uint32_t byte_4_lead = 0xF0; +} + +namespace Catch { + StringRef::StringRef( char const* rawChars ) noexcept + : StringRef( rawChars, static_cast(std::strlen(rawChars) ) ) + {} + + StringRef::operator std::string() const { + return std::string( m_start, m_size ); + } + + void StringRef::swap( StringRef& other ) noexcept { + std::swap( m_start, other.m_start ); + std::swap( m_size, other.m_size ); + std::swap( m_data, other.m_data ); + } + + auto StringRef::c_str() const -> char const* { + if( isSubstring() ) + const_cast( this )->takeOwnership(); + return m_start; + } + auto StringRef::currentData() const noexcept -> char const* { + return m_start; + } + + auto StringRef::isOwned() const noexcept -> bool { + return m_data != nullptr; + } + auto StringRef::isSubstring() const noexcept -> bool { + return m_start[m_size] != '\0'; + } + + void StringRef::takeOwnership() { + if( !isOwned() ) { + m_data = new char[m_size+1]; + memcpy( m_data, m_start, m_size ); + m_data[m_size] = '\0'; + m_start = m_data; + } + } + auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { + if( start < m_size ) + return StringRef( m_start+start, size ); + else + return StringRef(); + } + auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { + return + size() == other.size() && + (std::strncmp( m_start, other.m_start, size() ) == 0); + } + auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { + return !operator==( other ); + } + + auto StringRef::operator[](size_type index) const noexcept -> char { + return m_start[index]; + } + + auto StringRef::numberOfCharacters() const noexcept -> size_type { + size_type noChars = m_size; + // Make adjustments for uft encodings + for( size_type i=0; i < m_size; ++i ) { + char c = m_start[i]; + if( ( c & byte_2_lead ) == byte_2_lead ) { + noChars--; + if (( c & byte_3_lead ) == byte_3_lead ) + noChars--; + if( ( c & byte_4_lead ) == byte_4_lead ) + noChars--; + } + } + return noChars; + } + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { + std::string str; + str.reserve( lhs.size() + rhs.size() ); + str += lhs; + str += rhs; + return str; + } + auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + + auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { + return os.write(str.currentData(), str.size()); + } + + auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& { + lhs.append(rhs.currentData(), rhs.size()); + return lhs; + } + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_stringref.cpp +// start catch_tag_alias.cpp + +namespace Catch { + TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} +} +// end catch_tag_alias.cpp +// start catch_tag_alias_autoregistrar.cpp + +namespace Catch { + + RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { + CATCH_TRY { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + +} +// end catch_tag_alias_autoregistrar.cpp +// start catch_tag_alias_registry.cpp + +#include + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { + auto it = m_registry.find( alias ); + if( it != m_registry.end() ) + return &(it->second); + else + return nullptr; + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( auto const& registryKvp : m_registry ) { + std::size_t pos = expandedTestSpec.find( registryKvp.first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + registryKvp.second.tag + + expandedTestSpec.substr( pos + registryKvp.first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { + CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + + CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo ); + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } + +} // end namespace Catch +// end catch_tag_alias_registry.cpp +// start catch_test_case_info.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, '.' ) || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; + else if( tag == "!benchmark" ) + return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + else + return TestCaseInfo::None; + } + bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast(tag[0]) ); + } + void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << _lineInfo ); + } + } + + TestCase makeTestCase( ITestInvoker* _testCase, + std::string const& _className, + NameAndTags const& nameAndTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden = false; + + // Parse out tags + std::vector tags; + std::string desc, tag; + bool inTag = false; + std::string _descOrTags = nameAndTags.tags; + for (char c : _descOrTags) { + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( ( prop & TestCaseInfo::IsHidden ) != 0 ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + + tags.push_back( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + tags.push_back( "." ); + } + + TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, std::move(info) ); + } + + void setTags( TestCaseInfo& testCaseInfo, std::vector tags ) { + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); + testCaseInfo.lcaseTags.clear(); + + for( auto const& tag : tags ) { + std::string lcaseTag = toLower( tag ); + testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.push_back( lcaseTag ); + } + testCaseInfo.tags = std::move(tags); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } + + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + std::string TestCaseInfo::tagsAsString() const { + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto& tag : tags) { + full_size += tag.size(); + } + ret.reserve(full_size); + for (const auto& tag : tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); + } + + return ret; + } + + TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch +// end catch_test_case_info.cpp +// start catch_test_case_registry_impl.cpp + +#include + +namespace Catch { + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + + std::vector sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end() ); + break; + case RunTests::InRandomOrder: + seedRng( config ); + std::shuffle( sorted.begin(), sorted.end(), rng() ); + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector const& functions ) { + std::set seenFunctions; + for( auto const& function : functions ) { + auto prev = seenFunctions.insert( function ); + CATCH_ENFORCE( prev.second, + "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); + } + } + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( auto const& testCase : testCases ) + if( matchTest( testCase, testSpec, config ) ) + filtered.push_back( testCase ); + return filtered; + } + std::vector const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + void TestRegistry::registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name.empty() ) { + ReusableStringStream rss; + rss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( rss.str() ) ); + } + m_functions.push_back( testCase ); + } + + std::vector const& TestRegistry::getAllTests() const { + return m_functions; + } + std::vector const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + /////////////////////////////////////////////////////////////////////////// + TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} + + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + +} // end namespace Catch +// end catch_test_case_registry_impl.cpp +// start catch_test_case_tracker.cpp + +#include +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace Catch { +namespace TestCaseTracking { + + NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + + ITracker::~ITracker() = default; + + TrackerContext& TrackerContext::instance() { + static TrackerContext s_instance; + return s_instance; + } + + ITracker& TrackerContext::startRun() { + m_rootTracker = std::make_shared( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; + } + + void TrackerContext::endRun() { + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; + } + + void TrackerContext::startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void TrackerContext::completeCycle() { + m_runState = CompletedCycle; + } + + bool TrackerContext::completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& TrackerContext::currentTracker() { + return *m_currentTracker; + } + void TrackerContext::setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } + + TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), + m_ctx( ctx ), + m_parent( parent ) + {} + + NameAndLocation const& TrackerBase::nameAndLocation() const { + return m_nameAndLocation; + } + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + bool TrackerBase::isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } + bool TrackerBase::isOpen() const { + return m_runState != NotStarted && !isComplete(); + } + bool TrackerBase::hasChildren() const { + return !m_children.empty(); + } + + void TrackerBase::addChild( ITrackerPtr const& child ) { + m_children.push_back( child ); + } + + ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { + auto it = std::find_if( m_children.begin(), m_children.end(), + [&nameAndLocation]( ITrackerPtr const& tracker ){ + return + tracker->nameAndLocation().location == nameAndLocation.location && + tracker->nameAndLocation().name == nameAndLocation.name; + } ); + return( it != m_children.end() ) + ? *it + : nullptr; + } + ITracker& TrackerBase::parent() { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } + + void TrackerBase::openChild() { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + + bool TrackerBase::isSectionTracker() const { return false; } + bool TrackerBase::isIndexTracker() const { return false; } + + void TrackerBase::open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + void TrackerBase::close() { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NeedsAnotherRun: + break; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( m_children.empty() || m_children.back()->isComplete() ) + m_runState = CompletedSuccessfully; + break; + + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); + + default: + CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); + } + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::fail() { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::markAsNeedingAnotherRun() { + m_runState = NeedsAnotherRun; + } + + void TrackerBase::moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void TrackerBase::moveToThis() { + m_ctx.setCurrentTracker( this ); + } + + SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + std::shared_ptr section; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = std::static_pointer_cast( childTracker ); + } + else { + section = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void SectionTracker::tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void SectionTracker::addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void SectionTracker::addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + } + + IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), + m_size( size ) + {} + + bool IndexTracker::isIndexTracker() const { return true; } + + IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { + std::shared_ptr tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = std::static_pointer_cast( childTracker ); + } + else { + tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker, size ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + int IndexTracker::index() const { return m_index; } + + void IndexTracker::moveNext() { + m_index++; + m_children.clear(); + } + + void IndexTracker::close() { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) + m_runState = Executing; + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_test_case_tracker.cpp +// start catch_test_registry.cpp + +namespace Catch { + + auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); + } + + NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {} + + AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept { + CATCH_TRY { + getMutableRegistryHub() + .registerTest( + makeTestCase( + invoker, + extractClassName( classOrMethod ), + nameAndTags, + lineInfo)); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + + AutoReg::~AutoReg() = default; +} +// end catch_test_registry.cpp +// start catch_test_spec.cpp + +#include +#include +#include +#include + +namespace Catch { + + TestSpec::Pattern::~Pattern() = default; + TestSpec::NamePattern::~NamePattern() = default; + TestSpec::TagPattern::~TagPattern() = default; + TestSpec::ExcludedPattern::~ExcludedPattern() = default; + + TestSpec::NamePattern::NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + + TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { + return std::find(begin(testCase.lcaseTags), + end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); + } + + TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( auto const& pattern : m_patterns ) { + if( !pattern->matches( testCase ) ) + return false; + } + return true; + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + bool TestSpec::matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( auto const& filter : m_filters ) + if( filter.matches( testCase ) ) + return true; + return false; + } +} +// end catch_test_spec.cpp +// start catch_test_spec_parser.cpp + +namespace Catch { + + TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& TestSpecParser::parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } + + void TestSpecParser::visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + void TestSpecParser::escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } + std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + + void TestSpecParser::addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + + TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch +// end catch_test_spec_parser.cpp +// start catch_timer.cpp + +#include + +static const uint64_t nanosecondsInSecond = 1000000000; + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); + } + + namespace { + auto estimateClockResolution() -> uint64_t { + uint64_t sum = 0; + static const uint64_t iterations = 1000000; + + auto startTime = getCurrentNanosecondsSinceEpoch(); + + for( std::size_t i = 0; i < iterations; ++i ) { + + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } while( ticks == baseTicks ); + + auto delta = ticks - baseTicks; + sum += delta; + + // If we have been calibrating for over 3 seconds -- the clock + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { + return sum / i; + } + } + + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum/iterations; + } + } + auto getEstimatedClockResolution() -> uint64_t { + static auto s_resolution = estimateClockResolution(); + return s_resolution; + } + + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + auto Timer::getElapsedNanoseconds() const -> uint64_t { + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; + } + auto Timer::getElapsedMicroseconds() const -> uint64_t { + return getElapsedNanoseconds()/1000; + } + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast(getElapsedMicroseconds()/1000); + } + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds()/1000000.0; + } + +} // namespace Catch +// end catch_timer.cpp +// start catch_tostring.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +# pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + +// Enable specific decls locally +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +#include +#include + +namespace Catch { + +namespace Detail { + + const std::string unprintableString = "{?}"; + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + union _{ + int asInt; + char asChar[sizeof (int)]; + } u; + + u.asInt = 1; + return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; + } + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + rss << std::setw(2) << static_cast(bytes[i]); + return rss.str(); + } +} + +template +std::string fpToString( T value, int precision ) { + if (std::isnan(value)) { + return "nan"; + } + + ReusableStringStream rss; + rss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = rss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker::convert(const std::string& str) { + if (!getCurrentContext().getConfig()->showInvisibles()) { + return '"' + str + '"'; + } + + std::string s("\""); + for (char c : str) { + switch (c) { + case '\n': + s.append("\\n"); + break; + case '\t': + s.append("\\t"); + break; + default: + s.push_back(c); + break; + } + } + s.append("\""); + return s; +} + +#ifdef CATCH_CONFIG_WCHAR +std::string StringMaker::convert(const std::wstring& wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast(c) : '?'; + } + return ::Catch::Detail::stringify(s); +} +#endif + +std::string StringMaker::convert(char const* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(char* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +#ifdef CATCH_CONFIG_WCHAR +std::string StringMaker::convert(wchar_t const * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(wchar_t * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +#endif + +std::string StringMaker::convert(int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(bool b) { + return b ? "true" : "false"; +} + +std::string StringMaker::convert(char value) { + if (value == '\r') { + return "'\\r'"; + } else if (value == '\f') { + return "'\\f'"; + } else if (value == '\n') { + return "'\\n'"; + } else if (value == '\t') { + return "'\\t'"; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify(static_cast(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } +} +std::string StringMaker::convert(signed char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} +std::string StringMaker::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} + +std::string StringMaker::convert(std::nullptr_t) { + return "nullptr"; +} + +std::string StringMaker::convert(float value) { + return fpToString(value, 5) + 'f'; +} +std::string StringMaker::convert(double value) { + return fpToString(value, 10); +} + +std::string ratio_string::symbol() { return "a"; } +std::string ratio_string::symbol() { return "f"; } +std::string ratio_string::symbol() { return "p"; } +std::string ratio_string::symbol() { return "n"; } +std::string ratio_string::symbol() { return "u"; } +std::string ratio_string::symbol() { return "m"; } + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_tostring.cpp +// start catch_totals.cpp + +namespace Catch { + + Counts Counts::operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + + Counts& Counts::operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t Counts::total() const { + return passed + failed + failedButOk; + } + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool Counts::allOk() const { + return failed == 0; + } + + Totals Totals::operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals& Totals::operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Totals Totals::delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + +} +// end catch_totals.cpp +// start catch_uncaught_exceptions.cpp + +#include + +namespace Catch { + bool uncaught_exceptions() { +#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; +#else + return std::uncaught_exception(); +#endif + } +} // end namespace Catch +// end catch_uncaught_exceptions.cpp +// start catch_version.cpp + +#include + +namespace Catch { + + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) + {} + + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; + } + return os; + } + + Version const& libraryVersion() { + static Version version( 2, 4, 0, "", 0 ); + return version; + } + +} +// end catch_version.cpp +// start catch_wildcard_pattern.cpp + +#include + +namespace Catch { + + WildcardPattern::WildcardPattern( std::string const& pattern, + CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_pattern( adjustCase( pattern ) ) + { + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + + bool WildcardPattern::matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); + } + } + + std::string WildcardPattern::adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + } +} +// end catch_wildcard_pattern.cpp +// start catch_xmlwriter.cpp + +#include + +using uchar = unsigned char; + +namespace Catch { + +namespace { + + size_t trailingBytes(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + uint32_t headerValue(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + void hexEscapeChar(std::ostream& os, unsigned char c) { + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast(c); + } + +} // anonymous namespace + + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void XmlEncode::encodeTo( std::ostream& os ) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { + uchar c = m_str[idx]; + switch (c) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; + + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; + + default: + // Check for control characters and invalid utf-8 + + // Escape control characters in standard ascii + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; + } + + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } + + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || + c >= 0xF8) { + hexEscapeChar(os, c); + break; + } + + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + uchar nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } + + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + (0x80 <= value && value < 0x800 && encBytes > 2) || + (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000) + ) { + hexEscapeChar(os, c); + break; + } + + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; + } + } + } + + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept + : m_writer( other.m_writer ){ + other.m_writer = nullptr; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + if ( m_writer ) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + return *this; + } + + XmlWriter::ScopedElement::~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { + m_writer->writeText( text, indent ); + return *this; + } + + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + writeDeclaration(); + } + + XmlWriter::~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& XmlWriter::startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& XmlWriter::endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } + else { + m_os << m_indent << ""; + } + m_os << std::endl; + m_tags.pop_back(); + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& XmlWriter::writeComment( std::string const& text ) { + ensureTagClosed(); + m_os << m_indent << ""; + m_needsNewline = true; + return *this; + } + + void XmlWriter::writeStylesheetRef( std::string const& url ) { + m_os << "\n"; + } + + XmlWriter& XmlWriter::writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; + } + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } + } + + void XmlWriter::writeDeclaration() { + m_os << "\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } +} +// end catch_xmlwriter.cpp +// start catch_reporter_bases.cpp + +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result) { + result.getExpandedExpression(); + } + + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + + TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) + :StreamingReporterBase(_config) {} + + void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} + + bool TestEventListenerBase::assertionEnded(AssertionStats const &) { + return false; + } + +} // end namespace Catch +// end catch_reporter_bases.cpp +// start catch_reporter_compact.cpp + +namespace { + +#ifdef CATCH_PLATFORM_MAC + const char* failedString() { return "FAILED"; } + const char* passedString() { return "PASSED"; } +#else + const char* failedString() { return "failed"; } + const char* passedString() { return "passed"; } +#endif + + // Colour::LightGrey + Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } + + std::string bothOrAll( std::size_t count ) { + return count == 1 ? std::string() : + count == 2 ? "both " : "all " ; + } + +} // anon namespace + +namespace Catch { +namespace { +// Colour, message variants: +// - white: No tests ran. +// - red: Failed [both/all] N test cases, failed [both/all] M assertions. +// - white: Passed [both/all] N test cases (no assertions). +// - red: Failed N tests cases, failed M assertions. +// - green: Passed [both/all] N tests cases with M assertions. +void printTotals(std::ostream& out, const Totals& totals) { + if (totals.testCases.total() == 0) { + out << "No tests ran."; + } else if (totals.testCases.failed == totals.testCases.total()) { + Colour colour(Colour::ResultError); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll(totals.assertions.failed) : std::string(); + out << + "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << qualify_assertions_failed << + pluralise(totals.assertions.failed, "assertion") << '.'; + } else if (totals.assertions.total() == 0) { + out << + "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case") + << " (no assertions)."; + } else if (totals.assertions.failed) { + Colour colour(Colour::ResultError); + out << + "Failed " << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << pluralise(totals.assertions.failed, "assertion") << '.'; + } else { + Colour colour(Colour::ResultSuccess); + out << + "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case") << + " with " << pluralise(totals.assertions.passed, "assertion") << '.'; + } +} + +// Implementation of CompactReporter formatting +class AssertionPrinter { +public: + AssertionPrinter& operator= (AssertionPrinter const&) = delete; + AssertionPrinter(AssertionPrinter const&) = delete; + AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(_printInfoMessages) {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); + else + printResultType(Colour::Error, failedString()); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, failedString()); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType(Colour::Error, "** internal error **"); + break; + } + } + +private: + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ':'; + } + + void printResultType(Colour::Code colour, std::string const& passOrFail) const { + if (!passOrFail.empty()) { + { + Colour colourGuard(colour); + stream << ' ' << passOrFail; + } + stream << ':'; + } + } + + void printIssue(std::string const& issue) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if (result.hasExpression()) { + stream << ';'; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = dimColour()) { + if (itMessage == messages.end()) + return; + + // using messages.end() directly yields (or auto) compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ':'; + } + + for (; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + stream << " '" << itMessage->message << '\''; + if (++itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; + } + } + } + } + +private: + std::ostream& stream; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; +}; + +} // anon namespace + + std::string CompactReporter::getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + ReporterPreferences CompactReporter::getPreferences() const { + return m_reporterPrefs; + } + + void CompactReporter::noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } + + void CompactReporter::assertionStarting( AssertionInfo const& ) {} + + bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; + } + + void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + + void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( stream, _testRunStats.totals ); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + CompactReporter::~CompactReporter() {} + + CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + +} // end namespace Catch +// end catch_reporter_compact.cpp +// start catch_reporter_console.cpp + +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + +namespace { + +// Formatter impl for ConsoleReporter +class ConsoleAssertionPrinter { +public: + ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream), + stats(_stats), + result(_stats.assertionResult), + colour(Colour::None), + message(result.getMessage()), + messages(_stats.infoMessages), + printInfoMessages(_printInfoMessages) { + switch (result.getResultType()) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if (_stats.infoMessages.size() == 1) + messageLabel = "explicitly with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if (stats.totals.assertions.total() > 0) { + if (result.isOk()) + stream << '\n'; + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } else { + stream << '\n'; + } + printMessage(); + } + +private: + void printResultType() const { + if (!passOrFail.empty()) { + Colour colourGuard(colour); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if (result.hasExpression()) { + Colour colourGuard(Colour::OriginalExpression); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; + } + } + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << "with expansion:\n"; + Colour colourGuard(Colour::ReconstructedExpression); + stream << Column(result.getExpandedExpression()).indent(2) << '\n'; + } + } + void printMessage() const { + if (!messageLabel.empty()) + stream << messageLabel << ':' << '\n'; + for (auto const& msg : messages) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || msg.type != ResultWas::Info) + stream << Column(msg.message).indent(2) << '\n'; + } + } + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; +}; + +std::size_t makeRatio(std::size_t number, std::size_t total) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : ratio; +} + +std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) { + if (i > j && i > k) + return i; + else if (j > k) + return j; + else + return k; +} + +struct ColumnInfo { + enum Justification { Left, Right }; + std::string name; + int width; + Justification justification; +}; +struct ColumnBreak {}; +struct RowBreak {}; + +class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; + + uint64_t m_inNanoseconds; + Unit m_units; + +public: + explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds), + m_units(units) { + if (m_units == Unit::Auto) { + if (m_inNanoseconds < s_nanosecondsInAMicrosecond) + m_units = Unit::Nanoseconds; + else if (m_inNanoseconds < s_nanosecondsInAMillisecond) + m_units = Unit::Microseconds; + else if (m_inNanoseconds < s_nanosecondsInASecond) + m_units = Unit::Milliseconds; + else if (m_inNanoseconds < s_nanosecondsInAMinute) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; + } + + } + + auto value() const -> double { + switch (m_units) { + case Unit::Microseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMicrosecond); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); + case Unit::Seconds: + return m_inNanoseconds / static_cast(s_nanosecondsInASecond); + case Unit::Minutes: + return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); + default: + return static_cast(m_inNanoseconds); + } + } + auto unitsAsString() const -> std::string { + switch (m_units) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "µs"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; + } + + } + friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { + return os << duration.value() << " " << duration.unitsAsString(); + } +}; +} // end anon namespace + +class TablePrinter { + std::ostream& m_os; + std::vector m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + +public: + TablePrinter( std::ostream& os, std::vector columnInfos ) + : m_os( os ), + m_columnInfos( std::move( columnInfos ) ) {} + + auto columnInfos() const -> std::vector const& { + return m_columnInfos; + } + + void open() { + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); + for (auto const& info : m_columnInfos) + *this << info.name << ColumnBreak(); + *this << RowBreak(); + m_os << Catch::getLineOfChars<'-'>() << "\n"; + } + } + void close() { + if (m_isOpen) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; + } + } + + template + friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + tp.m_oss << value; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + auto colStr = tp.m_oss.str(); + // This takes account of utf8 encodings + auto strSize = Catch::StringRef(colStr).numberOfCharacters(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; + tp.m_os << "\n"; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = (strSize + 2 < static_cast(colInfo.width)) + ? std::string(colInfo.width - (strSize + 2), ' ') + : std::string(); + if (colInfo.justification == ColumnInfo::Left) + tp.m_os << colStr << padding << " "; + else + tp.m_os << padding << colStr << " "; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + if (tp.m_currentColumn > 0) { + tp.m_os << "\n"; + tp.m_currentColumn = -1; + } + return tp; + } +}; + +ConsoleReporter::ConsoleReporter(ReporterConfig const& config) + : StreamingReporterBase(config), + m_tablePrinter(new TablePrinter(config.stream(), + { + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left }, + { "iters", 8, ColumnInfo::Right }, + { "elapsed ns", 14, ColumnInfo::Right }, + { "average", 14, ColumnInfo::Right } + })) {} +ConsoleReporter::~ConsoleReporter() = default; + +std::string ConsoleReporter::getDescription() { + return "Reports test results as plain lines of text"; +} + +void ConsoleReporter::noMatchingTestCases(std::string const& spec) { + stream << "No test cases matched '" << spec << '\'' << std::endl; +} + +void ConsoleReporter::assertionStarting(AssertionInfo const&) {} + +bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return false; + + lazyPrint(); + + ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults); + printer.print(); + stream << std::endl; + return true; +} + +void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); +} +void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { + m_tablePrinter->close(); + if (_sectionStats.missingAssertions) { + lazyPrint(); + Colour colour(Colour::ResultError); + if (m_sectionStack.size() > 1) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if (m_headerPrinted) { + m_headerPrinted = false; + } + StreamingReporterBase::sectionEnded(_sectionStats); +} + +void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { + lazyPrintWithoutClosingBenchmarkTable(); + + auto nameCol = Column( info.name ).width( static_cast( m_tablePrinter->columnInfos()[0].width - 2 ) ); + + bool firstLine = true; + for (auto line : nameCol) { + if (!firstLine) + (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); + else + firstLine = false; + + (*m_tablePrinter) << line << ColumnBreak(); + } +} +void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) { + Duration average(stats.elapsedTimeInNanoseconds / stats.iterations); + (*m_tablePrinter) + << stats.iterations << ColumnBreak() + << stats.elapsedTimeInNanoseconds << ColumnBreak() + << average << ColumnBreak(); +} + +void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + m_tablePrinter->close(); + StreamingReporterBase::testCaseEnded(_testCaseStats); + m_headerPrinted = false; +} +void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) { + if (currentGroupInfo.used) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals(_testGroupStats.totals); + stream << '\n' << std::endl; + } + StreamingReporterBase::testGroupEnded(_testGroupStats); +} +void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { + printTotalsDivider(_testRunStats.totals); + printTotals(_testRunStats.totals); + stream << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); +} + +void ConsoleReporter::lazyPrint() { + + m_tablePrinter->close(); + lazyPrintWithoutClosingBenchmarkTable(); +} + +void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { + + if (!currentTestRunInfo.used) + lazyPrintRunInfo(); + if (!currentGroupInfo.used) + lazyPrintGroupInfo(); + + if (!m_headerPrinted) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } +} +void ConsoleReporter::lazyPrintRunInfo() { + stream << '\n' << getLineOfChars<'~'>() << '\n'; + Colour colour(Colour::SecondaryText); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion() << " host application.\n" + << "Run with -? for options\n\n"; + + if (m_config->rngSeed() != 0) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; +} +void ConsoleReporter::lazyPrintGroupInfo() { + if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { + printClosedHeader("Group: " + currentGroupInfo->name); + currentGroupInfo.used = true; + } +} +void ConsoleReporter::printTestCaseAndSectionHeader() { + assert(!m_sectionStack.empty()); + printOpenHeader(currentTestCaseInfo->name); + + if (m_sectionStack.size() > 1) { + Colour colourGuard(Colour::Headers); + + auto + it = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); + } + + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + + if (!lineInfo.empty()) { + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard(Colour::FileName); + stream << lineInfo << '\n'; + } + stream << getLineOfChars<'.'>() << '\n' << std::endl; +} + +void ConsoleReporter::printClosedHeader(std::string const& _name) { + printOpenHeader(_name); + stream << getLineOfChars<'.'>() << '\n'; +} +void ConsoleReporter::printOpenHeader(std::string const& _name) { + stream << getLineOfChars<'-'>() << '\n'; + { + Colour colourGuard(Colour::Headers); + printHeaderString(_name); + } +} + +// if string has a : in first line will set indent to follow it on +// subsequent lines +void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n'; +} + +struct SummaryColumn { + + SummaryColumn( std::string _label, Colour::Code _colour ) + : label( std::move( _label ) ), + colour( _colour ) {} + SummaryColumn addRow( std::size_t count ) { + ReusableStringStream rss; + rss << count; + std::string row = rss.str(); + for (auto& oldRow : rows) { + while (oldRow.size() < row.size()) + oldRow = ' ' + oldRow; + while (oldRow.size() > row.size()) + row = ' ' + row; + } + rows.push_back(row); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; + +}; + +void ConsoleReporter::printTotals( Totals const& totals ) { + if (totals.testCases.total() == 0) { + stream << Colour(Colour::Warning) << "No tests ran\n"; + } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion") << " in " + << pluralise(totals.testCases.passed, "test case") << ')' + << '\n'; + } else { + + std::vector columns; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + + printSummaryRow("test cases", columns, 0); + printSummaryRow("assertions", columns, 1); + } +} +void ConsoleReporter::printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row) { + for (auto col : cols) { + std::string value = col.rows[row]; + if (col.label.empty()) { + stream << label << ": "; + if (value != "0") + stream << value; + else + stream << Colour(Colour::Warning) << "- none -"; + } else if (value != "0") { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(col.colour) + << value << ' ' << col.label; + } + } + stream << '\n'; +} + +void ConsoleReporter::printTotalsDivider(Totals const& totals) { + if (totals.testCases.total() > 0) { + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + stream << Colour(Colour::Error) << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) << std::string(passedRatio, '='); + } else { + stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << '\n'; +} +void ConsoleReporter::printSummaryDivider() { + stream << getLineOfChars<'-'>() << '\n'; +} + +CATCH_REGISTER_REPORTER("console", ConsoleReporter) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_console.cpp +// start catch_reporter_junit.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif + + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + + std::string fileNameTag(const std::vector &tags) { + auto it = std::find_if(begin(tags), + end(tags), + [] (std::string const& tag) {return tag.front() == '#'; }); + if (it != tags.end()) + return it->substr(1); + return std::string(); + } + } // anonymous namespace + + JunitReporter::JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + JunitReporter::~JunitReporter() {} + + std::string JunitReporter::getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {} + + void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + } + + void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) { + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { + m_okToFail = testCaseInfo.okToFail(); + } + + bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + void JunitReporter::testRunEndedCumulative() { + xml.endElement(); + } + + void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + + // Write test cases + for( auto const& child : groupNode.children ) + writeTestCase( *child ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false ); + } + + void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + className = fileNameTag(stats.testInfo.tags); + if ( className.empty() ) + className = "global"; + } + + if ( !m_config->name().empty() ) + className = m_config->name() + "." + className; + + writeSection( className, "", rootSection ); + } + + void JunitReporter::writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; + + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + } + for( auto const& childNode : sectionNode.childSections ) + if( className.empty() ) + writeSection( name, "", *childNode ); + else + writeSection( className, name, *childNode ); + } + + void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { + for( auto const& assertion : sectionNode.assertions ) + writeAssertion( assertion ); + } + + void JunitReporter::writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + ReusableStringStream rss; + if( !result.getMessage().empty() ) + rss << result.getMessage() << '\n'; + for( auto const& msg : stats.infoMessages ) + if( msg.type == ResultWas::Info ) + rss << msg.message << '\n'; + + rss << "at " << result.getSourceInfo(); + xml.writeText( rss.str(), false ); + } + } + + CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + +} // end namespace Catch +// end catch_reporter_junit.cpp +// start catch_reporter_listening.cpp + +#include + +namespace Catch { + + ListeningReporter::ListeningReporter() { + // We will assume that listeners will always want all assertions + m_preferences.shouldReportAllAssertions = true; + } + + void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) { + m_listeners.push_back( std::move( listener ) ); + } + + void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) { + assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); + m_reporter = std::move( reporter ); + m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut; + } + + ReporterPreferences ListeningReporter::getPreferences() const { + return m_preferences; + } + + std::set ListeningReporter::getSupportedVerbosities() { + return std::set{ }; + } + + void ListeningReporter::noMatchingTestCases( std::string const& spec ) { + for ( auto const& listener : m_listeners ) { + listener->noMatchingTestCases( spec ); + } + m_reporter->noMatchingTestCases( spec ); + } + + void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkStarting( benchmarkInfo ); + } + m_reporter->benchmarkStarting( benchmarkInfo ); + } + void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkEnded( benchmarkStats ); + } + m_reporter->benchmarkEnded( benchmarkStats ); + } + + void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testRunStarting( testRunInfo ); + } + m_reporter->testRunStarting( testRunInfo ); + } + + void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupStarting( groupInfo ); + } + m_reporter->testGroupStarting( groupInfo ); + } + + void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseStarting( testInfo ); + } + m_reporter->testCaseStarting( testInfo ); + } + + void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->sectionStarting( sectionInfo ); + } + m_reporter->sectionStarting( sectionInfo ); + } + + void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->assertionStarting( assertionInfo ); + } + m_reporter->assertionStarting( assertionInfo ); + } + + // The return value indicates if the messages buffer should be cleared: + bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) { + for( auto const& listener : m_listeners ) { + static_cast( listener->assertionEnded( assertionStats ) ); + } + return m_reporter->assertionEnded( assertionStats ); + } + + void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) { + for ( auto const& listener : m_listeners ) { + listener->sectionEnded( sectionStats ); + } + m_reporter->sectionEnded( sectionStats ); + } + + void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseEnded( testCaseStats ); + } + m_reporter->testCaseEnded( testCaseStats ); + } + + void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupEnded( testGroupStats ); + } + m_reporter->testGroupEnded( testGroupStats ); + } + + void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) { + for ( auto const& listener : m_listeners ) { + listener->testRunEnded( testRunStats ); + } + m_reporter->testRunEnded( testRunStats ); + } + + void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->skipTest( testInfo ); + } + m_reporter->skipTest( testInfo ); + } + + bool ListeningReporter::isMulti() const { + return true; + } + +} // end namespace Catch +// end catch_reporter_listening.cpp +// start catch_reporter_xml.cpp + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + XmlReporter::XmlReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_xml(_config.stream()) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + XmlReporter::~XmlReporter() = default; + + std::string XmlReporter::getDescription() { + return "Reports test results as an XML document"; + } + + std::string XmlReporter::getStylesheetRef() const { + return std::string(); + } + + void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + + void XmlReporter::noMatchingTestCases( std::string const& s ) { + StreamingReporterBase::noMatchingTestCases( s ); + } + + void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { + StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); + } + + void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } + + void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString() ); + + writeSourceInfo( testInfo.lineInfo ); + + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } + + void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); + } + } + + void XmlReporter::assertionStarting( AssertionInfo const& ) { } + + bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) { + + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults || result.getResultType() == ResultWas::Warning ) { + // Print any info messages in tags. + for( auto const& msg : assertionStats.infoMessages ) { + if( msg.type == ResultWas::Info && includeResults ) { + m_xml.scopedElement( "Info" ) + .writeText( msg.message ); + } else if ( msg.type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( msg.message ); + } + } + } + + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return true; + + // Print the expression if there is one. + if( result.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); + + m_xml.scopedElement( "Original" ) + .writeText( result.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( result.getExpandedExpression() ); + } + + // And... Print a result applicable to each result type. + switch( result.getResultType() ) { + case ResultWas::ThrewException: + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( result.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + default: + break; + } + + if( result.hasExpression() ) + m_xml.endElement(); + + return true; + } + + void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + + m_xml.endElement(); + } + } + + void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + + m_xml.endElement(); + } + + void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_xml.cpp + +namespace Catch { + LeakDetector leakDetector; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_impl.hpp +#endif + +#ifdef CATCH_CONFIG_MAIN +// start catch_default_main.hpp + +#ifndef __OBJC__ + +#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { +#else +// Standard C/C++ main entry point +int main (int argc, char * argv[]) { +#endif + + return Catch::Session().run( argc, argv ); +} + +#else // __OBJC__ + +// Objective-C entry point +int main (int argc, char * const argv[]) { +#if !CATCH_ARC_ENABLED + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run( argc, (char**)argv ); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return result; +} + +#endif // __OBJC__ + +// end catch_default_main.hpp +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +# undef CLARA_CONFIG_MAIN +#endif + +#if !defined(CATCH_CONFIG_DISABLE) +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + +#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + +#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + +#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", __VA_ARGS__ ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ ) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) +#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +#define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +#define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +#define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +#define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +#define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + +#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + +#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + +#define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ ) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) +#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) + +#define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +#define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +#define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +#define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +#define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +#define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + +using Catch::Detail::Approx; + +#else // CATCH_CONFIG_DISABLE + +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( ... ) (void)(0) +#define CATCH_REQUIRE_FALSE( ... ) (void)(0) + +#define CATCH_REQUIRE_THROWS( ... ) (void)(0) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) + +#define CATCH_CHECK( ... ) (void)(0) +#define CATCH_CHECK_FALSE( ... ) (void)(0) +#define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) +#define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CATCH_CHECK_NOFAIL( ... ) (void)(0) + +#define CATCH_CHECK_THROWS( ... ) (void)(0) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) (void)(0) + +#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) (void)(0) +#define CATCH_WARN( msg ) (void)(0) +#define CATCH_CAPTURE( msg ) (void)(0) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define CATCH_SECTION( ... ) +#define CATCH_DYNAMIC_SECTION( ... ) +#define CATCH_FAIL( ... ) (void)(0) +#define CATCH_FAIL_CHECK( ... ) (void)(0) +#define CATCH_SUCCEED( ... ) (void)(0) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) +#define CATCH_GIVEN( desc ) +#define CATCH_AND_GIVEN( desc ) +#define CATCH_WHEN( desc ) +#define CATCH_AND_WHEN( desc ) +#define CATCH_THEN( desc ) +#define CATCH_AND_THEN( desc ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) (void)(0) +#define REQUIRE_FALSE( ... ) (void)(0) + +#define REQUIRE_THROWS( ... ) (void)(0) +#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) (void)(0) + +#define CHECK( ... ) (void)(0) +#define CHECK_FALSE( ... ) (void)(0) +#define CHECKED_IF( ... ) if (__VA_ARGS__) +#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CHECK_NOFAIL( ... ) (void)(0) + +#define CHECK_THROWS( ... ) (void)(0) +#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) (void)(0) + +#define REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) (void)(0) +#define WARN( msg ) (void)(0) +#define CAPTURE( msg ) (void)(0) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define METHOD_AS_TEST_CASE( method, ... ) +#define REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define SECTION( ... ) +#define DYNAMIC_SECTION( ... ) +#define FAIL( ... ) (void)(0) +#define FAIL_CHECK( ... ) (void)(0) +#define SUCCEED( ... ) (void)(0) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) + +#define GIVEN( desc ) +#define AND_GIVEN( desc ) +#define WHEN( desc ) +#define AND_WHEN( desc ) +#define THEN( desc ) +#define AND_THEN( desc ) + +using Catch::Detail::Approx; + +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +// start catch_reenable_warnings.h + + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + +// end catch_reenable_warnings.h +// end catch.hpp +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + diff --git a/gulrak-filesystem/test/cmake/ParseAndAddCatchTests.cmake b/gulrak-filesystem/test/cmake/ParseAndAddCatchTests.cmake new file mode 100644 index 0000000..5e89cb7 --- /dev/null +++ b/gulrak-filesystem/test/cmake/ParseAndAddCatchTests.cmake @@ -0,0 +1,230 @@ +#==================================================================================================# +# supported macros # +# - TEST_CASE, # +# - SCENARIO, # +# - TEST_CASE_METHOD, # +# - CATCH_TEST_CASE, # +# - CATCH_SCENARIO, # +# - CATCH_TEST_CASE_METHOD. # +# # +# Usage # +# 1. make sure this module is in the path or add this otherwise: # +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") # +# 2. make sure that you've enabled testing option for the project by the call: # +# enable_testing() # +# 3. add the lines to the script for testing target (sample CMakeLists.txt): # +# project(testing_target) # +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") # +# enable_testing() # +# # +# find_path(CATCH_INCLUDE_DIR "catch.hpp") # +# include_directories(${INCLUDE_DIRECTORIES} ${CATCH_INCLUDE_DIR}) # +# # +# file(GLOB SOURCE_FILES "*.cpp") # +# add_executable(${PROJECT_NAME} ${SOURCE_FILES}) # +# # +# include(ParseAndAddCatchTests) # +# ParseAndAddCatchTests(${PROJECT_NAME}) # +# # +# The following variables affect the behavior of the script: # +# # +# PARSE_CATCH_TESTS_VERBOSE (Default OFF) # +# -- enables debug messages # +# PARSE_CATCH_TESTS_NO_HIDDEN_TESTS (Default OFF) # +# -- excludes tests marked with [!hide], [.] or [.foo] tags # +# PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME (Default ON) # +# -- adds fixture class name to the test name # +# PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME (Default ON) # +# -- adds cmake target name to the test name # +# PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS (Default OFF) # +# -- causes CMake to rerun when file with tests changes so that new tests will be discovered # +# # +# One can also set (locally) the optional variable OptionalCatchTestLauncher to precise the way # +# a test should be run. For instance to use test MPI, one can write # +# set(OptionalCatchTestLauncher ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${NUMPROC}) # +# just before calling this ParseAndAddCatchTests function # +# # +# The AdditionalCatchParameters optional variable can be used to pass extra argument to the test # +# command. For example, to include successful tests in the output, one can write # +# set(AdditionalCatchParameters --success) # +# # +# After the script, the ParseAndAddCatchTests_TESTS property for the target, and for each source # +# file in the target is set, and contains the list of the tests extracted from that target, or # +# from that file. This is useful, for example to add further labels or properties to the tests. # +# # +#==================================================================================================# + +if (CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.8) + message(FATAL_ERROR "ParseAndAddCatchTests requires CMake 2.8.8 or newer") +endif() + +option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF) +option(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS "Exclude tests with [!hide], [.] or [.foo] tags" OFF) +option(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the test name" ON) +option(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME "Add target name to the test name" ON) +option(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS "Add test file to CMAKE_CONFIGURE_DEPENDS property" OFF) + +function(ParseAndAddCatchTests_PrintDebugMessage) + if(PARSE_CATCH_TESTS_VERBOSE) + message(STATUS "ParseAndAddCatchTests: ${ARGV}") + endif() +endfunction() + +# This removes the contents between +# - block comments (i.e. /* ... */) +# - full line comments (i.e. // ... ) +# contents have been read into '${CppCode}'. +# !keep partial line comments +function(ParseAndAddCatchTests_RemoveComments CppCode) + string(ASCII 2 CMakeBeginBlockComment) + string(ASCII 3 CMakeEndBlockComment) + string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}") + string(REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${CppCode} "${${CppCode}}") + string(REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${CppCode} "${${CppCode}}") + string(REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${CppCode} "${${CppCode}}") + + set(${CppCode} "${${CppCode}}" PARENT_SCOPE) +endfunction() + +# Worker function +function(ParseAndAddCatchTests_ParseFile SourceFile TestTarget) + # If SourceFile is an object library, do not scan it (as it is not a file). Exit without giving a warning about a missing file. + if(SourceFile MATCHES "\\\$") + ParseAndAddCatchTests_PrintDebugMessage("Detected OBJECT library: ${SourceFile} this will not be scanned for tests.") + return() + endif() + # According to CMake docs EXISTS behavior is well-defined only for full paths. + get_filename_component(SourceFile ${SourceFile} ABSOLUTE) + if(NOT EXISTS ${SourceFile}) + message(WARNING "Cannot find source file: ${SourceFile}") + return() + endif() + ParseAndAddCatchTests_PrintDebugMessage("parsing ${SourceFile}") + file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME) + + # Remove block and fullline comments + ParseAndAddCatchTests_RemoveComments(Contents) + + # Find definition of test names + string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^\)]+\\)+[ \t\n]*{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}") + + if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests) + ParseAndAddCatchTests_PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property") + set_property( + DIRECTORY + APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS ${SourceFile} + ) + endif() + + foreach(TestName ${Tests}) + # Strip newlines + string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}") + + # Get test type and fixture if applicable + string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}") + string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}") + string(REGEX REPLACE "${TestType}\\([ \t]*" "" TestFixture "${TestTypeAndFixture}") + + # Get string parts of test definition + string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}") + + # Strip wrapping quotation marks + string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}") + string(REPLACE "\";\"" ";" TestStrings "${TestStrings}") + + # Validate that a test name and tags have been provided + list(LENGTH TestStrings TestStringsLength) + if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1) + message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}") + endif() + + # Assign name and tags + list(GET TestStrings 0 Name) + if("${TestType}" STREQUAL "SCENARIO") + set(Name "Scenario: ${Name}") + endif() + if(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND "${TestType}" MATCHES "(CATCH_)?TEST_CASE_METHOD" AND TestFixture ) + set(CTestName "${TestFixture}:${Name}") + else() + set(CTestName "${Name}") + endif() + if(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME) + set(CTestName "${TestTarget}:${CTestName}") + endif() + # add target to labels to enable running all tests added from this target + set(Labels ${TestTarget}) + if(TestStringsLength EQUAL 2) + list(GET TestStrings 1 Tags) + string(TOLOWER "${Tags}" Tags) + # remove target from labels if the test is hidden + if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*") + list(REMOVE_ITEM Labels ${TestTarget}) + endif() + string(REPLACE "]" ";" Tags "${Tags}") + string(REPLACE "[" "" Tags "${Tags}") + else() + # unset tags variable from previous loop + unset(Tags) + endif() + + list(APPEND Labels ${Tags}) + + set(HiddenTagFound OFF) + foreach(label ${Labels}) + string(REGEX MATCH "^!hide|^\\." result ${label}) + if(result) + set(HiddenTagFound ON) + break() + endif(result) + endforeach(label) + if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_LESS "3.9") + ParseAndAddCatchTests_PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label") + else() + ParseAndAddCatchTests_PrintDebugMessage("Adding test \"${CTestName}\"") + if(Labels) + ParseAndAddCatchTests_PrintDebugMessage("Setting labels to ${Labels}") + endif() + + # Escape commas in the test spec + string(REPLACE "," "\\," Name ${Name}) + + # Work around CMake 3.18.0 change in `add_test()`, before the escaped quotes were neccessary, + # only with CMake 3.18.0 the escaped double quotes confuse the call. This change is reverted in 3.18.1 + if(NOT ${CMAKE_VERSION} VERSION_EQUAL "3.18") + set(CTestName "\"${CTestName}\"") + endif() + # Add the test and set its properties + add_test(NAME "${CTestName}" COMMAND ${OptionalCatchTestLauncher} $ ${Name} ${AdditionalCatchParameters}) + # Old CMake versions do not document VERSION_GREATER_EQUAL, so we use VERSION_GREATER with 3.8 instead + if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_GREATER "3.8") + ParseAndAddCatchTests_PrintDebugMessage("Setting DISABLED test property") + set_tests_properties("${CTestName}" PROPERTIES DISABLED ON) + else() + set_tests_properties("${CTestName}" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran" + LABELS "${Labels}") + endif() + set_property( + TARGET ${TestTarget} + APPEND + PROPERTY ParseAndAddCatchTests_TESTS "${CTestName}") + set_property( + SOURCE ${SourceFile} + APPEND + PROPERTY ParseAndAddCatchTests_TESTS "${CTestName}") + endif() + + + endforeach() +endfunction() + +# entry point +function(ParseAndAddCatchTests TestTarget) + ParseAndAddCatchTests_PrintDebugMessage("Started parsing ${TestTarget}") + get_target_property(SourceFiles ${TestTarget} SOURCES) + ParseAndAddCatchTests_PrintDebugMessage("Found the following sources: ${SourceFiles}") + foreach(SourceFile ${SourceFiles}) + ParseAndAddCatchTests_ParseFile(${SourceFile} ${TestTarget}) + endforeach() + ParseAndAddCatchTests_PrintDebugMessage("Finished parsing ${TestTarget}") +endfunction() diff --git a/gulrak-filesystem/test/exception.cpp b/gulrak-filesystem/test/exception.cpp new file mode 100644 index 0000000..8d8b745 --- /dev/null +++ b/gulrak-filesystem/test/exception.cpp @@ -0,0 +1,5 @@ +#include + +int main() { + return 0; +} diff --git a/gulrak-filesystem/test/filesystem_test.cpp b/gulrak-filesystem/test/filesystem_test.cpp new file mode 100644 index 0000000..7fdd0a5 --- /dev/null +++ b/gulrak-filesystem/test/filesystem_test.cpp @@ -0,0 +1,2956 @@ +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (defined(WIN32) || defined(_WIN32)) && !defined(__GNUC__) +#define NOMINMAX 1 +#endif + +#ifdef USE_STD_FS +#include +namespace fs { +using namespace std::filesystem; +using ifstream = std::ifstream; +using ofstream = std::ofstream; +using fstream = std::fstream; +} // namespace fs +#ifdef __GNUC__ +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif +#ifdef _MSC_VER +#define IS_WCHAR_PATH +#endif +#ifdef WIN32 +#define GHC_OS_WINDOWS +#endif +#else +#ifdef GHC_FILESYSTEM_FWD_TEST +#include +#else +#include +#endif +namespace fs { +using namespace ghc::filesystem; +using ifstream = ghc::filesystem::ifstream; +using ofstream = ghc::filesystem::ofstream; +using fstream = ghc::filesystem::fstream; +} // namespace fs +#endif + +#if defined(WIN32) || defined(_WIN32) +#include +#else +#include +#include +#include +#include +#include +#endif + +#ifndef GHC_FILESYSTEM_FWD_TEST +#define CATCH_CONFIG_MAIN +#endif +#include "catch.hpp" + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Behaviour Switches (should match the config in ghc/filesystem.hpp): +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories +#define TEST_LWG_2682_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular +// file with that name, it is superceded by P1164R1, so only activate if really needed +// #define TEST_LWG_2935_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2) +#define TEST_LWG_2937_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +template +std::time_t to_time_t(TP tp) +{ + using namespace std::chrono; + auto sctp = time_point_cast(tp - TP::clock::now() + system_clock::now()); + return system_clock::to_time_t(sctp); +} + +template +TP from_time_t(std::time_t t) +{ + using namespace std::chrono; + auto sctp = system_clock::from_time_t(t); + auto tp = time_point_cast(sctp - system_clock::now() + TP::clock::now()); + return tp; +} + +namespace Catch { +template <> +struct StringMaker +{ + static std::string convert(fs::path const& value) { return '"' + value.string() + '"'; } +}; + +template <> +struct StringMaker +{ + static std::string convert(fs::perms const& value) { return std::to_string(static_cast(value)); } +}; + +template <> +struct StringMaker +{ + static std::string convert(fs::file_status const& value) { + return std::string("[") + std::to_string(static_cast(value.type())) + "," + std::to_string(static_cast(value.permissions())) + "]"; + } +}; + +#ifdef __cpp_lib_char8_t +template <> +struct StringMaker +{ + static std::string convert(char8_t const& value) { return std::to_string(static_cast(value)); } +}; +#endif + +template <> +struct StringMaker +{ + static std::string convert(fs::file_time_type const& value) + { + std::time_t t = to_time_t(value); + std::tm* ptm = std::localtime(&t); + std::ostringstream os; + if (ptm) { + std::tm ttm = *ptm; + os << std::put_time(&ttm, "%Y-%m-%d %H:%M:%S"); + } + else { + os << "(invalid-time)"; + } + return os.str(); + } +}; +} // namespace Catch + +enum class TempOpt { none, change_path }; +class TemporaryDirectory +{ +public: + TemporaryDirectory(TempOpt opt = TempOpt::none) + { + static auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + static auto rng = std::bind(std::uniform_int_distribution(0, 35), std::mt19937(static_cast(seed) ^ static_cast(reinterpret_cast(&opt)))); + std::string filename; + do { + filename = "test_"; + for (int i = 0; i < 8; ++i) { + filename += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[rng()]; + } + _path = fs::canonical(fs::temp_directory_path()) / filename; + } while (fs::exists(_path)); + fs::create_directories(_path); + if (opt == TempOpt::change_path) { + _orig_dir = fs::current_path(); + fs::current_path(_path); + } + } + + ~TemporaryDirectory() + { + if (!_orig_dir.empty()) { + fs::current_path(_orig_dir); + } + fs::remove_all(_path); + } + + const fs::path& path() const { return _path; } + +private: + fs::path _path; + fs::path _orig_dir; +}; + +static void generateFile(const fs::path& pathname, int withSize = -1) +{ + fs::ofstream outfile(pathname); + if (withSize < 0) { + outfile << "Hello world!" << std::endl; + } + else { + outfile << std::string(size_t(withSize), '*'); + } +} + +#ifdef GHC_OS_WINDOWS +inline bool isWow64Proc() +{ + typedef BOOL(WINAPI * IsWow64Process_t)(HANDLE, PBOOL); + BOOL bIsWow64 = FALSE; + auto fnIsWow64Process = (IsWow64Process_t)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); + if (NULL != fnIsWow64Process) { + if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64)) { + bIsWow64 = FALSE; + } + } + return bIsWow64 == TRUE; +} + +static bool is_symlink_creation_supported() +{ + bool result = true; + HKEY key; + REGSAM flags = KEY_READ; +#ifdef _WIN64 + flags |= KEY_WOW64_64KEY; +#elif defined(KEY_WOW64_64KEY) + if (isWow64Proc()) { + flags |= KEY_WOW64_64KEY; + } + else { + flags |= KEY_WOW64_32KEY; + } +#else + result = false; +#endif + if (result) { + auto err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", 0, flags, &key); + if (err == ERROR_SUCCESS) { + DWORD val = 0, size = sizeof(DWORD); + err = RegQueryValueExW(key, L"AllowDevelopmentWithoutDevLicense", 0, NULL, reinterpret_cast(&val), &size); + RegCloseKey(key); + if (err != ERROR_SUCCESS) { + result = false; + } + else { + result = (val != 0); + } + } + else { + result = false; + } + } + if (!result) { + std::clog << "Warning: Symlink creation not supported." << std::endl; + } + return result; +} +#else +static bool is_symlink_creation_supported() +{ + return true; +} +#endif + +static bool has_host_root_name_support() +{ + return fs::path("//host").has_root_name(); +} + +template +class TestAllocator +{ +public: + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using difference_type = ptrdiff_t; + using size_type = size_t; + TestAllocator() noexcept {} + template + TestAllocator(TestAllocator const&) noexcept + { + } + value_type* allocate(std::size_t n) { return static_cast(::operator new(n * sizeof(value_type))); } + void deallocate(value_type* p, std::size_t) noexcept { ::operator delete(p); } + template + struct rebind { + typedef TestAllocator other; + }; +}; + +template +bool operator==(TestAllocator const&, TestAllocator const&) noexcept +{ + return true; +} + +template +bool operator!=(TestAllocator const& x, TestAllocator const& y) noexcept +{ + return !(x == y); +} + +TEST_CASE("Temporary Directory", "[fs.test.tempdir]") +{ + fs::path tempPath; + { + TemporaryDirectory t; + tempPath = t.path(); + REQUIRE(fs::exists(fs::path(t.path()))); + REQUIRE(fs::is_directory(t.path())); + } + REQUIRE(!fs::exists(tempPath)); +} + +#ifdef GHC_FILESYSTEM_VERSION +TEST_CASE("fs::detail::fromUtf8", "[filesystem][fs.detail.utf8]") +{ + CHECK(fs::detail::fromUtf8("foobar").length() == 6); + CHECK(fs::detail::fromUtf8("foobar") == L"foobar"); + CHECK(fs::detail::fromUtf8(u8"föobar").length() == 6); + CHECK(fs::detail::fromUtf8(u8"föobar") == L"föobar"); + + CHECK(fs::detail::toUtf8(std::wstring(L"foobar")).length() == 6); + CHECK(fs::detail::toUtf8(std::wstring(L"foobar")) == "foobar"); + CHECK(fs::detail::toUtf8(std::wstring(L"föobar")).length() == 7); + //CHECK(fs::detail::toUtf8(std::wstring(L"föobar")) == u8"föobar"); + +#ifdef GHC_RAISE_UNICODE_ERRORS + CHECK_THROWS_AS(fs::detail::fromUtf8(std::string("\xed\xa0\x80")), fs::filesystem_error); + CHECK_THROWS_AS(fs::detail::fromUtf8(std::string("\xc3")), fs::filesystem_error); +#else + CHECK(std::u16string(2,0xfffd) == fs::detail::fromUtf8(std::string("\xed\xa0\x80"))); + CHECK(std::u16string(1,0xfffd) == fs::detail::fromUtf8(std::string("\xc3"))); +#endif +} + +TEST_CASE("fs::detail::toUtf8", "[filesystem][fs.detail.utf8]") +{ + std::string t; + CHECK(std::string("\xc3\xa4/\xe2\x82\xac\xf0\x9d\x84\x9e") == fs::detail::toUtf8(std::u16string(u"\u00E4/\u20AC\U0001D11E"))); +#ifdef GHC_RAISE_UNICODE_ERRORS + CHECK_THROWS_AS(fs::detail::toUtf8(std::u16string(1, 0xd800)), fs::filesystem_error); + CHECK_THROWS_AS(fs::detail::appendUTF8(t, 0x200000), fs::filesystem_error); +#else + CHECK(std::string("\xEF\xBF\xBD") == fs::detail::toUtf8(std::u16string(1, 0xd800))); + fs::detail::appendUTF8(t, 0x200000); + CHECK(std::string("\xEF\xBF\xBD") == t); +#endif +} +#endif + +TEST_CASE("fs.path.generic - path::preferred_separator", "[filesystem][path][fs.path.generic]") +{ +#ifdef GHC_OS_WINDOWS + CHECK(fs::path::preferred_separator == '\\'); +#else + CHECK(fs::path::preferred_separator == '/'); +#endif +} + +#ifndef GHC_OS_WINDOWS +TEST_CASE("fs.path.generic - path(\"//host\").has_root_name()", "[filesystem][path][fs.path.generic]") +{ + if (!has_host_root_name_support()) { + WARN("This implementation doesn't support path(\"//host\").has_root_name() == true [C++17 30.12.8.1 par. 4] on this platform, tests based on this are skipped. (Should be okay.)"); + } +} +#endif + +TEST_CASE("fs.path.construct - path constructors and destructor", "[filesystem][path][fs.path.construct]") +{ + CHECK("/usr/local/bin" == fs::path("/usr/local/bin").generic_string()); + std::string str = "/usr/local/bin"; +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + std::u8string u8str = u8"/usr/local/bin"; +#endif + std::u16string u16str = u"/usr/local/bin"; + std::u32string u32str = U"/usr/local/bin"; +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + CHECK(u8str == fs::path(u8str).generic_u8string()); +#endif + CHECK(u16str == fs::path(u16str).generic_u16string()); + CHECK(u32str == fs::path(u32str).generic_u32string()); + CHECK(str == fs::path(str, fs::path::format::generic_format)); + CHECK(str == fs::path(str.begin(), str.end())); + CHECK(fs::path(std::wstring(3, 67)) == "CCC"); +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + CHECK(str == fs::path(u8str.begin(), u8str.end())); +#endif + CHECK(str == fs::path(u16str.begin(), u16str.end())); + CHECK(str == fs::path(u32str.begin(), u32str.end())); +#ifdef GHC_FILESYSTEM_VERSION + CHECK(fs::path("///foo/bar") == "/foo/bar"); + CHECK(fs::path("//foo//bar") == "//foo/bar"); +#endif +#ifdef GHC_OS_WINDOWS + CHECK("\\usr\\local\\bin" == fs::path("/usr/local/bin")); + CHECK("C:\\usr\\local\\bin" == fs::path("C:\\usr\\local\\bin")); +#else + CHECK("/usr/local/bin" == fs::path("/usr/local/bin")); +#endif + if (has_host_root_name_support()) { + CHECK("//host/foo/bar" == fs::path("//host/foo/bar")); + } + +#if !defined(GHC_OS_WINDOWS) && !(defined(__GLIBCXX__) && !(defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 8))) && !defined(USE_STD_FS) + std::locale loc; + bool testUTF8Locale = false; + try { + if (const char* lang = std::getenv("LANG")) { + loc = std::locale(lang); + } + else { + loc = std::locale("en_US.UTF-8"); + } + std::string name = loc.name(); + if (name.length() > 5 && (name.substr(name.length() - 5) == "UTF-8" || name.substr(name.length() - 5) == "utf-8")) { + testUTF8Locale = true; + } + } + catch (std::runtime_error&) { + WARN("Couldn't create an UTF-8 locale!"); + } + if (testUTF8Locale) { + CHECK("/usr/local/bin" == fs::path("/usr/local/bin", loc)); + CHECK(str == fs::path(str.begin(), str.end(), loc)); + CHECK(str == fs::path(u16str.begin(), u16str.end(), loc)); + CHECK(str == fs::path(u32str.begin(), u32str.end(), loc)); + } +#endif +} + +TEST_CASE("fs.path.assign - path assignments", "[filesystem][path][fs.path.assign]") +{ + fs::path p1{"/foo/bar"}; + fs::path p2{"/usr/local"}; + fs::path p3; + p3 = p1; + REQUIRE(p1 == p3); + p3 = fs::path{"/usr/local"}; + REQUIRE(p2 == p3); + p3 = fs::path{L"/usr/local"}; + REQUIRE(p2 == p3); + p3.assign(L"/usr/local"); + REQUIRE(p2 == p3); +#if defined(IS_WCHAR_PATH) || defined(GHC_USE_WCHAR_T) + p3 = fs::path::string_type{L"/foo/bar"}; + REQUIRE(p1 == p3); + p3.assign(fs::path::string_type{L"/usr/local"}); + REQUIRE(p2 == p3); +#else + p3 = fs::path::string_type{"/foo/bar"}; + REQUIRE(p1 == p3); + p3.assign(fs::path::string_type{"/usr/local"}); + REQUIRE(p2 == p3); +#endif + p3 = std::u16string(u"/foo/bar"); + REQUIRE(p1 == p3); + p3 = U"/usr/local"; + REQUIRE(p2 == p3); + p3.assign(std::u16string(u"/foo/bar")); + REQUIRE(p1 == p3); + std::string s{"/usr/local"}; + p3.assign(s.begin(), s.end()); + REQUIRE(p2 == p3); +} + +TEST_CASE("fs.path.append - path appends", "[filesystem][path][fs.path.append]") +{ +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("foo") / "c:/bar" == "c:/bar"); + CHECK(fs::path("foo") / "c:" == "c:"); + CHECK(fs::path("c:") / "" == "c:"); + CHECK(fs::path("c:foo") / "/bar" == "c:/bar"); + CHECK(fs::path("c:foo") / "c:bar" == "c:foo/bar"); +#else + CHECK(fs::path("foo") / "" == "foo/"); + CHECK(fs::path("foo") / "/bar" == "/bar"); + CHECK(fs::path("/foo") / "/" == "/"); + if (has_host_root_name_support()) { + CHECK(fs::path("//host/foo") / "/bar" == "/bar"); + CHECK(fs::path("//host") / "/" == "//host/"); + CHECK(fs::path("//host/foo") / "/" == "/"); + } +#endif + CHECK(fs::path("/foo/bar") / "some///other" == "/foo/bar/some/other"); + fs::path p1{"/tmp/test"}; + fs::path p2{"foobar.txt"}; + fs::path p3 = p1 / p2; + CHECK("/tmp/test/foobar.txt" == p3); + // TODO: append(first, last) +} + +TEST_CASE("fs.path.concat - path concatenation", "[filesystem][path][fs.path.concat]") +{ + CHECK((fs::path("foo") += fs::path("bar")) == "foobar"); + CHECK((fs::path("foo") += fs::path("/bar")) == "foo/bar"); + + CHECK((fs::path("foo") += std::string("bar")) == "foobar"); + CHECK((fs::path("foo") += std::string("/bar")) == "foo/bar"); + + CHECK((fs::path("foo") += "bar") == "foobar"); + CHECK((fs::path("foo") += "/bar") == "foo/bar"); + CHECK((fs::path("foo") += L"bar") == "foobar"); + CHECK((fs::path("foo") += L"/bar") == "foo/bar"); + + CHECK((fs::path("foo") += 'b') == "foob"); + CHECK((fs::path("foo") += '/') == "foo/"); + CHECK((fs::path("foo") += L'b') == "foob"); + CHECK((fs::path("foo") += L'/') == "foo/"); + + CHECK((fs::path("foo") += std::string("bar")) == "foobar"); + CHECK((fs::path("foo") += std::string("/bar")) == "foo/bar"); + + CHECK((fs::path("foo") += std::u16string(u"bar")) == "foobar"); + CHECK((fs::path("foo") += std::u16string(u"/bar")) == "foo/bar"); + + CHECK((fs::path("foo") += std::u32string(U"bar")) == "foobar"); + CHECK((fs::path("foo") += std::u32string(U"/bar")) == "foo/bar"); + + CHECK(fs::path("foo").concat("bar") == "foobar"); + CHECK(fs::path("foo").concat("/bar") == "foo/bar"); + CHECK(fs::path("foo").concat(L"bar") == "foobar"); + CHECK(fs::path("foo").concat(L"/bar") == "foo/bar"); + std::string bar = "bar"; + CHECK(fs::path("foo").concat(bar.begin(), bar.end()) == "foobar"); +#ifndef USE_STD_FS + CHECK((fs::path("/foo/bar") += "/some///other") == "/foo/bar/some/other"); +#endif + // TODO: contat(first, last) +} + +TEST_CASE("fs.path.modifiers - path modifiers", "[filesystem][path][fs.path.modifiers]") +{ + fs::path p = fs::path("/foo/bar"); + p.clear(); + CHECK(p == ""); + + // make_preferred() is a no-op +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("foo\\bar") == "foo/bar"); + CHECK(fs::path("foo\\bar").make_preferred() == "foo/bar"); +#else + CHECK(fs::path("foo\\bar") == "foo\\bar"); + CHECK(fs::path("foo\\bar").make_preferred() == "foo\\bar"); +#endif + CHECK(fs::path("foo/bar").make_preferred() == "foo/bar"); + + CHECK(fs::path("foo/bar").remove_filename() == "foo/"); + CHECK(fs::path("foo/").remove_filename() == "foo/"); + CHECK(fs::path("/foo").remove_filename() == "/"); + CHECK(fs::path("/").remove_filename() == "/"); + + CHECK(fs::path("/foo").replace_filename("bar") == "/bar"); + CHECK(fs::path("/").replace_filename("bar") == "/bar"); + CHECK(fs::path("/foo").replace_filename("b//ar") == "/b/ar"); + + CHECK(fs::path("/foo/bar.txt").replace_extension("odf") == "/foo/bar.odf"); + CHECK(fs::path("/foo/bar.txt").replace_extension() == "/foo/bar"); + CHECK(fs::path("/foo/bar").replace_extension("odf") == "/foo/bar.odf"); + CHECK(fs::path("/foo/bar").replace_extension(".odf") == "/foo/bar.odf"); + CHECK(fs::path("/foo/bar.").replace_extension(".odf") == "/foo/bar.odf"); + CHECK(fs::path("/foo/bar/").replace_extension("odf") == "/foo/bar/.odf"); + + fs::path p1 = "foo"; + fs::path p2 = "bar"; + p1.swap(p2); + CHECK(p1 == "bar"); + CHECK(p2 == "foo"); +} + +TEST_CASE("fs.path.native.obs - path native format observers", "[filesystem][path][fs.path.native.obs]") +{ +#ifdef GHC_OS_WINDOWS +#if defined(IS_WCHAR_PATH) || defined(GHC_USE_WCHAR_T) + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").native() == fs::path::string_type(L"\u00E4\\\u20AC")); + // CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("ä\\€")); // MSVCs returns local DBCS encoding +#else + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").native() == fs::path::string_type("\xc3\xa4\\\xe2\x82\xac")); + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("\xc3\xa4\\\xe2\x82\xac")); + CHECK(!::strcmp(fs::u8path("\xc3\xa4\\\xe2\x82\xac").c_str(), "\xc3\xa4\\\xe2\x82\xac")); + CHECK((std::string)fs::u8path("\xc3\xa4\\\xe2\x82\xac") == std::string("\xc3\xa4\\\xe2\x82\xac")); +#endif + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").wstring() == std::wstring(L"\u00E4\\\u20AC")); +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u8string() == std::u8string(u8"\u00E4\\\u20AC")); +#else + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u8string() == std::string("\xc3\xa4\\\xe2\x82\xac")); +#endif + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u16string() == std::u16string(u"\u00E4\\\u20AC")); + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u32string() == std::u32string(U"\U000000E4\\\U000020AC")); +#else + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").native() == fs::path::string_type("\xc3\xa4/\xe2\x82\xac")); + CHECK(!::strcmp(fs::u8path("\xc3\xa4/\xe2\x82\xac").c_str(), "\xc3\xa4/\xe2\x82\xac")); + CHECK((std::string)fs::u8path("\xc3\xa4/\xe2\x82\xac") == std::string("\xc3\xa4/\xe2\x82\xac")); + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").string() == std::string("\xc3\xa4/\xe2\x82\xac")); + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").wstring() == std::wstring(L"ä/€")); +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u8string() == std::u8string(u8"\xc3\xa4/\xe2\x82\xac")); +#else + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u8string() == std::string("\xc3\xa4/\xe2\x82\xac")); +#endif + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u16string() == std::u16string(u"\u00E4/\u20AC")); + INFO("This check might fail on GCC8 (with \"Illegal byte sequence\") due to not detecting the valid unicode codepoint U+1D11E."); + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac\xf0\x9d\x84\x9e").u16string() == std::u16string(u"\u00E4/\u20AC\U0001D11E")); + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").u32string() == std::u32string(U"\U000000E4/\U000020AC")); +#endif +} + +TEST_CASE("fs.path.generic.obs - path generic format observers", "[filesystem][path][fs.path.generic.obs]") +{ +#ifdef GHC_OS_WINDOWS +#ifndef IS_WCHAR_PATH + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_string() == std::string("\xc3\xa4/\xe2\x82\xac")); +#endif +#ifndef USE_STD_FS + auto t = fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_string, TestAllocator>(); + CHECK(t.c_str() == std::string("\xc3\xa4/\xe2\x82\xac")); +#endif + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_wstring() == std::wstring(L"\U000000E4/\U000020AC")); +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u8string() == std::u8string(u8"\u00E4/\u20AC")); +#else + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u8string() == std::string("\xc3\xa4/\xe2\x82\xac")); +#endif + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u16string() == std::u16string(u"\u00E4/\u20AC")); + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_u32string() == std::u32string(U"\U000000E4/\U000020AC")); +#else + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_string() == std::string("\xc3\xa4/\xe2\x82\xac")); +#ifndef USE_STD_FS + auto t = fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_string, TestAllocator>(); + CHECK(t.c_str() == std::string("\xc3\xa4/\xe2\x82\xac")); +#endif + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_wstring() == std::wstring(L"ä/€")); +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u8string() == std::u8string(u8"\xc3\xa4/\xe2\x82\xac")); +#else + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u8string() == std::string("\xc3\xa4/\xe2\x82\xac")); +#endif + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u16string() == std::u16string(u"\u00E4/\u20AC")); + CHECK(fs::u8path("\xc3\xa4/\xe2\x82\xac").generic_u32string() == std::u32string(U"\U000000E4/\U000020AC")); +#endif +} + +TEST_CASE("fs.path.compare - path compare", "[filesystem][path][fs.path.compare]") +{ + CHECK(fs::path("/foo/b").compare("/foo/a") > 0); + CHECK(fs::path("/foo/b").compare("/foo/b") == 0); + CHECK(fs::path("/foo/b").compare("/foo/c") < 0); + + CHECK(fs::path("/foo/b").compare(std::string("/foo/a")) > 0); + CHECK(fs::path("/foo/b").compare(std::string("/foo/b")) == 0); + CHECK(fs::path("/foo/b").compare(std::string("/foo/c")) < 0); + + CHECK(fs::path("/foo/b").compare(fs::path("/foo/a")) > 0); + CHECK(fs::path("/foo/b").compare(fs::path("/foo/b")) == 0); + CHECK(fs::path("/foo/b").compare(fs::path("/foo/c")) < 0); + +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("c:\\a\\b").compare("C:\\a\\b") == 0); + CHECK(fs::path("c:\\a\\b").compare("d:\\a\\b") != 0); + CHECK(fs::path("c:\\a\\b").compare("C:\\A\\b") != 0); +#endif + +#ifdef LWG_2936_BEHAVIOUR + CHECK(fs::path("/a/b/").compare("/a/b/c") < 0); + CHECK(fs::path("/a/b/").compare("a/c") > 0); +#endif // LWG_2936_BEHAVIOUR +} + +TEST_CASE("fs.path.decompose - path decomposition", "[filesystem][path][fs.path.decompose]") +{ + // root_name() + CHECK(fs::path("").root_name() == ""); + CHECK(fs::path(".").root_name() == ""); + CHECK(fs::path("..").root_name() == ""); + CHECK(fs::path("foo").root_name() == ""); + CHECK(fs::path("/").root_name() == ""); + CHECK(fs::path("/foo").root_name() == ""); + CHECK(fs::path("foo/").root_name() == ""); + CHECK(fs::path("/foo/").root_name() == ""); + CHECK(fs::path("foo/bar").root_name() == ""); + CHECK(fs::path("/foo/bar").root_name() == ""); + CHECK(fs::path("///foo/bar").root_name() == ""); +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("C:/foo").root_name() == "C:"); + CHECK(fs::path("C:\\foo").root_name() == "C:"); + CHECK(fs::path("C:foo").root_name() == "C:"); +#endif + + // root_directory() + CHECK(fs::path("").root_directory() == ""); + CHECK(fs::path(".").root_directory() == ""); + CHECK(fs::path("..").root_directory() == ""); + CHECK(fs::path("foo").root_directory() == ""); + CHECK(fs::path("/").root_directory() == "/"); + CHECK(fs::path("/foo").root_directory() == "/"); + CHECK(fs::path("foo/").root_directory() == ""); + CHECK(fs::path("/foo/").root_directory() == "/"); + CHECK(fs::path("foo/bar").root_directory() == ""); + CHECK(fs::path("/foo/bar").root_directory() == "/"); + CHECK(fs::path("///foo/bar").root_directory() == "/"); +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("C:/foo").root_directory() == "/"); + CHECK(fs::path("C:\\foo").root_directory() == "/"); + CHECK(fs::path("C:foo").root_directory() == ""); +#endif + + // root_path() + CHECK(fs::path("").root_path() == ""); + CHECK(fs::path(".").root_path() == ""); + CHECK(fs::path("..").root_path() == ""); + CHECK(fs::path("foo").root_path() == ""); + CHECK(fs::path("/").root_path() == "/"); + CHECK(fs::path("/foo").root_path() == "/"); + CHECK(fs::path("foo/").root_path() == ""); + CHECK(fs::path("/foo/").root_path() == "/"); + CHECK(fs::path("foo/bar").root_path() == ""); + CHECK(fs::path("/foo/bar").root_path() == "/"); + CHECK(fs::path("///foo/bar").root_path() == "/"); +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("C:/foo").root_path() == "C:/"); + CHECK(fs::path("C:\\foo").root_path() == "C:/"); + CHECK(fs::path("C:foo").root_path() == "C:"); +#endif + + // relative_path() + CHECK(fs::path("").relative_path() == ""); + CHECK(fs::path(".").relative_path() == "."); + CHECK(fs::path("..").relative_path() == ".."); + CHECK(fs::path("foo").relative_path() == "foo"); + CHECK(fs::path("/").relative_path() == ""); + CHECK(fs::path("/foo").relative_path() == "foo"); + CHECK(fs::path("foo/").relative_path() == "foo/"); + CHECK(fs::path("/foo/").relative_path() == "foo/"); + CHECK(fs::path("foo/bar").relative_path() == "foo/bar"); + CHECK(fs::path("/foo/bar").relative_path() == "foo/bar"); + CHECK(fs::path("///foo/bar").relative_path() == "foo/bar"); +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("C:/foo").relative_path() == "foo"); + CHECK(fs::path("C:\\foo").relative_path() == "foo"); + CHECK(fs::path("C:foo").relative_path() == "foo"); +#endif + + // parent_path() + CHECK(fs::path("").parent_path() == ""); + CHECK(fs::path(".").parent_path() == ""); + CHECK(fs::path("..").parent_path() == ""); // unintuitive but as defined in the standard + CHECK(fs::path("foo").parent_path() == ""); + CHECK(fs::path("/").parent_path() == "/"); + CHECK(fs::path("/foo").parent_path() == "/"); + CHECK(fs::path("foo/").parent_path() == "foo"); + CHECK(fs::path("/foo/").parent_path() == "/foo"); + CHECK(fs::path("foo/bar").parent_path() == "foo"); + CHECK(fs::path("/foo/bar").parent_path() == "/foo"); + CHECK(fs::path("///foo/bar").parent_path() == "/foo"); +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("C:/foo").parent_path() == "C:/"); + CHECK(fs::path("C:\\foo").parent_path() == "C:/"); + CHECK(fs::path("C:foo").parent_path() == "C:"); +#endif + + // filename() + CHECK(fs::path("").filename() == ""); + CHECK(fs::path(".").filename() == "."); + CHECK(fs::path("..").filename() == ".."); + CHECK(fs::path("foo").filename() == "foo"); + CHECK(fs::path("/").filename() == ""); + CHECK(fs::path("/foo").filename() == "foo"); + CHECK(fs::path("foo/").filename() == ""); + CHECK(fs::path("/foo/").filename() == ""); + CHECK(fs::path("foo/bar").filename() == "bar"); + CHECK(fs::path("/foo/bar").filename() == "bar"); + CHECK(fs::path("///foo/bar").filename() == "bar"); +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("C:/foo").filename() == "foo"); + CHECK(fs::path("C:\\foo").filename() == "foo"); + CHECK(fs::path("C:foo").filename() == "foo"); +#endif + + // stem() + CHECK(fs::path("/foo/bar.txt").stem() == "bar"); + { + fs::path p = "foo.bar.baz.tar"; + CHECK(p.extension() == ".tar"); + p = p.stem(); + CHECK(p.extension() == ".baz"); + p = p.stem(); + CHECK(p.extension() == ".bar"); + p = p.stem(); + CHECK(p == "foo"); + } + CHECK(fs::path("/foo/.profile").stem() == ".profile"); + CHECK(fs::path(".bar").stem() == ".bar"); + CHECK(fs::path("..bar").stem() == "."); + + // extension() + CHECK(fs::path("/foo/bar.txt").extension() == ".txt"); + CHECK(fs::path("/foo/bar").extension() == ""); + CHECK(fs::path("/foo/.profile").extension() == ""); + CHECK(fs::path(".bar").extension() == ""); + CHECK(fs::path("..bar").extension() == ".bar"); + + if (has_host_root_name_support()) { + // //host-based root-names + CHECK(fs::path("//host").root_name() == "//host"); + CHECK(fs::path("//host/foo").root_name() == "//host"); + CHECK(fs::path("//host").root_directory() == ""); + CHECK(fs::path("//host/foo").root_directory() == "/"); + CHECK(fs::path("//host").root_path() == "//host"); + CHECK(fs::path("//host/foo").root_path() == "//host/"); + CHECK(fs::path("//host").relative_path() == ""); + CHECK(fs::path("//host/foo").relative_path() == "foo"); + CHECK(fs::path("//host").parent_path() == "//host"); + CHECK(fs::path("//host/foo").parent_path() == "//host/"); + CHECK(fs::path("//host").filename() == ""); + CHECK(fs::path("//host/foo").filename() == "foo"); + } +} + +TEST_CASE("fs.path.query - path query", "[fielsystem][path][fs.path.query]") +{ + // empty + CHECK(fs::path("").empty()); + CHECK(!fs::path("foo").empty()); + + // has_root_path() + CHECK(!fs::path("foo").has_root_path()); + CHECK(!fs::path("foo/bar").has_root_path()); + CHECK(fs::path("/foo").has_root_path()); +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("C:foo").has_root_path()); + CHECK(fs::path("C:/foo").has_root_path()); +#endif + + // has_root_name() + CHECK(!fs::path("foo").has_root_name()); + CHECK(!fs::path("foo/bar").has_root_name()); + CHECK(!fs::path("/foo").has_root_name()); +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("C:foo").has_root_name()); + CHECK(fs::path("C:/foo").has_root_name()); +#endif + + // has_root_directory() + CHECK(!fs::path("foo").has_root_directory()); + CHECK(!fs::path("foo/bar").has_root_directory()); + CHECK(fs::path("/foo").has_root_directory()); +#ifdef GHC_OS_WINDOWS + CHECK(!fs::path("C:foo").has_root_directory()); + CHECK(fs::path("C:/foo").has_root_directory()); +#endif + + // has_relative_path() + CHECK(!fs::path("").has_relative_path()); + CHECK(!fs::path("/").has_relative_path()); + CHECK(fs::path("/foo").has_relative_path()); + + // has_parent_path() + CHECK(!fs::path("").has_parent_path()); + CHECK(!fs::path(".").has_parent_path()); + CHECK(!fs::path("..").has_parent_path()); // unintuitive but as defined in the standard + CHECK(!fs::path("foo").has_parent_path()); + CHECK(fs::path("/").has_parent_path()); + CHECK(fs::path("/foo").has_parent_path()); + CHECK(fs::path("foo/").has_parent_path()); + CHECK(fs::path("/foo/").has_parent_path()); + + // has_filename() + CHECK(fs::path("foo").has_filename()); + CHECK(fs::path("foo/bar").has_filename()); + CHECK(!fs::path("/foo/bar/").has_filename()); + + // has_stem() + CHECK(fs::path("foo").has_stem()); + CHECK(fs::path("foo.bar").has_stem()); + CHECK(fs::path(".profile").has_stem()); + CHECK(!fs::path("/foo/").has_stem()); + + // has_extension() + CHECK(!fs::path("foo").has_extension()); + CHECK(fs::path("foo.bar").has_extension()); + CHECK(!fs::path(".profile").has_extension()); + + // is_absolute() + CHECK(!fs::path("foo/bar").is_absolute()); +#ifdef GHC_OS_WINDOWS + CHECK(!fs::path("/foo").is_absolute()); + CHECK(!fs::path("c:foo").is_absolute()); + CHECK(fs::path("c:/foo").is_absolute()); +#else + CHECK(fs::path("/foo").is_absolute()); +#endif + + // is_relative() + CHECK(fs::path("foo/bar").is_relative()); +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("/foo").is_relative()); + CHECK(fs::path("c:foo").is_relative()); + CHECK(!fs::path("c:/foo").is_relative()); +#else + CHECK(!fs::path("/foo").is_relative()); +#endif + + if (has_host_root_name_support()) { + CHECK(fs::path("//host").has_root_name()); + CHECK(fs::path("//host/foo").has_root_name()); + CHECK(fs::path("//host").has_root_path()); + CHECK(fs::path("//host/foo").has_root_path()); + CHECK(!fs::path("//host").has_root_directory()); + CHECK(fs::path("//host/foo").has_root_directory()); + CHECK(!fs::path("//host").has_relative_path()); + CHECK(fs::path("//host/foo").has_relative_path()); + CHECK(fs::path("//host/foo").is_absolute()); + CHECK(!fs::path("//host/foo").is_relative()); + } +} + +TEST_CASE("fs.path.gen - path generation", "[filesystem][path][fs.path.gen]") +{ + // lexically_normal() + CHECK(fs::path("foo/./bar/..").lexically_normal() == "foo/"); + CHECK(fs::path("foo/.///bar/../").lexically_normal() == "foo/"); + CHECK(fs::path("/foo/../..").lexically_normal() == "/"); + CHECK(fs::path("foo/..").lexically_normal() == "."); + CHECK(fs::path("ab/cd/ef/../../qw").lexically_normal() == "ab/qw"); + CHECK(fs::path("a/b/../../../c").lexically_normal() == "../c"); + CHECK(fs::path("../").lexically_normal() == ".."); +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("\\/\\///\\/").lexically_normal() == "/"); + CHECK(fs::path("a/b/..\\//..///\\/../c\\\\/").lexically_normal() == "../c/"); + CHECK(fs::path("..a/b/..\\//..///\\/../c\\\\/").lexically_normal() == "../c/"); + CHECK(fs::path("..\\").lexically_normal() == ".."); +#endif + + // lexically_relative() + CHECK(fs::path("/a/d").lexically_relative("/a/b/c") == "../../d"); + CHECK(fs::path("/a/b/c").lexically_relative("/a/d") == "../b/c"); + CHECK(fs::path("a/b/c").lexically_relative("a") == "b/c"); + CHECK(fs::path("a/b/c").lexically_relative("a/b/c/x/y") == "../.."); + CHECK(fs::path("a/b/c").lexically_relative("a/b/c") == "."); + CHECK(fs::path("a/b").lexically_relative("c/d") == "../../a/b"); + CHECK(fs::path("a/b").lexically_relative("a/") == "b"); + if (has_host_root_name_support()) { + CHECK(fs::path("//host1/foo").lexically_relative("//host2.bar") == ""); + } +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("c:/foo").lexically_relative("/bar") == ""); + CHECK(fs::path("c:foo").lexically_relative("c:/bar") == ""); + CHECK(fs::path("foo").lexically_relative("/bar") == ""); + CHECK(fs::path("c:/foo/bar.txt").lexically_relative("c:/foo/") == "bar.txt"); + CHECK(fs::path("c:/foo/bar.txt").lexically_relative("C:/foo/") == "bar.txt"); +#else + CHECK(fs::path("/foo").lexically_relative("bar") == ""); + CHECK(fs::path("foo").lexically_relative("/bar") == ""); +#endif + + // lexically_proximate() + CHECK(fs::path("/a/d").lexically_proximate("/a/b/c") == "../../d"); + if (has_host_root_name_support()) { + CHECK(fs::path("//host1/a/d").lexically_proximate("//host2/a/b/c") == "//host1/a/d"); + } + CHECK(fs::path("a/d").lexically_proximate("/a/b/c") == "a/d"); +#ifdef GHC_OS_WINDOWS + CHECK(fs::path("c:/a/d").lexically_proximate("c:/a/b/c") == "../../d"); + CHECK(fs::path("c:/a/d").lexically_proximate("d:/a/b/c") == "c:/a/d"); + CHECK(fs::path("c:/foo").lexically_proximate("/bar") == "c:/foo"); + CHECK(fs::path("c:foo").lexically_proximate("c:/bar") == "c:foo"); + CHECK(fs::path("foo").lexically_proximate("/bar") == "foo"); +#else + CHECK(fs::path("/foo").lexically_proximate("bar") == "/foo"); + CHECK(fs::path("foo").lexically_proximate("/bar") == "foo"); +#endif +} + +static std::string iterateResult(const fs::path& path) +{ + std::ostringstream result; + for (fs::path::const_iterator i = path.begin(); i != path.end(); ++i) { + if (i != path.begin()) { + result << ","; + } + result << i->generic_string(); + } + return result.str(); +} + +static std::string reverseIterateResult(const fs::path& path) +{ + std::ostringstream result; + fs::path::const_iterator iter = path.end(); + bool first = true; + if (iter != path.begin()) { + do { + --iter; + if (!first) { + result << ","; + } + first = false; + result << iter->generic_string(); + } while (iter != path.begin()); + } + return result.str(); +} + +TEST_CASE("fs.path.itr - path iterators", "[filesystem][path][fs.path.itr]") +{ + CHECK(iterateResult(fs::path()).empty()); + CHECK("." == iterateResult(fs::path("."))); + CHECK(".." == iterateResult(fs::path(".."))); + CHECK("foo" == iterateResult(fs::path("foo"))); + CHECK("/" == iterateResult(fs::path("/"))); + CHECK("/,foo" == iterateResult(fs::path("/foo"))); + CHECK("foo," == iterateResult(fs::path("foo/"))); + CHECK("/,foo," == iterateResult(fs::path("/foo/"))); + CHECK("foo,bar" == iterateResult(fs::path("foo/bar"))); + CHECK("/,foo,bar" == iterateResult(fs::path("/foo/bar"))); +#ifndef USE_STD_FS + // ghc::filesystem enforces redundant slashes to be reduced to one + CHECK("/,foo,bar" == iterateResult(fs::path("///foo/bar"))); +#else + // typically std::filesystem keeps them + CHECK("///,foo,bar" == iterateResult(fs::path("///foo/bar"))); +#endif + CHECK("/,foo,bar," == iterateResult(fs::path("/foo/bar///"))); + CHECK("foo,.,bar,..," == iterateResult(fs::path("foo/.///bar/../"))); +#ifdef GHC_OS_WINDOWS + CHECK("C:,/,foo" == iterateResult(fs::path("C:/foo"))); +#endif + + CHECK(reverseIterateResult(fs::path()).empty()); + CHECK("." == reverseIterateResult(fs::path("."))); + CHECK(".." == reverseIterateResult(fs::path(".."))); + CHECK("foo" == reverseIterateResult(fs::path("foo"))); + CHECK("/" == reverseIterateResult(fs::path("/"))); + CHECK("foo,/" == reverseIterateResult(fs::path("/foo"))); + CHECK(",foo" == reverseIterateResult(fs::path("foo/"))); + CHECK(",foo,/" == reverseIterateResult(fs::path("/foo/"))); + CHECK("bar,foo" == reverseIterateResult(fs::path("foo/bar"))); + CHECK("bar,foo,/" == reverseIterateResult(fs::path("/foo/bar"))); +#ifndef USE_STD_FS + // ghc::filesystem enforces redundant slashes to be reduced to one + CHECK("bar,foo,/" == reverseIterateResult(fs::path("///foo/bar"))); +#else + // typically std::filesystem keeps them + CHECK("bar,foo,///" == reverseIterateResult(fs::path("///foo/bar"))); +#endif + CHECK(",bar,foo,/" == reverseIterateResult(fs::path("/foo/bar///"))); + CHECK(",..,bar,.,foo" == reverseIterateResult(fs::path("foo/.///bar/../"))); +#ifdef GHC_OS_WINDOWS + CHECK("foo,/,C:" == reverseIterateResult(fs::path("C:/foo"))); + CHECK("foo,C:" == reverseIterateResult(fs::path("C:foo"))); +#endif + { + fs::path p1 = "/foo/bar/test.txt"; + fs::path p2; + for (auto pe : p1) { + p2 /= pe; + } + CHECK(p1 == p2); + CHECK("bar" == *(--fs::path("/foo/bar").end())); + auto p = fs::path("/foo/bar"); + auto pi = p.end(); + pi--; + CHECK("bar" == *pi); + } + + if (has_host_root_name_support()) { + CHECK("foo" == *(--fs::path("//host/foo").end())); + auto p = fs::path("//host/foo"); + auto pi = p.end(); + pi--; + CHECK("foo" == *pi); + CHECK("//host" == iterateResult(fs::path("//host"))); + CHECK("//host,/,foo" == iterateResult(fs::path("//host/foo"))); + CHECK("//host" == reverseIterateResult(fs::path("//host"))); + CHECK("foo,/,//host" == reverseIterateResult(fs::path("//host/foo"))); + { + fs::path p1 = "//host/foo/bar/test.txt"; + fs::path p2; + for (auto pe : p1) { + p2 /= pe; + } + CHECK(p1 == p2); + } + } +} + +TEST_CASE("fs.path.nonmember - path non-member functions", "[filesystem][path][fs.path.nonmember]") +{ + fs::path p1("foo/bar"); + fs::path p2("some/other"); + fs::swap(p1, p2); + CHECK(p1 == "some/other"); + CHECK(p2 == "foo/bar"); + CHECK(hash_value(p1)); + CHECK(p2 < p1); + CHECK(p2 <= p1); + CHECK(p1 <= p1); + CHECK(!(p1 < p2)); + CHECK(!(p1 <= p2)); + CHECK(p1 > p2); + CHECK(p1 >= p2); + CHECK(p1 >= p1); + CHECK(!(p2 > p1)); + CHECK(!(p2 >= p1)); + CHECK(p1 != p2); + CHECK(p1 / p2 == "some/other/foo/bar"); +} + +TEST_CASE("fs.path.io - path inserter and extractor", "[filesystem][path][fs.path.io]") +{ + { + std::ostringstream os; + os << fs::path("/root/foo bar"); +#ifdef GHC_OS_WINDOWS + CHECK(os.str() == "\"\\\\root\\\\foo bar\""); +#else + CHECK(os.str() == "\"/root/foo bar\""); +#endif + } + { + std::ostringstream os; + os << fs::path("/root/foo\"bar"); +#ifdef GHC_OS_WINDOWS + CHECK(os.str() == "\"\\\\root\\\\foo\\\"bar\""); +#else + CHECK(os.str() == "\"/root/foo\\\"bar\""); +#endif + } + + { + std::istringstream is("\"/root/foo bar\""); + fs::path p; + is >> p; + CHECK(p == fs::path("/root/foo bar")); + CHECK((is.flags() & std::ios_base::skipws) == std::ios_base::skipws); + } + { + std::istringstream is("\"/root/foo bar\""); + is >> std::noskipws; + fs::path p; + is >> p; + CHECK(p == fs::path("/root/foo bar")); + CHECK((is.flags() & std::ios_base::skipws) != std::ios_base::skipws); + } + { + std::istringstream is("\"/root/foo\\\"bar\""); + fs::path p; + is >> p; + CHECK(p == fs::path("/root/foo\"bar")); + } + { + std::istringstream is("/root/foo"); + fs::path p; + is >> p; + CHECK(p == fs::path("/root/foo")); + } +} + +TEST_CASE("fs.path.factory - path factory functions", "[filesystem][path][fs.path.factory]") +{ + CHECK(fs::u8path("foo/bar") == fs::path("foo/bar")); + CHECK(fs::u8path("foo/bar") == fs::path("foo/bar")); + std::string str("/foo/bar/test.txt"); + CHECK(fs::u8path(str.begin(), str.end()) == str); +} + +TEST_CASE("fs.class.filesystem_error - class filesystem_error", "[filesystem][filesystem_error][fs.class.filesystem_error]") +{ + std::error_code ec(1, std::system_category()); + fs::filesystem_error fse("None", std::error_code()); + fse = fs::filesystem_error("Some error", ec); + CHECK(fse.code().value() == 1); + CHECK(!std::string(fse.what()).empty()); + CHECK(fse.path1().empty()); + CHECK(fse.path2().empty()); + fse = fs::filesystem_error("Some error", fs::path("foo/bar"), ec); + CHECK(!std::string(fse.what()).empty()); + CHECK(fse.path1() == "foo/bar"); + CHECK(fse.path2().empty()); + fse = fs::filesystem_error("Some error", fs::path("foo/bar"), fs::path("some/other"), ec); + CHECK(!std::string(fse.what()).empty()); + CHECK(fse.path1() == "foo/bar"); + CHECK(fse.path2() == "some/other"); +} + +constexpr fs::perms constExprOwnerAll() +{ + return fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec; +} + +TEST_CASE("fs.enum - enum class perms", "[filesystem][enum][fs.enum]") +{ + static_assert(constExprOwnerAll() == fs::perms::owner_all, "constexpr didn't result in owner_all"); + CHECK((fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec) == fs::perms::owner_all); + CHECK((fs::perms::group_read | fs::perms::group_write | fs::perms::group_exec) == fs::perms::group_all); + CHECK((fs::perms::others_read | fs::perms::others_write | fs::perms::others_exec) == fs::perms::others_all); + CHECK((fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all) == fs::perms::all); + CHECK((fs::perms::all | fs::perms::set_uid | fs::perms::set_gid | fs::perms::sticky_bit) == fs::perms::mask); +} + +TEST_CASE("fs.class.file_status - class file_status", "[filesystem][file_status][fs.class.file_status]") +{ + { + fs::file_status fs; + CHECK(fs.type() == fs::file_type::none); + CHECK(fs.permissions() == fs::perms::unknown); + } + { + fs::file_status fs{fs::file_type::regular}; + CHECK(fs.type() == fs::file_type::regular); + CHECK(fs.permissions() == fs::perms::unknown); + } + { + fs::file_status fs{fs::file_type::directory, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec}; + CHECK(fs.type() == fs::file_type::directory); + CHECK(fs.permissions() == fs::perms::owner_all); + fs.type(fs::file_type::block); + CHECK(fs.type() == fs::file_type::block); + fs.type(fs::file_type::character); + CHECK(fs.type() == fs::file_type::character); + fs.type(fs::file_type::fifo); + CHECK(fs.type() == fs::file_type::fifo); + fs.type(fs::file_type::symlink); + CHECK(fs.type() == fs::file_type::symlink); + fs.type(fs::file_type::socket); + CHECK(fs.type() == fs::file_type::socket); + fs.permissions(fs.permissions() | fs::perms::group_all | fs::perms::others_all); + CHECK(fs.permissions() == fs::perms::all); + } + { + fs::file_status fst(fs::file_type::regular); + fs::file_status fs(std::move(fst)); + CHECK(fs.type() == fs::file_type::regular); + CHECK(fs.permissions() == fs::perms::unknown); + } +#if !defined(USE_STD_FS) || defined(GHC_FILESYSTEM_RUNNING_CPP20) + { + fs::file_status fs1{fs::file_type::regular, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec}; + fs::file_status fs2{fs::file_type::regular, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec}; + fs::file_status fs3{fs::file_type::directory, fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec}; + fs::file_status fs4{fs::file_type::regular, fs::perms::owner_read | fs::perms::owner_write}; + CHECK(fs1 == fs2); + CHECK_FALSE(fs1 == fs3); + CHECK_FALSE(fs1 == fs4); + } +#endif +} + +TEST_CASE("fs.dir.entry - class directory_entry", "[filesystem][directory_entry][fs.dir.entry]") +{ + TemporaryDirectory t; + std::error_code ec; + auto de = fs::directory_entry(t.path()); + CHECK(de.path() == t.path()); + CHECK((fs::path)de == t.path()); + CHECK(de.exists()); + CHECK(!de.is_block_file()); + CHECK(!de.is_character_file()); + CHECK(de.is_directory()); + CHECK(!de.is_fifo()); + CHECK(!de.is_other()); + CHECK(!de.is_regular_file()); + CHECK(!de.is_socket()); + CHECK(!de.is_symlink()); + CHECK(de.status().type() == fs::file_type::directory); + ec.clear(); + CHECK(de.status(ec).type() == fs::file_type::directory); + CHECK(!ec); + CHECK_NOTHROW(de.refresh()); + fs::directory_entry none; + CHECK_THROWS_AS(none.refresh(), fs::filesystem_error); + ec.clear(); + CHECK_NOTHROW(none.refresh(ec)); + CHECK(ec); + CHECK_THROWS_AS(de.assign(""), fs::filesystem_error); + ec.clear(); + CHECK_NOTHROW(de.assign("", ec)); + CHECK(ec); + generateFile(t.path() / "foo", 1234); + auto now = fs::file_time_type::clock::now(); + CHECK_NOTHROW(de.assign(t.path() / "foo")); + CHECK_NOTHROW(de.assign(t.path() / "foo", ec)); + CHECK(!ec); + de = fs::directory_entry(t.path() / "foo"); + CHECK(de.path() == t.path() / "foo"); + CHECK(de.exists()); + CHECK(de.exists(ec)); + CHECK(!ec); + CHECK(!de.is_block_file()); + CHECK(!de.is_block_file(ec)); + CHECK(!ec); + CHECK(!de.is_character_file()); + CHECK(!de.is_character_file(ec)); + CHECK(!ec); + CHECK(!de.is_directory()); + CHECK(!de.is_directory(ec)); + CHECK(!ec); + CHECK(!de.is_fifo()); + CHECK(!de.is_fifo(ec)); + CHECK(!ec); + CHECK(!de.is_other()); + CHECK(!de.is_other(ec)); + CHECK(!ec); + CHECK(de.is_regular_file()); + CHECK(de.is_regular_file(ec)); + CHECK(!ec); + CHECK(!de.is_socket()); + CHECK(!de.is_socket(ec)); + CHECK(!ec); + CHECK(!de.is_symlink()); + CHECK(!de.is_symlink(ec)); + CHECK(!ec); + CHECK(de.file_size() == 1234); + CHECK(de.file_size(ec) == 1234); + CHECK(std::abs(std::chrono::duration_cast(de.last_write_time() - now).count()) < 3); + ec.clear(); + CHECK(std::abs(std::chrono::duration_cast(de.last_write_time(ec) - now).count()) < 3); + CHECK(!ec); +#ifndef GHC_OS_WEB + CHECK(de.hard_link_count() == 1); + CHECK(de.hard_link_count(ec) == 1); + CHECK(!ec); +#endif + CHECK_THROWS_AS(de.replace_filename("bar"), fs::filesystem_error); + CHECK_NOTHROW(de.replace_filename("foo")); + ec.clear(); + CHECK_NOTHROW(de.replace_filename("bar", ec)); + CHECK(ec); + auto de2none = fs::directory_entry(); + ec.clear(); +#ifndef GHC_OS_WEB + CHECK(de2none.hard_link_count(ec) == static_cast(-1)); + CHECK_THROWS_AS(de2none.hard_link_count(), fs::filesystem_error); + CHECK(ec); +#endif + ec.clear(); + CHECK_NOTHROW(de2none.last_write_time(ec)); + CHECK_THROWS_AS(de2none.last_write_time(), fs::filesystem_error); + CHECK(ec); + ec.clear(); + CHECK_THROWS_AS(de2none.file_size(), fs::filesystem_error); + CHECK(de2none.file_size(ec) == static_cast(-1)); + CHECK(ec); + ec.clear(); + CHECK(de2none.status().type() == fs::file_type::not_found); + CHECK(de2none.status(ec).type() == fs::file_type::not_found); + CHECK(ec); + generateFile(t.path() / "a"); + generateFile(t.path() / "b"); + auto d1 = fs::directory_entry(t.path() / "a"); + auto d2 = fs::directory_entry(t.path() / "b"); + CHECK(d1 < d2); + CHECK(!(d2 < d1)); + CHECK(d1 <= d2); + CHECK(!(d2 <= d1)); + CHECK(d2 > d1); + CHECK(!(d1 > d2)); + CHECK(d2 >= d1); + CHECK(!(d1 >= d2)); + CHECK(d1 != d2); + CHECK(!(d2 != d2)); + CHECK(d1 == d1); + CHECK(!(d1 == d2)); +} + +TEST_CASE("fs.class.directory_iterator - class directory_iterator", "[filesystem][directory_iterator][fs.class.directory_iterator]") +{ + { + TemporaryDirectory t; + CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator()); + generateFile(t.path() / "test", 1234); + REQUIRE(fs::directory_iterator(t.path()) != fs::directory_iterator()); + auto iter = fs::directory_iterator(t.path()); + fs::directory_iterator iter2(iter); + fs::directory_iterator iter3, iter4; + iter3 = iter; + CHECK(iter->path().filename() == "test"); + CHECK(iter2->path().filename() == "test"); + CHECK(iter3->path().filename() == "test"); + iter4 = std::move(iter3); + CHECK(iter4->path().filename() == "test"); + CHECK(iter->path() == t.path() / "test"); + CHECK(!iter->is_symlink()); + CHECK(iter->is_regular_file()); + CHECK(!iter->is_directory()); + CHECK(iter->file_size() == 1234); + CHECK(++iter == fs::directory_iterator()); + CHECK_THROWS_AS(fs::directory_iterator(t.path() / "non-existing"), fs::filesystem_error); + int cnt = 0; + for(auto de : fs::directory_iterator(t.path())) { + ++cnt; + } + CHECK(cnt == 1); + } + if (is_symlink_creation_supported()) { + TemporaryDirectory t; + fs::path td = t.path() / "testdir"; + CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator()); + generateFile(t.path() / "test", 1234); + fs::create_directory(td); + REQUIRE_NOTHROW(fs::create_symlink(t.path() / "test", td / "testlink")); + std::error_code ec; + REQUIRE(fs::directory_iterator(td) != fs::directory_iterator()); + auto iter = fs::directory_iterator(td); + CHECK(iter->path().filename() == "testlink"); + CHECK(iter->path() == td / "testlink"); + CHECK(iter->is_symlink()); + CHECK(iter->is_regular_file()); + CHECK(!iter->is_directory()); + CHECK(iter->file_size() == 1234); + CHECK(++iter == fs::directory_iterator()); + } + { + // Issue #8: check if resources are freed when iterator reaches end() + TemporaryDirectory t(TempOpt::change_path); + auto p = fs::path("test/"); + fs::create_directory(p); + auto iter = fs::directory_iterator(p); + while (iter != fs::directory_iterator()) { + ++iter; + } + CHECK(fs::remove_all(p) == 1); + CHECK_NOTHROW(fs::create_directory(p)); + } +} + +TEST_CASE("fs.class.rec.dir.itr - class recursive_directory_iterator", "[filesystem][recursive_directory_iterator][fs.class.rec.dir.itr]") +{ + { + auto iter = fs::recursive_directory_iterator("."); + iter.pop(); + CHECK(iter == fs::recursive_directory_iterator()); + } + { + TemporaryDirectory t; + CHECK(fs::recursive_directory_iterator(t.path()) == fs::recursive_directory_iterator()); + generateFile(t.path() / "test", 1234); + REQUIRE(fs::recursive_directory_iterator(t.path()) != fs::recursive_directory_iterator()); + auto iter = fs::recursive_directory_iterator(t.path()); + CHECK(iter->path().filename() == "test"); + CHECK(iter->path() == t.path() / "test"); + CHECK(!iter->is_symlink()); + CHECK(iter->is_regular_file()); + CHECK(!iter->is_directory()); + CHECK(iter->file_size() == 1234); + CHECK(++iter == fs::recursive_directory_iterator()); + } + + { + TemporaryDirectory t; + fs::path td = t.path() / "testdir"; + fs::create_directories(td); + generateFile(td / "test", 1234); + REQUIRE(fs::recursive_directory_iterator(t.path()) != fs::recursive_directory_iterator()); + auto iter = fs::recursive_directory_iterator(t.path()); + + CHECK(iter->path().filename() == "testdir"); + CHECK(iter->path() == td); + CHECK(!iter->is_symlink()); + CHECK(!iter->is_regular_file()); + CHECK(iter->is_directory()); + + CHECK(++iter != fs::recursive_directory_iterator()); + + CHECK(iter->path().filename() == "test"); + CHECK(iter->path() == td / "test"); + CHECK(!iter->is_symlink()); + CHECK(iter->is_regular_file()); + CHECK(!iter->is_directory()); + CHECK(iter->file_size() == 1234); + + CHECK(++iter == fs::recursive_directory_iterator()); + } + { + TemporaryDirectory t; + std::error_code ec; + CHECK(fs::recursive_directory_iterator(t.path(), fs::directory_options::none) == fs::recursive_directory_iterator()); + CHECK(fs::recursive_directory_iterator(t.path(), fs::directory_options::none, ec) == fs::recursive_directory_iterator()); + CHECK(!ec); + CHECK(fs::recursive_directory_iterator(t.path(), ec) == fs::recursive_directory_iterator()); + CHECK(!ec); + generateFile(t.path() / "test"); + fs::recursive_directory_iterator rd1(t.path()); + CHECK(fs::recursive_directory_iterator(rd1) != fs::recursive_directory_iterator()); + fs::recursive_directory_iterator rd2(t.path()); + CHECK(fs::recursive_directory_iterator(std::move(rd2)) != fs::recursive_directory_iterator()); + fs::recursive_directory_iterator rd3(t.path(), fs::directory_options::skip_permission_denied); + CHECK(rd3.options() == fs::directory_options::skip_permission_denied); + fs::recursive_directory_iterator rd4; + rd4 = std::move(rd3); + CHECK(rd4 != fs::recursive_directory_iterator()); + CHECK_NOTHROW(++rd4); + CHECK(rd4 == fs::recursive_directory_iterator()); + fs::recursive_directory_iterator rd5; + rd5 = rd4; + } + { + TemporaryDirectory t(TempOpt::change_path); + generateFile("a"); + fs::create_directory("d1"); + fs::create_directory("d1/d2"); + generateFile("d1/b"); + generateFile("d1/c"); + generateFile("d1/d2/d"); + generateFile("e"); + auto iter = fs::recursive_directory_iterator("."); + std::multimap result; + while(iter != fs::recursive_directory_iterator()) { + result.insert(std::make_pair(iter->path().generic_string(), iter.depth())); + ++iter; + } + std::stringstream os; + for(auto p : result) { + os << "[" << p.first << "," << p.second << "],"; + } + CHECK(os.str() == "[./a,0],[./d1,0],[./d1/b,1],[./d1/c,1],[./d1/d2,1],[./d1/d2/d,2],[./e,0],"); + } + { + TemporaryDirectory t(TempOpt::change_path); + generateFile("a"); + fs::create_directory("d1"); + fs::create_directory("d1/d2"); + generateFile("d1/b"); + generateFile("d1/c"); + generateFile("d1/d2/d"); + generateFile("e"); + std::multiset result; + for(auto de : fs::recursive_directory_iterator(".")) { + result.insert(de.path().generic_string()); + } + std::stringstream os; + for(auto p : result) { + os << p << ","; + } + CHECK(os.str() == "./a,./d1,./d1/b,./d1/c,./d1/d2,./d1/d2/d,./e,"); + } + { + TemporaryDirectory t(TempOpt::change_path); + generateFile("a"); + fs::create_directory("d1"); + fs::create_directory("d1/d2"); + generateFile("d1/d2/b"); + generateFile("e"); + auto iter = fs::recursive_directory_iterator("."); + std::multimap result; + while(iter != fs::recursive_directory_iterator()) { + result.insert(std::make_pair(iter->path().generic_string(), iter.depth())); + if(iter->path() == "./d1/d2") { + iter.disable_recursion_pending(); + } + ++iter; + } + std::stringstream os; + for(auto p : result) { + os << "[" << p.first << "," << p.second << "],"; + } + CHECK(os.str() == "[./a,0],[./d1,0],[./d1/d2,1],[./e,0],"); + } + { + TemporaryDirectory t(TempOpt::change_path); + generateFile("a"); + fs::create_directory("d1"); + fs::create_directory("d1/d2"); + generateFile("d1/d2/b"); + generateFile("e"); + auto iter = fs::recursive_directory_iterator("."); + std::multimap result; + while(iter != fs::recursive_directory_iterator()) { + result.insert(std::make_pair(iter->path().generic_string(), iter.depth())); + if(iter->path() == "./d1/d2") { + iter.pop(); + } + else { + ++iter; + } + } + std::stringstream os; + for(auto p : result) { + os << "[" << p.first << "," << p.second << "],"; + } + CHECK(os.str() == "[./a,0],[./d1,0],[./d1/d2,1],[./e,0],"); + } + if (is_symlink_creation_supported()) { + TemporaryDirectory t(TempOpt::change_path); + fs::create_directory("d1"); + generateFile("d1/a"); + fs::create_directory("d2"); + generateFile("d2/b"); + fs::create_directory_symlink("../d1", "d2/ds1"); + fs::create_directory_symlink("d3", "d2/ds2"); + std::multiset result; + REQUIRE_NOTHROW([&](){ + for (const auto& de : fs::recursive_directory_iterator("d2", fs::directory_options::follow_directory_symlink)) { + result.insert(de.path().generic_string()); + } + }()); + std::stringstream os; + for(const auto& p : result) { + os << p << ","; + } + CHECK(os.str() == "d2/b,d2/ds1,d2/ds1/a,d2/ds2,"); + os.str(""); + result.clear(); + REQUIRE_NOTHROW([&](){ + for (const auto& de : fs::recursive_directory_iterator("d2")) { + result.insert(de.path().generic_string()); + } + }()); + for(const auto& p : result) { + os << p << ","; + } + CHECK(os.str() == "d2/b,d2/ds1,d2/ds2,"); + } +} + +TEST_CASE("fs.op.absolute - absolute", "[filesystem][operations][fs.op.absolute]") +{ + CHECK(fs::absolute("") == fs::current_path() / ""); + CHECK(fs::absolute(fs::current_path()) == fs::current_path()); + CHECK(fs::absolute(".") == fs::current_path() / "."); + CHECK((fs::absolute("..") == fs::current_path().parent_path() || fs::absolute("..") == fs::current_path() / "..")); + CHECK(fs::absolute("foo") == fs::current_path() / "foo"); + std::error_code ec; + CHECK(fs::absolute("", ec) == fs::current_path() / ""); + CHECK(!ec); + CHECK(fs::absolute("foo", ec) == fs::current_path() / "foo"); + CHECK(!ec); +} + +TEST_CASE("fs.op.canonical - canonical", "[filesystem][operations][fs.op.canonical]") +{ + CHECK_THROWS_AS(fs::canonical(""), fs::filesystem_error); + { + std::error_code ec; + CHECK(fs::canonical("", ec) == ""); + CHECK(ec); + } + CHECK(fs::canonical(fs::current_path()) == fs::current_path()); + + CHECK(fs::canonical(".") == fs::current_path()); + CHECK(fs::canonical("..") == fs::current_path().parent_path()); + CHECK(fs::canonical("/") == fs::current_path().root_path()); + CHECK_THROWS_AS(fs::canonical("foo"), fs::filesystem_error); + { + std::error_code ec; + CHECK_NOTHROW(fs::canonical("foo", ec)); + CHECK(ec); + } + { + TemporaryDirectory t(TempOpt::change_path); + auto dir = t.path() / "d0"; + fs::create_directories(dir / "d1"); + generateFile(dir / "f0"); + fs::path rel(dir.filename()); + CHECK(fs::canonical(dir) == dir); + CHECK(fs::canonical(rel) == dir); + CHECK(fs::canonical(dir / "f0") == dir / "f0"); + CHECK(fs::canonical(rel / "f0") == dir / "f0"); + CHECK(fs::canonical(rel / "./f0") == dir / "f0"); + CHECK(fs::canonical(rel / "d1/../f0") == dir / "f0"); + } + + if (is_symlink_creation_supported()) { + TemporaryDirectory t(TempOpt::change_path); + fs::create_directory(t.path() / "dir1"); + generateFile(t.path() / "dir1/test1"); + fs::create_directory(t.path() / "dir2"); + fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym"); + CHECK(fs::canonical(t.path() / "dir2/dirSym/test1") == t.path() / "dir1/test1"); + } +} + +TEST_CASE("fs.op.copy - copy", "[filesystem][operations][fs.op.copy]") +{ + { + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + fs::create_directory("dir1"); + generateFile("dir1/file1"); + generateFile("dir1/file2"); + fs::create_directory("dir1/dir2"); + generateFile("dir1/dir2/file3"); + CHECK_NOTHROW(fs::copy("dir1", "dir3")); + CHECK(fs::exists("dir3/file1")); + CHECK(fs::exists("dir3/file2")); + CHECK(!fs::exists("dir3/dir2")); + CHECK_NOTHROW(fs::copy("dir1", "dir4", fs::copy_options::recursive, ec)); + CHECK(!ec); + CHECK(fs::exists("dir4/file1")); + CHECK(fs::exists("dir4/file2")); + CHECK(fs::exists("dir4/dir2/file3")); + fs::create_directory("dir5"); + generateFile("dir5/file1"); + CHECK_THROWS_AS(fs::copy("dir1/file1", "dir5/file1"), fs::filesystem_error); + CHECK_NOTHROW(fs::copy("dir1/file1", "dir5/file1", fs::copy_options::skip_existing)); + } + if (is_symlink_creation_supported()) { + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + fs::create_directory("dir1"); + generateFile("dir1/file1"); + generateFile("dir1/file2"); + fs::create_directory("dir1/dir2"); + generateFile("dir1/dir2/file3"); +#ifdef TEST_LWG_2682_BEHAVIOUR + REQUIRE_THROWS_AS(fs::copy("dir1", "dir3", fs::copy_options::create_symlinks | fs::copy_options::recursive), fs::filesystem_error); +#else + REQUIRE_NOTHROW(fs::copy("dir1", "dir3", fs::copy_options::create_symlinks | fs::copy_options::recursive)); + CHECK(!ec); + CHECK(fs::exists("dir3/file1")); + CHECK(fs::is_symlink("dir3/file1")); + CHECK(fs::exists("dir3/file2")); + CHECK(fs::is_symlink("dir3/file2")); + CHECK(fs::exists("dir3/dir2/file3")); + CHECK(fs::is_symlink("dir3/dir2/file3")); +#endif + } +#ifndef GHC_OS_WEB + { + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + fs::create_directory("dir1"); + generateFile("dir1/file1"); + generateFile("dir1/file2"); + fs::create_directory("dir1/dir2"); + generateFile("dir1/dir2/file3"); + auto f1hl = fs::hard_link_count("dir1/file1"); + auto f2hl = fs::hard_link_count("dir1/file2"); + auto f3hl = fs::hard_link_count("dir1/dir2/file3"); + CHECK_NOTHROW(fs::copy("dir1", "dir3", fs::copy_options::create_hard_links | fs::copy_options::recursive, ec)); + REQUIRE(!ec); + CHECK(fs::exists("dir3/file1")); + CHECK(fs::hard_link_count("dir1/file1") == f1hl + 1); + CHECK(fs::exists("dir3/file2")); + CHECK(fs::hard_link_count("dir1/file2") == f2hl + 1); + CHECK(fs::exists("dir3/dir2/file3")); + CHECK(fs::hard_link_count("dir1/dir2/file3") == f3hl + 1); + } +#endif +} + +TEST_CASE("fs.op.copy_file - copy_file", "[filesystem][operations][fs.op.copy_file]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + generateFile("foo", 100); + CHECK(!fs::exists("bar")); + CHECK(fs::copy_file("foo", "bar")); + CHECK(fs::exists("bar")); + CHECK(fs::file_size("foo") == fs::file_size("bar")); + CHECK(fs::copy_file("foo", "bar2", ec)); + CHECK(!ec); + std::this_thread::sleep_for(std::chrono::seconds(1)); + generateFile("foo2", 200); + CHECK(fs::copy_file("foo2", "bar", fs::copy_options::update_existing)); + CHECK(fs::file_size("bar") == 200); + CHECK(!fs::copy_file("foo", "bar", fs::copy_options::update_existing)); + CHECK(fs::file_size("bar") == 200); + CHECK(fs::copy_file("foo", "bar", fs::copy_options::overwrite_existing)); + CHECK(fs::file_size("bar") == 100); + CHECK_THROWS_AS(fs::copy_file("foobar", "foobar2"), fs::filesystem_error); + CHECK_NOTHROW(fs::copy_file("foobar", "foobar2", ec)); + CHECK(ec); + CHECK(!fs::exists("foobar")); +} + +TEST_CASE("fs.op.copy_symlink - copy_symlink", "[filesystem][operations][fs.op.copy_symlink]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + generateFile("foo"); + fs::create_directory("dir"); + if (is_symlink_creation_supported()) { + fs::create_symlink("foo", "sfoo"); + fs::create_directory_symlink("dir", "sdir"); + CHECK_NOTHROW(fs::copy_symlink("sfoo", "sfooc")); + CHECK(fs::exists("sfooc")); + CHECK_NOTHROW(fs::copy_symlink("sfoo", "sfooc2", ec)); + CHECK(fs::exists("sfooc2")); + CHECK(!ec); + CHECK_NOTHROW(fs::copy_symlink("sdir", "sdirc")); + CHECK(fs::exists("sdirc")); + CHECK_NOTHROW(fs::copy_symlink("sdir", "sdirc2", ec)); + CHECK(fs::exists("sdirc2")); + CHECK(!ec); + } + CHECK_THROWS_AS(fs::copy_symlink("bar", "barc"), fs::filesystem_error); + CHECK_NOTHROW(fs::copy_symlink("bar", "barc", ec)); + CHECK(ec); +} + +TEST_CASE("fs.op.create_directories - create_directories", "[filesystem][operations][fs.op.create_directories]") +{ + TemporaryDirectory t; + fs::path p = t.path() / "testdir"; + fs::path p2 = p / "nested"; + REQUIRE(!fs::exists(p)); + REQUIRE(!fs::exists(p2)); + CHECK(fs::create_directories(p2)); + CHECK(fs::is_directory(p)); + CHECK(fs::is_directory(p2)); + CHECK(!fs::create_directories(p2)); +#ifdef TEST_LWG_2935_BEHAVIOUR + INFO("This test expects LWG #2935 result conformance."); + p = t.path() / "testfile"; + generateFile(p); + CHECK(fs::is_regular_file(p)); + CHECK(!fs::is_directory(p)); + bool created = false; + CHECK_NOTHROW((created = fs::create_directories(p))); + CHECK(!created); + CHECK(fs::is_regular_file(p)); + CHECK(!fs::is_directory(p)); + std::error_code ec; + CHECK_NOTHROW((created = fs::create_directories(p, ec))); + CHECK(!created); + CHECK(!ec); + CHECK(fs::is_regular_file(p)); + CHECK(!fs::is_directory(p)); + CHECK(!fs::create_directories(p, ec)); +#else + INFO("This test expects conformance with P1164R1. (implemented by GCC with issue #86910.)"); + p = t.path() / "testfile"; + generateFile(p); + CHECK(fs::is_regular_file(p)); + CHECK(!fs::is_directory(p)); + CHECK_THROWS_AS(fs::create_directories(p), fs::filesystem_error); + CHECK(fs::is_regular_file(p)); + CHECK(!fs::is_directory(p)); + std::error_code ec; + CHECK_NOTHROW(fs::create_directories(p, ec)); + CHECK(ec); + CHECK(fs::is_regular_file(p)); + CHECK(!fs::is_directory(p)); + CHECK(!fs::create_directories(p, ec)); +#endif +} + +TEST_CASE("fs.op.create_directory - create_directory", "[filesystem][operations][fs.op.create_directory]") +{ + TemporaryDirectory t; + fs::path p = t.path() / "testdir"; + REQUIRE(!fs::exists(p)); + CHECK(fs::create_directory(p)); + CHECK(fs::is_directory(p)); + CHECK(!fs::is_regular_file(p)); + CHECK(fs::create_directory(p / "nested", p)); + CHECK(fs::is_directory(p / "nested")); + CHECK(!fs::is_regular_file(p / "nested")); +#ifdef TEST_LWG_2935_BEHAVIOUR + INFO("This test expects LWG #2935 result conformance."); + p = t.path() / "testfile"; + generateFile(p); + CHECK(fs::is_regular_file(p)); + CHECK(!fs::is_directory(p)); + bool created = false; + CHECK_NOTHROW((created = fs::create_directory(p))); + CHECK(!created); + CHECK(fs::is_regular_file(p)); + CHECK(!fs::is_directory(p)); + std::error_code ec; + CHECK_NOTHROW((created = fs::create_directory(p, ec))); + CHECK(!created); + CHECK(!ec); + CHECK(fs::is_regular_file(p)); + CHECK(!fs::is_directory(p)); + CHECK(!fs::create_directories(p, ec)); +#else + INFO("This test expects conformance with P1164R1. (implemented by GCC with issue #86910.)"); + p = t.path() / "testfile"; + generateFile(p); + CHECK(fs::is_regular_file(p)); + CHECK(!fs::is_directory(p)); + REQUIRE_THROWS_AS(fs::create_directory(p), fs::filesystem_error); + CHECK(fs::is_regular_file(p)); + CHECK(!fs::is_directory(p)); + std::error_code ec; + REQUIRE_NOTHROW(fs::create_directory(p, ec)); + CHECK(ec); + CHECK(fs::is_regular_file(p)); + CHECK(!fs::is_directory(p)); + CHECK(!fs::create_directory(p, ec)); +#endif +} + +TEST_CASE("fs.op.create_directory_symlink - create_directory_symlink", "[filesystem][operations][fs.op.create_directory_symlink]") +{ + if (is_symlink_creation_supported()) { + TemporaryDirectory t; + fs::create_directory(t.path() / "dir1"); + generateFile(t.path() / "dir1/test1"); + fs::create_directory(t.path() / "dir2"); + fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym"); + CHECK(fs::exists(t.path() / "dir2/dirSym")); + CHECK(fs::is_symlink(t.path() / "dir2/dirSym")); + CHECK(fs::exists(t.path() / "dir2/dirSym/test1")); + CHECK(fs::is_regular_file(t.path() / "dir2/dirSym/test1")); + CHECK_THROWS_AS(fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym"), fs::filesystem_error); + std::error_code ec; + CHECK_NOTHROW(fs::create_directory_symlink(t.path() / "dir1", t.path() / "dir2/dirSym", ec)); + CHECK(ec); + } +} + +TEST_CASE("fs.op.create_hard_link - create_hard_link", "[filesystem][operations][fs.op.create_hard_link]") +{ +#ifndef GHC_OS_WEB + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + generateFile("foo", 1234); + CHECK_NOTHROW(fs::create_hard_link("foo", "bar")); + CHECK(fs::exists("bar")); + CHECK(!fs::is_symlink("bar")); + CHECK_NOTHROW(fs::create_hard_link("foo", "bar2", ec)); + CHECK(fs::exists("bar2")); + CHECK(!fs::is_symlink("bar2")); + CHECK(!ec); + CHECK_THROWS_AS(fs::create_hard_link("nofoo", "bar"), fs::filesystem_error); + CHECK_NOTHROW(fs::create_hard_link("nofoo", "bar", ec)); + CHECK(ec); +#endif +} + +TEST_CASE("fs.op.create_symlink - create_symlink", "[filesystem][operations][fs.op.create_symlink]") +{ + if (is_symlink_creation_supported()) { + TemporaryDirectory t; + fs::create_directory(t.path() / "dir1"); + generateFile(t.path() / "dir1/test1"); + fs::create_directory(t.path() / "dir2"); + fs::create_symlink(t.path() / "dir1/test1", t.path() / "dir2/fileSym"); + CHECK(fs::exists(t.path() / "dir2/fileSym")); + CHECK(fs::is_symlink(t.path() / "dir2/fileSym")); + CHECK(fs::exists(t.path() / "dir2/fileSym")); + CHECK(fs::is_regular_file(t.path() / "dir2/fileSym")); + CHECK_THROWS_AS(fs::create_symlink(t.path() / "dir1", t.path() / "dir2/fileSym"), fs::filesystem_error); + std::error_code ec; + CHECK_NOTHROW(fs::create_symlink(t.path() / "dir1", t.path() / "dir2/fileSym", ec)); + CHECK(ec); + } +} + +TEST_CASE("fs.op.current_path - current_path", "[filesystem][operations][fs.op.current_path]") +{ + TemporaryDirectory t; + std::error_code ec; + fs::path p1 = fs::current_path(); + CHECK_NOTHROW(fs::current_path(t.path())); + CHECK(p1 != fs::current_path()); + CHECK_NOTHROW(fs::current_path(p1, ec)); + CHECK(!ec); + CHECK_THROWS_AS(fs::current_path(t.path() / "foo"), fs::filesystem_error); + CHECK(p1 == fs::current_path()); + CHECK_NOTHROW(fs::current_path(t.path() / "foo", ec)); + CHECK(ec); +} + +TEST_CASE("fs.op.equivalent - equivalent", "[filesystem][operations][fs.op.equivalent]") +{ + TemporaryDirectory t(TempOpt::change_path); + generateFile("foo", 1234); + CHECK(fs::equivalent(t.path() / "foo", "foo")); + if (is_symlink_creation_supported()) { + std::error_code ec(42, std::system_category()); + fs::create_symlink("foo", "foo2"); + CHECK(fs::equivalent("foo", "foo2")); + CHECK(fs::equivalent("foo", "foo2", ec)); + CHECK(!ec); + } +#ifdef TEST_LWG_2937_BEHAVIOUR + INFO("This test expects LWG #2937 result conformance."); + std::error_code ec; + bool result = false; + REQUIRE_THROWS_AS(fs::equivalent("foo", "foo3"), fs::filesystem_error); + CHECK_NOTHROW(result = fs::equivalent("foo", "foo3", ec)); + CHECK(!result); + CHECK(ec); + ec.clear(); + CHECK_THROWS_AS(fs::equivalent("foo3", "foo"), fs::filesystem_error); + CHECK_NOTHROW(result = fs::equivalent("foo3", "foo", ec)); + CHECK(!result); + CHECK(ec); + ec.clear(); + CHECK_THROWS_AS(fs::equivalent("foo3", "foo4"), fs::filesystem_error); + CHECK_NOTHROW(result = fs::equivalent("foo3", "foo4", ec)); + CHECK(!result); + CHECK(ec); +#else + INFO("This test expects conformance predating LWG #2937 result."); + std::error_code ec; + bool result = false; + REQUIRE_NOTHROW(result = fs::equivalent("foo", "foo3")); + CHECK(!result); + CHECK_NOTHROW(result = fs::equivalent("foo", "foo3", ec)); + CHECK(!result); + CHECK(!ec); + ec.clear(); + CHECK_NOTHROW(result = fs::equivalent("foo3", "foo")); + CHECK(!result); + CHECK_NOTHROW(result = fs::equivalent("foo3", "foo", ec)); + CHECK(!result); + CHECK(!ec); + ec.clear(); + CHECK_THROWS_AS(result = fs::equivalent("foo4", "foo3"), fs::filesystem_error); + CHECK(!result); + CHECK_NOTHROW(result = fs::equivalent("foo4", "foo3", ec)); + CHECK(!result); + CHECK(ec); +#endif +} + +TEST_CASE("fs.op.exists - exists", "[filesystem][operations][fs.op.exists]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + CHECK(!fs::exists("")); + CHECK(!fs::exists("foo")); + CHECK(!fs::exists("foo", ec)); + CHECK(!ec); + ec = std::error_code(42, std::system_category()); + CHECK(!fs::exists("foo", ec)); +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + CHECK(!fs::exists(u8"foo")); +#endif + CHECK(!ec); + ec.clear(); + CHECK(fs::exists(t.path())); + CHECK(fs::exists(t.path(), ec)); + CHECK(!ec); + ec = std::error_code(42, std::system_category()); + CHECK(fs::exists(t.path(), ec)); + CHECK(!ec); +#if defined(GHC_OS_WINDOWS) && !defined(GHC_FILESYSTEM_FWD) + if (::GetFileAttributesW(L"C:\\fs-test") != INVALID_FILE_ATTRIBUTES) { + CHECK(fs::exists("C:\\fs-test")); + } +#endif +} + +TEST_CASE("fs.op.file_size - file_size", "[filesystem][operations][fs.op.file_size]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + generateFile("foo", 0); + generateFile("bar", 1234); + CHECK(fs::file_size("foo") == 0); + ec = std::error_code(42, std::system_category()); + CHECK(fs::file_size("foo", ec) == 0); + CHECK(!ec); + ec.clear(); + CHECK(fs::file_size("bar") == 1234); + ec = std::error_code(42, std::system_category()); + CHECK(fs::file_size("bar", ec) == 1234); + CHECK(!ec); + ec.clear(); + CHECK_THROWS_AS(fs::file_size("foobar"), fs::filesystem_error); + CHECK(fs::file_size("foobar", ec) == static_cast(-1)); + CHECK(ec); + ec.clear(); +} + +#ifndef GHC_OS_WINDOWS +static uintmax_t getHardlinkCount(const fs::path& p) +{ + struct stat st = {}; + auto rc = ::lstat(p.c_str(), &st); + return rc == 0 ? st.st_nlink : ~0u; +} +#endif + +TEST_CASE("fs.op.hard_link_count - hard_link_count", "[filesystem][operations][fs.op.hard_link_count]") +{ +#ifndef GHC_OS_WEB + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; +#ifdef GHC_OS_WINDOWS + // windows doesn't implement "."/".." as hardlinks, so it + // starts with 1 and subdirectories don't change the count + CHECK(fs::hard_link_count(t.path()) == 1); + fs::create_directory("dir"); + CHECK(fs::hard_link_count(t.path()) == 1); +#else + // unix/bsd/linux typically implements "."/".." as hardlinks + // so an empty dir has 2 (from parent and the ".") and + // adding a subdirectory adds one due to its ".." + CHECK(fs::hard_link_count(t.path()) == getHardlinkCount(t.path())); + fs::create_directory("dir"); + CHECK(fs::hard_link_count(t.path()) == getHardlinkCount(t.path())); +#endif + generateFile("foo"); + CHECK(fs::hard_link_count(t.path() / "foo") == 1); + ec = std::error_code(42, std::system_category()); + CHECK(fs::hard_link_count(t.path() / "foo", ec) == 1); + CHECK(!ec); + CHECK_THROWS_AS(fs::hard_link_count(t.path() / "bar"), fs::filesystem_error); + CHECK_NOTHROW(fs::hard_link_count(t.path() / "bar", ec)); + CHECK(ec); + ec.clear(); +#else + WARN("Test for unsupportet features are disabled on JS/Wasm target."); +#endif +} + +class FileTypeMixFixture +{ +public: + FileTypeMixFixture() + : _t(TempOpt::change_path) + , _hasFifo(false) + , _hasSocket(false) + { + generateFile("regular"); + fs::create_directory("directory"); + if (is_symlink_creation_supported()) { + fs::create_symlink("regular", "file_symlink"); + fs::create_directory_symlink("directory", "dir_symlink"); + } +#if !defined(GHC_OS_WINDOWS) && !defined(GHC_OS_WEB) + REQUIRE(::mkfifo("fifo", 0644) == 0); + _hasFifo = true; + struct ::sockaddr_un addr; + addr.sun_family = AF_UNIX; + std::strncpy(addr.sun_path, "socket", sizeof(addr.sun_path)); + int fd = socket(PF_UNIX, SOCK_STREAM, 0); + bind(fd, (struct sockaddr*)&addr, sizeof addr); + _hasSocket = true; +#endif + } + + ~FileTypeMixFixture() {} + + bool has_fifo() const { return _hasFifo; } + + bool has_socket() const { return _hasSocket; } + + fs::path block_path() const + { + std::error_code ec; + if (fs::exists("/dev/sda", ec)) { + return "/dev/sda"; + } + else if (fs::exists("/dev/disk0", ec)) { + return "/dev/disk0"; + } + return fs::path(); + } + + fs::path character_path() const + { + std::error_code ec; + if (fs::exists("/dev/null", ec)) { + return "/dev/null"; + } + else if (fs::exists("NUL", ec)) { + return "NUL"; + } + return fs::path(); + } + fs::path temp_path() const { return _t.path(); } + +private: + TemporaryDirectory _t; + bool _hasFifo; + bool _hasSocket; +}; + +TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_block_file - is_block_file", "[filesystem][operations][fs.op.is_block_file]") +{ + std::error_code ec; + CHECK(!fs::is_block_file("directory")); + CHECK(!fs::is_block_file("regular")); + if (is_symlink_creation_supported()) { + CHECK(!fs::is_block_file("dir_symlink")); + CHECK(!fs::is_block_file("file_symlink")); + } + CHECK((has_fifo() ? !fs::is_block_file("fifo") : true)); + CHECK((has_socket() ? !fs::is_block_file("socket") : true)); + CHECK((block_path().empty() ? true : fs::is_block_file(block_path()))); + CHECK((character_path().empty() ? true : !fs::is_block_file(character_path()))); + CHECK_NOTHROW(fs::is_block_file("notfound")); + CHECK_NOTHROW(fs::is_block_file("notfound", ec)); + CHECK(ec); + ec.clear(); + CHECK(!fs::is_block_file(fs::file_status(fs::file_type::none))); + CHECK(!fs::is_block_file(fs::file_status(fs::file_type::not_found))); + CHECK(!fs::is_block_file(fs::file_status(fs::file_type::regular))); + CHECK(!fs::is_block_file(fs::file_status(fs::file_type::directory))); + CHECK(!fs::is_block_file(fs::file_status(fs::file_type::symlink))); + CHECK(fs::is_block_file(fs::file_status(fs::file_type::block))); + CHECK(!fs::is_block_file(fs::file_status(fs::file_type::character))); + CHECK(!fs::is_block_file(fs::file_status(fs::file_type::fifo))); + CHECK(!fs::is_block_file(fs::file_status(fs::file_type::socket))); + CHECK(!fs::is_block_file(fs::file_status(fs::file_type::unknown))); +} + +TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_character_file - is_character_file", "[filesystem][operations][fs.op.is_character_file]") +{ + std::error_code ec; + CHECK(!fs::is_character_file("directory")); + CHECK(!fs::is_character_file("regular")); + if (is_symlink_creation_supported()) { + CHECK(!fs::is_character_file("dir_symlink")); + CHECK(!fs::is_character_file("file_symlink")); + } + CHECK((has_fifo() ? !fs::is_character_file("fifo") : true)); + CHECK((has_socket() ? !fs::is_character_file("socket") : true)); + CHECK((block_path().empty() ? true : !fs::is_character_file(block_path()))); + CHECK((character_path().empty() ? true : fs::is_character_file(character_path()))); + CHECK_NOTHROW(fs::is_character_file("notfound")); + CHECK_NOTHROW(fs::is_character_file("notfound", ec)); + CHECK(ec); + ec.clear(); + CHECK(!fs::is_character_file(fs::file_status(fs::file_type::none))); + CHECK(!fs::is_character_file(fs::file_status(fs::file_type::not_found))); + CHECK(!fs::is_character_file(fs::file_status(fs::file_type::regular))); + CHECK(!fs::is_character_file(fs::file_status(fs::file_type::directory))); + CHECK(!fs::is_character_file(fs::file_status(fs::file_type::symlink))); + CHECK(!fs::is_character_file(fs::file_status(fs::file_type::block))); + CHECK(fs::is_character_file(fs::file_status(fs::file_type::character))); + CHECK(!fs::is_character_file(fs::file_status(fs::file_type::fifo))); + CHECK(!fs::is_character_file(fs::file_status(fs::file_type::socket))); + CHECK(!fs::is_character_file(fs::file_status(fs::file_type::unknown))); +} + +TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_directory - is_directory", "[filesystem][operations][fs.op.is_directory]") +{ + std::error_code ec; + CHECK(fs::is_directory("directory")); + CHECK(!fs::is_directory("regular")); + if (is_symlink_creation_supported()) { + CHECK(fs::is_directory("dir_symlink")); + CHECK(!fs::is_directory("file_symlink")); + } + CHECK((has_fifo() ? !fs::is_directory("fifo") : true)); + CHECK((has_socket() ? !fs::is_directory("socket") : true)); + CHECK((block_path().empty() ? true : !fs::is_directory(block_path()))); + CHECK((character_path().empty() ? true : !fs::is_directory(character_path()))); + CHECK_NOTHROW(fs::is_directory("notfound")); + CHECK_NOTHROW(fs::is_directory("notfound", ec)); + CHECK(ec); + ec.clear(); + CHECK(!fs::is_directory(fs::file_status(fs::file_type::none))); + CHECK(!fs::is_directory(fs::file_status(fs::file_type::not_found))); + CHECK(!fs::is_directory(fs::file_status(fs::file_type::regular))); + CHECK(fs::is_directory(fs::file_status(fs::file_type::directory))); + CHECK(!fs::is_directory(fs::file_status(fs::file_type::symlink))); + CHECK(!fs::is_directory(fs::file_status(fs::file_type::block))); + CHECK(!fs::is_directory(fs::file_status(fs::file_type::character))); + CHECK(!fs::is_directory(fs::file_status(fs::file_type::fifo))); + CHECK(!fs::is_directory(fs::file_status(fs::file_type::socket))); + CHECK(!fs::is_directory(fs::file_status(fs::file_type::unknown))); +} + +TEST_CASE("fs.op.is_empty - is_empty", "[filesystem][operations][fs.op.is_empty]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + CHECK(fs::is_empty(t.path())); + CHECK(fs::is_empty(t.path(), ec)); + CHECK(!ec); + generateFile("foo", 0); + generateFile("bar", 1234); + CHECK(fs::is_empty("foo")); + CHECK(fs::is_empty("foo", ec)); + CHECK(!ec); + CHECK(!fs::is_empty("bar")); + CHECK(!fs::is_empty("bar", ec)); + CHECK(!ec); + CHECK_THROWS_AS(fs::is_empty("foobar"), fs::filesystem_error); + bool result = false; + CHECK_NOTHROW(result = fs::is_empty("foobar", ec)); + CHECK(!result); + CHECK(ec); +} + +TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_fifo - is_fifo", "[filesystem][operations][fs.op.is_fifo]") +{ + std::error_code ec; + CHECK(!fs::is_fifo("directory")); + CHECK(!fs::is_fifo("regular")); + if (is_symlink_creation_supported()) { + CHECK(!fs::is_fifo("dir_symlink")); + CHECK(!fs::is_fifo("file_symlink")); + } + CHECK((has_fifo() ? fs::is_fifo("fifo") : true)); + CHECK((has_socket() ? !fs::is_fifo("socket") : true)); + CHECK((block_path().empty() ? true : !fs::is_fifo(block_path()))); + CHECK((character_path().empty() ? true : !fs::is_fifo(character_path()))); + CHECK_NOTHROW(fs::is_fifo("notfound")); + CHECK_NOTHROW(fs::is_fifo("notfound", ec)); + CHECK(ec); + ec.clear(); + CHECK(!fs::is_fifo(fs::file_status(fs::file_type::none))); + CHECK(!fs::is_fifo(fs::file_status(fs::file_type::not_found))); + CHECK(!fs::is_fifo(fs::file_status(fs::file_type::regular))); + CHECK(!fs::is_fifo(fs::file_status(fs::file_type::directory))); + CHECK(!fs::is_fifo(fs::file_status(fs::file_type::symlink))); + CHECK(!fs::is_fifo(fs::file_status(fs::file_type::block))); + CHECK(!fs::is_fifo(fs::file_status(fs::file_type::character))); + CHECK(fs::is_fifo(fs::file_status(fs::file_type::fifo))); + CHECK(!fs::is_fifo(fs::file_status(fs::file_type::socket))); + CHECK(!fs::is_fifo(fs::file_status(fs::file_type::unknown))); +} + +TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_other - is_other", "[filesystem][operations][fs.op.is_other]") +{ + std::error_code ec; + CHECK(!fs::is_other("directory")); + CHECK(!fs::is_other("regular")); + if (is_symlink_creation_supported()) { + CHECK(!fs::is_other("dir_symlink")); + CHECK(!fs::is_other("file_symlink")); + } + CHECK((has_fifo() ? fs::is_other("fifo") : true)); + CHECK((has_socket() ? fs::is_other("socket") : true)); + CHECK((block_path().empty() ? true : fs::is_other(block_path()))); + CHECK((character_path().empty() ? true : fs::is_other(character_path()))); + CHECK_NOTHROW(fs::is_other("notfound")); + CHECK_NOTHROW(fs::is_other("notfound", ec)); + CHECK(ec); + ec.clear(); + CHECK(!fs::is_other(fs::file_status(fs::file_type::none))); + CHECK(!fs::is_other(fs::file_status(fs::file_type::not_found))); + CHECK(!fs::is_other(fs::file_status(fs::file_type::regular))); + CHECK(!fs::is_other(fs::file_status(fs::file_type::directory))); + CHECK(!fs::is_other(fs::file_status(fs::file_type::symlink))); + CHECK(fs::is_other(fs::file_status(fs::file_type::block))); + CHECK(fs::is_other(fs::file_status(fs::file_type::character))); + CHECK(fs::is_other(fs::file_status(fs::file_type::fifo))); + CHECK(fs::is_other(fs::file_status(fs::file_type::socket))); + CHECK(fs::is_other(fs::file_status(fs::file_type::unknown))); +} + +TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_regular_file - is_regular_file", "[filesystem][operations][fs.op.is_regular_file]") +{ + std::error_code ec; + CHECK(!fs::is_regular_file("directory")); + CHECK(fs::is_regular_file("regular")); + if (is_symlink_creation_supported()) { + CHECK(!fs::is_regular_file("dir_symlink")); + CHECK(fs::is_regular_file("file_symlink")); + } + CHECK((has_fifo() ? !fs::is_regular_file("fifo") : true)); + CHECK((has_socket() ? !fs::is_regular_file("socket") : true)); + CHECK((block_path().empty() ? true : !fs::is_regular_file(block_path()))); + CHECK((character_path().empty() ? true : !fs::is_regular_file(character_path()))); + CHECK_NOTHROW(fs::is_regular_file("notfound")); + CHECK_NOTHROW(fs::is_regular_file("notfound", ec)); + CHECK(ec); + ec.clear(); + CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::none))); + CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::not_found))); + CHECK(fs::is_regular_file(fs::file_status(fs::file_type::regular))); + CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::directory))); + CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::symlink))); + CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::block))); + CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::character))); + CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::fifo))); + CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::socket))); + CHECK(!fs::is_regular_file(fs::file_status(fs::file_type::unknown))); +} + +TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_socket - is_socket", "[filesystem][operations][fs.op.is_socket]") +{ + std::error_code ec; + CHECK(!fs::is_socket("directory")); + CHECK(!fs::is_socket("regular")); + if (is_symlink_creation_supported()) { + CHECK(!fs::is_socket("dir_symlink")); + CHECK(!fs::is_socket("file_symlink")); + } + CHECK((has_fifo() ? !fs::is_socket("fifo") : true)); + CHECK((has_socket() ? fs::is_socket("socket") : true)); + CHECK((block_path().empty() ? true : !fs::is_socket(block_path()))); + CHECK((character_path().empty() ? true : !fs::is_socket(character_path()))); + CHECK_NOTHROW(fs::is_socket("notfound")); + CHECK_NOTHROW(fs::is_socket("notfound", ec)); + CHECK(ec); + ec.clear(); + CHECK(!fs::is_socket(fs::file_status(fs::file_type::none))); + CHECK(!fs::is_socket(fs::file_status(fs::file_type::not_found))); + CHECK(!fs::is_socket(fs::file_status(fs::file_type::regular))); + CHECK(!fs::is_socket(fs::file_status(fs::file_type::directory))); + CHECK(!fs::is_socket(fs::file_status(fs::file_type::symlink))); + CHECK(!fs::is_socket(fs::file_status(fs::file_type::block))); + CHECK(!fs::is_socket(fs::file_status(fs::file_type::character))); + CHECK(!fs::is_socket(fs::file_status(fs::file_type::fifo))); + CHECK(fs::is_socket(fs::file_status(fs::file_type::socket))); + CHECK(!fs::is_socket(fs::file_status(fs::file_type::unknown))); +} + +TEST_CASE_METHOD(FileTypeMixFixture, "fs.op.is_symlink - is_symlink", "[filesystem][operations][fs.op.is_symlink]") +{ + std::error_code ec; + CHECK(!fs::is_symlink("directory")); + CHECK(!fs::is_symlink("regular")); + if (is_symlink_creation_supported()) { + CHECK(fs::is_symlink("dir_symlink")); + CHECK(fs::is_symlink("file_symlink")); + } + CHECK((has_fifo() ? !fs::is_symlink("fifo") : true)); + CHECK((has_socket() ? !fs::is_symlink("socket") : true)); + CHECK((block_path().empty() ? true : !fs::is_symlink(block_path()))); + CHECK((character_path().empty() ? true : !fs::is_symlink(character_path()))); + CHECK_NOTHROW(fs::is_symlink("notfound")); + CHECK_NOTHROW(fs::is_symlink("notfound", ec)); + CHECK(ec); + ec.clear(); + CHECK(!fs::is_symlink(fs::file_status(fs::file_type::none))); + CHECK(!fs::is_symlink(fs::file_status(fs::file_type::not_found))); + CHECK(!fs::is_symlink(fs::file_status(fs::file_type::regular))); + CHECK(!fs::is_symlink(fs::file_status(fs::file_type::directory))); + CHECK(fs::is_symlink(fs::file_status(fs::file_type::symlink))); + CHECK(!fs::is_symlink(fs::file_status(fs::file_type::block))); + CHECK(!fs::is_symlink(fs::file_status(fs::file_type::character))); + CHECK(!fs::is_symlink(fs::file_status(fs::file_type::fifo))); + CHECK(!fs::is_symlink(fs::file_status(fs::file_type::socket))); + CHECK(!fs::is_symlink(fs::file_status(fs::file_type::unknown))); +} + +#ifndef GHC_OS_WEB +static fs::file_time_type timeFromString(const std::string& str) +{ + struct ::tm tm; + ::memset(&tm, 0, sizeof(::tm)); + std::istringstream is(str); + is >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S"); + if (is.fail()) { + throw std::exception(); + } + return from_time_t(std::mktime(&tm)); +} +#endif + +TEST_CASE("fs.op.last_write_time - last_write_time", "[filesystem][operations][fs.op.last_write_time]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + fs::file_time_type ft; + generateFile("foo"); + auto now = fs::file_time_type::clock::now(); + CHECK(std::abs(std::chrono::duration_cast(fs::last_write_time(t.path()) - now).count()) < 3); + CHECK(std::abs(std::chrono::duration_cast(fs::last_write_time("foo") - now).count()) < 3); + CHECK_THROWS_AS(fs::last_write_time("bar"), fs::filesystem_error); + CHECK_NOTHROW(ft = fs::last_write_time("bar", ec)); + CHECK(ft == fs::file_time_type::min()); + CHECK(ec); + ec.clear(); + if (is_symlink_creation_supported()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + fs::create_symlink("foo", "foo2"); + ft = fs::last_write_time("foo"); + // checks that the time of the symlink is fetched + CHECK(ft == fs::last_write_time("foo2")); + } +#ifndef GHC_OS_WEB + auto nt = timeFromString("2015-10-21T04:30:00"); + CHECK_NOTHROW(fs::last_write_time(t.path() / "foo", nt)); + CHECK(std::abs(std::chrono::duration_cast(fs::last_write_time("foo") - nt).count()) < 1); + nt = timeFromString("2015-10-21T04:29:00"); + CHECK_NOTHROW(fs::last_write_time("foo", nt, ec)); + CHECK(std::abs(std::chrono::duration_cast(fs::last_write_time("foo") - nt).count()) < 1); + CHECK(!ec); + CHECK_THROWS_AS(fs::last_write_time("bar", nt), fs::filesystem_error); + CHECK_NOTHROW(fs::last_write_time("bar", nt, ec)); + CHECK(ec); +#endif +} + +TEST_CASE("fs.op.permissions - permissions", "[filesystem][operations][fs.op.permissions]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + generateFile("foo", 512); + auto allWrite = fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write; + CHECK_NOTHROW(fs::permissions("foo", allWrite, fs::perm_options::remove)); + CHECK((fs::status("foo").permissions() & fs::perms::owner_write) != fs::perms::owner_write); +#if !defined(GHC_OS_WINDOWS) + if (geteuid() != 0) +#endif + { + CHECK_THROWS_AS(fs::resize_file("foo", 1024), fs::filesystem_error); + CHECK(fs::file_size("foo") == 512); + } + CHECK_NOTHROW(fs::permissions("foo", fs::perms::owner_write, fs::perm_options::add)); + CHECK((fs::status("foo").permissions() & fs::perms::owner_write) == fs::perms::owner_write); + CHECK_NOTHROW(fs::resize_file("foo", 2048)); + CHECK(fs::file_size("foo") == 2048); + CHECK_THROWS_AS(fs::permissions("bar", fs::perms::owner_write, fs::perm_options::add), fs::filesystem_error); + CHECK_NOTHROW(fs::permissions("bar", fs::perms::owner_write, fs::perm_options::add, ec)); + CHECK(ec); + CHECK_THROWS_AS(fs::permissions("bar", fs::perms::owner_write, static_cast(0)), fs::filesystem_error); +} + +TEST_CASE("fs.op.proximate - proximate", "[filesystem][operations][fs.op.proximate]") +{ + std::error_code ec; + CHECK(fs::proximate("/a/d", "/a/b/c") == "../../d"); + CHECK(fs::proximate("/a/d", "/a/b/c", ec) == "../../d"); + CHECK(!ec); + CHECK(fs::proximate("/a/b/c", "/a/d") == "../b/c"); + CHECK(fs::proximate("/a/b/c", "/a/d", ec) == "../b/c"); + CHECK(!ec); + CHECK(fs::proximate("a/b/c", "a") == "b/c"); + CHECK(fs::proximate("a/b/c", "a", ec) == "b/c"); + CHECK(!ec); + CHECK(fs::proximate("a/b/c", "a/b/c/x/y") == "../.."); + CHECK(fs::proximate("a/b/c", "a/b/c/x/y", ec) == "../.."); + CHECK(!ec); + CHECK(fs::proximate("a/b/c", "a/b/c") == "."); + CHECK(fs::proximate("a/b/c", "a/b/c", ec) == "."); + CHECK(!ec); + CHECK(fs::proximate("a/b", "c/d") == "../../a/b"); + CHECK(fs::proximate("a/b", "c/d", ec) == "../../a/b"); + CHECK(!ec); +#ifndef GHC_OS_WINDOWS + if (has_host_root_name_support()) { + CHECK(fs::proximate("//host1/a/d", "//host2/a/b/c") == "//host1/a/d"); + CHECK(fs::proximate("//host1/a/d", "//host2/a/b/c", ec) == "//host1/a/d"); + CHECK(!ec); + } +#endif +} + +TEST_CASE("fs.op.read_symlink - read_symlink", "[filesystem][operations][fs.op.read_symlink]") +{ + if (is_symlink_creation_supported()) { + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + generateFile("foo"); + fs::create_symlink(t.path() / "foo", "bar"); + CHECK(fs::read_symlink("bar") == t.path() / "foo"); + CHECK(fs::read_symlink("bar", ec) == t.path() / "foo"); + CHECK(!ec); + CHECK_THROWS_AS(fs::read_symlink("foobar"), fs::filesystem_error); + CHECK(fs::read_symlink("foobar", ec) == fs::path()); + CHECK(ec); + } +} + +TEST_CASE("fs.op.relative - relative", "[filesystem][operations][fs.op.relative]") +{ + CHECK(fs::relative("/a/d", "/a/b/c") == "../../d"); + CHECK(fs::relative("/a/b/c", "/a/d") == "../b/c"); + CHECK(fs::relative("a/b/c", "a") == "b/c"); + CHECK(fs::relative("a/b/c", "a/b/c/x/y") == "../.."); + CHECK(fs::relative("a/b/c", "a/b/c") == "."); + CHECK(fs::relative("a/b", "c/d") == "../../a/b"); + std::error_code ec; + CHECK(fs::relative(fs::current_path() / "foo", ec) == "foo"); + CHECK(!ec); +} + +TEST_CASE("fs.op.remove - remove", "[filesystem][operations][fs.op.remove]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + generateFile("foo"); + CHECK(fs::remove("foo")); + CHECK(!fs::exists("foo")); + CHECK(!fs::remove("foo")); + generateFile("foo"); + CHECK(fs::remove("foo", ec)); + CHECK(!fs::exists("foo")); + if (is_symlink_creation_supported()) { + generateFile("foo"); + fs::create_symlink("foo", "bar"); + CHECK(fs::exists(fs::symlink_status("bar"))); + CHECK(fs::remove("bar", ec)); + CHECK(fs::exists("foo")); + CHECK(!fs::exists(fs::symlink_status("bar"))); + } + CHECK(!fs::remove("bar")); + CHECK(!fs::remove("bar", ec)); + CHECK(!ec); +} + +TEST_CASE("fs.op.remove_all - remove_all", "[filesystem][operations][fs.op.remove_all]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + generateFile("foo"); + CHECK(fs::remove_all("foo", ec) == 1); + CHECK(!ec); + ec.clear(); + CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator()); + fs::create_directories("dir1/dir1a"); + fs::create_directories("dir1/dir1b"); + generateFile("dir1/dir1a/f1"); + generateFile("dir1/dir1b/f2"); + CHECK_NOTHROW(fs::remove_all("dir1/non-existing", ec)); + CHECK(!ec); + CHECK(fs::remove_all("dir1/non-existing", ec) == 0); + if (is_symlink_creation_supported()) { + fs::create_directory_symlink("dir1", "dir1link"); + CHECK(fs::remove_all("dir1link") == 1); + } + CHECK(fs::remove_all("dir1") == 5); + CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator()); +} + +TEST_CASE("fs.op.rename - rename", "[filesystem][operations][fs.op.rename]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + generateFile("foo", 123); + fs::create_directory("dir1"); + CHECK_NOTHROW(fs::rename("foo", "bar")); + CHECK(!fs::exists("foo")); + CHECK(fs::exists("bar")); + CHECK_NOTHROW(fs::rename("dir1", "dir2")); + CHECK(fs::exists("dir2")); + generateFile("foo2", 42); + CHECK_NOTHROW(fs::rename("bar", "foo2")); + CHECK(fs::exists("foo2")); + CHECK(fs::file_size("foo2") == 123u); + CHECK(!fs::exists("bar")); + CHECK_NOTHROW(fs::rename("foo2", "foo", ec)); + CHECK(!ec); + CHECK_THROWS_AS(fs::rename("foobar", "barfoo"), fs::filesystem_error); + CHECK_NOTHROW(fs::rename("foobar", "barfoo", ec)); + CHECK(ec); + CHECK(!fs::exists("barfoo")); +} + +TEST_CASE("fs.op.resize_file - resize_file", "[filesystem][operations][fs.op.resize_file]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + generateFile("foo", 1024); + CHECK(fs::file_size("foo") == 1024); + CHECK_NOTHROW(fs::resize_file("foo", 2048)); + CHECK(fs::file_size("foo") == 2048); + CHECK_NOTHROW(fs::resize_file("foo", 1000, ec)); + CHECK(!ec); + CHECK(fs::file_size("foo") == 1000); + CHECK_THROWS_AS(fs::resize_file("bar", 2048), fs::filesystem_error); + CHECK(!fs::exists("bar")); + CHECK_NOTHROW(fs::resize_file("bar", 4096, ec)); + CHECK(ec); + CHECK(!fs::exists("bar")); +} + +TEST_CASE("fs.op.space - space", "[filesystem][operations][fs.op.space]") +{ + { + fs::space_info si; + CHECK_NOTHROW(si = fs::space(fs::current_path())); + CHECK(si.capacity > 1024 * 1024); + CHECK(si.capacity > si.free); + CHECK(si.free >= si.available); + } + { + std::error_code ec; + fs::space_info si; + CHECK_NOTHROW(si = fs::space(fs::current_path(), ec)); + CHECK(si.capacity > 1024 * 1024); + CHECK(si.capacity > si.free); + CHECK(si.free >= si.available); + CHECK(!ec); + } +#ifndef GHC_OS_WEB // statvfs under emscripten always returns a result, so this tests would fail + { + std::error_code ec; + fs::space_info si; + CHECK_NOTHROW(si = fs::space("foobar42", ec)); + CHECK(si.capacity == static_cast(-1)); + CHECK(si.free == static_cast(-1)); + CHECK(si.available == static_cast(-1)); + CHECK(ec); + } + CHECK_THROWS_AS(fs::space("foobar42"), fs::filesystem_error); +#endif +} + +TEST_CASE("fs.op.status - status", "[filesystem][operations][fs.op.status]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + fs::file_status fs; + CHECK_NOTHROW(fs = fs::status("foo")); + CHECK(fs.type() == fs::file_type::not_found); + CHECK(fs.permissions() == fs::perms::unknown); + CHECK_NOTHROW(fs = fs::status("bar", ec)); + CHECK(fs.type() == fs::file_type::not_found); + CHECK(fs.permissions() == fs::perms::unknown); + CHECK(ec); + ec.clear(); + fs = fs::status(t.path()); + CHECK(fs.type() == fs::file_type::directory); + CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write)); + generateFile("foobar"); + fs = fs::status(t.path() / "foobar"); + CHECK(fs.type() == fs::file_type::regular); + CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write)); + if (is_symlink_creation_supported()) { + fs::create_symlink(t.path() / "foobar", t.path() / "barfoo"); + fs = fs::status(t.path() / "barfoo"); + CHECK(fs.type() == fs::file_type::regular); + CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write)); + } +} + +TEST_CASE("fs.op.status_known - status_known", "[filesystem][operations][fs.op.status_known]") +{ + CHECK(!fs::status_known(fs::file_status())); + CHECK(fs::status_known(fs::file_status(fs::file_type::not_found))); + CHECK(fs::status_known(fs::file_status(fs::file_type::regular))); + CHECK(fs::status_known(fs::file_status(fs::file_type::directory))); + CHECK(fs::status_known(fs::file_status(fs::file_type::symlink))); + CHECK(fs::status_known(fs::file_status(fs::file_type::character))); + CHECK(fs::status_known(fs::file_status(fs::file_type::fifo))); + CHECK(fs::status_known(fs::file_status(fs::file_type::socket))); + CHECK(fs::status_known(fs::file_status(fs::file_type::unknown))); +} + +TEST_CASE("fs.op.symlink_status - symlink_status", "[filesystem][operations][fs.op.symlink_status]") +{ + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + fs::file_status fs; + CHECK_NOTHROW(fs = fs::symlink_status("foo")); + CHECK(fs.type() == fs::file_type::not_found); + CHECK(fs.permissions() == fs::perms::unknown); + CHECK_NOTHROW(fs = fs::symlink_status("bar", ec)); + CHECK(fs.type() == fs::file_type::not_found); + CHECK(fs.permissions() == fs::perms::unknown); + CHECK(ec); + ec.clear(); + fs = fs::symlink_status(t.path()); + CHECK(fs.type() == fs::file_type::directory); + CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write)); + generateFile("foobar"); + fs = fs::symlink_status(t.path() / "foobar"); + CHECK(fs.type() == fs::file_type::regular); + CHECK((fs.permissions() & (fs::perms::owner_read | fs::perms::owner_write)) == (fs::perms::owner_read | fs::perms::owner_write)); + if (is_symlink_creation_supported()) { + fs::create_symlink(t.path() / "foobar", t.path() / "barfoo"); + fs = fs::symlink_status(t.path() / "barfoo"); + CHECK(fs.type() == fs::file_type::symlink); + } +} + +TEST_CASE("fs.op.temp_dir_path - temporary_directory_path", "[filesystem][operations][fs.op.temp_dir_path]") +{ + std::error_code ec; + CHECK_NOTHROW(fs::exists(fs::temp_directory_path())); + CHECK_NOTHROW(fs::exists(fs::temp_directory_path(ec))); + CHECK(!fs::temp_directory_path().empty()); + CHECK(!ec); +} + +TEST_CASE("fs.op.weakly_canonical - weakly_canonical", "[filesystem][operations][fs.op.weakly_canonical]") +{ + INFO("This might fail on std::implementations that return fs::current_path() for fs::canonical(\"\")"); + CHECK(fs::weakly_canonical("") == "."); + if(fs::weakly_canonical("") == ".") { + CHECK(fs::weakly_canonical("foo/bar") == "foo/bar"); + CHECK(fs::weakly_canonical("foo/./bar") == "foo/bar"); + CHECK(fs::weakly_canonical("foo/../bar") == "bar"); + } + else { + CHECK(fs::weakly_canonical("foo/bar") == fs::current_path() / "foo/bar"); + CHECK(fs::weakly_canonical("foo/./bar") == fs::current_path() / "foo/bar"); + CHECK(fs::weakly_canonical("foo/../bar") == fs::current_path() / "bar"); + } + + { + TemporaryDirectory t(TempOpt::change_path); + auto dir = t.path() / "d0"; + fs::create_directories(dir / "d1"); + generateFile(dir / "f0"); + fs::path rel(dir.filename()); + CHECK(fs::weakly_canonical(dir) == dir); + CHECK(fs::weakly_canonical(rel) == dir); + CHECK(fs::weakly_canonical(dir / "f0") == dir / "f0"); + CHECK(fs::weakly_canonical(dir / "f0/") == dir / "f0/"); + CHECK(fs::weakly_canonical(dir / "f1") == dir / "f1"); + CHECK(fs::weakly_canonical(rel / "f0") == dir / "f0"); + CHECK(fs::weakly_canonical(rel / "f0/") == dir / "f0/"); + CHECK(fs::weakly_canonical(rel / "f1") == dir / "f1"); + CHECK(fs::weakly_canonical(rel / "./f0") == dir / "f0"); + CHECK(fs::weakly_canonical(rel / "./f1") == dir / "f1"); + CHECK(fs::weakly_canonical(rel / "d1/../f0") == dir / "f0"); + CHECK(fs::weakly_canonical(rel / "d1/../f1") == dir / "f1"); + CHECK(fs::weakly_canonical(rel / "d1/../f1/../f2") == dir / "f2"); + } +} + +TEST_CASE("std::string_view support", "[filesystem][fs.string_view]") +{ +#if defined(GHC_HAS_STD_STRING_VIEW) || defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW) + +#if defined(GHC_HAS_STD_STRING_VIEW) + using namespace std::literals; + using string_view = std::string_view; + using wstring_view = std::wstring_view; +#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW) + using string_view = std::experimental::string_view; + using wstring_view = std::experimental::wstring_view; +#endif + + { + std::string p("foo/bar"); + string_view sv(p); + CHECK(fs::path(sv, fs::path::format::generic_format).generic_string() == "foo/bar"); + fs::path p2("fo"); + p2 += string_view("o"); + CHECK(p2 == "foo"); + CHECK(p2.compare(string_view("foo")) == 0); + } + { + auto p = fs::path{"XYZ"}; + p /= string_view("Appendix"); + CHECK(p == "XYZ/Appendix"); + } + { + std::wstring p(L"foo/bar"); + wstring_view sv(p); + CHECK(fs::path(sv, fs::path::format::generic_format).generic_string() == "foo/bar"); + fs::path p2(L"fo"); + p2 += wstring_view(L"o"); + CHECK(p2 == "foo"); + CHECK(p2.compare(wstring_view(L"foo")) == 0); + } + +#else + WARN("std::string_view specific tests are empty without std::string_view."); +#endif +} + +TEST_CASE("Windows: Long filename support", "[filesystem][path][fs.path.win.long]") +{ +#ifdef GHC_OS_WINDOWS + TemporaryDirectory t(TempOpt::change_path); + char c = 'A'; + fs::path dir{"\\\\?\\"}; + dir += fs::current_path().u8string(); + for (; c <= 'Z'; ++c) { + std::string part = std::string(16, c); + dir /= part; + CHECK_NOTHROW(fs::create_directory(dir)); + CHECK(fs::exists(dir)); + generateFile(dir / "f0"); + REQUIRE(fs::exists(dir / "f0")); + } + CHECK(c > 'Z'); + fs::remove_all(fs::current_path() / std::string(16, 'A')); + CHECK(!fs::exists(fs::current_path() / std::string(16, 'A'))); + CHECK_NOTHROW(fs::create_directories(dir)); + CHECK(fs::exists(dir)); + generateFile(dir / "f0"); + CHECK(fs::exists(dir / "f0")); +#else + WARN("Windows specific tests are empty on non-Windows systems."); +#endif +} + +TEST_CASE("Windows: path namespace handling", "[filesystem][path][fs.path.win.namespaces]") +{ +#ifdef GHC_OS_WINDOWS + { + std::error_code ec; + fs::path p(R"(\\localhost\c$\Windows)"); + auto symstat = fs::symlink_status(p, ec); + CHECK(!ec); + auto p2 = fs::canonical(p, ec); + CHECK(!ec); + CHECK(p2 == p); + } + + struct TestInfo + { + std::string _path; + std::string _string; + std::string _rootName; + std::string _rootPath; + std::string _iterateResult; + }; + std::vector variants = { + {R"(C:\Windows\notepad.exe)", R"(C:\Windows\notepad.exe)", "C:", "C:\\", "C:,/,Windows,notepad.exe"}, +#ifdef USE_STD_FS + {R"(\\?\C:\Windows\notepad.exe)", R"(\\?\C:\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,C:,Windows,notepad.exe"}, + {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "\\??", "\\??\\", "/??,/,C:,Windows,notepad.exe"}, +#else + {R"(\\?\C:\Windows\notepad.exe)", R"(\\?\C:\Windows\notepad.exe)", "C:", "C:\\", "//?/,C:,/,Windows,notepad.exe"}, + {R"(\??\C:\Windows\notepad.exe)", R"(\??\C:\Windows\notepad.exe)", "C:", "C:\\", "/?\?/,C:,/,Windows,notepad.exe"}, +#endif + {R"(\\.\C:\Windows\notepad.exe)", R"(\\.\C:\Windows\notepad.exe)", "\\\\.", "\\\\.\\", "//.,/,C:,Windows,notepad.exe"}, + {R"(\\?\HarddiskVolume1\Windows\notepad.exe)", R"(\\?\HarddiskVolume1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,HarddiskVolume1,Windows,notepad.exe"}, + {R"(\\?\Harddisk0Partition1\Windows\notepad.exe)", R"(\\?\Harddisk0Partition1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,Harddisk0Partition1,Windows,notepad.exe"}, + {R"(\\.\GLOBALROOT\Device\HarddiskVolume1\Windows\notepad.exe)", R"(\\.\GLOBALROOT\Device\HarddiskVolume1\Windows\notepad.exe)", "\\\\.", "\\\\.\\", "//.,/,GLOBALROOT,Device,HarddiskVolume1,Windows,notepad.exe"}, + {R"(\\?\GLOBALROOT\Device\Harddisk0\Partition1\Windows\notepad.exe)", R"(\\?\GLOBALROOT\Device\Harddisk0\Partition1\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,GLOBALROOT,Device,Harddisk0,Partition1,Windows,notepad.exe"}, + {R"(\\?\Volume{e8a4a89d-0000-0000-0000-100000000000}\Windows\notepad.exe)", R"(\\?\Volume{e8a4a89d-0000-0000-0000-100000000000}\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,Volume{e8a4a89d-0000-0000-0000-100000000000},Windows,notepad.exe"}, + {R"(\\LOCALHOST\C$\Windows\notepad.exe)", R"(\\LOCALHOST\C$\Windows\notepad.exe)", "\\\\LOCALHOST", "\\\\LOCALHOST\\", "//LOCALHOST,/,C$,Windows,notepad.exe"}, + {R"(\\?\UNC\C$\Windows\notepad.exe)", R"(\\?\UNC\C$\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,UNC,C$,Windows,notepad.exe"}, + {R"(\\?\GLOBALROOT\Device\Mup\C$\Windows\notepad.exe)", R"(\\?\GLOBALROOT\Device\Mup\C$\Windows\notepad.exe)", "\\\\?", "\\\\?\\", "//?,/,GLOBALROOT,Device,Mup,C$,Windows,notepad.exe"}, + }; + + for (auto ti : variants) { + INFO("Used path: " + ti._path); + auto p = fs::path(ti._path); + CHECK(p.string() == ti._string); + CHECK(p.is_absolute()); + CHECK(p.root_name().string() == ti._rootName); + CHECK(p.root_path().string() == ti._rootPath); + CHECK(iterateResult(p) == ti._iterateResult); + } +#else + WARN("Windows specific tests are empty on non-Windows systems."); +#endif +} + +TEST_CASE("Windows: Mapped folders handling ", "[filesystem][fs.win][fs.win.mapped]") +{ +#ifdef GHC_OS_WINDOWS + // this test expects a mapped volume on C:\\fs-test as is the case on the development test system + // does nothing on other systems + if (fs::exists("C:\\fs-test")) { + CHECK(fs::canonical("C:\\fs-test\\Test.txt").string() == "C:\\fs-test\\Test.txt"); + } +#else + WARN("Windows specific tests are empty on non-Windows systems."); +#endif +} + +TEST_CASE("Windows: Deletion of Read-only Files", "[filesystem][fs.win][fs.win.remove]") +{ +#ifdef GHC_OS_WINDOWS + TemporaryDirectory t(TempOpt::change_path); + std::error_code ec; + generateFile("foo", 512); + auto allWrite = fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write; + CHECK_NOTHROW(fs::permissions("foo", allWrite, fs::perm_options::remove)); + CHECK_NOTHROW(fs::remove("foo")); + CHECK(!fs::exists("foo")); +#else + WARN("Windows specific tests are empty on non-Windows systems."); +#endif +} diff --git a/gulrak-filesystem/test/fwd_test.cpp b/gulrak-filesystem/test/fwd_test.cpp new file mode 100644 index 0000000..7905707 --- /dev/null +++ b/gulrak-filesystem/test/fwd_test.cpp @@ -0,0 +1,7 @@ +// This test file is part of the fwd_test.cpp/impl_test.cpp pair +// and used to test the new optional two-part usage of ghc::filesystem +// where exactly one cpp includes fs_impl.hpp and all others use +// fs_fwd.hpp (to test this with maximum functionality, the unit tests +// are included here, signaling they should only include the fs_fwd.hpp) +#define GHC_FILESYSTEM_FWD_TEST +#include "filesystem_test.cpp" diff --git a/gulrak-filesystem/test/impl_test.cpp b/gulrak-filesystem/test/impl_test.cpp new file mode 100644 index 0000000..092be63 --- /dev/null +++ b/gulrak-filesystem/test/impl_test.cpp @@ -0,0 +1,8 @@ +// This test file is part of the fwd_test.cpp/impl_test.cpp pair +// and used to test the new optional two-part usage of ghc::filesystem +// where exactly one cpp includes fs_impl.hpp and all others use +// fs_fwd.hpp (to test this with maximum functionality, the unit tests +// are included here, signaling they should only include the fs_fwd.hpp) +#include +#define CATCH_CONFIG_MAIN +#include "catch.hpp" diff --git a/gulrak-filesystem/test/multi1.cpp b/gulrak-filesystem/test/multi1.cpp new file mode 100644 index 0000000..6a9fac4 --- /dev/null +++ b/gulrak-filesystem/test/multi1.cpp @@ -0,0 +1,42 @@ +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include +namespace fs = ghc::filesystem; + +// This test and the one in multi2.cpp doesn't actualy test relevant functionality, +// it is just used to check that it is possible to include filesystem.h in multiple +// source files. +TEST_CASE("Multifile-test 1", "[multi]") +{ + CHECK("/usr/local/bin" == fs::path("/usr/local/bin").generic_string()); + std::string str = "/usr/local/bin"; + std::u16string u16str = u"/usr/local/bin"; + std::u32string u32str = U"/usr/local/bin"; + CHECK(str == fs::path(str.begin(), str.end())); + CHECK(str == fs::path(u16str.begin(), u16str.end())); + CHECK(str == fs::path(u32str.begin(), u32str.end())); +} diff --git a/gulrak-filesystem/test/multi2.cpp b/gulrak-filesystem/test/multi2.cpp new file mode 100644 index 0000000..6bf3250 --- /dev/null +++ b/gulrak-filesystem/test/multi2.cpp @@ -0,0 +1,40 @@ +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +#include "catch.hpp" +#include +namespace fs = ghc::filesystem; + +// This test and the one in multi1.cpp doesn't actualy test relevant functionality, +// it is just used to check that it is possible to include filesystem.h in multiple +// source files. +TEST_CASE("Multifile-test 2", "[multi]") +{ + CHECK("/usr/local/bin" == fs::path("/usr/local/bin").generic_string()); + std::string str = "/usr/local/bin"; + std::u16string u16str = u"/usr/local/bin"; + std::u32string u32str = U"/usr/local/bin"; + CHECK(str == fs::path(str.begin(), str.end())); + CHECK(str == fs::path(u16str.begin(), u16str.end())); + CHECK(str == fs::path(u32str.begin(), u32str.end())); +} diff --git a/osx_environment.sh b/osx_environment.sh index 09fa347..2ee886d 100644 --- a/osx_environment.sh +++ b/osx_environment.sh @@ -1,7 +1,7 @@ #!/bin/bash # Checks if directory exists, otherwise asks to install package. -function check_dir_exists() { +check_dir_exists() { local path=$1 local package=$2 @@ -11,7 +11,7 @@ function check_dir_exists() { fi } -if [ ! $BARRIER_BUILD_ENV ]; then +if [ -z "$BARRIER_BUILD_ENV" ]; then check_dir_exists '/Applications/Xcode.app' 'Xcode' printf "Modifying environment for Barrier build...\n" @@ -29,18 +29,15 @@ if [ ! $BARRIER_BUILD_ENV ]; then elif command -v brew; then printf "Detected Homebrew\n" - QT_PATH=$(brew --prefix qt) - OPENSSL_PATH=$(brew --prefix openssl) + QT_PATH=$(brew --prefix qt@5) - check_dir_exists "$QT_PATH" 'qt' - check_dir_exists "$OPENSSL_PATH" 'openssl' + check_dir_exists "$QT_PATH" 'qt5' export BARRIER_BUILD_BREW=1 - export CMAKE_PREFIX_PATH="$QT_PATH:$CMAKE_PREFIX_PATH" - export LD_LIBRARY_PATH="$OPENSSL_PATH/lib:$LD_LIBRARY_PATH" - export CPATH="$OPENSSL_PATH/include:$CPATH" - export PKG_CONFIG_PATH="$OPENSSL_PATH/lib/pkgconfig:$PKG_CONFIG_PATH" - + export CMAKE_PREFIX_PATH="/opt/procursus:$QT_PATH:$CMAKE_PREFIX_PATH" + export LD_LIBRARY_PATH="/opt/procursus/lib:$LD_LIBRARY_PATH" + export CPATH="/opt/procursus/include:$CPATH" + export PKG_CONFIG_PATH="/opt/procursus/lib/pkgconfig:$PKG_CONFIG_PATH" else printf "Neither Homebrew nor Macports is installed. Can't get dependency paths\n" exit 1 diff --git a/res/Readme.txt b/res/Readme.txt index 0b2802b..9cfcf3c 100644 --- a/res/Readme.txt +++ b/res/Readme.txt @@ -1,4 +1,4 @@ -Thank you for chosing Barrier! +Thank you for choosing Barrier! https://github.com/debauchee/barrier/ Barrier allows you to share your keyboard and mouse between computers over a network. diff --git a/res/barrier.desktop b/res/barrier.desktop index 6bb60e1..a47fd8e 100644 --- a/res/barrier.desktop +++ b/res/barrier.desktop @@ -5,6 +5,5 @@ Comment=Keyboard and mouse sharing solution Exec=barrier Icon=barrier Terminal=false -Categories=Utility;DesktopUtility; +Categories=Utility;RemoteAccess; Keywords=keyboard;mouse;sharing;network;share; - diff --git a/res/config.h.in b/res/config.h.in index 2bd3b3b..53d3a3e 100644 --- a/res/config.h.in +++ b/res/config.h.in @@ -19,9 +19,6 @@ /* Define if your compiler has standard C++ library support. */ #cmakedefine HAVE_CXX_STDLIB ${HAVE_CXX_STDLIB} -/* Define if the header file declares function prototypes. */ -#cmakedefine HAVE_DPMS_PROTOTYPES ${HAVE_DPMS_PROTOTYPES} - /* Define if you have a working `getpwuid_r` function. */ #cmakedefine HAVE_GETPWUID_R ${HAVE_GETPWUID_R} @@ -97,9 +94,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H} -/* Define to 1 if you have the `vsnprintf` function. */ -#cmakedefine HAVE_VSNPRINTF ${HAVE_VSNPRINTF} - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WCHAR_H ${HAVE_WCHAR_H} @@ -115,9 +109,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_X11_EXTENSIONS_XKBSTR_H ${HAVE_X11_EXTENSIONS_XKBSTR_H} -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_X11_EXTENSIONS_XTEST_H ${HAVE_X11_EXTENSIONS_XTEST_H} - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_X11_XKBLIB_H ${HAVE_X11_XKBLIB_H} @@ -160,8 +151,5 @@ /* Define to 1 if your declares `struct tm`. */ #cmakedefine TM_IN_SYS_TIME ${TM_IN_SYS_TIME} -/* Define to 1 if the X Window System is missing or not being used. */ -#cmakedefine X_DISPLAY_MISSING ${X_DISPLAY_MISSING} - /* Define to `unsigned int` if does not define. */ #cmakedefine size_t ${size_t} diff --git a/res/makeicon.sh b/res/makeicon.sh index 2883755..cfebe30 100755 --- a/res/makeicon.sh +++ b/res/makeicon.sh @@ -1,10 +1,10 @@ #!/bin/sh ICNS_BASE=../dist/macos/bundle/Barrier.app/Contents/Resources if ! which magick >/dev/null 2>&1; then - echo "Need ImageMagic for this" + echo "Need ImageMagick for this" exit 10 fi -cd $(dirname $0) || exit $? +cd "$(dirname "$0")" || exit $? if [ ! -r barrier.png ]; then echo "Use inkscape (or another vector graphics editor) to create barrier.png from barrier.svg first" exit 10 @@ -12,11 +12,11 @@ fi rm -rf work || exit $? mkdir -p work || exit $? for s in 16 24 32 48 64 128 256 512 1024; do - magick convert barrier.png -resize ${s}x${s} -depth 8 work/${s}.png || exit $? + magick convert barrier.png -resize "${s}x${s}" -depth 8 "work/${s}.png" || exit $? done # windows icon magick convert work/{16,24,32,48,64,128}.png barrier.png barrier.ico || exit $? # macos icon -png2icns $ICNS_BASE/Barrier.icns work/{16,32,256,512,1024}.png || exit $? +png2icns "$ICNS_BASE/Barrier.icns" work/{16,32,256,512,1024}.png || exit $? rm -rf work echo Done diff --git a/res/openssl/barrier.conf b/res/openssl/barrier.conf deleted file mode 100644 index a29abfd..0000000 --- a/res/openssl/barrier.conf +++ /dev/null @@ -1,65 +0,0 @@ -# -# Barrier OpenSSL configuration file. -# Used for generation of certificate requests. -# - -dir = . - -[ca] -default_ca = CA_default - -[CA_default] -serial = $dir/serial -database = $dir/certindex.txt -new_certs_dir = $dir/certs -certificate = $dir/cacert.pem -private_key = $dir/private/cakey.pem -default_days = 365 -default_md = md5 -preserve = no -email_in_dn = no -nameopt = default_ca -certopt = default_ca -policy = policy_match - -[policy_match] -countryName = match -stateOrProvinceName = match -organizationName = match -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -[req] -default_bits = 2048 # Size of keys -default_keyfile = key.pem # name of generated keys -default_md = md5 # message digest algorithm -string_mask = nombstr # permitted characters -distinguished_name = req_distinguished_name -req_extensions = v3_req - -[req_distinguished_name] -0.organizationName = Organization Name (company) -organizationalUnitName = Organizational Unit Name (department, division) -emailAddress = Email Address -emailAddress_max = 40 -localityName = Locality Name (city, district) -stateOrProvinceName = State or Province Name (full name) -countryName = Country Name (2 letter code) -countryName_min = 2 -countryName_max = 2 -commonName = Common Name (hostname, IP, or your name) -commonName_max = 64 -0.organizationName_default = My Company -localityName_default = My Town -stateOrProvinceName_default = State or Providence -countryName_default = US - -[v3_ca] -basicConstraints = CA:TRUE -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid:always,issuer:always - -[v3_req] -basicConstraints = CA:FALSE -subjectKeyIdentifier = hash diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 25b1f47..9e20c67 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,7 +1,7 @@ name: barrier base: core18 version: master -version-script: git describe --tags --long | sed "s/^v//" +version-script: git describe --tags | sed "s/^v//" adopt-info: appstream-flathub grade: stable # must be 'stable' to release into candidate/stable channels confinement: strict # use 'strict' once you have the right plugs and slots diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 55aad09..cc44fb2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2011 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -19,7 +19,13 @@ include_directories (${CMAKE_CURRENT_BINARY_DIR}/lib) add_subdirectory(lib) add_subdirectory(cmd) -add_subdirectory(test) + +include(../cmake/gtest.cmake) + +if (BARRIER_BUILD_TESTS) + add_subdirectory(test/integtests) + add_subdirectory(test/unittests) +endif() if (BARRIER_BUILD_GUI) add_subdirectory(gui) diff --git a/src/cmd/CMakeLists.txt b/src/cmd/CMakeLists.txt index ebf2a0d..946b19a 100644 --- a/src/cmd/CMakeLists.txt +++ b/src/cmd/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2011 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -18,6 +18,5 @@ add_subdirectory(barrierc) add_subdirectory(barriers) if (WIN32) - add_subdirectory(barrierd) + add_subdirectory(barrierd) endif() - diff --git a/src/cmd/barrierc/CMakeLists.txt b/src/cmd/barrierc/CMakeLists.txt index 0c08550..45e9ab0 100644 --- a/src/cmd/barrierc/CMakeLists.txt +++ b/src/cmd/barrierc/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -21,16 +21,7 @@ set(sources if (WIN32) file(GLOB arch_headers "MSWindows*.h") file(GLOB arch_sources "MSWindows*.cpp") - list(APPEND sources - resource.h - barrierc.ico - barrierc.rc - tb_error.ico - tb_idle.ico - tb_run.ico - tb_wait.ico - barrierc.exe.manifest - ) + list(APPEND sources barrierc.rc) elseif (APPLE) file(GLOB arch_headers "OSX*.h") file(GLOB arch_sources "OSX*.cpp") @@ -55,4 +46,3 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") install (TARGETS barrierc DESTINATION bin) endif() - diff --git a/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.cpp b/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.cpp index 8d17900..dd2f59c 100644 --- a/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.cpp +++ b/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.h b/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.h index 91688e8..868b35b 100644 --- a/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.h +++ b/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/cmd/barrierc/OSXClientTaskBarReceiver.cpp b/src/cmd/barrierc/OSXClientTaskBarReceiver.cpp index 7e79991..e273f99 100644 --- a/src/cmd/barrierc/OSXClientTaskBarReceiver.cpp +++ b/src/cmd/barrierc/OSXClientTaskBarReceiver.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -66,4 +66,3 @@ createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events { return new OSXClientTaskBarReceiver(logBuffer, events); } - diff --git a/src/cmd/barrierc/OSXClientTaskBarReceiver.h b/src/cmd/barrierc/OSXClientTaskBarReceiver.h index fcc763a..59d5344 100644 --- a/src/cmd/barrierc/OSXClientTaskBarReceiver.h +++ b/src/cmd/barrierc/OSXClientTaskBarReceiver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/cmd/barrierc/XWindowsClientTaskBarReceiver.cpp b/src/cmd/barrierc/XWindowsClientTaskBarReceiver.cpp index f56481c..f829ae0 100644 --- a/src/cmd/barrierc/XWindowsClientTaskBarReceiver.cpp +++ b/src/cmd/barrierc/XWindowsClientTaskBarReceiver.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/cmd/barrierc/XWindowsClientTaskBarReceiver.h b/src/cmd/barrierc/XWindowsClientTaskBarReceiver.h index 73250d0..c3db22e 100644 --- a/src/cmd/barrierc/XWindowsClientTaskBarReceiver.h +++ b/src/cmd/barrierc/XWindowsClientTaskBarReceiver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/cmd/barrierc/barrierc.cpp b/src/cmd/barrierc/barrierc.cpp index 28d8efc..466a032 100644 --- a/src/cmd/barrierc/barrierc.cpp +++ b/src/cmd/barrierc/barrierc.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -32,13 +32,13 @@ #endif int -main(int argc, char** argv) +main(int argc, char** argv) { #if SYSAPI_WIN32 // record window instance for tray icon, etc ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); #endif - + Arch arch; arch.init(); diff --git a/src/cmd/barrierc/barrierc.exe.manifest b/src/cmd/barrierc/barrierc.exe.manifest deleted file mode 100644 index 2c6f365..0000000 --- a/src/cmd/barrierc/barrierc.exe.manifest +++ /dev/null @@ -1,2 +0,0 @@ - -PerMonitortrue \ No newline at end of file diff --git a/src/cmd/barrierc/barrierc.rc b/src/cmd/barrierc/barrierc.rc index b34127c..3000dcc 100644 --- a/src/cmd/barrierc/barrierc.rc +++ b/src/cmd/barrierc/barrierc.rc @@ -1,4 +1,4 @@ -//Microsoft Developer Studio generated resource script. +// Microsoft Visual C++ generated resource script. // #include "resource.h" @@ -8,9 +8,6 @@ // Generated from the TEXTINCLUDE 2 resource. // #include -#if !defined(IDC_STATIC) -#define IDC_STATIC (-1) -#endif ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS @@ -19,10 +16,8 @@ // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) -#endif //_WIN32 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// @@ -30,18 +25,18 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // TEXTINCLUDE // -1 TEXTINCLUDE DISCARDABLE +1 TEXTINCLUDE BEGIN "resource.h\0" END -2 TEXTINCLUDE DISCARDABLE +2 TEXTINCLUDE BEGIN "#include \r\n" "\0" END -3 TEXTINCLUDE DISCARDABLE +3 TEXTINCLUDE BEGIN "\r\n" "\0" @@ -52,29 +47,56 @@ END ///////////////////////////////////////////////////////////////////////////// // -// Icon +// Version // -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_BARRIER ICON DISCARDABLE "barrierc.ico" -IDI_TASKBAR_NOT_RUNNING ICON DISCARDABLE "tb_idle.ico" -IDI_TASKBAR_NOT_WORKING ICON DISCARDABLE "tb_error.ico" -IDI_TASKBAR_NOT_CONNECTED ICON DISCARDABLE "tb_wait.ico" -IDI_TASKBAR_CONNECTED ICON DISCARDABLE "tb_run.ico" +VS_VERSION_INFO VERSIONINFO + FILEVERSION BARRIER_VERSION_MAJOR, BARRIER_VERSION_MINOR, BARRIER_VERSION_PATCH, BARRIER_BUILD_NUMBER + PRODUCTVERSION BARRIER_VERSION_MAJOR, BARRIER_VERSION_MINOR, BARRIER_VERSION_PATCH, BARRIER_BUILD_NUMBER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Debauchee Open Source Group" + VALUE "CompanyWeb", "https://github.com/debauchee/barrier/" + VALUE "FileVersion", BARRIER_VERSION + VALUE "LegalCopyright", "Copyright (C) 2018 Debauchee Open Source Group\nCopyright (C) 2012-2016 Symless Ltd.\nCopyright (C) 2008-2014 Nick Bolton\nCopyright (C) 2002-2014 Chris Schoeneman" + VALUE "ProductName", "Barrier" + VALUE "ProductVersion", BARRIER_VERSION + VALUE "OriginalFilename", "barrierc.exe" + VALUE "FileDescription", "Open source KVM software client" + VALUE "InternalName", "barrierc" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + ///////////////////////////////////////////////////////////////////////////// // -// Dialog +// Icon // -IDD_TASKBAR_STATUS DIALOG DISCARDABLE 0, 0, 145, 18 -STYLE DS_MODALFRAME | WS_POPUP -FONT 8, "MS Sans Serif" -BEGIN - EDITTEXT IDC_TASKBAR_STATUS_STATUS,3,3,139,12,ES_AUTOHSCROLL | - ES_READONLY | NOT WS_BORDER -END +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_BARRIER ICON "barrierc.ico" +IDI_TASKBAR_NOT_RUNNING ICON "tb_idle.ico" +IDI_TASKBAR_NOT_WORKING ICON "tb_error.ico" +IDI_TASKBAR_NOT_CONNECTED ICON "tb_wait.ico" +IDI_TASKBAR_CONNECTED ICON "tb_run.ico" ///////////////////////////////////////////////////////////////////////////// @@ -82,7 +104,7 @@ END // Menu // -IDR_TASKBAR MENU DISCARDABLE +IDR_TASKBAR MENU BEGIN POPUP "Barrier" BEGIN @@ -92,19 +114,12 @@ BEGIN POPUP "Set Log Level" BEGIN MENUITEM "Error", IDC_TASKBAR_LOG_LEVEL_ERROR - MENUITEM "Warning", IDC_TASKBAR_LOG_LEVEL_WARNING - MENUITEM "Note", IDC_TASKBAR_LOG_LEVEL_NOTE - MENUITEM "Info", IDC_TASKBAR_LOG_LEVEL_INFO - MENUITEM "Debug", IDC_TASKBAR_LOG_LEVEL_DEBUG - MENUITEM "Debug1", IDC_TASKBAR_LOG_LEVEL_DEBUG1 - MENUITEM "Debug2", IDC_TASKBAR_LOG_LEVEL_DEBUG2 - END MENUITEM SEPARATOR MENUITEM "Quit", IDC_TASKBAR_QUIT @@ -112,12 +127,26 @@ BEGIN END +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_TASKBAR_STATUS DIALOG 0, 0, 145, 18 +STYLE DS_MODALFRAME | WS_POPUP +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_TASKBAR_STATUS_STATUS,3,3,139,12,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER +END + + ///////////////////////////////////////////////////////////////////////////// // // String Table // -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDS_FAILED "Barrier is about to quit with errors or warnings. Please check the log then click OK." IDS_INIT_FAILED "Barrier failed to initialize: %{1}" diff --git a/src/cmd/barrierc/resource.h b/src/cmd/barrierc/resource.h index 57b271e..5603ba5 100644 --- a/src/cmd/barrierc/resource.h +++ b/src/cmd/barrierc/resource.h @@ -26,7 +26,7 @@ #define IDC_TASKBAR_LOG_LEVEL_DEBUG2 40015 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 109 diff --git a/src/cmd/barrierd/CMakeLists.txt b/src/cmd/barrierd/CMakeLists.txt index aeae94c..5c57882 100644 --- a/src/cmd/barrierd/CMakeLists.txt +++ b/src/cmd/barrierd/CMakeLists.txt @@ -16,6 +16,9 @@ file(GLOB headers "*.h") file(GLOB sources "*.cpp") +if (WIN32) + list(APPEND sources barrierd.rc) +endif() if (WIN32) add_executable (barrierd WIN32 ${sources}) diff --git a/src/cmd/barrierd/barrierd.cpp b/src/cmd/barrierd/barrierd.cpp index dd351f9..710e89d 100644 --- a/src/cmd/barrierd/barrierd.cpp +++ b/src/cmd/barrierd/barrierd.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/cmd/barrierd/barrierd.ico b/src/cmd/barrierd/barrierd.ico new file mode 100644 index 0000000..6e90545 Binary files /dev/null and b/src/cmd/barrierd/barrierd.ico differ diff --git a/src/cmd/barrierd/barrierd.rc b/src/cmd/barrierd/barrierd.rc new file mode 100644 index 0000000..c5ad502 --- /dev/null +++ b/src/cmd/barrierd/barrierd.rc @@ -0,0 +1,111 @@ +// Microsoft Visual C++ generated resource script. +// + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include \r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION BARRIER_VERSION_MAJOR, BARRIER_VERSION_MINOR, BARRIER_VERSION_PATCH, BARRIER_BUILD_NUMBER + PRODUCTVERSION BARRIER_VERSION_MAJOR, BARRIER_VERSION_MINOR, BARRIER_VERSION_PATCH, BARRIER_BUILD_NUMBER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Debauchee Open Source Group" + VALUE "CompanyWeb", "https://github.com/debauchee/barrier/" + VALUE "FileVersion", BARRIER_VERSION + VALUE "LegalCopyright", "Copyright (C) 2018 Debauchee Open Source Group\nCopyright (C) 2012-2016 Symless Ltd.\nCopyright (C) 2008-2014 Nick Bolton\nCopyright (C) 2002-2014 Chris Schoeneman" + VALUE "ProductName", "Barrier" + VALUE "ProductVersion", BARRIER_VERSION + VALUE "OriginalFilename", "barrierd.exe" + VALUE "FileDescription", "Open source KVM software daemon" + VALUE "InternalName", "barrierd" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_BARRIER ICON "barrierd.ico" + + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/cmd/barriers/CMakeLists.txt b/src/cmd/barriers/CMakeLists.txt index 912421f..c9fa750 100644 --- a/src/cmd/barriers/CMakeLists.txt +++ b/src/cmd/barriers/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -21,16 +21,7 @@ set(sources if (WIN32) file(GLOB arch_headers "MSWindows*.h") file(GLOB arch_sources "MSWindows*.cpp") - list(APPEND sources - resource.h - barriers.ico - barriers.rc - tb_error.ico - tb_idle.ico - tb_run.ico - tb_wait.ico - barriers.exe.manifest - ) + list(APPEND sources barriers.rc) elseif (APPLE) file(GLOB arch_headers "OSX*.h") file(GLOB arch_sources "OSX*.cpp") @@ -55,5 +46,3 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") install (TARGETS barriers DESTINATION bin) endif() - - diff --git a/src/cmd/barriers/MSWindowsServerTaskBarReceiver.cpp b/src/cmd/barriers/MSWindowsServerTaskBarReceiver.cpp index a221dac..c86048a 100644 --- a/src/cmd/barriers/MSWindowsServerTaskBarReceiver.cpp +++ b/src/cmd/barriers/MSWindowsServerTaskBarReceiver.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/cmd/barriers/MSWindowsServerTaskBarReceiver.h b/src/cmd/barriers/MSWindowsServerTaskBarReceiver.h index a308ab4..c79a2b2 100644 --- a/src/cmd/barriers/MSWindowsServerTaskBarReceiver.h +++ b/src/cmd/barriers/MSWindowsServerTaskBarReceiver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/cmd/barriers/OSXServerTaskBarReceiver.cpp b/src/cmd/barriers/OSXServerTaskBarReceiver.cpp index bbe8fd1..5fb525a 100644 --- a/src/cmd/barriers/OSXServerTaskBarReceiver.cpp +++ b/src/cmd/barriers/OSXServerTaskBarReceiver.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/cmd/barriers/OSXServerTaskBarReceiver.h b/src/cmd/barriers/OSXServerTaskBarReceiver.h index ab6928f..1ad4501 100644 --- a/src/cmd/barriers/OSXServerTaskBarReceiver.h +++ b/src/cmd/barriers/OSXServerTaskBarReceiver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/cmd/barriers/XWindowsServerTaskBarReceiver.cpp b/src/cmd/barriers/XWindowsServerTaskBarReceiver.cpp index 7f525c5..6dfcdaa 100644 --- a/src/cmd/barriers/XWindowsServerTaskBarReceiver.cpp +++ b/src/cmd/barriers/XWindowsServerTaskBarReceiver.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/cmd/barriers/XWindowsServerTaskBarReceiver.h b/src/cmd/barriers/XWindowsServerTaskBarReceiver.h index 549a62f..ce56a3b 100644 --- a/src/cmd/barriers/XWindowsServerTaskBarReceiver.h +++ b/src/cmd/barriers/XWindowsServerTaskBarReceiver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/cmd/barriers/barriers.cpp b/src/cmd/barriers/barriers.cpp index 21c4d80..169c5d3 100644 --- a/src/cmd/barriers/barriers.cpp +++ b/src/cmd/barriers/barriers.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -32,7 +32,7 @@ #endif int -main(int argc, char** argv) +main(int argc, char** argv) { #if SYSAPI_WIN32 // record window instance for tray icon, etc @@ -45,7 +45,7 @@ main(int argc, char** argv) */ setenv("OS_ACTIVITY_DT_MODE", "NO", true); #endif - + Arch arch; arch.init(); diff --git a/src/cmd/barriers/barriers.exe.manifest b/src/cmd/barriers/barriers.exe.manifest deleted file mode 100644 index 7309fde..0000000 --- a/src/cmd/barriers/barriers.exe.manifest +++ /dev/null @@ -1,2 +0,0 @@ - -true \ No newline at end of file diff --git a/src/cmd/barriers/barriers.rc b/src/cmd/barriers/barriers.rc index c4d263c..f9d8af1 100644 --- a/src/cmd/barriers/barriers.rc +++ b/src/cmd/barriers/barriers.rc @@ -16,10 +16,8 @@ // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) -#endif //_WIN32 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// @@ -27,18 +25,18 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // TEXTINCLUDE // -1 TEXTINCLUDE +1 TEXTINCLUDE BEGIN "resource.h\0" END -2 TEXTINCLUDE +2 TEXTINCLUDE BEGIN "#include \r\n" "\0" END -3 TEXTINCLUDE +3 TEXTINCLUDE BEGIN "\r\n" "\0" @@ -47,6 +45,46 @@ END #endif // APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION BARRIER_VERSION_MAJOR, BARRIER_VERSION_MINOR, BARRIER_VERSION_PATCH, BARRIER_BUILD_NUMBER + PRODUCTVERSION BARRIER_VERSION_MAJOR, BARRIER_VERSION_MINOR, BARRIER_VERSION_PATCH, BARRIER_BUILD_NUMBER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Debauchee Open Source Group" + VALUE "CompanyWeb", "https://github.com/debauchee/barrier/" + VALUE "FileVersion", BARRIER_VERSION + VALUE "LegalCopyright", "Copyright (C) 2018 Debauchee Open Source Group\nCopyright (C) 2012-2016 Symless Ltd.\nCopyright (C) 2008-2014 Nick Bolton\nCopyright (C) 2002-2014 Chris Schoeneman" + VALUE "ProductName", "Barrier" + VALUE "ProductVersion", BARRIER_VERSION + VALUE "OriginalFilename", "barriers.exe" + VALUE "FileDescription", "Open source KVM software server" + VALUE "InternalName", "barriers" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + ///////////////////////////////////////////////////////////////////////////// // // Icon @@ -54,18 +92,19 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_BARRIER ICON "barriers.ico" -IDI_TASKBAR_NOT_RUNNING ICON "tb_idle.ico" -IDI_TASKBAR_NOT_WORKING ICON "tb_error.ico" +IDI_BARRIER ICON "barriers.ico" +IDI_TASKBAR_NOT_RUNNING ICON "tb_idle.ico" +IDI_TASKBAR_NOT_WORKING ICON "tb_error.ico" IDI_TASKBAR_NOT_CONNECTED ICON "tb_wait.ico" -IDI_TASKBAR_CONNECTED ICON "tb_run.ico" +IDI_TASKBAR_CONNECTED ICON "tb_run.ico" + ///////////////////////////////////////////////////////////////////////////// // // Menu // -IDR_TASKBAR MENU +IDR_TASKBAR MENU BEGIN POPUP "Barrier" BEGIN @@ -110,7 +149,7 @@ END // String Table // -STRINGTABLE +STRINGTABLE BEGIN IDS_FAILED "Barrier is about to quit with errors or warnings. Please check the log then click OK." IDS_INIT_FAILED "Barrier failed to initialize: %{1}" diff --git a/src/cmd/barriers/resource.h b/src/cmd/barriers/resource.h index 9594db1..3ee57e7 100644 --- a/src/cmd/barriers/resource.h +++ b/src/cmd/barriers/resource.h @@ -31,7 +31,7 @@ #define ID_BARRIER_RESETSERVER 40017 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 109 diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index f29fd91..570e842 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -6,15 +6,124 @@ set (CMAKE_AUTORCC ON) set (CMAKE_AUTOUIC ON) set (CMAKE_INCLUDE_CURRENT_DIR ON) -file (GLOB GUI_SOURCE_FILES src/*.cpp src/*.h) -file (GLOB GUI_UI_FILES src/*.ui) +# files that are used both in tests and the app +set(GUI_COMMON_SOURCE_FILES + src/Action.cpp + src/Hotkey.cpp + src/KeySequence.cpp +) + +set(GUI_COMMON_HEADER_FILES + src/Action.h + src/Hotkey.h + src/KeySequence.h +) + +set(GUI_SOURCE_FILES + src/AboutDialog.cpp + src/ActionDialog.cpp + src/AddClientDialog.cpp + src/AppConfig.cpp + src/BarrierLocale.cpp + src/BaseConfig.cpp + src/CommandProcess.cpp + src/DataDownloader.cpp + src/DisplayIsValid.cpp + src/FingerprintAcceptDialog.cpp + src/HotkeyDialog.cpp + src/IpcClient.cpp + src/Ipc.cpp + src/IpcReader.cpp + src/KeySequenceWidget.cpp + src/LogWindow.cpp + src/main.cpp + src/MainWindow.cpp + src/NewScreenWidget.cpp + src/QBarrierApplication.cpp + src/QUtility.cpp + src/Screen.cpp + src/ScreenSettingsDialog.cpp + src/ScreenSetupModel.cpp + src/ScreenSetupView.cpp + src/ServerConfig.cpp + src/ServerConfigDialog.cpp + src/SettingsDialog.cpp + src/SetupWizard.cpp + src/SslCertificate.cpp + src/TrashScreenWidget.cpp + src/VersionChecker.cpp + src/ZeroconfBrowser.cpp + src/ZeroconfRegister.cpp + src/ZeroconfServer.cpp + src/ZeroconfService.cpp + src/ZeroconfThread.cpp +) + +set(GUI_HEADER_FILES + src/AboutDialog.h + src/ActionDialog.h + src/AddClientDialog.h + src/AppConfig.h + src/BarrierLocale.h + src/BaseConfig.h + src/CommandProcess.h + src/DataDownloader.h + src/DisplayIsValid.h + src/ElevateMode.h + src/HotkeyDialog.h + src/IpcClient.h + src/Ipc.h + src/IpcReader.h + src/KeySequenceWidget.h + src/LogWindow.h + src/MainWindow.h + src/NewScreenWidget.h + src/ProcessorArch.h + src/QBarrierApplication.h + src/QUtility.h + src/Screen.h + src/ScreenSettingsDialog.h + src/ScreenSetupModel.h + src/ScreenSetupView.h + src/ServerConfigDialog.h + src/ServerConfig.h + src/SettingsDialog.h + src/SetupWizard.h + src/ShutdownCh.h + src/SslCertificate.h + src/TrashScreenWidget.h + src/VersionChecker.h + src/ZeroconfBrowser.h + src/ZeroconfRecord.h + src/ZeroconfRegister.h + src/ZeroconfServer.h + src/ZeroconfService.h + src/ZeroconfThread.h +) + +set(GUI_UI_FILES + src/AboutDialogBase.ui + src/ActionDialogBase.ui + src/AddClientDialogBase.ui + src/FingerprintAcceptDialog.ui + src/HotkeyDialogBase.ui + src/LogWindowBase.ui + src/MainWindowBase.ui + src/ScreenSettingsDialogBase.ui + src/ServerConfigDialogBase.ui + src/SettingsDialogBase.ui + src/SetupWizardBase.ui +) if (WIN32) set (GUI_RC_FILES res/win/Barrier.rc) endif() add_executable (barrier WIN32 + ${GUI_COMMON_SOURCE_FILES} + ${GUI_COMMON_HEADER_FILES} ${GUI_SOURCE_FILES} + ${GUI_HEADER_FILES} ${GUI_UI_FILES} ${GUI_RC_FILES} res/Barrier.qrc @@ -22,13 +131,13 @@ add_executable (barrier WIN32 include_directories (./src) -target_link_libraries (barrier Qt5::Core Qt5::Widgets Qt5::Network) +target_link_libraries(barrier net base io Qt5::Core Qt5::Widgets Qt5::Network ${OPENSSL_LIBS}) target_compile_definitions (barrier PRIVATE -DBARRIER_VERSION_STAGE="${BARRIER_VERSION_STAGE}") target_compile_definitions (barrier PRIVATE -DBARRIER_REVISION="${BARRIER_REVISION}") if (WIN32) include_directories ($ENV{BONJOUR_SDK_HOME}/Include) - find_library (DNSSD_LIB dnssd.lib + find_library (DNSSD_LIB dnssd.lib HINTS ENV BONJOUR_SDK_HOME PATH_SUFFIXES "Lib/x64") set_target_properties (barrier PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMT") @@ -51,3 +160,22 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "BSD") install (TARGETS barrier DESTINATION bin) endif() + +if (BARRIER_BUILD_TESTS) + set(GUI_TEST_SOURCE_FILES + test/KeySequenceTests.cpp + test/HotkeyTests.cpp + test/main.cpp + ) + + add_executable(guiunittests + ${GUI_TEST_SOURCE_FILES} + ${GUI_COMMON_SOURCE_FILES} + ${GUI_COMMON_HEADER_FILES} + ) + + add_test(guiunittests guiunittests) + + target_include_directories(guiunittests PUBLIC ../../ext) + target_link_libraries(guiunittests gtest gmock Qt5::Core Qt5::Widgets Qt5::Network ${libs}) +endif() diff --git a/src/gui/gui.pro b/src/gui/gui.pro deleted file mode 100644 index 14a2407..0000000 --- a/src/gui/gui.pro +++ /dev/null @@ -1,162 +0,0 @@ -QT += widgets \ - network -TEMPLATE = app -TARGET = barrier -DEFINES += VERSION_STAGE=\\\"$$QMAKE_VERSION_STAGE\\\" -DEFINES += VERSION_REVISION=\\\"$$QMAKE_VERSION_REVISION\\\" -DEFINES -= UNICODE -DEFINES += _MBCS -DEPENDPATH += . \ - res -INCLUDEPATH += . \ - src \ - ../lib/shared/ -FORMS += src/MainWindowBase.ui \ - src/AboutDialogBase.ui \ - src/ServerConfigDialogBase.ui \ - src/ScreenSettingsDialogBase.ui \ - src/ActionDialogBase.ui \ - src/HotkeyDialogBase.ui \ - src/SettingsDialogBase.ui \ - src/SetupWizardBase.ui \ - src/AddClientDialogBase.ui \ - src/ActivationDialog.ui \ - src/CancelActivationDialog.ui \ - src/FailedLoginDialog.ui -SOURCES += src/main.cpp \ - src/MainWindow.cpp \ - src/AboutDialog.cpp \ - src/ServerConfig.cpp \ - src/ServerConfigDialog.cpp \ - src/ScreenSetupView.cpp \ - src/Screen.cpp \ - src/ScreenSetupModel.cpp \ - src/NewScreenWidget.cpp \ - src/TrashScreenWidget.cpp \ - src/ScreenSettingsDialog.cpp \ - src/BaseConfig.cpp \ - src/HotkeyDialog.cpp \ - src/ActionDialog.cpp \ - src/Hotkey.cpp \ - src/Action.cpp \ - src/KeySequence.cpp \ - src/KeySequenceWidget.cpp \ - src/SettingsDialog.cpp \ - src/AppConfig.cpp \ - src/QBarrierApplication.cpp \ - src/VersionChecker.cpp \ - src/SetupWizard.cpp \ - src/IpcClient.cpp \ - src/IpcReader.cpp \ - src/Ipc.cpp \ - src/BarrierLocale.cpp \ - src/QUtility.cpp \ - src/ZeroconfServer.cpp \ - src/ZeroconfThread.cpp \ - src/ZeroconfRegister.cpp \ - src/ZeroconfBrowser.cpp \ - src/ZeroconfService.cpp \ - src/DataDownloader.cpp \ - src/AddClientDialog.cpp \ - src/CommandProcess.cpp \ - src/CoreInterface.cpp \ - src/Fingerprint.cpp \ - src/SslCertificate.cpp \ - src/WebClient.cpp \ - src/ActivationNotifier.cpp \ - src/ActivationDialog.cpp \ - src/CancelActivationDialog.cpp \ - src/FailedLoginDialog.cpp \ - ../lib/shared/SerialKey.cpp \ - src/LicenseManager.cpp -HEADERS += src/MainWindow.h \ - src/AboutDialog.h \ - src/ServerConfig.h \ - src/ServerConfigDialog.h \ - src/ScreenSetupView.h \ - src/Screen.h \ - src/ScreenSetupModel.h \ - src/NewScreenWidget.h \ - src/TrashScreenWidget.h \ - src/ScreenSettingsDialog.h \ - src/BaseConfig.h \ - src/HotkeyDialog.h \ - src/ActionDialog.h \ - src/Hotkey.h \ - src/Action.h \ - src/KeySequence.h \ - src/KeySequenceWidget.h \ - src/SettingsDialog.h \ - src/AppConfig.h \ - src/QBarrierApplication.h \ - src/VersionChecker.h \ - src/SetupWizard.h \ - src/IpcClient.h \ - src/IpcReader.h \ - src/Ipc.h \ - src/BarrierLocale.h \ - src/QUtility.h \ - src/ZeroconfServer.h \ - src/ZeroconfThread.h \ - src/ZeroconfRegister.h \ - src/ZeroconfRecord.h \ - src/ZeroconfBrowser.h \ - src/ZeroconfService.h \ - src/DataDownloader.h \ - src/AddClientDialog.h \ - src/CommandProcess.h \ - src/ProcessorArch.h \ - src/CoreInterface.h \ - src/Fingerprint.h \ - src/SslCertificate.h \ - src/WebClient.h \ - src/ActivationNotifier.h \ - src/ElevateMode.h \ - src/ActivationDialog.h \ - src/CancelActivationDialog.h \ - src/FailedLoginDialog.h \ - ../lib/shared/EditionType.h \ - ../lib/shared/SerialKey.h \ - src/LicenseManager.h -RESOURCES += res/Barrier.qrc -RC_FILE = res/win/Barrier.rc -macx { - QMAKE_INFO_PLIST = res/mac/Info.plist - TARGET = Barrier - QBARRIER_ICON.files = res/mac/Barrier.icns - QBARRIER_ICON.path = Contents/Resources - QMAKE_BUNDLE_DATA += QBARRIER_ICON - LIBS += $$MACX_LIBS -} -unix:!macx:LIBS += -ldns_sd -debug { - OBJECTS_DIR = tmp/debug - MOC_DIR = tmp/debug - RCC_DIR = tmp/debug -} -release { - OBJECTS_DIR = tmp/release - MOC_DIR = tmp/release - RCC_DIR = tmp/release -} -win32-msvc2015 { - LIBS += -lAdvapi32 - QMAKE_LFLAGS += /NODEFAULTLIB:LIBCMT -} -win32-msvc* { - contains(QMAKE_HOST.arch, x86):{ - QMAKE_LFLAGS *= /MACHINE:X86 - LIBS += -L"$$(BONJOUR_SDK_HOME)/Lib/Win32" -ldnssd - } - - contains(QMAKE_HOST.arch, x86_64):{ - QMAKE_LFLAGS *= /MACHINE:X64 - LIBS += -L"$$(BONJOUR_SDK_HOME)/Lib/x64" -ldnssd - } -} -win32 { - Debug:DESTDIR = ../../bin/Debug - Release:DESTDIR = ../../bin/Release - INCLUDEPATH += "$$(BONJOUR_SDK_HOME)/Include" -} -else:DESTDIR = ../../bin diff --git a/src/gui/gui.ts b/src/gui/gui.ts index 99bc7aa..308d41e 100644 --- a/src/gui/gui.ts +++ b/src/gui/gui.ts @@ -650,12 +650,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/lang.cmd b/src/gui/lang.cmd index 4418593..fbab95c 100644 --- a/src/gui/lang.cmd +++ b/src/gui/lang.cmd @@ -1 +1 @@ -lupdate -noobsolete gui.pro -ts gui.ts \ No newline at end of file +lupdate -noobsolete src/* -ts gui.ts diff --git a/src/gui/langbuild.cmd b/src/gui/langbuild.cmd old mode 100644 new mode 100755 index b86d202..aaeefbb --- a/src/gui/langbuild.cmd +++ b/src/gui/langbuild.cmd @@ -1,2 +1,2 @@ -cd res/lang -lrelease *.ts \ No newline at end of file +cd res/lang +lrelease *.ts diff --git a/src/gui/res/lang/Languages.xml b/src/gui/res/lang/Languages.xml index 5948f9c..723d667 100644 --- a/src/gui/res/lang/Languages.xml +++ b/src/gui/res/lang/Languages.xml @@ -1,46 +1,46 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/gui/res/lang/gui_af-ZA.ts b/src/gui/res/lang/gui_af-ZA.ts index 7ff6304..5e0121a 100644 --- a/src/gui/res/lang/gui_af-ZA.ts +++ b/src/gui/res/lang/gui_af-ZA.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_ar.ts b/src/gui/res/lang/gui_ar.ts index 84ae1e6..3882b6d 100644 --- a/src/gui/res/lang/gui_ar.ts +++ b/src/gui/res/lang/gui_ar.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_bg-BG.qm b/src/gui/res/lang/gui_bg-BG.qm index 013137d..7d02c13 100644 Binary files a/src/gui/res/lang/gui_bg-BG.qm and b/src/gui/res/lang/gui_bg-BG.qm differ diff --git a/src/gui/res/lang/gui_bg-BG.ts b/src/gui/res/lang/gui_bg-BG.ts index 6a373d8..ea45271 100644 --- a/src/gui/res/lang/gui_bg-BG.ts +++ b/src/gui/res/lang/gui_bg-BG.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Конфигурации на Синерджи (*.sgc);; Всички файлове (*.*) + All files (*.*) + Всички файлове (*.*) + + + + Barrier Configurations (*.sgc) + Конфигурации на Синерджи (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Конфигурации на Синерджи (*.conf);; Всички файлове (*.*) + Barrier Configurations (*.conf) + Конфигурации на Синерджи (*.conf) diff --git a/src/gui/res/lang/gui_ca-AD.qm b/src/gui/res/lang/gui_ca-AD.qm index 351c40d..755791b 100644 Binary files a/src/gui/res/lang/gui_ca-AD.qm and b/src/gui/res/lang/gui_ca-AD.qm differ diff --git a/src/gui/res/lang/gui_ca-AD.ts b/src/gui/res/lang/gui_ca-AD.ts index 34093d9..3cd7895 100644 --- a/src/gui/res/lang/gui_ca-AD.ts +++ b/src/gui/res/lang/gui_ca-AD.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Configuracions Barrier (*.sgc);;Tots els arxius (*.*) + All files (*.*) + Tots els arxius (*.*) + + + + Barrier Configurations (*.sgc) + Configuracions Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Configuracions Barrier (*.conf);;Tots els arxius (*.*) + Barrier Configurations (*.conf) + Configuracions Barrier (*.conf) @@ -900,8 +905,8 @@ To automatically trust this fingerprint for future connections, click Yes. To re Drag new screens to the grid or move existing ones around. Drag a screen to the trashcan to delete it. Double click on a screen to edit its settings. - Arrossega noves pantalles a la graella o mou les actuals al voltant. -Arrossega una pantalla a la paperera per eliminar-la. + Arrossega noves pantalles a la graella o mou les actuals al voltant. +Arrossega una pantalla a la paperera per eliminar-la. Fes doble clic a una pantalla per editar la seva configuració. @@ -1330,7 +1335,7 @@ p, li { white-space: pre-wrap; } Server response: %1 - Error inici de sessió, hi ha hagut un error. + Error inici de sessió, hi ha hagut un error. Resposta del servidor: %1 diff --git a/src/gui/res/lang/gui_cs-CZ.qm b/src/gui/res/lang/gui_cs-CZ.qm index 1ac02c8..23ed7bd 100644 Binary files a/src/gui/res/lang/gui_cs-CZ.qm and b/src/gui/res/lang/gui_cs-CZ.qm differ diff --git a/src/gui/res/lang/gui_cs-CZ.ts b/src/gui/res/lang/gui_cs-CZ.ts index fd7b9c7..16142bf 100644 --- a/src/gui/res/lang/gui_cs-CZ.ts +++ b/src/gui/res/lang/gui_cs-CZ.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Nastavení Barrier (*.sgc);;Všechny soubory (*.*) + All files (*.*) + Všechny soubory (*.*) + + + + Barrier Configurations (*.sgc) + Nastavení Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Nastavení Barrier (*.conf);;Všechny soubory (*.*) + Barrier Configurations (*.conf) + Nastavení Barrier (*.conf) diff --git a/src/gui/res/lang/gui_cy.qm b/src/gui/res/lang/gui_cy.qm index 87bc58c..0cdb24d 100644 Binary files a/src/gui/res/lang/gui_cy.qm and b/src/gui/res/lang/gui_cy.qm differ diff --git a/src/gui/res/lang/gui_cy.ts b/src/gui/res/lang/gui_cy.ts index be9cd1d..2478fee 100644 --- a/src/gui/res/lang/gui_cy.ts +++ b/src/gui/res/lang/gui_cy.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Cyfluniadau Barrier (*.sgc);;Pob ffeil (*.*) + All files (*.*) + Pob ffeil (*.*) + + + + Barrier Configurations (*.sgc) + Cyfluniadau Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Cyfluniadau Barrier (*.conf);;Pob ffeil (*.*) + Barrier Configurations (*.conf) + Cyfluniadau Barrier (*.conf) diff --git a/src/gui/res/lang/gui_da.qm b/src/gui/res/lang/gui_da.qm index f78dbb7..dc7812b 100644 Binary files a/src/gui/res/lang/gui_da.qm and b/src/gui/res/lang/gui_da.qm differ diff --git a/src/gui/res/lang/gui_da.ts b/src/gui/res/lang/gui_da.ts index 2b17c0b..7bec518 100644 --- a/src/gui/res/lang/gui_da.ts +++ b/src/gui/res/lang/gui_da.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Barrier-konfigurationer (*.sgc);;Alle filer (*.*) + All files (*.*) + Alle filer (*.*) + + + + Barrier Configurations (*.sgc) + Barrier-konfigurationer (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrier-konfigurationer (*.conf);;Alle filer (*.*) + Barrier Configurations (*.conf) + Barrier-konfigurationer (*.conf) @@ -1329,7 +1334,7 @@ p, li { white-space: pre-wrap; } Server response: %1 - Fejl i login, en fejl opstod. + Fejl i login, en fejl opstod. Server svar: %1 diff --git a/src/gui/res/lang/gui_de.qm b/src/gui/res/lang/gui_de.qm index eb0bb40..2e4f300 100644 Binary files a/src/gui/res/lang/gui_de.qm and b/src/gui/res/lang/gui_de.qm differ diff --git a/src/gui/res/lang/gui_de.ts b/src/gui/res/lang/gui_de.ts index 67eb994..f43ee39 100644 --- a/src/gui/res/lang/gui_de.ts +++ b/src/gui/res/lang/gui_de.ts @@ -1,12 +1,14 @@ - + + + AboutDialogBase About Barrier - Über Barrier + Über Barrier - + <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -26,22 +28,30 @@ Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> The Barrier GUI is based on QSynergy by Volker Lanz.<br /><br /> Visit our website for help and info (symless.com). </p> - + <p> +Anwendung zum Teilen von Tastatur und Maus. Betriebssystemübergreifend und Open Source.<br /><br /> +Copyright © 2012-2016 Symless Ltd.<br /> +Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br /><br /> +Barrier wird herausgegeben unter der GNU General Public License (GPLv2).<br /><br /> +Barrier basiert auf CosmoSynergy von Richard Lee und Adam Feder.<br /> +Die Barrier-GUI basiert auf QSynergy von Volker Lanz.<br /><br /> +Besuchen Sie unsere Website für Hilfe und Info (symless.com). +</p> Unknown - Unbekannt + Unbekannt Version: - Version: + Version: &Ok - &Ok + &Ok @@ -49,97 +59,97 @@ Visit our website for help and info (symless.com). Configure Action - Aktion konfigurieren + Aktion konfigurieren Choose the action to perform - Wähle eine Aktion, die ausgeführt werden soll + Wähle eine Aktion, die ausgeführt werden soll Press a hotkey - Hotkey drücken + Hotkey drücken Release a hotkey - Hotkey loslassen + Hotkey loslassen Press and release a hotkey - Hotkey drücken und loslassen + Hotkey drücken und loslassen only on these screens - Nur auf diesen Bildschirmen + nur auf diesen Bildschirmen Switch to screen - Zu Anzeige Wechseln + Zum Bildschirm wechseln Switch in direction - In Richtung wechseln + In Richtung wechseln left - links + links right - rechts + rechts up - hoch + hoch down - runter + runter Lock cursor to screen - Cursor auf Anzeige beschränken + Cursor auf Bildschirm beschränken toggle - umschalten + umschalten on - ein + ein off - aus + aus This action is performed when - Diese Aktion wird ausgeführt, wenn + Diese Aktion wird ausgeführt, wenn the hotkey is pressed - wenn der Hotkey gedrückt wird + wenn der Hotkey gedrückt wird the hotkey is released - wenn der Hotkey losgelassen wird + wenn der Hotkey losgelassen wird @@ -147,17 +157,17 @@ Visit our website for help and info (symless.com). Dialog - + Dialog TextLabel - + TextLabel Ignore auto connect clients - + Autoconnect-Clients ignorieren @@ -165,12 +175,12 @@ Visit our website for help and info (symless.com). Hotkey - Hotkey + Hotkey Enter the specification for the hotkey: - Gib die Definition für den Hotkey ein: + Definition für den Hotkey eingeben: @@ -178,189 +188,193 @@ Visit our website for help and info (symless.com). &Start - &Start + &Start &File - &Datei + &Datei &Edit - &Bearbeiten + &Bearbeiten &Window - &Fenster + &Fenster &Help - &Hilfe + &Hilfe <p>Your version of Barrier is out of date. Version <b>%1</b> is now available to <a href="%2">download</a>.</p> <p>Version %1 is now available, <a href="%2">visit website</a>.</p> - <p>Ihre Barrier Version ist veraltet. Version <b>%1</b> ist jetzt zum <a href="%2">Download</a> verfügbar.</p> + <p>Ihre Barrier-Version ist veraltet. Version <b>%1</b> ist jetzt zum <a href="%2">Download</a> verfügbar.</p> Program can not be started - Das Programm konnte nicht gestartet werden + Programm konnte nicht gestartet werden The executable<br><br>%1<br><br>could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program. - Die Anwendung<br><br>%1<br><br> konnte nicht gestartet werden, obwohl sie vorhanden ist. Bitte überprüfen sie, ob sie die benötigten Berechtigungen zur Ausführung der Anwendung haben, + Die Anwendung<br><br>%1<br><br> konnte nicht gestartet werden, obwohl sie vorhanden ist. Bitte überprüfen Sie, ob Sie ausreichende Rechte zur Ausführung der Anwendung haben. Barrier client not found - Der Barrier Client wurde nicht gefunden + Barrier-Client wurde nicht gefunden The executable for the barrier client does not exist. - Die ausführbare Datei für den Barrier Client existiert nicht. + Die Anwendung für den Barrier-Client existiert nicht. Hostname is empty - Der Hostname is leer + Hostname ist leer Please fill in a hostname for the barrier client to connect to. - Bitte tragen Sie einen Hostnamen ein, zu dem sich der Barrier-Client verbinden soll. + Bitte tragen Sie einen Hostnamen ein, zu dem sich der Barrier-Client verbinden soll. Cannot write configuration file - Konfigurationsdatei konnte nicht geschrieben werden + Konfigurationsdatei konnte nicht geschrieben werden The temporary configuration file required to start barrier can not be written. - Die temporäre Konfigurationsdatei konnte nicht geschrieben werden. Sie wird jedoch für den Start von Barrier benötigt. + Die für den Start von Barrier nötige temporäre Konfigurationsdatei kann nicht geschrieben werden. Configuration filename invalid - Der Dateiname der Konfigurationsdatei ist ungültig. + Dateiname der Konfigurationsdatei ungültig You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now? - Sie haben keine gültige Konfigurationsdatei angegeben. Wollen sie jetzt nach dieser Datei suchen? + Sie haben keine gültige Konfigurationsdatei für den Barrier-Server angegeben. Wollen sie jetzt nach dieser Datei suchen? Barrier server not found - Der Barrier Server wurde nicht gefunden. + Barrier-Server nicht gefunden The executable for the barrier server does not exist. - Die ausführbare Datei für den Barrier Server existiert nicht. + Die Anwendung für den Barrier-Server existiert nicht. Barrier terminated with an error - Barrier wurde mit einem Fehler beendet. + Barrier mit Fehler beendet Barrier terminated unexpectedly with an exit code of %1.<br><br>Please see the log output for details. - Barrier wurde unerwartet mit Abbruchcode %1 beendet. <br><br> Weitere Informationen können dem Log entnommen werden. + Barrier unerwartet mit Exit-Code %1 beendet. <br><br>Siehe Logausgabe für weitere Details. &Stop - &Stopp + &Stop Please add the server (%1) to the grid. - + Bitte Server (%1) zum Gitter hinzufügen. Please drag the new client screen (%1) to the desired position on the grid. - + Bitte den Bildschirm des neuen Clients zur gewünschten Position auf dem Gitter ziehen. Failed to detect system architecture. - + System-Architektur konnte nicht ermittelt werden. Cancel - + Abbrechen Failed to download Bonjour installer to location: %1 - + Bonjour-Installer konnte nicht heruntergeladen werden nach: %1 Do you want to enable auto config and install Bonjour? This feature helps you establish the connection. - + Möchten Sie Bonjour installieren, um Autoconfig zu aktivieren? + +Diese Funktion hilft Ihnen beim Verbindungsaufbau. Auto config feature requires Bonjour. Do you want to install Bonjour? - + Für die Autoconfig-Funktion ist Bonjour nötig. + +Möchten Sie Bonjour installieren? Barrier is starting. - Barrier wird gestartet. + Barrier startet. Barrier is running. - Barrier läuft. + Barrier läuft. Barrier is not running. - Barrier wird nicht ausgeführt. + Barrier läuft nicht. Unknown - Unbekannt + Unbekannt Barrier - Barrier + Barrier Browse for a barriers config file - Nach einer Konfigurationsdatei für Barrier suchen. + Nach einer Konfigurationsdatei für Barrier suchen Barrier is now connected, You can close the config window. Barrier will remain connected in the background. - + Barrier ist jetzt verbunden. Sie können das Konfigurationsfenster schließen. Barrier wird im Hintergrund verbunden bleiben. Security question - + Sicherheitsfrage @@ -368,25 +382,31 @@ Do you want to install Bonjour? %1 -This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user). +This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user). To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No. - + Vertrauen Sie diesem Fingerabdruck? + +%1 + +Dies ist ein Server-Fingerabdruck. Sie sollten ihn mit dem Fingerabdruck auf dem Server-Bildschirm vergleichen. Wenn die zwei nicht identisch sind, handelt es sich wahrscheinlich nicht um den erwarteten Server (es könnte ein böswilliger User sein). + +Um diesem Fingerabdruck bei zukünftigen Verbindeungen automatisch zu vertrauen, klicken Sie auf Ja. Um diesen Fingerabdruck abzulehnen und die Verbindung vom Server zu trennen, klicken Sie auf Nein. Save configuration as... - Konfiguration speichern unter ... + Konfiguration speichern unter ... Save failed - Speichern fehlgeschlagen + Speichern fehlgeschlagen Could not save configuration to file. - Konfiguration konnte nicht in Datei gespeichert werden + Konfiguration konnte nicht in Datei gespeichert werden @@ -394,168 +414,168 @@ To automatically trust this fingerprint for future connections, click Yes. To re Barrier - Barrier + Barrier - Ser&ver (share this computer's mouse and keyboard): - + Ser&ver (share this computer's mouse and keyboard): + Ser&ver (Maus und Tastatur dieses Rechners teilen): Screen name: - Anzeigename: + Bildschirmname: &Server IP: - Server IP: + &Server IP: &Start - &Start + &Start Use existing configuration: - Verwende bestehende Konfiguration: + Verwende bestehende Konfiguration: &Configuration file: - &Konfigurationsdatei: + &Konfigurationsdatei: &Browse... - &Durchsuchen... + &Durchsuchen ... Configure interactively: - Interaktiv konfigurieren: + Interaktiv konfigurieren: &Configure Server... - Server &konfigurieren... + Server &konfigurieren ... Ready - Fertig + Bereit Log - Log + Log &Reload - &Anwenden + &Neu laden IP addresses: - IP Adressen: + IP-Adressen: Fingerprint: - + Fingerabdruck: - &Client (use another computer's mouse and keyboard): - + &Client (use another computer's mouse and keyboard): + &Client (Maus und Tastatur eines anderen Rechners benutzen): Auto config - + Autoconfig &About Barrier... - &Über Barrier... + &Über Barrier ... &Quit - &Beenden + &Beenden Quit - Beenden + Beenden Run - Start + Start S&top - S&topp + S&top Stop - Stop + Stop S&how Status - S&tatus anzeigen + S&tatus anzeigen &Hide - &Verstecken + &Verstecken Hide - Verstecken + Verstecken &Show - &Zeigen + An&zeigen Show - Anzeigen + Anzeigen Save configuration &as... - Configuration speichern &unter ... + Konfiguration speichern &unter ... Save the interactively generated server configuration to a file. - Speichere die interaktiv erstellte Konfiguration in eine Datei. + Speichere die interaktiv erstellte Konfiguration in eine Datei. Settings - Einstellungen + Einstellungen Edit settings - Einstellungen bearbeiten + Einstellungen bearbeiten Run Wizard - Assistent ausführen + Assistent ausführen @@ -563,7 +583,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Unnamed - Unbenannt + Unbenannt @@ -571,28 +591,29 @@ To automatically trust this fingerprint for future connections, click Yes. To re Failed to get plugin directory. - + Plugin-Verzeichnis konnte nicht ermittelt werden. Failed to get profile directory. - + Profil-Verzeichnis konnte nicht ermittelt werden. - Failed to download plugin '%1' to: %2 + Failed to download plugin '%1' to: %2 %3 - + Plugin '%1' konnte nicht runtergeladen werden nach: %2 +%3 Could not get Windows architecture type. - + Art der Windows-Architektur konnte nicht ermittelt werden. Could not get Linux architecture type. - + Art der Linux-Architektur konnte nicht ermittelt werden. @@ -600,66 +621,71 @@ To automatically trust this fingerprint for future connections, click Yes. To re Setup Barrier - Barrier einrichten + Barrier einrichten Please wait... - + Bitte warten ... Error: %1 - + Fehler: %1 Setup complete. - + Einrichtung abgeschlossen. - Downloading '%1' plugin (%2/%3)... - + Downloading '%1' plugin (%2/%3)... + Lade Plugin '%1' herunter (%2/%3) ... Plugins installed successfully. - + Plugins erfolgreich installiert. Generating SSL certificate... - + Erzeuge SSL-Zertifikat ... Downloading plugin: %1 (1/%2) - + Lade Plugin herunter: %1 (1/%2) Getting plugin list... - + Ermittle Plugin-Liste ... QObject - Barrier Configurations (*.sgc);;All files (*.*) - Barrier Konfigurationen (*.sgc);;Alle Dateien (*.*) + All files (*.*) + Alle Dateien (*.*) + + + + Barrier Configurations (*.sgc) + Barrier-Konfigurationen (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrier Konfigurationen (*.conf);;Alle Dateien (*.*) + Barrier Configurations (*.conf) + Barrier-Konfigurationen (*.conf) System tray is unavailable, quitting. - Infobereich ist nicht verfügbar. Beende Programm. + Systemabschnitt ist nicht verfügbar, beende. @@ -667,22 +693,22 @@ To automatically trust this fingerprint for future connections, click Yes. To re Screen name is empty - Der Anzeigename ist leer + Bildschirmname ist leer The screen name cannot be empty. Please either fill in a name or cancel the dialog. - Der Bildschirmname darf nicht leer sein. Bitte trage einen Namen ein oder schließe das Fenster. + Der Bildschirmname darf nicht leer sein. Bitte einen Namen eintragen oder das Dialogfenster abbrechen. Screen name matches alias - Der Anzeigename passt zum Alias + Bildschirmname passt zum Alias The screen name cannot be the same as an alias. Please either remove the alias or change the screen name. - Der Anzeigename kann nicht derselbe sein wie der Alias. Bitte entfernen sie den Alias oder ändern sie den Anzeigenamen. + Der Bildschirmname kann nicht derselbe sein wie ein Alias. Bitte entfernen sie den Alias oder ändern sie den Bildschirmnamen. @@ -690,37 +716,37 @@ To automatically trust this fingerprint for future connections, click Yes. To re Screen Settings - Anzeigeeinstellungen + Bildschirmeinstellungen Screen &name: - Anzeige&name: + Bildschirm&name: A&liases - A&liase + A&liase &Add - &Hinzufügen + &Hinzufügen &Remove - &Entfernen + &Entfernen &Modifier keys - &Zusatztasten + &Zusatztasten &Shift: - Um&schalt: + Um&schalt: @@ -729,7 +755,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Shift - Umschalt + Umschalt @@ -738,7 +764,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Ctrl - Strg + Strg @@ -747,7 +773,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Alt - Alt + Alt @@ -756,7 +782,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Meta - Meta + Meta @@ -765,7 +791,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Super - Windows + Windows @@ -774,82 +800,82 @@ To automatically trust this fingerprint for future connections, click Yes. To re None - Keine + Keine &Ctrl: - &Strg: + &Strg: Al&t: - Al&t: + Al&t: M&eta: - M&eta: + M&eta: S&uper: - S&uper: + S&uper: &Dead corners - "&Tote" Ecken + "&Tote" Ecken Top-left - Oben-links + Oben links Top-right - Oben-rechts + Oben rechts Bottom-left - Unten-links + Unten links Bottom-right - Unten-rechts + Unten rechts Corner Si&ze: - &Größe: + Ecken&größe: &Fixes - &Korrekturen + &Korrekturen Fix CAPS LOCK key - Korrektur für Feststelltaste + Korrektur für Feststelltaste Fix NUM LOCK key - Korrektur für Num Lock + Korrektur für Num-Lock-Taste Fix SCROLL LOCK key - Korrektur für Scroll Lock + Korrektur für Rollen-Taste Fix XTest for Xinerama - Korrektur für XTest mit Xinerama + Korrektur für XTest für Xinerama @@ -857,7 +883,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re <center>Screen: <b>%1</b></center><br>Double click to edit settings<br>Drag screen to the trashcan to remove it - <center>Anzeige: <b>%1</b></center><br>Klicken Sie doppelt um die Einstellungen zu ändern<br>Ziehen Sie die Anzeige in den Papierkorb um sie zu entfernen + <center>Bildschirm: <b>%1</b></center><br>Doppelklick, um die Einstellungen zu ändern<br>Bildschirm in den Papierkorb ziehen, um ihn zu entfernen @@ -865,7 +891,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Configure server - + Server konfigurieren @@ -873,168 +899,168 @@ To automatically trust this fingerprint for future connections, click Yes. To re Server Configuration - Server Konfiguration + Server-Konfiguration Screens and links - Anzeigen und Verbindungen + Bildschirme und Verbindungen Drag a screen from the grid to the trashcan to remove it. - Ziehen Sie eine Anzeige vom Raster in den Papierkorb um sie zu entfernen. + Ziehen Sie einen Bildschirm auf dem Gitter in den Papierkorb, um ihn zu entfernen. Configure the layout of your barrier server configuration. - Konfigurieren Sie die Anordnung Ihrer Barrier Server Konfiguration. + Konfigurieren Sie die Anordnung Ihrer Barrier-Server-Konfiguration. Drag this button to the grid to add a new screen. - Ziehen diese Symbol auf das Raster um eine neue Anzeige hinzuzufügen. + Ziehen Sie dieses Symbol auf das Gitter, um einen neuen Bildschirm hinzuzufügen. Drag new screens to the grid or move existing ones around. Drag a screen to the trashcan to delete it. Double click on a screen to edit its settings. - Ziehen Sie neue Anzeigen auf das Raster oder verschieben sie existierende. -Ziehen Sie eine Anzeige in den Papierkorb um sie zu entfernen. -Klicken sie doppelt auf eine Anzeige um die Einstellungen zu bearbeiten. + Ziehen Sie neue Bildschirme auf das Gitter oder verschieben sie existierende. +Ziehen Sie einen Bildschirm in den Papierkorb, um ihn zu entfernen. +Klicken Sie doppelt auf einen Bildschirm, um dessen Einstellungen zu bearbeiten. Hotkeys - Hotkeys + Hotkeys &Hotkeys - &Hotkeys + &Hotkeys &New - &Neu + &Neu &Edit - &Bearbeiten + &Bearbeiten &Remove - &Entfernen + &Entfernen A&ctions - &Befehle + &Befehle Ne&w - Ne&u + Ne&u E&dit - Än&dern + Än&dern Re&move - &Entfernen + &Entfernen Advanced server settings - Erwiterte Servereinstellungen + Erweiterte Server-Einstellungen &Switch - Wech&sel + Wech&seln Switch &after waiting - Wechsel n&ach Wartezeit + Wechseln n&ach Wartezeit ms - ms + ms Switch on double &tap within - Wechsel nach doppel&ter Randberührung innerhalb von + Wechsel nach doppel&ter Randberührung innerhalb von &Options - &Optionen + &Optionen &Check clients every - Prüfe auf Meldungen vom &Client aller + Prüfe &Clients alle Use &relative mouse moves - Ve&rwende relative Mausbewegungen + Ve&rwende relative Mausbewegungen S&ynchronize screen savers - Bildschirmschoner s&ynchronisieren + Bildschirmschoner s&ynchronisieren - Don't take &foreground window on Windows servers - Auf Windows Servern &Fenster im Vordergrund nicht aktivieren + Don't take &foreground window on Windows servers + Auf Windows-Servern &Fenster im Vordergrund nicht aktivieren Ignore auto config clients - + Ignoriere Autoconfig-Clients &Dead corners - "&Tote" Ecken + "&Tote" Ecken To&p-left - O&ben-links + O&ben links Top-rig&ht - Oben-rec&hts + Oben rec&hts &Bottom-left - &Unten-links + &Unten links Bottom-ri&ght - Unten-rec&hts + Unten rec&hts Cor&ner Size: - &Größe: + Ecken&größe: @@ -1042,20 +1068,20 @@ Klicken sie doppelt auf eine Anzeige um die Einstellungen zu bearbeiten. Save log file to... - Speicherort des Logfiles + Logfile speichern nach ... Elevate Barrier - Barrier Befördern + Barrier als Admin ausführen Are you sure you want to elevate Barrier? This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to. - Sind Sie sicher das Sie Barrier Erweiterte Benutzerrechte einräumen wollen? -Das erlaubt Barrier mit Prozessen die höhere Rechte haben und dem UAC-Dialog zu interagieren, kann aber bei normalen Prozessen Probleme verursachen. Erweiterte Rechte an Barrier bitte nur vergeben wenn es unbedingt nötig ist. + Sind Sie sicher, dass Sie Barrier erweiterte Benutzerrechte einräumen wollen? +Das erlaubt Barrier, mit Prozessen, die höhere Rechte haben, und dem UAC-Dialog zu interagieren, kann aber bei normalen Prozessen Probleme verursachen. Erweiterte Rechte an Barrier bitte nur vergeben, wenn es unbedingt nötig ist. @@ -1063,107 +1089,107 @@ Das erlaubt Barrier mit Prozessen die höhere Rechte haben und dem UAC-Dialog zu Settings - Einstellungen + Einstellungen Sc&reen name: - &Anzeigename: + &Bildschirmname: P&ort: - P&ort: + P&ort: &Interface: - Schn&ittstelle + Schn&ittstelle Elevate mode - + Admin-Modus &Hide on startup - + Beim Starten &verstecken &Network Security - + &Netzwerksicherheit Use &SSL encryption (unique certificate) - + Verwende &SSL-Verschlüsselung (eindeutiges Zertifikat) Logging - Protokollierung + Protokollierung &Logging level: - &Umfang: + &Umfang: Log to file: - In Datei: + Protokolliere in Datei: Browse... - Durchsuchen... + Durchsuchen ... Error - Fehler + Fehler &Language: - Sprache: + Sprache: &Miscellaneous - &Sonstiges + &Sonstiges Warning - Warnung + Warnung Note - Hinweis + Hinweis Info - Info + Info Debug - Debug + Debug Debug1 - Debug1 + Debug1 Debug2 - Debug2 + Debug2 @@ -1171,17 +1197,17 @@ Das erlaubt Barrier mit Prozessen die höhere Rechte haben und dem UAC-Dialog zu Setup Barrier - Barrier einrichten + Barrier einrichten Please select an option. - Bitte wählen Sie eine option aus. + Bitte wählen Sie eine Option aus. Please enter your email address and password. - Bitte geben Sie Ihre E-Mail Adresse und Ihr Passwort ein. + Bitte geben Sie Ihre E-Mail-Adresse und Ihr Passwort ein. @@ -1189,85 +1215,93 @@ Das erlaubt Barrier mit Prozessen die höhere Rechte haben und dem UAC-Dialog zu Setup Barrier - Barrier einrichten + Barrier einrichten Welcome - Willkommen + Willkommen Thanks for installing Barrier! - Danke, dass du Barrier installiert hast! + Danke, dass Sie Barrier installiert haben! - Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux). - Mit Barrier können Sie einfach Ihre Tastatur und Maus an mehreren Computern auf Ihrem Schreibtisch nutzen, und es ist Frei und Open Source. Bewegen Sie einfach ihre Maus über den Rand des Bildschirms eines Computers auf den Bildschirm eines anderen. Sie können sogar den Inhalt ihrer Zwischenablage an alle Computer verteilen. Alles was Sie brauchen ist ein Netzwerk-Anschluss. Barrier funktioniert auf Windows, Mac OS X und Linux. + Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux). + Mit Barrier können Sie einfach Ihre Tastatur und Maus mit mehreren Computern auf Ihrem Schreibtisch teilen, und es ist frei und Open Source. Bewegen Sie einfach ihre Maus über den Rand des Bildschirms eines Computers auf den Bildschirm eines anderen. Sie können sogar den Inhalt ihrer Zwischenablage teilen. Alles, was Sie brauchen, ist ein Netzwerk-Verbindung. Barrier ist betriebssystemübergreifend (funktioniert auf Windows, Mac OS X und Linux). Activate - + Aktivieren &Activate now... - + Jetzt &aktivieren ... Email: - + E-Mail: Password: - + Passwort: <a href="https://symless.com/account/reset/">Forgot password</a> - + <a href="https://symless.com/account/reset/">Passwort vergessen</a> &Skip activation - + Aktivierung über&springen - &Server (share this computer's mouse and keyboard) - + &Server (share this computer's mouse and keyboard) + &Server (Maus und Tastatur dieses Rechners teilen) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.</span></p></body></html> - +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Meine Haupt-Maus und -Tastatur sind mit diesem Rechner verbunden. Das ermöglicht Ihnen, den Mauszeiger zum Bildschirm eines anderen Rechners zu bewegen. Es kann nur ein Server in Ihren Einstellungen vorhanden sein.</span></p></body></html> - &Client (use another computer's mouse and keyboard) - + &Client (use another computer's mouse and keyboard) + &Client (Maus und Tastatur eines anderen Rechners benutzen) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.</span></p></body></html> - +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Sie haben bereits einen Server eingerichtet. Dieser Rechner wird von der Maus und Tastatur des Servers kontrolliert. Es kann viele Clients in Ihren Einstellungen geben.</span></p></body></html> Server or Client? - Server oder Client? + Server oder Client? @@ -1275,22 +1309,22 @@ p, li { white-space: pre-wrap; } Failed to get profile directory. - + Profil-Verzeichnis kann nicht ermittelt werden. SSL certificate generated. - + SSZ-Zertifikat erzeugt. SSL fingerprint generated. - + SSL-Fingerabdruck erzeugt. Failed to find SSL fingerprint. - + SSL-Fingerabdruck kann nicht gefunden werden. @@ -1298,7 +1332,7 @@ p, li { white-space: pre-wrap; } Unknown - Unbekannt + Unbekannt @@ -1308,19 +1342,22 @@ p, li { white-space: pre-wrap; } An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details. %1 - + Ein Fehler ist beim Einloggen aufgetreten. Bitte kontaktieren Sie das Helpdesk und halten Sie die folgenden Informationen bereit. + +%1 Login failed, invalid email or password. - Login fehlgeschlagen, falsche E-Mail Adresse oder Passwort. + Login fehlgeschlagen, E-Mail-Adresse oder Passwort ungültig. Login failed, an error occurred. %1 - Login fehlgeschlagen, ein Fehler ist aufgetreten. + Login fehlgeschlagen, ein Fehler ist aufgetreten. + %1 @@ -1330,8 +1367,10 @@ p, li { white-space: pre-wrap; } Server response: %1 - Login fehlgeschlagen, ein Fehler ist aufgetreten. -Serverantwort: + Login fehlgeschlagen, ein Fehler ist aufgetreten. + +Server-Antwort: + %1 @@ -1339,19 +1378,23 @@ Serverantwort: An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details. %1 - + Ein error ist bei der Abfrage der Plugin-Liste aufgetreten. Bitte kontaktieren Sie das Helpdesk und halten Sie die folgenden Informationen bereit. + +%1 Get plugin list failed, invalid user email or password. - + Konnte Plugin-Liste nicht ermitteln, E-Mail-Adresse oder Passwort ungültig. Get plugin list failed, an error occurred. %1 - + Konnte Plugin-Liste nicht ermitteln, ein Fehler ist aufgetreten. + +%1 @@ -1360,7 +1403,11 @@ Serverantwort: Server response: %1 - + Konnte Plugin-Liste nicht ermitteln, ein Fehler ist aufgetreten. + +Server-Antwort: + +%1 @@ -1368,44 +1415,44 @@ Server response: zeroconf server detected: %1 - + Zeroconf-Server erkannt: %1 zeroconf client detected: %1 - + Zeroconf-Client erkannt: %1 Zero configuration service - + Zero-Configuration-Dienst Error code: %1. - + Fehlercode: %1. Unable to start the zeroconf: %1. - + Zeroconf konnte nicht gestartet werden: %1. Barrier - Barrier + Barrier Failed to get local IP address. Please manually type in server address on your clients - + Lokale IP-Adresse nicht ermittelbar. Bitte die Server-Adresse an den Clients manuell eingeben %1 - + %1 - \ No newline at end of file + diff --git a/src/gui/res/lang/gui_es.qm b/src/gui/res/lang/gui_es.qm index d09974e..1a8735d 100644 Binary files a/src/gui/res/lang/gui_es.qm and b/src/gui/res/lang/gui_es.qm differ diff --git a/src/gui/res/lang/gui_es.ts b/src/gui/res/lang/gui_es.ts index 4b3523a..39fb569 100644 --- a/src/gui/res/lang/gui_es.ts +++ b/src/gui/res/lang/gui_es.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Configuraciones Barrier (*.sgc);;Todos los archivos (*.*) + All files (*.*) + Todos los archivos (*.*) + + + + Barrier Configurations (*.sgc) + Configuraciones Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Configuraciones Barrier (*.conf);;Todos los archivos (*.*) + Barrier Configurations (*.conf) + Configuraciones Barrier (*.conf) diff --git a/src/gui/res/lang/gui_et-EE.qm b/src/gui/res/lang/gui_et-EE.qm index 7c2fc9b..2c8fe11 100644 Binary files a/src/gui/res/lang/gui_et-EE.qm and b/src/gui/res/lang/gui_et-EE.qm differ diff --git a/src/gui/res/lang/gui_et-EE.ts b/src/gui/res/lang/gui_et-EE.ts index 033b9d7..ddbf6f7 100644 --- a/src/gui/res/lang/gui_et-EE.ts +++ b/src/gui/res/lang/gui_et-EE.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Barrier seaded (*.sgc);;kõik failid (*.*) + All files (*.*) + Kõik failid (*.*) + + + + Barrier Configurations (*.sgc) + Barrier seaded (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrier seaded (*.conf);;Kõik failid (*.*) + Barrier Configurations (*.conf) + Barrier seaded (*.conf) diff --git a/src/gui/res/lang/gui_fi.qm b/src/gui/res/lang/gui_fi.qm index 1e7178c..7a8ae49 100644 Binary files a/src/gui/res/lang/gui_fi.qm and b/src/gui/res/lang/gui_fi.qm differ diff --git a/src/gui/res/lang/gui_fi.ts b/src/gui/res/lang/gui_fi.ts index e5aea17..6609da9 100644 --- a/src/gui/res/lang/gui_fi.ts +++ b/src/gui/res/lang/gui_fi.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Barrier Configuration (*.sgc);;Kaikki tiedostot (*.*) + All files (*.*) + Kaikki tiedostot (*.*) + + + + Barrier Configurations (*.sgc) + Barrier Configuration (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrier Configuration (*.conf);;Kaikki tiedostot (*.*) + Barrier Configurations (*.conf) + Barrier Configuration (*.conf) diff --git a/src/gui/res/lang/gui_fr.qm b/src/gui/res/lang/gui_fr.qm index 38116ce..a68df9d 100644 Binary files a/src/gui/res/lang/gui_fr.qm and b/src/gui/res/lang/gui_fr.qm differ diff --git a/src/gui/res/lang/gui_fr.ts b/src/gui/res/lang/gui_fr.ts index ed37997..66258ed 100644 --- a/src/gui/res/lang/gui_fr.ts +++ b/src/gui/res/lang/gui_fr.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Configuration Barrier (*.sgc);; Tous les fichiers (*.*) + All files (*.*) + Tous les fichiers (*.*) + + + + Barrier Configurations (*.sgc) + Configuration Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) + Configuration Barrier (*.conf) diff --git a/src/gui/res/lang/gui_gl.ts b/src/gui/res/lang/gui_gl.ts index 2f89802..e0a95b7 100644 --- a/src/gui/res/lang/gui_gl.ts +++ b/src/gui/res/lang/gui_gl.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_grk.ts b/src/gui/res/lang/gui_grk.ts index ffbede5..751db12 100644 --- a/src/gui/res/lang/gui_grk.ts +++ b/src/gui/res/lang/gui_grk.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_he.qm b/src/gui/res/lang/gui_he.qm index 43fa7af..9bbf530 100644 Binary files a/src/gui/res/lang/gui_he.qm and b/src/gui/res/lang/gui_he.qm differ diff --git a/src/gui/res/lang/gui_he.ts b/src/gui/res/lang/gui_he.ts index 5a1ddcf..2850e83 100644 --- a/src/gui/res/lang/gui_he.ts +++ b/src/gui/res/lang/gui_he.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - קבצי הגדרות של Barrier (*.sgc);;All Files (*.*) + All files (*.*) + All Files (*.*) + + + + Barrier Configurations (*.sgc) + קבצי הגדרות של Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - קבצי הגדרות של Barrier (*.conf);;All Files (*.*) + Barrier Configurations (*.conf) + קבצי הגדרות של Barrier (*.conf) diff --git a/src/gui/res/lang/gui_hi.ts b/src/gui/res/lang/gui_hi.ts index 57b409e..460a1bc 100644 --- a/src/gui/res/lang/gui_hi.ts +++ b/src/gui/res/lang/gui_hi.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_hr-HR.qm b/src/gui/res/lang/gui_hr-HR.qm index a7e5563..9dd5d45 100644 Binary files a/src/gui/res/lang/gui_hr-HR.qm and b/src/gui/res/lang/gui_hr-HR.qm differ diff --git a/src/gui/res/lang/gui_hr-HR.ts b/src/gui/res/lang/gui_hr-HR.ts index 4695e4e..c9883ed 100644 --- a/src/gui/res/lang/gui_hr-HR.ts +++ b/src/gui/res/lang/gui_hr-HR.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Barrier postavke (*.sgc);;Sve datoteke (*.*) + All files (*.*) + Sve datoteke (*.*) + + + + Barrier Configurations (*.sgc) + Barrier postavke (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrier postavke (*.conf);;Sve datoteke (*.*) + Barrier Configurations (*.conf) + Barrier postavke (*.conf) diff --git a/src/gui/res/lang/gui_hu-HU.qm b/src/gui/res/lang/gui_hu-HU.qm index 10361b7..678cc5c 100644 Binary files a/src/gui/res/lang/gui_hu-HU.qm and b/src/gui/res/lang/gui_hu-HU.qm differ diff --git a/src/gui/res/lang/gui_hu-HU.ts b/src/gui/res/lang/gui_hu-HU.ts index 7c26d46..536c904 100644 --- a/src/gui/res/lang/gui_hu-HU.ts +++ b/src/gui/res/lang/gui_hu-HU.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Barrier konfiguráció (*.sgc);;Minden fájl (*.*) + All files (*.*) + Minden fájl (*.*) + + + + Barrier Configurations (*.sgc) + Barrier konfiguráció (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrier konfiguráció (*.conf);;Minden fájl (*.*) + Barrier Configurations (*.conf) + Barrier konfiguráció (*.conf) diff --git a/src/gui/res/lang/gui_id.ts b/src/gui/res/lang/gui_id.ts index ed52252..1552cb8 100644 --- a/src/gui/res/lang/gui_id.ts +++ b/src/gui/res/lang/gui_id.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_is-IS.ts b/src/gui/res/lang/gui_is-IS.ts index 2764171..4a76edd 100644 --- a/src/gui/res/lang/gui_is-IS.ts +++ b/src/gui/res/lang/gui_is-IS.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_it.qm b/src/gui/res/lang/gui_it.qm index bc8dc7e..9ba0ea0 100644 Binary files a/src/gui/res/lang/gui_it.qm and b/src/gui/res/lang/gui_it.qm differ diff --git a/src/gui/res/lang/gui_it.ts b/src/gui/res/lang/gui_it.ts index c1096dd..c0a6c58 100644 --- a/src/gui/res/lang/gui_it.ts +++ b/src/gui/res/lang/gui_it.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Configurazioni di Barrier (*.sgc);;Tutti i files (*.*) + All files (*.*) + Tutti i files (*.*) + + + + Barrier Configurations (*.sgc) + Configurazioni di Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Configurazioni di Barrier (*.conf);;Tutti i files (*.*) + Barrier Configurations (*.conf) + Configurazioni di Barrier (*.conf) diff --git a/src/gui/res/lang/gui_ja-JP.qm b/src/gui/res/lang/gui_ja-JP.qm index 90ff995..7209979 100644 Binary files a/src/gui/res/lang/gui_ja-JP.qm and b/src/gui/res/lang/gui_ja-JP.qm differ diff --git a/src/gui/res/lang/gui_ja-JP.ts b/src/gui/res/lang/gui_ja-JP.ts index e7ff5bd..1d80d2d 100644 --- a/src/gui/res/lang/gui_ja-JP.ts +++ b/src/gui/res/lang/gui_ja-JP.ts @@ -1,12 +1,14 @@ - + + + AboutDialogBase About Barrier - Barrierについて + Barrier について - + <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -26,22 +28,30 @@ Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> The Barrier GUI is based on QSynergy by Volker Lanz.<br /><br /> Visit our website for help and info (symless.com). </p> - + <p> +キーボードとマウスの共有ソフトウェアです。クロスプラットフォームでオープンソースです。<br /><br /> +Copyright © 2012-2016 Symless Ltd.<br /> +Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br /><br /> +Barrier は GNU General Public (GPLv2). のライセンスで公開されています。<br /><br /> +Barrier のベースは CosmoSynergy (Richard Lee, Adam Feder 開発) です。<br /> +Barrier の GUI は QSynergy (Volker Lanz 開発) がベースです。<br /><br /> +Visit our website for help and info (symless.com). +</p> Unknown - 不明 + 不明 Version: - バージョン: + バージョン: &Ok - OK + OK(&O) @@ -49,97 +59,97 @@ Visit our website for help and info (symless.com). Configure Action - 動作を構成 + アクションの設定 Choose the action to perform - 実行する動作を選択 + 実行するアクションを選択 Press a hotkey - ホットキーを押す + ホットキーを押す Release a hotkey - ホットキーを離す + ホットキーを離す Press and release a hotkey - ホットキーを押して離す + ホットキーを押して離す only on these screens - これらの画面だけ + これらのモニターのみにする Switch to screen - 画面に切り替え + モニターを切り替え Switch in direction - 切り替える方向 + 切り替える方向 left - + right - + up - + down - + Lock cursor to screen - カーソルを画面に限定 + カーソルを画面に限定 toggle - 切り替え + 切り替え on - オン + オン off - オフ + オフ This action is performed when - この動作を実行する時: + このアクション実行のタイミング the hotkey is pressed - ホットキーを押したとき + ホットキーを押したとき the hotkey is released - ホットキーを離したとき + ホットキーを離したとき @@ -147,7 +157,7 @@ Visit our website for help and info (symless.com). Dialog - + ダイアログ @@ -157,7 +167,7 @@ Visit our website for help and info (symless.com). Ignore auto connect clients - + クライアントの自動接続を無視 @@ -165,12 +175,30 @@ Visit our website for help and info (symless.com). Hotkey - ホットキー + ホットキー Enter the specification for the hotkey: - ホットキーの指定方法を入力してください: + ホットキーを入力してください: + + + + LogWindowBase + + + Log - Barrier + ログ - Barrier + + + + &Clear Log + ログ消去(&C) + + + + &Hide + 閉じる @@ -178,189 +206,193 @@ Visit our website for help and info (symless.com). &Start - 開始 + 開始(&S) &File - ファイル + ファイル(&F) &Edit - 編集 + 編集(&E) &Window - ウィンドウ + ウィンドウ(&W) &Help - ヘルプ + ヘルプ(&H) <p>Your version of Barrier is out of date. Version <b>%1</b> is now available to <a href="%2">download</a>.</p> <p>Version %1 is now available, <a href="%2">visit website</a>.</p> - + <p>お使いの Barrier のバージョンが古くなっています。 新しいバージョン <b>%1</b> が<a href="%2">ダウンロード</a>できます。</p> Program can not be started - プログラムを開始できません + プログラムを開始できません The executable<br><br>%1<br><br>could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program. - 実行ファイル<br><br>%1<br><br>は存在しますが、開始できませんでした。このプログラムを動作させる十分な権限があるかどうか確認してください。 + 実行ファイル<br><br>%1<br><br>は存在しますが、開始できませんでした。このプログラムを動作させる十分な権限があるかどうか確認してください。 Barrier client not found - Barrierクライアントが見つかりません + Barrier のクライアントが見つかりません The executable for the barrier client does not exist. - Barrierクライアントの実行ファイルが存在しません。 + Barrier のクライアントの実行ファイルが存在しません。 Hostname is empty - ホスト名が入力されていません + ホスト名がありません Please fill in a hostname for the barrier client to connect to. - Barrierクライアントで接続するホスト名を入力してください。 + Barrier のクライアントが接続するホスト名を入力してください。 Cannot write configuration file - 構成ファイルに書き込めません + 構成ファイルに書き込めません The temporary configuration file required to start barrier can not be written. - Barrierの開始に必要な一時的な構成ファイルを書き込めません。 + Barrier の開始に必要な一時的な構成ファイルに書き込めません。 Configuration filename invalid - 構成ファイル名が正しくありません。 + 構成ファイルのファイル名が正しくありません You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now? - Barrierサーバーの正しい構成ファイルを書き込んでいません。今、構成ファイルを閲覧しますか? + Barrier のサーバー用の正しい構成ファイルではありません。構成ファイルを開きますか? Barrier server not found - Barrierサーバーが見つかりません + Barrier のサーバーが見つかりません The executable for the barrier server does not exist. - Barrierサーバーの実行ファイルが存在しません。 + Barrier のサーバーの実行ファイルが存在しません。 Barrier terminated with an error - Barrierはエラーで終了しました + Barrier はエラーで終了しました Barrier terminated unexpectedly with an exit code of %1.<br><br>Please see the log output for details. - Barrierは予期しない終了コード%1で終了しました。<br><br>詳細はログの出力を参照してください。 + Barrier は予期しない終了コード %1 で終了しました。<br><br>詳細はログの出力を参照してください。 &Stop - 停止 + 停止(&S) Please add the server (%1) to the grid. - + グリッドにサーバー (%1) を追加してください。 Please drag the new client screen (%1) to the desired position on the grid. - + グリッド上の希望する位置に新しいクライアントのモニタ (%1) をドラッグしてください。 Failed to detect system architecture. - + システムアーキテクチャの検出に失敗しました。 Cancel - + キャンセル Failed to download Bonjour installer to location: %1 - + Bonjour のインストーラーのダウンロードに失敗。場所: %1 Do you want to enable auto config and install Bonjour? This feature helps you establish the connection. - + 自動構成を有効にし、Bonjour をインストールしますか? + +この機能は接続を確立するためのお手伝いをします。 Auto config feature requires Bonjour. Do you want to install Bonjour? - + 自動構成の機能には Bonjour が必要です。 + +Bonjour をインストールしますか? Barrier is starting. - Barrierを開始中です。 + Barrier を開始中です。 Barrier is running. - Barrierは動作中です。 + Barrier は動作中です。 Barrier is not running. - Barrierは動作していません。 + Barrier は動作していません。 Unknown - 不明 + 不明 Barrier - Barrier + Barrier Browse for a barriers config file - Barrierの設定ファイルを参照 + Barrier の構成ファイルを参照 Barrier is now connected, You can close the config window. Barrier will remain connected in the background. - + ただいま Barrier は接続されました。設定ウインドウを閉じることができます。それでもバックグラウンドで接続を維持します。 Security question - + セキュリティの質問 @@ -368,25 +400,31 @@ Do you want to install Bonjour? %1 -This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user). +This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user). To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No. - + このフィンガープリントを信頼しますか? + +%1 + +これはサーバーのフィンガープリントです。 このフィンガープリントを、サーバー側のモニタに表示されているフィンガープリントとで確認してください。その2つが一致しない場合、接続したいサーバーではありません (悪意のあるサーバの可能性があります)。 + +「はい」で、今後の接続でも、このフィンガープリントを自動的に信頼します。「いいえ」で、このフィンガープリントを拒否しサーバーから接続を切断します。 Save configuration as... - 設定に名前をつけて保存 + 構成設定に名前をつけて保存 Save failed - 保存できませんでした + 保存できませんでした Could not save configuration to file. - 設定をファイルに保存できませんでした + 構成設定をファイルに保存できませんでした。 @@ -394,168 +432,193 @@ To automatically trust this fingerprint for future connections, click Yes. To re Barrier - Barrier + Barrier - Ser&ver (share this computer's mouse and keyboard): - + Ser&ver (share this computer's mouse and keyboard): + サーバー (このコンピューターのキーボードとマウスを共有する)(&V): Screen name: - 画面の名前: + モニター名: &Server IP: - サーバー IP: + サーバー IP (&S): &Start - 開始 + 開始(&S) Use existing configuration: - 既存の設定を使用 + 既存の構成設定を使用: &Configuration file: - 設定ファイル: + 構成ファイル:(&C): &Browse... - 参照 + 参照(&B)... Configure interactively: - インタラクティブモードで設定: + 手動で構成を設定: &Configure Server... - サーバーを設定 + サーバーの構成設定(&S)... Ready - 準備完了 + 準備完了 Log - ログ + ログ &Reload - 適用 + 適用(&R) IP addresses: - IPアドレス: + IPアドレス: Fingerprint: - + フィンガープリント: - &Client (use another computer's mouse and keyboard): - + &Client (use another computer's mouse and keyboard): + クライアント (ほかのコンピューターのマウスとキーボードを使う)(&C): Auto config - + 自動構成 &About Barrier... - Barrierについて... + Barrier について(&A)... &Quit - 終了 + 終了(&Q) Quit - 終了 + 終了(&Q) Run - 実行 + 開始(&R) S&top - 停止 + 停止(&S) Stop - 停止 + 停止(&S) S&how Status - 状態を表示 + 状態を表示(&S) &Hide - 隠す + 隠す(&H) Hide - 隠す + 隠す(&H) &Show - 表示する + 表示(&S) Show - 表示する + 表示(&S) Save configuration &as... - 設定に名前をつけて保存 + 構成設定に名前をつけて保存 Save the interactively generated server configuration to a file. - インタラクティブモードで生成したサーバ設定をファイルに保存 + 手動で設定したサーバーの構成をファイルに保存する。 Settings - 設定 + 設定 Edit settings - 設定を編集 + 設定を編集 Run Wizard - ウィザードを実行する + ウィザードを実行 + + + + S&ave configuration + 構成設定の保存(&A) + + + + Change &Settings + 設定の変更(&S) + + + + Show &Log + ログを表示(&L) + + + + Show Log + ログを表示(&L) + + + + SSL Fingerprint: + SSLフィンガープリント: @@ -563,7 +626,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Unnamed - 名前なし + 名前なし @@ -571,28 +634,29 @@ To automatically trust this fingerprint for future connections, click Yes. To re Failed to get plugin directory. - + プラグインのフォルダ取得に失敗しました。 Failed to get profile directory. - + プロファイルのフォルダ取得に失敗しました。 - Failed to download plugin '%1' to: %2 + Failed to download plugin '%1' to: %2 %3 - + プラグイン「%1」の %2 へのダウンロードに失敗 +%3 Could not get Windows architecture type. - + Windows のアーキテクチャの種類を取得できません。 Could not get Linux architecture type. - + Linux のアーキテクチャの種類を取得できません。 @@ -600,66 +664,66 @@ To automatically trust this fingerprint for future connections, click Yes. To re Setup Barrier - Barrierのセットアップ + Barrier のセットアップ Please wait... - + お待ちください... Error: %1 - + エラー: %1 Setup complete. - + セットアップは完了しました。 - Downloading '%1' plugin (%2/%3)... - + Downloading '%1' plugin (%2/%3)... + プラグイン「%1」をダウンロード中 (%2/%3)... Plugins installed successfully. - + プラグインは正常にインストールされました。 Generating SSL certificate... - + SSL 証明書を生成中... Downloading plugin: %1 (1/%2) - + プラグインをダウンロード中: %1 (1/%2) Getting plugin list... - + プラグイン一覧を取得中... QObject - Barrier Configurations (*.sgc);;All files (*.*) - Barrierの構成(*.sgc);;すべてのファイル(*.*) + Barrier Configurations (*.sgc) + Barrier 構成設定 (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrierの構成(*.conf);;すべてのファイル(*.*) + Barrier Configurations (*.conf) + Barrier 構成設定 (*.conf) System tray is unavailable, quitting. - タスクトレイを利用できません。終了します。 + システムトレイを利用できません。終了します。 @@ -667,22 +731,22 @@ To automatically trust this fingerprint for future connections, click Yes. To re Screen name is empty - 画面の名前が空です。 + モニター名がありません The screen name cannot be empty. Please either fill in a name or cancel the dialog. - 画面の名前を空にすることはできません。名前を入力するかダイアログをキャンセルしてください。 + モニター名を空にはできません。名前を入力するか、ダイアログをキャンセルしてください。 Screen name matches alias - 画面の名前は別名と一致 + モニター名は別名に一致 The screen name cannot be the same as an alias. Please either remove the alias or change the screen name. - 画面の名前を別名と同じにすることは出来ません。別名を削除するか画面の名前を変更してください。 + モニター名を別名と同じにできません。別名を削除するか、モニター名を変更してください。 @@ -690,37 +754,37 @@ To automatically trust this fingerprint for future connections, click Yes. To re Screen Settings - 画面の設定 + モニターの設定 Screen &name: - 画面の名前 + モニター名(&N): A&liases - 別名 + 別名(&A) &Add - 追加 + 追加(&A) &Remove - 削除 + 除去(&R) &Modifier keys - 修飾キー + 修飾キー(&M) &Shift: - シフト + Shift(&S): @@ -729,7 +793,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Shift - シフト + @@ -738,7 +802,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Ctrl - コントロール + @@ -747,7 +811,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Alt - Alt + @@ -756,7 +820,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Meta - メタ + メタ @@ -765,7 +829,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Super - スーパー + スーパー @@ -774,82 +838,87 @@ To automatically trust this fingerprint for future connections, click Yes. To re None - なし + なし &Ctrl: - &undefinedCtrl: + Ctrl (&C): Al&t: - &undefinedl&t: + Alt (&T): M&eta: - &undefined&eta: + メタ (&E): S&uper: - &undefined&uper: + スーパー (&S): &Dead corners - 無効とする角 + 無効とする隅(&D) Top-left - 左上 + 左上 Top-right - 右上 + 右上 Bottom-left - 左下 + 左下 Bottom-right - 右下 + 右下 Corner Si&ze: - 角の大きさ + 隅の大きさ(&Z): &Fixes - 修正 + 修正(&F) Fix CAPS LOCK key - CAPSロックキーを固定 + CAPS ロックキーを固定 Fix NUM LOCK key - NUMロックキーを固定 + NUM ロックキーを固定 Fix SCROLL LOCK key - SCROLLロックキーを固定 + SCROLL ロックキーを固定 Fix XTest for Xinerama - Xinerama向けにXTestを修正 + Xinerama 向けに XTest を修正 + + + + Fix Preserve Focus + フォーカスの維持を修正 @@ -857,7 +926,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re <center>Screen: <b>%1</b></center><br>Double click to edit settings<br>Drag screen to the trashcan to remove it - <center>画面: <b>%1</b></center><br>ダブルクリックで設定を編集<br>削除するときは画面をゴミ箱にドラッグします + <center>モニター: <b>%1</b></center><br>ダブルクリックで設定を編集<br>削除するときは画面をゴミ箱にドラッグします @@ -865,7 +934,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Configure server - + サーバーの構成設定 @@ -873,168 +942,178 @@ To automatically trust this fingerprint for future connections, click Yes. To re Server Configuration - サーバーの構成 + サーバーの構成設定 Screens and links - 画面とリンク + モニタの結びつき Drag a screen from the grid to the trashcan to remove it. - 削除する時はグリッド内の画面をゴミ箱にドラッグしてください。 + 削除するには、グリッド内のモニターをゴミ箱にドラッグしてください。 Configure the layout of your barrier server configuration. - サーバ構成の配置を設定する + Barrier のサーバー構成の配置を設定します。 Drag this button to the grid to add a new screen. - 新規画面の追加はこのボタンをグリッド内にドラッグします。 + 新規モニターの追加するには、このボタンをグリッド内にドラッグします。 Drag new screens to the grid or move existing ones around. Drag a screen to the trashcan to delete it. Double click on a screen to edit its settings. - 新規画面をグリッド内にドラッグするか既存画面を移動してください。 -画面をゴミ箱にドラッグすると削除します。 -設定を編集する場合は画面上でダブルクリックしてください。 + 新規モニターをグリッド内にドラッグするか、既存のモニターを移動してください。 +削除するには、モニターをゴミ箱にドラッグします。 +モニターの設定を編集するには、モニターをダブルクリックします。 Hotkeys - ホットキー + ホットキー &Hotkeys - ホットキー + ホットキー(&H) &New - 新規 + 新規(&N) &Edit - 編集 + 編集(&E) &Remove - 削除 + 除去(&R) A&ctions - アクション + アクション(&A) Ne&w - 新規 + 新規(&W) E&dit - 編集 + 編集(&D) Re&move - 削除 + 除去(&M) Advanced server settings - サーバーの詳細な設定 + サーバーの詳細設定 &Switch - 切り替え + 切り替え(&S) Switch &after waiting - 次の時間の後切り替え + 次の時間の後切り替え(&A) ms - ミリ秒 + ミリ秒 Switch on double &tap within - 次の時間内のダブルタップで切り替え + 次の時間内のダブルタップで切り替え(&T) &Options - オプション + オプション(&O) &Check clients every - クライアント確認頻度 + クライアント確認頻度(&C) Use &relative mouse moves - マウスの相対的な動きを使用 + 相対的なマウスの移動を使用(&R) S&ynchronize screen savers - スクリーンセーバーの同期 + スクリーンセーバーの同期(&Y) - Don't take &foreground window on Windows servers - Windowsサーバでウィンドウを前面に表示しない + Don't take &foreground window on Windows servers + Windows サーバ上ではウィンドウを前面に表示しない(&F) Ignore auto config clients - + 自動構成のクライアントを無視 &Dead corners - 無効とする角 + 無効とする隅(&D) To&p-left - 左上 + 左上(&P) Top-rig&ht - 右上 + 右上(&H) &Bottom-left - 左下 + 左下(&B) Bottom-ri&ght - 右下 + 右下(&G) Cor&ner Size: - 隅の大きさ: + 隅の大きさ(&N): + + + + Enable drag and drop file transfers + ドラッグアンドドロップでファイル転送する + + + + Enable clipboard sharing + クリップボードを共有 @@ -1042,20 +1121,20 @@ Double click on a screen to edit its settings. Save log file to... - ログファイルの保存先 + ログファイルの保存先 Elevate Barrier - Barrierの権限昇格 + Barrier の権限昇格 Are you sure you want to elevate Barrier? This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to. - 本当に Barrier を昇格させてよろしいですか? -これにより昇格されたプロセスや UAC dialog と、Barrier とが互いに作用しあうことができるようになる反面、昇格されていないプロセスとの間で問題を生じることもあり得ます。確かに必要であると判断できる場合にのみ Barrier の昇格を行ってください。 + Barrier を昇格させてもよろしいですか? +この許可により Barrier は権限を持つプロセスや UAC ダイアログやりとりできます。一方で、昇格されていないプロセスとのやり取りに問題が起こることもあります。確かに必要である場合にのみ Barrier を昇格させてください。 @@ -1063,107 +1142,162 @@ This allows Barrier to interact with elevated processes and the UAC dialog, but Settings - 設定 + 設定 Sc&reen name: - スクリーン名: + モニター名(&R): P&ort: - ポート: + ポート(&O): &Interface: - インターフェース: + インターフェース(&I): Elevate mode - + 特権の状態 &Hide on startup - + 起動時に隠す(&H) &Network Security - + ネットワークのセキュリティ(&N) Use &SSL encryption (unique certificate) - + SSL 暗号化を使用 (固有の証明書)(&S) Logging - ログ + ログ記録 &Logging level: - ログレベル: + ログ対象(&L): Log to file: - ログ記録先ファイル: + ログ記録ファイル: Browse... - 参照... + 参照... Error - エラー + エラー &Language: - 言語 + 言語(&L): &Miscellaneous - その他 + その他(&M) Warning - 警告 + 警告 Note - 通知 + 通知 Info - 情報 + 情報 Debug - デバッグ情報 + デバッグ情報 Debug1 - デバッグ情報1 + デバッグ情報1 Debug2 - デバッグ情報2 + デバッグ情報2 + + + + General + 一般 + + + + Elevate + 権限昇格 + + + + Specify when the Barrier service should run at an elevated privilege level + Barrier のサービスを昇格した権限で実行するタイミングの指定 + + + + As Needed + 必要に応じて + + + + Always + 常に + + + + Never + なし + + + + Minimize to System &Tray + システムトレイに最小化(&T) + + + + Networking + ネットワーク + + + + &Address: + アドレス(&A): + + + + Enable &SSL + SSLを使用(&S) + + + + Start &Barrier on startup + 起動時に Barrier を開始する (&B) @@ -1171,17 +1305,17 @@ This allows Barrier to interact with elevated processes and the UAC dialog, but Setup Barrier - Barrierのセットアップ + Barrier のセットアップ Please select an option. - オプションを選択してください。 + オプションを選択してください。 Please enter your email address and password. - メールアドレスとパスワードを入力してください。 + メールアドレスとパスワードを入力してください。 @@ -1189,85 +1323,93 @@ This allows Barrier to interact with elevated processes and the UAC dialog, but Setup Barrier - Barrierのセットアップ + Barrier のセットアップ Welcome - ようこそ + ようこそ Thanks for installing Barrier! - Barrierをインストールしていただき、ありがとうございます! + Barrier をインストールしていただき、ありがとうございます! - Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux). - Barrierは複数のコンピュータ間のマウスとキーボードを簡単に共有することができるフリーのオープンソースソフトウェアです。あるコンピュータの画面の端にマウスを移動すると別のコンピュータの画面に移ります。クリップボードを共有することもできます。必要なのは ネットワーク接続だけです。 BarrierはクロスプラットフォームでWindows, Mac OS X, Linux上で動作します。 + Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux). + Barrier によって、複数のデスクトップパソコン間でマウスとキーボードを簡単に共有することができます。そしてこれは無料でオープンソースのソフトウェアです。あるコンピュータの画面の端にマウスを移動するだけで、別のコンピュータの画面に移ることができます。クリップボードを共有することもできます。必要なのはネットワーク接続だけです。 Barrier はクロスプラットフォームで Windows, Mac OS X, Linux で動作します。 Activate - + アクティベート &Activate now... - + 今すぐアクティベート(&A)... Email: - + 電子メール: Password: - + パスワード: <a href="https://symless.com/account/reset/">Forgot password</a> - + <a href="https://symless.com/account/reset/">パスワードを忘れた場合</a> &Skip activation - + アクティベートを省略(&S) - &Server (share this computer's mouse and keyboard) - + &Server (share this computer's mouse and keyboard) + サーバー (このコンピューターのキーボードとマウスを共有する)(&V) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.</span></p></body></html> - +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">使いたいマウスとキーボードがこのパソコンに接続されている場合、こちらを選択することで、別のパソコンのモニタへとマウスを移動することができます。サーバーは1つしか設置できません。</span></p></body></html> - &Client (use another computer's mouse and keyboard) - + &Client (use another computer's mouse and keyboard) + クライアント (ほかのコンピューターのマウスとキーボードを使う)(&C) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.</span></p></body></html> - +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">既にサーバーを設置してある場合、このパソコンをサーバーに接続されたマウスとキーボードで制御できます。複数の数のクライアントを配置できます。</span></p></body></html> Server or Client? - サーバーまたはクライアント + サーバーにしますか?またはクライアントですか? @@ -1275,22 +1417,22 @@ p, li { white-space: pre-wrap; } Failed to get profile directory. - + プロファイルのフォルダ取得に失敗しました。 SSL certificate generated. - + SSL 証明書を生成しました。 SSL fingerprint generated. - + SSL フィンガープリントを生成しました。 Failed to find SSL fingerprint. - + SSL フィンガープリントが見つかりませんでした。 @@ -1298,7 +1440,7 @@ p, li { white-space: pre-wrap; } Unknown - 不明 + 不明 @@ -1308,19 +1450,21 @@ p, li { white-space: pre-wrap; } An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details. %1 - + サインイン時にエラーが発生しました。ヘルプデスクにお問い合わせの上、以下の詳細を報告してください。 + +%1 Login failed, invalid email or password. - ログインは失敗しました。メールアドレスまたはパスワードが無効です。 + ログインに失敗しました。メールアドレスまたはパスワードが無効です。 Login failed, an error occurred. %1 - エラーが発生し、ログインが失敗しました。 + エラーが発生し、ログインに失敗しました。 %1 @@ -1330,8 +1474,8 @@ p, li { white-space: pre-wrap; } Server response: %1 - エラーが発生し、ログインが失敗しました。 -サーバの応答: + エラーが発生し、ログインに失敗しました。 +サーバの応答: %1 @@ -1339,19 +1483,23 @@ Server response: An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details. %1 - + プラグイン一覧の要求時にエラーが発生しました。ヘルプデスクにお問い合わせの上、以下の詳細を報告してください。 + +%1 Get plugin list failed, invalid user email or password. - + プラグイン一覧の取得に失敗しました。メールアドレスまたはパスワードが無効です。 Get plugin list failed, an error occurred. %1 - + エラーが発生し、プラグイン一覧の取得に失敗しました。 + +%1 @@ -1360,7 +1508,11 @@ Server response: Server response: %1 - + エラーが発生し、プラグイン一覧の取得に失敗しました。 + +サーバの応答: + +%1 @@ -1368,44 +1520,44 @@ Server response: zeroconf server detected: %1 - + ゼロ構成のサーバーを検出: %1 zeroconf client detected: %1 - + ゼロ構成のクライアントを検出: %1 Zero configuration service - + ゼロ構成のサービス Error code: %1. - + エラーコード: %1 Unable to start the zeroconf: %1. - + ゼロ構成は開始できません: %1 Barrier - Barrier + Barrier Failed to get local IP address. Please manually type in server address on your clients - + ローカル IP アドレスの取得に失敗しました。クライアント上でサーバーのアドレスを手動で入力してください %1 - + - \ No newline at end of file + diff --git a/src/gui/res/lang/gui_ko.qm b/src/gui/res/lang/gui_ko.qm index 06612cb..54b9461 100644 Binary files a/src/gui/res/lang/gui_ko.qm and b/src/gui/res/lang/gui_ko.qm differ diff --git a/src/gui/res/lang/gui_ko.ts b/src/gui/res/lang/gui_ko.ts index 8c477fc..46f7223 100644 --- a/src/gui/res/lang/gui_ko.ts +++ b/src/gui/res/lang/gui_ko.ts @@ -204,7 +204,7 @@ Visit our website for help and info (symless.com). <p>Your version of Barrier is out of date. Version <b>%1</b> is now available to <a href="%2">download</a>.</p> <p>Version %1 is now available, <a href="%2">visit website</a>.</p> - <p>사용 중인 시너지는 최신 버전이 아닙니다. 새 버전(<b>%1</b>)을 <a href="%2">다운로드</a> 받을 수 있습니다.</p> + <p>사용 중인 배리어는 최신 버전이 아닙니다. 새 버전(<b>%1</b>)을 <a href="%2">다운로드</a> 받을 수 있습니다.</p> @@ -600,7 +600,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Setup Barrier - 시너지 설정 + 배리어 설정 @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Barrier 설정파일 (*.sgc);;모든 파일 (*.*) + All files (*.*) + 모든 파일 (*.*) + + + + Barrier Configurations (*.sgc) + Barrier 설정파일 (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrier 설정파일 (*.conf);;모든 파일 (*.*) + Barrier Configurations (*.conf) + Barrier 설정파일 (*.conf) @@ -1171,7 +1176,7 @@ This allows Barrier to interact with elevated processes and the UAC dialog, but Setup Barrier - 시너지 설정 + 배리어 설정 @@ -1189,7 +1194,7 @@ This allows Barrier to interact with elevated processes and the UAC dialog, but Setup Barrier - 시너지 설정 + 배리어 설정 @@ -1199,7 +1204,7 @@ This allows Barrier to interact with elevated processes and the UAC dialog, but Thanks for installing Barrier! - 시너지를 설치하여 주셔서 감사합니다. + 배리어를 설치하여 주셔서 감사합니다. diff --git a/src/gui/res/lang/gui_lt.ts b/src/gui/res/lang/gui_lt.ts index 66976b1..58e9101 100644 --- a/src/gui/res/lang/gui_lt.ts +++ b/src/gui/res/lang/gui_lt.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_lv.ts b/src/gui/res/lang/gui_lv.ts index 27b74f0..4618c05 100644 --- a/src/gui/res/lang/gui_lv.ts +++ b/src/gui/res/lang/gui_lv.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_mr.ts b/src/gui/res/lang/gui_mr.ts index b91bd19..a646004 100644 --- a/src/gui/res/lang/gui_mr.ts +++ b/src/gui/res/lang/gui_mr.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_nl-NL.qm b/src/gui/res/lang/gui_nl-NL.qm index 102cec8..ac9a67c 100644 Binary files a/src/gui/res/lang/gui_nl-NL.qm and b/src/gui/res/lang/gui_nl-NL.qm differ diff --git a/src/gui/res/lang/gui_nl-NL.ts b/src/gui/res/lang/gui_nl-NL.ts index 5c5b490..4ea0ee8 100644 --- a/src/gui/res/lang/gui_nl-NL.ts +++ b/src/gui/res/lang/gui_nl-NL.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Barrier Configuratie (*.sgc);;Alle bestanden (*.*) + All files (*.*) + Alle bestanden (*.*) + + + + Barrier Configurations (*.sgc) + Barrier Configuratie (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrier configuratie (*.conf);;Alle bestanden (*.*) + Barrier Configurations (*.conf) + Barrier configuratie (*.conf) @@ -1329,7 +1334,7 @@ p, li { white-space: pre-wrap; } Server response: %1 - Inloggen mislukt, er is een fout opgetreden. + Inloggen mislukt, er is een fout opgetreden. Foutmelding: %1 diff --git a/src/gui/res/lang/gui_no.qm b/src/gui/res/lang/gui_no.qm index 903ae48..c4d1d81 100644 Binary files a/src/gui/res/lang/gui_no.qm and b/src/gui/res/lang/gui_no.qm differ diff --git a/src/gui/res/lang/gui_no.ts b/src/gui/res/lang/gui_no.ts index d27a620..0ccd61e 100644 --- a/src/gui/res/lang/gui_no.ts +++ b/src/gui/res/lang/gui_no.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Barrier-oppsett (*.sgc);;Alle filer (*.*) + All files (*.*) + Alle filer (*.*) + + + + Barrier Configurations (*.sgc) + Barrier-oppsett (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrier-oppsett (*.conf);;Alle filer (*.*) + Barrier Configurations (*.conf) + Barrier-oppsett (*.conf) diff --git a/src/gui/res/lang/gui_pes-IR.ts b/src/gui/res/lang/gui_pes-IR.ts index 31d7a67..3aaffed 100644 --- a/src/gui/res/lang/gui_pes-IR.ts +++ b/src/gui/res/lang/gui_pes-IR.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_pl-PL.qm b/src/gui/res/lang/gui_pl-PL.qm index 83e7352..65a2116 100644 Binary files a/src/gui/res/lang/gui_pl-PL.qm and b/src/gui/res/lang/gui_pl-PL.qm differ diff --git a/src/gui/res/lang/gui_pl-PL.ts b/src/gui/res/lang/gui_pl-PL.ts index 6fb96a0..7262a2a 100644 --- a/src/gui/res/lang/gui_pl-PL.ts +++ b/src/gui/res/lang/gui_pl-PL.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Konfiguracje Barrier (*.sgc);;Wszystkie pliki (*.*) + All files (*.*) + Wszystkie pliki (*.*) + + + + Barrier Configurations (*.sgc) + Konfiguracje Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Konfiguracje Barrier (*.conf);;Wszystkie pliki (*.*) + Barrier Configurations (*.conf) + Konfiguracje Barrier (*.conf) @@ -1320,7 +1325,7 @@ p, li { white-space: pre-wrap; } Login failed, an error occurred. %1 - Logowanie nie powiodło się, wystąpił błąd. + Logowanie nie powiodło się, wystąpił błąd. %1 @@ -1330,7 +1335,7 @@ p, li { white-space: pre-wrap; } Server response: %1 - Logowanie nie powiodło się, wystąpił błąd. + Logowanie nie powiodło się, wystąpił błąd. Odpowiedź serwera: %1 diff --git a/src/gui/res/lang/gui_pt-BR.qm b/src/gui/res/lang/gui_pt-BR.qm index 1deecfd..0825fcf 100644 Binary files a/src/gui/res/lang/gui_pt-BR.qm and b/src/gui/res/lang/gui_pt-BR.qm differ diff --git a/src/gui/res/lang/gui_pt-BR.ts b/src/gui/res/lang/gui_pt-BR.ts index 6245bca..6e85dd8 100644 --- a/src/gui/res/lang/gui_pt-BR.ts +++ b/src/gui/res/lang/gui_pt-BR.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Configurações do Barrier (*.sgc);;Todos os arquivos (*.*) + All files (*.*) + Todos os arquivos (*.*) + + + + Barrier Configurations (*.sgc) + Configurações do Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Configurações do Barrier (*.conf);;Todos os arquivos (*.*) + Barrier Configurations (*.conf) + Configurações do Barrier (*.conf) diff --git a/src/gui/res/lang/gui_pt-PT.qm b/src/gui/res/lang/gui_pt-PT.qm index 987d8ab..e824cce 100644 Binary files a/src/gui/res/lang/gui_pt-PT.qm and b/src/gui/res/lang/gui_pt-PT.qm differ diff --git a/src/gui/res/lang/gui_pt-PT.ts b/src/gui/res/lang/gui_pt-PT.ts index 03fc40d..9ae8899 100644 --- a/src/gui/res/lang/gui_pt-PT.ts +++ b/src/gui/res/lang/gui_pt-PT.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Configuração de barrier (*.sgc);;Todos os ficheiros (*.*) + All files (*.*) + Todos os ficheiros (*.*) + + + + Barrier Configurations (*.sgc) + Configuração de barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Configuração de barrier (*.conf);;Todos os ficheiros (*.*) + Barrier Configurations (*.conf) + Configuração de barrier (*.conf)) diff --git a/src/gui/res/lang/gui_ro.qm b/src/gui/res/lang/gui_ro.qm index 6d1fdd2..1a69dad 100644 Binary files a/src/gui/res/lang/gui_ro.qm and b/src/gui/res/lang/gui_ro.qm differ diff --git a/src/gui/res/lang/gui_ro.ts b/src/gui/res/lang/gui_ro.ts index f82d128..c3c9c43 100644 --- a/src/gui/res/lang/gui_ro.ts +++ b/src/gui/res/lang/gui_ro.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Configurații Barrier (*.sgc);;Toate Fișierele (*.*) + All files (*.*) + Toate Fișierele (*.*) + + + + Barrier Configurations (*.sgc) + Configurații Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Configurații Barrier (*.conf);;Toate Fișierele (*.*) + Barrier Configurations (*.conf) + Configurații Barrier (*.conf) diff --git a/src/gui/res/lang/gui_ru.qm b/src/gui/res/lang/gui_ru.qm index 30ed0a7..ef3852c 100644 Binary files a/src/gui/res/lang/gui_ru.qm and b/src/gui/res/lang/gui_ru.qm differ diff --git a/src/gui/res/lang/gui_ru.ts b/src/gui/res/lang/gui_ru.ts index 411272f..f984240 100644 --- a/src/gui/res/lang/gui_ru.ts +++ b/src/gui/res/lang/gui_ru.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Конфигурация Barrier (*.sgc);;Все файлы (*.*) + All files (*.*) + Все файлы (*.*) + + + + Barrier Configurations (*.sgc) + Конфигурация Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Конфигурация Barrier (*.conf);;Все файлы (*.*) + Barrier Configurations (*.conf) + Конфигурация Barrier (*.conf) @@ -1320,7 +1325,7 @@ p, li { white-space: pre-wrap; } Login failed, an error occurred. %1 - Войти не удалось, произошла ошибка. + Войти не удалось, произошла ошибка. %1 @@ -1331,7 +1336,7 @@ p, li { white-space: pre-wrap; } Server response: %1 - Войти не удалось, произошла ошибка. + Войти не удалось, произошла ошибка. Ответ сервера: diff --git a/src/gui/res/lang/gui_si.ts b/src/gui/res/lang/gui_si.ts index 33597e9..9abc5df 100644 --- a/src/gui/res/lang/gui_si.ts +++ b/src/gui/res/lang/gui_si.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_sk-SK.qm b/src/gui/res/lang/gui_sk-SK.qm index acdf4f6..0e5efca 100644 Binary files a/src/gui/res/lang/gui_sk-SK.qm and b/src/gui/res/lang/gui_sk-SK.qm differ diff --git a/src/gui/res/lang/gui_sk-SK.ts b/src/gui/res/lang/gui_sk-SK.ts index 3e63795..0bbeacf 100644 --- a/src/gui/res/lang/gui_sk-SK.ts +++ b/src/gui/res/lang/gui_sk-SK.ts @@ -1,12 +1,14 @@ - + + + AboutDialogBase About Barrier - + O Barrier - + <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -26,22 +28,30 @@ Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> The Barrier GUI is based on QSynergy by Volker Lanz.<br /><br /> Visit our website for help and info (symless.com). </p> - + <p> +Aplikácia na zdieľanie klávesnice a myši. Podporuje viacero platforiem a má otvorený zdrojový kód.<br /><br /> +Autorské práva © 2012-2016 Symless Ltd.<br /> +Autorské práva © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br /><br /> +Barrier je vydaný pod licenciou GNU General Public License (GPLv2).<br /><br /> +Základom pre Barrier je CosmoSynergy od Richarda Lee-a a Adama Federa.<br /> +Barrier GUI je postavené na QSynergy od Volkera Lanza.<br /><br /> +Pre pomoc a ďalšie informácie navštívte našu webovú stránku (symless.com). +</p> Unknown - + Neznáma Version: - + Verzia: &Ok - + &Ok @@ -49,97 +59,97 @@ Visit our website for help and info (symless.com). Configure Action - + Nastaviť akciu Choose the action to perform - + Vyberte akciu, ktorá sa má vykonať Press a hotkey - + Stlačte klávesovú skratku Release a hotkey - + Uvoľnite klávesovú skratku Press and release a hotkey - + Stlačte a uvoľnite klávesovú skratku only on these screens - + iba na týchto obrazovkách Switch to screen - + Prepnúť na obrazovku Switch in direction - + Prepnúť v smere left - + doľava right - + doprava up - + hore down - + dole Lock cursor to screen - + Zamknúť kurzor na obrazovke toggle - + prepnúť on - + zap off - + vyp This action is performed when - + Táto akcia sa vykoná pri the hotkey is pressed - + stlačení klávesovej skratky the hotkey is released - + uvoľnení klávesovej skratky @@ -147,17 +157,17 @@ Visit our website for help and info (symless.com). Dialog - + Dialógové okno TextLabel - + TextovýPopis Ignore auto connect clients - + Ignorovať automatické pripojenia klientov @@ -165,12 +175,12 @@ Visit our website for help and info (symless.com). Hotkey - + Klávesová skratka Enter the specification for the hotkey: - + Vložte popis klávesovej skratky: @@ -178,189 +188,193 @@ Visit our website for help and info (symless.com). &Start - + S&pustiť &File - + &Súbor &Edit - + &Upraviť &Window - + &Okno &Help - + &Pomocník <p>Your version of Barrier is out of date. Version <b>%1</b> is now available to <a href="%2">download</a>.</p> <p>Version %1 is now available, <a href="%2">visit website</a>.</p> - + <p>Vaša verzia Barrier nie je aktuálna. Na <a href="%2">stiahnutie</a> je k dispozícii verzia <b>%1</b>.</p> Program can not be started - + Program sa nepodarilo spustiť The executable<br><br>%1<br><br>could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program. - + Súbor <br><br>%1<br><br>sa nepodarilo spustiť aj napriek tomu, že existuje. Prosím, skontrolujte, či máte dostatočné práva na spustenie tohto programu. Barrier client not found - + Nepodarilo sa nájsť klienta Barrier The executable for the barrier client does not exist. - + Spustiteľný súbor klienta barrier neexistuje. Hostname is empty - + Názov hostiteľa je prázdny Please fill in a hostname for the barrier client to connect to. - + Prosím, vyplňte názov hostiteľa, ku ktorému sa klient barrier má pripojiť. Cannot write configuration file - + Nepodarilo sa zapísať súbor s konfiguráciou The temporary configuration file required to start barrier can not be written. - + Nepodarilo sa zapísať do dočasného súboru, ktorý je potrebný pre spustenie barrier. Configuration filename invalid - + Neplatný názov konfiguračného súboru You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now? - + Nezadali ste platný konfiguračný súbor pre server barrier. Želáte si teraz vybrať súbor s konfiguráciou? Barrier server not found - + Server barrier nebol nájdený The executable for the barrier server does not exist. - + Spustiteľný súbor servera barrier neexistuje. Barrier terminated with an error - + Barrier skončil s chybou Barrier terminated unexpectedly with an exit code of %1.<br><br>Please see the log output for details. - + Barrier bolo neočakávane ukončený s chybovým kódom %1.<br><br>Podrobnosti nájdete v súbore s protokolom. &Stop - + &Zastaviť Please add the server (%1) to the grid. - + Pridajte, prosím, server (%1) do mriežky. Please drag the new client screen (%1) to the desired position on the grid. - + Ťahaním presuňte obrazovku nového klienta (%1) na požadovanú pozíciu v mriežke. Failed to detect system architecture. - + Nepodarilo sa zistiť systémovú architektúru. Cancel - + Zrušiť Failed to download Bonjour installer to location: %1 - + Nepodarilo sa stiahnuť inštalátor Bonjour do umiestnenia: %1 Do you want to enable auto config and install Bonjour? This feature helps you establish the connection. - + Želáte si povoliť autokonfiguráciu a nainštalovať Bonjour? + +Táto funkcia vám pomôže s nadväzovaním spojenia. Auto config feature requires Bonjour. Do you want to install Bonjour? - + Funkcia autokonfigurácie vyžaduje Bonjour. + +Želáte si nainštalovať Bonjour? Barrier is starting. - + Barrier sa spúšťa. Barrier is running. - + Barrier je spustený. Barrier is not running. - + Barrier nie je spustený. Unknown - + Neznáma Barrier - + Barrier Browse for a barriers config file - + Vybrať súbor s konfiguráciou barrier Barrier is now connected, You can close the config window. Barrier will remain connected in the background. - + Barrier je pripojený, môžete zavrieť okno s nastaveniami. Barrier zostane bežať na pozadí. Security question - + Bezpečnostná otázka @@ -368,25 +382,31 @@ Do you want to install Bonjour? %1 -This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user). +This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user). To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No. - + Dôverujete tomuto otlačku? + +%1 + +Toto je digitálny otlačok servera. Mali by ste ho porovnať s otlačkom na jednej z obrazoviek vášho servera. Ak nie sú rovnaké, potom sa pravdepodobne nepripájate na server, na ktorý chcete (môže sa jednať o zlomyselného používateľa). + +Ak chcete automaticky dôverovať tomuto otlačku pri ďalších spojeniach, kliknite na Áno. Pre odmietnutie tohto otlačku a odpojenie od servera kliknite na Nie. Save configuration as... - + Uložiť konfiguráciu ako... Save failed - + Chyba pri ukladaní Could not save configuration to file. - + Nepodarilo sa uložiť súbor s konfiguráciou do súboru. @@ -394,168 +414,168 @@ To automatically trust this fingerprint for future connections, click Yes. To re Barrier - + Barrier - Ser&ver (share this computer's mouse and keyboard): - + Ser&ver (share this computer's mouse and keyboard): + Ser&ver (zdieľať klávesnicu a myš tohto počítača): Screen name: - + Názov obrazovky: &Server IP: - + &IP servera: &Start - + S&pustiť Use existing configuration: - + Použiť existujúcu konfiguráciu: &Configuration file: - + Sú&bor s konfiguráciou: &Browse... - + &Prechádzať... Configure interactively: - + Interaktívna konfigurácia: &Configure Server... - + &Konfigurovať server... Ready - + Pripravené Log - + Protokol &Reload - + &Znovu načítať IP addresses: - + IP adresy: Fingerprint: - + Otlačok: - &Client (use another computer's mouse and keyboard): - + &Client (use another computer's mouse and keyboard): + &Klient (používa klávesnicu a myš iného počítača): Auto config - + Automatická konfigurácia &About Barrier... - + &O Barrier... &Quit - + &Ukončiť Quit - + Ukončiť Run - + Spustiť S&top - + Za&staviť Stop - + Zastaviť S&how Status - + Zo&braziť stav &Hide - + &Skryť Hide - + Skryť &Show - + Z&obraziť Show - + Zobraziť Save configuration &as... - + Uložiť konfiguráciu &ako... Save the interactively generated server configuration to a file. - + Uložiť interaktívne vygenerovanú konfiguráciu serveru do súboru. Settings - + Nastavenia Edit settings - + Upraviť nastavenia Run Wizard - + Spustiť sprievodcu @@ -563,7 +583,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Unnamed - + Bezmena @@ -571,28 +591,29 @@ To automatically trust this fingerprint for future connections, click Yes. To re Failed to get plugin directory. - + Nepodarilo sa získať adresár s pluginmi. Failed to get profile directory. - + Nepodarilo sa získať adresár s profilom. - Failed to download plugin '%1' to: %2 + Failed to download plugin '%1' to: %2 %3 - + Nepodarilo sa stiahnuť plugin '%1' do: %2 +%3 Could not get Windows architecture type. - + Nepodarilo sa zistiť typ architektúry Windows. Could not get Linux architecture type. - + Nepodarilo sa zistiť typ architektúry Linuxu. @@ -600,66 +621,71 @@ To automatically trust this fingerprint for future connections, click Yes. To re Setup Barrier - + Nastaviť Barrier Please wait... - + Čakajte prosím... Error: %1 - + Chyba: %1 Setup complete. - + Nastavovanie dokončené. - Downloading '%1' plugin (%2/%3)... - + Downloading '%1' plugin (%2/%3)... + Sťahujem plugin '%1' (%2/%3)... Plugins installed successfully. - + Pluginy úspešne nainštalované. Generating SSL certificate... - + Generujem SSL certifikát... Downloading plugin: %1 (1/%2) - + Sťahujem plugin: %1 (1/%2) Getting plugin list... - + Získavam zoznam pluginov... QObject - Barrier Configurations (*.sgc);;All files (*.*) - + All files (*.*) + Všetky súbory (*.*) + + + + Barrier Configurations (*.sgc) + Konfiguračné súbory Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - + Barrier Configurations (*.conf) + Konfiguračné súbory Barrier (*.conf) System tray is unavailable, quitting. - + Systémová oblasť nie je dostupná, končím. @@ -667,22 +693,22 @@ To automatically trust this fingerprint for future connections, click Yes. To re Screen name is empty - + Názov obrazovky je prázdny The screen name cannot be empty. Please either fill in a name or cancel the dialog. - + Názov obrazovky nemôže byť prázdny. Zadajte, prosím, názov alebo zrušte toto dialógové okno. Screen name matches alias - + Názov obrazovky koliduje s jej alternatívnym názvom The screen name cannot be the same as an alias. Please either remove the alias or change the screen name. - + Názov obrazovky nemôže byť rovnaký ako jej alternatívny názov. Prosím, odstráňte alternatívny názov alebo zmeňte názov obrazovky. @@ -690,37 +716,37 @@ To automatically trust this fingerprint for future connections, click Yes. To re Screen Settings - + Nastavenia obrazovky Screen &name: - + &Názov obrazovky: A&liases - + &Alternatívne názvy &Add - + &Pridať &Remove - + O&dstrániť &Modifier keys - + &Modifikátory klávesov &Shift: - + &Shift: @@ -729,7 +755,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Shift - + Shift @@ -738,7 +764,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Ctrl - + Ctrl @@ -747,7 +773,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Alt - + Alt @@ -756,7 +782,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Meta - + Meta @@ -765,7 +791,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Super - + Super @@ -774,82 +800,82 @@ To automatically trust this fingerprint for future connections, click Yes. To re None - + Žiadny &Ctrl: - + &Ctrl: Al&t: - + Al&t: M&eta: - + M&eta: S&uper: - + S&uper: &Dead corners - + &Mŕtve rohy Top-left - + Vľavo hore Top-right - + Vpravo hore Bottom-left - + Vľavo dole Bottom-right - + Vpravo dole Corner Si&ze: - + Veľ&kosť rohov: &Fixes - + &Opravy Fix CAPS LOCK key - + Opraviť správanie klávesu CAPS LOCK Fix NUM LOCK key - + Opraviť správanie klávesu NUM LOCK Fix SCROLL LOCK key - + Opraviť správanie klávesu SCROLL LOCK Fix XTest for Xinerama - + Opravit XTest pre Xinerama @@ -857,7 +883,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re <center>Screen: <b>%1</b></center><br>Double click to edit settings<br>Drag screen to the trashcan to remove it - + <center>Obrazovka: <b>%1</b></center><br>Dvojitým kliknutím upravíte nastavenia<br>Pre odstránenie obrazovky ju myšou presuňte do koša @@ -865,7 +891,7 @@ To automatically trust this fingerprint for future connections, click Yes. To re Configure server - + Konfigurovať server @@ -873,166 +899,168 @@ To automatically trust this fingerprint for future connections, click Yes. To re Server Configuration - + Konfigurácia servera Screens and links - + Obrazovky a prepojenia Drag a screen from the grid to the trashcan to remove it. - + Pre odstránenie obrazovky ju chyťte a presuňte z mriežky do koša. Configure the layout of your barrier server configuration. - + Nastaviť rozloženie konfigurácie vášho servera barrier. Drag this button to the grid to add a new screen. - + Pre vytvorenie novej obrazovky presuňte toto tlačidlo do mriežky. Drag new screens to the grid or move existing ones around. Drag a screen to the trashcan to delete it. Double click on a screen to edit its settings. - + Ťahaním pridajte nové obrazovky do mriežky alebo presuňte existujúce. +Presuňte obrazovku do koša, ak ju chcete odstrániť. +Dvojitým kliknutím na obrazovku upravíte jej nastavenia. Hotkeys - + Klávesové skratky &Hotkeys - + &Klávesové skratky &New - + &Nový &Edit - + &Upraviť &Remove - + O&dstrániť A&ctions - + &Akcie Ne&w - + No&vý E&dit - + U&praviť Re&move - + Ods&trániť Advanced server settings - + Pokročilé nastavenia serveru &Switch - + &Prepnúť Switch &after waiting - + Prepnúť &po prestávke ms - + ms Switch on double &tap within - + Prepnúť po &dvojitom ťuknutí počas &Options - + &Možnosti &Check clients every - + K&ontrolovať klientov každých Use &relative mouse moves - + Používať &relatívne pohyby myši S&ynchronize screen savers - + &Synchronizovať šetriče obrazovky - Don't take &foreground window on Windows servers - + Don't take &foreground window on Windows servers + Na serveroch s &Windows sa neprepínať do popredia Ignore auto config clients - + Ignorovať klientov s autokonfiguráciou &Dead corners - + &Mŕtve rohy To&p-left - + &Vľavo hore Top-rig&ht - + Vpravo &dole &Bottom-left - + &Vľavo dole Bottom-ri&ght - + V&pravo dole Cor&ner Size: - + &Veľkost rohov: @@ -1040,19 +1068,21 @@ Double click on a screen to edit its settings. Save log file to... - + Uložiť súbor s protokolom do... Elevate Barrier - + Zvýšiť stupeň oprávnení pre Barrier Are you sure you want to elevate Barrier? This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to. - + Naozaj chcete zvýšiť stupeň oprávnení pre Barrier? + +Toto umožní Barrier pracovať s procesmi, ktoré majú takisto vyšší stupeň oprávnení a s oknom riadenia používateľských účtov (UAC), ale môže to tiež spôsobiť problémy aplikáciam s bežnými oprávneniami. Túto možnosť by ste mali použiť iba, ak ju skutočne potrebujete. @@ -1060,107 +1090,107 @@ This allows Barrier to interact with elevated processes and the UAC dialog, but Settings - + Nastavenia Sc&reen name: - + &Názov obrazovky: P&ort: - + P&ort: &Interface: - + &Používateľské rozhranie: Elevate mode - + Režim oprávnení &Hide on startup - + &Skryť pri spustení &Network Security - + Sieťová &bezpečnosť Use &SSL encryption (unique certificate) - + Používať šifrovanie &SSL (jedinečný certifikát) Logging - + Protokolovanie &Logging level: - + Ú&roveň protokolu: Log to file: - + Protokol ukladať do súboru: Browse... - + Prechádzať... Error - + Chyba &Language: - + &Jazyk: &Miscellaneous - + &Rôzne Warning - + Varovanie Note - + Poznámka Info - Informácie + Informácie Debug - + Ladenie Debug1 - + Ladenie1 Debug2 - + Ladenie2 @@ -1168,17 +1198,17 @@ This allows Barrier to interact with elevated processes and the UAC dialog, but Setup Barrier - + Nastaviť Barrier Please select an option. - + Prosím, vyberte si jednu z možností. Please enter your email address and password. - + Zadajte, prosím, vašu emailovú adresu a heslo. @@ -1186,85 +1216,93 @@ This allows Barrier to interact with elevated processes and the UAC dialog, but Setup Barrier - + Nastaviť Barrier Welcome - + Vitajte Thanks for installing Barrier! - + Ďakujeme, že ste sa rozhodli nainštalovať Barrier! - Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux). - Barrier vám umožní ľahko zdieľať myš a klávesnicu medzi viacerými počítačmi na stole, je to zadarmo a Open Source. Len presunúť kurzor myši mimo okraj jedného počítača na obrazovke na ďalšie. Môžete dokonca zdieľať všetky vaše schránok. Všetko, čo potrebujete, je pripojenie k sieti. Barrier je cross-platformové (práca na Windows, Mac OS X a Linux). + Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux). + Barrier vám umožní ľahko zdieľať myš a klávesnicu medzi viacerými počítačmi, je zadarmo a má otvorený zdrojový kód. Stačí len presunúť kurzor myši mimo okraj obrazovky jedného počítača a presuniete sa na druhý počítač. Môžete dokonca zdieľať všetky vaše schránky. Všetko, čo potrebujete, je pripojenie k sieti. Barrier podporuje viacero platforiem (funguje vo Windowse, Mac OS X a Linuxe). Activate - + Aktivovať &Activate now... - + &Aktivovať teraz... Email: - + Email: Password: - + Heslo: <a href="https://symless.com/account/reset/">Forgot password</a> - + <a href="https://symless.com/account/reset/">Zabudnuté heslo</a> &Skip activation - + &Preskočiť aktiváciu - &Server (share this computer's mouse and keyboard) - + &Server (share this computer's mouse and keyboard) + &Server (zdieľať klávesnicu a myš tohto počítača) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.</span></p></body></html> - +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">K tomuto počítaču je pripojená moja hlavná klávesnica a myš. Toto vám umožní presunúť kurzor myši na obrazovku iného počítača. V úlohe servera môže byť len jeden počítač.</span></p></body></html> - &Client (use another computer's mouse and keyboard) - + &Client (use another computer's mouse and keyboard) + &Klient (použiť klávesnicu a myš iného počítača) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.</span></p></body></html> - +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Server už ste nastavili. Tento počítač bude ovládaný klávesnicou a myšou pripojenou k serveru. V roli klienta môže byť viacero počítačov.</span></p></body></html> Server or Client? - + Server alebo klient? @@ -1272,22 +1310,22 @@ p, li { white-space: pre-wrap; } Failed to get profile directory. - + Nepodarilo sa získať adresár s profilom. SSL certificate generated. - + SSL certifikát bol vygenerovaný. SSL fingerprint generated. - + SSL otlačok bol vygenerovaný. Failed to find SSL fingerprint. - + Nepodarilo sa nájsť otlačok SSL. @@ -1295,7 +1333,7 @@ p, li { white-space: pre-wrap; } Unknown - + Neznáma @@ -1305,19 +1343,23 @@ p, li { white-space: pre-wrap; } An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details. %1 - + Počas pokusu o prihlásenie došlo k chybe. Prosím, kontaktujte zákaznickú podporu a poskytnite im nasledujúce údaje. + +%1 Login failed, invalid email or password. - + Prihlásenie nebolo úspešné, neplatná emailová adresa alebo heslo. Login failed, an error occurred. %1 - + Prihlásenie nebolo úspešné, nastala chyba. + +%1 @@ -1326,26 +1368,33 @@ p, li { white-space: pre-wrap; } Server response: %1 - + Prihlásenie nebolo úspešné, nastala chyba. + +Odpoveď servera: +%1 An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details. %1 - + Počas získavania zoznamu pluginov došlo k chybe. Prosím, kontaktujte zákaznickú podporu a poskytnite im nasledujúce údaje. + +%1 Get plugin list failed, invalid user email or password. - + Zlyhalo získavanie zoznamu pluginov, neplatná emailová adresa alebo heslo. Get plugin list failed, an error occurred. %1 - + Zlyhalo získavanie zoznamu pluginov, nastala chyba. + +%1 @@ -1354,7 +1403,10 @@ Server response: Server response: %1 - + Zlyhalo získavanie zoznamu pluginov, nastala chyba. + +Odpoveď servera: +%1 @@ -1362,44 +1414,44 @@ Server response: zeroconf server detected: %1 - + detegovaný zeroconf server: %1 zeroconf client detected: %1 - + detegovaný zeroconf klient: %1 Zero configuration service - + Služba Zero configuration Error code: %1. - + Kód chyby: %1. Unable to start the zeroconf: %1. - + Nepodarilo sa spustiť zeroconf: %1. Barrier - + Barrier Failed to get local IP address. Please manually type in server address on your clients - + Nepodarilo sa získať miestnu IP adresu. Zadajte, prosím, adresu servera ručne v nastaveniach vašich klientov %1 - + %1 - \ No newline at end of file + diff --git a/src/gui/res/lang/gui_sl-SI.ts b/src/gui/res/lang/gui_sl-SI.ts index 5aa6cde..181d5e2 100644 --- a/src/gui/res/lang/gui_sl-SI.ts +++ b/src/gui/res/lang/gui_sl-SI.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_sq-AL.qm b/src/gui/res/lang/gui_sq-AL.qm index c80f8ab..b54bb6e 100644 Binary files a/src/gui/res/lang/gui_sq-AL.qm and b/src/gui/res/lang/gui_sq-AL.qm differ diff --git a/src/gui/res/lang/gui_sq-AL.ts b/src/gui/res/lang/gui_sq-AL.ts index 1fcd217..fa65c6c 100644 --- a/src/gui/res/lang/gui_sq-AL.ts +++ b/src/gui/res/lang/gui_sq-AL.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Konfiguracioni i Barrier (*.sgc);;Te gjithe (*.*) + All files (*.*) + Te gjithe (*.*) + + + + Barrier Configurations (*.sgc) + Konfiguracioni i Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Konfiguracioni i Barrier (*.conf);;Te gjithe (*.*) + Barrier Configurations (*.conf) + Konfiguracioni i Barrier (*.conf) diff --git a/src/gui/res/lang/gui_sr.ts b/src/gui/res/lang/gui_sr.ts index 4ae87d8..d46168f 100644 --- a/src/gui/res/lang/gui_sr.ts +++ b/src/gui/res/lang/gui_sr.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_sv.qm b/src/gui/res/lang/gui_sv.qm index f014cfd..897abe3 100644 Binary files a/src/gui/res/lang/gui_sv.qm and b/src/gui/res/lang/gui_sv.qm differ diff --git a/src/gui/res/lang/gui_sv.ts b/src/gui/res/lang/gui_sv.ts index f0c239e..d28810c 100644 --- a/src/gui/res/lang/gui_sv.ts +++ b/src/gui/res/lang/gui_sv.ts @@ -669,13 +669,18 @@ Klicka "Ja" för att automatiskt lita på fingeravtrycket i framtida a QObject - Barrier Configurations (*.sgc);;All files (*.*) - Barrier-konfigurationer (*.sgc);;Alla filer (*.*) + All files (*.*) + Alla filer (*.*) + + + + Barrier Configurations (*.sgc) + Barrier-konfigurationer (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrier-konfigurationer (*.conf);;Alla filer (*.*) + Barrier Configurations (*.conf) + Barrier-konfigurationer (*.conf) diff --git a/src/gui/res/lang/gui_th-TH.ts b/src/gui/res/lang/gui_th-TH.ts index 07c74b8..88d7894 100644 --- a/src/gui/res/lang/gui_th-TH.ts +++ b/src/gui/res/lang/gui_th-TH.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_tr-TR.ts b/src/gui/res/lang/gui_tr-TR.ts index 01a681b..924662f 100644 --- a/src/gui/res/lang/gui_tr-TR.ts +++ b/src/gui/res/lang/gui_tr-TR.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) @@ -900,8 +905,8 @@ To automatically trust this fingerprint for future connections, click Yes. To re Drag new screens to the grid or move existing ones around. Drag a screen to the trashcan to delete it. Double click on a screen to edit its settings. - Izgaraya yeni ekranları sürükleyin veya çevresinde mevcut olanları taşıyın. -Silmek için çöp tenekesine ekranı sürükleyin. + Izgaraya yeni ekranları sürükleyin veya çevresinde mevcut olanları taşıyın. +Silmek için çöp tenekesine ekranı sürükleyin. Kendi ayarlarınızı düzenlemek için bir ekran üzerine çift tıklayın. diff --git a/src/gui/res/lang/gui_uk.qm b/src/gui/res/lang/gui_uk.qm index 3e9c9c5..86cb8ef 100644 Binary files a/src/gui/res/lang/gui_uk.qm and b/src/gui/res/lang/gui_uk.qm differ diff --git a/src/gui/res/lang/gui_uk.ts b/src/gui/res/lang/gui_uk.ts index 1f2dffe..b7297c1 100644 --- a/src/gui/res/lang/gui_uk.ts +++ b/src/gui/res/lang/gui_uk.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Конфігурація Barrier (*.sgc);;Всі файли (*.*) + All files (*.*) + Всі файли (*.*) + + + + Barrier Configurations (*.sgc) + Конфігурація Barrier (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Конфігурація Barrier (*.conf);;Всі файли (*.*) + Barrier Configurations (*.conf) + Конфігурація Barrier (*.conf) diff --git a/src/gui/res/lang/gui_ur.ts b/src/gui/res/lang/gui_ur.ts index 9b36dcf..c378089 100644 --- a/src/gui/res/lang/gui_ur.ts +++ b/src/gui/res/lang/gui_ur.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_vi.ts b/src/gui/res/lang/gui_vi.ts index a3c6025..16d013a 100644 --- a/src/gui/res/lang/gui_vi.ts +++ b/src/gui/res/lang/gui_vi.ts @@ -648,12 +648,17 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) + All files (*.*) + + + + + Barrier Configurations (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) + Barrier Configurations (*.conf) diff --git a/src/gui/res/lang/gui_zh-CN.qm b/src/gui/res/lang/gui_zh-CN.qm index f11d0b5..21c874e 100644 Binary files a/src/gui/res/lang/gui_zh-CN.qm and b/src/gui/res/lang/gui_zh-CN.qm differ diff --git a/src/gui/res/lang/gui_zh-CN.ts b/src/gui/res/lang/gui_zh-CN.ts index 0c8522a..d7a331c 100644 --- a/src/gui/res/lang/gui_zh-CN.ts +++ b/src/gui/res/lang/gui_zh-CN.ts @@ -1,1411 +1,1323 @@ - + + + AboutDialogBase - + About Barrier - 关于Barrier + 关于 Barrier - - + + + + Unknown + 未知 + + + + Version: + 版本: + + + + &Ok + 确定(&O) + + + <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> +Copyright © 2018 Debauchee Open Source Group<br /> Copyright © 2012-2016 Symless Ltd.<br /> Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br /><br /> Barrier is released under the GNU General Public License (GPLv2).<br /><br /> Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> -The Barrier GUI is based on QSynergy by Volker Lanz.<br /><br /> -Visit our website for help and info (symless.com). +The Barrier GUI is based on QSynergy by Volker Lanz. </p> - <p> -Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> + <p> +共享键盘和鼠标,跨平台并开源。<br /><br /> +Copyright © 2018 Debauchee Open Source Group<br /> Copyright © 2012-2016 Symless Ltd.<br /> Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br /><br /> -Barrier is released under the GNU General Public License (GPLv2).<br /><br /> -Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> -The Barrier GUI is based on QSynergy by Volker Lanz.<br /><br /> -Visit our website for help and info (symless.com). -</p> - - - - - Unknown - 未知 - - - - Version: - 版本: +Barrier 适用 GNU 通用公共许可证(GPLv2)。<br /><br /> +Barrier 基于 Richard Lee 和 Adam Feder 制作的 CosmoSynergy .<br /> +Barrier GUI 基于 Volker Lanz 制作的 QSynergy. +</p> - - &Ok - 确定 + + Build Date: + 日期: ActionDialogBase - + Configure Action - 行为配置 + 配置动作 - + Choose the action to perform - 选择要执行的行为 + 选择要执行的动作 - + Press a hotkey - 按下热键 + 按下热键 - + Release a hotkey - 松开热键 + 松开热键 - + Press and release a hotkey - 按下一个键然后松开 + 单击热键 - + only on these screens - 仅仅在这些屏幕上 + 仅在这些屏幕上 - + Switch to screen - 切换到屏幕 + 切换到屏幕 - + Switch in direction - 方向切换 + 切换方向 - + left - + - + right - 右侧 + 右侧 - + up - 上方 + 上方 - + down - 下方 + 下方 - + Lock cursor to screen - 锁定指针于屏幕 + 锁定指针于屏幕 - + toggle - 切换 + 切换 - + on - 启用 + 启用 - + off - 禁用 + 禁用 - + This action is performed when - 当……时启用此行为 + 当……时启用此动作 - + the hotkey is pressed - 热键被按下 + 热键被按下 - + the hotkey is released - 热键被松开 + 热键被松开 + + + + Toggle screen + 切换屏幕 AddClientDialog - + Dialog - + TextLabel - + Ignore auto connect clients - + 忽略自动连接的客户端 HotkeyDialogBase - + Hotkey - 热键 + 热键 - + Enter the specification for the hotkey: - 输入热键的说明: + 指定热键: - MainWindow + LogWindowBase - - &Start - 开始 + + Log - Barrier + 日志 - Barrier - - &File - 文件 + + &Clear Log + 清空(&C) - - &Edit - 编辑 + + &Hide + 隐藏(&H) + + + MainWindow - - &Window - 窗口 + + &Start + 开始(&S) - + &Help - 帮助 - - - - <p>Your version of Barrier is out of date. Version <b>%1</b> is now available to <a href="%2">download</a>.</p> - <p>Version %1 is now available, <a href="%2">visit website</a>.</p> - <p>您正在使用的Barrier版本有些过时了,有新版 <b>%1</b> 可以 <a href="%2">下载</a>。</p> + 帮助(&S) - + Program can not be started - 程序无法启动 + 程序无法启动 - + The executable<br><br>%1<br><br>could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program. - 可执行程序<br><br>%1<br><br>没有成功运行,虽然程序本身存在。请检查你是否有运行此程序的权限。 + 可执行程序<br><br>%1<br><br>没有成功运行,虽然程序本身存在。请检查你是否有运行此程序的权限。 - + Barrier client not found - 未找到Barrier客户端 + 未找到Barrier客户端 - + The executable for the barrier client does not exist. - Barrier客户端的可执行程序不存在。 + Barrier客户端的可执行程序不存在。 - + Hostname is empty - 主机名为空 + 主机名为空 - + Please fill in a hostname for the barrier client to connect to. - 请为Barrier客户端设置一个用于连接的主机名 + 请为Barrier客户端设置一个用于连接的主机名。 - + Cannot write configuration file - 不能写入配置文件 + 不能写入配置文件 - + The temporary configuration file required to start barrier can not be written. - 启动Barrier所需的临时配置文件不可写。 + 启动Barrier所需的临时配置文件不可写。 - + Configuration filename invalid - 配置文件名非法 + 无效配置文件名 - + You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now? - 你没有为Barrier服务端设置一个可用的配置文件。需要现在浏览配置文件吗? + Barrier服务端缺少有效的配置文件。需要现在浏览配置文件吗? - + Barrier server not found - 未找到Barrier服务端 + 未找到Barrier服务端 - + The executable for the barrier server does not exist. - Barrier服务端可执行程序不存在。 - - - - Barrier terminated with an error - Barrier因错终止运行 - - - - Barrier terminated unexpectedly with an exit code of %1.<br><br>Please see the log output for details. - Barrier意外终止运行,退出代码 %1。<br><br>请查看输出日志了解详情。 + Barrier服务端可执行程序不存在。 - + &Stop - 停止 + 停止(&S) - + Please add the server (%1) to the grid. - + 请添加服务端(%1)到网格。 - + Please drag the new client screen (%1) to the desired position on the grid. - + 请将新屏幕(%1)拖动到网格中的合适位置。 - + Failed to detect system architecture. - + 检测系统架构失败 - + Cancel - + 取消 - + Failed to download Bonjour installer to location: %1 - + 无法下载Bonjou安装包至 %1 - + Do you want to enable auto config and install Bonjour? This feature helps you establish the connection. - + 是否开启自动配置并下载Bonjour? + +此功能帮助你建立连接。 - + Auto config feature requires Bonjour. Do you want to install Bonjour? - + 自动配置需要Bonjour + +是否安装Bonjour? - + Barrier is starting. - Barrier正在启动 + Barrier正在启动 - + Barrier is running. - Barrier正在运行 + Barrier正在运行 - + Barrier is not running. - Barrier没有运行 + Barrier没有运行 - + Unknown - 未知 + 未知 - - - + + + Barrier - Barrier + Barrier - + Browse for a barriers config file - 浏览Barrier配置文件 + 浏览Barrier配置文件 - - Barrier is now connected, You can close the config window. Barrier will remain connected in the background. - - - - + Security question - + 安全确认 - + Do you trust this fingerprint? %1 -This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user). +This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user). To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No. - + 是否信任此指纹? + +%1 + +这是服务端的指纹,请将其与你服务端屏幕的指纹进行对比。如果两者不一致,你可能没有连接到预期的服务端,或是遇到了恶意攻击。 + +点击确认以自动信任此指纹在未来的连接,点击停止来中断连接。 - + Save configuration as... - 保存配置到文件 + 保存配置到文件 - + Save failed - 保存失败 + 保存失败 - + Could not save configuration to file. - 不能保存配置到文件 + 无法保存配置到文件 + + + + &Barrier + &Barrier + + + + Barrier is now connected. You can close the config window and Barrier will remain connected in the background. + Barrier已连接。现在可以关闭窗口,Barrier会在后台保持连接。 MainWindowBase - + Barrier - Barrier + Barrier - - Ser&ver (share this computer's mouse and keyboard): - + + Ser&ver (share this computer's mouse and keyboard): + 服务端 - 共享此电脑的鼠标和键盘 - + Screen name: - 屏幕名: + 屏幕名: - + &Server IP: - 服务端IP + 服务端IP: - - + + &Start - 开始 + 开始(&S) - + Use existing configuration: - 使用已有的配置: + 使用已有的配置: - + &Configuration file: - 配置文件: + 配置文件: - + &Browse... - 浏览… + 浏览(&B)… - + Configure interactively: - 交互配置: + 交互配置: - + &Configure Server... - 设置服务端… + 设置服务端(&C)… - + Ready - 准备完毕 - - - - Log - 日志 + 准备完毕 - + &Reload - 应用 + 应用(&R) - + IP addresses: - IP地址 + IP地址: - - Fingerprint: - - - - - &Client (use another computer's mouse and keyboard): - + + &Client (use another computer's mouse and keyboard): + 客户端 - 使用另一电脑的鼠标和键盘 - + Auto config - + 自动配置 - + &About Barrier... - 关于Barrier… + 关于Barrier(&A)… - + &Quit - 退出 + 退出(&Q) - + Quit - 退出 + 退出 - + Run - 运行 + 运行 - + S&top - 停止 + 停止(&t) - + Stop - 停止 - - - - S&how Status - 显示状态 + 停止 - + &Hide - 隐藏 + 隐藏(&H) - + Hide - 隐藏 + 隐藏 - + &Show - 显示 + 显示(&S) - + Show - 显示 + 显示 - - Save configuration &as... - 保存配置到… - - - + Save the interactively generated server configuration to a file. - 保存通过交互配置生成的配置到文件。 - - - - Settings - 设置 + 保存交互生成的配置到文件。 - + Edit settings - 编辑设置 + 编辑设置 - - Run Wizard - 运行向导 + + SSL Fingerprint: + SSL指纹: - - - NewScreenWidget - - Unnamed - 未命名 + + S&ave configuration + 保存配置(&a) - - - PluginManager - - Failed to get plugin directory. - + + Change &Settings + 更改设置(&S) - - Failed to get profile directory. - + + Show &Log + 显示日志(&L) - - Failed to download plugin '%1' to: %2 -%3 - - - - - Could not get Windows architecture type. - - - - - Could not get Linux architecture type. - + + Show Log + 显示日志 - PluginWizardPage - - - Setup Barrier - 设置Barrier - - - - Please wait... - - - - - Error: %1 - - - - - - Setup complete. - - - - - Downloading '%1' plugin (%2/%3)... - - - - - Plugins installed successfully. - - - - - Generating SSL certificate... - - - - - Downloading plugin: %1 (1/%2) - - + NewScreenWidget - - Getting plugin list... - + + Unnamed + 未命名 QObject - - Barrier Configurations (*.sgc);;All files (*.*) - Barrier配置文件(*.sgc);;所有文件 (*.*) + + All files (*.*) + 所有文件 (*.*) - - Barrier Configurations (*.conf);;All files (*.*) - Barrier配置文件(*.conf);;所有文件 (*.*) + + Barrier Configurations (*.sgc) + Barrier配置文件 (*.sgc) - - System tray is unavailable, quitting. - 系统托盘不可用,程序退出。 + + Barrier Configurations (*.conf) + Barrier配置文件 (*.conf) ScreenSettingsDialog - + Screen name is empty - 屏幕名为空 + 屏幕名为空 - + The screen name cannot be empty. Please either fill in a name or cancel the dialog. - 屏幕名不能为空。请填入一个名字或者关闭对话框。 + 屏幕名不能为空。请填入一个名字或者关闭对话框。 - + Screen name matches alias - 屏幕名对应别名 + 屏幕名对应别名 - + The screen name cannot be the same as an alias. Please either remove the alias or change the screen name. - 屏幕名不能与别名相同,请取消或者更改别名。 + 屏幕名不能与别名相同,请取消或者更改别名。 ScreenSettingsDialogBase - + Screen Settings - 屏幕设置 + 屏幕设置 - + Screen &name: - 屏幕名: + 屏幕名: - + A&liases - 别名 + 别名 - + &Add - 添加 + 添加(&A) - + &Remove - 删除 + 删除(&R) - + &Modifier keys - 修改按键 + 修改按键 - + &Shift: - Shift: + Shift: - - - - - + + + + + Shift - Shift + Shift - - - - - + + + + + Ctrl - Ctrl + Ctrl - - - - - + + + + + Alt - Alt + Alt - - - - - + + + + + Meta - Meta + Meta - - - - - + + + + + Super - 超级 + 超级 - - - - - + + + + + None - + - + &Ctrl: - Ctrl: + &Ctrl: - + Al&t: - Alt: + Al&t: - + M&eta: - Meta: + M&eta: - + S&uper: - Super: + S&uper: - + &Dead corners - 死角 + 死角 - + Top-left - 左上角 + 左上角 - + Top-right - 右上角 + 右上角 - + Bottom-left - 左下角 + 左下角 - + Bottom-right - 右下角 + 右下角 - + Corner Si&ze: - 死角大小 + 死角大小 - + &Fixes - 修改 + 修改 - + Fix CAPS LOCK key - 修复caps lock键 + 修复caps lock键 - + Fix NUM LOCK key - 修复num lock键 + 修复num lock键 - + Fix SCROLL LOCK key - 修复scroll lock键 + 修复scroll lock键 - + Fix XTest for Xinerama - 修复Xinerama的XTest + 修复Xinerama的XTest + + + + Fix Preserve Focus + 修复焦点 ScreenSetupModel - + <center>Screen: <b>%1</b></center><br>Double click to edit settings<br>Drag screen to the trashcan to remove it - <center>屏幕设置: <b>%1</b></center><br>双击以修改设置<br>将屏幕拖到废纸篓来移除 + <center>屏幕设置: <b>%1</b></center><br>双击以修改设置<br>将屏幕拖到废纸篓来移除 + + + + ScreenSetupView + + + Unnamed + 未命名 ServerConfigDialog - + Configure server - + 设置服务端 ServerConfigDialogBase - + Server Configuration - 服务端配置 + 服务端配置 - + Screens and links - 屏幕和联接 + 屏幕和联接 - + Drag a screen from the grid to the trashcan to remove it. - 从格子中拖动屏幕到垃圾桶进行删除。 + 从网格中拖动屏幕到垃圾桶进行删除。 - + Configure the layout of your barrier server configuration. - 设置Barrier服务端配置的屏幕布局。 + 配置Barrier服务端的屏幕布局。 - + Drag this button to the grid to add a new screen. - 拖动此按钮到格子中进行添加屏幕。 + 拖动此按钮到网格以添加屏幕。 - + Drag new screens to the grid or move existing ones around. Drag a screen to the trashcan to delete it. Double click on a screen to edit its settings. - 拖动屏幕(图标)到网格中或者移动已经在网格中的屏幕的位置。 + 拖动屏幕图标到网格,或者移动已经在网格中的屏幕。 拖动屏幕到垃圾桶进行删除。 双击屏幕编辑其设置。 - + Hotkeys - 热键 + 热键 - + &Hotkeys - 热键 + 热键(&H) - + &New - 新建 + 新建(&N) - + &Edit - 编辑 + 编辑(&E) - + &Remove - 删除 + 删除(&R) - + A&ctions - 行为 + 动作 - + Ne&w - 新建 + 新建(&w) - + E&dit - 编辑 + 编辑(&d) - + Re&move - 删除 + 删除(&m) - + Advanced server settings - 服务端高级设置 + 服务端高级设置 - + &Switch - 切换 + 触边切换 - + Switch &after waiting - 等待后切换 + 延时 - - - + + + ms - 毫秒 + 毫秒 - + Switch on double &tap within - 双击tap切换 + 二次触边 - + &Options - 选项 + 选项 - + &Check clients every - 客户端检查周期 + 客户端检查周期 - + Use &relative mouse moves - 使用相关的鼠标动作 + 使用相对的鼠标动作 - + S&ynchronize screen savers - 同步屏幕保护 + 同步屏幕保护 - - Don't take &foreground window on Windows servers - 不要移动前台窗口在Windows服务器上 + + Don't take &foreground window on Windows servers + 不要在Windows服务端获取前台窗口 - + Ignore auto config clients - + 忽略自动配置的客户端 - + &Dead corners - 死角 + 死角 - + To&p-left - 左上角 + 左上角 - + Top-rig&ht - 右上角 + 右上角 - + &Bottom-left - 左下角 + 左下角 - + Bottom-ri&ght - 右下角 + 右下角 - + Cor&ner Size: - 死角大小 + 死角大小 - - - SettingsDialog - - Save log file to... - 保存日志文件… + + Bump against the screen edge with the mouse pointer twice in quick succession. + 要触发二次触边,快速并连续地滑动指针到屏幕边缘。 - - Elevate Barrier - 评价Barrier + + Enable drag and drop file transfers + 启用拖放文件传输 - - Are you sure you want to elevate Barrier? - -This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to. - 您确定要elevate Barrier吗? -这会允许Barrier和elevated进程交互和UAC对话框,但是可能引起一切和非elevated进程交互的问题。只有在你需要的时候才Elevate Barrier。 + + Enable clipboard sharing + 启用剪切板共享 + + + + SettingsDialog + + + Save log file to... + 保存日志文件… SettingsDialogBase - + Settings - 设置 + 设置 - + Sc&reen name: - 屏幕名称: + 屏幕名称: - + P&ort: - 端口: - - - - &Interface: - 界面 + 端口: - - Elevate mode - - - - + &Hide on startup - + 最小化启动 - - &Network Security - - - - - Use &SSL encryption (unique certificate) - - - - + Logging - 日志记录 + 日志 - + &Logging level: - 日志等级 + 日志等级 - + Log to file: - 记录到文件 + 记录到文件 - + Browse... - 浏览… + 浏览… - + Error - 错误 + 错误 - + &Language: - 语言: - - - - &Miscellaneous - 其他 + 语言: - + Warning - 警告 + 警告 - + Note - 注意 + 注意 - + Info - 信息 + 信息 - + Debug - 调试 + 调试 - + Debug1 - 调试1 + 调试1 - + Debug2 - 调试2 + 调试2 - - - SetupWizard - - Setup Barrier - 设置Barrier + + General + 通用 - - Please select an option. - 请选择一个选项 + + Elevate + 提权 - - Please enter your email address and password. - 请输入您的邮箱地址和密码。 + + Specify when the Barrier service should run at an elevated privilege level + 设置Barrier服务何时应该提权运行 - - - SetupWizardBase - - Setup Barrier - 设置Barrier + + As Needed + 按需 - - Welcome - 欢迎 + + Always + 总是 - - Thanks for installing Barrier! - 感谢您安装Barrier! + + Never + 从不 - - Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux). - Barrier允许你轻松地在你办公桌上多台计算机之间共享你的鼠标和键盘,它免费并且开放源代码。你只要将鼠标(指针)从一台计算机的屏幕边缘移出到另一个屏幕就行了。甚至可以共享你的剪贴板。你所需要的仅仅是一个网络连接。Barrier是跨平台的(可以运行于Windows,Mac OS X和Linux)。 + + Minimize to System &Tray + 最小化到系统托盘 - - Activate - + + Start &Barrier on startup + 自动启动 - - &Activate now... - + + Networking + 网络 - - Email: - + + &Address: + 地址: - - Password: - + + Enable &SSL + 开启SSL + + + SetupWizard - - <a href="https://symless.com/account/reset/">Forgot password</a> - + + Setup Barrier + 设置Barrier - - &Skip activation - + + Please select an option. + 请选择一个选项 + + + + SetupWizardBase + + + Setup Barrier + 设置Barrier - - &Server (share this computer's mouse and keyboard) - + + Welcome + 欢迎 + + + + Thanks for installing Barrier! + 感谢您安装Barrier! - + + Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux). + Barrier可以在多台计算机间轻松共享你的鼠标和键盘,它还是自由开源软件。将鼠标指针从计算机的屏幕边缘移出到另一个屏幕,并同时共享剪贴板,只要你有一个网络连接。Barrier是跨平台的(可以运行于Windows,Mac OS X和Linux)。 + + + + &Server (share this computer's mouse and keyboard) + 服务端 - 共享此电脑的鼠标和键盘 + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.</span></p></body></html> - +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">这台电脑连接了鼠标和键盘,需要共享给其他电脑。一套配置只能有一个服务端。</span></p></body></html> - - &Client (use another computer's mouse and keyboard) - + + &Client (use another computer's mouse and keyboard) + 客户端 - 使用另一电脑的鼠标和键盘 - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.</span></p></body></html> - +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">已经配置了服务端,此电脑将由服务端的鼠标和键盘进行控制。可以配置多个客户端。</span></p></body></html> - + Server or Client? - (此计算机作为)服务端还是客户端? + 服务端还是客户端? SslCertificate - + Failed to get profile directory. - + 获取配置目录失败。 - + SSL certificate generated. - + SSL证书已生成。 - + SSL fingerprint generated. - + SSL指纹已生成。 - + Failed to find SSL fingerprint. - + 无法找到SSL指纹。 - - - VersionChecker - - Unknown - 未知 + + Could not read from default certificate file. + 无法读取默认证书文件。 - - - WebClient - - An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details. - -%1 - - - - - Login failed, invalid email or password. - 登录失败,邮箱地址或密码错误。 + + Error loading default certificate file to memory. + 读取默认证书文件失败。 - - Login failed, an error occurred. - -%1 - 登录失败,出错了。 -%1 + + Default certificate key file does not contain valid public key + 默认证书文件没有有效的公钥 - - Login failed, an error occurred. - -Server response: - -%1 - 登录失败,出错了。 -服务器回应: -%1 + + Public key in default certificate key file is not RSA or DSA + 默认证书文件的公钥不是RSA或DSA - - An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details. - -%1 - - - - - Get plugin list failed, invalid user email or password. - - - - - Get plugin list failed, an error occurred. - -%1 - + + Public key in default certificate key file is too small. + 默认证书文件位数不足。 + + + VersionChecker - - Get plugin list failed, an error occurred. - -Server response: - -%1 - + + Unknown + 未知 ZeroconfService - + zeroconf server detected: %1 - + 检测到zeroconf服务端:%1 - + zeroconf client detected: %1 - + 检测到zeroconf客户端:%1 - - + + Zero configuration service - + zeroconf服务 - + Error code: %1. - + 错误代码:%1。 - + Unable to start the zeroconf: %1. - + 无法启动zeroconf:%1。 - + Barrier - Barrier + Barrier - + Failed to get local IP address. Please manually type in server address on your clients - + 无法获取本地IP地址。请在客户端上手动输入服务端地址。 - - + + %1 - + %1 - \ No newline at end of file + diff --git a/src/gui/res/lang/gui_zh-TW.qm b/src/gui/res/lang/gui_zh-TW.qm index 5cc8653..ac43686 100644 Binary files a/src/gui/res/lang/gui_zh-TW.qm and b/src/gui/res/lang/gui_zh-TW.qm differ diff --git a/src/gui/res/lang/gui_zh-TW.ts b/src/gui/res/lang/gui_zh-TW.ts index 902a444..d4d305a 100644 --- a/src/gui/res/lang/gui_zh-TW.ts +++ b/src/gui/res/lang/gui_zh-TW.ts @@ -648,13 +648,18 @@ To automatically trust this fingerprint for future connections, click Yes. To re QObject - Barrier Configurations (*.sgc);;All files (*.*) - Barrier 設定檔 (*.sgc);;所有檔案 (*.*) + All files (*.*) + 所有檔案 (*.*) + + + + Barrier Configurations (*.sgc) + Barrier 設定檔 (*.sgc) - Barrier Configurations (*.conf);;All files (*.*) - Barrier 設定檔 (*.conf);;所有檔案 (*.*) + Barrier Configurations (*.conf) + Barrier 設定檔 (*.conf) diff --git a/src/gui/res/win/Barrier.rc b/src/gui/res/win/Barrier.rc index 9db2b3e..2825690 100644 --- a/src/gui/res/win/Barrier.rc +++ b/src/gui/res/win/Barrier.rc @@ -1 +1,112 @@ -IDI_ICON1 ICON DISCARDABLE "../icons/256x256/barrier.ico" +// Microsoft Visual C++ generated resource script. +// +//#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include \r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION BARRIER_VERSION_MAJOR, BARRIER_VERSION_MINOR, BARRIER_VERSION_PATCH, BARRIER_BUILD_NUMBER + PRODUCTVERSION BARRIER_VERSION_MAJOR, BARRIER_VERSION_MINOR, BARRIER_VERSION_PATCH, BARRIER_BUILD_NUMBER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Debauchee Open Source Group" + VALUE "CompanyWeb", "https://github.com/debauchee/barrier/" + VALUE "FileVersion", BARRIER_VERSION + VALUE "LegalCopyright", "Copyright (C) 2018 Debauchee Open Source Group\nCopyright (C) 2012-2016 Symless Ltd.\nCopyright (C) 2008-2014 Nick Bolton\nCopyright (C) 2002-2014 Chris Schoeneman" + VALUE "ProductName", "Barrier" + VALUE "ProductVersion", BARRIER_VERSION + VALUE "OriginalFilename", "barrier.exe" + VALUE "FileDescription", "Open source KVM software" + VALUE "InternalName", "barrier" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_BARRIER ICON "../icons/256x256/barrier.ico" + + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/gui/src/AboutDialog.cpp b/src/gui/src/AboutDialog.cpp index 76ba26b..b594bee 100644 --- a/src/gui/src/AboutDialog.cpp +++ b/src/gui/src/AboutDialog.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/gui/src/AboutDialog.h b/src/gui/src/AboutDialog.h index 3c498b4..6fdb0a2 100644 --- a/src/gui/src/AboutDialog.h +++ b/src/gui/src/AboutDialog.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -40,4 +40,3 @@ class AboutDialog : public QDialog, public Ui::AboutDialogBase }; #endif - diff --git a/src/gui/src/Action.cpp b/src/gui/src/Action.cpp index 909e983..f34d1e8 100644 --- a/src/gui/src/Action.cpp +++ b/src/gui/src/Action.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -51,7 +51,7 @@ QString Action::text() const * in the end but now argument inside. If you need a function with no * argument, it can not have () in the end. */ - QString text = QString(m_ActionTypeNames[keySequence().isMouseButton() ? + QString text = QString(m_ActionTypeNames[m_KeySequence.isMouseButton() ? type() + int(mouseDown) : type()]); switch (type()) @@ -61,9 +61,9 @@ QString Action::text() const case keystroke: { text += "("; - text += keySequence().toString(); + text += m_KeySequence.toString(); - if (!keySequence().isMouseButton()) + if (!m_KeySequence.isMouseButton()) { const QStringList& screens = typeScreenNames(); if (haveScreens() && !screens.isEmpty()) @@ -116,15 +116,15 @@ QString Action::text() const void Action::loadSettings(QSettings& settings) { - keySequence().loadSettings(settings); + m_KeySequence.loadSettings(settings); setType(settings.value("type", keyDown).toInt()); - typeScreenNames().clear(); + m_TypeScreenNames.clear(); int numTypeScreens = settings.beginReadArray("typeScreenNames"); for (int i = 0; i < numTypeScreens; i++) { settings.setArrayIndex(i); - typeScreenNames().append(settings.value("typeScreenName").toString()); + m_TypeScreenNames.append(settings.value("typeScreenName").toString()); } settings.endArray(); @@ -137,7 +137,7 @@ void Action::loadSettings(QSettings& settings) void Action::saveSettings(QSettings& settings) const { - keySequence().saveSettings(settings); + m_KeySequence.saveSettings(settings); settings.setValue("type", type()); settings.beginWriteArray("typeScreenNames"); @@ -164,4 +164,3 @@ QTextStream& operator<<(QTextStream& outStream, const Action& action) return outStream; } - diff --git a/src/gui/src/Action.h b/src/gui/src/Action.h index b738f3b..c260bad 100644 --- a/src/gui/src/Action.h +++ b/src/gui/src/Action.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -32,9 +32,6 @@ class QTextStream; class Action { - friend class ActionDialog; - friend QTextStream& operator<<(QTextStream& outStream, const Action& action); - public: enum ActionType { keyDown, keyUp, keystroke, switchToScreen, toggleScreen, switchInDirection, @@ -48,25 +45,31 @@ class Action public: QString text() const; const KeySequence& keySequence() const { return m_KeySequence; } + void setKeySequence(const KeySequence& seq) { m_KeySequence = seq; } + void loadSettings(QSettings& settings); void saveSettings(QSettings& settings) const; + int type() const { return m_Type; } + void setType(int t) { m_Type = t; } + const QStringList& typeScreenNames() const { return m_TypeScreenNames; } - const QString& switchScreenName() const { return m_SwitchScreenName; } - int switchDirection() const { return m_SwitchDirection; } - int lockCursorMode() const { return m_LockCursorMode; } - bool activeOnRelease() const { return m_ActiveOnRelease; } - bool haveScreens() const { return m_HasScreens; } + void appendTypeScreenName(QString name) { m_TypeScreenNames.append(name); } + void clearTypeScreenNames() { m_TypeScreenNames.clear(); } - protected: - KeySequence& keySequence() { return m_KeySequence; } - void setKeySequence(const KeySequence& seq) { m_KeySequence = seq; } - void setType(int t) { m_Type = t; } - QStringList& typeScreenNames() { return m_TypeScreenNames; } + const QString& switchScreenName() const { return m_SwitchScreenName; } void setSwitchScreenName(const QString& n) { m_SwitchScreenName = n; } + + int switchDirection() const { return m_SwitchDirection; } void setSwitchDirection(int d) { m_SwitchDirection = d; } + + int lockCursorMode() const { return m_LockCursorMode; } void setLockCursorMode(int m) { m_LockCursorMode = m; } + + bool activeOnRelease() const { return m_ActiveOnRelease; } void setActiveOnRelease(bool b) { m_ActiveOnRelease = b; } + + bool haveScreens() const { return m_HasScreens; } void setHaveScreens(bool b) { m_HasScreens = b; } private: @@ -84,8 +87,6 @@ class Action static const char* m_LockCursorModeNames[]; }; -typedef QList ActionList; - QTextStream& operator<<(QTextStream& outStream, const Action& action); #endif diff --git a/src/gui/src/ActionDialog.cpp b/src/gui/src/ActionDialog.cpp index 2796885..89a037e 100644 --- a/src/gui/src/ActionDialog.cpp +++ b/src/gui/src/ActionDialog.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -58,7 +58,7 @@ ActionDialog::ActionDialog(QWidget* parent, ServerConfig& config, Hotkey& hotkey m_pGroupBoxScreens->setChecked(m_Action.haveScreens()); int idx = 0; - foreach(const Screen& screen, serverConfig().screens()) + for (const Screen& screen : serverConfig().screens()) { if (!screen.isNull()) { QListWidgetItem *pListItem = new QListWidgetItem(screen.name()); @@ -72,6 +72,7 @@ ActionDialog::ActionDialog(QWidget* parent, ServerConfig& config, Hotkey& hotkey idx++; } + } } void ActionDialog::accept() @@ -83,9 +84,10 @@ void ActionDialog::accept() m_Action.setType(m_pButtonGroupType->checkedId()); m_Action.setHaveScreens(m_pGroupBoxScreens->isChecked()); - m_Action.typeScreenNames().clear(); - foreach(const QListWidgetItem* pItem, m_pListScreens->selectedItems()) - m_Action.typeScreenNames().append(pItem->text()); + m_Action.clearTypeScreenNames(); + for (const QListWidgetItem* pItem : m_pListScreens->selectedItems()) { + m_Action.appendTypeScreenName(pItem->text()); + } m_Action.setSwitchScreenName(m_pComboSwitchToScreen->currentText()); m_Action.setSwitchDirection(m_pComboSwitchInDirection->currentIndex()); diff --git a/src/gui/src/ActionDialog.h b/src/gui/src/ActionDialog.h index 388be1f..ea476c5 100644 --- a/src/gui/src/ActionDialog.h +++ b/src/gui/src/ActionDialog.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 2f8779d..894ce49 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -60,6 +60,7 @@ AppConfig::AppConfig(QSettings* settings) : m_AutoConfigPrompted(false), m_CryptoEnabled(false), m_AutoHide(false), + m_AutoStart(false), m_MinimizeToTray(false) { Q_ASSERT(m_pSettings); @@ -157,7 +158,10 @@ void AppConfig::loadSettings() m_ElevateMode = static_cast(elevateMode.toInt()); m_AutoConfigPrompted = settings().value("autoConfigPrompted", false).toBool(); m_CryptoEnabled = settings().value("cryptoEnabled", true).toBool(); + // TODO: set default value of requireClientCertificate to true on Barrier 2.5.0 + m_RequireClientCertificate = settings().value("requireClientCertificate", false).toBool(); m_AutoHide = settings().value("autoHide", false).toBool(); + m_AutoStart = settings().value("autoStart", false).toBool(); m_MinimizeToTray = settings().value("minimizeToTray", false).toBool(); } @@ -179,7 +183,9 @@ void AppConfig::saveSettings() settings().setValue("elevateModeEnum", static_cast(m_ElevateMode)); settings().setValue("autoConfigPrompted", m_AutoConfigPrompted); settings().setValue("cryptoEnabled", m_CryptoEnabled); + settings().setValue("requireClientCertificate", m_RequireClientCertificate); settings().setValue("autoHide", m_AutoHide); + settings().setValue("autoStart", m_AutoStart); settings().setValue("minimizeToTray", m_MinimizeToTray); settings().sync(); } @@ -208,7 +214,7 @@ void AppConfig::setElevateMode(ElevateMode em) { m_ElevateMode = em; } void AppConfig::setAutoConfig(bool autoConfig) { m_AutoConfig = autoConfig; } -bool AppConfig::autoConfigPrompted() { return m_AutoConfigPrompted; } +bool AppConfig::autoConfigPrompted() { return m_AutoConfigPrompted; } void AppConfig::setAutoConfigPrompted(bool prompted) { m_AutoConfigPrompted = prompted; } @@ -222,10 +228,18 @@ void AppConfig::setCryptoEnabled(bool e) { m_CryptoEnabled = e; } bool AppConfig::getCryptoEnabled() const { return m_CryptoEnabled; } +void AppConfig::setRequireClientCertificate(bool e) { m_RequireClientCertificate = e; } + +bool AppConfig::getRequireClientCertificate() const { return m_RequireClientCertificate; } + void AppConfig::setAutoHide(bool b) { m_AutoHide = b; } bool AppConfig::getAutoHide() { return m_AutoHide; } +void AppConfig::setAutoStart(bool b) { m_AutoStart = b; } + +bool AppConfig::getAutoStart() { return m_AutoStart; } + void AppConfig::setMinimizeToTray(bool b) { m_MinimizeToTray = b; } bool AppConfig::getMinimizeToTray() { return m_MinimizeToTray; } diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index c9ed38d..0dabb18 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -91,9 +91,15 @@ class AppConfig: public QObject void setCryptoEnabled(bool e); bool getCryptoEnabled() const; + void setRequireClientCertificate(bool e); + bool getRequireClientCertificate() const; + void setAutoHide(bool b); bool getAutoHide(); + void setAutoStart(bool b); + bool getAutoStart(); + void setMinimizeToTray(bool b); bool getMinimizeToTray(); @@ -129,7 +135,9 @@ protected: ElevateMode m_ElevateMode; bool m_AutoConfigPrompted; bool m_CryptoEnabled; + bool m_RequireClientCertificate = false; bool m_AutoHide; + bool m_AutoStart; bool m_MinimizeToTray; static const char m_BarriersName[]; diff --git a/src/gui/src/BaseConfig.cpp b/src/gui/src/BaseConfig.cpp index 241f590..0f31493 100644 --- a/src/gui/src/BaseConfig.cpp +++ b/src/gui/src/BaseConfig.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -44,4 +44,3 @@ const char* BaseConfig::m_SwitchCornerNames[] = "bottom-left", "bottom-right" }; - diff --git a/src/gui/src/BaseConfig.h b/src/gui/src/BaseConfig.h index cf41ac6..9dc6b65 100644 --- a/src/gui/src/BaseConfig.h +++ b/src/gui/src/BaseConfig.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/gui/src/Fingerprint.cpp b/src/gui/src/Fingerprint.cpp deleted file mode 100644 index 24c8a1a..0000000 --- a/src/gui/src/Fingerprint.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2015-2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "Fingerprint.h" - -#include "common/DataDirectories.h" - -#include -#include - -static const char kDirName[] = "SSL/Fingerprints"; -static const char kLocalFilename[] = "Local.txt"; -static const char kTrustedServersFilename[] = "TrustedServers.txt"; -static const char kTrustedClientsFilename[] = "TrustedClients.txt"; - -Fingerprint::Fingerprint(const QString& filename) -{ - m_Filename = filename; -} - -void Fingerprint::trust(const QString& fingerprintText, bool append) -{ - Fingerprint::persistDirectory(); - - QIODevice::OpenMode openMode; - if (append) { - openMode = QIODevice::Append; - } - else { - openMode = QIODevice::WriteOnly; - } - - QFile file(filePath()); - if (file.open(openMode)) - { - QTextStream out(&file); - out << fingerprintText << "\n"; - file.close(); - } -} - -bool Fingerprint::fileExists() const -{ - QString dirName = Fingerprint::directoryPath(); - if (!QDir(dirName).exists()) { - return false; - } - - QFile file(filePath()); - return file.exists(); -} - -bool Fingerprint::isTrusted(const QString& fingerprintText) -{ - QStringList list = readList(); - foreach (QString trusted, list) - { - if (trusted == fingerprintText) { - return true; - } - } - return false; -} - -QStringList Fingerprint::readList(const int readTo) -{ - QStringList list; - - QString dirName = Fingerprint::directoryPath(); - if (!QDir(dirName).exists()) { - return list; - } - - QFile file(filePath()); - - if (file.open(QIODevice::ReadOnly)) - { - QTextStream in(&file); - while (!in.atEnd()) - { - list.append(in.readLine()); - if (list.size() == readTo) { - break; - } - } - file.close(); - } - - return list; -} - -QString Fingerprint::readFirst() -{ - QStringList list = readList(1); - return list.at(0); -} - -QString Fingerprint::filePath() const -{ - QString dir = Fingerprint::directoryPath(); - return QString("%1/%2").arg(dir).arg(m_Filename); -} - -void Fingerprint::persistDirectory() -{ - QDir dir(Fingerprint::directoryPath()); - if (!dir.exists()) { - dir.mkpath("."); - } -} - -QString Fingerprint::directoryPath() -{ - auto profileDir = QString::fromStdString(DataDirectories::profile()); - - return QString("%1/%2") - .arg(profileDir) - .arg(kDirName); -} - -Fingerprint Fingerprint::local() -{ - return Fingerprint(kLocalFilename); -} - -Fingerprint Fingerprint::trustedServers() -{ - return Fingerprint(kTrustedServersFilename); -} - -Fingerprint Fingerprint::trustedClients() -{ - return Fingerprint(kTrustedClientsFilename); -} diff --git a/src/gui/src/Fingerprint.h b/src/gui/src/Fingerprint.h deleted file mode 100644 index 5a38d20..0000000 --- a/src/gui/src/Fingerprint.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2015-2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -class Fingerprint -{ -public: - void trust(const QString& fingerprintText, bool append = true); - bool isTrusted(const QString& fingerprintText); - QStringList readList(const int readTo = -1); - QString readFirst(); - QString filePath() const; - bool fileExists() const; - - static Fingerprint local(); - static Fingerprint trustedServers(); - static Fingerprint trustedClients(); - static QString directoryPath(); - static void persistDirectory(); - -private: - Fingerprint(const QString& filename); - - QString m_Filename; -}; diff --git a/src/gui/src/FingerprintAcceptDialog.cpp b/src/gui/src/FingerprintAcceptDialog.cpp new file mode 100644 index 0000000..e0dc7e6 --- /dev/null +++ b/src/gui/src/FingerprintAcceptDialog.cpp @@ -0,0 +1,65 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "FingerprintAcceptDialog.h" +#include "ui_FingerprintAcceptDialog.h" +#include "net/SecureUtils.h" + +FingerprintAcceptDialog::FingerprintAcceptDialog(QWidget *parent, + BarrierType type, + const barrier::FingerprintData& fingerprint_sha1, + const barrier::FingerprintData& fingerprint_sha256) : + QDialog(parent), + ui_{std::make_unique()} +{ + ui_->setupUi(this); + + if (type == BarrierType::Server) { + ui_->label_sha1->hide(); + ui_->label_sha1_fingerprint_full->hide(); + } else { + ui_->label_sha1_fingerprint_full->setText( + QString::fromStdString(barrier::format_ssl_fingerprint(fingerprint_sha1.data))); + } + + ui_->label_sha256_fingerprint_full->setText( + QString::fromStdString(barrier::format_ssl_fingerprint_columns(fingerprint_sha256.data))); + ui_->label_sha256_fingerprint_randomart->setText( + QString::fromStdString(barrier::create_fingerprint_randomart(fingerprint_sha256.data))); + + QString explanation; + if (type == BarrierType::Server) { + explanation = tr("This is a client fingerprint. You should compare this " + "fingerprint to the one on your client's screen. If the " + "two don't match exactly, then it's probably not the client " + "you're expecting (it could be a malicious user).\n\n" + "To automatically trust this fingerprint for future " + "connections, click Yes. To reject this fingerprint and " + "disconnect the client, click No."); + } else { + explanation = tr("This is a server fingerprint. You should compare this " + "fingerprint to the one on your server's screen. If the " + "two don't match exactly, then it's probably not the server " + "you're expecting (it could be a malicious user).\n\n" + "To automatically trust this fingerprint for future " + "connections, click Yes. To reject this fingerprint and " + "disconnect from the server, click No."); + } + ui_->label_explanation->setText(explanation); +} + +FingerprintAcceptDialog::~FingerprintAcceptDialog() = default; diff --git a/src/gui/src/FingerprintAcceptDialog.h b/src/gui/src/FingerprintAcceptDialog.h new file mode 100644 index 0000000..da8884c --- /dev/null +++ b/src/gui/src/FingerprintAcceptDialog.h @@ -0,0 +1,45 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_GUI_FINGERPRINT_ACCEPT_DIALOG_H +#define BARRIER_GUI_FINGERPRINT_ACCEPT_DIALOG_H + +#include "net/FingerprintData.h" +#include "barrier/BarrierType.h" +#include +#include + +namespace Ui { +class FingerprintAcceptDialog; +} + +class FingerprintAcceptDialog : public QDialog +{ + Q_OBJECT + +public: + explicit FingerprintAcceptDialog(QWidget* parent, + BarrierType type, + const barrier::FingerprintData& fingerprint_sha1, + const barrier::FingerprintData& fingerprint_sha256); + ~FingerprintAcceptDialog() override; + +private: + std::unique_ptr ui_; +}; + +#endif // BARRIER_GUI_FINGERPRINT_ACCEPT_DIALOG_H diff --git a/src/gui/src/FingerprintAcceptDialog.ui b/src/gui/src/FingerprintAcceptDialog.ui new file mode 100644 index 0000000..9c181ec --- /dev/null +++ b/src/gui/src/FingerprintAcceptDialog.ui @@ -0,0 +1,174 @@ + + + FingerprintAcceptDialog + + + + 0 + 0 + 600 + 400 + + + + + 0 + 0 + + + + Security question + + + + QLayout::SetFixedSize + + + + + Qt::Horizontal + + + QDialogButtonBox::No|QDialogButtonBox::Yes + + + + + + + + 0 + 0 + + + + SHA1 (deprecated, compare to old servers only) + + + + + + + + 0 + 0 + + + + + + + true + + + 10 + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Do you trust this fingerprint? + + + + + + + + 0 + 0 + + + + + Courier + 75 + true + + + + + + + Qt::AlignCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + Qt::AlignCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + SHA256: + + + + + + + + + buttonBox + accepted() + FingerprintAcceptDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FingerprintAcceptDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/gui/src/Hotkey.cpp b/src/gui/src/Hotkey.cpp index c7138e7..4c008f8 100644 --- a/src/gui/src/Hotkey.cpp +++ b/src/gui/src/Hotkey.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -28,9 +28,9 @@ Hotkey::Hotkey() : QString Hotkey::text() const { - QString text = keySequence().toString(); + QString text = m_KeySequence.toString(); - if (keySequence().isMouseButton()) + if (m_KeySequence.isMouseButton()) return "mousebutton(" + text + ")"; return "keystroke(" + text + ")"; @@ -38,16 +38,16 @@ QString Hotkey::text() const void Hotkey::loadSettings(QSettings& settings) { - keySequence().loadSettings(settings); + m_KeySequence.loadSettings(settings); - actions().clear(); + m_Actions.clear(); int num = settings.beginReadArray("actions"); for (int i = 0; i < num; i++) { settings.setArrayIndex(i); Action a; a.loadSettings(settings); - actions().append(a); + m_Actions.push_back(a); } settings.endArray(); @@ -55,21 +55,33 @@ void Hotkey::loadSettings(QSettings& settings) void Hotkey::saveSettings(QSettings& settings) const { - keySequence().saveSettings(settings); + m_KeySequence.saveSettings(settings); settings.beginWriteArray("actions"); - for (int i = 0; i < actions().size(); i++) + for (int i = 0; i < m_Actions.size(); i++) { settings.setArrayIndex(i); - actions()[i].saveSettings(settings); + m_Actions[i].saveSettings(settings); } settings.endArray(); } QTextStream& operator<<(QTextStream& outStream, const Hotkey& hotkey) { - for (int i = 0; i < hotkey.actions().size(); i++) - outStream << "\t" << hotkey.text() << " = " << hotkey.actions()[i] << endl; + // Don't write config if there are no actions + if (hotkey.actions().size() == 0) { + return outStream; + } + + outStream << "\t" << hotkey.text() << " = "; + for (int i = 0; i < hotkey.actions().size(); i++) { + outStream << hotkey.actions()[i]; + if (i != hotkey.actions().size() - 1) { + outStream << ", "; + } + } + + outStream << "\n"; return outStream; } diff --git a/src/gui/src/Hotkey.h b/src/gui/src/Hotkey.h index 475da02..5e6fc98 100644 --- a/src/gui/src/Hotkey.h +++ b/src/gui/src/Hotkey.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -27,40 +27,35 @@ #include "Action.h" #include "KeySequence.h" +#include + class HotkeyDialog; class ServerConfigDialog; class QSettings; class Hotkey { - friend class HotkeyDialog; - friend class ServerConfigDialog; - friend QTextStream& operator<<(QTextStream& outStream, const Hotkey& hotkey); - public: Hotkey(); - public: QString text() const; const KeySequence& keySequence() const { return m_KeySequence; } - const ActionList& actions() const { return m_Actions; } + void setKeySequence(const KeySequence& seq) { m_KeySequence = seq; } + + const std::vector& actions() const { return m_Actions; } + void appendAction(const Action& action) { m_Actions.push_back(action); } + void setAction(int index, const Action& action) { m_Actions[index] = action; } + void removeAction(int index) { m_Actions.erase(m_Actions.begin() + index); } void loadSettings(QSettings& settings); void saveSettings(QSettings& settings) const; - protected: - KeySequence& keySequence() { return m_KeySequence; } - void setKeySequence(const KeySequence& seq) { m_KeySequence = seq; } - ActionList& actions() { return m_Actions; } - private: KeySequence m_KeySequence; - ActionList m_Actions; + std::vector m_Actions; }; -typedef QList HotkeyList; - QTextStream& operator<<(QTextStream& outStream, const Hotkey& hotkey); #endif diff --git a/src/gui/src/HotkeyDialog.cpp b/src/gui/src/HotkeyDialog.cpp index ef25c3f..5b3e5a8 100644 --- a/src/gui/src/HotkeyDialog.cpp +++ b/src/gui/src/HotkeyDialog.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/gui/src/HotkeyDialog.h b/src/gui/src/HotkeyDialog.h index a13fc24..3a9967c 100644 --- a/src/gui/src/HotkeyDialog.h +++ b/src/gui/src/HotkeyDialog.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/gui/src/IpcClient.h b/src/gui/src/IpcClient.h index cd398b3..6040029 100644 --- a/src/gui/src/IpcClient.h +++ b/src/gui/src/IpcClient.h @@ -28,7 +28,7 @@ class IpcReader; class IpcClient : public QObject { - Q_OBJECT + Q_OBJECT public: IpcClient(); diff --git a/src/gui/src/KeySequence.cpp b/src/gui/src/KeySequence.cpp index cc74cb2..ddf7d33 100644 --- a/src/gui/src/KeySequence.cpp +++ b/src/gui/src/KeySequence.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -55,6 +55,8 @@ static const struct { Qt::Key_Help, "Help" }, { Qt::Key_Enter, "KP_Enter" }, { Qt::Key_Clear, "Clear" }, + { Qt::Key_Comma, "Comma" }, + { Qt::Key_Semicolon, "Semicolon" }, { Qt::Key_Back, "WWWBack" }, { Qt::Key_Forward, "WWWForward" }, @@ -152,12 +154,12 @@ bool KeySequence::appendKey(int key, int modifiers) void KeySequence::loadSettings(QSettings& settings) { - sequence().clear(); + m_Sequence.clear(); int num = settings.beginReadArray("keys"); for (int i = 0; i < num; i++) { settings.setArrayIndex(i); - sequence().append(settings.value("key", 0).toInt()); + m_Sequence.append(settings.value("key", 0).toInt()); } settings.endArray(); @@ -168,10 +170,10 @@ void KeySequence::loadSettings(QSettings& settings) void KeySequence::saveSettings(QSettings& settings) const { settings.beginWriteArray("keys"); - for (int i = 0; i < sequence().size(); i++) + for (int i = 0; i < m_Sequence.size(); i++) { settings.setArrayIndex(i); - settings.setValue("key", sequence()[i]); + settings.setValue("key", m_Sequence[i]); } settings.endArray(); } @@ -211,23 +213,22 @@ QString KeySequence::keyToString(int key) // treat key pad like normal keys (FIXME: we should have another lookup table for keypad keys instead) key &= ~Qt::KeypadModifier; + // a special key? + int i = 0; + while (keyname[i].name) { + if (key == keyname[i].key) + return QString::fromUtf8(keyname[i].name); + i++; + } + // a printable 7 bit character? - if (key < 0x80 && key != Qt::Key_Space) + if (key < 0x80) return QChar(key & 0x7f).toLower(); // a function key? if (key >= Qt::Key_F1 && key <= Qt::Key_F35) return QString::fromUtf8("F%1").arg(key - Qt::Key_F1 + 1); - // a special key? - int i=0; - while (keyname[i].name) - { - if (key == keyname[i].key) - return QString::fromUtf8(keyname[i].name); - i++; - } - // representable in ucs2? if (key < 0x10000) return QString("\\u%1").arg(QChar(key).toLower().unicode(), 4, 16, QChar('0')); diff --git a/src/gui/src/KeySequence.h b/src/gui/src/KeySequence.h index 8d9706d..0331091 100644 --- a/src/gui/src/KeySequence.h +++ b/src/gui/src/KeySequence.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -32,7 +32,7 @@ class KeySequence public: QString toString() const; - bool appendKey(int modifiers, int key); + bool appendKey(int key, int modifiers); bool appendMouseButton(int button); bool isMouseButton() const; bool valid() const { return m_IsValid; } @@ -44,7 +44,6 @@ class KeySequence private: void setValid(bool b) { m_IsValid = b; } void setModifiers(int i) { m_Modifiers = i; } - QList& sequence() { return m_Sequence; } private: QList m_Sequence; @@ -55,4 +54,3 @@ class KeySequence }; #endif - diff --git a/src/gui/src/KeySequenceWidget.cpp b/src/gui/src/KeySequenceWidget.cpp index e5823e1..067c5bf 100644 --- a/src/gui/src/KeySequenceWidget.cpp +++ b/src/gui/src/KeySequenceWidget.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/gui/src/KeySequenceWidget.h b/src/gui/src/KeySequenceWidget.h index eaef514..636375b 100644 --- a/src/gui/src/KeySequenceWidget.h +++ b/src/gui/src/KeySequenceWidget.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -78,4 +78,3 @@ class KeySequenceWidget : public QPushButton }; #endif - diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 01133bf..d17548a 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -20,18 +20,21 @@ #include "MainWindow.h" -#include "Fingerprint.h" #include "AboutDialog.h" #include "ServerConfigDialog.h" #include "SettingsDialog.h" #include "ZeroconfService.h" #include "DataDownloader.h" #include "CommandProcess.h" +#include "FingerprintAcceptDialog.h" #include "QUtility.h" #include "ProcessorArch.h" #include "SslCertificate.h" #include "ShutdownCh.h" +#include "base/String.h" #include "common/DataDirectories.h" +#include "net/FingerprintDatabase.h" +#include "net/SecureUtils.h" #include #include @@ -53,17 +56,20 @@ #include #endif +static const QString allFilesFilter(QObject::tr("All files (*.*)")); #if defined(Q_OS_WIN) static const char barrierConfigName[] = "barrier.sgc"; -static const QString barrierConfigFilter(QObject::tr("Barrier Configurations (*.sgc);;All files (*.*)")); +static const QString barrierConfigFilter(QObject::tr("Barrier Configurations (*.sgc)")); static QString bonjourBaseUrl = "http://binaries.symless.com/bonjour/"; static const char bonjourFilename32[] = "Bonjour.msi"; static const char bonjourFilename64[] = "Bonjour64.msi"; static const char bonjourTargetFilename[] = "Bonjour.msi"; #else static const char barrierConfigName[] = "barrier.conf"; -static const QString barrierConfigFilter(QObject::tr("Barrier Configurations (*.conf);;All files (*.*)")); +static const QString barrierConfigFilter(QObject::tr("Barrier Configurations (*.conf)")); #endif +static const QString barrierConfigOpenFilter(barrierConfigFilter + ";;" + allFilesFilter); +static const QString barrierConfigSaveFilter(barrierConfigFilter); static const char* barrierIconFiles[] = { @@ -80,6 +86,14 @@ static const char* barrierIconFiles[] = #endif }; +static const char* barrierIconNames[] = +{ + "barrier-disconnected", + "barrier-disconnected", + "barrier-connected", + "barrier-transfering" +}; + static const char* barrierLargeIcon = ":/res/icons/256x256/barrier.ico"; MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : @@ -145,9 +159,22 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_pComboServerList->hide(); m_pLabelPadlock->hide(); + frame_fingerprint_details->hide(); updateSSLFingerprint(); + connect(toolbutton_show_fingerprint, &QToolButton::clicked, [this](bool checked) + { + m_fingerprint_expanded = !m_fingerprint_expanded; + if (m_fingerprint_expanded) { + frame_fingerprint_details->show(); + toolbutton_show_fingerprint->setArrowType(Qt::ArrowType::UpArrow); + } else { + frame_fingerprint_details->hide(); + toolbutton_show_fingerprint->setArrowType(Qt::ArrowType::DownArrow); + } + }); + // resize window to smallest reasonable size resize(0, 0); } @@ -191,7 +218,7 @@ void MainWindow::open() // only start if user has previously started. this stops the gui from // auto hiding before the user has configured barrier (which of course // confuses first time users, who think barrier has crashed). - if (appConfig().startedBefore() && appConfig().processMode() == Desktop) { + if (appConfig().startedBefore() && appConfig().getAutoStart()) { m_SuppressEmptyServerWarning = true; startBarrier(); m_SuppressEmptyServerWarning = false; @@ -296,7 +323,7 @@ void MainWindow::saveSettings() void MainWindow::setIcon(qBarrierState state) { if (m_pTrayIcon) { - QIcon icon = QIcon(barrierIconFiles[state]); + QIcon icon = QIcon::fromTheme(barrierIconNames[state], QIcon(barrierIconFiles[state])); #if defined(Q_OS_MAC) icon.setIsMask(true); #endif @@ -325,8 +352,7 @@ void MainWindow::logOutput() if (m_pBarrier) { QString text(m_pBarrier->readAllStandardOutput()); - foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) - { + for (QString line : text.split(QRegExp("\r|\n|\r\n"))) { if (!line.isEmpty()) { appendLogRaw(line); @@ -363,7 +389,7 @@ void MainWindow::appendLogError(const QString& text) void MainWindow::appendLogRaw(const QString& text) { - foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) { + for (QString line : text.split(QRegExp("\r|\n|\r\n"))) { if (!line.isEmpty()) { m_pLogWindow->appendRaw(line); updateFromLogLine(line); @@ -402,41 +428,57 @@ void MainWindow::checkConnected(const QString& line) void MainWindow::checkFingerprint(const QString& line) { - QRegExp fingerprintRegex(".*server fingerprint: ([A-F0-9:]+)"); + QRegExp fingerprintRegex(".*peer fingerprint \\(SHA1\\): ([A-F0-9:]+) \\(SHA256\\): ([A-F0-9:]+)"); if (!fingerprintRegex.exactMatch(line)) { return; } - QString fingerprint = fingerprintRegex.cap(1); - if (Fingerprint::trustedServers().isTrusted(fingerprint)) { + barrier::FingerprintData fingerprint_sha1 = { + barrier::fingerprint_type_to_string(barrier::FingerprintType::SHA1), + barrier::string::from_hex(fingerprintRegex.cap(1).toStdString()) + }; + + barrier::FingerprintData fingerprint_sha256 = { + barrier::fingerprint_type_to_string(barrier::FingerprintType::SHA256), + barrier::string::from_hex(fingerprintRegex.cap(2).toStdString()) + }; + + bool is_client = barrier_type() == BarrierType::Client; + + auto db_path = is_client + ? barrier::DataDirectories::trusted_servers_ssl_fingerprints_path() + : barrier::DataDirectories::trusted_clients_ssl_fingerprints_path(); + + auto db_dir = db_path.parent_path(); + if (!barrier::fs::exists(db_dir)) { + barrier::fs::create_directories(db_dir); + } + + // We compare only SHA256 fingerprints, but show both SHA1 and SHA256 so that the users can + // still verify fingerprints on old Barrier servers. This way the only time when we are exposed + // to SHA1 vulnerabilities is when the user is reconnecting again. + barrier::FingerprintDatabase db; + db.read(db_path); + if (db.is_trusted(fingerprint_sha256)) { return; } static bool messageBoxAlreadyShown = false; if (!messageBoxAlreadyShown) { - stopBarrier(); + if (is_client) { + stopBarrier(); + } messageBoxAlreadyShown = true; - QMessageBox::StandardButton fingerprintReply = - QMessageBox::information( - this, tr("Security question"), - tr("Do you trust this fingerprint?\n\n" - "%1\n\n" - "This is a server fingerprint. You should compare this " - "fingerprint to the one on your server's screen. If the " - "two don't match exactly, then it's probably not the server " - "you're expecting (it could be a malicious user).\n\n" - "To automatically trust this fingerprint for future " - "connections, click Yes. To reject this fingerprint and " - "disconnect from the server, click No.") - .arg(fingerprint), - QMessageBox::Yes | QMessageBox::No); - - if (fingerprintReply == QMessageBox::Yes) { + FingerprintAcceptDialog dialog{this, barrier_type(), fingerprint_sha1, fingerprint_sha256}; + if (dialog.exec() == QDialog::Accepted) { // restart core process after trusting fingerprint. - Fingerprint::trustedServers().trust(fingerprint); - startBarrier(); + db.add_trusted(fingerprint_sha256); + db.write(db_path); + if (is_client) { + startBarrier(); + } } messageBoxAlreadyShown = false; @@ -506,8 +548,8 @@ void MainWindow::startBarrier() #endif - if (m_AppConfig->getCryptoEnabled()) { - args << "--enable-crypto"; + if (!m_AppConfig->getCryptoEnabled()) { + args << "--disable-crypto"; } #if defined(Q_OS_WIN) @@ -515,11 +557,11 @@ void MainWindow::startBarrier() // launched the process (e.g. when launched with elevation). setting the // profile dir on launch ensures it uses the same profile dir is used // no matter how its relaunched. - args << "--profile-dir" << QString::fromStdString("\"" + DataDirectories::profile() + "\""); + args << "--profile-dir" << QString::fromStdString("\"" + barrier::DataDirectories::profile().u8string() + "\""); #endif - if ((barrierType() == barrierClient && !clientArgs(args, app)) - || (barrierType() == barrierServer && !serverArgs(args, app))) + if ((barrier_type() == BarrierType::Client && !clientArgs(args, app)) + || (barrier_type() == BarrierType::Server && !serverArgs(args, app))) { stopBarrier(); return; @@ -534,7 +576,7 @@ void MainWindow::startBarrier() m_pLogWindow->startNewInstance(); - appendLogInfo("starting " + QString(barrierType() == barrierServer ? "server" : "client")); + appendLogInfo("starting " + QString(barrier_type() == BarrierType::Server ? "server" : "client")); qDebug() << args; @@ -615,7 +657,7 @@ QString MainWindow::configFilename() if (m_pRadioInternalConfig->isChecked()) { // TODO: no need to use a temporary file, since we need it to - // be permenant (since it'll be used for Windows services, etc). + // be permanent (since it'll be used for Windows services, etc). m_pTempConfigFile = new QTemporaryFile(); if (!m_pTempConfigFile->open()) { @@ -644,6 +686,11 @@ QString MainWindow::configFilename() return filename; } +BarrierType MainWindow::barrier_type() const +{ + return m_pGroupClient->isChecked() ? BarrierType::Client : BarrierType::Server; +} + QString MainWindow::address() { QString address = appConfig().networkInterface(); @@ -680,6 +727,10 @@ bool MainWindow::serverArgs(QStringList& args, QString& app) args << "--log" << appConfig().logFilenameCmd(); } + if (!appConfig().getRequireClientCertificate()) { + args << "--disable-client-cert-checking"; + } + QString configFilename = this->configFilename(); #if defined(Q_OS_WIN) // wrap in quotes in case username contains spaces. @@ -720,7 +771,7 @@ void MainWindow::stopBarrier() void MainWindow::stopService() { - // send empty command to stop service from laucning anything. + // send empty command to stop service from launching anything. m_IpcClient.sendCommand("", appConfig().elevateMode()); } @@ -915,6 +966,14 @@ void MainWindow::changeEvent(QEvent* event) QMainWindow::changeEvent(event); } +bool MainWindow::event(QEvent* event) +{ + if (event->type() == QEvent::LayoutRequest) { + setFixedSize(sizeHint()); + } + return QMainWindow::event(event); +} + void MainWindow::updateZeroconfService() { QMutexLocker locker(&m_UpdateZeroconfMutex); @@ -926,7 +985,7 @@ void MainWindow::updateZeroconfService() m_pZeroconfService = NULL; } - if (m_AppConfig->autoConfig() || barrierType() == barrierServer) { + if (m_AppConfig->autoConfig() || barrier_type() == BarrierType::Server) { m_pZeroconfService = new ZeroconfService(this); } } @@ -949,14 +1008,53 @@ void MainWindow::updateSSLFingerprint() { if (m_AppConfig->getCryptoEnabled() && m_pSslCertificate == nullptr) { m_pSslCertificate = new SslCertificate(this); + connect(m_pSslCertificate, &SslCertificate::info, [&](QString info) + { + appendLogInfo(info); + }); m_pSslCertificate->generateCertificate(); } - if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists()) { - m_pLabelLocalFingerprint->setText(Fingerprint::local().readFirst()); - m_pLabelLocalFingerprint->setTextInteractionFlags(Qt::TextSelectableByMouse); - } else { - m_pLabelLocalFingerprint->setText("Disabled"); + + toolbutton_show_fingerprint->setEnabled(false); + m_pLabelLocalFingerprint->setText("Disabled"); + + if (!m_AppConfig->getCryptoEnabled()) { + return; + } + + auto local_path = barrier::DataDirectories::local_ssl_fingerprints_path(); + if (!barrier::fs::exists(local_path)) { + return; } + + barrier::FingerprintDatabase db; + db.read(local_path); + if (db.fingerprints().size() != 2) { + return; + } + + for (const auto& fingerprint : db.fingerprints()) { + if (fingerprint.algorithm == "sha1") { + auto fingerprint_str = barrier::format_ssl_fingerprint(fingerprint.data); + label_sha1_fingerprint_full->setText(QString::fromStdString(fingerprint_str)); + continue; + } + + if (fingerprint.algorithm == "sha256") { + auto fingerprint_str = barrier::format_ssl_fingerprint(fingerprint.data); + fingerprint_str.resize(40); + fingerprint_str += " ..."; + + auto fingerprint_str_cols = barrier::format_ssl_fingerprint_columns(fingerprint.data); + auto fingerprint_randomart = barrier::create_fingerprint_randomart(fingerprint.data); + + m_pLabelLocalFingerprint->setText(QString::fromStdString(fingerprint_str)); + label_sha256_fingerprint_full->setText(QString::fromStdString(fingerprint_str_cols)); + label_sha256_randomart->setText(QString::fromStdString(fingerprint_randomart)); + } + } + + toolbutton_show_fingerprint->setEnabled(true); } void MainWindow::on_m_pGroupClient_toggled(bool on) @@ -977,7 +1075,7 @@ void MainWindow::on_m_pGroupServer_toggled(bool on) bool MainWindow::on_m_pButtonBrowseConfigFile_clicked() { - QString fileName = QFileDialog::getOpenFileName(this, tr("Browse for a barriers config file"), QString(), barrierConfigFilter); + QString fileName = QFileDialog::getOpenFileName(this, tr("Browse for a barriers config file"), QString(), barrierConfigOpenFilter); if (!fileName.isEmpty()) { @@ -988,9 +1086,9 @@ bool MainWindow::on_m_pButtonBrowseConfigFile_clicked() return false; } -bool MainWindow::on_m_pActionSave_triggered() +bool MainWindow::on_m_pActionSave_triggered() { - QString fileName = QFileDialog::getSaveFileName(this, tr("Save configuration as..."), QString(), tr("Barrier Configuration (*.sgc)")); + QString fileName = QFileDialog::getSaveFileName(this, tr("Save configuration as..."), QString(), barrierConfigSaveFilter); if (!fileName.isEmpty() && !serverConfig().save(fileName)) { diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index c115b91..0c582c9 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -20,6 +20,8 @@ #define MAINWINDOW__H +#include "barrier/BarrierType.h" + #include #include #include @@ -76,12 +78,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase barrierTransfering }; - enum qBarrierType - { - barrierClient, - barrierServer - }; - enum qLevel { Error, Info @@ -98,7 +94,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase public: void setVisible(bool visible); - int barrierType() const { return m_pGroupClient->isChecked() ? barrierClient : barrierServer; } + BarrierType barrier_type() const; int barrierState() const { return m_BarrierState; } QString hostname() const { return m_pLineEditHostname->text(); } QString configFilename(); @@ -157,6 +153,7 @@ public slots: void stopService(); void stopDesktop(); void changeEvent(QEvent* event); + bool event(QEvent* event); void retranslateMenuBar(); #if defined(Q_OS_WIN) bool isServiceRunning(QString name); @@ -202,6 +199,8 @@ public slots: QStringList m_PendingClientNames; LogWindow *m_pLogWindow; + bool m_fingerprint_expanded = false; + private slots: void on_m_pCheckBoxAutoConfig_toggled(bool checked); void on_m_pComboServerList_currentIndexChanged(QString ); @@ -211,4 +210,3 @@ private slots: }; #endif - diff --git a/src/gui/src/MainWindowBase.ui b/src/gui/src/MainWindowBase.ui index 117405c..88994cf 100644 --- a/src/gui/src/MainWindowBase.ui +++ b/src/gui/src/MainWindowBase.ui @@ -2,31 +2,20 @@ MainWindowBase - - - 0 - 0 - 600 - 550 - - 0 0 - - - 600 - 0 - - Barrier + + QLayout::SetFixedSize + @@ -66,30 +55,6 @@ - - - - - - - 0 - 0 - - - - SSL Fingerprint: - - - - - - - - - - - - @@ -239,6 +204,107 @@ + + + + + + + 0 + 0 + + + + SSL Fingerprint: + + + + + + + + + + Qt::PlainText + + + + + + + ... + + + Qt::DownArrow + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + QLayout::SetMinimumSize + + + + + + Courier + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + SHA1 (deprecated, compare to old clients and servers only): + + + + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + SHA256: + + + + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + @@ -253,7 +319,7 @@ - :/res/icons/16x16/padlock.png + :/res/icons/16x16/padlock.png @@ -388,7 +454,7 @@ - Show &Log + Show &Log Show Log @@ -399,7 +465,7 @@ - + diff --git a/src/gui/src/NewScreenWidget.cpp b/src/gui/src/NewScreenWidget.cpp index 0336249..28e99fa 100644 --- a/src/gui/src/NewScreenWidget.cpp +++ b/src/gui/src/NewScreenWidget.cpp @@ -1,12 +1,12 @@ -/* +/* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -45,4 +45,3 @@ void NewScreenWidget::mousePressEvent(QMouseEvent* event) pDrag->exec(Qt::CopyAction, Qt::CopyAction); } - diff --git a/src/gui/src/NewScreenWidget.h b/src/gui/src/NewScreenWidget.h index 34f3269..6e52795 100644 --- a/src/gui/src/NewScreenWidget.h +++ b/src/gui/src/NewScreenWidget.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -37,4 +37,3 @@ class NewScreenWidget : public QLabel }; #endif - diff --git a/src/gui/src/QBarrierApplication.cpp b/src/gui/src/QBarrierApplication.cpp index f2da382..f9362c7 100644 --- a/src/gui/src/QBarrierApplication.cpp +++ b/src/gui/src/QBarrierApplication.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -38,8 +38,7 @@ QBarrierApplication::~QBarrierApplication() void QBarrierApplication::commitData(QSessionManager&) { - foreach(QWidget* widget, topLevelWidgets()) - { + for (QWidget* widget : topLevelWidgets()) { MainWindow* mainWindow = qobject_cast(widget); if (mainWindow) mainWindow->saveSettings(); diff --git a/src/gui/src/QBarrierApplication.h b/src/gui/src/QBarrierApplication.h index 95729b2..bd348b3 100644 --- a/src/gui/src/QBarrierApplication.h +++ b/src/gui/src/QBarrierApplication.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -44,4 +44,3 @@ class QBarrierApplication : public QApplication }; #endif - diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index 7757adf..2d05fc3 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -51,8 +51,7 @@ QString hash(const QString& string) QString getFirstMacAddress() { QString mac; - foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) - { + for (const QNetworkInterface &interface : QNetworkInterface::allInterfaces()) { mac = interface.hardwareAddress(); if (mac.size() != 0) { diff --git a/src/gui/src/Screen.cpp b/src/gui/src/Screen.cpp index 38d02e8..1e3b888 100644 --- a/src/gui/src/Screen.cpp +++ b/src/gui/src/Screen.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -125,8 +125,9 @@ QTextStream& Screen::writeAliasesSection(QTextStream& outStream) const { outStream << "\t" << name() << ":" << endl; - foreach (const QString& alias, aliases()) + for (const QString& alias : aliases()) { outStream << "\t\t" << alias << endl; + } } return outStream; diff --git a/src/gui/src/Screen.h b/src/gui/src/Screen.h index d728af1..6496221 100644 --- a/src/gui/src/Screen.h +++ b/src/gui/src/Screen.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -101,10 +101,7 @@ class Screen : public BaseConfig bool m_Swapped; }; -typedef QList ScreenList; - QDataStream& operator<<(QDataStream& outStream, const Screen& screen); QDataStream& operator>>(QDataStream& inStream, Screen& screen); #endif - diff --git a/src/gui/src/ScreenSettingsDialog.cpp b/src/gui/src/ScreenSettingsDialog.cpp index 123bb8a..9055e35 100644 --- a/src/gui/src/ScreenSettingsDialog.cpp +++ b/src/gui/src/ScreenSettingsDialog.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -152,4 +152,3 @@ void ScreenSettingsDialog::on_m_pListAliases_itemSelectionChanged() { m_pButtonRemoveAlias->setEnabled(!m_pListAliases->selectedItems().isEmpty()); } - diff --git a/src/gui/src/ScreenSettingsDialog.h b/src/gui/src/ScreenSettingsDialog.h index 1c525d2..953d1f8 100644 --- a/src/gui/src/ScreenSettingsDialog.h +++ b/src/gui/src/ScreenSettingsDialog.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -50,4 +50,3 @@ class ScreenSettingsDialog : public QDialog, public Ui::ScreenSettingsDialogBase }; #endif - diff --git a/src/gui/src/ScreenSetupModel.cpp b/src/gui/src/ScreenSetupModel.cpp index fce1a8c..5c933f8 100644 --- a/src/gui/src/ScreenSetupModel.cpp +++ b/src/gui/src/ScreenSetupModel.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -24,7 +24,7 @@ const QString ScreenSetupModel::m_MimeType = "application/x-qbarrier-screen"; -ScreenSetupModel::ScreenSetupModel(ScreenList& screens, int numColumns, int numRows) : +ScreenSetupModel::ScreenSetupModel(std::vector& screens, int numColumns, int numRows) : QAbstractTableModel(NULL), m_Screens(screens), m_NumColumns(numColumns), @@ -71,7 +71,7 @@ Qt::ItemFlags ScreenSetupModel::flags(const QModelIndex& index) const if (!screen(index).isNull()) return Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled; - return Qt::ItemIsDropEnabled; + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled; } Qt::DropActions ScreenSetupModel::supportedDropActions() const @@ -91,9 +91,10 @@ QMimeData* ScreenSetupModel::mimeData(const QModelIndexList& indexes) const QDataStream stream(&encodedData, QIODevice::WriteOnly); - foreach (const QModelIndex& index, indexes) + for (const QModelIndex& index : indexes) { if (index.isValid()) stream << index.column() << index.row() << screen(index); + } pMimeData->setData(m_MimeType, encodedData); @@ -109,7 +110,7 @@ bool ScreenSetupModel::dropMimeData(const QMimeData* data, Qt::DropAction action return false; if (!parent.isValid() || row != -1 || column != -1) - return false; + return false; QByteArray encodedData = data->data(m_MimeType); QDataStream stream(&encodedData, QIODevice::ReadOnly); @@ -140,4 +141,3 @@ bool ScreenSetupModel::dropMimeData(const QMimeData* data, Qt::DropAction action return true; } - diff --git a/src/gui/src/ScreenSetupModel.h b/src/gui/src/ScreenSetupModel.h index ba46af3..0f558b4 100644 --- a/src/gui/src/ScreenSetupModel.h +++ b/src/gui/src/ScreenSetupModel.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -38,7 +38,7 @@ class ScreenSetupModel : public QAbstractTableModel friend class ServerConfigDialog; public: - ScreenSetupModel(ScreenList& screens, int numColumns, int numRows); + ScreenSetupModel(std::vector& screens, int numColumns, int numRows); public: static const QString& mimeType() { return m_MimeType; } @@ -60,7 +60,7 @@ class ScreenSetupModel : public QAbstractTableModel Screen& screen(int column, int row) { return m_Screens[row * m_NumColumns + column]; } private: - ScreenList& m_Screens; + std::vector& m_Screens; const int m_NumColumns; const int m_NumRows; @@ -68,4 +68,3 @@ class ScreenSetupModel : public QAbstractTableModel }; #endif - diff --git a/src/gui/src/ScreenSetupView.cpp b/src/gui/src/ScreenSetupView.cpp index 46e7099..5459f23 100644 --- a/src/gui/src/ScreenSetupView.cpp +++ b/src/gui/src/ScreenSetupView.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -30,6 +30,7 @@ ScreenSetupView::ScreenSetupView(QWidget* parent) : setDropIndicatorShown(true); setDragDropMode(DragDrop); setSelectionMode(SingleSelection); + setTabKeyNavigation(false); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -65,18 +66,83 @@ void ScreenSetupView::resizeEvent(QResizeEvent* event) event->ignore(); } +void ScreenSetupView::enter(const QModelIndex& index) +{ + if (!index.isValid()) + return; + Screen& screen = model()->screen(index); + if (screen.isNull()) + screen = Screen(tr("Unnamed")); + ScreenSettingsDialog dlg(this, &screen); + dlg.exec(); +} + +void ScreenSetupView::remove(const QModelIndex& index) +{ + if (!index.isValid()) + return; + Screen& screen = model()->screen(index); + if (!screen.isNull()) { + screen = Screen(); + emit dataChanged(index, index); + } +} + +void ScreenSetupView::keyPressEvent(QKeyEvent* event) +{ + if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Delete) + { + QModelIndexList indexes = selectedIndexes(); + if (indexes.count() == 1 && indexes[0].isValid()) + { + if (event->key() == Qt::Key_Return) + enter(indexes[0]); + else if (event->key() == Qt::Key_Delete) + remove(indexes[0]); + } + // Do not let base handle the event, at least not for return key because it + // results in next esc/return key in the opened Screen Settings dialog not + // only closing that but also the parent Server Configuration dialog. + } + else if ((event->modifiers() & Qt::ControlModifier) + && (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right + || event->key() == Qt::Key_Up || event->key() == Qt::Key_Down)) + { + QModelIndexList indexes = selectedIndexes(); + if (indexes.count() == 1 && indexes[0].isValid()) + { + const QModelIndex& fromIndex = indexes[0]; + QModelIndex toIndex; + + if (event->key() == Qt::Key_Left) + toIndex = fromIndex.sibling(fromIndex.row(), fromIndex.column() - 1); + else if (event->key() == Qt::Key_Right) + toIndex = fromIndex.sibling(fromIndex.row(), fromIndex.column() + 1); + else if (event->key() == Qt::Key_Up) + toIndex = fromIndex.sibling(fromIndex.row() - 1, fromIndex.column()); + else if (event->key() == Qt::Key_Down) + toIndex = fromIndex.sibling(fromIndex.row() + 1, fromIndex.column()); + + if (toIndex.isValid() && fromIndex != toIndex) + std::swap(model()->screen(fromIndex), model()->screen(toIndex)); + } + // In this case let base also handle the event, because it will proceed moving + // the selection to target, update the view according to model changes etc. + QTableView::keyPressEvent(event); + } + else + { + QTableView::keyPressEvent(event); + } +} + void ScreenSetupView::mouseDoubleClickEvent(QMouseEvent* event) { if (event->buttons() & Qt::LeftButton) { int col = columnAt(event->pos().x()); int row = rowAt(event->pos().y()); - - if (!model()->screen(col, row).isNull()) - { - ScreenSettingsDialog dlg(this, &model()->screen(col, row)); - dlg.exec(); - } + enter(model()->createIndex(row, col)); } else event->ignore(); @@ -159,4 +225,3 @@ QStyleOptionViewItem ScreenSetupView::viewOptions() const option.textElideMode = Qt::ElideMiddle; return option; } - diff --git a/src/gui/src/ScreenSetupView.h b/src/gui/src/ScreenSetupView.h index a660511..64968a0 100644 --- a/src/gui/src/ScreenSetupView.h +++ b/src/gui/src/ScreenSetupView.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -44,6 +44,7 @@ class ScreenSetupView : public QTableView protected: void mouseDoubleClickEvent(QMouseEvent*) override; + void keyPressEvent(QKeyEvent*) override; void setTableSize(); void resizeEvent(QResizeEvent*) override; void dragEnterEvent(QDragEnterEvent* event) override; @@ -51,7 +52,9 @@ class ScreenSetupView : public QTableView void startDrag(Qt::DropActions supportedActions) override; QStyleOptionViewItem viewOptions() const override; void scrollTo(const QModelIndex&, ScrollHint) override {} + private: + void enter(const QModelIndex&); + void remove(const QModelIndex&); }; #endif - diff --git a/src/gui/src/ServerConfig.cpp b/src/gui/src/ServerConfig.cpp index 9fedd1e..3392572 100644 --- a/src/gui/src/ServerConfig.cpp +++ b/src/gui/src/ServerConfig.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -183,7 +183,7 @@ void ServerConfig::loadSettings() settings().setArrayIndex(i); Hotkey h; h.loadSettings(settings()); - hotkeys().append(h); + hotkeys().push_back(h); } settings().endArray(); @@ -212,17 +212,19 @@ QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config) { outStream << "section: screens" << endl; - foreach (const Screen& s, config.screens()) + for (const Screen& s : config.screens()) { if (!s.isNull()) s.writeScreensSection(outStream); + } outStream << "end" << endl << endl; outStream << "section: aliases" << endl; - foreach (const Screen& s, config.screens()) + for (const Screen& s : config.screens()) { if (!s.isNull()) s.writeAliasesSection(outStream); + } outStream << "end" << endl << endl; @@ -270,8 +272,9 @@ QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config) outStream << "\t" << "switchCornerSize = " << config.switchCornerSize() << endl; - foreach(const Hotkey& hotkey, config.hotkeys()) + for (const Hotkey& hotkey : config.hotkeys()) { outStream << hotkey; + } outStream << "end" << endl << endl; @@ -282,9 +285,10 @@ int ServerConfig::numScreens() const { int rval = 0; - foreach(const Screen& s, screens()) + for (const Screen& s : screens()) { if (!s.isNull()) rval++; + } return rval; } diff --git a/src/gui/src/ServerConfig.h b/src/gui/src/ServerConfig.h index c9648b0..9f3e2db 100644 --- a/src/gui/src/ServerConfig.h +++ b/src/gui/src/ServerConfig.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -44,7 +44,7 @@ class ServerConfig : public BaseConfig ~ServerConfig(); public: - const ScreenList& screens() const { return m_Screens; } + const std::vector& screens() const { return m_Screens; } int numColumns() const { return m_NumColumns; } int numRows() const { return m_NumRows; } bool hasHeartbeat() const { return m_HasHeartbeat; } @@ -59,7 +59,7 @@ class ServerConfig : public BaseConfig bool switchCorner(SwitchCorner c) const { return m_SwitchCorners[static_cast(c)]; } int switchCornerSize() const { return m_SwitchCornerSize; } const QList& switchCorners() const { return m_SwitchCorners; } - const HotkeyList& hotkeys() const { return m_Hotkeys; } + const std::vector& hotkeys() const { return m_Hotkeys; } bool ignoreAutoConfigClient() const { return m_IgnoreAutoConfigClient; } bool enableDragAndDrop() const { return m_EnableDragAndDrop; } bool clipboardSharing() const { return m_ClipboardSharing; } @@ -73,9 +73,9 @@ class ServerConfig : public BaseConfig protected: QSettings& settings() { return *m_pSettings; } - ScreenList& screens() { return m_Screens; } - void setScreens(const ScreenList& screens) { m_Screens = screens; } - void addScreen(const Screen& screen) { m_Screens.append(screen); } + std::vector& screens() { return m_Screens; } + void setScreens(const std::vector& screens) { m_Screens = screens; } + void addScreen(const Screen& screen) { m_Screens.push_back(screen); } void setNumColumns(int n) { m_NumColumns = n; } void setNumRows(int n) { m_NumRows = n; } void haveHeartbeat(bool on) { m_HasHeartbeat = on; } @@ -93,7 +93,7 @@ class ServerConfig : public BaseConfig void setEnableDragAndDrop(bool on) { m_EnableDragAndDrop = on; } void setClipboardSharing(bool on) { m_ClipboardSharing = on; } QList& switchCorners() { return m_SwitchCorners; } - HotkeyList& hotkeys() { return m_Hotkeys; } + std::vector& hotkeys() { return m_Hotkeys; } void init(); int adjacentScreenIndex(int idx, int deltaColumn, int deltaRow) const; @@ -106,7 +106,7 @@ class ServerConfig : public BaseConfig private: QSettings* m_pSettings; - ScreenList m_Screens; + std::vector m_Screens; int m_NumColumns; int m_NumRows; bool m_HasHeartbeat; @@ -120,7 +120,7 @@ class ServerConfig : public BaseConfig int m_SwitchDoubleTap; int m_SwitchCornerSize; QList m_SwitchCorners; - HotkeyList m_Hotkeys; + std::vector m_Hotkeys; QString m_ServerName; bool m_IgnoreAutoConfigClient; bool m_EnableDragAndDrop; @@ -138,4 +138,3 @@ enum { }; #endif - diff --git a/src/gui/src/ServerConfigDialog.cpp b/src/gui/src/ServerConfigDialog.cpp index 2e7ffc9..84c2047 100644 --- a/src/gui/src/ServerConfigDialog.cpp +++ b/src/gui/src/ServerConfigDialog.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -60,8 +60,9 @@ ServerConfigDialog::ServerConfigDialog(QWidget* parent, ServerConfig& config, co m_pCheckBoxEnableClipboard->setChecked(serverConfig().clipboardSharing()); - foreach(const Hotkey& hotkey, serverConfig().hotkeys()) + for (const Hotkey& hotkey : serverConfig().hotkeys()) { m_pListHotkeys->addItem(hotkey.text()); + } m_pScreenSetupView->setModel(&m_ScreenSetupModel); @@ -75,7 +76,7 @@ void ServerConfigDialog::showEvent(QShowEvent* event) if (!m_Message.isEmpty()) { - // TODO: ideally this massage box should pop up after the dialog is shown + // TODO: ideally this message box should pop up after the dialog is shown QMessageBox::information(this, tr("Configure server"), m_Message); } } @@ -121,7 +122,7 @@ void ServerConfigDialog::on_m_pButtonNewHotkey_clicked() HotkeyDialog dlg(this, hotkey); if (dlg.exec() == QDialog::Accepted) { - serverConfig().hotkeys().append(hotkey); + serverConfig().hotkeys().push_back(hotkey); m_pListHotkeys->addItem(hotkey.text()); } } @@ -140,7 +141,7 @@ void ServerConfigDialog::on_m_pButtonRemoveHotkey_clicked() { int idx = m_pListHotkeys->currentRow(); Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size()); - serverConfig().hotkeys().removeAt(idx); + serverConfig().hotkeys().erase(serverConfig().hotkeys().begin() + idx); m_pListActions->clear(); delete m_pListHotkeys->item(idx); } @@ -168,8 +169,9 @@ void ServerConfigDialog::on_m_pListHotkeys_itemSelectionChanged() Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size()); const Hotkey& hotkey = serverConfig().hotkeys()[idx]; - foreach(const Action& action, hotkey.actions()) + for (const Action& action : hotkey.actions()) { m_pListActions->addItem(action.text()); + } } } @@ -183,7 +185,7 @@ void ServerConfigDialog::on_m_pButtonNewAction_clicked() ActionDialog dlg(this, serverConfig(), hotkey, action); if (dlg.exec() == QDialog::Accepted) { - hotkey.actions().append(action); + hotkey.appendAction(action); m_pListActions->addItem(action.text()); } } @@ -196,11 +198,13 @@ void ServerConfigDialog::on_m_pButtonEditAction_clicked() int idxAction = m_pListActions->currentRow(); Q_ASSERT(idxAction >= 0 && idxAction < hotkey.actions().size()); - Action& action = hotkey.actions()[idxAction]; + Action action = hotkey.actions()[idxAction]; ActionDialog dlg(this, serverConfig(), hotkey, action); - if (dlg.exec() == QDialog::Accepted) + if (dlg.exec() == QDialog::Accepted) { + hotkey.setAction(idxAction, action); m_pListActions->currentItem()->setText(action.text()); + } } void ServerConfigDialog::on_m_pButtonRemoveAction_clicked() @@ -212,7 +216,7 @@ void ServerConfigDialog::on_m_pButtonRemoveAction_clicked() int idxAction = m_pListActions->currentRow(); Q_ASSERT(idxAction >= 0 && idxAction < hotkey.actions().size()); - hotkey.actions().removeAt(idxAction); + hotkey.removeAction(idxAction); delete m_pListActions->currentItem(); } diff --git a/src/gui/src/ServerConfigDialog.h b/src/gui/src/ServerConfigDialog.h index 1ef17d1..91da51a 100644 --- a/src/gui/src/ServerConfigDialog.h +++ b/src/gui/src/ServerConfigDialog.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -63,4 +63,3 @@ class ServerConfigDialog : public QDialog, public Ui::ServerConfigDialogBase }; #endif - diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index 24bb86c..8d01c77 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -48,8 +48,10 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : m_pLineEditLogFilename->setText(appConfig().logFilename()); setIndexFromItemData(m_pComboLanguage, appConfig().language()); m_pCheckBoxAutoHide->setChecked(appConfig().getAutoHide()); + m_pCheckBoxAutoStart->setChecked(appConfig().getAutoStart()); m_pCheckBoxMinimizeToTray->setChecked(appConfig().getMinimizeToTray()); m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled()); + checkbox_require_client_certificate->setChecked(m_appConfig.getRequireClientCertificate()); #if defined(Q_OS_WIN) m_pComboElevate->setCurrentIndex(static_cast(appConfig().elevateMode())); @@ -66,12 +68,14 @@ void SettingsDialog::accept() m_appConfig.setPort(m_pSpinBoxPort->value()); m_appConfig.setNetworkInterface(m_pLineEditInterface->text()); m_appConfig.setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked()); + m_appConfig.setRequireClientCertificate(checkbox_require_client_certificate->isChecked()); m_appConfig.setLogLevel(m_pComboLogLevel->currentIndex()); m_appConfig.setLogToFile(m_pCheckBoxLogToFile->isChecked()); m_appConfig.setLogFilename(m_pLineEditLogFilename->text()); m_appConfig.setLanguage(m_pComboLanguage->itemData(m_pComboLanguage->currentIndex()).toString()); m_appConfig.setElevateMode(static_cast(m_pComboElevate->currentIndex())); m_appConfig.setAutoHide(m_pCheckBoxAutoHide->isChecked()); + m_appConfig.setAutoStart(m_pCheckBoxAutoStart->isChecked()); m_appConfig.setMinimizeToTray(m_pCheckBoxMinimizeToTray->isChecked()); m_appConfig.saveSettings(); QDialog::accept(); diff --git a/src/gui/src/SettingsDialog.h b/src/gui/src/SettingsDialog.h index b733bcc..210b6c0 100644 --- a/src/gui/src/SettingsDialog.h +++ b/src/gui/src/SettingsDialog.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/gui/src/SettingsDialogBase.ui b/src/gui/src/SettingsDialogBase.ui index d75c18c..44ed98c 100644 --- a/src/gui/src/SettingsDialogBase.ui +++ b/src/gui/src/SettingsDialogBase.ui @@ -7,7 +7,7 @@ 0 0 368 - 380 + 428 @@ -126,6 +126,13 @@ + + + + Start &Barrier on startup + + + @@ -135,6 +142,16 @@ Networking + + + + &Address: + + + m_pLineEditInterface + + + @@ -164,16 +181,6 @@ - - - - &Address: - - - m_pLineEditInterface - - - @@ -181,13 +188,20 @@ - + Enable &SSL + + + + Require client certificate + + + @@ -209,19 +223,20 @@ false - - - - - 75 - 0 - - + + - &Logging level: + Log to file: - - m_pComboLogLevel + + + + + + false + + + Browse... @@ -264,10 +279,19 @@ - - + + + + + 75 + 0 + + - Log to file: + &Logging level: + + + m_pComboLogLevel @@ -278,16 +302,6 @@ - - - - false - - - Browse... - - - @@ -322,6 +336,7 @@ m_pComboElevate m_pCheckBoxMinimizeToTray m_pCheckBoxAutoHide + m_pCheckBoxAutoStart m_pSpinBoxPort m_pLineEditInterface m_pCheckBoxEnableCrypto diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index 0cfdb81..262d63c 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include "SetupWizard.h" #include "MainWindow.h" #include "QBarrierApplication.h" @@ -37,7 +37,7 @@ SetupWizard::SetupWizard(MainWindow& mainWindow, bool startMain) : #elif defined(Q_OS_WIN) - // when areo is disabled on windows, the next/back buttons + // when aero is disabled on windows, the next/back buttons // are hidden (must be a qt bug) -- resizing the window // to +1 of the original height seems to fix this. // NOTE: calling setMinimumSize after this will break @@ -58,7 +58,7 @@ SetupWizard::~SetupWizard() } bool SetupWizard::validateCurrentPage() -{ +{ QMessageBox message; message.setWindowTitle(tr("Setup Barrier")); message.setIcon(QMessageBox::Information); diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h index 80e19e9..73223b2 100644 --- a/src/gui/src/SetupWizard.h +++ b/src/gui/src/SetupWizard.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/gui/src/ShutdownCh.h b/src/gui/src/ShutdownCh.h index 2462cae..54ec84d 100644 --- a/src/gui/src/ShutdownCh.h +++ b/src/gui/src/ShutdownCh.h @@ -19,4 +19,3 @@ // included in both the GUI and the child apps (server & client) const char ShutdownCh = 'S'; - diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp index 80fdfc2..65ac08a 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/gui/src/SslCertificate.cpp @@ -16,163 +16,111 @@ */ #include "SslCertificate.h" -#include "Fingerprint.h" #include "common/DataDirectories.h" +#include "base/finally.h" +#include "io/filesystem.h" +#include "net/FingerprintDatabase.h" +#include "net/SecureUtils.h" -#include -#include -#include - -static const char kCertificateLifetime[] = "365"; -static const char kCertificateSubjectInfo[] = "/CN=Barrier"; -static const char kCertificateFilename[] = "Barrier.pem"; -static const char kSslDir[] = "SSL"; -static const char kUnixOpenSslCommand[] = "openssl"; - -#if defined(Q_OS_WIN) -static const char kWinOpenSslBinary[] = "openssl.exe"; -static const char kConfigFile[] = "barrier.conf"; -#endif +#include +#include +#include +#include +#include SslCertificate::SslCertificate(QObject *parent) : QObject(parent) { - m_ProfileDir = QString::fromStdString(DataDirectories::profile()); - if (m_ProfileDir.isEmpty()) { + if (barrier::DataDirectories::profile().empty()) { emit error(tr("Failed to get profile directory.")); } } -bool SslCertificate::runTool(const QStringList& args) +void SslCertificate::generateCertificate() { - QString program; -#if defined(Q_OS_WIN) - program = QCoreApplication::applicationDirPath(); - program.append("\\").append(kWinOpenSslBinary); -#else - program = kUnixOpenSslCommand; -#endif - - - QStringList environment; -#if defined(Q_OS_WIN) - environment << QString("OPENSSL_CONF=%1\\%2") - .arg(QCoreApplication::applicationDirPath()) - .arg(kConfigFile); -#endif - - QProcess process; - process.setEnvironment(environment); - process.start(program, args); - - bool success = process.waitForStarted(); - - QString standardError; - if (success && process.waitForFinished()) - { - m_ToolOutput = process.readAllStandardOutput().trimmed(); - standardError = process.readAllStandardError().trimmed(); - } + auto cert_path = barrier::DataDirectories::ssl_certificate_path(); + + if (!barrier::fs::exists(cert_path) || !is_certificate_valid(cert_path)) { + try { + auto cert_dir = cert_path.parent_path(); + if (!barrier::fs::exists(cert_dir)) { + barrier::fs::create_directories(cert_dir); + } + + barrier::generate_pem_self_signed_cert(cert_path.u8string()); + } catch (const std::exception& e) { + emit error(QString("SSL tool failed: %1").arg(e.what())); + return; + } - int code = process.exitCode(); - if (!success || code != 0) - { - emit error( - QString("SSL tool failed: %1\n\nCode: %2\nError: %3") - .arg(program) - .arg(process.exitCode()) - .arg(standardError.isEmpty() ? "Unknown" : standardError)); - return false; + emit info(tr("SSL certificate generated.")); } - return true; + generate_fingerprint(cert_path); + + emit generateFinished(); } -void SslCertificate::generateCertificate() +void SslCertificate::generate_fingerprint(const barrier::fs::path& cert_path) { - QString sslDirPath = QString("%1%2%3") - .arg(m_ProfileDir) - .arg(QDir::separator()) - .arg(kSslDir); - - QString filename = QString("%1%2%3") - .arg(sslDirPath) - .arg(QDir::separator()) - .arg(kCertificateFilename); - - QFile file(filename); - if (!file.exists()) { - QStringList arguments; - - // self signed certificate - arguments.append("req"); - arguments.append("-x509"); - arguments.append("-nodes"); - - // valide duration - arguments.append("-days"); - arguments.append(kCertificateLifetime); - - // subject information - arguments.append("-subj"); - - QString subInfo(kCertificateSubjectInfo); - arguments.append(subInfo); - - // private key - arguments.append("-newkey"); - arguments.append("rsa:2048"); - - QDir sslDir(sslDirPath); - if (!sslDir.exists()) { - sslDir.mkpath("."); + try { + auto local_path = barrier::DataDirectories::local_ssl_fingerprints_path(); + auto local_dir = local_path.parent_path(); + if (!barrier::fs::exists(local_dir)) { + barrier::fs::create_directories(local_dir); } - // key output filename - arguments.append("-keyout"); - arguments.append(filename); - - // certificate output filename - arguments.append("-out"); - arguments.append(filename); + barrier::FingerprintDatabase db; + db.add_trusted(barrier::get_pem_file_cert_fingerprint(cert_path.u8string(), + barrier::FingerprintType::SHA1)); + db.add_trusted(barrier::get_pem_file_cert_fingerprint(cert_path.u8string(), + barrier::FingerprintType::SHA256)); + db.write(local_path); - if (!runTool(arguments)) { - return; - } - - emit info(tr("SSL certificate generated.")); + emit info(tr("SSL fingerprint generated.")); + } catch (const std::exception& e) { + emit error(tr("Failed to find SSL fingerprint.") + e.what()); } - - generateFingerprint(filename); - - emit generateFinished(); } -void SslCertificate::generateFingerprint(const QString& certificateFilename) +bool SslCertificate::is_certificate_valid(const barrier::fs::path& path) { - QStringList arguments; - arguments.append("x509"); - arguments.append("-fingerprint"); - arguments.append("-sha1"); - arguments.append("-noout"); - arguments.append("-in"); - arguments.append(certificateFilename); - - if (!runTool(arguments)) { - return; + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + auto fp = barrier::fopen_utf8_path(path, "r"); + if (!fp) { + emit info(tr("Could not read from default certificate file.")); + return false; } + auto file_close = barrier::finally([fp]() { std::fclose(fp); }); - // find the fingerprint from the tool output - int i = m_ToolOutput.indexOf("="); - if (i != -1) { - i++; - QString fingerprint = m_ToolOutput.mid( - i, m_ToolOutput.size() - i); + auto* cert = PEM_read_X509(fp, nullptr, nullptr, nullptr); + if (!cert) { + emit info(tr("Error loading default certificate file to memory.")); + return false; + } + auto cert_free = barrier::finally([cert]() { X509_free(cert); }); - Fingerprint::local().trust(fingerprint, false); - emit info(tr("SSL fingerprint generated.")); + auto* pubkey = X509_get_pubkey(cert); + if (!pubkey) { + emit info(tr("Default certificate key file does not contain valid public key")); + return false; + } + auto pubkey_free = barrier::finally([pubkey]() { EVP_PKEY_free(pubkey); }); + + auto type = EVP_PKEY_type(EVP_PKEY_id(pubkey)); + if (type != EVP_PKEY_RSA && type != EVP_PKEY_DSA) { + emit info(tr("Public key in default certificate key file is not RSA or DSA")); + return false; } - else { - emit error(tr("Failed to find SSL fingerprint.")); + + auto bits = EVP_PKEY_bits(pubkey); + if (bits < 2048) { + // We could have small keys in old barrier installations + emit info(tr("Public key in default certificate key file is too small.")); + return false; } + + return true; } diff --git a/src/gui/src/SslCertificate.h b/src/gui/src/SslCertificate.h index 8b20913..eae7fd6 100644 --- a/src/gui/src/SslCertificate.h +++ b/src/gui/src/SslCertificate.h @@ -18,10 +18,12 @@ #pragma once #include +#include +#include "io/filesystem.h" class SslCertificate : public QObject { -Q_OBJECT + Q_OBJECT public: explicit SslCertificate(QObject *parent = 0); @@ -35,10 +37,7 @@ signals: void generateFinished(); private: - bool runTool(const QStringList& args); - void generateFingerprint(const QString& certificateFilename); + void generate_fingerprint(const barrier::fs::path& cert_path); -private: - QString m_ProfileDir; - QString m_ToolOutput; + bool is_certificate_valid(const barrier::fs::path& path); }; diff --git a/src/gui/src/TrashScreenWidget.cpp b/src/gui/src/TrashScreenWidget.cpp index 42a9d56..6f2abad 100644 --- a/src/gui/src/TrashScreenWidget.cpp +++ b/src/gui/src/TrashScreenWidget.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -40,4 +40,3 @@ void TrashScreenWidget::dropEvent(QDropEvent* event) else event->ignore(); } - diff --git a/src/gui/src/TrashScreenWidget.h b/src/gui/src/TrashScreenWidget.h index 7ab887e..967f73d 100644 --- a/src/gui/src/TrashScreenWidget.h +++ b/src/gui/src/TrashScreenWidget.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -39,4 +39,3 @@ class TrashScreenWidget : public QLabel }; #endif - diff --git a/src/gui/src/ZeroconfService.cpp b/src/gui/src/ZeroconfService.cpp index 02902ee..fbb0ea1 100644 --- a/src/gui/src/ZeroconfService.cpp +++ b/src/gui/src/ZeroconfService.cpp @@ -66,7 +66,7 @@ ZeroconfService::ZeroconfService(MainWindow* mainWindow) : m_ServiceRegistered(false) { silence_avahi_warning(); - if (m_pMainWindow->barrierType() == MainWindow::barrierServer) { + if (m_pMainWindow->barrier_type() == BarrierType::Server) { if (registerService(true)) { m_pZeroconfBrowser = new ZeroconfBrowser(this); connect(m_pZeroconfBrowser, SIGNAL( @@ -101,7 +101,7 @@ ZeroconfService::~ZeroconfService() void ZeroconfService::serverDetected(const QList& list) { - foreach (ZeroconfRecord record, list) { + for (ZeroconfRecord record : list) { registerService(false); m_pMainWindow->appendLogInfo(tr("zeroconf server detected: %1").arg( record.serviceName)); @@ -111,7 +111,7 @@ void ZeroconfService::serverDetected(const QList& list) void ZeroconfService::clientDetected(const QList& list) { - foreach (ZeroconfRecord record, list) { + for (ZeroconfRecord record : list) { m_pMainWindow->appendLogInfo(tr("zeroconf client detected: %1").arg( record.serviceName)); m_pMainWindow->autoAddScreen(record.serviceName); @@ -127,15 +127,15 @@ void ZeroconfService::errorHandle(DNSServiceErrorType errorCode) QString ZeroconfService::getLocalIPAddresses() { QStringList addresses; - foreach (const QHostAddress& address, QNetworkInterface::allAddresses()) { + for (const QHostAddress& address : QNetworkInterface::allAddresses()) { if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress(QHostAddress::LocalHost)) { addresses.append(address.toString()); } } - foreach (const QString& preferedIP, preferedIPAddress) { - foreach (const QString& address, addresses) { + for (const QString& preferedIP : preferedIPAddress) { + for (const QString& address : addresses) { if (address.startsWith(preferedIP)) { return address; } diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index 73251d5..776b44d 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -97,6 +97,12 @@ int main(int argc, char* argv[]) QApplication::setQuitOnLastWindowClosed(false); + if (QGuiApplication::platformName() == "wayland") { + QMessageBox::warning( + NULL, "Barrier", + "You are using wayland session, which is currently not fully supported by Barrier."); + } + QSettings settings; AppConfig appConfig (&settings); diff --git a/src/gui/test/HotkeyTests.cpp b/src/gui/test/HotkeyTests.cpp new file mode 100644 index 0000000..7607dfb --- /dev/null +++ b/src/gui/test/HotkeyTests.cpp @@ -0,0 +1,320 @@ +/* barrier -- mouse and keyboard sharing utility + Copyright (C) 2021 Povilas Kanapickas + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include "../src/Hotkey.h" +#include +#include "Utils.h" + +#include +#include + +struct TestAction +{ + Action::ActionType type = Action::keyDown; + std::vector keys; + std::vector type_screen_names; + std::string screen_name; + Action::SwitchDirection switch_direction; + Action::LockCursorMode lock_cursor_mode; + + static TestAction createKeyAction(Action::ActionType type, const std::vector& keys, + const std::vector& type_screen_names = {}) + { + TestAction action; + action.type = Action::keyDown; + action.keys = keys; + action.type_screen_names = type_screen_names; + return action; + } + + static TestAction createKeyDown(const std::vector& keys, + const std::vector& type_screen_names = {}) + { + return createKeyAction(Action::keyDown, keys, type_screen_names); + } + + static TestAction createKeyUp(const std::vector& keys, + const std::vector& type_screen_names = {}) + { + return createKeyAction(Action::keyUp, keys, type_screen_names); + } + + static TestAction createKeyStroke(const std::vector& keys, + const std::vector& type_screen_names = {}) + { + return createKeyAction(Action::keystroke, keys, type_screen_names); + } + + static TestAction createSwitchToScreen(const std::string& screen_name) + { + TestAction action; + action.type = Action::switchToScreen; + action.screen_name = screen_name; + return action; + } + + static TestAction createToggleScreen() + { + TestAction action; + action.type = Action::toggleScreen; + return action; + } + + static TestAction createSwitchInDirection(Action::SwitchDirection switch_direction) + { + TestAction action; + action.type = Action::switchInDirection; + action.switch_direction = switch_direction; + return action; + } + + static TestAction createLockCursorToScreen(Action::LockCursorMode lock_cursor_mode) + { + TestAction action; + action.type = Action::lockCursorToScreen; + action.lock_cursor_mode = lock_cursor_mode; + return action; + } +}; + +struct TestHotKey +{ + std::vector keys; + std::vector actions; +}; + +namespace { + + Action createAction(const TestAction& test_action) + { + Action action; + action.setType(test_action.type); + + switch (test_action.type) { + case Action::keyDown: + case Action::keyUp: + case Action::keystroke: { + KeySequence sequence; + for (auto key : test_action.keys) { + sequence.appendKey(key.key, key.modifier); + } + action.setKeySequence(sequence); + for (const auto& type_screen_name : test_action.type_screen_names) { + action.appendTypeScreenName(QString::fromStdString(type_screen_name)); + } + break; + } + case Action::switchToScreen: + action.setSwitchScreenName(QString::fromStdString(test_action.screen_name)); + break; + case Action::toggleScreen: + break; + case Action::switchInDirection: + action.setSwitchDirection(test_action.switch_direction); + break; + case Action::lockCursorToScreen: + action.setLockCursorMode(test_action.lock_cursor_mode); + break; + } + return action; + } + + Hotkey createHotkey(const TestHotKey& test_hotkey) + { + Hotkey hotkey; + KeySequence sequence; + for (auto key : test_hotkey.keys) { + sequence.appendKey(key.key, key.modifier); + } + hotkey.setKeySequence(sequence); + + for (auto action : test_hotkey.actions) { + hotkey.appendAction(createAction(action)); + } + return hotkey; + } + + std::string hotkeyToStringViaTextStream(const Hotkey& hotkey) + { + QString result; + QTextStream stream{&result}; + stream << hotkey; + return result.toStdString(); + } +} // namespace + +void doHotkeyLoadSaveTest(const TestHotKey& test_hotkey) +{ + auto filename = getTemporaryFilename(); + + Hotkey hotkey_before, hotkey_after; + { + QSettings settings(filename, QSettings::NativeFormat); + + hotkey_before = createHotkey(test_hotkey); + + settings.beginGroup("test"); + hotkey_before.saveSettings(settings); + settings.endGroup(); + } + { + QSettings settings(filename, QSettings::NativeFormat); + + settings.beginGroup("test"); + hotkey_after.loadSettings(settings); + settings.endGroup(); + + ASSERT_EQ(hotkey_before.keySequence().sequence(), hotkey_after.keySequence().sequence()); + ASSERT_EQ(hotkey_before.keySequence().modifiers(), hotkey_after.keySequence().modifiers()); + + const auto& actions_before = hotkey_before.actions(); + const auto& actions_after = hotkey_after.actions(); + + ASSERT_EQ(actions_before.size(), actions_after.size()); + for (int i = 0; i < actions_before.size(); ++i) { + const auto& action_before = actions_before[i]; + const auto& action_after = actions_after[i]; + + ASSERT_EQ(action_before.keySequence().sequence(), action_after.keySequence().sequence()); + ASSERT_EQ(action_before.keySequence().modifiers(), action_after.keySequence().modifiers()); + ASSERT_EQ(action_before.type(), action_after.type()); + ASSERT_EQ(action_before.typeScreenNames(), action_after.typeScreenNames()); + ASSERT_EQ(action_before.switchScreenName(), action_after.switchScreenName()); + ASSERT_EQ(action_before.switchDirection(), action_after.switchDirection()); + ASSERT_EQ(action_before.lockCursorMode(), action_after.lockCursorMode()); + ASSERT_EQ(action_before.activeOnRelease(), action_after.activeOnRelease()); + ASSERT_EQ(action_before.haveScreens(), action_after.haveScreens()); + } + } + + QFile::remove(filename); +} + +TEST(HotkeyLoadSaveTests, Empty) +{ + TestHotKey hotkey; + doHotkeyLoadSaveTest(hotkey); +} + +TEST(HotkeyLoadSaveTests, KeysNoActions) +{ + TestHotKey hotkey = {{{Qt::Key_A, Qt::NoModifier}, {Qt::Key_B, Qt::NoModifier}}, {}}; + doHotkeyLoadSaveTest(hotkey); +} + +TEST(HotkeyLoadSaveTests, CommaKeyNoActions) +{ + TestHotKey hotkey = { + { + {Qt::Key_A, Qt::NoModifier}, + {Qt::Key_Comma, Qt::NoModifier}, + {Qt::Key_B, Qt::NoModifier} + }, {}}; + doHotkeyLoadSaveTest(hotkey); +} + +TEST(HotkeyLoadSaveTests, KeysSingleAction) +{ + TestHotKey hotkey = { + { + {Qt::Key_A, Qt::NoModifier}, + {Qt::Key_B, Qt::NoModifier} + }, + { + TestAction::createKeyDown({{Qt::Key_Z, Qt::NoModifier}}) + } + }; + doHotkeyLoadSaveTest(hotkey); +} + +TEST(HotkeyLoadSaveTests, KeysMultipleAction) +{ + TestHotKey hotkey = { + { + {Qt::Key_A, Qt::NoModifier}, + {Qt::Key_B, Qt::NoModifier} + }, + { + TestAction::createKeyDown({{Qt::Key_Z, Qt::NoModifier}}), + TestAction::createSwitchToScreen("test_screen") + } + }; + doHotkeyLoadSaveTest(hotkey); +} + +TEST(HotkeyToTexStreamTests, Empty) +{ + TestHotKey hotkey; + ASSERT_EQ(hotkeyToStringViaTextStream(createHotkey(hotkey)), ""); +} + +TEST(HotkeyToTexStreamTests, KeysNoActions) +{ + TestHotKey hotkey = { + { + {Qt::Key_A, Qt::NoModifier}, + {Qt::Key_B, Qt::NoModifier} + }, + {} + }; + ASSERT_EQ(hotkeyToStringViaTextStream(createHotkey(hotkey)), ""); +} + +TEST(HotkeyToTexStreamTests, KeysSingleAction) +{ + TestHotKey hotkey = { + { + {Qt::Key_A, Qt::NoModifier}, + {Qt::Key_B, Qt::NoModifier} + }, + {} + }; + ASSERT_EQ(hotkeyToStringViaTextStream(createHotkey(hotkey)), ""); +} + + +TEST(HotkeyToTexStreamTests, KeysCommaSingleAction) +{ + TestHotKey hotkey = { + { + {Qt::Key_A, Qt::NoModifier}, + {Qt::Key_Comma, Qt::NoModifier}, + {Qt::Key_B, Qt::NoModifier} + }, + { + TestAction::createKeyDown({{Qt::Key_Z, Qt::NoModifier}}) + } + }; + ASSERT_EQ(hotkeyToStringViaTextStream(createHotkey(hotkey)), + "\tkeystroke(a+Comma+b) = keyDown(z,*)\n"); +} + +TEST(HotkeyToTexStreamTests, KeysMultipleAction) +{ + TestHotKey hotkey = { + { + {Qt::Key_A, Qt::NoModifier}, + {Qt::Key_B, Qt::NoModifier} + }, + { + TestAction::createKeyDown({{Qt::Key_Z, Qt::NoModifier}}), + TestAction::createSwitchToScreen("test_screen") + } + }; + ASSERT_EQ(hotkeyToStringViaTextStream(createHotkey(hotkey)), + "\tkeystroke(a+b) = keyDown(z,*), switchToScreen(test_screen)\n"); +} diff --git a/src/gui/test/KeySequenceTests.cpp b/src/gui/test/KeySequenceTests.cpp new file mode 100644 index 0000000..9fa9273 --- /dev/null +++ b/src/gui/test/KeySequenceTests.cpp @@ -0,0 +1,145 @@ +/* barrier -- mouse and keyboard sharing utility + Copyright (C) 2021 Povilas Kanapickas + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "../src/KeySequence.h" +#include "Utils.h" +#include +#include + +#include +#include +#include + +namespace { + + auto s_key_sequence_test_keys = { + Qt::Key_Space, + Qt::Key_Escape, + Qt::Key_Tab, + Qt::Key_Backtab, + Qt::Key_Backspace, + Qt::Key_Return, + Qt::Key_Insert, + Qt::Key_Delete, + Qt::Key_Pause, + Qt::Key_Print, + Qt::Key_SysReq, + Qt::Key_Home, + Qt::Key_End, + Qt::Key_Left, + Qt::Key_Up, + Qt::Key_Right, + Qt::Key_Down, + Qt::Key_Comma, + Qt::Key_Semicolon, + Qt::Key_PageUp, + Qt::Key_PageDown, + Qt::Key_CapsLock, + Qt::Key_NumLock, + Qt::Key_ScrollLock, + Qt::Key_Help, + Qt::Key_Enter, + Qt::Key_Clear, + Qt::Key_Back, + Qt::Key_Forward, + Qt::Key_Stop, + Qt::Key_Refresh, + Qt::Key_VolumeDown, + Qt::Key_VolumeMute, + Qt::Key_VolumeUp, + Qt::Key_MediaPlay, + Qt::Key_MediaStop, + Qt::Key_MediaPrevious, + Qt::Key_MediaNext, + Qt::Key_HomePage, + Qt::Key_Favorites, + Qt::Key_Search, + Qt::Key_Standby, + Qt::Key_LaunchMail, + Qt::Key_LaunchMedia, + Qt::Key_Launch0, + Qt::Key_Launch1, + Qt::Key_Select, + }; + + std::string keySequenceToString(const std::vector& key_pairs) + { + KeySequence sequence; + for (auto key_pair : key_pairs) { + sequence.appendKey(key_pair.key, key_pair.modifier); + } + return sequence.toString().toStdString(); + } +} // namespace + +class KeySequenceLoadSaveTestFixture : + public ::testing::TestWithParam {}; + +TEST_P(KeySequenceLoadSaveTestFixture, SupportsSpecialSymbols) +{ + int key = GetParam(); + + auto filename = getTemporaryFilename(); + + { + QSettings settings(filename, QSettings::NativeFormat); + KeySequence sequence; + + sequence.appendKey(key, 0); + sequence.appendKey(key, 0); + settings.beginGroup("test"); + sequence.saveSettings(settings); + settings.endGroup(); + } + { + QSettings settings(filename, QSettings::NativeFormat); + KeySequence sequence; + + settings.beginGroup("test"); + sequence.loadSettings(settings); + settings.endGroup(); + + const auto& data = sequence.sequence(); + ASSERT_EQ(data.size(), 2); + ASSERT_EQ(data[0], key); + ASSERT_EQ(data[1], key); + } + + QFile::remove(filename); +} + +INSTANTIATE_TEST_CASE_P( + KeySequenceLoadSaveTests, + KeySequenceLoadSaveTestFixture, + ::testing::ValuesIn(s_key_sequence_test_keys)); + +TEST(KeySequenceTests, ToString) +{ + ASSERT_EQ(keySequenceToString({{Qt::Key_Menu, Qt::MetaModifier}}), + "Meta"); + ASSERT_EQ(keySequenceToString({{Qt::Key_A, 0}, {Qt::Key_B, 0}}), + "a+b"); + ASSERT_EQ(keySequenceToString({{Qt::Key_A, 0}, {Qt::Key_Comma, 0}, {Qt::Key_B, 0}}), + "a+Comma+b"); + ASSERT_EQ(keySequenceToString({{Qt::Key_A, 0}, {Qt::Key_Semicolon, 0}, {Qt::Key_B, 0}}), + "a+Semicolon+b"); + ASSERT_EQ(keySequenceToString({{Qt::Key_A, 0}, {Qt::Key_Shift, Qt::ShiftModifier}, + {Qt::Key_0, Qt::ShiftModifier}}), + "a+Shift+0"); + ASSERT_EQ(keySequenceToString({{Qt::Key_A, 0}, {Qt::Key_Control, Qt::ControlModifier}, + {Qt::Key_0, Qt::ControlModifier}}), + "a+Control+0"); +} diff --git a/src/gui/test/Utils.h b/src/gui/test/Utils.h new file mode 100644 index 0000000..da66ce7 --- /dev/null +++ b/src/gui/test/Utils.h @@ -0,0 +1,38 @@ +/* barrier -- mouse and keyboard sharing utility + Copyright (C) 2021 Povilas Kanapickas + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_GUI_TEST_UTILS_H +#define BARRIER_GUI_TEST_UTILS_H + +#include +#include + +struct TestKey +{ + int key = 0; + int modifier = Qt::NoModifier; + + TestKey(int key, int modifier) : key{key}, modifier{modifier} {} +}; + +inline QString getTemporaryFilename() +{ + QTemporaryFile temp_file; + temp_file.open(); + return temp_file.fileName(); +} + +#endif // BARRIER_GUI_TEST_UTILS_H diff --git a/src/gui/test/main.cpp b/src/gui/test/main.cpp new file mode 100644 index 0000000..b57dd76 --- /dev/null +++ b/src/gui/test/main.cpp @@ -0,0 +1,23 @@ +/* barrier -- mouse and keyboard sharing utility + Copyright (C) 2021 Povilas Kanapickas + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return (RUN_ALL_TESTS() == 1) ? 1 : 0; +} diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 70a0629..fdec71c 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/Arch.cpp b/src/lib/arch/Arch.cpp index 0a3b3e5..810b74f 100644 --- a/src/lib/arch/Arch.cpp +++ b/src/lib/arch/Arch.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/Arch.h b/src/lib/arch/Arch.h index 940a2e3..3b37617 100644 --- a/src/lib/arch/Arch.h +++ b/src/lib/arch/Arch.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/ArchConsoleStd.cpp b/src/lib/arch/ArchConsoleStd.cpp index f7f7691..2e188bb 100644 --- a/src/lib/arch/ArchConsoleStd.cpp +++ b/src/lib/arch/ArchConsoleStd.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -30,4 +30,4 @@ ArchConsoleStd::writeConsole(ELevel level, const char* str) std::cout << str << std::endl; std::cout.flush(); -} \ No newline at end of file +} diff --git a/src/lib/arch/ArchConsoleStd.h b/src/lib/arch/ArchConsoleStd.h index 8560fad..1d306e7 100644 --- a/src/lib/arch/ArchConsoleStd.h +++ b/src/lib/arch/ArchConsoleStd.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/ArchDaemonNone.cpp b/src/lib/arch/ArchDaemonNone.cpp index 1222549..5cb4681 100644 --- a/src/lib/arch/ArchDaemonNone.cpp +++ b/src/lib/arch/ArchDaemonNone.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/ArchDaemonNone.h b/src/lib/arch/ArchDaemonNone.h index e02405f..425ae56 100644 --- a/src/lib/arch/ArchDaemonNone.h +++ b/src/lib/arch/ArchDaemonNone.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/CMakeLists.txt b/src/lib/arch/CMakeLists.txt index db92634..5ff7b51 100644 --- a/src/lib/arch/CMakeLists.txt +++ b/src/lib/arch/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/IArchConsole.h b/src/lib/arch/IArchConsole.h index d115c50..41e3527 100644 --- a/src/lib/arch/IArchConsole.h +++ b/src/lib/arch/IArchConsole.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/IArchDaemon.h b/src/lib/arch/IArchDaemon.h index 23a34f2..6a4fb67 100644 --- a/src/lib/arch/IArchDaemon.h +++ b/src/lib/arch/IArchDaemon.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -64,7 +64,7 @@ public: Installs the default daemon. */ virtual void installDaemon() = 0; - + //! Uninstall daemon /*! Uninstalls the default daemon. @@ -76,7 +76,7 @@ public: Daemonize. Throw XArchDaemonFailed on error. \c name is the name of the daemon. Once daemonized, \c func is invoked and daemonize returns when and what it does. - + Exactly what happens when daemonizing depends on the platform.
  • unix: diff --git a/src/lib/arch/IArchLog.h b/src/lib/arch/IArchLog.h index 165b1df..704ad47 100644 --- a/src/lib/arch/IArchLog.h +++ b/src/lib/arch/IArchLog.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/IArchMultithread.h b/src/lib/arch/IArchMultithread.h index e8d358b..6a57694 100644 --- a/src/lib/arch/IArchMultithread.h +++ b/src/lib/arch/IArchMultithread.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -19,8 +19,9 @@ #pragma once #include "common/IInterface.h" +#include -/*! +/*! \class ArchCondImpl \brief Internal condition variable data. An architecture dependent type holding the necessary data for a @@ -28,35 +29,35 @@ condition variable. */ class ArchCondImpl; -/*! +/*! \var ArchCond \brief Opaque condition variable type. An opaque type representing a condition variable. */ typedef ArchCondImpl* ArchCond; -/*! +/*! \class ArchMutexImpl \brief Internal mutex data. An architecture dependent type holding the necessary data for a mutex. */ class ArchMutexImpl; -/*! +/*! \var ArchMutex \brief Opaque mutex type. An opaque type representing a mutex. */ typedef ArchMutexImpl* ArchMutex; -/*! +/*! \class ArchThreadImpl \brief Internal thread data. An architecture dependent type holding the necessary data for a thread. */ class ArchThreadImpl; -/*! +/*! \var ArchThread \brief Opaque thread type. An opaque type representing a thread. @@ -71,7 +72,7 @@ barrier. Each architecture must implement this interface. class IArchMultithread : public IInterface { public: //! Type of thread entry point - typedef void* (*ThreadFunc)(void*); + typedef void (*ThreadFunc)(void*); //! Type of thread identifier typedef unsigned int ThreadID; //! Types of signals @@ -160,7 +161,7 @@ public: Creates and starts a new thread, using \c func as the entry point and passing it \c userData. The thread is an opaque data type. */ - virtual ArchThread newThread(ThreadFunc func, void* userData) = 0; + virtual ArchThread newThread(const std::function& func) = 0; //! Get a reference to the calling thread /*! @@ -235,15 +236,6 @@ public: */ virtual bool isExitedThread(ArchThread thread) = 0; - //! Returns the exit code of a thread - /*! - Waits indefinitely for \c thread to exit (if it hasn't yet) then - returns the thread's exit code. - - (Cancellation point) - */ - virtual void* getResultOfThread(ArchThread thread) = 0; - //! Returns an ID for a thread /*! Returns some ID number for \c thread. This is for logging purposes. diff --git a/src/lib/arch/IArchNetwork.h b/src/lib/arch/IArchNetwork.h index b859506..7eae63e 100644 --- a/src/lib/arch/IArchNetwork.h +++ b/src/lib/arch/IArchNetwork.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -24,21 +24,21 @@ class ArchThreadImpl; typedef ArchThreadImpl* ArchThread; -/*! +/*! \class ArchSocketImpl \brief Internal socket data. An architecture dependent type holding the necessary data for a socket. */ class ArchSocketImpl; -/*! +/*! \var ArchSocket \brief Opaque socket type. An opaque type representing a socket. */ typedef ArchSocketImpl* ArchSocket; -/*! +/*! \class ArchNetAddressImpl \brief Internal network address data. An architecture dependent type holding the necessary data for a network @@ -46,7 +46,7 @@ address. */ class ArchNetAddressImpl; -/*! +/*! \var ArchNetAddress \brief Opaque network address type. An opaque type representing a network address. diff --git a/src/lib/arch/IArchSleep.h b/src/lib/arch/IArchSleep.h index 9999d0e..86c571f 100644 --- a/src/lib/arch/IArchSleep.h +++ b/src/lib/arch/IArchSleep.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/IArchString.cpp b/src/lib/arch/IArchString.cpp index 0f7f300..b1118ec 100644 --- a/src/lib/arch/IArchString.cpp +++ b/src/lib/arch/IArchString.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/IArchString.h b/src/lib/arch/IArchString.h index ea10b65..f1803d8 100644 --- a/src/lib/arch/IArchString.h +++ b/src/lib/arch/IArchString.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -46,16 +46,6 @@ public: //! @name manipulators //@{ - //! printf() to limited size buffer with va_list - /*! - This method is equivalent to vsprintf() except it will not write - more than \c n bytes to the buffer, returning -1 if the output - was truncated and the number of bytes written not including the - trailing NUL otherwise. - */ - virtual int vsnprintf(char* str, - int size, const char* fmt, va_list ap); - //! Convert multibyte string to wide character string virtual int convStringMBToWC(wchar_t*, const char*, UInt32 n, bool* errors); diff --git a/src/lib/arch/IArchSystem.h b/src/lib/arch/IArchSystem.h index 9446505..3e7aa98 100644 --- a/src/lib/arch/IArchSystem.h +++ b/src/lib/arch/IArchSystem.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/IArchTaskBar.h b/src/lib/arch/IArchTaskBar.h index 85a32d8..fc5f0d0 100644 --- a/src/lib/arch/IArchTaskBar.h +++ b/src/lib/arch/IArchTaskBar.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/IArchTaskBarReceiver.h b/src/lib/arch/IArchTaskBarReceiver.h index 997c8ae..b4f0d30 100644 --- a/src/lib/arch/IArchTaskBarReceiver.h +++ b/src/lib/arch/IArchTaskBarReceiver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/IArchTime.h b/src/lib/arch/IArchTime.h index abb3cdd..297aab7 100644 --- a/src/lib/arch/IArchTime.h +++ b/src/lib/arch/IArchTime.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/XArch.h b/src/lib/arch/XArch.h index 8484d06..2fcf686 100644 --- a/src/lib/arch/XArch.h +++ b/src/lib/arch/XArch.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -57,7 +57,7 @@ class XArchEval { public: XArchEval() { } virtual ~XArchEval() noexcept { } - + virtual std::string eval() const = 0; }; diff --git a/src/lib/arch/multibyte.h b/src/lib/arch/multibyte.h index 4a4e0ec..7812e0b 100644 --- a/src/lib/arch/multibyte.h +++ b/src/lib/arch/multibyte.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchConsoleUnix.cpp b/src/lib/arch/unix/ArchConsoleUnix.cpp index 79a4634..d5e8e1d 100644 --- a/src/lib/arch/unix/ArchConsoleUnix.cpp +++ b/src/lib/arch/unix/ArchConsoleUnix.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchConsoleUnix.h b/src/lib/arch/unix/ArchConsoleUnix.h index 8326ab5..136c606 100644 --- a/src/lib/arch/unix/ArchConsoleUnix.h +++ b/src/lib/arch/unix/ArchConsoleUnix.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchDaemonUnix.cpp b/src/lib/arch/unix/ArchDaemonUnix.cpp index a03bf7a..f1ac1d5 100644 --- a/src/lib/arch/unix/ArchDaemonUnix.cpp +++ b/src/lib/arch/unix/ArchDaemonUnix.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -53,15 +53,15 @@ execSelfNonDaemonized() { extern char** NXArgv; char** selfArgv = NXArgv; - + setenv("_BARRIER_DAEMONIZED", "", 1); - + execvp(selfArgv[0], selfArgv); return 0; } bool alreadyDaemonized() { - return getenv("_BARRIER_DAEMONIZED") != NULL; + return std::getenv("_BARRIER_DAEMONIZED") != NULL; } #endif @@ -73,7 +73,7 @@ ArchDaemonUnix::daemonize(const char* name, DaemonFunc func) if (alreadyDaemonized()) return func(1, &name); #endif - + // fork so shell thinks we're done and so we're not a process // group leader switch (fork()) { @@ -92,7 +92,7 @@ ArchDaemonUnix::daemonize(const char* name, DaemonFunc func) // become leader of a new session setsid(); - + #ifndef __APPLE__ // NB: don't run chdir on apple; causes strange behaviour. // chdir to root so we don't keep mounted filesystems points busy @@ -115,18 +115,18 @@ ArchDaemonUnix::daemonize(const char* name, DaemonFunc func) // of standard I/O safely goes in the bit bucket. open("/dev/null", O_RDONLY); open("/dev/null", O_RDWR); - + int dupErr = dup(1); if (dupErr < 0) { // NB: file logging actually isn't working at this point! LOG((CLOG_ERR "dup error: %i", dupErr)); } - + #ifdef __APPLE__ return execSelfNonDaemonized(); #endif - + // invoke function return func(1, &name); } diff --git a/src/lib/arch/unix/ArchDaemonUnix.h b/src/lib/arch/unix/ArchDaemonUnix.h index 530159a..52a531e 100644 --- a/src/lib/arch/unix/ArchDaemonUnix.h +++ b/src/lib/arch/unix/ArchDaemonUnix.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchInternetUnix.cpp b/src/lib/arch/unix/ArchInternetUnix.cpp index 76966dc..dcea2ae 100644 --- a/src/lib/arch/unix/ArchInternetUnix.cpp +++ b/src/lib/arch/unix/ArchInternetUnix.cpp @@ -94,16 +94,16 @@ std::string CurlFacade::get(const std::string& url) userAgent << "Barrier "; userAgent << kVersion; curl_easy_setopt(m_curl, CURLOPT_USERAGENT, userAgent.str().c_str()); - + std::string result; curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &result); - + CURLcode code = curl_easy_perform(m_curl); if (code != CURLE_OK) { LOG((CLOG_ERR "curl perform error: %s", curl_easy_strerror(code))); throw XArch("CURL perform failed."); } - + return result; } @@ -114,7 +114,7 @@ std::string CurlFacade::urlEncode(const std::string& url) if (resultCStr == NULL) { throw XArch("CURL escape failed."); } - + std::string result(resultCStr); curl_free(resultCStr); diff --git a/src/lib/arch/unix/ArchLogUnix.cpp b/src/lib/arch/unix/ArchLogUnix.cpp index b1f9089..2389f48 100644 --- a/src/lib/arch/unix/ArchLogUnix.cpp +++ b/src/lib/arch/unix/ArchLogUnix.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchLogUnix.h b/src/lib/arch/unix/ArchLogUnix.h index cdd733f..f37bab8 100644 --- a/src/lib/arch/unix/ArchLogUnix.h +++ b/src/lib/arch/unix/ArchLogUnix.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchMultithreadPosix.cpp b/src/lib/arch/unix/ArchMultithreadPosix.cpp index 4866edc..8400f9d 100644 --- a/src/lib/arch/unix/ArchMultithreadPosix.cpp +++ b/src/lib/arch/unix/ArchMultithreadPosix.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -59,24 +59,19 @@ public: int m_refCount; IArchMultithread::ThreadID m_id; pthread_t m_thread; - IArchMultithread::ThreadFunc m_func; - void* m_userData; + std::function func_;; bool m_cancel; bool m_cancelling; bool m_exited; - void* m_result; void* m_networkData; }; ArchThreadImpl::ArchThreadImpl() : m_refCount(1), m_id(0), - m_func(NULL), - m_userData(NULL), m_cancel(false), m_cancelling(false), m_exited(false), - m_result(NULL), m_networkData(NULL) { // do nothing @@ -319,11 +314,8 @@ ArchMultithreadPosix::unlockMutex(ArchMutex mutex) } } -ArchThread -ArchMultithreadPosix::newThread(ThreadFunc func, void* data) +ArchThread ArchMultithreadPosix::newThread(const std::function& func) { - assert(func != NULL); - // initialize signal handler. we do this here instead of the // constructor so we can avoid daemonizing (using fork()) // when there are multiple threads. clients can safely @@ -341,8 +333,7 @@ ArchMultithreadPosix::newThread(ThreadFunc func, void* data) // create thread impl for new thread ArchThreadImpl* thread = new ArchThreadImpl; - thread->m_func = func; - thread->m_userData = data; + thread->func_ = func; // create the thread. pthread_create() on RedHat 7.2 smp fails // if passed a NULL attr so use a default attr. @@ -389,7 +380,7 @@ ArchMultithreadPosix::closeThread(ArchThread thread) // decrement ref count and clean up thread if no more references if (--thread->m_refCount == 0) { // detach from thread (unless it's the main thread) - if (thread->m_func != NULL) { + if (thread->func_) { pthread_detach(thread->m_thread); } @@ -526,13 +517,6 @@ ArchMultithreadPosix::isExitedThread(ArchThread thread) return thread->m_exited; } -void* -ArchMultithreadPosix::getResultOfThread(ArchThread thread) -{ - std::lock_guard lock(m_threadMutex); - return thread->m_result; -} - IArchMultithread::ThreadID ArchMultithreadPosix::getIDOfThread(ArchThread thread) { @@ -549,7 +533,7 @@ ArchMultithreadPosix::setSignalHandler( } void -ArchMultithreadPosix::raiseSignal(ESignal signal) +ArchMultithreadPosix::raiseSignal(ESignal signal) { std::lock_guard lock(m_threadMutex); if (m_signalFunc[signal] != NULL) { @@ -699,10 +683,8 @@ ArchMultithreadPosix::doThreadFunc(ArchThread thread) std::lock_guard lock(m_threadMutex); } - void* result = NULL; try { - // go - result = (*thread->m_func)(thread->m_userData); + thread->func_(); } catch (XThreadCancel&) { @@ -721,7 +703,6 @@ ArchMultithreadPosix::doThreadFunc(ArchThread thread) // thread has exited { std::lock_guard lock(m_threadMutex); - thread->m_result = result; thread->m_exited = true; } diff --git a/src/lib/arch/unix/ArchMultithreadPosix.h b/src/lib/arch/unix/ArchMultithreadPosix.h index 4bd879f..798147a 100644 --- a/src/lib/arch/unix/ArchMultithreadPosix.h +++ b/src/lib/arch/unix/ArchMultithreadPosix.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -67,7 +67,7 @@ public: virtual void closeMutex(ArchMutex); virtual void lockMutex(ArchMutex); virtual void unlockMutex(ArchMutex); - virtual ArchThread newThread(ThreadFunc, void*); + virtual ArchThread newThread(const std::function& func); virtual ArchThread newCurrentThread(); virtual ArchThread copyThread(ArchThread); virtual void closeThread(ArchThread); @@ -77,7 +77,6 @@ public: virtual bool wait(ArchThread, double timeout); virtual bool isSameThread(ArchThread, ArchThread); virtual bool isExitedThread(ArchThread); - virtual void* getResultOfThread(ArchThread); virtual ThreadID getIDOfThread(ArchThread); virtual void setSignalHandler(ESignal, SignalFunc, void*); virtual void raiseSignal(ESignal); diff --git a/src/lib/arch/unix/ArchNetworkBSD.cpp b/src/lib/arch/unix/ArchNetworkBSD.cpp index 496c988..5507bfe 100644 --- a/src/lib/arch/unix/ArchNetworkBSD.cpp +++ b/src/lib/arch/unix/ArchNetworkBSD.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchNetworkBSD.h b/src/lib/arch/unix/ArchNetworkBSD.h index 3f5679a..e3f11d5 100644 --- a/src/lib/arch/unix/ArchNetworkBSD.h +++ b/src/lib/arch/unix/ArchNetworkBSD.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchSleepUnix.cpp b/src/lib/arch/unix/ArchSleepUnix.cpp index 48e2600..1f91c34 100644 --- a/src/lib/arch/unix/ArchSleepUnix.cpp +++ b/src/lib/arch/unix/ArchSleepUnix.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchSleepUnix.h b/src/lib/arch/unix/ArchSleepUnix.h index 3e307a5..d2cdbca 100644 --- a/src/lib/arch/unix/ArchSleepUnix.h +++ b/src/lib/arch/unix/ArchSleepUnix.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchStringUnix.cpp b/src/lib/arch/unix/ArchStringUnix.cpp index 591c826..dbb91c1 100644 --- a/src/lib/arch/unix/ArchStringUnix.cpp +++ b/src/lib/arch/unix/ArchStringUnix.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -25,7 +25,6 @@ // #include "arch/multibyte.h" -#include "arch/vsnprintf.h" ArchStringUnix::ArchStringUnix() { diff --git a/src/lib/arch/unix/ArchStringUnix.h b/src/lib/arch/unix/ArchStringUnix.h index f7d0035..551f497 100644 --- a/src/lib/arch/unix/ArchStringUnix.h +++ b/src/lib/arch/unix/ArchStringUnix.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchSystemUnix.cpp b/src/lib/arch/unix/ArchSystemUnix.cpp index f51e47f..f956998 100644 --- a/src/lib/arch/unix/ArchSystemUnix.cpp +++ b/src/lib/arch/unix/ArchSystemUnix.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchSystemUnix.h b/src/lib/arch/unix/ArchSystemUnix.h index aa9c564..f3c2ad6 100644 --- a/src/lib/arch/unix/ArchSystemUnix.h +++ b/src/lib/arch/unix/ArchSystemUnix.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchTaskBarXWindows.cpp b/src/lib/arch/unix/ArchTaskBarXWindows.cpp index c3577ad..522c7fd 100644 --- a/src/lib/arch/unix/ArchTaskBarXWindows.cpp +++ b/src/lib/arch/unix/ArchTaskBarXWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchTaskBarXWindows.h b/src/lib/arch/unix/ArchTaskBarXWindows.h index f2c8977..5b60a08 100644 --- a/src/lib/arch/unix/ArchTaskBarXWindows.h +++ b/src/lib/arch/unix/ArchTaskBarXWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchTimeUnix.cpp b/src/lib/arch/unix/ArchTimeUnix.cpp index 24685aa..665a7eb 100644 --- a/src/lib/arch/unix/ArchTimeUnix.cpp +++ b/src/lib/arch/unix/ArchTimeUnix.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/ArchTimeUnix.h b/src/lib/arch/unix/ArchTimeUnix.h index 3c5c0f8..c6a7dc6 100644 --- a/src/lib/arch/unix/ArchTimeUnix.h +++ b/src/lib/arch/unix/ArchTimeUnix.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/XArchUnix.cpp b/src/lib/arch/unix/XArchUnix.cpp index fc7ff65..db2006d 100644 --- a/src/lib/arch/unix/XArchUnix.cpp +++ b/src/lib/arch/unix/XArchUnix.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/unix/XArchUnix.h b/src/lib/arch/unix/XArchUnix.h index 93d6d62..be2e689 100644 --- a/src/lib/arch/unix/XArchUnix.h +++ b/src/lib/arch/unix/XArchUnix.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/vsnprintf.h b/src/lib/arch/vsnprintf.h deleted file mode 100644 index 5a4e3dc..0000000 --- a/src/lib/arch/vsnprintf.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "arch/IArchString.h" - -#if HAVE_VSNPRINTF - -#if !defined(ARCH_VSNPRINTF) -# define ARCH_VSNPRINTF vsnprintf -#endif - -int -IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap) -{ - int n = ::ARCH_VSNPRINTF(str, size, fmt, ap); - if (n > size) { - n = -1; - } - return n; -} - -#elif SYSAPI_UNIX // !HAVE_VSNPRINTF - -#include - -int -IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap) -{ - static FILE* bitbucket = fopen("/dev/null", "w"); - if (bitbucket == NULL) { - // uh oh - if (size > 0) { - str[0] = '\0'; - } - return 0; - } - else { - // count the characters using the bitbucket - int n = vfprintf(bitbucket, fmt, ap); - if (n + 1 <= size) { - // it'll fit so print it into str - vsprintf(str, fmt, ap); - } - return n; - } -} - -#else // !HAVE_VSNPRINTF && !SYSAPI_UNIX - -#error vsnprintf not implemented - -#endif // !HAVE_VSNPRINTF diff --git a/src/lib/arch/win32/ArchConsoleWindows.cpp b/src/lib/arch/win32/ArchConsoleWindows.cpp index 4514555..2c61eb7 100644 --- a/src/lib/arch/win32/ArchConsoleWindows.cpp +++ b/src/lib/arch/win32/ArchConsoleWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/ArchConsoleWindows.h b/src/lib/arch/win32/ArchConsoleWindows.h index f1f0cc9..a6c6d68 100644 --- a/src/lib/arch/win32/ArchConsoleWindows.h +++ b/src/lib/arch/win32/ArchConsoleWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/ArchDaemonWindows.cpp b/src/lib/arch/win32/ArchDaemonWindows.cpp index efcf235..df0a4ce 100644 --- a/src/lib/arch/win32/ArchDaemonWindows.cpp +++ b/src/lib/arch/win32/ArchDaemonWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -441,7 +441,7 @@ ArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) // create synchronization objects m_serviceMutex = ARCH->newMutex(); m_serviceCondVar = ARCH->newCondVar(); - + // register our service handler function m_statusHandle = RegisterServiceCtrlHandler(argv[0], &ArchDaemonWindows::serviceHandlerEntry); @@ -667,7 +667,7 @@ ArchDaemonWindows::stop(const char* name) // ask the service to stop, asynchronously SERVICE_STATUS ss; if (!ControlService(service, SERVICE_CONTROL_STOP, &ss)) { - DWORD dwErrCode = GetLastError(); + DWORD dwErrCode = GetLastError(); if (dwErrCode != ERROR_SERVICE_NOT_ACTIVE) { throw XArchDaemonFailed(new XArchEvalWindows()); } @@ -681,7 +681,7 @@ ArchDaemonWindows::installDaemon() if (!isDaemonInstalled(DEFAULT_DAEMON_NAME)) { char path[MAX_PATH]; GetModuleFileName(ArchMiscWindows::instanceWin32(), path, MAX_PATH); - + // wrap in quotes so a malicious user can't start \Program.exe as admin. std::stringstream ss; ss << '"'; diff --git a/src/lib/arch/win32/ArchDaemonWindows.h b/src/lib/arch/win32/ArchDaemonWindows.h index 2db9792..7813f09 100644 --- a/src/lib/arch/win32/ArchDaemonWindows.h +++ b/src/lib/arch/win32/ArchDaemonWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/ArchInternetWindows.cpp b/src/lib/arch/win32/ArchInternetWindows.cpp index 8a28fde..fb75e7d 100644 --- a/src/lib/arch/win32/ArchInternetWindows.cpp +++ b/src/lib/arch/win32/ArchInternetWindows.cpp @@ -72,7 +72,7 @@ std::string ArchInternetWindows::urlEncode(const std::string& url) std::string result(buffer); - // the win32 url encoding funcitons are pretty useless (to us) and only + // the win32 url encoding functions are pretty useless (to us) and only // escape "unsafe" chars, but not + or =, so we need to replace these // manually (and probably many other chars). barrier::string::findReplaceAll(result, "+", "%2B"); @@ -121,12 +121,12 @@ std::string WinINetRequest::send() openSession(); connect(); openRequest(); - + std::string headers("Content-Type: text/html"); if (!HttpSendRequest(m_request, headers.c_str(), (DWORD)headers.length(), NULL, NULL)) { throw XArch(new XArchEvalWindows()); } - + std::stringstream result; CHAR buffer[1025]; DWORD read = 0; @@ -170,7 +170,7 @@ WinINetRequest::connect() INTERNET_SERVICE_HTTP, NULL, NULL); - + if (m_connect == NULL) { throw XArch(new XArchEvalWindows()); } diff --git a/src/lib/arch/win32/ArchLogWindows.cpp b/src/lib/arch/win32/ArchLogWindows.cpp index bc17abf..1b8855a 100644 --- a/src/lib/arch/win32/ArchLogWindows.cpp +++ b/src/lib/arch/win32/ArchLogWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/ArchLogWindows.h b/src/lib/arch/win32/ArchLogWindows.h index 3a997f1..efc466f 100644 --- a/src/lib/arch/win32/ArchLogWindows.h +++ b/src/lib/arch/win32/ArchLogWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/ArchMiscWindows.cpp b/src/lib/arch/win32/ArchMiscWindows.cpp index 2c022b1..ab16daa 100644 --- a/src/lib/arch/win32/ArchMiscWindows.cpp +++ b/src/lib/arch/win32/ArchMiscWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -429,7 +429,7 @@ ArchMiscWindows::wakeupDisplay() } bool -ArchMiscWindows::wasLaunchedAsService() +ArchMiscWindows::wasLaunchedAsService() { std::string name; if (!getParentProcessName(name)) { @@ -441,9 +441,9 @@ ArchMiscWindows::wasLaunchedAsService() } bool ArchMiscWindows::getParentProcessName(std::string &name) -{ +{ PROCESSENTRY32 parentEntry; - if (!getParentProcessEntry(parentEntry)){ + if (!getParentProcessEntry(parentEntry)) { LOG((CLOG_ERR "could not get entry for parent process")); return false; } @@ -452,14 +452,14 @@ bool ArchMiscWindows::getParentProcessName(std::string &name) return true; } -BOOL WINAPI +BOOL WINAPI ArchMiscWindows::getSelfProcessEntry(PROCESSENTRY32& entry) { // get entry from current PID return getProcessEntry(entry, GetCurrentProcessId()); } -BOOL WINAPI +BOOL WINAPI ArchMiscWindows::getParentProcessEntry(PROCESSENTRY32& entry) { // get the current process, so we can get parent PID @@ -472,24 +472,24 @@ ArchMiscWindows::getParentProcessEntry(PROCESSENTRY32& entry) return getProcessEntry(entry, selfEntry.th32ParentProcessID); } -BOOL WINAPI +BOOL WINAPI ArchMiscWindows::getProcessEntry(PROCESSENTRY32& entry, DWORD processID) { // first we need to take a snapshot of the running processes HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) { - LOG((CLOG_ERR "could not get process snapshot (error: %i)", + LOG((CLOG_ERR "could not get process snapshot (error: %i)", GetLastError())); return FALSE; } entry.dwSize = sizeof(PROCESSENTRY32); - // get the first process, and if we can't do that then it's + // get the first process, and if we can't do that then it's // unlikely we can go any further BOOL gotEntry = Process32First(snapshot, &entry); if (!gotEntry) { - LOG((CLOG_ERR "could not get first process entry (error: %i)", + LOG((CLOG_ERR "could not get first process entry (error: %i)", GetLastError())); return FALSE; } diff --git a/src/lib/arch/win32/ArchMiscWindows.h b/src/lib/arch/win32/ArchMiscWindows.h index 91cd8f5..d5373a0 100644 --- a/src/lib/arch/win32/ArchMiscWindows.h +++ b/src/lib/arch/win32/ArchMiscWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -167,7 +167,7 @@ public: static HINSTANCE instanceWin32(); static void setInstanceWin32(HINSTANCE instance); - + static BOOL WINAPI getProcessEntry(PROCESSENTRY32& entry, DWORD processID); static BOOL WINAPI getSelfProcessEntry(PROCESSENTRY32& entry); static BOOL WINAPI getParentProcessEntry(PROCESSENTRY32& entry); diff --git a/src/lib/arch/win32/ArchMultithreadWindows.cpp b/src/lib/arch/win32/ArchMultithreadWindows.cpp index d3fd059..43a7374 100644 --- a/src/lib/arch/win32/ArchMultithreadWindows.cpp +++ b/src/lib/arch/win32/ArchMultithreadWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -49,12 +49,10 @@ public: int m_refCount; HANDLE m_thread; DWORD m_id; - IArchMultithread::ThreadFunc m_func; - void* m_userData; + std::function func_; HANDLE m_cancel; bool m_cancelling; HANDLE m_exit; - void* m_result; void* m_networkData; }; @@ -62,10 +60,7 @@ ArchThreadImpl::ArchThreadImpl() : m_refCount(1), m_thread(NULL), m_id(0), - m_func(NULL), - m_userData(NULL), m_cancelling(false), - m_result(NULL), m_networkData(NULL) { m_exit = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -292,15 +287,13 @@ ArchMultithreadWindows::unlockMutex(ArchMutex mutex) LeaveCriticalSection(&mutex->m_mutex); } -ArchThread -ArchMultithreadWindows::newThread(ThreadFunc func, void* data) +ArchThread ArchMultithreadWindows::newThread(const std::function& func) { lockMutex(m_threadMutex); // create thread impl for new thread ArchThreadImpl* thread = new ArchThreadImpl; - thread->m_func = func; - thread->m_userData = data; + thread->func_ = func; // create thread unsigned int id = 0; @@ -523,15 +516,6 @@ ArchMultithreadWindows::isExitedThread(ArchThread thread) return (WaitForSingleObject(thread->m_exit, 0) == WAIT_OBJECT_0); } -void* -ArchMultithreadWindows::getResultOfThread(ArchThread thread) -{ - lockMutex(m_threadMutex); - void* result = thread->m_result; - unlockMutex(m_threadMutex); - return result; -} - IArchMultithread::ThreadID ArchMultithreadWindows::getIDOfThread(ArchThread thread) { @@ -678,10 +662,8 @@ ArchMultithreadWindows::doThreadFunc(ArchThread thread) lockMutex(m_threadMutex); unlockMutex(m_threadMutex); - void* result = NULL; try { - // go - result = (*thread->m_func)(thread->m_userData); + thread->func_(); } catch (XThreadCancel&) { @@ -695,9 +677,6 @@ ArchMultithreadWindows::doThreadFunc(ArchThread thread) } // thread has exited - lockMutex(m_threadMutex); - thread->m_result = result; - unlockMutex(m_threadMutex); SetEvent(thread->m_exit); // done with thread diff --git a/src/lib/arch/win32/ArchMultithreadWindows.h b/src/lib/arch/win32/ArchMultithreadWindows.h index 99aa640..31a2b30 100644 --- a/src/lib/arch/win32/ArchMultithreadWindows.h +++ b/src/lib/arch/win32/ArchMultithreadWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -73,7 +73,7 @@ public: virtual void closeMutex(ArchMutex); virtual void lockMutex(ArchMutex); virtual void unlockMutex(ArchMutex); - virtual ArchThread newThread(ThreadFunc, void*); + virtual ArchThread newThread(const std::function& func); virtual ArchThread newCurrentThread(); virtual ArchThread copyThread(ArchThread); virtual void closeThread(ArchThread); @@ -83,7 +83,6 @@ public: virtual bool wait(ArchThread, double timeout); virtual bool isSameThread(ArchThread, ArchThread); virtual bool isExitedThread(ArchThread); - virtual void* getResultOfThread(ArchThread); virtual ThreadID getIDOfThread(ArchThread); virtual void setSignalHandler(ESignal, SignalFunc, void*); virtual void raiseSignal(ESignal); diff --git a/src/lib/arch/win32/ArchNetworkWinsock.cpp b/src/lib/arch/win32/ArchNetworkWinsock.cpp index 4bc61d8..2d444fd 100644 --- a/src/lib/arch/win32/ArchNetworkWinsock.cpp +++ b/src/lib/arch/win32/ArchNetworkWinsock.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/ArchNetworkWinsock.h b/src/lib/arch/win32/ArchNetworkWinsock.h index 0b01671..7f65795 100644 --- a/src/lib/arch/win32/ArchNetworkWinsock.h +++ b/src/lib/arch/win32/ArchNetworkWinsock.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/ArchSleepWindows.cpp b/src/lib/arch/win32/ArchSleepWindows.cpp index 69648a7..0395031 100644 --- a/src/lib/arch/win32/ArchSleepWindows.cpp +++ b/src/lib/arch/win32/ArchSleepWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -57,5 +57,5 @@ ArchSleepWindows::sleep(double timeout) else { Sleep((DWORD)(1000.0 * timeout)); } - ARCH->testCancelThread(); + ARCH->testCancelThread(); } diff --git a/src/lib/arch/win32/ArchSleepWindows.h b/src/lib/arch/win32/ArchSleepWindows.h index d673caf..da89cea 100644 --- a/src/lib/arch/win32/ArchSleepWindows.h +++ b/src/lib/arch/win32/ArchSleepWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/ArchStringWindows.cpp b/src/lib/arch/win32/ArchStringWindows.cpp index deaf536..0033659 100644 --- a/src/lib/arch/win32/ArchStringWindows.cpp +++ b/src/lib/arch/win32/ArchStringWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -26,11 +26,6 @@ // ArchStringWindows // -#include "arch/multibyte.h" -#define HAVE_VSNPRINTF 1 -#define ARCH_VSNPRINTF _vsnprintf -#include "arch/vsnprintf.h" - ArchStringWindows::ArchStringWindows() { } diff --git a/src/lib/arch/win32/ArchStringWindows.h b/src/lib/arch/win32/ArchStringWindows.h index 23812dc..b2869b4 100644 --- a/src/lib/arch/win32/ArchStringWindows.h +++ b/src/lib/arch/win32/ArchStringWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/ArchSystemWindows.cpp b/src/lib/arch/win32/ArchSystemWindows.cpp index cf3b066..badcf28 100644 --- a/src/lib/arch/win32/ArchSystemWindows.cpp +++ b/src/lib/arch/win32/ArchSystemWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/ArchSystemWindows.h b/src/lib/arch/win32/ArchSystemWindows.h index 3d45ee6..835560d 100644 --- a/src/lib/arch/win32/ArchSystemWindows.h +++ b/src/lib/arch/win32/ArchSystemWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/ArchTaskBarWindows.cpp b/src/lib/arch/win32/ArchTaskBarWindows.cpp index 731dc59..bf71b74 100644 --- a/src/lib/arch/win32/ArchTaskBarWindows.cpp +++ b/src/lib/arch/win32/ArchTaskBarWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -88,7 +88,7 @@ ArchTaskBarWindows::init() // create a window on the current desktop with the current // thread then the current thread won't be able to switch // desktops if it needs to. - m_thread = ARCH->newThread(&ArchTaskBarWindows::threadEntry, this); + m_thread = ARCH->newThread([this]() { threadMainLoop(); }); // wait for child thread while (!m_ready) { @@ -501,14 +501,7 @@ ArchTaskBarWindows::threadMainLoop() UnregisterClass(className, instanceWin32()); } -void* -ArchTaskBarWindows::threadEntry(void* self) -{ - static_cast(self)->threadMainLoop(); - return NULL; -} - HINSTANCE ArchTaskBarWindows::instanceWin32() { return ArchMiscWindows::instanceWin32(); -} \ No newline at end of file +} diff --git a/src/lib/arch/win32/ArchTaskBarWindows.h b/src/lib/arch/win32/ArchTaskBarWindows.h index 0edddf8..2b8b7ad 100644 --- a/src/lib/arch/win32/ArchTaskBarWindows.h +++ b/src/lib/arch/win32/ArchTaskBarWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -36,7 +36,7 @@ public: virtual void init(); - //! Add a dialog window + //! Add a dialog window /*! Tell the task bar event loop about a dialog. Win32 annoyingly requires messages destined for modeless dialog boxes to be @@ -84,7 +84,6 @@ private: static LRESULT CALLBACK staticWndProc(HWND, UINT, WPARAM, LPARAM); void threadMainLoop(); - static void* threadEntry(void*); HINSTANCE instanceWin32(); diff --git a/src/lib/arch/win32/ArchTimeWindows.cpp b/src/lib/arch/win32/ArchTimeWindows.cpp index 568a483..eae0c15 100644 --- a/src/lib/arch/win32/ArchTimeWindows.cpp +++ b/src/lib/arch/win32/ArchTimeWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/ArchTimeWindows.h b/src/lib/arch/win32/ArchTimeWindows.h index 42351a1..c695e79 100644 --- a/src/lib/arch/win32/ArchTimeWindows.h +++ b/src/lib/arch/win32/ArchTimeWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/arch/win32/XArchWindows.cpp b/src/lib/arch/win32/XArchWindows.cpp index e116eda..eb3c151 100644 --- a/src/lib/arch/win32/XArchWindows.cpp +++ b/src/lib/arch/win32/XArchWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -57,58 +57,58 @@ XArchEvalWinsock::eval() const noexcept // may not look up network error messages correctly. we'll have // to do it ourself. static const struct { int m_code; const char* m_msg; } s_netErrorCodes[] = { - /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"}, - /* 10009 */{WSAEBADF, "Bad file handle"}, - /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"}, - /* 10014 */{WSAEFAULT, "WSAEFAULT"}, - /* 10022 */{WSAEINVAL, "WSAEINVAL"}, - /* 10024 */{WSAEMFILE, "No more file descriptors available"}, - /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"}, - /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"}, + /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"}, + /* 10009 */{WSAEBADF, "Bad file handle"}, + /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"}, + /* 10014 */{WSAEFAULT, "WSAEFAULT"}, + /* 10022 */{WSAEINVAL, "WSAEINVAL"}, + /* 10024 */{WSAEMFILE, "No more file descriptors available"}, + /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"}, + /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"}, /* 10037 */{WSAEALREADY, "The asynchronous routine being canceled has already completed"}, /* 10038 */{WSAENOTSOCK, "At least on descriptor is not a socket"}, /* 10039 */{WSAEDESTADDRREQ, "A destination address is required"}, /* 10040 */{WSAEMSGSIZE, "The datagram was too large to fit into the specified buffer and was truncated"}, - /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"}, - /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"}, - /* 10043 */{WSAEPROTONOSUPPORT,"The specified protocol is not supported"}, - /* 10044 */{WSAESOCKTNOSUPPORT,"The specified socket type is not supported by this address family"}, - /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"}, + /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"}, + /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"}, + /* 10043 */{WSAEPROTONOSUPPORT, "The specified protocol is not supported"}, + /* 10044 */{WSAESOCKTNOSUPPORT, "The specified socket type is not supported by this address family"}, + /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"}, /* 10046 */{WSAEPFNOSUPPORT, "BSD: Protocol family not supported"}, /* 10047 */{WSAEAFNOSUPPORT, "The specified address family is not supported"}, - /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"}, - /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"}, + /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"}, + /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"}, /* 10050 */{WSAENETDOWN, "The Windows Sockets implementation has detected that the network subsystem has failed"}, - /* 10051 */{WSAENETUNREACH, "The network can't be reached from this host at this time"}, - /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"}, + /* 10051 */{WSAENETUNREACH, "The network can't be reached from this host at this time"}, + /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"}, /* 10053 */{WSAECONNABORTED, "The virtual circuit was aborted due to timeout or other failure"}, - /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"}, - /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occured. The socket cannot be created"}, - /* 10056 */{WSAEISCONN, "The socket is already connected"}, + /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"}, + /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occurred. The socket cannot be created"}, + /* 10056 */{WSAEISCONN, "The socket is already connected"}, /* 10057 */{WSAENOTCONN, "The socket is not connected"}, - /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"}, + /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"}, /* 10059 */{WSAETOOMANYREFS, "BSD: Too many references"}, - /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"}, + /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"}, /* 10061 */{WSAECONNREFUSED, "Connection was refused"}, - /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"}, + /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"}, /* 10063 */{WSAENAMETOOLONG, "Undocumented WinSock error code used in BSD"}, - /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"}, + /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"}, /* 10065 */{WSAEHOSTUNREACH, "No route to host"}, - /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"}, + /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"}, /* 10067 */{WSAEPROCLIM, "Undocumented WinSock error code"}, - /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"}, - /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"}, - /* 10070 */{WSAESTALE, "Undocumented WinSock error code"}, - /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"}, - /* 10091 */{WSASYSNOTREADY, "Underlying network subsytem is not ready for network communication"}, - /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"}, - /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"}, - /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"}, - /* 11001 */{WSAHOST_NOT_FOUND, "The specified host is unknown"}, - /* 11002 */{WSATRY_AGAIN, "A temporary error occurred on an authoritative name server"}, - /* 11003 */{WSANO_RECOVERY, "A non-recoverable name server error occurred"}, - /* 11004 */{WSANO_DATA, "The requested name is valid but does not have an IP address"}, - /* end */{0, NULL} + /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"}, + /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"}, + /* 10070 */{WSAESTALE, "Undocumented WinSock error code"}, + /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"}, + /* 10091 */{WSASYSNOTREADY, "Underlying network subsystem is not ready for network communication"}, + /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"}, + /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"}, + /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"}, + /* 11001 */{WSAHOST_NOT_FOUND, "The specified host is unknown"}, + /* 11002 */{WSATRY_AGAIN, "A temporary error occurred on an authoritative name server"}, + /* 11003 */{WSANO_RECOVERY, "A non-recoverable name server error occurred"}, + /* 11004 */{WSANO_DATA, "The requested name is valid but does not have an IP address"}, + /* end */{0, NULL} }; for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) { diff --git a/src/lib/arch/win32/XArchWindows.h b/src/lib/arch/win32/XArchWindows.h index 4fb2a23..5ac71f2 100644 --- a/src/lib/arch/win32/XArchWindows.h +++ b/src/lib/arch/win32/XArchWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/App.cpp b/src/lib/barrier/App.cpp index 8a79aa2..2b3eccc 100644 --- a/src/lib/barrier/App.cpp +++ b/src/lib/barrier/App.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -34,7 +34,6 @@ #if SYSAPI_WIN32 #include "base/IEventQueue.h" -#include "base/TMethodJob.h" #endif #include @@ -79,18 +78,18 @@ App::~App() void App::version() { - std::cout << argsBase().m_exename << " " << kVersion << std::endl; - std::cout <<"Protocol version " << kProtocolMajorVersion << "." << kProtocolMinorVersion << std::endl; - std::cout << kCopyright << std::endl; + std::cout << argsBase().m_exename << " " << kVersion << "\n"; + std::cout <<"Protocol version " << kProtocolMajorVersion << "." << kProtocolMinorVersion << "\n"; + std::cout << kCopyright << "\n"; } int App::run(int argc, char** argv) -{ +{ #if MAC_OS_X_VERSION_10_7 // dock hide only supported on lion :( ProcessSerialNumber psn = { 0, kCurrentProcess }; - + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" GetCurrentProcess(&psn); @@ -101,7 +100,7 @@ App::run(int argc, char** argv) // install application in to arch appUtil().adoptApp(this); - + // HACK: fail by default (saves us setting result in each catch) int result = kExitFailed; @@ -110,7 +109,7 @@ App::run(int argc, char** argv) } catch (XExitApp& e) { // instead of showing a nasty error, just exit with the error code. - // not sure if i like this behaviour, but it's probably better than + // not sure if i like this behaviour, but it's probably better than // using the exit(int) function! result = e.getCode(); } @@ -122,7 +121,7 @@ App::run(int argc, char** argv) } appUtil().beforeAppExit(); - + return result; } @@ -137,7 +136,7 @@ App::daemonMainLoop(int, const char**) return mainLoop(); } -void +void App::setupFileLogging() { if (argsBase().m_logFile != NULL) { @@ -147,24 +146,24 @@ App::setupFileLogging() } } -void +void App::loggingFilterWarning() { if (CLOG->getFilter() > CLOG->getConsoleMaxLevel()) { if (argsBase().m_logFile == NULL) { - LOG((CLOG_WARN "log messages above %s are NOT sent to console (use file logging)", + LOG((CLOG_WARN "log messages above %s are NOT sent to console (use file logging)", CLOG->getFilterName(CLOG->getConsoleMaxLevel()))); } } } -void +void App::initApp(int argc, const char** argv) { // parse command line parseArgs(argc, argv); - - DataDirectories::profile(argsBase().m_profileDirectory); + + barrier::DataDirectories::profile(argsBase().m_profileDirectory); // set log filter if (!CLOG->setFilter(argsBase().m_logFilter)) { @@ -173,9 +172,12 @@ App::initApp(int argc, const char** argv) m_bye(kExitArgs); } loggingFilterWarning(); - + if (argsBase().m_enableDragDrop) { LOG((CLOG_INFO "drag and drop enabled")); + if (!argsBase().m_dropTarget.empty()) { + LOG((CLOG_INFO "drop target: %s", argsBase().m_dropTarget.c_str())); + } } // setup file logging after parsing args @@ -226,15 +228,14 @@ App::handleIpcMessage(const Event& e, void*) } } -void -App::runEventsLoop(void*) +void App::run_events_loop() { m_events->loop(); - + #if defined(MAC_OS_X_VERSION_10_7) - + stopCocoaLoop(); - + #endif } diff --git a/src/lib/barrier/App.h b/src/lib/barrier/App.h index 8040da8..8e17a71 100644 --- a/src/lib/barrier/App.h +++ b/src/lib/barrier/App.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -59,7 +59,7 @@ public: // Parse command line arguments. virtual void parseArgs(int argc, const char* const* argv) = 0; - + int run(int argc, char** argv); int daemonMainLoop(int, const char**); @@ -90,11 +90,11 @@ public: ARCH_APP_UTIL& appUtil() { return m_appUtil; } - virtual IArchTaskBarReceiver* taskBarReceiver() const { return m_taskBarReceiver; } + virtual IArchTaskBarReceiver* taskBarReceiver() const { return m_taskBarReceiver; } virtual void setByeFunc(void(*bye)(int)) { m_bye = bye; } virtual void bye(int error) { m_bye(error); } - + virtual IEventQueue* getEvents() const { return m_events; } void setSocketMultiplexer(std::unique_ptr&& sm) { m_socketMultiplexer = std::move(sm); } @@ -108,7 +108,7 @@ private: protected: void initIpcClient(); void cleanupIpcClient(); - void runEventsLoop(void*); + void run_events_loop(); IArchTaskBarReceiver* m_taskBarReceiver; bool m_suspended; @@ -135,7 +135,7 @@ public: virtual void startNode(); virtual int mainLoop(); virtual int foregroundStartup(int argc, char** argv); - virtual barrier::Screen* + virtual barrier::Screen* createScreen(); virtual void loadConfig(); virtual bool loadConfig(const String& pathname); @@ -166,7 +166,10 @@ private: " -l --log write log messages to file.\n" \ " --no-tray disable the system tray icon.\n" \ " --enable-drag-drop enable file drag & drop.\n" \ - " --enable-crypto enable the crypto (ssl) plugin.\n" + " --enable-crypto enable the crypto (ssl) plugin (default, deprecated).\n" \ + " --disable-crypto disable the crypto (ssl) plugin.\n" \ + " --profile-dir use named profile directory instead.\n" \ + " --drop-dir use named drop target directory instead.\n" #define HELP_COMMON_INFO_2 \ " -h, --help display this help and exit.\n" \ @@ -191,12 +194,11 @@ private: // windows args # define HELP_SYS_ARGS \ - " [--service ] [--relaunch] [--exit-pause]" + " [--exit-pause]" # define HELP_SYS_INFO \ " --service manage the windows service, valid options are:\n" \ " install/uninstall/start/stop\n" \ - " --relaunch persistently relaunches process in current user \n" \ - " session (useful for vista and upward).\n" \ + " (obsolete, use barrierd instead)\n" \ " --exit-pause wait for key press on exit, can be useful for\n" \ " reading error messages that occur on exit.\n" #endif diff --git a/src/lib/barrier/AppUtil.cpp b/src/lib/barrier/AppUtil.cpp index 3298d7b..d615648 100644 --- a/src/lib/barrier/AppUtil.cpp +++ b/src/lib/barrier/AppUtil.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -15,11 +15,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #include "barrier/AppUtil.h" AppUtil* AppUtil::s_instance = nullptr; - + AppUtil::AppUtil() : m_app(nullptr) { diff --git a/src/lib/barrier/AppUtil.h b/src/lib/barrier/AppUtil.h index 6f5f073..097c7f2 100644 --- a/src/lib/barrier/AppUtil.h +++ b/src/lib/barrier/AppUtil.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #pragma once #include "barrier/IAppUtil.h" @@ -33,7 +33,7 @@ public: static AppUtil& instance(); static void exitAppStatic(int code) { instance().exitApp(code); } virtual void beforeAppExit() {} - + private: IApp* m_app; static AppUtil* s_instance; diff --git a/src/lib/barrier/ArgParser.cpp b/src/lib/barrier/ArgParser.cpp index ec3991c..99cd803 100644 --- a/src/lib/barrier/ArgParser.cpp +++ b/src/lib/barrier/ArgParser.cpp @@ -24,7 +24,7 @@ #include "barrier/ArgsBase.h" #include "base/Log.h" #include "base/String.h" -#include "common/PathUtilities.h" +#include "io/filesystem.h" #ifdef WINAPI_MSWINDOWS #include @@ -65,7 +65,9 @@ ArgParser::parseServerArgs(ServerArgs& args, int argc, const char* const* argv) // save screen change script path args.m_screenChangeScript = argv[++i]; } - else { + else if (isArg(i, argc, argv, nullptr, "--disable-client-cert-checking")) { + args.check_client_certificates = false; + } else { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, args.m_exename.c_str(), argv[i], args.m_exename.c_str())); return false; } @@ -133,10 +135,10 @@ ArgParser::parseClientArgs(ClientArgs& args, int argc, const char* const* argv) return true; } +#if WINAPI_MSWINDOWS bool -ArgParser::parsePlatformArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i) +ArgParser::parseMSWindowsArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i) { -#if WINAPI_MSWINDOWS if (isArg(i, argc, argv, NULL, "--service")) { LOG((CLOG_WARN "obsolete argument --service, use barrierd instead.")); argsBase.m_shouldExit = true; @@ -153,25 +155,46 @@ ArgParser::parsePlatformArg(ArgsBase& argsBase, const int& argc, const char* con } return true; -#elif WINAPI_XWINDOWS +} +#endif + +#if WINAPI_CARBON +bool +ArgParser::parseCarbonArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i) +{ + // no options for carbon + return false; +} +#endif + +#if WINAPI_XWINDOWS +bool +ArgParser::parseXWindowsArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i) +{ if (isArg(i, argc, argv, "-display", "--display", 1)) { // use alternative display argsBase.m_display = argv[++i]; } - else if (isArg(i, argc, argv, NULL, "--no-xinitthreads")) { argsBase.m_disableXInitThreads = true; - } - - else { + } else { // option not supported here return false; } return true; +} +#endif + +bool +ArgParser::parsePlatformArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i) +{ +#if WINAPI_MSWINDOWS + return parseMSWindowsArg(argsBase, argc, argv, i); #elif WINAPI_CARBON - // no options for carbon - return false; + return parseCarbonArg(argsBase, argc, argv, i); +#elif WINAPI_XWINDOWS + return parseXWindowsArg(argsBase, argc, argv, i); #endif } @@ -257,14 +280,20 @@ ArgParser::parseGenericArgs(int argc, const char* const* argv, int& i) argsBase().m_enableDragDrop = true; } } + else if (isArg(i, argc, argv, NULL, "--drop-dir")) { + argsBase().m_dropTarget = argv[++i]; + } else if (isArg(i, argc, argv, NULL, "--enable-crypto")) { - argsBase().m_enableCrypto = true; + LOG((CLOG_INFO "--enable-crypto is used by default. The option is deprecated.")); + } + else if (isArg(i, argc, argv, NULL, "--disable-crypto")) { + argsBase().m_enableCrypto = false; } else if (isArg(i, argc, argv, NULL, "--profile-dir", 1)) { - argsBase().m_profileDirectory = argv[++i]; + argsBase().m_profileDirectory = barrier::fs::u8path(argv[++i]); } else if (isArg(i, argc, argv, NULL, "--plugin-dir", 1)) { - argsBase().m_pluginDirectory = argv[++i]; + argsBase().m_pluginDirectory = barrier::fs::u8path(argv[++i]); } else { // option not supported here @@ -349,7 +378,7 @@ ArgParser::splitCommandString(String& command, std::vector& argv) if (space > leftDoubleQuote && space < rightDoubleQuote) { ignoreThisSpace = true; } - else if (space > rightDoubleQuote){ + else if (space > rightDoubleQuote) { searchDoubleQuotes(command, leftDoubleQuote, rightDoubleQuote, rightDoubleQuote + 1); } @@ -460,7 +489,13 @@ void ArgParser::updateCommonArgs(const char* const* argv) { argsBase().m_name = ARCH->getHostName(); - argsBase().m_exename = PathUtilities::basename(argv[0]); + argsBase().m_exename = parse_exename(argv[0]); +} + +std::string ArgParser::parse_exename(const char* arg) +{ + // FIXME: we assume UTF-8 encoding, but on Windows this is not correct + return barrier::fs::u8path(arg).filename().u8string(); } bool diff --git a/src/lib/barrier/ArgParser.h b/src/lib/barrier/ArgParser.h index 32300c6..472d93a 100644 --- a/src/lib/barrier/ArgParser.h +++ b/src/lib/barrier/ArgParser.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #pragma once #include "base/String.h" @@ -41,21 +41,27 @@ public: const char* name1, const char* name2, int minRequiredParameters = 0); static void splitCommandString(String& command, std::vector& argv); - static bool searchDoubleQuotes(String& command, size_t& left, + static bool searchDoubleQuotes(String& command, size_t& left, size_t& right, size_t startPos = 0); static void removeDoubleQuotes(String& arg); static const char** getArgv(std::vector& argsArray); - static String assembleCommand(std::vector& argsArray, + static String assembleCommand(std::vector& argsArray, String ignoreArg = "", int parametersRequired = 0); + static std::string parse_exename(const char* arg); + private: void updateCommonArgs(const char* const* argv); bool checkUnexpectedArgs(); - + static ArgsBase& argsBase() { return *m_argsBase; } + bool parseMSWindowsArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i); + bool parseCarbonArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i); + bool parseXWindowsArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i); + private: App* m_app; - + static ArgsBase* m_argsBase; }; diff --git a/src/lib/barrier/ArgsBase.cpp b/src/lib/barrier/ArgsBase.cpp index eb63150..e3e3803 100644 --- a/src/lib/barrier/ArgsBase.cpp +++ b/src/lib/barrier/ArgsBase.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -39,10 +39,11 @@ m_display(NULL), m_disableTray(false), m_enableIpc(false), m_enableDragDrop(false), +m_dropTarget(""), m_shouldExit(false), m_barrierAddress(), -m_enableCrypto(false), -m_profileDirectory(""), + m_enableCrypto(true), +m_profileDirectory(), m_pluginDirectory("") { } diff --git a/src/lib/barrier/ArgsBase.h b/src/lib/barrier/ArgsBase.h index 99929b3..cdb5092 100644 --- a/src/lib/barrier/ArgsBase.h +++ b/src/lib/barrier/ArgsBase.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -19,6 +19,7 @@ #pragma once #include "base/String.h" +#include "io/filesystem.h" class ArgsBase { public: @@ -38,6 +39,7 @@ public: bool m_disableTray; bool m_enableIpc; bool m_enableDragDrop; + String m_dropTarget; #if SYSAPI_WIN32 bool m_debugServiceWait; bool m_pauseOnExit; @@ -49,6 +51,6 @@ public: bool m_shouldExit; String m_barrierAddress; bool m_enableCrypto; - String m_profileDirectory; - String m_pluginDirectory; + barrier::fs::path m_profileDirectory; + barrier::fs::path m_pluginDirectory; }; diff --git a/src/lib/barrier/BarrierType.h b/src/lib/barrier/BarrierType.h new file mode 100644 index 0000000..0a18477 --- /dev/null +++ b/src/lib/barrier/BarrierType.h @@ -0,0 +1,26 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_BARRIER_BARRIER_TYPE_H +#define BARRIER_LIB_BARRIER_BARRIER_TYPE_H + +enum class BarrierType { + Server, + Client +}; + +#endif // BARRIER_LIB_BARRIER_BARRIER_TYPE_H diff --git a/src/lib/barrier/CMakeLists.txt b/src/lib/barrier/CMakeLists.txt index 6978aef..b60936d 100644 --- a/src/lib/barrier/CMakeLists.txt +++ b/src/lib/barrier/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/Chunk.cpp b/src/lib/barrier/Chunk.cpp index f11bff5..fd69cdb 100644 --- a/src/lib/barrier/Chunk.cpp +++ b/src/lib/barrier/Chunk.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/Chunk.h b/src/lib/barrier/Chunk.h index 42b85bf..26a343d 100644 --- a/src/lib/barrier/Chunk.h +++ b/src/lib/barrier/Chunk.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -17,7 +17,7 @@ #pragma once -#include "common/basic_types.h" +#include class Chunk { public: diff --git a/src/lib/barrier/ClientApp.cpp b/src/lib/barrier/ClientApp.cpp index b1a7661..4b0ef61 100644 --- a/src/lib/barrier/ClientApp.cpp +++ b/src/lib/barrier/ClientApp.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -37,10 +37,8 @@ #include "base/TMethodEventJob.h" #include "base/log_outputters.h" #include "base/EventQueue.h" -#include "base/TMethodJob.h" #include "base/Log.h" #include "common/Version.h" -#include "common/PathUtilities.h" #if WINAPI_MSWINDOWS #include "platform/MSWindowsScreen.h" @@ -118,23 +116,23 @@ ClientApp::help() #endif std::ostringstream buffer; - buffer << "Start the barrier client and connect to a remote server component." << std::endl - << std::endl + buffer << "Start the barrier client and connect to a remote server component.\n" + << "\n" << "Usage: " << args().m_exename << " [--yscroll ]" << WINAPI_ARG << HELP_SYS_ARGS - << HELP_COMMON_ARGS << " " << std::endl - << std::endl - << "Options:" << std::endl + << HELP_COMMON_ARGS << " \n" + << "\n" + << "Options:\n" << HELP_COMMON_INFO_1 << WINAPI_INFO << HELP_SYS_INFO - << " --yscroll defines the vertical scrolling delta, which is" << std::endl - << " 120 by default." << std::endl + << " --yscroll defines the vertical scrolling delta, which is\n" + << " 120 by default.\n" << HELP_COMMON_INFO_2 - << std::endl - << "Default options are marked with a *" << std::endl - << std::endl - << "The server address is of the form: [][:]. The hostname" << std::endl - << "must be the address or hostname of the server. Placing brackets around" << std::endl - << "an IPv6 address is required when also specifying a port number and " << std::endl - << "optional otherwise. The default port number is " << kDefaultPort << "." << std::endl; + << "\n" + << "Default options are marked with a *\n" + << "\n" + << "The server address is of the form: [][:]. The hostname\n" + << "must be the address or hostname of the server. Placing brackets around\n" + << "an IPv6 address is required when also specifying a port number and \n" + << "optional otherwise. The default port number is " << kDefaultPort << ".\n"; LOG((CLOG_PRINT "%s", buffer.str().c_str())); } @@ -235,6 +233,9 @@ barrier::Screen* ClientApp::openClientScreen() { barrier::Screen* screen = createScreen(); + if (!argsBase().m_dropTarget.empty()) { + screen->setDropTarget(argsBase().m_dropTarget); + } screen->setEnableDragDrop(argsBase().m_enableDragDrop); m_events->adoptHandler(m_events->forIScreen().error(), screen->getEventTarget(), @@ -449,7 +450,7 @@ ClientApp::mainLoop() // start client, etc appUtil().startNode(); - + // init ipc client after node start, since create a new screen wipes out // the event queue (the screen ctors call adoptBuffer). if (argsBase().m_enableIpc) { @@ -460,24 +461,21 @@ ClientApp::mainLoop() // later. the timer installed by startClient() will take care of // that. DAEMON_RUNNING(true); - + #if defined(MAC_OS_X_VERSION_10_7) - - Thread thread( - new TMethodJob( - this, &ClientApp::runEventsLoop, - NULL)); - + + Thread thread([this](){ run_events_loop(); }); + // wait until carbon loop is ready OSXScreen* screen = dynamic_cast( m_clientScreen->getPlatformScreen()); screen->waitForCarbonLoop(); - + runCocoaApp(); #else m_events->loop(); #endif - + DAEMON_RUNNING(false); // close down @@ -519,7 +517,7 @@ ClientApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc { // general initialization m_serverAddress = new NetworkAddress; - args().m_exename = PathUtilities::basename(argv[0]); + argsBase().m_exename = ArgParser::parse_exename(argv[0]); // install caller's output filter if (outputter != NULL) { @@ -548,7 +546,7 @@ ClientApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc return result; } -void +void ClientApp::startNode() { // start the client. if this return false then we've failed and diff --git a/src/lib/barrier/ClientApp.h b/src/lib/barrier/ClientApp.h index 777f3d3..d1db8d0 100644 --- a/src/lib/barrier/ClientApp.h +++ b/src/lib/barrier/ClientApp.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -24,7 +24,6 @@ namespace barrier { class Screen; } class Event; class Client; class NetworkAddress; -class Thread; class ClientArgs; class ClientApp : public App { @@ -64,7 +63,7 @@ public: void handleClientConnected(const Event&, void*); void handleClientFailed(const Event& e, void*); void handleClientDisconnected(const Event&, void*); - Client* openClient(const String& name, const NetworkAddress& address, + Client* openClient(const String& name, const NetworkAddress& address, barrier::Screen* screen); void closeClient(Client* client); bool startClient(); diff --git a/src/lib/barrier/ClientArgs.cpp b/src/lib/barrier/ClientArgs.cpp index 5c9ed88..9d1d37a 100644 --- a/src/lib/barrier/ClientArgs.cpp +++ b/src/lib/barrier/ClientArgs.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/ClientArgs.h b/src/lib/barrier/ClientArgs.h index 70285fa..c11fa28 100644 --- a/src/lib/barrier/ClientArgs.h +++ b/src/lib/barrier/ClientArgs.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/ClientTaskBarReceiver.cpp b/src/lib/barrier/ClientTaskBarReceiver.cpp index 2ea6566..ea29f3d 100644 --- a/src/lib/barrier/ClientTaskBarReceiver.cpp +++ b/src/lib/barrier/ClientTaskBarReceiver.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/ClientTaskBarReceiver.h b/src/lib/barrier/ClientTaskBarReceiver.h index da15154..b79b958 100644 --- a/src/lib/barrier/ClientTaskBarReceiver.h +++ b/src/lib/barrier/ClientTaskBarReceiver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/Clipboard.cpp b/src/lib/barrier/Clipboard.cpp index a6a166d..d7ad0b8 100644 --- a/src/lib/barrier/Clipboard.cpp +++ b/src/lib/barrier/Clipboard.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/Clipboard.h b/src/lib/barrier/Clipboard.h index 23bea75..5365a8c 100644 --- a/src/lib/barrier/Clipboard.h +++ b/src/lib/barrier/Clipboard.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/ClipboardChunk.cpp b/src/lib/barrier/ClipboardChunk.cpp index bc71471..c2ffab0 100644 --- a/src/lib/barrier/ClipboardChunk.cpp +++ b/src/lib/barrier/ClipboardChunk.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -74,7 +74,7 @@ ClipboardChunk::end(ClipboardID id, UInt32 sequence) { ClipboardChunk* end = new ClipboardChunk(CLIPBOARD_CHUNK_META_SIZE); char* chunk = end->m_chunk; - + chunk[0] = id; std::memcpy (&chunk[1], &sequence, 4); chunk[5] = kDataEnd; @@ -95,7 +95,7 @@ ClipboardChunk::assemble(barrier::IStream* stream, if (!ProtocolUtil::readf(stream, kMsgDClipboard + 4, &id, &sequence, &mark, &data)) { return kError; } - + if (mark == kDataStart) { s_expectedSize = barrier::string::stringToSizeType(data); LOG((CLOG_DEBUG "start receiving clipboard data")); diff --git a/src/lib/barrier/ClipboardChunk.h b/src/lib/barrier/ClipboardChunk.h index 6402aca..a96877f 100644 --- a/src/lib/barrier/ClipboardChunk.h +++ b/src/lib/barrier/ClipboardChunk.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/DragInformation.cpp b/src/lib/barrier/DragInformation.cpp index db28f3d..5a46678 100644 --- a/src/lib/barrier/DragInformation.cpp +++ b/src/lib/barrier/DragInformation.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -41,7 +41,7 @@ DragInformation::parseDragInfo(DragFileList& dragFileList, UInt32 fileNum, Strin if (data.find("/", startPos) != string::npos) { slash = "/"; } - + UInt32 index = 0; while (index < fileNum) { findResult1 = data.find(',', startPos); @@ -51,7 +51,7 @@ DragInformation::parseDragInfo(DragFileList& dragFileList, UInt32 fileNum, Strin //TODO: file number does not match, something goes wrong break; } - + // set filename if (findResult1 - findResult2 > 1) { String filename = data.substr(findResult2 + 1, @@ -61,7 +61,7 @@ DragInformation::parseDragInfo(DragFileList& dragFileList, UInt32 fileNum, Strin dragFileList.push_back(di); } startPos = findResult1 + 1; - + //set filesize findResult2 = data.find(',', startPos); if (findResult2 - findResult1 > 1) { @@ -71,7 +71,7 @@ DragInformation::parseDragInfo(DragFileList& dragFileList, UInt32 fileNum, Strin dragFileList.at(index).setFilesize(size); } startPos = findResult1 + 1; - + ++index; } @@ -151,8 +151,8 @@ DragInformation::getFileSize(String& filename) stringstream ss; ss << size; - + file. close(); - + return ss.str(); } diff --git a/src/lib/barrier/DragInformation.h b/src/lib/barrier/DragInformation.h index b985bd1..a5d76b0 100644 --- a/src/lib/barrier/DragInformation.h +++ b/src/lib/barrier/DragInformation.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -28,12 +28,12 @@ class DragInformation { public: DragInformation(); ~DragInformation() { } - + String& getFilename() { return m_filename; } void setFilename(String& name) { m_filename = name; } size_t getFilesize() { return m_filesize; } void setFilesize(size_t size) { m_filesize = size; } - + static void parseDragInfo(DragFileList& dragFileList, UInt32 fileNum, String data); static String getDragFileExtension(String filename); // helper function to setup drag info diff --git a/src/lib/barrier/DropHelper.cpp b/src/lib/barrier/DropHelper.cpp index ee5e5ee..af22b52 100644 --- a/src/lib/barrier/DropHelper.cpp +++ b/src/lib/barrier/DropHelper.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -18,6 +18,7 @@ #include "barrier/DropHelper.h" #include "base/Log.h" +#include "io/filesystem.h" #include @@ -35,15 +36,15 @@ DropHelper::writeToDir(const String& destination, DragFileList& fileList, String dropTarget.append("/"); #endif dropTarget.append(fileList.at(0).getFilename()); - file.open(dropTarget.c_str(), std::ios::out | std::ios::binary); + barrier::open_utf8_path(file, dropTarget, std::ios::out | std::ios::binary); if (!file.is_open()) { LOG((CLOG_ERR "drop file failed: can not open %s", dropTarget.c_str())); } - + file.write(data.c_str(), data.size()); file.close(); - LOG((CLOG_DEBUG "%s is saved to %s", fileList.at(0).getFilename().c_str(), destination.c_str())); + LOG((CLOG_INFO "dropped file \"%s\" in \"%s\"", fileList.at(0).getFilename().c_str(), destination.c_str())); fileList.clear(); } diff --git a/src/lib/barrier/DropHelper.h b/src/lib/barrier/DropHelper.h index 67facbb..801779c 100644 --- a/src/lib/barrier/DropHelper.h +++ b/src/lib/barrier/DropHelper.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/FileChunk.cpp b/src/lib/barrier/FileChunk.cpp index 3a98568..08c489b 100644 --- a/src/lib/barrier/FileChunk.cpp +++ b/src/lib/barrier/FileChunk.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/FileChunk.h b/src/lib/barrier/FileChunk.h index bdc2f64..bdb5006 100644 --- a/src/lib/barrier/FileChunk.h +++ b/src/lib/barrier/FileChunk.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/IApp.h b/src/lib/barrier/IApp.h index 3a8cd56..0c516b9 100644 --- a/src/lib/barrier/IApp.h +++ b/src/lib/barrier/IApp.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/IAppUtil.h b/src/lib/barrier/IAppUtil.h index 39df65d..521bfce 100644 --- a/src/lib/barrier/IAppUtil.h +++ b/src/lib/barrier/IAppUtil.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #pragma once #include "common/IInterface.h" diff --git a/src/lib/barrier/IClient.h b/src/lib/barrier/IClient.h index d9b2194..e1382b7 100644 --- a/src/lib/barrier/IClient.h +++ b/src/lib/barrier/IClient.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/IClipboard.cpp b/src/lib/barrier/IClipboard.cpp index 19b4b56..d484121 100644 --- a/src/lib/barrier/IClipboard.cpp +++ b/src/lib/barrier/IClipboard.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -66,13 +66,13 @@ IClipboard::unmarshall(IClipboard* clipboard, const String& data, Time time) String IClipboard::marshall(const IClipboard* clipboard) { - // return data format: + // return data format: // 4 bytes => number of formats included // 4 bytes => format enum // 4 bytes => clipboard data size n // n bytes => clipboard data // back to the second 4 bytes if there is another format - + assert(clipboard != NULL); String data; diff --git a/src/lib/barrier/IClipboard.h b/src/lib/barrier/IClipboard.h index e11b264..436b21b 100644 --- a/src/lib/barrier/IClipboard.h +++ b/src/lib/barrier/IClipboard.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/IKeyState.cpp b/src/lib/barrier/IKeyState.cpp index 5d1114c..e89c0e9 100644 --- a/src/lib/barrier/IKeyState.cpp +++ b/src/lib/barrier/IKeyState.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/IKeyState.h b/src/lib/barrier/IKeyState.h index b9d4706..e7f88fa 100644 --- a/src/lib/barrier/IKeyState.h +++ b/src/lib/barrier/IKeyState.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -122,14 +122,14 @@ public: complete and false if normal key processing should continue. */ virtual bool fakeCtrlAltDel() = 0; - + //! Fake a media key /*! Synthesizes a media key down and up. Only Mac would implement this by use cocoa appkit framework. */ virtual bool fakeMediaKey(KeyID id) = 0; - + //@} //! @name accessors //@{ diff --git a/src/lib/barrier/INode.h b/src/lib/barrier/INode.h index 2e78f7c..02fd9de 100644 --- a/src/lib/barrier/INode.h +++ b/src/lib/barrier/INode.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -21,5 +21,5 @@ #include "common/IInterface.h" class INode : IInterface { - + }; diff --git a/src/lib/barrier/IPlatformScreen.cpp b/src/lib/barrier/IPlatformScreen.cpp index d1d9f78..a037f75 100644 --- a/src/lib/barrier/IPlatformScreen.cpp +++ b/src/lib/barrier/IPlatformScreen.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2016 Symless. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file COPYING that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/IPlatformScreen.h b/src/lib/barrier/IPlatformScreen.h index 440e218..995ff73 100644 --- a/src/lib/barrier/IPlatformScreen.h +++ b/src/lib/barrier/IPlatformScreen.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -197,7 +197,8 @@ public: virtual void fakeDraggingFiles(DragFileList fileList) = 0; virtual const String& getDropTarget() const = 0; - + virtual void setDropTarget(const String&) = 0; + protected: //! Handle system event /*! diff --git a/src/lib/barrier/IPrimaryScreen.cpp b/src/lib/barrier/IPrimaryScreen.cpp index 4954e4f..2220212 100644 --- a/src/lib/barrier/IPrimaryScreen.cpp +++ b/src/lib/barrier/IPrimaryScreen.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/IPrimaryScreen.h b/src/lib/barrier/IPrimaryScreen.h index 7f3fa9c..0cf3688 100644 --- a/src/lib/barrier/IPrimaryScreen.h +++ b/src/lib/barrier/IPrimaryScreen.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/IScreen.h b/src/lib/barrier/IScreen.h index 47d6578..a1e7c47 100644 --- a/src/lib/barrier/IScreen.h +++ b/src/lib/barrier/IScreen.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -66,6 +66,6 @@ public: Return the current position of the cursor in \c x and \c y. */ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; - + //@} }; diff --git a/src/lib/barrier/IScreenSaver.h b/src/lib/barrier/IScreenSaver.h index fc21ac5..2099f6d 100644 --- a/src/lib/barrier/IScreenSaver.h +++ b/src/lib/barrier/IScreenSaver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -56,7 +56,7 @@ public: //! Deactivate screen saver /*! - Deactivate (i.e. hide) the screen saver, reseting the screen saver + Deactivate (i.e. hide) the screen saver, resetting the screen saver timer. */ virtual void deactivate() = 0; diff --git a/src/lib/barrier/ISecondaryScreen.h b/src/lib/barrier/ISecondaryScreen.h index 527ca2e..85da511 100644 --- a/src/lib/barrier/ISecondaryScreen.h +++ b/src/lib/barrier/ISecondaryScreen.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/KeyMap.cpp b/src/lib/barrier/KeyMap.cpp index 621e747..7d53deb 100644 --- a/src/lib/barrier/KeyMap.cpp +++ b/src/lib/barrier/KeyMap.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2005 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -733,7 +733,7 @@ KeyMap::keyForModifier(KeyButton button, SInt32 group, assert(modifierBit >= 0 && modifierBit < kKeyModifierNumBits); assert(group >= 0 && group < getNumGroups()); - // find a key that generates the given modifier in the given group + // find a key that generates the given modifier in the given group // but doesn't use the given button, presumably because we're trying // to generate a KeyID that's only bound the the given button. // this is important when a shift button is modified by shift; we @@ -989,7 +989,7 @@ KeyMap::addKeystrokes(EKeystroke type, const KeyItem& keyItem, } } break; - + case kKeystrokeRelease: keystrokes.push_back(Keystroke(button, false, false, data)); if (keyItem.m_generates != 0 && !keyItem.m_lock) { @@ -1011,19 +1011,19 @@ KeyMap::addKeystrokes(EKeystroke type, const KeyItem& keyItem, } } break; - + case kKeystrokeRepeat: keystrokes.push_back(Keystroke(button, false, true, data)); keystrokes.push_back(Keystroke(button, true, true, data)); // no modifier changes on key repeat break; - + case kKeystrokeClick: keystrokes.push_back(Keystroke(button, true, false, data)); keystrokes.push_back(Keystroke(button, false, false, data)); // no modifier changes on key click break; - + case kKeystrokeModify: case kKeystrokeUnmodify: if (keyItem.m_lock) { diff --git a/src/lib/barrier/KeyMap.h b/src/lib/barrier/KeyMap.h index b6eb865..bc72020 100644 --- a/src/lib/barrier/KeyMap.h +++ b/src/lib/barrier/KeyMap.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2005 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -24,7 +24,9 @@ #include "common/stdset.h" #include "common/stdvector.h" +#ifdef BARRIER_TEST_ENV #include +#endif namespace barrier { @@ -320,13 +322,14 @@ public: Converts a string into a modifier mask. Returns \c true on success and \c false if the string cannot be parsed. The modifiers plus any remaining leading and trailing whitespace is stripped from the input - string. + string. */ static bool parseModifiers(String&, KeyModifierMask&); //@} private: +#ifdef BARRIER_TEST_ENV FRIEND_TEST(KeyMapTests, findBestKey_requiredDown_matchExactFirstItem); FRIEND_TEST(KeyMapTests, @@ -340,6 +343,7 @@ private: FRIEND_TEST(KeyMapTests, findBestKey_onlyOneRequiredDown_matchTwoRequiredChangesItem); FRIEND_TEST(KeyMapTests, findBestKey_noRequiredDown_cannotMatch); +#endif private: //! Ways to synthesize a key @@ -351,7 +355,7 @@ private: kKeystrokeModify, //!< Synthesize pressing a modifier kKeystrokeUnmodify //!< Synthesize releasing a modifier }; - + // A list of ways to synthesize a KeyID typedef std::vector KeyEntryList; diff --git a/src/lib/barrier/KeyState.cpp b/src/lib/barrier/KeyState.cpp index fc5579d..4c04277 100644 --- a/src/lib/barrier/KeyState.cpp +++ b/src/lib/barrier/KeyState.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -529,7 +529,7 @@ KeyState::addActiveModifierCB(KeyID, SInt32 group, (keyItem.m_generates & context->m_mask) != 0) { context->m_activeModifiers.insert(std::make_pair( keyItem.m_generates, keyItem)); - } + } } void @@ -581,10 +581,10 @@ KeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID) LOG((CLOG_DEBUG1 "emulating media key")); fakeMediaKey(id); } - + return; } - + KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask); updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers); if (localID != 0) { diff --git a/src/lib/barrier/KeyState.h b/src/lib/barrier/KeyState.h index 737d515..6f32b5d 100644 --- a/src/lib/barrier/KeyState.h +++ b/src/lib/barrier/KeyState.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -74,7 +74,7 @@ public: virtual void fakeAllKeysUp(); virtual bool fakeCtrlAltDel() = 0; virtual bool fakeMediaKey(KeyID id); - + virtual bool isKeyDown(KeyButton) const; virtual KeyModifierMask getActiveModifiers() const; @@ -157,7 +157,7 @@ public: AddActiveModifierContext& operator=(const AddActiveModifierContext&); }; private: - + class ButtonToKeyLess { public: bool operator()(const barrier::KeyMap::ButtonToKeyMap::value_type& a, diff --git a/src/lib/barrier/PacketStreamFilter.cpp b/src/lib/barrier/PacketStreamFilter.cpp index 16f0fe7..5955b6c 100644 --- a/src/lib/barrier/PacketStreamFilter.cpp +++ b/src/lib/barrier/PacketStreamFilter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -17,6 +17,7 @@ */ #include "barrier/PacketStreamFilter.h" +#include "barrier/protocol_types.h" #include "base/IEventQueue.h" #include "mt/Lock.h" #include "base/TMethodEventJob.h" @@ -133,8 +134,7 @@ PacketStreamFilter::isReadyNoLock() const return (m_size != 0 && m_buffer.getSize() >= m_size); } -void -PacketStreamFilter::readPacketSize() +bool PacketStreamFilter::readPacketSize() { // note -- m_mutex must be locked on entry @@ -146,7 +146,13 @@ PacketStreamFilter::readPacketSize() ((UInt32)buffer[1] << 16) | ((UInt32)buffer[2] << 8) | (UInt32)buffer[3]; + + if (m_size > PROTOCOL_MAX_MESSAGE_LENGTH) { + m_events->addEvent(Event(m_events->forIStream().inputFormatError(), getEventTarget())); + return false; + } } + return true; } bool @@ -160,13 +166,17 @@ PacketStreamFilter::readMore() UInt32 n = getStream()->read(buffer, sizeof(buffer)); while (n > 0) { m_buffer.write(buffer, n); + + // if we don't yet have the next packet size then get it, if possible. + // Note that we can't wait for whole pending data to arrive because it may be huge in + // case of malicious or erroneous peer. + if (!readPacketSize()) { + break; + } + n = getStream()->read(buffer, sizeof(buffer)); } - // if we don't yet have the next packet size then get it, - // if possible. - readPacketSize(); - // note if we now have a whole packet bool isReady = isReadyNoLock(); diff --git a/src/lib/barrier/PacketStreamFilter.h b/src/lib/barrier/PacketStreamFilter.h index bcbd604..0c4bb04 100644 --- a/src/lib/barrier/PacketStreamFilter.h +++ b/src/lib/barrier/PacketStreamFilter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -24,7 +24,7 @@ class IEventQueue; -//! Packetizing stream filter +//! Packetizing stream filter /*! Filters a stream to read and write packets. */ @@ -47,7 +47,9 @@ protected: private: bool isReadyNoLock() const; - void readPacketSize(); + + // returns false on erroneous packet size + bool readPacketSize(); bool readMore(); private: diff --git a/src/lib/barrier/PlatformScreen.cpp b/src/lib/barrier/PlatformScreen.cpp index b0fdc75..d72e69a 100644 --- a/src/lib/barrier/PlatformScreen.cpp +++ b/src/lib/barrier/PlatformScreen.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/PlatformScreen.h b/src/lib/barrier/PlatformScreen.h index 38bf8de..19a3da1 100644 --- a/src/lib/barrier/PlatformScreen.h +++ b/src/lib/barrier/PlatformScreen.h @@ -101,6 +101,7 @@ public: virtual void fakeDraggingFiles(DragFileList fileList) { throw std::runtime_error("fakeDraggingFiles not implemented"); } virtual const String& getDropTarget() const { throw std::runtime_error("getDropTarget not implemented"); } + virtual void setDropTarget(const String&) { throw std::runtime_error("setDropTarget not implemented"); } protected: //! Update mouse buttons diff --git a/src/lib/barrier/PortableTaskBarReceiver.cpp b/src/lib/barrier/PortableTaskBarReceiver.cpp index 384cacd..e9a5f40 100644 --- a/src/lib/barrier/PortableTaskBarReceiver.cpp +++ b/src/lib/barrier/PortableTaskBarReceiver.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -111,7 +111,7 @@ PortableTaskBarReceiver::getToolTip() const case kNotWorking: return barrier::string::sprintf("%s: %s", kAppVersion, m_errorMessage.c_str()); - + case kNotConnected: return barrier::string::sprintf("%s: Unknown", kAppVersion); diff --git a/src/lib/barrier/PortableTaskBarReceiver.h b/src/lib/barrier/PortableTaskBarReceiver.h index d335e44..3db27ba 100644 --- a/src/lib/barrier/PortableTaskBarReceiver.h +++ b/src/lib/barrier/PortableTaskBarReceiver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/ProtocolUtil.cpp b/src/lib/barrier/ProtocolUtil.cpp index e742687..5a71010 100644 --- a/src/lib/barrier/ProtocolUtil.cpp +++ b/src/lib/barrier/ProtocolUtil.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -19,6 +19,8 @@ #include "barrier/ProtocolUtil.h" #include "io/IStream.h" #include "base/Log.h" +#include "barrier/protocol_types.h" +#include "barrier/XBarrier.h" #include "common/stdvector.h" #include "base/String.h" @@ -80,7 +82,7 @@ ProtocolUtil::vwritef(barrier::IStream* stream, // fill buffer UInt8* buffer = new UInt8[size]; - writef(buffer, fmt, args); + writef_void(buffer, fmt, args); try { // write buffer @@ -159,6 +161,10 @@ ProtocolUtil::vreadf(barrier::IStream* stream, const char* fmt, va_list args) (static_cast(buffer[2]) << 8) | static_cast(buffer[3]); + if (n > PROTOCOL_MAX_LIST_LENGTH) { + throw XBadClient("Too long message received"); + } + // convert it void* v = va_arg(args, void*); switch (len) { @@ -211,6 +217,10 @@ ProtocolUtil::vreadf(barrier::IStream* stream, const char* fmt, va_list args) (static_cast(buffer[2]) << 8) | static_cast(buffer[3]); + if (len > PROTOCOL_MAX_STRING_LENGTH) { + throw XBadClient("Too long message received"); + } + // use a fixed size buffer if its big enough const bool useFixed = (len <= sizeof(buffer)); @@ -339,7 +349,7 @@ ProtocolUtil::getLength(const char* fmt, va_list args) } void -ProtocolUtil::writef(void* buffer, const char* fmt, va_list args) +ProtocolUtil::writef_void(void* buffer, const char* fmt, va_list args) { UInt8* dst = static_cast(buffer); diff --git a/src/lib/barrier/ProtocolUtil.h b/src/lib/barrier/ProtocolUtil.h index 9930cfc..af4fea8 100644 --- a/src/lib/barrier/ProtocolUtil.h +++ b/src/lib/barrier/ProtocolUtil.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -38,7 +38,7 @@ public: regular characters and format specifiers. Format specifiers begin with \%. All characters not part of a format specifier are regular and are transmitted unchanged. - + Format specifiers are: - \%\% -- literal `\%' - \%1i -- converts integer argument to 1 byte integer @@ -58,7 +58,7 @@ public: Read formatted binary data from a buffer. This performs the reverse operation of writef(). Returns true if the entire format was successfully parsed, false otherwise. - + Format specifiers are: - \%\% -- read (and discard) a literal `\%' - \%1i -- reads a 1 byte integer; argument is a SInt32* or UInt32* @@ -79,7 +79,7 @@ private: const char* fmt, va_list); static UInt32 getLength(const char* fmt, va_list); - static void writef(void*, const char* fmt, va_list); + static void writef_void(void*, const char* fmt, va_list); static UInt32 eatLength(const char** fmt); static void read(barrier::IStream*, void*, UInt32); }; diff --git a/src/lib/barrier/Screen.cpp b/src/lib/barrier/Screen.cpp index 32442f6..2a2c877 100644 --- a/src/lib/barrier/Screen.cpp +++ b/src/lib/barrier/Screen.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -376,7 +376,7 @@ Screen::isLockedToScreen() const if (buttonID != kButtonLeft) { LOG((CLOG_DEBUG "locked by mouse buttonID: %d", buttonID)); } - + if (m_enableDragDrop) { return (buttonID == kButtonLeft) ? false : true; } @@ -466,6 +466,12 @@ Screen::getDropTarget() const return m_screen->getDropTarget(); } +void +Screen::setDropTarget(const String& target) +{ + return m_screen->setDropTarget(target); +} + void* Screen::getEventTarget() const { diff --git a/src/lib/barrier/Screen.h b/src/lib/barrier/Screen.h index b16feff..1c8e7de 100644 --- a/src/lib/barrier/Screen.h +++ b/src/lib/barrier/Screen.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -42,7 +42,7 @@ public: Screen(IPlatformScreen* platformScreen, IEventQueue* events); virtual ~Screen(); -#ifdef TEST_ENV +#ifdef BARRIER_TEST_ENV Screen() : m_mock(true) { } #endif @@ -223,7 +223,7 @@ public: //! Change dragging status void setDraggingStarted(bool started); - + //! Fake a files dragging operation void startDraggingFiles(DragFileList& fileList); @@ -278,7 +278,7 @@ public: //! Test if file is dragged on primary screen bool isDraggingStarted() const; - + //! Test if file is dragged on secondary screen bool isFakeDraggingStarted() const; @@ -290,6 +290,8 @@ public: //! Get the drop target directory const String& getDropTarget() const; + //! Set the drop target directory + void setDropTarget(const String&); //@} @@ -299,7 +301,7 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCursorPos(SInt32& x, SInt32& y) const; - + IPlatformScreen* getPlatformScreen() { return m_screen; } protected: diff --git a/src/lib/barrier/ServerApp.cpp b/src/lib/barrier/ServerApp.cpp index 18cf935..71158ce 100644 --- a/src/lib/barrier/ServerApp.cpp +++ b/src/lib/barrier/ServerApp.cpp @@ -34,13 +34,11 @@ #include "base/EventQueue.h" #include "base/log_outputters.h" #include "base/FunctionEventJob.h" -#include "base/TMethodJob.h" #include "base/IEventQueue.h" #include "base/Log.h" #include "base/TMethodEventJob.h" #include "common/Version.h" #include "common/DataDirectories.h" -#include "common/PathUtilities.h" #if SYSAPI_WIN32 #include "arch/win32/ArchMiscWindows.h" @@ -128,30 +126,43 @@ ServerApp::help() # define WINAPI_INFO "" #endif + // refer to custom profile directory even if not saved yet + barrier::fs::path profile_path = argsBase().m_profileDirectory; + if (profile_path.empty()) { + profile_path = barrier::DataDirectories::profile(); + } + + auto usr_config_path = (profile_path / barrier::fs::u8path(USR_CONFIG_NAME)).u8string(); + auto sys_config_path = (barrier::DataDirectories::systemconfig() / + barrier::fs::u8path(SYS_CONFIG_NAME)).u8string(); + std::ostringstream buffer; - buffer << "Start the barrier server component." << std::endl - << std::endl + buffer << "Start the barrier server component.\n" + << "\n" << "Usage: " << args().m_exename << " [--address
    ]" << " [--config ]" - << WINAPI_ARGS << HELP_SYS_ARGS << HELP_COMMON_ARGS << std::endl - << std::endl - << "Options:" << std::endl - << " -a, --address
    listen for clients on the given address." << std::endl - << " -c, --config use the named configuration file instead." << std::endl - << HELP_COMMON_INFO_1 << WINAPI_INFO << HELP_SYS_INFO << HELP_COMMON_INFO_2 << std::endl - << "Default options are marked with a *" << std::endl - << std::endl - << "The argument for --address is of the form: [][:]. The" << std::endl - << "hostname must be the address or hostname of an interface on the system." << std::endl - << "Placing brackets around an IPv6 address is required when also specifying " << std::endl - << "a port number and optional otherwise. The default is to listen on all" << std::endl - << "interfaces using port number " << kDefaultPort << "." << std::endl - << std::endl - << "If no configuration file pathname is provided then the first of the" << std::endl - << "following to load successfully sets the configuration:" << std::endl - << " " << PathUtilities::concat(DataDirectories::profile(), USR_CONFIG_NAME) << std::endl - << " " << PathUtilities::concat(DataDirectories::systemconfig(), SYS_CONFIG_NAME) << std::endl; + << WINAPI_ARGS << HELP_SYS_ARGS << HELP_COMMON_ARGS << "\n" + << "\n" + << "Options:\n" + << " -a, --address
    listen for clients on the given address.\n" + << " -c, --config use the named configuration file instead.\n" + << HELP_COMMON_INFO_1 + << " --disable-client-cert-checking disable client SSL certificate \n" + " checking (deprecated)\n" + << WINAPI_INFO << HELP_SYS_INFO << HELP_COMMON_INFO_2 << "\n" + << "Default options are marked with a *\n" + << "\n" + << "The argument for --address is of the form: [][:]. The\n" + << "hostname must be the address or hostname of an interface on the system.\n" + << "Placing brackets around an IPv6 address is required when also specifying \n" + << "a port number and optional otherwise. The default is to listen on all\n" + << "interfaces using port number " << kDefaultPort << ".\n" + << "\n" + << "If no configuration file pathname is provided then the first of the\n" + << "following to load successfully sets the configuration:\n" + << " " << usr_config_path << "\n" + << " " << sys_config_path << "\n"; LOG((CLOG_PRINT "%s", buffer.str().c_str())); } @@ -188,25 +199,25 @@ ServerApp::loadConfig() // load the default configuration if no explicit file given else { - String path = DataDirectories::profile(); + auto path = barrier::DataDirectories::profile(); if (!path.empty()) { // complete path - path = PathUtilities::concat(path, USR_CONFIG_NAME); + path /= barrier::fs::u8path(USR_CONFIG_NAME); // now try loading the user's configuration - if (loadConfig(path)) { + if (loadConfig(path.u8string())) { loaded = true; - args().m_configFile = path; + args().m_configFile = path.u8string(); } } if (!loaded) { // try the system-wide config file - path = DataDirectories::systemconfig(); + path = barrier::DataDirectories::systemconfig(); if (!path.empty()) { - path = PathUtilities::concat(path, SYS_CONFIG_NAME); - if (loadConfig(path)) { + path /= barrier::fs::u8path(SYS_CONFIG_NAME); + if (loadConfig(path.u8string())) { loaded = true; - args().m_configFile = path; + args().m_configFile = path.u8string(); } } } @@ -496,6 +507,9 @@ barrier::Screen* ServerApp::openServerScreen() { barrier::Screen* screen = createScreen(); + if (!argsBase().m_dropTarget.empty()) { + screen->setDropTarget(argsBase().m_dropTarget); + } screen->setEnableDragDrop(argsBase().m_enableDragDrop); m_events->adoptHandler(m_events->forIScreen().error(), screen->getEventTarget(), @@ -643,11 +657,18 @@ ServerApp::handleResume(const Event&, void*) ClientListener* ServerApp::openClientListener(const NetworkAddress& address) { + auto security_level = ConnectionSecurityLevel::PLAINTEXT; + if (args().m_enableCrypto) { + security_level = ConnectionSecurityLevel::ENCRYPTED; + if (args().check_client_certificates) { + security_level = ConnectionSecurityLevel::ENCRYPTED_AUTHENTICATED; + } + } + ClientListener* listen = new ClientListener( address, new TCPSocketFactory(m_events, getSocketMultiplexer()), - m_events, - args().m_enableCrypto); + m_events, security_level); m_events->adoptHandler( m_events->forClientListener().connected(), listen, @@ -775,10 +796,7 @@ ServerApp::mainLoop() #if defined(MAC_OS_X_VERSION_10_7) - Thread thread( - new TMethodJob( - this, &ServerApp::runEventsLoop, - NULL)); + Thread thread([this](){ run_events_loop(); }); // wait until carbon loop is ready OSXScreen* screen = dynamic_cast( @@ -823,7 +841,7 @@ ServerApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc // general initialization m_barrierAddress = new NetworkAddress; args().m_config = new Config(m_events); - args().m_exename = PathUtilities::basename(argv[0]); + args().m_exename = ArgParser::parse_exename(argv[0]); // install caller's output filter if (outputter != NULL) { diff --git a/src/lib/barrier/ServerApp.h b/src/lib/barrier/ServerApp.h index 528aa24..ca38568 100644 --- a/src/lib/barrier/ServerApp.h +++ b/src/lib/barrier/ServerApp.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -51,7 +51,7 @@ class ServerApp : public App { public: ServerApp(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver); virtual ~ServerApp(); - + // Parse server specific command line arguments. void parseArgs(int argc, const char* const* argv); @@ -104,7 +104,7 @@ public: static ServerApp& instance() { return (ServerApp&)App::instance(); } Server* getServerPtr() { return m_server; } - + Server* m_server; EServerState m_serverState; barrier::Screen* m_serverScreen; diff --git a/src/lib/barrier/ServerArgs.h b/src/lib/barrier/ServerArgs.h index 6d91233..6323705 100644 --- a/src/lib/barrier/ServerArgs.h +++ b/src/lib/barrier/ServerArgs.h @@ -30,4 +30,5 @@ public: String m_configFile; Config* m_config; String m_screenChangeScript; + bool check_client_certificates = true; }; diff --git a/src/lib/barrier/ServerTaskBarReceiver.cpp b/src/lib/barrier/ServerTaskBarReceiver.cpp index b427cd1..c3ba7cf 100644 --- a/src/lib/barrier/ServerTaskBarReceiver.cpp +++ b/src/lib/barrier/ServerTaskBarReceiver.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -125,7 +125,7 @@ ServerTaskBarReceiver::getToolTip() const case kNotWorking: return barrier::string::sprintf("%s: %s", kAppVersion, m_errorMessage.c_str()); - + case kNotConnected: return barrier::string::sprintf("%s: Waiting for clients", kAppVersion); diff --git a/src/lib/barrier/ServerTaskBarReceiver.h b/src/lib/barrier/ServerTaskBarReceiver.h index 3cef9c0..086de8c 100644 --- a/src/lib/barrier/ServerTaskBarReceiver.h +++ b/src/lib/barrier/ServerTaskBarReceiver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/StreamChunker.cpp b/src/lib/barrier/StreamChunker.cpp index 579d02f..0f6c0a9 100644 --- a/src/lib/barrier/StreamChunker.cpp +++ b/src/lib/barrier/StreamChunker.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -42,14 +42,13 @@ bool StreamChunker::s_interruptFile = false; Mutex* StreamChunker::s_interruptMutex = NULL; void -StreamChunker::sendFile( - char* filename, +StreamChunker::sendFile(const char* filename, IEventQueue* events, void* eventTarget) { s_isChunkingFile = true; - - std::fstream file(static_cast(filename), std::ios::in | std::ios::binary); + + std::fstream file(filename, std::ios::in | std::ios::binary); if (!file.is_open()) { throw runtime_error("failed to open file"); @@ -76,9 +75,9 @@ StreamChunker::sendFile( LOG((CLOG_DEBUG "file transmission interrupted")); break; } - + events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); - + // make sure we don't read too much from the mock data. if (sentLength + chunkSize > size) { chunkSize = size - sentLength; @@ -106,7 +105,7 @@ StreamChunker::sendFile( events->addEvent(Event(events->forFile().fileChunkSending(), eventTarget, end)); file.close(); - + s_isChunkingFile = false; } @@ -122,16 +121,16 @@ StreamChunker::sendClipboard( // send first message (data size) String dataSize = barrier::string::sizeTypeToString(size); ClipboardChunk* sizeMessage = ClipboardChunk::start(id, sequence, dataSize); - + events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, sizeMessage)); // send clipboard chunk with a fixed size size_t sentLength = 0; size_t chunkSize = g_chunkSize; - + while (true) { events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); - + // make sure we don't read too much from the mock data. if (sentLength + chunkSize > size) { chunkSize = size - sentLength; @@ -139,7 +138,7 @@ StreamChunker::sendClipboard( String chunk(data.substr(sentLength, chunkSize).c_str(), chunkSize); ClipboardChunk* dataChunk = ClipboardChunk::data(id, sequence, chunk); - + events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, dataChunk)); sentLength += chunkSize; @@ -152,7 +151,7 @@ StreamChunker::sendClipboard( ClipboardChunk* end = ClipboardChunk::end(id, sequence); events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, end)); - + LOG((CLOG_DEBUG "sent clipboard size=%d", sentLength)); } diff --git a/src/lib/barrier/StreamChunker.h b/src/lib/barrier/StreamChunker.h index ab57c7e..ed5d83c 100644 --- a/src/lib/barrier/StreamChunker.h +++ b/src/lib/barrier/StreamChunker.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -25,10 +25,7 @@ class Mutex; class StreamChunker { public: - static void sendFile( - char* filename, - IEventQueue* events, - void* eventTarget); + static void sendFile(const char* filename, IEventQueue* events, void* eventTarget); static void sendClipboard( String& data, size_t size, @@ -37,7 +34,7 @@ public: IEventQueue* events, void* eventTarget); static void interruptFile(); - + private: static bool s_isChunkingFile; static bool s_interruptFile; diff --git a/src/lib/barrier/XBarrier.cpp b/src/lib/barrier/XBarrier.cpp index 7fe3577..44593d1 100644 --- a/src/lib/barrier/XBarrier.cpp +++ b/src/lib/barrier/XBarrier.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -118,6 +118,6 @@ int XExitApp::getCode() const noexcept String XExitApp::getWhat() const noexcept { return format( - "XExitApp", "exiting with code %{1}", + "XExitApp", "exiting with code %{1}", barrier::string::sprintf("%d", m_code).c_str()); } diff --git a/src/lib/barrier/XBarrier.h b/src/lib/barrier/XBarrier.h index fef931e..b7a3891 100644 --- a/src/lib/barrier/XBarrier.h +++ b/src/lib/barrier/XBarrier.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -113,8 +113,8 @@ private: //! Generic exit eception /*! -Thrown when we want to abort, with the opportunity to clean up. This is a -little bit of a hack, but it's a better way of exiting, than just calling +Thrown when we want to abort, with the opportunity to clean up. This is a +little bit of a hack, but it's a better way of exiting, than just calling exit(int). */ class XExitApp : public XBarrier { @@ -127,7 +127,7 @@ public: protected: virtual std::string getWhat() const noexcept; - + private: int m_code; }; diff --git a/src/lib/barrier/XScreen.cpp b/src/lib/barrier/XScreen.cpp index 3398423..bb5c860 100644 --- a/src/lib/barrier/XScreen.cpp +++ b/src/lib/barrier/XScreen.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/XScreen.h b/src/lib/barrier/XScreen.h index f8fe7a7..7033556 100644 --- a/src/lib/barrier/XScreen.h +++ b/src/lib/barrier/XScreen.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/clipboard_types.h b/src/lib/barrier/clipboard_types.h index 54f2732..7de608c 100644 --- a/src/lib/barrier/clipboard_types.h +++ b/src/lib/barrier/clipboard_types.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/key_types.cpp b/src/lib/barrier/key_types.cpp index 902670d..33ee3eb 100644 --- a/src/lib/barrier/key_types.cpp +++ b/src/lib/barrier/key_types.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -17,6 +17,7 @@ */ #include "barrier/key_types.h" +#include const KeyNameMapEntry kKeyNameMap[] = { { "AltGr", kKeyAltGr }, @@ -43,6 +44,7 @@ const KeyNameMapEntry kKeyNameMap[] = { { "Control_R", kKeyControl_R }, { "Delete", kKeyDelete }, { "Down", kKeyDown }, + { "EisuToggle", kKeyEisuToggle }, { "Eject", kKeyEject }, { "End", kKeyEnd }, { "Escape", kKeyEscape }, @@ -89,6 +91,7 @@ const KeyNameMapEntry kKeyNameMap[] = { { "Hyper_L", kKeyHyper_L }, { "Hyper_R", kKeyHyper_R }, { "Insert", kKeyInsert }, + { "Kana", kKeyKana }, { "KP_0", kKeyKP_0 }, { "KP_1", kKeyKP_1 }, { "KP_2", kKeyKP_2 }, @@ -130,6 +133,7 @@ const KeyNameMapEntry kKeyNameMap[] = { { "Menu", kKeyMenu }, { "Meta_L", kKeyMeta_L }, { "Meta_R", kKeyMeta_R }, + { "Muhenkan", kKeyMuhenkan }, { "NumLock", kKeyNumLock }, { "PageDown", kKeyPageDown }, { "PageUp", kKeyPageUp }, @@ -191,6 +195,12 @@ const KeyNameMapEntry kKeyNameMap[] = { { "Bar", 0x007c }, { "BraceR", 0x007d }, { "Tilde", 0x007e }, + { "Copy", kKeyCopy }, + { "Cut", kKeyCut }, + { "Open", kKeyOpen }, + { "Paste", kKeyPaste }, + { "Props", kKeyProps }, + { "Front", kKeyFront }, { NULL, 0 }, }; diff --git a/src/lib/barrier/key_types.h b/src/lib/barrier/key_types.h index 7a8ea53..863dc17 100644 --- a/src/lib/barrier/key_types.h +++ b/src/lib/barrier/key_types.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -109,11 +109,13 @@ static const KeyID kKeyPause = 0xEF13; /* Pause, hold */ static const KeyID kKeyScrollLock = 0xEF14; static const KeyID kKeySysReq = 0xEF15; static const KeyID kKeyEscape = 0xEF1B; +static const KeyID kKeyMuhenkan = 0xEF22; /* Cancel Conversion */ static const KeyID kKeyHenkan = 0xEF23; /* Start/Stop Conversion */ static const KeyID kKeyKana = 0xEF26; /* Kana */ static const KeyID kKeyHiraganaKatakana = 0xEF27; /* Hiragana/Katakana toggle */ static const KeyID kKeyZenkaku = 0xEF2A; /* Zenkaku/Hankaku */ static const KeyID kKeyKanzi = 0xEF2A; /* Kanzi */ +static const KeyID kKeyEisuToggle = 0xEF30; /* Alphanumeric toggle */ static const KeyID kKeyHangul = 0xEF31; /* Hangul */ static const KeyID kKeyHanja = 0xEF34; /* Hanja */ static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */ @@ -170,7 +172,7 @@ static const KeyID kKeyKP_Separator= 0xEFAC; /* separator, often comma */ static const KeyID kKeyKP_Subtract = 0xEFAD; static const KeyID kKeyKP_Decimal = 0xEFAE; static const KeyID kKeyKP_Divide = 0xEFAF; -static const KeyID kKeyKP_0 = 0xEFB0; +static const KeyID kKeyKP_0 = 0xEFB0; static const KeyID kKeyKP_1 = 0xEFB1; static const KeyID kKeyKP_2 = 0xEFB2; static const KeyID kKeyKP_3 = 0xEFB3; @@ -234,6 +236,13 @@ static const KeyID kKeySuper_R = 0xEFEC; /* Right super */ static const KeyID kKeyHyper_L = 0xEFED; /* Left hyper */ static const KeyID kKeyHyper_R = 0xEFEE; /* Right hyper */ +static const KeyID kKeyCopy = 0x1008EF57; +static const KeyID kKeyCut = 0x1008EF58; +static const KeyID kKeyOpen = 0x1008EF6b; +static const KeyID kKeyPaste = 0x1008EF6d; +static const KeyID kKeyProps = 0x1005EF70; +static const KeyID kKeyFront = 0x1005EF71; + // multi-key character composition static const KeyID kKeyCompose = 0xEF20; static const KeyID kKeyDeadGrave = 0x0300; @@ -284,6 +293,8 @@ static const KeyID kKeyAppUser1 = 0xE0B6; static const KeyID kKeyAppUser2 = 0xE0B7; static const KeyID kKeyBrightnessDown = 0xE0B8; static const KeyID kKeyBrightnessUp = 0xE0B9; +static const KeyID kKeyKbdBrightnessDown = 0xE0BA; +static const KeyID kKeyKbdBrightnessUp = 0xE0BB; static const KeyID kKeyMissionControl = 0xE0C0; static const KeyID kKeyLaunchpad = 0xE0C1; diff --git a/src/lib/barrier/mouse_types.h b/src/lib/barrier/mouse_types.h index 62a2396..ccb18ac 100644 --- a/src/lib/barrier/mouse_types.h +++ b/src/lib/barrier/mouse_types.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/option_types.h b/src/lib/barrier/option_types.h index 6323e37..42103d4 100644 --- a/src/lib/barrier/option_types.h +++ b/src/lib/barrier/option_types.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/protocol_types.cpp b/src/lib/barrier/protocol_types.cpp index 07acf61..9710841 100644 --- a/src/lib/barrier/protocol_types.cpp +++ b/src/lib/barrier/protocol_types.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/protocol_types.h b/src/lib/barrier/protocol_types.h index bc5e037..f730606 100644 --- a/src/lib/barrier/protocol_types.h +++ b/src/lib/barrier/protocol_types.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -20,6 +20,8 @@ #include "base/EventTypes.h" +#include + // protocol version number // 1.0: initial protocol // 1.1: adds KeyCode to key press, release, and repeat @@ -51,6 +53,12 @@ static const double kKeepAlivesUntilDeath = 3.0; static const double kHeartRate = -1.0; static const double kHeartBeatsUntilDeath = 3.0; +// Messages of very large size indicate a likely protocol error. We don't parse such messages and +// drop connection instead. Note that e.g. the clipboard messages are already limited to 32kB. +static constexpr std::uint32_t PROTOCOL_MAX_MESSAGE_LENGTH = 4 * 1024 * 1024; +static constexpr std::uint32_t PROTOCOL_MAX_LIST_LENGTH = 1024 * 1024; +static constexpr std::uint32_t PROTOCOL_MAX_STRING_LENGTH = 1024 * 1024; + // direction constants enum EDirection { kNoDirection, @@ -267,8 +275,8 @@ extern const char* kMsgDSetOptions; // 2 means the file transfer is finished. extern const char* kMsgDFileTransfer; -// drag infomation: primary <-> secondary -// transfer drag infomation. The first 2 bytes are used for storing +// drag information: primary <-> secondary +// transfer drag information. The first 2 bytes are used for storing // the number of dragging objects. Then the following string consists // of each object's directory. extern const char* kMsgDDragInfo; diff --git a/src/lib/barrier/unix/AppUtilUnix.cpp b/src/lib/barrier/unix/AppUtilUnix.cpp index a1548d8..272dcf4 100644 --- a/src/lib/barrier/unix/AppUtilUnix.cpp +++ b/src/lib/barrier/unix/AppUtilUnix.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/barrier/unix/AppUtilUnix.h b/src/lib/barrier/unix/AppUtilUnix.h index fefcfea..9555ccd 100644 --- a/src/lib/barrier/unix/AppUtilUnix.h +++ b/src/lib/barrier/unix/AppUtilUnix.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -28,7 +28,7 @@ class AppUtilUnix : public AppUtil { public: AppUtilUnix(IEventQueue* events); virtual ~AppUtilUnix(); - + int run(int argc, char** argv); void startNode(); }; diff --git a/src/lib/barrier/win32/AppUtilWindows.cpp b/src/lib/barrier/win32/AppUtilWindows.cpp index 560b029..b19cf15 100644 --- a/src/lib/barrier/win32/AppUtilWindows.cpp +++ b/src/lib/barrier/win32/AppUtilWindows.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -60,13 +60,13 @@ BOOL WINAPI AppUtilWindows::consoleHandler(DWORD) } static -int -mainLoopStatic() +int +mainLoopStatic() { return AppUtil::instance().app().mainLoop(); } -int +int AppUtilWindows::daemonNTMainLoop(int argc, const char** argv) { app().initApp(argc, argv); @@ -74,11 +74,11 @@ AppUtilWindows::daemonNTMainLoop(int argc, const char** argv) // NB: what the hell does this do?! app().argsBase().m_backend = false; - + return ArchMiscWindows::runDaemon(mainLoopStatic); } -void +void AppUtilWindows::exitApp(int code) { switch (m_exitMode) { @@ -97,7 +97,7 @@ int daemonNTMainLoopStatic(int argc, const char** argv) return AppUtilWindows::instance().daemonNTMainLoop(argc, argv); } -int +int AppUtilWindows::daemonNTStartup(int, char**) { SystemLogger sysLogger(app().daemonName(), false); @@ -126,7 +126,7 @@ AppUtilWindows::beforeAppExit() // a new console window, and will normally close on exit (making it so // that we can't see error messages). if (app().argsBase().m_pauseOnExit) { - std::cout << std::endl << "press any key to exit..." << std::endl; + std::cout << "\n" << "press any key to exit...\n"; int c = _getch(); } } @@ -155,13 +155,13 @@ AppUtilWindows::run(int argc, char** argv) return app().runInner(argc, argv, NULL, startup); } -AppUtilWindows& +AppUtilWindows& AppUtilWindows::instance() { return (AppUtilWindows&)AppUtil::instance(); } -void +void AppUtilWindows::debugServiceWait() { if (app().argsBase().m_debugServiceWait) @@ -169,8 +169,8 @@ AppUtilWindows::debugServiceWait() while(true) { // this code is only executed when the process is launched via the - // windows service controller (and --debug-service-wait arg is - // used). to debug, set a breakpoint on this line so that + // windows service controller (and --debug-service-wait arg is + // used). to debug, set a breakpoint on this line so that // execution is delayed until the debugger is attached. ARCH->sleep(1); LOG((CLOG_INFO "waiting for debugger to attach")); @@ -178,7 +178,7 @@ AppUtilWindows::debugServiceWait() } } -void +void AppUtilWindows::startNode() { app().startNode(); diff --git a/src/lib/barrier/win32/AppUtilWindows.h b/src/lib/barrier/win32/AppUtilWindows.h index c5da228..23e7919 100644 --- a/src/lib/barrier/win32/AppUtilWindows.h +++ b/src/lib/barrier/win32/AppUtilWindows.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -39,7 +39,7 @@ public: virtual ~AppUtilWindows(); int daemonNTStartup(int, char**); - + int daemonNTMainLoop(int argc, const char** argv); void debugServiceWait(); diff --git a/src/lib/barrier/win32/DaemonApp.cpp b/src/lib/barrier/win32/DaemonApp.cpp index 482c465..81ec5ff 100644 --- a/src/lib/barrier/win32/DaemonApp.cpp +++ b/src/lib/barrier/win32/DaemonApp.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -28,7 +28,6 @@ #include "net/SocketMultiplexer.h" #include "arch/XArch.h" #include "base/Log.h" -#include "base/TMethodJob.h" #include "base/TMethodEventJob.h" #include "base/EventQueue.h" #include "base/log_outputters.h" @@ -87,7 +86,7 @@ DaemonApp::run(int argc, char** argv) { // win32 instance needed for threading, etc. ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); - + Arch arch; arch.init(); @@ -174,7 +173,7 @@ DaemonApp::mainLoop(bool daemonized) try { DAEMON_RUNNING(true); - + if (daemonized) { m_fileLogOutputter = new FileLogOutputter(logFilename().c_str()); CLOG->insert(m_fileLogOutputter); @@ -190,19 +189,19 @@ DaemonApp::mainLoop(bool daemonized) // send logging to gui via ipc, log system adopts outputter. m_ipcLogOutputter = new IpcLogOutputter(*m_ipcServer, kIpcClientGui, true); CLOG->insert(m_ipcLogOutputter); - + m_watchdog = new MSWindowsWatchdog(daemonized, false, *m_ipcServer, *m_ipcLogOutputter); m_watchdog->setFileLogOutputter(m_fileLogOutputter); - + m_events->adoptHandler( m_events->forIpcServer().messageReceived(), m_ipcServer, new TMethodEventJob(this, &DaemonApp::handleIpcMessage)); m_ipcServer->listen(); - + // install the platform event queue to handle service stop events. m_events->adoptBuffer(new MSWindowsEventQueueBuffer(m_events)); - + String command = ARCH->setting("Command"); bool elevate = ARCH->setting("Elevate") == "1"; if (command != "") { @@ -219,11 +218,11 @@ DaemonApp::mainLoop(bool daemonized) m_events->removeHandler( m_events->forIpcServer().messageReceived(), m_ipcServer); - + CLOG->remove(m_ipcLogOutputter); delete m_ipcLogOutputter; delete m_ipcServer; - + DAEMON_RUNNING(false); } catch (std::exception& e) { @@ -245,7 +244,7 @@ DaemonApp::logFilename() { string logFilename = ARCH->setting("LogFilename"); if (logFilename.empty()) - logFilename = DataDirectories::global() + "\\" + LOG_FILENAME; + logFilename = (barrier::DataDirectories::global() / LOG_FILENAME).u8string(); MSWindowsUtil::createDirectory(logFilename, true); return logFilename; } @@ -287,7 +286,7 @@ DaemonApp::handleIpcMessage(const Event& e, void*) } delete[] argv; - + String logLevel(argBase->m_logFilter); if (!logLevel.empty()) { try { diff --git a/src/lib/barrier/win32/DaemonApp.h b/src/lib/barrier/win32/DaemonApp.h index 2a8484b..e0eb385 100644 --- a/src/lib/barrier/win32/DaemonApp.h +++ b/src/lib/barrier/win32/DaemonApp.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #pragma once #include "arch/Arch.h" diff --git a/src/lib/base/CMakeLists.txt b/src/lib/base/CMakeLists.txt index 66ba5a6..9bd4472 100644 --- a/src/lib/base/CMakeLists.txt +++ b/src/lib/base/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/ELevel.h b/src/lib/base/ELevel.h index ec0f94f..45b51d7 100644 --- a/src/lib/base/ELevel.h +++ b/src/lib/base/ELevel.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/Event.cpp b/src/lib/base/Event.cpp index f2c1a12..cc548fc 100644 --- a/src/lib/base/Event.cpp +++ b/src/lib/base/Event.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/Event.h b/src/lib/base/Event.h index 2741813..38a2cf1 100644 --- a/src/lib/base/Event.h +++ b/src/lib/base/Event.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -71,7 +71,7 @@ public: Deletes event data for the given event (using free()). */ static void deleteData(const Event&); - + //! Set data (non-POD) /*! Set non-POD (non plain old data), where delete is called when the event @@ -114,7 +114,7 @@ public: Returns the event flags. */ Flags getFlags() const; - + //@} private: diff --git a/src/lib/base/EventQueue.cpp b/src/lib/base/EventQueue.cpp index 2429522..7764ed8 100644 --- a/src/lib/base/EventQueue.cpp +++ b/src/lib/base/EventQueue.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -100,7 +100,7 @@ EventQueue::~EventQueue() delete m_buffer; delete m_readyCondVar; delete m_readyMutex; - + ARCH->setSignalHandler(Arch::kINTERRUPT, NULL, NULL); ARCH->setSignalHandler(Arch::kTERMINATE, NULL, NULL); } @@ -121,7 +121,7 @@ EventQueue::loop() addEventToBuffer(event); m_pending.pop(); } - + Event event; getEvent(event); while (event.getType() != Event::kQuit) { @@ -298,7 +298,7 @@ EventQueue::addEvent(const Event& event) default: break; } - + if ((event.getFlags() & Event::kDeliverImmediately) != 0) { dispatchEvent(event); Event::deleteData(event); @@ -315,10 +315,10 @@ void EventQueue::addEventToBuffer(const Event& event) { std::lock_guard lock(m_mutex); - + // store the event's data locally UInt32 eventID = saveEvent(event); - + // add it if (!m_buffer->addEvent(eventID)) { // failed to send event @@ -568,7 +568,7 @@ EventQueue::waitForReady() const { double timeout = ARCH->time() + 10; Lock lock(m_readyMutex); - + while (!m_readyCondVar->wait()) { if (ARCH->time() > timeout) { throw std::runtime_error("event queue is not ready within 5 sec"); diff --git a/src/lib/base/EventQueue.h b/src/lib/base/EventQueue.h index 842c5ca..55b6fce 100644 --- a/src/lib/base/EventQueue.h +++ b/src/lib/base/EventQueue.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -71,7 +71,7 @@ private: double getNextTimerTimeout() const; void addEventToBuffer(const Event& event); bool parent_requests_shutdown() const; - + private: class Timer { public: diff --git a/src/lib/base/EventTypes.cpp b/src/lib/base/EventTypes.cpp index 2ba2077..173a0a9 100644 --- a/src/lib/base/EventTypes.cpp +++ b/src/lib/base/EventTypes.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -56,6 +56,7 @@ REGISTER_EVENT(IStream, outputFlushed) REGISTER_EVENT(IStream, outputError) REGISTER_EVENT(IStream, inputShutdown) REGISTER_EVENT(IStream, outputShutdown) +REGISTER_EVENT(IStream, inputFormatError) // // IpcClient diff --git a/src/lib/base/EventTypes.h b/src/lib/base/EventTypes.h index f81617e..995490e 100644 --- a/src/lib/base/EventTypes.h +++ b/src/lib/base/EventTypes.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -133,14 +133,20 @@ public: */ Event::Type outputShutdown(); + /** Get input format error event type + + This is sent when a stream receives an irrecoverable input format error. + */ + Event::Type inputFormatError(); //@} - + private: Event::Type m_inputReady; Event::Type m_outputFlushed; Event::Type m_outputError; Event::Type m_inputShutdown; Event::Type m_outputShutdown; + Event::Type m_inputFormatError; }; class IpcClientEvents : public EventTypes { @@ -159,7 +165,7 @@ public: Event::Type messageReceived(); //@} - + private: Event::Type m_connected; Event::Type m_messageReceived; @@ -179,7 +185,7 @@ public: //! Raised when the client disconnects from the server. Event::Type disconnected(); - + //@} private: @@ -198,7 +204,7 @@ public: //! Raised when we have created the client proxy. Event::Type clientConnected(); - + //! Raised when a message is received through a client proxy. Event::Type messageReceived(); @@ -242,7 +248,7 @@ public: event when a remote connection has been established. */ Event::Type connected(); - + //! Get secure connected event type /*! Returns the secure socket connected event type. A secure socket sends @@ -342,14 +348,14 @@ public: //! @name accessors //@{ - + //! Get accepted event type /*! Returns the accepted event type. This is sent whenever a server accepts a client. */ Event::Type accepted(); - + //! Get connected event type /*! Returns the connected event type. This is sent whenever a @@ -419,7 +425,7 @@ public: Event::Type failure(); //@} - + private: Event::Type m_success; Event::Type m_failure; @@ -510,7 +516,7 @@ public: Event::Type screenSwitched(); //@} - + private: Event::Type m_error; Event::Type m_connected; @@ -529,16 +535,16 @@ public: m_reloadConfig(Event::kUnknown), m_forceReconnect(Event::kUnknown), m_resetServer(Event::kUnknown) { } - + //! @name accessors //@{ - + Event::Type reloadConfig(); Event::Type forceReconnect(); Event::Type resetServer(); //@} - + private: Event::Type m_reloadConfig; Event::Type m_forceReconnect; @@ -565,7 +571,7 @@ public: Event::Type keyRepeat(); //@} - + private: Event::Type m_keyDown; Event::Type m_keyUp; @@ -589,7 +595,7 @@ public: //! @name accessors //@{ - + //! button down event type. Event data is ButtonInfo*. Event::Type buttonDown(); @@ -677,7 +683,7 @@ public: to sleep or a user session is deactivated (fast user switching). */ Event::Type suspend(); - + //! Get resume event type /*! Returns the resume event type. This is sent whenever the system wakes @@ -686,7 +692,7 @@ public: Event::Type resume(); //@} - + private: Event::Type m_error; Event::Type m_shapeChanged; @@ -722,7 +728,7 @@ public: //! Clipboard sending event type /*! - Returns the clipboard sending event type. This is used to send + Returns the clipboard sending event type. This is used to send clipboard chunks. */ Event::Type clipboardSending(); diff --git a/src/lib/base/FunctionEventJob.cpp b/src/lib/base/FunctionEventJob.cpp index 705e058..751b50d 100644 --- a/src/lib/base/FunctionEventJob.cpp +++ b/src/lib/base/FunctionEventJob.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/FunctionEventJob.h b/src/lib/base/FunctionEventJob.h index 4b2c2fc..3d9acaa 100644 --- a/src/lib/base/FunctionEventJob.h +++ b/src/lib/base/FunctionEventJob.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/FunctionJob.cpp b/src/lib/base/FunctionJob.cpp deleted file mode 100644 index 859010e..0000000 --- a/src/lib/base/FunctionJob.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "base/FunctionJob.h" - -// -// FunctionJob -// - -FunctionJob::FunctionJob(void (*func)(void*), void* arg) : - m_func(func), - m_arg(arg) -{ - // do nothing -} - -FunctionJob::~FunctionJob() -{ - // do nothing -} - -void -FunctionJob::run() -{ - if (m_func != NULL) { - m_func(m_arg); - } -} diff --git a/src/lib/base/FunctionJob.h b/src/lib/base/FunctionJob.h deleted file mode 100644 index 9cdfa9d..0000000 --- a/src/lib/base/FunctionJob.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "base/IJob.h" - -//! Use a function as a job -/*! -A job class that invokes a function. -*/ -class FunctionJob : public IJob { -public: - //! run() invokes \c func(arg) - FunctionJob(void (*func)(void*), void* arg = NULL); - virtual ~FunctionJob(); - - // IJob overrides - virtual void run(); - -private: - void (*m_func)(void*); - void* m_arg; -}; diff --git a/src/lib/base/IEventJob.h b/src/lib/base/IEventJob.h index 3e4a420..844cc4a 100644 --- a/src/lib/base/IEventJob.h +++ b/src/lib/base/IEventJob.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/IEventQueue.h b/src/lib/base/IEventQueue.h index 150595c..833081c 100644 --- a/src/lib/base/IEventQueue.h +++ b/src/lib/base/IEventQueue.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -184,7 +184,7 @@ public: be added. */ virtual void waitForReady() const = 0; - + //@} //! @name accessors //@{ @@ -216,7 +216,7 @@ public: virtual void* getSystemTarget() = 0; //@} - + // // Event type providers. // diff --git a/src/lib/base/IEventQueueBuffer.h b/src/lib/base/IEventQueueBuffer.h index b594436..f25cc66 100644 --- a/src/lib/base/IEventQueueBuffer.h +++ b/src/lib/base/IEventQueueBuffer.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -38,7 +38,7 @@ public: //! @name manipulators //@{ - + //! Initialize /*! Useful for platform-specific initialisation from a specific thread. diff --git a/src/lib/base/IJob.h b/src/lib/base/IJob.h index f966ec0..29aa941 100644 --- a/src/lib/base/IJob.h +++ b/src/lib/base/IJob.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/ILogOutputter.h b/src/lib/base/ILogOutputter.h index ab218fc..e11f9e2 100644 --- a/src/lib/base/ILogOutputter.h +++ b/src/lib/base/ILogOutputter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/Log.cpp b/src/lib/base/Log.cpp index a3b328d..8f52a80 100644 --- a/src/lib/base/Log.cpp +++ b/src/lib/base/Log.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -25,7 +25,7 @@ #include #include #include -#include +#include // names of priorities static const char* g_priority[] = { @@ -62,7 +62,7 @@ Log::Log() { assert(s_log == NULL); - // other initalization + // other initialization m_maxPriority = g_defaultMaxPriority; m_maxNewlineLength = 0; insert(new ConsoleLogOutputter); @@ -145,7 +145,7 @@ Log::print(const char* file, int line, const char* fmt, ...) // try printing into the buffer va_list args; va_start(args, fmt); - int n = ARCH->vsnprintf(buffer, len - sPad, fmt, args); + int n = std::vsnprintf(buffer, len - sPad, fmt, args); va_end(args); // if the buffer wasn't big enough then make it bigger and try again diff --git a/src/lib/base/Log.h b/src/lib/base/Log.h index 0ed458f..22224b9 100644 --- a/src/lib/base/Log.h +++ b/src/lib/base/Log.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -91,7 +91,7 @@ public: then it simply returns true. */ bool setFilter(const char* name); - + //! Set the minimum priority filter (by ordinal). void setFilter(int); @@ -193,9 +193,9 @@ otherwise it expands to a call that doesn't. #define CLOG_TRACE __FILE__, __LINE__, #endif -// the CLOG_* defines are line and file plus %z and an octal number (060=0, -// 071=9), but the limitation is that once we run out of numbers at either -// end, then we resort to using non-numerical chars. this still works (since +// the CLOG_* defines are line and file plus %z and an octal number (060=0, +// 071=9), but the limitation is that once we run out of numbers at either +// end, then we resort to using non-numerical chars. this still works (since // to deduce the number we subtract octal \060, so '/' is -1, and ':' is 10 #define CLOG_PRINT CLOG_TRACE "%z\057" // char is '/' diff --git a/src/lib/base/PriorityQueue.h b/src/lib/base/PriorityQueue.h index d2ca70e..8aac88c 100644 --- a/src/lib/base/PriorityQueue.h +++ b/src/lib/base/PriorityQueue.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/SimpleEventQueueBuffer.cpp b/src/lib/base/SimpleEventQueueBuffer.cpp index b55fe55..a26f255 100644 --- a/src/lib/base/SimpleEventQueueBuffer.cpp +++ b/src/lib/base/SimpleEventQueueBuffer.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/SimpleEventQueueBuffer.h b/src/lib/base/SimpleEventQueueBuffer.h index 4aa76d3..db6a502 100644 --- a/src/lib/base/SimpleEventQueueBuffer.h +++ b/src/lib/base/SimpleEventQueueBuffer.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -28,7 +28,7 @@ An event queue buffer provides a queue of events for an IEventQueue. */ class SimpleEventQueueBuffer : public IEventQueueBuffer { public: - SimpleEventQueueBuffer(); + SimpleEventQueueBuffer(); ~SimpleEventQueueBuffer(); // IEventQueueBuffer overrides diff --git a/src/lib/base/Stopwatch.cpp b/src/lib/base/Stopwatch.cpp index b9ceb85..a174ea6 100644 --- a/src/lib/base/Stopwatch.cpp +++ b/src/lib/base/Stopwatch.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/Stopwatch.h b/src/lib/base/Stopwatch.h index dda74ea..81098fd 100644 --- a/src/lib/base/Stopwatch.h +++ b/src/lib/base/Stopwatch.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -88,7 +88,7 @@ public: */ bool isStopped() const; - // return the time since the last reset(). + // return the time since the last reset(). //! Get elapsed time /*! Returns the time since the last reset(). This cannot trigger the diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index 1ab3623..adbb11d 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -35,6 +35,42 @@ namespace barrier { namespace string { +namespace { + +// returns negative in case of non-matching character +int hex_to_number(char ch) +{ + switch (ch) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + + case 'a': return 10; + case 'b': return 11; + case 'c': return 12; + case 'd': return 13; + case 'e': return 14; + case 'f': return 15; + + case 'A': return 10; + case 'B': return 11; + case 'C': return 12; + case 'D': return 13; + case 'E': return 14; + case 'F': return 15; + } + return -1; +} + +} // namespace + std::string format(const char* fmt, ...) { @@ -135,7 +171,7 @@ sprintf(const char* fmt, ...) // try printing into the buffer va_list args; va_start(args, fmt); - int n = ARCH->vsnprintf(buffer, len, fmt, args); + int n = std::vsnprintf(buffer, len, fmt, args); va_end(args); // if the buffer wasn't big enough then make it bigger and try again @@ -185,16 +221,42 @@ removeFileExt(std::string filename) return filename.substr(0, dot); } -void -toHex(std::string& subject, int width, const char fill) +std::string to_hex(const std::vector& subject, int width, const char fill) { std::stringstream ss; ss << std::hex; - for (unsigned int i = 0; i < subject.length(); i++) { - ss << std::setw(width) << std::setfill(fill) << (int)(unsigned char)subject[i]; + for (unsigned int i = 0; i < subject.size(); i++) { + ss << std::setw(width) << std::setfill(fill) << static_cast(subject[i]); } - subject = ss.str(); + return ss.str(); +} + +std::vector from_hex(const std::string& data) +{ + std::vector result; + result.reserve(data.size() / 2); + + std::size_t i = 0; + while (i < data.size()) { + if (data[i] == ':') { + i++; + continue; + } + + if (i + 2 > data.size()) { + return {}; // uneven character count follows, it's unclear how to interpret it + } + + auto high = hex_to_number(data[i]); + auto low = hex_to_number(data[i + 1]); + if (high < 0 || low < 0) { + return {}; + } + result.push_back(high * 16 + low); + i += 2; + } + return result; } void diff --git a/src/lib/base/String.h b/src/lib/base/String.h index 73526b4..9c5a53b 100644 --- a/src/lib/base/String.h +++ b/src/lib/base/String.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -75,7 +75,10 @@ std::string removeFileExt(std::string filename); /*! Convert each character in \c subject into hexdecimal form with \c width */ -void toHex(std::string& subject, int width, const char fill = '0'); +std::string to_hex(const std::vector& subject, int width, const char fill = '0'); + +/// Convert binary data from hexadecimal +std::vector from_hex(const std::string& data); //! Convert to all uppercase /*! diff --git a/src/lib/base/TMethodEventJob.h b/src/lib/base/TMethodEventJob.h index a65f8c9..04a36fd 100644 --- a/src/lib/base/TMethodEventJob.h +++ b/src/lib/base/TMethodEventJob.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/TMethodJob.h b/src/lib/base/TMethodJob.h deleted file mode 100644 index ec88f05..0000000 --- a/src/lib/base/TMethodJob.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "IJob.h" - -//! Use a function as a job -/*! -A job class that invokes a member function. -*/ -template -class TMethodJob : public IJob { -public: - //! run() invokes \c object->method(arg) - TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL); - virtual ~TMethodJob(); - - // IJob overrides - virtual void run(); - -private: - T* m_object; - void (T::*m_method)(void*); - void* m_arg; -}; - -template -inline -TMethodJob::TMethodJob(T* object, void (T::*method)(void*), void* arg) : - m_object(object), - m_method(method), - m_arg(arg) -{ - // do nothing -} - -template -inline -TMethodJob::~TMethodJob() -{ - // do nothing -} - -template -inline -void -TMethodJob::run() -{ - if (m_object != NULL) { - (m_object->*m_method)(m_arg); - } -} diff --git a/src/lib/base/Unicode.cpp b/src/lib/base/Unicode.cpp index 05b0212..2bd0360 100644 --- a/src/lib/base/Unicode.cpp +++ b/src/lib/base/Unicode.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/Unicode.h b/src/lib/base/Unicode.h index 9bf83ae..430bb1b 100644 --- a/src/lib/base/Unicode.h +++ b/src/lib/base/Unicode.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/XBase.cpp b/src/lib/base/XBase.cpp index cb0db2e..b7a7baf 100644 --- a/src/lib/base/XBase.cpp +++ b/src/lib/base/XBase.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/XBase.h b/src/lib/base/XBase.h index 59b700e..0b46750 100644 --- a/src/lib/base/XBase.h +++ b/src/lib/base/XBase.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/base/finally.h b/src/lib/base/finally.h new file mode 100644 index 0000000..f3be617 --- /dev/null +++ b/src/lib/base/finally.h @@ -0,0 +1,61 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_BASE_FINALLY_H +#define BARRIER_LIB_BASE_FINALLY_H + +#include + +namespace barrier { + +// this implements a common pattern of executing an action at the end of function + +template +class final_action { +public: + final_action() noexcept {} + final_action(Callable callable) noexcept : callable_{callable} {} + + ~final_action() noexcept + { + if (!invoked_) { + callable_(); + } + } + + final_action(final_action&& other) noexcept : + callable_{std::move(other.callable_)} + { + std::swap(invoked_, other.invoked_); + } + + final_action(const final_action&) = delete; + final_action& operator=(const final_action&) = delete; +private: + bool invoked_ = false; + Callable callable_; +}; + +template +inline final_action finally(Callable&& callable) noexcept +{ + return final_action(std::forward(callable)); +} + +} // namespace barrier + +#endif // BARRIER_LIB_BASE_FINALLY_H diff --git a/src/lib/base/log_outputters.cpp b/src/lib/base/log_outputters.cpp index af53192..176af2d 100644 --- a/src/lib/base/log_outputters.cpp +++ b/src/lib/base/log_outputters.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -17,10 +17,9 @@ */ #include "base/log_outputters.h" -#include "base/TMethodJob.h" #include "arch/Arch.h" #include "base/String.h" - +#include "io/filesystem.h" #include enum EFileLogOutputter { @@ -260,7 +259,7 @@ FileLogOutputter::write(ELevel level, const char *message) bool moveFile = false; std::ofstream m_handle; - m_handle.open(m_fileName.c_str(), std::fstream::app); + barrier::open_utf8_path(m_handle, m_fileName, std::fstream::app); if (m_handle.is_open() && m_handle.fail() != true) { m_handle << message << std::endl; @@ -305,7 +304,7 @@ MesssageBoxLogOutputter::~MesssageBoxLogOutputter() } void -MesssageBoxLogOutputter::open(const char* title) +MesssageBoxLogOutputter::open(const char* title) { // do nothing } diff --git a/src/lib/base/log_outputters.h b/src/lib/base/log_outputters.h index 15f1e7a..2a84170 100644 --- a/src/lib/base/log_outputters.h +++ b/src/lib/base/log_outputters.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/client/CMakeLists.txt b/src/lib/client/CMakeLists.txt index 97dc9db..033876f 100644 --- a/src/lib/client/CMakeLists.txt +++ b/src/lib/client/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 96d2c67..a7b15cf 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -37,7 +37,6 @@ #include "base/Log.h" #include "base/IEventQueue.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" #include #include @@ -127,6 +126,12 @@ Client::connect() return; } + auto security_level = ConnectionSecurityLevel::PLAINTEXT; + if (m_useSecureNetwork) { + // client always authenticates server + security_level = ConnectionSecurityLevel::ENCRYPTED_AUTHENTICATED; + } + try { // resolve the server hostname. do this every time we connect // in case we couldn't resolve the address earlier or the address @@ -134,20 +139,19 @@ Client::connect() // being shuttled between various networks). patch by Brent // Priddy. m_serverAddress.resolve(); - + // m_serverAddress will be null if the hostname address is not reolved if (m_serverAddress.getAddress() != NULL) { // to help users troubleshoot, show server host name (issue: 60) - LOG((CLOG_NOTE "connecting to '%s': %s:%i", + LOG((CLOG_NOTE "connecting to '%s': %s:%i", m_serverAddress.getHostname().c_str(), ARCH->addrToString(m_serverAddress.getAddress()).c_str(), m_serverAddress.getPort())); } // create the socket - IDataSocket* socket = m_socketFactory->create( - ARCH->getAddrFamily(m_serverAddress.getAddress()), - m_useSecureNetwork); + IDataSocket* socket = m_socketFactory->create(ARCH->getAddrFamily(m_serverAddress.getAddress()), + security_level); m_socket = dynamic_cast(socket); // filter socket messages, including a packetizing filter @@ -255,7 +259,7 @@ Client::leave() m_active = false; m_screen->leave(); - + if (m_enableClipboard) { // send clipboards that we own and that have changed for (ClipboardID id = 0; id < kClipboardEnd; ++id) { @@ -755,9 +759,7 @@ void Client::onFileRecieveCompleted() { if (isReceivedFileSizeValid()) { - m_writeToDropDirThread = new Thread( - new TMethodJob( - this, &Client::writeToDropDirThread)); + m_writeToDropDirThread = new Thread([this](){ write_to_drop_dir_thread(); }); } } @@ -767,15 +769,14 @@ Client::handleStopRetry(const Event&, void*) m_args.m_restartable = false; } -void -Client::writeToDropDirThread(void*) +void Client::write_to_drop_dir_thread() { LOG((CLOG_DEBUG "starting write to drop dir thread")); while (m_screen->isFakeDraggingStarted()) { ARCH->sleep(.1f); } - + DropHelper::writeToDir(m_screen->getDropTarget(), m_dragFileList, m_receivedFileData); } @@ -790,7 +791,7 @@ Client::dragInfoReceived(UInt32 fileNum, std::string data) } DragInformation::parseDragInfo(m_dragFileList, fileNum, data); - + m_screen->startDraggingFiles(m_dragFileList); } @@ -806,19 +807,14 @@ Client::sendFileToServer(const char* filename) if (m_sendFileThread != NULL) { StreamChunker::interruptFile(); } - - m_sendFileThread = new Thread( - new TMethodJob( - this, &Client::sendFileThread, - static_cast(const_cast(filename)))); + + m_sendFileThread = new Thread([this, filename]() { send_file_thread(filename); }); } -void -Client::sendFileThread(void* filename) +void Client::send_file_thread(const char* filename) { try { - char* name = static_cast(filename); - StreamChunker::sendFile(name, m_events, this); + StreamChunker::sendFile(filename, m_events, this); } catch (std::runtime_error& error) { LOG((CLOG_ERR "failed sending file chunks: %s", error.what())); diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index 7e566be..c172af2 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -90,11 +90,11 @@ public: //! Create a new thread and use it to send file to Server void sendFileToServer(const char* filename); - + //! Send dragging file information back to server void sendDragInfo(UInt32 fileCount, std::string& info, size_t size); - + //@} //! @name accessors //@{ @@ -118,8 +118,8 @@ public: to connect) to. */ NetworkAddress getServerAddress() const; - - //! Return true if recieved file size is valid + + //! Return true if received file size is valid bool isReceivedFileSizeValid(); //! Return expected file size @@ -167,8 +167,8 @@ private: void sendEvent(Event::Type, void*); void sendConnectionFailedEvent(const char* msg); void sendFileChunk(const void* data); - void sendFileThread(void*); - void writeToDropDirThread(void*); + void send_file_thread(const char* filename); + void write_to_drop_dir_thread(); void setupConnecting(); void setupConnection(); void setupScreen(); diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index c067f13..c6e3576 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -26,6 +26,7 @@ #include "barrier/ProtocolUtil.h" #include "barrier/option_types.h" #include "barrier/protocol_types.h" +#include "barrier/XBarrier.h" #include "io/IStream.h" #include "base/Log.h" #include "base/IEventQueue.h" @@ -124,17 +125,27 @@ ServerProxy::handleData(const Event&, void*) // parse message LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); - switch ((this->*m_parser)(code)) { - case kOkay: - break; - - case kUnknown: - LOG((CLOG_ERR "invalid message from server: %c%c%c%c", code[0], code[1], code[2], code[3])); + try { + switch ((this->*m_parser)(code)) { + case kOkay: + break; + + case kUnknown: + LOG((CLOG_ERR "invalid message from server: %c%c%c%c", code[0], code[1], code[2], code[3])); + m_client->disconnect("invalid message from server"); + return; + + case kDisconnect: + return; + } + } catch (const XBadClient& e) { + // TODO: disconnect handling is currently dispersed across both parseMessage() and + // handleData() functions, we should collect that to a single place + + LOG((CLOG_ERR "protocol error from server: %s", e.what())); + ProtocolUtil::writef(m_stream, kMsgEBad); m_client->disconnect("invalid message from server"); return; - - case kDisconnect: - return; } // next message @@ -553,7 +564,7 @@ ServerProxy::setClipboard() static std::string dataCached; ClipboardID id; UInt32 seq; - + int r = ClipboardChunk::assemble(m_stream, dataCached, id, seq); if (r == kStart) { @@ -562,7 +573,7 @@ ServerProxy::setClipboard() } else if (r == kFinish) { LOG((CLOG_DEBUG "received clipboard %d size=%d", id, dataCached.size())); - + // forward Clipboard clipboard; clipboard.unmarshall(dataCached, 0); diff --git a/src/lib/client/ServerProxy.h b/src/lib/client/ServerProxy.h index abca4c3..12a3226 100644 --- a/src/lib/client/ServerProxy.h +++ b/src/lib/client/ServerProxy.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -58,8 +58,8 @@ public: // sending dragging information to server void sendDragInfo(UInt32 fileCount, const char* info, size_t size); - -#ifdef TEST_ENV + +#ifdef BARRIER_TEST_ENV void handleDataForTest() { handleData(Event(), NULL); } #endif diff --git a/src/lib/common/CMakeLists.txt b/src/lib/common/CMakeLists.txt index b3791c1..82ca614 100644 --- a/src/lib/common/CMakeLists.txt +++ b/src/lib/common/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/DataDirectories.h b/src/lib/common/DataDirectories.h index 6b990c2..4550211 100644 --- a/src/lib/common/DataDirectories.h +++ b/src/lib/common/DataDirectories.h @@ -15,27 +15,36 @@ * along with this program. If not, see . */ -#pragma once +#ifndef BARRIER_LIB_COMMON_DATA_DIRECTORIES_H +#define BARRIER_LIB_COMMON_DATA_DIRECTORIES_H -#include +#include "io/filesystem.h" + +namespace barrier { class DataDirectories { public: - static const std::string& profile(); - static const std::string& profile(const std::string& path); + static const fs::path& profile(); + static const fs::path& profile(const fs::path& path); - static const std::string& global(); - static const std::string& global(const std::string& path); + static const fs::path& global(); + static const fs::path& global(const fs::path& path); - static const std::string& systemconfig(); - static const std::string& systemconfig(const std::string& path); + static const fs::path& systemconfig(); + static const fs::path& systemconfig(const fs::path& path); + static fs::path ssl_fingerprints_path(); + static fs::path local_ssl_fingerprints_path(); + static fs::path trusted_servers_ssl_fingerprints_path(); + static fs::path trusted_clients_ssl_fingerprints_path(); + static fs::path ssl_certificate_path(); private: - // static class - DataDirectories() {} - - static std::string _profile; - static std::string _global; - static std::string _systemconfig; + static fs::path _profile; + static fs::path _global; + static fs::path _systemconfig; }; + +} // namespace barrier + +#endif diff --git a/src/lib/common/DataDirectories_static.cpp b/src/lib/common/DataDirectories_static.cpp index 48dccb6..47f88e7 100644 --- a/src/lib/common/DataDirectories_static.cpp +++ b/src/lib/common/DataDirectories_static.cpp @@ -17,7 +17,40 @@ #include "DataDirectories.h" -// static member -std::string DataDirectories::_profile; -std::string DataDirectories::_global; -std::string DataDirectories::_systemconfig; +namespace barrier { + +fs::path DataDirectories::_profile; +fs::path DataDirectories::_global; +fs::path DataDirectories::_systemconfig; + +static const char kFingerprintsDirName[] = "SSL/Fingerprints"; +static const char kFingerprintsLocalFilename[] = "Local.txt"; +static const char kFingerprintsTrustedServersFilename[] = "TrustedServers.txt"; +static const char kFingerprintsTrustedClientsFilename[] = "TrustedClients.txt"; + +fs::path DataDirectories::ssl_fingerprints_path() +{ + return profile() / kFingerprintsDirName; +} + +fs::path DataDirectories::local_ssl_fingerprints_path() +{ + return ssl_fingerprints_path() / kFingerprintsLocalFilename; +} + +fs::path DataDirectories::trusted_servers_ssl_fingerprints_path() +{ + return ssl_fingerprints_path() / kFingerprintsTrustedServersFilename; +} + +fs::path DataDirectories::trusted_clients_ssl_fingerprints_path() +{ + return ssl_fingerprints_path() / kFingerprintsTrustedClientsFilename; +} + +fs::path DataDirectories::ssl_certificate_path() +{ + return profile() / "SSL" / "Barrier.pem"; +} + +} // namespace barrier diff --git a/src/lib/common/IInterface.h b/src/lib/common/IInterface.h index 84a76a9..65ede4d 100644 --- a/src/lib/common/IInterface.h +++ b/src/lib/common/IInterface.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/MacOSXPrecomp.h b/src/lib/common/MacOSXPrecomp.h deleted file mode 100644 index 7dbc8d0..0000000 --- a/src/lib/common/MacOSXPrecomp.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - // -// Prefix header for all source files of the 'deleteme' target in the 'deleteme' project. -// - -#include diff --git a/src/lib/common/PathUtilities.cpp b/src/lib/common/PathUtilities.cpp deleted file mode 100644 index a2ab38a..0000000 --- a/src/lib/common/PathUtilities.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* -* barrier -- mouse and keyboard sharing utility -* Copyright (C) 2018 Debauchee Open Source Group -* -* This package is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* found in the file LICENSE that should have accompanied this file. -* -* This package is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -/* - -These functions cover the vast majority of cases for different paths across -windows and unixes. They are not, however, fullproof and probably don't cover -fringe cases very well. The library below might be used as an alternative if -these implementations prove to be insufficient. As the library's readme states -it is simply a temporary band-aid until std::filesystem is integrated (C++17 -has it in std::experimental) and this class should also be treated as such. - -https://github.com/wjakob/filesystem/ - -*/ - -#include "PathUtilities.h" - -// keep the default platform delimiter as the first in the list -#ifdef _WIN32 -static const char *Delimiters = "\\/"; -#else -static const char *Delimiters = "/"; -#endif - -static const char DefaultDelimiter = Delimiters[0]; - -std::string PathUtilities::basename(const std::string& path) -{ - return path.substr(path.find_last_of(Delimiters) + 1); -} - -std::string PathUtilities::concat(const std::string& left, const std::string& right) -{ - // although npos is usually (-1) we can't count on that so handle it explicitly - auto leftEnd = left.find_last_not_of(Delimiters); - if (leftEnd == std::string::npos) - leftEnd = 0; - else - ++leftEnd; - auto rightStart = right.find_first_not_of(Delimiters, 0); - if (rightStart == std::string::npos) { - // both left/right are empty - if (left.size() == 0 && right.size() == 0) - return ""; - // right is full of delims, left is okay - if (leftEnd > 0) - return left.substr(0, leftEnd); - // both left/right useless but at least one has delims - return std::string(1, DefaultDelimiter); - } - if (leftEnd == 0) { - // right is okay and not prefixed with delims, left is empty - if (left.size() == 0 && rightStart == 0) - return right.substr(rightStart); - // (right is okay and prefixed with delims) OR left is full of delims - return DefaultDelimiter + right.substr(rightStart); - } - // concatenation using both left and right - return left.substr(0, leftEnd) + DefaultDelimiter + right.substr(rightStart); -} diff --git a/src/lib/common/PathUtilities.h b/src/lib/common/PathUtilities.h deleted file mode 100644 index 70b85b4..0000000 --- a/src/lib/common/PathUtilities.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -* barrier -- mouse and keyboard sharing utility -* Copyright (C) 2018 Debauchee Open Source Group -* -* This package is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* found in the file LICENSE that should have accompanied this file. -* -* This package is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#pragma once - -#include - -class PathUtilities -{ -public: - static std::string basename(const std::string& path); - static std::string concat(const std::string& left, const std::string& right); - -private: - // static class - PathUtilities() {} -}; diff --git a/src/lib/common/Version.cpp b/src/lib/common/Version.cpp index 94d6c5d..41d2804 100644 --- a/src/lib/common/Version.cpp +++ b/src/lib/common/Version.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/Version.h b/src/lib/common/Version.h index 66bb2e2..ad4a3e7 100644 --- a/src/lib/common/Version.h +++ b/src/lib/common/Version.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/basic_types.h b/src/lib/common/basic_types.h index f84550c..1882e57 100644 --- a/src/lib/common/basic_types.h +++ b/src/lib/common/basic_types.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -18,49 +18,7 @@ #pragma once -#include "common/common.h" - -// -// pick types of particular sizes -// - -#if !defined(TYPE_OF_SIZE_1) -# if SIZEOF_CHAR == 1 -# define TYPE_OF_SIZE_1 char -# endif -#endif - -#if !defined(TYPE_OF_SIZE_2) -# if SIZEOF_INT == 2 -# define TYPE_OF_SIZE_2 int -# else -# define TYPE_OF_SIZE_2 short -# endif -#endif - -#if !defined(TYPE_OF_SIZE_4) - // Carbon defines SInt32 and UInt32 in terms of long -# if SIZEOF_INT == 4 && !defined(__APPLE__) -# define TYPE_OF_SIZE_4 int -# else -# define TYPE_OF_SIZE_4 long -# endif -#endif - - // -// verify existence of required types -// - -#if !defined(TYPE_OF_SIZE_1) -# error No 1 byte integer type -#endif -#if !defined(TYPE_OF_SIZE_2) -# error No 2 byte integer type -#endif -#if !defined(TYPE_OF_SIZE_4) -# error No 4 byte integer type -#endif - +#include // // make typedefs @@ -75,18 +33,11 @@ #if defined(__APPLE__) #include #else -typedef signed TYPE_OF_SIZE_1 SInt8; -typedef signed TYPE_OF_SIZE_2 SInt16; -typedef signed TYPE_OF_SIZE_4 SInt32; -typedef unsigned TYPE_OF_SIZE_1 UInt8; -typedef unsigned TYPE_OF_SIZE_2 UInt16; -typedef unsigned TYPE_OF_SIZE_4 UInt32; +using SInt8 = std::int8_t; +using SInt16 = std::int16_t; +using SInt32 = std::int32_t; +using UInt8 = std::uint8_t; +using UInt16 = std::uint16_t; +using UInt32 = std::uint32_t; #endif #endif -// -// clean up -// - -#undef TYPE_OF_SIZE_1 -#undef TYPE_OF_SIZE_2 -#undef TYPE_OF_SIZE_4 diff --git a/src/lib/common/common.h b/src/lib/common/common.h index 5ea215f..3946256 100644 --- a/src/lib/common/common.h +++ b/src/lib/common/common.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -18,26 +18,8 @@ #pragma once -#if defined(_WIN32) -# define SYSAPI_WIN32 1 -# define WINAPI_MSWINDOWS 1 -#elif HAVE_CONFIG_H +#if HAVE_CONFIG_H # include "config.h" -#else -# error "config.h missing" -#endif - -// VC++ has built-in sized types -#if defined(_MSC_VER) -# include -# define TYPE_OF_SIZE_1 __int8 -# define TYPE_OF_SIZE_2 __int16 -# define TYPE_OF_SIZE_4 __int32 -#else -# define SIZE_OF_CHAR 1 -# define SIZE_OF_SHORT 2 -# define SIZE_OF_INT 4 -# define SIZE_OF_LONG 4 #endif // define NULL diff --git a/src/lib/common/stdbitset.h b/src/lib/common/stdbitset.h index 1096249..65433ac 100644 --- a/src/lib/common/stdbitset.h +++ b/src/lib/common/stdbitset.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/stddeque.h b/src/lib/common/stddeque.h index ffaed24..5033826 100644 --- a/src/lib/common/stddeque.h +++ b/src/lib/common/stddeque.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/stdfstream.h b/src/lib/common/stdfstream.h index e9aa263..0328a4b 100644 --- a/src/lib/common/stdfstream.h +++ b/src/lib/common/stdfstream.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/stdistream.h b/src/lib/common/stdistream.h index b19e2ab..2e9c3cc 100644 --- a/src/lib/common/stdistream.h +++ b/src/lib/common/stdistream.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/stdlist.h b/src/lib/common/stdlist.h index d530e57..f1c03e2 100644 --- a/src/lib/common/stdlist.h +++ b/src/lib/common/stdlist.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/stdmap.h b/src/lib/common/stdmap.h index 2351074..7a1040e 100644 --- a/src/lib/common/stdmap.h +++ b/src/lib/common/stdmap.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/stdostream.h b/src/lib/common/stdostream.h index bb82285..10b65d0 100644 --- a/src/lib/common/stdostream.h +++ b/src/lib/common/stdostream.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/stdpost.h b/src/lib/common/stdpost.h index 8046da0..d7f0f9c 100644 --- a/src/lib/common/stdpost.h +++ b/src/lib/common/stdpost.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/stdpre.h b/src/lib/common/stdpre.h index 8ccd308..bf8b9bf 100644 --- a/src/lib/common/stdpre.h +++ b/src/lib/common/stdpre.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/stdset.h b/src/lib/common/stdset.h index 1c98971..e247745 100644 --- a/src/lib/common/stdset.h +++ b/src/lib/common/stdset.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/stdsstream.h b/src/lib/common/stdsstream.h index 43671ff..1bfdd7e 100644 --- a/src/lib/common/stdsstream.h +++ b/src/lib/common/stdsstream.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/stdstring.h b/src/lib/common/stdstring.h index f320ca8..3b3ba0e 100644 --- a/src/lib/common/stdstring.h +++ b/src/lib/common/stdstring.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/stdvector.h b/src/lib/common/stdvector.h index ab4b853..4dd263d 100644 --- a/src/lib/common/stdvector.h +++ b/src/lib/common/stdvector.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/common/unix/DataDirectories.cpp b/src/lib/common/unix/DataDirectories.cpp index 72e510a..cf3ca24 100644 --- a/src/lib/common/unix/DataDirectories.cpp +++ b/src/lib/common/unix/DataDirectories.cpp @@ -18,11 +18,11 @@ #include "../DataDirectories.h" #include // sysconf -#include // getenv +#include // getenv #include // getpwuid(_r) #include // getpwuid(_r) -const std::string ProfileSubdir = "/barrier"; +namespace barrier { static std::string pw_dir(struct passwd* pwentp) { @@ -33,7 +33,7 @@ static std::string pw_dir(struct passwd* pwentp) #ifdef HAVE_GETPWUID_R -static std::string unix_home() +static fs::path unix_home() { long size = -1; #if defined(_SC_GETPW_R_SIZE_MAX) @@ -46,47 +46,47 @@ static std::string unix_home() struct passwd* pwentp; std::string buffer(size, 0); getpwuid_r(getuid(), &pwent, &buffer[0], size, &pwentp); - return pw_dir(pwentp); + return fs::u8path(pw_dir(pwentp)); } #else // not HAVE_GETPWUID_R -static std::string unix_home() +static fs::path unix_home() { - return pw_dir(getpwuid(getuid())); + return fs::u8path(pw_dir(getpwuid(getuid()))); } #endif // HAVE_GETPWUID_R -static std::string profile_basedir() +static fs::path profile_basedir() { #ifdef WINAPI_XWINDOWS // linux/bsd adheres to freedesktop standards // https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html - const char* dir = getenv("XDG_DATA_HOME"); + const char* dir = std::getenv("XDG_DATA_HOME"); if (dir != NULL) - return dir; - return unix_home() + "/.local/share"; + return fs::u8path(dir); + return unix_home() / ".local/share"; #else // macos has its own standards // https://developer.apple.com/library/content/documentation/General/Conceptual/MOSXAppProgrammingGuide/AppRuntime/AppRuntime.html - return unix_home() + "/Library/Application Support"; + return unix_home() / "Library/Application Support"; #endif } -const std::string& DataDirectories::profile() +const fs::path& DataDirectories::profile() { if (_profile.empty()) - _profile = profile_basedir() + ProfileSubdir; + _profile = profile_basedir() / "barrier"; return _profile; } -const std::string& DataDirectories::profile(const std::string& path) +const fs::path& DataDirectories::profile(const fs::path& path) { _profile = path; return _profile; } -const std::string& DataDirectories::global() +const fs::path& DataDirectories::global() { if (_global.empty()) // TODO: where on a unix system should public/global shared data go? @@ -94,21 +94,23 @@ const std::string& DataDirectories::global() _global = "/tmp"; return _global; } -const std::string& DataDirectories::global(const std::string& path) +const fs::path& DataDirectories::global(const fs::path& path) { _global = path; return _global; } -const std::string& DataDirectories::systemconfig() +const fs::path& DataDirectories::systemconfig() { if (_systemconfig.empty()) _systemconfig = "/etc"; return _systemconfig; } -const std::string& DataDirectories::systemconfig(const std::string& path) +const fs::path& DataDirectories::systemconfig(const fs::path& path) { _systemconfig = path; return _systemconfig; } + +} // namespace barrier diff --git a/src/lib/common/win32/DataDirectories.cpp b/src/lib/common/win32/DataDirectories.cpp index 15cb64e..31a428f 100644 --- a/src/lib/common/win32/DataDirectories.cpp +++ b/src/lib/common/win32/DataDirectories.cpp @@ -16,66 +16,62 @@ */ #include "../DataDirectories.h" +#include "encoding_utilities.h" #include -std::string unicode_to_mb(const WCHAR* utfStr) -{ - int utfLength = lstrlenW(utfStr); - int mbLength = WideCharToMultiByte(CP_UTF8, 0, utfStr, utfLength, NULL, 0, NULL, NULL); - std::string mbStr(mbLength, 0); - WideCharToMultiByte(CP_UTF8, 0, utfStr, utfLength, &mbStr[0], mbLength, NULL, NULL); - return mbStr; -} +namespace barrier { -std::string known_folder_path(const KNOWNFOLDERID& id) +fs::path known_folder_path(const KNOWNFOLDERID& id) { - std::string path; + fs::path path; WCHAR* buffer; HRESULT result = SHGetKnownFolderPath(id, 0, NULL, &buffer); if (result == S_OK) { - path = unicode_to_mb(buffer); + path = fs::path(std::wstring(buffer)); CoTaskMemFree(buffer); } return path; } -const std::string& DataDirectories::profile() +const fs::path& DataDirectories::profile() { if (_profile.empty()) - _profile = known_folder_path(FOLDERID_LocalAppData) + "\\Barrier"; + _profile = known_folder_path(FOLDERID_LocalAppData) / "Barrier"; return _profile; } -const std::string& DataDirectories::profile(const std::string& path) +const fs::path& DataDirectories::profile(const fs::path& path) { _profile = path; return _profile; } -const std::string& DataDirectories::global() +const fs::path& DataDirectories::global() { if (_global.empty()) - _global = known_folder_path(FOLDERID_ProgramData) + "\\Barrier"; + _global = known_folder_path(FOLDERID_ProgramData) / "Barrier"; return _global; } -const std::string& DataDirectories::global(const std::string& path) +const fs::path& DataDirectories::global(const fs::path& path) { _global = path; return _global; } -const std::string& DataDirectories::systemconfig() +const fs::path& DataDirectories::systemconfig() { // systemconfig() is a special case in that it will track the current value - // of global() unless and until it is explictly set otherwise + // of global() unless and until it is explicitly set otherwise // previously it would default to the windows folder which was horrible! if (_systemconfig.empty()) return global(); return _systemconfig; } -const std::string& DataDirectories::systemconfig(const std::string& path) +const fs::path& DataDirectories::systemconfig(const fs::path& path) { _systemconfig = path; return _systemconfig; } + +} // namespace barrier diff --git a/src/lib/common/win32/encoding_utilities.cpp b/src/lib/common/win32/encoding_utilities.cpp new file mode 100644 index 0000000..11781d3 --- /dev/null +++ b/src/lib/common/win32/encoding_utilities.cpp @@ -0,0 +1,37 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "encoding_utilities.h" +#include + +std::string win_wchar_to_utf8(const WCHAR* utfStr) +{ + int utfLength = lstrlenW(utfStr); + int mbLength = WideCharToMultiByte(CP_UTF8, 0, utfStr, utfLength, NULL, 0, NULL, NULL); + std::string mbStr(mbLength, 0); + WideCharToMultiByte(CP_UTF8, 0, utfStr, utfLength, &mbStr[0], mbLength, NULL, NULL); + return mbStr; +} + +std::vector utf8_to_win_char(const std::string& str) +{ + int result_len = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), NULL, 0); + std::vector result; + result.resize(result_len + 1, 0); + MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), result.data(), result_len); + return result; +} diff --git a/src/lib/common/win32/encoding_utilities.h b/src/lib/common/win32/encoding_utilities.h new file mode 100644 index 0000000..747371e --- /dev/null +++ b/src/lib/common/win32/encoding_utilities.h @@ -0,0 +1,28 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_COMMON_WIN32_ENCODING_UTILITIES_H +#define BARRIER_LIB_COMMON_WIN32_ENCODING_UTILITIES_H + +#include +#include +#include + +std::string win_wchar_to_utf8(const WCHAR* utfStr); +std::vector utf8_to_win_char(const std::string& str); + +#endif diff --git a/src/lib/io/CMakeLists.txt b/src/lib/io/CMakeLists.txt index 32ae7ec..10b79df 100644 --- a/src/lib/io/CMakeLists.txt +++ b/src/lib/io/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/io/IStream.h b/src/lib/io/IStream.h index cf93ac4..7601fc4 100644 --- a/src/lib/io/IStream.h +++ b/src/lib/io/IStream.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/io/StreamBuffer.cpp b/src/lib/io/StreamBuffer.cpp index 61f05ba..8abbbb8 100644 --- a/src/lib/io/StreamBuffer.cpp +++ b/src/lib/io/StreamBuffer.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -22,6 +22,8 @@ // StreamBuffer // +#include + const UInt32 StreamBuffer::kChunkSize = 4096; StreamBuffer::StreamBuffer() : diff --git a/src/lib/io/StreamBuffer.h b/src/lib/io/StreamBuffer.h index 49b666b..364175f 100644 --- a/src/lib/io/StreamBuffer.h +++ b/src/lib/io/StreamBuffer.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/io/StreamFilter.cpp b/src/lib/io/StreamFilter.cpp index 170e237..dca0a69 100644 --- a/src/lib/io/StreamFilter.cpp +++ b/src/lib/io/StreamFilter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/io/StreamFilter.h b/src/lib/io/StreamFilter.h index e578e0c..55d481e 100644 --- a/src/lib/io/StreamFilter.h +++ b/src/lib/io/StreamFilter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/io/XIO.cpp b/src/lib/io/XIO.cpp index 911fa87..f78e031 100644 --- a/src/lib/io/XIO.cpp +++ b/src/lib/io/XIO.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/io/XIO.h b/src/lib/io/XIO.h index 4964441..5803388 100644 --- a/src/lib/io/XIO.h +++ b/src/lib/io/XIO.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/io/filesystem.cpp b/src/lib/io/filesystem.cpp new file mode 100644 index 0000000..46ae06d --- /dev/null +++ b/src/lib/io/filesystem.cpp @@ -0,0 +1,71 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// this header must come first so that it picks up the filesystem implementation +#include + +#include "filesystem.h" +#if SYSAPI_WIN32 +#include "common/win32/encoding_utilities.h" +#endif +#include + +namespace barrier { + +namespace { + +template +void open_utf8_path_impl(Stream& stream, const fs::path& path, std::ios_base::openmode mode) +{ +#if SYSAPI_WIN32 + // on Windows we need to use a non-standard constructor from wchar_t* string + // which fs::path::native() returns + stream.open(path.native().c_str(), mode); +#else + stream.open(path.native().c_str(), mode); +#endif +} + +} // namespace + +void open_utf8_path(std::ifstream& stream, const fs::path& path, std::ios_base::openmode mode) +{ + open_utf8_path_impl(stream, path, mode); +} + +void open_utf8_path(std::ofstream& stream, const fs::path& path, std::ios_base::openmode mode) +{ + open_utf8_path_impl(stream, path, mode); +} + +void open_utf8_path(std::fstream& stream, const fs::path& path, std::ios_base::openmode mode) +{ + open_utf8_path_impl(stream, path, mode); +} + +std::FILE* fopen_utf8_path(const fs::path& path, const std::string& mode) +{ +#if SYSAPI_WIN32 + auto wchar_mode = utf8_to_win_char(mode); + return _wfopen(path.native().c_str(), + reinterpret_cast(wchar_mode.data())); +#else + return std::fopen(path.native().c_str(), mode.c_str()); +#endif +} + +} // namespace barrier diff --git a/src/lib/io/filesystem.h b/src/lib/io/filesystem.h new file mode 100644 index 0000000..78e3423 --- /dev/null +++ b/src/lib/io/filesystem.h @@ -0,0 +1,41 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_IO_FILESYSTEM_H +#define BARRIER_LIB_IO_FILESYSTEM_H + +#include +#include +#include +#include + +namespace barrier { + +namespace fs = ghc::filesystem; + +void open_utf8_path(std::ifstream& stream, const fs::path& path, + std::ios_base::openmode mode = std::ios_base::in); +void open_utf8_path(std::ofstream& stream, const fs::path& path, + std::ios_base::openmode mode = std::ios_base::out); +void open_utf8_path(std::fstream& stream, const fs::path& path, + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out); + +std::FILE* fopen_utf8_path(const fs::path& path, const std::string& mode); + +} // namespace barrier + +#endif // BARRIER_LIB_IO_FILESYSTEM_H diff --git a/src/lib/ipc/CMakeLists.txt b/src/lib/ipc/CMakeLists.txt index 3c7302a..e7ada8f 100644 --- a/src/lib/ipc/CMakeLists.txt +++ b/src/lib/ipc/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/ipc/Ipc.h b/src/lib/ipc/Ipc.h index bc69c08..65e7aaf 100644 --- a/src/lib/ipc/Ipc.h +++ b/src/lib/ipc/Ipc.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/ipc/IpcClient.cpp b/src/lib/ipc/IpcClient.cpp index 4eeae5b..ea5eee1 100644 --- a/src/lib/ipc/IpcClient.cpp +++ b/src/lib/ipc/IpcClient.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/ipc/IpcClient.h b/src/lib/ipc/IpcClient.h index 1e9bca6..900b46c 100644 --- a/src/lib/ipc/IpcClient.h +++ b/src/lib/ipc/IpcClient.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -42,7 +42,7 @@ public: //! Connects to the IPC server at localhost. void connect(); - + //! Disconnects from the IPC server. void disconnect(); diff --git a/src/lib/ipc/IpcClientProxy.cpp b/src/lib/ipc/IpcClientProxy.cpp index 432cc8c..cedfa5b 100644 --- a/src/lib/ipc/IpcClientProxy.cpp +++ b/src/lib/ipc/IpcClientProxy.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -67,7 +67,7 @@ IpcClientProxy::~IpcClientProxy() m_events->forIStream().inputShutdown(), m_stream.getEventTarget()); m_events->removeHandler( m_events->forIStream().outputShutdown(), m_stream.getEventTarget()); - + // don't delete the stream while it's being used. { std::lock_guard lock_read(m_readMutex); @@ -145,7 +145,7 @@ IpcClientProxy::send(const IpcMessage& message) ProtocolUtil::writef(&m_stream, kIpcMsgLogLine, &logLine); break; } - + case kIpcShutdown: ProtocolUtil::writef(&m_stream, kIpcMsgShutdown); break; diff --git a/src/lib/ipc/IpcClientProxy.h b/src/lib/ipc/IpcClientProxy.h index eb9f1e9..dbd0846 100644 --- a/src/lib/ipc/IpcClientProxy.h +++ b/src/lib/ipc/IpcClientProxy.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -46,7 +46,7 @@ private: IpcHelloMessage* parseHello(); IpcCommandMessage* parseCommand(); void disconnect(); - + private: barrier::IStream& m_stream; EIpcClientType m_clientType; diff --git a/src/lib/ipc/IpcLogOutputter.cpp b/src/lib/ipc/IpcLogOutputter.cpp index 44ecdba..5118a0a 100644 --- a/src/lib/ipc/IpcLogOutputter.cpp +++ b/src/lib/ipc/IpcLogOutputter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -28,7 +28,6 @@ #include "base/Event.h" #include "base/EventQueue.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" enum EIpcLogOutputter { kBufferMaxSize = 1000, @@ -54,8 +53,7 @@ IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType m_clientType(clientType) { if (useThread) { - m_bufferThread = new Thread(new TMethodJob( - this, &IpcLogOutputter::bufferThread)); + m_bufferThread = new Thread([this](){ buffer_thread(); }); } } @@ -142,8 +140,7 @@ IpcLogOutputter::isRunning() return m_running; } -void -IpcLogOutputter::bufferThread(void*) +void IpcLogOutputter::buffer_thread() { m_bufferThreadId = m_bufferThread->getID(); m_running = true; diff --git a/src/lib/ipc/IpcLogOutputter.h b/src/lib/ipc/IpcLogOutputter.h index cc7b2fe..3f3ab0a 100644 --- a/src/lib/ipc/IpcLogOutputter.h +++ b/src/lib/ipc/IpcLogOutputter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -50,7 +50,7 @@ public: virtual void close(); virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); - + //! @name manipulators //@{ @@ -76,23 +76,23 @@ public: when threaded mode is on. */ void sendBuffer(); - + //@} - + //! @name accessors //@{ - + //! Get the buffer size /*! Returns the maximum size of the buffer. */ UInt16 bufferMaxSize() const; - + //@} private: void init(); - void bufferThread(void*); + void buffer_thread(); std::string getChunk(size_t count); void appendBuffer(const std::string& text); bool isRunning(); diff --git a/src/lib/ipc/IpcMessage.cpp b/src/lib/ipc/IpcMessage.cpp index 9c321cb..f1b13b1 100644 --- a/src/lib/ipc/IpcMessage.cpp +++ b/src/lib/ipc/IpcMessage.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/ipc/IpcMessage.h b/src/lib/ipc/IpcMessage.h index d37ebc3..06ca08a 100644 --- a/src/lib/ipc/IpcMessage.h +++ b/src/lib/ipc/IpcMessage.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/ipc/IpcServer.cpp b/src/lib/ipc/IpcServer.cpp index 8df98d1..da201c1 100644 --- a/src/lib/ipc/IpcServer.cpp +++ b/src/lib/ipc/IpcServer.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/ipc/IpcServer.h b/src/lib/ipc/IpcServer.h index 179bad8..f6227f2 100644 --- a/src/lib/ipc/IpcServer.h +++ b/src/lib/ipc/IpcServer.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -73,7 +73,7 @@ private: private: typedef std::list ClientList; - + bool m_mock; IEventQueue* m_events; SocketMultiplexer* m_socketMultiplexer; @@ -82,7 +82,7 @@ private: ClientList m_clients; mutable std::mutex m_clientsMutex; -#ifdef TEST_ENV +#ifdef BARRIER_TEST_ENV public: IpcServer() : m_mock(true), diff --git a/src/lib/ipc/IpcServerProxy.cpp b/src/lib/ipc/IpcServerProxy.cpp index 49b3e00..073f7e7 100644 --- a/src/lib/ipc/IpcServerProxy.cpp +++ b/src/lib/ipc/IpcServerProxy.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -56,7 +56,7 @@ IpcServerProxy::handleData(const Event&, void*) LOG((CLOG_DEBUG "ipc read: %c%c%c%c", code[0], code[1], code[2], code[3])); - + IpcMessage* m = nullptr; if (memcmp(code, kIpcMsgLogLine, 4) == 0) { m = parseLogLine(); @@ -68,7 +68,7 @@ IpcServerProxy::handleData(const Event&, void*) LOG((CLOG_ERR "invalid ipc message")); disconnect(); } - + // don't delete with this event; the data is passed to a new event. Event e(m_events->forIpcServerProxy().messageReceived(), this, NULL, Event::kDontFreeData); e.setDataObject(m); @@ -76,7 +76,7 @@ IpcServerProxy::handleData(const Event&, void*) n = m_stream.read(code, 4); } - + LOG((CLOG_DEBUG "finished ipc handle data")); } @@ -110,7 +110,7 @@ IpcServerProxy::parseLogLine() { std::string logLine; ProtocolUtil::readf(&m_stream, kIpcMsgLogLine + 4, &logLine); - + // must be deleted by event handler. return new IpcLogLineMessage(logLine); } diff --git a/src/lib/ipc/IpcServerProxy.h b/src/lib/ipc/IpcServerProxy.h index f2218a4..f724f97 100644 --- a/src/lib/ipc/IpcServerProxy.h +++ b/src/lib/ipc/IpcServerProxy.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/mt/CMakeLists.txt b/src/lib/mt/CMakeLists.txt index 9ee5ea4..19d1829 100644 --- a/src/lib/mt/CMakeLists.txt +++ b/src/lib/mt/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/mt/CondVar.cpp b/src/lib/mt/CondVar.cpp index 11318f9..b9cf565 100644 --- a/src/lib/mt/CondVar.cpp +++ b/src/lib/mt/CondVar.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -24,7 +24,7 @@ // CondVarBase // -CondVarBase::CondVarBase(Mutex* mutex) : +CondVarBase::CondVarBase(Mutex* mutex) : m_mutex(mutex) { assert(m_mutex != NULL); diff --git a/src/lib/mt/CondVar.h b/src/lib/mt/CondVar.h index 0ab956b..fc18748 100644 --- a/src/lib/mt/CondVar.h +++ b/src/lib/mt/CondVar.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -78,7 +78,7 @@ public: signalled, otherwise up to \c timeout seconds or until signalled, whichever comes first. Returns true if the object was signalled during the wait, false otherwise. - + The proper way to wait for a condition is: \code cv.lock(); diff --git a/src/lib/mt/Lock.cpp b/src/lib/mt/Lock.cpp index 80721b9..6614858 100644 --- a/src/lib/mt/Lock.cpp +++ b/src/lib/mt/Lock.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/mt/Lock.h b/src/lib/mt/Lock.h index 4a3f311..e4bbacf 100644 --- a/src/lib/mt/Lock.h +++ b/src/lib/mt/Lock.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/mt/Mutex.cpp b/src/lib/mt/Mutex.cpp index e9a62e8..02d34c9 100644 --- a/src/lib/mt/Mutex.cpp +++ b/src/lib/mt/Mutex.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/mt/Mutex.h b/src/lib/mt/Mutex.h index 51a9649..53b8f45 100644 --- a/src/lib/mt/Mutex.h +++ b/src/lib/mt/Mutex.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/mt/Thread.cpp b/src/lib/mt/Thread.cpp index 7474c16..755c0e6 100644 --- a/src/lib/mt/Thread.cpp +++ b/src/lib/mt/Thread.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -28,12 +28,10 @@ // Thread // -Thread::Thread(IJob* job) +Thread::Thread(const std::function& fun) { - m_thread = ARCH->newThread(&Thread::threadFunc, job); + m_thread = ARCH->newThread([=](){ threadFunc(fun); }); if (m_thread == NULL) { - // couldn't create thread - delete job; throw XMTThreadUnavailable(); } } @@ -69,7 +67,7 @@ Thread::operator=(const Thread& thread) void Thread::exit(void* result) { - throw XThreadExit(result); + throw XThreadExit(); } void @@ -108,15 +106,6 @@ Thread::wait(double timeout) const return ARCH->wait(m_thread, timeout); } -void* -Thread::getResult() const -{ - if (wait()) - return ARCH->getResultOfThread(m_thread); - else - return NULL; -} - IArchMultithread::ThreadID Thread::getID() const { @@ -135,8 +124,7 @@ Thread::operator!=(const Thread& thread) const return !ARCH->isSameThread(m_thread, thread.m_thread); } -void* -Thread::threadFunc(void* vjob) +void Thread::threadFunc(const std::function& func) { // get this thread's id for logging IArchMultithread::ThreadID id; @@ -146,42 +134,26 @@ Thread::threadFunc(void* vjob) ARCH->closeThread(thread); } - // get job - IJob* job = static_cast(vjob); - - // run job - void* result = NULL; try { // go LOG((CLOG_DEBUG1 "thread 0x%08x entry", id)); - job->run(); + func(); LOG((CLOG_DEBUG1 "thread 0x%08x exit", id)); } catch (XThreadCancel&) { // client called cancel() LOG((CLOG_DEBUG1 "caught cancel on thread 0x%08x", id)); - delete job; throw; } - catch (XThreadExit& e) { - // client called exit() - result = e.m_result; - LOG((CLOG_DEBUG1 "caught exit on thread 0x%08x, result %p", id, result)); + catch (XThreadExit&) { + LOG((CLOG_DEBUG1 "caught exit on thread 0x%08x", id)); } catch (XBase& e) { LOG((CLOG_ERR "exception on thread 0x%08x: %s", id, e.what())); - delete job; throw; } catch (...) { LOG((CLOG_ERR "exception on thread 0x%08x: ", id)); - delete job; throw; } - - // done with job - delete job; - - // return exit result - return result; } diff --git a/src/lib/mt/Thread.h b/src/lib/mt/Thread.h index a7434fd..141d59f 100644 --- a/src/lib/mt/Thread.h +++ b/src/lib/mt/Thread.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -19,13 +19,12 @@ #pragma once #include "arch/IArchMultithread.h" - -class IJob; +#include //! Thread handle /*! Creating a Thread creates a new context of execution (i.e. thread) that -runs simulatenously with the calling thread. A Thread is only a handle +runs simultaneously with the calling thread. A Thread is only a handle to a thread; deleting a Thread does not cancel or destroy the thread it refers to and multiple Thread objects can refer to the same thread. @@ -44,10 +43,9 @@ class Thread { public: //! Run \c adoptedJob in a new thread /*! - Create and start a new thread executing the \c adoptedJob. The - new thread takes ownership of \c adoptedJob and will delete it. + Create and start a new thread executing the \c fun. */ - Thread(IJob* adoptedJob); + Thread(const std::function& fun); //! Duplicate a thread handle /*! @@ -79,8 +77,7 @@ public: /*! Terminate the calling thread. This function does not return but the stack is unwound and automatic objects are destroyed, as if - exit() threw an exception (which is, in fact, what it does). The - argument is saved as the result returned by getResult(). If you + exit() threw an exception (which is, in fact, what it does). If you have \c catch(...) blocks then you should add the following before each to avoid catching the exit: \code @@ -98,7 +95,7 @@ public: enabled. If cancellation is disabled then the cancel is remembered but not acted on until the first call to a cancellation point after cancellation is enabled. - + A cancellation point is a function that can act on cancellation. A cancellation point does not return if there's a cancel pending. Instead, it unwinds the stack and destroys automatic objects, as @@ -110,7 +107,7 @@ public: objects (like Lock). Clients are strongly encouraged to do the latter. During cancellation, further cancel() calls are ignored (i.e. a thread cannot be interrupted by a cancel during cancellation). - + Clients that \c catch(XThreadCancel) must always rethrow the exception. Clients that \c catch(...) must either rethrow the exception or include a \c catch(XThreadCancel) handler that @@ -122,7 +119,7 @@ public: /*! Change the priority of the thread. Normal priority is 0, 1 is the next lower, etc. -1 is the next higher, etc. but boosting - the priority may not be permitted and will be silenty ignored. + the priority may not be permitted and will be silently ignored. */ void setPriority(int n); @@ -167,16 +164,6 @@ public: */ bool wait(double timeout = -1.0) const; - //! Get the exit result - /*! - Returns the exit result. This does an implicit wait(). It returns - NULL immediately if called by a thread on itself or on a thread that - was cancelled. - - (cancellation point) - */ - void* getResult() const; - //! Get the thread id /*! Returns an integer id for this thread. This id must not be used to @@ -203,7 +190,7 @@ public: private: Thread(ArchThread); - static void* threadFunc(void*); + static void threadFunc(const std::function& func); private: ArchThread m_thread; diff --git a/src/lib/mt/XMT.cpp b/src/lib/mt/XMT.cpp index 2274a6b..2282d63 100644 --- a/src/lib/mt/XMT.cpp +++ b/src/lib/mt/XMT.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/mt/XMT.h b/src/lib/mt/XMT.h index 9e48fd9..ec4a683 100644 --- a/src/lib/mt/XMT.h +++ b/src/lib/mt/XMT.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/mt/XThread.h b/src/lib/mt/XThread.h index acc32e3..41172b3 100644 --- a/src/lib/mt/XThread.h +++ b/src/lib/mt/XThread.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -26,12 +26,4 @@ Thrown by Thread::exit() to exit a thread. Clients of Thread must not throw this type but must rethrow it if caught (by XThreadExit, XThread, or ...). */ -class XThreadExit : public XThread { -public: - //! \c result is the result of the thread - XThreadExit(void* result) : m_result(result) { } - ~XThreadExit() { } - -public: - void* m_result; -}; +class XThreadExit : public XThread {}; diff --git a/src/lib/net/CMakeLists.txt b/src/lib/net/CMakeLists.txt index 5439450..8c12ed9 100644 --- a/src/lib/net/CMakeLists.txt +++ b/src/lib/net/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/net/ConnectionSecurityLevel.h b/src/lib/net/ConnectionSecurityLevel.h new file mode 100644 index 0000000..913c2fd --- /dev/null +++ b/src/lib/net/ConnectionSecurityLevel.h @@ -0,0 +1,27 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_NET_CONNECTION_SECURITY_LEVEL_H +#define BARRIER_LIB_NET_CONNECTION_SECURITY_LEVEL_H + +enum class ConnectionSecurityLevel { + PLAINTEXT, + ENCRYPTED, + ENCRYPTED_AUTHENTICATED +}; + +#endif // BARRIER_LIB_NET_CONNECTION_SECURITY_LEVEL_H diff --git a/src/lib/net/FingerprintData.cpp b/src/lib/net/FingerprintData.cpp new file mode 100644 index 0000000..460b39b --- /dev/null +++ b/src/lib/net/FingerprintData.cpp @@ -0,0 +1,52 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "base/String.h" +#include "FingerprintDatabase.h" +#include "io/filesystem.h" +#include +#include + +namespace barrier { + +bool FingerprintData::operator==(const FingerprintData& other) const +{ + return algorithm == other.algorithm && data == other.data; +} + +const char* fingerprint_type_to_string(FingerprintType type) +{ + switch (type) { + case FingerprintType::INVALID: return "invalid"; + case FingerprintType::SHA1: return "sha1"; + case FingerprintType::SHA256: return "sha256"; + } + return "invalid"; +} + +FingerprintType fingerprint_type_from_string(const std::string& type) +{ + if (type == "sha1") { + return FingerprintType::SHA1; + } + if (type == "sha256") { + return FingerprintType::SHA256; + } + return FingerprintType::INVALID; +} + +} // namespace barrier diff --git a/src/lib/net/FingerprintData.h b/src/lib/net/FingerprintData.h new file mode 100644 index 0000000..938a695 --- /dev/null +++ b/src/lib/net/FingerprintData.h @@ -0,0 +1,46 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_NET_FINGERPRINT_DATA_H +#define BARRIER_LIB_NET_FINGERPRINT_DATA_H + +#include +#include + +namespace barrier { + +enum FingerprintType { + INVALID, + SHA1, // deprecated + SHA256, +}; + +struct FingerprintData { + std::string algorithm; + std::vector data; + + bool valid() const { return !algorithm.empty(); } + + bool operator==(const FingerprintData& other) const; +}; + +const char* fingerprint_type_to_string(FingerprintType type); +FingerprintType fingerprint_type_from_string(const std::string& type); + +} // namespace barrier + +#endif // BARRIER_LIB_NET_FINGERPRINT_TYPE_H diff --git a/src/lib/net/FingerprintDatabase.cpp b/src/lib/net/FingerprintDatabase.cpp new file mode 100644 index 0000000..def0de9 --- /dev/null +++ b/src/lib/net/FingerprintDatabase.cpp @@ -0,0 +1,135 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "base/String.h" +#include "FingerprintDatabase.h" +#include "io/filesystem.h" +#include +#include + +namespace barrier { + +void FingerprintDatabase::read(const fs::path& path) +{ + std::ifstream file; + open_utf8_path(file, path, std::ios_base::in); + read_stream(file); +} + +void FingerprintDatabase::write(const fs::path& path) +{ + std::ofstream file; + open_utf8_path(file, path, std::ios_base::out); + write_stream(file); +} + +void FingerprintDatabase::read_stream(std::istream& stream) +{ + if (!stream.good()) { + return; + } + + std::string line; + while (std::getline(stream, line)) { + if (line.empty()) { + continue; + } + + auto fingerprint = parse_db_line(line); + if (!fingerprint.valid()) { + continue; + } + + fingerprints_.push_back(fingerprint); + } +} + +void FingerprintDatabase::write_stream(std::ostream& stream) +{ + if (!stream.good()) { + return; + } + + for (const auto& fingerprint : fingerprints_) { + stream << to_db_line(fingerprint) << "\n"; + } +} + +void FingerprintDatabase::clear() +{ + fingerprints_.clear(); +} + +void FingerprintDatabase::add_trusted(const FingerprintData& fingerprint) +{ + if (is_trusted(fingerprint)) { + return; + } + fingerprints_.push_back(fingerprint); +} + +bool FingerprintDatabase::is_trusted(const FingerprintData& fingerprint) +{ + auto found_it = std::find(fingerprints_.begin(), fingerprints_.end(), fingerprint); + return found_it != fingerprints_.end(); +} + +FingerprintData FingerprintDatabase::parse_db_line(const std::string& line) +{ + FingerprintData result; + + // legacy v1 certificate handling + if (std::count(line.begin(), line.end(), ':') == 19 && line.size() == 40 + 19) { + auto data = string::from_hex(line); + if (data.empty()) { + return result; + } + result.algorithm = fingerprint_type_to_string(FingerprintType::SHA1); + result.data = data; + return result; + } + + auto version_end_pos = line.find(':'); + if (version_end_pos == std::string::npos) { + return result; + } + if (line.substr(0, version_end_pos) != "v2") { + return result; + } + auto algo_start_pos = version_end_pos + 1; + auto algo_end_pos = line.find(':', algo_start_pos); + if (algo_end_pos == std::string::npos) { + return result; + } + auto algorithm = line.substr(algo_start_pos, algo_end_pos - algo_start_pos); + auto data = string::from_hex(line.substr(algo_end_pos + 1)); + + if (data.empty()) { + return result; + } + + result.algorithm = algorithm; + result.data = data; + return result; +} + +std::string FingerprintDatabase::to_db_line(const FingerprintData& fingerprint) +{ + return "v2:" + fingerprint.algorithm + ":" + string::to_hex(fingerprint.data, 2); +} + +} // namespace barrier diff --git a/src/lib/net/FingerprintDatabase.h b/src/lib/net/FingerprintDatabase.h new file mode 100644 index 0000000..4927265 --- /dev/null +++ b/src/lib/net/FingerprintDatabase.h @@ -0,0 +1,53 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_NET_FINGERPRINT_DATABASE_H +#define BARRIER_LIB_NET_FINGERPRINT_DATABASE_H + +#include "FingerprintData.h" +#include "io/filesystem.h" +#include +#include +#include + +namespace barrier { + +class FingerprintDatabase { +public: + void read(const fs::path& path); + void write(const fs::path& path); + + void read_stream(std::istream& stream); + void write_stream(std::ostream& stream); + + void clear(); + void add_trusted(const FingerprintData& fingerprint); + bool is_trusted(const FingerprintData& fingerprint); + + const std::vector& fingerprints() const { return fingerprints_; } + + static FingerprintData parse_db_line(const std::string& line); + static std::string to_db_line(const FingerprintData& fingerprint); + +private: + + std::vector fingerprints_; +}; + +} // namespace barrier + +#endif // BARRIER_LIB_NET_FINGERPRINT_DATABASE_H diff --git a/src/lib/net/IDataSocket.cpp b/src/lib/net/IDataSocket.cpp index cc679c3..30e00f4 100644 --- a/src/lib/net/IDataSocket.cpp +++ b/src/lib/net/IDataSocket.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/net/IDataSocket.h b/src/lib/net/IDataSocket.h index c77a99c..0fc41bb 100644 --- a/src/lib/net/IDataSocket.h +++ b/src/lib/net/IDataSocket.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/net/IListenSocket.h b/src/lib/net/IListenSocket.h index 73dcc6e..5452a37 100644 --- a/src/lib/net/IListenSocket.h +++ b/src/lib/net/IListenSocket.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -41,7 +41,7 @@ public: */ virtual IDataSocket* accept() = 0; - + //@} // ISocket overrides diff --git a/src/lib/net/ISocket.h b/src/lib/net/ISocket.h index 0e9688b..f41fd55 100644 --- a/src/lib/net/ISocket.h +++ b/src/lib/net/ISocket.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/net/ISocketFactory.h b/src/lib/net/ISocketFactory.h index e440953..edfc8c9 100644 --- a/src/lib/net/ISocketFactory.h +++ b/src/lib/net/ISocketFactory.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -20,6 +20,7 @@ #include "common/IInterface.h" #include "arch/IArchNetwork.h" +#include "net/ConnectionSecurityLevel.h" class IDataSocket; class IListenSocket; @@ -35,14 +36,12 @@ public: //@{ //! Create data socket - virtual IDataSocket* create( - IArchNetwork::EAddressFamily family, - bool secure) const = 0; + virtual IDataSocket* create(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const = 0; //! Create listen socket - virtual IListenSocket* createListen( - IArchNetwork::EAddressFamily family, - bool secure) const = 0; + virtual IListenSocket* createListen(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const = 0; //@} }; diff --git a/src/lib/net/ISocketMultiplexerJob.h b/src/lib/net/ISocketMultiplexerJob.h index c27fce2..2dab87e 100644 --- a/src/lib/net/ISocketMultiplexerJob.h +++ b/src/lib/net/ISocketMultiplexerJob.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/net/NetworkAddress.cpp b/src/lib/net/NetworkAddress.cpp index c8ea9c6..3526fc0 100644 --- a/src/lib/net/NetworkAddress.cpp +++ b/src/lib/net/NetworkAddress.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/net/NetworkAddress.h b/src/lib/net/NetworkAddress.h index 87dc1e4..3a006af 100644 --- a/src/lib/net/NetworkAddress.h +++ b/src/lib/net/NetworkAddress.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -43,7 +43,7 @@ public: If \c hostname can be parsed as a numerical address then that's how it's used, otherwise it's used as a host name. If \c hostname ends in ":[0-9]+" then that suffix is extracted and used as the port, - overridding the port parameter. The resulting port must be a valid + overriding the port parameter. The resulting port must be a valid port number (zero is not a valid port number) otherwise \c XSocketAddress is thrown with an error of \c XSocketAddress::kBadPort. The hostname is not resolved by the c'tor; use \c resolve to do that. diff --git a/src/lib/net/SecureListenSocket.cpp b/src/lib/net/SecureListenSocket.cpp index a137f39..11efc5c 100644 --- a/src/lib/net/SecureListenSocket.cpp +++ b/src/lib/net/SecureListenSocket.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -20,23 +20,16 @@ #include "SecureSocket.h" #include "net/NetworkAddress.h" #include "net/SocketMultiplexer.h" -#include "net/TSocketMultiplexerMethodJob.h" +#include "arch/Arch.h" #include "arch/XArch.h" #include "common/DataDirectories.h" #include "base/String.h" -static const char s_certificateDir[] = { "SSL" }; -static const char s_certificateFilename[] = { "Barrier.pem" }; - -// -// SecureListenSocket -// - -SecureListenSocket::SecureListenSocket( - IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - IArchNetwork::EAddressFamily family) : - TCPListenSocket(events, socketMultiplexer, family) +SecureListenSocket::SecureListenSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) : + TCPListenSocket(events, socketMultiplexer, family), + security_level_{security_level} { } @@ -45,22 +38,15 @@ SecureListenSocket::accept() { SecureSocket* socket = NULL; try { - socket = new SecureSocket( - m_events, - m_socketMultiplexer, - ARCH->acceptSocket(m_socket, NULL)); + socket = new SecureSocket(m_events, m_socketMultiplexer, + ARCH->acceptSocket(m_socket, NULL), security_level_); socket->initSsl(true); if (socket != NULL) { setListeningJob(); } - std::string certificateFilename = barrier::string::sprintf("%s/%s/%s", - DataDirectories::profile().c_str(), - s_certificateDir, - s_certificateFilename); - - bool loaded = socket->loadCertificates(certificateFilename); + bool loaded = socket->load_certificates(barrier::DataDirectories::ssl_certificate_path()); if (!loaded) { delete socket; return NULL; diff --git a/src/lib/net/SecureListenSocket.h b/src/lib/net/SecureListenSocket.h index d0c6e23..a0e792a 100644 --- a/src/lib/net/SecureListenSocket.h +++ b/src/lib/net/SecureListenSocket.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -19,18 +19,21 @@ #include "net/TCPListenSocket.h" #include "common/stdset.h" +#include "ConnectionSecurityLevel.h" class IEventQueue; class SocketMultiplexer; class IDataSocket; -class SecureListenSocket : public TCPListenSocket{ +class SecureListenSocket : public TCPListenSocket { public: - SecureListenSocket(IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - IArchNetwork::EAddressFamily family); + SecureListenSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level); // IListenSocket overrides virtual IDataSocket* accept(); +private: + ConnectionSecurityLevel security_level_; }; diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index 855e16b..85e1d38 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -16,6 +16,7 @@ */ #include "SecureSocket.h" +#include "SecureUtils.h" #include "net/TSocketMultiplexerMethodJob.h" #include "base/TMethodEventJob.h" @@ -25,6 +26,8 @@ #include "base/Log.h" #include "base/String.h" #include "common/DataDirectories.h" +#include "io/filesystem.h" +#include "net/FingerprintDatabase.h" #include #include @@ -40,41 +43,36 @@ #define MAX_ERROR_SIZE 65535 +static const std::size_t MAX_INPUT_BUFFER_SIZE = 1024 * 1024; static const float s_retryDelay = 0.01f; enum { kMsgSize = 128 }; -static const char kFingerprintDirName[] = "SSL/Fingerprints"; -//static const char kFingerprintLocalFilename[] = "Local.txt"; -static const char kFingerprintTrustedServersFilename[] = "TrustedServers.txt"; -//static const char kFingerprintTrustedClientsFilename[] = "TrustedClients.txt"; - struct Ssl { SSL_CTX* m_context; SSL* m_ssl; }; -SecureSocket::SecureSocket( - IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - IArchNetwork::EAddressFamily family) : +SecureSocket::SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) : TCPSocket(events, socketMultiplexer, family), m_ssl(nullptr), m_secureReady(false), - m_fatal(false) + m_fatal(false), + security_level_{security_level} { } -SecureSocket::SecureSocket( - IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - ArchSocket socket) : +SecureSocket::SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + ArchSocket socket, ConnectionSecurityLevel security_level) : TCPSocket(events, socketMultiplexer, socket), m_ssl(nullptr), m_secureReady(false), - m_fatal(false) + m_fatal(false), + security_level_{security_level} { } @@ -103,6 +101,8 @@ SecureSocket::close() void SecureSocket::freeSSLResources() { + std::lock_guard ssl_lock{ssl_mutex_}; + if (m_ssl->m_ssl != NULL) { SSL_shutdown(m_ssl->m_ssl); SSL_free(m_ssl->m_ssl); @@ -133,30 +133,30 @@ std::unique_ptr SecureSocket::newJob() if (m_connected && !m_secureReady) { return {}; } - + return TCPSocket::newJob(); } void SecureSocket::secureConnect() { - setJob(std::make_unique>( - this, &SecureSocket::serviceConnect, - getSocket(), isReadable(), isWritable())); + setJob(std::make_unique([this](auto j, auto r, auto w, auto e) + { return serviceConnect(j, r, w, e); }, + getSocket(), isReadable(), isWritable())); } void SecureSocket::secureAccept() { - setJob(std::make_unique>( - this, &SecureSocket::serviceAccept, - getSocket(), isReadable(), isWritable())); + setJob(std::make_unique([this](auto j, auto r, auto w, auto e) + { return serviceAccept(j, r, w, e); }, + getSocket(), isReadable(), isWritable())); } TCPSocket::EJobResult SecureSocket::doRead() { - static UInt8 buffer[4096]; + UInt8 buffer[4096]; memset(buffer, 0, sizeof(buffer)); int bytesRead = 0; int status = 0; @@ -173,20 +173,24 @@ SecureSocket::doRead() else { return kRetry; } - + if (bytesRead > 0) { bool wasEmpty = (m_inputBuffer.getSize() == 0); - + // slurp up as much as possible do { m_inputBuffer.write(buffer, bytesRead); - + + if (m_inputBuffer.getSize() > MAX_INPUT_BUFFER_SIZE) { + break; + } + status = secureRead(buffer, sizeof(buffer), bytesRead); if (status < 0) { return kBreak; } } while (bytesRead > 0 || status > 0); - + // send input ready if input buffer was empty if (wasEmpty) { sendEvent(m_events->forIStream().inputReady()); @@ -204,18 +208,13 @@ SecureSocket::doRead() m_readable = false; return kNew; } - + return kRetry; } TCPSocket::EJobResult SecureSocket::doWrite() { - static bool s_retry = false; - static int s_retrySize = 0; - static std::unique_ptr s_staticBuffer; - static std::size_t s_staticBufferSize = 0; - // write data int bufferSize = 0; int bytesWrote = 0; @@ -224,34 +223,34 @@ SecureSocket::doWrite() if (!isSecureReady()) return kRetry; - if (s_retry) { - bufferSize = s_retrySize; + if (do_write_retry_) { + bufferSize = do_write_retry_size_; } else { bufferSize = m_outputBuffer.getSize(); - if (bufferSize > s_staticBufferSize) { - s_staticBuffer.reset(new char[bufferSize]); - s_staticBufferSize = bufferSize; + if (bufferSize > do_write_retry_buffer_size_) { + do_write_retry_buffer_.reset(new char[bufferSize]); + do_write_retry_buffer_size_ = bufferSize; } if (bufferSize > 0) { - memcpy(s_staticBuffer.get(), m_outputBuffer.peek(bufferSize), bufferSize); + std::memcpy(do_write_retry_buffer_.get(), m_outputBuffer.peek(bufferSize), bufferSize); } } - + if (bufferSize == 0) { return kRetry; } - status = secureWrite(s_staticBuffer.get(), bufferSize, bytesWrote); + status = secureWrite(do_write_retry_buffer_.get(), bufferSize, bytesWrote); if (status > 0) { - s_retry = false; + do_write_retry_ = false; } else if (status < 0) { return kBreak; } else if (status == 0) { - s_retry = true; - s_retrySize = bufferSize; + do_write_retry_ = true; + do_write_retry_size_ = bufferSize; return kNew; } - + if (bytesWrote > 0) { discardWrittenData(bytesWrote); return kNew; @@ -263,16 +262,16 @@ SecureSocket::doWrite() int SecureSocket::secureRead(void* buffer, int size, int& read) { + std::lock_guard ssl_lock{ssl_mutex_}; + if (m_ssl->m_ssl != NULL) { LOG((CLOG_DEBUG2 "reading secure socket")); read = SSL_read(m_ssl->m_ssl, buffer, size); - - static int retry; // Check result will cleanup the connection in the case of a fatal - checkResult(read, retry); - - if (retry) { + checkResult(read, secure_read_retry_); + + if (secure_read_retry_) { return 0; } @@ -289,17 +288,17 @@ SecureSocket::secureRead(void* buffer, int size, int& read) int SecureSocket::secureWrite(const void* buffer, int size, int& wrote) { + std::lock_guard ssl_lock{ssl_mutex_}; + if (m_ssl->m_ssl != NULL) { LOG((CLOG_DEBUG2 "writing secure socket:%p", this)); wrote = SSL_write(m_ssl->m_ssl, buffer, size); - - static int retry; // Check result will cleanup the connection in the case of a fatal - checkResult(wrote, retry); + checkResult(wrote, secure_write_retry_); - if (retry) { + if (secure_write_retry_) { return 0; } @@ -322,6 +321,8 @@ SecureSocket::isSecureReady() void SecureSocket::initSsl(bool server) { + std::lock_guard ssl_lock{ssl_mutex_}; + m_ssl = new Ssl(); m_ssl->m_context = NULL; m_ssl->m_ssl = NULL; @@ -329,54 +330,57 @@ SecureSocket::initSsl(bool server) initContext(server); } -bool SecureSocket::loadCertificates(std::string& filename) +bool SecureSocket::load_certificates(const barrier::fs::path& path) { - if (filename.empty()) { + std::lock_guard ssl_lock{ssl_mutex_}; + + if (path.empty()) { showError("ssl certificate is not specified"); return false; } else { - std::ifstream file(filename.c_str()); - bool exist = file.good(); - file.close(); - - if (!exist) { - std::string errorMsg("ssl certificate doesn't exist: "); - errorMsg.append(filename); - showError(errorMsg.c_str()); + if (!barrier::fs::is_regular_file(path)) { + showError("ssl certificate doesn't exist: " + path.u8string()); return false; } } int r = 0; - r = SSL_CTX_use_certificate_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM); + r = SSL_CTX_use_certificate_file(m_ssl->m_context, path.u8string().c_str(), SSL_FILETYPE_PEM); if (r <= 0) { - showError("could not use ssl certificate"); + showError("could not use ssl certificate: " + path.u8string()); return false; } - r = SSL_CTX_use_PrivateKey_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM); + r = SSL_CTX_use_PrivateKey_file(m_ssl->m_context, path.u8string().c_str(), SSL_FILETYPE_PEM); if (r <= 0) { - showError("could not use ssl private key"); + showError("could not use ssl private key: " + path.u8string()); return false; } r = SSL_CTX_check_private_key(m_ssl->m_context); if (!r) { - showError("could not verify ssl private key"); + showError("could not verify ssl private key: " + path.u8string()); return false; } return true; } +static int cert_verify_ignore_callback(X509_STORE_CTX*, void*) +{ + return 1; +} + void SecureSocket::initContext(bool server) { + // ssl_mutex_ is assumed to be acquired + SSL_library_init(); const SSL_METHOD* method; - + // load & register all cryptos, etc. OpenSSL_add_all_algorithms(); @@ -394,7 +398,7 @@ SecureSocket::initContext(bool server) else { method = SSLv23_client_method(); } - + // create new context from method SSL_METHOD* m = const_cast(method); m_ssl->m_context = SSL_CTX_new(m); @@ -403,13 +407,23 @@ SecureSocket::initContext(bool server) SSL_CTX_set_options(m_ssl->m_context, SSL_OP_NO_SSLv3); if (m_ssl->m_context == NULL) { - showError(); + showError(""); + } + + if (security_level_ == ConnectionSecurityLevel::ENCRYPTED_AUTHENTICATED) { + // We want to ask for peer certificate, but not verify it. If we don't ask for peer + // certificate, e.g. client won't send it. + SSL_CTX_set_verify(m_ssl->m_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + nullptr); + SSL_CTX_set_cert_verify_callback(m_ssl->m_context, cert_verify_ignore_callback, nullptr); } } void SecureSocket::createSSL() { + // ssl_mutex_ is assumed to be acquired + // I assume just one instance is needed // get new SSL state with context if (m_ssl->m_ssl == NULL) { @@ -421,17 +435,17 @@ SecureSocket::createSSL() int SecureSocket::secureAccept(int socket) { + std::lock_guard ssl_lock{ssl_mutex_}; + createSSL(); // set connection socket to SSL state SSL_set_fd(m_ssl->m_ssl, socket); - + LOG((CLOG_DEBUG2 "accepting secure socket")); int r = SSL_accept(m_ssl->m_ssl); - - static int retry; - checkResult(r, retry); + checkResult(r, secure_accept_retry_); if (isFatal()) { // tell user and sleep so the socket isn't hammered. @@ -439,12 +453,30 @@ SecureSocket::secureAccept(int socket) LOG((CLOG_INFO "client connection may not be secure")); m_secureReady = false; ARCH->sleep(1); - retry = 0; + secure_accept_retry_ = 0; return -1; // Failed, error out } // If not fatal and no retry, state is good - if (retry == 0) { + if (secure_accept_retry_ == 0) { + if (security_level_ == ConnectionSecurityLevel::ENCRYPTED_AUTHENTICATED) { + if (verify_cert_fingerprint( + barrier::DataDirectories::trusted_clients_ssl_fingerprints_path())) { + LOG((CLOG_INFO "accepted secure socket")); + if (!ensure_peer_certificate()) { + secure_accept_retry_ = 0; + disconnect(); + return -1;// Cert fail, error + } + } + else { + LOG((CLOG_ERR "failed to verify server certificate fingerprint")); + secure_accept_retry_ = 0; + disconnect(); + return -1; // Fingerprint failed, error + } + } + m_secureReady = true; LOG((CLOG_INFO "accepted secure socket")); if (CLOG->getFilter() >= kDEBUG1) { @@ -455,7 +487,7 @@ SecureSocket::secureAccept(int socket) } // If not fatal and retry is set, not ready, and return retry - if (retry > 0) { + if (secure_accept_retry_ > 0) { LOG((CLOG_DEBUG2 "retry accepting secure socket")); m_secureReady = false; ARCH->sleep(s_retryDelay); @@ -470,38 +502,45 @@ SecureSocket::secureAccept(int socket) int SecureSocket::secureConnect(int socket) { + // note that load_certificates acquires ssl_mutex_ + if (!load_certificates(barrier::DataDirectories::ssl_certificate_path())) { + LOG((CLOG_ERR "could not load client certificates")); + // FIXME: this is fatal error, but we current don't disconnect because whole logic in this + // function needs to be cleaned up + } + + std::lock_guard ssl_lock{ssl_mutex_}; + createSSL(); // attach the socket descriptor SSL_set_fd(m_ssl->m_ssl, socket); - + LOG((CLOG_DEBUG2 "connecting secure socket")); int r = SSL_connect(m_ssl->m_ssl); - - static int retry; - checkResult(r, retry); + checkResult(r, secure_connect_retry_); if (isFatal()) { LOG((CLOG_ERR "failed to connect secure socket")); - retry = 0; + secure_connect_retry_ = 0; return -1; } // If we should retry, not ready and return 0 - if (retry > 0) { + if (secure_connect_retry_ > 0) { LOG((CLOG_DEBUG2 "retry connect secure socket")); m_secureReady = false; ARCH->sleep(s_retryDelay); return 0; } - retry = 0; + secure_connect_retry_ = 0; // No error, set ready, process and return ok m_secureReady = true; - if (verifyCertFingerprint()) { + if (verify_cert_fingerprint(barrier::DataDirectories::trusted_servers_ssl_fingerprints_path())) { LOG((CLOG_INFO "connected to secure socket")); - if (!showCertificate()) { + if (!ensure_peer_certificate()) { disconnect(); return -1;// Cert fail, error } @@ -520,21 +559,22 @@ SecureSocket::secureConnect(int socket) } bool -SecureSocket::showCertificate() +SecureSocket::ensure_peer_certificate() { + // ssl_mutex_ is assumed to be acquired X509* cert; char* line; - + // get the server's certificate cert = SSL_get_peer_certificate(m_ssl->m_ssl); if (cert != NULL) { line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); - LOG((CLOG_INFO "server ssl certificate info: %s", line)); + LOG((CLOG_INFO "peer ssl certificate info: %s", line)); OPENSSL_free(line); X509_free(cert); } else { - showError("server has no ssl certificate"); + showError("peer has no ssl certificate"); return false; } @@ -544,6 +584,8 @@ SecureSocket::showCertificate() void SecureSocket::checkResult(int status, int& retry) { + // ssl_mutex_ is assumed to be acquired + // ssl errors are a little quirky. the "want" errors are normal and // should result in a retry. @@ -568,7 +610,7 @@ SecureSocket::checkResult(int status, int& retry) case SSL_ERROR_WANT_WRITE: // Need to make sure the socket is known to be writable so the impending - // select action actually triggers on a write. This isn't necessary for + // select action actually triggers on a write. This isn't necessary for // m_readable because the socket logic is always readable m_writable = true; retry++; @@ -618,16 +660,15 @@ SecureSocket::checkResult(int status, int& retry) if (isFatal()) { retry = 0; - showError(); + showError(""); disconnect(); } } -void -SecureSocket::showError(const char* reason) +void SecureSocket::showError(const std::string& reason) { - if (reason != NULL) { - LOG((CLOG_ERR "%s", reason)); + if (!reason.empty()) { + LOG((CLOG_ERR "%s", reason.c_str())); } std::string error = getError(); @@ -658,83 +699,49 @@ SecureSocket::disconnect() sendEvent(getEvents()->forIStream().inputShutdown()); } -void SecureSocket::formatFingerprint(std::string& fingerprint, bool hex, bool separator) +bool SecureSocket::verify_cert_fingerprint(const barrier::fs::path& fingerprint_db_path) { - if (hex) { - // to hexidecimal - barrier::string::toHex(fingerprint, 2); - } - - // all uppercase - barrier::string::uppercase(fingerprint); + // ssl_mutex_ is assumed to be acquired - if (separator) { - // add colon to separate each 2 charactors - size_t separators = fingerprint.size() / 2; - for (size_t i = 1; i < separators; i++) { - fingerprint.insert(i * 3 - 1, ":"); - } - } -} - -bool -SecureSocket::verifyCertFingerprint() -{ // calculate received certificate fingerprint - X509 *cert = cert = SSL_get_peer_certificate(m_ssl->m_ssl); - EVP_MD* tempDigest; - unsigned char tempFingerprint[EVP_MAX_MD_SIZE]; - unsigned int tempFingerprintLen; - tempDigest = (EVP_MD*)EVP_sha1(); - int digestResult = X509_digest(cert, tempDigest, tempFingerprint, &tempFingerprintLen); - - if (digestResult <= 0) { - LOG((CLOG_ERR "failed to calculate fingerprint, digest result: %d", digestResult)); + barrier::FingerprintData fingerprint_sha1, fingerprint_sha256; + try { + auto* cert = SSL_get_peer_certificate(m_ssl->m_ssl); + fingerprint_sha1 = barrier::get_ssl_cert_fingerprint(cert, + barrier::FingerprintType::SHA1); + fingerprint_sha256 = barrier::get_ssl_cert_fingerprint(cert, + barrier::FingerprintType::SHA256); + } catch (const std::exception& e) { + LOG((CLOG_ERR "%s", e.what())); return false; } - // format fingerprint into hexdecimal format with colon separator - std::string fingerprint(reinterpret_cast(tempFingerprint), tempFingerprintLen); - formatFingerprint(fingerprint); - LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); - - std::string trustedServersFilename; - trustedServersFilename = barrier::string::sprintf( - "%s/%s/%s", - DataDirectories::profile().c_str(), - kFingerprintDirName, - kFingerprintTrustedServersFilename); + // note: the GUI parses the following two lines of logs, don't change unnecessarily + LOG((CLOG_NOTE "peer fingerprint (SHA1): %s (SHA256): %s", + barrier::format_ssl_fingerprint(fingerprint_sha1.data).c_str(), + barrier::format_ssl_fingerprint(fingerprint_sha256.data).c_str())); // Provide debug hint as to what file is being used to verify fingerprint trust - LOG((CLOG_NOTE "trustedServersFilename: %s", trustedServersFilename.c_str() )); + LOG((CLOG_NOTE "fingerprint_db_path: %s", fingerprint_db_path.u8string().c_str())); - // check if this fingerprint exist - std::string fileLine; - std::ifstream file; - file.open(trustedServersFilename.c_str()); + barrier::FingerprintDatabase db; + db.read(fingerprint_db_path); - if (!file.is_open()) { - LOG((CLOG_NOTE "Unable to open trustedServersFile: %s", trustedServersFilename.c_str() )); + if (!db.fingerprints().empty()) { + LOG((CLOG_NOTE "Read %d fingerprints from: %s", db.fingerprints().size(), + fingerprint_db_path.u8string().c_str())); } else { - LOG((CLOG_NOTE "Opened trustedServersFilename: %s", trustedServersFilename.c_str() )); + LOG((CLOG_NOTE "Could not read fingerprints from: %s", + fingerprint_db_path.u8string().c_str())); } - bool isValid = false; - while (!file.eof() && file.is_open()) { - getline(file,fileLine); - if (!fileLine.empty()) { - if (fileLine.compare(fingerprint) == 0) { - LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint")); - isValid = true; - break; - } else { - LOG((CLOG_NOTE "Fingerprint does not match trusted fingerprint")); - } - } + if (db.is_trusted(fingerprint_sha256)) { + LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint")); + return true; + } else { + LOG((CLOG_NOTE "Fingerprint does not match trusted fingerprint")); + return false; } - - file.close(); - return isValid; } MultiplexerJobStatus SecureSocket::serviceConnect(ISocketMultiplexerJob* job, @@ -765,9 +772,9 @@ MultiplexerJobStatus SecureSocket::serviceConnect(ISocketMultiplexerJob* job, // Retry case return { true, - std::make_unique>( - this, &SecureSocket::serviceConnect, - getSocket(), isReadable(), isWritable()) + std::make_unique([this](auto j, auto r, auto w, auto e) + { return serviceConnect(j, r, w, e); }, + getSocket(), isReadable(), isWritable()) }; } @@ -795,9 +802,12 @@ MultiplexerJobStatus SecureSocket::serviceAccept(ISocketMultiplexerJob* job, } // Retry case - return {true, std::make_unique>( - this, &SecureSocket::serviceAccept, - getSocket(), isReadable(), isWritable())}; + return { + true, + std::make_unique([this](auto j, auto r, auto w, auto e) + { return serviceAccept(j, r, w, e); }, + getSocket(), isReadable(), isWritable()) + }; } void @@ -822,6 +832,8 @@ showCipherStackDesc(STACK_OF(SSL_CIPHER) * stack) { void SecureSocket::showSecureCipherInfo() { + // ssl_mutex_ is assumed to be acquired + STACK_OF(SSL_CIPHER) * sStack = SSL_get_ciphers(m_ssl->m_ssl); if (sStack == NULL) { @@ -832,8 +844,8 @@ SecureSocket::showSecureCipherInfo() showCipherStackDesc(sStack); } -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - // m_ssl->m_ssl->session->ciphers is not forward compatable, +#if OPENSSL_VERSION_NUMBER < 0x10100000L + // m_ssl->m_ssl->session->ciphers is not forward compatible, // In future release of OpenSSL, it's not visible, STACK_OF(SSL_CIPHER) * cStack = m_ssl->m_ssl->session->ciphers; #else @@ -864,6 +876,8 @@ SecureSocket::showSecureLibInfo() void SecureSocket::showSecureConnectInfo() { + // ssl_mutex_ is assumed to be acquired + const SSL_CIPHER* cipher = SSL_get_current_cipher(m_ssl->m_ssl); if (cipher != NULL) { diff --git a/src/lib/net/SecureSocket.h b/src/lib/net/SecureSocket.h index c602e2d..be7dc0d 100644 --- a/src/lib/net/SecureSocket.h +++ b/src/lib/net/SecureSocket.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -17,8 +17,11 @@ #pragma once +#include "ConnectionSecurityLevel.h" #include "net/TCPSocket.h" #include "net/XSocket.h" +#include "io/filesystem.h" +#include class IEventQueue; class SocketMultiplexer; @@ -32,10 +35,10 @@ A secure socket using SSL. */ class SecureSocket : public TCPSocket { public: - SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family); - SecureSocket(IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - ArchSocket socket); + SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family, ConnectionSecurityLevel security_level); + SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + ArchSocket socket, ConnectionSecurityLevel security_level); ~SecureSocket(); // ISocket overrides @@ -43,7 +46,7 @@ public: // IDataSocket overrides virtual void connect(const NetworkAddress&) override; - + std::unique_ptr newJob() override; bool isFatal() const override { return m_fatal; } void isFatal(bool b) { m_fatal = b; } @@ -55,35 +58,56 @@ public: EJobResult doRead() override; EJobResult doWrite() override; void initSsl(bool server); - bool loadCertificates(std::string& CertFile); + bool load_certificates(const barrier::fs::path& path); private: // SSL - void initContext(bool server); - void createSSL(); + void initContext(bool server); // may only be called with ssl_mutex_ acquired + void createSSL(); // may only be called with ssl_mutex_ acquired. int secureAccept(int s); int secureConnect(int s); - bool showCertificate(); - void checkResult(int n, int& retry); - void showError(const char* reason = NULL); + bool ensure_peer_certificate(); // may only be called with ssl_mutex_ acquired + + void checkResult(int n, int& retry); // may only be called with m_ssl_mutex_ acquired. + + void showError(const std::string& reason); std::string getError(); void disconnect(); - void formatFingerprint(std::string& fingerprint, bool hex = true, bool separator = true); - bool verifyCertFingerprint(); + + // may only be called with ssl_mutex_ acquired + bool verify_cert_fingerprint(const barrier::fs::path& fingerprint_db_path); MultiplexerJobStatus serviceConnect(ISocketMultiplexerJob*, bool, bool, bool); MultiplexerJobStatus serviceAccept(ISocketMultiplexerJob*, bool, bool, bool); - void showSecureConnectInfo(); - void showSecureLibInfo(); - void showSecureCipherInfo(); - + void showSecureConnectInfo(); // may only be called with ssl_mutex_ acquired + void showSecureLibInfo(); + void showSecureCipherInfo(); // may only be called with ssl_mutex_ acquired + void handleTCPConnected(const Event& event, void*); void freeSSLResources(); private: + // all accesses to m_ssl must be protected by this mutex. The only function that is called + // from outside SocketMultiplexer thread is close(), so we mostly care about things accessed + // by it. + std::mutex ssl_mutex_; + Ssl* m_ssl; bool m_secureReady; bool m_fatal; + ConnectionSecurityLevel security_level_ = ConnectionSecurityLevel::ENCRYPTED; + + int secure_accept_retry_ = 0; // used only in secureAccept() + int secure_connect_retry_ = 0; // used only in secureConnect() + int secure_read_retry_ = 0; // used only in secureRead() + int secure_write_retry_ = 0; // used only in secureWrite() + + // The following are used only from doWrite() + // FIXME: using std::vector would simplify logic significantly. + bool do_write_retry_ = false; + int do_write_retry_size_ = 0; + std::unique_ptr do_write_retry_buffer_; + std::size_t do_write_retry_buffer_size_ = 0; }; diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp new file mode 100644 index 0000000..c581dd4 --- /dev/null +++ b/src/lib/net/SecureUtils.cpp @@ -0,0 +1,312 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + ----------------------------------------------------------------------- + create_fingerprint_randomart() has been taken from the OpenSSH project. + Copyright information follows. + + Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + Copyright (c) 2008 Alexander von Gernler. All rights reserved. + Copyright (c) 2010,2011 Damien Miller. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "SecureUtils.h" +#include "base/String.h" +#include "base/finally.h" +#include "io/filesystem.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#if SYSAPI_WIN32 +// Windows builds require a shim that makes it possible to link to different +// versions of the Win32 C runtime. See OpenSSL FAQ. +#include +#endif + +namespace barrier { + +namespace { + +const EVP_MD* get_digest_for_type(FingerprintType type) +{ + switch (type) { + case FingerprintType::SHA1: return EVP_sha1(); + case FingerprintType::SHA256: return EVP_sha256(); + } + throw std::runtime_error("Unknown fingerprint type " + std::to_string(static_cast(type))); +} + +} // namespace + +std::string format_ssl_fingerprint(const std::vector& fingerprint, bool separator) +{ + std::string result = barrier::string::to_hex(fingerprint, 2); + + // all uppercase + barrier::string::uppercase(result); + + if (separator) { + // add colon to separate each 2 characters + size_t separators = result.size() / 2; + for (size_t i = 1; i < separators; i++) { + result.insert(i * 3 - 1, ":"); + } + } + return result; +} + +std::string format_ssl_fingerprint_columns(const std::vector& fingerprint) +{ + auto max_columns = 8; + + std::string hex = barrier::string::to_hex(fingerprint, 2); + barrier::string::uppercase(hex); + if (hex.empty() || hex.size() % 2 != 0) { + return hex; + } + + std::string separated; + for (std::size_t i = 0; i < hex.size(); i += max_columns * 2) { + for (std::size_t j = i; j < i + 16 && j < hex.size() - 1; j += 2) { + separated.push_back(hex[j]); + separated.push_back(hex[j + 1]); + separated.push_back(':'); + } + separated.push_back('\n'); + } + separated.pop_back(); // we don't need last newline character + return separated; +} + +FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type) +{ + if (!cert) { + throw std::runtime_error("certificate is null"); + } + + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int digest_length = 0; + int result = X509_digest(cert, get_digest_for_type(type), digest, &digest_length); + + if (result <= 0) { + throw std::runtime_error("failed to calculate fingerprint, digest result: " + + std::to_string(result)); + } + + std::vector digest_vec; + digest_vec.assign(reinterpret_cast(digest), + reinterpret_cast(digest) + digest_length); + return {fingerprint_type_to_string(type), digest_vec}; +} + +FingerprintData get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type) +{ + auto fp = fopen_utf8_path(path, "r"); + if (!fp) { + throw std::runtime_error("Could not open certificate path"); + } + auto file_close = finally([fp]() { std::fclose(fp); }); + + X509* cert = PEM_read_X509(fp, nullptr, nullptr, nullptr); + if (!cert) { + throw std::runtime_error("Certificate could not be parsed"); + } + auto cert_free = finally([cert]() { X509_free(cert); }); + + return get_ssl_cert_fingerprint(cert, type); +} + +void generate_pem_self_signed_cert(const std::string& path) +{ + auto expiration_days = 365; + + auto* private_key = EVP_PKEY_new(); + if (!private_key) { + throw std::runtime_error("Could not allocate private key for certificate"); + } + auto private_key_free = finally([private_key](){ EVP_PKEY_free(private_key); }); + + auto* rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr); + if (!rsa) { + throw std::runtime_error("Failed to generate RSA key"); + } + EVP_PKEY_assign_RSA(private_key, rsa); + + auto* cert = X509_new(); + if (!cert) { + throw std::runtime_error("Could not allocate certificate"); + } + auto cert_free = finally([cert]() { X509_free(cert); }); + + ASN1_INTEGER_set(X509_get_serialNumber(cert), 1); + X509_gmtime_adj(X509_get_notBefore(cert), 0); + X509_gmtime_adj(X509_get_notAfter(cert), expiration_days * 24 * 3600); + X509_set_pubkey(cert, private_key); + + auto* name = X509_get_subject_name(cert); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + reinterpret_cast("Barrier"), -1, -1, 0); + X509_set_issuer_name(cert, name); + + X509_sign(cert, private_key, EVP_sha256()); + + auto fp = fopen_utf8_path(path.c_str(), "r"); + if (!fp) { + throw std::runtime_error("Could not open certificate output path"); + } + auto file_close = finally([fp]() { std::fclose(fp); }); + + PEM_write_PrivateKey(fp, private_key, nullptr, nullptr, 0, nullptr, nullptr); + PEM_write_X509(fp, cert); +} + +/* + Draw an ASCII-Art representing the fingerprint so human brain can + profit from its built-in pattern recognition ability. + This technique is called "random art" and can be found in some + scientific publications like this original paper: + + "Hash Visualization: a New Technique to improve Real-World Security", + Perrig A. and Song D., 1999, International Workshop on Cryptographic + Techniques and E-Commerce (CrypTEC '99) + sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf + + The subject came up in a talk by Dan Kaminsky, too. + + If you see the picture is different, the key is different. + If the picture looks the same, you still know nothing. + + The algorithm used here is a worm crawling over a discrete plane, + leaving a trace (augmenting the field) everywhere it goes. + Movement is taken from dgst_raw 2bit-wise. Bumping into walls + makes the respective movement vector be ignored for this turn. + Graphs are not unambiguous, because circles in graphs can be +walked in either direction. + */ + +/* + Field sizes for the random art. Have to be odd, so the starting point + can be in the exact middle of the picture, and FLDBASE should be >=8 . + Else pictures would be too dense, and drawing the frame would + fail, too, because the key type would not fit in anymore. +*/ +#define FLDBASE 8 +#define FLDSIZE_Y (FLDBASE + 1) +#define FLDSIZE_X (FLDBASE * 2 + 1) + +std::string create_fingerprint_randomart(const std::vector& dgst_raw) +{ + /* + * Chars to be used after each other every time the worm + * intersects with itself. Matter of taste. + */ + const char* augmentation_string = " .o+=*BOX@%&#/^SE"; + char *p; + std::uint8_t field[FLDSIZE_X][FLDSIZE_Y]; + std::size_t i; + std::uint32_t b; + int x, y; + std::size_t len = strlen(augmentation_string) - 1; + + std::vector retval; + retval.reserve((FLDSIZE_X + 3) * (FLDSIZE_Y + 2)); + + auto add_char = [&retval](char ch) { retval.push_back(ch); }; + + /* initialize field */ + std::memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); + x = FLDSIZE_X / 2; + y = FLDSIZE_Y / 2; + + /* process raw key */ + for (i = 0; i < dgst_raw.size(); i++) { + /* each byte conveys four 2-bit move commands */ + int input = dgst_raw[i]; + for (b = 0; b < 4; b++) { + /* evaluate 2 bit, rest is shifted later */ + x += (input & 0x1) ? 1 : -1; + y += (input & 0x2) ? 1 : -1; + + /* assure we are still in bounds */ + x = std::max(x, 0); + y = std::max(y, 0); + x = std::min(x, FLDSIZE_X - 1); + y = std::min(y, FLDSIZE_Y - 1); + + /* augment the field */ + if (field[x][y] < len - 2) + field[x][y]++; + input = input >> 2; + } + } + + /* mark starting point and end point*/ + field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1; + field[x][y] = len; + + /* output upper border */ + add_char('+'); + for (i = 0; i < FLDSIZE_X; i++) + add_char('-'); + add_char('+'); + add_char('\n'); + + /* output content */ + for (y = 0; y < FLDSIZE_Y; y++) { + add_char('|'); + for (x = 0; x < FLDSIZE_X; x++) + add_char(augmentation_string[std::min(field[x][y], len)]); + add_char('|'); + add_char('\n'); + } + + /* output lower border */ + add_char('+'); + for (i = 0; i < FLDSIZE_X; i++) + add_char('-'); + add_char('+'); + + return std::string{retval.data(), retval.size()}; +} + +} // namespace barrier diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h new file mode 100644 index 0000000..c4d51f3 --- /dev/null +++ b/src/lib/net/SecureUtils.h @@ -0,0 +1,43 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_NET_SECUREUTILS_H +#define BARRIER_LIB_NET_SECUREUTILS_H + +#include "FingerprintData.h" +#include +#include +#include +#include + +namespace barrier { + +std::string format_ssl_fingerprint(const std::vector& fingerprint, + bool separator = true); +std::string format_ssl_fingerprint_columns(const std::vector& fingerprint); + +FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type); + +FingerprintData get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type); + +void generate_pem_self_signed_cert(const std::string& path); + +std::string create_fingerprint_randomart(const std::vector& dgst_raw); + +} // namespace barrier + +#endif // BARRIER_LIB_NET_SECUREUTILS_H diff --git a/src/lib/net/SocketMultiplexer.cpp b/src/lib/net/SocketMultiplexer.cpp index cdb9039..5a2328e 100644 --- a/src/lib/net/SocketMultiplexer.cpp +++ b/src/lib/net/SocketMultiplexer.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -26,7 +26,6 @@ #include "arch/Arch.h" #include "arch/XArch.h" #include "base/Log.h" -#include "base/TMethodJob.h" #include "common/stdvector.h" // @@ -58,8 +57,7 @@ SocketMultiplexer::SocketMultiplexer() : m_jobListLockLocker(NULL) { // start thread - m_thread = new Thread(new TMethodJob( - this, &SocketMultiplexer::serviceThread)); + m_thread = new Thread([this](){ service_thread(); }); } SocketMultiplexer::~SocketMultiplexer() @@ -95,7 +93,7 @@ void SocketMultiplexer::addSocket(ISocket* socket, std::unique_ptrsecond)) { @@ -138,8 +136,7 @@ SocketMultiplexer::removeSocket(ISocket* socket) unlockJobList(); } -void -SocketMultiplexer::serviceThread(void*) +void SocketMultiplexer::service_thread() { std::vector pfds; IArchNetwork::PollEntry pfd; @@ -179,7 +176,7 @@ SocketMultiplexer::serviceThread(void*) pfd.m_events |= IArchNetwork::kPOLLOUT; } pfds.push_back(pfd); - } + } jobCursor = nextCursor(cursor); } deleteCursor(cursor); diff --git a/src/lib/net/SocketMultiplexer.h b/src/lib/net/SocketMultiplexer.h index 9891558..b13c725 100644 --- a/src/lib/net/SocketMultiplexer.h +++ b/src/lib/net/SocketMultiplexer.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -67,7 +67,7 @@ private: // and m_update while m_pollable and m_polling are true. all other // threads must only modify these when m_pollable and m_polling are // false. only the service thread sets m_polling. - void serviceThread(void*); + void service_thread(); // create, iterate, and destroy a cursor. a cursor is used to // safely iterate through the job list while other threads modify diff --git a/src/lib/net/TCPListenSocket.cpp b/src/lib/net/TCPListenSocket.cpp index 26f03b0..2c305fc 100644 --- a/src/lib/net/TCPListenSocket.cpp +++ b/src/lib/net/TCPListenSocket.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -70,8 +70,10 @@ TCPListenSocket::bind(const NetworkAddress& addr) ARCH->bindSocket(m_socket, addr.getAddress()); ARCH->listenOnSocket(m_socket); - auto new_job = std::make_unique>( - this, &TCPListenSocket::serviceListening, m_socket, true, false); + auto new_job = std::make_unique( + [this](auto j, auto r, auto w, auto e) + { return serviceListening(j, r, w, e); }, + m_socket, true, false); m_socketMultiplexer->addSocket(this, std::move(new_job)); } @@ -136,8 +138,10 @@ TCPListenSocket::accept() void TCPListenSocket::setListeningJob() { - auto new_job = std::make_unique>( - this, &TCPListenSocket::serviceListening, m_socket, true, false); + auto new_job = std::make_unique( + [this](auto j, auto r, auto w, auto e) + { return serviceListening(j, r, w, e); }, + m_socket, true, false); m_socketMultiplexer->addSocket(this, std::move(new_job)); } diff --git a/src/lib/net/TCPListenSocket.h b/src/lib/net/TCPListenSocket.h index f3ababc..109c1c3 100644 --- a/src/lib/net/TCPListenSocket.h +++ b/src/lib/net/TCPListenSocket.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/net/TCPSocket.cpp b/src/lib/net/TCPSocket.cpp index 09a8f17..fa7edcc 100644 --- a/src/lib/net/TCPSocket.cpp +++ b/src/lib/net/TCPSocket.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -33,9 +33,7 @@ #include #include -// -// TCPSocket -// +static const std::size_t MAX_INPUT_BUFFER_SIZE = 1024 * 1024; TCPSocket::TCPSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family) : IDataSocket(events), @@ -335,19 +333,23 @@ TCPSocket::doRead() UInt8 buffer[4096]; memset(buffer, 0, sizeof(buffer)); size_t bytesRead = 0; - + bytesRead = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); - + if (bytesRead > 0) { bool wasEmpty = (m_inputBuffer.getSize() == 0); - + // slurp up as much as possible do { m_inputBuffer.write(buffer, (UInt32)bytesRead); + if (m_inputBuffer.getSize() > MAX_INPUT_BUFFER_SIZE) { + break; + } + bytesRead = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); } while (bytesRead > 0); - + // send input ready if input buffer was empty if (wasEmpty) { sendEvent(m_events->forIStream().inputReady()); @@ -365,7 +367,7 @@ TCPSocket::doRead() m_readable = false; return kNew; } - + return kRetry; } @@ -384,7 +386,7 @@ TCPSocket::doWrite() discardWrittenData(bytesWrote); return kNew; } - + return kRetry; } @@ -424,18 +426,20 @@ std::unique_ptr TCPSocket::newJob() if (!(m_readable || m_writable)) { return {}; } - return std::make_unique>( - this, &TCPSocket::serviceConnecting, - m_socket, m_readable, m_writable); + return std::make_unique( + [this](auto j, auto r, auto w, auto e) + { return serviceConnecting(j, r, w, e); }, + m_socket, m_readable, m_writable); } else { - if (!(m_readable || (m_writable && (m_outputBuffer.getSize() > 0)))) { + auto writable = m_writable && (m_outputBuffer.getSize() > 0); + if (!(m_readable || writable)) { return {}; } - return std::make_unique>( - this, &TCPSocket::serviceConnected, - m_socket, m_readable, - m_writable && (m_outputBuffer.getSize() > 0)); + return std::make_unique( + [this](auto j, auto r, auto w, auto e) + { return serviceConnected(j, r, w, e); }, + m_socket, m_readable, writable); } } diff --git a/src/lib/net/TCPSocket.h b/src/lib/net/TCPSocket.h index 0b98888..45aed07 100644 --- a/src/lib/net/TCPSocket.h +++ b/src/lib/net/TCPSocket.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -59,7 +59,7 @@ public: // IDataSocket overrides virtual void connect(const NetworkAddress&); - + virtual std::unique_ptr newJob(); protected: @@ -68,7 +68,7 @@ protected: kRetry, //!< Retry the same job kNew //!< Require a new job }; - + ArchSocket getSocket() { return m_socket; } IEventQueue* getEvents() { return m_events; } virtual EJobResult doRead(); @@ -105,7 +105,7 @@ protected: IEventQueue* m_events; StreamBuffer m_inputBuffer; StreamBuffer m_outputBuffer; - + private: Mutex m_mutex; ArchSocket m_socket; diff --git a/src/lib/net/TCPSocketFactory.cpp b/src/lib/net/TCPSocketFactory.cpp index 6ff4ef8..30e930e 100644 --- a/src/lib/net/TCPSocketFactory.cpp +++ b/src/lib/net/TCPSocketFactory.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -40,11 +40,12 @@ TCPSocketFactory::~TCPSocketFactory() // do nothing } -IDataSocket* -TCPSocketFactory::create(IArchNetwork::EAddressFamily family, bool secure) const +IDataSocket* TCPSocketFactory::create(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const { - if (secure) { - SecureSocket* secureSocket = new SecureSocket(m_events, m_socketMultiplexer, family); + if (security_level != ConnectionSecurityLevel::PLAINTEXT) { + SecureSocket* secureSocket = new SecureSocket(m_events, m_socketMultiplexer, family, + security_level); secureSocket->initSsl (false); return secureSocket; } @@ -53,12 +54,12 @@ TCPSocketFactory::create(IArchNetwork::EAddressFamily family, bool secure) const } } -IListenSocket* -TCPSocketFactory::createListen(IArchNetwork::EAddressFamily family, bool secure) const +IListenSocket* TCPSocketFactory::createListen(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const { IListenSocket* socket = NULL; - if (secure) { - socket = new SecureListenSocket(m_events, m_socketMultiplexer, family); + if (security_level != ConnectionSecurityLevel::PLAINTEXT) { + socket = new SecureListenSocket(m_events, m_socketMultiplexer, family, security_level); } else { socket = new TCPListenSocket(m_events, m_socketMultiplexer, family); diff --git a/src/lib/net/TCPSocketFactory.h b/src/lib/net/TCPSocketFactory.h index 0195ec4..ac21cab 100644 --- a/src/lib/net/TCPSocketFactory.h +++ b/src/lib/net/TCPSocketFactory.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -31,12 +31,11 @@ public: virtual ~TCPSocketFactory(); // ISocketFactory overrides - virtual IDataSocket* create( - IArchNetwork::EAddressFamily family, - bool secure) const; - virtual IListenSocket* createListen( - IArchNetwork::EAddressFamily family, - bool secure) const; + virtual IDataSocket* create(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const; + + virtual IListenSocket* createListen(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const; private: IEventQueue* m_events; diff --git a/src/lib/net/TSocketMultiplexerMethodJob.h b/src/lib/net/TSocketMultiplexerMethodJob.h index 9e74cdd..4b571ab 100644 --- a/src/lib/net/TSocketMultiplexerMethodJob.h +++ b/src/lib/net/TSocketMultiplexerMethodJob.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -25,81 +25,43 @@ /*! A socket multiplexer job class that invokes a member function. */ -template class TSocketMultiplexerMethodJob : public ISocketMultiplexerJob { public: - using Method = MultiplexerJobStatus (T::*)(ISocketMultiplexerJob*, bool, bool, bool); + using RunFunction = std::function; //! run() invokes \c object->method(arg) - TSocketMultiplexerMethodJob(T* object, Method method, - ArchSocket socket, bool readable, bool writeable); - virtual ~TSocketMultiplexerMethodJob(); + TSocketMultiplexerMethodJob(const RunFunction& func, + ArchSocket socket, bool readable, bool writable) : + func_{func}, + m_socket(ARCH->copySocket(socket)), + m_readable(readable), + m_writable(writable) + { + } + + ~TSocketMultiplexerMethodJob() override + { + ARCH->closeSocket(m_socket); + } // IJob overrides - virtual MultiplexerJobStatus run(bool readable, bool writable, bool error) override; - virtual ArchSocket getSocket() const override; - virtual bool isReadable() const override; - virtual bool isWritable() const override; + virtual MultiplexerJobStatus run(bool readable, bool writable, bool error) override + { + if (func_) { + return func_(this, readable, writable, error); + } + return {false, {}}; + } + + virtual ArchSocket getSocket() const override { return m_socket; } + virtual bool isReadable() const override { return m_readable; } + virtual bool isWritable() const override { return m_writable; } private: - T* m_object; - Method m_method; + RunFunction func_; ArchSocket m_socket; bool m_readable; bool m_writable; - void* m_arg; }; -template -inline -TSocketMultiplexerMethodJob::TSocketMultiplexerMethodJob(T* object, - Method method, ArchSocket socket, - bool readable, bool writable) : - m_object(object), - m_method(method), - m_socket(ARCH->copySocket(socket)), - m_readable(readable), - m_writable(writable) -{ - // do nothing -} - -template -inline -TSocketMultiplexerMethodJob::~TSocketMultiplexerMethodJob() -{ - ARCH->closeSocket(m_socket); -} - -template -inline MultiplexerJobStatus TSocketMultiplexerMethodJob::run(bool read, bool write, bool error) -{ - if (m_object != NULL) { - return (m_object->*m_method)(this, read, write, error); - } - return {false, {}}; -} - -template -inline -ArchSocket -TSocketMultiplexerMethodJob::getSocket() const -{ - return m_socket; -} - -template -inline -bool -TSocketMultiplexerMethodJob::isReadable() const -{ - return m_readable; -} -template -inline -bool -TSocketMultiplexerMethodJob::isWritable() const -{ - return m_writable; -} diff --git a/src/lib/net/XSocket.cpp b/src/lib/net/XSocket.cpp index eed7a26..6a50537 100644 --- a/src/lib/net/XSocket.cpp +++ b/src/lib/net/XSocket.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -63,7 +63,7 @@ std::string XSocketAddress::getWhat() const noexcept "invalid port" // m_port may not be set to the bad port }; return format(s_errorID[m_error], s_errorMsg[m_error], - m_hostname.c_str(), + m_hostname.c_str(), barrier::string::sprintf("%d", m_port).c_str()); } diff --git a/src/lib/net/XSocket.h b/src/lib/net/XSocket.h index d12278e..cafe59c 100644 --- a/src/lib/net/XSocket.h +++ b/src/lib/net/XSocket.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index a1718a1..75551b7 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/IMSWindowsClipboardFacade.h b/src/lib/platform/IMSWindowsClipboardFacade.h index 03c6248..d848184 100644 --- a/src/lib/platform/IMSWindowsClipboardFacade.h +++ b/src/lib/platform/IMSWindowsClipboardFacade.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -33,4 +33,4 @@ public: virtual ~IMSWindowsClipboardFacade() { } }; -#endif \ No newline at end of file +#endif diff --git a/src/lib/platform/IOSXKeyResource.cpp b/src/lib/platform/IOSXKeyResource.cpp index 0c5abe7..1866968 100644 --- a/src/lib/platform/IOSXKeyResource.cpp +++ b/src/lib/platform/IOSXKeyResource.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -128,7 +128,7 @@ IOSXKeyResource::getKeyID(UInt8 c) // encoding with char value 214). if it did then make no key, // otherwise CFStringCreateMutableCopy() will crash. if (cfString == NULL) { - return kKeyNone; + return kKeyNone; } // convert to precomposed diff --git a/src/lib/platform/IOSXKeyResource.h b/src/lib/platform/IOSXKeyResource.h index fc190ef..b032bd9 100644 --- a/src/lib/platform/IOSXKeyResource.h +++ b/src/lib/platform/IOSXKeyResource.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -27,10 +27,10 @@ public: virtual UInt32 getNumButtons() const = 0; virtual UInt32 getTableForModifier(UInt32 mask) const = 0; virtual KeyID getKey(UInt32 table, UInt32 button) const = 0; - + // Convert a character in the current script to the equivalent KeyID static KeyID getKeyID(UInt8); - + // Convert a unicode character to the equivalent KeyID. static KeyID unicharToKeyID(UniChar); }; diff --git a/src/lib/platform/IXWindowsImpl.h b/src/lib/platform/IXWindowsImpl.h index ddcaf2f..37c59b2 100644 --- a/src/lib/platform/IXWindowsImpl.h +++ b/src/lib/platform/IXWindowsImpl.h @@ -3,39 +3,31 @@ #include "config.h" -#if X_DISPLAY_MISSING -# error X11 is required to build barrier -#else -# include -# include -# define XK_MISCELLANY -# define XK_XKB_KEYS -# include -# if HAVE_X11_EXTENSIONS_DPMS_H - extern "C" { -# include - } -# endif -# if HAVE_X11_EXTENSIONS_XTEST_H -# include -# else -# error The XTest extension is required to build barrier -# endif -# if HAVE_X11_EXTENSIONS_XINERAMA_H - // Xinerama.h may lack extern "C" for inclusion by C++ - extern "C" { -# include - } -# endif -# if HAVE_X11_EXTENSIONS_XRANDR_H -# include -# endif -# if HAVE_XKB_EXTENSION -# include -# endif -# ifdef HAVE_XI2 -# include -# endif +#include +#include +#define XK_MISCELLANY +#define XK_XKB_KEYS +#include +#if HAVE_X11_EXTENSIONS_DPMS_H + extern "C" { +# include + } +#endif +#include +#if HAVE_X11_EXTENSIONS_XINERAMA_H + // Xinerama.h may lack extern "C" for inclusion by C++ + extern "C" { +# include + } +#endif +#if HAVE_X11_EXTENSIONS_XRANDR_H +# include +#endif +#if HAVE_XKB_EXTENSION +# include +#endif +#ifdef HAVE_XI2 +# include #endif class IXWindowsImpl { diff --git a/src/lib/platform/ImmuneKeysReader.cpp b/src/lib/platform/ImmuneKeysReader.cpp index 72baed3..eaeedc1 100644 --- a/src/lib/platform/ImmuneKeysReader.cpp +++ b/src/lib/platform/ImmuneKeysReader.cpp @@ -50,4 +50,4 @@ static void add_key(const char * const buffer, std::vector &keys) } } return true; -} \ No newline at end of file +} diff --git a/src/lib/platform/ImmuneKeysReader.h b/src/lib/platform/ImmuneKeysReader.h index b46cbbe..536dd45 100644 --- a/src/lib/platform/ImmuneKeysReader.h +++ b/src/lib/platform/ImmuneKeysReader.h @@ -27,8 +27,4 @@ class ImmuneKeysReader { public: static bool get_list(const char * const path, std::vector &keys, std::string &badLine); - -private: - // static class - explicit ImmuneKeysReader() {} }; diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp index 20445d4..fa59c3e 100644 --- a/src/lib/platform/MSWindowsClipboard.cpp +++ b/src/lib/platform/MSWindowsClipboard.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -70,7 +70,7 @@ MSWindowsClipboard::emptyUnowned() // empty the clipboard (and take ownership) if (!EmptyClipboard()) { - // unable to cause this in integ tests, but this error has never + // unable to cause this in integ tests, but this error has never // actually been reported by users. LOG((CLOG_DEBUG "failed to grab clipboard")); return false; diff --git a/src/lib/platform/MSWindowsClipboard.h b/src/lib/platform/MSWindowsClipboard.h index 35ccb56..415f31c 100644 --- a/src/lib/platform/MSWindowsClipboard.h +++ b/src/lib/platform/MSWindowsClipboard.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp b/src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp index 85a7dbe..e46b1de 100644 --- a/src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp +++ b/src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsClipboardAnyTextConverter.h b/src/lib/platform/MSWindowsClipboardAnyTextConverter.h index 622d281..463a24a 100644 --- a/src/lib/platform/MSWindowsClipboardAnyTextConverter.h +++ b/src/lib/platform/MSWindowsClipboardAnyTextConverter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp b/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp index 7d38fda..bac8cb6 100644 --- a/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp +++ b/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsClipboardBitmapConverter.h b/src/lib/platform/MSWindowsClipboardBitmapConverter.h index 2733884..9ca27ee 100644 --- a/src/lib/platform/MSWindowsClipboardBitmapConverter.h +++ b/src/lib/platform/MSWindowsClipboardBitmapConverter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsClipboardFacade.cpp b/src/lib/platform/MSWindowsClipboardFacade.cpp index 3b6478f..a6e1207 100644 --- a/src/lib/platform/MSWindowsClipboardFacade.cpp +++ b/src/lib/platform/MSWindowsClipboardFacade.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsClipboardFacade.h b/src/lib/platform/MSWindowsClipboardFacade.h index a95e835..cdbd796 100644 --- a/src/lib/platform/MSWindowsClipboardFacade.h +++ b/src/lib/platform/MSWindowsClipboardFacade.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsClipboardHTMLConverter.cpp b/src/lib/platform/MSWindowsClipboardHTMLConverter.cpp index a1f1212..d2ac3dd 100644 --- a/src/lib/platform/MSWindowsClipboardHTMLConverter.cpp +++ b/src/lib/platform/MSWindowsClipboardHTMLConverter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsClipboardHTMLConverter.h b/src/lib/platform/MSWindowsClipboardHTMLConverter.h index 51607a7..87d7fda 100644 --- a/src/lib/platform/MSWindowsClipboardHTMLConverter.h +++ b/src/lib/platform/MSWindowsClipboardHTMLConverter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsClipboardTextConverter.cpp b/src/lib/platform/MSWindowsClipboardTextConverter.cpp index 1500e7e..15f2cc2 100644 --- a/src/lib/platform/MSWindowsClipboardTextConverter.cpp +++ b/src/lib/platform/MSWindowsClipboardTextConverter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsClipboardTextConverter.h b/src/lib/platform/MSWindowsClipboardTextConverter.h index 6e265a2..4ebfa36 100644 --- a/src/lib/platform/MSWindowsClipboardTextConverter.h +++ b/src/lib/platform/MSWindowsClipboardTextConverter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsClipboardUTF16Converter.cpp b/src/lib/platform/MSWindowsClipboardUTF16Converter.cpp index 4b72717..52c6d4c 100644 --- a/src/lib/platform/MSWindowsClipboardUTF16Converter.cpp +++ b/src/lib/platform/MSWindowsClipboardUTF16Converter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsClipboardUTF16Converter.h b/src/lib/platform/MSWindowsClipboardUTF16Converter.h index 1a9d435..7314678 100644 --- a/src/lib/platform/MSWindowsClipboardUTF16Converter.h +++ b/src/lib/platform/MSWindowsClipboardUTF16Converter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsDebugOutputter.cpp b/src/lib/platform/MSWindowsDebugOutputter.cpp index 43c38ad..22442ad 100644 --- a/src/lib/platform/MSWindowsDebugOutputter.cpp +++ b/src/lib/platform/MSWindowsDebugOutputter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsDebugOutputter.h b/src/lib/platform/MSWindowsDebugOutputter.h index 01fd97e..e8ba5e6 100644 --- a/src/lib/platform/MSWindowsDebugOutputter.h +++ b/src/lib/platform/MSWindowsDebugOutputter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #pragma once #include "base/ILogOutputter.h" diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index 768ccb4..d126e13 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -3,11 +3,11 @@ * Copyright (C) 2018 Debauchee Open Source Group * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -27,9 +27,7 @@ #include "arch/win32/ArchMiscWindows.h" #include "base/Log.h" #include "base/IEventQueue.h" -#include "base/IJob.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" #include "base/IEventQueue.h" #include @@ -97,10 +95,9 @@ // MSWindowsDesks // -MSWindowsDesks::MSWindowsDesks( - bool isPrimary, bool noHooks, +MSWindowsDesks::MSWindowsDesks(bool isPrimary, bool noHooks, const IScreenSaver* screensaver, IEventQueue* events, - IJob* updateKeys, bool stopOnDeskSwitch) : + const std::function& updateKeys, bool stopOnDeskSwitch) : m_isPrimary(isPrimary), m_noHooks(noHooks), m_isOnScreen(m_isPrimary), @@ -130,7 +127,6 @@ MSWindowsDesks::~MSWindowsDesks() disable(); destroyClass(m_deskClass); destroyCursor(m_cursor); - delete m_updateKeys; } void @@ -602,13 +598,11 @@ MSWindowsDesks::deskLeave(Desk* desk, HKL keyLayout) } } -void -MSWindowsDesks::deskThread(void* vdesk) +void MSWindowsDesks::desk_thread(Desk* desk) { MSG msg; // use given desktop for this thread - Desk* desk = static_cast(vdesk); desk->m_threadID = GetCurrentThreadId(); desk->m_window = NULL; desk->m_foregroundWindow = NULL; @@ -709,7 +703,7 @@ MSWindowsDesks::deskThread(void* vdesk) } case BARRIER_MSG_SYNC_KEYS: - m_updateKeys->run(); + m_updateKeys(); break; case BARRIER_MSG_SCREENSAVER: @@ -752,8 +746,7 @@ MSWindowsDesks::Desk* MSWindowsDesks::addDesk(const std::string& name, HDESK hde desk->m_name = name; desk->m_desk = hdesk; desk->m_targetID = GetCurrentThreadId(); - desk->m_thread = new Thread(new TMethodJob( - this, &MSWindowsDesks::deskThread, desk)); + desk->m_thread = new Thread([this, desk]() { desk_thread(desk); }); waitForDesk(); m_desks.insert(std::make_pair(name, desk)); return desk; @@ -793,7 +786,7 @@ MSWindowsDesks::checkDesk() desk = index->second; } - // if we are told to shut down on desk switch, and this is not the + // if we are told to shut down on desk switch, and this is not the // first switch, then shut down. if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName) { LOG((CLOG_DEBUG "shutting down because of desk switch to \"%s\"", name.c_str())); @@ -803,7 +796,7 @@ MSWindowsDesks::checkDesk() // if active desktop changed then tell the old and new desk threads // about the change. don't switch desktops when the screensaver is - // active becaue we'd most likely switch to the screensaver desktop + // active because we'd most likely switch to the screensaver desktop // which would have the side effect of forcing the screensaver to // stop. if (name != m_activeDeskName && !m_screensaver->isActive()) { diff --git a/src/lib/platform/MSWindowsDesks.h b/src/lib/platform/MSWindowsDesks.h index 6e5e709..6292f98 100644 --- a/src/lib/platform/MSWindowsDesks.h +++ b/src/lib/platform/MSWindowsDesks.h @@ -3,11 +3,11 @@ * Copyright (C) 2018 Debauchee Open Source Group * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -26,15 +26,16 @@ #include "mt/CondVar.h" #include "mt/Mutex.h" #include "common/stdmap.h" +#include #include #define WIN32_LEAN_AND_MEAN #include + class Event; class EventQueueTimer; class Thread; -class IJob; class IScreenSaver; class IEventQueue; @@ -68,7 +69,7 @@ public: MSWindowsDesks( bool isPrimary, bool noHooks, const IScreenSaver* screensaver, IEventQueue* events, - IJob* updateKeys, bool stopOnDeskSwitch); + const std::function& updateKeys, bool stopOnDeskSwitch); ~MSWindowsDesks(); //! @name manipulators @@ -219,7 +220,7 @@ private: void deskMouseRelativeMove(SInt32 dx, SInt32 dy) const; void deskEnter(Desk* desk); void deskLeave(Desk* desk, HKL keyLayout); - void deskThread(void* vdesk); + void desk_thread(Desk* desk); // desk switch checking and handling Desk* addDesk(const std::string& name, HDESK hdesk); @@ -284,7 +285,7 @@ private: Desks m_desks; // keyboard stuff - IJob* m_updateKeys; + std::function m_updateKeys; HKL m_keyLayout; // options diff --git a/src/lib/platform/MSWindowsDropTarget.cpp b/src/lib/platform/MSWindowsDropTarget.cpp index d647808..095ab53 100644 --- a/src/lib/platform/MSWindowsDropTarget.cpp +++ b/src/lib/platform/MSWindowsDropTarget.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -53,7 +53,7 @@ MSWindowsDropTarget::DragEnter(IDataObject* dataObject, DWORD keyState, POINTL p if (m_allowDrop) { getDropData(dataObject); } - + *effect = DROPEFFECT_NONE; return S_OK; @@ -132,7 +132,7 @@ getDropData(IDataObject* dataObject) wcstombs(filename, wcData, wcslen(wcData)); MSWindowsDropTarget::instance().setDraggingFilename(filename); - + GlobalUnlock(stgMed.hGlobal); // release the data using the COM API @@ -167,7 +167,7 @@ ULONG __stdcall MSWindowsDropTarget::Release(void) { LONG count = InterlockedDecrement(&m_refCount); - + if (count == 0) { delete this; return 0; diff --git a/src/lib/platform/MSWindowsDropTarget.h b/src/lib/platform/MSWindowsDropTarget.h index 6d60845..6c3bf69 100644 --- a/src/lib/platform/MSWindowsDropTarget.h +++ b/src/lib/platform/MSWindowsDropTarget.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -44,7 +44,7 @@ public: std::string getDraggingFilename(); void clearDraggingFilename(); - static MSWindowsDropTarget& + static MSWindowsDropTarget& instance(); private: @@ -53,7 +53,7 @@ private: long m_refCount; bool m_allowDrop; std::string m_dragFilename; - + static MSWindowsDropTarget* s_instance; }; diff --git a/src/lib/platform/MSWindowsEventQueueBuffer.cpp b/src/lib/platform/MSWindowsEventQueueBuffer.cpp index 3111367..709d814 100644 --- a/src/lib/platform/MSWindowsEventQueueBuffer.cpp +++ b/src/lib/platform/MSWindowsEventQueueBuffer.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsEventQueueBuffer.h b/src/lib/platform/MSWindowsEventQueueBuffer.h index bc9bde1..6f918d4 100644 --- a/src/lib/platform/MSWindowsEventQueueBuffer.h +++ b/src/lib/platform/MSWindowsEventQueueBuffer.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsHook.cpp b/src/lib/platform/MSWindowsHook.cpp index b9b9740..3230d24 100644 --- a/src/lib/platform/MSWindowsHook.cpp +++ b/src/lib/platform/MSWindowsHook.cpp @@ -3,11 +3,11 @@ * Copyright (C) 2018 Debauchee Open Source Group * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -56,14 +56,13 @@ static BYTE g_deadKeyState[256] = { 0 }; static BYTE g_keyState[256] = { 0 }; static bool g_fakeServerInput = false; static std::vector g_immuneKeys; - -static const std::string ImmuneKeysPath = DataDirectories::profile() + "\\ImmuneKeys.txt"; +static std::string g_immuneKeysPath; static std::vector immune_keys_list() { std::vector keys; std::string badLine; - if (!ImmuneKeysReader::get_list(ImmuneKeysPath.c_str(), keys, badLine)) + if (!ImmuneKeysReader::get_list(g_immuneKeysPath.c_str(), keys, badLine)) LOG((CLOG_ERR "Reading immune keys stopped at: %s", badLine.c_str())); return keys; } @@ -144,9 +143,9 @@ keyboardGetState(BYTE keys[256], DWORD vkCode, bool kf_up) static WPARAM -makeKeyMsg(UINT virtKey, char c, bool noAltGr) +makeKeyMsg(UINT virtKey, WCHAR wc, bool noAltGr) { - return MAKEWPARAM(MAKEWORD(virtKey & 0xff, (BYTE)c), noAltGr ? 1 : 0); + return MAKEWPARAM((WORD)wc, MAKEWORD(virtKey & 0xff, noAltGr ? 1 : 0)); } static @@ -193,7 +192,7 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) (lParam & 0x80000000u) != 0) { g_deadRelease = 0; PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG, - wParam | 0x04000000, lParam); + wParam | 0x40000000, lParam); return false; } @@ -245,19 +244,19 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) } } - WORD c = 0; + WCHAR wc[2] = { 0, 0 }; // map the key event to a character. we have to put the dead // key back first and this has the side effect of removing it. if (g_deadVirtKey != 0) { - if (ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, - g_deadKeyState, &c, flags) == 2) { - // If ToAscii returned 2, it means that we accidentally removed + if (ToUnicode((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, + g_deadKeyState, wc, 2, flags) == 2) { + // If ToUnicode returned 2, it means that we accidentally removed // a double dead key instead of restoring it. Thus, we call - // ToAscii again with the same parameters to restore the + // ToUnicode again with the same parameters to restore the // internal dead key state. - ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, - g_deadKeyState, &c, flags); + ToUnicode((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, + g_deadKeyState, wc, 2, flags); // We need to keep track of this because g_deadVirtKey will be // cleared later on; this would cause the dead key release to @@ -267,7 +266,7 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) } UINT scanCode = ((lParam & 0x10ff0000u) >> 16); - int n = ToAscii((UINT)wParam, scanCode, keys, &c, flags); + int n = ToUnicode((UINT)wParam, scanCode, keys, wc, 2, flags); // if mapping failed and ctrl and alt are pressed then try again // with both not pressed. this handles the case where ctrl and @@ -279,12 +278,12 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) if (n == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) { noAltGr = true; PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG, - wParam | 0x05000000, lParam); + wParam | 0x50000000, lParam); if (g_deadVirtKey != 0) { - if (ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, - g_deadKeyState, &c, flags) == 2) { - ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, - g_deadKeyState, &c, flags); + if (ToUnicode((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, + g_deadKeyState, wc, 2, flags) == 2) { + ToUnicode((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, + g_deadKeyState, wc, 2, flags); g_deadRelease = g_deadVirtKey; } } @@ -298,12 +297,12 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) keys2[VK_LMENU] = 0; keys2[VK_RMENU] = 0; keys2[VK_MENU] = 0; - n = ToAscii((UINT)wParam, scanCode, keys2, &c, flags); + n = ToUnicode((UINT)wParam, scanCode, keys2, wc, 2, flags); } PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG, - wParam | ((c & 0xff) << 8) | - ((n & 0xff) << 16) | 0x06000000, + (wc[0] & 0xffff) | ((wParam & 0xff) << 16) | + ((n & 0xf) << 24) | 0x60000000, lParam); WPARAM charAndVirtKey = 0; bool clearDeadKey = false; @@ -329,12 +328,12 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) case 0: // key doesn't map to a character. this can happen if // non-character keys are pressed after a dead key. - charAndVirtKey = makeKeyMsg((UINT)wParam, (char)0, noAltGr); + charAndVirtKey = makeKeyMsg((UINT)wParam, (WCHAR)0, noAltGr); break; case 1: // key maps to a character composed with dead key - charAndVirtKey = makeKeyMsg((UINT)wParam, (char)LOBYTE(c), noAltGr); + charAndVirtKey = makeKeyMsg((UINT)wParam, wc[0], noAltGr); clearDeadKey = true; break; @@ -342,14 +341,14 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) // previous dead key not composed. send a fake key press // and release for the dead key to our window. WPARAM deadCharAndVirtKey = - makeKeyMsg((UINT)g_deadVirtKey, (char)LOBYTE(c), noAltGr); + makeKeyMsg((UINT)g_deadVirtKey, wc[0], noAltGr); PostThreadMessage(g_threadID, BARRIER_MSG_KEY, deadCharAndVirtKey, g_deadLParam & 0x7fffffffu); PostThreadMessage(g_threadID, BARRIER_MSG_KEY, deadCharAndVirtKey, g_deadLParam | 0x80000000u); // use uncomposed character - charAndVirtKey = makeKeyMsg((UINT)wParam, (char)HIBYTE(c), noAltGr); + charAndVirtKey = makeKeyMsg((UINT)wParam, wc[1], noAltGr); clearDeadKey = true; break; } @@ -357,8 +356,8 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) // put back the dead key, if any, for the application to use if (g_deadVirtKey != 0) { - ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, - g_deadKeyState, &c, flags); + ToUnicode((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, + g_deadKeyState, wc, 2, flags); } // clear out old dead key state @@ -375,7 +374,7 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) // forwarding. if (charAndVirtKey != 0) { PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG, - charAndVirtKey | 0x07000000, lParam); + charAndVirtKey | 0x70000000, lParam); PostThreadMessage(g_threadID, BARRIER_MSG_KEY, charAndVirtKey, lParam); } @@ -575,8 +574,9 @@ MSWindowsHook::install() g_fakeServerInput = false; // setup immune keys + g_immuneKeysPath = (barrier::DataDirectories::profile() / "ImmuneKeys.txt").u8string(); g_immuneKeys = immune_keys_list(); - LOG((CLOG_DEBUG "Found %u immune keys in %s", g_immuneKeys.size(), ImmuneKeysPath.c_str())); + LOG((CLOG_DEBUG "Found %u immune keys in %s", g_immuneKeys.size(), g_immuneKeysPath.c_str())); #if NO_GRAB_KEYBOARD // we only need the mouse hook @@ -638,4 +638,4 @@ void MSWindowsHook::uninstallScreenSaver() { g_hkMessage.unset(); -} \ No newline at end of file +} diff --git a/src/lib/platform/MSWindowsHook.h b/src/lib/platform/MSWindowsHook.h index 7b2121c..c6d91a2 100644 --- a/src/lib/platform/MSWindowsHook.h +++ b/src/lib/platform/MSWindowsHook.h @@ -3,11 +3,11 @@ * Copyright (C) 2018 Debauchee Open Source Group * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsHookResource.cpp b/src/lib/platform/MSWindowsHookResource.cpp index ced5ff1..c6349ab 100644 --- a/src/lib/platform/MSWindowsHookResource.cpp +++ b/src/lib/platform/MSWindowsHookResource.cpp @@ -30,4 +30,4 @@ bool WindowsHookResource::unset() } bool WindowsHookResource::is_set() const { return _hook != NULL; } -WindowsHookResource::operator HHOOK() const { return _hook; } \ No newline at end of file +WindowsHookResource::operator HHOOK() const { return _hook; } diff --git a/src/lib/platform/MSWindowsHookResource.h b/src/lib/platform/MSWindowsHookResource.h index b66c4b8..a3c9d83 100644 --- a/src/lib/platform/MSWindowsHookResource.h +++ b/src/lib/platform/MSWindowsHookResource.h @@ -17,4 +17,4 @@ public: private: HHOOK _hook; -}; \ No newline at end of file +}; diff --git a/src/lib/platform/MSWindowsKeyState.cpp b/src/lib/platform/MSWindowsKeyState.cpp index 2f29f72..c723d19 100644 --- a/src/lib/platform/MSWindowsKeyState.cpp +++ b/src/lib/platform/MSWindowsKeyState.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -21,7 +21,6 @@ #include "platform/MSWindowsDesks.h" #include "mt/Thread.h" #include "arch/win32/ArchMiscWindows.h" -#include "base/FunctionJob.h" #include "base/Log.h" #include "base/String.h" #include "base/IEventQueue.h" @@ -61,17 +60,17 @@ const KeyID MSWindowsKeyState::s_virtualKey[] = /* 0x012 */ { kKeyAlt_L }, // VK_MENU /* 0x013 */ { kKeyPause }, // VK_PAUSE /* 0x014 */ { kKeyCapsLock }, // VK_CAPITAL - /* 0x015 */ { kKeyKana }, // VK_HANGUL, VK_KANA - /* 0x016 */ { kKeyNone }, // undefined + /* 0x015 */ { kKeyNone }, // undefined + /* 0x016 */ { kKeyKana }, // VK_HANGUL, VK_KANA, VK_IME_ON /* 0x017 */ { kKeyNone }, // VK_JUNJA /* 0x018 */ { kKeyNone }, // VK_FINAL /* 0x019 */ { kKeyKanzi }, // VK_HANJA, VK_KANJI - /* 0x01a */ { kKeyNone }, // undefined + /* 0x01a */ { kKeyEisuToggle }, // VK_IME_OFF /* 0x01b */ { kKeyEscape }, // VK_ESCAPE - /* 0x01c */ { kKeyHenkan }, // VK_CONVERT - /* 0x01d */ { kKeyNone }, // VK_NONCONVERT - /* 0x01e */ { kKeyNone }, // VK_ACCEPT - /* 0x01f */ { kKeyNone }, // VK_MODECHANGE + /* 0x01c */ { kKeyHenkan }, // VK_CONVERT + /* 0x01d */ { kKeyMuhenkan }, // VK_NONCONVERT + /* 0x01e */ { kKeyNone }, // VK_ACCEPT + /* 0x01f */ { kKeyNone }, // VK_MODECHANGE /* 0x020 */ { kKeyNone }, // VK_SPACE /* 0x021 */ { kKeyKP_PageUp }, // VK_PRIOR /* 0x022 */ { kKeyKP_PageDown },// VK_NEXT @@ -286,15 +285,15 @@ const KeyID MSWindowsKeyState::s_virtualKey[] = /* 0x0f3 */ { kKeyZenkaku }, // VK_OEM_AUTO /* 0x0f4 */ { kKeyZenkaku }, // VK_OEM_ENLW /* 0x0f5 */ { kKeyNone }, // OEM specific - /* 0x0f6 */ { kKeyNone }, // VK_ATTN - /* 0x0f7 */ { kKeyNone }, // VK_CRSEL - /* 0x0f8 */ { kKeyNone }, // VK_EXSEL - /* 0x0f9 */ { kKeyNone }, // VK_EREOF - /* 0x0fa */ { kKeyNone }, // VK_PLAY - /* 0x0fb */ { kKeyNone }, // VK_ZOOM + /* 0x0f6 */ { kKeyNone }, // VK_ATTN + /* 0x0f7 */ { kKeyNone }, // VK_CRSEL + /* 0x0f8 */ { kKeyNone }, // VK_EXSEL + /* 0x0f9 */ { kKeyNone }, // VK_EREOF + /* 0x0fa */ { kKeyNone }, // VK_PLAY + /* 0x0fb */ { kKeyNone }, // VK_ZOOM /* 0x0fc */ { kKeyNone }, // reserved - /* 0x0fd */ { kKeyNone }, // VK_PA1 - /* 0x0fe */ { kKeyNone }, // VK_OEM_CLEAR + /* 0x0fd */ { kKeyNone }, // VK_PA1 + /* 0x0fe */ { kKeyNone }, // VK_OEM_CLEAR /* 0x0ff */ { kKeyNone }, // reserved /* 0x100 */ { kKeyNone }, // reserved @@ -320,15 +319,15 @@ const KeyID MSWindowsKeyState::s_virtualKey[] = /* 0x114 */ { kKeyNone }, // VK_CAPITAL /* 0x115 */ { kKeyHangul }, // VK_HANGUL /* 0x116 */ { kKeyNone }, // undefined - /* 0x117 */ { kKeyNone }, // VK_JUNJA - /* 0x118 */ { kKeyNone }, // VK_FINAL + /* 0x117 */ { kKeyNone }, // VK_JUNJA + /* 0x118 */ { kKeyNone }, // VK_FINAL /* 0x119 */ { kKeyHanja }, // VK_HANJA /* 0x11a */ { kKeyNone }, // undefined /* 0x11b */ { kKeyNone }, // VK_ESCAPE - /* 0x11c */ { kKeyNone }, // VK_CONVERT - /* 0x11d */ { kKeyNone }, // VK_NONCONVERT - /* 0x11e */ { kKeyNone }, // VK_ACCEPT - /* 0x11f */ { kKeyNone }, // VK_MODECHANGE + /* 0x11c */ { kKeyNone }, // VK_CONVERT + /* 0x11d */ { kKeyNone }, // VK_NONCONVERT + /* 0x11e */ { kKeyNone }, // VK_ACCEPT + /* 0x11f */ { kKeyNone }, // VK_MODECHANGE /* 0x120 */ { kKeyNone }, // VK_SPACE /* 0x121 */ { kKeyPageUp }, // VK_PRIOR /* 0x122 */ { kKeyPageDown }, // VK_NEXT @@ -543,15 +542,15 @@ const KeyID MSWindowsKeyState::s_virtualKey[] = /* 0x1f3 */ { kKeyNone }, // VK_OEM_AUTO /* 0x1f4 */ { kKeyNone }, // VK_OEM_ENLW /* 0x1f5 */ { kKeyNone }, // OEM specific - /* 0x1f6 */ { kKeyNone }, // VK_ATTN - /* 0x1f7 */ { kKeyNone }, // VK_CRSEL - /* 0x1f8 */ { kKeyNone }, // VK_EXSEL - /* 0x1f9 */ { kKeyNone }, // VK_EREOF - /* 0x1fa */ { kKeyNone }, // VK_PLAY - /* 0x1fb */ { kKeyNone }, // VK_ZOOM + /* 0x1f6 */ { kKeyNone }, // VK_ATTN + /* 0x1f7 */ { kKeyNone }, // VK_CRSEL + /* 0x1f8 */ { kKeyNone }, // VK_EXSEL + /* 0x1f9 */ { kKeyNone }, // VK_EREOF + /* 0x1fa */ { kKeyNone }, // VK_PLAY + /* 0x1fb */ { kKeyNone }, // VK_ZOOM /* 0x1fc */ { kKeyNone }, // reserved - /* 0x1fd */ { kKeyNone }, // VK_PA1 - /* 0x1fe */ { kKeyNone }, // VK_OEM_CLEAR + /* 0x1fd */ { kKeyNone }, // VK_PA1 + /* 0x1fe */ { kKeyNone }, // VK_OEM_CLEAR /* 0x1ff */ { kKeyNone } // reserved }; @@ -688,34 +687,17 @@ MSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey, KeyModifierControl | KeyModifierAlt; // extract character, virtual key, and if we didn't use AltGr - char c = (char)((charAndVirtKey & 0xff00u) >> 8); - UINT vkCode = (charAndVirtKey & 0xffu); - bool noAltGr = ((charAndVirtKey & 0xff0000u) != 0); + WCHAR wc = (WCHAR)(charAndVirtKey & 0xffffu); + UINT vkCode = ((charAndVirtKey >> 16) & 0xffu); + bool noAltGr = ((charAndVirtKey & 0xff000000u) != 0); // handle some keys via table lookup KeyID id = getKeyID(vkCode, (KeyButton)((info >> 16) & 0x1ffu)); // check if not in table; map character to key id - if (id == kKeyNone && c != 0) { - if ((c & 0x80u) == 0) { - // ASCII - id = static_cast(c) & 0xffu; - } - else { - // character is not really ASCII. instead it's some - // character in the current ANSI code page. try to - // convert that to a Unicode character. if we fail - // then use the single byte character as is. - char src = c; - wchar_t unicode; - if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, - &src, 1, &unicode, 1) > 0) { - id = static_cast(unicode); - } - else { - id = static_cast(c) & 0xffu; - } - } + if (id == kKeyNone && wc != 0) { + // UTF16 + id = static_cast(wc) & 0xffffu; } // set modifier mask @@ -821,15 +803,14 @@ MSWindowsKeyState::fakeCtrlAltDel() CloseHandle(hEvtSendSas); } else { - Thread cad(new FunctionJob(&MSWindowsKeyState::ctrlAltDelThread)); + Thread cad([this](){ ctrl_alt_del_thread(); }); cad.wait(); } return true; } -void -MSWindowsKeyState::ctrlAltDelThread(void*) +void MSWindowsKeyState::ctrl_alt_del_thread() { // get the Winlogon desktop at whatever privilege we can HDESK desk = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED); @@ -959,7 +940,7 @@ MSWindowsKeyState::getKeyMap(barrier::KeyMap& keyMap) // deal with certain virtual keys specially switch (vk) { case VK_SHIFT: - // this is important for sending the correct modifier to the + // this is important for sending the correct modifier to the // client, a patch from bug #242 (right shift broken for ms // remote desktop) removed this to just use left shift, which // caused bug #2799 (right shift broken for osx). @@ -1176,7 +1157,7 @@ MSWindowsKeyState::getKeyMap(barrier::KeyMap& keyMap) } } } - + // save each key. the map will automatically discard // duplicates, like an unshift and shifted version of // a key that's insensitive to shift. @@ -1347,7 +1328,7 @@ MSWindowsKeyState::getKeyID(UINT virtualKey, KeyButton button) const if ((LOWORD(m_keyLayout) & 0xffffu) == 0x0412u) { // 0x0412 : Korean Locale ID if (virtualKey == VK_HANGUL || virtualKey == VK_HANJA) { // If shift-space is used to change the input mode, - // the extented bit is not set. So add it to get right key id. + // the extended bit is not set. So add it to get right key id. button |= 0x100u; } } @@ -1374,7 +1355,7 @@ MSWindowsKeyState::getIDForKey(barrier::KeyMap::KeyItem& item, virtualKey, button, keyState, unicode, sizeof(unicode) / sizeof(unicode[0]), 0, hkl); KeyID id = static_cast(unicode[0]); - + switch (n) { case -1: return barrier::KeyMap::getDeadKey(id); diff --git a/src/lib/platform/MSWindowsKeyState.h b/src/lib/platform/MSWindowsKeyState.h index 3c5fa40..eedcd85 100644 --- a/src/lib/platform/MSWindowsKeyState.h +++ b/src/lib/platform/MSWindowsKeyState.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -169,7 +169,7 @@ private: typedef std::vector GroupList; // send ctrl+alt+del hotkey event on NT family - static void ctrlAltDelThread(void*); + static void ctrl_alt_del_thread(); bool getGroups(GroupList&) const; void setWindowGroup(SInt32 group); diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index df10270..c995a40 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -3,11 +3,11 @@ * Copyright (C) 2018 Debauchee Open Source Group * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -36,11 +36,9 @@ #include "mt/Thread.h" #include "arch/win32/ArchMiscWindows.h" #include "arch/Arch.h" -#include "base/FunctionJob.h" #include "base/Log.h" #include "base/IEventQueue.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" #include #include @@ -134,8 +132,7 @@ MSWindowsScreen::MSWindowsScreen( m_noHooks, m_screensaver, m_events, - new TMethodJob( - this, &MSWindowsScreen::updateKeysCB), + [this]() { updateKeysCB(); }, stopOnDeskSwitch); m_keyState = new MSWindowsKeyState(m_desks, getEventTarget(), m_events); @@ -145,16 +142,6 @@ MSWindowsScreen::MSWindowsScreen( forceShowCursor(); LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : "")); LOG((CLOG_DEBUG "window is 0x%08x", m_window)); - - // SHGetFolderPath is deprecated in vista, but use it for xp support. - char desktopPath[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath))) { - m_desktopPath = std::string(desktopPath); - LOG((CLOG_DEBUG "using desktop for drop target: %s", m_desktopPath.c_str())); - } - else { - LOG((CLOG_ERR "failed to get desktop path, no drop target available, error=%d", GetLastError())); - } OleInitialize(0); m_dropWindow = createDropWindow(m_class, "DropWindow"); @@ -365,17 +352,13 @@ MSWindowsScreen::leave() forceShowCursor(); if (isDraggingStarted() && !m_isPrimary) { - m_sendDragThread = new Thread( - new TMethodJob( - this, - &MSWindowsScreen::sendDragThread)); + m_sendDragThread = new Thread([this](){ send_drag_thread(); }); } return true; } -void -MSWindowsScreen::sendDragThread(void*) +void MSWindowsScreen::send_drag_thread() { std::string& draggingFilename = getDraggingFilename(); size_t size = draggingFilename.size(); @@ -389,7 +372,7 @@ MSWindowsScreen::sendDragThread(void*) LOG((CLOG_DEBUG "send dragging file to server")); client->sendFileToServer(draggingFilename.c_str()); } - + m_draggingStarted = false; } @@ -636,7 +619,7 @@ MSWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask) LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", barrier::KeyMap::formatKey(key, mask).c_str(), key, mask)); return 0; } - + LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", barrier::KeyMap::formatKey(key, mask).c_str(), key, mask, id)); return id; } @@ -1114,7 +1097,7 @@ MSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) static const KeyModifierMask s_ctrlAlt = KeyModifierControl | KeyModifierAlt; - LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, nagr=%d, lParam=0x%08x", (wParam & 0xff00u) >> 8, wParam & 0xffu, (wParam & 0x10000u) ? 1 : 0, lParam)); + LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, nagr=%d, lParam=0x%08x", wParam & 0xffffu, (wParam >> 16) & 0xffu, (wParam & 0x1000000u) ? 1 : 0, lParam)); // get event info KeyButton button = (KeyButton)((lParam & 0x01ff0000) >> 16); @@ -1132,7 +1115,7 @@ MSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) // that maps mouse buttons to keys is known to do this. // alternatively, we could just throw these events out. if (button == 0) { - button = m_keyState->virtualKeyToButton(wParam & 0xffu); + button = m_keyState->virtualKeyToButton((wParam >> 16) & 0xffu); if (button == 0) { return true; } @@ -1198,7 +1181,7 @@ MSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) if (!ignore()) { // check for ctrl+alt+del. we do not want to pass that to the // client. the user can use ctrl+alt+pause to emulate it. - UINT virtKey = (wParam & 0xffu); + UINT virtKey = (wParam >> 16) & 0xffu; if (virtKey == VK_DELETE && (state & s_ctrlAlt) == s_ctrlAlt) { LOG((CLOG_DEBUG "discard ctrl+alt+del")); return true; @@ -1212,9 +1195,9 @@ MSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) // pressed or released. when mapping the key we require that // we not use AltGr (the 0x10000 flag in wParam) and we not // use the keypad delete key (the 0x01000000 flag in lParam). - wParam = VK_DELETE | 0x00010000u; + wParam = (VK_DELETE << 16) | 0x01000000u; lParam &= 0xfe000000; - lParam |= m_keyState->virtualKeyToButton(wParam & 0xffu) << 16; + lParam |= m_keyState->virtualKeyToButton(VK_DELETE) << 16; lParam |= 0x01000001; } @@ -1242,7 +1225,7 @@ MSWindowsScreen::onHotKey(WPARAM wParam, LPARAM lParam) { // get the key info KeyModifierMask state = getActiveModifiers(); - UINT virtKey = (wParam & 0xffu); + UINT virtKey = (wParam >> 16) & 0xffu; UINT modifiers = 0; if ((state & KeyModifierShift) != 0) { modifiers |= MOD_SHIFT; @@ -1360,7 +1343,7 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) saveMousePosition(mx, my); if (m_isOnScreen) { - + // motion on primary screen sendEvent( m_events->forIPrimaryScreen().motionOnPrimary(), @@ -1370,15 +1353,15 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) m_draggingStarted = true; } } - else + else { // the motion is on the secondary screen, so we warp mouse back to - // center on the server screen. if we don't do this, then the mouse - // will always try to return to the original entry point on the + // center on the server screen. if we don't do this, then the mouse + // will always try to return to the original entry point on the // secondary screen. LOG((CLOG_DEBUG5 "warping server cursor to center: %+d,%+d", m_xCenter, m_yCenter)); warpCursorNoFlush(m_xCenter, m_yCenter); - + // examine the motion. if it's about the distance // from the center of the screen to an edge then // it's probably a bogus motion that we want to @@ -1389,7 +1372,7 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) x + bogusZoneSize > m_x + m_w - m_xCenter || -y + bogusZoneSize > m_yCenter - m_y || y + bogusZoneSize > m_y + m_h - m_yCenter) { - + LOG((CLOG_DEBUG "dropped bogus delta motion: %+d,%+d", x, y)); } else { @@ -1521,8 +1504,8 @@ MSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y) POINT cursorPos; GetCursorPos(&cursorPos); - // there is a bug or round error in SetCursorPos and GetCursorPos on - // a high DPI setting. The check here is for Vista/7 login screen. + // there is a bug or round error in SetCursorPos and GetCursorPos on + // a high DPI setting. The check here is for Vista/7 login screen. // since this feature is mainly for client, so only check on client. if (!isPrimary()) { if ((cursorPos.x != x) && (cursorPos.y != y)) { @@ -1531,7 +1514,7 @@ MSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y) // when at Vista/7 login screen, SetCursorPos does not work (which could be // an MS security feature). instead we can use fakeMouseMove, which calls // mouse_event. - // IMPORTANT: as of implementing this function, it has an annoying side + // IMPORTANT: as of implementing this function, it has an annoying side // effect; instead of the mouse returning to the correct exit point, it // returns to the center of the screen. this could have something to do with // the center screen warping technique used (see comments for onMouseMove @@ -1723,7 +1706,7 @@ MSWindowsScreen::mapPressFromEvent(WPARAM msg, LPARAM) const } void -MSWindowsScreen::updateKeysCB(void*) +MSWindowsScreen::updateKeysCB() { // record which keys we think are down bool down[IKeyState::kNumButtons]; @@ -1888,6 +1871,7 @@ std::string& MSWindowsScreen::getDraggingFilename() SWP_SHOWWINDOW); // TODO: fake these keys properly + ARCH->sleep(.05f); // A tiny sleep here makes the DragEnter event on m_dropWindow trigger much more consistently fakeKeyDown(kKeyEscape, 8192, 1); fakeKeyUp(1); fakeMouseButton(kButtonLeft, false); @@ -1909,21 +1893,39 @@ std::string& MSWindowsScreen::getDraggingFilename() m_draggingFilename = filename; } else { - LOG((CLOG_DEBUG "drag file name is invalid: %s", filename.c_str())); + LOG((CLOG_ERR "drag file name is invalid: %s", filename.c_str())); } } if (m_draggingFilename.empty()) { - LOG((CLOG_DEBUG "failed to get drag file name from OLE")); + LOG((CLOG_ERR "failed to get drag file name from OLE")); } } return m_draggingFilename; } -const std::string& MSWindowsScreen::getDropTarget() const +const std::string& +MSWindowsScreen::getDropTarget() const +{ + if (m_dropTargetPath.empty()) { + // SHGetFolderPath is deprecated in vista, but use it for xp support. + char desktopPath[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath))) { + m_dropTargetPath = std::string(desktopPath); + LOG((CLOG_INFO "using desktop for drop target: %s", m_dropTargetPath.c_str())); + } + else { + LOG((CLOG_ERR "failed to get desktop path, no drop target available, error=%d", GetLastError())); + } + } + return m_dropTargetPath; +} + +void +MSWindowsScreen::setDropTarget(const std::string& target) { - return m_desktopPath; + m_dropTargetPath = target; } bool @@ -1932,7 +1934,7 @@ MSWindowsScreen::isModifierRepeat(KeyModifierMask oldState, KeyModifierMask stat bool result = false; if (oldState == state && state != 0) { - UINT virtKey = (wParam & 0xffu); + UINT virtKey = (wParam >> 16) & 0xffu; if ((state & KeyModifierShift) != 0 && (virtKey == VK_LSHIFT || virtKey == VK_RSHIFT)) { result = true; diff --git a/src/lib/platform/MSWindowsScreen.h b/src/lib/platform/MSWindowsScreen.h index 49e09df..eaa7b88 100644 --- a/src/lib/platform/MSWindowsScreen.h +++ b/src/lib/platform/MSWindowsScreen.h @@ -3,11 +3,11 @@ * Copyright (C) 2018 Debauchee Open Source Group * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -120,6 +120,7 @@ public: virtual void fakeDraggingFiles(DragFileList fileList); virtual std::string& getDraggingFilename(); virtual const std::string& getDropTarget() const; + virtual void setDropTarget(const std::string&); protected: // IPlatformScreen overrides @@ -198,7 +199,7 @@ private: // HACK bool mapPressFromEvent(WPARAM msg, LPARAM button) const; // job to update the key state - void updateKeysCB(void*); + void updateKeysCB(); // determine whether the mouse is hidden by the system and force // it to be displayed if user has entered this secondary screen. @@ -212,16 +213,16 @@ private: // HACK // our window proc static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); - + // save last position of mouse to compute next delta movement void saveMousePosition(SInt32 x, SInt32 y); // check if it is a modifier key repeating message - bool isModifierRepeat(KeyModifierMask oldState, + bool isModifierRepeat(KeyModifierMask oldState, KeyModifierMask state, WPARAM wParam) const; // send drag info and data back to server - void sendDragThread(void*); + void send_drag_thread(); private: struct HotKeyItem { @@ -324,15 +325,15 @@ private: bool m_gotOldMouseKeys; MOUSEKEYS m_mouseKeys; MOUSEKEYS m_oldMouseKeys; - + MSWindowsHook m_hook; static MSWindowsScreen* s_screen; - + IEventQueue* m_events; - std::string m_desktopPath; + mutable std::string m_dropTargetPath; MSWindowsDropTarget* m_dropTarget; diff --git a/src/lib/platform/MSWindowsScreenSaver.cpp b/src/lib/platform/MSWindowsScreenSaver.cpp index f9c15fb..9f93792 100644 --- a/src/lib/platform/MSWindowsScreenSaver.cpp +++ b/src/lib/platform/MSWindowsScreenSaver.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -23,7 +23,6 @@ #include "arch/Arch.h" #include "arch/win32/ArchMiscWindows.h" #include "base/Log.h" -#include "base/TMethodJob.h" #include #include @@ -223,8 +222,7 @@ MSWindowsScreenSaver::watchDesktop() // watch desktop in another thread LOG((CLOG_DEBUG "watching screen saver desktop")); m_active = true; - m_watch = new Thread(new TMethodJob(this, - &MSWindowsScreenSaver::watchDesktopThread)); + m_watch = new Thread([this](){ watch_desktop_thread(); }); } void @@ -238,8 +236,7 @@ MSWindowsScreenSaver::watchProcess(HANDLE process) LOG((CLOG_DEBUG "watching screen saver process")); m_process = process; m_active = true; - m_watch = new Thread(new TMethodJob(this, - &MSWindowsScreenSaver::watchProcessThread)); + m_watch = new Thread([this](){ watch_process_thread(); }); } } @@ -260,8 +257,7 @@ MSWindowsScreenSaver::unwatchProcess() } } -void -MSWindowsScreenSaver::watchDesktopThread(void*) +void MSWindowsScreenSaver::watch_desktop_thread() { DWORD reserved = 0; TCHAR* name = NULL; @@ -283,8 +279,7 @@ MSWindowsScreenSaver::watchDesktopThread(void*) } } -void -MSWindowsScreenSaver::watchProcessThread(void*) +void MSWindowsScreenSaver::watch_process_thread() { for (;;) { Thread::testCancel(); diff --git a/src/lib/platform/MSWindowsScreenSaver.h b/src/lib/platform/MSWindowsScreenSaver.h index a117370..4f1dd5f 100644 --- a/src/lib/platform/MSWindowsScreenSaver.h +++ b/src/lib/platform/MSWindowsScreenSaver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -64,8 +64,8 @@ private: void watchDesktop(); void watchProcess(HANDLE process); void unwatchProcess(); - void watchDesktopThread(void*); - void watchProcessThread(void*); + void watch_desktop_thread(); + void watch_process_thread(); void setSecure(bool secure, bool saveSecureAsInt); bool isSecure(bool* wasSecureAnInt) const; diff --git a/src/lib/platform/MSWindowsSession.cpp b/src/lib/platform/MSWindowsSession.cpp index 8d4f8ce..daa9276 100644 --- a/src/lib/platform/MSWindowsSession.cpp +++ b/src/lib/platform/MSWindowsSession.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -45,7 +45,7 @@ MSWindowsSession::isProcessInSession(const char* name, PHANDLE process = NULL) PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); - // get the first process, and if we can't do that then it's + // get the first process, and if we can't do that then it's // unlikely we can go any further BOOL gotEntry = Process32First(snapshot, &entry); if (!gotEntry) { @@ -94,7 +94,7 @@ MSWindowsSession::isProcessInSession(const char* name, PHANDLE process = NULL) } std::string nameListJoin; - for(std::list::iterator it = nameList.begin(); + for (std::list::iterator it = nameList.begin(); it != nameList.end(); it++) { nameListJoin.append(*it); nameListJoin.append(", "); @@ -119,7 +119,7 @@ MSWindowsSession::isProcessInSession(const char* name, PHANDLE process = NULL) } } -HANDLE +HANDLE MSWindowsSession::getUserToken(LPSECURITY_ATTRIBUTES security) { HANDLE sourceToken; @@ -127,7 +127,7 @@ MSWindowsSession::getUserToken(LPSECURITY_ATTRIBUTES security) LOG((CLOG_ERR "could not get token from session %d", m_activeSessionId)); throw XArch(new XArchEvalWindows); } - + HANDLE newToken; if (!DuplicateTokenEx( sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security, @@ -136,7 +136,7 @@ MSWindowsSession::getUserToken(LPSECURITY_ATTRIBUTES security) LOG((CLOG_ERR "could not duplicate token")); throw XArch(new XArchEvalWindows); } - + LOG((CLOG_DEBUG "duplicated, new token: %i", newToken)); return newToken; } diff --git a/src/lib/platform/MSWindowsSession.h b/src/lib/platform/MSWindowsSession.h index e14d7e2..c3fff9d 100644 --- a/src/lib/platform/MSWindowsSession.h +++ b/src/lib/platform/MSWindowsSession.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -33,7 +33,7 @@ public: Returns true if the session ID has changed since updateActiveSession was called. */ BOOL hasChanged(); - + bool isProcessInSession(const char* name, PHANDLE process); HANDLE getUserToken(LPSECURITY_ATTRIBUTES security); diff --git a/src/lib/platform/MSWindowsUtil.cpp b/src/lib/platform/MSWindowsUtil.cpp index b6b809f..5a0778e 100644 --- a/src/lib/platform/MSWindowsUtil.cpp +++ b/src/lib/platform/MSWindowsUtil.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsUtil.h b/src/lib/platform/MSWindowsUtil.h index 59f2eac..df80f3f 100644 --- a/src/lib/platform/MSWindowsUtil.h +++ b/src/lib/platform/MSWindowsUtil.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index 0aa5505..2d9a61c 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -6,7 +6,7 @@ * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -29,7 +29,6 @@ #include "arch/win32/XArchWindows.h" #include "arch/Arch.h" #include "base/log_outputters.h" -#include "base/TMethodJob.h" #include "base/Log.h" #include "common/Version.h" @@ -81,21 +80,18 @@ MSWindowsWatchdog::MSWindowsWatchdog( { } -void +void MSWindowsWatchdog::startAsync() { - m_thread = new Thread(new TMethodJob( - this, &MSWindowsWatchdog::mainLoop, nullptr)); - - m_outputThread = new Thread(new TMethodJob( - this, &MSWindowsWatchdog::outputLoop, nullptr)); + m_thread = new Thread([this](){ main_loop(); }); + m_outputThread = new Thread([this](){ output_loop(); }); } void MSWindowsWatchdog::stop() { m_monitoring = false; - + m_thread->wait(5); delete m_thread; @@ -117,7 +113,7 @@ MSWindowsWatchdog::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES s LOG((CLOG_ERR "could not open token, process handle: %d", process)); throw XArch(new XArchEvalWindows()); } - + LOG((CLOG_DEBUG "got token %i, duplicating", sourceToken)); HANDLE newToken; @@ -129,22 +125,22 @@ MSWindowsWatchdog::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES s LOG((CLOG_ERR "could not duplicate token %i", sourceToken)); throw XArch(new XArchEvalWindows()); } - + LOG((CLOG_DEBUG "duplicated, new token: %i", newToken)); return newToken; } -HANDLE +HANDLE MSWindowsWatchdog::getUserToken(LPSECURITY_ATTRIBUTES security) { - // always elevate if we are at the vista/7 login screen. we could also + // always elevate if we are at the vista/7 login screen. we could also // elevate for the uac dialog (consent.exe) but this would be pointless, // since barrier would re-launch as non-elevated after the desk switch, // and so would be unusable with the new elevated process taking focus. if (m_elevateProcess || m_autoElevated) { LOG((CLOG_DEBUG "getting elevated token, %s", (m_elevateProcess ? "elevation required" : "at login screen"))); - + HANDLE process; if (!m_session.isProcessInSession("winlogon.exe", &process)) { throw XMSWindowsWatchdogError("cannot get user token without winlogon.exe"); @@ -157,8 +153,7 @@ MSWindowsWatchdog::getUserToken(LPSECURITY_ATTRIBUTES security) } } -void -MSWindowsWatchdog::mainLoop(void*) +void MSWindowsWatchdog::main_loop() { shutdownExistingProcesses(); @@ -169,10 +164,10 @@ MSWindowsWatchdog::mainLoop(void*) sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS"); } - SECURITY_ATTRIBUTES saAttr; - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; + SECURITY_ATTRIBUTES saAttr; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; if (!CreatePipe(&m_stdOutRead, &m_stdOutWrite, &saAttr, 0)) { throw XArch(new XArchEvalWindows()); @@ -196,7 +191,7 @@ MSWindowsWatchdog::mainLoop(void*) LOG((CLOG_INFO "backing off, wait=%ds, failures=%d", timeout, m_processFailures)); ARCH->sleep(timeout); } - + if (!getCommand().empty() && ((m_processFailures != 0) || m_session.hasChanged() || m_commandChanged)) { startProcess(); } @@ -205,7 +200,7 @@ MSWindowsWatchdog::mainLoop(void*) m_processFailures++; m_processRunning = false; - + LOG((CLOG_WARN "detected application not running, pid=%d", m_processInfo.dwProcessId)); } @@ -228,7 +223,7 @@ MSWindowsWatchdog::mainLoop(void*) // if the sas event failed, wait by sleeping. ARCH->sleep(1); - + } catch (std::exception& e) { LOG((CLOG_ERR "failed to launch, error: %s", e.what())); @@ -248,7 +243,7 @@ MSWindowsWatchdog::mainLoop(void*) LOG((CLOG_DEBUG "terminated running process on exit")); shutdownProcess(m_processInfo.hProcess, m_processInfo.dwProcessId, 20); } - + LOG((CLOG_DEBUG "watchdog main thread finished")); } @@ -260,7 +255,7 @@ MSWindowsWatchdog::isProcessActive() return exitCode == STILL_ACTIVE; } -void +void MSWindowsWatchdog::setFileLogOutputter(FileLogOutputter* outputter) { m_fileLogOutputter = outputter; @@ -367,7 +362,7 @@ BOOL MSWindowsWatchdog::doStartProcessAsUser(std::string& command, HANDLE userTo throw XArch(new XArchEvalWindows); } - DWORD creationFlags = + DWORD creationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT; @@ -421,14 +416,13 @@ MSWindowsWatchdog::getCommand() const return cmd; } -void -MSWindowsWatchdog::outputLoop(void*) +void MSWindowsWatchdog::output_loop() { // +1 char for \0 CHAR buffer[kOutputBufferSize + 1]; while (m_monitoring) { - + DWORD bytesRead; BOOL success = ReadFile(m_stdOutRead, buffer, kOutputBufferSize, &bytesRead, NULL); @@ -443,7 +437,7 @@ MSWindowsWatchdog::outputLoop(void*) if (m_fileLogOutputter != NULL) { m_fileLogOutputter->write(kINFO, buffer); } - } + } } } @@ -470,7 +464,7 @@ MSWindowsWatchdog::shutdownProcess(HANDLE handle, DWORD pid, int timeout) break; } else { - + double elapsed = (ARCH->time() - start); if (elapsed > timeout) { // if timeout reached, kill forcefully. @@ -500,7 +494,7 @@ MSWindowsWatchdog::shutdownExistingProcesses() PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); - // get the first process, and if we can't do that then it's + // get the first process, and if we can't do that then it's // unlikely we can go any further BOOL gotEntry = Process32First(snapshot, &entry); if (!gotEntry) { @@ -517,7 +511,7 @@ MSWindowsWatchdog::shutdownExistingProcesses() if (_stricmp(entry.szExeFile, "barrierc.exe") == 0 || _stricmp(entry.szExeFile, "barriers.exe") == 0) { - + HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID); shutdownProcess(handle, entry.th32ProcessID, 10); } diff --git a/src/lib/platform/MSWindowsWatchdog.h b/src/lib/platform/MSWindowsWatchdog.h index 0a81521..0595ca0 100644 --- a/src/lib/platform/MSWindowsWatchdog.h +++ b/src/lib/platform/MSWindowsWatchdog.h @@ -6,7 +6,7 @@ * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -48,8 +48,8 @@ public: void setFileLogOutputter(FileLogOutputter* outputter); private: - void mainLoop(void*); - void outputLoop(void*); + void main_loop(); + void output_loop(); void shutdownProcess(HANDLE handle, DWORD pid, int timeout); void shutdownExistingProcesses(); HANDLE duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security); @@ -81,7 +81,7 @@ private: //! Relauncher error /*! -An error occured in the process watchdog. +An error occurred in the process watchdog. */ class XMSWindowsWatchdogError : public XBarrier { public: diff --git a/src/lib/platform/OSXClipboard.cpp b/src/lib/platform/OSXClipboard.cpp index e55c8e0..ff1779c 100644 --- a/src/lib/platform/OSXClipboard.cpp +++ b/src/lib/platform/OSXClipboard.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -52,7 +52,7 @@ OSXClipboard::OSXClipboard() : OSStatus syncErr = PasteboardSynchronize(m_pboard); if (syncErr != noErr) { - LOG((CLOG_DEBUG "failed to syncronize clipboard: error %i", syncErr)); + LOG((CLOG_DEBUG "failed to synchronize clipboard: error %i", syncErr)); } } @@ -90,7 +90,7 @@ OSXClipboard::synchronize() return true; } return false; -} +} void OSXClipboard::add(EFormat format, const std::string& data) { @@ -126,15 +126,15 @@ void OSXClipboard::add(EFormat format, const std::string& data) flavorType, dataRef, kPasteboardFlavorNoFlags); - + LOG((CLOG_DEBUG "added %d bytes to clipboard format: %d", data.size(), format)); } - + } } bool -OSXClipboard::open(Time time) const +OSXClipboard::open(Time time) const { if (m_pboard == NULL) return false; diff --git a/src/lib/platform/OSXClipboard.h b/src/lib/platform/OSXClipboard.h index b1f9801..9d66600 100644 --- a/src/lib/platform/OSXClipboard.h +++ b/src/lib/platform/OSXClipboard.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/OSXClipboardAnyTextConverter.cpp b/src/lib/platform/OSXClipboardAnyTextConverter.cpp index ae8dc4a..2513db5 100644 --- a/src/lib/platform/OSXClipboardAnyTextConverter.cpp +++ b/src/lib/platform/OSXClipboardAnyTextConverter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/OSXClipboardAnyTextConverter.h b/src/lib/platform/OSXClipboardAnyTextConverter.h index f057bae..987f2c5 100644 --- a/src/lib/platform/OSXClipboardAnyTextConverter.h +++ b/src/lib/platform/OSXClipboardAnyTextConverter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/OSXClipboardHTMLConverter.cpp b/src/lib/platform/OSXClipboardHTMLConverter.cpp index 46a3d0f..0a2f5ec 100644 --- a/src/lib/platform/OSXClipboardHTMLConverter.cpp +++ b/src/lib/platform/OSXClipboardHTMLConverter.cpp @@ -66,7 +66,7 @@ std::string OSXClipboardHTMLConverter::convertString(const std::string& data, CFRelease(stringRef); return {}; } - + CFStringGetBytes(stringRef, entireString, toEncoding, 0, false, (UInt8*)buffer, buffSize, NULL); diff --git a/src/lib/platform/OSXClipboardTextConverter.cpp b/src/lib/platform/OSXClipboardTextConverter.cpp index a68258d..a4fb6ae 100644 --- a/src/lib/platform/OSXClipboardTextConverter.cpp +++ b/src/lib/platform/OSXClipboardTextConverter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -24,7 +24,7 @@ // OSXClipboardTextConverter // -OSXClipboardTextConverter::OSXClipboardTextConverter() +OSXClipboardTextConverter::OSXClipboardTextConverter() { // do nothing } @@ -59,12 +59,12 @@ std::string OSXClipboardTextConverter::convertString(const std::string& data, 0, false, NULL, 0, &buffSize); char* buffer = new char[buffSize]; - + if (buffer == NULL) { CFRelease(stringRef); return {}; } - + CFStringGetBytes(stringRef, entireString, toEncoding, 0, false, (UInt8*)buffer, buffSize, NULL); @@ -72,7 +72,7 @@ std::string OSXClipboardTextConverter::convertString(const std::string& data, delete[] buffer; CFRelease(stringRef); - + return result; } diff --git a/src/lib/platform/OSXClipboardTextConverter.h b/src/lib/platform/OSXClipboardTextConverter.h index 8211f1e..5a4b51f 100644 --- a/src/lib/platform/OSXClipboardTextConverter.h +++ b/src/lib/platform/OSXClipboardTextConverter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/OSXClipboardUTF16Converter.cpp b/src/lib/platform/OSXClipboardUTF16Converter.cpp index 8411e92..4f13cc0 100644 --- a/src/lib/platform/OSXClipboardUTF16Converter.cpp +++ b/src/lib/platform/OSXClipboardUTF16Converter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/OSXClipboardUTF16Converter.h b/src/lib/platform/OSXClipboardUTF16Converter.h index b279c99..5e448df 100644 --- a/src/lib/platform/OSXClipboardUTF16Converter.h +++ b/src/lib/platform/OSXClipboardUTF16Converter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/OSXDragSimulator.h b/src/lib/platform/OSXDragSimulator.h index cb361ca..6dd73f4 100644 --- a/src/lib/platform/OSXDragSimulator.h +++ b/src/lib/platform/OSXDragSimulator.h @@ -28,7 +28,7 @@ void runCocoaApp(); void stopCocoaLoop(); void fakeDragging(const char* str, int cursorX, int cursorY); CFStringRef getCocoaDropTarget(); - + #if defined(__cplusplus) } #endif diff --git a/src/lib/platform/OSXDragSimulator.m b/src/lib/platform/OSXDragSimulator.m deleted file mode 100644 index affed38..0000000 --- a/src/lib/platform/OSXDragSimulator.m +++ /dev/null @@ -1,102 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2013-2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#import "platform/OSXDragSimulator.h" - -#import "platform/OSXDragView.h" - -#import -#import -#import - -#if defined(MAC_OS_X_VERSION_10_7) - -NSWindow* g_dragWindow = NULL; -OSXDragView* g_dragView = NULL; -NSString* g_ext = NULL; - -void -runCocoaApp() -{ - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - - [NSApplication sharedApplication]; - - NSWindow* window = [[NSWindow alloc] - initWithContentRect: NSMakeRect(0, 0, 3, 3) - styleMask: NSBorderlessWindowMask - backing: NSBackingStoreBuffered - defer: NO]; - [window setTitle: @""]; - [window setAlphaValue:0.1]; - [window makeKeyAndOrderFront:nil]; - - OSXDragView* dragView = [[OSXDragView alloc] initWithFrame:NSMakeRect(0, 0, 3, 3)]; - - g_dragWindow = window; - g_dragView = dragView; - [window setContentView: dragView]; - - NSLog(@"starting cocoa loop"); - [NSApp run]; - - NSLog(@"cocoa: release"); - [pool release]; -} - -void -stopCocoaLoop() -{ - [NSApp stop: g_dragWindow]; -} - -void -fakeDragging(const char* str, int cursorX, int cursorY) -{ - g_ext = [NSString stringWithUTF8String:str]; - - dispatch_async(dispatch_get_main_queue(), ^{ - NSRect screen = [[NSScreen mainScreen] frame]; - NSLog ( @"screen size: witdh = %f height = %f", screen.size.width, screen.size.height); - NSLog ( @"mouseLocation: %d %d", cursorX, cursorY); - - int newPosX = 0; - int newPosY = 0; - newPosX = cursorX - 1; - newPosY = screen.size.height - cursorY - 1; - - NSRect rect = NSMakeRect(newPosX, newPosY, 3, 3); - NSLog ( @"newPosX: %d", newPosX); - NSLog ( @"newPosY: %d", newPosY); - - [g_dragWindow setFrame:rect display:NO]; - [g_dragWindow makeKeyAndOrderFront:nil]; - [NSApp activateIgnoringOtherApps:YES]; - - [g_dragView setFileExt:g_ext]; - - CGEventRef down = CGEventCreateMouseEvent(CGEventSourceCreate(kCGEventSourceStateHIDSystemState), kCGEventLeftMouseDown, CGPointMake(cursorX, cursorY), kCGMouseButtonLeft); - CGEventPost(kCGHIDEventTap, down); - }); -} - -CFStringRef -getCocoaDropTarget() -{ - // HACK: sleep, wait for cocoa drop target updated first - usleep(1000000); - return [g_dragView getDropTarget]; -} - -#endif diff --git a/src/lib/platform/OSXDragSimulator.mm b/src/lib/platform/OSXDragSimulator.mm new file mode 100644 index 0000000..735aa4a --- /dev/null +++ b/src/lib/platform/OSXDragSimulator.mm @@ -0,0 +1,102 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2013-2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#import "platform/OSXDragSimulator.h" + +#import "platform/OSXDragView.h" + +#import +#import +#import + +#if defined(MAC_OS_X_VERSION_10_7) + +NSWindow* g_dragWindow = NULL; +OSXDragView* g_dragView = NULL; +NSString* g_ext = NULL; + +void +runCocoaApp() +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + [NSApplication sharedApplication]; + + NSWindow* window = [[NSWindow alloc] + initWithContentRect: NSMakeRect(0, 0, 3, 3) + styleMask: NSBorderlessWindowMask + backing: NSBackingStoreBuffered + defer: NO]; + [window setTitle: @""]; + [window setAlphaValue:0.1]; + [window makeKeyAndOrderFront:nil]; + + OSXDragView* dragView = [[OSXDragView alloc] initWithFrame:NSMakeRect(0, 0, 3, 3)]; + + g_dragWindow = window; + g_dragView = dragView; + [window setContentView: dragView]; + + NSLog(@"starting cocoa loop"); + [NSApp run]; + + NSLog(@"cocoa: release"); + [pool release]; +} + +void +stopCocoaLoop() +{ + [NSApp stop: g_dragWindow]; +} + +void +fakeDragging(const char* str, int cursorX, int cursorY) +{ + g_ext = [NSString stringWithUTF8String:str]; + + dispatch_async(dispatch_get_main_queue(), ^{ + NSRect screen = [[NSScreen mainScreen] frame]; + NSLog ( @"screen size: width = %f height = %f", screen.size.width, screen.size.height); + NSLog ( @"mouseLocation: %d %d", cursorX, cursorY); + + int newPosX = 0; + int newPosY = 0; + newPosX = cursorX - 1; + newPosY = screen.size.height - cursorY - 1; + + NSRect rect = NSMakeRect(newPosX, newPosY, 3, 3); + NSLog ( @"newPosX: %d", newPosX); + NSLog ( @"newPosY: %d", newPosY); + + [g_dragWindow setFrame:rect display:NO]; + [g_dragWindow makeKeyAndOrderFront:nil]; + [NSApp activateIgnoringOtherApps:YES]; + + [g_dragView setFileExt:g_ext]; + + CGEventRef down = CGEventCreateMouseEvent(CGEventSourceCreate(kCGEventSourceStateHIDSystemState), kCGEventLeftMouseDown, CGPointMake(cursorX, cursorY), kCGMouseButtonLeft); + CGEventPost(kCGHIDEventTap, down); + }); +} + +CFStringRef +getCocoaDropTarget() +{ + // HACK: sleep, wait for cocoa drop target updated first + usleep(1000000); + return [g_dragView getDropTarget]; +} + +#endif diff --git a/src/lib/platform/OSXDragView.m b/src/lib/platform/OSXDragView.m deleted file mode 100644 index 67dac56..0000000 --- a/src/lib/platform/OSXDragView.m +++ /dev/null @@ -1,177 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2013-2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#import "platform/OSXDragView.h" - -#ifdef MAC_OS_X_VERSION_10_7 - -@implementation OSXDragView - -@dynamic draggingFormation; -@dynamic animatesToDestination; -@dynamic numberOfValidItemsForDrop; - -/* springLoadingHighlight is a property that will not be auto-synthesized by - clang. explicitly synthesizing it here as well as defining an empty handler - for resetSpringLoading() satisfies the compiler */ -@synthesize springLoadingHighlight = _springLoadingHighlight; - -/* unused */ -- (void) -resetSpringLoading -{ -} - -- (id) -initWithFrame:(NSRect)frame -{ - self = [super initWithFrame:frame]; - m_dropTarget = [[NSMutableString alloc] initWithCapacity:0]; - m_dragFileExt = [[NSMutableString alloc] initWithCapacity:0]; - return self; -} - -- (void) -drawRect:(NSRect)dirtyRect -{ -} - -- (BOOL) -acceptsFirstMouse:(NSEvent *)theEvent -{ - return YES; -} - -- (void) -mouseDown:(NSEvent *)theEvent -{ - NSLog ( @"cocoa mouse down"); - NSPoint dragPosition; - NSRect imageLocation; - dragPosition = [self convertPoint:[theEvent locationInWindow] - fromView:nil]; - - dragPosition.x -= 16; - dragPosition.y -= 16; - imageLocation.origin = dragPosition; - imageLocation.size = NSMakeSize(32,32); - [self dragPromisedFilesOfTypes:[NSArray arrayWithObject:m_dragFileExt] - fromRect:imageLocation - source:self - slideBack:NO - event:theEvent]; -} - -- (NSArray*) -namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination -{ - [m_dropTarget setString:@""]; - [m_dropTarget appendString:dropDestination.path]; - NSLog ( @"cocoa drop target: %@", m_dropTarget); - return nil; -} - -- (NSDragOperation) -draggingSourceOperationMaskForLocal:(BOOL)flag -{ - return NSDragOperationCopy; -} - -- (CFStringRef) -getDropTarget -{ - NSMutableString* string; - string = [[NSMutableString alloc] initWithCapacity:0]; - [string appendString:m_dropTarget]; - return (CFStringRef)string; -} - -- (void) -clearDropTarget -{ - [m_dropTarget setString:@""]; -} - -- (void) -setFileExt:(NSString*) ext -{ - [ext retain]; - [m_dragFileExt release]; - m_dragFileExt = ext; - NSLog(@"drag file ext: %@", m_dragFileExt); -} - -- (NSWindow *) -draggingDestinationWindow -{ - return nil; -} - -- (NSDragOperation) -draggingSourceOperationMask -{ - return NSDragOperationCopy; -} - -- (NSPoint)draggingLocation -{ - NSPoint point = NSMakePoint(0, 0); - return point; -} - -- (NSPoint)draggedImageLocation -{ - NSPoint point = NSMakePoint(0, 0); - return point; -} - -- (NSImage *)draggedImage -{ - return nil; -} - -- (NSPasteboard *)draggingPasteboard -{ - return nil; -} - -- (id)draggingSource -{ - return nil; -} - -- (NSInteger)draggingSequenceNumber -{ - return 0; -} - -- (void)slideDraggedImageTo:(NSPoint)screenPoint -{ -} - -- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context -{ - return NSDragOperationCopy; -} - -- (void)enumerateDraggingItemsWithOptions:(NSDraggingItemEnumerationOptions)enumOpts forView:(NSView *)view classes:(NSArray *)classArray searchOptions:(NSDictionary *)searchOptions usingBlock:(void (^)(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop))block -{ -} - -@end - -#endif diff --git a/src/lib/platform/OSXDragView.mm b/src/lib/platform/OSXDragView.mm new file mode 100644 index 0000000..67dac56 --- /dev/null +++ b/src/lib/platform/OSXDragView.mm @@ -0,0 +1,177 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2013-2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#import "platform/OSXDragView.h" + +#ifdef MAC_OS_X_VERSION_10_7 + +@implementation OSXDragView + +@dynamic draggingFormation; +@dynamic animatesToDestination; +@dynamic numberOfValidItemsForDrop; + +/* springLoadingHighlight is a property that will not be auto-synthesized by + clang. explicitly synthesizing it here as well as defining an empty handler + for resetSpringLoading() satisfies the compiler */ +@synthesize springLoadingHighlight = _springLoadingHighlight; + +/* unused */ +- (void) +resetSpringLoading +{ +} + +- (id) +initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + m_dropTarget = [[NSMutableString alloc] initWithCapacity:0]; + m_dragFileExt = [[NSMutableString alloc] initWithCapacity:0]; + return self; +} + +- (void) +drawRect:(NSRect)dirtyRect +{ +} + +- (BOOL) +acceptsFirstMouse:(NSEvent *)theEvent +{ + return YES; +} + +- (void) +mouseDown:(NSEvent *)theEvent +{ + NSLog ( @"cocoa mouse down"); + NSPoint dragPosition; + NSRect imageLocation; + dragPosition = [self convertPoint:[theEvent locationInWindow] + fromView:nil]; + + dragPosition.x -= 16; + dragPosition.y -= 16; + imageLocation.origin = dragPosition; + imageLocation.size = NSMakeSize(32,32); + [self dragPromisedFilesOfTypes:[NSArray arrayWithObject:m_dragFileExt] + fromRect:imageLocation + source:self + slideBack:NO + event:theEvent]; +} + +- (NSArray*) +namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination +{ + [m_dropTarget setString:@""]; + [m_dropTarget appendString:dropDestination.path]; + NSLog ( @"cocoa drop target: %@", m_dropTarget); + return nil; +} + +- (NSDragOperation) +draggingSourceOperationMaskForLocal:(BOOL)flag +{ + return NSDragOperationCopy; +} + +- (CFStringRef) +getDropTarget +{ + NSMutableString* string; + string = [[NSMutableString alloc] initWithCapacity:0]; + [string appendString:m_dropTarget]; + return (CFStringRef)string; +} + +- (void) +clearDropTarget +{ + [m_dropTarget setString:@""]; +} + +- (void) +setFileExt:(NSString*) ext +{ + [ext retain]; + [m_dragFileExt release]; + m_dragFileExt = ext; + NSLog(@"drag file ext: %@", m_dragFileExt); +} + +- (NSWindow *) +draggingDestinationWindow +{ + return nil; +} + +- (NSDragOperation) +draggingSourceOperationMask +{ + return NSDragOperationCopy; +} + +- (NSPoint)draggingLocation +{ + NSPoint point = NSMakePoint(0, 0); + return point; +} + +- (NSPoint)draggedImageLocation +{ + NSPoint point = NSMakePoint(0, 0); + return point; +} + +- (NSImage *)draggedImage +{ + return nil; +} + +- (NSPasteboard *)draggingPasteboard +{ + return nil; +} + +- (id)draggingSource +{ + return nil; +} + +- (NSInteger)draggingSequenceNumber +{ + return 0; +} + +- (void)slideDraggedImageTo:(NSPoint)screenPoint +{ +} + +- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context +{ + return NSDragOperationCopy; +} + +- (void)enumerateDraggingItemsWithOptions:(NSDraggingItemEnumerationOptions)enumOpts forView:(NSView *)view classes:(NSArray *)classArray searchOptions:(NSDictionary *)searchOptions usingBlock:(void (^)(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop))block +{ +} + +@end + +#endif diff --git a/src/lib/platform/OSXEventQueueBuffer.cpp b/src/lib/platform/OSXEventQueueBuffer.cpp index 8e18afc..e012c89 100644 --- a/src/lib/platform/OSXEventQueueBuffer.cpp +++ b/src/lib/platform/OSXEventQueueBuffer.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -83,11 +83,11 @@ OSXEventQueueBuffer::getEvent(Event& event, UInt32& dataID) else { UInt32 eventClass = GetEventClass(m_event); switch (eventClass) { - case 'Syne': + case 'Syne': dataID = GetEventKind(m_event); return kUser; - default: + default: event = Event(Event::kSystem, m_eventQueue->getSystemTarget(), &m_event); return kSystem; @@ -101,24 +101,24 @@ OSXEventQueueBuffer::addEvent(UInt32 dataID) EventRef event; OSStatus error = CreateEvent( kCFAllocatorDefault, - 'Syne', + 'Syne', dataID, 0, kEventAttributeNone, &event); if (error == noErr) { - + assert(m_carbonEventQueue != NULL); - + error = PostEventToQueue( m_carbonEventQueue, event, kEventPriorityStandard); - + ReleaseEvent(event); } - + return (error == noErr); } diff --git a/src/lib/platform/OSXEventQueueBuffer.h b/src/lib/platform/OSXEventQueueBuffer.h index 28c4a5d..8e0c903 100644 --- a/src/lib/platform/OSXEventQueueBuffer.h +++ b/src/lib/platform/OSXEventQueueBuffer.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 9db3c5d..aad2072 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -43,6 +43,9 @@ static const UInt32 s_launchpadVK = 131; static const UInt32 s_osxNumLock = 1 << 16; +static const UInt32 s_int4VK = 0x8a; // international4 +static const UInt32 s_int5VK = 0x8b; // international5 + struct KeyEntry { public: KeyID m_keyID; @@ -97,7 +100,7 @@ static const KeyEntry s_controlKeys[] = { { kKeyKP_Divide, kVK_ANSI_KeypadDivide }, { kKeyKP_Subtract, kVK_ANSI_KeypadMinus }, { kKeyKP_Enter, kVK_ANSI_KeypadEnter }, - + // virtual key 110 is fn+enter and i have no idea what that's supposed // to map to. also the enter key with numlock on is a modifier but i // don't know which. @@ -118,11 +121,18 @@ static const KeyEntry s_controlKeys[] = { // toggle modifiers { kKeyNumLock, s_numLockVK }, { kKeyCapsLock, s_capsLockVK }, - + { kKeyMissionControl, s_missionControlVK }, { kKeyLaunchpad, s_launchpadVK }, { kKeyBrightnessUp, s_brightnessUp }, - { kKeyBrightnessDown, s_brightnessDown } + { kKeyBrightnessDown, s_brightnessDown }, + + // JIS keyboards only + { kKeyEisuToggle, kVK_JIS_Eisu }, + { kKeyKana, kVK_JIS_Kana }, + { kKeyMuhenkan, s_int5VK }, + { kKeyHenkan, s_int4VK }, + { kKeyZenkaku, kVK_ANSI_Grave } }; @@ -159,7 +169,7 @@ OSXKeyState::init() // build virtual key map for (size_t i = 0; i < sizeof(s_controlKeys) / sizeof(s_controlKeys[0]); ++i) { - + m_virtualKeyMap[s_controlKeys[i].m_virtualKey] = s_controlKeys[i].m_keyID; } @@ -214,11 +224,11 @@ OSXKeyState::mapModifiersToCarbon(UInt32 mask) const if ((mask & kCGEventFlagMaskNumericPad) != 0) { outMask |= s_osxNumLock; } - + return outMask; } -KeyButton +KeyButton OSXKeyState::mapKeyFromEvent(KeyIDs& ids, KeyModifierMask* maskOut, CGEventRef event) const { @@ -253,7 +263,7 @@ OSXKeyState::mapKeyFromEvent(KeyIDs& ids, } // get keyboard info - TISInputSourceRef currentKeyboardLayout = TISCopyCurrentKeyboardLayoutInputSource(); + TISInputSourceRef currentKeyboardLayout = TISCopyCurrentKeyboardLayoutInputSource(); if (currentKeyboardLayout == NULL) { return kKeyNone; @@ -338,27 +348,27 @@ CGEventFlags OSXKeyState::getModifierStateAsOSXFlags() { CGEventFlags modifiers = CGEventFlags(0); - + if (m_shiftPressed) { modifiers |= CGEventFlags(kCGEventFlagMaskShift); } - + if (m_controlPressed) { modifiers |= CGEventFlags(kCGEventFlagMaskControl); } - + if (m_altPressed) { modifiers |= CGEventFlags(kCGEventFlagMaskAlternate); } - + if (m_superPressed) { modifiers |= CGEventFlags(kCGEventFlagMaskCommand); } - + if (m_capsPressed) { modifiers |= CGEventFlags(kCGEventFlagMaskAlphaShift); } - + return modifiers; } @@ -400,12 +410,12 @@ OSXKeyState::pollActiveGroup() const TISInputSourceRef keyboardLayout = TISCopyCurrentKeyboardLayoutInputSource(); CFDataRef id = (CFDataRef)TISGetInputSourceProperty( keyboardLayout, kTISPropertyInputSourceID); - + GroupMap::const_iterator i = m_groupMap.find(id); if (i != m_groupMap.end()) { return i->second; } - + LOG((CLOG_DEBUG "can't get the active group, use the first group instead")); return 0; @@ -447,7 +457,7 @@ OSXKeyState::getKeyMap(barrier::KeyMap& keyMap) const void* resource; bool layoutValid = false; - + // add regular keys // try uchr resource first CFDataRef resourceRef = (CFDataRef)TISGetInputSourceProperty( @@ -475,19 +485,19 @@ static io_connect_t getEventDriver(void) static mach_port_t sEventDrvrRef = 0; mach_port_t masterPort, service, iter; kern_return_t kr; - + if (!sEventDrvrRef) { // Get master device port kr = IOMasterPort(bootstrap_port, &masterPort); assert(KERN_SUCCESS == kr); - + kr = IOServiceGetMatchingServices(masterPort, IOServiceMatching(kIOHIDSystemClass), &iter); assert(KERN_SUCCESS == kr); - + service = IOIteratorNext(iter); assert(service); - + kr = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &sEventDrvrRef); assert(KERN_SUCCESS == kr); @@ -495,7 +505,7 @@ static io_connect_t getEventDriver(void) IOObjectRelease(service); IOObjectRelease(iter); } - + return sEventDrvrRef; } @@ -504,7 +514,7 @@ OSXKeyState::postHIDVirtualKey(const UInt8 virtualKeyCode, const bool postDown) { static UInt32 modifiers = 0; - + NXEventData event; IOGPoint loc = { 0, 0 }; UInt32 modifiersDelta = 0; @@ -541,7 +551,7 @@ OSXKeyState::postHIDVirtualKey(const UInt8 virtualKeyCode, m_capsPressed = postDown; break; } - + // update the modifier bit if (postDown) { modifiers |= modifiersDelta; @@ -549,7 +559,7 @@ OSXKeyState::postHIDVirtualKey(const UInt8 virtualKeyCode, else { modifiers &= ~modifiersDelta; } - + kern_return_t kr; event.key.keyCode = virtualKeyCode; kr = IOHIDPostEvent(getEventDriver(), NX_FLAGSCHANGED, loc, @@ -575,11 +585,11 @@ OSXKeyState::fakeKey(const Keystroke& keystroke) { switch (keystroke.m_type) { case Keystroke::kButton: { - + KeyButton button = keystroke.m_data.m_button.m_button; bool keyDown = keystroke.m_data.m_button.m_press; CGKeyCode virtualKey = mapKeyButtonToVirtualKey(button); - + LOG((CLOG_DEBUG1 " button=0x%04x virtualKey=0x%04x keyDown=%s", button, virtualKey, keyDown ? "down" : "up")); @@ -763,7 +773,7 @@ OSXKeyState::mapBarrierHotKeyToMac(KeyID key, KeyModifierMask mask, return false; } macVirtualKey = mapKeyButtonToVirtualKey(button); - + // calculate modifier mask macModifierMask = 0; if ((mask & KeyModifierShift) != 0) { @@ -784,10 +794,10 @@ OSXKeyState::mapBarrierHotKeyToMac(KeyID key, KeyModifierMask mask, if ((mask & KeyModifierNumLock) != 0) { macModifierMask |= s_osxNumLock; } - + return true; } - + void OSXKeyState::handleModifierKeys(void* target, KeyModifierMask oldMask, KeyModifierMask newMask) @@ -855,7 +865,7 @@ OSXKeyState::getGroups(GroupList& groups) const groups.clear(); for (CFIndex i = 0; i < n; ++i) { bool addToGroups = true; - TISInputSourceRef keyboardLayout = + TISInputSourceRef keyboardLayout = (TISInputSourceRef)CFArrayGetValueAtIndex(kbds, i); if (addToGroups) diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h index 4d92860..e688394 100644 --- a/src/lib/platform/OSXKeyState.h +++ b/src/lib/platform/OSXKeyState.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -67,7 +67,7 @@ public: Still required in a few places for translation calls. */ KeyModifierMask mapModifiersToCarbon(UInt32 mask) const; - + //! Map key event to keys /*! Converts a key event into a sequence of KeyIDs and the shadow modifier @@ -149,7 +149,7 @@ private: static UInt32 mapKeyButtonToVirtualKey(KeyButton keyButton); void init(); - + // Post a key event to HID manager. It posts an event to HID client, a // much lower level than window manager which's the target from carbon // CGEventPost diff --git a/src/lib/platform/OSXMediaKeySimulator.m b/src/lib/platform/OSXMediaKeySimulator.m deleted file mode 100644 index 5aacd10..0000000 --- a/src/lib/platform/OSXMediaKeySimulator.m +++ /dev/null @@ -1,92 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#import "platform/OSXMediaKeySimulator.h" - -#import - -int convertKeyIDToNXKeyType(KeyID id) -{ - // hidsystem/ev_keymap.h - // NX_KEYTYPE_SOUND_UP 0 - // NX_KEYTYPE_SOUND_DOWN 1 - // NX_KEYTYPE_BRIGHTNESS_UP 2 - // NX_KEYTYPE_BRIGHTNESS_DOWN 3 - // NX_KEYTYPE_MUTE 7 - // NX_KEYTYPE_EJECT 14 - // NX_KEYTYPE_PLAY 16 - // NX_KEYTYPE_NEXT 17 - // NX_KEYTYPE_PREVIOUS 18 - // NX_KEYTYPE_FAST 19 - // NX_KEYTYPE_REWIND 20 - - int type = -1; - switch (id) { - case kKeyAudioUp: - type = 0; - break; - case kKeyAudioDown: - type = 1; - break; - case kKeyBrightnessUp: - type = 2; - break; - case kKeyBrightnessDown: - type = 3; - break; - case kKeyAudioMute: - type = 7; - break; - case kKeyEject: - type = 14; - break; - case kKeyAudioPlay: - type = 16; - break; - case kKeyAudioNext: - type = 17; - break; - case kKeyAudioPrev: - type = 18; - break; - default: - break; - } - - return type; -} - -bool -fakeNativeMediaKey(KeyID id) -{ - - NSEvent* downRef = [NSEvent otherEventWithType:NSSystemDefined - location: NSMakePoint(0, 0) modifierFlags:0xa00 - timestamp:0 windowNumber:0 context:0 subtype:8 - data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xa) << 8) - data2:-1]; - CGEventRef downEvent = [downRef CGEvent]; - - NSEvent* upRef = [NSEvent otherEventWithType:NSSystemDefined - location: NSMakePoint(0, 0) modifierFlags:0xa00 - timestamp:0 windowNumber:0 context:0 subtype:8 - data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xb) << 8) - data2:-1]; - CGEventRef upEvent = [upRef CGEvent]; - - CGEventPost(0, downEvent); - CGEventPost(0, upEvent); - - return true; -} diff --git a/src/lib/platform/OSXMediaKeySimulator.mm b/src/lib/platform/OSXMediaKeySimulator.mm new file mode 100644 index 0000000..efc4251 --- /dev/null +++ b/src/lib/platform/OSXMediaKeySimulator.mm @@ -0,0 +1,92 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#import "platform/OSXMediaKeySimulator.h" + +#import + +int convertKeyIDToNXKeyType(KeyID id) +{ + // hidsystem/ev_keymap.h + // NX_KEYTYPE_SOUND_UP 0 + // NX_KEYTYPE_SOUND_DOWN 1 + // NX_KEYTYPE_BRIGHTNESS_UP 2 + // NX_KEYTYPE_BRIGHTNESS_DOWN 3 + // NX_KEYTYPE_MUTE 7 + // NX_KEYTYPE_EJECT 14 + // NX_KEYTYPE_PLAY 16 + // NX_KEYTYPE_NEXT 17 + // NX_KEYTYPE_PREVIOUS 18 + // NX_KEYTYPE_FAST 19 + // NX_KEYTYPE_REWIND 20 + + int type = -1; + switch (id) { + case kKeyAudioUp: + type = 0; + break; + case kKeyAudioDown: + type = 1; + break; + case kKeyBrightnessUp: + type = 2; + break; + case kKeyBrightnessDown: + type = 3; + break; + case kKeyAudioMute: + type = 7; + break; + case kKeyEject: + type = 14; + break; + case kKeyAudioPlay: + type = 16; + break; + case kKeyAudioNext: + type = 17; + break; + case kKeyAudioPrev: + type = 18; + break; + default: + break; + } + + return type; +} + +bool +fakeNativeMediaKey(KeyID id) +{ + + NSEvent* downRef = [NSEvent otherEventWithType:NSSystemDefined + location: NSMakePoint(0, 0) modifierFlags:0xa00 + timestamp:0 windowNumber:0 context:0 subtype:8 + data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xa) << 8) + data2:-1]; + CGEventRef downEvent = [downRef CGEvent]; + + NSEvent* upRef = [NSEvent otherEventWithType:NSSystemDefined + location: NSMakePoint(0, 0) modifierFlags:0xa00 + timestamp:0 windowNumber:0 context:0 subtype:8 + data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xb) << 8) + data2:-1]; + CGEventRef upEvent = [upRef CGEvent]; + + CGEventPost(kCGHIDEventTap, downEvent); + CGEventPost(kCGHIDEventTap, upEvent); + + return true; +} diff --git a/src/lib/platform/OSXMediaKeySupport.m b/src/lib/platform/OSXMediaKeySupport.m deleted file mode 100644 index 9c9dbc3..0000000 --- a/src/lib/platform/OSXMediaKeySupport.m +++ /dev/null @@ -1,154 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#import "platform/OSXMediaKeySupport.h" -#import -#import - -int convertKeyIDToNXKeyType(KeyID id) -{ - int type = -1; - - switch (id) { - case kKeyAudioUp: - type = NX_KEYTYPE_SOUND_UP; - break; - case kKeyAudioDown: - type = NX_KEYTYPE_SOUND_DOWN; - break; - case kKeyBrightnessUp: - type = NX_KEYTYPE_BRIGHTNESS_UP; - break; - case kKeyBrightnessDown: - type = NX_KEYTYPE_BRIGHTNESS_DOWN; - break; - case kKeyAudioMute: - type = NX_KEYTYPE_MUTE; - break; - case kKeyEject: - type = NX_KEYTYPE_EJECT; - break; - case kKeyAudioPlay: - type = NX_KEYTYPE_PLAY; - break; - case kKeyAudioNext: - type = NX_KEYTYPE_NEXT; - break; - case kKeyAudioPrev: - type = NX_KEYTYPE_PREVIOUS; - break; - default: - break; - } - - return type; -} - -static KeyID -convertNXKeyTypeToKeyID(uint32_t const type) -{ - KeyID id = 0; - - switch (type) { - case NX_KEYTYPE_SOUND_UP: - id = kKeyAudioUp; - break; - case NX_KEYTYPE_SOUND_DOWN: - id = kKeyAudioDown; - break; - case NX_KEYTYPE_MUTE: - id = kKeyAudioMute; - break; - case NX_KEYTYPE_EJECT: - id = kKeyEject; - break; - case NX_KEYTYPE_PLAY: - id = kKeyAudioPlay; - break; - case NX_KEYTYPE_FAST: - case NX_KEYTYPE_NEXT: - id = kKeyAudioNext; - break; - case NX_KEYTYPE_REWIND: - case NX_KEYTYPE_PREVIOUS: - id = kKeyAudioPrev; - break; - default: - break; - } - - return id; -} - -bool -isMediaKeyEvent(CGEventRef event) { - NSEvent* nsEvent = nil; - @try { - nsEvent = [NSEvent eventWithCGEvent: event]; - if ([nsEvent subtype] != 8) { - return false; - } - uint32_t const nxKeyId = ([nsEvent data1] & 0xFFFF0000) >> 16; - if (convertNXKeyTypeToKeyID (nxKeyId)) { - return true; - } - } @catch (NSException* e) { - } - return false; -} - -bool -getMediaKeyEventInfo(CGEventRef event, KeyID* const keyId, - bool* const down, bool* const isRepeat) { - NSEvent* nsEvent = nil; - @try { - nsEvent = [NSEvent eventWithCGEvent: event]; - } @catch (NSException* e) { - return false; - } - if (keyId) { - *keyId = convertNXKeyTypeToKeyID (([nsEvent data1] & 0xFFFF0000) >> 16); - } - if (down) { - *down = !([nsEvent data1] & 0x100); - } - if (isRepeat) { - *isRepeat = [nsEvent data1] & 0x1; - } - return true; -} - -bool -fakeNativeMediaKey(KeyID id) -{ - - NSEvent* downRef = [NSEvent otherEventWithType:NSSystemDefined - location: NSMakePoint(0, 0) modifierFlags:0xa00 - timestamp:0 windowNumber:0 context:0 subtype:8 - data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xa) << 8) - data2:-1]; - CGEventRef downEvent = [downRef CGEvent]; - - NSEvent* upRef = [NSEvent otherEventWithType:NSSystemDefined - location: NSMakePoint(0, 0) modifierFlags:0xa00 - timestamp:0 windowNumber:0 context:0 subtype:8 - data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xb) << 8) - data2:-1]; - CGEventRef upEvent = [upRef CGEvent]; - - CGEventPost(0, downEvent); - CGEventPost(0, upEvent); - - return true; -} diff --git a/src/lib/platform/OSXMediaKeySupport.mm b/src/lib/platform/OSXMediaKeySupport.mm new file mode 100644 index 0000000..a4d5767 --- /dev/null +++ b/src/lib/platform/OSXMediaKeySupport.mm @@ -0,0 +1,154 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#import "platform/OSXMediaKeySupport.h" +#import +#import + +int convertKeyIDToNXKeyType(KeyID id) +{ + int type = -1; + + switch (id) { + case kKeyAudioUp: + type = NX_KEYTYPE_SOUND_UP; + break; + case kKeyAudioDown: + type = NX_KEYTYPE_SOUND_DOWN; + break; + case kKeyBrightnessUp: + type = NX_KEYTYPE_BRIGHTNESS_UP; + break; + case kKeyBrightnessDown: + type = NX_KEYTYPE_BRIGHTNESS_DOWN; + break; + case kKeyAudioMute: + type = NX_KEYTYPE_MUTE; + break; + case kKeyEject: + type = NX_KEYTYPE_EJECT; + break; + case kKeyAudioPlay: + type = NX_KEYTYPE_PLAY; + break; + case kKeyAudioNext: + type = NX_KEYTYPE_NEXT; + break; + case kKeyAudioPrev: + type = NX_KEYTYPE_PREVIOUS; + break; + default: + break; + } + + return type; +} + +static KeyID +convertNXKeyTypeToKeyID(uint32_t const type) +{ + KeyID id = 0; + + switch (type) { + case NX_KEYTYPE_SOUND_UP: + id = kKeyAudioUp; + break; + case NX_KEYTYPE_SOUND_DOWN: + id = kKeyAudioDown; + break; + case NX_KEYTYPE_MUTE: + id = kKeyAudioMute; + break; + case NX_KEYTYPE_EJECT: + id = kKeyEject; + break; + case NX_KEYTYPE_PLAY: + id = kKeyAudioPlay; + break; + case NX_KEYTYPE_FAST: + case NX_KEYTYPE_NEXT: + id = kKeyAudioNext; + break; + case NX_KEYTYPE_REWIND: + case NX_KEYTYPE_PREVIOUS: + id = kKeyAudioPrev; + break; + default: + break; + } + + return id; +} + +bool +isMediaKeyEvent(CGEventRef event) { + NSEvent* nsEvent = nil; + @try { + nsEvent = [NSEvent eventWithCGEvent: event]; + if ([nsEvent subtype] != 8) { + return false; + } + uint32_t const nxKeyId = ([nsEvent data1] & 0xFFFF0000) >> 16; + if (convertNXKeyTypeToKeyID (nxKeyId)) { + return true; + } + } @catch (NSException* e) { + } + return false; +} + +bool +getMediaKeyEventInfo(CGEventRef event, KeyID* const keyId, + bool* const down, bool* const isRepeat) { + NSEvent* nsEvent = nil; + @try { + nsEvent = [NSEvent eventWithCGEvent: event]; + } @catch (NSException* e) { + return false; + } + if (keyId) { + *keyId = convertNXKeyTypeToKeyID (([nsEvent data1] & 0xFFFF0000) >> 16); + } + if (down) { + *down = !([nsEvent data1] & 0x100); + } + if (isRepeat) { + *isRepeat = [nsEvent data1] & 0x1; + } + return true; +} + +bool +fakeNativeMediaKey(KeyID id) +{ + + NSEvent* downRef = [NSEvent otherEventWithType:NSSystemDefined + location: NSMakePoint(0, 0) modifierFlags:0xa00 + timestamp:0 windowNumber:0 context:0 subtype:8 + data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xa) << 8) + data2:-1]; + CGEventRef downEvent = [downRef CGEvent]; + + NSEvent* upRef = [NSEvent otherEventWithType:NSSystemDefined + location: NSMakePoint(0, 0) modifierFlags:0xa00 + timestamp:0 windowNumber:0 context:0 subtype:8 + data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xb) << 8) + data2:-1]; + CGEventRef upEvent = [upRef CGEvent]; + + CGEventPost(kCGHIDEventTap, downEvent); + CGEventPost(kCGHIDEventTap, upEvent); + + return true; +} diff --git a/src/lib/platform/OSXPasteboardPeeker.h b/src/lib/platform/OSXPasteboardPeeker.h index 5105262..b69dba8 100644 --- a/src/lib/platform/OSXPasteboardPeeker.h +++ b/src/lib/platform/OSXPasteboardPeeker.h @@ -26,7 +26,7 @@ extern "C" { #endif CFStringRef getDraggedFileURL(); - + #if defined(__cplusplus) } #endif diff --git a/src/lib/platform/OSXPasteboardPeeker.m b/src/lib/platform/OSXPasteboardPeeker.m deleted file mode 100644 index ab39e26..0000000 --- a/src/lib/platform/OSXPasteboardPeeker.m +++ /dev/null @@ -1,37 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2013-2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#import "platform/OSXPasteboardPeeker.h" - -#import -#import -#import - -CFStringRef -getDraggedFileURL() -{ - NSString* pbName = NSDragPboard; - NSPasteboard* pboard = [NSPasteboard pasteboardWithName:pbName]; - - NSMutableString* string; - string = [[NSMutableString alloc] initWithCapacity:0]; - - NSArray* files = [pboard propertyListForType:NSFilenamesPboardType]; - for (id file in files) { - [string appendString: (NSString*)file]; - [string appendString: @"\0"]; - } - - return (CFStringRef)string; -} diff --git a/src/lib/platform/OSXPasteboardPeeker.mm b/src/lib/platform/OSXPasteboardPeeker.mm new file mode 100644 index 0000000..ab39e26 --- /dev/null +++ b/src/lib/platform/OSXPasteboardPeeker.mm @@ -0,0 +1,37 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2013-2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#import "platform/OSXPasteboardPeeker.h" + +#import +#import +#import + +CFStringRef +getDraggedFileURL() +{ + NSString* pbName = NSDragPboard; + NSPasteboard* pboard = [NSPasteboard pasteboardWithName:pbName]; + + NSMutableString* string; + string = [[NSMutableString alloc] initWithCapacity:0]; + + NSArray* files = [pboard propertyListForType:NSFilenamesPboardType]; + for (id file in files) { + [string appendString: (NSString*)file]; + [string appendString: @"\0"]; + } + + return (CFStringRef)string; +} diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index 68c7c68..691b74c 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -37,7 +37,7 @@ extern "C" { typedef int CGSConnectionID; CGError CGSSetConnectionProperty(CGSConnectionID cid, CGSConnectionID targetCID, CFStringRef key, CFTypeRef value); int _CGSDefaultConnection(); -} +} template @@ -98,10 +98,10 @@ public: virtual bool isPrimary() const; virtual void fakeDraggingFiles(DragFileList fileList); virtual std::string& getDraggingFilename(); - + const std::string& getDropTarget() const { return m_dropTarget; } void waitForCarbonLoop() const; - + protected: // IPlatformScreen overrides virtual void handleSystemEvent(const Event&, void*); @@ -112,7 +112,7 @@ private: void updateScreenShape(); void updateScreenShape(const CGDirectDisplayID, const CGDisplayChangeSummaryFlags); void postMouseEvent(CGPoint&) const; - + // convenience function to send events void sendEvent(Event::Type type, void* = NULL) const; void sendClipboardEvent(Event::Type type, ClipboardID id) const; @@ -124,16 +124,16 @@ private: // of the button pressed using the mac button mapping. bool onMouseButton(bool pressed, UInt16 macButton); bool onMouseWheel(SInt32 xDelta, SInt32 yDelta) const; - + void constructMouseButtonEventMap(); bool onKey(CGEventRef event); void onMediaKey(CGEventRef event); - + bool onHotKey(EventRef event) const; - - // Added here to allow the carbon cursor hack to be called. + + // Added here to allow the carbon cursor hack to be called. void showCursor(); void hideCursor(); @@ -172,9 +172,9 @@ private: static pascal OSStatus userSwitchCallback(EventHandlerCallRef nextHandler, EventRef theEvent, void* inUserData); - + // sleep / wakeup support - void watchSystemPowerThread(void*); + void watchSystemPowerThread(); static void testCanceled(CFRunLoopTimerRef timer, void*info); static void powerChangeCallback(void* refcon, io_service_t service, natural_t messageType, void* messageArgument); @@ -182,12 +182,12 @@ private: void* messageArgument); void handleConfirmSleep(const Event& event, void*); - + // global hotkey operating mode static bool isGlobalHotKeyOperatingModeAvailable(); static void setGlobalHotKeysEnabled(bool enabled); static bool getGlobalHotKeysEnabled(); - + // Quartz event tap support static CGEventRef handleCGInputEvent(CGEventTapProxy proxy, CGEventType type, @@ -197,12 +197,12 @@ private: CGEventType type, CGEventRef event, void* refcon); - + // convert CFString to char* static char* CFStringRefToUTF8String(CFStringRef aString); - - void getDropTargetThread(void*); - + + void get_drop_target_thread(); + private: struct HotKeyItem { public: @@ -225,13 +225,13 @@ private: kMouseButtonDown, kMouseButtonStateMax }; - + class MouseButtonState { public: void set(UInt32 button, EMouseButtonState state); bool any(); - void reset(); + void reset(); void overwrite(UInt32 buttons); bool test(UInt32 button) const; @@ -262,7 +262,7 @@ private: // mouse state mutable SInt32 m_xCursor, m_yCursor; mutable bool m_cursorPosValid; - + /* FIXME: this data structure is explicitly marked mutable due to a need to track the state of buttons since the remote side only lets us know of change events, and because the @@ -321,7 +321,7 @@ private: // global hotkey operating mode static bool s_testedForGHOM; static bool s_hasGHOM; - + // Quartz input event support CFMachPortRef m_eventTapPort; CFRunLoopSourceRef m_eventTapRLSR; @@ -336,10 +336,10 @@ private: bool m_autoShowHideCursor; IEventQueue* m_events; - + Thread* m_getDropTargetThread; std::string m_dropTarget; - + #if defined(MAC_OS_X_VERSION_10_7) Mutex* m_carbonLoopMutex; CondVar* m_carbonLoopReady; diff --git a/src/lib/platform/OSXScreen.mm b/src/lib/platform/OSXScreen.mm index 2b4594f..d41e321 100644 --- a/src/lib/platform/OSXScreen.mm +++ b/src/lib/platform/OSXScreen.mm @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -38,7 +38,6 @@ #include "base/Log.h" #include "base/IEventQueue.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" #include #include @@ -110,10 +109,10 @@ OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCurso updateScreenShape(m_displayID, 0); m_screensaver = new OSXScreenSaver(m_events, getEventTarget()); m_keyState = new OSXKeyState(m_events); - + // only needed when running as a server. if (m_isPrimary) { - + #if defined(MAC_OS_X_VERSION_10_9) // we can't pass options to show the dialog, this must be done by the gui. if (!AXIsProcessTrusted()) { @@ -126,7 +125,7 @@ OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCurso } #endif } - + // install display manager notification handler CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallback, this); @@ -157,8 +156,7 @@ OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCurso m_carbonLoopReady = new CondVar(m_carbonLoopMutex, false); #endif LOG((CLOG_DEBUG "starting watchSystemPowerThread")); - m_pmWatchThread = new Thread(new TMethodJob - (this, &OSXScreen::watchSystemPowerThread)); + m_pmWatchThread = new Thread([this](){ watchSystemPowerThread(); }); } catch (...) { m_events->removeHandler(m_events->forOSXScreen().confirmSleep(), @@ -166,7 +164,7 @@ OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCurso if (m_switchEventHandlerRef != 0) { RemoveEventHandler(m_switchEventHandlerRef); } - + CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this); delete m_keyState; @@ -217,7 +215,7 @@ OSXScreen::~OSXScreen() delete m_keyState; delete m_screensaver; - + #if defined(MAC_OS_X_VERSION_10_7) delete m_carbonLoopMutex; delete m_carbonLoopReady; @@ -273,7 +271,7 @@ OSXScreen::warpCursor(SInt32 x, SInt32 y) pos.x = x; pos.y = y; CGWarpMouseCursorPosition(pos); - + // save new cursor position m_xCursor = x; m_yCursor = y; @@ -325,7 +323,7 @@ OSXScreen::registerHotKey(KeyID key, KeyModifierMask mask) LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask)); return 0; } - + // choose hotkey id UInt32 id; if (!m_oldHotKeyIDs.empty()) { @@ -351,7 +349,7 @@ OSXScreen::registerHotKey(KeyID key, KeyModifierMask mask) } else { EventHotKeyID hkid = { 'SNRG', (UInt32)id }; - OSStatus status = RegisterEventHotKey(macKey, macMask, hkid, + OSStatus status = RegisterEventHotKey(macKey, macMask, hkid, GetApplicationEventTarget(), 0, &ref); okay = (status == noErr); @@ -366,7 +364,7 @@ OSXScreen::registerHotKey(KeyID key, KeyModifierMask mask) } m_hotKeys.insert(std::make_pair(id, HotKeyItem(ref, macKey, macMask))); - + LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", barrier::KeyMap::formatKey(key, mask).c_str(), key, mask, id)); return id; } @@ -468,7 +466,7 @@ OSXScreen::postMouseEvent(CGPoint& pos) const } } } - + CGEventType type = kCGEventMouseMoved; SInt8 button = m_buttonState.getFirstButtonDown(); @@ -478,10 +476,10 @@ OSXScreen::postMouseEvent(CGPoint& pos) const } CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, static_cast(button)); - + // Dragging events also need the click state CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState); - + // Fix for sticky keys CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags(); CGEventSetFlags(event, modifiers); @@ -503,7 +501,7 @@ OSXScreen::postMouseEvent(CGPoint& pos) const CGEventSetDoubleValueField(event, kCGMouseEventDeltaY, deltaFY); CGEventPost(kCGHIDEventTap, event); - + CFRelease(event); } @@ -515,7 +513,7 @@ OSXScreen::fakeMouseButton(ButtonID id, bool press) if (index >= NumButtonIDs) { return; } - + CGPoint pos; if (!m_cursorPosValid) { SInt32 x, y; @@ -533,79 +531,77 @@ OSXScreen::fakeMouseButton(ButtonID id, bool press) // since we don't have double click distance in NX APIs // we define our own defaults. const double maxDiff = sqrt(2) + 0.0001; - + double clickTime = [NSEvent doubleClickInterval]; - + // As long as the click is within the time window and distance window // increase clickState (double click, triple click, etc) - // This will allow for higher than triple click but the quartz documenation + // This will allow for higher than triple click but the quartz documentation // does not specify that this should be limited to triple click if (press) { - if ((ARCH->time() - m_lastClickTime) <= clickTime && diff <= maxDiff){ + if ((ARCH->time() - m_lastClickTime) <= clickTime && diff <= maxDiff) { m_clickState++; } else { m_clickState = 1; } - + m_lastClickTime = ARCH->time(); } - - if (m_clickState == 1){ + + if (m_clickState == 1) { m_lastSingleClickXCursor = m_xCursor; m_lastSingleClickYCursor = m_yCursor; } - + EMouseButtonState state = press ? kMouseButtonDown : kMouseButtonUp; - + LOG((CLOG_DEBUG1 "faking mouse button id: %d press: %s", index, press ? "pressed" : "released")); - + MouseButtonEventMapType thisButtonMap = MouseButtonEventMap[index]; CGEventType type = thisButtonMap[state]; CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, static_cast(index)); - + CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState); - + // Fix for sticky keys CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags(); CGEventSetFlags(event, modifiers); - + m_buttonState.set(index, state); CGEventPost(kCGHIDEventTap, event); - + CFRelease(event); - + if (!press && (id == kButtonLeft)) { if (m_fakeDraggingStarted) { - m_getDropTargetThread = new Thread(new TMethodJob( - this, &OSXScreen::getDropTargetThread)); + m_getDropTargetThread = new Thread([this](){ get_drop_target_thread(); }); } - + m_draggingStarted = false; } } -void -OSXScreen::getDropTargetThread(void*) +void OSXScreen::get_drop_target_thread() { #if defined(MAC_OS_X_VERSION_10_7) char* cstr = NULL; - + // wait for 5 secs for the drop destinaiton string to be filled. UInt32 timeout = ARCH->time() + 5; - + while (ARCH->time() < timeout) { CFStringRef cfstr = getCocoaDropTarget(); cstr = CFStringRefToUTF8String(cfstr); CFRelease(cfstr); - + if (cstr != NULL) { break; } ARCH->sleep(.1f); } - + if (cstr != NULL) { LOG((CLOG_DEBUG "drop target: %s", cstr)); m_dropTarget = cstr; @@ -626,12 +622,12 @@ OSXScreen::fakeMouseMove(SInt32 x, SInt32 y) if (m_fakeDraggingStarted) { m_buttonState.set(0, kMouseButtonDown); } - + // index 0 means left mouse button if (m_buttonState.test(0)) { m_draggingStarted = true; } - + // synthesize event CGPoint pos; pos.x = x; @@ -679,11 +675,11 @@ OSXScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const NULL, kCGScrollEventUnitLine, 2, mapScrollWheelFromBarrier(yDelta), -mapScrollWheelFromBarrier(xDelta)); - + // Fix for sticky keys CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags(); CGEventSetFlags(scrollEvent, modifiers); - + CGEventPost(kCGHIDEventTap, scrollEvent); CFRelease(scrollEvent); } @@ -754,11 +750,11 @@ OSXScreen::enable() if (m_isPrimary) { // FIXME -- start watching jump zones - + // kCGEventTapOptionDefault = 0x00000000 (Missing in 10.4, so specified literally) m_eventTapPort = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, - kCGEventMaskForAllEvents, - handleCGInputEvent, + kCGEventMaskForAllEvents, + handleCGInputEvent, this); } else { @@ -773,10 +769,10 @@ OSXScreen::enable() // there may be a better way to do this, but we register an event handler even if we're // not on the primary display (acting as a client). This way, if a local event comes in - // (either keyboard or mouse), we can make sure to show the cursor if we've hidden it. + // (either keyboard or mouse), we can make sure to show the cursor if we've hidden it. m_eventTapPort = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, - kCGEventMaskForAllEvents, - handleCGInputEventSecondary, + kCGEventMaskForAllEvents, + handleCGInputEventSecondary, this); } @@ -798,9 +794,9 @@ OSXScreen::disable() if (m_autoShowHideCursor) { showCursor(); } - + // FIXME -- stop watching jump zones, stop capturing input - + if (m_eventTapRLSR) { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode); CFRelease(m_eventTapRLSR); @@ -846,7 +842,7 @@ OSXScreen::enter() io_registry_entry_t entry = IORegistryEntryFromPath( kIOMasterPortDefault, "IOService:/IOResources/IODisplayWrangler"); - + if (entry != MACH_PORT_NULL) { IORegistryEntrySetCFProperty(entry, CFSTR("IORequestIdle"), kCFBooleanFalse); IOObjectRelease(entry); @@ -863,15 +859,15 @@ bool OSXScreen::leave() { hideCursor(); - + if (isDraggingStarted()) { String& fileList = getDraggingFilename(); - + if (!m_isPrimary) { if (fileList.empty() == false) { ClientApp& app = ClientApp::instance(); Client* client = app.getClientPtr(); - + DragInformation di; di.setFilename(fileList); DragFileList dragFileList; @@ -881,7 +877,7 @@ OSXScreen::leave() dragFileList, info); client->sendDragInfo(fileCount, info, info.size()); LOG((CLOG_DEBUG "send dragging file to server")); - + // TODO: what to do with multiple file or even // a folder client->sendFileToServer(fileList.c_str()); @@ -889,7 +885,7 @@ OSXScreen::leave() } m_draggingStarted = false; } - + if (m_isPrimary) { avoidHesitatingCursor(); @@ -906,8 +902,8 @@ OSXScreen::setClipboard(ClipboardID, const IClipboard* src) { if (src != NULL) { LOG((CLOG_DEBUG "setting clipboard")); - Clipboard::copy(&m_pasteboard, src); - } + Clipboard::copy(&m_pasteboard, src); + } return true; } @@ -1036,16 +1032,16 @@ OSXScreen::handleSystemEvent(const Event& event, void*) } break; - case kEventClassKeyboard: + case kEventClassKeyboard: switch (GetEventKind(*carbonEvent)) { case kEventHotKeyPressed: case kEventHotKeyReleased: onHotKey(*carbonEvent); break; } - + break; - + case kEventClassWindow: // 2nd param was formerly GetWindowEventTarget(m_userInputWindow) which is 32-bit only, // however as m_userInputWindow is never initialized to anything we can take advantage of @@ -1076,7 +1072,7 @@ OSXScreen::handleSystemEvent(const Event& event, void*) } } -bool +bool OSXScreen::onMouseMove(CGFloat mx, CGFloat my) { LOG((CLOG_DEBUG2 "mouse move %+f,%+f", mx, my)); @@ -1140,7 +1136,7 @@ OSXScreen::onMouseMove(CGFloat mx, CGFloat my) return true; } -bool +bool OSXScreen::onMouseButton(bool pressed, UInt16 macButton) { // Buttons 2 and 3 are inverted on the mac @@ -1176,7 +1172,7 @@ OSXScreen::onMouseButton(bool pressed, UInt16 macButton) } } } - + if (macButton == kButtonLeft) { EMouseButtonState state = pressed ? kMouseButtonDown : kMouseButtonUp; m_buttonState.set(kButtonLeft - 1, state); @@ -1186,10 +1182,9 @@ OSXScreen::onMouseButton(bool pressed, UInt16 macButton) } else { if (m_fakeDraggingStarted) { - m_getDropTargetThread = new Thread(new TMethodJob( - this, &OSXScreen::getDropTargetThread)); + m_getDropTargetThread = new Thread([this](){ get_drop_target_thread(); }); } - + m_draggingStarted = false; } } @@ -1218,16 +1213,16 @@ OSXScreen::displayReconfigurationCallback(CGDirectDisplayID displayID, CGDisplay // Closing or opening the lid when an external monitor is // connected causes an kCGDisplayBeginConfigurationFlag event - CGDisplayChangeSummaryFlags mask = kCGDisplayBeginConfigurationFlag | kCGDisplayMovedFlag | - kCGDisplaySetModeFlag | kCGDisplayAddFlag | kCGDisplayRemoveFlag | - kCGDisplayEnabledFlag | kCGDisplayDisabledFlag | - kCGDisplayMirrorFlag | kCGDisplayUnMirrorFlag | + CGDisplayChangeSummaryFlags mask = kCGDisplayBeginConfigurationFlag | kCGDisplayMovedFlag | + kCGDisplaySetModeFlag | kCGDisplayAddFlag | kCGDisplayRemoveFlag | + kCGDisplayEnabledFlag | kCGDisplayDisabledFlag | + kCGDisplayMirrorFlag | kCGDisplayUnMirrorFlag | kCGDisplayDesktopShapeChangedFlag; - + LOG((CLOG_DEBUG1 "event: display was reconfigured: %x %x %x", flags, mask, flags & mask)); if (flags & mask) { /* Something actually did change */ - + LOG((CLOG_DEBUG1 "event: screen changed shape; refreshing dimensions")); screen->updateScreenShape(displayID, flags); } @@ -1274,7 +1269,7 @@ OSXScreen::onKey(CGEventRef event) m_activeModifierHotKeyMask = 0; } } - + return true; } @@ -1342,7 +1337,7 @@ OSXScreen::onKey(CGEventRef event) } void -OSXScreen::onMediaKey(CGEventRef event) +OSXScreen::onMediaKey(CGEventRef event) { KeyID keyID; bool down; @@ -1405,7 +1400,7 @@ OSXScreen::mapBarrierButtonToMac(UInt16 button) const return static_cast(button); } -ButtonID +ButtonID OSXScreen::mapMacButtonToBarrier(UInt16 macButton) const { switch (macButton) { @@ -1418,7 +1413,7 @@ OSXScreen::mapMacButtonToBarrier(UInt16 macButton) const case 3: return kButtonMiddle; } - + return static_cast(macButton); } @@ -1445,8 +1440,8 @@ OSXScreen::getScrollSpeed() const double scaling = 0.0; CFPropertyListRef pref = ::CFPreferencesCopyValue( - CFSTR("com.apple.scrollwheel.scaling") , - kCFPreferencesAnyApplication, + CFSTR("com.apple.scrollwheel.scaling") , + kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); if (pref != NULL) { @@ -1535,7 +1530,7 @@ OSXScreen::updateScreenShape() if (CGGetActiveDisplayList(0, NULL, &displayCount) != CGDisplayNoErr) { return; } - + if (displayCount == 0) { return; } @@ -1579,13 +1574,13 @@ OSXScreen::updateScreenShape() (displayCount == 1) ? "display" : "displays")); } -#pragma mark - +#pragma mark - // // FAST USER SWITCH NOTIFICATION SUPPORT // // OSXScreen::userSwitchCallback(void*) -// +// // gets called if a fast user switch occurs // @@ -1611,18 +1606,17 @@ OSXScreen::userSwitchCallback(EventHandlerCallRef nextHandler, return (CallNextEventHandler(nextHandler, theEvent)); } -#pragma mark - +#pragma mark - // // SLEEP/WAKEUP NOTIFICATION SUPPORT // // OSXScreen::watchSystemPowerThread(void*) -// -// main of thread monitoring system power (sleep/wakup) using a CFRunLoop +// +// main of thread monitoring system power (sleep/wakeup) using a CFRunLoop // -void -OSXScreen::watchSystemPowerThread(void*) +void OSXScreen::watchSystemPowerThread() { io_object_t notifier; IONotificationPortRef notificationPortRef; @@ -1641,7 +1635,7 @@ OSXScreen::watchSystemPowerThread(void*) CFRunLoopAddSource(m_pmRunloop, runloopSourceRef, kCFRunLoopCommonModes); } - + // thread is ready { Lock lock(m_pmMutex); @@ -1658,15 +1652,15 @@ OSXScreen::watchSystemPowerThread(void*) } LOG((CLOG_DEBUG "started watchSystemPowerThread")); - + LOG((CLOG_DEBUG "waiting for event loop")); m_events->waitForReady(); - + #if defined(MAC_OS_X_VERSION_10_7) { Lock lockCarbon(m_carbonLoopMutex); if (*m_carbonLoopReady == false) { - + // we signalling carbon loop ready before starting // unless we know how to do it within the loop LOG((CLOG_DEBUG "signalling carbon loop ready")); @@ -1676,12 +1670,12 @@ OSXScreen::watchSystemPowerThread(void*) } } #endif - + // start the run loop LOG((CLOG_DEBUG "starting carbon loop")); CFRunLoopRun(); LOG((CLOG_DEBUG "carbon loop has stopped")); - + // cleanup if (notificationPortRef) { CFRunLoopRemoveSource(m_pmRunloop, @@ -1716,7 +1710,7 @@ OSXScreen::handlePowerChangeRequest(natural_t messageType, void* messageArg) getEventTarget(), messageArg, Event::kDontFreeData)); return; - + case kIOMessageSystemHasPoweredOn: LOG((CLOG_DEBUG "system wakeup")); m_events->addEvent(Event(m_events->forIScreen().resume(), @@ -1742,16 +1736,16 @@ OSXScreen::handleConfirmSleep(const Event& event, void*) if (m_pmRootPort != 0) { // deliver suspend event immediately. m_events->addEvent(Event(m_events->forIScreen().suspend(), - getEventTarget(), NULL, + getEventTarget(), NULL, Event::kDeliverImmediately)); - + LOG((CLOG_DEBUG "system will sleep")); IOAllowPowerChange(m_pmRootPort, messageArg); } } } -#pragma mark - +#pragma mark - // // GLOBAL HOTKEY OPERATING MODE SUPPORT (10.3) @@ -1941,7 +1935,7 @@ OSXScreen::handleCGInputEvent(CGEventTapProxy proxy, case kCGEventMouseMoved: pos = CGEventGetLocation(event); screen->onMouseMove(pos.x, pos.y); - + // The system ignores our cursor-centering calls if // we don't return the event. This should be harmless, // but might register as slight movement to other apps @@ -1980,10 +1974,10 @@ OSXScreen::handleCGInputEvent(CGEventTapProxy proxy, } break; } - + LOG((CLOG_DEBUG3 "unknown quartz event type: 0x%02x", type)); } - + if (screen->m_isOnScreen) { return event; } else { @@ -1992,38 +1986,38 @@ OSXScreen::handleCGInputEvent(CGEventTapProxy proxy, } void -OSXScreen::MouseButtonState::set(UInt32 button, EMouseButtonState state) +OSXScreen::MouseButtonState::set(UInt32 button, EMouseButtonState state) { bool newState = (state == kMouseButtonDown); m_buttons.set(button, newState); } bool -OSXScreen::MouseButtonState::any() +OSXScreen::MouseButtonState::any() { return m_buttons.any(); } void -OSXScreen::MouseButtonState::reset() +OSXScreen::MouseButtonState::reset() { m_buttons.reset(); } void -OSXScreen::MouseButtonState::overwrite(UInt32 buttons) +OSXScreen::MouseButtonState::overwrite(UInt32 buttons) { m_buttons = std::bitset(buttons); } bool -OSXScreen::MouseButtonState::test(UInt32 button) const +OSXScreen::MouseButtonState::test(UInt32 button) const { return m_buttons.test(button); } SInt8 -OSXScreen::MouseButtonState::getFirstButtonDown() const +OSXScreen::MouseButtonState::getFirstButtonDown() const { if (m_buttons.any()) { for (unsigned short button = 0; button < m_buttons.size(); button++) { @@ -2041,7 +2035,7 @@ OSXScreen::CFStringRefToUTF8String(CFStringRef aString) if (aString == NULL) { return NULL; } - + CFIndex length = CFStringGetLength(aString); CFIndex maxSize = CFStringGetMaximumSizeForEncoding( length, diff --git a/src/lib/platform/OSXScreenSaver.cpp b/src/lib/platform/OSXScreenSaver.cpp index a0282d9..4af761a 100644 --- a/src/lib/platform/OSXScreenSaver.cpp +++ b/src/lib/platform/OSXScreenSaver.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -48,17 +48,17 @@ OSXScreenSaver::OSXScreenSaver(IEventQueue* events, void* eventTarget) : launchEventTypes[0].eventKind = kEventAppLaunched; launchEventTypes[1].eventClass = kEventClassApplication; launchEventTypes[1].eventKind = kEventAppTerminated; - + EventHandlerUPP launchTerminationEventHandler = NewEventHandlerUPP(launchTerminationCallback); InstallApplicationEventHandler(launchTerminationEventHandler, 2, launchEventTypes, this, &m_launchTerminationEventHandlerRef); DisposeEventHandlerUPP(launchTerminationEventHandler); - + m_screenSaverPSN.highLongOfPSN = 0; m_screenSaverPSN.lowLongOfPSN = 0; - + if (isActive()) { getProcessSerialNumber("ScreenSaverEngine", m_screenSaverPSN); } @@ -128,7 +128,7 @@ OSXScreenSaver::processTerminated(ProcessSerialNumber psn) Event(m_events->forIPrimaryScreen().screensaverDeactivated(), m_eventTarget)); } - + m_screenSaverPSN.highLongOfPSN = 0; m_screenSaverPSN.lowLongOfPSN = 0; } @@ -140,7 +140,7 @@ OSXScreenSaver::launchTerminationCallback( EventRef theEvent, void* userData) { OSStatus result; - ProcessSerialNumber psn; + ProcessSerialNumber psn; EventParamType actualType; ByteCount actualSize; diff --git a/src/lib/platform/OSXScreenSaver.h b/src/lib/platform/OSXScreenSaver.h index 07f2a7b..9a040a7 100644 --- a/src/lib/platform/OSXScreenSaver.h +++ b/src/lib/platform/OSXScreenSaver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -36,11 +36,11 @@ public: virtual void activate(); virtual void deactivate(); virtual bool isActive() const; - + private: void processLaunched(ProcessSerialNumber psn); void processTerminated(ProcessSerialNumber psn); - + static pascal OSStatus launchTerminationCallback( EventHandlerCallRef nextHandler, diff --git a/src/lib/platform/OSXScreenSaverControl.h b/src/lib/platform/OSXScreenSaverControl.h index 76f8875..ff9d4f0 100644 --- a/src/lib/platform/OSXScreenSaverControl.h +++ b/src/lib/platform/OSXScreenSaverControl.h @@ -6,7 +6,7 @@ * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/OSXScreenSaverUtil.h b/src/lib/platform/OSXScreenSaverUtil.h index 045553d..2ccb3c0 100644 --- a/src/lib/platform/OSXScreenSaverUtil.h +++ b/src/lib/platform/OSXScreenSaverUtil.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/OSXScreenSaverUtil.m b/src/lib/platform/OSXScreenSaverUtil.m deleted file mode 100644 index 6d82f10..0000000 --- a/src/lib/platform/OSXScreenSaverUtil.m +++ /dev/null @@ -1,83 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#import "platform/OSXScreenSaverUtil.h" - -#import "platform/OSXScreenSaverControl.h" - -#import - -// -// screenSaverUtil functions -// -// Note: these helper functions exist only so we can avoid using ObjC++. -// autoconf/automake don't know about ObjC++ and I don't know how to -// teach them about it. -// - -void* -screenSaverUtilCreatePool() -{ - return [[NSAutoreleasePool alloc] init]; -} - -void -screenSaverUtilReleasePool(void* pool) -{ - [(NSAutoreleasePool*)pool release]; -} - -void* -screenSaverUtilCreateController() -{ - return [[ScreenSaverController controller] retain]; -} - -void -screenSaverUtilReleaseController(void* controller) -{ - [(ScreenSaverController*)controller release]; -} - -void -screenSaverUtilEnable(void* controller) -{ - [(ScreenSaverController*)controller setScreenSaverCanRun:YES]; -} - -void -screenSaverUtilDisable(void* controller) -{ - [(ScreenSaverController*)controller setScreenSaverCanRun:NO]; -} - -void -screenSaverUtilActivate(void* controller) -{ - [(ScreenSaverController*)controller setScreenSaverCanRun:YES]; - [(ScreenSaverController*)controller screenSaverStartNow]; -} - -void -screenSaverUtilDeactivate(void* controller, int isEnabled) -{ - [(ScreenSaverController*)controller screenSaverStopNow]; - [(ScreenSaverController*)controller setScreenSaverCanRun:isEnabled]; -} - -int -screenSaverUtilIsActive(void* controller) -{ - return [(ScreenSaverController*)controller screenSaverIsRunning]; -} diff --git a/src/lib/platform/OSXScreenSaverUtil.mm b/src/lib/platform/OSXScreenSaverUtil.mm new file mode 100644 index 0000000..6d82f10 --- /dev/null +++ b/src/lib/platform/OSXScreenSaverUtil.mm @@ -0,0 +1,83 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#import "platform/OSXScreenSaverUtil.h" + +#import "platform/OSXScreenSaverControl.h" + +#import + +// +// screenSaverUtil functions +// +// Note: these helper functions exist only so we can avoid using ObjC++. +// autoconf/automake don't know about ObjC++ and I don't know how to +// teach them about it. +// + +void* +screenSaverUtilCreatePool() +{ + return [[NSAutoreleasePool alloc] init]; +} + +void +screenSaverUtilReleasePool(void* pool) +{ + [(NSAutoreleasePool*)pool release]; +} + +void* +screenSaverUtilCreateController() +{ + return [[ScreenSaverController controller] retain]; +} + +void +screenSaverUtilReleaseController(void* controller) +{ + [(ScreenSaverController*)controller release]; +} + +void +screenSaverUtilEnable(void* controller) +{ + [(ScreenSaverController*)controller setScreenSaverCanRun:YES]; +} + +void +screenSaverUtilDisable(void* controller) +{ + [(ScreenSaverController*)controller setScreenSaverCanRun:NO]; +} + +void +screenSaverUtilActivate(void* controller) +{ + [(ScreenSaverController*)controller setScreenSaverCanRun:YES]; + [(ScreenSaverController*)controller screenSaverStartNow]; +} + +void +screenSaverUtilDeactivate(void* controller, int isEnabled) +{ + [(ScreenSaverController*)controller screenSaverStopNow]; + [(ScreenSaverController*)controller setScreenSaverCanRun:isEnabled]; +} + +int +screenSaverUtilIsActive(void* controller) +{ + return [(ScreenSaverController*)controller screenSaverIsRunning]; +} diff --git a/src/lib/platform/OSXUchrKeyResource.cpp b/src/lib/platform/OSXUchrKeyResource.cpp index e0230e9..8d16bfb 100644 --- a/src/lib/platform/OSXUchrKeyResource.cpp +++ b/src/lib/platform/OSXUchrKeyResource.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/OSXUchrKeyResource.h b/src/lib/platform/OSXUchrKeyResource.h index 47b63c9..8e1a813 100644 --- a/src/lib/platform/OSXUchrKeyResource.h +++ b/src/lib/platform/OSXUchrKeyResource.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -27,7 +27,7 @@ typedef TISInputSourceRef KeyLayout; class OSXUchrKeyResource : public IOSXKeyResource { public: OSXUchrKeyResource(const void*, UInt32 keyboardType); - + // KeyResource overrides virtual bool isValid() const; virtual UInt32 getNumModifierCombinations() const; @@ -35,15 +35,15 @@ public: virtual UInt32 getNumButtons() const; virtual UInt32 getTableForModifier(UInt32 mask) const; virtual KeyID getKey(UInt32 table, UInt32 button) const; - + private: typedef std::vector KeySequence; - + bool getDeadKey(KeySequence& keys, UInt16 index) const; bool getKeyRecord(KeySequence& keys, UInt16 index, UInt16& state) const; bool addSequence(KeySequence& keys, UCKeyCharSeq c) const; - + private: const UCKeyboardLayout* m_resource; const UCKeyModifiersToTableNum* m_m; diff --git a/src/lib/platform/XWindowsClipboard.cpp b/src/lib/platform/XWindowsClipboard.cpp index b0da695..27321e5 100644 --- a/src/lib/platform/XWindowsClipboard.cpp +++ b/src/lib/platform/XWindowsClipboard.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -84,9 +84,13 @@ XWindowsClipboard::XWindowsClipboard(IXWindowsImpl* impl, Display* display, // add converters, most desired first m_converters.push_back(new XWindowsClipboardHTMLConverter(m_display, "text/html")); + m_converters.push_back(new XWindowsClipboardHTMLConverter(m_display, + "application/x-moz-nativehtml")); m_converters.push_back(new XWindowsClipboardBMPConverter(m_display)); m_converters.push_back(new XWindowsClipboardUTF8Converter(m_display, "text/plain;charset=UTF-8")); + m_converters.push_back(new XWindowsClipboardUTF8Converter(m_display, + "text/plain;charset=utf-8")); m_converters.push_back(new XWindowsClipboardUTF8Converter(m_display, "UTF8_STRING")); m_converters.push_back(new XWindowsClipboardUCS2Converter(m_display, diff --git a/src/lib/platform/XWindowsClipboard.h b/src/lib/platform/XWindowsClipboard.h index 091036e..5f1b6cb 100644 --- a/src/lib/platform/XWindowsClipboard.h +++ b/src/lib/platform/XWindowsClipboard.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -25,11 +25,7 @@ #include "common/stdvector.h" #include "XWindowsImpl.h" -#if X_DISPLAY_MISSING -# error X11 is required to build barrier -#else -# include -#endif +#include class IXWindowsClipboardConverter; diff --git a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp index f6fed1c..0190f1d 100644 --- a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp +++ b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.h b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.h index 861b1b6..375d8cc 100644 --- a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.h +++ b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/XWindowsClipboardBMPConverter.cpp b/src/lib/platform/XWindowsClipboardBMPConverter.cpp index fcfdc69..fd3325e 100644 --- a/src/lib/platform/XWindowsClipboardBMPConverter.cpp +++ b/src/lib/platform/XWindowsClipboardBMPConverter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/XWindowsClipboardBMPConverter.h b/src/lib/platform/XWindowsClipboardBMPConverter.h index 4afa788..12716d1 100644 --- a/src/lib/platform/XWindowsClipboardBMPConverter.h +++ b/src/lib/platform/XWindowsClipboardBMPConverter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/XWindowsClipboardHTMLConverter.cpp b/src/lib/platform/XWindowsClipboardHTMLConverter.cpp index 6ae98a7..a13e80e 100644 --- a/src/lib/platform/XWindowsClipboardHTMLConverter.cpp +++ b/src/lib/platform/XWindowsClipboardHTMLConverter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/XWindowsClipboardHTMLConverter.h b/src/lib/platform/XWindowsClipboardHTMLConverter.h index 9a4ce61..b2a1a1e 100644 --- a/src/lib/platform/XWindowsClipboardHTMLConverter.h +++ b/src/lib/platform/XWindowsClipboardHTMLConverter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/XWindowsClipboardTextConverter.cpp b/src/lib/platform/XWindowsClipboardTextConverter.cpp index 2e18d91..ee34f13 100644 --- a/src/lib/platform/XWindowsClipboardTextConverter.cpp +++ b/src/lib/platform/XWindowsClipboardTextConverter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/XWindowsClipboardTextConverter.h b/src/lib/platform/XWindowsClipboardTextConverter.h index 99cdbcb..14e6c0f 100644 --- a/src/lib/platform/XWindowsClipboardTextConverter.h +++ b/src/lib/platform/XWindowsClipboardTextConverter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/XWindowsClipboardUCS2Converter.cpp b/src/lib/platform/XWindowsClipboardUCS2Converter.cpp index d3d5e04..75dd70e 100644 --- a/src/lib/platform/XWindowsClipboardUCS2Converter.cpp +++ b/src/lib/platform/XWindowsClipboardUCS2Converter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/XWindowsClipboardUCS2Converter.h b/src/lib/platform/XWindowsClipboardUCS2Converter.h index 16d880a..0bedb7a 100644 --- a/src/lib/platform/XWindowsClipboardUCS2Converter.h +++ b/src/lib/platform/XWindowsClipboardUCS2Converter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/XWindowsClipboardUTF8Converter.cpp b/src/lib/platform/XWindowsClipboardUTF8Converter.cpp index f470cf1..a452706 100644 --- a/src/lib/platform/XWindowsClipboardUTF8Converter.cpp +++ b/src/lib/platform/XWindowsClipboardUTF8Converter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/XWindowsClipboardUTF8Converter.h b/src/lib/platform/XWindowsClipboardUTF8Converter.h index 2219ed2..bb6cafb 100644 --- a/src/lib/platform/XWindowsClipboardUTF8Converter.h +++ b/src/lib/platform/XWindowsClipboardUTF8Converter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/platform/XWindowsEventQueueBuffer.cpp b/src/lib/platform/XWindowsEventQueueBuffer.cpp index 8b8c3b5..397973f 100644 --- a/src/lib/platform/XWindowsEventQueueBuffer.cpp +++ b/src/lib/platform/XWindowsEventQueueBuffer.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -104,7 +104,7 @@ XWindowsEventQueueBuffer::waitForEvent(double dtimeout) char buf[16]; ssize_t read_response = read(m_pipefd[0], buf, 15); - + // with linux automake, warnings are treated as errors by default if (read_response < 0) { @@ -181,7 +181,7 @@ XWindowsEventQueueBuffer::waitForEvent(double dtimeout) retval = poll(pfds, 2, TIMEOUT_DELAY); //16ms = 60hz, but we make it > to play nicely with the cpu if (pfds[1].revents & POLLIN) { ssize_t read_response = read(m_pipefd[0], buf, 15); - + // with linux automake, warnings are treated as errors by default if (read_response < 0) { diff --git a/src/lib/platform/XWindowsEventQueueBuffer.h b/src/lib/platform/XWindowsEventQueueBuffer.h index 13f6b16..dc35bf0 100644 --- a/src/lib/platform/XWindowsEventQueueBuffer.h +++ b/src/lib/platform/XWindowsEventQueueBuffer.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -23,11 +23,7 @@ #include "common/stdvector.h" #include "XWindowsImpl.h" -#if X_DISPLAY_MISSING -# error X11 is required to build barrier -#else -# include -#endif +#include class IEventQueue; diff --git a/src/lib/platform/XWindowsKeyState.cpp b/src/lib/platform/XWindowsKeyState.cpp index 9fb71ca..088adc6 100644 --- a/src/lib/platform/XWindowsKeyState.cpp +++ b/src/lib/platform/XWindowsKeyState.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -24,18 +24,14 @@ #include #include -#if X_DISPLAY_MISSING -# error X11 is required to build barrier -#else -# include -# include -# define XK_MISCELLANY -# define XK_XKB_KEYS -# include +#include +#include +#define XK_MISCELLANY +#define XK_XKB_KEYS +#include #if HAVE_XKB_EXTENSION # include #endif -#endif static const size_t ModifiersFromXDefaultSize = 32; @@ -348,7 +344,7 @@ XWindowsKeyState::updateKeysymMap(barrier::KeyMap& keyMap) else { tmpKeysyms[maxKeysyms * i + j] = NoSymbol; } - } + } } m_impl->XFree(allKeysyms); allKeysyms = tmpKeysyms; diff --git a/src/lib/platform/XWindowsKeyState.h b/src/lib/platform/XWindowsKeyState.h index f790390..ef35dd4 100644 --- a/src/lib/platform/XWindowsKeyState.h +++ b/src/lib/platform/XWindowsKeyState.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -23,18 +23,10 @@ #include "common/stdvector.h" #include "XWindowsImpl.h" -#if X_DISPLAY_MISSING -# error X11 is required to build barrier -#else -# include -# if HAVE_X11_EXTENSIONS_XTEST_H -# include -# else -# error The XTest extension is required to build barrier -# endif -# if HAVE_XKB_EXTENSION -# include -# endif +#include +#include +#if HAVE_XKB_EXTENSION +# include #endif class IEventQueue; @@ -136,7 +128,7 @@ private: bool m_lock; }; -#ifdef TEST_ENV +#ifdef BARRIER_TEST_ENV public: // yuck #endif typedef std::vector KeyModifierMaskList; @@ -169,7 +161,7 @@ private: // autorepeat state XKeyboardState m_keyboardState; -#ifdef TEST_ENV +#ifdef BARRIER_TEST_ENV public: SInt32 group() const { return m_group; } void group(const SInt32& group) { m_group = group; } diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index 5f1724c..8fb3569 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -97,7 +97,7 @@ XWindowsScreen::XWindowsScreen( if (mouseScrollDelta==0) m_mouseScrollDelta=120; s_screen = this; - + if (!disableXInitThreads) { // initializes Xlib support for concurrent threads. if (m_impl->XInitThreads() == 0) @@ -266,14 +266,14 @@ XWindowsScreen::enter() m_impl->DPMSForceLevel(m_display, DPMSModeOn); } #endif - + // unmap the hider/grab window. this also ungrabs the mouse and // keyboard if they're grabbed. m_impl->XUnmapWindow(m_display, m_window); /* maybe call this if entering for the screensaver // set keyboard focus to root window. the screensaver should then - // pick up key events for when the user enters a password to unlock. + // pick up key events for when the user enters a password to unlock. XSetInputFocus(m_display, PointerRoot, PointerRoot, CurrentTime); */ @@ -877,7 +877,7 @@ XWindowsScreen::openDisplay(const char* displayName) { // get the DISPLAY if (displayName == NULL) { - displayName = getenv("DISPLAY"); + displayName = std::getenv("DISPLAY"); if (displayName == NULL) { displayName = ":0.0"; } diff --git a/src/lib/platform/XWindowsScreen.h b/src/lib/platform/XWindowsScreen.h index 5573839..581e91f 100644 --- a/src/lib/platform/XWindowsScreen.h +++ b/src/lib/platform/XWindowsScreen.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -24,11 +24,7 @@ #include "common/stdvector.h" #include "XWindowsImpl.h" -#if X_DISPLAY_MISSING -# error X11 is required to build barrier -#else -# include -#endif +#include class XWindowsClipboard; class XWindowsKeyState; diff --git a/src/lib/platform/XWindowsScreenSaver.cpp b/src/lib/platform/XWindowsScreenSaver.cpp index 5c4ef91..d8787ab 100644 --- a/src/lib/platform/XWindowsScreenSaver.cpp +++ b/src/lib/platform/XWindowsScreenSaver.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -26,31 +26,11 @@ #include "base/TMethodEventJob.h" #include -#if HAVE_X11_EXTENSIONS_XTEST_H -# include -#else -# error The XTest extension is required to build barrier -#endif +#include #if HAVE_X11_EXTENSIONS_DPMS_H extern "C" { # include # include -# if !HAVE_DPMS_PROTOTYPES -# undef DPMSModeOn -# undef DPMSModeStandby -# undef DPMSModeSuspend -# undef DPMSModeOff -# define DPMSModeOn 0 -# define DPMSModeStandby 1 -# define DPMSModeSuspend 2 -# define DPMSModeOff 3 -extern Bool DPMSQueryExtension(Display *, int *, int *); -extern Bool DPMSCapable(Display *); -extern Status DPMSEnable(Display *); -extern Status DPMSDisable(Display *); -extern Status DPMSForceLevel(Display *, CARD16); -extern Status DPMSInfo(Display *, CARD16 *, BOOL *); -# endif } #endif @@ -490,7 +470,7 @@ XWindowsScreenSaver::addWatchXScreenSaver(Window window) } // if successful and window uses override_redirect (like xscreensaver - // does) then watch it for property changes. + // does) then watch it for property changes. if (!error && attr.override_redirect == True) { error = false; { diff --git a/src/lib/platform/XWindowsScreenSaver.h b/src/lib/platform/XWindowsScreenSaver.h index 1761db2..00285ec 100644 --- a/src/lib/platform/XWindowsScreenSaver.h +++ b/src/lib/platform/XWindowsScreenSaver.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -23,11 +23,7 @@ #include "common/stdmap.h" #include "XWindowsImpl.h" -#if X_DISPLAY_MISSING -# error X11 is required to build barrier -#else -# include -#endif +#include class Event; class EventQueueTimer; @@ -119,7 +115,7 @@ private: // the X display Display* m_display; - // window to receive xscreensaver repsonses + // window to receive xscreensaver responses Window m_xscreensaverSink; // the target for the events we generate diff --git a/src/lib/platform/XWindowsUtil.cpp b/src/lib/platform/XWindowsUtil.cpp index 3c90bd6..7d4bb63 100644 --- a/src/lib/platform/XWindowsUtil.cpp +++ b/src/lib/platform/XWindowsUtil.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -1250,7 +1250,7 @@ XK_uhorn // map "Internet" keys to KeyIDs static const KeySym s_map1008FF[] = { - /* 0x00 */ 0, 0, kKeyBrightnessUp, kKeyBrightnessDown, 0, 0, 0, 0, + /* 0x00 */ 0, 0, kKeyBrightnessUp, kKeyBrightnessDown, 0, kKeyKbdBrightnessUp, kKeyKbdBrightnessDown, 0, /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */ 0, kKeyAudioDown, kKeyAudioMute, kKeyAudioUp, /* 0x14 */ kKeyAudioPlay, kKeyAudioStop, kKeyAudioPrev, kKeyAudioNext, @@ -1285,6 +1285,45 @@ static const KeySym s_map1008FF[] = /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 }; +// map Sun keyboard keys to KeyIDs +// This is based on the the "Internet" keymap plus the missing keys +// Copy/Cut/Open/Paste/Props/Front +static const KeySym s_map1009FF[] = +{ + /* 0x00 */ 0, 0, kKeyBrightnessUp, kKeyBrightnessDown, 0, 0, 0, 0, + /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, kKeyAudioDown, kKeyAudioMute, kKeyAudioUp, + /* 0x14 */ kKeyAudioPlay, kKeyAudioStop, kKeyAudioPrev, kKeyAudioNext, + /* 0x18 */ kKeyWWWHome, kKeyAppMail, 0, kKeyWWWSearch, 0, 0, 0, 0, + /* 0x20 */ 0, 0, 0, 0, 0, 0, kKeyWWWBack, kKeyWWWForward, + /* 0x28 */ kKeyWWWStop, kKeyWWWRefresh, 0, 0, kKeyEject, 0, 0, 0, + /* 0x30 */ kKeyWWWFavorites, 0, kKeyAppMedia, 0, 0, 0, 0, 0, + /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x40 */ kKeyAppUser1, kKeyAppUser2, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, kKeyMissionControl, kKeyLaunchpad, 0, 0, 0, 0, + /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, kKeyCopy, + /* 0x58 */ kKeyCut, 0, 0, 0, 0, 0, 0, 0, + /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x68 */ 0, 0, 0, kKeyOpen, 0, kKeyPaste, 0, 0, + /* 0x70 */ kKeyProps, kKeyFront, 0, 0, 0, 0, 0, 0, + /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; // // XWindowsUtil @@ -1546,6 +1585,10 @@ XWindowsUtil::mapKeySymToKeyID(KeySym k) // "Internet" keys return s_map1008FF[k & 0xff]; + case 0x1009ff00: + // Additional Left-side keys provided by Sun Microsystems USB keyboards + return s_map1009FF[k & 0xff]; + default: { // lookup character in table KeySymMap::const_iterator index = s_keySymToUCS4.find(k); diff --git a/src/lib/platform/XWindowsUtil.h b/src/lib/platform/XWindowsUtil.h index f5b3ea8..938c332 100644 --- a/src/lib/platform/XWindowsUtil.h +++ b/src/lib/platform/XWindowsUtil.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -22,11 +22,7 @@ #include "common/stdmap.h" #include "common/stdvector.h" -#if X_DISPLAY_MISSING -# error X11 is required to build barrier -#else -# include -#endif +#include #include @@ -123,7 +119,7 @@ public: This class sets an X error handler in the c'tor and restores the previous error handler in the d'tor. A lock should only be installed while the display is locked by the thread. - + ErrorLock() ignores errors ErrorLock(bool* flag) sets *flag to true if any error occurs */ diff --git a/src/lib/platform/synwinhk.h b/src/lib/platform/synwinhk.h index 4b2d8e3..8fc4290 100644 --- a/src/lib/platform/synwinhk.h +++ b/src/lib/platform/synwinhk.h @@ -3,11 +3,11 @@ * Copyright (C) 2018 Debauchee Open Source Group * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/BaseClientProxy.cpp b/src/lib/server/BaseClientProxy.cpp index 6ccd251..78c4220 100644 --- a/src/lib/server/BaseClientProxy.cpp +++ b/src/lib/server/BaseClientProxy.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2006 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/BaseClientProxy.h b/src/lib/server/BaseClientProxy.h index a2c9459..5d5dc06 100644 --- a/src/lib/server/BaseClientProxy.h +++ b/src/lib/server/BaseClientProxy.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/CMakeLists.txt b/src/lib/server/CMakeLists.txt index 5242d6d..e1ed692 100644 --- a/src/lib/server/CMakeLists.txt +++ b/src/lib/server/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 00067ba..75724bc 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -36,25 +36,24 @@ ClientListener::ClientListener(const NetworkAddress& address, ISocketFactory* socketFactory, IEventQueue* events, - bool enableCrypto) : + ConnectionSecurityLevel security_level) : m_socketFactory(socketFactory), m_server(NULL), m_events(events), - m_useSecureNetwork(enableCrypto) + security_level_{security_level} { assert(m_socketFactory != NULL); try { - m_listen = m_socketFactory->createListen( - ARCH->getAddrFamily(address.getAddress()), - m_useSecureNetwork); + m_listen = m_socketFactory->createListen(ARCH->getAddrFamily(address.getAddress()), + security_level); // setup event handler m_events->adoptHandler(m_events->forIListenSocket().connecting(), m_listen, new TMethodEventJob(this, &ClientListener::handleClientConnecting)); - + // bind listen address LOG((CLOG_DEBUG1 "binding listen socket")); m_listen->bind(address); @@ -130,17 +129,17 @@ ClientListener::handleClientConnecting(const Event&, void*) if (socket == NULL) { return; } - + m_clientSockets.insert(socket); m_events->adoptHandler(m_events->forClientListener().accepted(), socket->getEventTarget(), new TMethodEventJob(this, &ClientListener::handleClientAccepted, socket)); - + // When using non SSL, server accepts clients immediately, while SSL // has to call secure accept which may require retry - if (!m_useSecureNetwork) { + if (security_level_ == ConnectionSecurityLevel::PLAINTEXT) { m_events->addEvent(Event(m_events->forClientListener().accepted(), socket->getEventTarget())); } @@ -152,7 +151,7 @@ ClientListener::handleClientAccepted(const Event&, void* vsocket) LOG((CLOG_NOTE "accepted client connection")); IDataSocket* socket = static_cast(vsocket); - + // filter socket messages, including a packetizing filter barrier::IStream* stream = new PacketStreamFilter(m_events, socket, false); assert(m_server != NULL); @@ -184,7 +183,6 @@ ClientListener::handleUnknownClient(const Event&, void* vclient) // get the real client proxy and install it ClientProxy* client = unknownClient->orphanClientProxy(); - bool handshakeOk = true; if (client != NULL) { // handshake was successful m_waitingClients.push_back(client); @@ -196,20 +194,17 @@ ClientListener::handleUnknownClient(const Event&, void* vclient) new TMethodEventJob(this, &ClientListener::handleClientDisconnected, client)); - } - else { - handshakeOk = false; + } else { + auto* stream = unknownClient->getStream(); + if (stream) { + stream->close(); + } } // now finished with unknown client m_events->removeHandler(m_events->forClientProxyUnknown().success(), client); m_events->removeHandler(m_events->forClientProxyUnknown().failure(), client); m_newClients.erase(unknownClient); - PacketStreamFilter* streamFileter = dynamic_cast(unknownClient->getStream()); - IDataSocket* socket = NULL; - if (streamFileter != NULL) { - socket = dynamic_cast(streamFileter->getStream()); - } delete unknownClient; } diff --git a/src/lib/server/ClientListener.h b/src/lib/server/ClientListener.h index b02cbb1..1debc2b 100644 --- a/src/lib/server/ClientListener.h +++ b/src/lib/server/ClientListener.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -23,6 +23,7 @@ #include "base/Event.h" #include "common/stddeque.h" #include "common/stdset.h" +#include "net/ConnectionSecurityLevel.h" class ClientProxy; class ClientProxyUnknown; @@ -36,10 +37,8 @@ class IDataSocket; class ClientListener { public: // The factories are adopted. - ClientListener(const NetworkAddress&, - ISocketFactory*, - IEventQueue* events, - bool enableCrypto); + ClientListener(const NetworkAddress&, ISocketFactory*, IEventQueue* events, + ConnectionSecurityLevel security_level); ~ClientListener(); //! @name manipulators @@ -86,6 +85,6 @@ private: WaitingClients m_waitingClients; Server* m_server; IEventQueue* m_events; - bool m_useSecureNetwork; + ConnectionSecurityLevel security_level_; ClientSockets m_clientSockets; }; diff --git a/src/lib/server/ClientProxy.cpp b/src/lib/server/ClientProxy.cpp index d91e186..76cdfa6 100644 --- a/src/lib/server/ClientProxy.cpp +++ b/src/lib/server/ClientProxy.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxy.h b/src/lib/server/ClientProxy.h index a3d87cb..b641011 100644 --- a/src/lib/server/ClientProxy.h +++ b/src/lib/server/ClientProxy.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxy1_0.cpp b/src/lib/server/ClientProxy1_0.cpp index 5cbaac2..33b0f15 100644 --- a/src/lib/server/ClientProxy1_0.cpp +++ b/src/lib/server/ClientProxy1_0.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -51,6 +51,10 @@ ClientProxy1_0::ClientProxy1_0(const std::string& name, barrier::IStream* stream stream->getEventTarget(), new TMethodEventJob(this, &ClientProxy1_0::handleDisconnect, NULL)); + m_events->adoptHandler(m_events->forIStream().inputFormatError(), + stream->getEventTarget(), + new TMethodEventJob(this, + &ClientProxy1_0::handleDisconnect, NULL)); m_events->adoptHandler(m_events->forIStream().outputShutdown(), stream->getEventTarget(), new TMethodEventJob(this, @@ -90,6 +94,8 @@ ClientProxy1_0::removeHandlers() getStream()->getEventTarget()); m_events->removeHandler(m_events->forIStream().outputShutdown(), getStream()->getEventTarget()); + m_events->removeHandler(m_events->forIStream().inputFormatError(), + getStream()->getEventTarget()); m_events->removeHandler(Event::kTimer, this); // remove timer @@ -148,9 +154,18 @@ ClientProxy1_0::handleData(const Event&, void*) } // parse message - LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); - if (!(this->*m_parser)(code)) { - LOG((CLOG_ERR "invalid message from client \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); + try { + LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); + if (!(this->*m_parser)(code)) { + LOG((CLOG_ERR "invalid message from client \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); + disconnect(); + return; + } + } catch (const XBadClient& e) { + // TODO: disconnect handling is currently dispersed across both parseMessage() and + // handleData() functions, we should collect that to a single place + + LOG((CLOG_ERR "protocol error from client: %s", e.what())); disconnect(); return; } @@ -173,6 +188,8 @@ ClientProxy1_0::parseHandshakeMessage(const UInt8* code) } else if (memcmp(code, kMsgDInfo, 4) == 0) { // future messages get parsed by parseMessage + // NOTE: we're taking address of virtual function here, + // not ClientProxy1_0 implementation of it. m_parser = &ClientProxy1_0::parseMessage; if (recvInfo()) { m_events->addEvent(Event(m_events->forClientProxy().ready(), getEventTarget())); diff --git a/src/lib/server/ClientProxy1_0.h b/src/lib/server/ClientProxy1_0.h index 98c68f3..45d7541 100644 --- a/src/lib/server/ClientProxy1_0.h +++ b/src/lib/server/ClientProxy1_0.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxy1_1.cpp b/src/lib/server/ClientProxy1_1.cpp index bb33ac1..6d2e007 100644 --- a/src/lib/server/ClientProxy1_1.cpp +++ b/src/lib/server/ClientProxy1_1.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxy1_1.h b/src/lib/server/ClientProxy1_1.h index ada4dcc..38fda52 100644 --- a/src/lib/server/ClientProxy1_1.h +++ b/src/lib/server/ClientProxy1_1.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxy1_2.cpp b/src/lib/server/ClientProxy1_2.cpp index e9527ef..5457dc0 100644 --- a/src/lib/server/ClientProxy1_2.cpp +++ b/src/lib/server/ClientProxy1_2.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxy1_2.h b/src/lib/server/ClientProxy1_2.h index 12d6b92..08b32fb 100644 --- a/src/lib/server/ClientProxy1_2.h +++ b/src/lib/server/ClientProxy1_2.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxy1_3.cpp b/src/lib/server/ClientProxy1_3.cpp index d0031ce..5012a4e 100644 --- a/src/lib/server/ClientProxy1_3.cpp +++ b/src/lib/server/ClientProxy1_3.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2006 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxy1_3.h b/src/lib/server/ClientProxy1_3.h index ad46cea..a459e1e 100644 --- a/src/lib/server/ClientProxy1_3.h +++ b/src/lib/server/ClientProxy1_3.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2006 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxy1_4.cpp b/src/lib/server/ClientProxy1_4.cpp index 9b12976..bc58626 100644 --- a/src/lib/server/ClientProxy1_4.cpp +++ b/src/lib/server/ClientProxy1_4.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxy1_4.h b/src/lib/server/ClientProxy1_4.h index cda090b..fa88234 100644 --- a/src/lib/server/ClientProxy1_4.h +++ b/src/lib/server/ClientProxy1_4.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index 40bba08..72c0bf8 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -86,7 +86,7 @@ ClientProxy1_5::fileChunkReceived() getStream(), server->getReceivedFileData(), server->getExpectedFileSize()); - + if (result == kFinish) { m_events->addEvent(Event(m_events->forFile().fileRecieveCompleted(), server)); @@ -106,6 +106,6 @@ ClientProxy1_5::dragInfoReceived() UInt32 fileNum = 0; std::string content; ProtocolUtil::readf(getStream(), kMsgDDragInfo + 4, &fileNum, &content); - + m_server->dragInfoReceived(fileNum, content); } diff --git a/src/lib/server/ClientProxy1_5.h b/src/lib/server/ClientProxy1_5.h index 4087730..2051f3c 100644 --- a/src/lib/server/ClientProxy1_5.h +++ b/src/lib/server/ClientProxy1_5.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index c829e84..29f3ce4 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -88,7 +88,7 @@ ClientProxy1_6::recvClipboard() // save clipboard m_clipboard[id].m_clipboard.unmarshall(dataCached, 0); m_clipboard[id].m_sequenceNumber = seq; - + // notify ClipboardInfo* info = new ClipboardInfo; info->m_id = id; diff --git a/src/lib/server/ClientProxy1_6.h b/src/lib/server/ClientProxy1_6.h index 830696a..a4c2e5d 100644 --- a/src/lib/server/ClientProxy1_6.h +++ b/src/lib/server/ClientProxy1_6.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/ClientProxyUnknown.cpp b/src/lib/server/ClientProxyUnknown.cpp index dc79da7..f9da361 100644 --- a/src/lib/server/ClientProxyUnknown.cpp +++ b/src/lib/server/ClientProxyUnknown.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -118,6 +118,10 @@ ClientProxyUnknown::addStreamHandlers() m_stream->getEventTarget(), new TMethodEventJob(this, &ClientProxyUnknown::handleDisconnect)); + m_events->adoptHandler(m_events->forIStream().inputFormatError(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &ClientProxyUnknown::handleDisconnect)); m_events->adoptHandler(m_events->forIStream().outputShutdown(), m_stream->getEventTarget(), new TMethodEventJob(this, @@ -149,6 +153,8 @@ ClientProxyUnknown::removeHandlers() m_stream->getEventTarget()); m_events->removeHandler(m_events->forIStream().inputShutdown(), m_stream->getEventTarget()); + m_events->removeHandler(m_events->forIStream().inputFormatError(), + m_stream->getEventTarget()); m_events->removeHandler(m_events->forIStream().outputShutdown(), m_stream->getEventTarget()); } diff --git a/src/lib/server/ClientProxyUnknown.h b/src/lib/server/ClientProxyUnknown.h index 5d59402..efb1b2b 100644 --- a/src/lib/server/ClientProxyUnknown.h +++ b/src/lib/server/ClientProxyUnknown.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/Config.cpp b/src/lib/server/Config.cpp index a47a391..bcdb88c 100644 --- a/src/lib/server/Config.cpp +++ b/src/lib/server/Config.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -1748,10 +1748,10 @@ std::ostream& operator<<(std::ostream& s, const Config& config) { // screens section - s << "section: screens" << std::endl; + s << "section: screens\n"; for (Config::const_iterator screen = config.begin(); screen != config.end(); ++screen) { - s << "\t" << screen->c_str() << ":" << std::endl; + s << "\t" << screen->c_str() << ":\n"; const Config::ScreenOptions* options = config.getOptions(*screen); if (options != NULL && options->size() > 0) { for (Config::ScreenOptions::const_iterator @@ -1760,31 +1760,31 @@ operator<<(std::ostream& s, const Config& config) const char* name = Config::getOptionName(option->first); std::string value = Config::getOptionValue(option->first, option->second); if (name != NULL && !value.empty()) { - s << "\t\t" << name << " = " << value << std::endl; + s << "\t\t" << name << " = " << value << "\n"; } } } } - s << "end" << std::endl; + s << "end\n"; // links section std::string neighbor; - s << "section: links" << std::endl; + s << "section: links\n"; for (Config::const_iterator screen = config.begin(); screen != config.end(); ++screen) { - s << "\t" << screen->c_str() << ":" << std::endl; + s << "\t" << screen->c_str() << ":\n"; for (Config::link_const_iterator link = config.beginNeighbor(*screen), - nend = config.endNeighbor(*screen); link != nend; ++link) { + nend = config.endNeighbor(*screen); link != nend; ++link) { s << "\t\t" << Config::dirName(link->first.getSide()) << Config::formatInterval(link->first.getInterval()) << " = " << link->second.getName().c_str() << Config::formatInterval(link->second.getInterval()) << - std::endl; + "\n"; } } - s << "end" << std::endl; + s << "end\n"; // aliases section (if there are any) if (config.m_map.size() != config.m_nameToCanonicalName.size()) { @@ -1802,20 +1802,20 @@ operator<<(std::ostream& s, const Config& config) // dump it std::string screen; - s << "section: aliases" << std::endl; + s << "section: aliases\n"; for (CMNameMap::const_iterator index = aliases.begin(); index != aliases.end(); ++index) { if (index->first != screen) { screen = index->first; - s << "\t" << screen.c_str() << ":" << std::endl; + s << "\t" << screen.c_str() << ":\n"; } - s << "\t\t" << index->second.c_str() << std::endl; + s << "\t\t" << index->second.c_str() << "\n"; } - s << "end" << std::endl; + s << "end\n"; } // options section - s << "section: options" << std::endl; + s << "section: options\n"; const Config::ScreenOptions* options = config.getOptions(""); if (options != NULL && options->size() > 0) { for (Config::ScreenOptions::const_iterator @@ -1824,16 +1824,16 @@ operator<<(std::ostream& s, const Config& config) const char* name = Config::getOptionName(option->first); std::string value = Config::getOptionValue(option->first, option->second); if (name != NULL && !value.empty()) { - s << "\t" << name << " = " << value << std::endl; + s << "\t" << name << " = " << value << "\n"; } } } if (config.m_barrierAddress.isValid()) { s << "\taddress = " << - config.m_barrierAddress.getHostname().c_str() << std::endl; + config.m_barrierAddress.getHostname().c_str() << "\n"; } s << config.m_inputFilter.format("\t"); - s << "end" << std::endl; + s << "end\n"; return s; } diff --git a/src/lib/server/Config.h b/src/lib/server/Config.h index c459393..17756a0 100644 --- a/src/lib/server/Config.h +++ b/src/lib/server/Config.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -174,7 +174,7 @@ public: Config(IEventQueue* events); virtual ~Config(); -#ifdef TEST_ENV +#ifdef BARRIER_TEST_ENV Config() : m_inputFilter(NULL) { } #endif diff --git a/src/lib/server/InputFilter.cpp b/src/lib/server/InputFilter.cpp index 38d9a84..a0dce17 100644 --- a/src/lib/server/InputFilter.cpp +++ b/src/lib/server/InputFilter.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2005 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -191,7 +191,7 @@ std::string InputFilter::MouseButtonCondition::format() const return barrier::string::sprintf("mousebutton(%s%d)", key.c_str(), m_button); } -InputFilter::EFilterStatus +InputFilter::EFilterStatus InputFilter::MouseButtonCondition::match(const Event& event) { static const KeyModifierMask s_ignoreMask = @@ -252,7 +252,7 @@ InputFilter::EFilterStatus InputFilter::ScreenConnectedCondition::match(const Event& event) { if (event.getType() == m_events->forServer().connected()) { - Server::ScreenConnectedInfo* info = + Server::ScreenConnectedInfo* info = static_cast(event.getData()); if (m_screen == info->m_screen || m_screen.empty()) { return kActivate; @@ -312,7 +312,7 @@ InputFilter::LockCursorToScreenAction::perform(const Event& event) }; // send event - Server::LockCursorToScreenInfo* info = + Server::LockCursorToScreenInfo* info = Server::LockCursorToScreenInfo::alloc(s_state[m_mode]); m_events->addEvent(Event(m_events->forServer().lockCursorToScreen(), event.getTarget(), info, @@ -350,7 +350,7 @@ InputFilter::SwitchToScreenAction::perform(const Event& event) // event if it has one. std::string screen = m_screen; if (screen.empty() && event.getType() == m_events->forServer().connected()) { - Server::ScreenConnectedInfo* info = + Server::ScreenConnectedInfo* info = static_cast(event.getData()); screen = info->m_screen; } @@ -493,7 +493,7 @@ InputFilter::KeyboardBroadcastAction::perform(const Event& event) }; // send event - Server::KeyboardBroadcastInfo* info = + Server::KeyboardBroadcastInfo* info = Server::KeyboardBroadcastInfo::alloc(s_state[m_mode], m_screens); m_events->addEvent(Event(m_events->forServer().keyboardBroadcast(), event.getTarget(), info, @@ -569,7 +569,7 @@ InputFilter::KeystrokeAction::perform(const Event& event) Event::Type type = m_press ? m_events->forIKeyState().keyDown() : m_events->forIKeyState().keyUp(); - + m_events->addEvent(Event(m_events->forIPrimaryScreen().fakeInputBegin(), event.getTarget(), NULL, Event::kDeliverImmediately)); diff --git a/src/lib/server/InputFilter.h b/src/lib/server/InputFilter.h index 0cb99da..5e6ef9c 100644 --- a/src/lib/server/InputFilter.h +++ b/src/lib/server/InputFilter.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2005 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -53,7 +53,7 @@ public: virtual void enablePrimary(PrimaryClient*); virtual void disablePrimary(PrimaryClient*); }; - + // KeystrokeCondition class KeystrokeCondition : public Condition { public: @@ -118,7 +118,7 @@ public: // ------------------------------------------------------------------------- // Input Filter Action Classes // ------------------------------------------------------------------------- - + class Action { public: Action(); @@ -129,7 +129,7 @@ public: virtual void perform(const Event&) = 0; }; - + // LockCursorToScreenAction class LockCursorToScreenAction : public Action { public: @@ -148,7 +148,7 @@ public: Mode m_mode; IEventQueue* m_events; }; - + // SwitchToScreenAction class SwitchToScreenAction : public Action { public: @@ -165,7 +165,7 @@ public: std::string m_screen; IEventQueue* m_events; }; - + // ToggleScreenAction class ToggleScreenAction : public Action { public: @@ -196,7 +196,7 @@ public: EDirection m_direction; IEventQueue* m_events; }; - + // KeyboardBroadcastAction class KeyboardBroadcastAction : public Action { public: @@ -333,7 +333,7 @@ public: InputFilter(const InputFilter&); virtual ~InputFilter(); -#ifdef TEST_ENV +#ifdef BARRIER_TEST_ENV InputFilter() : m_primaryClient(NULL) { } #endif diff --git a/src/lib/server/PrimaryClient.cpp b/src/lib/server/PrimaryClient.cpp index 04ae86c..6583e5d 100644 --- a/src/lib/server/PrimaryClient.cpp +++ b/src/lib/server/PrimaryClient.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/lib/server/PrimaryClient.h b/src/lib/server/PrimaryClient.h index 68b91e3..13be838 100644 --- a/src/lib/server/PrimaryClient.h +++ b/src/lib/server/PrimaryClient.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -37,7 +37,7 @@ public: PrimaryClient(const std::string& name, barrier::Screen* screen); ~PrimaryClient(); -#ifdef TEST_ENV +#ifdef BARRIER_TEST_ENV PrimaryClient() : BaseClientProxy("") { } #endif @@ -96,12 +96,12 @@ public: the edges of the screen, typically the center. */ void getCursorCenter(SInt32& x, SInt32& y) const; - + //! Get toggle key state /*! Returns the primary screen's current toggle modifier key state. */ - virtual KeyModifierMask + virtual KeyModifierMask getToggleMask() const; //! Get screen lock state diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 334049c..a169db1 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -39,7 +39,6 @@ #include "net/XSocket.h" #include "mt/Thread.h" #include "arch/Arch.h" -#include "base/TMethodJob.h" #include "base/IEventQueue.h" #include "base/Log.h" #include "base/TMethodEventJob.h" @@ -1136,9 +1135,9 @@ Server::processOptions() return; } - m_switchNeedsShift = false; // it seems if i don't add these + m_switchNeedsShift = false; // it seems if I don't add these m_switchNeedsControl = false; // lines, the 'reload config' option - m_switchNeedsAlt = false; // doesnt' work correct. + m_switchNeedsAlt = false; // doesn't work correct. bool newRelativeMoves = m_relativeMoves; for (Config::ScreenOptions::const_iterator index = options->begin(); @@ -1824,10 +1823,8 @@ Server::onMouseMovePrimary(SInt32 x, SInt32 y) && m_active != newScreen && m_waitDragInfoThread) { if (m_sendDragInfoThread == NULL) { - m_sendDragInfoThread = new Thread( - new TMethodJob( - this, - &Server::sendDragInfoThread, newScreen)); + m_sendDragInfoThread = new Thread([this, newScreen]() + { send_drag_info_thread(newScreen); }); } return false; @@ -1843,11 +1840,8 @@ Server::onMouseMovePrimary(SInt32 x, SInt32 y) return false; } -void -Server::sendDragInfoThread(void* arg) +void Server::send_drag_info_thread(BaseClientProxy* newScreen) { - BaseClientProxy* newScreen = static_cast(arg); - m_dragFileList.clear(); std::string& dragFileList = m_screen->getDraggingFilename(); if (!dragFileList.empty()) { @@ -2087,14 +2081,11 @@ void Server::onFileRecieveCompleted() { if (isReceivedFileSizeValid()) { - m_writeToDropDirThread = new Thread( - new TMethodJob( - this, &Server::writeToDropDirThread)); + m_writeToDropDirThread = new Thread([this]() { write_to_drop_dir_thread(); }); } } -void -Server::writeToDropDirThread(void*) +void Server::write_to_drop_dir_thread() { LOG((CLOG_DEBUG "starting write to drop dir thread")); @@ -2394,17 +2385,12 @@ Server::sendFileToClient(const char* filename) StreamChunker::interruptFile(); } - m_sendFileThread = new Thread( - new TMethodJob( - this, &Server::sendFileThread, - static_cast(const_cast(filename)))); + m_sendFileThread = new Thread([this, filename]() { send_file_thread(filename); }); } -void -Server::sendFileThread(void* data) +void Server::send_file_thread(const char* filename) { try { - char* filename = static_cast(data); LOG((CLOG_DEBUG "sending file to client, filename=%s", filename)); StreamChunker::sendFile(filename, m_events, this); } diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index bfd0a7d..ae8b2bd 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -110,7 +110,7 @@ public: barrier::Screen* screen, IEventQueue* events, ServerArgs const& args); ~Server(); -#ifdef TEST_ENV +#ifdef BARRIER_TEST_ENV Server() : m_mock(true), m_config(NULL) { } void setActive(BaseClientProxy* active) { m_active = active; } #endif @@ -150,7 +150,7 @@ public: //! Store ClientListener pointer void setListener(ClientListener* p) { m_clientListener = p; } - + //@} //! @name accessors //@{ @@ -166,8 +166,8 @@ public: Set the \c list to the names of the currently connected clients. */ void getClients(std::vector& list) const; - - //! Return true if recieved file size is valid + + //! Return true if received file size is valid bool isReceivedFileSizeValid(); //! Return expected file data size @@ -356,15 +356,15 @@ private: // force the cursor off of \p client void forceLeaveClient(BaseClientProxy* client); - - // thread funciton for sending file - void sendFileThread(void*); - + + // thread function for sending file + void send_file_thread(const char* filename); + // thread function for writing file to drop directory - void writeToDropDirThread(void*); + void write_to_drop_dir_thread(); // thread function for sending drag information - void sendDragInfoThread(void*); + void send_drag_info_thread(BaseClientProxy* newScreen); // send drag info to new client screen void sendDragInfo(BaseClientProxy* newScreen); @@ -448,7 +448,7 @@ private: bool m_switchNeedsShift; bool m_switchNeedsControl; bool m_switchNeedsAlt; - + // relative mouse move option bool m_relativeMoves; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt deleted file mode 100644 index daecb31..0000000 --- a/src/test/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -# barrier -- mouse and keyboard sharing utility -# Copyright (C) 2012-2016 Symless Ltd. -# Copyright (C) 2011 Nick Bolton -# -# This package is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# found in the file LICENSE that should have accompanied this file. -# -# This package is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -include_directories( - ../../ext/gtest - ../../ext/gtest/include - ../../ext/gmock - ../../ext/gmock/include) - -add_library(gtest STATIC ../../ext/gtest/src/gtest-all.cc) -add_library(gmock STATIC ../../ext/gmock/src/gmock-all.cc) - -if (UNIX) - # ignore warnings in gtest and gmock - set_target_properties(gtest PROPERTIES COMPILE_FLAGS "-w") - set_target_properties(gmock PROPERTIES COMPILE_FLAGS "-w") -endif() - -add_subdirectory(integtests) -add_subdirectory(unittests) diff --git a/src/test/global/TestEventQueue.cpp b/src/test/global/TestEventQueue.cpp index 4dd01e7..253e9ab 100644 --- a/src/test/global/TestEventQueue.cpp +++ b/src/test/global/TestEventQueue.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -23,7 +23,7 @@ #include void -TestEventQueue::raiseQuitEvent() +TestEventQueue::raiseQuitEvent() { addEvent(Event(Event::kQuit)); } diff --git a/src/test/global/TestEventQueue.h b/src/test/global/TestEventQueue.h index b932508..14568b6 100644 --- a/src/test/global/TestEventQueue.h +++ b/src/test/global/TestEventQueue.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/test/global/TestUtils.cpp b/src/test/global/TestUtils.cpp new file mode 100644 index 0000000..6a3193b --- /dev/null +++ b/src/test/global/TestUtils.cpp @@ -0,0 +1,37 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "TestUtils.h" +#include + +namespace barrier { + +std::vector generate_pseudo_random_bytes(std::size_t seed, std::size_t size) +{ + std::mt19937_64 engine{seed}; + std::uniform_int_distribution dist{0, 255}; + std::vector bytes; + + bytes.reserve(size); + for (std::size_t i = 0; i < size; ++i) { + bytes.push_back(dist(engine)); + } + + return bytes; +} + +} // namespace barrier diff --git a/src/test/global/TestUtils.h b/src/test/global/TestUtils.h new file mode 100644 index 0000000..31050ec --- /dev/null +++ b/src/test/global/TestUtils.h @@ -0,0 +1,30 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_TEST_GLOBAL_TEST_UTILS_H +#define BARRIER_TEST_GLOBAL_TEST_UTILS_H + +#include +#include + +namespace barrier { + +std::vector generate_pseudo_random_bytes(std::size_t seed, std::size_t size); + +} // namespace barrier + +#endif // BARRIER_TEST_GLOBAL_TEST_UTILS_H diff --git a/src/test/global/gmock.h b/src/test/global/gmock.h index 64597f4..8a27440 100644 --- a/src/test/global/gmock.h +++ b/src/test/global/gmock.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/test/global/gtest.h b/src/test/global/gtest.h index 0b2acbc..55cb10f 100644 --- a/src/test/global/gtest.h +++ b/src/test/global/gtest.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/test/guitests/src/VersionCheckerTests.cpp b/src/test/guitests/src/VersionCheckerTests.cpp index 0efc5f9..82212a7 100644 --- a/src/test/guitests/src/VersionCheckerTests.cpp +++ b/src/test/guitests/src/VersionCheckerTests.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/test/guitests/src/VersionCheckerTests.h b/src/test/guitests/src/VersionCheckerTests.h index 7884f3a..4273701 100644 --- a/src/test/guitests/src/VersionCheckerTests.h +++ b/src/test/guitests/src/VersionCheckerTests.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/test/guitests/src/main.cpp b/src/test/guitests/src/main.cpp index 2ff6e72..6b7677e 100644 --- a/src/test/guitests/src/main.cpp +++ b/src/test/guitests/src/main.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/test/integtests/CMakeLists.txt b/src/test/integtests/CMakeLists.txt index 0460d8d..cdb8844 100644 --- a/src/test/integtests/CMakeLists.txt +++ b/src/test/integtests/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -68,8 +68,6 @@ endif() include_directories( ../../ - ../../../ext/gtest/include - ../../../ext/gmock/include ) if (UNIX) @@ -80,4 +78,4 @@ endif() add_executable(integtests ${sources}) target_link_libraries(integtests - arch base client common io ipc mt net platform server synlib gtest gmock ${libs} ${OPENSSL_LIBS}) + arch base client common io ipc mt net platform server synlib ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES} ${libs} ${OPENSSL_LIBS}) diff --git a/src/test/integtests/Main.cpp b/src/test/integtests/Main.cpp index 76b42b6..f4eaca7 100644 --- a/src/test/integtests/Main.cpp +++ b/src/test/integtests/Main.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -44,7 +44,7 @@ main(int argc, char **argv) Arch arch; arch.init(); - + Log log; log.setFilter(kDEBUG2); @@ -67,7 +67,7 @@ main(int argc, char **argv) if (!lockFile.empty()) { unlock(lockFile); } - + // gtest seems to randomly finish with error codes (e.g. -1, -1073741819) // even when no tests have failed. not sure what causes this, but it // happens on all platforms and keeps leading to false positives. @@ -80,7 +80,7 @@ void lock(string lockFile) { double start = ARCH->time(); - + // keep checking until timeout is reached. while ((ARCH->time() - start) < LOCK_TIMEOUT) { @@ -102,7 +102,7 @@ lock(string lockFile) } void -unlock(string lockFile) +unlock(string lockFile) { remove(lockFile.c_str()); } diff --git a/src/test/integtests/ipc/IpcTests.cpp b/src/test/integtests/ipc/IpcTests.cpp index a0ee241..ce15d59 100644 --- a/src/test/integtests/ipc/IpcTests.cpp +++ b/src/test/integtests/ipc/IpcTests.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -19,7 +19,7 @@ // TODO: fix, tests failing intermittently on mac. #ifndef WINAPI_CARBON -#define TEST_ENV +#define BARRIER_TEST_ENV #include "test/global/TestEventQueue.h" #include "ipc/IpcServer.h" @@ -31,7 +31,6 @@ #include "net/SocketMultiplexer.h" #include "mt/Thread.h" #include "arch/Arch.h" -#include "base/TMethodJob.h" #include "base/String.h" #include "base/Log.h" #include "base/EventQueue.h" @@ -46,7 +45,7 @@ class IpcTests : public ::testing::Test public: IpcTests(); virtual ~IpcTests(); - + void connectToServer_handleMessageReceived(const Event&, void*); void sendMessageToServer_serverHandleMessageReceived(const Event&, void*); void sendMessageToClient_serverHandleClientConnected(const Event&, void*); @@ -76,15 +75,15 @@ TEST_F(IpcTests, connectToServer) m_events.forIpcServer().messageReceived(), &server, new TMethodEventJob( this, &IpcTests::connectToServer_handleMessageReceived)); - + IpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT); client.connect(); - + m_events.initQuitTimeout(5); m_events.loop(); m_events.removeHandler(m_events.forIpcServer().messageReceived(), &server); m_events.cleanupQuitTimeout(); - + EXPECT_EQ(true, m_connectToServer_helloMessageReceived); EXPECT_EQ(true, m_connectToServer_hasClientNode); } @@ -94,13 +93,13 @@ TEST_F(IpcTests, sendMessageToServer) SocketMultiplexer socketMultiplexer; IpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT); server.listen(); - + // event handler sends "test" command to server. m_events.adoptHandler( m_events.forIpcServer().messageReceived(), &server, new TMethodEventJob( this, &IpcTests::sendMessageToServer_serverHandleMessageReceived)); - + IpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT); client.connect(); m_sendMessageToServer_client = &client; @@ -128,7 +127,7 @@ TEST_F(IpcTests, sendMessageToClient) IpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT); client.connect(); - + m_events.adoptHandler( m_events.forIpcClient().messageReceived(), &client, new TMethodEventJob( diff --git a/src/test/integtests/net/NetworkTests.cpp b/src/test/integtests/net/NetworkTests.cpp index d404abc..92767bf 100644 --- a/src/test/integtests/net/NetworkTests.cpp +++ b/src/test/integtests/net/NetworkTests.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2013-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -18,7 +18,7 @@ // TODO: fix, tests failing intermittently on mac. #ifndef WINAPI_CARBON -#define TEST_ENV +#define BARRIER_TEST_ENV #include "test/mock/server/MockConfig.h" #include "test/mock/server/MockPrimaryClient.h" @@ -36,7 +36,6 @@ #include "net/TCPSocketFactory.h" #include "mt/Thread.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" #include "base/Log.h" #include @@ -84,19 +83,19 @@ public: } void sendMockData(void* eventTarget); - + void sendToClient_mockData_handleClientConnected(const Event&, void* vlistener); void sendToClient_mockData_fileRecieveCompleted(const Event&, void*); - + void sendToClient_mockFile_handleClientConnected(const Event&, void* vlistener); void sendToClient_mockFile_fileRecieveCompleted(const Event& event, void*); - + void sendToServer_mockData_handleClientConnected(const Event&, void* vlistener); void sendToServer_mockData_fileRecieveCompleted(const Event& event, void*); void sendToServer_mockFile_handleClientConnected(const Event&, void* vlistener); void sendToServer_mockFile_fileRecieveCompleted(const Event& event, void*); - + public: TestEventQueue m_events; UInt8* m_mockData; @@ -111,16 +110,17 @@ TEST_F(NetworkTests, sendToClient_mockData) NetworkAddress serverAddress(TEST_HOST, TEST_PORT); serverAddress.resolve(); - + // server SocketMultiplexer serverSocketMultiplexer; TCPSocketFactory* serverSocketFactory = new TCPSocketFactory(&m_events, &serverSocketMultiplexer); - ClientListener listener(serverAddress, serverSocketFactory, &m_events, false); + ClientListener listener(serverAddress, serverSocketFactory, &m_events, + ConnectionSecurityLevel::PLAINTEXT); NiceMock serverScreen; NiceMock primaryClient; NiceMock serverConfig; NiceMock serverInputFilter; - + m_events.adoptHandler( m_events.forClientListener().connected(), &listener, new TMethodEventJob( @@ -128,7 +128,7 @@ TEST_F(NetworkTests, sendToClient_mockData) ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true)); ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter)); - + ServerArgs serverArgs; serverArgs.m_enableDragDrop = true; Server server(serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs); @@ -139,7 +139,7 @@ TEST_F(NetworkTests, sendToClient_mockData) NiceMock clientScreen; SocketMultiplexer clientSocketMultiplexer; TCPSocketFactory* clientSocketFactory = new TCPSocketFactory(&m_events, &clientSocketMultiplexer); - + ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape)); ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); @@ -148,7 +148,7 @@ TEST_F(NetworkTests, sendToClient_mockData) clientArgs.m_enableDragDrop = true; clientArgs.m_enableCrypto = false; Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, clientArgs); - + m_events.adoptHandler( m_events.forFile().fileRecieveCompleted(), &client, new TMethodEventJob( @@ -169,16 +169,17 @@ TEST_F(NetworkTests, sendToClient_mockFile) NetworkAddress serverAddress(TEST_HOST, TEST_PORT); serverAddress.resolve(); - + // server SocketMultiplexer serverSocketMultiplexer; TCPSocketFactory* serverSocketFactory = new TCPSocketFactory(&m_events, &serverSocketMultiplexer); - ClientListener listener(serverAddress, serverSocketFactory, &m_events, false); + ClientListener listener(serverAddress, serverSocketFactory, &m_events, + ConnectionSecurityLevel::PLAINTEXT); NiceMock serverScreen; NiceMock primaryClient; NiceMock serverConfig; NiceMock serverInputFilter; - + m_events.adoptHandler( m_events.forClientListener().connected(), &listener, new TMethodEventJob( @@ -186,7 +187,7 @@ TEST_F(NetworkTests, sendToClient_mockFile) ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true)); ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter)); - + ServerArgs serverArgs; serverArgs.m_enableDragDrop = true; Server server(serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs); @@ -197,7 +198,7 @@ TEST_F(NetworkTests, sendToClient_mockFile) NiceMock clientScreen; SocketMultiplexer clientSocketMultiplexer; TCPSocketFactory* clientSocketFactory = new TCPSocketFactory(&m_events, &clientSocketMultiplexer); - + ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape)); ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); @@ -206,7 +207,7 @@ TEST_F(NetworkTests, sendToClient_mockFile) clientArgs.m_enableDragDrop = true; clientArgs.m_enableCrypto = false; Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, clientArgs); - + m_events.adoptHandler( m_events.forFile().fileRecieveCompleted(), &client, new TMethodEventJob( @@ -230,7 +231,8 @@ TEST_F(NetworkTests, sendToServer_mockData) // server SocketMultiplexer serverSocketMultiplexer; TCPSocketFactory* serverSocketFactory = new TCPSocketFactory(&m_events, &serverSocketMultiplexer); - ClientListener listener(serverAddress, serverSocketFactory, &m_events, false); + ClientListener listener(serverAddress, serverSocketFactory, &m_events, + ConnectionSecurityLevel::PLAINTEXT); NiceMock serverScreen; NiceMock primaryClient; NiceMock serverConfig; @@ -238,7 +240,7 @@ TEST_F(NetworkTests, sendToServer_mockData) ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true)); ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter)); - + ServerArgs serverArgs; serverArgs.m_enableDragDrop = true; Server server(serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs); @@ -249,7 +251,7 @@ TEST_F(NetworkTests, sendToServer_mockData) NiceMock clientScreen; SocketMultiplexer clientSocketMultiplexer; TCPSocketFactory* clientSocketFactory = new TCPSocketFactory(&m_events, &clientSocketMultiplexer); - + ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape)); ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); @@ -257,7 +259,7 @@ TEST_F(NetworkTests, sendToServer_mockData) clientArgs.m_enableDragDrop = true; clientArgs.m_enableCrypto = false; Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, clientArgs); - + m_events.adoptHandler( m_events.forClientListener().connected(), &listener, new TMethodEventJob( @@ -287,7 +289,8 @@ TEST_F(NetworkTests, sendToServer_mockFile) // server SocketMultiplexer serverSocketMultiplexer; TCPSocketFactory* serverSocketFactory = new TCPSocketFactory(&m_events, &serverSocketMultiplexer); - ClientListener listener(serverAddress, serverSocketFactory, &m_events, false); + ClientListener listener(serverAddress, serverSocketFactory, &m_events, + ConnectionSecurityLevel::PLAINTEXT); NiceMock serverScreen; NiceMock primaryClient; NiceMock serverConfig; @@ -295,7 +298,7 @@ TEST_F(NetworkTests, sendToServer_mockFile) ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true)); ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter)); - + ServerArgs serverArgs; serverArgs.m_enableDragDrop = true; Server server(serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs); @@ -306,7 +309,7 @@ TEST_F(NetworkTests, sendToServer_mockFile) NiceMock clientScreen; SocketMultiplexer clientSocketMultiplexer; TCPSocketFactory* clientSocketFactory = new TCPSocketFactory(&m_events, &clientSocketMultiplexer); - + ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape)); ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); @@ -334,7 +337,7 @@ TEST_F(NetworkTests, sendToServer_mockFile) m_events.cleanupQuitTimeout(); } -void +void NetworkTests::sendToClient_mockData_handleClientConnected(const Event&, void* vlistener) { ClientListener* listener = static_cast(vlistener); @@ -352,7 +355,7 @@ NetworkTests::sendToClient_mockData_handleClientConnected(const Event&, void* vl sendMockData(server); } -void +void NetworkTests::sendToClient_mockData_fileRecieveCompleted(const Event& event, void*) { Client* client = static_cast(event.getTarget()); @@ -361,7 +364,7 @@ NetworkTests::sendToClient_mockData_fileRecieveCompleted(const Event& event, voi m_events.raiseQuitEvent(); } -void +void NetworkTests::sendToClient_mockFile_handleClientConnected(const Event&, void* vlistener) { ClientListener* listener = static_cast(vlistener); @@ -379,7 +382,7 @@ NetworkTests::sendToClient_mockFile_handleClientConnected(const Event&, void* vl server->sendFileToClient(kMockFilename); } -void +void NetworkTests::sendToClient_mockFile_fileRecieveCompleted(const Event& event, void*) { Client* client = static_cast(event.getTarget()); @@ -388,14 +391,14 @@ NetworkTests::sendToClient_mockFile_fileRecieveCompleted(const Event& event, voi m_events.raiseQuitEvent(); } -void +void NetworkTests::sendToServer_mockData_handleClientConnected(const Event&, void* vclient) { Client* client = static_cast(vclient); sendMockData(client); } -void +void NetworkTests::sendToServer_mockData_fileRecieveCompleted(const Event& event, void*) { Server* server = static_cast(event.getTarget()); @@ -404,14 +407,14 @@ NetworkTests::sendToServer_mockData_fileRecieveCompleted(const Event& event, voi m_events.raiseQuitEvent(); } -void +void NetworkTests::sendToServer_mockFile_handleClientConnected(const Event&, void* vclient) { Client* client = static_cast(vclient); client->sendFileToServer(kMockFilename); } -void +void NetworkTests::sendToServer_mockFile_fileRecieveCompleted(const Event& event, void*) { Server* server = static_cast(event.getTarget()); @@ -420,13 +423,13 @@ NetworkTests::sendToServer_mockFile_fileRecieveCompleted(const Event& event, voi m_events.raiseQuitEvent(); } -void +void NetworkTests::sendMockData(void* eventTarget) { // send first message (file size) String size = barrier::string::sizeTypeToString(kMockDataSize); FileChunk* sizeMessage = FileChunk::start(size); - + m_events.addEvent(Event(m_events.forFile().fileChunkSending(), eventTarget, sizeMessage)); // send chunk messages with incrementing chunk size @@ -452,7 +455,7 @@ NetworkTests::sendMockData(void* eventTarget) } } - + // send last message FileChunk* transferFinished = FileChunk::end(); m_events.addEvent(Event(m_events.forFile().fileChunkSending(), eventTarget, transferFinished)); diff --git a/src/test/integtests/platform/MSWindowsClipboardTests.cpp b/src/test/integtests/platform/MSWindowsClipboardTests.cpp index f9d09d1..edf5a97 100644 --- a/src/test/integtests/platform/MSWindowsClipboardTests.cpp +++ b/src/test/integtests/platform/MSWindowsClipboardTests.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -36,7 +36,7 @@ protected: } private: - void emptyClipboard() + void emptyClipboard() { MSWindowsClipboard clipboard(NULL); clipboard.open(0); diff --git a/src/test/integtests/platform/MSWindowsKeyStateTests.cpp b/src/test/integtests/platform/MSWindowsKeyStateTests.cpp index 9373d14..6f6edf5 100644 --- a/src/test/integtests/platform/MSWindowsKeyStateTests.cpp +++ b/src/test/integtests/platform/MSWindowsKeyStateTests.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#define TEST_ENV +#define BARRIER_TEST_ENV #include "test/mock/barrier/MockEventQueue.h" #include "test/mock/barrier/MockKeyMap.h" @@ -24,7 +24,6 @@ #include "platform/MSWindowsDesks.h" #include "platform/MSWindowsScreen.h" #include "platform/MSWindowsScreenSaver.h" -#include "base/TMethodJob.h" #include "test/global/gtest.h" #include "test/global/gmock.h" @@ -50,10 +49,7 @@ protected: MSWindowsDesks* newDesks(IEventQueue* eventQueue) { - return new MSWindowsDesks( - true, false, m_screensaver, eventQueue, - new TMethodJob( - this, &MSWindowsKeyStateTests::updateKeysCB), false); + return new MSWindowsDesks(true, false, m_screensaver, eventQueue, [](){}, false); } void* getEventTarget() const @@ -62,9 +58,7 @@ protected: } private: - void updateKeysCB(void*) { } IScreenSaver* m_screensaver; - MSWindowsHook m_hook; }; TEST_F(MSWindowsKeyStateTests, disable_eventQueueNotUsed) @@ -73,7 +67,7 @@ TEST_F(MSWindowsKeyStateTests, disable_eventQueueNotUsed) MSWindowsDesks* desks = newDesks(&eventQueue); MockKeyMap keyMap; MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap); - + EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(0); keyState.disable(); diff --git a/src/test/integtests/platform/OSXClipboardTests.cpp b/src/test/integtests/platform/OSXClipboardTests.cpp index 45b73bd..093c738 100644 --- a/src/test/integtests/platform/OSXClipboardTests.cpp +++ b/src/test/integtests/platform/OSXClipboardTests.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -25,9 +25,9 @@ TEST(OSXClipboardTests, empty_openCalled_returnsTrue) { OSXClipboard clipboard; clipboard.open(0); - + bool actual = clipboard.empty(); - + EXPECT_EQ(true, actual); } @@ -36,9 +36,9 @@ TEST(OSXClipboardTests, empty_singleFormat_hasReturnsFalse) OSXClipboard clipboard; clipboard.open(0); clipboard.add(OSXClipboard::kText, "barrier rocks!"); - + clipboard.empty(); - + bool actual = clipboard.has(OSXClipboard::kText); EXPECT_EQ(false, actual); } @@ -47,9 +47,9 @@ TEST(OSXClipboardTests, add_newValue_valueWasStored) { OSXClipboard clipboard; clipboard.open(0); - + clipboard.add(IClipboard::kText, "barrier rocks!"); - + String actual = clipboard.get(IClipboard::kText); EXPECT_EQ("barrier rocks!", actual); } @@ -58,10 +58,10 @@ TEST(OSXClipboardTests, add_replaceValue_valueWasReplaced) { OSXClipboard clipboard; clipboard.open(0); - + clipboard.add(IClipboard::kText, "barrier rocks!"); clipboard.add(IClipboard::kText, "maxivista sucks"); // haha, just kidding. - + String actual = clipboard.get(IClipboard::kText); EXPECT_EQ("maxivista sucks", actual); } @@ -69,18 +69,18 @@ TEST(OSXClipboardTests, add_replaceValue_valueWasReplaced) TEST(OSXClipboardTests, open_timeIsZero_returnsTrue) { OSXClipboard clipboard; - + bool actual = clipboard.open(0); - + EXPECT_EQ(true, actual); } TEST(OSXClipboardTests, open_timeIsOne_returnsTrue) { OSXClipboard clipboard; - + bool actual = clipboard.open(1); - + EXPECT_EQ(true, actual); } @@ -88,9 +88,9 @@ TEST(OSXClipboardTests, close_isOpen_noErrors) { OSXClipboard clipboard; clipboard.open(0); - + clipboard.close(); - + // can't assert anything } @@ -98,9 +98,9 @@ TEST(OSXClipboardTests, getTime_openWithNoEmpty_returnsOne) { OSXClipboard clipboard; clipboard.open(1); - + OSXClipboard::Time actual = clipboard.getTime(); - + // this behavior is different to that of Clipboard which only // returns the value passed into open(t) after empty() is called. EXPECT_EQ((UInt32)1, actual); @@ -111,9 +111,9 @@ TEST(OSXClipboardTests, getTime_openAndEmpty_returnsOne) OSXClipboard clipboard; clipboard.open(1); clipboard.empty(); - + OSXClipboard::Time actual = clipboard.getTime(); - + EXPECT_EQ((UInt32)1, actual); } @@ -123,9 +123,9 @@ TEST(OSXClipboardTests, has_withFormatAdded_returnsTrue) clipboard.open(0); clipboard.empty(); clipboard.add(IClipboard::kText, "barrier rocks!"); - + bool actual = clipboard.has(IClipboard::kText); - + EXPECT_EQ(true, actual); } @@ -134,9 +134,9 @@ TEST(OSXClipboardTests, has_withNoFormats_returnsFalse) OSXClipboard clipboard; clipboard.open(0); clipboard.empty(); - + bool actual = clipboard.has(IClipboard::kText); - + EXPECT_EQ(false, actual); } @@ -145,9 +145,9 @@ TEST(OSXClipboardTests, get_withNoFormats_returnsEmpty) OSXClipboard clipboard; clipboard.open(0); clipboard.empty(); - + String actual = clipboard.get(IClipboard::kText); - + EXPECT_EQ("", actual); } @@ -157,8 +157,8 @@ TEST(OSXClipboardTests, get_withFormatAdded_returnsExpected) clipboard.open(0); clipboard.empty(); clipboard.add(IClipboard::kText, "barrier rocks!"); - + String actual = clipboard.get(IClipboard::kText); - + EXPECT_EQ("barrier rocks!", actual); } diff --git a/src/test/integtests/platform/OSXScreenTests.cpp b/src/test/integtests/platform/OSXScreenTests.cpp index 96beb4d..390e22f 100644 --- a/src/test/integtests/platform/OSXScreenTests.cpp +++ b/src/test/integtests/platform/OSXScreenTests.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/test/integtests/platform/XWindowsClipboardTests.cpp b/src/test/integtests/platform/XWindowsClipboardTests.cpp index 652ee5e..52eacda 100644 --- a/src/test/integtests/platform/XWindowsClipboardTests.cpp +++ b/src/test/integtests/platform/XWindowsClipboardTests.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -33,12 +33,12 @@ protected: m_display = XOpenDisplay(NULL); int screen = DefaultScreen(m_display); Window root = XRootWindow(m_display, screen); - + XSetWindowAttributes attr; attr.do_not_propagate_mask = 0; attr.override_redirect = True; attr.cursor = Cursor(); - + m_window = XCreateWindow( m_display, root, 0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent, 0, &attr); @@ -68,9 +68,9 @@ protected: TEST_F(CXWindowsClipboardTests, empty_openCalled_returnsTrue) { CXWindowsClipboard clipboard = createClipboard(); - + bool actual = clipboard.empty(); - + EXPECT_EQ(true, actual); } @@ -78,9 +78,9 @@ TEST_F(CXWindowsClipboardTests, empty_singleFormat_hasReturnsFalse) { CXWindowsClipboard clipboard = createClipboard(); clipboard.add(CXWindowsClipboard::kText, "barrier rocks!"); - + clipboard.empty(); - + bool actual = clipboard.has(CXWindowsClipboard::kText); EXPECT_FALSE(actual); } @@ -88,9 +88,9 @@ TEST_F(CXWindowsClipboardTests, empty_singleFormat_hasReturnsFalse) TEST_F(CXWindowsClipboardTests, add_newValue_valueWasStored) { CXWindowsClipboard clipboard = createClipboard(); - + clipboard.add(IClipboard::kText, "barrier rocks!"); - + String actual = clipboard.get(IClipboard::kText); EXPECT_EQ("barrier rocks!", actual); } @@ -98,10 +98,10 @@ TEST_F(CXWindowsClipboardTests, add_newValue_valueWasStored) TEST_F(CXWindowsClipboardTests, add_replaceValue_valueWasReplaced) { CXWindowsClipboard clipboard = createClipboard(); - + clipboard.add(IClipboard::kText, "barrier rocks!"); clipboard.add(IClipboard::kText, "maxivista sucks"); // haha, just kidding. - + String actual = clipboard.get(IClipboard::kText); EXPECT_EQ("maxivista sucks", actual); } @@ -109,10 +109,10 @@ TEST_F(CXWindowsClipboardTests, add_replaceValue_valueWasReplaced) TEST_F(CXWindowsClipboardTests, close_isOpen_noErrors) { CXWindowsClipboard clipboard = createClipboard(); - + // clipboard opened in createClipboard() clipboard.close(); - + // can't assert anything } @@ -120,27 +120,27 @@ TEST_F(CXWindowsClipboardTests, has_withFormatAdded_returnsTrue) { CXWindowsClipboard clipboard = createClipboard(); clipboard.add(IClipboard::kText, "barrier rocks!"); - + bool actual = clipboard.has(IClipboard::kText); - + EXPECT_EQ(true, actual); } TEST_F(CXWindowsClipboardTests, has_withNoFormats_returnsFalse) { CXWindowsClipboard clipboard = createClipboard(); - + bool actual = clipboard.has(IClipboard::kText); - + EXPECT_FALSE(actual); } TEST_F(CXWindowsClipboardTests, get_withNoFormats_returnsEmpty) { CXWindowsClipboard clipboard = createClipboard(); - + String actual = clipboard.get(IClipboard::kText); - + EXPECT_EQ("", actual); } @@ -148,9 +148,9 @@ TEST_F(CXWindowsClipboardTests, get_withFormatAdded_returnsExpected) { CXWindowsClipboard clipboard = createClipboard(); clipboard.add(IClipboard::kText, "barrier rocks!"); - + String actual = clipboard.get(IClipboard::kText); - + EXPECT_EQ("barrier rocks!", actual); } diff --git a/src/test/integtests/platform/XWindowsKeyStateTests.cpp b/src/test/integtests/platform/XWindowsKeyStateTests.cpp index 9f6716d..28d090f 100644 --- a/src/test/integtests/platform/XWindowsKeyStateTests.cpp +++ b/src/test/integtests/platform/XWindowsKeyStateTests.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#define TEST_ENV +#define BARRIER_TEST_ENV #include "test/mock/barrier/MockKeyMap.h" #include "test/mock/barrier/MockEventQueue.h" diff --git a/src/test/integtests/platform/XWindowsScreenSaverTests.cpp b/src/test/integtests/platform/XWindowsScreenSaverTests.cpp index c6a2710..a3af21d 100644 --- a/src/test/integtests/platform/XWindowsScreenSaverTests.cpp +++ b/src/test/integtests/platform/XWindowsScreenSaverTests.cpp @@ -23,6 +23,7 @@ #include "platform/XWindowsScreenSaver.h" #include "test/global/gtest.h" +#include #include using ::testing::_; @@ -30,7 +31,12 @@ using ::testing::_; // TODO: not working on build machine for some reason TEST(CXWindowsScreenSaverTests, activate_defaultScreen_todo) { - Display* display = XOpenDisplay(":0.0"); + const char* displayName = std::getenv("DISPLAY"); + if (displayName == NULL) { + displayName = ":0.0"; + } + + Display* display = XOpenDisplay(displayName); Window window = DefaultRootWindow(display); MockEventQueue eventQueue; EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(1); diff --git a/src/test/integtests/platform/XWindowsScreenTests.cpp b/src/test/integtests/platform/XWindowsScreenTests.cpp index d8f75e1..53f113e 100644 --- a/src/test/integtests/platform/XWindowsScreenTests.cpp +++ b/src/test/integtests/platform/XWindowsScreenTests.cpp @@ -20,16 +20,22 @@ #include "platform/XWindowsScreen.h" #include "test/global/gtest.h" +#include using ::testing::_; TEST(CXWindowsScreenTests, fakeMouseMove_nonPrimary_getCursorPosValuesCorrect) { + const char* displayName = std::getenv("DISPLAY"); + if (displayName == NULL) { + displayName = ":0.0"; + } + MockEventQueue eventQueue; EXPECT_CALL(eventQueue, adoptHandler(_, _, _)).Times(2); EXPECT_CALL(eventQueue, adoptBuffer(_)).Times(2); EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(2); - XWindowsScreen screen(new XWindowsImpl(), ":0.0", false, false, 0, &eventQueue); + XWindowsScreen screen(new XWindowsImpl(), displayName, false, false, 0, &eventQueue); screen.fakeMouseMove(10, 20); diff --git a/src/test/mock/barrier/MockApp.h b/src/test/mock/barrier/MockApp.h index 91745d3..fd4094d 100644 --- a/src/test/mock/barrier/MockApp.h +++ b/src/test/mock/barrier/MockApp.h @@ -17,7 +17,7 @@ #pragma once -#define TEST_ENV +#define BARRIER_TEST_ENV #include "barrier/App.h" diff --git a/src/test/mock/barrier/MockArgParser.h b/src/test/mock/barrier/MockArgParser.h index b1dc07c..35d4ff9 100644 --- a/src/test/mock/barrier/MockArgParser.h +++ b/src/test/mock/barrier/MockArgParser.h @@ -17,7 +17,7 @@ #pragma once -#define TEST_ENV +#define BARRIER_TEST_ENV #include "barrier/ArgParser.h" diff --git a/src/test/mock/barrier/MockEventQueue.h b/src/test/mock/barrier/MockEventQueue.h index 735b9fc..76175bb 100644 --- a/src/test/mock/barrier/MockEventQueue.h +++ b/src/test/mock/barrier/MockEventQueue.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/test/mock/barrier/MockKeyState.h b/src/test/mock/barrier/MockKeyState.h index 308e90a..d245ee6 100644 --- a/src/test/mock/barrier/MockKeyState.h +++ b/src/test/mock/barrier/MockKeyState.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/test/mock/barrier/MockScreen.h b/src/test/mock/barrier/MockScreen.h index 78c195a..3c05218 100644 --- a/src/test/mock/barrier/MockScreen.h +++ b/src/test/mock/barrier/MockScreen.h @@ -17,7 +17,7 @@ #pragma once -#define TEST_ENV +#define BARRIER_TEST_ENV #include "barrier/Screen.h" diff --git a/src/test/mock/ipc/MockIpcServer.h b/src/test/mock/ipc/MockIpcServer.h index 4124b41..5b09b39 100644 --- a/src/test/mock/ipc/MockIpcServer.h +++ b/src/test/mock/ipc/MockIpcServer.h @@ -34,7 +34,7 @@ public: MockIpcServer() : m_sendCond(ARCH->newCondVar()), m_sendMutex(ARCH->newMutex()) { } - + ~MockIpcServer() { if (m_sendCond != NULL) { ARCH->closeCondVar(m_sendCond); diff --git a/src/test/mock/server/MockConfig.h b/src/test/mock/server/MockConfig.h index 4161de0..c0b40dc 100644 --- a/src/test/mock/server/MockConfig.h +++ b/src/test/mock/server/MockConfig.h @@ -17,7 +17,7 @@ #pragma once -#define TEST_ENV +#define BARRIER_TEST_ENV #include "server/Config.h" diff --git a/src/test/mock/server/MockInputFilter.h b/src/test/mock/server/MockInputFilter.h index edf6de1..09aeee2 100644 --- a/src/test/mock/server/MockInputFilter.h +++ b/src/test/mock/server/MockInputFilter.h @@ -17,7 +17,7 @@ #pragma once -#define TEST_ENV +#define BARRIER_TEST_ENV #include "server/InputFilter.h" diff --git a/src/test/mock/server/MockPrimaryClient.h b/src/test/mock/server/MockPrimaryClient.h index 80f18a1..db76187 100644 --- a/src/test/mock/server/MockPrimaryClient.h +++ b/src/test/mock/server/MockPrimaryClient.h @@ -17,7 +17,7 @@ #pragma once -#define TEST_ENV +#define BARRIER_TEST_ENV #include "server/PrimaryClient.h" #include "base/String.h" diff --git a/src/test/mock/server/MockServer.h b/src/test/mock/server/MockServer.h index a45ee08..74ad1d0 100644 --- a/src/test/mock/server/MockServer.h +++ b/src/test/mock/server/MockServer.h @@ -17,7 +17,7 @@ #pragma once -#define TEST_ENV +#define BARRIER_TEST_ENV #include "server/Server.h" diff --git a/src/test/unittests/CMakeLists.txt b/src/test/unittests/CMakeLists.txt index c46375c..8cf5e9a 100644 --- a/src/test/unittests/CMakeLists.txt +++ b/src/test/unittests/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -50,8 +50,6 @@ list(APPEND headers ${platform_sources}) include_directories( ../../ - ../../../ext/gtest/include - ../../../ext/gmock/include ../../../ext ) @@ -67,4 +65,4 @@ endif() add_executable(unittests ${sources}) target_link_libraries(unittests - arch base client server common io net platform server synlib mt ipc gtest gmock ${libs} ${OPENSSL_LIBS}) + arch base client server common io net platform server synlib mt ipc ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES} ${libs} ${OPENSSL_LIBS}) diff --git a/src/test/unittests/Main.cpp b/src/test/unittests/Main.cpp index 7f0d0fe..52ad252 100644 --- a/src/test/unittests/Main.cpp +++ b/src/test/unittests/Main.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -35,12 +35,12 @@ main(int argc, char **argv) Arch arch; arch.init(); - + Log log; log.setFilter(kDEBUG4); testing::InitGoogleTest(&argc, argv); - + // gtest seems to randomly finish with error codes (e.g. -1, -1073741819) // even when no tests have failed. not sure what causes this, but it // happens on all platforms and keeps leading to false positives. diff --git a/src/test/unittests/barrier/ArgParserTests.cpp b/src/test/unittests/barrier/ArgParserTests.cpp index e14877e..311162f 100644 --- a/src/test/unittests/barrier/ArgParserTests.cpp +++ b/src/test/unittests/barrier/ArgParserTests.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/test/unittests/barrier/ClientArgsParsingTests.cpp b/src/test/unittests/barrier/ClientArgsParsingTests.cpp index 5a1e7d0..7aaa5db 100644 --- a/src/test/unittests/barrier/ClientArgsParsingTests.cpp +++ b/src/test/unittests/barrier/ClientArgsParsingTests.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/test/unittests/barrier/ClipboardChunkTests.cpp b/src/test/unittests/barrier/ClipboardChunkTests.cpp index e0e37be..784dee8 100644 --- a/src/test/unittests/barrier/ClipboardChunkTests.cpp +++ b/src/test/unittests/barrier/ClipboardChunkTests.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/src/test/unittests/barrier/ClipboardTests.cpp b/src/test/unittests/barrier/ClipboardTests.cpp index f710751..c1afdfb 100644 --- a/src/test/unittests/barrier/ClipboardTests.cpp +++ b/src/test/unittests/barrier/ClipboardTests.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -192,7 +192,7 @@ TEST(ClipboardTests, marshall_withTextAdded_lastSizeCharIs14) EXPECT_EQ(14, (int)actual[11]); } -// TODO: there's some integer -> char encoding going on here. i find it +// TODO: there's some integer -> char encoding going on here. i find it // hard to believe that the clipboard is the only thing doing this. maybe // we should refactor this stuff out of the clipboard. TEST(ClipboardTests, marshall_withTextSize285_sizeCharsValid) @@ -212,12 +212,12 @@ TEST(ClipboardTests, marshall_withTextSize285_sizeCharsValid) String actual = clipboard.marshall(); - // 4 asserts here, but that's ok because we're really just asserting 1 + // 4 asserts here, but that's ok because we're really just asserting 1 // thing. the 32-bit size value is split into 4 chars. if the size is 285 - // (29 more than the 8-bit max size), the last char "rolls over" to 29 - // (this is caused by a bit-wise & on 0xff and 8-bit truncation). each - // char before the last stores a bit-shifted version of the number, each - // 1 more power than the last, which is done by bit-shifting [0] by 24, + // (29 more than the 8-bit max size), the last char "rolls over" to 29 + // (this is caused by a bit-wise & on 0xff and 8-bit truncation). each + // char before the last stores a bit-shifted version of the number, each + // 1 more power than the last, which is done by bit-shifting [0] by 24, // [1] by 16, [2] by 8 ([3] is not bit-shifted). EXPECT_EQ(0, actual[8]); // 285 >> 24 = 285 / (256^3) = 0 EXPECT_EQ(0, actual[9]); // 285 >> 16 = 285 / (256^2) = 0 diff --git a/src/test/unittests/barrier/GenericArgsParsingTests.cpp b/src/test/unittests/barrier/GenericArgsParsingTests.cpp index f43070b..7f19918 100644 --- a/src/test/unittests/barrier/GenericArgsParsingTests.cpp +++ b/src/test/unittests/barrier/GenericArgsParsingTests.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -50,7 +50,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_logLevelCmd_setLogLevel) ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kLogLevelCmd, i); String logFilter(argsBase.m_logFilter); @@ -68,7 +68,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_logFileCmd_saveLogFilename) ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kLogFileCmd, i); String logFile(argsBase.m_logFile); @@ -86,7 +86,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_logFileCmdWithSpace_saveLogFilena ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kLogFileCmdWithSpace, i); String logFile(argsBase.m_logFile); @@ -104,7 +104,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_noDeamonCmd_daemonFalse) ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kNoDeamonCmd, i); EXPECT_FALSE(argsBase.m_daemon); @@ -120,7 +120,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_deamonCmd_daemonTrue) ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kDeamonCmd, i); EXPECT_EQ(true, argsBase.m_daemon); @@ -136,7 +136,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_nameCmd_saveName) ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kNameCmd, i); EXPECT_EQ("mock", argsBase.m_name); @@ -152,7 +152,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_noRestartCmd_restartFalse) ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kNoRestartCmd, i); EXPECT_FALSE(argsBase.m_restartable); @@ -168,7 +168,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_restartCmd_restartTrue) ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kRestartCmd, i); EXPECT_EQ(true, argsBase.m_restartable); @@ -184,7 +184,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_backendCmd_backendTrue) ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kBackendCmd, i); EXPECT_EQ(true, argsBase.m_backend); @@ -200,7 +200,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_noHookCmd_noHookTrue) ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kNoHookCmd, i); EXPECT_EQ(true, argsBase.m_noHooks); @@ -219,7 +219,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_helpCmd_showHelp) ArgsBase argsBase; argParser.setArgsBase(argsBase); ON_CALL(app, help()).WillByDefault(Invoke(showMockHelp)); - + argParser.parseGenericArgs(argc, kHelpCmd, i); EXPECT_EQ(true, g_helpShowed); @@ -239,7 +239,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_versionCmd_showVersion) ArgsBase argsBase; argParser.setArgsBase(argsBase); ON_CALL(app, version()).WillByDefault(Invoke(showMockVersion)); - + argParser.parseGenericArgs(argc, kVersionCmd, i); EXPECT_EQ(true, g_versionShowed); @@ -255,7 +255,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_noTrayCmd_disableTrayTrue) ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kNoTrayCmd, i); EXPECT_EQ(true, argsBase.m_disableTray); @@ -271,7 +271,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_ipcCmd_enableIpcTrue) ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kIpcCmd, i); EXPECT_EQ(true, argsBase.m_enableIpc); @@ -288,7 +288,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_dragDropCmdOnNonLinux_enableDragD ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kDragDropCmd, i); EXPECT_EQ(true, argsBase.m_enableDragDrop); @@ -306,7 +306,7 @@ TEST(GenericArgsParsingTests, parseGenericArgs_dragDropCmdOnLinux_enableDragDrop ArgParser argParser(NULL); ArgsBase argsBase; argParser.setArgsBase(argsBase); - + argParser.parseGenericArgs(argc, kDragDropCmd, i); EXPECT_FALSE(argsBase.m_enableDragDrop); diff --git a/src/test/unittests/barrier/KeyMapTests.cpp b/src/test/unittests/barrier/KeyMapTests.cpp index 5980633..20c1c55 100644 --- a/src/test/unittests/barrier/KeyMapTests.cpp +++ b/src/test/unittests/barrier/KeyMapTests.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2016 Symless - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -15,6 +15,8 @@ * along with this program. If not, see . */ +#define BARRIER_TEST_ENV + #include "barrier/KeyMap.h" #include "test/global/gtest.h" @@ -28,7 +30,7 @@ using ::testing::ReturnRef; using ::testing::SaveArg; namespace barrier { - + TEST(KeyMapTests, findBestKey_requiredDown_matchExactFirstItem) { KeyMap keyMap; @@ -44,7 +46,7 @@ TEST(KeyMapTests, findBestKey_requiredDown_matchExactFirstItem) EXPECT_EQ(0, keyMap.findBestKey(entryList, currentState, desiredState)); } - + TEST(KeyMapTests, findBestKey_requiredAndExtraSensitiveDown_matchExactFirstItem) { KeyMap keyMap; @@ -82,7 +84,7 @@ TEST(KeyMapTests, findBestKey_requiredAndExtraSensitiveDown_matchExactSecondItem EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState)); } - + TEST(KeyMapTests, findBestKey_extraSensitiveDown_matchExactSecondItem) { KeyMap keyMap; @@ -101,7 +103,7 @@ TEST(KeyMapTests, findBestKey_extraSensitiveDown_matchExactSecondItem) itemList2.push_back(item2); entryList.push_back(itemList1); entryList.push_back(itemList2); - + EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState)); } @@ -123,7 +125,7 @@ TEST(KeyMapTests, findBestKey_noRequiredDown_matchOneRequiredChangeItem) itemList2.push_back(item2); entryList.push_back(itemList1); entryList.push_back(itemList2); - + EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState)); } @@ -148,7 +150,7 @@ TEST(KeyMapTests, findBestKey_onlyOneRequiredDown_matchTwoRequiredChangesItem) EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState)); } - + TEST(KeyMapTests, findBestKey_noRequiredDown_cannotMatch) { KeyMap keyMap; @@ -161,31 +163,31 @@ TEST(KeyMapTests, findBestKey_noRequiredDown_cannotMatch) KeyModifierMask desiredState = 0; itemList.push_back(item); entryList.push_back(itemList); - + EXPECT_EQ(-1, keyMap.findBestKey(entryList, currentState, desiredState)); } - + TEST(KeyMapTests, isCommand_shiftMask_returnFalse) { KeyMap keyMap; KeyModifierMask mask= KeyModifierShift; - + EXPECT_FALSE(keyMap.isCommand(mask)); } - + TEST(KeyMapTests, isCommand_controlMask_returnTrue) { KeyMap keyMap; KeyModifierMask mask= KeyModifierControl; - + EXPECT_EQ(true, keyMap.isCommand(mask)); } - + TEST(KeyMapTests, isCommand_alternateMask_returnTrue) { KeyMap keyMap; KeyModifierMask mask= KeyModifierAlt; - + EXPECT_EQ(true, keyMap.isCommand(mask)); } @@ -193,15 +195,15 @@ TEST(KeyMapTests, isCommand_alternateGraphicMask_returnTrue) { KeyMap keyMap; KeyModifierMask mask= KeyModifierAltGr; - + EXPECT_EQ(true, keyMap.isCommand(mask)); } - + TEST(KeyMapTests, isCommand_metaMask_returnTrue) { KeyMap keyMap; KeyModifierMask mask= KeyModifierMeta; - + EXPECT_EQ(true, keyMap.isCommand(mask)); } @@ -209,8 +211,8 @@ TEST(KeyMapTests, isCommand_superMask_returnTrue) { KeyMap keyMap; KeyModifierMask mask= KeyModifierSuper; - + EXPECT_EQ(true, keyMap.isCommand(mask)); } - + } diff --git a/src/test/unittests/barrier/KeyStateTests.cpp b/src/test/unittests/barrier/KeyStateTests.cpp index d4154d8..bc41933 100644 --- a/src/test/unittests/barrier/KeyStateTests.cpp +++ b/src/test/unittests/barrier/KeyStateTests.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -100,7 +100,7 @@ TEST(KeyStateTests, sendKeyEvent_halfDuplex_addEventCalledTwice) KeyStateImpl keyState(eventQueue, keyMap); IKeyStateEvents keyStateEvents; keyStateEvents.setEvents(&eventQueue); - + ON_CALL(keyMap, isHalfDuplex(_, _)).WillByDefault(Return(true)); ON_CALL(eventQueue, forIKeyState()).WillByDefault(ReturnRef(keyStateEvents)); @@ -116,7 +116,7 @@ TEST(KeyStateTests, sendKeyEvent_keyRepeat_addEventCalledOnce) KeyStateImpl keyState(eventQueue, keyMap); IKeyStateEvents keyStateEvents; keyStateEvents.setEvents(&eventQueue); - + ON_CALL(eventQueue, forIKeyState()).WillByDefault(ReturnRef(keyStateEvents)); EXPECT_CALL(eventQueue, addEvent(_)).Times(1); @@ -131,7 +131,7 @@ TEST(KeyStateTests, sendKeyEvent_keyDown_addEventCalledOnce) KeyStateImpl keyState(eventQueue, keyMap); IKeyStateEvents keyStateEvents; keyStateEvents.setEvents(&eventQueue); - + ON_CALL(eventQueue, forIKeyState()).WillByDefault(ReturnRef(keyStateEvents)); EXPECT_CALL(eventQueue, addEvent(_)).Times(1); @@ -146,7 +146,7 @@ TEST(KeyStateTests, sendKeyEvent_keyUp_addEventCalledOnce) KeyStateImpl keyState(eventQueue, keyMap); IKeyStateEvents keyStateEvents; keyStateEvents.setEvents(&eventQueue); - + ON_CALL(eventQueue, forIKeyState()).WillByDefault(ReturnRef(keyStateEvents)); EXPECT_CALL(eventQueue, addEvent(_)).Times(1); diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index 39ad6e8..cc8e4fc 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2014-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -56,12 +56,38 @@ TEST(StringTests, sprintf_formatWithArgument_formatedString) TEST(StringTests, toHex_plaintext_hexString) { - String subject = "foobar"; + std::vector subject{'f', 'o', 'o', 'b', 'a', 'r'}; int width = 2; - string::toHex(subject, width); + EXPECT_EQ("666f6f626172", string::to_hex(subject, width)); +} + +TEST(StringTests, fromhex_plaintext_string) +{ + auto result = string::from_hex("666f6f626172"); + std::string expected = "foobar"; + EXPECT_EQ(result, std::vector(expected.begin(), expected.end())); +} + +TEST(StringTests, fromhex_plaintext_string_colons) +{ + auto result = string::from_hex("66:6f:6f:62:61:72"); + std::string expected = "foobar"; + EXPECT_EQ(result, std::vector(expected.begin(), expected.end())); +} - EXPECT_EQ("666f6f626172", subject); +TEST(StringTests, fromhex_binary_string) +{ + auto result = string::from_hex("01020304050600fff9"); + auto expected = std::vector{1, 2, 3, 4, 5, 6, 0, 0xff, 0xf9}; + EXPECT_EQ(result, expected); +} + +TEST(StringTests, fromhex_invalid_string) +{ + EXPECT_TRUE(string::from_hex("66:6").empty()); + EXPECT_TRUE(string::from_hex("66:612").empty()); + EXPECT_TRUE(string::from_hex("66:WW").empty()); } TEST(StringTests, uppercase_lowercaseInput_uppercaseOutput) diff --git a/src/test/unittests/ipc/IpcLogOutputterTests.cpp b/src/test/unittests/ipc/IpcLogOutputterTests.cpp index bbfed9c..6db9ac9 100644 --- a/src/test/unittests/ipc/IpcLogOutputterTests.cpp +++ b/src/test/unittests/ipc/IpcLogOutputterTests.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -#define TEST_ENV +#define BARRIER_TEST_ENV #include "test/mock/ipc/MockIpcServer.h" @@ -50,7 +50,7 @@ TEST(IpcLogOutputterTests, write_threadingEnabled_bufferIsSent) { MockIpcServer mockServer; mockServer.delegateToFake(); - + ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); EXPECT_CALL(mockServer, hasClients(_)).Times(AtLeast(3)); @@ -67,7 +67,7 @@ TEST(IpcLogOutputterTests, write_threadingEnabled_bufferIsSent) TEST(IpcLogOutputterTests, write_overBufferMaxSize_firstLineTruncated) { MockIpcServer mockServer; - + ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); EXPECT_CALL(mockServer, hasClients(_)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\nmock 3\n"), _)).Times(1); @@ -85,7 +85,7 @@ TEST(IpcLogOutputterTests, write_overBufferMaxSize_firstLineTruncated) TEST(IpcLogOutputterTests, write_underBufferMaxSize_allLinesAreSent) { MockIpcServer mockServer; - + ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); EXPECT_CALL(mockServer, hasClients(_)).Times(1); @@ -107,7 +107,7 @@ TEST(IpcLogOutputterTests, write_underBufferMaxSize_allLinesAreSent) TEST(IpcLogOutputterTests, write_overBufferRateLimit_lastLineTruncated) { MockIpcServer mockServer; - + ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); EXPECT_CALL(mockServer, hasClients(_)).Times(2); @@ -123,7 +123,7 @@ TEST(IpcLogOutputterTests, write_overBufferRateLimit_lastLineTruncated) outputter.write(kNOTE, "mock 3"); outputter.sendBuffer(); - + // after waiting the time limit send another to make sure // we can log after the time limit passes. // HACK: sleep causes the unit test to fail intermittently, @@ -140,7 +140,7 @@ TEST(IpcLogOutputterTests, write_overBufferRateLimit_lastLineTruncated) TEST(IpcLogOutputterTests, write_underBufferRateLimit_allLinesAreSent) { MockIpcServer mockServer; - + ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); EXPECT_CALL(mockServer, hasClients(_)).Times(2); @@ -154,7 +154,7 @@ TEST(IpcLogOutputterTests, write_underBufferRateLimit_allLinesAreSent) outputter.write(kNOTE, "mock 1"); outputter.write(kNOTE, "mock 2"); outputter.sendBuffer(); - + // after waiting the time limit send another to make sure // we can log after the time limit passes. outputter.write(kNOTE, "mock 3"); diff --git a/src/test/unittests/net/FingerprintDatabaseTests.cpp b/src/test/unittests/net/FingerprintDatabaseTests.cpp new file mode 100644 index 0000000..61bed0e --- /dev/null +++ b/src/test/unittests/net/FingerprintDatabaseTests.cpp @@ -0,0 +1,95 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "net/FingerprintDatabase.h" +#include "test/global/gtest.h" + +namespace barrier { + +TEST(FingerprintDatabase, parse_db_line) +{ + ASSERT_FALSE(FingerprintDatabase::parse_db_line("").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("abcd").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("v1:algo:something").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("v2:algo:something").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("v2:algo:01020304abc").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("v2:algo:01020304ZZ").valid()); + ASSERT_EQ(FingerprintDatabase::parse_db_line("v2:algo:01020304ab"), + (FingerprintData{"algo", {1, 2, 3, 4, 0xab}})); +} + +TEST(FingerprintDatabase, read) +{ + std::istringstream stream; + stream.str(R"( +v2:algo1:01020304ab +v2:algo2:03040506ab +AB:CD:EF:00:01:02:03:04:05:06:07:08:09:10:11:12:13:14:15:16 +)"); + FingerprintDatabase db; + db.read_stream(stream); + + std::vector expected = { + { "algo1", { 1, 2, 3, 4, 0xab } }, + { "algo2", { 3, 4, 5, 6, 0xab } }, + { "sha1", { 0xab, 0xcd, 0xef, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 } }, + }; + ASSERT_EQ(db.fingerprints(), expected); +} + +TEST(FingerprintDatabase, write) +{ + std::ostringstream stream; + + FingerprintDatabase db; + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + db.add_trusted({ "algo2", { 3, 4, 5, 6, 0xab } }); + db.write_stream(stream); + + ASSERT_EQ(stream.str(), R"(v2:algo1:01020304ab +v2:algo2:03040506ab +)"); +} + +TEST(FingerprintDatabase, clear) +{ + FingerprintDatabase db; + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + db.clear(); + ASSERT_TRUE(db.fingerprints().empty()); +} + +TEST(FingerprintDatabase, add_trusted_no_duplicates) +{ + FingerprintDatabase db; + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + db.add_trusted({ "algo2", { 3, 4, 5, 6, 0xab } }); + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + ASSERT_EQ(db.fingerprints().size(), 2); +} + +TEST(FingerprintDatabase, is_trusted) +{ + FingerprintDatabase db; + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + ASSERT_TRUE(db.is_trusted({ "algo1", { 1, 2, 3, 4, 0xab } })); + ASSERT_FALSE(db.is_trusted({ "algo2", { 1, 2, 3, 4, 0xab } })); + ASSERT_FALSE(db.is_trusted({ "algo1", { 1, 2, 3, 4, 0xac } })); +} + +} // namespace barrier diff --git a/src/test/unittests/net/SecureUtilsTests.cpp b/src/test/unittests/net/SecureUtilsTests.cpp new file mode 100644 index 0000000..0cce693 --- /dev/null +++ b/src/test/unittests/net/SecureUtilsTests.cpp @@ -0,0 +1,73 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) 2021 Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "net/SecureUtils.h" + +#include "test/global/gtest.h" +#include "test/global/TestUtils.h" + +namespace barrier { + +TEST(SecureUtilsTest, FormatSslFingerprintHexWithSeparators) +{ + auto fingerprint = generate_pseudo_random_bytes(0, 32); + ASSERT_EQ(format_ssl_fingerprint(fingerprint, true), + "28:FD:0A:98:8A:0E:A1:6C:D7:E8:6C:A7:EE:58:41:71:" + "CA:B2:8E:49:25:94:90:25:26:05:8D:AF:63:ED:2E:30"); +} + +TEST(SecureUtilsTest, CreateFingerprintRandomArt) +{ + ASSERT_EQ(create_fingerprint_randomart(generate_pseudo_random_bytes(0, 32)), + "+-----------------+\n" + "|*X+. . |\n" + "|*oo + |\n" + "| + = |\n" + "| B . . |\n" + "|.+... o S |\n" + "|E+ ++. . |\n" + "|B*++.. . |\n" + "|+o*o o . |\n" + "|+o*Bo . |\n" + "+-----------------+"); + ASSERT_EQ(create_fingerprint_randomart(generate_pseudo_random_bytes(1, 32)), + "+-----------------+\n" + "| .oo+ . .B=. |\n" + "| .o.+ . o o.= |\n" + "|o..+.. o . E * |\n" + "|oo..+ . * * |\n" + "|B o.....S. o . |\n" + "|+=o..... |\n" + "| + + . |\n" + "|o. .. |\n" + "|..o.. |\n" + "+-----------------+"); + ASSERT_EQ(create_fingerprint_randomart(generate_pseudo_random_bytes(2, 32)), + "+-----------------+\n" + "| ... .o.o.|\n" + "| o .=.E|\n" + "| . + o ...+.|\n" + "| * o = o ... |\n" + "| * + S & . |\n" + "| = + % @ |\n" + "| . . = X o |\n" + "| . . O . |\n" + "| . + |\n" + "+-----------------+"); +} + +} // namespace barrier diff --git a/src/test/unittests/platform/OSXKeyStateTests.cpp b/src/test/unittests/platform/OSXKeyStateTests.cpp index dd9e80f..0e4ec83 100644 --- a/src/test/unittests/platform/OSXKeyStateTests.cpp +++ b/src/test/unittests/platform/OSXKeyStateTests.cpp @@ -30,27 +30,27 @@ TEST(OSXKeyStateTests, mapModifiersFromOSX_OSXMask_returnBarrierMask) OSXKeyState keyState(&eventQueue, keyMap); KeyModifierMask outMask = 0; - + UInt32 shiftMask = 0 | kCGEventFlagMaskShift; outMask = keyState.mapModifiersFromOSX(shiftMask); EXPECT_EQ(KeyModifierShift, outMask); - + UInt32 ctrlMask = 0 | kCGEventFlagMaskControl; outMask = keyState.mapModifiersFromOSX(ctrlMask); EXPECT_EQ(KeyModifierControl, outMask); - + UInt32 altMask = 0 | kCGEventFlagMaskAlternate; outMask = keyState.mapModifiersFromOSX(altMask); EXPECT_EQ(KeyModifierAlt, outMask); - + UInt32 cmdMask = 0 | kCGEventFlagMaskCommand; outMask = keyState.mapModifiersFromOSX(cmdMask); EXPECT_EQ(KeyModifierSuper, outMask); - + UInt32 capsMask = 0 | kCGEventFlagMaskAlphaShift; outMask = keyState.mapModifiersFromOSX(capsMask); EXPECT_EQ(KeyModifierCapsLock, outMask); - + UInt32 numMask = 0 | kCGEventFlagMaskNumericPad; outMask = keyState.mapModifiersFromOSX(numMask); EXPECT_EQ(KeyModifierNumLock, outMask); diff --git a/towncrier.toml b/towncrier.toml new file mode 100644 index 0000000..d8f0a61 --- /dev/null +++ b/towncrier.toml @@ -0,0 +1,39 @@ +[tool.towncrier] + package = "" + directory = "doc/newsfragments" + filename = "doc/release_notes/index.md" + template = "doc/release_notes/index.template.jinja" + title_format = "\nBarrier `{version}` ( `{project_date}` )\n================================\n" + start_string = "[comment]: <> (towncrier release notes start)" + [[tool.towncrier.section]] + path = "" + + [[tool.towncrier.type]] + directory = "security" + name = "Security fixes" + showcontent = false + + [[tool.towncrier.type]] + directory = "feature" + name = "Features" + showcontent = true + + [[tool.towncrier.type]] + directory = "bugfix" + name = "Bug fixes" + showcontent = true + + [[tool.towncrier.type]] + directory = "doc" + name = "Improved Documentation" + showcontent = true + + [[tool.towncrier.type]] + directory = "removal" + name = "Deprecations and Removals" + showcontent = true + + [[tool.towncrier.type]] + directory = "misc" + name = "Miscellaneous" + showcontent = false -- cgit v1.2.3