From 008102df56a653e62f1404431e516258a433c59a Mon Sep 17 00:00:00 2001 From: Bart Beumer Date: Sun, 30 Mar 2025 18:56:40 +0200 Subject: [PATCH] Introduce libmagic. --- .devcontainer/Dockerfile | 1 + .devcontainer/devcontainer.json | 13 ---- .gitignore | 3 + CMakeLists.txt | 1 + bmrshared-magic/CMakeLists.txt | 2 + .../include/bmrshared/magic_file_info.hpp | 26 ++++++++ bmrshared-magic/lib/CMakeLists.txt | 31 ++++++++++ bmrshared-magic/lib/magic_file_info.cpp | 59 +++++++++++++++++++ bmrshared-magic/tst/CMakeLists.txt | 24 ++++++++ bmrshared-magic/tst/test_magic.cpp | 26 ++++++++ bmrshared/tst/CMakeLists.txt | 5 -- conanfile.py | 20 +++++++ conanfile.txt | 7 --- 13 files changed, 193 insertions(+), 25 deletions(-) create mode 100644 bmrshared-magic/CMakeLists.txt create mode 100644 bmrshared-magic/include/bmrshared/magic_file_info.hpp create mode 100644 bmrshared-magic/lib/CMakeLists.txt create mode 100644 bmrshared-magic/lib/magic_file_info.cpp create mode 100644 bmrshared-magic/tst/CMakeLists.txt create mode 100644 bmrshared-magic/tst/test_magic.cpp create mode 100644 conanfile.py delete mode 100644 conanfile.txt diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 3e6e1c4..83985d5 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -8,6 +8,7 @@ RUN apk update && \ boost-build \ build-base \ cmake \ + gdb \ git \ libstdc++ \ libtool \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a8e6b1f..99cdf95 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,12 +4,8 @@ "dockerfile": "Dockerfile" }, - // Features to add to the dev container. More info: https://containers.dev/features. - // "features": {}, - // Configure tool-specific properties. "customizations": { - // Configure properties specific to VS Code. "vscode": { "settings": {}, "extensions": [ @@ -20,13 +16,4 @@ ] } } - - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "gcc -v", - - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" } \ No newline at end of file diff --git a/.gitignore b/.gitignore index e257658..2b02f5f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ + +/build + # ---> C++ # Prerequisites *.d diff --git a/CMakeLists.txt b/CMakeLists.txt index b4d6c65..77d12a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,3 +4,4 @@ project(all) enable_testing() add_subdirectory(bmrshared) +add_subdirectory(bmrshared-magic) \ No newline at end of file diff --git a/bmrshared-magic/CMakeLists.txt b/bmrshared-magic/CMakeLists.txt new file mode 100644 index 0000000..86d49d9 --- /dev/null +++ b/bmrshared-magic/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(lib) +add_subdirectory(tst) diff --git a/bmrshared-magic/include/bmrshared/magic_file_info.hpp b/bmrshared-magic/include/bmrshared/magic_file_info.hpp new file mode 100644 index 0000000..2134636 --- /dev/null +++ b/bmrshared-magic/include/bmrshared/magic_file_info.hpp @@ -0,0 +1,26 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2025 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// + +#include +#include + +namespace bmrshared +{ +class magic_file_info final +{ + public: + explicit magic_file_info(const std::filesystem::path& path); + ~magic_file_info(); + + std::string get_mime(const std::filesystem::path& path) const; + + private: + magic_t m_magic_cookie; +}; + +} // namespace bmrshared diff --git a/bmrshared-magic/lib/CMakeLists.txt b/bmrshared-magic/lib/CMakeLists.txt new file mode 100644 index 0000000..c5863d4 --- /dev/null +++ b/bmrshared-magic/lib/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.20) + +find_package(libmagic 5.45 REQUIRED) + +project(bmrshared-magic) + +add_library( + ${PROJECT_NAME} + magic_file_info.cpp +) + +set_property( + TARGET ${PROJECT_NAME} + PROPERTY CXX_STANDARD 20 +) + +target_include_directories( + ${PROJECT_NAME} + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include +) + +target_link_libraries( + ${PROJECT_NAME} + PUBLIC + libmagic::libmagic +) + +install( + FILES ${CONAN_LIBMAGIC_PACKAGE_FOLDER}/res/magic.mgc + DESTINATION bin +) diff --git a/bmrshared-magic/lib/magic_file_info.cpp b/bmrshared-magic/lib/magic_file_info.cpp new file mode 100644 index 0000000..37dc532 --- /dev/null +++ b/bmrshared-magic/lib/magic_file_info.cpp @@ -0,0 +1,59 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2025 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#include + +namespace bmrshared +{ +magic_file_info::magic_file_info(const std::filesystem::path& path) + : m_magic_cookie(nullptr) +{ + bool load_ok = false; + m_magic_cookie = magic_open(MAGIC_MIME_TYPE); + + if (m_magic_cookie != nullptr && std::filesystem::is_regular_file(path)) + { + load_ok = (magic_load(m_magic_cookie, path.native().c_str()) == 0); + } + + if (m_magic_cookie && !load_ok) + { + magic_close(m_magic_cookie); + m_magic_cookie = nullptr; + } + + if (!load_ok) + { + throw std::runtime_error("Could not initialize libmagic."); + } +} + +magic_file_info::~magic_file_info() +{ + magic_close(m_magic_cookie); +} + +std::string magic_file_info::get_mime(const std::filesystem::path& path) const +{ + std::string mime; + if (std::filesystem::is_regular_file(path)) + { + const char* file_name = path.native().c_str(); + const char* magic_full = nullptr; + if (file_name != nullptr) + { + magic_full = magic_file(m_magic_cookie, file_name); + } + if (magic_full != nullptr) + { + mime = magic_full; + } + } + return mime; +} + +} // namespace bmrshared diff --git a/bmrshared-magic/tst/CMakeLists.txt b/bmrshared-magic/tst/CMakeLists.txt new file mode 100644 index 0000000..ef84587 --- /dev/null +++ b/bmrshared-magic/tst/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.20) + +find_package(GTest REQUIRED) +include(GoogleTest) + +project(bmrshared-magic-test) +add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME}) + +add_executable( + ${PROJECT_NAME} + test_magic.cpp +) + +set_property( + TARGET ${PROJECT_NAME} + PROPERTY CXX_STANDARD 20 +) + +target_link_libraries( + ${PROJECT_NAME} + PUBLIC + bmrshared-magic + GTest::gtest_main +) diff --git a/bmrshared-magic/tst/test_magic.cpp b/bmrshared-magic/tst/test_magic.cpp new file mode 100644 index 0000000..b4ce5c2 --- /dev/null +++ b/bmrshared-magic/tst/test_magic.cpp @@ -0,0 +1,26 @@ +// GNU Lesser General Public License v3.0 +// Copyright (c) 2025 Bart Beumer +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License v3.0 as published by +// the Free Software Foundation. +// +#include +#include + +namespace +{ + constexpr std::string_view magic_file_location = "/usr/local/bin/magic.mgc"; +} + +TEST(test_magic, no_throw_on_query) +{ + bmrshared::magic_file_info mfi(magic_file_location); + EXPECT_NO_THROW(mfi.get_mime(magic_file_location)); +} + +TEST(test_magic, throw_on_wrong_magic_file) +{ + EXPECT_THROW(bmrshared::magic_file_info mfi(""), std::exception); + EXPECT_THROW(bmrshared::magic_file_info mfi("/etc/hostname"), std::exception); +} diff --git a/bmrshared/tst/CMakeLists.txt b/bmrshared/tst/CMakeLists.txt index c0ce19f..244ea75 100644 --- a/bmrshared/tst/CMakeLists.txt +++ b/bmrshared/tst/CMakeLists.txt @@ -26,8 +26,3 @@ target_link_libraries( GTest::gmock ) - -install( - TARGETS ${PROJECT_NAME} - RUNTIME DESTINATION bin -) diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 0000000..1029895 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,20 @@ +import os +from conan import ConanFile +from conan.tools.files import copy +from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps + +class HelloConan(ConanFile): + settings = "os", "compiler", "build_type", "arch" + requires = "boost/1.84.0", "gtest/1.14.0", "libmagic/5.45" + generators = "CMakeDeps" + build_policy = "*" + + def generate(self): + # We need to find the folder of libmagic and supply it to cmake so that + # we can deploy the magic file. + libmagic = self.dependencies["libmagic"] + + tc = CMakeToolchain(self) + tc.variables["CONAN_LIBMAGIC_PACKAGE_FOLDER"] = libmagic.package_folder + tc.generate() + diff --git a/conanfile.txt b/conanfile.txt deleted file mode 100644 index 218cfb2..0000000 --- a/conanfile.txt +++ /dev/null @@ -1,7 +0,0 @@ -[requires] -boost/1.84.0 -gtest/1.14.0 - -[generators] -CMakeDeps -CMakeToolchain