diff --git a/.github/codeql-queries/exiv2-cpp-queries/unsafe_vector_access.ql b/.github/codeql-queries/exiv2-cpp-queries/unsafe_vector_access.ql index 4a4c578b..7a9356bc 100644 --- a/.github/codeql-queries/exiv2-cpp-queries/unsafe_vector_access.ql +++ b/.github/codeql-queries/exiv2-cpp-queries/unsafe_vector_access.ql @@ -53,7 +53,7 @@ predicate indexK_with_fixedarray(ClassTemplateInstantiation t, ArrayIndexCall ca t.getSimpleName() = "array" and idx = call.getArgument(0) and lowerBound(idx) >= 0 and - upperBound(idx) < t.getTemplateArgument(1).(Literal).getValue().toInt() + upperBound(idx) < lowerBound(t.getTemplateArgument(1)) ) } diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 85138a0c..2b4b08e5 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -2,6 +2,9 @@ # https://google.github.io/oss-fuzz/getting-started/continuous-integration/ name: CIFuzz +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true on: pull_request: paths-ignore: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 98057a60..5580c29b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -5,6 +5,10 @@ # or to provide custom queries or build logic. name: "CodeQL" +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: push: branches: [0.27-maintenance, main] diff --git a/.github/workflows/nightly_Linux_distributions.yml b/.github/workflows/nightly_Linux_distributions.yml index f4c5b14c..eabf655a 100644 --- a/.github/workflows/nightly_Linux_distributions.yml +++ b/.github/workflows/nightly_Linux_distributions.yml @@ -6,6 +6,10 @@ on: schedule: - cron: 0 4 * * * +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + name: Nightly - Linux distributions jobs: diff --git a/.github/workflows/on_PR_linux_fuzz.yml b/.github/workflows/on_PR_linux_fuzz.yml index e28a3410..32bf2988 100644 --- a/.github/workflows/on_PR_linux_fuzz.yml +++ b/.github/workflows/on_PR_linux_fuzz.yml @@ -4,6 +4,10 @@ name: On PRs - Linux-Ubuntu Quick Fuzz +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: pull_request: paths-ignore: @@ -24,18 +28,8 @@ jobs: - name: build and compile run: | - mkdir build && cd build && \ - cmake -GNinja -DEXIV2_ENABLE_PNG=ON \ - -DEXIV2_BUILD_SAMPLES=ON \ - -DEXIV2_ENABLE_WEBREADY=ON \ - -DEXIV2_ENABLE_CURL=ON \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \ - -DCMAKE_CXX_COMPILER=$(which clang++) \ - -DEXIV2_BUILD_FUZZ_TESTS=ON \ - -DEXIV2_TEAM_USE_SANITIZERS=ON \ - .. && \ - cmake --build . --parallel + cmake --preset linux-sanitizers -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=$(which clang++) -DEXIV2_BUILD_FUZZ_TESTS=ON -DEXIV2_BUILD_UNIT_TESTS=OFF + cmake --build build --parallel - name: Fuzz run: | diff --git a/.github/workflows/on_PR_linux_matrix.yml b/.github/workflows/on_PR_linux_matrix.yml index a3c3e991..419e1684 100644 --- a/.github/workflows/on_PR_linux_matrix.yml +++ b/.github/workflows/on_PR_linux_matrix.yml @@ -1,5 +1,9 @@ name: On PRs - Linux-Ubuntu Matrix +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: pull_request: paths-ignore: @@ -22,7 +26,7 @@ jobs: - name: install dependencies run: | sudo apt-get install ninja-build - pip3 install conan==1.45.0 + pip3 install conan==1.48.1 - name: Conan common config run: | @@ -39,20 +43,8 @@ jobs: - name: Build run: | - cd build && \ - cmake -GNinja \ - -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ - -DBUILD_SHARED_LIBS=${{matrix.shared_libraries}} \ - -DEXIV2_BUILD_SAMPLES=ON \ - -DEXIV2_ENABLE_PNG=ON \ - -DEXIV2_ENABLE_WEBREADY=ON \ - -DEXIV2_ENABLE_CURL=ON \ - -DEXIV2_BUILD_UNIT_TESTS=ON \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \ - -DCMAKE_INSTALL_PREFIX=install \ - .. && \ - cmake --build . --parallel + cmake --preset base_linux -S . -B build -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.shared_libraries}} + cmake --build build --parallel - name: Install run: | diff --git a/.github/workflows/on_PR_linux_special_builds.yml b/.github/workflows/on_PR_linux_special_builds.yml index 4cceffc3..6339cb76 100644 --- a/.github/workflows/on_PR_linux_special_builds.yml +++ b/.github/workflows/on_PR_linux_special_builds.yml @@ -1,5 +1,9 @@ name: On PRs - Linux Special Builds +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: workflow_dispatch: pull_request: @@ -20,7 +24,7 @@ jobs: - name: install dependencies run: | sudo apt-get install ninja-build - pip3 install conan==1.45.0 + pip3 install conan==1.48.1 pip3 install gcovr - name: Conan common config @@ -37,21 +41,8 @@ jobs: - name: Build run: | - cd build && \ - cmake -GNinja \ - -DCMAKE_BUILD_TYPE=Debug \ - -DBUILD_SHARED_LIBS=ON \ - -DEXIV2_ENABLE_PNG=ON \ - -DEXIV2_ENABLE_WEBREADY=ON \ - -DEXIV2_ENABLE_CURL=ON \ - -DEXIV2_BUILD_UNIT_TESTS=ON \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \ - -DEXIV2_BUILD_SAMPLES=ON \ - -DBUILD_WITH_COVERAGE=ON \ - -DCMAKE_INSTALL_PREFIX=install \ - .. && \ - cmake --build . --parallel + cmake --preset linux-coverage -S . -B build + cmake --build build --parallel - name: Tests + Upload coverage run: | @@ -78,9 +69,8 @@ jobs: - name: install dependencies run: | - sudo apt-get update sudo apt-get install valgrind ninja-build - pip3 install conan==1.45.0 + pip3 install conan==1.48.1 - name: Conan common config run: | @@ -96,9 +86,8 @@ jobs: - name: Build run: | - cd build - cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DEXIV2_ENABLE_PNG=ON -DEXIV2_ENABLE_WEBREADY=ON -DEXIV2_ENABLE_CURL=ON -DEXIV2_BUILD_UNIT_TESTS=ON -DEXIV2_ENABLE_BMFF=ON -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON -DBUILD_WITH_COVERAGE=OFF -DCMAKE_INSTALL_PREFIX=install .. - cmake --build . --parallel + cmake --preset linux-release -S . -B build + cmake --build build --parallel - name: Tests with valgrind run: | @@ -116,7 +105,7 @@ jobs: - name: install dependencies run: | sudo apt-get install ninja-build - pip3 install conan==1.45.0 + pip3 install conan==1.48.1 - name: Conan common config run: | @@ -132,22 +121,8 @@ jobs: - name: Build run: | - cd build && \ - cmake -GNinja \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=ON \ - -DEXIV2_BUILD_SAMPLES=ON \ - -DEXIV2_ENABLE_PNG=ON \ - -DEXIV2_ENABLE_WEBREADY=ON \ - -DEXIV2_ENABLE_CURL=ON \ - -DEXIV2_BUILD_UNIT_TESTS=ON \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \ - -DBUILD_WITH_COVERAGE=OFF \ - -DEXIV2_TEAM_USE_SANITIZERS=ON \ - -DCMAKE_INSTALL_PREFIX=install \ - .. && \ - cmake --build . --parallel + cmake --preset linux-sanitizers -S . -B build + cmake --build build --parallel - name: Tests run: | @@ -163,9 +138,8 @@ jobs: - name: install dependencies run: | - sudo apt-get update - sudo apt-get install valgrind doxygen graphviz gettext - pip3 install conan==1.45.0 + sudo apt-get install valgrind doxygen graphviz gettext ninja-build + pip3 install conan==1.48.1 - name: Conan common config run: | @@ -181,22 +155,8 @@ jobs: - name: Build run: | - cd build && \ - cmake -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=ON \ - -DEXIV2_BUILD_SAMPLES=ON \ - -DEXIV2_ENABLE_PNG=ON \ - -DEXIV2_ENABLE_WEBREADY=ON \ - -DEXIV2_ENABLE_CURL=ON \ - -DEXIV2_BUILD_UNIT_TESTS=ON \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \ - -DBUILD_WITH_COVERAGE=ON \ - -DEXIV2_BUILD_DOC=ON \ - -DEXIV2_ENABLE_NLS=ON \ - -DCMAKE_CXX_FLAGS="-DEXIV2_DEBUG_MESSAGES" \ - .. && \ - cmake --build . --parallel + cmake --preset linux-release -S . -B build -DEXIV2_BUILD_DOC=ON -DCMAKE_CXX_FLAGS="-DEXIV2_DEBUG_MESSAGES" + cmake --build build --parallel - name: Generate documentation run: | diff --git a/.github/workflows/on_PR_linux_staticAnalysis.yml b/.github/workflows/on_PR_linux_staticAnalysis.yml index c3c764cb..75a7fc56 100644 --- a/.github/workflows/on_PR_linux_staticAnalysis.yml +++ b/.github/workflows/on_PR_linux_staticAnalysis.yml @@ -1,5 +1,9 @@ name: On PRs - Linux - Static Analysis +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: workflow_dispatch: pull_request: @@ -19,7 +23,7 @@ jobs: - name: install dependencies run: | - pip3 install conan==1.45.0 + pip3 install conan==1.48.1 sudo add-apt-repository ppa:ubuntu-lxc/daily -y wget -q -O - https://files.pvs-studio.com/etc/pubkey.txt |sudo apt-key add - sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.pvs-studio.com/etc/viva64.list @@ -40,19 +44,7 @@ jobs: - name: Configure run: | - cd build && \ - cmake -DCMAKE_BUILD_TYPE=Debug \ - -DBUILD_SHARED_LIBS=ON \ - -DEXIV2_ENABLE_PNG=ON \ - -DEXIV2_ENABLE_WEBREADY=ON \ - -DEXIV2_ENABLE_CURL=ON \ - -DEXIV2_BUILD_UNIT_TESTS=ON \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \ - -DBUILD_WITH_COVERAGE=ON \ - -DCMAKE_INSTALL_PREFIX=install \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - .. \ + cmake --preset linux-debug-NoConan -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON - name: Static Analysis env: diff --git a/.github/workflows/on_PR_mac_matrix.yml b/.github/workflows/on_PR_mac_matrix.yml index 55bedca1..a8167d76 100644 --- a/.github/workflows/on_PR_mac_matrix.yml +++ b/.github/workflows/on_PR_mac_matrix.yml @@ -1,5 +1,9 @@ name: On PRs - Mac Matrix +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: pull_request: paths-ignore: @@ -33,21 +37,8 @@ jobs: - name: Build run: | - mkdir build && cd build && \ - cmake -GNinja \ - -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ - -DBUILD_SHARED_LIBS=${{matrix.shared_libraries}} \ - -DEXIV2_BUILD_SAMPLES=ON \ - -DEXIV2_ENABLE_PNG=ON \ - -DEXIV2_ENABLE_WEBREADY=ON \ - -DEXIV2_ENABLE_CURL=ON \ - -DEXIV2_BUILD_UNIT_TESTS=ON \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \ - -DCMAKE_INSTALL_PREFIX=install \ - -DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations" \ - .. && \ - cmake --build . --parallel + cmake --preset base_mac -S . -B build -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.shared_libraries}} -DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations" + cmake --build build --parallel - name: Install run: | diff --git a/.github/workflows/on_PR_mac_special_builds.yml b/.github/workflows/on_PR_mac_special_builds.yml index 3b159b07..705dbdaf 100644 --- a/.github/workflows/on_PR_mac_special_builds.yml +++ b/.github/workflows/on_PR_mac_special_builds.yml @@ -1,5 +1,9 @@ name: On PRs - Mac Special Builds +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: workflow_dispatch: pull_request: @@ -28,22 +32,8 @@ jobs: - name: Build run: | - mkdir build && cd build && \ - cmake -GNinja \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=ON \ - -DEXIV2_BUILD_SAMPLES=ON \ - -DEXIV2_ENABLE_PNG=ON \ - -DEXIV2_ENABLE_WEBREADY=ON \ - -DEXIV2_ENABLE_CURL=ON \ - -DEXIV2_BUILD_UNIT_TESTS=ON \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \ - -DBUILD_WITH_COVERAGE=OFF \ - -DEXIV2_TEAM_USE_SANITIZERS=ON \ - -DCMAKE_INSTALL_PREFIX=install \ - .. && \ - cmake --build . --parallel + cmake --preset base_mac -S . -B build -DEXIV2_TEAM_USE_SANITIZERS=ON + cmake --build build --parallel - name: Tests run: | diff --git a/.github/workflows/on_PR_windows_matrix.yml b/.github/workflows/on_PR_windows_matrix.yml index 13cf3acc..d8f8edcc 100644 --- a/.github/workflows/on_PR_windows_matrix.yml +++ b/.github/workflows/on_PR_windows_matrix.yml @@ -1,5 +1,9 @@ name: On PRs - Windows Matrix +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: pull_request: paths-ignore: @@ -50,7 +54,7 @@ jobs: - name: Install Conan & Common config run: | - pip.exe install "conan==1.45.0" + pip.exe install "conan==1.48.1" conan config install https://github.com/conan-io/conanclientcert.git conan profile new --detect default conan profile update settings.build_type=${{matrix.build_type}} default @@ -73,23 +77,12 @@ jobs: - name: Build run: | - cmake -GNinja ` - -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ` - -DBUILD_SHARED_LIBS=${{matrix.shared_libraries}} ` - -DEXIV2_ENABLE_NLS=OFF ` - -DEXIV2_ENABLE_WEBREADY=ON ` - -DEXIV2_ENABLE_BMFF=ON ` - -DEXIV2_BUILD_UNIT_TESTS=ON ` - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON ` - -DCMAKE_INSTALL_PREFIX=install ` - -S . -B build && ` + cmake --preset base_windows -S . -B build -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.shared_libraries}} cmake --build build --parallel - name: Install run: | - cd build - cmake --install . - tree /f install + cmake --install build - name: Test if: ${{matrix.platform == 'x64'}} @@ -122,26 +115,26 @@ jobs: install: >- base-devel pacboy: >- - toolchain:p + cc:p + gcc-libs:p + libwinpthread:p cmake:p + ninja:p expat:p gettext:p gtest:p libiconv:p zlib:p + curl:p - name: Build run: | - cmake -G"MSYS Makefiles" \ - -DCMAKE_CXX_FLAGS=-Wno-deprecated \ + cmake --preset base_windows -S . -B build \ -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ -DBUILD_SHARED_LIBS=${{matrix.shared_libraries}} \ - -DEXIV2_BUILD_SAMPLES=ON \ - -DEXIV2_ENABLE_NLS=OFF \ - -DEXIV2_ENABLE_WEBREADY=ON \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_BUILD_UNIT_TESTS=ON \ - -S . -B build && \ + -DCONAN_AUTO_INSTALL=OFF \ + -DCMAKE_CXX_FLAGS=-Wno-deprecated \ + -DEXIV2_TEAM_WARNINGS_AS_ERRORS=OFF cmake --build build --parallel - name: Test @@ -176,6 +169,7 @@ jobs: gcc-g++ cmake ninja + libcurl-devel libexpat-devel libxml2-devel libxslt-devel @@ -190,6 +184,7 @@ jobs: -DEXIV2_ENABLE_NLS=OFF \ -DEXIV2_ENABLE_WIN_UNICODE=OFF \ -DEXIV2_ENABLE_WEBREADY=ON \ + -DEXIV2_ENABLE_CURL=ON \ -DEXIV2_ENABLE_BMFF=ON \ -DEXIV2_BUILD_UNIT_TESTS=OFF \ -S . -B build && \ diff --git a/.github/workflows/on_push_BasicWinLinMac.yml b/.github/workflows/on_push_BasicWinLinMac.yml index 4ea08930..16fb7904 100644 --- a/.github/workflows/on_push_BasicWinLinMac.yml +++ b/.github/workflows/on_push_BasicWinLinMac.yml @@ -8,6 +8,10 @@ on: paths-ignore: - "*.md" +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + name: On PUSH - Basic CI for main platforms jobs: @@ -41,35 +45,16 @@ jobs: - name: Install Conan & Common config run: | - pip.exe install "conan==1.45.0" + pip.exe install "conan==1.48.1" conan profile new --detect default conan profile show default conan profile update settings.compiler="Visual Studio" default conan profile update settings.compiler.version=17 default conan config set storage.path=$Env:GITHUB_WORKSPACE/conanCache - - name: Run Conan - run: | - md build - cd build - conan profile list - conan install .. --build missing - - name: Build run: | - cmake -GNinja ` - -DCMAKE_BUILD_TYPE=Release ` - -DBUILD_SHARED_LIBS=ON ` - -DEXIV2_BUILD_SAMPLES=ON ` - -DEXIV2_ENABLE_NLS=OFF ` - -DEXIV2_ENABLE_PNG=ON ` - -DEXIV2_ENABLE_WEBREADY=ON ` - -DEXIV2_ENABLE_BMFF=ON ` - -DEXIV2_BUILD_UNIT_TESTS=ON ` - -DEXIV2_ENABLE_WIN_UNICODE=OFF ` - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON ` - -DCMAKE_INSTALL_PREFIX=install .. ` - -S . -B build && ` + cmake --preset win-release -S . -B build cmake --build build --parallel @@ -88,7 +73,7 @@ jobs: - name: install dependencies run: | sudo apt-get install ninja-build - pip3 install conan==1.45.0 + pip3 install conan==1.48.1 - name: Conan run: | @@ -100,20 +85,8 @@ jobs: - name: build and compile run: | - cd build && \ - cmake -GNinja \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=ON \ - -DEXIV2_BUILD_SAMPLES=ON \ - -DEXIV2_ENABLE_PNG=ON \ - -DEXIV2_ENABLE_WEBREADY=ON \ - -DEXIV2_ENABLE_CURL=ON \ - -DEXIV2_BUILD_UNIT_TESTS=ON \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \ - -DCMAKE_INSTALL_PREFIX=install \ - .. && \ - cmake --build . --parallel + cmake --preset linux-release-NoConan -S . -B build + cmake --build build --parallel - name: Test run: | @@ -141,21 +114,8 @@ jobs: - name: build and compile run: | - mkdir build && cd build && \ - cmake -GNinja \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=ON \ - -DEXIV2_BUILD_SAMPLES=ON \ - -DEXIV2_ENABLE_PNG=ON \ - -DEXIV2_ENABLE_WEBREADY=ON \ - -DEXIV2_ENABLE_CURL=ON \ - -DEXIV2_BUILD_UNIT_TESTS=ON \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \ - -DCMAKE_INSTALL_PREFIX=install \ - -DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations" \ - .. && \ - cmake --build . --parallel + cmake --preset base_mac -S . -B build -DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations" + cmake --build build --parallel - name: Test run: | diff --git a/.github/workflows/on_push_ExtraJobsForMain.yml b/.github/workflows/on_push_ExtraJobsForMain.yml index f5f9a78e..13e690e5 100644 --- a/.github/workflows/on_push_ExtraJobsForMain.yml +++ b/.github/workflows/on_push_ExtraJobsForMain.yml @@ -1,5 +1,9 @@ name: On PUSH - Linux Special Builds for main branch +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: push: branches: @@ -8,6 +12,7 @@ on: - '!*' paths-ignore: - "*.md" + workflow_dispatch: jobs: special_debugRelease: @@ -19,8 +24,9 @@ jobs: - name: install dependencies run: | - pip3 install conan==1.45.0 - pip install gcovr + sudo apt-get install ninja-build + pip3 install conan==1.48.1 + pip3 install gcovr - name: Conan common config run: | @@ -36,20 +42,8 @@ jobs: - name: Build run: | - cd build && \ - cmake -DCMAKE_BUILD_TYPE=Debug \ - -DBUILD_SHARED_LIBS=ON \ - -DEXIV2_ENABLE_PNG=ON \ - -DEXIV2_ENABLE_WEBREADY=ON \ - -DEXIV2_ENABLE_CURL=ON \ - -DEXIV2_BUILD_UNIT_TESTS=ON \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \ - -DEXIV2_BUILD_SAMPLES=ON \ - -DBUILD_WITH_COVERAGE=ON \ - -DCMAKE_INSTALL_PREFIX=install \ - .. && \ - cmake --build . + cmake --preset linux-coverage -S . -B build + cmake --build build - name: Tests + Upload coverage run: | diff --git a/.github/workflows/on_push_clang_format.yml b/.github/workflows/on_push_clang_format.yml index 688939a9..2f7132e7 100644 --- a/.github/workflows/on_push_clang_format.yml +++ b/.github/workflows/on_push_clang_format.yml @@ -1,4 +1,7 @@ name: Clang Format Checker +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true on: [push, pull_request] jobs: clang-format-checking: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e399a1b0..e1fb904a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,7 @@ name: Release +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true on: push: tags: @@ -22,7 +25,7 @@ jobs: - name: Install dependencies run: | sudo apt-get install ninja-build gettext doxygen graphviz - pip3 install conan==1.45.0 + pip3 install conan==1.48.1 - name: Conan common config run: | @@ -39,16 +42,7 @@ jobs: - name: Build packaged release run: | - cmake -GNinja -S . -B build \ - -DEXIV2_TEAM_PACKAGING=ON \ - -DBUILD_SHARED_LIBS=ON \ - -DEXIV2_ENABLE_WEBREADY=OFF \ - -DEXIV2_ENABLE_NLS=ON \ - -DCMAKE_BUILD_TYPE=Release \ - -DEXIV2_ENABLE_BMFF=ON \ - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON \ - -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \ - -DEXIV2_BUILD_DOC=ON + cmake --preset linux-all -S . -B build -DEXIV2_TEAM_PACKAGING=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DEXIV2_ENABLE_WEBREADY=OFF -DEXIV2_ENABLE_CURL=OFF cmake --build build -t doc cmake --build build -t package @@ -114,20 +108,28 @@ jobs: - name: Set up Python uses: actions/setup-python@v3 with: - python-version: 3.9 + python-version: 3.7 - name: Install doxygen run: | choco install doxygen.install choco install graphviz + - name: Restore conan cache + uses: actions/cache@v2 + with: + path: ${{github.workspace}}/conanCache + key: ${{runner.os}}-release-win-${{ hashFiles('conanfile.py') }} + - name: Install Conan & Common config run: | - pip.exe install "conan==1.45.0" + pip.exe install "conan==1.48.1" conan profile new --detect default + conan profile show default conan profile update settings.build_type=Release default conan profile update settings.compiler="Visual Studio" default conan profile update settings.compiler.version=17 default + conan config set storage.path=$Env:GITHUB_WORKSPACE/conanCache - name: Run Conan run: | @@ -137,18 +139,9 @@ jobs: - name: Build packaged release run: | - cmake -GNinja -S . -B build ` - -DEXIV2_TEAM_PACKAGING=ON ` - -DBUILD_SHARED_LIBS=ON ` - -DEXIV2_ENABLE_WEBREADY=OFF ` - -DEXIV2_ENABLE_NLS=OFF ` - -DCMAKE_BUILD_TYPE=Release ` - -DEXIV2_ENABLE_BMFF=ON ` - -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON ` - -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON ` - -DEXIV2_BUILD_DOC=ON - cmake --build build -t doc - cmake --build build -t package + cmake --preset win-release -S . -B build -DEXIV2_TEAM_PACKAGING=ON -DEXIV2_BUILD_DOC=ON -DEXIV2_ENABLE_WEBREADY=OFF -DEXIV2_ENABLE_CURL=OFF + cmake --build build --parallel -t doc + cmake --build build --parallel -t package - uses: actions/upload-artifact@v3 with: @@ -196,14 +189,14 @@ jobs: with: script: | try{ - const rel_id = await github.repos.getReleaseByTag({ + const rel_id = await github.rest.repos.getReleaseByTag({ ...context.repo, tag: "nightly" }).then(result => result.data.id); console.log( "Found existing nightly release with id: ", rel_id); - await github.repos.deleteRelease({ + await github.rest.repos.deleteRelease({ ...context.repo, release_id: rel_id }); @@ -215,7 +208,7 @@ jobs: } try{ - await github.git.deleteRef({ + await github.rest.git.deleteRef({ ...context.repo, ref: "tags/nightly" }); diff --git a/.gitignore b/.gitignore index c46bd2e7..02b6d1ad 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,6 @@ doc/html contrib/vms/.vagrant /.vscode .vs/ +CMakeUserPresets.json *cppcheck* diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000..045053c8 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,149 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "base_ninja", + "description": "Base preset to use ninja as generator", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/build-${presetName}", + "installDir": "${sourceDir}/build-${presetName}/install", + "cacheVariables": { + "BUILD_SHARED_LIBS": true, + "CONAN_AUTO_INSTALL": true, + "EXIV2_BUILD_SAMPLES": true, + "EXIV2_ENABLE_WEBREADY": true, + "EXIV2_ENABLE_CURL": true, + "EXIV2_ENABLE_PNG": true, + "EXIV2_ENABLE_BMFF": true, + "EXIV2_BUILD_UNIT_TESTS": true, + "EXIV2_TEAM_WARNINGS_AS_ERRORS": true, + "EXIV2_ENABLE_NLS": false + } + }, + { + "name": "base_windows", + "description": "Base preset for Windows (specially useful for CI jobs)", + "displayName": "Base preset for Windows (specially useful for CI jobs)", + "inherits": "base_ninja", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "base_linux", + "description": "Base preset for Linux", + "displayName": "Base preset for Linux with default compiler: GCC (specially useful for CI jobs)", + "inherits": "base_ninja", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "cacheVariables": { + "BUILD_WITH_CCACHE": true + } + }, + { + "name": "base_mac", + "description": "Base preset for macOS (no conan usage)", + "displayName": "Base preset for macOS with default compiler: AppleClang (specially useful for CI jobs)", + "inherits": "base_ninja", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CONAN_AUTO_INSTALL": false + } + }, + + { + "name": "msvc", + "displayName": "Visual Studio cl toolchain (also usable from VS Code)", + "inherits": "base_windows", + "architecture": { + "value": "x64", + "strategy": "external" + }, + "toolset": { + "value": "host=x64", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_C_COMPILER": "cl.exe", + "CMAKE_CXX_COMPILER": "cl.exe", + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "win-debug", + "displayName": "Windows Debug with configured architecture", + "description": "Sets Debug build type with the preloaded Visual Studio Environment", + "inherits": "base_windows", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "win-release", + "displayName": "Windows Release with configured architecture", + "description": "Sets Release build type with the preloaded Visual Studio Environment", + "inherits": "base_windows", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "linux-debug", + "displayName": "Linux Debug (Ninja Generator) with default architecture", + "inherits": "base_linux", + "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } + }, + { + "name": "linux-release", + "displayName": "Linux Release (Ninja Generator) with default architecture", + "inherits": "base_linux", + "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } + }, + { + "name": "linux-debug-NoConan", + "displayName": "Same as linux-debug but without automatic conan execution", + "inherits": "linux-debug", + "cacheVariables": { "CONAN_AUTO_INSTALL": false } + }, + { + "name": "linux-release-NoConan", + "displayName": "Same as linux-release but without automatic conan execution", + "inherits": "linux-release", + "cacheVariables": { "CONAN_AUTO_INSTALL": false } + }, + { + "name": "linux-coverage", + "displayName": "Same as linux-debug-NoConan with coverage enabled", + "inherits": "linux-debug-NoConan", + "cacheVariables": { "BUILD_WITH_COVERAGE": true } + }, + { + "name": "linux-sanitizers", + "displayName": "Same as linux-debug-NoConan with sanitizers enabled", + "inherits": "linux-debug-NoConan", + "cacheVariables": { "EXIV2_TEAM_USE_SANITIZERS": true } + }, + { + "name": "linux-all", + "displayName": "Same as linux-release-NoConan and with rest of things enabled (doc + NLS)", + "description": "requires installation of packages: doxygen graphviz gettext", + "inherits": "linux-release-NoConan", + "cacheVariables": { + "EXIV2_ENABLE_NLS": true, + "EXIV2_BUILD_DOC": true + } + } + + ] +} diff --git a/README.md b/README.md index f6a906ea..6aeb2f18 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ The file ReadMe.txt in a build bundle describes how to install the library on th - [Building, Installing, Using and Uninstalling Exiv2](#B_I_U) - [Build, Install, Use and Uninstall Exiv2 on a UNIX-like system](#B_I_U_Unix) - [Build and Install Exiv2 with Visual Studio](#B_I_U_VisualStudio) + - [Configure the project with CMake presets](#CMakePresets) - [Build Options](#BuildOptions) - [Dependencies](#Dependencies) - [Building and linking your code with Exiv2](#BuildAndLinkYourCode) @@ -126,7 +127,7 @@ path. ## Build and Install Exiv2 with Visual Studio -We recommend that you use conan to download the Exiv2 external dependencies on Windows. On other platforms (macOS, Linux and others), traditionally the platform package managers have been used. These are discussed at [Platform Notes](#PlatformNotes). The options to configure and compile the project using Visual Studio are similar to UNIX like systems. +We recommend to use conan to download the Exiv2 external dependencies on Windows. On other platforms (macOS, Linux and others), traditionally the platform package managers have been used. However, conan can be used in any platform/architecture to bring the project dependencies. These are discussed at [Platform Notes](#PlatformNotes). The options to configure and compile the project using Visual Studio are similar to UNIX like systems. See [README-CONAN](README-CONAN.md) for more information about Conan. When you build, you may install with the following command. @@ -136,6 +137,99 @@ When you build, you may install with the following command. ``` This will create and copy the exiv2 build artefacts to `%ProgramFiles%/exiv2`. To be able to run the `exiv2` command line application from any terminal you should modify your path to include `%ProgramFiles%/exiv2/bin`. +[TOC](#TOC) +
+ +## Configure the project with the CMake presets + +CMake presets (see documentation [here](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html)) were added recently to the project to ease the CMake configuration process for typical configurations. The presets are defined in the file `CMakePresets.json` and they can be used from the terminal or interpreted by different IDEs. Please note that one needs to use a recent version of CMake (>= 3.21) supporting the presets feature. + +One can list the available presets using the `--list-presets` option: + +```bash +# Running the command from a Windows terminal +$ cmake --list-presets +Available configure presets: + + "msvc" - Visual Studio cl toolchain (also usable from VS Code) + "win-debug" - Windows Debug with configured architecture + "win-release" - Windows Release with configured architecture + +# Running the command from a Linux terminal +$ cmake --list-presets +Available configure presets: + + "linux-debug" - Linux Debug with default architecture + "linux-release" - Linux Release with default architecture +``` + +The project configuration with a specific preset can be choosen with the CMake `--preset` option. In the following terminal output we comment out some interesting things happening during the project configuration: + +```bash +# Configuring the project using a preset +$ cmake --preset win-release +Preset CMake variables: + +# Note that with the usage of a preset, we pass many different options to CMake. + BUILD_SHARED_LIBS:BOOL="TRUE" + CMAKE_BUILD_TYPE="Release" + CMAKE_INSTALL_PREFIX:PATH="C:/dev/personal/exiv2/build-win-release/install" + # A build & install directory are configured with the preset + CONAN_AUTO_INSTALL:BOOL="TRUE" + EXIV2_BUILD_SAMPLES:BOOL="TRUE" + EXIV2_BUILD_UNIT_TESTS:BOOL="TRUE" + EXIV2_ENABLE_BMFF:BOOL="TRUE" + EXIV2_ENABLE_CURL:BOOL="TRUE" + EXIV2_ENABLE_NLS:BOOL="FALSE" + EXIV2_ENABLE_PNG:BOOL="TRUE" + EXIV2_ENABLE_WEBREADY:BOOL="TRUE" + EXIV2_TEAM_WARNINGS_AS_ERRORS:BOOL="TRUE" + +# Conan can be automatically detected in your system and it is run automatically to bring the +# project dependencies +-- Conan: Detected VS runtime: MD +-- Conan: checking conan executable +-- Conan: Found program C:/dev/envs/conan/Scripts/conan.exe +-- Conan: Version found Conan version 1.47.0 +-- Conan executing: C:/dev/envs/conan/Scripts/conan.exe install .. --remote conancenter --build missing --options webready=True --settings arch=x86_64 --settings build_type=Release --settings compiler=Visual Studio --settings compiler.version=17 --settings compiler.runtime=MD +... + +# CMake finds the project dependencies which were automatically handled by conan +-- Conan: Using autogenerated FindZLIB.cmake +-- Library zlib found C:/Users/luis/.conan/data/zlib/1.2.11/_/_/package/-- Conan: Using autogenerated FindCURL.cmake +-- Library libcurl_imp found C:/Users/luis/.conan/data/libcurl/7.79.0/_/_/package/ +... + +# CMake finish the project configuration and prints a report +-- Install prefix: C:/dev/personal/exiv2/build-win-release/install +-- ------------------------------------------------------------------ +-- CMake Generator: Ninja +-- CMAKE_BUILD_TYPE: Release +-- Compiler info: MSVC (C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.30.30705/bin/Hostx64/x64/cl.exe) ; version: 19.30.30705.0 +-- CMAKE_CXX_STANDARD:17 +-- --- Compiler flags --- +-- General: /DWIN32 /D_WINDOWS /W3 /GR /EHsc + /MP + /utf-8 + /WX +-- Extra: +-- Debug: /MDd /Zi /Ob0 /Ox /Zo +-- Release: /MD /O2 /DNDEBUG +-- RelWithDebInfo: /MD /Zi /O2 /DNDEBUG +-- MinSizeRel: /MD /O1 /DNDEBUG +-- --- Linker flags --- +-- General: /machine:x64 /WX +-- Debug: /debug /INCREMENTAL +-- Release: /INCREMENTAL:NO +-- RelWithDebInfo: /debug /INCREMENTAL +-- MinSizeRel: /INCREMENTAL:NO +-- +... +-- Build files have been written to: C:/dev/personal/exiv2/build-win-release +``` + +Note that the usage of CMake presets allow the project contributors to use the same set of options easily in different environments (using terminal, IDEs or CI). + [TOC](#TOC)
@@ -145,7 +239,7 @@ There are two groups of CMake options which are relevant to the project: global | Options | Purpose (_default_) | |:------------- |:------------- | -| CMAKE\_INSTALL\_PREFIX
CMAKE\_BUILD\_TYPE
BUILD\_SHARED\_LIBS | Where to install on your computer _**(/usr/local)**_
Type of build _**(Release)**_ See: [Debugging Exiv2](#Debugging)
Build exiv2lib as shared or static _**(On)**_ | +| CMAKE\_INSTALL\_PREFIX
CMAKE\_BUILD\_TYPE
BUILD\_SHARED\_LIBS | Where to install on your computer _**(/usr/local)**_
Type of build _**(Release)**_ See: [Debugging Exiv2](#Debugging)
Build exiv2lib as SHARED or STATIC | Options defined at `exiv2/CMakeLists.txt` include: @@ -160,10 +254,10 @@ option( EXIV2_ENABLE_BMFF "Build with BMFF support" ON 577 rmills@rmillsmm:~/gnu/github/exiv2/exiv2 $ ``` -Options are defined on the CMake command-line: +Using the command-line, these variables can be set/updated using the option `-D`: ```bash -$ cmake -DBUILD_SHARED_LIBS=On -DEXIV2_ENABLE_NLS=Off +$ cmake -DBUILD_SHARED_LIBS=ON -DEXIV2_ENABLE_NLS=OFF ``` [TOC](#TOC) @@ -175,9 +269,9 @@ The following Exiv2 features require external libraries: | Feature | Package | Default | To change default | Availability | |:-------------------------- |:-------- |:--------:| :---------------------------- |:----------- | -| PNG image support | zlib | ON | -DEXIV2\_ENABLE\_PNG=Off | [http://zlib.net/](http://zlib.net/) | -| XMP support | expat | ON | -DEXIV2\_ENABLE\_XMP=Off | [http://expat.sourceforge.net](http://expat.sourceforge.net)/
Use _**Expat 2.2.6**_ and later | -| Natural language system | gettext | OFF | -DEXIV2\_ENABLE\_NLS=On | [http://www.gnu.org/software/gettext/](http://www.gnu.org/software/gettext/) | +| PNG image support | zlib | ON | -DEXIV2\_ENABLE\_PNG=OFF | [http://zlib.net/](http://zlib.net/) | +| XMP support | expat | ON | -DEXIV2\_ENABLE\_XMP=OFF | [http://expat.sourceforge.net](http://expat.sourceforge.net)/
Use _**Expat 2.2.6**_ and later | +| Natural language system | gettext | OFF | -DEXIV2\_ENABLE\_NLS=ON | [http://www.gnu.org/software/gettext/](http://www.gnu.org/software/gettext/) | | Character set conversion | libiconv | | Disabled for Visual Studio.
Linked when installed on UNIX like platforms. | [https://www.gnu.org/software/libiconv/](https://www.gnu.org/software/libiconv/) | On UNIX systems, you may install the dependencies using the distribution's package management system. Install the @@ -297,7 +391,7 @@ Localisation is supported on a UNIX-like platform: Linux, macOS, Cygwin and Min Crowdin have provided Exiv2 with a free open-source license to use their services. The Exiv2 localisation project is located at [https://crowdin.com/project/exiv2](https://crowdin.com/project/exiv2). You will also need to register to have a free user account on Crowdin. The Crowdin setup is discussed here: [https://github.com/Exiv2/exiv2/issues/1510](https://github.com/Exiv2/exiv2/issues/1510). It is recommended that you coordinate with Leonardo before contributing localisation changes on Crowdin. You can contact Leonardo by via GitHub. -To build localisation support, use the CMake option `-DEXIV2_ENABLE_NLS=On`. You must install the `gettext` package with your package manager or from source. The `gettext` package is available from [http://www.gnu.org/software/gettext/](http://www.gnu.org/software/gettext/) and includes the library `libintl` and utilities to build localisation files. If CMake produces error messages which mention libintl or gettext, you should verify that the package `gettext` has been correctly built and installed. +To build localisation support, use the CMake option `-DEXIV2_ENABLE_NLS=ON`. You must install the `gettext` package with your package manager or from source. The `gettext` package is available from [http://www.gnu.org/software/gettext/](http://www.gnu.org/software/gettext/) and includes the library `libintl` and utilities to build localisation files. If CMake produces error messages which mention libintl or gettext, you should verify that the package `gettext` has been correctly built and installed. You must install the build to test localisation. This ensures that the localisation message files can be found at run-time. You cannot test localisation in the directory `build\bin`. @@ -386,11 +480,11 @@ $ Building documentation requires installing special tools. You will probably prefer to read the documentation on-line from the project website: https://exiv2.org -To build documentation, use the CMake option **`-DEXIV2_BUILD_DOC=On`**. +To build documentation, use the CMake option **`-DEXIV2_BUILD_DOC=ON`**. Additionally, you will require an additional build step to actually build the documentation. ```bash -$ cmake ..options.. -DEXIV2_BUILD_DOC=On +$ cmake ..options.. -DEXIV2_BUILD_DOC=ON $ cmake --build . --target doc ``` @@ -437,7 +531,7 @@ When the Exiv2 websites are updated, the generated tag webpages are reformatted ## Building Exiv2 Packages -To enable the building of Exiv2 packages, use the CMake option `-DEXIV2_TEAM_PACKAGING=On`. +To enable the building of Exiv2 packages, use the CMake option `-DEXIV2_TEAM_PACKAGING=ON`. You should not build Exiv2 Packages. This feature is intended for use by Team Exiv2 to create Platform and Source Packages on the buildserver. @@ -451,7 +545,7 @@ Create and build exiv2 for your platform. $ git clone https://github.com/exiv2/exiv2 $ mkdir -p exiv2/build $ cd exiv2/build -$ cmake .. -G "Unix Makefiles" -DEXIV2_TEAM_PACKAGING=On +$ cmake .. -G "Unix Makefiles" -DEXIV2_TEAM_PACKAGING=ON ... -- Build files have been written to: .../build $ cmake --build . --config Release @@ -603,19 +697,19 @@ Installing and using ccache (and other similar utilities), is platform dependent $ sudo apt install --yes ccache ``` -To build with ccache, use the CMake option **-DBUILD\_WITH\_CCACHE=On** +To build with ccache, use the CMake option **-DBUILD\_WITH\_CCACHE=ON** ```bash $ cd $ mkdir build ; cd build ; cd build -$ cmake .. -G "Unix Makefiles" -DBUILD_WITH_CCACHE=On +$ cmake .. -G "Unix Makefiles" -DBUILD_WITH_CCACHE=ON $ cmake --build . # Build again to appreciate the performance gain $ cmake --build . --target clean $ cmake --build . ``` -Due to the way in which ccache is installed in Fedora (and other Linux distros), ccache effectively replaces the compiler. A default build or **-DBUILD\_WITH\_CCACHE=Off** is not effective and the environment variable CCACHE_DISABLE is required to disable ccache. [https://github.com/Exiv2/exiv2/issues/361](https://github.com/Exiv2/exiv2/issues/361) +Due to the way in which ccache is installed in Fedora (and other Linux distros), ccache effectively replaces the compiler. A default build or **-DBUILD\_WITH\_CCACHE=OFF** is not effective and the environment variable CCACHE_DISABLE is required to disable ccache. [https://github.com/Exiv2/exiv2/issues/361](https://github.com/Exiv2/exiv2/issues/361) [TOC](#TOC)
@@ -707,10 +801,9 @@ $ make Note, you may wish to choose to build with optional features and/or build static libraries. To do this, request appropriately on the mingw64-cmake command: ```bash -$ mingw64-cmake .. -DEXIV2_TEAM_EXTRA_WARNINGS=On \ - -DEXIV2_ENABLE_WEBREADY=On \ - -DEXIV2_ENABLE_WIN_UNICODE=On \ - -DBUILD_SHARED_LIBS=Off +$ mingw64-cmake .. -DEXIV2_TEAM_EXTRA_WARNINGS=ON \ + -DEXIV2_ENABLE_WEBREADY=ON \ + -DBUILD_SHARED_LIBS=OFF ``` The options available for cross-compiling are the same as provided for all builds. See: [Build Options](#BuildOptions) @@ -773,7 +866,7 @@ You will find that 3 tests fail at the end of the test suite. It is safe to ign ## Static and Shared Libraries -You can build either static or shared libraries. Both can be linked with either static or shared run-time libraries. You specify the shared/static with the option `-BUILD_SHARED_LIBS=On|Off` You specify the run-time with the option `-DEXIV2_ENABLE_DYNAMIC_RUNTIME=On|Off`. The default for both options default is On. So you build shared and use the shared libraries which are `.dll` on Windows (msvc, Cygwin and MinGW/msys), `.dylib` on macOS and `.so` on Linux and UNIX. +You can build either static or shared libraries. Both can be linked with either static or shared run-time libraries. You specify the shared/static with the option `-BUILD_SHARED_LIBS=ON|OFF` You specify the run-time with the option `-DEXIV2_ENABLE_DYNAMIC_RUNTIME=ON|OFF`. The default for both options default is ON. So you build shared and use the shared libraries which are `.dll` on Windows (msvc, Cygwin and MinGW/msys), `.dylib` on macOS and `.so` on Linux and UNIX. CMake creates your build artefacts in the directories `bin` and `lib`. The `bin` directory contains your executables and .DLLs. The `lib` directory contains your static libraries. When you install exiv2, the build artefacts are copied to your system's prefix directory which by default is `/usr/local/`. If you wish to test and use your build without installing, you will have to set you PATH appropriately. Linux/Unix users should also set `LD_LIBRARY_PATH` and macOS users should set `DYLD_LIBRARY_PATH`. @@ -806,13 +899,13 @@ This is discussed: [https://github.com/Exiv2/exiv2/issues/1230](https://github.c **Attention is drawn to the possibility that bmff support may be the subject of patent rights. _Exiv2 shall not be held responsible for identifying any or all such patent rights. Exiv2 shall not be held responsible for the legal consequences of the use of this code_.** -Access to the bmff code is guarded in two ways. Firstly, you have to build the library with the CMake option: `-DEXIV2_ENABLE_BMFF=On`. Secondly, the application must enable bmff support at run-time by calling the following function. +Access to the bmff code is guarded in two ways. Firstly, you have to build the library with the CMake option: `-DEXIV2_ENABLE_BMFF=ON`. Secondly, the application must enable bmff support at run-time by calling the following function. ```cpp EXIV2API bool enableBMFF(bool enable); ``` -The return value from `enableBMFF()` is true if the library has been build with bmff support (CMake option -DEXIV2_ENABLE_BMFF=On). +The return value from `enableBMFF()` is true if the library has been build with bmff support (CMake option -DEXIV2_ENABLE_BMFF=ON). Applications may wish to provide a preference setting to enable bmff support and thereby place the responsibility for the use of this code with the user of the application. @@ -928,7 +1021,7 @@ The Variable EXIV2\_PORT or EXIV2\_HTTP can be set to None to skip http tests. You can run tests directly from the build: ```bash -$ cmake .. -G "Unix Makefiles" -DEXIV2_BUILD_UNIT_TESTS=On +$ cmake .. -G "Unix Makefiles" -DEXIV2_BUILD_UNIT_TESTS=ON ... lots of output and build summary ... $ cmake --build . ... lots of output ... @@ -993,7 +1086,7 @@ As a summary, the procedure is: c:\...\exiv2>mkdir build c:\...\exiv2>cd build c:\...\exiv2\build>conan install .. --build missing --profile msvc2019Release -c:\...\exiv2\build>cmake .. -DEXIV2_BUILD_UNIT_TESTS=On -G "Visual Studio 16 2019" +c:\...\exiv2\build>cmake .. -DEXIV2_BUILD_UNIT_TESTS=ON -G "Visual Studio 16 2019" c:\...\exiv2\build>cmake --build . --config Release ... lots of output from compiler and linker ... c:\...\exiv2\build>ctest -C Release @@ -1012,7 +1105,7 @@ set EXIV2_PORT= ## Unit Tests -The code for the unit tests is in `/unitTests`. To include unit tests in the build, use the *CMake* option `-DEXIV2_BUILD_UNIT_TESTS=On`. +The code for the unit tests is in `/unitTests`. To include unit tests in the build, use the *CMake* option `-DEXIV2_BUILD_UNIT_TESTS=ON`. There is a discussion on the web about installing GTest: [https://github.com/Exiv2/exiv2/issues/575](https://github.com/Exiv2/exiv2/issues/575) diff --git a/app/actions.cpp b/app/actions.cpp index 752d026c..430a9bb4 100644 --- a/app/actions.cpp +++ b/app/actions.cpp @@ -6,12 +6,14 @@ #include "app_utils.hpp" #include "config.h" #include "easyaccess.hpp" +#include "enforce.hpp" #include "exif.hpp" #include "futils.hpp" #include "i18n.h" // NLS support. #include "image.hpp" #include "iptc.hpp" #include "preview.hpp" +#include "safe_op.hpp" #include "types.hpp" #include "xmp_exiv2.hpp" @@ -38,9 +40,10 @@ #include #endif -#if !defined(__MINGW__) && !defined(_MSC_VER) -#define _fileno(a) a -#define _setmode(a, b) +#ifndef _WIN32 +#define _setmode(a, b) \ + do { \ + } while (false) #endif namespace fs = std::filesystem; @@ -177,7 +180,7 @@ int setModeAndPrintStructure(Exiv2::PrintStructureOption option, const std::stri ascii.write_uint8(str.size() * 3, 0); std::copy(str.begin(), str.end(), iccProfile.begin()); if (Exiv2::base64encode(iccProfile.c_data(), str.size(), reinterpret_cast(ascii.data()), str.size() * 3)) { - long chunk = 60; + const size_t chunk = 60; std::string code = std::string("data:") + ascii.c_str(); size_t length = code.size(); for (size_t start = 0; start < length; start += chunk) { @@ -197,34 +200,25 @@ int setModeAndPrintStructure(Exiv2::PrintStructureOption option, const std::stri int Print::run(const std::string& path) { try { path_ = path; - int rc = 0; switch (Params::instance().printMode_) { case Params::pmSummary: - rc = Params::instance().greps_.empty() ? printSummary() : printList(); - break; + return Params::instance().greps_.empty() ? printSummary() : printList(); case Params::pmList: - rc = printList(); - break; + return printList(); case Params::pmComment: - rc = printComment(); - break; + return printComment(); case Params::pmPreview: - rc = printPreviewList(); - break; + return printPreviewList(); case Params::pmStructure: - rc = printStructure(std::cout, Exiv2::kpsBasic, path_); - break; + return printStructure(std::cout, Exiv2::kpsBasic, path_); case Params::pmRecursive: - rc = printStructure(std::cout, Exiv2::kpsRecursive, path_); - break; + return printStructure(std::cout, Exiv2::kpsRecursive, path_); case Params::pmXMP: - rc = setModeAndPrintStructure(Exiv2::kpsXMP, path_, binary()); - break; + return setModeAndPrintStructure(Exiv2::kpsXMP, path_, binary()); case Params::pmIccProfile: - rc = setModeAndPrintStructure(Exiv2::kpsIccProfile, path_, binary()); - break; + return setModeAndPrintStructure(Exiv2::kpsIccProfile, path_, binary()); } - return rc; + return 0; } catch (const Exiv2::Error& e) { std::cerr << "Exiv2 exception in print action for file " << path << ":\n" << e << "\n"; return 1; @@ -242,7 +236,7 @@ int Print::printSummary() { auto image = Exiv2::ImageFactory::open(path_); image->readMetadata(); - Exiv2::ExifData& exifData = image->exifData(); + const Exiv2::ExifData& exifData = image->exifData(); align_ = 16; // Filename @@ -362,8 +356,8 @@ int Print::printList() { auto image = Exiv2::ImageFactory::open(path_); image->readMetadata(); // Set defaults for metadata types and data columns - if (Params::instance().printTags_ == Exiv2::mdNone) { - Params::instance().printTags_ = Exiv2::mdExif | Exiv2::mdIptc | Exiv2::mdXmp; + if (Params::instance().printTags_ == MetadataId::invalid) { + Params::instance().printTags_ = MetadataId::exif | MetadataId::iptc | MetadataId::xmp; } if (Params::instance().printItems_ == 0) { Params::instance().printItems_ = Params::prKey | Params::prType | Params::prCount | Params::prTrans; @@ -374,7 +368,7 @@ int Print::printList() { int Print::printMetadata(const Exiv2::Image* image) { bool ret = false; bool noExif = false; - if (Params::instance().printTags_ & Exiv2::mdExif) { + if ((Params::instance().printTags_ & MetadataId::exif) == MetadataId::exif) { const Exiv2::ExifData& exifData = image->exifData(); for (auto&& md : exifData) { ret |= printMetadatum(md, image); @@ -384,7 +378,7 @@ int Print::printMetadata(const Exiv2::Image* image) { } bool noIptc = false; - if (Params::instance().printTags_ & Exiv2::mdIptc) { + if ((Params::instance().printTags_ & MetadataId::iptc) == MetadataId::iptc) { const Exiv2::IptcData& iptcData = image->iptcData(); for (auto&& md : iptcData) { ret |= printMetadatum(md, image); @@ -394,7 +388,7 @@ int Print::printMetadata(const Exiv2::Image* image) { } bool noXmp = false; - if (Params::instance().printTags_ & Exiv2::mdXmp) { + if ((Params::instance().printTags_ & MetadataId::xmp) == MetadataId::xmp) { const Exiv2::XmpData& xmpData = image->xmpData(); for (auto&& md : xmpData) { ret |= printMetadatum(md, image); @@ -434,7 +428,7 @@ bool Print::grepTag(const std::string& key) { bool Print::keyTag(const std::string& key) { bool result = Params::instance().keys_.empty(); - for (auto&& k : Params::instance().keys_) { + for (const auto& k : Params::instance().keys_) { if (result) break; result = key == k; @@ -496,6 +490,12 @@ bool Print::printMetadatum(const Exiv2::Metadatum& md, const Exiv2::Image* pImag first = false; std::cout << std::setw(30) << std::setfill(' ') << std::left << md.tagLabel(); } + if (Params::instance().printItems_ & Params::prDesc) { + if (!first) + std::cout << " "; + first = false; + std::cout << std::setw(30) << std::setfill(' ') << std::left << md.tagDesc(); + } if (Params::instance().printItems_ & Params::prType) { if (!first) std::cout << " "; @@ -586,7 +586,7 @@ int Print::printPreviewList() { int cnt = 0; Exiv2::PreviewManager pm(*image); Exiv2::PreviewPropertiesList list = pm.getPreviewProperties(); - for (auto&& pos : list) { + for (const auto& pos : list) { if (manyFiles) { std::cout << std::setfill(' ') << std::left << std::setw(20) << path_ << " "; } @@ -735,7 +735,7 @@ int Erase::eraseThumbnail(Exiv2::Image* image) { } int Erase::eraseExifData(Exiv2::Image* image) { - if (Params::instance().verbose_ && image->exifData().count() > 0) { + if (Params::instance().verbose_ && !image->exifData().empty()) { std::cout << _("Erasing Exif data from the file") << std::endl; } image->clearExifData(); @@ -743,7 +743,7 @@ int Erase::eraseExifData(Exiv2::Image* image) { } int Erase::eraseIptcData(Exiv2::Image* image) { - if (Params::instance().verbose_ && image->iptcData().count() > 0) { + if (Params::instance().verbose_ && !image->iptcData().empty()) { std::cout << _("Erasing IPTC data from the file") << std::endl; } image->clearIptcData(); @@ -759,7 +759,7 @@ int Erase::eraseComment(Exiv2::Image* image) { } int Erase::eraseXmpData(Exiv2::Image* image) { - if (Params::instance().verbose_ && image->xmpData().count() > 0) { + if (Params::instance().verbose_ && !image->xmpData().empty()) { std::cout << _("Erasing XMP data from the file") << std::endl; } image->clearXmpData(); // Quick fix for bug #612 @@ -1141,28 +1141,28 @@ int Modify::applyCommands(Exiv2::Image* pImage) { } // loop through command table and apply each command - ModifyCmds& modifyCmds = Params::instance().modifyCmds_; + const ModifyCmds& modifyCmds = Params::instance().modifyCmds_; int rc = 0; int ret = 0; - for (auto&& cmd : modifyCmds) { + for (const auto& cmd : modifyCmds) { switch (cmd.cmdId_) { - case add: + case CmdId::add: ret = addMetadatum(pImage, cmd); if (rc == 0) rc = ret; break; - case set: + case CmdId::set: ret = setMetadatum(pImage, cmd); if (rc == 0) rc = ret; break; - case del: + case CmdId::del: delMetadatum(pImage, cmd); break; - case reg: + case CmdId::reg: regNamespace(cmd); break; - case invalidCmdId: + case CmdId::invalid: break; } } @@ -1182,13 +1182,13 @@ int Modify::addMetadatum(Exiv2::Image* pImage, const ModifyCmd& modifyCmd) { auto value = Exiv2::Value::create(modifyCmd.typeId_); int rc = value->read(modifyCmd.value_); if (0 == rc) { - if (modifyCmd.metadataId_ == exif) { + if (modifyCmd.metadataId_ == MetadataId::exif) { exifData.add(Exiv2::ExifKey(modifyCmd.key_), value.get()); } - if (modifyCmd.metadataId_ == iptc) { + if (modifyCmd.metadataId_ == MetadataId::iptc) { iptcData.add(Exiv2::IptcKey(modifyCmd.key_), value.get()); } - if (modifyCmd.metadataId_ == xmp) { + if (modifyCmd.metadataId_ == MetadataId::xmp) { xmpData.add(Exiv2::XmpKey(modifyCmd.key_), value.get()); } } else { @@ -1211,19 +1211,19 @@ int Modify::setMetadatum(Exiv2::Image* pImage, const ModifyCmd& modifyCmd) { Exiv2::IptcData& iptcData = pImage->iptcData(); Exiv2::XmpData& xmpData = pImage->xmpData(); Exiv2::Metadatum* metadatum = nullptr; - if (modifyCmd.metadataId_ == exif) { + if (modifyCmd.metadataId_ == MetadataId::exif) { auto pos = exifData.findKey(Exiv2::ExifKey(modifyCmd.key_)); if (pos != exifData.end()) { metadatum = &(*pos); } } - if (modifyCmd.metadataId_ == iptc) { + if (modifyCmd.metadataId_ == MetadataId::iptc) { auto pos = iptcData.findKey(Exiv2::IptcKey(modifyCmd.key_)); if (pos != iptcData.end()) { metadatum = &(*pos); } } - if (modifyCmd.metadataId_ == xmp) { + if (modifyCmd.metadataId_ == MetadataId::xmp) { auto pos = xmpData.findKey(Exiv2::XmpKey(modifyCmd.key_)); if (pos != xmpData.end()) { metadatum = &(*pos); @@ -1244,13 +1244,13 @@ int Modify::setMetadatum(Exiv2::Image* pImage, const ModifyCmd& modifyCmd) { if (metadatum) { metadatum->setValue(value.get()); } else { - if (modifyCmd.metadataId_ == exif) { + if (modifyCmd.metadataId_ == MetadataId::exif) { exifData.add(Exiv2::ExifKey(modifyCmd.key_), value.get()); } - if (modifyCmd.metadataId_ == iptc) { + if (modifyCmd.metadataId_ == MetadataId::iptc) { iptcData.add(Exiv2::IptcKey(modifyCmd.key_), value.get()); } - if (modifyCmd.metadataId_ == xmp) { + if (modifyCmd.metadataId_ == MetadataId::xmp) { xmpData.add(Exiv2::XmpKey(modifyCmd.key_), value.get()); } } @@ -1271,21 +1271,21 @@ void Modify::delMetadatum(Exiv2::Image* pImage, const ModifyCmd& modifyCmd) { Exiv2::ExifData& exifData = pImage->exifData(); Exiv2::IptcData& iptcData = pImage->iptcData(); Exiv2::XmpData& xmpData = pImage->xmpData(); - if (modifyCmd.metadataId_ == exif) { + if (modifyCmd.metadataId_ == MetadataId::exif) { Exiv2::ExifData::iterator pos; const Exiv2::ExifKey exifKey(modifyCmd.key_); while ((pos = exifData.findKey(exifKey)) != exifData.end()) { exifData.erase(pos); } } - if (modifyCmd.metadataId_ == iptc) { + if (modifyCmd.metadataId_ == MetadataId::iptc) { Exiv2::IptcData::iterator pos; const Exiv2::IptcKey iptcKey(modifyCmd.key_); while ((pos = iptcData.findKey(iptcKey)) != iptcData.end()) { iptcData.erase(pos); } } - if (modifyCmd.metadataId_ == xmp) { + if (modifyCmd.metadataId_ == MetadataId::xmp) { Exiv2::XmpData::iterator pos; const Exiv2::XmpKey xmpKey(modifyCmd.key_); if ((pos = xmpData.findKey(xmpKey)) != xmpData.end()) { @@ -1407,19 +1407,47 @@ int Adjust::adjustDateTime(Exiv2::ExifData& exifData, const std::string& key, co std::cerr << path << ": " << _("Failed to parse timestamp") << " `" << timeStr << "'\n"; return 1; } - const long monOverflow = (tm.tm_mon + monthAdjustment_) / 12; - tm.tm_mon = (tm.tm_mon + monthAdjustment_) % 12; - tm.tm_year += yearAdjustment_ + monOverflow; + + // bounds checking for yearAdjustment_ + enforce(yearAdjustment_ >= std::numeric_limits::min(), + "year adjustment too low"); + enforce(yearAdjustment_ <= std::numeric_limits::max(), + "year adjustment too high"); + const auto yearAdjustment = static_cast(yearAdjustment_); + + // bounds checking for monthAdjustment_ + enforce(monthAdjustment_ >= std::numeric_limits::min(), + "month adjustment too low"); + enforce(monthAdjustment_ <= std::numeric_limits::max(), + "month adjustment too high"); + const auto monthAdjustment = static_cast(monthAdjustment_); + + // bounds checking for dayAdjustment_ + static constexpr time_t secondsInDay = 24 * 60 * 60; + enforce(dayAdjustment_ >= std::numeric_limits::min() / secondsInDay, + "day adjustment too low"); + enforce(dayAdjustment_ <= std::numeric_limits::max() / secondsInDay, + "day adjustment too high"); + const auto dayAdjustment = static_cast(dayAdjustment_); + + // bounds checking for adjustment_ + enforce(adjustment_ >= std::numeric_limits::min(), "seconds adjustment too low"); + enforce(adjustment_ <= std::numeric_limits::max(), "seconds adjustment too high"); + const auto adjustment = static_cast(adjustment_); + + const auto monOverflow = Safe::add(tm.tm_mon, monthAdjustment) / 12; + tm.tm_mon = Safe::add(tm.tm_mon, monthAdjustment) % 12; + tm.tm_year = Safe::add(tm.tm_year, Safe::add(yearAdjustment, monOverflow)); // Let's not create files with non-4-digit years, we can't read them. if (tm.tm_year > 9999 - 1900 || tm.tm_year < 1000 - 1900) { if (Params::instance().verbose_) std::cout << std::endl; - std::cerr << path << ": " << _("Can't adjust timestamp by") << " " << yearAdjustment_ + monOverflow << " " + std::cerr << path << ": " << _("Can't adjust timestamp by") << " " << yearAdjustment + monOverflow << " " << _("years") << "\n"; return 1; } time_t time = mktime(&tm); - time += adjustment_ + dayAdjustment_ * 86400; + time = Safe::add(time, Safe::add(adjustment, dayAdjustment * secondsInDay)); timeStr = time2Str(time); if (Params::instance().verbose_) { std::cout << " " << _("to") << " " << timeStr << std::endl; @@ -1587,25 +1615,31 @@ int str2Tm(const std::string& timeStr, struct tm* tm) { std::memset(tm, 0x0, sizeof(struct tm)); tm->tm_isdst = -1; - long tmp = 0; + int64_t tmp = 0; if (!Util::strtol(timeStr.substr(0, 4).c_str(), tmp)) return 5; - tm->tm_year = tmp - 1900; + // tmp is a 4-digit number so this cast cannot overflow + tm->tm_year = static_casttm_year)>(tmp - 1900); if (!Util::strtol(timeStr.substr(5, 2).c_str(), tmp)) return 6; - tm->tm_mon = tmp - 1; + // tmp is a 2-digit number so this cast cannot overflow + tm->tm_mon = static_casttm_mon)>(tmp - 1); if (!Util::strtol(timeStr.substr(8, 2).c_str(), tmp)) return 7; - tm->tm_mday = tmp; + // tmp is a 2-digit number so this cast cannot overflow + tm->tm_mday = static_casttm_mday)>(tmp); if (!Util::strtol(timeStr.substr(11, 2).c_str(), tmp)) return 8; - tm->tm_hour = tmp; + // tmp is a 2-digit number so this cast cannot overflow + tm->tm_hour = static_casttm_hour)>(tmp); if (!Util::strtol(timeStr.substr(14, 2).c_str(), tmp)) return 9; - tm->tm_min = tmp; + // tmp is a 2-digit number so this cast cannot overflow + tm->tm_min = static_casttm_min)>(tmp); if (!Util::strtol(timeStr.substr(17, 2).c_str(), tmp)) return 10; - tm->tm_sec = tmp; + // tmp is a 2-digit number so this cast cannot overflow + tm->tm_sec = static_casttm_sec)>(tmp); // Conversions to set remaining fields of the tm structure if (mktime(tm) == static_cast(-1)) @@ -1615,7 +1649,7 @@ int str2Tm(const std::string& timeStr, struct tm* tm) { } // str2Tm std::string time2Str(time_t time) { - struct tm* tm = localtime(&time); + auto tm = localtime(&time); return tm2Str(tm); } // time2Str @@ -1633,7 +1667,7 @@ std::string tm2Str(const struct tm* tm) { std::string temporaryPath() { static int count = 0; - std::lock_guard guard(cs); + auto guard = std::scoped_lock(cs); #if defined(_MSC_VER) || defined(__MINGW__) HANDLE process = 0; @@ -1698,7 +1732,7 @@ int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType std::cout << _("Writing Exif data from") << " " << source << " " << _("to") << " " << target << std::endl; } if (preserve) { - for (auto&& exif : sourceImage->exifData()) { + for (const auto& exif : sourceImage->exifData()) { targetImage->exifData()[exif.key()] = exif.value(); } } else { @@ -1710,7 +1744,7 @@ int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType std::cout << _("Writing IPTC data from") << " " << source << " " << _("to") << " " << target << std::endl; } if (preserve) { - for (auto&& iptc : sourceImage->iptcData()) { + for (const auto& iptc : sourceImage->iptcData()) { targetImage->iptcData()[iptc.key()] = iptc.value(); } } else { @@ -1723,7 +1757,7 @@ int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType } // #1148 use Raw XMP packet if there are no XMP modification commands - int tRawSidecar = Params::ctXmpSidecar | Params::ctXmpRaw; // option -eXX + Params::CommonTarget tRawSidecar = Params::ctXmpSidecar | Params::ctXmpRaw; // option -eXX if (Params::instance().modifyCmds_.empty() && (Params::instance().target_ & tRawSidecar) == tRawSidecar) { // std::cout << "short cut" << std::endl; // http://www.cplusplus.com/doc/tutorial/files/ @@ -1733,7 +1767,7 @@ int metacopy(const std::string& source, const std::string& tgt, Exiv2::ImageType os.close(); rc = 0; } else if (preserve) { - for (auto&& xmp : sourceImage->xmpData()) { + for (const auto& xmp : sourceImage->xmpData()) { targetImage->xmpData()[xmp.key()] = xmp.value(); } } else { diff --git a/app/actions.hpp b/app/actions.hpp index 62a1c557..1a701157 100644 --- a/app/actions.hpp +++ b/app/actions.hpp @@ -97,8 +97,10 @@ class TaskFactory { */ static TaskFactory& instance(); + ~TaskFactory() = default; //! Prevent copy construction: not implemented. TaskFactory(const TaskFactory&) = delete; + TaskFactory& operator=(const TaskFactory&) = delete; //! Destructor void cleanup(); @@ -187,10 +189,10 @@ class Adjust : public Task { private: int adjustDateTime(Exiv2::ExifData& exifData, const std::string& key, const std::string& path) const; - long adjustment_{0}; - long yearAdjustment_{0}; - long monthAdjustment_{0}; - long dayAdjustment_{0}; + int64_t adjustment_{0}; + int64_t yearAdjustment_{0}; + int64_t monthAdjustment_{0}; + int64_t dayAdjustment_{0}; }; // class Adjust diff --git a/app/app_utils.cpp b/app/app_utils.cpp index cc28ece2..d36694b2 100644 --- a/app/app_utils.cpp +++ b/app/app_utils.cpp @@ -1,19 +1,26 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include "app_utils.hpp" #include #include +#include namespace Util { -bool strtol(const char* nptr, long& n) { +bool strtol(const char* nptr, int64_t& n) { if (!nptr || *nptr == '\0') return false; char* endptr = nullptr; - long tmp = std::strtol(nptr, &endptr, 10); + long long tmp = std::strtoll(nptr, &endptr, 10); if (*endptr != '\0') return false; - if (tmp == LONG_MAX || tmp == LONG_MIN) + // strtoll returns LLONG_MAX or LLONG_MIN if an overflow occurs. + if (tmp == LLONG_MAX || tmp == LLONG_MIN) return false; - n = tmp; + if (tmp < std::numeric_limits::min()) + return false; + if (tmp > std::numeric_limits::max()) + return false; + n = static_cast(tmp); return true; } diff --git a/app/app_utils.hpp b/app/app_utils.hpp index 90a6a792..731a71af 100644 --- a/app/app_utils.hpp +++ b/app/app_utils.hpp @@ -3,13 +3,15 @@ #ifndef APP_UTILS_HPP_ #define APP_UTILS_HPP_ +#include + namespace Util { /*! - @brief Convert a C string to a long value, which is returned in n. + @brief Convert a C string to an int64_t value, which is returned in n. Returns true if the conversion is successful, else false. n is not modified if the conversion is unsuccessful. See strtol(2). */ -bool strtol(const char* nptr, long& n); +bool strtol(const char* nptr, int64_t& n); } // namespace Util #endif // #ifndef UTILS_HPP_ diff --git a/app/exiv2.cpp b/app/exiv2.cpp index d30cfcb5..063c5332 100644 --- a/app/exiv2.cpp +++ b/app/exiv2.cpp @@ -34,15 +34,19 @@ // ***************************************************************************** // local declarations namespace { -const Params::YodAdjust emptyYodAdjust_[] = { - {false, "-Y", 0}, - {false, "-O", 0}, - {false, "-D", 0}, +constexpr auto emptyYodAdjust_ = std::array{ + Params::YodAdjust{false, "-Y", 0}, + Params::YodAdjust{false, "-O", 0}, + Params::YodAdjust{false, "-D", 0}, }; //! List of all command identifiers and corresponding strings -const CmdIdAndString cmdIdAndString[] = { - {add, "add"}, {set, "set"}, {del, "del"}, {reg, "reg"}, {invalidCmdId, "invalidCmd"}, // End of list marker +constexpr auto cmdIdAndString = std::array{ + CmdIdAndString{CmdId::add, "add"}, + CmdIdAndString{CmdId::set, "set"}, + CmdIdAndString{CmdId::del, "del"}, + CmdIdAndString{CmdId::reg, "reg"}, + CmdIdAndString{CmdId::invalid, "invalidCmd"}, // End of list marker }; // Return a command Id for a command string @@ -50,7 +54,7 @@ CmdId commandId(const std::string& cmdString); // Evaluate [-]HH[:MM[:SS]], returns true and sets time to the value // in seconds if successful, else returns false. -bool parseTime(const std::string& ts, long& time); +bool parseTime(const std::string& ts, int64_t& time); /*! @brief Parse the oparg string into a bitmap of common targets. @@ -58,7 +62,7 @@ bool parseTime(const std::string& ts, long& time); @param action Action being processed @return A bitmap of common targets or -1 in case of a parse error */ -int parseCommonTargets(const std::string& optArg, const std::string& action); +int64_t parseCommonTargets(const std::string& optArg, const std::string& action); /*! @brief Parse numbers separated by commas into container @@ -137,7 +141,7 @@ int main(int argc, char* const argv[]) { try { // Create the required action class - auto task = Action::TaskFactory::instance().create(Action::TaskType(params.action_)); + auto task = Action::TaskFactory::instance().create(static_cast(params.action_)); // Process all files auto filesCount = params.files_.size(); @@ -147,7 +151,7 @@ int main(int argc, char* const argv[]) { } else { int w = filesCount > 9 ? filesCount > 99 ? 3 : 2 : 1; int n = 1; - for (auto&& file : params.files_) { + for (const auto& file : params.files_) { // If extracting to stdout then ignore verbose if (params.verbose_ && !(params.action_ & Action::extract && params.target_ & Params::ctStdInOut)) { std::cout << _("File") << " " << std::setw(w) << std::right << n++ << "/" << filesCount << ": " << file @@ -279,14 +283,16 @@ void Params::help(std::ostream& os) const { << _(" X : Extract \"raw\" XMP\n") << _(" -P flgs Print flags for fine control of tag lists ('print' action):\n") << _(" E : Exif tags\n") << _(" I : IPTC tags\n") << _(" X : XMP tags\n") - << _(" x : Tag number (Exif and IPTC only)\n") + << _(" x : Tag number for Exif or IPTC tags (in hexadecimal)\n") << _(" g : Group name (e.g. Exif.Photo.UserComment, Photo)\n") << _(" k : Key (e.g. Exif.Photo.UserComment)\n") << _(" l : Tag label (e.g. Exif.Photo.UserComment, 'User comment')\n") + << _(" d : Tag description\n") << _(" n : Tag name (e.g. Exif.Photo.UserComment, UserComment)\n") << _(" y : Type\n") - << _(" c : Number of components (count)\n") - << _(" s : Size in bytes (Ascii and Comment types include NULL)\n") - << _(" v : Plain data value, untranslated (vanilla)\n") + << _(" y : Type\n") << _(" c : Number of components (count)\n") + << _(" s : Size in bytes of vanilla value (may include NULL)\n") + << _(" v : Plain data value of untranslated (vanilla)\n") + << _(" V : Plain data value, data type and the word 'set'\n") << _(" t : Interpreted (translated) human readable values\n") << _(" h : Hex dump of the data\n") << _(" -d tgt1 Delete target(s) for the 'delete' action. Possible targets are:\n") @@ -672,13 +678,13 @@ int Params::evalPrintFlags(const std::string& optArg) { for (auto&& i : optArg) { switch (i) { case 'E': - printTags_ |= Exiv2::mdExif; + printTags_ |= MetadataId::exif; break; case 'I': - printTags_ |= Exiv2::mdIptc; + printTags_ |= MetadataId::iptc; break; case 'X': - printTags_ |= Exiv2::mdXmp; + printTags_ |= MetadataId::xmp; break; case 'x': printItems_ |= prTag; @@ -716,6 +722,9 @@ int Params::evalPrintFlags(const std::string& optArg) { case 'V': printItems_ |= prSet | prKey | prType | prValue; break; + case 'd': + printItems_ |= prDesc; + break; default: std::cerr << progname() << ": " << _("Unrecognized print item") << " `" << i << "'\n"; rc = 1; @@ -735,81 +744,68 @@ int Params::evalPrintFlags(const std::string& optArg) { } // Params::evalPrintFlags int Params::evalDelete(const std::string& optArg) { - int rc = 0; switch (action_) { case Action::none: action_ = Action::erase; - target_ = 0; + target_ = static_cast(0); // fallthrough - case Action::erase: - rc = parseCommonTargets(optArg, "erase"); + case Action::erase: { + const auto rc = parseCommonTargets(optArg, "erase"); if (rc > 0) { - target_ |= rc; - rc = 0; - } else { - rc = 1; + target_ |= static_cast(rc); + return 0; } - break; + return 1; + } default: std::cerr << progname() << ": " << _("Option -d is not compatible with a previous option\n"); - rc = 1; - break; + return 1; } - return rc; } // Params::evalDelete int Params::evalExtract(const std::string& optArg) { - int rc = 0; switch (action_) { case Action::none: case Action::modify: action_ = Action::extract; - target_ = 0; + target_ = static_cast(0); // fallthrough - case Action::extract: - rc = parseCommonTargets(optArg, "extract"); + case Action::extract: { + const auto rc = parseCommonTargets(optArg, "extract"); if (rc > 0) { - target_ |= rc; - rc = 0; - } else { - rc = 1; + target_ |= static_cast(rc); + return 0; } - break; + return 1; + } default: std::cerr << progname() << ": " << _("Option -e is not compatible with a previous option\n"); - rc = 1; - break; + return 1; } - return rc; } // Params::evalExtract int Params::evalInsert(const std::string& optArg) { - int rc = 0; switch (action_) { case Action::none: case Action::modify: action_ = Action::insert; - target_ = 0; + target_ = static_cast(0); // fallthrough - case Action::insert: - rc = parseCommonTargets(optArg, "insert"); + case Action::insert: { + const auto rc = parseCommonTargets(optArg, "insert"); if (rc > 0) { - target_ |= rc; - rc = 0; - } else { - rc = 1; + target_ |= static_cast(rc); + return 0; } - break; + return 1; + } default: std::cerr << progname() << ": " << _("Option -i is not compatible with a previous option\n"); - rc = 1; - break; + return 1; } - return rc; } // Params::evalInsert int Params::evalModify(int opt, const std::string& optArg) { - int rc = 0; switch (action_) { case Action::none: action_ = Action::modify; @@ -823,14 +819,12 @@ int Params::evalModify(int opt, const std::string& optArg) { cmdFiles_.push_back(optArg); // parse the files later if (opt == 'M') cmdLines_.push_back(optArg); // parse the commands later - break; + return 0; default: std::cerr << progname() << ": " << _("Option") << " -" << static_cast(opt) << " " << _("is not compatible with a previous option\n"); - rc = 1; - break; + return 1; } - return rc; } // Params::evalModify int Params::nonoption(const std::string& argv) { @@ -1090,11 +1084,11 @@ cleanup: // ***************************************************************************** // local implementations namespace { -bool parseTime(const std::string& ts, long& time) { +bool parseTime(const std::string& ts, int64_t& time) { std::string hstr, mstr, sstr; auto cts = new char[ts.length() + 1]; strcpy(cts, ts.c_str()); - char* tmp = ::strtok(cts, ":"); + auto tmp = ::strtok(cts, ":"); if (tmp) hstr = tmp; tmp = ::strtok(nullptr, ":"); @@ -1106,7 +1100,7 @@ bool parseTime(const std::string& ts, long& time) { delete[] cts; int sign = 1; - long hh(0), mm(0), ss(0); + int64_t hh(0), mm(0), ss(0); // [-]HH part if (!Util::strtol(hstr.c_str(), hh)) return false; @@ -1145,11 +1139,11 @@ void printUnrecognizedArgument(const char argc, const std::string& action) { << argc << "'\n"; } -int parseCommonTargets(const std::string& optArg, const std::string& action) { - int rc = 0; - int target = 0; - int all = Params::ctExif | Params::ctIptc | Params::ctComment | Params::ctXmp; - int extra = Params::ctXmpSidecar | Params::ctExif | Params::ctIptc | Params::ctXmp; +int64_t parseCommonTargets(const std::string& optArg, const std::string& action) { + int64_t rc = 0; + auto target = static_cast(0); + Params::CommonTarget all = Params::ctExif | Params::ctIptc | Params::ctComment | Params::ctXmp; + Params::CommonTarget extra = Params::ctXmpSidecar | Params::ctExif | Params::ctIptc | Params::ctXmp; for (size_t i = 0; rc == 0 && i < optArg.size(); ++i) { switch (optArg[i]) { case 'e': @@ -1183,7 +1177,7 @@ int parseCommonTargets(const std::string& optArg, const std::string& action) { target |= extra; // -eX if (i > 0) { // -eXX or -iXX target |= Params::ctXmpRaw; - target &= ~extra; // turn off those bits + target = static_cast(target & ~extra); // turn off those bits } break; @@ -1204,7 +1198,7 @@ int parseCommonTargets(const std::string& optArg, const std::string& action) { break; } } - return rc ? rc : target; + return rc ? rc : static_cast(target); } int parsePreviewNumbers(Params::PreviewNumbers& previewNumbers, const std::string& optArg, int j) { @@ -1334,38 +1328,38 @@ bool parseLine(ModifyCmd& modifyCmd, const std::string& line, int num) { std::string cmd(line.substr(cmdStart, cmdEnd - cmdStart)); CmdId cmdId = commandId(cmd); - if (cmdId == invalidCmdId) { + if (cmdId == CmdId::invalid) { throw Exiv2::Error(Exiv2::ErrorCode::kerErrorMessage, Exiv2::toString(num) + ": " + _("Invalid command") + " `" + cmd + "'"); } Exiv2::TypeId defaultType = Exiv2::invalidTypeId; std::string key(line.substr(keyStart, keyEnd - keyStart)); - MetadataId metadataId = invalidMetadataId; - if (cmdId != reg) { + MetadataId metadataId = MetadataId::invalid; + if (cmdId != CmdId::reg) { try { Exiv2::IptcKey iptcKey(key); - metadataId = iptc; + metadataId = MetadataId::iptc; defaultType = Exiv2::IptcDataSets::dataSetType(iptcKey.tag(), iptcKey.record()); } catch (const Exiv2::Error&) { } - if (metadataId == invalidMetadataId) { + if (metadataId == MetadataId::invalid) { try { Exiv2::ExifKey exifKey(key); - metadataId = exif; + metadataId = MetadataId::exif; defaultType = exifKey.defaultTypeId(); } catch (const Exiv2::Error&) { } } - if (metadataId == invalidMetadataId) { + if (metadataId == MetadataId::invalid) { try { Exiv2::XmpKey xmpKey(key); - metadataId = xmp; + metadataId = MetadataId::xmp; defaultType = Exiv2::XmpProperties::propertyType(xmpKey); } catch (const Exiv2::Error&) { } } - if (metadataId == invalidMetadataId) { + if (metadataId == MetadataId::invalid) { throw Exiv2::Error(Exiv2::ErrorCode::kerErrorMessage, Exiv2::toString(num) + ": " + _("Invalid key") + " `" + key + "'"); } @@ -1373,7 +1367,7 @@ bool parseLine(ModifyCmd& modifyCmd, const std::string& line, int num) { std::string value; Exiv2::TypeId type = defaultType; bool explicitType = false; - if (cmdId != del) { + if (cmdId != CmdId::del) { // Get type and value std::string::size_type typeStart = std::string::npos; if (keyEnd != std::string::npos) @@ -1386,12 +1380,12 @@ bool parseLine(ModifyCmd& modifyCmd, const std::string& line, int num) { if (valStart != std::string::npos) valEnd = line.find_last_not_of(delim); - if (cmdId == reg && (keyEnd == std::string::npos || valStart == std::string::npos)) { + if (cmdId == CmdId::reg && (keyEnd == std::string::npos || valStart == std::string::npos)) { throw Exiv2::Error(Exiv2::ErrorCode::kerErrorMessage, Exiv2::toString(num) + ": " + _("Invalid command line") + " "); } - if (cmdId != reg && typeStart != std::string::npos && typeEnd != std::string::npos) { + if (cmdId != CmdId::reg && typeStart != std::string::npos && typeEnd != std::string::npos) { std::string typeStr(line.substr(typeStart, typeEnd - typeStart)); Exiv2::TypeId tmpType = Exiv2::TypeInfo::typeId(typeStr); if (tmpType != Exiv2::invalidTypeId) { @@ -1421,7 +1415,7 @@ bool parseLine(ModifyCmd& modifyCmd, const std::string& line, int num) { modifyCmd.explicitType_ = explicitType; modifyCmd.value_ = value; - if (cmdId == reg) { + if (cmdId == CmdId::reg) { if (value.empty()) { throw Exiv2::Error(Exiv2::ErrorCode::kerErrorMessage, Exiv2::toString(num) + ": " + _("Empty value for key") + +" `" + key + "'"); @@ -1436,11 +1430,8 @@ bool parseLine(ModifyCmd& modifyCmd, const std::string& line, int num) { } // parseLine CmdId commandId(const std::string& cmdString) { - int i = 0; - while (cmdIdAndString[i].first != invalidCmdId && cmdIdAndString[i].second != cmdString) { - ++i; - } - return cmdIdAndString[i].first; + auto it = std::find_if(cmdIdAndString.begin(), cmdIdAndString.end(), [&](auto cs) { return cs.second == cmdString; }); + return it != cmdIdAndString.end() ? it->first : CmdId::invalid; } std::string parseEscapes(const std::string& input) { @@ -1452,7 +1443,7 @@ std::string parseEscapes(const std::string& input) { continue; } size_t escapeStart = i; - if (!(input.length() - 1 > i)) { + if (input.length() - 1 <= i) { result.push_back(ch); continue; } @@ -1473,7 +1464,7 @@ std::string parseEscapes(const std::string& input) { break; case 'u': // Escaping of unicode if (input.length() >= 4 && input.length() - 4 > i) { - int acc = 0; + uint32_t acc = 0; for (int j = 0; j < 4; ++j) { ++i; acc <<= 4; @@ -1484,19 +1475,19 @@ std::string parseEscapes(const std::string& input) { } else if (input[i] >= 'A' && input[i] <= 'F') { acc |= input[i] - 'A' + 10; } else { - acc = -1; + acc = 0xFFFFFFFF; break; } } - if (acc == -1) { + if (acc == 0xFFFFFFFF) { result.push_back('\\'); i = escapeStart; break; } std::string ucs2toUtf8; - ucs2toUtf8.push_back(static_cast((acc & 0xff00) >> 8)); - ucs2toUtf8.push_back(static_cast(acc & 0x00ff)); + ucs2toUtf8.push_back(static_cast((acc & 0xff00U) >> 8)); + ucs2toUtf8.push_back(static_cast(acc & 0x00ffU)); if (Exiv2::convertStringCharset(ucs2toUtf8, "UCS-2BE", "UTF-8")) { result.append(ucs2toUtf8); diff --git a/app/exiv2app.hpp b/app/exiv2app.hpp index c12fce61..8107774d 100644 --- a/app/exiv2app.hpp +++ b/app/exiv2app.hpp @@ -21,28 +21,40 @@ #include //! Command identifiers -enum CmdId { - invalidCmdId, +enum class CmdId { + invalid, add, set, del, reg, }; //! Metadata identifiers -enum MetadataId { - invalidMetadataId = Exiv2::mdNone, // 0 - exif = Exiv2::mdExif, // 1 - iptc = Exiv2::mdIptc, // 2 - xmp = Exiv2::mdXmp, // 8 +enum class MetadataId : uint32_t { + invalid = Exiv2::mdNone, // 0 + exif = Exiv2::mdExif, // 1 + iptc = Exiv2::mdIptc, // 2 + xmp = Exiv2::mdXmp, // 8 }; +inline MetadataId operator&(MetadataId x, MetadataId y) { + return static_cast(static_cast(x) & static_cast(y)); +} + +inline MetadataId operator|(MetadataId x, MetadataId y) { + return static_cast(static_cast(x) | static_cast(y)); +} + +inline MetadataId& operator|=(MetadataId& x, MetadataId y) { + return x = x | y; +} + //! Structure for one parsed modification command struct ModifyCmd { //! C'tor ModifyCmd() = default; - CmdId cmdId_{invalidCmdId}; //!< Command identifier + CmdId cmdId_{CmdId::invalid}; //!< Command identifier std::string key_; //!< Exiv2 key string - MetadataId metadataId_{invalidMetadataId}; //!< Metadata identifier + MetadataId metadataId_{MetadataId::invalid}; //!< Metadata identifier Exiv2::TypeId typeId_{Exiv2::invalidTypeId}; //!< Exiv2 type identifier //! Flag to indicate if the type was explicitly specified (true) bool explicitType_{false}; @@ -51,7 +63,7 @@ struct ModifyCmd { //! Container for modification commands using ModifyCmds = std::vector; //! Structure to link command identifiers to strings -using CmdIdAndString = std::pair; +using CmdIdAndString = std::pair; /*! @brief Implements the command line handling for the program. @@ -110,7 +122,9 @@ class Params : public Util::Getopt { static Params& instance(); //! Prevent copy-construction: not implemented. + ~Params() override = default; Params(const Params&) = delete; + Params& operator=(const Params&) = delete; //! Enumerates print modes enum PrintMode { @@ -125,7 +139,7 @@ class Params : public Util::Getopt { }; //! Individual items to print, bitmap - enum PrintItem { + enum PrintItem : uint32_t { prTag = 1, prGroup = 2, prKey = 4, @@ -137,11 +151,12 @@ class Params : public Util::Getopt { prValue = 256, prTrans = 512, prHex = 1024, - prSet = 2048 + prSet = 2048, + prDesc = 4096 }; //! Enumerates common targets, bitmap - enum CommonTarget { + enum CommonTarget : uint32_t { ctExif = 1, ctIptc = 2, ctComment = 4, @@ -173,7 +188,7 @@ class Params : public Util::Getopt { struct YodAdjust { bool flag_; //!< Adjustment flag. const char* option_; //!< Adjustment option string. - long adjustment_; //!< Adjustment value. + int64_t adjustment_; //!< Adjustment value. }; bool help_{false}; //!< Help option flag. @@ -188,27 +203,27 @@ class Params : public Util::Getopt { FileExistsPolicy fileExistsPolicy_{askPolicy}; //!< What to do if file to rename exists. bool adjust_{false}; //!< Adjustment flag. PrintMode printMode_{pmSummary}; //!< Print mode. - unsigned long printItems_{0}; //!< Print items. - unsigned long printTags_{Exiv2::mdNone}; //!< Print tags (bitmap of MetadataId flags). + PrintItem printItems_{0}; //!< Print items. + MetadataId printTags_{Exiv2::mdNone}; //!< Print tags (bitmap of MetadataId flags). //! %Action (integer rather than TaskType to avoid dependency). int action_{0}; - int target_; //!< What common target to process. - - long adjustment_{0}; //!< Adjustment in seconds. - YodAdjust yodAdjust_[3]; //!< Year, month and day adjustment info. - std::string format_; //!< Filename format (-r option arg). - bool formatSet_{false}; //!< Whether the format is set with -r - CmdFiles cmdFiles_; //!< Names of the modification command files - CmdLines cmdLines_; //!< Commands from the command line - ModifyCmds modifyCmds_; //!< Parsed modification commands - std::string jpegComment_; //!< Jpeg comment to set in the image - std::string directory_; //!< Location for files to extract/insert - std::string suffix_; //!< File extension of the file to insert - Files files_; //!< List of non-option arguments. - PreviewNumbers previewNumbers_; //!< List of preview numbers - std::vector greps_; //!< List of keys to 'grep' from the metadata - Keys keys_; //!< List of keys to match from the metadata - std::string charset_; //!< Charset to use for UNICODE Exif user comment + CommonTarget target_; //!< What common target to process. + + int64_t adjustment_{0}; //!< Adjustment in seconds. + std::array yodAdjust_; //!< Year, month and day adjustment info. + std::string format_; //!< Filename format (-r option arg). + bool formatSet_{false}; //!< Whether the format is set with -r + CmdFiles cmdFiles_; //!< Names of the modification command files + CmdLines cmdLines_; //!< Commands from the command line + ModifyCmds modifyCmds_; //!< Parsed modification commands + std::string jpegComment_; //!< Jpeg comment to set in the image + std::string directory_; //!< Location for files to extract/insert + std::string suffix_; //!< File extension of the file to insert + Files files_; //!< List of non-option arguments. + PreviewNumbers previewNumbers_; //!< List of preview numbers + std::vector greps_; //!< List of keys to 'grep' from the metadata + Keys keys_; //!< List of keys to match from the metadata + std::string charset_; //!< Charset to use for UNICODE Exif user comment Exiv2::DataBuf stdinBuf; //!< DataBuf with the binary bytes from stdin @@ -270,4 +285,20 @@ class Params : public Util::Getopt { }; // class Params +inline Params::CommonTarget operator|(Params::CommonTarget x, Params::CommonTarget y) { + return static_cast(static_cast(x) | static_cast(y)); +} + +inline Params::CommonTarget& operator|=(Params::CommonTarget& x, Params::CommonTarget y) { + return x = x | y; +} + +inline Params::PrintItem operator|(Params::PrintItem x, Params::PrintItem y) { + return static_cast(static_cast(x) | static_cast(y)); +} + +inline Params::PrintItem& operator|=(Params::PrintItem& x, Params::PrintItem y) { + return x = x | y; +} + #endif // #ifndef EXIV2APP_HPP_ diff --git a/cmake/config.h.cmake b/cmake/config.h.cmake index 0bed81c5..c42d9ed8 100644 --- a/cmake/config.h.cmake +++ b/cmake/config.h.cmake @@ -62,10 +62,10 @@ /* Define to the version of this package. */ #cmakedefine EXV_PACKAGE_VERSION "@PROJECT_VERSION@" -#define EXIV2_MAJOR_VERSION (@PROJECT_VERSION_MAJOR@) -#define EXIV2_MINOR_VERSION (@PROJECT_VERSION_MINOR@) -#define EXIV2_PATCH_VERSION (@PROJECT_VERSION_PATCH@) -#define EXIV2_TWEAK_VERSION (@PROJECT_VERSION_TWEAK@) +#define EXIV2_MAJOR_VERSION (@PROJECT_VERSION_MAJOR@U) +#define EXIV2_MINOR_VERSION (@PROJECT_VERSION_MINOR@U) +#define EXIV2_PATCH_VERSION (@PROJECT_VERSION_PATCH@U) +#define EXIV2_TWEAK_VERSION (@PROJECT_VERSION_TWEAK@U) // Definition to enable translation of Nikon lens names. #cmakedefine EXV_HAVE_LENSDATA diff --git a/cmake/findDependencies.cmake b/cmake/findDependencies.cmake index 8ce82d4b..9315f43c 100644 --- a/cmake/findDependencies.cmake +++ b/cmake/findDependencies.cmake @@ -1,5 +1,24 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") +if (CONAN_AUTO_INSTALL) + # Download automatically the cmake-conan integration file + if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") + message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") + file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/master/conan.cmake" + "${CMAKE_BINARY_DIR}/conan.cmake" + TLS_VERIFY ON) + endif() + + include(${CMAKE_BINARY_DIR}/conan.cmake) + + conan_cmake_autodetect(settings) + conan_cmake_install(PATH_OR_REFERENCE .. + BUILD missing + REMOTE conancenter + OPTIONS webready=True + SETTINGS ${settings}) +endif() + if (APPLE) # On Apple, we use the conan cmake_paths generator if (EXISTS ${CMAKE_BINARY_DIR}/conan_paths.cmake) diff --git a/conanfile.py b/conanfile.py index 6946e3df..b9bed818 100644 --- a/conanfile.py +++ b/conanfile.py @@ -21,10 +21,10 @@ class Exiv2Conan(ConanFile): self.options['gtest'].shared = False def requirements(self): - self.requires('zlib/1.2.11') + self.requires('zlib/1.2.12') if self.options.webready: - self.requires('libcurl/7.79.0') + self.requires('libcurl/7.80.0') if os_info.is_windows and self.options.iconv: self.requires('libiconv/1.16') @@ -37,7 +37,7 @@ class Exiv2Conan(ConanFile): if self.options.xmp: self.requires('XmpSdk/2016.7@piponazo/stable') # from conan-piponazo else: - self.requires('expat/2.4.1') + self.requires('expat/2.4.8') def imports(self): self.copy('*.dll', dst='bin', src='bin') diff --git a/exiv2.md b/exiv2.md index 6049327f..86dd7fd2 100644 --- a/exiv2.md +++ b/exiv2.md @@ -566,34 +566,33 @@ as well as data columns included in the print output. Valid flags are: | g | Group name (e.g., for Exif.Photo.UserComment, outputs Photo) | | k | Key (e.g., Exif.Photo.UserComment) | | l | Tag label (human-readable tagname, e.g., for Exif.Photo.UserComment, outputs 'User comment') | -| n | Tagname (e.g., for Exif.Photo.UserComment, outputs UserComment) | +| d | Tag description | +| n | Tag name (e.g., for Exif.Photo.UserComment, outputs UserComment) | | y | Type (for available types, see [Exif/IPTC/XMP types](#exiv2_types)) | | c | Number of components (for single entry types, the number of **sizeof('type')** in 'size'. For multi-entry types, the number of entries. See [Exif/IPTC/XMP types](#exiv2_types)) | | s | Size in bytes of vanilla output (see note in [Exif 'Comment' values](#exif_comment_values)). Some types include a *NULL* character in the size (see [Exif/IPTC/XMP types](#exiv2_types)) | | v | Plain data value (vanilla values, i.e., untranslated) | -| V | Plain data value, data type and the word 'set ' (see ['MODIFY' COMMANDS](#modify_cmds))| +| V | Plain data value, data type and the word 'set' (see ['MODIFY' COMMANDS](#modify_cmds))| | t | Interpreted (translated) human-readable data values (includes plain vanilla values) | | h | Hex dump of the data | +**--Print** *flgs* can be combined with [--grep str](#grep_str) or +[--key key](#key_key) to further filter the output. +