diff --git a/.clang-format b/.clang-format
new file mode 100644
index 000000000..d6e9cfceb
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,92 @@
+# This file is an example configuration for clang-format 5.0.
+#
+# Note that this style definition should only be understood as a hint
+# for writing new code. The rules are still work-in-progress and does
+# not yet exactly match the style we have in the existing code.
+
+# C Language specifics
+Language: Cpp
+
+# Use tabs whenever we need to fill whitespace that spans at least from one tab
+# stop to the next one.
+#
+# These settings are mirrored in .editorconfig. Keep them in sync.
+UseTab: ForIndentation
+TabWidth: 8
+IndentWidth: 8
+ContinuationIndentWidth: 8
+ColumnLimit: 80
+
+AlignAfterOpenBracket: AlwaysBreak
+AlignEscapedNewlines: Left
+AlignTrailingComments: false
+
+# Allow putting parameters onto the next line
+AllowAllArgumentsOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+
+# Don't allow short braced statements to be on a single line
+# if (a) not if (a) return;
+# return;
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortLoopsOnASingleLine: false
+AllowShortLambdasOnASingleLine: None
+
+# Pack as many parameters or arguments onto the same line as possible
+# int myFunction(int aaaaaaaaaaaa, int bbbbbbbb,
+# int cccc);
+BinPackArguments: true
+BinPackParameters: false
+
+BreakBeforeBraces: Linux
+BreakBeforeBinaryOperators: None
+BreakBeforeTernaryOperators: false
+BreakStringLiterals: false
+
+# The number of spaces before trailing line comments (// - comments).
+# This does not affect trailing block comments (/* - comments).
+SpacesBeforeTrailingComments: 1
+
+# Don't insert spaces in casts
+# x = (int32) y; not x = ( int32 ) y;
+SpacesInCStyleCastParentheses: false
+
+# Don't insert spaces inside container literals
+# var arr = [1, 2, 3]; not var arr = [ 1, 2, 3 ];
+SpacesInContainerLiterals: false
+
+# Don't insert spaces after '(' or before ')'
+# f(arg); not f( arg );
+SpacesInParentheses: false
+
+# Don't insert spaces after '[' or before ']'
+# int a[5]; not int a[ 5 ];
+SpacesInSquareBrackets: false
+
+# Insert a space after '{' and before '}' in struct initializers
+Cpp11BracedListStyle: false
+
+# A list of macros that should be interpreted as foreach loops instead of as
+# function calls.
+ForEachMacros:
+ - 'git_array_foreach'
+ - 'git_vector_foreach'
+
+# The maximum number of consecutive empty lines to keep.
+MaxEmptyLinesToKeep: 1
+
+# No empty line at the start of a block.
+KeepEmptyLinesAtTheStartOfBlocks: false
+
+# Penalties
+# This decides what order things should be done if a line is too long
+PenaltyBreakAssignment: 10
+PenaltyBreakBeforeFirstCallParameter: 30
+PenaltyBreakComment: 10
+PenaltyBreakFirstLessLess: 0
+PenaltyBreakString: 10
+PenaltyExcessCharacter: 100
+PenaltyReturnTypeOnItsOwnLine: 60
+
+SortIncludes: false
diff --git a/.github/release.yml b/.github/release.yml
index c3c8da290..79158f492 100644
--- a/.github/release.yml
+++ b/.github/release.yml
@@ -6,10 +6,13 @@ changelog:
- title: Bug fixes
labels:
- bug
+ - title: Security fixes
+ labels:
+ - security
- title: Code cleanups
labels:
- cleanup
- - title: CI improvements
+ - title: Build and CI improvements
labels:
- build
- title: Documentation improvements
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
new file mode 100644
index 000000000..285c273b1
--- /dev/null
+++ b/.github/workflows/benchmark.yml
@@ -0,0 +1,82 @@
+# Benchmark libgit2 against the git reference implementation.
+name: Benchmark
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '15 4 * * *'
+
+jobs:
+ # Run our nightly builds. We build a matrix with the various build
+ # targets and their details. Then we build either in a docker container
+ # (Linux) or on the actual hosts (macOS, Windows).
+ build:
+ # Only run scheduled workflows on the main repository; prevents people
+ # from using build minutes on their forks.
+ if: github.repository == 'libgit2/libgit2'
+
+ strategy:
+ matrix:
+ platform:
+ - name: "Linux (clang, OpenSSL)"
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_CLI=ON -DCMAKE_BUILD_TYPE=Release
+ CMAKE_BUILD_OPTIONS: --config Release
+ id: linux
+ os: ubuntu-latest
+ setup-script: ubuntu
+ - name: "macOS"
+ os: macos-10.15
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_CLI=ON -DCMAKE_BUILD_TYPE=Release
+ CMAKE_BUILD_OPTIONS: --config Release
+ PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
+ id: macos
+ setup-script: osx
+ - name: "Windows (amd64, Visual Studio)"
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A x64 -DDEPRECATE_HARD=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_CLI=ON -DCMAKE_BUILD_TYPE=Release
+ CMAKE_BUILD_OPTIONS: --config Release
+ id: windows
+ setup-script: win32
+ fail-fast: false
+ name: "Build ${{ matrix.platform.name }}"
+ env: ${{ matrix.platform.env }}
+ runs-on: ${{ matrix.platform.os }}
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v2
+ with:
+ path: source
+ fetch-depth: 0
+ - name: Set up benchmark environment
+ run: source/ci/setup-${{ matrix.platform.setup-script }}-benchmark.sh
+ shell: bash
+ if: matrix.platform.setup-script != ''
+ - name: Build
+ run: |
+ mkdir build && cd build
+ ../source/ci/build.sh
+ shell: bash
+ - name: Benchmark
+ run: |
+ if [[ "$(uname -s)" == MINGW* ]]; then
+ GIT2_CLI="$(cygpath -w $(pwd))\\build\\Release\\git2_cli"
+ else
+ GIT2_CLI="$(pwd)/build/git2_cli"
+ fi
+
+ mkdir benchmark && cd benchmark
+ ../source/tests/benchmarks/benchmark.sh --baseline-cli "git" --cli "${GIT2_CLI}" --json benchmarks.json --zip benchmarks.zip
+ shell: bash
+ - name: Upload results
+ uses: actions/upload-artifact@v2
+ with:
+ name: benchmark-${{ matrix.platform.id }}
+ path: benchmark
+ if: always()
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
deleted file mode 100644
index 38b4a044a..000000000
--- a/.github/workflows/codeql.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-name: "CodeQL"
-
-on:
- workflow_dispatch:
- schedule:
- - cron: '21 3 * * 1'
-
-env:
- docker-registry: docker.pkg.github.com
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
-
- steps:
- - name: Check out repository
- uses: actions/checkout@v2
- with:
- fetch-depth: 0
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v1
- with:
- languages: 'cpp'
-
- - name: Build
- run: |
- mkdir build
- cd build
- cmake .. -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
- cmake --build .
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 74bab53f3..bcad84b8b 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -10,7 +10,7 @@ on:
workflow_dispatch:
env:
- docker-registry: docker.pkg.github.com
+ docker-registry: ghcr.io
docker-config-path: source/ci/docker
jobs:
@@ -69,7 +69,7 @@ jobs:
if [ "${{ matrix.container.base }}" != "" ]; then
BASE_ARG="--build-arg BASE=${{ matrix.container.base }}"
fi
- docker build -t ${{ env.docker-registry-container-sha }} ${BASE_ARG} -f ${{ env.dockerfile }} .
+ docker build -t ${{ env.docker-registry-container-sha }} --build-arg UID=$(id -u) --build-arg GID=$(id -g) ${BASE_ARG} -f ${{ env.dockerfile }} .
docker tag ${{ env.docker-registry-container-sha }} ${{ env.docker-registry-container-latest }}
docker push ${{ env.docker-registry-container-sha }}
docker push ${{ env.docker-registry-container-latest }}
@@ -85,6 +85,7 @@ jobs:
matrix:
platform:
- name: "Linux (Xenial, GCC, OpenSSL)"
+ id: xenial-gcc-openssl
container:
name: xenial
env:
@@ -93,6 +94,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON
os: ubuntu-latest
- name: Linux (Xenial, GCC, mbedTLS)
+ id: xenial-gcc-mbedtls
container:
name: xenial
env:
@@ -101,6 +103,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
os: ubuntu-latest
- name: "Linux (Xenial, Clang, OpenSSL)"
+ id: xenial-clang-openssl
container:
name: xenial
env:
@@ -109,6 +112,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
os: ubuntu-latest
- name: "Linux (Xenial, Clang, mbedTLS)"
+ id: xenial-clang-mbedtls
container:
name: xenial
env:
@@ -117,6 +121,7 @@ jobs:
CMAKE_GENERATOR: Ninja
os: ubuntu-latest
- name: "Linux (MemorySanitizer)"
+ id: memorysanitizer
container:
name: focal
env:
@@ -130,6 +135,7 @@ jobs:
UBSAN_OPTIONS: print_stacktrace=1
os: ubuntu-latest
- name: "Linux (UndefinedBehaviorSanitizer)"
+ id: ubsanitizer
container:
name: focal
env:
@@ -143,6 +149,7 @@ jobs:
UBSAN_OPTIONS: print_stacktrace=1
os: ubuntu-latest
- name: "Linux (ThreadSanitizer)"
+ id: threadsanitizer
container:
name: focal
env:
@@ -157,6 +164,7 @@ jobs:
TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1
os: ubuntu-latest
- name: "macOS"
+ id: macos
os: macos-10.15
env:
CC: clang
@@ -166,6 +174,7 @@ jobs:
SKIP_NEGOTIATE_TESTS: true
setup-script: osx
- name: "Windows (amd64, Visual Studio)"
+ id: windows-amd64-vs
os: windows-2019
env:
ARCH: amd64
@@ -174,6 +183,7 @@ jobs:
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (x86, Visual Studio)"
+ id: windows-x86-vs
os: windows-2019
env:
ARCH: x86
@@ -182,6 +192,7 @@ jobs:
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (amd64, mingw)"
+ id: windows-amd64-mingw
os: windows-2019
setup-script: mingw
env:
@@ -193,6 +204,7 @@ jobs:
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- name: "Windows (x86, mingw)"
+ id: windows-x86-mingw
os: windows-2019
setup-script: mingw
env:
@@ -214,7 +226,7 @@ jobs:
path: source
fetch-depth: 0
- name: Set up build environment
- run: source/ci/setup-${{ matrix.platform.setup-script }}.sh
+ run: source/ci/setup-${{ matrix.platform.setup-script }}-build.sh
shell: bash
if: matrix.platform.setup-script != ''
- name: Setup QEMU
@@ -229,7 +241,11 @@ jobs:
working-directory: ${{ env.docker-config-path }}
if: matrix.platform.container.name != ''
- name: Create container
- run: docker build -t ${{ env.docker-registry-container-sha }} -f ${{ env.dockerfile }} .
+ run: |
+ if [ "${{ matrix.container.base }}" != "" ]; then
+ BASE_ARG="--build-arg BASE=${{ matrix.container.base }}"
+ fi
+ docker build -t ${{ env.docker-registry-container-sha }} --build-arg UID=$(id -u) --build-arg GID=$(id -g) ${BASE_ARG} -f ${{ env.dockerfile }} .
working-directory: ${{ env.docker-config-path }}
if: matrix.platform.container.name != '' && env.docker-container-exists != 'true'
- name: Build and test
@@ -237,10 +253,12 @@ jobs:
export GITTEST_NEGOTIATE_PASSWORD="${{ secrets.GITTEST_NEGOTIATE_PASSWORD }}"
if [ -n "${{ matrix.platform.container.name }}" ]; then
+ mkdir build
docker run \
--rm \
- --user libgit2:libgit2 \
+ --user "$(id -u):$(id -g)" \
-v "$(pwd)/source:/home/libgit2/source" \
+ -v "$(pwd)/build:/home/libgit2/build" \
-w /home/libgit2 \
-e ASAN_SYMBOLIZER_PATH \
-e CC \
@@ -248,19 +266,40 @@ jobs:
-e CMAKE_GENERATOR \
-e CMAKE_OPTIONS \
-e GITTEST_NEGOTIATE_PASSWORD \
+ -e GITTEST_FLAKY_STAT \
-e PKG_CONFIG_PATH \
-e SKIP_NEGOTIATE_TESTS \
-e SKIP_SSH_TESTS \
-e TSAN_OPTIONS \
-e UBSAN_OPTIONS \
${{ env.docker-registry-container-sha }} \
- /bin/bash -c "mkdir build && cd build && ../source/ci/build.sh && ../source/ci/test.sh"
+ /bin/bash -c "cd build && ../source/ci/build.sh && ../source/ci/test.sh"
else
- mkdir build && cd build
+ mkdir build
+ cd build
../source/ci/build.sh
../source/ci/test.sh
fi
shell: bash
+ - name: Upload test results
+ uses: actions/upload-artifact@v3
+ if: success() || failure()
+ with:
+ name: test-results-${{ matrix.platform.id }}
+ path: build/results_*.xml
+
+ test_results:
+ name: Test results
+ needs: [ build ]
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download test results
+ uses: actions/download-artifact@v3
+ - name: Generate test summary
+ uses: test-summary/action@v1
+ with:
+ paths: 'test-results-*/*.xml'
+
# Generate documentation using docurium. We'll upload the documentation
# as a build artifact so that it can be reviewed as part of a pull
@@ -270,6 +309,7 @@ jobs:
documentation:
name: Generate documentation
needs: [ containers ]
+ if: success() || failure()
runs-on: ubuntu-latest
steps:
- name: Check out repository
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 5513d5b43..856da28a3 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -7,7 +7,7 @@ on:
- cron: '15 1 * * *'
env:
- docker-registry: docker.pkg.github.com
+ docker-registry: ghcr.io
docker-config-path: source/ci/docker
jobs:
@@ -15,6 +15,10 @@ jobs:
# targets and their details. Then we build either in a docker container
# (Linux) or on the actual hosts (macOS, Windows).
build:
+ # Only run scheduled workflows on the main repository; prevents people
+ # from using build minutes on their forks.
+ if: github.repository == 'libgit2/libgit2'
+
strategy:
matrix:
platform:
@@ -247,6 +251,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
SKIP_PROXY_TESTS: true
+ GITTEST_FLAKY_STAT: true
os: ubuntu-latest
- name: "Linux (arm64, Bionic, GCC, OpenSSL)"
container:
@@ -271,7 +276,7 @@ jobs:
path: source
fetch-depth: 0
- name: Set up build environment
- run: source/ci/setup-${{ matrix.platform.setup-script }}.sh
+ run: source/ci/setup-${{ matrix.platform.setup-script }}-build.sh
shell: bash
if: matrix.platform.setup-script != ''
- name: Setup QEMU
@@ -305,6 +310,7 @@ jobs:
-e CMAKE_GENERATOR \
-e CMAKE_OPTIONS \
-e GITTEST_NEGOTIATE_PASSWORD \
+ -e GITTEST_FLAKY_STAT \
-e PKG_CONFIG_PATH \
-e SKIP_NEGOTIATE_TESTS \
-e SKIP_SSH_TESTS \
@@ -319,6 +325,10 @@ jobs:
shell: bash
coverity:
+ # Only run scheduled workflows on the main repository; prevents people
+ # from using build minutes on their forks.
+ if: github.repository == 'libgit2/libgit2'
+
name: Coverity
runs-on: ubuntu-latest
steps:
@@ -338,3 +348,32 @@ jobs:
run: source/ci/coverity.sh
env:
COVERITY_TOKEN: ${{ secrets.coverity_token }}
+
+ codeql:
+ # Only run scheduled workflows on the main repository; prevents people
+ # from using build minutes on their forks.
+ if: github.repository == 'libgit2/libgit2'
+
+ name: CodeQL
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ with:
+ languages: 'cpp'
+
+ - name: Build
+ run: |
+ mkdir build
+ cd build
+ cmake .. -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
+ cmake --build .
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9ff6748e0..2b5a2842c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,9 +1,12 @@
-# CMake build script for the libgit2 project
+# libgit2: the cross-platform, linkable library implementation of git.
# See `README.md` for build instructions.
+#
+# This top-level CMakeLists.txt sets up configuration options and
+# determines which subprojects to build.
cmake_minimum_required(VERSION 3.5.1)
-project(libgit2 VERSION "1.4.3" LANGUAGES C)
+project(libgit2 VERSION "1.5.0" LANGUAGES C)
# Add find modules to the path
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
@@ -15,6 +18,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
# Optional subsystems
option(BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON)
option(BUILD_TESTS "Build Tests using the Clar suite" ON)
+option(BUILD_CLI "Build the command-line interface" ON)
option(BUILD_EXAMPLES "Build library usage example apps" OFF)
option(BUILD_FUZZERS "Build the fuzz targets" OFF)
@@ -25,7 +29,8 @@ option(USE_NSEC "Support nanosecond precision file mtimes and cti
# Backend selection
option(USE_SSH "Link with libssh2 to enable SSH support" OFF)
option(USE_HTTPS "Enable HTTPS support. Can be set to a specific backend" ON)
-option(USE_SHA1 "Enable SHA1. Can be set to CollisionDetection(ON)/HTTPS/Generic" ON)
+option(USE_SHA1 "Enable SHA1. Can be set to CollisionDetection(ON)/HTTPS" ON)
+option(USE_SHA256 "Enable SHA256. Can be set to HTTPS/Builtin" ON)
option(USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF)
set(USE_HTTP_PARSER "" CACHE STRING "Specifies the HTTP Parser implementation; either system or builtin.")
set(REGEX_BACKEND "" CACHE STRING "Regular expression implementation. One of regcomp_l, pcre2, pcre, regcomp, or builtin.")
@@ -127,6 +132,14 @@ if(BUILD_FUZZERS)
endif()
+# Export for people who use us as a dependency
+
+if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
+ set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE)
+ set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE)
+endif()
+
+
# Summary
feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:")
diff --git a/COPYING b/COPYING
index ccfb7dbf8..28226696d 100644
--- a/COPYING
+++ b/COPYING
@@ -1144,3 +1144,43 @@ worldwide. This software is distributed without any warranty.
See .
+----------------------------------------------------------------------
+
+The built-in SHA256 support (src/hash/rfc6234) is taken from RFC 6234
+under the following license:
+
+Copyright (c) 2011 IETF Trust and the persons identified as
+authors of the code. All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+- Redistributions of source code must retain the above
+ copyright notice, this list of conditions and
+ the following disclaimer.
+
+- Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+- Neither the name of Internet Society, IETF or IETF Trust, nor
+ the names of specific contributors, may be used to endorse or
+ promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/README.md b/README.md
index c73b50701..a9deaa01d 100644
--- a/README.md
+++ b/README.md
@@ -392,6 +392,8 @@ Here are the bindings to libgit2 that are currently available:
* parrot-libgit2
* Perl
* Git-Raw
+* Pharo Smalltalk
+ * libgit2-pharo-bindings
* PHP
* php-git
* Python
@@ -405,6 +407,8 @@ Here are the bindings to libgit2 that are currently available:
* git2-rs
* Swift
* SwiftGit2
+* Tcl
+ * lg2
* Vala
* libgit2.vapi
diff --git a/ci/build.sh b/ci/build.sh
index 5a51f925a..21a45af5f 100755
--- a/ci/build.sh
+++ b/ci/build.sh
@@ -59,7 +59,7 @@ echo "##########################################################################
echo "## Configuring build environment"
echo "##############################################################################"
-echo cmake -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G \"${CMAKE_GENERATOR}\" ${CMAKE_OPTIONS} -S \"${SOURCE_DIR}\"
+echo "${CMAKE}" -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G \"${CMAKE_GENERATOR}\" ${CMAKE_OPTIONS} -S \"${SOURCE_DIR}\"
env PATH="${BUILD_PATH}" "${CMAKE}" -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G "${CMAKE_GENERATOR}" ${CMAKE_OPTIONS} -S "${SOURCE_DIR}"
echo ""
@@ -69,10 +69,11 @@ echo "##########################################################################
# Determine parallelism; newer cmake supports `--build --parallel` but
# we cannot yet rely on that.
-if [ "${CMAKE_GENERATOR}" = "Unix Makefiles" -a "${CORES}" != "" ]; then
+if [ "${CMAKE_GENERATOR}" = "Unix Makefiles" -a "${CORES}" != "" -a "${CMAKE_BUILD_OPTIONS}" = "" ]; then
BUILDER=(make -j ${CORES})
else
- BUILDER=("${CMAKE}" --build .)
+ BUILDER=("${CMAKE}" --build . ${CMAKE_BUILD_OPTIONS})
fi
+echo "${BUILDER[@]}"
env PATH="${BUILD_PATH}" "${BUILDER[@]}"
diff --git a/ci/docker/bionic b/ci/docker/bionic
index 51af5c01c..f1b69edef 100644
--- a/ci/docker/bionic
+++ b/ci/docker/bionic
@@ -28,17 +28,22 @@ RUN apt-get update && \
FROM apt AS mbedtls
RUN cd /tmp && \
- curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \
- tar -xz && \
- cd mbedtls-2.16.2 && \
+ curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \
+ tar -xz && \
+ cd mbedtls-mbedtls-2.16.2 && \
scripts/config.pl set MBEDTLS_MD4_C 1 && \
CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \
ninja install && \
cd .. && \
- rm -rf mbedtls-2.16.2
+ rm -rf mbedtls-mbedtls-2.16.2
FROM mbedtls AS adduser
-RUN useradd --shell /bin/bash libgit2 --create-home
+ARG UID=""
+ARG GID=""
+RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
+ if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
+ groupadd ${GROUP_ARG} libgit2 && \
+ useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
FROM adduser AS configure
RUN mkdir /var/run/sshd
diff --git a/ci/docker/centos7 b/ci/docker/centos7
index 8105f1442..28ed65081 100644
--- a/ci/docker/centos7
+++ b/ci/docker/centos7
@@ -48,7 +48,12 @@ RUN cd /tmp && \
rm -rf cmake-3.21.1
FROM cmake AS adduser
-RUN useradd --shell /bin/bash libgit2 --create-home
+ARG UID=""
+ARG GID=""
+RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
+ if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
+ groupadd ${GROUP_ARG} libgit2 && \
+ useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
FROM adduser AS configure
ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
diff --git a/ci/docker/centos8 b/ci/docker/centos8
index cca088103..81f0c3c76 100644
--- a/ci/docker/centos8
+++ b/ci/docker/centos8
@@ -1,6 +1,10 @@
ARG BASE=centos:8
-FROM ${BASE} AS yum
+FROM ${BASE} AS stream
+RUN dnf -y --disablerepo '*' --enablerepo=extras swap centos-linux-repos centos-stream-repos && \
+ dnf -y distro-sync
+
+FROM stream AS yum
RUN yum install -y \
which \
bzip2 \
@@ -40,7 +44,12 @@ RUN cd /tmp && \
rm -rf valgrind-3.15.0
FROM valgrind AS adduser
-RUN useradd --shell /bin/bash libgit2 --create-home
+ARG UID=""
+ARG GID=""
+RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
+ if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
+ groupadd ${GROUP_ARG} libgit2 && \
+ useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
FROM adduser AS configure
ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
diff --git a/ci/docker/focal b/ci/docker/focal
index 37d7d6356..b3a402cb0 100644
--- a/ci/docker/focal
+++ b/ci/docker/focal
@@ -32,9 +32,9 @@ RUN apt-get update && \
FROM apt AS mbedtls
RUN cd /tmp && \
- curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \
+ curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \
tar -xz && \
- cd mbedtls-2.16.2 && \
+ cd mbedtls-mbedtls-2.16.2 && \
scripts/config.pl unset MBEDTLS_AESNI_C && \
scripts/config.pl set MBEDTLS_MD4_C 1 && \
mkdir build build-msan && \
@@ -45,7 +45,7 @@ RUN cd /tmp && \
CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF -DCMAKE_BUILD_TYPE=MemSanDbg -DCMAKE_INSTALL_PREFIX=/usr/local/msan .. && \
ninja install && \
cd .. && \
- rm -rf mbedtls-2.16.2
+ rm -rf mbedtls-mbedtls-2.16.2
FROM mbedtls AS libssh2
RUN cd /tmp && \
@@ -73,7 +73,13 @@ RUN cd /tmp && \
rm -rf valgrind-3.15.0
FROM valgrind AS adduser
-RUN useradd --shell /bin/bash libgit2 --create-home
+ARG UID=""
+ARG GID=""
+RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
+ if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
+ groupadd ${GROUP_ARG} libgit2 && \
+ useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
+
FROM adduser AS configure
RUN mkdir /var/run/sshd
diff --git a/ci/docker/xenial b/ci/docker/xenial
index c19fe421d..f5fa5a315 100644
--- a/ci/docker/xenial
+++ b/ci/docker/xenial
@@ -30,14 +30,14 @@ RUN apt-get update && \
FROM apt AS mbedtls
RUN cd /tmp && \
- curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \
- tar -xz && \
- cd mbedtls-2.16.2 && \
+ curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \
+ tar -xz && \
+ cd mbedtls-mbedtls-2.16.2 && \
scripts/config.pl set MBEDTLS_MD4_C 1 && \
CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \
ninja install && \
cd .. && \
- rm -rf mbedtls-2.16.2
+ rm -rf mbedtls-mbedtls-2.16.2
FROM mbedtls AS libssh2
RUN cd /tmp && \
@@ -60,7 +60,13 @@ RUN cd /tmp && \
rm -rf valgrind-3.15.0
FROM valgrind AS adduser
-RUN useradd --shell /bin/bash libgit2 --create-home
+ARG UID=""
+ARG GID=""
+RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
+ if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
+ groupadd ${GROUP_ARG} libgit2 && \
+ useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
+
FROM adduser AS configure
RUN mkdir /var/run/sshd
diff --git a/ci/getcontainer.sh b/ci/getcontainer.sh
index 07ef7b8ea..81d0c1d92 100755
--- a/ci/getcontainer.sh
+++ b/ci/getcontainer.sh
@@ -37,9 +37,13 @@ DOCKER_REGISTRY_CONTAINER_SHA="${DOCKER_REGISTRY_CONTAINER}:${DOCKER_SHA}"
echo "docker-registry-container-sha=${DOCKER_REGISTRY_CONTAINER_SHA}" >> $GITHUB_ENV
echo "docker-registry-container-latest=${DOCKER_REGISTRY_CONTAINER}:latest" >> $GITHUB_ENV
+echo "::: logging in to ${DOCKER_REGISTRY} as ${GITHUB_ACTOR}"
+
exists="true"
docker login https://${DOCKER_REGISTRY} -u ${GITHUB_ACTOR} -p ${GITHUB_TOKEN} || exists="false"
+echo "::: pulling ${DOCKER_REGISTRY_CONTAINER_SHA}"
+
if [ "${exists}" != "false" ]; then
docker pull ${DOCKER_REGISTRY_CONTAINER_SHA} || exists="false"
fi
diff --git a/ci/setup-mingw.sh b/ci/setup-mingw-build.sh
similarity index 97%
rename from ci/setup-mingw.sh
rename to ci/setup-mingw-build.sh
index f5a882d2b..3d72b24eb 100755
--- a/ci/setup-mingw.sh
+++ b/ci/setup-mingw-build.sh
@@ -1,4 +1,6 @@
-#!/bin/sh -e
+#!/bin/sh
+
+set -ex
echo "##############################################################################"
echo "## Downloading mingw"
diff --git a/ci/setup-osx-benchmark.sh b/ci/setup-osx-benchmark.sh
new file mode 100755
index 000000000..80d87682b
--- /dev/null
+++ b/ci/setup-osx-benchmark.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+set -ex
+
+brew update
+brew install hyperfine
diff --git a/ci/setup-osx.sh b/ci/setup-osx-build.sh
similarity index 95%
rename from ci/setup-osx.sh
rename to ci/setup-osx-build.sh
index 2e630eedb..0b95e7629 100755
--- a/ci/setup-osx.sh
+++ b/ci/setup-osx-build.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-set -x
+set -ex
brew update
brew install pkgconfig zlib curl openssl libssh2 ninja
diff --git a/ci/setup-ubuntu-benchmark.sh b/ci/setup-ubuntu-benchmark.sh
new file mode 100755
index 000000000..561a18fd9
--- /dev/null
+++ b/ci/setup-ubuntu-benchmark.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+set -ex
+
+sudo apt-get update
+sudo apt-get install -y --no-install-recommends \
+ cargo \
+ cmake \
+ gcc \
+ git \
+ krb5-user \
+ libkrb5-dev \
+ libssl-dev \
+ libz-dev \
+ make \
+ ninja-build \
+ pkgconf
+
+wget https://github.com/sharkdp/hyperfine/releases/download/v1.12.0/hyperfine_1.12.0_amd64.deb
+sudo dpkg -i hyperfine_1.12.0_amd64.deb
diff --git a/ci/setup-win32-benchmark.sh b/ci/setup-win32-benchmark.sh
new file mode 100755
index 000000000..0eac2f666
--- /dev/null
+++ b/ci/setup-win32-benchmark.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+set -ex
+
+choco install hyperfine zip
+
+CHOCO_PATH=$(mktemp -d)
+curl -L https://github.com/ethomson/PurgeStandbyList/releases/download/v1.0/purgestandbylist.1.0.0.nupkg -o "${CHOCO_PATH}/purgestandbylist.1.0.0.nupkg"
+choco install purgestandbylist -s $(cygpath -w "${CHOCO_PATH}")
diff --git a/ci/test.sh b/ci/test.sh
index a94839778..0815522a9 100755
--- a/ci/test.sh
+++ b/ci/test.sh
@@ -159,10 +159,18 @@ fi
if [ -z "$SKIP_OFFLINE_TESTS" ]; then
echo ""
echo "##############################################################################"
- echo "## Running (offline) tests"
+ echo "## Running core tests"
echo "##############################################################################"
+ echo ""
+ echo "Running libgit2 integration (offline) tests"
+ echo ""
run_test offline
+
+ echo ""
+ echo "Running utility tests"
+ echo ""
+ run_test util
fi
if [ -n "$RUN_INVASIVE_TESTS" ]; then
@@ -186,7 +194,7 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
echo ""
echo "##############################################################################"
- echo "## Running (online) tests"
+ echo "## Running networking (online) tests"
echo "##############################################################################"
export GITTEST_REMOTE_REDIRECT_INITIAL="http://localhost:9000/initial-redirect/libgit2/TestGitRepository"
@@ -198,9 +206,9 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
# Run the online tests that immutably change global state separately
# to avoid polluting the test environment.
echo ""
- echo "##############################################################################"
- echo "## Running (online_customcert) tests"
- echo "##############################################################################"
+ echo "Running custom certificate (online_customcert) tests"
+ echo ""
+
run_test online_customcert
fi
diff --git a/cmake/AddClarTest.cmake b/cmake/AddClarTest.cmake
new file mode 100644
index 000000000..743941638
--- /dev/null
+++ b/cmake/AddClarTest.cmake
@@ -0,0 +1,7 @@
+function(ADD_CLAR_TEST project name)
+ if(NOT USE_LEAK_CHECKER STREQUAL "OFF")
+ add_test(${name} "${PROJECT_SOURCE_DIR}/script/${USE_LEAK_CHECKER}.sh" "${PROJECT_BINARY_DIR}/${project}" ${ARGN})
+ else()
+ add_test(${name} "${PROJECT_BINARY_DIR}/${project}" ${ARGN})
+ endif()
+endfunction(ADD_CLAR_TEST)
diff --git a/cmake/FindPCRE.cmake b/cmake/FindPCRE.cmake
index 3a7cfad91..02e7edce1 100644
--- a/cmake/FindPCRE.cmake
+++ b/cmake/FindPCRE.cmake
@@ -16,19 +16,18 @@
# PCRE_FOUND - True if pcre found.
# Look for the header file.
-find_path(PCRE_INCLUDE_DIR NAMES pcreposix.h)
+find_path(PCRE_INCLUDE_DIR NAMES pcre.h)
# Look for the library.
find_library(PCRE_LIBRARY NAMES pcre)
-find_library(PCRE_POSIX_LIBRARY NAMES pcreposix)
# Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE.
include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_POSIX_LIBRARY PCRE_INCLUDE_DIR)
+find_package_handle_standard_args(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR)
# Copy the results to the output variables.
if(PCRE_FOUND)
- set(PCRE_LIBRARIES ${PCRE_LIBRARY} ${PCRE_POSIX_LIBRARY})
+ set(PCRE_LIBRARIES ${PCRE_LIBRARY})
set(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR})
else(PCRE_FOUND)
set(PCRE_LIBRARIES)
diff --git a/cmake/FindPCRE2.cmake b/cmake/FindPCRE2.cmake
index d4b8e6761..41c165b65 100644
--- a/cmake/FindPCRE2.cmake
+++ b/cmake/FindPCRE2.cmake
@@ -16,7 +16,7 @@
# PCRE2_FOUND - True if pcre found.
# Look for the header file.
-find_path(PCRE2_INCLUDE_DIR NAMES pcre2posix.h)
+find_path(PCRE2_INCLUDE_DIR NAMES pcre2.h)
# Look for the library.
find_library(PCRE2_LIBRARY NAMES pcre2-8)
diff --git a/cmake/SelectHTTPSBackend.cmake b/cmake/SelectHTTPSBackend.cmake
index 79319502e..20221bf9f 100644
--- a/cmake/SelectHTTPSBackend.cmake
+++ b/cmake/SelectHTTPSBackend.cmake
@@ -64,7 +64,7 @@ if(USE_HTTPS)
if(NOT CERT_LOCATION)
message(STATUS "Auto-detecting default certificates location")
- if(CMAKE_SYSTEM_NAME MATCHES Darwin)
+ if(EXISTS "/usr/local/opt/openssl/bin/openssl")
# Check for an Homebrew installation
set(OPENSSL_CMD "/usr/local/opt/openssl/bin/openssl")
else()
diff --git a/cmake/SelectHashes.cmake b/cmake/SelectHashes.cmake
index bedd8c4e3..faf9e2ea3 100644
--- a/cmake/SelectHashes.cmake
+++ b/cmake/SelectHashes.cmake
@@ -4,6 +4,9 @@ include(SanitizeBool)
# USE_SHA1=CollisionDetection(ON)/HTTPS/Generic/OFF
sanitizebool(USE_SHA1)
+sanitizebool(USE_SHA256)
+
+# sha1
if(USE_SHA1 STREQUAL ON)
SET(USE_SHA1 "CollisionDetection")
@@ -21,32 +24,77 @@ endif()
if(USE_SHA1 STREQUAL "CollisionDetection")
set(GIT_SHA1_COLLISIONDETECT 1)
- add_definitions(-DSHA1DC_NO_STANDARD_INCLUDES=1)
- add_definitions(-DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\"common.h\")
- add_definitions(-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"common.h\")
elseif(USE_SHA1 STREQUAL "OpenSSL")
- # OPENSSL_FOUND should already be set, we're checking USE_HTTPS
-
set(GIT_SHA1_OPENSSL 1)
+elseif(USE_SHA1 STREQUAL "OpenSSL-Dynamic")
+ set(GIT_SHA1_OPENSSL 1)
+ set(GIT_SHA1_OPENSSL_DYNAMIC 1)
+ list(APPEND LIBGIT2_SYSTEM_LIBS dl)
+elseif(USE_SHA1 STREQUAL "CommonCrypto")
+ set(GIT_SHA1_COMMON_CRYPTO 1)
+elseif(USE_SHA1 STREQUAL "mbedTLS")
+ set(GIT_SHA1_MBEDTLS 1)
+elseif(USE_SHA1 STREQUAL "Win32")
+ set(GIT_SHA1_WIN32 1)
+else()
+ message(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}")
+endif()
+
+# sha256
+
+if(USE_SHA256 STREQUAL ON AND USE_HTTPS)
+ SET(USE_SHA256 "HTTPS")
+elseif(USE_SHA256 STREQUAL ON)
+ SET(USE_SHA256 "Builtin")
+endif()
+
+if(USE_SHA256 STREQUAL "HTTPS")
+ if(USE_HTTPS STREQUAL "SecureTransport")
+ set(USE_SHA256 "CommonCrypto")
+ elseif(USE_HTTPS STREQUAL "WinHTTP")
+ set(USE_SHA256 "Win32")
+ elseif(USE_HTTPS)
+ set(USE_SHA256 ${USE_HTTPS})
+ endif()
+endif()
+
+if(USE_SHA256 STREQUAL "Builtin")
+ set(GIT_SHA256_BUILTIN 1)
+elseif(USE_SHA256 STREQUAL "OpenSSL")
+ set(GIT_SHA256_OPENSSL 1)
+elseif(USE_SHA256 STREQUAL "OpenSSL-Dynamic")
+ set(GIT_SHA256_OPENSSL 1)
+ set(GIT_SHA256_OPENSSL_DYNAMIC 1)
+ list(APPEND LIBGIT2_SYSTEM_LIBS dl)
+elseif(USE_SHA256 STREQUAL "CommonCrypto")
+ set(GIT_SHA256_COMMON_CRYPTO 1)
+elseif(USE_SHA256 STREQUAL "mbedTLS")
+ set(GIT_SHA256_MBEDTLS 1)
+elseif(USE_SHA256 STREQUAL "Win32")
+ set(GIT_SHA256_WIN32 1)
+else()
+ message(FATAL_ERROR "Asked for unknown SHA256 backend: ${USE_SHA256}")
+endif()
+
+# add library requirements
+if(USE_SHA1 STREQUAL "OpenSSL" OR USE_SHA256 STREQUAL "OpenSSL")
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
list(APPEND LIBGIT2_PC_LIBS "-lssl")
else()
list(APPEND LIBGIT2_PC_REQUIRES "openssl")
endif()
-elseif(USE_SHA1 STREQUAL "CommonCrypto")
- set(GIT_SHA1_COMMON_CRYPTO 1)
-elseif(USE_SHA1 STREQUAL "mbedTLS")
- set(GIT_SHA1_MBEDTLS 1)
+endif()
+
+if(USE_SHA1 STREQUAL "mbedTLS" OR USE_SHA256 STREQUAL "mbedTLS")
list(APPEND LIBGIT2_SYSTEM_INCLUDES ${MBEDTLS_INCLUDE_DIR})
list(APPEND LIBGIT2_SYSTEM_LIBS ${MBEDTLS_LIBRARIES})
# mbedTLS has no pkgconfig file, hence we can't require it
# https://github.com/ARMmbed/mbedtls/issues/228
# For now, pass its link flags as our own
list(APPEND LIBGIT2_PC_LIBS ${MBEDTLS_LIBRARIES})
-elseif(USE_SHA1 STREQUAL "Win32")
- set(GIT_SHA1_WIN32 1)
-elseif(NOT (USE_SHA1 STREQUAL "Generic"))
- message(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}")
endif()
-add_feature_info(SHA ON "using ${USE_SHA1}")
+# notify feature enablement
+
+add_feature_info(SHA1 ON "using ${USE_SHA1}")
+add_feature_info(SHA256 ON "using ${USE_SHA256}")
diff --git a/docs/changelog.md b/docs/changelog.md
index 32a67d2c7..989800a11 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -1,39 +1,79 @@
-v1.4.3
-------
+v1.5
+----
-🔒 This is a security release to provide compatibility with git's changes to address [CVE 2022-24765](https://github.blog/2022-04-12-git-security-vulnerability-announced/).
-
-**libgit2 is not directly affected** by this vulnerability, because libgit2 does not directly invoke any executable. But we are providing these changes as a security release for any users that use libgit2 for repository discovery and then _also_ use git on that repository. In this release, we will now validate that the user opening the repository is the same user that owns the on-disk repository. This is to match git's behavior.
-
-In addition, we are providing several correctness fixes where invalid input can lead to a crash. These may prevent possible denial of service attacks. At this time there are not known exploits to these issues.
-
-Full list of changes:
-
-* Validate repository directory ownership (v1.4) by @ethomson in https://github.com/libgit2/libgit2/pull/6267
-* midx: Fix an undefined behavior (left-shift signed overflow) by @lhchavez in https://github.com/libgit2/libgit2/pull/6260
-* fetch: support OID refspec without dst by @ethomson in https://github.com/libgit2/libgit2/pull/6251
-* Fix crash when regenerating a patch with unquoted spaces in filename by @jorio in https://github.com/libgit2/libgit2/pull/6244
-
-All users of the v1.4 release line are recommended to upgrade.
-
-**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.4.2...v1.4.3
-
-v1.4.2
-------
-
-This is a bugfix release with the following changes:
+This is release v1.5.0, "Stubentiger". This release adds the basis for an experimental CLI, continues preparing for SHA256 support, adds a benchmarking utility, and has numerous new features and bugfixes.
+## What's Changed
+### New features
+* The beginnings of a git-compatible CLI for testing and benchmarking by @ethomson in https://github.com/libgit2/libgit2/pull/6133
+* Add `clone` support to the CLI @ethomson in https://github.com/libgit2/libgit2/pull/6274
+* A benchmarking suite to compare libgit2 functionality against git by @ethomson in https://github.com/libgit2/libgit2/pull/6235
+* SHA256: add a SHA256 implementation backend by @ethomson in https://github.com/libgit2/libgit2/pull/6144
+* SHA256: support dynamically loaded openssl by @ethomson in https://github.com/libgit2/libgit2/pull/6258
+* Transport: introduce `git_transport_smart_remote_connect_options` by @lhchavez in https://github.com/libgit2/libgit2/pull/6278
+### Bug fixes
+* Free parent and ref in lg2_commit before returning. by @apnadkarni in https://github.com/libgit2/libgit2/pull/6219
+* xdiff: use xdl_free not free by @ethomson in https://github.com/libgit2/libgit2/pull/6223
* remote: do store the update_tips callback error value by @carlosmn in https://github.com/libgit2/libgit2/pull/6226
* win32: `find_system_dirs` does not return `GIT_ENOTFOUND` by @ethomson in https://github.com/libgit2/libgit2/pull/6228
+* Some minor fixes for issues discovered by coverity by @ethomson in https://github.com/libgit2/libgit2/pull/6238
+* Fix a string concatenation bug when validating extensions by @bierbaum in https://github.com/libgit2/libgit2/pull/6246
+* fetch: support OID refspec without dst by @ethomson in https://github.com/libgit2/libgit2/pull/6251
+* Fix crash when regenerating a patch with unquoted spaces in filename by @jorio in https://github.com/libgit2/libgit2/pull/6244
+* midx: Fix an undefined behavior (left-shift signed overflow) by @lhchavez in https://github.com/libgit2/libgit2/pull/6260
+* Validate repository directory ownership by @ethomson in https://github.com/libgit2/libgit2/pull/6266
+* midx: fix large offset table check. by @ccstolley in https://github.com/libgit2/libgit2/pull/6309
+* midx: do not verify the checksum on load by @carlosmn in https://github.com/libgit2/libgit2/pull/6291
+* revparse: Remove error-prone, redundant test by @dongcarl in https://github.com/libgit2/libgit2/pull/6299
+* refs: fix missing error message by @zawata in https://github.com/libgit2/libgit2/pull/6305
+* CLI: progress updates by @ethomson in https://github.com/libgit2/libgit2/pull/6319
+* A couple of simplications around mwindow by @carlosmn in https://github.com/libgit2/libgit2/pull/6288
+* config: update config entry iteration lifecycle by @ethomson in https://github.com/libgit2/libgit2/pull/6320
+* repo: allow administrator to own the configuration by @ethomson in https://github.com/libgit2/libgit2/pull/6321
+* filter: Fix Segfault by @zawata in https://github.com/libgit2/libgit2/pull/6303
+* ntlmclient: LibreSSL 3.5 removed HMAC_CTX_cleanup by @vishwin in https://github.com/libgit2/libgit2/pull/6340
+* Fix internal git_sysdir_find* function usage within public git_config_find* functions by @kcsaul in https://github.com/libgit2/libgit2/pull/6335
+* fix interactive rebase detect. by @i-tengfei in https://github.com/libgit2/libgit2/pull/6334
+* cmake: drop posix dependency from pcre* detection by @jpalus in https://github.com/libgit2/libgit2/pull/6333
+* Fix erroneously lax configuration ownership checks by @ethomson in https://github.com/libgit2/libgit2/pull/6341
+* pack: don't pretend we support pack files v3 by @ethomson in https://github.com/libgit2/libgit2/pull/6347
+* Fix creation of branches and tags with invalid names by @lya001 in https://github.com/libgit2/libgit2/pull/6348
+### Security fixes
+* Fixes for CVE 2022-29187 by @ethomson in https://github.com/libgit2/libgit2/pull/6349
+* zlib: update bundled zlib to v1.2.12 by @ethomson in https://github.com/libgit2/libgit2/pull/6350
+### Code cleanups
+* sha256: refactoring in preparation for sha256 by @ethomson in https://github.com/libgit2/libgit2/pull/6265
+* remote: Delete a now-inexistent API declaration by @lhchavez in https://github.com/libgit2/libgit2/pull/6276
+* Fix missing include by @cschlack in https://github.com/libgit2/libgit2/pull/6277
+### Build and CI improvements
+* meta: show build status for v1.3 and v1.4 branches by @ethomson in https://github.com/libgit2/libgit2/pull/6216
+* cmake: Fix package name for system http-parser by @mgorny in https://github.com/libgit2/libgit2/pull/6217
+* meta: update version number to v1.5.0-alpha by @ethomson in https://github.com/libgit2/libgit2/pull/6220
+* cmake: export libraries needed to compile against libgit2 by @ethomson in https://github.com/libgit2/libgit2/pull/6239
+* clone: update bitbucket tests by @ethomson in https://github.com/libgit2/libgit2/pull/6252
+* diff: don't stat empty file on arm32 (flaky test) by @ethomson in https://github.com/libgit2/libgit2/pull/6259
+* tests: support flaky stat by @ethomson in https://github.com/libgit2/libgit2/pull/6262
+* Include test results data in CI by @ethomson in https://github.com/libgit2/libgit2/pull/6306
+* Add a .clang-format with our style by @ethomson in https://github.com/libgit2/libgit2/pull/6023
+* CI: limits actions scheduled workflows to the main repo by @ethomson in https://github.com/libgit2/libgit2/pull/6342
+* ci: update dockerfiles for mbedTLS new url by @ethomson in https://github.com/libgit2/libgit2/pull/6343
+### Documentation improvements
+* Add Pharo to language bindings by @theseion in https://github.com/libgit2/libgit2/pull/6310
+* Add link to Tcl bindings for libgit2 by @apnadkarni in https://github.com/libgit2/libgit2/pull/6318
+* fix couple of typos by @SkinnyMind in https://github.com/libgit2/libgit2/pull/6287
+* update documentation for default status options by @ethomson in https://github.com/libgit2/libgit2/pull/6322
-v1.4.1
-------
+## New Contributors
+* @bierbaum made their first contribution in https://github.com/libgit2/libgit2/pull/6246
+* @dongcarl made their first contribution in https://github.com/libgit2/libgit2/pull/6299
+* @SkinnyMind made their first contribution in https://github.com/libgit2/libgit2/pull/6287
+* @zawata made their first contribution in https://github.com/libgit2/libgit2/pull/6305
+* @vishwin made their first contribution in https://github.com/libgit2/libgit2/pull/6340
+* @i-tengfei made their first contribution in https://github.com/libgit2/libgit2/pull/6334
+* @jpalus made their first contribution in https://github.com/libgit2/libgit2/pull/6333
+* @lya001 made their first contribution in https://github.com/libgit2/libgit2/pull/6348
-This is a bugfix release with the following changes:
-
-* xdiff: use xdl_free not free by @ethomson
-* cmake: Fix package name for system http-parser by @mgorny
-* Free parent and ref in lg2_commit before returning by @apnadkarni
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.4.0...v1.5.0
v1.4
----
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 235e72ada..8e38c7d4e 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,3 +1,5 @@
+# examples: code usage examples of libgit2
+
file(GLOB SRC_EXAMPLES *.c *.h)
add_executable(lg2 ${SRC_EXAMPLES})
@@ -10,7 +12,7 @@ target_include_directories(lg2 PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_
target_include_directories(lg2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
if(WIN32 OR ANDROID)
- target_link_libraries(lg2 git2)
+ target_link_libraries(lg2 libgit2package)
else()
- target_link_libraries(lg2 git2 pthread)
+ target_link_libraries(lg2 libgit2package pthread)
endif()
diff --git a/fuzzers/CMakeLists.txt b/fuzzers/CMakeLists.txt
index eaa490fd9..a2c19ed40 100644
--- a/fuzzers/CMakeLists.txt
+++ b/fuzzers/CMakeLists.txt
@@ -1,3 +1,5 @@
+# fuzzers: libFuzzer and standalone fuzzing utilities
+
if(BUILD_FUZZERS AND NOT USE_STANDALONE_FUZZERS)
set(CMAKE_REQUIRED_FLAGS "-fsanitize=fuzzer-no-link")
add_c_flag(-fsanitize=fuzzer)
diff --git a/fuzzers/packfile_fuzzer.c b/fuzzers/packfile_fuzzer.c
index 6002fa1de..23ecaf80f 100644
--- a/fuzzers/packfile_fuzzer.c
+++ b/fuzzers/packfile_fuzzer.c
@@ -94,7 +94,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
fprintf(stderr, "Failed to compute the SHA1 hash\n");
abort();
}
- if (git_indexer_append(indexer, &oid, sizeof(oid), &stats) < 0) {
+ if (git_indexer_append(indexer, &oid.id, GIT_OID_RAWSZ, &stats) < 0) {
goto cleanup;
}
}
diff --git a/include/git2/branch.h b/include/git2/branch.h
index 36a393d10..27c6fa157 100644
--- a/include/git2/branch.h
+++ b/include/git2/branch.h
@@ -129,8 +129,8 @@ GIT_EXTERN(void) git_branch_iterator_free(git_branch_iterator *iter);
* See `git_tag_create()` for rules about valid names.
*
* Note that if the move succeeds, the old reference object will not
- + be valid anymore, and should be freed immediately by the user using
- + `git_reference_free()`.
+ * be valid anymore, and should be freed immediately by the user using
+ * `git_reference_free()`.
*
* @param out New reference object for the updated name.
*
diff --git a/include/git2/common.h b/include/git2/common.h
index b8c4ad531..c3e3e7b4e 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -121,6 +121,17 @@ GIT_BEGIN_DECL
*/
GIT_EXTERN(int) git_libgit2_version(int *major, int *minor, int *rev);
+/**
+ * Return the prerelease state of the libgit2 library currently being
+ * used. For nightly builds during active development, this will be
+ * "alpha". Releases may have a "beta" or release candidate ("rc1",
+ * "rc2", etc) prerelease. For a final release, this function returns
+ * NULL.
+ *
+ * @return the name of the prerelease state or NULL
+ */
+GIT_EXTERN(const char *) git_libgit2_prerelease(void);
+
/**
* Combinations of these values describe the features with which libgit2
* was compiled
diff --git a/include/git2/config.h b/include/git2/config.h
index a5486a4c9..cfab0c757 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -122,7 +122,7 @@ typedef struct {
* global configuration file.
*
* This method will not guess the path to the xdg compatible
- * config file (.config/git/config).
+ * config file (`.config/git/config`).
*
* @param out Pointer to a user-allocated git_buf in which to store the path
* @return 0 if a global configuration file has been found. Its path will be stored in `out`.
@@ -149,8 +149,8 @@ GIT_EXTERN(int) git_config_find_xdg(git_buf *out);
/**
* Locate the path to the system configuration file
*
- * If /etc/gitconfig doesn't exist, it will look for
- * %PROGRAMFILES%\Git\etc\gitconfig.
+ * If `/etc/gitconfig` doesn't exist, it will look for
+ * `%PROGRAMFILES%\Git\etc\gitconfig`.
*
* @param out Pointer to a user-allocated git_buf in which to store the path
* @return 0 if a system configuration file has been
@@ -161,7 +161,7 @@ GIT_EXTERN(int) git_config_find_system(git_buf *out);
/**
* Locate the path to the configuration file in ProgramData
*
- * Look for the file in %PROGRAMDATA%\Git\config used by portable git.
+ * Look for the file in `%PROGRAMDATA%\Git\config` used by portable git.
*
* @param out Pointer to a user-allocated git_buf in which to store the path
* @return 0 if a ProgramData configuration file has been
@@ -449,8 +449,8 @@ GIT_EXTERN(int) git_config_multivar_iterator_new(git_config_iterator **out, cons
/**
* Return the current entry and advance the iterator
*
- * The pointers returned by this function are valid until the iterator
- * is freed.
+ * The pointers returned by this function are valid until the next call
+ * to `git_config_next` or until the iterator is freed.
*
* @param entry pointer to store the entry
* @param iter the iterator
diff --git a/include/git2/deprecated.h b/include/git2/deprecated.h
index f73d7da61..c32abeeb7 100644
--- a/include/git2/deprecated.h
+++ b/include/git2/deprecated.h
@@ -436,6 +436,8 @@ GIT_EXTERN(int) git_diff_format_email_options_init(
#define GITERR_WORKTREE GIT_ERROR_WORKTREE
#define GITERR_SHA1 GIT_ERROR_SHA1
+#define GIT_ERROR_SHA1 GIT_ERROR_SHA
+
/**
* Return the last `git_error` object that was generated for the
* current thread. This is an alias of `git_error_last` and is
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 16712f988..a61964bbb 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -107,7 +107,7 @@ typedef enum {
GIT_ERROR_FILESYSTEM,
GIT_ERROR_PATCH,
GIT_ERROR_WORKTREE,
- GIT_ERROR_SHA1,
+ GIT_ERROR_SHA,
GIT_ERROR_HTTP,
GIT_ERROR_INTERNAL
} git_error_t;
@@ -131,7 +131,8 @@ GIT_EXTERN(const git_error *) git_error_last(void);
GIT_EXTERN(void) git_error_clear(void);
/**
- * Set the error message string for this thread.
+ * Set the error message string for this thread, using `printf`-style
+ * formatting.
*
* This function is public so that custom ODB backends and the like can
* relay an error message through libgit2. Most regular users of libgit2
@@ -144,7 +145,20 @@ GIT_EXTERN(void) git_error_clear(void);
*
* @param error_class One of the `git_error_t` enum above describing the
* general subsystem that is responsible for the error.
- * @param string The formatted error message to keep
+ * @param fmt The `printf`-style format string; subsequent arguments must
+ * be the arguments for the format string.
+ */
+GIT_EXTERN(void) git_error_set(int error_class, const char *fmt, ...)
+ GIT_FORMAT_PRINTF(2, 3);
+
+/**
+ * Set the error message string for this thread. This function is like
+ * `git_error_set` but takes a static string instead of a `printf`-style
+ * format.
+ *
+ * @param error_class One of the `git_error_t` enum above describing the
+ * general subsystem that is responsible for the error.
+ * @param string The error message to keep
* @return 0 on success or -1 on failure
*/
GIT_EXTERN(int) git_error_set_str(int error_class, const char *string);
diff --git a/include/git2/merge.h b/include/git2/merge.h
index e90941a49..fcce5594d 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -603,7 +603,7 @@ GIT_EXTERN(int) git_merge_commits(
* completes, resolve any conflicts and prepare a commit.
*
* For compatibility with git, the repository is put into a merging
- * state. Once the commit is done (or if the uses wishes to abort),
+ * state. Once the commit is done (or if the user wishes to abort),
* you should clear this state by calling
* `git_repository_state_cleanup()`.
*
diff --git a/include/git2/status.h b/include/git2/status.h
index d8f9663b4..bb28e875b 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -227,13 +227,16 @@ typedef struct {
/**
* The `show` value is one of the `git_status_show_t` constants that
- * control which files to scan and in what order.
+ * control which files to scan and in what order. The default is
+ * `GIT_STATUS_SHOW_INDEX_AND_WORKDIR`.
*/
git_status_show_t show;
/**
* The `flags` value is an OR'ed combination of the
- * `git_status_opt_t` values above.
+ * `git_status_opt_t` values above. The default is
+ * `GIT_STATUS_OPT_DEFAULTS`, which matches git's default
+ * behavior.
*/
unsigned int flags;
diff --git a/include/git2/sys/remote.h b/include/git2/sys/remote.h
index dd243ca55..0eae9234d 100644
--- a/include/git2/sys/remote.h
+++ b/include/git2/sys/remote.h
@@ -8,6 +8,8 @@
#ifndef INCLUDE_sys_git_remote_h
#define INCLUDE_sys_git_remote_h
+#include "git2/remote.h"
+
/**
* @file git2/sys/remote.h
* @brief Low-level remote functionality for custom transports
@@ -26,6 +28,19 @@ typedef enum {
GIT_REMOTE_CAPABILITY_REACHABLE_OID = (1 << 1),
} git_remote_capability_t;
+/**
+ * Disposes libgit2-initialized fields from a git_remote_connect_options.
+ * This should only be used for git_remote_connect_options returned by
+ * git_transport_remote_connect_options.
+ *
+ * Note that this does not free the `git_remote_connect_options` itself, just
+ * the memory pointed to by it.
+ *
+ * @param opts The `git_remote_connect_options` struct to dispose.
+ */
+GIT_EXTERN(void) git_remote_connect_options_dispose(
+ git_remote_connect_options *opts);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h
index f0c2a3eab..06ae7079f 100644
--- a/include/git2/sys/transport.h
+++ b/include/git2/sys/transport.h
@@ -9,10 +9,11 @@
#define INCLUDE_sys_git_transport_h
#include "git2/net.h"
+#include "git2/proxy.h"
+#include "git2/remote.h"
+#include "git2/strarray.h"
#include "git2/transport.h"
#include "git2/types.h"
-#include "git2/strarray.h"
-#include "git2/proxy.h"
/**
* @file git2/sys/transport.h
@@ -261,14 +262,17 @@ GIT_EXTERN(int) git_transport_smart_certificate_check(git_transport *transport,
GIT_EXTERN(int) git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods);
/**
- * Get a copy of the proxy options
+ * Get a copy of the remote connect options
*
- * The url is copied and must be freed by the caller.
+ * All data is copied and must be freed by the caller by calling
+ * `git_remote_connect_options_dispose`.
*
* @param out options struct to fill
* @param transport the transport to extract the data from.
*/
-GIT_EXTERN(int) git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport);
+GIT_EXTERN(int) git_transport_remote_connect_options(
+ git_remote_connect_options *out,
+ git_transport *transport);
/*
*** End of base transport interface ***
diff --git a/include/git2/version.h b/include/git2/version.h
index 980f70b69..d591904fd 100644
--- a/include/git2/version.h
+++ b/include/git2/version.h
@@ -7,12 +7,33 @@
#ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__
-#define LIBGIT2_VERSION "1.4.3"
-#define LIBGIT2_VER_MAJOR 1
-#define LIBGIT2_VER_MINOR 4
-#define LIBGIT2_VER_REVISION 3
-#define LIBGIT2_VER_PATCH 0
+/**
+ * The version string for libgit2. This string follows semantic
+ * versioning (v2) guidelines.
+ */
+#define LIBGIT2_VERSION "1.5.0"
-#define LIBGIT2_SOVERSION "1.4"
+/** The major version number for this version of libgit2. */
+#define LIBGIT2_VER_MAJOR 1
+
+/** The minor version number for this version of libgit2. */
+#define LIBGIT2_VER_MINOR 5
+
+/** The revision ("teeny") version number for this version of libgit2. */
+#define LIBGIT2_VER_REVISION 0
+
+/** The Windows DLL patch number for this version of libgit2. */
+#define LIBGIT2_VER_PATCH 0
+
+/**
+ * The prerelease string for this version of libgit2. For development
+ * (nightly) builds, this will be "alpha". For prereleases, this will be
+ * a prerelease name like "beta" or "rc1". For final releases, this will
+ * be `NULL`.
+ */
+#define LIBGIT2_VER_PRERELEASE NULL
+
+/** The library ABI soversion for this version of libgit2. */
+#define LIBGIT2_SOVERSION "1.5"
#endif
diff --git a/package.json b/package.json
index 5caa47aea..8676fe513 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "libgit2",
- "version": "1.4.3",
+ "version": "1.5.0",
"repo": "https://github.com/libgit2/libgit2",
"description": " A cross-platform, linkable library implementation of Git that you can use in your application.",
"install": "mkdir build && cd build && cmake .. && cmake --build ."
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e7b54d036..d16cfe538 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,12 +1,22 @@
-add_library(git2internal OBJECT)
-set_target_properties(git2internal PROPERTIES C_STANDARD 90)
-set_target_properties(git2internal PROPERTIES C_EXTENSIONS OFF)
+# The main libgit2 source tree: this CMakeLists.txt identifies platform
+# support and includes the subprojects that make up core libgit2 support.
+#
+# Optional build configuration settings
+#
if(DEPRECATE_HARD)
add_definitions(-DGIT_DEPRECATE_HARD)
endif()
+if(USE_LEAK_CHECKER STREQUAL "valgrind")
+ add_definitions(-DVALGRIND)
+endif()
+
+#
+# Optional debugging functionality
+#
+
if(DEBUG_POOL)
set(GIT_DEBUG_POOL 1)
endif()
@@ -22,29 +32,32 @@ if(DEBUG_STRICT_OPEN)
endif()
add_feature_info(debugopen GIT_DEBUG_STRICT_OPEN "path validation in open")
+#
+# Optional feature enablement
+#
-include(PkgBuildConfig)
-include(SanitizeBool)
+include(SelectGSSAPI)
+include(SelectHTTPSBackend)
+include(SelectHashes)
+include(SelectHTTPParser)
+include(SelectRegex)
+include(SelectSSH)
+include(SelectWinHTTP)
+include(SelectZlib)
-# This variable will contain the libraries we need to put into
-# libgit2.pc's Requires.private. That is, what we're linking to or
-# what someone who's statically linking us needs to link to.
-set(LIBGIT2_PC_REQUIRES "")
-# This will be set later if we use the system's http-parser library or
-# use iconv (OSX) and will be written to the Libs.private field in the
-# pc file.
-set(LIBGIT2_PC_LIBS "")
+#
+# Platform support
+#
-set(LIBGIT2_INCLUDES
- "${CMAKE_CURRENT_BINARY_DIR}"
- "${PROJECT_SOURCE_DIR}/src"
- "${PROJECT_SOURCE_DIR}/include")
+# futimes/futimens
if(HAVE_FUTIMENS)
set(GIT_USE_FUTIMENS 1)
endif ()
add_feature_info(futimens GIT_USE_FUTIMENS "futimens support")
+# qsort
+
check_prototype_definition(qsort_r
"void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *))"
"" "stdlib.h" GIT_QSORT_R_BSD)
@@ -55,69 +68,85 @@ check_prototype_definition(qsort_r
check_function_exists(qsort_s GIT_QSORT_S)
+# random / entropy data
+
check_function_exists(getentropy GIT_RAND_GETENTROPY)
+check_function_exists(getloadavg GIT_RAND_GETLOADAVG)
-# Find required dependencies
+# determine architecture of the machine
-if(WIN32)
- list(APPEND LIBGIT2_SYSTEM_LIBS ws2_32)
-elseif(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
- list(APPEND LIBGIT2_SYSTEM_LIBS socket nsl)
- list(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl")
-elseif(CMAKE_SYSTEM_NAME MATCHES "Haiku")
- list(APPEND LIBGIT2_SYSTEM_LIBS network)
- list(APPEND LIBGIT2_PC_LIBS "-lnetwork")
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(GIT_ARCH_64 1)
+elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(GIT_ARCH_32 1)
+elseif(CMAKE_SIZEOF_VOID_P)
+ message(FATAL_ERROR "Unsupported architecture (pointer size is ${CMAKE_SIZEOF_VOID_P} bytes)")
+else()
+ message(FATAL_ERROR "Unsupported architecture (CMAKE_SIZEOF_VOID_P is unset)")
endif()
+# nanosecond mtime/ctime support
+
+if(USE_NSEC)
+ set(GIT_USE_NSEC 1)
+endif()
+
+# high-resolution stat support
+
+if(HAVE_STRUCT_STAT_ST_MTIM)
+ set(GIT_USE_STAT_MTIM 1)
+elseif(HAVE_STRUCT_STAT_ST_MTIMESPEC)
+ set(GIT_USE_STAT_MTIMESPEC 1)
+elseif(HAVE_STRUCT_STAT_ST_MTIME_NSEC)
+ set(GIT_USE_STAT_MTIME_NSEC 1)
+endif()
+
+# realtime support
+
check_library_exists(rt clock_gettime "time.h" NEED_LIBRT)
if(NEED_LIBRT)
list(APPEND LIBGIT2_SYSTEM_LIBS rt)
list(APPEND LIBGIT2_PC_LIBS "-lrt")
endif()
+# platform libraries
+
+if(WIN32)
+ list(APPEND LIBGIT2_SYSTEM_LIBS ws2_32)
+endif()
+
+if(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
+ list(APPEND LIBGIT2_SYSTEM_LIBS socket nsl)
+ list(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl")
+endif()
+
+if(CMAKE_SYSTEM_NAME MATCHES "Haiku")
+ list(APPEND LIBGIT2_SYSTEM_LIBS gnu network)
+ list(APPEND LIBGIT2_PC_LIBS "-lgnu -lnetwork")
+endif()
+
+if(AMIGA)
+ add_definitions(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP)
+endif()
+
+# threads
+
if(USE_THREADS)
- list(APPEND LIBGIT2_SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT})
- list(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT})
+ if(NOT WIN32)
+ find_package(Threads REQUIRED)
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT})
+ list(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT})
+ endif()
+
+ set(GIT_THREADS 1)
endif()
add_feature_info(threadsafe USE_THREADS "threadsafe support")
+#
+# Optional bundled features
+#
-if(WIN32 AND EMBED_SSH_PATH)
- file(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
- list(SORT SRC_SSH)
- target_sources(git2internal PRIVATE ${SRC_SSH})
-
- list(APPEND LIBGIT2_SYSTEM_INCLUDES "${EMBED_SSH_PATH}/include")
- file(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"")
- set(GIT_SSH 1)
-endif()
-
-include(SelectHTTPSBackend)
-include(SelectHashes)
-include(SelectHTTPParser)
-include(SelectRegex)
-include(SelectSSH)
-include(SelectWinHTTP)
-include(SelectZlib)
-
-
-if(USE_SHA1 STREQUAL "CollisionDetection")
- file(GLOB SRC_SHA1 hash/sha1/collisiondetect.* hash/sha1/sha1dc/*)
-elseif(USE_SHA1 STREQUAL "OpenSSL")
- file(GLOB SRC_SHA1 hash/sha1/openssl.*)
-elseif(USE_SHA1 STREQUAL "CommonCrypto")
- file(GLOB SRC_SHA1 hash/sha1/common_crypto.*)
-elseif(USE_SHA1 STREQUAL "mbedTLS")
- file(GLOB SRC_SHA1 hash/sha1/mbedtls.*)
-elseif(USE_SHA1 STREQUAL "Win32")
- file(GLOB SRC_SHA1 hash/sha1/win32.*)
-elseif(USE_SHA1 STREQUAL "Generic")
- file(GLOB SRC_SHA1 hash/sha1/generic.*)
-endif()
-list(APPEND SRC_SHA1 "hash/sha1.h")
-target_sources(git2internal PRIVATE ${SRC_SHA1})
-
-# Optional external dependency: ntlmclient
+# ntlmclient
if(USE_NTLMCLIENT)
set(GIT_NTLM 1)
add_subdirectory("${PROJECT_SOURCE_DIR}/deps/ntlmclient" "${PROJECT_BINARY_DIR}/deps/ntlmclient")
@@ -126,11 +155,10 @@ if(USE_NTLMCLIENT)
endif()
add_feature_info(ntlmclient GIT_NTLM "NTLM authentication support for Unix")
-# Optional external dependency: GSSAPI
+#
+# Optional external dependencies
-include(SelectGSSAPI)
-
-# Optional external dependency: iconv
+# iconv
if(USE_ICONV)
find_package(Iconv)
endif()
@@ -142,166 +170,28 @@ if(ICONV_FOUND)
endif()
add_feature_info(iconv GIT_USE_ICONV "iconv encoding conversion support")
-
-if(USE_THREADS)
- if(NOT WIN32)
- find_package(Threads REQUIRED)
- endif()
-
- set(GIT_THREADS 1)
-endif()
-
-if(USE_NSEC)
- set(GIT_USE_NSEC 1)
-endif()
-
-if(HAVE_STRUCT_STAT_ST_MTIM)
- set(GIT_USE_STAT_MTIM 1)
-elseif(HAVE_STRUCT_STAT_ST_MTIMESPEC)
- set(GIT_USE_STAT_MTIMESPEC 1)
-elseif(HAVE_STRUCT_STAT_ST_MTIME_NSEC)
- set(GIT_USE_STAT_MTIME_NSEC 1)
-endif()
-
-target_compile_definitions(git2internal PRIVATE _FILE_OFFSET_BITS=64)
-
-# Collect sourcefiles
-file(GLOB SRC_H
- "${PROJECT_SOURCE_DIR}/include/git2.h"
- "${PROJECT_SOURCE_DIR}/include/git2/*.h"
- "${PROJECT_SOURCE_DIR}/include/git2/sys/*.h")
-list(SORT SRC_H)
-target_sources(git2internal PRIVATE ${SRC_H})
-
-# On Windows use specific platform sources
-if(WIN32 AND NOT CYGWIN)
- set(WIN_RC "win32/git2.rc")
-
- file(GLOB SRC_OS win32/*.c win32/*.h)
- list(SORT SRC_OS)
- target_sources(git2internal PRIVATE ${SRC_OS})
-elseif(AMIGA)
- target_compile_definitions(git2internal PRIVATE NO_ADDRINFO NO_READDIR_R NO_MMAP)
-else()
- file(GLOB SRC_OS unix/*.c unix/*.h)
- list(SORT SRC_OS)
- target_sources(git2internal PRIVATE ${SRC_OS})
-endif()
-
-if(USE_LEAK_CHECKER STREQUAL "valgrind")
- target_compile_definitions(git2internal PRIVATE VALGRIND)
-endif()
-
-file(GLOB SRC_GIT2 *.c *.h
- allocators/*.c allocators/*.h
- streams/*.c streams/*.h
- transports/*.c transports/*.h
- xdiff/*.c xdiff/*.h)
-list(SORT SRC_GIT2)
-target_sources(git2internal PRIVATE ${SRC_GIT2})
-
-if(APPLE)
- # The old Secure Transport API has been deprecated in macOS 10.15.
- set_source_files_properties(streams/stransport.c PROPERTIES COMPILE_FLAGS -Wno-deprecated)
-endif()
-
-# the xdiff dependency is not (yet) warning-free, disable warnings as
-# errors for the xdiff sources until we've sorted them out
-if(MSVC)
- set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS -WX-)
- set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS -WX-)
- set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS -WX-)
- set_source_files_properties(xdiff/xmerge.c PROPERTIES COMPILE_FLAGS -WX-)
- set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS -WX-)
- set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS -WX-)
-else()
- set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
- set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
- set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
- set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
- set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
-endif()
-
-# Determine architecture of the machine
-if(CMAKE_SIZEOF_VOID_P EQUAL 8)
- set(GIT_ARCH_64 1)
-elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
- set(GIT_ARCH_32 1)
-elseif(CMAKE_SIZEOF_VOID_P)
- message(FATAL_ERROR "Unsupported architecture (pointer size is ${CMAKE_SIZEOF_VOID_P} bytes)")
-else()
- message(FATAL_ERROR "Unsupported architecture (CMAKE_SIZEOF_VOID_P is unset)")
-endif()
+#
+# Configure support
+#
configure_file(features.h.in git2/sys/features.h)
-ide_split_sources(git2internal)
-list(APPEND LIBGIT2_OBJECTS $ ${LIBGIT2_DEPENDENCY_OBJECTS})
+#
+# Include child projects
+#
-target_include_directories(git2internal PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${PROJECT_SOURCE_DIR}/include)
-target_include_directories(git2internal SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
+add_subdirectory(libgit2)
+add_subdirectory(util)
+if(BUILD_CLI)
+ add_subdirectory(cli)
+endif()
+
+# re-export these to the root so that peer projects (tests, fuzzers,
+# examples) can use them
set(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE)
set(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE)
set(LIBGIT2_DEPENDENCY_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES} PARENT_SCOPE)
set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE)
set(LIBGIT2_SYSTEM_INCLUDES ${LIBGIT2_SYSTEM_INCLUDES} PARENT_SCOPE)
set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE)
-
-if(XCODE_VERSION)
- # This is required for Xcode to actually link the libgit2 library
- # when using only object libraries.
- file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/dummy.c "")
- list(APPEND LIBGIT2_OBJECTS ${CMAKE_CURRENT_BINARY_DIR}/dummy.c)
-endif()
-
-# Compile and link libgit2
-add_library(git2 ${WIN_RC} ${LIBGIT2_OBJECTS})
-target_link_libraries(git2 ${LIBGIT2_SYSTEM_LIBS})
-
-set_target_properties(git2 PROPERTIES C_STANDARD 90)
-set_target_properties(git2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
-set_target_properties(git2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
-set_target_properties(git2 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
-
-# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
-# Win64+MSVC+static libs = linker error
-if(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
- set_target_properties(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
-endif()
-
-ide_split_sources(git2)
-
-if(SONAME)
- set_target_properties(git2 PROPERTIES VERSION ${libgit2_VERSION})
- set_target_properties(git2 PROPERTIES SOVERSION "${libgit2_VERSION_MAJOR}.${libgit2_VERSION_MINOR}")
- if(LIBGIT2_FILENAME)
- target_compile_definitions(git2 PRIVATE LIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
- set_target_properties(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
- elseif(DEFINED LIBGIT2_PREFIX)
- set_target_properties(git2 PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
- endif()
-endif()
-
-pkg_build_config(NAME libgit2
- VERSION ${libgit2_VERSION}
- DESCRIPTION "The git library, take 2"
- LIBS_SELF git2
- PRIVATE_LIBS ${LIBGIT2_PC_LIBS}
- REQUIRES ${LIBGIT2_PC_REQUIRES}
-)
-
-if(MSVC_IDE)
- # Precompiled headers
- set_target_properties(git2 PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
- set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
-endif()
-
-# Install
-install(TARGETS git2
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
- LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
- ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-)
-install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/git2 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
-install(FILES ${PROJECT_SOURCE_DIR}/include/git2.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 000000000..10b86c1dc
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,12 @@
+# libgit2 sources
+
+This is the source that makes up the core of libgit2 and its related
+projects.
+
+* `cli`
+ A git-compatible command-line interface that uses libgit2.
+* `libgit2`
+ This is the libgit2 project, a cross-platform, linkable library
+ implementation of Git that you can use in your application.
+* `util`
+ A shared utility library for these projects.
diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt
new file mode 100644
index 000000000..4f347e93f
--- /dev/null
+++ b/src/cli/CMakeLists.txt
@@ -0,0 +1,53 @@
+set(CLI_INCLUDES
+ "${libgit2_BINARY_DIR}/src"
+ "${libgit2_SOURCE_DIR}/src/util"
+ "${libgit2_SOURCE_DIR}/src/cli"
+ "${libgit2_SOURCE_DIR}/include")
+
+if(WIN32 AND NOT CYGWIN)
+ file(GLOB CLI_SRC_OS win32/*.c)
+ list(SORT CLI_SRC_OS)
+else()
+ file(GLOB CLI_SRC_OS unix/*.c)
+ list(SORT CLI_SRC_OS)
+endif()
+
+file(GLOB CLI_SRC_C *.c *.h)
+list(SORT CLI_SRC_C)
+
+#
+# The CLI currently needs to be statically linked against libgit2 because
+# the utility library uses libgit2's thread-local error buffers. TODO:
+# remove this dependency and allow us to dynamically link against libgit2.
+#
+
+if(BUILD_CLI STREQUAL "dynamic")
+ set(CLI_LIBGIT2_LIBRARY libgit2package)
+else()
+ set(CLI_LIBGIT2_OBJECTS $)
+endif()
+
+#
+# Compile and link the CLI
+#
+
+add_executable(git2_cli ${CLI_SRC_C} ${CLI_SRC_OS} ${CLI_OBJECTS}
+ $
+ ${CLI_LIBGIT2_OBJECTS}
+ ${LIBGIT2_DEPENDENCY_OBJECTS})
+target_link_libraries(git2_cli ${CLI_LIBGIT2_LIBRARY} ${LIBGIT2_SYSTEM_LIBS})
+
+set_target_properties(git2_cli PROPERTIES C_STANDARD 90)
+set_target_properties(git2_cli PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
+
+ide_split_sources(git2_cli)
+
+target_include_directories(git2_cli PRIVATE ${CLI_INCLUDES})
+
+if(MSVC_IDE)
+ # Precompiled headers
+ set_target_properties(git2_cli PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
+ set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
+endif()
+
+install(TARGETS git2_cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/cli/README.md b/src/cli/README.md
new file mode 100644
index 000000000..3087c39c4
--- /dev/null
+++ b/src/cli/README.md
@@ -0,0 +1,26 @@
+# cli
+
+A git-compatible command-line interface that uses libgit2.
+
+## Adding commands
+
+1. Individual commands have a `main`-like top-level entrypoint. For example:
+
+ ```c
+ int cmd_help(int argc, char **argv)
+ ```
+
+ Although this is the same signature as `main`, commands are not built as
+ individual standalone executables, they'll be linked into the main cli.
+ (Though there may be an option for command executables to be built as
+ standalone executables in the future.)
+
+2. Commands are prototyped in `cmd.h` and added to `main.c`'s list of
+ commands (`cli_cmds[]`). Commands should be specified with their name,
+ entrypoint and a brief description that can be printed in `git help`.
+ This is done because commands are linked into the main cli.
+
+3. Commands should accept a `--help` option that displays their help
+ information. This will be shown when a user runs ` --help` and
+ when a user runs `help `.
+
diff --git a/src/hash/sha1/generic.h b/src/cli/cli.h
similarity index 52%
rename from src/hash/sha1/generic.h
rename to src/cli/cli.h
index 53fc0823e..7dede6785 100644
--- a/src/hash/sha1/generic.h
+++ b/src/cli/cli.h
@@ -5,15 +5,16 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_hash_sha1_generic_h__
-#define INCLUDE_hash_sha1_generic_h__
+#ifndef CLI_cli_h__
+#define CLI_cli_h__
-#include "hash/sha1.h"
+#define PROGRAM_NAME "git2"
-struct git_hash_sha1_ctx {
- uint64_t size;
- unsigned int H[5];
- unsigned int W[16];
-};
+#include "git2_util.h"
-#endif
+#include "error.h"
+#include "opt.h"
+#include "opt_usage.h"
+#include "sighandler.h"
+
+#endif /* CLI_cli_h__ */
diff --git a/src/cli/cmd.c b/src/cli/cmd.c
new file mode 100644
index 000000000..2a7e71cdb
--- /dev/null
+++ b/src/cli/cmd.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "cli.h"
+#include "cmd.h"
+
+const cli_cmd_spec *cli_cmd_spec_byname(const char *name)
+{
+ const cli_cmd_spec *cmd;
+
+ for (cmd = cli_cmds; cmd->name; cmd++) {
+ if (!strcmp(cmd->name, name))
+ return cmd;
+ }
+
+ return NULL;
+}
diff --git a/src/cli/cmd.h b/src/cli/cmd.h
new file mode 100644
index 000000000..8b1a1b38f
--- /dev/null
+++ b/src/cli/cmd.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef CLI_cmd_h__
+#define CLI_cmd_h__
+
+/* Command definitions */
+typedef struct {
+ const char *name;
+ int (*fn)(int argc, char **argv);
+ const char *desc;
+} cli_cmd_spec;
+
+/* Options that are common to all commands (eg --help, --git-dir) */
+extern const cli_opt_spec cli_common_opts[];
+
+/* All the commands supported by the CLI */
+extern const cli_cmd_spec cli_cmds[];
+
+/* Find a command by name */
+extern const cli_cmd_spec *cli_cmd_spec_byname(const char *name);
+
+/* Commands */
+extern int cmd_cat_file(int argc, char **argv);
+extern int cmd_clone(int argc, char **argv);
+extern int cmd_hash_object(int argc, char **argv);
+extern int cmd_help(int argc, char **argv);
+
+#endif /* CLI_cmd_h__ */
diff --git a/src/cli/cmd_cat_file.c b/src/cli/cmd_cat_file.c
new file mode 100644
index 000000000..fb53a722b
--- /dev/null
+++ b/src/cli/cmd_cat_file.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include
+#include "cli.h"
+#include "cmd.h"
+
+#define COMMAND_NAME "cat-file"
+
+typedef enum {
+ DISPLAY_CONTENT = 0,
+ DISPLAY_EXISTS,
+ DISPLAY_PRETTY,
+ DISPLAY_SIZE,
+ DISPLAY_TYPE
+} display_t;
+
+static int show_help;
+static int display = DISPLAY_CONTENT;
+static char *type_name, *object_spec;
+
+static const cli_opt_spec opts[] = {
+ { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL,
+ "display help about the " COMMAND_NAME " command" },
+
+ { CLI_OPT_TYPE_SWITCH, NULL, 't', &display, DISPLAY_TYPE,
+ CLI_OPT_USAGE_REQUIRED, NULL, "display the type of the object" },
+ { CLI_OPT_TYPE_SWITCH, NULL, 's', &display, DISPLAY_SIZE,
+ CLI_OPT_USAGE_CHOICE, NULL, "display the size of the object" },
+ { CLI_OPT_TYPE_SWITCH, NULL, 'e', &display, DISPLAY_EXISTS,
+ CLI_OPT_USAGE_CHOICE, NULL, "displays nothing unless the object is corrupt" },
+ { CLI_OPT_TYPE_SWITCH, NULL, 'p', &display, DISPLAY_PRETTY,
+ CLI_OPT_USAGE_CHOICE, NULL, "pretty-print the object" },
+ { CLI_OPT_TYPE_ARG, "type", 0, &type_name, 0,
+ CLI_OPT_USAGE_CHOICE, "type", "the type of object to display" },
+ { CLI_OPT_TYPE_ARG, "object", 0, &object_spec, 0,
+ CLI_OPT_USAGE_REQUIRED, "object", "the object to display" },
+ { 0 },
+};
+
+static void print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Display the content for the given object in the repository.\n");
+ printf("\n");
+
+ printf("Options:\n");
+
+ cli_opt_help_fprint(stdout, opts);
+}
+
+static int print_odb(git_object *object, display_t display)
+{
+ git_odb *odb = NULL;
+ git_odb_object *odb_object = NULL;
+ const unsigned char *content;
+ git_object_size_t size;
+ int ret = 0;
+
+ /*
+ * Our parsed blobs retain the raw content; all other objects are
+ * parsed into a working representation. To get the raw content,
+ * we need to do an ODB lookup. (Thankfully, this should be cached
+ * in-memory from our last call.)
+ */
+ if (git_object_type(object) == GIT_OBJECT_BLOB) {
+ content = git_blob_rawcontent((git_blob *)object);
+ size = git_blob_rawsize((git_blob *)object);
+ } else {
+ if (git_repository_odb(&odb, git_object_owner(object)) < 0 ||
+ git_odb_read(&odb_object, odb, git_object_id(object)) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ content = git_odb_object_data(odb_object);
+ size = git_odb_object_size(odb_object);
+ }
+
+ switch (display) {
+ case DISPLAY_SIZE:
+ if (printf("%" PRIu64 "\n", size) < 0)
+ ret = cli_error_os();
+ break;
+ case DISPLAY_CONTENT:
+ if (p_write(fileno(stdout), content, (size_t)size) < 0)
+ ret = cli_error_os();
+ break;
+ default:
+ GIT_ASSERT(0);
+ }
+
+done:
+ git_odb_object_free(odb_object);
+ git_odb_free(odb);
+ return ret;
+}
+
+static int print_type(git_object *object)
+{
+ if (printf("%s\n", git_object_type2string(git_object_type(object))) < 0)
+ return cli_error_os();
+
+ return 0;
+}
+
+static int print_pretty(git_object *object)
+{
+ const git_tree_entry *entry;
+ size_t i, count;
+
+ /*
+ * Only trees are stored in an unreadable format and benefit from
+ * pretty-printing.
+ */
+ if (git_object_type(object) != GIT_OBJECT_TREE)
+ return print_odb(object, DISPLAY_CONTENT);
+
+ for (i = 0, count = git_tree_entrycount((git_tree *)object); i < count; i++) {
+ entry = git_tree_entry_byindex((git_tree *)object, i);
+
+ if (printf("%06o %s %s\t%s\n",
+ git_tree_entry_filemode_raw(entry),
+ git_object_type2string(git_tree_entry_type(entry)),
+ git_oid_tostr_s(git_tree_entry_id(entry)),
+ git_tree_entry_name(entry)) < 0)
+ return cli_error_os();
+ }
+
+ return 0;
+}
+
+int cmd_cat_file(int argc, char **argv)
+{
+ git_repository *repo = NULL;
+ git_object *object = NULL;
+ git_object_t type;
+ cli_opt invalid_opt;
+ int giterr, ret = 0;
+
+ if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
+ return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
+
+ if (show_help) {
+ print_help();
+ return 0;
+ }
+
+ if (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0)
+ return cli_error_git();
+
+ if ((giterr = git_revparse_single(&object, repo, object_spec)) < 0) {
+ if (display == DISPLAY_EXISTS && giterr == GIT_ENOTFOUND)
+ ret = 1;
+ else
+ ret = cli_error_git();
+
+ goto done;
+ }
+
+ if (type_name) {
+ git_object *peeled;
+
+ if ((type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID) {
+ ret = cli_error_usage("invalid object type '%s'", type_name);
+ goto done;
+ }
+
+ if (git_object_peel(&peeled, object, type) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ git_object_free(object);
+ object = peeled;
+ }
+
+ switch (display) {
+ case DISPLAY_EXISTS:
+ ret = 0;
+ break;
+ case DISPLAY_TYPE:
+ ret = print_type(object);
+ break;
+ case DISPLAY_PRETTY:
+ ret = print_pretty(object);
+ break;
+ default:
+ ret = print_odb(object, display);
+ break;
+ }
+
+done:
+ git_object_free(object);
+ git_repository_free(repo);
+ return ret;
+}
diff --git a/src/cli/cmd_clone.c b/src/cli/cmd_clone.c
new file mode 100644
index 000000000..a382b5875
--- /dev/null
+++ b/src/cli/cmd_clone.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include
+#include
+#include "cli.h"
+#include "cmd.h"
+#include "error.h"
+#include "sighandler.h"
+#include "progress.h"
+
+#include "fs_path.h"
+#include "futils.h"
+
+#define COMMAND_NAME "clone"
+
+static char *branch, *remote_path, *local_path;
+static int show_help, quiet, checkout = 1, bare;
+static bool local_path_exists;
+static cli_progress progress = CLI_PROGRESS_INIT;
+
+static const cli_opt_spec opts[] = {
+ { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL,
+ "display help about the " COMMAND_NAME " command" },
+
+ { CLI_OPT_TYPE_SWITCH, "quiet", 'q', &quiet, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "display the type of the object" },
+ { CLI_OPT_TYPE_SWITCH, "no-checkout", 'n', &checkout, 0,
+ CLI_OPT_USAGE_DEFAULT, NULL, "don't checkout HEAD" },
+ { CLI_OPT_TYPE_SWITCH, "bare", 0, &bare, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "don't create a working directory" },
+ { CLI_OPT_TYPE_VALUE, "branch", 'b', &branch, 0,
+ CLI_OPT_USAGE_DEFAULT, "name", "branch to check out" },
+ { CLI_OPT_TYPE_LITERAL },
+ { CLI_OPT_TYPE_ARG, "repository", 0, &remote_path, 0,
+ CLI_OPT_USAGE_REQUIRED, "repository", "repository path" },
+ { CLI_OPT_TYPE_ARG, "directory", 0, &local_path, 0,
+ CLI_OPT_USAGE_DEFAULT, "directory", "directory to clone into" },
+ { 0 }
+};
+
+static void print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Clone a repository into a new directory.\n");
+ printf("\n");
+
+ printf("Options:\n");
+
+ cli_opt_help_fprint(stdout, opts);
+}
+
+static char *compute_local_path(const char *orig_path)
+{
+ const char *slash;
+ char *local_path;
+
+ if ((slash = strrchr(orig_path, '/')) == NULL &&
+ (slash = strrchr(orig_path, '\\')) == NULL)
+ local_path = git__strdup(orig_path);
+ else
+ local_path = git__strdup(slash + 1);
+
+ return local_path;
+}
+
+static bool validate_local_path(const char *path)
+{
+ if (!git_fs_path_exists(path))
+ return false;
+
+ if (!git_fs_path_isdir(path) || !git_fs_path_is_empty_dir(path)) {
+ fprintf(stderr, "fatal: destination path '%s' already exists and is not an empty directory.\n",
+ path);
+ exit(128);
+ }
+
+ return true;
+}
+
+static void cleanup(void)
+{
+ int rmdir_flags = GIT_RMDIR_REMOVE_FILES;
+
+ cli_progress_abort(&progress);
+
+ if (local_path_exists)
+ rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
+
+ if (!git_fs_path_isdir(local_path))
+ return;
+
+ git_futils_rmdir_r(local_path, NULL, rmdir_flags);
+}
+
+static void interrupt_cleanup(void)
+{
+ cleanup();
+ exit(130);
+}
+
+int cmd_clone(int argc, char **argv)
+{
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_repository *repo = NULL;
+ cli_opt invalid_opt;
+ char *computed_path = NULL;
+ int ret = 0;
+
+ if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
+ return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
+
+ if (show_help) {
+ print_help();
+ return 0;
+ }
+
+ if (!remote_path) {
+ ret = cli_error_usage("you must specify a repository to clone");
+ goto done;
+ }
+
+ if (bare)
+ clone_opts.bare = 1;
+
+ if (branch)
+ clone_opts.checkout_branch = branch;
+
+ if (!checkout)
+ clone_opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
+
+ if (!local_path)
+ local_path = computed_path = compute_local_path(remote_path);
+
+ local_path_exists = validate_local_path(local_path);
+
+ cli_sighandler_set_interrupt(interrupt_cleanup);
+
+ if (!local_path_exists &&
+ git_futils_mkdir(local_path, 0777, 0) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ if (!quiet) {
+ clone_opts.fetch_opts.callbacks.sideband_progress = cli_progress_fetch_sideband;
+ clone_opts.fetch_opts.callbacks.transfer_progress = cli_progress_fetch_transfer;
+ clone_opts.fetch_opts.callbacks.payload = &progress;
+
+ clone_opts.checkout_opts.progress_cb = cli_progress_checkout;
+ clone_opts.checkout_opts.progress_payload = &progress;
+
+ printf("Cloning into '%s'...\n", local_path);
+ }
+
+ if (git_clone(&repo, remote_path, local_path, &clone_opts) < 0) {
+ cleanup();
+ ret = cli_error_git();
+ goto done;
+ }
+
+ cli_progress_finish(&progress);
+
+done:
+ cli_progress_dispose(&progress);
+ git__free(computed_path);
+ git_repository_free(repo);
+ return ret;
+}
diff --git a/src/cli/cmd_hash_object.c b/src/cli/cmd_hash_object.c
new file mode 100644
index 000000000..5cfe9146a
--- /dev/null
+++ b/src/cli/cmd_hash_object.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include
+#include "cli.h"
+#include "cmd.h"
+
+#include "futils.h"
+
+#define COMMAND_NAME "hash-object"
+
+static int show_help;
+static char *type_name;
+static int write_object, read_stdin, literally;
+static char **filenames;
+
+static const cli_opt_spec opts[] = {
+ { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL,
+ "display help about the " COMMAND_NAME " command" },
+
+ { CLI_OPT_TYPE_VALUE, NULL, 't', &type_name, 0,
+ CLI_OPT_USAGE_DEFAULT, "type", "the type of object to hash (default: \"blob\")" },
+ { CLI_OPT_TYPE_SWITCH, NULL, 'w', &write_object, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "write the object to the object database" },
+ { CLI_OPT_TYPE_SWITCH, "literally", 0, &literally, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "do not validate the object contents" },
+ { CLI_OPT_TYPE_SWITCH, "stdin", 0, &read_stdin, 1,
+ CLI_OPT_USAGE_REQUIRED, NULL, "read content from stdin" },
+ { CLI_OPT_TYPE_ARGS, "file", 0, &filenames, 0,
+ CLI_OPT_USAGE_CHOICE, "file", "the file (or files) to read and hash" },
+ { 0 },
+};
+
+static void print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Compute the object ID for a given file and optionally write that file\nto the object database.\n");
+ printf("\n");
+
+ printf("Options:\n");
+
+ cli_opt_help_fprint(stdout, opts);
+}
+
+static int hash_buf(git_odb *odb, git_str *buf, git_object_t type)
+{
+ git_oid oid;
+
+ if (!literally) {
+ int valid = 0;
+
+ if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, type) < 0 || !valid)
+ return cli_error_git();
+ }
+
+ if (write_object) {
+ if (git_odb_write(&oid, odb, buf->ptr, buf->size, type) < 0)
+ return cli_error_git();
+ } else {
+ if (git_odb_hash(&oid, buf->ptr, buf->size, type) < 0)
+ return cli_error_git();
+ }
+
+ if (printf("%s\n", git_oid_tostr_s(&oid)) < 0)
+ return cli_error_os();
+
+ return 0;
+}
+
+int cmd_hash_object(int argc, char **argv)
+{
+ git_repository *repo = NULL;
+ git_odb *odb = NULL;
+ git_str buf = GIT_STR_INIT;
+ cli_opt invalid_opt;
+ git_object_t type = GIT_OBJECT_BLOB;
+ char **filename;
+ int ret = 0;
+
+ if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
+ return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
+
+ if (show_help) {
+ print_help();
+ return 0;
+ }
+
+ if (type_name && (type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID)
+ return cli_error_usage("invalid object type '%s'", type_name);
+
+ if (write_object &&
+ (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0 ||
+ git_repository_odb(&odb, repo) < 0)) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ /*
+ * TODO: we're reading blobs, we shouldn't pull them all into main
+ * memory, we should just stream them into the odb instead.
+ * (Or create a `git_odb_writefile` API.)
+ */
+ if (read_stdin) {
+ if (git_futils_readbuffer_fd_full(&buf, fileno(stdin)) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ if ((ret = hash_buf(odb, &buf, type)) != 0)
+ goto done;
+ } else {
+ for (filename = filenames; *filename; filename++) {
+ if (git_futils_readbuffer(&buf, *filename) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ if ((ret = hash_buf(odb, &buf, type)) != 0)
+ goto done;
+ }
+ }
+
+done:
+ git_str_dispose(&buf);
+ git_odb_free(odb);
+ git_repository_free(repo);
+ return ret;
+}
diff --git a/src/cli/cmd_help.c b/src/cli/cmd_help.c
new file mode 100644
index 000000000..7ee982242
--- /dev/null
+++ b/src/cli/cmd_help.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include
+#include
+#include "cli.h"
+#include "cmd.h"
+
+#define COMMAND_NAME "help"
+
+static char *command;
+static int show_help;
+
+static const cli_opt_spec opts[] = {
+ { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_HIDDEN, NULL, "display help about the help command" },
+ { CLI_OPT_TYPE_ARG, "command", 0, &command, 0,
+ CLI_OPT_USAGE_DEFAULT, "command", "the command to show help for" },
+ { 0 },
+};
+
+static int print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Display help information about %s. If a command is specified, help\n", PROGRAM_NAME);
+ printf("about that command will be shown. Otherwise, general information about\n");
+ printf("%s will be shown, including the commands available.\n", PROGRAM_NAME);
+
+ return 0;
+}
+
+static int print_commands(void)
+{
+ const cli_cmd_spec *cmd;
+
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, NULL, cli_common_opts);
+ printf("\n");
+
+ printf("These are the %s commands available:\n\n", PROGRAM_NAME);
+
+ for (cmd = cli_cmds; cmd->name; cmd++)
+ printf(" %-11s %s\n", cmd->name, cmd->desc);
+
+ printf("\nSee '%s help ' for more information on a specific command.\n", PROGRAM_NAME);
+
+ return 0;
+}
+
+int cmd_help(int argc, char **argv)
+{
+ char *fake_args[2];
+ const cli_cmd_spec *cmd;
+ cli_opt invalid_opt;
+
+ if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
+ return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
+
+ /* Show the meta-help */
+ if (show_help)
+ return print_help();
+
+ /* We were not asked to show help for a specific command. */
+ if (!command)
+ return print_commands();
+
+ /*
+ * If we were asked for help for a command (eg, `help `),
+ * delegate back to that command's `--help` option. This lets
+ * commands own their help. Emulate the command-line arguments
+ * that would invoke ` --help` and invoke that command.
+ */
+ fake_args[0] = command;
+ fake_args[1] = "--help";
+
+ if ((cmd = cli_cmd_spec_byname(command)) == NULL)
+ return cli_error("'%s' is not a %s command. See '%s help'.",
+ command, PROGRAM_NAME, PROGRAM_NAME);
+
+ return cmd->fn(2, fake_args);
+}
diff --git a/src/cli/error.h b/src/cli/error.h
new file mode 100644
index 000000000..cce7a54c0
--- /dev/null
+++ b/src/cli/error.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef CLI_error_h__
+#define CLI_error_h__
+
+#include "cli.h"
+#include
+
+#define CLI_EXIT_OK 0
+#define CLI_EXIT_ERROR 1
+#define CLI_EXIT_OS 128
+#define CLI_EXIT_GIT 128
+#define CLI_EXIT_USAGE 129
+
+#define cli_error__print(fmt) do { \
+ va_list ap; \
+ va_start(ap, fmt); \
+ fprintf(stderr, "%s: ", PROGRAM_NAME); \
+ vfprintf(stderr, fmt, ap); \
+ fprintf(stderr, "\n"); \
+ va_end(ap); \
+ } while(0)
+
+GIT_INLINE(int) cli_error(const char *fmt, ...)
+{
+ cli_error__print(fmt);
+ return CLI_EXIT_ERROR;
+}
+
+GIT_INLINE(int) cli_error_usage(const char *fmt, ...)
+{
+ cli_error__print(fmt);
+ return CLI_EXIT_USAGE;
+}
+
+GIT_INLINE(int) cli_error_git(void)
+{
+ const git_error *err = git_error_last();
+ fprintf(stderr, "%s: %s\n", PROGRAM_NAME,
+ err ? err->message : "unknown error");
+ return CLI_EXIT_GIT;
+}
+
+#define cli_error_os() (perror(PROGRAM_NAME), CLI_EXIT_OS)
+
+#endif /* CLI_error_h__ */
diff --git a/src/cli/main.c b/src/cli/main.c
new file mode 100644
index 000000000..cbfc50eec
--- /dev/null
+++ b/src/cli/main.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include
+#include
+#include "cli.h"
+#include "cmd.h"
+
+static int show_help = 0;
+static int show_version = 0;
+static char *command = NULL;
+static char **args = NULL;
+
+const cli_opt_spec cli_common_opts[] = {
+ { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "display help information" },
+ { CLI_OPT_TYPE_SWITCH, "version", 0, &show_version, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "display the version" },
+ { CLI_OPT_TYPE_ARG, "command", 0, &command, 0,
+ CLI_OPT_USAGE_REQUIRED, "command", "the command to run" },
+ { CLI_OPT_TYPE_ARGS, "args", 0, &args, 0,
+ CLI_OPT_USAGE_DEFAULT, "args", "arguments for the command" },
+ { 0 }
+};
+
+const cli_cmd_spec cli_cmds[] = {
+ { "cat-file", cmd_cat_file, "Display an object in the repository" },
+ { "clone", cmd_clone, "Clone a repository into a new directory" },
+ { "hash-object", cmd_hash_object, "Hash a raw object and product its object ID" },
+ { "help", cmd_help, "Display help information" },
+ { NULL }
+};
+
+int main(int argc, char **argv)
+{
+ const cli_cmd_spec *cmd;
+ cli_opt_parser optparser;
+ cli_opt opt;
+ char *help_args[3] = { NULL };
+ int help_args_len;
+ int args_len = 0;
+ int ret = 0;
+
+ if (git_libgit2_init() < 0) {
+ cli_error("failed to initialize libgit2");
+ exit(CLI_EXIT_GIT);
+ }
+
+ cli_opt_parser_init(&optparser, cli_common_opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU);
+
+ /* Parse the top-level (common) options and command information */
+ while (cli_opt_parser_next(&opt, &optparser)) {
+ if (!opt.spec) {
+ cli_opt_status_fprint(stderr, PROGRAM_NAME, &opt);
+ cli_opt_usage_fprint(stderr, PROGRAM_NAME, NULL, cli_common_opts);
+ ret = CLI_EXIT_USAGE;
+ goto done;
+ }
+
+ /*
+ * When we see a command, stop parsing and capture the
+ * remaining arguments as args for the command itself.
+ */
+ if (command) {
+ args = &argv[optparser.idx];
+ args_len = (int)(argc - optparser.idx);
+ break;
+ }
+ }
+
+ if (show_version) {
+ printf("%s version %s\n", PROGRAM_NAME, LIBGIT2_VERSION);
+ goto done;
+ }
+
+ /*
+ * If `--help ` is specified, delegate to that command's
+ * `--help` option. If no command is specified, run the `help`
+ * command. Do this by updating the args to emulate that behavior.
+ */
+ if (!command || show_help) {
+ help_args[0] = command ? (char *)command : "help";
+ help_args[1] = command ? "--help" : NULL;
+ help_args_len = command ? 2 : 1;
+
+ command = help_args[0];
+ args = help_args;
+ args_len = help_args_len;
+ }
+
+ if ((cmd = cli_cmd_spec_byname(command)) == NULL) {
+ ret = cli_error("'%s' is not a %s command. See '%s help'.",
+ command, PROGRAM_NAME, PROGRAM_NAME);
+ goto done;
+ }
+
+ ret = cmd->fn(args_len, args);
+
+done:
+ git_libgit2_shutdown();
+ return ret;
+}
diff --git a/src/cli/opt.c b/src/cli/opt.c
new file mode 100644
index 000000000..72df5877f
--- /dev/null
+++ b/src/cli/opt.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright (c), Edward Thomson
+ * All rights reserved.
+ *
+ * This file is part of adopt, distributed under the MIT license.
+ * For full terms and conditions, see the included LICENSE file.
+ *
+ * THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT.
+ *
+ * This file was produced by using the `rename.pl` script included with
+ * adopt. The command-line specified was:
+ *
+ * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "cli.h"
+#include "opt.h"
+
+#ifdef _WIN32
+# include
+#else
+# include
+# include
+#endif
+
+#ifdef _MSC_VER
+# define alloca _alloca
+#endif
+
+#define spec_is_option_type(x) \
+ ((x)->type == CLI_OPT_TYPE_BOOL || \
+ (x)->type == CLI_OPT_TYPE_SWITCH || \
+ (x)->type == CLI_OPT_TYPE_VALUE)
+
+GIT_INLINE(const cli_opt_spec *) spec_for_long(
+ int *is_negated,
+ int *has_value,
+ const char **value,
+ const cli_opt_parser *parser,
+ const char *arg)
+{
+ const cli_opt_spec *spec;
+ char *eql;
+ size_t eql_pos;
+
+ eql = strchr(arg, '=');
+ eql_pos = (eql = strchr(arg, '=')) ? (size_t)(eql - arg) : strlen(arg);
+
+ for (spec = parser->specs; spec->type; ++spec) {
+ /* Handle -- (everything after this is literal) */
+ if (spec->type == CLI_OPT_TYPE_LITERAL && arg[0] == '\0')
+ return spec;
+
+ /* Handle --no-option arguments for bool types */
+ if (spec->type == CLI_OPT_TYPE_BOOL &&
+ strncmp(arg, "no-", 3) == 0 &&
+ strcmp(arg + 3, spec->name) == 0) {
+ *is_negated = 1;
+ return spec;
+ }
+
+ /* Handle the typical --option arguments */
+ if (spec_is_option_type(spec) &&
+ spec->name &&
+ strcmp(arg, spec->name) == 0)
+ return spec;
+
+ /* Handle --option=value arguments */
+ if (spec->type == CLI_OPT_TYPE_VALUE &&
+ eql &&
+ strncmp(arg, spec->name, eql_pos) == 0 &&
+ spec->name[eql_pos] == '\0') {
+ *has_value = 1;
+ *value = arg[eql_pos + 1] ? &arg[eql_pos + 1] : NULL;
+ return spec;
+ }
+ }
+
+ return NULL;
+}
+
+GIT_INLINE(const cli_opt_spec *) spec_for_short(
+ const char **value,
+ const cli_opt_parser *parser,
+ const char *arg)
+{
+ const cli_opt_spec *spec;
+
+ for (spec = parser->specs; spec->type; ++spec) {
+ /* Handle -svalue short options with a value */
+ if (spec->type == CLI_OPT_TYPE_VALUE &&
+ arg[0] == spec->alias &&
+ arg[1] != '\0') {
+ *value = &arg[1];
+ return spec;
+ }
+
+ /* Handle typical -s short options */
+ if (arg[0] == spec->alias) {
+ *value = NULL;
+ return spec;
+ }
+ }
+
+ return NULL;
+}
+
+GIT_INLINE(const cli_opt_spec *) spec_for_arg(cli_opt_parser *parser)
+{
+ const cli_opt_spec *spec;
+ size_t args = 0;
+
+ for (spec = parser->specs; spec->type; ++spec) {
+ if (spec->type == CLI_OPT_TYPE_ARG) {
+ if (args == parser->arg_idx) {
+ parser->arg_idx++;
+ return spec;
+ }
+
+ args++;
+ }
+
+ if (spec->type == CLI_OPT_TYPE_ARGS && args == parser->arg_idx)
+ return spec;
+ }
+
+ return NULL;
+}
+
+GIT_INLINE(int) spec_is_choice(const cli_opt_spec *spec)
+{
+ return ((spec + 1)->type &&
+ ((spec + 1)->usage & CLI_OPT_USAGE_CHOICE));
+}
+
+/*
+ * If we have a choice with switches and bare arguments, and we see
+ * the switch, then we no longer expect the bare argument.
+ */
+GIT_INLINE(void) consume_choices(const cli_opt_spec *spec, cli_opt_parser *parser)
+{
+ /* back up to the beginning of the choices */
+ while (spec->type && (spec->usage & CLI_OPT_USAGE_CHOICE))
+ --spec;
+
+ if (!spec_is_choice(spec))
+ return;
+
+ do {
+ if (spec->type == CLI_OPT_TYPE_ARG)
+ parser->arg_idx++;
+ ++spec;
+ } while(spec->type && (spec->usage & CLI_OPT_USAGE_CHOICE));
+}
+
+static cli_opt_status_t parse_long(cli_opt *opt, cli_opt_parser *parser)
+{
+ const cli_opt_spec *spec;
+ char *arg = parser->args[parser->idx++];
+ const char *value = NULL;
+ int is_negated = 0, has_value = 0;
+
+ opt->arg = arg;
+
+ if ((spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2])) == NULL) {
+ opt->spec = NULL;
+ opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION;
+ goto done;
+ }
+
+ opt->spec = spec;
+
+ /* Future options parsed as literal */
+ if (spec->type == CLI_OPT_TYPE_LITERAL)
+ parser->in_literal = 1;
+
+ /* --bool or --no-bool */
+ else if (spec->type == CLI_OPT_TYPE_BOOL && spec->value)
+ *((int *)spec->value) = !is_negated;
+
+ /* --accumulate */
+ else if (spec->type == CLI_OPT_TYPE_ACCUMULATOR && spec->value)
+ *((int *)spec->value) += spec->switch_value ? spec->switch_value : 1;
+
+ /* --switch */
+ else if (spec->type == CLI_OPT_TYPE_SWITCH && spec->value)
+ *((int *)spec->value) = spec->switch_value;
+
+ /* Parse values as "--foo=bar" or "--foo bar" */
+ else if (spec->type == CLI_OPT_TYPE_VALUE) {
+ if (has_value)
+ opt->value = (char *)value;
+ else if ((parser->idx + 1) <= parser->args_len)
+ opt->value = parser->args[parser->idx++];
+
+ if (spec->value)
+ *((char **)spec->value) = opt->value;
+ }
+
+ /* Required argument was not provided */
+ if (spec->type == CLI_OPT_TYPE_VALUE &&
+ !opt->value &&
+ !(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL))
+ opt->status = CLI_OPT_STATUS_MISSING_VALUE;
+ else
+ opt->status = CLI_OPT_STATUS_OK;
+
+ consume_choices(opt->spec, parser);
+
+done:
+ return opt->status;
+}
+
+static cli_opt_status_t parse_short(cli_opt *opt, cli_opt_parser *parser)
+{
+ const cli_opt_spec *spec;
+ char *arg = parser->args[parser->idx++];
+ const char *value;
+
+ opt->arg = arg;
+
+ if ((spec = spec_for_short(&value, parser, &arg[1 + parser->in_short])) == NULL) {
+ opt->spec = NULL;
+ opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION;
+ goto done;
+ }
+
+ opt->spec = spec;
+
+ if (spec->type == CLI_OPT_TYPE_BOOL && spec->value)
+ *((int *)spec->value) = 1;
+
+ else if (spec->type == CLI_OPT_TYPE_ACCUMULATOR && spec->value)
+ *((int *)spec->value) += spec->switch_value ? spec->switch_value : 1;
+
+ else if (spec->type == CLI_OPT_TYPE_SWITCH && spec->value)
+ *((int *)spec->value) = spec->switch_value;
+
+ /* Parse values as "-ifoo" or "-i foo" */
+ else if (spec->type == CLI_OPT_TYPE_VALUE) {
+ if (value)
+ opt->value = (char *)value;
+ else if ((parser->idx + 1) <= parser->args_len)
+ opt->value = parser->args[parser->idx++];
+
+ if (spec->value)
+ *((char **)spec->value) = opt->value;
+ }
+
+ /*
+ * Handle compressed short arguments, like "-fbcd"; see if there's
+ * another character after the one we processed. If not, advance
+ * the parser index.
+ */
+ if (spec->type != CLI_OPT_TYPE_VALUE && arg[2 + parser->in_short] != '\0') {
+ parser->in_short++;
+ parser->idx--;
+ } else {
+ parser->in_short = 0;
+ }
+
+ /* Required argument was not provided */
+ if (spec->type == CLI_OPT_TYPE_VALUE && !opt->value)
+ opt->status = CLI_OPT_STATUS_MISSING_VALUE;
+ else
+ opt->status = CLI_OPT_STATUS_OK;
+
+ consume_choices(opt->spec, parser);
+
+done:
+ return opt->status;
+}
+
+static cli_opt_status_t parse_arg(cli_opt *opt, cli_opt_parser *parser)
+{
+ const cli_opt_spec *spec = spec_for_arg(parser);
+
+ opt->spec = spec;
+ opt->arg = parser->args[parser->idx];
+
+ if (!spec) {
+ parser->idx++;
+ opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION;
+ } else if (spec->type == CLI_OPT_TYPE_ARGS) {
+ if (spec->value)
+ *((char ***)spec->value) = &parser->args[parser->idx];
+
+ /*
+ * We have started a list of arguments; the remainder of
+ * given arguments need not be examined.
+ */
+ parser->in_args = (parser->args_len - parser->idx);
+ parser->idx = parser->args_len;
+ opt->args_len = parser->in_args;
+ opt->status = CLI_OPT_STATUS_OK;
+ } else {
+ if (spec->value)
+ *((char **)spec->value) = parser->args[parser->idx];
+
+ parser->idx++;
+ opt->status = CLI_OPT_STATUS_OK;
+ }
+
+ return opt->status;
+}
+
+static int support_gnu_style(unsigned int flags)
+{
+ if ((flags & CLI_OPT_PARSE_FORCE_GNU) != 0)
+ return 1;
+
+ if ((flags & CLI_OPT_PARSE_GNU) == 0)
+ return 0;
+
+ /* TODO: Windows */
+#if defined(_WIN32) && defined(UNICODE)
+ if (_wgetenv(L"POSIXLY_CORRECT") != NULL)
+ return 0;
+#else
+ if (getenv("POSIXLY_CORRECT") != NULL)
+ return 0;
+#endif
+
+ return 1;
+}
+
+void cli_opt_parser_init(
+ cli_opt_parser *parser,
+ const cli_opt_spec specs[],
+ char **args,
+ size_t args_len,
+ unsigned int flags)
+{
+ assert(parser);
+
+ memset(parser, 0x0, sizeof(cli_opt_parser));
+
+ parser->specs = specs;
+ parser->args = args;
+ parser->args_len = args_len;
+ parser->flags = flags;
+
+ parser->needs_sort = support_gnu_style(flags);
+}
+
+GIT_INLINE(const cli_opt_spec *) spec_for_sort(
+ int *needs_value,
+ const cli_opt_parser *parser,
+ const char *arg)
+{
+ int is_negated, has_value = 0;
+ const char *value;
+ const cli_opt_spec *spec = NULL;
+ size_t idx = 0;
+
+ *needs_value = 0;
+
+ if (strncmp(arg, "--", 2) == 0) {
+ spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2]);
+ *needs_value = !has_value;
+ }
+
+ else if (strncmp(arg, "-", 1) == 0) {
+ spec = spec_for_short(&value, parser, &arg[1]);
+
+ /*
+ * Advance through compressed short arguments to see if
+ * the last one has a value, eg "-xvffilename".
+ */
+ while (spec && !value && arg[1 + ++idx] != '\0')
+ spec = spec_for_short(&value, parser, &arg[1 + idx]);
+
+ *needs_value = (value == NULL);
+ }
+
+ return spec;
+}
+
+/*
+ * Some parsers allow for handling arguments like "file1 --help file2";
+ * this is done by re-sorting the arguments in-place; emulate that.
+ */
+static int sort_gnu_style(cli_opt_parser *parser)
+{
+ size_t i, j, insert_idx = parser->idx, offset;
+ const cli_opt_spec *spec;
+ char *option, *value;
+ int needs_value, changed = 0;
+
+ parser->needs_sort = 0;
+
+ for (i = parser->idx; i < parser->args_len; i++) {
+ spec = spec_for_sort(&needs_value, parser, parser->args[i]);
+
+ /* Not a "-" or "--" prefixed option. No change. */
+ if (!spec)
+ continue;
+
+ /* A "--" alone means remaining args are literal. */
+ if (spec->type == CLI_OPT_TYPE_LITERAL)
+ break;
+
+ option = parser->args[i];
+
+ /*
+ * If the argument is a value type and doesn't already
+ * have a value (eg "--foo=bar" or "-fbar") then we need
+ * to copy the next argument as its value.
+ */
+ if (spec->type == CLI_OPT_TYPE_VALUE && needs_value) {
+ /*
+ * A required value is not provided; set parser
+ * index to this value so that we fail on it.
+ */
+ if (i + 1 >= parser->args_len) {
+ parser->idx = i;
+ return 1;
+ }
+
+ value = parser->args[i + 1];
+ offset = 1;
+ } else {
+ value = NULL;
+ offset = 0;
+ }
+
+ /* Caller error if args[0] is an option. */
+ if (i == 0)
+ return 0;
+
+ /* Shift args up one (or two) and insert the option */
+ for (j = i; j > insert_idx; j--)
+ parser->args[j + offset] = parser->args[j - 1];
+
+ parser->args[insert_idx] = option;
+
+ if (value)
+ parser->args[insert_idx + 1] = value;
+
+ insert_idx += (1 + offset);
+ i += offset;
+
+ changed = 1;
+ }
+
+ return changed;
+}
+
+cli_opt_status_t cli_opt_parser_next(cli_opt *opt, cli_opt_parser *parser)
+{
+ assert(opt && parser);
+
+ memset(opt, 0x0, sizeof(cli_opt));
+
+ if (parser->idx >= parser->args_len) {
+ opt->args_len = parser->in_args;
+ return CLI_OPT_STATUS_DONE;
+ }
+
+ /* Handle options in long form, those beginning with "--" */
+ if (strncmp(parser->args[parser->idx], "--", 2) == 0 &&
+ !parser->in_short &&
+ !parser->in_literal)
+ return parse_long(opt, parser);
+
+ /* Handle options in short form, those beginning with "-" */
+ else if (parser->in_short ||
+ (strncmp(parser->args[parser->idx], "-", 1) == 0 &&
+ !parser->in_literal))
+ return parse_short(opt, parser);
+
+ /*
+ * We've reached the first "bare" argument. In POSIX mode, all
+ * remaining items on the command line are arguments. In GNU
+ * mode, there may be long or short options after this. Sort any
+ * options up to this position then re-parse the current position.
+ */
+ if (parser->needs_sort && sort_gnu_style(parser))
+ return cli_opt_parser_next(opt, parser);
+
+ return parse_arg(opt, parser);
+}
+
+GIT_INLINE(int) spec_included(const cli_opt_spec **specs, const cli_opt_spec *spec)
+{
+ const cli_opt_spec **i;
+
+ for (i = specs; *i; ++i) {
+ if (spec == *i)
+ return 1;
+ }
+
+ return 0;
+}
+
+static cli_opt_status_t validate_required(
+ cli_opt *opt,
+ const cli_opt_spec specs[],
+ const cli_opt_spec **given_specs)
+{
+ const cli_opt_spec *spec, *required;
+ int given;
+
+ /*
+ * Iterate over the possible specs to identify requirements and
+ * ensure that those have been given on the command-line.
+ * Note that we can have required *choices*, where one in a
+ * list of choices must be specified.
+ */
+ for (spec = specs, required = NULL, given = 0; spec->type; ++spec) {
+ if (!required && (spec->usage & CLI_OPT_USAGE_REQUIRED)) {
+ required = spec;
+ given = 0;
+ } else if (!required) {
+ continue;
+ }
+
+ if (!given)
+ given = spec_included(given_specs, spec);
+
+ /*
+ * Validate the requirement unless we're in a required
+ * choice. In that case, keep the required state and
+ * validate at the end of the choice list.
+ */
+ if (!spec_is_choice(spec)) {
+ if (!given) {
+ opt->spec = required;
+ opt->status = CLI_OPT_STATUS_MISSING_ARGUMENT;
+ break;
+ }
+
+ required = NULL;
+ given = 0;
+ }
+ }
+
+ return opt->status;
+}
+
+cli_opt_status_t cli_opt_parse(
+ cli_opt *opt,
+ const cli_opt_spec specs[],
+ char **args,
+ size_t args_len,
+ unsigned int flags)
+{
+ cli_opt_parser parser;
+ const cli_opt_spec **given_specs;
+ size_t given_idx = 0;
+
+ cli_opt_parser_init(&parser, specs, args, args_len, flags);
+
+ given_specs = alloca(sizeof(const cli_opt_spec *) * (args_len + 1));
+
+ while (cli_opt_parser_next(opt, &parser)) {
+ if (opt->status != CLI_OPT_STATUS_OK &&
+ opt->status != CLI_OPT_STATUS_DONE)
+ return opt->status;
+
+ if ((opt->spec->usage & CLI_OPT_USAGE_STOP_PARSING))
+ return (opt->status = CLI_OPT_STATUS_DONE);
+
+ given_specs[given_idx++] = opt->spec;
+ }
+
+ given_specs[given_idx] = NULL;
+
+ return validate_required(opt, specs, given_specs);
+}
+
+static int spec_name_fprint(FILE *file, const cli_opt_spec *spec)
+{
+ int error;
+
+ if (spec->type == CLI_OPT_TYPE_ARG)
+ error = fprintf(file, "%s", spec->value_name);
+ else if (spec->type == CLI_OPT_TYPE_ARGS)
+ error = fprintf(file, "%s", spec->value_name);
+ else if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
+ error = fprintf(file, "-%c", spec->alias);
+ else
+ error = fprintf(file, "--%s", spec->name);
+
+ return error;
+}
+
+int cli_opt_status_fprint(
+ FILE *file,
+ const char *command,
+ const cli_opt *opt)
+{
+ const cli_opt_spec *choice;
+ int error;
+
+ if (command && (error = fprintf(file, "%s: ", command)) < 0)
+ return error;
+
+ switch (opt->status) {
+ case CLI_OPT_STATUS_DONE:
+ error = fprintf(file, "finished processing arguments (no error)\n");
+ break;
+ case CLI_OPT_STATUS_OK:
+ error = fprintf(file, "no error\n");
+ break;
+ case CLI_OPT_STATUS_UNKNOWN_OPTION:
+ error = fprintf(file, "unknown option: %s\n", opt->arg);
+ break;
+ case CLI_OPT_STATUS_MISSING_VALUE:
+ if ((error = fprintf(file, "argument '")) < 0 ||
+ (error = spec_name_fprint(file, opt->spec)) < 0 ||
+ (error = fprintf(file, "' requires a value.\n")) < 0)
+ break;
+ break;
+ case CLI_OPT_STATUS_MISSING_ARGUMENT:
+ if (spec_is_choice(opt->spec)) {
+ int is_choice = 1;
+
+ if (spec_is_choice((opt->spec)+1))
+ error = fprintf(file, "one of");
+ else
+ error = fprintf(file, "either");
+
+ if (error < 0)
+ break;
+
+ for (choice = opt->spec; is_choice; ++choice) {
+ is_choice = spec_is_choice(choice);
+
+ if (!is_choice)
+ error = fprintf(file, " or");
+ else if (choice != opt->spec)
+ error = fprintf(file, ",");
+
+ if ((error < 0) ||
+ (error = fprintf(file, " '")) < 0 ||
+ (error = spec_name_fprint(file, choice)) < 0 ||
+ (error = fprintf(file, "'")) < 0)
+ break;
+
+ if (!spec_is_choice(choice))
+ break;
+ }
+
+ if ((error < 0) ||
+ (error = fprintf(file, " is required.\n")) < 0)
+ break;
+ } else {
+ if ((error = fprintf(file, "argument '")) < 0 ||
+ (error = spec_name_fprint(file, opt->spec)) < 0 ||
+ (error = fprintf(file, "' is required.\n")) < 0)
+ break;
+ }
+
+ break;
+ default:
+ error = fprintf(file, "unknown status: %d\n", opt->status);
+ break;
+ }
+
+ return error;
+}
+
diff --git a/src/cli/opt.h b/src/cli/opt.h
new file mode 100644
index 000000000..6c1d4603e
--- /dev/null
+++ b/src/cli/opt.h
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c), Edward Thomson
+ * All rights reserved.
+ *
+ * This file is part of adopt, distributed under the MIT license.
+ * For full terms and conditions, see the included LICENSE file.
+ *
+ * THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT.
+ *
+ * This file was produced by using the `rename.pl` script included with
+ * adopt. The command-line specified was:
+ *
+ * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
+ */
+
+#ifndef CLI_opt_h__
+#define CLI_opt_h__
+
+#include
+#include
+
+/**
+ * The type of argument to be parsed.
+ */
+typedef enum {
+ CLI_OPT_TYPE_NONE = 0,
+
+ /**
+ * An option that, when specified, sets a given value to true.
+ * This is useful for options like "--debug". A negation
+ * option (beginning with "no-") is implicitly specified; for
+ * example "--no-debug". The `value` pointer in the returned
+ * option will be set to `1` when this is specified, and set to
+ * `0` when the negation "no-" option is specified.
+ */
+ CLI_OPT_TYPE_BOOL,
+
+ /**
+ * An option that, when specified, sets the given `value` pointer
+ * to the specified `switch_value`. This is useful for booleans
+ * where you do not want the implicit negation that comes with an
+ * `CLI_OPT_TYPE_BOOL`, or for switches that multiplex a value, like
+ * setting a mode. For example, `--read` may set the `value` to
+ * `MODE_READ` and `--write` may set the `value` to `MODE_WRITE`.
+ */
+ CLI_OPT_TYPE_SWITCH,
+
+ /**
+ * An option that, when specified, increments the given
+ * `value` by the given `switch_value`. This can be specified
+ * multiple times to continue to increment the `value`.
+ * (For example, "-vvv" to set verbosity to 3.)
+ */
+ CLI_OPT_TYPE_ACCUMULATOR,
+
+ /**
+ * An option that takes a value, for example `-n value`,
+ * `-nvalue`, `--name value` or `--name=value`.
+ */
+ CLI_OPT_TYPE_VALUE,
+
+ /**
+ * A bare "--" that indicates that arguments following this are
+ * literal. This allows callers to specify things that might
+ * otherwise look like options, for example to operate on a file
+ * named "-rf" then you can invoke "program -- -rf" to treat
+ * "-rf" as an argument not an option.
+ */
+ CLI_OPT_TYPE_LITERAL,
+
+ /**
+ * A single argument, not an option. When options are exhausted,
+ * arguments will be matches in the order that they're specified
+ * in the spec list. For example, if two `CLI_OPT_TYPE_ARGS` are
+ * specified, `input_file` and `output_file`, then the first bare
+ * argument on the command line will be `input_file` and the
+ * second will be `output_file`.
+ */
+ CLI_OPT_TYPE_ARG,
+
+ /**
+ * A collection of arguments. This is useful when you want to take
+ * a list of arguments, for example, multiple paths. When specified,
+ * the value will be set to the first argument in the list.
+ */
+ CLI_OPT_TYPE_ARGS,
+} cli_opt_type_t;
+
+/**
+ * Additional information about an option, including parsing
+ * restrictions and usage information to be displayed to the end-user.
+ */
+typedef enum {
+ /** Defaults for the argument. */
+ CLI_OPT_USAGE_DEFAULT = 0,
+
+ /** This argument is required. */
+ CLI_OPT_USAGE_REQUIRED = (1u << 0),
+
+ /**
+ * This is a multiple choice argument, combined with the previous
+ * argument. For example, when the previous argument is `-f` and
+ * this optional is applied to an argument of type `-b` then one
+ * of `-f` or `-b` may be specified.
+ */
+ CLI_OPT_USAGE_CHOICE = (1u << 1),
+
+ /**
+ * This argument short-circuits the remainder of parsing.
+ * Useful for arguments like `--help`.
+ */
+ CLI_OPT_USAGE_STOP_PARSING = (1u << 2),
+
+ /** The argument's value is optional ("-n" or "-n foo") */
+ CLI_OPT_USAGE_VALUE_OPTIONAL = (1u << 3),
+
+ /** This argument should not be displayed in usage. */
+ CLI_OPT_USAGE_HIDDEN = (1u << 4),
+
+ /** In usage, show the long format instead of the abbreviated format. */
+ CLI_OPT_USAGE_SHOW_LONG = (1u << 5),
+} cli_opt_usage_t;
+
+typedef enum {
+ /** Default parsing behavior. */
+ CLI_OPT_PARSE_DEFAULT = 0,
+
+ /**
+ * Parse with GNU `getopt_long` style behavior, where options can
+ * be intermixed with arguments at any position (for example,
+ * "file1 --help file2".) Like `getopt_long`, this can mutate the
+ * arguments given.
+ */
+ CLI_OPT_PARSE_GNU = (1u << 0),
+
+ /**
+ * Force GNU `getopt_long` style behavior; the `POSIXLY_CORRECT`
+ * environment variable is ignored.
+ */
+ CLI_OPT_PARSE_FORCE_GNU = (1u << 1),
+} cli_opt_flag_t;
+
+/** Specification for an available option. */
+typedef struct cli_opt_spec {
+ /** Type of option expected. */
+ cli_opt_type_t type;
+
+ /** Name of the long option. */
+ const char *name;
+
+ /** The alias is the short (one-character) option alias. */
+ const char alias;
+
+ /**
+ * If this spec is of type `CLI_OPT_TYPE_BOOL`, this is a pointer
+ * to an `int` that will be set to `1` if the option is specified.
+ *
+ * If this spec is of type `CLI_OPT_TYPE_SWITCH`, this is a pointer
+ * to an `int` that will be set to the opt's `switch_value` (below)
+ * when this option is specified.
+ *
+ * If this spec is of type `CLI_OPT_TYPE_ACCUMULATOR`, this is a
+ * pointer to an `int` that will be incremented by the opt's
+ * `switch_value` (below). If no `switch_value` is provided then
+ * the value will be incremented by 1.
+ *
+ * If this spec is of type `CLI_OPT_TYPE_VALUE`,
+ * `CLI_OPT_TYPE_VALUE_OPTIONAL`, or `CLI_OPT_TYPE_ARG`, this is
+ * a pointer to a `char *` that will be set to the value
+ * specified on the command line.
+ *
+ * If this spec is of type `CLI_OPT_TYPE_ARGS`, this is a pointer
+ * to a `char **` that will be set to the remaining values
+ * specified on the command line.
+ */
+ void *value;
+
+ /**
+ * If this spec is of type `CLI_OPT_TYPE_SWITCH`, this is the value
+ * to set in the option's `value` pointer when it is specified. If
+ * this spec is of type `CLI_OPT_TYPE_ACCUMULATOR`, this is the value
+ * to increment in the option's `value` pointer when it is
+ * specified. This is ignored for other opt types.
+ */
+ int switch_value;
+
+ /**
+ * Optional usage flags that change parsing behavior and how
+ * usage information is shown to the end-user.
+ */
+ uint32_t usage;
+
+ /**
+ * The name of the value, provided when creating usage information.
+ * This is required only for the functions that display usage
+ * information and only when a spec is of type `CLI_OPT_TYPE_VALUE,
+ * `CLI_OPT_TYPE_ARG` or `CLI_OPT_TYPE_ARGS``.
+ */
+ const char *value_name;
+
+ /**
+ * Optional short description of the option to display to the
+ * end-user. This is only used when creating usage information.
+ */
+ const char *help;
+} cli_opt_spec;
+
+/** Return value for `cli_opt_parser_next`. */
+typedef enum {
+ /** Parsing is complete; there are no more arguments. */
+ CLI_OPT_STATUS_DONE = 0,
+
+ /**
+ * This argument was parsed correctly; the `opt` structure is
+ * populated and the value pointer has been set.
+ */
+ CLI_OPT_STATUS_OK = 1,
+
+ /**
+ * The argument could not be parsed correctly, it does not match
+ * any of the specifications provided.
+ */
+ CLI_OPT_STATUS_UNKNOWN_OPTION = 2,
+
+ /**
+ * The argument matched a spec of type `CLI_OPT_VALUE`, but no value
+ * was provided.
+ */
+ CLI_OPT_STATUS_MISSING_VALUE = 3,
+
+ /** A required argument was not provided. */
+ CLI_OPT_STATUS_MISSING_ARGUMENT = 4,
+} cli_opt_status_t;
+
+/** An option provided on the command-line. */
+typedef struct cli_opt {
+ /** The status of parsing the most recent argument. */
+ cli_opt_status_t status;
+
+ /**
+ * The specification that was provided on the command-line, or
+ * `NULL` if the argument did not match an `cli_opt_spec`.
+ */
+ const cli_opt_spec *spec;
+
+ /**
+ * The argument as it was specified on the command-line, including
+ * dashes, eg, `-f` or `--foo`.
+ */
+ char *arg;
+
+ /**
+ * If the spec is of type `CLI_OPT_VALUE` or `CLI_OPT_VALUE_OPTIONAL`,
+ * this is the value provided to the argument.
+ */
+ char *value;
+
+ /**
+ * If the argument is of type `CLI_OPT_ARGS`, this is the number of
+ * arguments remaining. This value is persisted even when parsing
+ * is complete and `status` == `CLI_OPT_STATUS_DONE`.
+ */
+ size_t args_len;
+} cli_opt;
+
+/* The internal parser state. Callers should not modify this structure. */
+typedef struct cli_opt_parser {
+ const cli_opt_spec *specs;
+ char **args;
+ size_t args_len;
+ unsigned int flags;
+
+ /* Parser state */
+ size_t idx;
+ size_t arg_idx;
+ size_t in_args;
+ size_t in_short;
+ int needs_sort : 1,
+ in_literal : 1;
+} cli_opt_parser;
+
+/**
+ * Parses all the command-line arguments and updates all the options using
+ * the pointers provided. Parsing stops on any invalid argument and
+ * information about the failure will be provided in the opt argument.
+ *
+ * This is the simplest way to parse options; it handles the initialization
+ * (`parser_init`) and looping (`parser_next`).
+ *
+ * @param opt The The `cli_opt` information that failed parsing
+ * @param specs A NULL-terminated array of `cli_opt_spec`s that can be parsed
+ * @param args The arguments that will be parsed
+ * @param args_len The length of arguments to be parsed
+ * @param flags The `cli_opt_flag_t flags for parsing
+ */
+cli_opt_status_t cli_opt_parse(
+ cli_opt *opt,
+ const cli_opt_spec specs[],
+ char **args,
+ size_t args_len,
+ unsigned int flags);
+
+/**
+ * Initializes a parser that parses the given arguments according to the
+ * given specifications.
+ *
+ * @param parser The `cli_opt_parser` that will be initialized
+ * @param specs A NULL-terminated array of `cli_opt_spec`s that can be parsed
+ * @param args The arguments that will be parsed
+ * @param args_len The length of arguments to be parsed
+ * @param flags The `cli_opt_flag_t flags for parsing
+ */
+void cli_opt_parser_init(
+ cli_opt_parser *parser,
+ const cli_opt_spec specs[],
+ char **args,
+ size_t args_len,
+ unsigned int flags);
+
+/**
+ * Parses the next command-line argument and places the information about
+ * the argument into the given `opt` data.
+ *
+ * @param opt The `cli_opt` information parsed from the argument
+ * @param parser An `cli_opt_parser` that has been initialized with
+ * `cli_opt_parser_init`
+ * @return true if the caller should continue iterating, or 0 if there are
+ * no arguments left to process.
+ */
+cli_opt_status_t cli_opt_parser_next(
+ cli_opt *opt,
+ cli_opt_parser *parser);
+
+/**
+ * Prints the status after parsing the most recent argument. This is
+ * useful for printing an error message when an unknown argument was
+ * specified, or when an argument was specified without a value.
+ *
+ * @param file The file to print information to
+ * @param command The name of the command to use when printing (optional)
+ * @param opt The option that failed to parse
+ * @return 0 on success, -1 on failure
+ */
+int cli_opt_status_fprint(
+ FILE *file,
+ const char *command,
+ const cli_opt *opt);
+
+#endif /* CLI_opt_h__ */
diff --git a/src/cli/opt_usage.c b/src/cli/opt_usage.c
new file mode 100644
index 000000000..478b41631
--- /dev/null
+++ b/src/cli/opt_usage.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "cli.h"
+#include "str.h"
+
+static int print_spec_name(git_str *out, const cli_opt_spec *spec)
+{
+ if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
+ !(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL) &&
+ !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
+ return git_str_printf(out, "-%c <%s>", spec->alias, spec->value_name);
+ if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
+ !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
+ return git_str_printf(out, "-%c [<%s>]", spec->alias, spec->value_name);
+ if (spec->type == CLI_OPT_TYPE_VALUE &&
+ !(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL))
+ return git_str_printf(out, "--%s[=<%s>]", spec->name, spec->value_name);
+ if (spec->type == CLI_OPT_TYPE_VALUE)
+ return git_str_printf(out, "--%s=<%s>", spec->name, spec->value_name);
+ if (spec->type == CLI_OPT_TYPE_ARG)
+ return git_str_printf(out, "<%s>", spec->value_name);
+ if (spec->type == CLI_OPT_TYPE_ARGS)
+ return git_str_printf(out, "<%s>...", spec->value_name);
+ if (spec->type == CLI_OPT_TYPE_LITERAL)
+ return git_str_printf(out, "--");
+ if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
+ return git_str_printf(out, "-%c", spec->alias);
+ if (spec->name)
+ return git_str_printf(out, "--%s", spec->name);
+
+ GIT_ASSERT(0);
+}
+
+/*
+ * This is similar to adopt's function, but modified to understand
+ * that we have a command ("git") and a "subcommand" ("checkout").
+ * It also understands a terminal's line length and wrap appropriately,
+ * using a `git_str` for storage.
+ */
+int cli_opt_usage_fprint(
+ FILE *file,
+ const char *command,
+ const char *subcommand,
+ const cli_opt_spec specs[])
+{
+ git_str usage = GIT_BUF_INIT, opt = GIT_BUF_INIT;
+ const cli_opt_spec *spec;
+ size_t i, prefixlen, linelen;
+ bool choice = false, next_choice = false, optional = false;
+ int error;
+
+ /* TODO: query actual console width. */
+ int console_width = 80;
+
+ if ((error = git_str_printf(&usage, "usage: %s", command)) < 0)
+ goto done;
+
+ if (subcommand &&
+ (error = git_str_printf(&usage, " %s", subcommand)) < 0)
+ goto done;
+
+ linelen = git_str_len(&usage);
+ prefixlen = linelen + 1;
+
+ for (spec = specs; spec->type; ++spec) {
+ if (!choice)
+ optional = !(spec->usage & CLI_OPT_USAGE_REQUIRED);
+
+ next_choice = !!((spec + 1)->usage & CLI_OPT_USAGE_CHOICE);
+
+ if (spec->usage & CLI_OPT_USAGE_HIDDEN)
+ continue;
+
+ if (choice)
+ git_str_putc(&opt, '|');
+ else
+ git_str_clear(&opt);
+
+ if (optional && !choice)
+ git_str_putc(&opt, '[');
+ if (!optional && !choice && next_choice)
+ git_str_putc(&opt, '(');
+
+ if ((error = print_spec_name(&opt, spec)) < 0)
+ goto done;
+
+ if (!optional && choice && !next_choice)
+ git_str_putc(&opt, ')');
+ else if (optional && !next_choice)
+ git_str_putc(&opt, ']');
+
+ if ((choice = next_choice))
+ continue;
+
+ if (git_str_oom(&opt)) {
+ error = -1;
+ goto done;
+ }
+
+ if (linelen > prefixlen &&
+ console_width > 0 &&
+ linelen + git_str_len(&opt) + 1 > (size_t)console_width) {
+ git_str_putc(&usage, '\n');
+
+ for (i = 0; i < prefixlen; i++)
+ git_str_putc(&usage, ' ');
+
+ linelen = prefixlen;
+ } else {
+ git_str_putc(&usage, ' ');
+ linelen += git_str_len(&opt) + 1;
+ }
+
+ git_str_puts(&usage, git_str_cstr(&opt));
+
+ if (git_str_oom(&usage)) {
+ error = -1;
+ goto done;
+ }
+ }
+
+ error = fprintf(file, "%s\n", git_str_cstr(&usage));
+
+done:
+ error = (error < 0) ? -1 : 0;
+
+ git_str_dispose(&usage);
+ git_str_dispose(&opt);
+ return error;
+}
+
+int cli_opt_usage_error(
+ const char *subcommand,
+ const cli_opt_spec specs[],
+ const cli_opt *invalid_opt)
+{
+ cli_opt_status_fprint(stderr, PROGRAM_NAME, invalid_opt);
+ cli_opt_usage_fprint(stderr, PROGRAM_NAME, subcommand, specs);
+ return CLI_EXIT_USAGE;
+}
+
+int cli_opt_help_fprint(
+ FILE *file,
+ const cli_opt_spec specs[])
+{
+ git_str help = GIT_BUF_INIT;
+ const cli_opt_spec *spec;
+ int error = 0;
+
+ /* Display required arguments first */
+ for (spec = specs; spec->type; ++spec) {
+ if (! (spec->usage & CLI_OPT_USAGE_REQUIRED) ||
+ (spec->usage & CLI_OPT_USAGE_HIDDEN))
+ continue;
+
+ git_str_printf(&help, " ");
+
+ if ((error = print_spec_name(&help, spec)) < 0)
+ goto done;
+
+ git_str_printf(&help, ": %s\n", spec->help);
+ }
+
+ /* Display the remaining arguments */
+ for (spec = specs; spec->type; ++spec) {
+ if ((spec->usage & CLI_OPT_USAGE_REQUIRED) ||
+ (spec->usage & CLI_OPT_USAGE_HIDDEN))
+ continue;
+
+ git_str_printf(&help, " ");
+
+ if ((error = print_spec_name(&help, spec)) < 0)
+ goto done;
+
+ git_str_printf(&help, ": %s\n", spec->help);
+
+ }
+
+ if (git_str_oom(&help) ||
+ p_write(fileno(file), help.ptr, help.size) < 0)
+ error = -1;
+
+done:
+ error = (error < 0) ? -1 : 0;
+
+ git_str_dispose(&help);
+ return error;
+}
+
diff --git a/src/cli/opt_usage.h b/src/cli/opt_usage.h
new file mode 100644
index 000000000..c752494e1
--- /dev/null
+++ b/src/cli/opt_usage.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef CLI_opt_usage_h__
+#define CLI_opt_usage_h__
+
+/**
+ * Prints usage information to the given file handle.
+ *
+ * @param file The file to print information to
+ * @param command The name of the command to use when printing
+ * @param subcommand The name of the subcommand (eg "checkout") to use when printing, or NULL to skip
+ * @param specs The specifications allowed by the command
+ * @return 0 on success, -1 on failure
+ */
+int cli_opt_usage_fprint(
+ FILE *file,
+ const char *command,
+ const char *subcommand,
+ const cli_opt_spec specs[]);
+
+int cli_opt_usage_error(
+ const char *subcommand,
+ const cli_opt_spec specs[],
+ const cli_opt *invalid_opt);
+
+int cli_opt_help_fprint(
+ FILE *file,
+ const cli_opt_spec specs[]);
+
+#endif /* CLI_opt_usage_h__ */
diff --git a/src/cli/progress.c b/src/cli/progress.c
new file mode 100644
index 000000000..ba52655e7
--- /dev/null
+++ b/src/cli/progress.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include
+#include
+#include
+
+#include "progress.h"
+#include "error.h"
+
+/*
+ * Show updates to the percentage and number of objects received
+ * separately from the throughput to give an accurate progress while
+ * avoiding too much noise on the screen.
+ */
+#define PROGRESS_UPDATE_TIME 0.10
+#define THROUGHPUT_UPDATE_TIME 1.00
+
+#define is_nl(c) ((c) == '\r' || (c) == '\n')
+
+#define return_os_error(msg) do { \
+ git_error_set(GIT_ERROR_OS, "%s", msg); return -1; } while(0)
+
+GIT_INLINE(size_t) no_nl_len(const char *str, size_t len)
+{
+ size_t i = 0;
+
+ while (i < len && !is_nl(str[i]))
+ i++;
+
+ return i;
+}
+
+GIT_INLINE(size_t) nl_len(bool *has_nl, const char *str, size_t len)
+{
+ size_t i = no_nl_len(str, len);
+
+ *has_nl = false;
+
+ while (i < len && is_nl(str[i])) {
+ *has_nl = true;
+ i++;
+ }
+
+ return i;
+}
+
+static int progress_write(cli_progress *progress, bool force, git_str *line)
+{
+ bool has_nl;
+ size_t no_nl = no_nl_len(line->ptr, line->size);
+ size_t nl = nl_len(&has_nl, line->ptr + no_nl, line->size - no_nl);
+ double now = git__timer();
+ size_t i;
+
+ /* Avoid spamming the console with progress updates */
+ if (!force && line->ptr[line->size - 1] != '\n' && progress->last_update) {
+ if (now - progress->last_update < PROGRESS_UPDATE_TIME) {
+ git_str_clear(&progress->deferred);
+ git_str_put(&progress->deferred, line->ptr, line->size);
+ return git_str_oom(&progress->deferred) ? -1 : 0;
+ }
+ }
+
+ /*
+ * If there's something on this line already (eg, a progress line
+ * with only a trailing `\r` that we'll print over) then we need
+ * to really print over it in case we're writing a shorter line.
+ */
+ if (printf("%.*s", (int)no_nl, line->ptr) < 0)
+ return_os_error("could not print status");
+
+ if (progress->onscreen.size) {
+ for (i = no_nl; i < progress->onscreen.size; i++) {
+ if (printf(" ") < 0)
+ return_os_error("could not print status");
+ }
+ }
+
+ if (printf("%.*s", (int)nl, line->ptr + no_nl) < 0 ||
+ fflush(stdout) != 0)
+ return_os_error("could not print status");
+
+ git_str_clear(&progress->onscreen);
+
+ if (line->ptr[line->size - 1] == '\n') {
+ progress->last_update = 0;
+ } else {
+ git_str_put(&progress->onscreen, line->ptr, line->size);
+ progress->last_update = now;
+ }
+
+ git_str_clear(&progress->deferred);
+ return git_str_oom(&progress->onscreen) ? -1 : 0;
+}
+
+static int progress_printf(cli_progress *progress, bool force, const char *fmt, ...)
+ GIT_FORMAT_PRINTF(3, 4);
+
+int progress_printf(cli_progress *progress, bool force, const char *fmt, ...)
+{
+ git_str buf = GIT_BUF_INIT;
+ va_list ap;
+ int error;
+
+ va_start(ap, fmt);
+ error = git_str_vprintf(&buf, fmt, ap);
+ va_end(ap);
+
+ if (error < 0)
+ return error;
+
+ error = progress_write(progress, force, &buf);
+
+ git_str_dispose(&buf);
+ return error;
+}
+
+static int progress_complete(cli_progress *progress)
+{
+ if (progress->deferred.size)
+ progress_write(progress, true, &progress->deferred);
+
+ if (progress->onscreen.size)
+ if (printf("\n") < 0)
+ return_os_error("could not print status");
+
+ git_str_clear(&progress->deferred);
+ git_str_clear(&progress->onscreen);
+ progress->last_update = 0;
+ progress->action_start = 0;
+ progress->action_finish = 0;
+
+ return 0;
+}
+
+GIT_INLINE(int) percent(size_t completed, size_t total)
+{
+ if (total == 0)
+ return (completed == 0) ? 100 : 0;
+
+ return (int)(((double)completed / (double)total) * 100);
+}
+
+int cli_progress_fetch_sideband(const char *str, int len, void *payload)
+{
+ cli_progress *progress = (cli_progress *)payload;
+ size_t remain;
+
+ if (len <= 0)
+ return 0;
+
+ /* Accumulate the sideband data, then print it line-at-a-time. */
+ if (git_str_put(&progress->sideband, str, len) < 0)
+ return -1;
+
+ str = progress->sideband.ptr;
+ remain = progress->sideband.size;
+
+ while (remain) {
+ bool has_nl;
+ size_t line_len = nl_len(&has_nl, str, remain);
+
+ if (!has_nl)
+ break;
+
+ if (line_len < INT_MAX) {
+ int error = progress_printf(progress, true,
+ "remote: %.*s", (int)line_len, str);
+
+ if (error < 0)
+ return error;
+ }
+
+ str += line_len;
+ remain -= line_len;
+ }
+
+ git_str_consume_bytes(&progress->sideband, (progress->sideband.size - remain));
+
+ return 0;
+}
+
+static int fetch_receiving(
+ cli_progress *progress,
+ const git_indexer_progress *stats)
+{
+ char *recv_units[] = { "B", "KiB", "MiB", "GiB", "TiB", NULL };
+ char *rate_units[] = { "B/s", "KiB/s", "MiB/s", "GiB/s", "TiB/s", NULL };
+
+ double now, recv_len, rate, elapsed;
+ size_t recv_unit_idx = 0, rate_unit_idx = 0;
+ bool done = (stats->received_objects == stats->total_objects);
+
+ if (!progress->action_start)
+ progress->action_start = git__timer();
+
+ if (done && progress->action_finish)
+ now = progress->action_finish;
+ else if (done)
+ progress->action_finish = now = git__timer();
+ else
+ now = git__timer();
+
+ if (progress->throughput_update &&
+ now - progress->throughput_update < THROUGHPUT_UPDATE_TIME) {
+ elapsed = progress->throughput_update -
+ progress->action_start;
+ recv_len = progress->throughput_bytes;
+ } else {
+ elapsed = now - progress->action_start;
+ recv_len = (double)stats->received_bytes;
+
+ progress->throughput_update = now;
+ progress->throughput_bytes = recv_len;
+ }
+
+ rate = elapsed ? recv_len / elapsed : 0;
+
+ while (recv_len > 1024 && recv_units[recv_unit_idx+1]) {
+ recv_len /= 1024;
+ recv_unit_idx++;
+ }
+
+ while (rate > 1024 && rate_units[rate_unit_idx+1]) {
+ rate /= 1024;
+ rate_unit_idx++;
+ }
+
+ return progress_printf(progress, false,
+ "Receiving objects: %3d%% (%d/%d), %.2f %s | %.2f %s%s\r",
+ percent(stats->received_objects, stats->total_objects),
+ stats->received_objects,
+ stats->total_objects,
+ recv_len, recv_units[recv_unit_idx],
+ rate, rate_units[rate_unit_idx],
+ done ? ", done." : "");
+}
+
+static int fetch_resolving(
+ cli_progress *progress,
+ const git_indexer_progress *stats)
+{
+ bool done = (stats->indexed_deltas == stats->total_deltas);
+
+ return progress_printf(progress, false,
+ "Resolving deltas: %3d%% (%d/%d)%s\r",
+ percent(stats->indexed_deltas, stats->total_deltas),
+ stats->indexed_deltas, stats->total_deltas,
+ done ? ", done." : "");
+}
+
+int cli_progress_fetch_transfer(const git_indexer_progress *stats, void *payload)
+{
+ cli_progress *progress = (cli_progress *)payload;
+ int error = 0;
+
+ switch (progress->action) {
+ case CLI_PROGRESS_NONE:
+ progress->action = CLI_PROGRESS_RECEIVING;
+ /* fall through */
+
+ case CLI_PROGRESS_RECEIVING:
+ if ((error = fetch_receiving(progress, stats)) < 0)
+ break;
+
+ /*
+ * Upgrade from receiving to resolving; do this after the
+ * final call to cli_progress_fetch_receiving (above) to
+ * ensure that we've printed a final "done" string after
+ * any sideband data.
+ */
+ if (!stats->indexed_deltas)
+ break;
+
+ progress_complete(progress);
+ progress->action = CLI_PROGRESS_RESOLVING;
+ /* fall through */
+
+ case CLI_PROGRESS_RESOLVING:
+ error = fetch_resolving(progress, stats);
+ break;
+
+ default:
+ /* should not be reached */
+ GIT_ASSERT(!"unexpected progress state");
+ }
+
+ return error;
+}
+
+void cli_progress_checkout(
+ const char *path,
+ size_t completed_steps,
+ size_t total_steps,
+ void *payload)
+{
+ cli_progress *progress = (cli_progress *)payload;
+ bool done = (completed_steps == total_steps);
+
+ GIT_UNUSED(path);
+
+ if (progress->action != CLI_PROGRESS_CHECKING_OUT) {
+ progress_complete(progress);
+ progress->action = CLI_PROGRESS_CHECKING_OUT;
+ }
+
+ progress_printf(progress, false,
+ "Checking out files: %3d%% (%" PRIuZ "/%" PRIuZ ")%s\r",
+ percent(completed_steps, total_steps),
+ completed_steps, total_steps,
+ done ? ", done." : "");
+}
+
+int cli_progress_abort(cli_progress *progress)
+{
+ if (progress->onscreen.size > 0 && printf("\n") < 0)
+ return_os_error("could not print status");
+
+ return 0;
+}
+
+int cli_progress_finish(cli_progress *progress)
+{
+ int error = progress->action ? progress_complete(progress) : 0;
+
+ progress->action = 0;
+ return error;
+}
+
+void cli_progress_dispose(cli_progress *progress)
+{
+ if (progress == NULL)
+ return;
+
+ git_str_dispose(&progress->sideband);
+ git_str_dispose(&progress->onscreen);
+ git_str_dispose(&progress->deferred);
+
+ memset(progress, 0, sizeof(cli_progress));
+}
diff --git a/src/cli/progress.h b/src/cli/progress.h
new file mode 100644
index 000000000..7a445ec29
--- /dev/null
+++ b/src/cli/progress.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef CLI_progress_h__
+#define CLI_progress_h__
+
+#include
+#include "str.h"
+
+/*
+ * A general purpose set of progress printing functions. An individual
+ * `cli_progress` object is capable of displaying progress for a single
+ * function, even if that function displays multiple pieces of progress
+ * (like `git_clone`). `cli_progress_finish` should be called after
+ * any function invocation to re-set state.
+ */
+
+typedef enum {
+ CLI_PROGRESS_NONE,
+ CLI_PROGRESS_RECEIVING,
+ CLI_PROGRESS_RESOLVING,
+ CLI_PROGRESS_CHECKING_OUT
+} cli_progress_t;
+
+typedef struct {
+ cli_progress_t action;
+
+ /* Actions may time themselves (eg fetch) but are not required to */
+ double action_start;
+ double action_finish;
+
+ /* Last console update, avoid too frequent updates. */
+ double last_update;
+
+ /* Accumulators for partial output and deferred updates. */
+ git_str sideband;
+ git_str onscreen;
+ git_str deferred;
+
+ /* Last update about throughput */
+ double throughput_update;
+ double throughput_bytes;
+} cli_progress;
+
+#define CLI_PROGRESS_INIT { 0 }
+
+/**
+ * Prints sideband data from fetch to the console. Suitable for a
+ * `sideband_progress` callback for `git_fetch_options`.
+ *
+ * @param str The sideband string
+ * @param len The length of the sideband string
+ * @param payload A pointer to the cli_progress
+ * @return 0 on success, -1 on failure
+ */
+extern int cli_progress_fetch_sideband(
+ const char *str,
+ int len,
+ void *payload);
+
+/**
+ * Prints fetch transfer statistics to the console. Suitable for a
+ * `transfer_progress` callback for `git_fetch_options`.
+ *
+ * @param stats The indexer stats
+ * @param payload A pointer to the cli_progress
+ * @return 0 on success, -1 on failure
+ */
+extern int cli_progress_fetch_transfer(
+ const git_indexer_progress *stats,
+ void *payload);
+
+/**
+ * Prints checkout progress to the console. Suitable for a
+ * `progress_cb` callback for `git_checkout_options`.
+ *
+ * @param path The path being written
+ * @param completed_steps The completed checkout steps
+ * @param total_steps The total number of checkout steps
+ * @param payload A pointer to the cli_progress
+ */
+extern void cli_progress_checkout(
+ const char *path,
+ size_t completed_steps,
+ size_t total_steps,
+ void *payload);
+
+/**
+ * Stop displaying progress quickly; suitable for stopping an application
+ * quickly. Does not display any lines that were buffered, just gets the
+ * console back to a sensible place.
+ *
+ * @param progress The progress information
+ * @return 0 on success, -1 on failure
+ */
+extern int cli_progress_abort(cli_progress *progress);
+
+/**
+ * Finishes displaying progress; flushes any buffered output.
+ *
+ * @param progress The progress information
+ * @return 0 on success, -1 on failure
+ */
+extern int cli_progress_finish(cli_progress *progress);
+
+/**
+ * Disposes the progress information.
+ *
+ * @param progress The progress information
+ */
+extern void cli_progress_dispose(cli_progress *progress);
+
+#endif /* CLI_progress_h__ */
diff --git a/src/cli/sighandler.h b/src/cli/sighandler.h
new file mode 100644
index 000000000..877223e02
--- /dev/null
+++ b/src/cli/sighandler.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef CLI_sighandler_h__
+#define CLI_sighandler_h__
+
+/**
+ * Sets up a signal handler that will run when the process is interrupted
+ * (via SIGINT on POSIX or Control-C or Control-Break on Windows).
+ *
+ * @param handler The function to run on interrupt
+ * @return 0 on success, -1 on failure
+ */
+int cli_sighandler_set_interrupt(void (*handler)(void));
+
+#endif /* CLI_sighandler_h__ */
diff --git a/src/cli/unix/sighandler.c b/src/cli/unix/sighandler.c
new file mode 100644
index 000000000..6b4982d48
--- /dev/null
+++ b/src/cli/unix/sighandler.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include
+#include
+#include "git2_util.h"
+#include "cli.h"
+
+static void (*interrupt_handler)(void) = NULL;
+
+static void interrupt_proxy(int signal)
+{
+ GIT_UNUSED(signal);
+ interrupt_handler();
+}
+
+int cli_sighandler_set_interrupt(void (*handler)(void))
+{
+ void (*result)(int);
+
+ if ((interrupt_handler = handler) != NULL)
+ result = signal(SIGINT, interrupt_proxy);
+ else
+ result = signal(SIGINT, SIG_DFL);
+
+ if (result == SIG_ERR) {
+ git_error_set(GIT_ERROR_OS, "could not set signal handler");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/win32/precompiled.c b/src/cli/win32/precompiled.c
similarity index 100%
rename from src/win32/precompiled.c
rename to src/cli/win32/precompiled.c
diff --git a/src/cli/win32/precompiled.h b/src/cli/win32/precompiled.h
new file mode 100644
index 000000000..b0309b864
--- /dev/null
+++ b/src/cli/win32/precompiled.h
@@ -0,0 +1,3 @@
+#include
+
+#include "cli.h"
diff --git a/src/cli/win32/sighandler.c b/src/cli/win32/sighandler.c
new file mode 100644
index 000000000..cc0b64640
--- /dev/null
+++ b/src/cli/win32/sighandler.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2_util.h"
+#include
+
+#include "cli.h"
+
+static void (*interrupt_handler)(void) = NULL;
+
+static BOOL WINAPI interrupt_proxy(DWORD signal)
+{
+ GIT_UNUSED(signal);
+ interrupt_handler();
+ return TRUE;
+}
+
+int cli_sighandler_set_interrupt(void (*handler)(void))
+{
+ BOOL result;
+
+ if ((interrupt_handler = handler) != NULL)
+ result = SetConsoleCtrlHandler(interrupt_proxy, FALSE);
+ else
+ result = SetConsoleCtrlHandler(NULL, FALSE);
+
+ if (!result) {
+ git_error_set(GIT_ERROR_OS, "could not set control control handler");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/features.h.in b/src/features.h.in
index f920135da..fbf0cab60 100644
--- a/src/features.h.in
+++ b/src/features.h.in
@@ -46,8 +46,17 @@
#cmakedefine GIT_SHA1_WIN32 1
#cmakedefine GIT_SHA1_COMMON_CRYPTO 1
#cmakedefine GIT_SHA1_OPENSSL 1
+#cmakedefine GIT_SHA1_OPENSSL_DYNAMIC 1
#cmakedefine GIT_SHA1_MBEDTLS 1
+#cmakedefine GIT_SHA256_BUILTIN 1
+#cmakedefine GIT_SHA256_WIN32 1
+#cmakedefine GIT_SHA256_COMMON_CRYPTO 1
+#cmakedefine GIT_SHA256_OPENSSL 1
+#cmakedefine GIT_SHA256_OPENSSL_DYNAMIC 1
+#cmakedefine GIT_SHA256_MBEDTLS 1
+
#cmakedefine GIT_RAND_GETENTROPY 1
+#cmakedefine GIT_RAND_GETLOADAVG 1
#endif
diff --git a/src/hash/sha1.h b/src/hash/sha1.h
deleted file mode 100644
index 4b4dae3f8..000000000
--- a/src/hash/sha1.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_hash_sha1_h__
-#define INCLUDE_hash_sha1_h__
-
-#include "common.h"
-
-typedef struct git_hash_sha1_ctx git_hash_sha1_ctx;
-
-#if defined(GIT_SHA1_COLLISIONDETECT)
-# include "sha1/collisiondetect.h"
-#elif defined(GIT_SHA1_COMMON_CRYPTO)
-# include "sha1/common_crypto.h"
-#elif defined(GIT_SHA1_OPENSSL)
-# include "sha1/openssl.h"
-#elif defined(GIT_SHA1_WIN32)
-# include "sha1/win32.h"
-#elif defined(GIT_SHA1_MBEDTLS)
-# include "sha1/mbedtls.h"
-#else
-# include "sha1/generic.h"
-#endif
-
-#define GIT_HASH_SHA1_SIZE 20
-
-int git_hash_sha1_global_init(void);
-
-int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx);
-void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx);
-
-int git_hash_sha1_init(git_hash_sha1_ctx *c);
-int git_hash_sha1_update(git_hash_sha1_ctx *c, const void *data, size_t len);
-int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *c);
-
-#endif
diff --git a/src/hash/sha1/generic.c b/src/hash/sha1/generic.c
deleted file mode 100644
index 85b34c578..000000000
--- a/src/hash/sha1/generic.c
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "generic.h"
-
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
-
-/*
- * Force usage of rol or ror by selecting the one with the smaller constant.
- * It _can_ generate slightly smaller code (a constant of 1 is special), but
- * perhaps more importantly it's possibly faster on any uarch that does a
- * rotate with a loop.
- */
-
-#define SHA_ASM(op, x, n) (__extension__ ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }))
-#define SHA_ROL(x,n) SHA_ASM("rol", x, n)
-#define SHA_ROR(x,n) SHA_ASM("ror", x, n)
-
-#else
-
-#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r)))
-#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n))
-#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n)
-
-#endif
-
-/*
- * If you have 32 registers or more, the compiler can (and should)
- * try to change the array[] accesses into registers. However, on
- * machines with less than ~25 registers, that won't really work,
- * and at least gcc will make an unholy mess of it.
- *
- * So to avoid that mess which just slows things down, we force
- * the stores to memory to actually happen (we might be better off
- * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
- * suggested by Artur Skawina - that will also make gcc unable to
- * try to do the silly "optimize away loads" part because it won't
- * see what the value will be).
- *
- * Ben Herrenschmidt reports that on PPC, the C version comes close
- * to the optimized asm with this (ie on PPC you don't want that
- * 'volatile', since there are lots of registers).
- *
- * On ARM we get the best code generation by forcing a full memory barrier
- * between each SHA_ROUND, otherwise gcc happily get wild with spilling and
- * the stack frame size simply explode and performance goes down the drain.
- */
-
-#if defined(__i386__) || defined(__x86_64__)
- #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
-#elif defined(__GNUC__) && defined(__arm__)
- #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
-#else
- #define setW(x, val) (W(x) = (val))
-#endif
-
-/*
- * Performance might be improved if the CPU architecture is OK with
- * unaligned 32-bit loads and a fast ntohl() is available.
- * Otherwise fall back to byte loads and shifts which is portable,
- * and is faster on architectures with memory alignment issues.
- */
-
-#if defined(__i386__) || defined(__x86_64__) || \
- defined(_M_IX86) || defined(_M_X64) || \
- defined(__ppc__) || defined(__ppc64__) || \
- defined(__powerpc__) || defined(__powerpc64__) || \
- defined(__s390__) || defined(__s390x__)
-
-#define get_be32(p) ntohl(*(const unsigned int *)(p))
-#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
-
-#else
-
-#define get_be32(p) ( \
- (*((const unsigned char *)(p) + 0) << 24) | \
- (*((const unsigned char *)(p) + 1) << 16) | \
- (*((const unsigned char *)(p) + 2) << 8) | \
- (*((const unsigned char *)(p) + 3) << 0) )
-#define put_be32(p, v) do { \
- unsigned int __v = (v); \
- *((unsigned char *)(p) + 0) = __v >> 24; \
- *((unsigned char *)(p) + 1) = __v >> 16; \
- *((unsigned char *)(p) + 2) = __v >> 8; \
- *((unsigned char *)(p) + 3) = __v >> 0; } while (0)
-
-#endif
-
-/* This "rolls" over the 512-bit array */
-#define W(x) (array[(x)&15])
-
-/*
- * Where do we get the source from? The first 16 iterations get it from
- * the input data, the next mix it from the 512-bit array.
- */
-#define SHA_SRC(t) get_be32(data + t)
-#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)
-
-#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \
- unsigned int TEMP = input(t); setW(t, TEMP); \
- E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \
- B = SHA_ROR(B, 2); } while (0)
-
-#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
-#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
-#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )
-#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )
-#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E )
-
-static void hash__block(git_hash_sha1_ctx *ctx, const unsigned int *data)
-{
- unsigned int A,B,C,D,E;
- unsigned int array[16];
-
- A = ctx->H[0];
- B = ctx->H[1];
- C = ctx->H[2];
- D = ctx->H[3];
- E = ctx->H[4];
-
- /* Round 1 - iterations 0-16 take their input from 'data' */
- T_0_15( 0, A, B, C, D, E);
- T_0_15( 1, E, A, B, C, D);
- T_0_15( 2, D, E, A, B, C);
- T_0_15( 3, C, D, E, A, B);
- T_0_15( 4, B, C, D, E, A);
- T_0_15( 5, A, B, C, D, E);
- T_0_15( 6, E, A, B, C, D);
- T_0_15( 7, D, E, A, B, C);
- T_0_15( 8, C, D, E, A, B);
- T_0_15( 9, B, C, D, E, A);
- T_0_15(10, A, B, C, D, E);
- T_0_15(11, E, A, B, C, D);
- T_0_15(12, D, E, A, B, C);
- T_0_15(13, C, D, E, A, B);
- T_0_15(14, B, C, D, E, A);
- T_0_15(15, A, B, C, D, E);
-
- /* Round 1 - tail. Input from 512-bit mixing array */
- T_16_19(16, E, A, B, C, D);
- T_16_19(17, D, E, A, B, C);
- T_16_19(18, C, D, E, A, B);
- T_16_19(19, B, C, D, E, A);
-
- /* Round 2 */
- T_20_39(20, A, B, C, D, E);
- T_20_39(21, E, A, B, C, D);
- T_20_39(22, D, E, A, B, C);
- T_20_39(23, C, D, E, A, B);
- T_20_39(24, B, C, D, E, A);
- T_20_39(25, A, B, C, D, E);
- T_20_39(26, E, A, B, C, D);
- T_20_39(27, D, E, A, B, C);
- T_20_39(28, C, D, E, A, B);
- T_20_39(29, B, C, D, E, A);
- T_20_39(30, A, B, C, D, E);
- T_20_39(31, E, A, B, C, D);
- T_20_39(32, D, E, A, B, C);
- T_20_39(33, C, D, E, A, B);
- T_20_39(34, B, C, D, E, A);
- T_20_39(35, A, B, C, D, E);
- T_20_39(36, E, A, B, C, D);
- T_20_39(37, D, E, A, B, C);
- T_20_39(38, C, D, E, A, B);
- T_20_39(39, B, C, D, E, A);
-
- /* Round 3 */
- T_40_59(40, A, B, C, D, E);
- T_40_59(41, E, A, B, C, D);
- T_40_59(42, D, E, A, B, C);
- T_40_59(43, C, D, E, A, B);
- T_40_59(44, B, C, D, E, A);
- T_40_59(45, A, B, C, D, E);
- T_40_59(46, E, A, B, C, D);
- T_40_59(47, D, E, A, B, C);
- T_40_59(48, C, D, E, A, B);
- T_40_59(49, B, C, D, E, A);
- T_40_59(50, A, B, C, D, E);
- T_40_59(51, E, A, B, C, D);
- T_40_59(52, D, E, A, B, C);
- T_40_59(53, C, D, E, A, B);
- T_40_59(54, B, C, D, E, A);
- T_40_59(55, A, B, C, D, E);
- T_40_59(56, E, A, B, C, D);
- T_40_59(57, D, E, A, B, C);
- T_40_59(58, C, D, E, A, B);
- T_40_59(59, B, C, D, E, A);
-
- /* Round 4 */
- T_60_79(60, A, B, C, D, E);
- T_60_79(61, E, A, B, C, D);
- T_60_79(62, D, E, A, B, C);
- T_60_79(63, C, D, E, A, B);
- T_60_79(64, B, C, D, E, A);
- T_60_79(65, A, B, C, D, E);
- T_60_79(66, E, A, B, C, D);
- T_60_79(67, D, E, A, B, C);
- T_60_79(68, C, D, E, A, B);
- T_60_79(69, B, C, D, E, A);
- T_60_79(70, A, B, C, D, E);
- T_60_79(71, E, A, B, C, D);
- T_60_79(72, D, E, A, B, C);
- T_60_79(73, C, D, E, A, B);
- T_60_79(74, B, C, D, E, A);
- T_60_79(75, A, B, C, D, E);
- T_60_79(76, E, A, B, C, D);
- T_60_79(77, D, E, A, B, C);
- T_60_79(78, C, D, E, A, B);
- T_60_79(79, B, C, D, E, A);
-
- ctx->H[0] += A;
- ctx->H[1] += B;
- ctx->H[2] += C;
- ctx->H[3] += D;
- ctx->H[4] += E;
-}
-
-int git_hash_sha1_global_init(void)
-{
- return 0;
-}
-
-int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
-{
- return git_hash_sha1_init(ctx);
-}
-
-void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
-{
- GIT_UNUSED(ctx);
-}
-
-int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
-{
- ctx->size = 0;
-
- /* Initialize H with the magic constants (see FIPS180 for constants) */
- ctx->H[0] = 0x67452301;
- ctx->H[1] = 0xefcdab89;
- ctx->H[2] = 0x98badcfe;
- ctx->H[3] = 0x10325476;
- ctx->H[4] = 0xc3d2e1f0;
-
- return 0;
-}
-
-int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
-{
- unsigned int lenW = ctx->size & 63;
-
- ctx->size += len;
-
- /* Read the data into W and process blocks as they get full */
- if (lenW) {
- unsigned int left = 64 - lenW;
- if (len < left)
- left = (unsigned int)len;
- memcpy(lenW + (char *)ctx->W, data, left);
- lenW = (lenW + left) & 63;
- len -= left;
- data = ((const char *)data + left);
- if (lenW)
- return 0;
- hash__block(ctx, ctx->W);
- }
- while (len >= 64) {
- hash__block(ctx, data);
- data = ((const char *)data + 64);
- len -= 64;
- }
- if (len)
- memcpy(ctx->W, data, len);
-
- return 0;
-}
-
-int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
-{
- static const unsigned char pad[64] = { 0x80 };
- unsigned int padlen[2];
- int i;
-
- /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
- padlen[0] = htonl((uint32_t)(ctx->size >> 29));
- padlen[1] = htonl((uint32_t)(ctx->size << 3));
-
- i = ctx->size & 63;
- git_hash_sha1_update(ctx, pad, 1+ (63 & (55 - i)));
- git_hash_sha1_update(ctx, padlen, 8);
-
- /* Output hash */
- for (i = 0; i < 5; i++)
- put_be32(out + i*4, ctx->H[i]);
-
- return 0;
-}
diff --git a/src/hash/sha1/openssl.c b/src/hash/sha1/openssl.c
deleted file mode 100644
index 64bf99b3c..000000000
--- a/src/hash/sha1/openssl.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "openssl.h"
-
-int git_hash_sha1_global_init(void)
-{
- return 0;
-}
-
-int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
-{
- return git_hash_sha1_init(ctx);
-}
-
-void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
-{
- GIT_UNUSED(ctx);
-}
-
-int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
-{
- GIT_ASSERT_ARG(ctx);
-
- if (SHA1_Init(&ctx->c) != 1) {
- git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to initialize hash context");
- return -1;
- }
-
- return 0;
-}
-
-int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
-{
- GIT_ASSERT_ARG(ctx);
-
- if (SHA1_Update(&ctx->c, data, len) != 1) {
- git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to update hash");
- return -1;
- }
-
- return 0;
-}
-
-int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
-{
- GIT_ASSERT_ARG(ctx);
-
- if (SHA1_Final(out, &ctx->c) != 1) {
- git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to finalize hash");
- return -1;
- }
-
- return 0;
-}
diff --git a/src/hash/sha1/win32.c b/src/hash/sha1/win32.c
deleted file mode 100644
index b89dfbad8..000000000
--- a/src/hash/sha1/win32.c
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "win32.h"
-
-#include "runtime.h"
-
-#include
-#include
-
-#define GIT_HASH_CNG_DLL_NAME "bcrypt.dll"
-
-/* BCRYPT_SHA1_ALGORITHM */
-#define GIT_HASH_CNG_HASH_TYPE L"SHA1"
-
-/* BCRYPT_OBJECT_LENGTH */
-#define GIT_HASH_CNG_HASH_OBJECT_LEN L"ObjectLength"
-
-/* BCRYPT_HASH_REUSEABLE_FLAGS */
-#define GIT_HASH_CNG_HASH_REUSABLE 0x00000020
-
-static git_hash_prov hash_prov = {0};
-
-/* Hash initialization */
-
-/* Initialize CNG, if available */
-GIT_INLINE(int) hash_cng_prov_init(void)
-{
- char dll_path[MAX_PATH];
- DWORD dll_path_len, size_len;
-
- /* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */
- if (!git_has_win32_version(6, 0, 1)) {
- git_error_set(GIT_ERROR_SHA1, "CryptoNG is not supported on this platform");
- return -1;
- }
-
- /* Load bcrypt.dll explicitly from the system directory */
- if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 ||
- dll_path_len > MAX_PATH ||
- StringCchCat(dll_path, MAX_PATH, "\\") < 0 ||
- StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 ||
- (hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL) {
- git_error_set(GIT_ERROR_SHA1, "CryptoNG library could not be loaded");
- return -1;
- }
-
- /* Load the function addresses */
- if ((hash_prov.prov.cng.open_algorithm_provider = (hash_win32_cng_open_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptOpenAlgorithmProvider")) == NULL ||
- (hash_prov.prov.cng.get_property = (hash_win32_cng_get_property_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptGetProperty")) == NULL ||
- (hash_prov.prov.cng.create_hash = (hash_win32_cng_create_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCreateHash")) == NULL ||
- (hash_prov.prov.cng.finish_hash = (hash_win32_cng_finish_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptFinishHash")) == NULL ||
- (hash_prov.prov.cng.hash_data = (hash_win32_cng_hash_data_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptHashData")) == NULL ||
- (hash_prov.prov.cng.destroy_hash = (hash_win32_cng_destroy_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptDestroyHash")) == NULL ||
- (hash_prov.prov.cng.close_algorithm_provider = (hash_win32_cng_close_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCloseAlgorithmProvider")) == NULL) {
- FreeLibrary(hash_prov.prov.cng.dll);
-
- git_error_set(GIT_ERROR_OS, "CryptoNG functions could not be loaded");
- return -1;
- }
-
- /* Load the SHA1 algorithm */
- if (hash_prov.prov.cng.open_algorithm_provider(&hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0) {
- FreeLibrary(hash_prov.prov.cng.dll);
-
- git_error_set(GIT_ERROR_OS, "algorithm provider could not be initialized");
- return -1;
- }
-
- /* Get storage space for the hash object */
- if (hash_prov.prov.cng.get_property(hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_prov.prov.cng.hash_object_size, sizeof(DWORD), &size_len, 0) < 0) {
- hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
- FreeLibrary(hash_prov.prov.cng.dll);
-
- git_error_set(GIT_ERROR_OS, "algorithm handle could not be found");
- return -1;
- }
-
- hash_prov.type = CNG;
- return 0;
-}
-
-GIT_INLINE(void) hash_cng_prov_shutdown(void)
-{
- hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
- FreeLibrary(hash_prov.prov.cng.dll);
-
- hash_prov.type = INVALID;
-}
-
-/* Initialize CryptoAPI */
-GIT_INLINE(int) hash_cryptoapi_prov_init()
-{
- if (!CryptAcquireContext(&hash_prov.prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
- git_error_set(GIT_ERROR_OS, "legacy hash context could not be started");
- return -1;
- }
-
- hash_prov.type = CRYPTOAPI;
- return 0;
-}
-
-GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void)
-{
- CryptReleaseContext(hash_prov.prov.cryptoapi.handle, 0);
-
- hash_prov.type = INVALID;
-}
-
-static void sha1_shutdown(void)
-{
- if (hash_prov.type == CNG)
- hash_cng_prov_shutdown();
- else if(hash_prov.type == CRYPTOAPI)
- hash_cryptoapi_prov_shutdown();
-}
-
-int git_hash_sha1_global_init(void)
-{
- int error = 0;
-
- if (hash_prov.type != INVALID)
- return 0;
-
- if ((error = hash_cng_prov_init()) < 0)
- error = hash_cryptoapi_prov_init();
-
- if (!error)
- error = git_runtime_shutdown_register(sha1_shutdown);
-
- return error;
-}
-
-/* CryptoAPI: available in Windows XP and newer */
-
-GIT_INLINE(int) hash_ctx_cryptoapi_init(git_hash_sha1_ctx *ctx)
-{
- ctx->type = CRYPTOAPI;
- ctx->prov = &hash_prov;
-
- return git_hash_sha1_init(ctx);
-}
-
-GIT_INLINE(int) hash_cryptoapi_init(git_hash_sha1_ctx *ctx)
-{
- if (ctx->ctx.cryptoapi.valid)
- CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
-
- if (!CryptCreateHash(ctx->prov->prov.cryptoapi.handle, CALG_SHA1, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) {
- ctx->ctx.cryptoapi.valid = 0;
- git_error_set(GIT_ERROR_OS, "legacy hash implementation could not be created");
- return -1;
- }
-
- ctx->ctx.cryptoapi.valid = 1;
- return 0;
-}
-
-GIT_INLINE(int) hash_cryptoapi_update(git_hash_sha1_ctx *ctx, const void *_data, size_t len)
-{
- const BYTE *data = (BYTE *)_data;
-
- GIT_ASSERT(ctx->ctx.cryptoapi.valid);
-
- while (len > 0) {
- DWORD chunk = (len > MAXDWORD) ? MAXDWORD : (DWORD)len;
-
- if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, data, chunk, 0)) {
- git_error_set(GIT_ERROR_OS, "legacy hash data could not be updated");
- return -1;
- }
-
- data += chunk;
- len -= chunk;
- }
-
- return 0;
-}
-
-GIT_INLINE(int) hash_cryptoapi_final(unsigned char *out, git_hash_sha1_ctx *ctx)
-{
- DWORD len = GIT_HASH_SHA1_SIZE;
- int error = 0;
-
- GIT_ASSERT(ctx->ctx.cryptoapi.valid);
-
- if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out, &len, 0)) {
- git_error_set(GIT_ERROR_OS, "legacy hash data could not be finished");
- error = -1;
- }
-
- CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
- ctx->ctx.cryptoapi.valid = 0;
-
- return error;
-}
-
-GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_sha1_ctx *ctx)
-{
- if (ctx->ctx.cryptoapi.valid)
- CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
-}
-
-/* CNG: Available in Windows Server 2008 and newer */
-
-GIT_INLINE(int) hash_ctx_cng_init(git_hash_sha1_ctx *ctx)
-{
- if ((ctx->ctx.cng.hash_object = git__malloc(hash_prov.prov.cng.hash_object_size)) == NULL)
- return -1;
-
- if (hash_prov.prov.cng.create_hash(hash_prov.prov.cng.handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_prov.prov.cng.hash_object_size, NULL, 0, 0) < 0) {
- git__free(ctx->ctx.cng.hash_object);
-
- git_error_set(GIT_ERROR_OS, "hash implementation could not be created");
- return -1;
- }
-
- ctx->type = CNG;
- ctx->prov = &hash_prov;
-
- return 0;
-}
-
-GIT_INLINE(int) hash_cng_init(git_hash_sha1_ctx *ctx)
-{
- BYTE hash[GIT_OID_RAWSZ];
-
- if (!ctx->ctx.cng.updated)
- return 0;
-
- /* CNG needs to be finished to restart */
- if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0) {
- git_error_set(GIT_ERROR_OS, "hash implementation could not be finished");
- return -1;
- }
-
- ctx->ctx.cng.updated = 0;
-
- return 0;
-}
-
-GIT_INLINE(int) hash_cng_update(git_hash_sha1_ctx *ctx, const void *_data, size_t len)
-{
- PBYTE data = (PBYTE)_data;
-
- while (len > 0) {
- ULONG chunk = (len > ULONG_MAX) ? ULONG_MAX : (ULONG)len;
-
- if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, data, chunk, 0) < 0) {
- git_error_set(GIT_ERROR_OS, "hash could not be updated");
- return -1;
- }
-
- data += chunk;
- len -= chunk;
- }
-
- return 0;
-}
-
-GIT_INLINE(int) hash_cng_final(unsigned char *out, git_hash_sha1_ctx *ctx)
-{
- if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out, GIT_HASH_SHA1_SIZE, 0) < 0) {
- git_error_set(GIT_ERROR_OS, "hash could not be finished");
- return -1;
- }
-
- ctx->ctx.cng.updated = 0;
-
- return 0;
-}
-
-GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_sha1_ctx *ctx)
-{
- ctx->prov->prov.cng.destroy_hash(ctx->ctx.cng.hash_handle);
- git__free(ctx->ctx.cng.hash_object);
-}
-
-/* Indirection between CryptoAPI and CNG */
-
-int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
-{
- int error = 0;
-
- GIT_ASSERT_ARG(ctx);
-
- /*
- * When compiled with GIT_THREADS, the global hash_prov data is
- * initialized with git_libgit2_init. Otherwise, it must be initialized
- * at first use.
- */
- if (hash_prov.type == INVALID && (error = git_hash_sha1_global_init()) < 0)
- return error;
-
- memset(ctx, 0x0, sizeof(git_hash_sha1_ctx));
-
- return (hash_prov.type == CNG) ? hash_ctx_cng_init(ctx) : hash_ctx_cryptoapi_init(ctx);
-}
-
-int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
-{
- GIT_ASSERT_ARG(ctx);
- GIT_ASSERT_ARG(ctx->type);
- return (ctx->type == CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx);
-}
-
-int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
-{
- GIT_ASSERT_ARG(ctx);
- GIT_ASSERT_ARG(ctx->type);
- return (ctx->type == CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len);
-}
-
-int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
-{
- GIT_ASSERT_ARG(ctx);
- GIT_ASSERT_ARG(ctx->type);
- return (ctx->type == CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx);
-}
-
-void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
-{
- if (!ctx)
- return;
- else if (ctx->type == CNG)
- hash_ctx_cng_cleanup(ctx);
- else if(ctx->type == CRYPTOAPI)
- hash_ctx_cryptoapi_cleanup(ctx);
-}
diff --git a/src/hash/sha1/win32.h b/src/hash/sha1/win32.h
deleted file mode 100644
index 791d20a42..000000000
--- a/src/hash/sha1/win32.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_hash_sha1_win32_h__
-#define INCLUDE_hash_sha1_win32_h__
-
-#include "hash/sha1.h"
-
-#include
-#include
-
-enum hash_win32_prov_type {
- INVALID = 0,
- CRYPTOAPI,
- CNG
-};
-
-/*
- * CryptoAPI is available for hashing on Windows XP and newer.
- */
-
-struct hash_cryptoapi_prov {
- HCRYPTPROV handle;
-};
-
-/*
- * CNG (bcrypt.dll) is significantly more performant than CryptoAPI and is
- * preferred, however it is only available on Windows 2008 and newer and
- * must therefore be dynamically loaded, and we must inline constants that
- * would not exist when building in pre-Windows 2008 environments.
- */
-
-/* Function declarations for CNG */
-typedef NTSTATUS (WINAPI *hash_win32_cng_open_algorithm_provider_fn)(
- HANDLE /* BCRYPT_ALG_HANDLE */ *phAlgorithm,
- LPCWSTR pszAlgId,
- LPCWSTR pszImplementation,
- DWORD dwFlags);
-
-typedef NTSTATUS (WINAPI *hash_win32_cng_get_property_fn)(
- HANDLE /* BCRYPT_HANDLE */ hObject,
- LPCWSTR pszProperty,
- PUCHAR pbOutput,
- ULONG cbOutput,
- ULONG *pcbResult,
- ULONG dwFlags);
-
-typedef NTSTATUS (WINAPI *hash_win32_cng_create_hash_fn)(
- HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm,
- HANDLE /* BCRYPT_HASH_HANDLE */ *phHash,
- PUCHAR pbHashObject, ULONG cbHashObject,
- PUCHAR pbSecret,
- ULONG cbSecret,
- ULONG dwFlags);
-
-typedef NTSTATUS (WINAPI *hash_win32_cng_finish_hash_fn)(
- HANDLE /* BCRYPT_HASH_HANDLE */ hHash,
- PUCHAR pbOutput,
- ULONG cbOutput,
- ULONG dwFlags);
-
-typedef NTSTATUS (WINAPI *hash_win32_cng_hash_data_fn)(
- HANDLE /* BCRYPT_HASH_HANDLE */ hHash,
- PUCHAR pbInput,
- ULONG cbInput,
- ULONG dwFlags);
-
-typedef NTSTATUS (WINAPI *hash_win32_cng_destroy_hash_fn)(
- HANDLE /* BCRYPT_HASH_HANDLE */ hHash);
-
-typedef NTSTATUS (WINAPI *hash_win32_cng_close_algorithm_provider_fn)(
- HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm,
- ULONG dwFlags);
-
-struct hash_cng_prov {
- /* DLL for CNG */
- HINSTANCE dll;
-
- /* Function pointers for CNG */
- hash_win32_cng_open_algorithm_provider_fn open_algorithm_provider;
- hash_win32_cng_get_property_fn get_property;
- hash_win32_cng_create_hash_fn create_hash;
- hash_win32_cng_finish_hash_fn finish_hash;
- hash_win32_cng_hash_data_fn hash_data;
- hash_win32_cng_destroy_hash_fn destroy_hash;
- hash_win32_cng_close_algorithm_provider_fn close_algorithm_provider;
-
- HANDLE /* BCRYPT_ALG_HANDLE */ handle;
- DWORD hash_object_size;
-};
-
-typedef struct {
- enum hash_win32_prov_type type;
-
- union {
- struct hash_cryptoapi_prov cryptoapi;
- struct hash_cng_prov cng;
- } prov;
-} git_hash_prov;
-
-/* Hash contexts */
-
-struct hash_cryptoapi_ctx {
- bool valid;
- HCRYPTHASH hash_handle;
-};
-
-struct hash_cng_ctx {
- bool updated;
- HANDLE /* BCRYPT_HASH_HANDLE */ hash_handle;
- PBYTE hash_object;
-};
-
-struct git_hash_sha1_ctx {
- enum hash_win32_prov_type type;
- git_hash_prov *prov;
-
- union {
- struct hash_cryptoapi_ctx cryptoapi;
- struct hash_cng_ctx cng;
- } ctx;
-};
-
-#endif
diff --git a/src/libgit2/CMakeLists.txt b/src/libgit2/CMakeLists.txt
new file mode 100644
index 000000000..0c7ddddba
--- /dev/null
+++ b/src/libgit2/CMakeLists.txt
@@ -0,0 +1,131 @@
+# libgit2: the shared library: this CMakeLists.txt compiles the core
+# git library functionality.
+
+add_library(libgit2 OBJECT)
+set_target_properties(libgit2 PROPERTIES C_STANDARD 90)
+set_target_properties(libgit2 PROPERTIES C_EXTENSIONS OFF)
+
+include(PkgBuildConfig)
+
+set(LIBGIT2_INCLUDES
+ "${PROJECT_BINARY_DIR}/src"
+ "${PROJECT_SOURCE_DIR}/src/libgit2"
+ "${PROJECT_SOURCE_DIR}/src/util"
+ "${PROJECT_SOURCE_DIR}/include")
+
+if(WIN32 AND EMBED_SSH_PATH)
+ file(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
+ list(SORT SRC_SSH)
+ target_sources(libgit2 PRIVATE ${SRC_SSH})
+
+ list(APPEND LIBGIT2_SYSTEM_INCLUDES "${EMBED_SSH_PATH}/include")
+ file(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"")
+ set(GIT_SSH 1)
+endif()
+
+# Collect sourcefiles
+file(GLOB SRC_H
+ "${PROJECT_SOURCE_DIR}/include/git2.h"
+ "${PROJECT_SOURCE_DIR}/include/git2/*.h"
+ "${PROJECT_SOURCE_DIR}/include/git2/sys/*.h")
+list(SORT SRC_H)
+target_sources(libgit2 PRIVATE ${SRC_H})
+
+file(GLOB SRC_GIT2 *.c *.h
+ streams/*.c streams/*.h
+ transports/*.c transports/*.h
+ xdiff/*.c xdiff/*.h)
+list(SORT SRC_GIT2)
+target_sources(libgit2 PRIVATE ${SRC_GIT2})
+
+if(WIN32 AND NOT CYGWIN)
+ # Add resource information on Windows
+ set(SRC_RC "git2.rc")
+endif()
+
+if(APPLE)
+ # The old Secure Transport API has been deprecated in macOS 10.15.
+ set_source_files_properties(streams/stransport.c PROPERTIES COMPILE_FLAGS -Wno-deprecated)
+endif()
+
+# the xdiff dependency is not (yet) warning-free, disable warnings
+# as errors for the xdiff sources until we've sorted them out
+if(MSVC)
+ set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xdiff/xmerge.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS -WX-)
+else()
+ set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
+ set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
+ set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
+ set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
+ set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
+endif()
+
+ide_split_sources(libgit2)
+list(APPEND LIBGIT2_OBJECTS $ $ ${LIBGIT2_DEPENDENCY_OBJECTS})
+
+target_include_directories(libgit2 PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${PROJECT_SOURCE_DIR}/include)
+target_include_directories(libgit2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
+
+set(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE)
+set(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE)
+set(LIBGIT2_DEPENDENCY_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES} PARENT_SCOPE)
+set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE)
+set(LIBGIT2_SYSTEM_INCLUDES ${LIBGIT2_SYSTEM_INCLUDES} PARENT_SCOPE)
+set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE)
+
+#
+# Compile and link libgit2
+#
+
+add_library(libgit2package ${SRC_RC} ${LIBGIT2_OBJECTS})
+target_link_libraries(libgit2package ${LIBGIT2_SYSTEM_LIBS})
+
+set_target_properties(libgit2package PROPERTIES C_STANDARD 90)
+set_target_properties(libgit2package PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+set_target_properties(libgit2package PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+set_target_properties(libgit2package PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+
+# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
+# Win64+MSVC+static libs = linker error
+if(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
+ set_target_properties(libgit2package PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
+endif()
+
+ide_split_sources(libgit2package)
+
+if(SONAME)
+ set_target_properties(libgit2package PROPERTIES VERSION ${libgit2_VERSION})
+ set_target_properties(libgit2package PROPERTIES SOVERSION "${libgit2_VERSION_MAJOR}.${libgit2_VERSION_MINOR}")
+ if(LIBGIT2_FILENAME)
+ target_compile_definitions(libgit2package PRIVATE LIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
+ set_target_properties(libgit2package PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
+ elseif(DEFINED LIBGIT2_PREFIX)
+ set_target_properties(libgit2package PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
+ endif()
+endif()
+
+pkg_build_config(NAME libgit2
+ VERSION ${libgit2_VERSION}
+ DESCRIPTION "The git library, take 2"
+ LIBS_SELF git2
+ PRIVATE_LIBS ${LIBGIT2_PC_LIBS}
+ REQUIRES ${LIBGIT2_PC_REQUIRES})
+
+if(MSVC_IDE)
+ # Precompiled headers
+ set_target_properties(libgit2package PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
+ set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
+endif()
+
+# Install
+install(TARGETS libgit2package
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/git2 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+install(FILES ${PROJECT_SOURCE_DIR}/include/git2.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
diff --git a/src/annotated_commit.c b/src/libgit2/annotated_commit.c
similarity index 100%
rename from src/annotated_commit.c
rename to src/libgit2/annotated_commit.c
diff --git a/src/annotated_commit.h b/src/libgit2/annotated_commit.h
similarity index 100%
rename from src/annotated_commit.h
rename to src/libgit2/annotated_commit.h
diff --git a/src/apply.c b/src/libgit2/apply.c
similarity index 100%
rename from src/apply.c
rename to src/libgit2/apply.c
diff --git a/src/apply.h b/src/libgit2/apply.h
similarity index 100%
rename from src/apply.h
rename to src/libgit2/apply.h
diff --git a/src/attr.c b/src/libgit2/attr.c
similarity index 100%
rename from src/attr.c
rename to src/libgit2/attr.c
diff --git a/src/attr.h b/src/libgit2/attr.h
similarity index 100%
rename from src/attr.h
rename to src/libgit2/attr.h
diff --git a/src/attr_file.c b/src/libgit2/attr_file.c
similarity index 100%
rename from src/attr_file.c
rename to src/libgit2/attr_file.c
diff --git a/src/attr_file.h b/src/libgit2/attr_file.h
similarity index 100%
rename from src/attr_file.h
rename to src/libgit2/attr_file.h
diff --git a/src/attrcache.c b/src/libgit2/attrcache.c
similarity index 100%
rename from src/attrcache.c
rename to src/libgit2/attrcache.c
diff --git a/src/attrcache.h b/src/libgit2/attrcache.h
similarity index 100%
rename from src/attrcache.h
rename to src/libgit2/attrcache.h
diff --git a/src/blame.c b/src/libgit2/blame.c
similarity index 100%
rename from src/blame.c
rename to src/libgit2/blame.c
diff --git a/src/blame.h b/src/libgit2/blame.h
similarity index 100%
rename from src/blame.h
rename to src/libgit2/blame.h
diff --git a/src/blame_git.c b/src/libgit2/blame_git.c
similarity index 100%
rename from src/blame_git.c
rename to src/libgit2/blame_git.c
diff --git a/src/blame_git.h b/src/libgit2/blame_git.h
similarity index 100%
rename from src/blame_git.h
rename to src/libgit2/blame_git.h
diff --git a/src/blob.c b/src/libgit2/blob.c
similarity index 99%
rename from src/blob.c
rename to src/libgit2/blob.c
index 19ce8b3b5..b1680d3a8 100644
--- a/src/blob.c
+++ b/src/libgit2/blob.c
@@ -101,7 +101,7 @@ static int write_file_stream(
git_oid *id, git_odb *odb, const char *path, git_object_size_t file_size)
{
int fd, error;
- char buffer[FILEIO_BUFSIZE];
+ char buffer[GIT_BUFSIZE_FILEIO];
git_odb_stream *stream = NULL;
ssize_t read_len = -1;
git_object_size_t written = 0;
diff --git a/src/blob.h b/src/libgit2/blob.h
similarity index 100%
rename from src/blob.h
rename to src/libgit2/blob.h
diff --git a/src/branch.c b/src/libgit2/branch.c
similarity index 98%
rename from src/branch.c
rename to src/libgit2/branch.c
index 2e29af99d..2dd7d2bb4 100644
--- a/src/branch.c
+++ b/src/libgit2/branch.c
@@ -53,6 +53,17 @@ static int not_a_local_branch(const char *reference_name)
return -1;
}
+static bool branch_name_is_valid(const char *branch_name)
+{
+ /*
+ * Discourage branch name starting with dash,
+ * https://github.com/git/git/commit/6348624010888b
+ * and discourage HEAD as branch name,
+ * https://github.com/git/git/commit/a625b092cc5994
+ */
+ return branch_name[0] != '-' && git__strcmp(branch_name, "HEAD");
+}
+
static int create_branch(
git_reference **ref_out,
git_repository *repository,
@@ -73,8 +84,8 @@ static int create_branch(
GIT_ASSERT_ARG(ref_out);
GIT_ASSERT_ARG(git_commit_owner(commit) == repository);
- if (!git__strcmp(branch_name, "HEAD")) {
- git_error_set(GIT_ERROR_REFERENCE, "'HEAD' is not a valid branch name");
+ if (!branch_name_is_valid(branch_name)) {
+ git_error_set(GIT_ERROR_REFERENCE, "'%s' is not a valid branch name", branch_name);
error = -1;
goto cleanup;
}
@@ -797,13 +808,7 @@ int git_branch_name_is_valid(int *valid, const char *name)
*valid = 0;
- /*
- * Discourage branch name starting with dash,
- * https://github.com/git/git/commit/6348624010888b
- * and discourage HEAD as branch name,
- * https://github.com/git/git/commit/a625b092cc5994
- */
- if (!name || name[0] == '-' || !git__strcmp(name, "HEAD"))
+ if (!name || !branch_name_is_valid(name))
goto done;
if ((error = git_str_puts(&ref_name, GIT_REFS_HEADS_DIR)) < 0 ||
diff --git a/src/branch.h b/src/libgit2/branch.h
similarity index 100%
rename from src/branch.h
rename to src/libgit2/branch.h
diff --git a/src/buf.c b/src/libgit2/buf.c
similarity index 100%
rename from src/buf.c
rename to src/libgit2/buf.c
diff --git a/src/buf.h b/src/libgit2/buf.h
similarity index 100%
rename from src/buf.h
rename to src/libgit2/buf.h
diff --git a/src/cache.c b/src/libgit2/cache.c
similarity index 100%
rename from src/cache.c
rename to src/libgit2/cache.c
diff --git a/src/cache.h b/src/libgit2/cache.h
similarity index 100%
rename from src/cache.h
rename to src/libgit2/cache.h
diff --git a/src/checkout.c b/src/libgit2/checkout.c
similarity index 100%
rename from src/checkout.c
rename to src/libgit2/checkout.c
diff --git a/src/checkout.h b/src/libgit2/checkout.h
similarity index 100%
rename from src/checkout.h
rename to src/libgit2/checkout.h
diff --git a/src/cherrypick.c b/src/libgit2/cherrypick.c
similarity index 100%
rename from src/cherrypick.c
rename to src/libgit2/cherrypick.c
diff --git a/src/clone.c b/src/libgit2/clone.c
similarity index 100%
rename from src/clone.c
rename to src/libgit2/clone.c
diff --git a/src/clone.h b/src/libgit2/clone.h
similarity index 100%
rename from src/clone.h
rename to src/libgit2/clone.h
diff --git a/src/commit.c b/src/libgit2/commit.c
similarity index 100%
rename from src/commit.c
rename to src/libgit2/commit.c
diff --git a/src/commit.h b/src/libgit2/commit.h
similarity index 100%
rename from src/commit.h
rename to src/libgit2/commit.h
diff --git a/src/commit_graph.c b/src/libgit2/commit_graph.c
similarity index 97%
rename from src/commit_graph.c
rename to src/libgit2/commit_graph.c
index 70e866b92..10947acec 100644
--- a/src/commit_graph.c
+++ b/src/libgit2/commit_graph.c
@@ -138,7 +138,7 @@ static int commit_graph_parse_oid_lookup(
struct git_commit_graph_chunk *chunk_oid_lookup)
{
uint32_t i;
- git_oid *oid, *prev_oid, zero_oid = {{0}};
+ unsigned char *oid, *prev_oid, zero_oid[GIT_OID_RAWSZ] = {0};
if (chunk_oid_lookup->offset == 0)
return commit_graph_error("missing OID Lookup chunk");
@@ -147,10 +147,10 @@ static int commit_graph_parse_oid_lookup(
if (chunk_oid_lookup->length != file->num_commits * GIT_OID_RAWSZ)
return commit_graph_error("OID Lookup chunk has wrong length");
- file->oid_lookup = oid = (git_oid *)(data + chunk_oid_lookup->offset);
- prev_oid = &zero_oid;
- for (i = 0; i < file->num_commits; ++i, ++oid) {
- if (git_oid_cmp(prev_oid, oid) >= 0)
+ file->oid_lookup = oid = (unsigned char *)(data + chunk_oid_lookup->offset);
+ prev_oid = zero_oid;
+ for (i = 0; i < file->num_commits; ++i, oid += GIT_OID_RAWSZ) {
+ if (git_oid_raw_cmp(prev_oid, oid) >= 0)
return commit_graph_error("OID Lookup index is non-monotonic");
prev_oid = oid;
}
@@ -437,7 +437,7 @@ static int git_commit_graph_entry_get_byindex(
}
commit_data = file->commit_data + pos * (GIT_OID_RAWSZ + 4 * sizeof(uint32_t));
- git_oid_cpy(&e->tree_oid, (const git_oid *)commit_data);
+ git_oid_fromraw(&e->tree_oid, commit_data);
e->parent_indices[0] = ntohl(*((uint32_t *)(commit_data + GIT_OID_RAWSZ)));
e->parent_indices[1] = ntohl(
*((uint32_t *)(commit_data + GIT_OID_RAWSZ + sizeof(uint32_t))));
@@ -470,7 +470,8 @@ static int git_commit_graph_entry_get_byindex(
e->parent_count++;
}
}
- git_oid_cpy(&e->sha1, &file->oid_lookup[pos]);
+
+ git_oid_fromraw(&e->sha1, &file->oid_lookup[pos * GIT_OID_RAWSZ]);
return 0;
}
@@ -514,7 +515,7 @@ int git_commit_graph_entry_find(
{
int pos, found = 0;
uint32_t hi, lo;
- const git_oid *current = NULL;
+ const unsigned char *current = NULL;
GIT_ASSERT_ARG(e);
GIT_ASSERT_ARG(file);
@@ -528,26 +529,25 @@ int git_commit_graph_entry_find(
if (pos >= 0) {
/* An object matching exactly the oid was found */
found = 1;
- current = file->oid_lookup + pos;
+ current = file->oid_lookup + (pos * GIT_OID_RAWSZ);
} else {
/* No object was found */
/* pos refers to the object with the "closest" oid to short_oid */
pos = -1 - pos;
if (pos < (int)file->num_commits) {
- current = file->oid_lookup + pos;
+ current = file->oid_lookup + (pos * GIT_OID_RAWSZ);
- if (!git_oid_ncmp(short_oid, current, len))
+ if (!git_oid_raw_ncmp(short_oid->id, current, len))
found = 1;
}
}
if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)file->num_commits) {
/* Check for ambiguousity */
- const git_oid *next = current + 1;
+ const unsigned char *next = current + GIT_OID_RAWSZ;
- if (!git_oid_ncmp(short_oid, next, len)) {
+ if (!git_oid_raw_ncmp(short_oid->id, next, len))
found = 2;
- }
}
if (!found)
@@ -1019,7 +1019,9 @@ static int commit_graph_write(
/* Fill the OID Lookup table. */
git_vector_foreach (&w->commits, i, packed_commit) {
error = git_str_put(&oid_lookup,
- (const char *)&packed_commit->sha1, sizeof(git_oid));
+ (const char *)&packed_commit->sha1.id,
+ GIT_OID_RAWSZ);
+
if (error < 0)
goto cleanup;
}
@@ -1034,8 +1036,9 @@ static int commit_graph_write(
unsigned int parentcount = (unsigned int)git_array_size(packed_commit->parents);
error = git_str_put(&commit_data,
- (const char *)&packed_commit->tree_oid,
- sizeof(git_oid));
+ (const char *)&packed_commit->tree_oid.id,
+ GIT_OID_RAWSZ);
+
if (error < 0)
goto cleanup;
diff --git a/src/commit_graph.h b/src/libgit2/commit_graph.h
similarity index 99%
rename from src/commit_graph.h
rename to src/libgit2/commit_graph.h
index 45e125b9e..b78ab8177 100644
--- a/src/commit_graph.h
+++ b/src/libgit2/commit_graph.h
@@ -36,7 +36,7 @@ typedef struct git_commit_graph_file {
uint32_t num_commits;
/* The OID Lookup table. */
- git_oid *oid_lookup;
+ unsigned char *oid_lookup;
/*
* The Commit Data table. Each entry contains the OID of the commit followed
diff --git a/src/commit_list.c b/src/libgit2/commit_list.c
similarity index 100%
rename from src/commit_list.c
rename to src/libgit2/commit_list.c
diff --git a/src/commit_list.h b/src/libgit2/commit_list.h
similarity index 100%
rename from src/commit_list.h
rename to src/libgit2/commit_list.h
diff --git a/src/libgit2/common.h b/src/libgit2/common.h
new file mode 100644
index 000000000..bb9ec5ac1
--- /dev/null
+++ b/src/libgit2/common.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_common_h__
+#define INCLUDE_common_h__
+
+#include "git2_util.h"
+#include "errors.h"
+
+/*
+* Include the declarations for deprecated functions; this ensures
+* that they're decorated with the proper extern/visibility attributes.
+*/
+#include "git2/deprecated.h"
+
+#include "posix.h"
+
+/**
+ * Initialize a structure with a version.
+ */
+GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int version)
+{
+ memset(structure, 0, len);
+ *((int*)structure) = version;
+}
+#define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V)
+
+#define GIT_INIT_STRUCTURE_FROM_TEMPLATE(PTR,VERSION,TYPE,TPL) do { \
+ TYPE _tmpl = TPL; \
+ GIT_ERROR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \
+ memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0)
+
+/**
+ * Check a versioned structure for validity
+ */
+GIT_INLINE(int) git_error__check_version(const void *structure, unsigned int expected_max, const char *name)
+{
+ unsigned int actual;
+
+ if (!structure)
+ return 0;
+
+ actual = *(const unsigned int*)structure;
+ if (actual > 0 && actual <= expected_max)
+ return 0;
+
+ git_error_set(GIT_ERROR_INVALID, "invalid version %d on %s", actual, name);
+ return -1;
+}
+#define GIT_ERROR_CHECK_VERSION(S,V,N) if (git_error__check_version(S,V,N) < 0) return -1
+
+#endif
diff --git a/src/config.c b/src/libgit2/config.c
similarity index 99%
rename from src/config.c
rename to src/libgit2/config.c
index 6bd59f2a5..5c366e221 100644
--- a/src/config.c
+++ b/src/libgit2/config.c
@@ -1137,7 +1137,7 @@ int git_config__find_global(git_str *path)
int git_config_find_xdg(git_buf *path)
{
- GIT_BUF_WRAP_PRIVATE(path, git_sysdir_find_global_file, GIT_CONFIG_FILENAME_XDG);
+ GIT_BUF_WRAP_PRIVATE(path, git_sysdir_find_xdg_file, GIT_CONFIG_FILENAME_XDG);
}
int git_config__find_xdg(git_str *path)
@@ -1147,7 +1147,7 @@ int git_config__find_xdg(git_str *path)
int git_config_find_system(git_buf *path)
{
- GIT_BUF_WRAP_PRIVATE(path, git_sysdir_find_global_file, GIT_CONFIG_FILENAME_SYSTEM);
+ GIT_BUF_WRAP_PRIVATE(path, git_sysdir_find_system_file, GIT_CONFIG_FILENAME_SYSTEM);
}
int git_config__find_system(git_str *path)
@@ -1170,10 +1170,13 @@ int git_config_find_programdata(git_buf *path)
int git_config__find_programdata(git_str *path)
{
+ git_fs_path_owner_t owner_level =
+ GIT_FS_PATH_OWNER_CURRENT_USER |
+ GIT_FS_PATH_OWNER_ADMINISTRATOR;
bool is_safe;
if (git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA) < 0 ||
- git_fs_path_owner_is_system_or_current_user(&is_safe, path->ptr) < 0)
+ git_fs_path_owner_is(&is_safe, path->ptr, owner_level) < 0)
return -1;
if (!is_safe) {
diff --git a/src/config.h b/src/libgit2/config.h
similarity index 100%
rename from src/config.h
rename to src/libgit2/config.h
diff --git a/src/config_backend.h b/src/libgit2/config_backend.h
similarity index 100%
rename from src/config_backend.h
rename to src/libgit2/config_backend.h
diff --git a/src/config_cache.c b/src/libgit2/config_cache.c
similarity index 100%
rename from src/config_cache.c
rename to src/libgit2/config_cache.c
diff --git a/src/config_entries.c b/src/libgit2/config_entries.c
similarity index 100%
rename from src/config_entries.c
rename to src/libgit2/config_entries.c
diff --git a/src/config_entries.h b/src/libgit2/config_entries.h
similarity index 100%
rename from src/config_entries.h
rename to src/libgit2/config_entries.h
diff --git a/src/config_file.c b/src/libgit2/config_file.c
similarity index 100%
rename from src/config_file.c
rename to src/libgit2/config_file.c
diff --git a/src/config_mem.c b/src/libgit2/config_mem.c
similarity index 100%
rename from src/config_mem.c
rename to src/libgit2/config_mem.c
diff --git a/src/config_parse.c b/src/libgit2/config_parse.c
similarity index 100%
rename from src/config_parse.c
rename to src/libgit2/config_parse.c
diff --git a/src/config_parse.h b/src/libgit2/config_parse.h
similarity index 100%
rename from src/config_parse.h
rename to src/libgit2/config_parse.h
diff --git a/src/config_snapshot.c b/src/libgit2/config_snapshot.c
similarity index 100%
rename from src/config_snapshot.c
rename to src/libgit2/config_snapshot.c
diff --git a/src/crlf.c b/src/libgit2/crlf.c
similarity index 100%
rename from src/crlf.c
rename to src/libgit2/crlf.c
diff --git a/src/delta.c b/src/libgit2/delta.c
similarity index 100%
rename from src/delta.c
rename to src/libgit2/delta.c
diff --git a/src/delta.h b/src/libgit2/delta.h
similarity index 100%
rename from src/delta.h
rename to src/libgit2/delta.h
diff --git a/src/describe.c b/src/libgit2/describe.c
similarity index 100%
rename from src/describe.c
rename to src/libgit2/describe.c
diff --git a/src/diff.c b/src/libgit2/diff.c
similarity index 100%
rename from src/diff.c
rename to src/libgit2/diff.c
diff --git a/src/diff.h b/src/libgit2/diff.h
similarity index 100%
rename from src/diff.h
rename to src/libgit2/diff.h
diff --git a/src/diff_driver.c b/src/libgit2/diff_driver.c
similarity index 100%
rename from src/diff_driver.c
rename to src/libgit2/diff_driver.c
diff --git a/src/diff_driver.h b/src/libgit2/diff_driver.h
similarity index 100%
rename from src/diff_driver.h
rename to src/libgit2/diff_driver.h
diff --git a/src/diff_file.c b/src/libgit2/diff_file.c
similarity index 100%
rename from src/diff_file.c
rename to src/libgit2/diff_file.c
diff --git a/src/diff_file.h b/src/libgit2/diff_file.h
similarity index 100%
rename from src/diff_file.h
rename to src/libgit2/diff_file.h
diff --git a/src/diff_generate.c b/src/libgit2/diff_generate.c
similarity index 100%
rename from src/diff_generate.c
rename to src/libgit2/diff_generate.c
diff --git a/src/diff_generate.h b/src/libgit2/diff_generate.h
similarity index 100%
rename from src/diff_generate.h
rename to src/libgit2/diff_generate.h
diff --git a/src/diff_parse.c b/src/libgit2/diff_parse.c
similarity index 100%
rename from src/diff_parse.c
rename to src/libgit2/diff_parse.c
diff --git a/src/diff_parse.h b/src/libgit2/diff_parse.h
similarity index 100%
rename from src/diff_parse.h
rename to src/libgit2/diff_parse.h
diff --git a/src/diff_print.c b/src/libgit2/diff_print.c
similarity index 100%
rename from src/diff_print.c
rename to src/libgit2/diff_print.c
diff --git a/src/diff_stats.c b/src/libgit2/diff_stats.c
similarity index 100%
rename from src/diff_stats.c
rename to src/libgit2/diff_stats.c
diff --git a/src/diff_stats.h b/src/libgit2/diff_stats.h
similarity index 100%
rename from src/diff_stats.h
rename to src/libgit2/diff_stats.h
diff --git a/src/diff_tform.c b/src/libgit2/diff_tform.c
similarity index 100%
rename from src/diff_tform.c
rename to src/libgit2/diff_tform.c
diff --git a/src/diff_tform.h b/src/libgit2/diff_tform.h
similarity index 100%
rename from src/diff_tform.h
rename to src/libgit2/diff_tform.h
diff --git a/src/diff_xdiff.c b/src/libgit2/diff_xdiff.c
similarity index 99%
rename from src/diff_xdiff.c
rename to src/libgit2/diff_xdiff.c
index 3f6eccac1..5f56c5209 100644
--- a/src/diff_xdiff.c
+++ b/src/libgit2/diff_xdiff.c
@@ -11,6 +11,7 @@
#include "diff.h"
#include "diff_driver.h"
#include "patch_generate.h"
+#include "utf8.h"
static int git_xdiff_scan_int(const char **str, int *value)
{
diff --git a/src/diff_xdiff.h b/src/libgit2/diff_xdiff.h
similarity index 100%
rename from src/diff_xdiff.h
rename to src/libgit2/diff_xdiff.h
diff --git a/src/email.c b/src/libgit2/email.c
similarity index 100%
rename from src/email.c
rename to src/libgit2/email.c
diff --git a/src/email.h b/src/libgit2/email.h
similarity index 100%
rename from src/email.h
rename to src/libgit2/email.h
diff --git a/src/errors.c b/src/libgit2/errors.c
similarity index 100%
rename from src/errors.c
rename to src/libgit2/errors.c
diff --git a/src/errors.h b/src/libgit2/errors.h
similarity index 93%
rename from src/errors.h
rename to src/libgit2/errors.h
index a2f60f752..772c7bad1 100644
--- a/src/errors.h
+++ b/src/libgit2/errors.h
@@ -11,9 +11,8 @@
#include "common.h"
/*
- * Set the error message for this thread, formatting as needed.
+ * `vprintf`-style formatting for the error message for this thread.
*/
-void git_error_set(int error_class, const char *fmt, ...) GIT_FORMAT_PRINTF(2, 3);
void git_error_vset(int error_class, const char *fmt, va_list ap);
/**
diff --git a/src/fetch.c b/src/libgit2/fetch.c
similarity index 100%
rename from src/fetch.c
rename to src/libgit2/fetch.c
diff --git a/src/fetch.h b/src/libgit2/fetch.h
similarity index 100%
rename from src/fetch.h
rename to src/libgit2/fetch.h
diff --git a/src/fetchhead.c b/src/libgit2/fetchhead.c
similarity index 100%
rename from src/fetchhead.c
rename to src/libgit2/fetchhead.c
diff --git a/src/fetchhead.h b/src/libgit2/fetchhead.h
similarity index 100%
rename from src/fetchhead.h
rename to src/libgit2/fetchhead.h
diff --git a/src/filter.c b/src/libgit2/filter.c
similarity index 96%
rename from src/filter.c
rename to src/libgit2/filter.c
index 2712e8c60..80a3cae67 100644
--- a/src/filter.c
+++ b/src/libgit2/filter.c
@@ -893,6 +893,17 @@ static int buffered_stream_write(
return git_str_put(&buffered_stream->input, buffer, len);
}
+#ifndef GIT_DEPRECATE_HARD
+# define BUF_TO_STRUCT(b, s) \
+ (b)->ptr = (s)->ptr; \
+ (b)->size = (s)->size; \
+ (b)->reserved = (s)->asize;
+# define STRUCT_TO_BUF(s, b) \
+ (s)->ptr = (b)->ptr; \
+ (s)->size = (b)->size; \
+ (s)->asize = (b)->reserved;
+#endif
+
static int buffered_stream_close(git_writestream *s)
{
struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
@@ -902,6 +913,25 @@ static int buffered_stream_close(git_writestream *s)
GIT_ASSERT_ARG(buffered_stream);
+#ifndef GIT_DEPRECATE_HARD
+ if (buffered_stream->write_fn == NULL) {
+ git_buf legacy_output = GIT_BUF_INIT,
+ legacy_input = GIT_BUF_INIT;
+
+ BUF_TO_STRUCT(&legacy_output, buffered_stream->output);
+ BUF_TO_STRUCT(&legacy_input, &buffered_stream->input);
+
+ error = buffered_stream->legacy_write_fn(
+ buffered_stream->filter,
+ buffered_stream->payload,
+ &legacy_output,
+ &legacy_input,
+ buffered_stream->source);
+
+ STRUCT_TO_BUF(buffered_stream->output, &legacy_output);
+ STRUCT_TO_BUF(&buffered_stream->input, &legacy_input);
+ } else
+#endif
error = buffered_stream->write_fn(
buffered_stream->filter,
buffered_stream->payload,
@@ -1085,7 +1115,7 @@ int git_filter_list_stream_file(
const char *path,
git_writestream *target)
{
- char buf[FILTERIO_BUFSIZE];
+ char buf[GIT_BUFSIZE_FILTERIO];
git_str abspath = GIT_STR_INIT;
const char *base = repo ? git_repository_workdir(repo) : NULL;
git_vector filter_streams = GIT_VECTOR_INIT;
diff --git a/src/filter.h b/src/libgit2/filter.h
similarity index 100%
rename from src/filter.h
rename to src/libgit2/filter.h
diff --git a/src/win32/git2.rc b/src/libgit2/git2.rc
similarity index 100%
rename from src/win32/git2.rc
rename to src/libgit2/git2.rc
diff --git a/src/graph.c b/src/libgit2/graph.c
similarity index 100%
rename from src/graph.c
rename to src/libgit2/graph.c
diff --git a/src/hashsig.c b/src/libgit2/hashsig.c
similarity index 100%
rename from src/hashsig.c
rename to src/libgit2/hashsig.c
diff --git a/src/ident.c b/src/libgit2/ident.c
similarity index 100%
rename from src/ident.c
rename to src/libgit2/ident.c
diff --git a/src/idxmap.c b/src/libgit2/idxmap.c
similarity index 100%
rename from src/idxmap.c
rename to src/libgit2/idxmap.c
diff --git a/src/idxmap.h b/src/libgit2/idxmap.h
similarity index 100%
rename from src/idxmap.h
rename to src/libgit2/idxmap.h
diff --git a/src/ignore.c b/src/libgit2/ignore.c
similarity index 100%
rename from src/ignore.c
rename to src/libgit2/ignore.c
diff --git a/src/ignore.h b/src/libgit2/ignore.h
similarity index 100%
rename from src/ignore.h
rename to src/libgit2/ignore.h
diff --git a/src/index.c b/src/libgit2/index.c
similarity index 99%
rename from src/index.c
rename to src/libgit2/index.c
index aa97c6421..f44c507d3 100644
--- a/src/index.c
+++ b/src/libgit2/index.c
@@ -74,7 +74,7 @@ struct entry_short {
uint32_t uid;
uint32_t gid;
uint32_t file_size;
- git_oid oid;
+ unsigned char oid[GIT_OID_RAWSZ];
uint16_t flags;
char path[1]; /* arbitrary length */
};
@@ -88,7 +88,7 @@ struct entry_long {
uint32_t uid;
uint32_t gid;
uint32_t file_size;
- git_oid oid;
+ unsigned char oid[GIT_OID_RAWSZ];
uint16_t flags;
uint16_t flags_extended;
char path[1]; /* arbitrary length */
@@ -2480,9 +2480,11 @@ static int read_entry(
entry.uid = ntohl(source.uid);
entry.gid = ntohl(source.gid);
entry.file_size = ntohl(source.file_size);
- git_oid_cpy(&entry.id, &source.oid);
entry.flags = ntohs(source.flags);
+ if (git_oid_fromraw(&entry.id, source.oid) < 0)
+ return -1;
+
if (entry.flags & GIT_INDEX_ENTRY_EXTENDED) {
uint16_t flags_raw;
size_t flags_offset;
@@ -2803,9 +2805,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha
ondisk.uid = htonl(entry->uid);
ondisk.gid = htonl(entry->gid);
ondisk.file_size = htonl((uint32_t)entry->file_size);
-
- git_oid_cpy(&ondisk.oid, &entry->id);
-
+ git_oid_raw_cpy(ondisk.oid, entry->id.id);
ondisk.flags = htons(entry->flags);
if (entry->flags & GIT_INDEX_ENTRY_EXTENDED) {
diff --git a/src/index.h b/src/libgit2/index.h
similarity index 100%
rename from src/index.h
rename to src/libgit2/index.h
diff --git a/src/indexer.c b/src/libgit2/indexer.c
similarity index 99%
rename from src/indexer.c
rename to src/libgit2/indexer.c
index f9a32e7ac..afb9ce81a 100644
--- a/src/indexer.c
+++ b/src/libgit2/indexer.c
@@ -385,7 +385,7 @@ static int check_object_connectivity(git_indexer *idx, const git_rawobj *obj)
size_t i;
git_array_foreach(tree->entries, i, entry)
- if (add_expected_oid(idx, entry->oid) < 0)
+ if (add_expected_oid(idx, &entry->oid) < 0)
goto out;
break;
@@ -1269,7 +1269,7 @@ int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
/* Write out the object names (SHA-1 hashes) */
git_vector_foreach(&idx->objects, i, entry) {
- git_filebuf_write(&index_file, &entry->oid, sizeof(git_oid));
+ git_filebuf_write(&index_file, &entry->oid.id, GIT_OID_RAWSZ);
}
/* Write out the CRC32 values */
diff --git a/src/indexer.h b/src/libgit2/indexer.h
similarity index 100%
rename from src/indexer.h
rename to src/libgit2/indexer.h
diff --git a/src/iterator.c b/src/libgit2/iterator.c
similarity index 99%
rename from src/iterator.c
rename to src/libgit2/iterator.c
index 15bb63dc8..bc6f766ce 100644
--- a/src/iterator.c
+++ b/src/libgit2/iterator.c
@@ -613,7 +613,7 @@ GIT_INLINE(int) tree_iterator_frame_push_neighbors(
break;
if ((error = git_tree_lookup(&tree,
- iter->base.repo, entry->tree_entry->oid)) < 0)
+ iter->base.repo, &entry->tree_entry->oid)) < 0)
break;
if (git_vector_insert(&parent_frame->similar_trees, tree) < 0)
@@ -659,7 +659,7 @@ GIT_INLINE(int) tree_iterator_frame_push(
int error;
if ((error = git_tree_lookup(&tree,
- iter->base.repo, entry->tree_entry->oid)) < 0 ||
+ iter->base.repo, &entry->tree_entry->oid)) < 0 ||
(error = tree_iterator_frame_init(iter, tree, entry)) < 0)
goto done;
@@ -740,7 +740,7 @@ static void tree_iterator_set_current(
iter->entry.mode = tree_entry->attr;
iter->entry.path = iter->entry_path.ptr;
- git_oid_cpy(&iter->entry.id, tree_entry->oid);
+ git_oid_cpy(&iter->entry.id, &tree_entry->oid);
}
static int tree_iterator_advance(const git_index_entry **out, git_iterator *i)
diff --git a/src/iterator.h b/src/libgit2/iterator.h
similarity index 100%
rename from src/iterator.h
rename to src/libgit2/iterator.h
diff --git a/src/libgit2.c b/src/libgit2/libgit2.c
similarity index 99%
rename from src/libgit2.c
rename to src/libgit2/libgit2.c
index c99d5b942..2fda0722e 100644
--- a/src/libgit2.c
+++ b/src/libgit2/libgit2.c
@@ -107,6 +107,11 @@ int git_libgit2_version(int *major, int *minor, int *rev)
return 0;
}
+const char *git_libgit2_prerelease(void)
+{
+ return LIBGIT2_VER_PRERELEASE;
+}
+
int git_libgit2_features(void)
{
return 0
diff --git a/src/libgit2.h b/src/libgit2/libgit2.h
similarity index 100%
rename from src/libgit2.h
rename to src/libgit2/libgit2.h
diff --git a/src/mailmap.c b/src/libgit2/mailmap.c
similarity index 100%
rename from src/mailmap.c
rename to src/libgit2/mailmap.c
diff --git a/src/mailmap.h b/src/libgit2/mailmap.h
similarity index 100%
rename from src/mailmap.h
rename to src/libgit2/mailmap.h
diff --git a/src/merge.c b/src/libgit2/merge.c
similarity index 100%
rename from src/merge.c
rename to src/libgit2/merge.c
diff --git a/src/merge.h b/src/libgit2/merge.h
similarity index 100%
rename from src/merge.h
rename to src/libgit2/merge.h
diff --git a/src/merge_driver.c b/src/libgit2/merge_driver.c
similarity index 100%
rename from src/merge_driver.c
rename to src/libgit2/merge_driver.c
diff --git a/src/merge_driver.h b/src/libgit2/merge_driver.h
similarity index 100%
rename from src/merge_driver.h
rename to src/libgit2/merge_driver.h
diff --git a/src/merge_file.c b/src/libgit2/merge_file.c
similarity index 100%
rename from src/merge_file.c
rename to src/libgit2/merge_file.c
diff --git a/src/message.c b/src/libgit2/message.c
similarity index 100%
rename from src/message.c
rename to src/libgit2/message.c
diff --git a/src/midx.c b/src/libgit2/midx.c
similarity index 95%
rename from src/midx.c
rename to src/libgit2/midx.c
index 0092601f6..67eab9acb 100644
--- a/src/midx.c
+++ b/src/libgit2/midx.c
@@ -115,7 +115,7 @@ static int midx_parse_oid_lookup(
struct git_midx_chunk *chunk_oid_lookup)
{
uint32_t i;
- git_oid *oid, *prev_oid, zero_oid = {{0}};
+ unsigned char *oid, *prev_oid, zero_oid[GIT_OID_RAWSZ] = {0};
if (chunk_oid_lookup->offset == 0)
return midx_error("missing OID Lookup chunk");
@@ -124,10 +124,10 @@ static int midx_parse_oid_lookup(
if (chunk_oid_lookup->length != idx->num_objects * GIT_OID_RAWSZ)
return midx_error("OID Lookup chunk has wrong length");
- idx->oid_lookup = oid = (git_oid *)(data + chunk_oid_lookup->offset);
- prev_oid = &zero_oid;
- for (i = 0; i < idx->num_objects; ++i, ++oid) {
- if (git_oid_cmp(prev_oid, oid) >= 0)
+ idx->oid_lookup = oid = (unsigned char *)(data + chunk_oid_lookup->offset);
+ prev_oid = zero_oid;
+ for (i = 0; i < idx->num_objects; ++i, oid += GIT_OID_RAWSZ) {
+ if (git_oid_raw_cmp(prev_oid, oid) >= 0)
return midx_error("OID Lookup index is non-monotonic");
prev_oid = oid;
}
@@ -179,7 +179,6 @@ int git_midx_parse(
uint32_t i;
off64_t last_chunk_offset, chunk_offset, trailer_offset;
size_t checksum_size;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
int error;
struct git_midx_chunk chunk_packfile_names = {0},
chunk_oid_fanout = {0},
@@ -217,11 +216,6 @@ int git_midx_parse(
return midx_error("wrong index size");
memcpy(idx->checksum, data + trailer_offset, checksum_size);
- if (git_hash_buf(checksum, data, (size_t)trailer_offset, GIT_HASH_ALGORITHM_SHA1) < 0)
- return midx_error("could not calculate signature");
- if (memcmp(checksum, idx->checksum, checksum_size) != 0)
- return midx_error("index signature mismatch");
-
chunk_hdr = data + sizeof(struct git_midx_header);
last_chunk = NULL;
for (i = 0; i < hdr->chunks; ++i, chunk_hdr += 12) {
@@ -389,7 +383,7 @@ int git_midx_entry_find(
int pos, found = 0;
size_t pack_index;
uint32_t hi, lo;
- const git_oid *current = NULL;
+ unsigned char *current = NULL;
const unsigned char *object_offset;
off64_t offset;
@@ -403,26 +397,25 @@ int git_midx_entry_find(
if (pos >= 0) {
/* An object matching exactly the oid was found */
found = 1;
- current = idx->oid_lookup + pos;
+ current = idx->oid_lookup + (pos * GIT_OID_RAWSZ);
} else {
/* No object was found */
/* pos refers to the object with the "closest" oid to short_oid */
pos = -1 - pos;
if (pos < (int)idx->num_objects) {
- current = idx->oid_lookup + pos;
+ current = idx->oid_lookup + (pos * GIT_OID_RAWSZ);
- if (!git_oid_ncmp(short_oid, current, len))
+ if (!git_oid_raw_ncmp(short_oid->id, current, len))
found = 1;
}
}
if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)idx->num_objects) {
/* Check for ambiguousity */
- const git_oid *next = current + 1;
+ const unsigned char *next = current + GIT_OID_RAWSZ;
- if (!git_oid_ncmp(short_oid, next, len)) {
+ if (!git_oid_raw_ncmp(short_oid->id, next, len))
found = 2;
- }
}
if (!found)
@@ -432,8 +425,8 @@ int git_midx_entry_find(
object_offset = idx->object_offsets + pos * 8;
offset = ntohl(*((uint32_t *)(object_offset + 4)));
- if (offset & 0x80000000) {
- uint32_t object_large_offsets_pos = offset & 0x7fffffff;
+ if (idx->object_large_offsets && offset & 0x80000000) {
+ uint32_t object_large_offsets_pos = (uint32_t) (offset ^ 0x80000000);
const unsigned char *object_large_offsets_index = idx->object_large_offsets;
/* Make sure we're not being sent out of bounds */
@@ -450,7 +443,7 @@ int git_midx_entry_find(
return midx_error("invalid index into the packfile names table");
e->pack_index = pack_index;
e->offset = offset;
- git_oid_cpy(&e->sha1, current);
+ git_oid_fromraw(&e->sha1, current);
return 0;
}
@@ -459,13 +452,17 @@ int git_midx_foreach_entry(
git_odb_foreach_cb cb,
void *data)
{
+ git_oid oid;
size_t i;
int error;
GIT_ASSERT_ARG(idx);
for (i = 0; i < idx->num_objects; ++i) {
- if ((error = cb(&idx->oid_lookup[i], data)) != 0)
+ if ((error = git_oid_fromraw(&oid, &idx->oid_lookup[i * GIT_OID_RAWSZ])) < 0)
+ return error;
+
+ if ((error = cb(&oid, data)) != 0)
return git_error_set_after_callback(error);
}
@@ -751,7 +748,7 @@ static int midx_write(
/* Fill the OID Lookup table. */
git_vector_foreach (&object_entries, i, entry) {
- error = git_str_put(&oid_lookup, (const char *)&entry->sha1, sizeof(entry->sha1));
+ error = git_str_put(&oid_lookup, (char *)&entry->sha1.id, GIT_OID_RAWSZ);
if (error < 0)
goto cleanup;
}
diff --git a/src/midx.h b/src/libgit2/midx.h
similarity index 98%
rename from src/midx.h
rename to src/libgit2/midx.h
index 7dd851bd3..bcb0d9a0a 100644
--- a/src/midx.h
+++ b/src/libgit2/midx.h
@@ -17,6 +17,7 @@
#include "map.h"
#include "mwindow.h"
#include "odb.h"
+#include "oid.h"
/*
* A multi-pack-index file.
@@ -40,7 +41,7 @@ typedef struct git_midx_file {
uint32_t num_objects;
/* The OID Lookup table. */
- git_oid *oid_lookup;
+ unsigned char *oid_lookup;
/* The Object Offsets table. Each entry has two 4-byte fields with the pack index and the offset. */
const unsigned char *object_offsets;
diff --git a/src/mwindow.c b/src/libgit2/mwindow.c
similarity index 95%
rename from src/mwindow.c
rename to src/libgit2/mwindow.c
index d06b7a80e..ad649490a 100644
--- a/src/mwindow.c
+++ b/src/libgit2/mwindow.c
@@ -186,13 +186,16 @@ int git_mwindow_free_all(git_mwindow_file *mwf)
}
/*
- * Check if a window 'win' contains the address 'offset'
+ * Check if a window 'win' contains both the address 'offset' and 'extra'.
+ *
+ * 'extra' is the size of the hash we're using as we always want to make sure
+ * that it's contained.
*/
-int git_mwindow_contains(git_mwindow *win, off64_t offset)
+int git_mwindow_contains(git_mwindow *win, off64_t offset, off64_t extra)
{
off64_t win_off = win->offset;
return win_off <= offset
- && offset <= (off64_t)(win_off + win->window_map.len);
+ && (offset + extra) <= (off64_t)(win_off + win->window_map.len);
}
#define GIT_MWINDOW__LRU -1
@@ -237,9 +240,7 @@ static bool git_mwindow_scan_recently_used(
* store it in the output parameter. If lru_window is NULL,
* it's the first loop, so store it as well.
*/
- if (!lru_window ||
- (comparison_sign == GIT_MWINDOW__LRU && lru_window->last_used > w->last_used) ||
- (comparison_sign == GIT_MWINDOW__MRU && lru_window->last_used < w->last_used)) {
+ if (!lru_window || (comparison_sign * w->last_used) > lru_window->last_used) {
lru_window = w;
lru_last = w_last;
found = true;
@@ -406,14 +407,13 @@ unsigned char *git_mwindow_open(
return NULL;
}
- if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) {
+ if (!w || !(git_mwindow_contains(w, offset, extra))) {
if (w) {
w->inuse_cnt--;
}
for (w = mwf->windows; w; w = w->next) {
- if (git_mwindow_contains(w, offset) &&
- git_mwindow_contains(w, offset + extra))
+ if (git_mwindow_contains(w, offset, extra))
break;
}
diff --git a/src/mwindow.h b/src/libgit2/mwindow.h
similarity index 95%
rename from src/mwindow.h
rename to src/libgit2/mwindow.h
index e3a03f019..e32ab99d4 100644
--- a/src/mwindow.h
+++ b/src/libgit2/mwindow.h
@@ -38,7 +38,7 @@ typedef struct git_mwindow_ctl {
git_vector windowfiles;
} git_mwindow_ctl;
-int git_mwindow_contains(git_mwindow *win, off64_t offset);
+int git_mwindow_contains(git_mwindow *win, off64_t offset, off64_t extra);
int git_mwindow_free_all(git_mwindow_file *mwf); /* locks */
unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, off64_t offset, size_t extra, unsigned int *left);
int git_mwindow_file_register(git_mwindow_file *mwf);
diff --git a/src/netops.c b/src/libgit2/netops.c
similarity index 99%
rename from src/netops.c
rename to src/libgit2/netops.c
index 0a27365b8..00640c600 100644
--- a/src/netops.c
+++ b/src/libgit2/netops.c
@@ -12,7 +12,6 @@
#include "posix.h"
#include "str.h"
-#include "http_parser.h"
#include "runtime.h"
int gitno_recv(gitno_buffer *buf)
diff --git a/src/netops.h b/src/libgit2/netops.h
similarity index 100%
rename from src/netops.h
rename to src/libgit2/netops.h
diff --git a/src/notes.c b/src/libgit2/notes.c
similarity index 100%
rename from src/notes.c
rename to src/libgit2/notes.c
diff --git a/src/notes.h b/src/libgit2/notes.h
similarity index 100%
rename from src/notes.h
rename to src/libgit2/notes.h
diff --git a/src/object.c b/src/libgit2/object.c
similarity index 99%
rename from src/object.c
rename to src/libgit2/object.c
index 7bc256fce..b828f88f6 100644
--- a/src/object.c
+++ b/src/libgit2/object.c
@@ -250,6 +250,7 @@ int git_object_lookup_prefix(
if (error < 0)
return error;
+ GIT_ASSERT(odb_obj);
error = git_object__from_odb_object(object_out, repo, odb_obj, type);
git_odb_object_free(odb_obj);
diff --git a/src/object.h b/src/libgit2/object.h
similarity index 100%
rename from src/object.h
rename to src/libgit2/object.h
diff --git a/src/object_api.c b/src/libgit2/object_api.c
similarity index 100%
rename from src/object_api.c
rename to src/libgit2/object_api.c
diff --git a/src/odb.c b/src/libgit2/odb.c
similarity index 99%
rename from src/odb.c
rename to src/libgit2/odb.c
index 14eff53c7..7b98c72ee 100644
--- a/src/odb.c
+++ b/src/libgit2/odb.c
@@ -198,7 +198,7 @@ void git_odb_object_free(git_odb_object *object)
int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_object_t type)
{
size_t hdr_len;
- char hdr[64], buffer[FILEIO_BUFSIZE];
+ char hdr[64], buffer[GIT_BUFSIZE_FILEIO];
git_hash_ctx ctx;
ssize_t read_len = 0;
int error = 0;
@@ -1067,7 +1067,7 @@ int git_odb_expand_ids(
int git_odb_read_header(size_t *len_p, git_object_t *type_p, git_odb *db, const git_oid *id)
{
int error;
- git_odb_object *object;
+ git_odb_object *object = NULL;
error = git_odb__read_header_or_object(&object, len_p, type_p, db, id);
diff --git a/src/odb.h b/src/libgit2/odb.h
similarity index 100%
rename from src/odb.h
rename to src/libgit2/odb.h
diff --git a/src/odb_loose.c b/src/libgit2/odb_loose.c
similarity index 100%
rename from src/odb_loose.c
rename to src/libgit2/odb_loose.c
diff --git a/src/odb_mempack.c b/src/libgit2/odb_mempack.c
similarity index 100%
rename from src/odb_mempack.c
rename to src/libgit2/odb_mempack.c
diff --git a/src/odb_pack.c b/src/libgit2/odb_pack.c
similarity index 100%
rename from src/odb_pack.c
rename to src/libgit2/odb_pack.c
diff --git a/src/offmap.c b/src/libgit2/offmap.c
similarity index 100%
rename from src/offmap.c
rename to src/libgit2/offmap.c
diff --git a/src/offmap.h b/src/libgit2/offmap.h
similarity index 100%
rename from src/offmap.h
rename to src/libgit2/offmap.h
diff --git a/src/oid.c b/src/libgit2/oid.c
similarity index 96%
rename from src/oid.c
rename to src/libgit2/oid.c
index 19061e899..fb92174ab 100644
--- a/src/oid.c
+++ b/src/libgit2/oid.c
@@ -187,8 +187,7 @@ int git_oid_fromraw(git_oid *out, const unsigned char *raw)
int git_oid_cpy(git_oid *out, const git_oid *src)
{
- memcpy(out->id, src->id, sizeof(out->id));
- return 0;
+ return git_oid_raw_cpy(out->id, src->id);
}
int git_oid_cmp(const git_oid *a, const git_oid *b)
@@ -203,25 +202,7 @@ int git_oid_equal(const git_oid *a, const git_oid *b)
int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len)
{
- const unsigned char *a = oid_a->id;
- const unsigned char *b = oid_b->id;
-
- if (len > GIT_OID_HEXSZ)
- len = GIT_OID_HEXSZ;
-
- while (len > 1) {
- if (*a != *b)
- return 1;
- a++;
- b++;
- len -= 2;
- };
-
- if (len)
- if ((*a ^ *b) & 0xf0)
- return 1;
-
- return 0;
+ return git_oid_raw_ncmp(oid_a->id, oid_b->id, len);
}
int git_oid_strcmp(const git_oid *oid_a, const char *str)
diff --git a/src/oid.h b/src/libgit2/oid.h
similarity index 70%
rename from src/oid.h
rename to src/libgit2/oid.h
index 5baec33e5..abae9a4b2 100644
--- a/src/oid.h
+++ b/src/libgit2/oid.h
@@ -25,11 +25,44 @@ extern const git_oid git_oid__empty_tree_sha1;
*/
char *git_oid_allocfmt(const git_oid *id);
-GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2)
+GIT_INLINE(int) git_oid_raw_ncmp(
+ const unsigned char *sha1,
+ const unsigned char *sha2,
+ size_t len)
+{
+ if (len > GIT_OID_HEXSZ)
+ len = GIT_OID_HEXSZ;
+
+ while (len > 1) {
+ if (*sha1 != *sha2)
+ return 1;
+ sha1++;
+ sha2++;
+ len -= 2;
+ };
+
+ if (len)
+ if ((*sha1 ^ *sha2) & 0xf0)
+ return 1;
+
+ return 0;
+}
+
+GIT_INLINE(int) git_oid_raw_cmp(
+ const unsigned char *sha1,
+ const unsigned char *sha2)
{
return memcmp(sha1, sha2, GIT_OID_RAWSZ);
}
+GIT_INLINE(int) git_oid_raw_cpy(
+ unsigned char *dst,
+ const unsigned char *src)
+{
+ memcpy(dst, src, GIT_OID_RAWSZ);
+ return 0;
+}
+
/*
* Compare two oid structures.
*
@@ -39,7 +72,7 @@ GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char
*/
GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
{
- return git_oid__hashcmp(a->id, b->id);
+ return git_oid_raw_cmp(a->id, b->id);
}
GIT_INLINE(void) git_oid__cpy_prefix(
diff --git a/src/oidarray.c b/src/libgit2/oidarray.c
similarity index 100%
rename from src/oidarray.c
rename to src/libgit2/oidarray.c
diff --git a/src/oidarray.h b/src/libgit2/oidarray.h
similarity index 100%
rename from src/oidarray.h
rename to src/libgit2/oidarray.h
diff --git a/src/oidmap.c b/src/libgit2/oidmap.c
similarity index 98%
rename from src/oidmap.c
rename to src/libgit2/oidmap.c
index 0ae8bf33e..eaf9fa051 100644
--- a/src/oidmap.c
+++ b/src/libgit2/oidmap.c
@@ -19,7 +19,7 @@ __KHASH_TYPE(oid, const git_oid *, void *)
GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid)
{
khint_t h;
- memcpy(&h, oid, sizeof(khint_t));
+ memcpy(&h, oid->id, sizeof(khint_t));
return h;
}
diff --git a/src/oidmap.h b/src/libgit2/oidmap.h
similarity index 100%
rename from src/oidmap.h
rename to src/libgit2/oidmap.h
diff --git a/src/pack-objects.c b/src/libgit2/pack-objects.c
similarity index 100%
rename from src/pack-objects.c
rename to src/libgit2/pack-objects.c
diff --git a/src/pack-objects.h b/src/libgit2/pack-objects.h
similarity index 100%
rename from src/pack-objects.h
rename to src/libgit2/pack-objects.h
diff --git a/src/pack.c b/src/libgit2/pack.c
similarity index 97%
rename from src/pack.c
rename to src/libgit2/pack.c
index 5c0cba7e8..16fe378bd 100644
--- a/src/pack.c
+++ b/src/libgit2/pack.c
@@ -1001,13 +1001,14 @@ int get_delta_base(
base_offset = delta_obj_offset - unsigned_base_offset;
*curpos += used;
} else if (type == GIT_OBJECT_REF_DELTA) {
+ git_oid base_oid;
+ git_oid_fromraw(&base_oid, base_info);
+
/* If we have the cooperative cache, search in it first */
if (p->has_cache) {
struct git_pack_entry *entry;
- git_oid oid;
- git_oid_fromraw(&oid, base_info);
- if ((entry = git_oidmap_get(p->idx_cache, &oid)) != NULL) {
+ if ((entry = git_oidmap_get(p->idx_cache, &base_oid)) != NULL) {
if (entry->offset == 0)
return packfile_error("delta offset is zero");
@@ -1024,7 +1025,7 @@ int get_delta_base(
}
/* The base entry _must_ be in the same pack */
- if (pack_entry_find_offset(&base_offset, &unused, p, (git_oid *)base_info, GIT_OID_HEXSZ) < 0)
+ if (pack_entry_find_offset(&base_offset, &unused, p, &base_oid, GIT_OID_HEXSZ) < 0)
return packfile_error("base entry delta is not in the same pack");
*curpos += 20;
} else
@@ -1082,7 +1083,7 @@ static int packfile_open_locked(struct git_pack_file *p)
{
struct stat st;
struct git_pack_header hdr;
- git_oid sha1;
+ unsigned char sha1[GIT_OID_RAWSZ];
unsigned char *idx_sha1;
if (pack_index_open_locked(p) < 0)
@@ -1130,12 +1131,12 @@ static int packfile_open_locked(struct git_pack_file *p)
/* Verify the pack matches its index. */
if (p->num_objects != ntohl(hdr.hdr_entries) ||
- p_pread(p->mwf.fd, sha1.id, GIT_OID_RAWSZ, p->mwf.size - GIT_OID_RAWSZ) < 0)
+ p_pread(p->mwf.fd, sha1, GIT_OID_RAWSZ, p->mwf.size - GIT_OID_RAWSZ) < 0)
goto cleanup;
idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40;
- if (git_oid__cmp(&sha1, (git_oid *)idx_sha1) != 0)
+ if (git_oid_raw_cmp(sha1, idx_sha1) != 0)
goto cleanup;
if (git_mwindow_file_register(&p->mwf) < 0)
@@ -1340,10 +1341,14 @@ int git_pack_foreach_entry(
}
git_vector_free(&offsets);
- p->oids = (git_oid **)git_vector_detach(NULL, NULL, &oids);
+ p->oids = (unsigned char **)git_vector_detach(NULL, NULL, &oids);
}
- /* We need to copy the OIDs to another array before we relinquish the lock to avoid races. */
+ /*
+ * We need to copy the OIDs to another array before we
+ * relinquish the lock to avoid races. We can also take
+ * this opportunity to put them into normal form.
+ */
git_array_init_to_size(oids, p->num_objects);
if (!oids.ptr) {
git_mutex_unlock(&p->lock);
@@ -1357,7 +1362,7 @@ int git_pack_foreach_entry(
git_array_clear(oids);
GIT_ERROR_CHECK_ALLOC(oid);
}
- git_oid_cpy(oid, p->oids[i]);
+ git_oid_fromraw(oid, p->oids[i]);
}
git_mutex_unlock(&p->lock);
@@ -1380,7 +1385,7 @@ int git_pack_foreach_entry_offset(
{
const unsigned char *index;
off64_t current_offset;
- const git_oid *current_oid;
+ git_oid current_oid;
uint32_t i;
int error = 0;
@@ -1422,8 +1427,9 @@ int git_pack_foreach_entry_offset(
current_offset = (((off64_t)ntohl(*((uint32_t *)(large_offset_ptr + 0)))) << 32) |
ntohl(*((uint32_t *)(large_offset_ptr + 4)));
}
- current_oid = (const git_oid *)(index + 20 * i);
- if ((error = cb(current_oid, current_offset, data)) != 0) {
+
+ git_oid_fromraw(¤t_oid, (index + 20 * i));
+ if ((error = cb(¤t_oid, current_offset, data)) != 0) {
error = git_error_set_after_callback(error);
goto cleanup;
}
@@ -1431,8 +1437,8 @@ int git_pack_foreach_entry_offset(
} else {
for (i = 0; i < p->num_objects; i++) {
current_offset = ntohl(*(const uint32_t *)(index + 24 * i));
- current_oid = (const git_oid *)(index + 24 * i + 4);
- if ((error = cb(current_oid, current_offset, data)) != 0) {
+ git_oid_fromraw(¤t_oid, (index + 24 * i + 4));
+ if ((error = cb(¤t_oid, current_offset, data)) != 0) {
error = git_error_set_after_callback(error);
goto cleanup;
}
@@ -1451,7 +1457,7 @@ int git_pack__lookup_sha1(const void *oid_lookup_table, size_t stride, unsigned
while (lo < hi) {
unsigned mi = (lo + hi) / 2;
- int cmp = git_oid__hashcmp(base + mi * stride, oid_prefix);
+ int cmp = git_oid_raw_cmp(base + mi * stride, oid_prefix);
if (!cmp)
return mi;
@@ -1530,7 +1536,7 @@ static int pack_entry_find_offset(
if (pos < (int)p->num_objects) {
current = index + pos * stride;
- if (!git_oid_ncmp(short_oid, (const git_oid *)current, len))
+ if (!git_oid_raw_ncmp(short_oid->id, current, len))
found = 1;
}
}
@@ -1539,7 +1545,7 @@ static int pack_entry_find_offset(
/* Check for ambiguousity */
const unsigned char *next = current + stride;
- if (!git_oid_ncmp(short_oid, (const git_oid *)next, len)) {
+ if (!git_oid_raw_ncmp(short_oid->id, next, len)) {
found = 2;
}
}
diff --git a/src/pack.h b/src/libgit2/pack.h
similarity index 98%
rename from src/pack.h
rename to src/libgit2/pack.h
index bf279c6b6..d90588f79 100644
--- a/src/pack.h
+++ b/src/libgit2/pack.h
@@ -19,6 +19,7 @@
#include "offmap.h"
#include "oidmap.h"
#include "zstream.h"
+#include "oid.h"
/**
* Function type for callbacks from git_pack_foreach_entry_offset.
@@ -32,7 +33,7 @@ typedef int git_pack_foreach_entry_offset_cb(
#define PACK_SIGNATURE 0x5041434b /* "PACK" */
#define PACK_VERSION 2
-#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))
+#define pack_version_ok(v) ((v) == htonl(2))
struct git_pack_header {
uint32_t hdr_signature;
uint32_t hdr_version;
@@ -104,7 +105,7 @@ struct git_pack_file {
git_time_t mtime;
unsigned pack_local:1, pack_keep:1, has_cache:1;
git_oidmap *idx_cache;
- git_oid **oids;
+ unsigned char **oids;
git_pack_cache bases; /* delta base cache */
diff --git a/src/parse.c b/src/libgit2/parse.c
similarity index 100%
rename from src/parse.c
rename to src/libgit2/parse.c
diff --git a/src/parse.h b/src/libgit2/parse.h
similarity index 100%
rename from src/parse.h
rename to src/libgit2/parse.h
diff --git a/src/patch.c b/src/libgit2/patch.c
similarity index 100%
rename from src/patch.c
rename to src/libgit2/patch.c
diff --git a/src/patch.h b/src/libgit2/patch.h
similarity index 100%
rename from src/patch.h
rename to src/libgit2/patch.h
diff --git a/src/patch_generate.c b/src/libgit2/patch_generate.c
similarity index 100%
rename from src/patch_generate.c
rename to src/libgit2/patch_generate.c
diff --git a/src/patch_generate.h b/src/libgit2/patch_generate.h
similarity index 100%
rename from src/patch_generate.h
rename to src/libgit2/patch_generate.h
diff --git a/src/patch_parse.c b/src/libgit2/patch_parse.c
similarity index 100%
rename from src/patch_parse.c
rename to src/libgit2/patch_parse.c
diff --git a/src/patch_parse.h b/src/libgit2/patch_parse.h
similarity index 100%
rename from src/patch_parse.h
rename to src/libgit2/patch_parse.h
diff --git a/src/path.c b/src/libgit2/path.c
similarity index 99%
rename from src/path.c
rename to src/libgit2/path.c
index 05a3dc2cf..a19340efe 100644
--- a/src/path.c
+++ b/src/libgit2/path.c
@@ -9,6 +9,7 @@
#include "repository.h"
#include "fs_path.h"
+#include "utf8.h"
typedef struct {
git_repository *repo;
diff --git a/src/path.h b/src/libgit2/path.h
similarity index 100%
rename from src/path.h
rename to src/libgit2/path.h
diff --git a/src/pathspec.c b/src/libgit2/pathspec.c
similarity index 100%
rename from src/pathspec.c
rename to src/libgit2/pathspec.c
diff --git a/src/pathspec.h b/src/libgit2/pathspec.h
similarity index 100%
rename from src/pathspec.h
rename to src/libgit2/pathspec.h
diff --git a/src/proxy.c b/src/libgit2/proxy.c
similarity index 100%
rename from src/proxy.c
rename to src/libgit2/proxy.c
diff --git a/src/proxy.h b/src/libgit2/proxy.h
similarity index 100%
rename from src/proxy.h
rename to src/libgit2/proxy.h
diff --git a/src/push.c b/src/libgit2/push.c
similarity index 100%
rename from src/push.c
rename to src/libgit2/push.c
diff --git a/src/push.h b/src/libgit2/push.h
similarity index 100%
rename from src/push.h
rename to src/libgit2/push.h
diff --git a/src/reader.c b/src/libgit2/reader.c
similarity index 100%
rename from src/reader.c
rename to src/libgit2/reader.c
diff --git a/src/reader.h b/src/libgit2/reader.h
similarity index 100%
rename from src/reader.h
rename to src/libgit2/reader.h
diff --git a/src/rebase.c b/src/libgit2/rebase.c
similarity index 99%
rename from src/rebase.c
rename to src/libgit2/rebase.c
index 6f01d3990..5e48f0dfb 100644
--- a/src/rebase.c
+++ b/src/libgit2/rebase.c
@@ -35,6 +35,7 @@
#define ONTO_FILE "onto"
#define ONTO_NAME_FILE "onto_name"
#define QUIET_FILE "quiet"
+#define INTERACTIVE_FILE "interactive"
#define MSGNUM_FILE "msgnum"
#define END_FILE "end"
@@ -92,6 +93,7 @@ static int rebase_state_type(
git_repository *repo)
{
git_str path = GIT_STR_INIT;
+ git_str interactive_path = GIT_STR_INIT;
git_rebase_t type = GIT_REBASE_NONE;
if (git_str_joinpath(&path, repo->gitdir, REBASE_APPLY_DIR) < 0)
@@ -107,7 +109,14 @@ static int rebase_state_type(
return -1;
if (git_fs_path_isdir(git_str_cstr(&path))) {
- type = GIT_REBASE_MERGE;
+ if (git_str_joinpath(&interactive_path, path.ptr, INTERACTIVE_FILE) < 0)
+ return -1;
+
+ if (git_fs_path_isfile(interactive_path.ptr))
+ type = GIT_REBASE_INTERACTIVE;
+ else
+ type = GIT_REBASE_MERGE;
+
goto done;
}
@@ -118,6 +127,7 @@ done:
*path_out = git_str_detach(&path);
git_str_dispose(&path);
+ git_str_dispose(&interactive_path);
return 0;
}
diff --git a/src/refdb.c b/src/libgit2/refdb.c
similarity index 100%
rename from src/refdb.c
rename to src/libgit2/refdb.c
diff --git a/src/refdb.h b/src/libgit2/refdb.h
similarity index 100%
rename from src/refdb.h
rename to src/libgit2/refdb.h
diff --git a/src/refdb_fs.c b/src/libgit2/refdb_fs.c
similarity index 99%
rename from src/refdb_fs.c
rename to src/libgit2/refdb_fs.c
index 95bda9404..0f49b16bb 100644
--- a/src/refdb_fs.c
+++ b/src/libgit2/refdb_fs.c
@@ -740,7 +740,7 @@ static int packed_lookup(
return 0;
}
}
- return GIT_ENOTFOUND;
+ return ref_error_notfound(ref_name);
parse_failed:
git_error_set(GIT_ERROR_REFERENCE, "corrupted packed references file");
@@ -1361,7 +1361,11 @@ static int packed_write(refdb_fs_backend *backend)
for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) {
struct packref *ref = git_sortedcache_entry(refcache, i);
- GIT_ASSERT(ref);
+
+ GIT_ASSERT_WITH_CLEANUP(ref, {
+ error = -1;
+ goto fail;
+ });
if ((error = packed_find_peel(backend, ref)) < 0)
goto fail;
diff --git a/src/reflog.c b/src/libgit2/reflog.c
similarity index 100%
rename from src/reflog.c
rename to src/libgit2/reflog.c
diff --git a/src/reflog.h b/src/libgit2/reflog.h
similarity index 100%
rename from src/reflog.h
rename to src/libgit2/reflog.h
diff --git a/src/refs.c b/src/libgit2/refs.c
similarity index 100%
rename from src/refs.c
rename to src/libgit2/refs.c
diff --git a/src/refs.h b/src/libgit2/refs.h
similarity index 100%
rename from src/refs.h
rename to src/libgit2/refs.h
diff --git a/src/refspec.c b/src/libgit2/refspec.c
similarity index 100%
rename from src/refspec.c
rename to src/libgit2/refspec.c
diff --git a/src/refspec.h b/src/libgit2/refspec.h
similarity index 100%
rename from src/refspec.h
rename to src/libgit2/refspec.h
diff --git a/src/remote.c b/src/libgit2/remote.c
similarity index 99%
rename from src/remote.c
rename to src/libgit2/remote.c
index 1a79faaab..3d0593712 100644
--- a/src/remote.c
+++ b/src/libgit2/remote.c
@@ -1830,7 +1830,7 @@ static int update_one_tip(
}
if (error == GIT_ENOTFOUND) {
- memset(&old, 0, GIT_OID_RAWSZ);
+ memset(&old, 0, sizeof(git_oid));
error = 0;
if (autotag && (error = git_vector_insert(update_heads, head)) < 0)
diff --git a/src/remote.h b/src/libgit2/remote.h
similarity index 95%
rename from src/remote.h
rename to src/libgit2/remote.h
index ea9c7d17f..41ee58e0f 100644
--- a/src/remote.h
+++ b/src/libgit2/remote.h
@@ -11,6 +11,7 @@
#include "git2/remote.h"
#include "git2/transport.h"
+#include "git2/sys/remote.h"
#include "git2/sys/transport.h"
#include "refspec.h"
@@ -53,7 +54,6 @@ int git_remote_connect_options_normalize(
git_remote_connect_options *dst,
git_repository *repo,
const git_remote_connect_options *src);
-void git_remote_connect_options_dispose(git_remote_connect_options *opts);
int git_remote_capabilities(unsigned int *out, git_remote *remote);
diff --git a/src/repo_template.h b/src/libgit2/repo_template.h
similarity index 100%
rename from src/repo_template.h
rename to src/libgit2/repo_template.h
diff --git a/src/repository.c b/src/libgit2/repository.c
similarity index 97%
rename from src/repository.c
rename to src/libgit2/repository.c
index 6119c1c70..f761b5f32 100644
--- a/src/repository.c
+++ b/src/libgit2/repository.c
@@ -488,7 +488,7 @@ static int read_gitfile(git_str *path_out, const char *file_path)
typedef struct {
const char *repo_path;
git_str tmp;
- bool is_safe;
+ bool *is_safe;
} validate_ownership_data;
static int validate_ownership_cb(const git_config_entry *entry, void *payload)
@@ -496,49 +496,102 @@ static int validate_ownership_cb(const git_config_entry *entry, void *payload)
validate_ownership_data *data = payload;
if (strcmp(entry->value, "") == 0)
- data->is_safe = false;
+ *data->is_safe = false;
if (git_fs_path_prettify_dir(&data->tmp, entry->value, NULL) == 0 &&
strcmp(data->tmp.ptr, data->repo_path) == 0)
- data->is_safe = true;
+ *data->is_safe = true;
return 0;
}
-static int validate_ownership(const char *repo_path)
+static int validate_ownership_config(bool *is_safe, const char *path)
{
- git_config *config = NULL;
- validate_ownership_data data = { repo_path, GIT_STR_INIT, false };
- bool is_safe;
+ validate_ownership_data ownership_data = {
+ path, GIT_STR_INIT, is_safe
+ };
+ git_config *config;
int error;
- if ((error = git_fs_path_owner_is_current_user(&is_safe, repo_path)) < 0) {
- if (error == GIT_ENOTFOUND)
- error = 0;
+ if (load_global_config(&config) != 0)
+ return 0;
- goto done;
- }
+ error = git_config_get_multivar_foreach(config,
+ "safe.directory", NULL,
+ validate_ownership_cb,
+ &ownership_data);
- if (is_safe) {
+ git_config_free(config);
+ git_str_dispose(&ownership_data.tmp);
+
+ return error;
+}
+
+static int validate_ownership_path(bool *is_safe, const char *path)
+{
+ git_fs_path_owner_t owner_level =
+ GIT_FS_PATH_OWNER_CURRENT_USER |
+ GIT_FS_PATH_USER_IS_ADMINISTRATOR |
+ GIT_FS_PATH_OWNER_RUNNING_SUDO;
+ int error = 0;
+
+ if (path)
+ error = git_fs_path_owner_is(is_safe, path, owner_level);
+
+ if (error == GIT_ENOTFOUND) {
+ *is_safe = true;
error = 0;
- goto done;
}
- if (load_global_config(&config) == 0) {
- error = git_config_get_multivar_foreach(config, "safe.directory", NULL, validate_ownership_cb, &data);
+ return error;
+}
- if (!error && data.is_safe)
+static int validate_ownership(git_repository *repo)
+{
+ const char *validation_paths[3] = { NULL }, *path;
+ size_t validation_len = 0, i;
+ bool is_safe = false;
+ int error = 0;
+
+ /*
+ * If there's a worktree, validate the permissions to it *and*
+ * the git directory, and use the worktree as the configuration
+ * key for allowlisting the directory. In a bare setup, only
+ * look at the gitdir and use that as the allowlist. So we
+ * examine all `validation_paths` but use only the first as
+ * the configuration lookup.
+ */
+
+ if (repo->workdir)
+ validation_paths[validation_len++] = repo->workdir;
+
+ if (repo->gitlink)
+ validation_paths[validation_len++] = repo->gitlink;
+
+ validation_paths[validation_len++] = repo->gitdir;
+
+ for (i = 0; i < validation_len; i++) {
+ path = validation_paths[i];
+
+ if ((error = validate_ownership_path(&is_safe, path)) < 0)
goto done;
+
+ if (!is_safe)
+ break;
}
- git_error_set(GIT_ERROR_CONFIG,
- "repository path '%s' is not owned by current user",
- repo_path);
- error = GIT_EOWNER;
+ if (is_safe ||
+ (error = validate_ownership_config(&is_safe, validation_paths[0])) < 0)
+ goto done;
+
+ if (!is_safe) {
+ git_error_set(GIT_ERROR_CONFIG,
+ "repository path '%s' is not owned by current user",
+ path);
+ error = GIT_EOWNER;
+ }
done:
- git_config_free(config);
- git_str_dispose(&data.tmp);
return error;
}
@@ -915,7 +968,6 @@ int git_repository_open_ext(
gitlink = GIT_STR_INIT, commondir = GIT_STR_INIT;
git_repository *repo = NULL;
git_config *config = NULL;
- const char *validation_path;
int version = 0;
if (flags & GIT_REPOSITORY_OPEN_FROM_ENV)
@@ -974,12 +1026,11 @@ int git_repository_open_ext(
}
/*
- * Ensure that the git directory is owned by the current user.
+ * Ensure that the git directory and worktree are
+ * owned by the current user.
*/
- validation_path = repo->is_bare ? repo->gitdir : repo->workdir;
-
if (git_repository__validate_ownership &&
- (error = validate_ownership(validation_path)) < 0)
+ (error = validate_ownership(repo)) < 0)
goto cleanup;
cleanup:
@@ -1530,6 +1581,7 @@ static int check_valid_extension(const git_config_entry *entry, void *payload)
}
for (i = 0; i < ARRAY_SIZE(builtin_extensions); i++) {
+ git_str_clear(&cfg);
extension = builtin_extensions[i];
if ((error = git_str_printf(&cfg, "extensions.%s", extension)) < 0)
diff --git a/src/repository.h b/src/libgit2/repository.h
similarity index 100%
rename from src/repository.h
rename to src/libgit2/repository.h
diff --git a/src/reset.c b/src/libgit2/reset.c
similarity index 100%
rename from src/reset.c
rename to src/libgit2/reset.c
diff --git a/src/revert.c b/src/libgit2/revert.c
similarity index 100%
rename from src/revert.c
rename to src/libgit2/revert.c
diff --git a/src/revparse.c b/src/libgit2/revparse.c
similarity index 100%
rename from src/revparse.c
rename to src/libgit2/revparse.c
diff --git a/src/revwalk.c b/src/libgit2/revwalk.c
similarity index 100%
rename from src/revwalk.c
rename to src/libgit2/revwalk.c
diff --git a/src/revwalk.h b/src/libgit2/revwalk.h
similarity index 100%
rename from src/revwalk.h
rename to src/libgit2/revwalk.h
diff --git a/src/settings.h b/src/libgit2/settings.h
similarity index 100%
rename from src/settings.h
rename to src/libgit2/settings.h
diff --git a/src/signature.c b/src/libgit2/signature.c
similarity index 100%
rename from src/signature.c
rename to src/libgit2/signature.c
diff --git a/src/signature.h b/src/libgit2/signature.h
similarity index 100%
rename from src/signature.h
rename to src/libgit2/signature.h
diff --git a/src/stash.c b/src/libgit2/stash.c
similarity index 100%
rename from src/stash.c
rename to src/libgit2/stash.c
diff --git a/src/status.c b/src/libgit2/status.c
similarity index 100%
rename from src/status.c
rename to src/libgit2/status.c
diff --git a/src/status.h b/src/libgit2/status.h
similarity index 100%
rename from src/status.h
rename to src/libgit2/status.h
diff --git a/src/strarray.c b/src/libgit2/strarray.c
similarity index 100%
rename from src/strarray.c
rename to src/libgit2/strarray.c
diff --git a/src/stream.h b/src/libgit2/stream.h
similarity index 100%
rename from src/stream.h
rename to src/libgit2/stream.h
diff --git a/src/streams/mbedtls.c b/src/libgit2/streams/mbedtls.c
similarity index 100%
rename from src/streams/mbedtls.c
rename to src/libgit2/streams/mbedtls.c
diff --git a/src/streams/mbedtls.h b/src/libgit2/streams/mbedtls.h
similarity index 100%
rename from src/streams/mbedtls.h
rename to src/libgit2/streams/mbedtls.h
diff --git a/src/streams/openssl.c b/src/libgit2/streams/openssl.c
similarity index 100%
rename from src/streams/openssl.c
rename to src/libgit2/streams/openssl.c
diff --git a/src/streams/openssl.h b/src/libgit2/streams/openssl.h
similarity index 100%
rename from src/streams/openssl.h
rename to src/libgit2/streams/openssl.h
diff --git a/src/streams/openssl_dynamic.c b/src/libgit2/streams/openssl_dynamic.c
similarity index 100%
rename from src/streams/openssl_dynamic.c
rename to src/libgit2/streams/openssl_dynamic.c
diff --git a/src/streams/openssl_dynamic.h b/src/libgit2/streams/openssl_dynamic.h
similarity index 100%
rename from src/streams/openssl_dynamic.h
rename to src/libgit2/streams/openssl_dynamic.h
diff --git a/src/streams/openssl_legacy.c b/src/libgit2/streams/openssl_legacy.c
similarity index 100%
rename from src/streams/openssl_legacy.c
rename to src/libgit2/streams/openssl_legacy.c
diff --git a/src/streams/openssl_legacy.h b/src/libgit2/streams/openssl_legacy.h
similarity index 100%
rename from src/streams/openssl_legacy.h
rename to src/libgit2/streams/openssl_legacy.h
diff --git a/src/streams/registry.c b/src/libgit2/streams/registry.c
similarity index 100%
rename from src/streams/registry.c
rename to src/libgit2/streams/registry.c
diff --git a/src/streams/registry.h b/src/libgit2/streams/registry.h
similarity index 100%
rename from src/streams/registry.h
rename to src/libgit2/streams/registry.h
diff --git a/src/streams/socket.c b/src/libgit2/streams/socket.c
similarity index 100%
rename from src/streams/socket.c
rename to src/libgit2/streams/socket.c
diff --git a/src/streams/socket.h b/src/libgit2/streams/socket.h
similarity index 100%
rename from src/streams/socket.h
rename to src/libgit2/streams/socket.h
diff --git a/src/streams/stransport.c b/src/libgit2/streams/stransport.c
similarity index 100%
rename from src/streams/stransport.c
rename to src/libgit2/streams/stransport.c
diff --git a/src/streams/stransport.h b/src/libgit2/streams/stransport.h
similarity index 100%
rename from src/streams/stransport.h
rename to src/libgit2/streams/stransport.h
diff --git a/src/streams/tls.c b/src/libgit2/streams/tls.c
similarity index 100%
rename from src/streams/tls.c
rename to src/libgit2/streams/tls.c
diff --git a/src/streams/tls.h b/src/libgit2/streams/tls.h
similarity index 100%
rename from src/streams/tls.h
rename to src/libgit2/streams/tls.h
diff --git a/src/submodule.c b/src/libgit2/submodule.c
similarity index 100%
rename from src/submodule.c
rename to src/libgit2/submodule.c
diff --git a/src/submodule.h b/src/libgit2/submodule.h
similarity index 100%
rename from src/submodule.h
rename to src/libgit2/submodule.h
diff --git a/src/sysdir.c b/src/libgit2/sysdir.c
similarity index 100%
rename from src/sysdir.c
rename to src/libgit2/sysdir.c
diff --git a/src/sysdir.h b/src/libgit2/sysdir.h
similarity index 100%
rename from src/sysdir.h
rename to src/libgit2/sysdir.h
diff --git a/src/tag.c b/src/libgit2/tag.c
similarity index 98%
rename from src/tag.c
rename to src/libgit2/tag.c
index 5734106fa..792155a4b 100644
--- a/src/tag.c
+++ b/src/libgit2/tag.c
@@ -244,6 +244,15 @@ on_error:
return -1;
}
+static bool tag_name_is_valid(const char *tag_name)
+{
+ /*
+ * Discourage tag name starting with dash,
+ * https://github.com/git/git/commit/4f0accd638b8d2
+ */
+ return tag_name[0] != '-';
+}
+
static int git_tag_create__internal(
git_oid *oid,
git_repository *repo,
@@ -269,6 +278,11 @@ static int git_tag_create__internal(
return -1;
}
+ if (!tag_name_is_valid(tag_name)) {
+ git_error_set(GIT_ERROR_TAG, "'%s' is not a valid tag name", tag_name);
+ return -1;
+ }
+
error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
if (error < 0 && error != GIT_ENOTFOUND)
goto cleanup;
@@ -542,11 +556,7 @@ int git_tag_name_is_valid(int *valid, const char *name)
*valid = 0;
- /*
- * Discourage tag name starting with dash,
- * https://github.com/git/git/commit/4f0accd638b8d2
- */
- if (!name || name[0] == '-')
+ if (!name || !tag_name_is_valid(name))
goto done;
if ((error = git_str_puts(&ref_name, GIT_REFS_TAGS_DIR)) < 0 ||
diff --git a/src/tag.h b/src/libgit2/tag.h
similarity index 100%
rename from src/tag.h
rename to src/libgit2/tag.h
diff --git a/src/threadstate.c b/src/libgit2/threadstate.c
similarity index 100%
rename from src/threadstate.c
rename to src/libgit2/threadstate.c
diff --git a/src/threadstate.h b/src/libgit2/threadstate.h
similarity index 100%
rename from src/threadstate.h
rename to src/libgit2/threadstate.h
diff --git a/src/trace.c b/src/libgit2/trace.c
similarity index 100%
rename from src/trace.c
rename to src/libgit2/trace.c
diff --git a/src/trace.h b/src/libgit2/trace.h
similarity index 100%
rename from src/trace.h
rename to src/libgit2/trace.h
diff --git a/src/trailer.c b/src/libgit2/trailer.c
similarity index 100%
rename from src/trailer.c
rename to src/libgit2/trailer.c
diff --git a/src/transaction.c b/src/libgit2/transaction.c
similarity index 100%
rename from src/transaction.c
rename to src/libgit2/transaction.c
diff --git a/src/transaction.h b/src/libgit2/transaction.h
similarity index 100%
rename from src/transaction.h
rename to src/libgit2/transaction.h
diff --git a/src/transport.c b/src/libgit2/transport.c
similarity index 100%
rename from src/transport.c
rename to src/libgit2/transport.c
diff --git a/src/transports/auth.c b/src/libgit2/transports/auth.c
similarity index 100%
rename from src/transports/auth.c
rename to src/libgit2/transports/auth.c
diff --git a/src/transports/auth.h b/src/libgit2/transports/auth.h
similarity index 100%
rename from src/transports/auth.h
rename to src/libgit2/transports/auth.h
diff --git a/src/transports/auth_negotiate.c b/src/libgit2/transports/auth_negotiate.c
similarity index 100%
rename from src/transports/auth_negotiate.c
rename to src/libgit2/transports/auth_negotiate.c
diff --git a/src/transports/auth_negotiate.h b/src/libgit2/transports/auth_negotiate.h
similarity index 100%
rename from src/transports/auth_negotiate.h
rename to src/libgit2/transports/auth_negotiate.h
diff --git a/src/transports/auth_ntlm.c b/src/libgit2/transports/auth_ntlm.c
similarity index 100%
rename from src/transports/auth_ntlm.c
rename to src/libgit2/transports/auth_ntlm.c
diff --git a/src/transports/auth_ntlm.h b/src/libgit2/transports/auth_ntlm.h
similarity index 100%
rename from src/transports/auth_ntlm.h
rename to src/libgit2/transports/auth_ntlm.h
diff --git a/src/transports/credential.c b/src/libgit2/transports/credential.c
similarity index 100%
rename from src/transports/credential.c
rename to src/libgit2/transports/credential.c
diff --git a/src/transports/credential_helpers.c b/src/libgit2/transports/credential_helpers.c
similarity index 100%
rename from src/transports/credential_helpers.c
rename to src/libgit2/transports/credential_helpers.c
diff --git a/src/transports/git.c b/src/libgit2/transports/git.c
similarity index 100%
rename from src/transports/git.c
rename to src/libgit2/transports/git.c
diff --git a/src/transports/http.c b/src/libgit2/transports/http.c
similarity index 100%
rename from src/transports/http.c
rename to src/libgit2/transports/http.c
diff --git a/src/transports/http.h b/src/libgit2/transports/http.h
similarity index 100%
rename from src/transports/http.h
rename to src/libgit2/transports/http.h
diff --git a/src/transports/httpclient.c b/src/libgit2/transports/httpclient.c
similarity index 99%
rename from src/transports/httpclient.c
rename to src/libgit2/transports/httpclient.c
index 75782da82..f07923ef2 100644
--- a/src/transports/httpclient.c
+++ b/src/libgit2/transports/httpclient.c
@@ -395,7 +395,7 @@ static int on_body(http_parser *parser, const char *buf, size_t len)
size_t max_len;
/* Saw data when we expected not to (eg, in consume_response_body) */
- if (ctx->output_buf == NULL && ctx->output_size == 0) {
+ if (ctx->output_buf == NULL || ctx->output_size == 0) {
ctx->parse_status = PARSE_STATUS_NO_OUTPUT;
return 0;
}
diff --git a/src/transports/httpclient.h b/src/libgit2/transports/httpclient.h
similarity index 100%
rename from src/transports/httpclient.h
rename to src/libgit2/transports/httpclient.h
diff --git a/src/transports/local.c b/src/libgit2/transports/local.c
similarity index 100%
rename from src/transports/local.c
rename to src/libgit2/transports/local.c
diff --git a/src/transports/smart.c b/src/libgit2/transports/smart.c
similarity index 97%
rename from src/transports/smart.c
rename to src/libgit2/transports/smart.c
index 801fcbe53..7f57dba2a 100644
--- a/src/transports/smart.c
+++ b/src/libgit2/transports/smart.c
@@ -425,6 +425,18 @@ int git_transport_smart_credentials(git_credential **out, git_transport *transpo
return connect_opts->callbacks.credentials(out, t->url, user, methods, connect_opts->callbacks.payload);
}
+int git_transport_remote_connect_options(
+ git_remote_connect_options *out,
+ git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(transport);
+
+ return git_remote_connect_options_dup(out, &t->connect_opts);
+}
+
int git_transport_smart(git_transport **out, git_remote *owner, void *param)
{
transport_smart *t;
diff --git a/src/transports/smart.h b/src/libgit2/transports/smart.h
similarity index 100%
rename from src/transports/smart.h
rename to src/libgit2/transports/smart.h
diff --git a/src/transports/smart_pkt.c b/src/libgit2/transports/smart_pkt.c
similarity index 100%
rename from src/transports/smart_pkt.c
rename to src/libgit2/transports/smart_pkt.c
diff --git a/src/transports/smart_protocol.c b/src/libgit2/transports/smart_protocol.c
similarity index 100%
rename from src/transports/smart_protocol.c
rename to src/libgit2/transports/smart_protocol.c
diff --git a/src/transports/ssh.c b/src/libgit2/transports/ssh.c
similarity index 100%
rename from src/transports/ssh.c
rename to src/libgit2/transports/ssh.c
diff --git a/src/transports/ssh.h b/src/libgit2/transports/ssh.h
similarity index 100%
rename from src/transports/ssh.h
rename to src/libgit2/transports/ssh.h
diff --git a/src/transports/winhttp.c b/src/libgit2/transports/winhttp.c
similarity index 100%
rename from src/transports/winhttp.c
rename to src/libgit2/transports/winhttp.c
diff --git a/src/tree-cache.c b/src/libgit2/tree-cache.c
similarity index 99%
rename from src/tree-cache.c
rename to src/libgit2/tree-cache.c
index 0977c92f3..cd69e7bf8 100644
--- a/src/tree-cache.c
+++ b/src/libgit2/tree-cache.c
@@ -263,7 +263,7 @@ static void write_tree(git_str *out, git_tree_cache *tree)
git_str_printf(out, "%s%c%"PRIdZ" %"PRIuZ"\n", tree->name, 0, tree->entry_count, tree->children_count);
if (tree->entry_count != -1)
- git_str_put(out, (const char *) &tree->oid, GIT_OID_RAWSZ);
+ git_str_put(out, (char *)&tree->oid.id, GIT_OID_RAWSZ);
for (i = 0; i < tree->children_count; i++)
write_tree(out, tree->children[i]);
diff --git a/src/tree-cache.h b/src/libgit2/tree-cache.h
similarity index 100%
rename from src/tree-cache.h
rename to src/libgit2/tree-cache.h
diff --git a/src/tree.c b/src/libgit2/tree.c
similarity index 97%
rename from src/tree.c
rename to src/libgit2/tree.c
index a1545dc2d..a5371fd87 100644
--- a/src/tree.c
+++ b/src/libgit2/tree.c
@@ -82,6 +82,7 @@ int git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2)
static git_tree_entry *alloc_entry(const char *filename, size_t filename_len, const git_oid *id)
{
git_tree_entry *entry = NULL;
+ char *filename_ptr;
size_t tree_len;
TREE_ENTRY_CHECK_NAMELEN(filename_len);
@@ -95,21 +96,13 @@ static git_tree_entry *alloc_entry(const char *filename, size_t filename_len, co
if (!entry)
return NULL;
- {
- char *filename_ptr;
- void *id_ptr;
-
- filename_ptr = ((char *) entry) + sizeof(git_tree_entry);
- memcpy(filename_ptr, filename, filename_len);
- entry->filename = filename_ptr;
-
- id_ptr = filename_ptr + filename_len + 1;
- git_oid_cpy(id_ptr, id);
- entry->oid = id_ptr;
- }
-
+ filename_ptr = ((char *) entry) + sizeof(git_tree_entry);
+ memcpy(filename_ptr, filename, filename_len);
+ entry->filename = filename_ptr;
entry->filename_len = (uint16_t)filename_len;
+ git_oid_cpy(&entry->oid, id);
+
return entry;
}
@@ -231,7 +224,7 @@ int git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source)
GIT_ASSERT_ARG(source);
- cpy = alloc_entry(source->filename, source->filename_len, source->oid);
+ cpy = alloc_entry(source->filename, source->filename_len, &source->oid);
if (cpy == NULL)
return -1;
@@ -269,7 +262,7 @@ const char *git_tree_entry_name(const git_tree_entry *entry)
const git_oid *git_tree_entry_id(const git_tree_entry *entry)
{
GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
- return entry->oid;
+ return &entry->oid;
}
git_object_t git_tree_entry_type(const git_tree_entry *entry)
@@ -292,7 +285,7 @@ int git_tree_entry_to_object(
GIT_ASSERT_ARG(entry);
GIT_ASSERT_ARG(object_out);
- return git_object_lookup(object_out, repo, entry->oid, GIT_OBJECT_ANY);
+ return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJECT_ANY);
}
static const git_tree_entry *entry_fromname(
@@ -331,7 +324,7 @@ const git_tree_entry *git_tree_entry_byid(
GIT_ASSERT_ARG_WITH_RETVAL(tree, NULL);
git_array_foreach(tree->entries, i, e) {
- if (memcmp(&e->oid->id, &id->id, sizeof(id->id)) == 0)
+ if (git_oid_equal(&e->oid, id))
return e;
}
@@ -432,7 +425,7 @@ int git_tree__parse_raw(void *_tree, const char *data, size_t size)
entry->attr = attr;
entry->filename_len = (uint16_t)filename_len;
entry->filename = buffer;
- entry->oid = (git_oid *) ((char *) buffer + filename_len + 1);
+ git_oid_fromraw(&entry->oid, ((unsigned char *) buffer + filename_len + 1));
}
buffer += filename_len + 1;
@@ -536,7 +529,7 @@ static int git_treebuilder__write_with_buffer(
git_str_printf(buf, "%o ", entry->attr);
git_str_put(buf, entry->filename, entry->filename_len + 1);
- git_str_put(buf, (char *)entry->oid->id, GIT_OID_RAWSZ);
+ git_str_put(buf, (char *)entry->oid.id, GIT_OID_RAWSZ);
if (git_str_oom(buf)) {
error = -1;
@@ -765,7 +758,7 @@ int git_treebuilder_new(
git_array_foreach(source->entries, i, entry_src) {
if (append_entry(
bld, entry_src->filename,
- entry_src->oid,
+ &entry_src->oid,
entry_src->attr,
false) < 0)
goto on_error;
@@ -798,7 +791,7 @@ int git_treebuilder_insert(
return error;
if ((entry = git_strmap_get(bld->map, filename)) != NULL) {
- git_oid_cpy((git_oid *) entry->oid, id);
+ git_oid_cpy(&entry->oid, id);
} else {
entry = alloc_entry(filename, strlen(filename), id);
GIT_ERROR_CHECK_ALLOC(entry);
@@ -954,7 +947,7 @@ int git_tree_entry_bypath(
return git_tree_entry_dup(entry_out, entry);
}
- if (git_tree_lookup(&subtree, root->object.repo, entry->oid) < 0)
+ if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0)
return -1;
error = git_tree_entry_bypath(
@@ -995,7 +988,7 @@ static int tree_walk(
git_tree *subtree;
size_t path_len = git_str_len(path);
- error = git_tree_lookup(&subtree, tree->object.repo, entry->oid);
+ error = git_tree_lookup(&subtree, tree->object.repo, &entry->oid);
if (error < 0)
break;
diff --git a/src/tree.h b/src/libgit2/tree.h
similarity index 98%
rename from src/tree.h
rename to src/libgit2/tree.h
index 6bd9ed652..0dd963ff2 100644
--- a/src/tree.h
+++ b/src/libgit2/tree.h
@@ -19,7 +19,7 @@
struct git_tree_entry {
uint16_t attr;
uint16_t filename_len;
- const git_oid *oid;
+ git_oid oid;
const char *filename;
};
diff --git a/src/userdiff.h b/src/libgit2/userdiff.h
similarity index 100%
rename from src/userdiff.h
rename to src/libgit2/userdiff.h
diff --git a/src/worktree.c b/src/libgit2/worktree.c
similarity index 100%
rename from src/worktree.c
rename to src/libgit2/worktree.c
diff --git a/src/worktree.h b/src/libgit2/worktree.h
similarity index 100%
rename from src/worktree.h
rename to src/libgit2/worktree.h
diff --git a/src/xdiff/git-xdiff.h b/src/libgit2/xdiff/git-xdiff.h
similarity index 100%
rename from src/xdiff/git-xdiff.h
rename to src/libgit2/xdiff/git-xdiff.h
diff --git a/src/xdiff/xdiff.h b/src/libgit2/xdiff/xdiff.h
similarity index 100%
rename from src/xdiff/xdiff.h
rename to src/libgit2/xdiff/xdiff.h
diff --git a/src/xdiff/xdiffi.c b/src/libgit2/xdiff/xdiffi.c
similarity index 100%
rename from src/xdiff/xdiffi.c
rename to src/libgit2/xdiff/xdiffi.c
diff --git a/src/xdiff/xdiffi.h b/src/libgit2/xdiff/xdiffi.h
similarity index 100%
rename from src/xdiff/xdiffi.h
rename to src/libgit2/xdiff/xdiffi.h
diff --git a/src/xdiff/xemit.c b/src/libgit2/xdiff/xemit.c
similarity index 100%
rename from src/xdiff/xemit.c
rename to src/libgit2/xdiff/xemit.c
diff --git a/src/xdiff/xemit.h b/src/libgit2/xdiff/xemit.h
similarity index 100%
rename from src/xdiff/xemit.h
rename to src/libgit2/xdiff/xemit.h
diff --git a/src/xdiff/xhistogram.c b/src/libgit2/xdiff/xhistogram.c
similarity index 100%
rename from src/xdiff/xhistogram.c
rename to src/libgit2/xdiff/xhistogram.c
diff --git a/src/xdiff/xinclude.h b/src/libgit2/xdiff/xinclude.h
similarity index 100%
rename from src/xdiff/xinclude.h
rename to src/libgit2/xdiff/xinclude.h
diff --git a/src/xdiff/xmacros.h b/src/libgit2/xdiff/xmacros.h
similarity index 100%
rename from src/xdiff/xmacros.h
rename to src/libgit2/xdiff/xmacros.h
diff --git a/src/xdiff/xmerge.c b/src/libgit2/xdiff/xmerge.c
similarity index 100%
rename from src/xdiff/xmerge.c
rename to src/libgit2/xdiff/xmerge.c
diff --git a/src/xdiff/xpatience.c b/src/libgit2/xdiff/xpatience.c
similarity index 100%
rename from src/xdiff/xpatience.c
rename to src/libgit2/xdiff/xpatience.c
diff --git a/src/xdiff/xprepare.c b/src/libgit2/xdiff/xprepare.c
similarity index 100%
rename from src/xdiff/xprepare.c
rename to src/libgit2/xdiff/xprepare.c
diff --git a/src/xdiff/xprepare.h b/src/libgit2/xdiff/xprepare.h
similarity index 100%
rename from src/xdiff/xprepare.h
rename to src/libgit2/xdiff/xprepare.h
diff --git a/src/xdiff/xtypes.h b/src/libgit2/xdiff/xtypes.h
similarity index 100%
rename from src/xdiff/xtypes.h
rename to src/libgit2/xdiff/xtypes.h
diff --git a/src/xdiff/xutils.c b/src/libgit2/xdiff/xutils.c
similarity index 100%
rename from src/xdiff/xutils.c
rename to src/libgit2/xdiff/xutils.c
diff --git a/src/xdiff/xutils.h b/src/libgit2/xdiff/xutils.h
similarity index 100%
rename from src/xdiff/xutils.h
rename to src/libgit2/xdiff/xutils.h
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
new file mode 100644
index 000000000..b2833954d
--- /dev/null
+++ b/src/util/CMakeLists.txt
@@ -0,0 +1,74 @@
+# util: a shared library for common utility functions for libgit2 projects
+
+add_library(util OBJECT)
+set_target_properties(util PROPERTIES C_STANDARD 90)
+set_target_properties(util PROPERTIES C_EXTENSIONS OFF)
+
+set(UTIL_INCLUDES
+ "${PROJECT_BINARY_DIR}/src"
+ "${PROJECT_SOURCE_DIR}/src/util"
+ "${PROJECT_SOURCE_DIR}/include")
+
+file(GLOB UTIL_SRC *.c *.h allocators/*.c allocators/*.h hash.h)
+list(SORT UTIL_SRC)
+
+#
+# Platform specific sources
+#
+
+if(WIN32 AND NOT CYGWIN)
+ file(GLOB UTIL_SRC_OS win32/*.c win32/*.h)
+ list(SORT UTIL_SRC_OS)
+elseif(NOT AMIGA)
+ file(GLOB UTIL_SRC_OS unix/*.c unix/*.h)
+ list(SORT UTIL_SRC_OS)
+endif()
+
+#
+# Hash backend selection
+#
+
+if(USE_SHA1 STREQUAL "CollisionDetection")
+ file(GLOB UTIL_SRC_SHA1 hash/collisiondetect.* hash/sha1dc/*)
+ target_compile_definitions(util PRIVATE SHA1DC_NO_STANDARD_INCLUDES=1)
+ target_compile_definitions(util PRIVATE SHA1DC_CUSTOM_INCLUDE_SHA1_C=\"git2_util.h\")
+ target_compile_definitions(util PRIVATE SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"git2_util.h\")
+elseif(USE_SHA1 STREQUAL "OpenSSL" OR USE_SHA1 STREQUAL "OpenSSL-Dynamic")
+ file(GLOB UTIL_SRC_SHA1 hash/openssl.*)
+elseif(USE_SHA1 STREQUAL "CommonCrypto")
+ file(GLOB UTIL_SRC_SHA1 hash/common_crypto.*)
+elseif(USE_SHA1 STREQUAL "mbedTLS")
+ file(GLOB UTIL_SRC_SHA1 hash/mbedtls.*)
+elseif(USE_SHA1 STREQUAL "Win32")
+ file(GLOB UTIL_SRC_SHA1 hash/win32.*)
+else()
+ message(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}")
+endif()
+
+list(SORT UTIL_SRC_SHA1)
+
+if(USE_SHA256 STREQUAL "Builtin")
+ file(GLOB UTIL_SRC_SHA256 hash/builtin.* hash/rfc6234/*)
+elseif(USE_SHA256 STREQUAL "OpenSSL" OR USE_SHA256 STREQUAL "OpenSSL-Dynamic")
+ file(GLOB UTIL_SRC_SHA256 hash/openssl.*)
+elseif(USE_SHA256 STREQUAL "CommonCrypto")
+ file(GLOB UTIL_SRC_SHA256 hash/common_crypto.*)
+elseif(USE_SHA256 STREQUAL "mbedTLS")
+ file(GLOB UTIL_SRC_SHA256 hash/mbedtls.*)
+elseif(USE_SHA256 STREQUAL "Win32")
+ file(GLOB UTIL_SRC_SHA256 hash/win32.*)
+else()
+ message(FATAL_ERROR "Asked for unknown SHA256 backend: ${USE_SHA256}")
+endif()
+
+list(SORT UTIL_SRC_SHA256)
+
+#
+# Build the library
+#
+
+target_sources(util PRIVATE ${UTIL_SRC} ${UTIL_SRC_OS} ${UTIL_SRC_SHA1} ${UTIL_SRC_SHA256})
+ide_split_sources(util)
+
+target_include_directories(util PRIVATE ${UTIL_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${libgit2_SOURCE_DIR}/include)
+target_include_directories(util SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
diff --git a/src/alloc.c b/src/util/alloc.c
similarity index 100%
rename from src/alloc.c
rename to src/util/alloc.c
diff --git a/src/alloc.h b/src/util/alloc.h
similarity index 100%
rename from src/alloc.h
rename to src/util/alloc.h
diff --git a/src/allocators/failalloc.c b/src/util/allocators/failalloc.c
similarity index 100%
rename from src/allocators/failalloc.c
rename to src/util/allocators/failalloc.c
diff --git a/src/allocators/failalloc.h b/src/util/allocators/failalloc.h
similarity index 97%
rename from src/allocators/failalloc.h
rename to src/util/allocators/failalloc.h
index 6115e51e7..91264a0bb 100644
--- a/src/allocators/failalloc.h
+++ b/src/util/allocators/failalloc.h
@@ -8,7 +8,7 @@
#ifndef INCLUDE_allocators_failalloc_h__
#define INCLUDE_allocators_failalloc_h__
-#include "common.h"
+#include "git2_util.h"
extern void *git_failalloc_malloc(size_t len, const char *file, int line);
extern void *git_failalloc_calloc(size_t nelem, size_t elsize, const char *file, int line);
diff --git a/src/allocators/stdalloc.c b/src/util/allocators/stdalloc.c
similarity index 100%
rename from src/allocators/stdalloc.c
rename to src/util/allocators/stdalloc.c
diff --git a/src/allocators/stdalloc.h b/src/util/allocators/stdalloc.h
similarity index 94%
rename from src/allocators/stdalloc.h
rename to src/util/allocators/stdalloc.h
index fa23fe6e3..955038cb0 100644
--- a/src/allocators/stdalloc.h
+++ b/src/util/allocators/stdalloc.h
@@ -8,7 +8,7 @@
#ifndef INCLUDE_allocators_stdalloc_h__
#define INCLUDE_allocators_stdalloc_h__
-#include "common.h"
+#include "git2_util.h"
#include "alloc.h"
diff --git a/src/allocators/win32_leakcheck.c b/src/util/allocators/win32_leakcheck.c
similarity index 100%
rename from src/allocators/win32_leakcheck.c
rename to src/util/allocators/win32_leakcheck.c
diff --git a/src/allocators/win32_leakcheck.h b/src/util/allocators/win32_leakcheck.h
similarity index 94%
rename from src/allocators/win32_leakcheck.h
rename to src/util/allocators/win32_leakcheck.h
index 089690f90..edcd9307f 100644
--- a/src/allocators/win32_leakcheck.h
+++ b/src/util/allocators/win32_leakcheck.h
@@ -8,7 +8,7 @@
#ifndef INCLUDE_allocators_win32_leakcheck_h
#define INCLUDE_allocators_win32_leakcheck_h
-#include "common.h"
+#include "git2_util.h"
#include "alloc.h"
diff --git a/src/array.h b/src/util/array.h
similarity index 99%
rename from src/array.h
rename to src/util/array.h
index e97688b36..cbab52ad1 100644
--- a/src/array.h
+++ b/src/util/array.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_array_h__
#define INCLUDE_array_h__
-#include "common.h"
+#include "git2_util.h"
/*
* Use this to declare a typesafe resizable array of items, a la:
diff --git a/src/assert_safe.h b/src/util/assert_safe.h
similarity index 79%
rename from src/assert_safe.h
rename to src/util/assert_safe.h
index 8c261100f..cc0bac551 100644
--- a/src/assert_safe.h
+++ b/src/util/assert_safe.h
@@ -24,6 +24,8 @@
# define GIT_ASSERT_WITH_RETVAL(expr, fail) assert(expr)
# define GIT_ASSERT_ARG_WITH_RETVAL(expr, fail) assert(expr)
+
+# define GIT_ASSERT_WITH_CLEANUP(expr, cleanup) assert(expr)
#else
/** Internal consistency check to stop the function. */
@@ -53,6 +55,20 @@
} \
} while(0)
+/**
+ * Go to to the given label on assertion failures; useful when you have
+ * taken a lock or otherwise need to release a resource.
+ */
+# define GIT_ASSERT_WITH_CLEANUP(expr, cleanup) \
+ GIT_ASSERT__WITH_CLEANUP(expr, GIT_ERROR_INTERNAL, "unrecoverable internal error", cleanup)
+
+# define GIT_ASSERT__WITH_CLEANUP(expr, code, msg, cleanup) do { \
+ if (!(expr)) { \
+ git_error_set(code, "%s: '%s'", msg, #expr); \
+ cleanup; \
+ } \
+ } while(0)
+
#endif /* GIT_ASSERT_HARD */
#endif
diff --git a/src/bitvec.h b/src/util/bitvec.h
similarity index 100%
rename from src/bitvec.h
rename to src/util/bitvec.h
diff --git a/src/cc-compat.h b/src/util/cc-compat.h
similarity index 100%
rename from src/cc-compat.h
rename to src/util/cc-compat.h
diff --git a/src/date.c b/src/util/date.c
similarity index 98%
rename from src/date.c
rename to src/util/date.c
index 0e5ffc96b..4d757e21a 100644
--- a/src/date.c
+++ b/src/util/date.c
@@ -1,10 +1,11 @@
/*
- * GIT - The information manager from hell
+ * Copyright (C) the libgit2 contributors. All rights reserved.
*
- * Copyright (C) Linus Torvalds, 2005
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "git2_util.h"
#ifndef GIT_WIN32
#include
diff --git a/src/date.h b/src/util/date.h
similarity index 100%
rename from src/date.h
rename to src/util/date.h
diff --git a/src/filebuf.c b/src/util/filebuf.c
similarity index 99%
rename from src/filebuf.c
rename to src/util/filebuf.c
index eafcba3bd..e014d43b2 100644
--- a/src/filebuf.c
+++ b/src/util/filebuf.c
@@ -65,7 +65,7 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode)
if ((flags & GIT_FILEBUF_APPEND) && git_fs_path_exists(file->path_original) == true) {
git_file source;
- char buffer[FILEIO_BUFSIZE];
+ char buffer[GIT_BUFSIZE_FILEIO];
ssize_t read_bytes;
int error = 0;
diff --git a/src/filebuf.h b/src/util/filebuf.h
similarity index 99%
rename from src/filebuf.h
rename to src/util/filebuf.h
index adbb19936..4a61ae4e3 100644
--- a/src/filebuf.h
+++ b/src/util/filebuf.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_filebuf_h__
#define INCLUDE_filebuf_h__
-#include "common.h"
+#include "git2_util.h"
#include "futils.h"
#include "hash.h"
diff --git a/src/fs_path.c b/src/util/fs_path.c
similarity index 94%
rename from src/fs_path.c
rename to src/util/fs_path.c
index a67617eca..6c87bfd01 100644
--- a/src/fs_path.c
+++ b/src/util/fs_path.c
@@ -7,8 +7,9 @@
#include "fs_path.h"
+#include "git2_util.h"
+#include "futils.h"
#include "posix.h"
-#include "repository.h"
#ifdef GIT_WIN32
#include "win32/posix.h"
#include "win32/w32_buffer.h"
@@ -21,6 +22,13 @@
#include
#include
+#define ensure_error_set(code) do { \
+ const git_error *e = git_error_last(); \
+ if (!e || !e->message) \
+ git_error_set(e ? e->klass : GIT_ERROR_CALLBACK, \
+ "filesystem callback returned %d", code); \
+ } while(0)
+
static int dos_drive_prefix_length(const char *path)
{
int i;
@@ -101,7 +109,7 @@ int git_fs_path_basename_r(git_str *buffer, const char *path)
/* Empty or NULL string gets treated as "." */
if (path == NULL || *path == '\0') {
startp = ".";
- len = 1;
+ len = 1;
goto Exit;
}
@@ -113,7 +121,7 @@ int git_fs_path_basename_r(git_str *buffer, const char *path)
/* All slashes becomes "/" */
if (endp == path && *endp == '/') {
startp = "/";
- len = 1;
+ len = 1;
goto Exit;
}
@@ -185,8 +193,7 @@ int git_fs_path_dirname_r(git_str *buffer, const char *path)
if (endp - path + 1 > INT_MAX) {
git_error_set(GIT_ERROR_INVALID, "path too long");
- len = -1;
- goto Exit;
+ return -1;
}
if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) {
@@ -211,8 +218,7 @@ int git_fs_path_dirname_r(git_str *buffer, const char *path)
if (endp - path + 1 > INT_MAX) {
git_error_set(GIT_ERROR_INVALID, "path too long");
- len = -1;
- goto Exit;
+ return -1;
}
if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) {
@@ -530,7 +536,7 @@ int git_fs_path_walk_up(
if (!scan) {
error = cb(data, "");
if (error)
- git_error_set_after_callback(error);
+ ensure_error_set(error);
return error;
}
@@ -543,7 +549,7 @@ int git_fs_path_walk_up(
iter.ptr[scan] = oldc;
if (error) {
- git_error_set_after_callback(error);
+ ensure_error_set(error);
break;
}
@@ -563,7 +569,7 @@ int git_fs_path_walk_up(
if (!error && stop == 0 && iter.ptr[0] != '/') {
error = cb(data, "");
if (error)
- git_error_set_after_callback(error);
+ ensure_error_set(error);
}
return error;
@@ -1167,7 +1173,7 @@ int git_fs_path_direach(
/* Only set our own error if the callback did not set one already */
if (error != 0) {
if (!git_error_last())
- git_error_set_after_callback(error);
+ ensure_error_set(error);
break;
}
@@ -1779,9 +1785,9 @@ done:
return supported;
}
-static git_fs_path__mock_owner_t mock_owner = GIT_FS_PATH_MOCK_OWNER_NONE;
+static git_fs_path_owner_t mock_owner = GIT_FS_PATH_OWNER_NONE;
-void git_fs_path__set_owner(git_fs_path__mock_owner_t owner)
+void git_fs_path__set_owner(git_fs_path_owner_t owner)
{
mock_owner = owner;
}
@@ -1873,74 +1879,52 @@ static int file_owner_sid(PSID *out, const char *path)
return error;
}
-int git_fs_path_owner_is_current_user(bool *out, const char *path)
+int git_fs_path_owner_is(
+ bool *out,
+ const char *path,
+ git_fs_path_owner_t owner_type)
{
PSID owner_sid = NULL, user_sid = NULL;
- int error = -1;
+ BOOL is_admin, admin_owned;
+ int error;
if (mock_owner) {
- *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_CURRENT_USER);
+ *out = ((mock_owner & owner_type) != 0);
return 0;
}
- if ((error = file_owner_sid(&owner_sid, path)) < 0 ||
- (error = current_user_sid(&user_sid)) < 0)
+ if ((error = file_owner_sid(&owner_sid, path)) < 0)
goto done;
- *out = EqualSid(owner_sid, user_sid);
- error = 0;
+ if ((owner_type & GIT_FS_PATH_OWNER_CURRENT_USER) != 0) {
+ if ((error = current_user_sid(&user_sid)) < 0)
+ goto done;
-done:
- git__free(owner_sid);
- git__free(user_sid);
- return error;
-}
-
-int git_fs_path_owner_is_system(bool *out, const char *path)
-{
- PSID owner_sid;
-
- if (mock_owner) {
- *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_SYSTEM);
- return 0;
+ if (EqualSid(owner_sid, user_sid)) {
+ *out = true;
+ goto done;
+ }
}
- if (file_owner_sid(&owner_sid, path) < 0)
- return -1;
+ admin_owned =
+ IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) ||
+ IsWellKnownSid(owner_sid, WinLocalSystemSid);
- *out = IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) ||
- IsWellKnownSid(owner_sid, WinLocalSystemSid);
-
- git__free(owner_sid);
- return 0;
-}
-
-int git_fs_path_owner_is_system_or_current_user(bool *out, const char *path)
-{
- PSID owner_sid = NULL, user_sid = NULL;
- int error = -1;
-
- if (mock_owner) {
- *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_SYSTEM ||
- mock_owner == GIT_FS_PATH_MOCK_OWNER_CURRENT_USER);
- return 0;
- }
-
- if (file_owner_sid(&owner_sid, path) < 0)
- goto done;
-
- if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) ||
- IsWellKnownSid(owner_sid, WinLocalSystemSid)) {
- *out = 1;
- error = 0;
+ if (admin_owned &&
+ (owner_type & GIT_FS_PATH_OWNER_ADMINISTRATOR) != 0) {
+ *out = true;
goto done;
}
- if (current_user_sid(&user_sid) < 0)
+ if (admin_owned &&
+ (owner_type & GIT_FS_PATH_USER_IS_ADMINISTRATOR) != 0 &&
+ CheckTokenMembership(NULL, owner_sid, &is_admin) &&
+ is_admin) {
+ *out = true;
goto done;
+ }
- *out = EqualSid(owner_sid, user_sid);
- error = 0;
+ *out = false;
done:
git__free(owner_sid);
@@ -1950,12 +1934,36 @@ done:
#else
-static int fs_path_owner_is(bool *out, const char *path, uid_t *uids, size_t uids_len)
+static int sudo_uid_lookup(uid_t *out)
+{
+ git_str uid_str = GIT_STR_INIT;
+ int64_t uid;
+ int error;
+
+ if ((error = git__getenv(&uid_str, "SUDO_UID")) == 0 &&
+ (error = git__strntol64(&uid, uid_str.ptr, uid_str.size, NULL, 10)) == 0 &&
+ uid == (int64_t)((uid_t)uid)) {
+ *out = (uid_t)uid;
+ }
+
+ git_str_dispose(&uid_str);
+ return error;
+}
+
+int git_fs_path_owner_is(
+ bool *out,
+ const char *path,
+ git_fs_path_owner_t owner_type)
{
struct stat st;
- size_t i;
+ uid_t euid, sudo_uid;
- *out = false;
+ if (mock_owner) {
+ *out = ((mock_owner & owner_type) != 0);
+ return 0;
+ }
+
+ euid = geteuid();
if (p_lstat(path, &st) != 0) {
if (errno == ENOENT)
@@ -1965,55 +1973,42 @@ static int fs_path_owner_is(bool *out, const char *path, uid_t *uids, size_t uid
return -1;
}
- for (i = 0; i < uids_len; i++) {
- if (uids[i] == st.st_uid) {
- *out = true;
- break;
- }
- }
-
- return 0;
-}
-
-int git_fs_path_owner_is_current_user(bool *out, const char *path)
-{
- uid_t userid = geteuid();
-
- if (mock_owner) {
- *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_CURRENT_USER);
+ if ((owner_type & GIT_FS_PATH_OWNER_CURRENT_USER) != 0 &&
+ st.st_uid == euid) {
+ *out = true;
return 0;
}
- return fs_path_owner_is(out, path, &userid, 1);
+ if ((owner_type & GIT_FS_PATH_OWNER_ADMINISTRATOR) != 0 &&
+ st.st_uid == 0) {
+ *out = true;
+ return 0;
+ }
+
+ if ((owner_type & GIT_FS_PATH_OWNER_RUNNING_SUDO) != 0 &&
+ euid == 0 &&
+ sudo_uid_lookup(&sudo_uid) == 0 &&
+ st.st_uid == sudo_uid) {
+ *out = true;
+ return 0;
+ }
+
+ *out = false;
+ return 0;
+}
+
+#endif
+
+int git_fs_path_owner_is_current_user(bool *out, const char *path)
+{
+ return git_fs_path_owner_is(out, path, GIT_FS_PATH_OWNER_CURRENT_USER);
}
int git_fs_path_owner_is_system(bool *out, const char *path)
{
- uid_t userid = 0;
-
- if (mock_owner) {
- *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_SYSTEM);
- return 0;
- }
-
- return fs_path_owner_is(out, path, &userid, 1);
+ return git_fs_path_owner_is(out, path, GIT_FS_PATH_OWNER_ADMINISTRATOR);
}
-int git_fs_path_owner_is_system_or_current_user(bool *out, const char *path)
-{
- uid_t userids[2] = { geteuid(), 0 };
-
- if (mock_owner) {
- *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_SYSTEM ||
- mock_owner == GIT_FS_PATH_MOCK_OWNER_CURRENT_USER);
- return 0;
- }
-
- return fs_path_owner_is(out, path, userids, 2);
-}
-
-#endif
-
int git_fs_path_find_executable(git_str *fullpath, const char *executable)
{
#ifdef GIT_WIN32
diff --git a/src/fs_path.h b/src/util/fs_path.h
similarity index 95%
rename from src/fs_path.h
rename to src/util/fs_path.h
index 7e6a22d4f..e5ca67378 100644
--- a/src/fs_path.h
+++ b/src/util/fs_path.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_fs_path_h__
#define INCLUDE_fs_path_h__
-#include "common.h"
+#include "git2_util.h"
#include "posix.h"
#include "str.h"
@@ -732,18 +732,42 @@ int git_fs_path_normalize_slashes(git_str *out, const char *path);
bool git_fs_path_supports_symlinks(const char *dir);
typedef enum {
- GIT_FS_PATH_MOCK_OWNER_NONE = 0, /* do filesystem lookups as normal */
- GIT_FS_PATH_MOCK_OWNER_SYSTEM = 1,
- GIT_FS_PATH_MOCK_OWNER_CURRENT_USER = 2,
- GIT_FS_PATH_MOCK_OWNER_OTHER = 3
-} git_fs_path__mock_owner_t;
+ GIT_FS_PATH_OWNER_NONE = 0,
+
+ /** The file must be owned by the current user. */
+ GIT_FS_PATH_OWNER_CURRENT_USER = (1 << 0),
+
+ /** The file must be owned by the system account. */
+ GIT_FS_PATH_OWNER_ADMINISTRATOR = (1 << 1),
+
+ /**
+ * The file may be owned by a system account if the current
+ * user is in an administrator group. Windows only; this is
+ * a noop on non-Windows systems.
+ */
+ GIT_FS_PATH_USER_IS_ADMINISTRATOR = (1 << 2),
+
+ /**
+ * The file is owned by the current user, who is running `sudo`.
+ */
+ GIT_FS_PATH_OWNER_RUNNING_SUDO = (1 << 3),
+
+ /** The file may be owned by another user. */
+ GIT_FS_PATH_OWNER_OTHER = (1 << 4)
+} git_fs_path_owner_t;
/**
* Sets the mock ownership for files; subsequent calls to
- * `git_fs_path_owner_is_*` functions will return this data until cleared
- * with `GIT_FS_PATH_MOCK_OWNER_NONE`.
+ * `git_fs_path_owner_is_*` functions will return this data until
+ * cleared with `GIT_FS_PATH_OWNER_NONE`.
*/
-void git_fs_path__set_owner(git_fs_path__mock_owner_t owner);
+void git_fs_path__set_owner(git_fs_path_owner_t owner);
+
+/** Verify that the file in question is owned by the given owner. */
+int git_fs_path_owner_is(
+ bool *out,
+ const char *path,
+ git_fs_path_owner_t owner_type);
/**
* Verify that the file in question is owned by an administrator or system
@@ -757,12 +781,6 @@ int git_fs_path_owner_is_system(bool *out, const char *path);
int git_fs_path_owner_is_current_user(bool *out, const char *path);
-/**
- * Verify that the file in question is owned by an administrator or system
- * account _or_ the current user;
- */
-int git_fs_path_owner_is_system_or_current_user(bool *out, const char *path);
-
/**
* Search the current PATH for the given executable, returning the full
* path if it is found.
diff --git a/src/futils.c b/src/util/futils.c
similarity index 96%
rename from src/futils.c
rename to src/util/futils.c
index 42c35955e..cb872de09 100644
--- a/src/futils.c
+++ b/src/util/futils.c
@@ -167,18 +167,60 @@ int git_futils_readbuffer_fd(git_str *buf, git_file fd, size_t len)
/* p_read loops internally to read len bytes */
read_size = p_read(fd, buf->ptr, len);
- if (read_size != (ssize_t)len) {
+ if (read_size < 0) {
git_error_set(GIT_ERROR_OS, "failed to read descriptor");
git_str_dispose(buf);
return -1;
}
+ if ((size_t)read_size != len) {
+ git_error_set(GIT_ERROR_FILESYSTEM, "could not read (expected %" PRIuZ " bytes, read %" PRIuZ ")", len, (size_t)read_size);
+ git_str_dispose(buf);
+ return -1;
+ }
+
buf->ptr[read_size] = '\0';
buf->size = read_size;
return 0;
}
+int git_futils_readbuffer_fd_full(git_str *buf, git_file fd)
+{
+ static size_t blocksize = 10240;
+ size_t alloc_len = 0, total_size = 0;
+ ssize_t read_size = 0;
+
+ git_str_clear(buf);
+
+ while (true) {
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, blocksize);
+
+ if (git_str_grow(buf, alloc_len) < 0)
+ return -1;
+
+ /* p_read loops internally to read blocksize bytes */
+ read_size = p_read(fd, buf->ptr, blocksize);
+
+ if (read_size < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to read descriptor");
+ git_str_dispose(buf);
+ return -1;
+ }
+
+ total_size += read_size;
+
+ if ((size_t)read_size < blocksize) {
+ break;
+ }
+ }
+
+ buf->ptr[total_size] = '\0';
+ buf->size = total_size;
+
+ return 0;
+}
+
int git_futils_readbuffer_updated(
git_str *out,
const char *path,
@@ -856,7 +898,7 @@ int git_futils_fake_symlink(const char *target, const char *path)
static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done)
{
int error = 0;
- char buffer[FILEIO_BUFSIZE];
+ char buffer[GIT_BUFSIZE_FILEIO];
ssize_t len = 0;
while (!error && (len = p_read(ifd, buffer, sizeof(buffer))) > 0)
diff --git a/src/futils.h b/src/util/futils.h
similarity index 99%
rename from src/futils.h
rename to src/util/futils.h
index a82ec41cc..3f207afb2 100644
--- a/src/futils.h
+++ b/src/util/futils.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_futils_h__
#define INCLUDE_futils_h__
-#include "common.h"
+#include "git2_util.h"
#include "map.h"
#include "posix.h"
@@ -27,6 +27,7 @@ extern int git_futils_readbuffer_updated(
const char *path,
unsigned char checksum[GIT_HASH_SHA1_SIZE],
int *updated);
+extern int git_futils_readbuffer_fd_full(git_str *obj, git_file fd);
extern int git_futils_readbuffer_fd(git_str *obj, git_file fd, size_t len);
/* Additional constants for `git_futils_writebuffer`'s `open_flags`. We
diff --git a/src/common.h b/src/util/git2_util.h
similarity index 71%
rename from src/common.h
rename to src/util/git2_util.h
index 549bddb59..ad3f1c71f 100644
--- a/src/common.h
+++ b/src/util/git2_util.h
@@ -4,8 +4,8 @@
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_common_h__
-#define INCLUDE_common_h__
+#ifndef INCLUDE_git2_util_h__
+#define INCLUDE_git2_util_h__
#ifndef LIBGIT2_NO_FEATURES_H
# include "git2/sys/features.h"
@@ -14,13 +14,13 @@
#include "git2/common.h"
#include "cc-compat.h"
+typedef struct git_str git_str;
+
/** Declare a function as always inlined. */
#if defined(_MSC_VER)
# define GIT_INLINE(type) static __inline type
#elif defined(__GNUC__)
# define GIT_INLINE(type) static __inline__ type
-#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-# define GIT_INLINE(type) static inline type
#else
# define GIT_INLINE(type) static type
#endif
@@ -67,12 +67,12 @@
# include
# include "win32/msvc-compat.h"
# include "win32/mingw-compat.h"
-# include "win32/w32_common.h"
# include "win32/win32-compat.h"
-# include "win32/error.h"
+# include "win32/w32_common.h"
# include "win32/version.h"
+# include "win32/error.h"
# ifdef GIT_THREADS
-# include "win32/thread.h"
+# include "win32/thread.h"
# endif
#else
@@ -99,24 +99,17 @@
#include "git2/types.h"
#include "git2/errors.h"
-#include "errors.h"
#include "thread.h"
#include "integer.h"
#include "assert_safe.h"
-#include "utf8.h"
-
-/*
- * Include the declarations for deprecated functions; this ensures
- * that they're decorated with the proper extern/visibility attributes.
- */
-#include "git2/deprecated.h"
#include "posix.h"
-#define DEFAULT_BUFSIZE 65536
-#define FILEIO_BUFSIZE DEFAULT_BUFSIZE
-#define FILTERIO_BUFSIZE DEFAULT_BUFSIZE
-#define NETIO_BUFSIZE DEFAULT_BUFSIZE
+#define GIT_BUFSIZE_DEFAULT 65536
+#define GIT_BUFSIZE_FILEIO GIT_BUFSIZE_DEFAULT
+#define GIT_BUFSIZE_FILTERIO GIT_BUFSIZE_DEFAULT
+#define GIT_BUFSIZE_NETIO GIT_BUFSIZE_DEFAULT
+
/**
* Check a pointer allocation result, returning -1 if it failed.
@@ -126,7 +119,7 @@
} while(0)
/**
- * Check a string buffer allocation result, returning -1 if it failed.
+ * Check a buffer allocation result, returning -1 if it failed.
*/
#define GIT_ERROR_CHECK_ALLOC_STR(buf) do { \
if ((void *)(buf) == NULL || git_str_oom(buf)) { return -1; } \
@@ -138,40 +131,6 @@
#define GIT_ERROR_CHECK_ERROR(code) \
do { int _err = (code); if (_err) return _err; } while (0)
-/**
- * Check a versioned structure for validity
- */
-GIT_INLINE(int) git_error__check_version(const void *structure, unsigned int expected_max, const char *name)
-{
- unsigned int actual;
-
- if (!structure)
- return 0;
-
- actual = *(const unsigned int*)structure;
- if (actual > 0 && actual <= expected_max)
- return 0;
-
- git_error_set(GIT_ERROR_INVALID, "invalid version %d on %s", actual, name);
- return -1;
-}
-#define GIT_ERROR_CHECK_VERSION(S,V,N) if (git_error__check_version(S,V,N) < 0) return -1
-
-/**
- * Initialize a structure with a version.
- */
-GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int version)
-{
- memset(structure, 0, len);
- *((int*)structure) = version;
-}
-#define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V)
-
-#define GIT_INIT_STRUCTURE_FROM_TEMPLATE(PTR,VERSION,TYPE,TPL) do { \
- TYPE _tmpl = TPL; \
- GIT_ERROR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \
- memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0)
-
/** Check for additive overflow, setting an error if would occur. */
#define GIT_ADD_SIZET_OVERFLOW(out, one, two) \
@@ -204,11 +163,6 @@ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int v
#define GIT_ERROR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \
if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; }
-/* NOTE: other git_error functions are in the public errors.h header file */
-
-/* Forward declare git_str */
-typedef struct git_str git_str;
-
#include "util.h"
#endif
diff --git a/src/hash.c b/src/util/hash.c
similarity index 82%
rename from src/hash.c
rename to src/util/hash.c
index 98ceb05d2..ff900cea7 100644
--- a/src/hash.c
+++ b/src/util/hash.c
@@ -9,7 +9,11 @@
int git_hash_global_init(void)
{
- return git_hash_sha1_global_init();
+ if (git_hash_sha1_global_init() < 0 ||
+ git_hash_sha256_global_init() < 0)
+ return -1;
+
+ return 0;
}
int git_hash_ctx_init(git_hash_ctx *ctx, git_hash_algorithm_t algorithm)
@@ -20,6 +24,9 @@ int git_hash_ctx_init(git_hash_ctx *ctx, git_hash_algorithm_t algorithm)
case GIT_HASH_ALGORITHM_SHA1:
error = git_hash_sha1_ctx_init(&ctx->ctx.sha1);
break;
+ case GIT_HASH_ALGORITHM_SHA256:
+ error = git_hash_sha256_ctx_init(&ctx->ctx.sha256);
+ break;
default:
git_error_set(GIT_ERROR_INTERNAL, "unknown hash algorithm");
error = -1;
@@ -35,6 +42,9 @@ void git_hash_ctx_cleanup(git_hash_ctx *ctx)
case GIT_HASH_ALGORITHM_SHA1:
git_hash_sha1_ctx_cleanup(&ctx->ctx.sha1);
return;
+ case GIT_HASH_ALGORITHM_SHA256:
+ git_hash_sha256_ctx_cleanup(&ctx->ctx.sha256);
+ return;
default:
/* unreachable */ ;
}
@@ -45,6 +55,8 @@ int git_hash_init(git_hash_ctx *ctx)
switch (ctx->algorithm) {
case GIT_HASH_ALGORITHM_SHA1:
return git_hash_sha1_init(&ctx->ctx.sha1);
+ case GIT_HASH_ALGORITHM_SHA256:
+ return git_hash_sha256_init(&ctx->ctx.sha256);
default:
/* unreachable */ ;
}
@@ -58,6 +70,8 @@ int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
switch (ctx->algorithm) {
case GIT_HASH_ALGORITHM_SHA1:
return git_hash_sha1_update(&ctx->ctx.sha1, data, len);
+ case GIT_HASH_ALGORITHM_SHA256:
+ return git_hash_sha256_update(&ctx->ctx.sha256, data, len);
default:
/* unreachable */ ;
}
@@ -71,6 +85,8 @@ int git_hash_final(unsigned char *out, git_hash_ctx *ctx)
switch (ctx->algorithm) {
case GIT_HASH_ALGORITHM_SHA1:
return git_hash_sha1_final(out, &ctx->ctx.sha1);
+ case GIT_HASH_ALGORITHM_SHA256:
+ return git_hash_sha256_final(out, &ctx->ctx.sha256);
default:
/* unreachable */ ;
}
diff --git a/src/hash.h b/src/util/hash.h
similarity index 89%
rename from src/hash.h
rename to src/util/hash.h
index 507c1cb25..387c5a66f 100644
--- a/src/hash.h
+++ b/src/util/hash.h
@@ -8,9 +8,9 @@
#ifndef INCLUDE_hash_h__
#define INCLUDE_hash_h__
-#include "common.h"
+#include "git2_util.h"
-#include "hash/sha1.h"
+#include "hash/sha.h"
typedef struct {
void *data;
@@ -19,12 +19,14 @@ typedef struct {
typedef enum {
GIT_HASH_ALGORITHM_NONE = 0,
- GIT_HASH_ALGORITHM_SHA1
+ GIT_HASH_ALGORITHM_SHA1,
+ GIT_HASH_ALGORITHM_SHA256
} git_hash_algorithm_t;
typedef struct git_hash_ctx {
union {
git_hash_sha1_ctx sha1;
+ git_hash_sha256_ctx sha256;
} ctx;
git_hash_algorithm_t algorithm;
} git_hash_ctx;
diff --git a/src/util/hash/builtin.c b/src/util/hash/builtin.c
new file mode 100644
index 000000000..cc4aa58fe
--- /dev/null
+++ b/src/util/hash/builtin.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "builtin.h"
+
+int git_hash_sha256_global_init(void)
+{
+ return 0;
+}
+
+int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx)
+{
+ return git_hash_sha256_init(ctx);
+}
+
+void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx)
+{
+ GIT_UNUSED(ctx);
+}
+
+int git_hash_sha256_init(git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ if (SHA256Reset(&ctx->c)) {
+ git_error_set(GIT_ERROR_SHA, "SHA256 error");
+ return -1;
+ }
+ return 0;
+}
+
+int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+ if (SHA256Input(&ctx->c, data, len)) {
+ git_error_set(GIT_ERROR_SHA, "SHA256 error");
+ return -1;
+ }
+ return 0;
+}
+
+int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ if (SHA256Result(&ctx->c, out)) {
+ git_error_set(GIT_ERROR_SHA, "SHA256 error");
+ return -1;
+ }
+ return 0;
+}
diff --git a/src/hash/sha1/openssl.h b/src/util/hash/builtin.h
similarity index 58%
rename from src/hash/sha1/openssl.h
rename to src/util/hash/builtin.h
index a223ca03e..769df1ac2 100644
--- a/src/hash/sha1/openssl.h
+++ b/src/util/hash/builtin.h
@@ -5,15 +5,15 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_hash_sha1_openssl_h__
-#define INCLUDE_hash_sha1_openssl_h__
+#ifndef INCLUDE_hash_builtin_h__
+#define INCLUDE_hash_builtin_h__
-#include "hash/sha1.h"
+#include "hash/sha.h"
-#include
+#include "rfc6234/sha.h"
-struct git_hash_sha1_ctx {
- SHA_CTX c;
+struct git_hash_sha256_ctx {
+ SHA256Context c;
};
#endif
diff --git a/src/hash/sha1/collisiondetect.c b/src/util/hash/collisiondetect.c
similarity index 92%
rename from src/hash/sha1/collisiondetect.c
rename to src/util/hash/collisiondetect.c
index ec7059c4c..c51a402d7 100644
--- a/src/hash/sha1/collisiondetect.c
+++ b/src/util/hash/collisiondetect.c
@@ -40,7 +40,7 @@ int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
{
GIT_ASSERT_ARG(ctx);
if (SHA1DCFinal(out, &ctx->c)) {
- git_error_set(GIT_ERROR_SHA1, "SHA1 collision attack detected");
+ git_error_set(GIT_ERROR_SHA, "SHA1 collision attack detected");
return -1;
}
diff --git a/src/hash/sha1/collisiondetect.h b/src/util/hash/collisiondetect.h
similarity index 71%
rename from src/hash/sha1/collisiondetect.h
rename to src/util/hash/collisiondetect.h
index eb88e86c1..8de550230 100644
--- a/src/hash/sha1/collisiondetect.h
+++ b/src/util/hash/collisiondetect.h
@@ -5,10 +5,10 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_hash_sha1_collisiondetect_h__
-#define INCLUDE_hash_sha1_collisiondetect_h__
+#ifndef INCLUDE_hash_collisiondetect_h__
+#define INCLUDE_hash_collisiondetect_h__
-#include "hash/sha1.h"
+#include "hash/sha.h"
#include "sha1dc/sha1.h"
diff --git a/src/hash/sha1/common_crypto.c b/src/util/hash/common_crypto.c
similarity index 54%
rename from src/hash/sha1/common_crypto.c
rename to src/util/hash/common_crypto.c
index 9d608f449..b327ba9e3 100644
--- a/src/hash/sha1/common_crypto.c
+++ b/src/util/hash/common_crypto.c
@@ -9,6 +9,8 @@
#define CC_LONG_MAX ((CC_LONG)-1)
+#ifdef GIT_SHA1_COMMON_CRYPTO
+
int git_hash_sha1_global_init(void)
{
return 0;
@@ -55,3 +57,56 @@ int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
CC_SHA1_Final(out, &ctx->c);
return 0;
}
+
+#endif
+
+#ifdef GIT_SHA256_COMMON_CRYPTO
+
+int git_hash_sha256_global_init(void)
+{
+ return 0;
+}
+
+int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx)
+{
+ return git_hash_sha256_init(ctx);
+}
+
+void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx)
+{
+ GIT_UNUSED(ctx);
+}
+
+int git_hash_sha256_init(git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ CC_SHA256_Init(&ctx->c);
+ return 0;
+}
+
+int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *_data, size_t len)
+{
+ const unsigned char *data = _data;
+
+ GIT_ASSERT_ARG(ctx);
+
+ while (len > 0) {
+ CC_LONG chunk = (len > CC_LONG_MAX) ? CC_LONG_MAX : (CC_LONG)len;
+
+ CC_SHA256_Update(&ctx->c, data, chunk);
+
+ data += chunk;
+ len -= chunk;
+ }
+
+ return 0;
+}
+
+int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ CC_SHA256_Final(out, &ctx->c);
+ return 0;
+}
+
+#endif
diff --git a/src/hash/sha1/common_crypto.h b/src/util/hash/common_crypto.h
similarity index 57%
rename from src/hash/sha1/common_crypto.h
rename to src/util/hash/common_crypto.h
index a5fcfb33e..157712b5f 100644
--- a/src/hash/sha1/common_crypto.h
+++ b/src/util/hash/common_crypto.h
@@ -5,15 +5,23 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_hash_sha1_common_crypto_h__
-#define INCLUDE_hash_sha1_common_crypto_h__
+#ifndef INCLUDE_hash_common_crypto_h__
+#define INCLUDE_hash_common_crypto_h__
-#include "hash/sha1.h"
+#include "hash/sha.h"
#include
+#ifdef GIT_SHA1_COMMON_CRYPTO
struct git_hash_sha1_ctx {
CC_SHA1_CTX c;
};
+#endif
+
+#ifdef GIT_SHA256_COMMON_CRYPTO
+struct git_hash_sha256_ctx {
+ CC_SHA256_CTX c;
+};
+#endif
#endif
diff --git a/src/hash/sha1/mbedtls.c b/src/util/hash/mbedtls.c
similarity index 53%
rename from src/hash/sha1/mbedtls.c
rename to src/util/hash/mbedtls.c
index 56016bec8..ecdfb7879 100644
--- a/src/hash/sha1/mbedtls.c
+++ b/src/util/hash/mbedtls.c
@@ -7,6 +7,8 @@
#include "mbedtls.h"
+#ifdef GIT_SHA1_MBEDTLS
+
int git_hash_sha1_global_init(void)
{
return 0;
@@ -44,3 +46,47 @@ int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
mbedtls_sha1_finish(&ctx->c, out);
return 0;
}
+
+#endif
+
+#ifdef GIT_SHA256_MBEDTLS
+
+int git_hash_sha256_global_init(void)
+{
+ return 0;
+}
+
+int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx)
+{
+ return git_hash_sha256_init(ctx);
+}
+
+void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx)
+{
+ if (ctx)
+ mbedtls_sha256_free(&ctx->c);
+}
+
+int git_hash_sha256_init(git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ mbedtls_sha256_init(&ctx->c);
+ mbedtls_sha256_starts(&ctx->c, 0);
+ return 0;
+}
+
+int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+ mbedtls_sha256_update(&ctx->c, data, len);
+ return 0;
+}
+
+int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ mbedtls_sha256_finish(&ctx->c, out);
+ return 0;
+}
+
+#endif
diff --git a/src/hash/sha1/mbedtls.h b/src/util/hash/mbedtls.h
similarity index 54%
rename from src/hash/sha1/mbedtls.h
rename to src/util/hash/mbedtls.h
index 15f7462a4..05fb38b0e 100644
--- a/src/hash/sha1/mbedtls.h
+++ b/src/util/hash/mbedtls.h
@@ -5,15 +5,25 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_hash_sha1_mbedtls_h__
-#define INCLUDE_hash_sha1_mbedtls_h__
+#ifndef INCLUDE_hash_mbedtls_h__
+#define INCLUDE_hash_mbedtls_h__
-#include "hash/sha1.h"
+#include "hash/sha.h"
-#include
+#ifdef GIT_SHA1_MBEDTLS
+# include
struct git_hash_sha1_ctx {
mbedtls_sha1_context c;
};
+#endif
+
+#ifdef GIT_SHA256_MBEDTLS
+# include
+
+struct git_hash_sha256_ctx {
+ mbedtls_sha256_context c;
+};
+#endif
#endif /* INCLUDE_hash_sha1_mbedtls_h__ */
diff --git a/src/util/hash/openssl.c b/src/util/hash/openssl.c
new file mode 100644
index 000000000..649358ca2
--- /dev/null
+++ b/src/util/hash/openssl.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "openssl.h"
+
+#ifdef GIT_OPENSSL_DYNAMIC
+# include
+
+int handle_count;
+void *openssl_handle;
+
+static int git_hash_openssl_global_shutdown(void)
+{
+ if (--handle_count == 0) {
+ dlclose(openssl_handle);
+ openssl_handle = NULL;
+ }
+
+ return 0;
+}
+
+static int git_hash_openssl_global_init(void)
+{
+ if (!handle_count) {
+ if ((openssl_handle = dlopen("libssl.so.1.1", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.1.1.dylib", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.1.0.0", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.1.0.0.dylib", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL) {
+ git_error_set(GIT_ERROR_SSL, "could not load ssl libraries");
+ return -1;
+ }
+ }
+
+ if (git_hash_openssl_global_shutdown() < 0)
+ return -1;
+
+ handle_count++;
+ return 0;
+}
+
+#endif
+
+#ifdef GIT_SHA1_OPENSSL
+
+# ifdef GIT_OPENSSL_DYNAMIC
+static int (*SHA1_Init)(SHA_CTX *c);
+static int (*SHA1_Update)(SHA_CTX *c, const void *data, size_t len);
+static int (*SHA1_Final)(unsigned char *md, SHA_CTX *c);
+# endif
+
+int git_hash_sha1_global_init(void)
+{
+#ifdef GIT_OPENSSL_DYNAMIC
+ if (git_hash_openssl_global_init() < 0)
+ return -1;
+
+ if ((SHA1_Init = dlsym(openssl_handle, "SHA1_Init")) == NULL ||
+ (SHA1_Update = dlsym(openssl_handle, "SHA1_Update")) == NULL ||
+ (SHA1_Final = dlsym(openssl_handle, "SHA1_Final")) == NULL) {
+ const char *msg = dlerror();
+ git_error_set(GIT_ERROR_SSL, "could not load hash function: %s", msg ? msg : "unknown error");
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
+{
+ return git_hash_sha1_init(ctx);
+}
+
+void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
+{
+ GIT_UNUSED(ctx);
+}
+
+int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+
+ if (SHA1_Init(&ctx->c) != 1) {
+ git_error_set(GIT_ERROR_SHA, "failed to initialize sha1 context");
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+
+ if (SHA1_Update(&ctx->c, data, len) != 1) {
+ git_error_set(GIT_ERROR_SHA, "failed to update sha1");
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+
+ if (SHA1_Final(out, &ctx->c) != 1) {
+ git_error_set(GIT_ERROR_SHA, "failed to finalize sha1");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif
+
+#ifdef GIT_SHA256_OPENSSL
+
+# ifdef GIT_OPENSSL_DYNAMIC
+static int (*SHA256_Init)(SHA256_CTX *c);
+static int (*SHA256_Update)(SHA256_CTX *c, const void *data, size_t len);
+static int (*SHA256_Final)(unsigned char *md, SHA256_CTX *c);
+#endif
+
+int git_hash_sha256_global_init(void)
+{
+#ifdef GIT_OPENSSL_DYNAMIC
+ if (git_hash_openssl_global_init() < 0)
+ return -1;
+
+ if ((SHA256_Init = dlsym(openssl_handle, "SHA256_Init")) == NULL ||
+ (SHA256_Update = dlsym(openssl_handle, "SHA256_Update")) == NULL ||
+ (SHA256_Final = dlsym(openssl_handle, "SHA256_Final")) == NULL) {
+ const char *msg = dlerror();
+ git_error_set(GIT_ERROR_SSL, "could not load hash function: %s", msg ? msg : "unknown error");
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx)
+{
+ return git_hash_sha256_init(ctx);
+}
+
+void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx)
+{
+ GIT_UNUSED(ctx);
+}
+
+int git_hash_sha256_init(git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+
+ if (SHA256_Init(&ctx->c) != 1) {
+ git_error_set(GIT_ERROR_SHA, "failed to initialize sha256 context");
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+
+ if (SHA256_Update(&ctx->c, data, len) != 1) {
+ git_error_set(GIT_ERROR_SHA, "failed to update sha256");
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+
+ if (SHA256_Final(out, &ctx->c) != 1) {
+ git_error_set(GIT_ERROR_SHA, "failed to finalize sha256");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/src/util/hash/openssl.h b/src/util/hash/openssl.h
new file mode 100644
index 000000000..7cb089abc
--- /dev/null
+++ b/src/util/hash/openssl.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_hash_openssl_h__
+#define INCLUDE_hash_openssl_h__
+
+#include "hash/sha.h"
+
+#ifndef GIT_OPENSSL_DYNAMIC
+# include
+#else
+
+typedef struct {
+ unsigned int h0, h1, h2, h3, h4;
+ unsigned int Nl, Nh;
+ unsigned int data[16];
+ unsigned int num;
+} SHA_CTX;
+
+typedef struct {
+ unsigned int h[8];
+ unsigned int Nl, Nh;
+ unsigned int data[16];
+ unsigned int num, md_len;
+} SHA256_CTX;
+
+#endif
+
+#ifdef GIT_SHA1_OPENSSL
+struct git_hash_sha1_ctx {
+ SHA_CTX c;
+};
+#endif
+
+#ifdef GIT_SHA256_OPENSSL
+struct git_hash_sha256_ctx {
+ SHA256_CTX c;
+};
+#endif
+
+#endif
diff --git a/src/util/hash/rfc6234/sha.h b/src/util/hash/rfc6234/sha.h
new file mode 100644
index 000000000..e0c400ca1
--- /dev/null
+++ b/src/util/hash/rfc6234/sha.h
@@ -0,0 +1,355 @@
+/**************************** sha.h ****************************/
+/***************** See RFC 6234 for details. *******************/
+/*
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ - Redistributions of source code must retain the above
+ copyright notice, this list of conditions and
+ the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ - Neither the name of Internet Society, IETF or IETF Trust, nor
+ the names of specific contributors, may be used to endorse or
+ promote products derived from this software without specific
+ prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef _SHA_H_
+#define _SHA_H_
+
+/*
+ * Description:
+ * This file implements the Secure Hash Algorithms
+ * as defined in the U.S. National Institute of Standards
+ * and Technology Federal Information Processing Standards
+ * Publication (FIPS PUB) 180-3 published in October 2008
+ * and formerly defined in its predecessors, FIPS PUB 180-1
+ * and FIP PUB 180-2.
+ *
+ * A combined document showing all algorithms is available at
+ * http://csrc.nist.gov/publications/fips/
+ * fips180-3/fips180-3_final.pdf
+ *
+ * The five hashes are defined in these sizes:
+ * SHA-1 20 byte / 160 bit
+ * SHA-224 28 byte / 224 bit
+ * SHA-256 32 byte / 256 bit
+ * SHA-384 48 byte / 384 bit
+ * SHA-512 64 byte / 512 bit
+ *
+ * Compilation Note:
+ * These files may be compiled with two options:
+ * USE_32BIT_ONLY - use 32-bit arithmetic only, for systems
+ * without 64-bit integers
+ *
+ * USE_MODIFIED_MACROS - use alternate form of the SHA_Ch()
+ * and SHA_Maj() macros that are equivalent
+ * and potentially faster on many systems
+ *
+ */
+
+#include
+/*
+ * If you do not have the ISO standard stdint.h header file, then you
+ * must typedef the following:
+ * name meaning
+ * uint64_t unsigned 64-bit integer
+ * uint32_t unsigned 32-bit integer
+ * uint8_t unsigned 8-bit integer (i.e., unsigned char)
+ * int_least16_t integer of >= 16 bits
+ *
+ * See stdint-example.h
+ */
+
+#ifndef _SHA_enum_
+#define _SHA_enum_
+/*
+ * All SHA functions return one of these values.
+ */
+enum {
+ shaSuccess = 0,
+ shaNull, /* Null pointer parameter */
+ shaInputTooLong, /* input data too long */
+ shaStateError, /* called Input after FinalBits or Result */
+ shaBadParam /* passed a bad parameter */
+};
+#endif /* _SHA_enum_ */
+
+/*
+ * These constants hold size information for each of the SHA
+ * hashing operations
+ */
+enum {
+ SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64,
+ SHA256_Message_Block_Size = 64, SHA384_Message_Block_Size = 128,
+ SHA512_Message_Block_Size = 128,
+ USHA_Max_Message_Block_Size = SHA512_Message_Block_Size,
+ SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32,
+ SHA384HashSize = 48, SHA512HashSize = 64,
+ USHAMaxHashSize = SHA512HashSize,
+
+ SHA1HashSizeBits = 160, SHA224HashSizeBits = 224,
+ SHA256HashSizeBits = 256, SHA384HashSizeBits = 384,
+ SHA512HashSizeBits = 512, USHAMaxHashSizeBits = SHA512HashSizeBits
+};
+
+/*
+ * These constants are used in the USHA (Unified SHA) functions.
+ */
+typedef enum SHAversion {
+ SHA1, SHA224, SHA256, SHA384, SHA512
+} SHAversion;
+
+/*
+ * This structure will hold context information for the SHA-1
+ * hashing operation.
+ */
+typedef struct SHA1Context {
+ uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
+
+ uint32_t Length_High; /* Message length in bits */
+ uint32_t Length_Low; /* Message length in bits */
+
+ int_least16_t Message_Block_Index; /* Message_Block array index */
+ /* 512-bit message blocks */
+ uint8_t Message_Block[SHA1_Message_Block_Size];
+
+ int Computed; /* Is the hash computed? */
+ int Corrupted; /* Cumulative corruption code */
+} SHA1Context;
+
+/*
+ * This structure will hold context information for the SHA-256
+ * hashing operation.
+ */
+typedef struct SHA256Context {
+ uint32_t Intermediate_Hash[SHA256HashSize/4]; /* Message Digest */
+
+ uint32_t Length_High; /* Message length in bits */
+ uint32_t Length_Low; /* Message length in bits */
+
+ int_least16_t Message_Block_Index; /* Message_Block array index */
+ /* 512-bit message blocks */
+ uint8_t Message_Block[SHA256_Message_Block_Size];
+
+ int Computed; /* Is the hash computed? */
+ int Corrupted; /* Cumulative corruption code */
+} SHA256Context;
+
+/*
+ * This structure will hold context information for the SHA-512
+ * hashing operation.
+ */
+typedef struct SHA512Context {
+#ifdef USE_32BIT_ONLY
+ uint32_t Intermediate_Hash[SHA512HashSize/4]; /* Message Digest */
+ uint32_t Length[4]; /* Message length in bits */
+#else /* !USE_32BIT_ONLY */
+ uint64_t Intermediate_Hash[SHA512HashSize/8]; /* Message Digest */
+ uint64_t Length_High, Length_Low; /* Message length in bits */
+#endif /* USE_32BIT_ONLY */
+
+ int_least16_t Message_Block_Index; /* Message_Block array index */
+ /* 1024-bit message blocks */
+ uint8_t Message_Block[SHA512_Message_Block_Size];
+
+ int Computed; /* Is the hash computed?*/
+ int Corrupted; /* Cumulative corruption code */
+} SHA512Context;
+
+/*
+ * This structure will hold context information for the SHA-224
+ * hashing operation. It uses the SHA-256 structure for computation.
+ */
+typedef struct SHA256Context SHA224Context;
+
+/*
+ * This structure will hold context information for the SHA-384
+ * hashing operation. It uses the SHA-512 structure for computation.
+ */
+typedef struct SHA512Context SHA384Context;
+
+/*
+ * This structure holds context information for all SHA
+ * hashing operations.
+ */
+typedef struct USHAContext {
+ int whichSha; /* which SHA is being used */
+ union {
+ SHA1Context sha1Context;
+ SHA224Context sha224Context; SHA256Context sha256Context;
+ SHA384Context sha384Context; SHA512Context sha512Context;
+ } ctx;
+} USHAContext;
+
+/*
+ * This structure will hold context information for the HMAC
+ * keyed-hashing operation.
+ */
+typedef struct HMACContext {
+ int whichSha; /* which SHA is being used */
+ int hashSize; /* hash size of SHA being used */
+ int blockSize; /* block size of SHA being used */
+ USHAContext shaContext; /* SHA context */
+ unsigned char k_opad[USHA_Max_Message_Block_Size];
+ /* outer padding - key XORd with opad */
+ int Computed; /* Is the MAC computed? */
+ int Corrupted; /* Cumulative corruption code */
+
+} HMACContext;
+
+/*
+ * This structure will hold context information for the HKDF
+ * extract-and-expand Key Derivation Functions.
+ */
+typedef struct HKDFContext {
+ int whichSha; /* which SHA is being used */
+ HMACContext hmacContext;
+ int hashSize; /* hash size of SHA being used */
+ unsigned char prk[USHAMaxHashSize];
+ /* pseudo-random key - output of hkdfInput */
+ int Computed; /* Is the key material computed? */
+ int Corrupted; /* Cumulative corruption code */
+} HKDFContext;
+
+/*
+ * Function Prototypes
+ */
+
+/* SHA-1 */
+extern int SHA1Reset(SHA1Context *);
+extern int SHA1Input(SHA1Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA1FinalBits(SHA1Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA1Result(SHA1Context *,
+ uint8_t Message_Digest[SHA1HashSize]);
+
+/* SHA-224 */
+extern int SHA224Reset(SHA224Context *);
+extern int SHA224Input(SHA224Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA224FinalBits(SHA224Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA224Result(SHA224Context *,
+ uint8_t Message_Digest[SHA224HashSize]);
+
+/* SHA-256 */
+extern int SHA256Reset(SHA256Context *);
+extern int SHA256Input(SHA256Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA256FinalBits(SHA256Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA256Result(SHA256Context *,
+ uint8_t Message_Digest[SHA256HashSize]);
+
+/* SHA-384 */
+extern int SHA384Reset(SHA384Context *);
+extern int SHA384Input(SHA384Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA384FinalBits(SHA384Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA384Result(SHA384Context *,
+ uint8_t Message_Digest[SHA384HashSize]);
+
+/* SHA-512 */
+extern int SHA512Reset(SHA512Context *);
+extern int SHA512Input(SHA512Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA512FinalBits(SHA512Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA512Result(SHA512Context *,
+ uint8_t Message_Digest[SHA512HashSize]);
+
+/* Unified SHA functions, chosen by whichSha */
+extern int USHAReset(USHAContext *context, SHAversion whichSha);
+extern int USHAInput(USHAContext *context,
+ const uint8_t *bytes, unsigned int bytecount);
+extern int USHAFinalBits(USHAContext *context,
+ uint8_t bits, unsigned int bit_count);
+extern int USHAResult(USHAContext *context,
+ uint8_t Message_Digest[USHAMaxHashSize]);
+extern int USHABlockSize(enum SHAversion whichSha);
+extern int USHAHashSize(enum SHAversion whichSha);
+extern int USHAHashSizeBits(enum SHAversion whichSha);
+extern const char *USHAHashName(enum SHAversion whichSha);
+
+/*
+ * HMAC Keyed-Hashing for Message Authentication, RFC 2104,
+ * for all SHAs.
+ * This interface allows a fixed-length text input to be used.
+ */
+extern int hmac(SHAversion whichSha, /* which SHA algorithm to use */
+ const unsigned char *text, /* pointer to data stream */
+ int text_len, /* length of data stream */
+ const unsigned char *key, /* pointer to authentication key */
+ int key_len, /* length of authentication key */
+ uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */
+
+/*
+ * HMAC Keyed-Hashing for Message Authentication, RFC 2104,
+ * for all SHAs.
+ * This interface allows any length of text input to be used.
+ */
+extern int hmacReset(HMACContext *context, enum SHAversion whichSha,
+ const unsigned char *key, int key_len);
+extern int hmacInput(HMACContext *context, const unsigned char *text,
+ int text_len);
+extern int hmacFinalBits(HMACContext *context, uint8_t bits,
+ unsigned int bit_count);
+extern int hmacResult(HMACContext *context,
+ uint8_t digest[USHAMaxHashSize]);
+
+/*
+ * HKDF HMAC-based Extract-and-Expand Key Derivation Function,
+ * RFC 5869, for all SHAs.
+ */
+extern int hkdf(SHAversion whichSha, const unsigned char *salt,
+ int salt_len, const unsigned char *ikm, int ikm_len,
+ const unsigned char *info, int info_len,
+ uint8_t okm[ ], int okm_len);
+extern int hkdfExtract(SHAversion whichSha, const unsigned char *salt,
+ int salt_len, const unsigned char *ikm,
+ int ikm_len, uint8_t prk[USHAMaxHashSize]);
+extern int hkdfExpand(SHAversion whichSha, const uint8_t prk[ ],
+ int prk_len, const unsigned char *info,
+ int info_len, uint8_t okm[ ], int okm_len);
+
+/*
+ * HKDF HMAC-based Extract-and-Expand Key Derivation Function,
+ * RFC 5869, for all SHAs.
+ * This interface allows any length of text input to be used.
+ */
+extern int hkdfReset(HKDFContext *context, enum SHAversion whichSha,
+ const unsigned char *salt, int salt_len);
+extern int hkdfInput(HKDFContext *context, const unsigned char *ikm,
+ int ikm_len);
+extern int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits,
+ unsigned int ikm_bit_count);
+extern int hkdfResult(HKDFContext *context,
+ uint8_t prk[USHAMaxHashSize],
+ const unsigned char *info, int info_len,
+ uint8_t okm[USHAMaxHashSize], int okm_len);
+#endif /* _SHA_H_ */
diff --git a/src/util/hash/rfc6234/sha224-256.c b/src/util/hash/rfc6234/sha224-256.c
new file mode 100644
index 000000000..c8e0cf854
--- /dev/null
+++ b/src/util/hash/rfc6234/sha224-256.c
@@ -0,0 +1,601 @@
+/************************* sha224-256.c ************************/
+/***************** See RFC 6234 for details. *******************/
+/* Copyright (c) 2011 IETF Trust and the persons identified as */
+/* authors of the code. All rights reserved. */
+/* See sha.h for terms of use and redistribution. */
+
+/*
+ * Description:
+ * This file implements the Secure Hash Algorithms SHA-224 and
+ * SHA-256 as defined in the U.S. National Institute of Standards
+ * and Technology Federal Information Processing Standards
+ * Publication (FIPS PUB) 180-3 published in October 2008
+ * and formerly defined in its predecessors, FIPS PUB 180-1
+ * and FIP PUB 180-2.
+ *
+ * A combined document showing all algorithms is available at
+ * http://csrc.nist.gov/publications/fips/
+ * fips180-3/fips180-3_final.pdf
+ *
+ * The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit
+ * message digests for a given data stream. It should take about
+ * 2**n steps to find a message with the same digest as a given
+ * message and 2**(n/2) to find any two messages with the same
+ * digest, when n is the digest size in bits. Therefore, this
+ * algorithm can serve as a means of providing a
+ * "fingerprint" for a message.
+ *
+ * Portability Issues:
+ * SHA-224 and SHA-256 are defined in terms of 32-bit "words".
+ * This code uses (included via "sha.h") to define 32-
+ * and 8-bit unsigned integer types. If your C compiler does not
+ * support 32-bit unsigned integers, this code is not
+ * appropriate.
+ *
+ * Caveats:
+ * SHA-224 and SHA-256 are designed to work with messages less
+ * than 2^64 bits long. This implementation uses SHA224/256Input()
+ * to hash the bits that are a multiple of the size of an 8-bit
+ * octet, and then optionally uses SHA224/256FinalBits()
+ * to hash the final few bits of the input.
+ */
+
+#include "sha.h"
+
+/*
+ * These definitions are defined in FIPS 180-3, section 4.1.
+ * Ch() and Maj() are defined identically in sections 4.1.1,
+ * 4.1.2, and 4.1.3.
+ *
+ * The definitions used in FIPS 180-3 are as follows:
+ */
+#ifndef USE_MODIFIED_MACROS
+#define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
+#define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#else /* USE_MODIFIED_MACROS */
+/*
+ * The following definitions are equivalent and potentially faster.
+ */
+#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z))
+#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
+#endif /* USE_MODIFIED_MACROS */
+
+#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z))
+
+/* Define the SHA shift, rotate left, and rotate right macros */
+#define SHA256_SHR(bits,word) ((word) >> (bits))
+#define SHA256_ROTL(bits,word) \
+ (((word) << (bits)) | ((word) >> (32-(bits))))
+#define SHA256_ROTR(bits,word) \
+ (((word) >> (bits)) | ((word) << (32-(bits))))
+
+/* Define the SHA SIGMA and sigma macros */
+#define SHA256_SIGMA0(word) \
+ (SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word))
+#define SHA256_SIGMA1(word) \
+ (SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word))
+#define SHA256_sigma0(word) \
+ (SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word))
+#define SHA256_sigma1(word) \
+ (SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word))
+
+/*
+ * Add "length" to the length.
+ * Set Corrupted when overflow has occurred.
+ */
+static uint32_t addTemp;
+#define SHA224_256AddLength(context, length) \
+ (addTemp = (context)->Length_Low, (context)->Corrupted = \
+ (((context)->Length_Low += (length)) < addTemp) && \
+ (++(context)->Length_High == 0) ? shaInputTooLong : \
+ (context)->Corrupted )
+
+/* Local Function Prototypes */
+static int SHA224_256Reset(SHA256Context *context, uint32_t *H0);
+static void SHA224_256ProcessMessageBlock(SHA256Context *context);
+static void SHA224_256Finalize(SHA256Context *context,
+ uint8_t Pad_Byte);
+static void SHA224_256PadMessage(SHA256Context *context,
+ uint8_t Pad_Byte);
+static int SHA224_256ResultN(SHA256Context *context,
+ uint8_t Message_Digest[ ], int HashSize);
+
+/* Initial Hash Values: FIPS 180-3 section 5.3.2 */
+static uint32_t SHA224_H0[SHA256HashSize/4] = {
+ 0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939,
+ 0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4
+};
+
+/* Initial Hash Values: FIPS 180-3 section 5.3.3 */
+static uint32_t SHA256_H0[SHA256HashSize/4] = {
+ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
+ 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
+};
+
+/*
+ * SHA224Reset
+ *
+ * Description:
+ * This function will initialize the SHA224Context in preparation
+ * for computing a new SHA224 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA224Reset(SHA224Context *context)
+{
+ return SHA224_256Reset(context, SHA224_H0);
+}
+
+/*
+ * SHA224Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * message_array[ ]: [in]
+ * An array of octets representing the next portion of
+ * the message.
+ * length: [in]
+ * The length of the message in message_array.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int SHA224Input(SHA224Context *context, const uint8_t *message_array,
+ unsigned int length)
+{
+ return SHA256Input(context, message_array, length);
+}
+
+/*
+ * SHA224FinalBits
+ *
+ * Description:
+ * This function will add in any final bits of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * message_bits: [in]
+ * The final bits of the message, in the upper portion of the
+ * byte. (Use 0b###00000 instead of 0b00000### to input the
+ * three bits ###.)
+ * length: [in]
+ * The number of bits in message_bits, between 1 and 7.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA224FinalBits(SHA224Context *context,
+ uint8_t message_bits, unsigned int length)
+{
+ return SHA256FinalBits(context, message_bits, length);
+}
+
+/*
+ * SHA224Result
+ *
+ * Description:
+ * This function will return the 224-bit message digest
+ * into the Message_Digest array provided by the caller.
+ * NOTE:
+ * The first octet of hash is stored in the element with index 0,
+ * the last octet of hash in the element with index 27.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA hash.
+ * Message_Digest[ ]: [out]
+ * Where the digest is returned.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA224Result(SHA224Context *context,
+ uint8_t Message_Digest[SHA224HashSize])
+{
+ return SHA224_256ResultN(context, Message_Digest, SHA224HashSize);
+}
+
+/*
+ * SHA256Reset
+ *
+ * Description:
+ * This function will initialize the SHA256Context in preparation
+ * for computing a new SHA256 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA256Reset(SHA256Context *context)
+{
+ return SHA224_256Reset(context, SHA256_H0);
+}
+
+/*
+ * SHA256Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * message_array[ ]: [in]
+ * An array of octets representing the next portion of
+ * the message.
+ * length: [in]
+ * The length of the message in message_array.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA256Input(SHA256Context *context, const uint8_t *message_array,
+ unsigned int length)
+{
+ if (!context) return shaNull;
+ if (!length) return shaSuccess;
+ if (!message_array) return shaNull;
+ if (context->Computed) return context->Corrupted = shaStateError;
+ if (context->Corrupted) return context->Corrupted;
+
+ while (length--) {
+ context->Message_Block[context->Message_Block_Index++] =
+ *message_array;
+
+ if ((SHA224_256AddLength(context, 8) == shaSuccess) &&
+ (context->Message_Block_Index == SHA256_Message_Block_Size))
+ SHA224_256ProcessMessageBlock(context);
+
+ message_array++;
+ }
+
+ return context->Corrupted;
+
+}
+
+/*
+ * SHA256FinalBits
+ *
+ * Description:
+ * This function will add in any final bits of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * message_bits: [in]
+ * The final bits of the message, in the upper portion of the
+ * byte. (Use 0b###00000 instead of 0b00000### to input the
+ * three bits ###.)
+ * length: [in]
+ * The number of bits in message_bits, between 1 and 7.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA256FinalBits(SHA256Context *context,
+ uint8_t message_bits, unsigned int length)
+{
+ static uint8_t masks[8] = {
+ /* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80,
+ /* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0,
+ /* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8,
+ /* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE
+ };
+ static uint8_t markbit[8] = {
+ /* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40,
+ /* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10,
+ /* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04,
+ /* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01
+ };
+
+ if (!context) return shaNull;
+ if (!length) return shaSuccess;
+ if (context->Corrupted) return context->Corrupted;
+ if (context->Computed) return context->Corrupted = shaStateError;
+ if (length >= 8) return context->Corrupted = shaBadParam;
+
+ SHA224_256AddLength(context, length);
+ SHA224_256Finalize(context, (uint8_t)
+ ((message_bits & masks[length]) | markbit[length]));
+
+ return context->Corrupted;
+}
+
+/*
+ * SHA256Result
+ *
+ * Description:
+ * This function will return the 256-bit message digest
+ * into the Message_Digest array provided by the caller.
+ * NOTE:
+ * The first octet of hash is stored in the element with index 0,
+ * the last octet of hash in the element with index 31.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA hash.
+ * Message_Digest[ ]: [out]
+ * Where the digest is returned.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA256Result(SHA256Context *context,
+ uint8_t Message_Digest[SHA256HashSize])
+{
+ return SHA224_256ResultN(context, Message_Digest, SHA256HashSize);
+}
+
+/*
+ * SHA224_256Reset
+ *
+ * Description:
+ * This helper function will initialize the SHA256Context in
+ * preparation for computing a new SHA-224 or SHA-256 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ * H0[ ]: [in]
+ * The initial hash value array to use.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+static int SHA224_256Reset(SHA256Context *context, uint32_t *H0)
+{
+ if (!context) return shaNull;
+
+ context->Length_High = context->Length_Low = 0;
+ context->Message_Block_Index = 0;
+
+ context->Intermediate_Hash[0] = H0[0];
+ context->Intermediate_Hash[1] = H0[1];
+ context->Intermediate_Hash[2] = H0[2];
+ context->Intermediate_Hash[3] = H0[3];
+ context->Intermediate_Hash[4] = H0[4];
+ context->Intermediate_Hash[5] = H0[5];
+ context->Intermediate_Hash[6] = H0[6];
+ context->Intermediate_Hash[7] = H0[7];
+
+ context->Computed = 0;
+ context->Corrupted = shaSuccess;
+
+ return shaSuccess;
+}
+
+/*
+ * SHA224_256ProcessMessageBlock
+ *
+ * Description:
+ * This helper function will process the next 512 bits of the
+ * message stored in the Message_Block array.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the
+ * names used in the Secure Hash Standard.
+ */
+static void SHA224_256ProcessMessageBlock(SHA256Context *context)
+{
+ /* Constants defined in FIPS 180-3, section 4.2.2 */
+ static const uint32_t K[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
+ 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
+ 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
+ 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
+ 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
+ 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
+ 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
+ 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
+ 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
+ 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+ };
+ int t, t4; /* Loop counter */
+ uint32_t temp1, temp2; /* Temporary word value */
+ uint32_t W[64]; /* Word sequence */
+ uint32_t A, B, C, D, E, F, G, H; /* Word buffers */
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for (t = t4 = 0; t < 16; t++, t4 += 4)
+ W[t] = (((uint32_t)context->Message_Block[t4]) << 24) |
+ (((uint32_t)context->Message_Block[t4 + 1]) << 16) |
+ (((uint32_t)context->Message_Block[t4 + 2]) << 8) |
+ (((uint32_t)context->Message_Block[t4 + 3]));
+
+ for (t = 16; t < 64; t++)
+ W[t] = SHA256_sigma1(W[t-2]) + W[t-7] +
+ SHA256_sigma0(W[t-15]) + W[t-16];
+
+ A = context->Intermediate_Hash[0];
+ B = context->Intermediate_Hash[1];
+ C = context->Intermediate_Hash[2];
+ D = context->Intermediate_Hash[3];
+ E = context->Intermediate_Hash[4];
+ F = context->Intermediate_Hash[5];
+ G = context->Intermediate_Hash[6];
+ H = context->Intermediate_Hash[7];
+
+ for (t = 0; t < 64; t++) {
+ temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E,F,G) + K[t] + W[t];
+ temp2 = SHA256_SIGMA0(A) + SHA_Maj(A,B,C);
+ H = G;
+ G = F;
+ F = E;
+ E = D + temp1;
+ D = C;
+ C = B;
+ B = A;
+ A = temp1 + temp2;
+ }
+
+ context->Intermediate_Hash[0] += A;
+ context->Intermediate_Hash[1] += B;
+ context->Intermediate_Hash[2] += C;
+ context->Intermediate_Hash[3] += D;
+ context->Intermediate_Hash[4] += E;
+ context->Intermediate_Hash[5] += F;
+ context->Intermediate_Hash[6] += G;
+ context->Intermediate_Hash[7] += H;
+
+ context->Message_Block_Index = 0;
+}
+
+/*
+ * SHA224_256Finalize
+ *
+ * Description:
+ * This helper function finishes off the digest calculations.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * Pad_Byte: [in]
+ * The last byte to add to the message block before the 0-padding
+ * and length. This will contain the last bits of the message
+ * followed by another single bit. If the message was an
+ * exact multiple of 8-bits long, Pad_Byte will be 0x80.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+static void SHA224_256Finalize(SHA256Context *context,
+ uint8_t Pad_Byte)
+{
+ int i;
+ SHA224_256PadMessage(context, Pad_Byte);
+ /* message may be sensitive, so clear it out */
+ for (i = 0; i < SHA256_Message_Block_Size; ++i)
+ context->Message_Block[i] = 0;
+ context->Length_High = 0; /* and clear length */
+ context->Length_Low = 0;
+ context->Computed = 1;
+}
+
+/*
+ * SHA224_256PadMessage
+ *
+ * Description:
+ * According to the standard, the message must be padded to the next
+ * even multiple of 512 bits. The first padding bit must be a '1'.
+ * The last 64 bits represent the length of the original message.
+ * All bits in between should be 0. This helper function will pad
+ * the message according to those rules by filling the
+ * Message_Block array accordingly. When it returns, it can be
+ * assumed that the message digest has been computed.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to pad.
+ * Pad_Byte: [in]
+ * The last byte to add to the message block before the 0-padding
+ * and length. This will contain the last bits of the message
+ * followed by another single bit. If the message was an
+ * exact multiple of 8-bits long, Pad_Byte will be 0x80.
+ *
+ * Returns:
+ * Nothing.
+ */
+static void SHA224_256PadMessage(SHA256Context *context,
+ uint8_t Pad_Byte)
+{
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second
+ * block.
+ */
+ if (context->Message_Block_Index >= (SHA256_Message_Block_Size-8)) {
+ context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
+ while (context->Message_Block_Index < SHA256_Message_Block_Size)
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ SHA224_256ProcessMessageBlock(context);
+ } else
+ context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
+
+ while (context->Message_Block_Index < (SHA256_Message_Block_Size-8))
+ context->Message_Block[context->Message_Block_Index++] = 0;
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ context->Message_Block[56] = (uint8_t)(context->Length_High >> 24);
+ context->Message_Block[57] = (uint8_t)(context->Length_High >> 16);
+ context->Message_Block[58] = (uint8_t)(context->Length_High >> 8);
+ context->Message_Block[59] = (uint8_t)(context->Length_High);
+ context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24);
+ context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16);
+ context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8);
+ context->Message_Block[63] = (uint8_t)(context->Length_Low);
+
+ SHA224_256ProcessMessageBlock(context);
+}
+
+/*
+ * SHA224_256ResultN
+ *
+ * Description:
+ * This helper function will return the 224-bit or 256-bit message
+ * digest into the Message_Digest array provided by the caller.
+ * NOTE:
+ * The first octet of hash is stored in the element with index 0,
+ * the last octet of hash in the element with index 27/31.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA hash.
+ * Message_Digest[ ]: [out]
+ * Where the digest is returned.
+ * HashSize: [in]
+ * The size of the hash, either 28 or 32.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+static int SHA224_256ResultN(SHA256Context *context,
+ uint8_t Message_Digest[ ], int HashSize)
+{
+ int i;
+
+ if (!context) return shaNull;
+ if (!Message_Digest) return shaNull;
+ if (context->Corrupted) return context->Corrupted;
+
+ if (!context->Computed)
+ SHA224_256Finalize(context, 0x80);
+
+ for (i = 0; i < HashSize; ++i)
+ Message_Digest[i] = (uint8_t)
+ (context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ));
+
+ return shaSuccess;
+}
+
diff --git a/src/util/hash/sha.h b/src/util/hash/sha.h
new file mode 100644
index 000000000..4f596234c
--- /dev/null
+++ b/src/util/hash/sha.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_hash_sha_h__
+#define INCLUDE_hash_sha_h__
+
+#include "git2_util.h"
+
+typedef struct git_hash_sha1_ctx git_hash_sha1_ctx;
+typedef struct git_hash_sha256_ctx git_hash_sha256_ctx;
+
+#if defined(GIT_SHA1_COMMON_CRYPTO) || defined(GIT_SHA256_COMMON_CRYPTO)
+# include "common_crypto.h"
+#endif
+
+#if defined(GIT_SHA1_OPENSSL) || defined(GIT_SHA256_OPENSSL)
+# include "openssl.h"
+#endif
+
+#if defined(GIT_SHA1_WIN32) || defined(GIT_SHA256_WIN32)
+# include "win32.h"
+#endif
+
+#if defined(GIT_SHA1_MBEDTLS) || defined(GIT_SHA256_MBEDTLS)
+# include "mbedtls.h"
+#endif
+
+#if defined(GIT_SHA1_COLLISIONDETECT)
+# include "collisiondetect.h"
+#endif
+
+#if defined(GIT_SHA256_BUILTIN)
+# include "builtin.h"
+#endif
+
+/*
+ * SHA1
+ */
+
+#define GIT_HASH_SHA1_SIZE 20
+
+int git_hash_sha1_global_init(void);
+
+int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx);
+void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx);
+
+int git_hash_sha1_init(git_hash_sha1_ctx *c);
+int git_hash_sha1_update(git_hash_sha1_ctx *c, const void *data, size_t len);
+int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *c);
+
+/*
+ * SHA256
+ */
+
+#define GIT_HASH_SHA256_SIZE 32
+
+int git_hash_sha256_global_init(void);
+
+int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx);
+void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx);
+
+int git_hash_sha256_init(git_hash_sha256_ctx *c);
+int git_hash_sha256_update(git_hash_sha256_ctx *c, const void *data, size_t len);
+int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *c);
+
+#endif
diff --git a/src/hash/sha1/sha1dc/sha1.c b/src/util/hash/sha1dc/sha1.c
similarity index 100%
rename from src/hash/sha1/sha1dc/sha1.c
rename to src/util/hash/sha1dc/sha1.c
diff --git a/src/hash/sha1/sha1dc/sha1.h b/src/util/hash/sha1dc/sha1.h
similarity index 100%
rename from src/hash/sha1/sha1dc/sha1.h
rename to src/util/hash/sha1dc/sha1.h
diff --git a/src/hash/sha1/sha1dc/ubc_check.c b/src/util/hash/sha1dc/ubc_check.c
similarity index 100%
rename from src/hash/sha1/sha1dc/ubc_check.c
rename to src/util/hash/sha1dc/ubc_check.c
diff --git a/src/hash/sha1/sha1dc/ubc_check.h b/src/util/hash/sha1dc/ubc_check.h
similarity index 100%
rename from src/hash/sha1/sha1dc/ubc_check.h
rename to src/util/hash/sha1dc/ubc_check.h
diff --git a/src/util/hash/win32.c b/src/util/hash/win32.c
new file mode 100644
index 000000000..f80c0d5ca
--- /dev/null
+++ b/src/util/hash/win32.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "win32.h"
+
+#include "runtime.h"
+
+#include
+#include
+
+#define GIT_HASH_CNG_DLL_NAME "bcrypt.dll"
+
+/* BCRYPT_SHA1_ALGORITHM */
+#define GIT_HASH_CNG_SHA1_TYPE L"SHA1"
+#define GIT_HASH_CNG_SHA256_TYPE L"SHA256"
+
+/* BCRYPT_OBJECT_LENGTH */
+#define GIT_HASH_CNG_HASH_OBJECT_LEN L"ObjectLength"
+
+/* BCRYPT_HASH_REUSEABLE_FLAGS */
+#define GIT_HASH_CNG_HASH_REUSABLE 0x00000020
+
+/* Definitions */
+
+/* CryptoAPI is available for hashing on Windows XP and newer. */
+struct cryptoapi_provider {
+ HCRYPTPROV handle;
+};
+
+/*
+ * CNG (bcrypt.dll) is significantly more performant than CryptoAPI and is
+ * preferred, however it is only available on Windows 2008 and newer and
+ * must therefore be dynamically loaded, and we must inline constants that
+ * would not exist when building in pre-Windows 2008 environments.
+ */
+
+/* Function declarations for CNG */
+typedef NTSTATUS (WINAPI *cng_open_algorithm_provider_fn)(
+ HANDLE /* BCRYPT_ALG_HANDLE */ *phAlgorithm,
+ LPCWSTR pszAlgId,
+ LPCWSTR pszImplementation,
+ DWORD dwFlags);
+
+typedef NTSTATUS (WINAPI *cng_get_property_fn)(
+ HANDLE /* BCRYPT_HANDLE */ hObject,
+ LPCWSTR pszProperty,
+ PUCHAR pbOutput,
+ ULONG cbOutput,
+ ULONG *pcbResult,
+ ULONG dwFlags);
+
+typedef NTSTATUS (WINAPI *cng_create_hash_fn)(
+ HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm,
+ HANDLE /* BCRYPT_HASH_HANDLE */ *phHash,
+ PUCHAR pbHashObject, ULONG cbHashObject,
+ PUCHAR pbSecret,
+ ULONG cbSecret,
+ ULONG dwFlags);
+
+typedef NTSTATUS (WINAPI *cng_finish_hash_fn)(
+ HANDLE /* BCRYPT_HASH_HANDLE */ hHash,
+ PUCHAR pbOutput,
+ ULONG cbOutput,
+ ULONG dwFlags);
+
+typedef NTSTATUS (WINAPI *cng_hash_data_fn)(
+ HANDLE /* BCRYPT_HASH_HANDLE */ hHash,
+ PUCHAR pbInput,
+ ULONG cbInput,
+ ULONG dwFlags);
+
+typedef NTSTATUS (WINAPI *cng_destroy_hash_fn)(
+ HANDLE /* BCRYPT_HASH_HANDLE */ hHash);
+
+typedef NTSTATUS (WINAPI *cng_close_algorithm_provider_fn)(
+ HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm,
+ ULONG dwFlags);
+
+struct cng_provider {
+ /* DLL for CNG */
+ HINSTANCE dll;
+
+ /* Function pointers for CNG */
+ cng_open_algorithm_provider_fn open_algorithm_provider;
+ cng_get_property_fn get_property;
+ cng_create_hash_fn create_hash;
+ cng_finish_hash_fn finish_hash;
+ cng_hash_data_fn hash_data;
+ cng_destroy_hash_fn destroy_hash;
+ cng_close_algorithm_provider_fn close_algorithm_provider;
+
+ HANDLE /* BCRYPT_ALG_HANDLE */ sha1_handle;
+ DWORD sha1_object_size;
+
+ HANDLE /* BCRYPT_ALG_HANDLE */ sha256_handle;
+ DWORD sha256_object_size;
+};
+
+typedef struct {
+ git_hash_win32_provider_t type;
+
+ union {
+ struct cryptoapi_provider cryptoapi;
+ struct cng_provider cng;
+ } provider;
+} hash_win32_provider;
+
+/* Hash provider definition */
+
+static hash_win32_provider hash_provider = {0};
+
+/* Hash initialization */
+
+/* Initialize CNG, if available */
+GIT_INLINE(int) cng_provider_init(void)
+{
+ char dll_path[MAX_PATH];
+ DWORD dll_path_len, size_len;
+
+ /* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */
+ if (!git_has_win32_version(6, 0, 1)) {
+ git_error_set(GIT_ERROR_SHA, "CryptoNG is not supported on this platform");
+ return -1;
+ }
+
+ /* Load bcrypt.dll explicitly from the system directory */
+ if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 ||
+ dll_path_len > MAX_PATH ||
+ StringCchCat(dll_path, MAX_PATH, "\\") < 0 ||
+ StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 ||
+ (hash_provider.provider.cng.dll = LoadLibrary(dll_path)) == NULL) {
+ git_error_set(GIT_ERROR_SHA, "CryptoNG library could not be loaded");
+ return -1;
+ }
+
+ /* Load the function addresses */
+ if ((hash_provider.provider.cng.open_algorithm_provider = (cng_open_algorithm_provider_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptOpenAlgorithmProvider"))) == NULL ||
+ (hash_provider.provider.cng.get_property = (cng_get_property_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptGetProperty"))) == NULL ||
+ (hash_provider.provider.cng.create_hash = (cng_create_hash_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptCreateHash"))) == NULL ||
+ (hash_provider.provider.cng.finish_hash = (cng_finish_hash_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptFinishHash"))) == NULL ||
+ (hash_provider.provider.cng.hash_data = (cng_hash_data_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptHashData"))) == NULL ||
+ (hash_provider.provider.cng.destroy_hash = (cng_destroy_hash_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptDestroyHash"))) == NULL ||
+ (hash_provider.provider.cng.close_algorithm_provider = (cng_close_algorithm_provider_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptCloseAlgorithmProvider"))) == NULL) {
+ FreeLibrary(hash_provider.provider.cng.dll);
+
+ git_error_set(GIT_ERROR_OS, "CryptoNG functions could not be loaded");
+ return -1;
+ }
+
+ /* Load the SHA1 algorithm */
+ if (hash_provider.provider.cng.open_algorithm_provider(&hash_provider.provider.cng.sha1_handle, GIT_HASH_CNG_SHA1_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0 ||
+ hash_provider.provider.cng.get_property(hash_provider.provider.cng.sha1_handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_provider.provider.cng.sha1_object_size, sizeof(DWORD), &size_len, 0) < 0) {
+ git_error_set(GIT_ERROR_OS, "algorithm provider could not be initialized");
+ goto on_error;
+ }
+
+ /* Load the SHA256 algorithm */
+ if (hash_provider.provider.cng.open_algorithm_provider(&hash_provider.provider.cng.sha256_handle, GIT_HASH_CNG_SHA256_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0 ||
+ hash_provider.provider.cng.get_property(hash_provider.provider.cng.sha256_handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_provider.provider.cng.sha256_object_size, sizeof(DWORD), &size_len, 0) < 0) {
+ git_error_set(GIT_ERROR_OS, "algorithm provider could not be initialized");
+ goto on_error;
+ }
+
+ hash_provider.type = GIT_HASH_WIN32_CNG;
+ return 0;
+
+on_error:
+ if (hash_provider.provider.cng.sha1_handle)
+ hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha1_handle, 0);
+
+ if (hash_provider.provider.cng.sha256_handle)
+ hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha256_handle, 0);
+
+ if (hash_provider.provider.cng.dll)
+ FreeLibrary(hash_provider.provider.cng.dll);
+
+ return -1;
+}
+
+GIT_INLINE(void) cng_provider_shutdown(void)
+{
+ if (hash_provider.type == GIT_HASH_WIN32_INVALID)
+ return;
+
+ hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha1_handle, 0);
+ hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha256_handle, 0);
+ FreeLibrary(hash_provider.provider.cng.dll);
+
+ hash_provider.type = GIT_HASH_WIN32_INVALID;
+}
+
+/* Initialize CryptoAPI */
+GIT_INLINE(int) cryptoapi_provider_init(void)
+{
+ if (!CryptAcquireContext(&hash_provider.provider.cryptoapi.handle, NULL, 0, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
+ git_error_set(GIT_ERROR_OS, "legacy hash context could not be started");
+ return -1;
+ }
+
+ hash_provider.type = GIT_HASH_WIN32_CRYPTOAPI;
+ return 0;
+}
+
+GIT_INLINE(void) cryptoapi_provider_shutdown(void)
+{
+ if (hash_provider.type == GIT_HASH_WIN32_INVALID)
+ return;
+
+ CryptReleaseContext(hash_provider.provider.cryptoapi.handle, 0);
+
+ hash_provider.type = GIT_HASH_WIN32_INVALID;
+}
+
+static void hash_provider_shutdown(void)
+{
+ if (hash_provider.type == GIT_HASH_WIN32_CNG)
+ cng_provider_shutdown();
+ else if (hash_provider.type == GIT_HASH_WIN32_CRYPTOAPI)
+ cryptoapi_provider_shutdown();
+}
+
+static int hash_provider_init(void)
+{
+ int error = 0;
+
+ if (hash_provider.type != GIT_HASH_WIN32_INVALID)
+ return 0;
+
+ if ((error = cng_provider_init()) < 0)
+ error = cryptoapi_provider_init();
+
+ if (!error)
+ error = git_runtime_shutdown_register(hash_provider_shutdown);
+
+ return error;
+}
+
+git_hash_win32_provider_t git_hash_win32_provider(void)
+{
+ return hash_provider.type;
+}
+
+int git_hash_win32_set_provider(git_hash_win32_provider_t provider)
+{
+ if (provider == hash_provider.type)
+ return 0;
+
+ hash_provider_shutdown();
+
+ if (provider == GIT_HASH_WIN32_CNG)
+ return cng_provider_init();
+ else if (provider == GIT_HASH_WIN32_CRYPTOAPI)
+ return cryptoapi_provider_init();
+
+ git_error_set(GIT_ERROR_SHA, "unsupported win32 provider");
+ return -1;
+}
+
+/* CryptoAPI: available in Windows XP and newer */
+
+GIT_INLINE(int) hash_cryptoapi_init(git_hash_win32_ctx *ctx)
+{
+ if (ctx->ctx.cryptoapi.valid)
+ CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
+
+ if (!CryptCreateHash(hash_provider.provider.cryptoapi.handle, ctx->algorithm, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) {
+ ctx->ctx.cryptoapi.valid = 0;
+ git_error_set(GIT_ERROR_OS, "legacy hash implementation could not be created");
+ return -1;
+ }
+
+ ctx->ctx.cryptoapi.valid = 1;
+ return 0;
+}
+
+GIT_INLINE(int) hash_cryptoapi_update(git_hash_win32_ctx *ctx, const void *_data, size_t len)
+{
+ const BYTE *data = (BYTE *)_data;
+
+ GIT_ASSERT(ctx->ctx.cryptoapi.valid);
+
+ while (len > 0) {
+ DWORD chunk = (len > MAXDWORD) ? MAXDWORD : (DWORD)len;
+
+ if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, data, chunk, 0)) {
+ git_error_set(GIT_ERROR_OS, "legacy hash data could not be updated");
+ return -1;
+ }
+
+ data += chunk;
+ len -= chunk;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) hash_cryptoapi_final(unsigned char *out, git_hash_win32_ctx *ctx)
+{
+ DWORD len = ctx->algorithm == CALG_SHA_256 ? GIT_HASH_SHA256_SIZE : GIT_HASH_SHA1_SIZE;
+ int error = 0;
+
+ GIT_ASSERT(ctx->ctx.cryptoapi.valid);
+
+ if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out, &len, 0)) {
+ git_error_set(GIT_ERROR_OS, "legacy hash data could not be finished");
+ error = -1;
+ }
+
+ CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
+ ctx->ctx.cryptoapi.valid = 0;
+
+ return error;
+}
+
+GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_win32_ctx *ctx)
+{
+ if (ctx->ctx.cryptoapi.valid)
+ CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
+}
+
+GIT_INLINE(int) hash_sha1_cryptoapi_ctx_init_init(git_hash_win32_ctx *ctx)
+{
+ ctx->algorithm = CALG_SHA1;
+ return hash_cryptoapi_init(ctx);
+}
+
+GIT_INLINE(int) hash_sha256_cryptoapi_ctx_init(git_hash_win32_ctx *ctx)
+{
+ ctx->algorithm = CALG_SHA_256;
+ return hash_cryptoapi_init(ctx);
+}
+
+/* CNG: Available in Windows Server 2008 and newer */
+
+GIT_INLINE(int) hash_sha1_cng_ctx_init(git_hash_win32_ctx *ctx)
+{
+ if ((ctx->ctx.cng.hash_object = git__malloc(hash_provider.provider.cng.sha1_object_size)) == NULL)
+ return -1;
+
+ if (hash_provider.provider.cng.create_hash(hash_provider.provider.cng.sha1_handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_provider.provider.cng.sha1_object_size, NULL, 0, 0) < 0) {
+ git__free(ctx->ctx.cng.hash_object);
+
+ git_error_set(GIT_ERROR_OS, "sha1 implementation could not be created");
+ return -1;
+ }
+
+ ctx->algorithm = CALG_SHA1;
+ return 0;
+}
+
+GIT_INLINE(int) hash_sha256_cng_ctx_init(git_hash_win32_ctx *ctx)
+{
+ if ((ctx->ctx.cng.hash_object = git__malloc(hash_provider.provider.cng.sha256_object_size)) == NULL)
+ return -1;
+
+ if (hash_provider.provider.cng.create_hash(hash_provider.provider.cng.sha256_handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_provider.provider.cng.sha256_object_size, NULL, 0, 0) < 0) {
+ git__free(ctx->ctx.cng.hash_object);
+
+ git_error_set(GIT_ERROR_OS, "sha256 implementation could not be created");
+ return -1;
+ }
+
+ ctx->algorithm = CALG_SHA_256;
+ return 0;
+}
+
+GIT_INLINE(int) hash_cng_init(git_hash_win32_ctx *ctx)
+{
+ BYTE hash[GIT_HASH_SHA256_SIZE];
+ ULONG size = ctx->algorithm == CALG_SHA_256 ? GIT_HASH_SHA256_SIZE : GIT_HASH_SHA1_SIZE;
+
+ if (!ctx->ctx.cng.updated)
+ return 0;
+
+ /* CNG needs to be finished to restart */
+ if (hash_provider.provider.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, size, 0) < 0) {
+ git_error_set(GIT_ERROR_OS, "hash implementation could not be finished");
+ return -1;
+ }
+
+ ctx->ctx.cng.updated = 0;
+
+ return 0;
+}
+
+GIT_INLINE(int) hash_cng_update(git_hash_win32_ctx *ctx, const void *_data, size_t len)
+{
+ PBYTE data = (PBYTE)_data;
+
+ while (len > 0) {
+ ULONG chunk = (len > ULONG_MAX) ? ULONG_MAX : (ULONG)len;
+
+ if (hash_provider.provider.cng.hash_data(ctx->ctx.cng.hash_handle, data, chunk, 0) < 0) {
+ git_error_set(GIT_ERROR_OS, "hash could not be updated");
+ return -1;
+ }
+
+ data += chunk;
+ len -= chunk;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) hash_cng_final(unsigned char *out, git_hash_win32_ctx *ctx)
+{
+ ULONG size = ctx->algorithm == CALG_SHA_256 ? GIT_HASH_SHA256_SIZE : GIT_HASH_SHA1_SIZE;
+
+ if (hash_provider.provider.cng.finish_hash(ctx->ctx.cng.hash_handle, out, size, 0) < 0) {
+ git_error_set(GIT_ERROR_OS, "hash could not be finished");
+ return -1;
+ }
+
+ ctx->ctx.cng.updated = 0;
+
+ return 0;
+}
+
+GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_win32_ctx *ctx)
+{
+ hash_provider.provider.cng.destroy_hash(ctx->ctx.cng.hash_handle);
+ git__free(ctx->ctx.cng.hash_object);
+}
+
+/* Indirection between CryptoAPI and CNG */
+
+GIT_INLINE(int) hash_sha1_win32_ctx_init(git_hash_win32_ctx *ctx)
+{
+ GIT_ASSERT_ARG(hash_provider.type);
+
+ memset(ctx, 0x0, sizeof(git_hash_win32_ctx));
+ return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_sha1_cng_ctx_init(ctx) : hash_sha1_cryptoapi_ctx_init_init(ctx);
+}
+
+GIT_INLINE(int) hash_sha256_win32_ctx_init(git_hash_win32_ctx *ctx)
+{
+ GIT_ASSERT_ARG(hash_provider.type);
+
+ memset(ctx, 0x0, sizeof(git_hash_win32_ctx));
+ return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_sha256_cng_ctx_init(ctx) : hash_sha256_cryptoapi_ctx_init(ctx);
+}
+
+GIT_INLINE(int) hash_win32_init(git_hash_win32_ctx *ctx)
+{
+ return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx);
+}
+
+GIT_INLINE(int) hash_win32_update(git_hash_win32_ctx *ctx, const void *data, size_t len)
+{
+ return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len);
+}
+
+GIT_INLINE(int) hash_win32_final(unsigned char *out, git_hash_win32_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx);
+}
+
+GIT_INLINE(void) hash_win32_cleanup(git_hash_win32_ctx *ctx)
+{
+ if (hash_provider.type == GIT_HASH_WIN32_CNG)
+ hash_ctx_cng_cleanup(ctx);
+ else if(hash_provider.type == GIT_HASH_WIN32_CRYPTOAPI)
+ hash_ctx_cryptoapi_cleanup(ctx);
+}
+
+#ifdef GIT_SHA1_WIN32
+
+int git_hash_sha1_global_init(void)
+{
+ return hash_provider_init();
+}
+
+int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_sha1_win32_ctx_init(&ctx->win32);
+}
+
+int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_win32_init(&ctx->win32);
+}
+
+int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_win32_update(&ctx->win32, data, len);
+}
+
+int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_win32_final(out, &ctx->win32);
+}
+
+void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ hash_win32_cleanup(&ctx->win32);
+}
+
+#endif
+
+#ifdef GIT_SHA256_WIN32
+
+int git_hash_sha256_global_init(void)
+{
+ return hash_provider_init();
+}
+
+int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_sha256_win32_ctx_init(&ctx->win32);
+}
+
+int git_hash_sha256_init(git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_win32_init(&ctx->win32);
+}
+
+int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_win32_update(&ctx->win32, data, len);
+}
+
+int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_win32_final(out, &ctx->win32);
+}
+
+void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ hash_win32_cleanup(&ctx->win32);
+}
+
+#endif
diff --git a/src/util/hash/win32.h b/src/util/hash/win32.h
new file mode 100644
index 000000000..a9fb87aee
--- /dev/null
+++ b/src/util/hash/win32.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_hash_win32_h__
+#define INCLUDE_hash_win32_h__
+
+#include "hash/sha.h"
+
+#include
+
+typedef enum {
+ GIT_HASH_WIN32_INVALID = 0,
+ GIT_HASH_WIN32_CRYPTOAPI,
+ GIT_HASH_WIN32_CNG
+} git_hash_win32_provider_t;
+
+struct git_hash_win32_cryptoapi_ctx {
+ bool valid;
+ HCRYPTHASH hash_handle;
+};
+
+struct git_hash_win32_cng_ctx {
+ bool updated;
+ HANDLE /* BCRYPT_HASH_HANDLE */ hash_handle;
+ PBYTE hash_object;
+};
+
+typedef struct {
+ ALG_ID algorithm;
+
+ union {
+ struct git_hash_win32_cryptoapi_ctx cryptoapi;
+ struct git_hash_win32_cng_ctx cng;
+ } ctx;
+} git_hash_win32_ctx;
+
+/*
+ * Gets/sets the current hash provider (cng or cryptoapi). This is only
+ * for testing purposes.
+ */
+git_hash_win32_provider_t git_hash_win32_provider(void);
+int git_hash_win32_set_provider(git_hash_win32_provider_t provider);
+
+#ifdef GIT_SHA1_WIN32
+struct git_hash_sha1_ctx {
+ git_hash_win32_ctx win32;
+};
+#endif
+
+#ifdef GIT_SHA256_WIN32
+struct git_hash_sha256_ctx {
+ git_hash_win32_ctx win32;
+};
+#endif
+
+#endif
diff --git a/src/integer.h b/src/util/integer.h
similarity index 100%
rename from src/integer.h
rename to src/util/integer.h
diff --git a/src/khash.h b/src/util/khash.h
similarity index 100%
rename from src/khash.h
rename to src/util/khash.h
diff --git a/src/map.h b/src/util/map.h
similarity index 98%
rename from src/map.h
rename to src/util/map.h
index 01931d199..c101e46f6 100644
--- a/src/map.h
+++ b/src/util/map.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_map_h__
#define INCLUDE_map_h__
-#include "common.h"
+#include "git2_util.h"
/* p_mmap() prot values */
diff --git a/src/net.c b/src/util/net.c
similarity index 99%
rename from src/net.c
rename to src/util/net.c
index a76fd1d7c..b2236daf8 100644
--- a/src/net.c
+++ b/src/util/net.c
@@ -6,7 +6,6 @@
*/
#include "net.h"
-#include "netops.h"
#include
diff --git a/src/net.h b/src/util/net.h
similarity index 99%
rename from src/net.h
rename to src/util/net.h
index 499315e6c..88030a952 100644
--- a/src/net.h
+++ b/src/util/net.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_net_h__
#define INCLUDE_net_h__
-#include "common.h"
+#include "git2_util.h"
typedef struct git_net_url {
char *scheme;
diff --git a/src/pool.c b/src/util/pool.c
similarity index 100%
rename from src/pool.c
rename to src/util/pool.c
diff --git a/src/pool.h b/src/util/pool.h
similarity index 99%
rename from src/pool.h
rename to src/util/pool.h
index cecb84665..0238431b0 100644
--- a/src/pool.h
+++ b/src/util/pool.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_pool_h__
#define INCLUDE_pool_h__
-#include "common.h"
+#include "git2_util.h"
#include "vector.h"
diff --git a/src/posix.c b/src/util/posix.c
similarity index 100%
rename from src/posix.c
rename to src/util/posix.c
diff --git a/src/posix.h b/src/util/posix.h
similarity index 99%
rename from src/posix.h
rename to src/util/posix.h
index e6f603078..c8f8cd9d2 100644
--- a/src/posix.h
+++ b/src/util/posix.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_posix_h__
#define INCLUDE_posix_h__
-#include "common.h"
+#include "git2_util.h"
#include
#include
diff --git a/src/pqueue.c b/src/util/pqueue.c
similarity index 100%
rename from src/pqueue.c
rename to src/util/pqueue.c
diff --git a/src/pqueue.h b/src/util/pqueue.h
similarity index 98%
rename from src/pqueue.h
rename to src/util/pqueue.h
index 4db74ea03..97232b4a9 100644
--- a/src/pqueue.h
+++ b/src/util/pqueue.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_pqueue_h__
#define INCLUDE_pqueue_h__
-#include "common.h"
+#include "git2_util.h"
#include "vector.h"
diff --git a/src/rand.c b/src/util/rand.c
similarity index 98%
rename from src/rand.c
rename to src/util/rand.c
index 0a208134e..d28e4aa97 100644
--- a/src/rand.c
+++ b/src/util/rand.c
@@ -6,7 +6,7 @@ worldwide. This software is distributed without any warranty.
See . */
-#include "common.h"
+#include "git2_util.h"
#include "rand.h"
#include "runtime.h"
@@ -106,8 +106,6 @@ GIT_INLINE(int) getseed(uint64_t *seed)
return -1;
}
- getloadavg(loadavg, 3);
-
*seed = 0;
*seed |= ((uint64_t)tv.tv_usec << 40);
*seed |= ((uint64_t)tv.tv_sec);
@@ -119,9 +117,15 @@ GIT_INLINE(int) getseed(uint64_t *seed)
*seed ^= ((uint64_t)getuid() << 8);
*seed ^= ((uint64_t)getgid());
+# if defined(GIT_RAND_GETLOADAVG)
+ getloadavg(loadavg, 3);
+
convert.f = loadavg[0]; *seed ^= (convert.d >> 36);
convert.f = loadavg[1]; *seed ^= (convert.d);
convert.f = loadavg[2]; *seed ^= (convert.d >> 16);
+# else
+ GIT_UNUSED(loadavg[0]);
+# endif
convert.f = git__timer(); *seed ^= (convert.d);
diff --git a/src/rand.h b/src/util/rand.h
similarity index 97%
rename from src/rand.h
rename to src/util/rand.h
index 2e60561e5..fa0619aa2 100644
--- a/src/rand.h
+++ b/src/util/rand.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_rand_h__
#define INCLUDE_rand_h__
-#include "common.h"
+#include "git2_util.h"
/**
* Initialize the random number generation subsystem. This will
diff --git a/src/regexp.c b/src/util/regexp.c
similarity index 100%
rename from src/regexp.c
rename to src/util/regexp.c
diff --git a/src/regexp.h b/src/util/regexp.h
similarity index 99%
rename from src/regexp.h
rename to src/util/regexp.h
index 2592ef383..d0862b107 100644
--- a/src/regexp.h
+++ b/src/util/regexp.h
@@ -8,7 +8,7 @@
#ifndef INCLUDE_regexp_h__
#define INCLUDE_regexp_h__
-#include "common.h"
+#include "git2_util.h"
#if defined(GIT_REGEX_BUILTIN) || defined(GIT_REGEX_PCRE)
# include "pcre.h"
diff --git a/src/runtime.c b/src/util/runtime.c
similarity index 99%
rename from src/runtime.c
rename to src/util/runtime.c
index c05dee8b9..a7711ffc4 100644
--- a/src/runtime.c
+++ b/src/util/runtime.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "git2_util.h"
#include "runtime.h"
static git_runtime_shutdown_fn shutdown_callback[32];
diff --git a/src/runtime.h b/src/util/runtime.h
similarity index 98%
rename from src/runtime.h
rename to src/util/runtime.h
index 24ac58ee9..6cbfd6043 100644
--- a/src/runtime.h
+++ b/src/util/runtime.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_runtime_h__
#define INCLUDE_runtime_h__
-#include "common.h"
+#include "git2_util.h"
typedef int (*git_runtime_init_fn)(void);
typedef void (*git_runtime_shutdown_fn)(void);
diff --git a/src/sortedcache.c b/src/util/sortedcache.c
similarity index 100%
rename from src/sortedcache.c
rename to src/util/sortedcache.c
diff --git a/src/sortedcache.h b/src/util/sortedcache.h
similarity index 99%
rename from src/sortedcache.h
rename to src/util/sortedcache.h
index ef260a093..3eee4659f 100644
--- a/src/sortedcache.h
+++ b/src/util/sortedcache.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_sorted_cache_h__
#define INCLUDE_sorted_cache_h__
-#include "common.h"
+#include "git2_util.h"
#include "util.h"
#include "futils.h"
diff --git a/src/str.c b/src/util/str.c
similarity index 100%
rename from src/str.c
rename to src/util/str.c
diff --git a/src/str.h b/src/util/str.h
similarity index 99%
rename from src/str.h
rename to src/util/str.h
index ef769ce2f..588e6fc22 100644
--- a/src/str.h
+++ b/src/util/str.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_str_h__
#define INCLUDE_str_h__
-#include "common.h"
+#include "git2_util.h"
struct git_str {
char *ptr;
diff --git a/src/strmap.c b/src/util/strmap.c
similarity index 100%
rename from src/strmap.c
rename to src/util/strmap.c
diff --git a/src/strmap.h b/src/util/strmap.h
similarity index 99%
rename from src/strmap.h
rename to src/util/strmap.h
index 9f5e4cc8b..b64d3dcb5 100644
--- a/src/strmap.h
+++ b/src/util/strmap.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_strmap_h__
#define INCLUDE_strmap_h__
-#include "common.h"
+#include "git2_util.h"
/** A map with C strings as key. */
typedef struct kh_str_s git_strmap;
diff --git a/src/strnlen.h b/src/util/strnlen.h
similarity index 100%
rename from src/strnlen.h
rename to src/util/strnlen.h
diff --git a/src/thread.c b/src/util/thread.c
similarity index 99%
rename from src/thread.c
rename to src/util/thread.c
index 3171771d7..bc7364f8c 100644
--- a/src/thread.c
+++ b/src/util/thread.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "git2_util.h"
#if !defined(GIT_THREADS)
diff --git a/src/thread.h b/src/util/thread.h
similarity index 100%
rename from src/thread.h
rename to src/util/thread.h
diff --git a/src/tsort.c b/src/util/tsort.c
similarity index 99%
rename from src/tsort.c
rename to src/util/tsort.c
index 045efad23..2ef03d03a 100644
--- a/src/tsort.c
+++ b/src/util/tsort.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "git2_util.h"
/**
* An array-of-pointers implementation of Python's Timsort
diff --git a/src/unix/map.c b/src/util/unix/map.c
similarity index 98%
rename from src/unix/map.c
rename to src/util/unix/map.c
index 23fcb786e..933077689 100644
--- a/src/unix/map.c
+++ b/src/util/unix/map.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "git2_util.h"
#if !defined(GIT_WIN32) && !defined(NO_MMAP)
diff --git a/src/unix/posix.h b/src/util/unix/posix.h
similarity index 99%
rename from src/unix/posix.h
rename to src/util/unix/posix.h
index 49065e533..778477e8e 100644
--- a/src/unix/posix.h
+++ b/src/util/unix/posix.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_unix_posix_h__
#define INCLUDE_unix_posix_h__
-#include "common.h"
+#include "git2_util.h"
#include
#include
diff --git a/src/unix/pthread.h b/src/util/unix/pthread.h
similarity index 100%
rename from src/unix/pthread.h
rename to src/util/unix/pthread.h
diff --git a/src/unix/realpath.c b/src/util/unix/realpath.c
similarity index 96%
rename from src/unix/realpath.c
rename to src/util/unix/realpath.c
index f1ca669f7..9e31a63b9 100644
--- a/src/unix/realpath.c
+++ b/src/util/unix/realpath.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "git2_util.h"
#ifndef GIT_WIN32
diff --git a/src/utf8.c b/src/util/utf8.c
similarity index 99%
rename from src/utf8.c
rename to src/util/utf8.c
index 77065cb71..c566fdf20 100644
--- a/src/utf8.c
+++ b/src/util/utf8.c
@@ -7,7 +7,7 @@
#include "utf8.h"
-#include "common.h"
+#include "git2_util.h"
/*
* git_utf8_iterate is taken from the utf8proc project,
diff --git a/src/utf8.h b/src/util/utf8.h
similarity index 98%
rename from src/utf8.h
rename to src/util/utf8.h
index dff91b294..753ab07e2 100644
--- a/src/utf8.h
+++ b/src/util/utf8.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_utf8_h__
#define INCLUDE_utf8_h__
-#include "common.h"
+#include "git2_util.h"
/*
* Iterate through an UTF-8 string, yielding one codepoint at a time.
diff --git a/src/util.c b/src/util/util.c
similarity index 99%
rename from src/util.c
rename to src/util/util.c
index e06d4ca09..aee95fddf 100644
--- a/src/util.c
+++ b/src/util/util.c
@@ -7,7 +7,7 @@
#include "util.h"
-#include "common.h"
+#include "git2_util.h"
#ifdef GIT_WIN32
# include "win32/utf-conv.h"
diff --git a/src/util.h b/src/util/util.h
similarity index 99%
rename from src/util.h
rename to src/util/util.h
index 141779ade..8d6d1d6b6 100644
--- a/src/util.h
+++ b/src/util/util.h
@@ -12,7 +12,7 @@
#endif
#include "str.h"
-#include "common.h"
+#include "git2_util.h"
#include "strnlen.h"
#include "thread.h"
diff --git a/src/varint.c b/src/util/varint.c
similarity index 100%
rename from src/varint.c
rename to src/util/varint.c
diff --git a/src/varint.h b/src/util/varint.h
similarity index 94%
rename from src/varint.h
rename to src/util/varint.h
index 652e22486..79b8f5548 100644
--- a/src/varint.h
+++ b/src/util/varint.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_varint_h__
#define INCLUDE_varint_h__
-#include "common.h"
+#include "git2_util.h"
#include
diff --git a/src/vector.c b/src/util/vector.c
similarity index 100%
rename from src/vector.c
rename to src/util/vector.c
diff --git a/src/vector.h b/src/util/vector.h
similarity index 99%
rename from src/vector.h
rename to src/util/vector.h
index ae3c79a4c..e50cdfefc 100644
--- a/src/vector.h
+++ b/src/util/vector.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_vector_h__
#define INCLUDE_vector_h__
-#include "common.h"
+#include "git2_util.h"
typedef int (*git_vector_cmp)(const void *, const void *);
diff --git a/src/wildmatch.c b/src/util/wildmatch.c
similarity index 100%
rename from src/wildmatch.c
rename to src/util/wildmatch.c
diff --git a/src/wildmatch.h b/src/util/wildmatch.h
similarity index 95%
rename from src/wildmatch.h
rename to src/util/wildmatch.h
index 44bb575a6..f20640500 100644
--- a/src/wildmatch.h
+++ b/src/util/wildmatch.h
@@ -8,7 +8,7 @@
#ifndef INCLUDE_wildmatch_h__
#define INCLUDE_wildmatch_h__
-#include "common.h"
+#include "git2_util.h"
#define WM_CASEFOLD 1
#define WM_PATHNAME 2
diff --git a/src/win32/dir.c b/src/util/win32/dir.c
similarity index 100%
rename from src/win32/dir.c
rename to src/util/win32/dir.c
diff --git a/src/win32/dir.h b/src/util/win32/dir.h
similarity index 97%
rename from src/win32/dir.h
rename to src/util/win32/dir.h
index acd64729e..810111534 100644
--- a/src/win32/dir.h
+++ b/src/util/win32/dir.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_win32_dir_h__
#define INCLUDE_win32_dir_h__
-#include "common.h"
+#include "git2_util.h"
#include "w32_util.h"
diff --git a/src/win32/error.c b/src/util/win32/error.c
similarity index 100%
rename from src/win32/error.c
rename to src/util/win32/error.c
diff --git a/src/win32/error.h b/src/util/win32/error.h
similarity index 93%
rename from src/win32/error.h
rename to src/util/win32/error.h
index 9e81141ce..fd53b7f99 100644
--- a/src/win32/error.h
+++ b/src/util/win32/error.h
@@ -8,7 +8,7 @@
#ifndef INCLUDE_win32_error_h__
#define INCLUDE_win32_error_h__
-#include "common.h"
+#include "git2_util.h"
extern char *git_win32_get_error_message(DWORD error_code);
diff --git a/src/win32/findfile.c b/src/util/win32/findfile.c
similarity index 100%
rename from src/win32/findfile.c
rename to src/util/win32/findfile.c
diff --git a/src/win32/findfile.h b/src/util/win32/findfile.h
similarity index 96%
rename from src/win32/findfile.h
rename to src/util/win32/findfile.h
index 61fb7dbad..7b191d1fe 100644
--- a/src/win32/findfile.h
+++ b/src/util/win32/findfile.h
@@ -8,7 +8,7 @@
#ifndef INCLUDE_win32_findfile_h__
#define INCLUDE_win32_findfile_h__
-#include "common.h"
+#include "git2_util.h"
/** Sets the mock registry root for Git for Windows for testing. */
extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir);
diff --git a/src/win32/map.c b/src/util/win32/map.c
similarity index 99%
rename from src/win32/map.c
rename to src/util/win32/map.c
index 2aabc9b15..52e1363ea 100644
--- a/src/win32/map.c
+++ b/src/util/win32/map.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "git2_util.h"
#include "map.h"
#include
diff --git a/src/win32/mingw-compat.h b/src/util/win32/mingw-compat.h
similarity index 100%
rename from src/win32/mingw-compat.h
rename to src/util/win32/mingw-compat.h
diff --git a/src/win32/msvc-compat.h b/src/util/win32/msvc-compat.h
similarity index 100%
rename from src/win32/msvc-compat.h
rename to src/util/win32/msvc-compat.h
diff --git a/src/win32/path_w32.c b/src/util/win32/path_w32.c
similarity index 100%
rename from src/win32/path_w32.c
rename to src/util/win32/path_w32.c
diff --git a/src/win32/path_w32.h b/src/util/win32/path_w32.h
similarity index 99%
rename from src/win32/path_w32.h
rename to src/util/win32/path_w32.h
index 837b11ebd..b241d5c8a 100644
--- a/src/win32/path_w32.h
+++ b/src/util/win32/path_w32.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_win32_path_w32_h__
#define INCLUDE_win32_path_w32_h__
-#include "common.h"
+#include "git2_util.h"
/**
* Create a Win32 path (in UCS-2 format) from a UTF-8 string. If the given
diff --git a/src/win32/posix.h b/src/util/win32/posix.h
similarity index 99%
rename from src/win32/posix.h
rename to src/util/win32/posix.h
index 578347f15..03fa2ac52 100644
--- a/src/win32/posix.h
+++ b/src/util/win32/posix.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_win32_posix_h__
#define INCLUDE_win32_posix_h__
-#include "common.h"
+#include "git2_util.h"
#include "../posix.h"
#include "win32-compat.h"
#include "path_w32.h"
diff --git a/src/win32/posix_w32.c b/src/util/win32/posix_w32.c
similarity index 99%
rename from src/win32/posix_w32.c
rename to src/util/win32/posix_w32.c
index 5f7cd0c26..5862e5c9a 100644
--- a/src/win32/posix_w32.c
+++ b/src/util/win32/posix_w32.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "common.h"
+#include "git2_util.h"
#include "../posix.h"
#include "../futils.h"
diff --git a/tests/precompiled.c b/src/util/win32/precompiled.c
similarity index 100%
rename from tests/precompiled.c
rename to src/util/win32/precompiled.c
diff --git a/src/win32/precompiled.h b/src/util/win32/precompiled.h
similarity index 93%
rename from src/win32/precompiled.h
rename to src/util/win32/precompiled.h
index 806b1698a..1163c3d63 100644
--- a/src/win32/precompiled.h
+++ b/src/util/win32/precompiled.h
@@ -1,4 +1,4 @@
-#include "common.h"
+#include "git2_util.h"
#include
#include
diff --git a/src/win32/reparse.h b/src/util/win32/reparse.h
similarity index 100%
rename from src/win32/reparse.h
rename to src/util/win32/reparse.h
diff --git a/src/win32/thread.c b/src/util/win32/thread.c
similarity index 100%
rename from src/win32/thread.c
rename to src/util/win32/thread.c
diff --git a/src/win32/thread.h b/src/util/win32/thread.h
similarity index 98%
rename from src/win32/thread.h
rename to src/util/win32/thread.h
index 8305036b4..184762e2a 100644
--- a/src/win32/thread.h
+++ b/src/util/win32/thread.h
@@ -8,7 +8,7 @@
#ifndef INCLUDE_win32_thread_h__
#define INCLUDE_win32_thread_h__
-#include "common.h"
+#include "git2_util.h"
#if defined (_MSC_VER)
# define GIT_RESTRICT __restrict
diff --git a/src/win32/utf-conv.c b/src/util/win32/utf-conv.c
similarity index 100%
rename from src/win32/utf-conv.c
rename to src/util/win32/utf-conv.c
diff --git a/src/win32/utf-conv.h b/src/util/win32/utf-conv.h
similarity index 98%
rename from src/win32/utf-conv.h
rename to src/util/win32/utf-conv.h
index 6090a4b35..120d647ef 100644
--- a/src/win32/utf-conv.h
+++ b/src/util/win32/utf-conv.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_win32_utf_conv_h__
#define INCLUDE_win32_utf_conv_h__
-#include "common.h"
+#include "git2_util.h"
#include
diff --git a/src/win32/version.h b/src/util/win32/version.h
similarity index 100%
rename from src/win32/version.h
rename to src/util/win32/version.h
diff --git a/src/win32/w32_buffer.c b/src/util/win32/w32_buffer.c
similarity index 100%
rename from src/win32/w32_buffer.c
rename to src/util/win32/w32_buffer.c
diff --git a/src/win32/w32_buffer.h b/src/util/win32/w32_buffer.h
similarity index 95%
rename from src/win32/w32_buffer.h
rename to src/util/win32/w32_buffer.h
index 4227296d8..68ea96035 100644
--- a/src/win32/w32_buffer.h
+++ b/src/util/win32/w32_buffer.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_win32_w32_buffer_h__
#define INCLUDE_win32_w32_buffer_h__
-#include "common.h"
+#include "git2_util.h"
#include "str.h"
/**
diff --git a/src/win32/w32_common.h b/src/util/win32/w32_common.h
similarity index 100%
rename from src/win32/w32_common.h
rename to src/util/win32/w32_common.h
diff --git a/src/win32/w32_leakcheck.c b/src/util/win32/w32_leakcheck.c
similarity index 100%
rename from src/win32/w32_leakcheck.c
rename to src/util/win32/w32_leakcheck.c
diff --git a/src/win32/w32_leakcheck.h b/src/util/win32/w32_leakcheck.h
similarity index 99%
rename from src/win32/w32_leakcheck.h
rename to src/util/win32/w32_leakcheck.h
index cb45e3675..82d863851 100644
--- a/src/win32/w32_leakcheck.h
+++ b/src/util/win32/w32_leakcheck.h
@@ -8,7 +8,7 @@
#ifndef INCLUDE_win32_leakcheck_h__
#define INCLUDE_win32_leakcheck_h__
-#include "common.h"
+#include "git2_util.h"
/* Initialize the win32 leak checking system. */
int git_win32_leakcheck_global_init(void);
diff --git a/src/win32/w32_util.c b/src/util/win32/w32_util.c
similarity index 100%
rename from src/win32/w32_util.c
rename to src/util/win32/w32_util.c
diff --git a/src/win32/w32_util.h b/src/util/win32/w32_util.h
similarity index 99%
rename from src/win32/w32_util.h
rename to src/util/win32/w32_util.h
index 1321d30e6..519663720 100644
--- a/src/win32/w32_util.h
+++ b/src/util/win32/w32_util.h
@@ -8,7 +8,7 @@
#ifndef INCLUDE_win32_w32_util_h__
#define INCLUDE_win32_w32_util_h__
-#include "common.h"
+#include "git2_util.h"
#include "utf-conv.h"
#include "posix.h"
diff --git a/src/win32/win32-compat.h b/src/util/win32/win32-compat.h
similarity index 100%
rename from src/win32/win32-compat.h
rename to src/util/win32/win32-compat.h
diff --git a/src/zstream.c b/src/util/zstream.c
similarity index 100%
rename from src/zstream.c
rename to src/util/zstream.c
diff --git a/src/zstream.h b/src/util/zstream.h
similarity index 98%
rename from src/zstream.h
rename to src/util/zstream.h
index 3f8b1c72f..d78b11291 100644
--- a/src/zstream.h
+++ b/src/util/zstream.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_zstream_h__
#define INCLUDE_zstream_h__
-#include "common.h"
+#include "git2_util.h"
#include
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index f293c158d..df100e980 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,96 +1,6 @@
-set(Python_ADDITIONAL_VERSIONS 3 2.7)
-find_package(PythonInterp)
+# The main libgit2 tests tree: this CMakeLists.txt includes the
+# subprojects that make up core libgit2 support.
-if(NOT PYTHONINTERP_FOUND)
- message(FATAL_ERROR "Could not find a python interpreter, which is needed to build the tests. "
- "Make sure python is available, or pass -DBUILD_TESTS=OFF to skip building the tests")
-ENDIF()
-
-set(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/resources/")
-set(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
-add_definitions(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
-add_definitions(-DCLAR_TMPDIR=\"libgit2_tests\")
-add_definitions(-DCLAR_WIN32_LONGPATHS)
-add_definitions(-D_FILE_OFFSET_BITS=64)
-
-# Ensure that we do not use deprecated functions internally
-add_definitions(-DGIT_DEPRECATE_HARD)
-
-set(TEST_INCLUDES "${CLAR_PATH}" "${CMAKE_CURRENT_BINARY_DIR}")
-file(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/*/*.h)
-set(SRC_CLAR "main.c" "clar_libgit2.c" "clar_libgit2_trace.c" "clar_libgit2_timer.c" "clar.c")
-
-if(MSVC_IDE)
- list(APPEND SRC_CLAR "precompiled.c")
-endif()
-
-add_custom_command(
- OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/clar.suite ${CMAKE_CURRENT_BINARY_DIR}/clar_suite.h
- COMMAND ${PYTHON_EXECUTABLE} generate.py -o "${CMAKE_CURRENT_BINARY_DIR}" -f -xonline -xstress -xperf .
- DEPENDS ${SRC_TEST}
- WORKING_DIRECTORY ${CLAR_PATH}
-)
-
-set_source_files_properties(
- ${CLAR_PATH}/clar.c
- PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clar.suite)
-
-add_executable(libgit2_tests ${SRC_CLAR} ${SRC_TEST} ${LIBGIT2_OBJECTS})
-
-set_target_properties(libgit2_tests PROPERTIES C_STANDARD 90)
-set_target_properties(libgit2_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
-
-target_include_directories(libgit2_tests PRIVATE ${TEST_INCLUDES} ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES})
-target_include_directories(libgit2_tests SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
-target_link_libraries(libgit2_tests ${LIBGIT2_SYSTEM_LIBS})
-
-ide_split_sources(libgit2_tests)
-
-#
-# Old versions of gcc require us to declare our test functions; don't do
-# this on newer compilers to avoid unnecessary recompilation.
-#
-if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
- target_compile_options(libgit2_tests PRIVATE -include "clar_suite.h")
-endif()
-
-if(MSVC_IDE)
- # Precompiled headers
- set_target_properties(libgit2_tests PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
- set_source_files_properties("precompiled.c" COMPILE_FLAGS "/Ycprecompiled.h")
-endif()
-
-function(ADD_CLAR_TEST name)
- if(NOT USE_LEAK_CHECKER STREQUAL "OFF")
- add_test(${name} "${PROJECT_SOURCE_DIR}/script/${USE_LEAK_CHECKER}.sh" "${PROJECT_BINARY_DIR}/libgit2_tests" ${ARGN})
- else()
- add_test(${name} "${PROJECT_BINARY_DIR}/libgit2_tests" ${ARGN})
- endif()
-endfunction(ADD_CLAR_TEST)
-
-add_clar_test(offline -v -xonline)
-add_clar_test(invasive -v -score::ftruncate -sfilter::stream::bigfile -sodb::largefiles -siterator::workdir::filesystem_gunk -srepo::init -srepo::init::at_filesystem_root)
-add_clar_test(online -v -sonline -xonline::customcert)
-add_clar_test(online_customcert -v -sonline::customcert)
-add_clar_test(gitdaemon -v -sonline::push)
-add_clar_test(ssh -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths -sonline::clone::path_whitespace_ssh)
-add_clar_test(proxy -v -sonline::clone::proxy)
-add_clar_test(auth_clone -v -sonline::clone::cred)
-add_clar_test(auth_clone_and_push -v -sonline::clone::push -sonline::push)
-
-#
-# Header file validation project: ensure that we do not publish any sloppy
-# definitions in our headers and that a consumer can include
-# even when they have aggressive C90 warnings enabled.
-#
-
-add_executable(headertest headertest.c)
-set_target_properties(headertest PROPERTIES C_STANDARD 90)
-set_target_properties(headertest PROPERTIES C_EXTENSIONS OFF)
-target_include_directories(headertest PRIVATE ${LIBGIT2_INCLUDES})
-
-if (MSVC)
- target_compile_options(headertest PUBLIC /W4 /WX)
-else()
- target_compile_options(headertest PUBLIC -Wall -Wextra -pedantic -Werror)
-endif()
+add_subdirectory(headertest)
+add_subdirectory(libgit2)
+add_subdirectory(util)
diff --git a/tests/README.md b/tests/README.md
index 4369a8f33..460e045e3 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -1,33 +1,58 @@
-Writing Clar tests for libgit2
-==============================
+# libgit2 tests
-For information on the Clar testing framework and a detailed introduction
-please visit:
+These are the unit and integration tests for the libgit2 projects.
-https://github.com/vmg/clar
+* `benchmarks`
+ These are benchmark tests that excercise the CLI.
+* `clar`
+ This is [clar](https://github.com/clar-test/clar) the common test framework.
+* `headertest`
+ This is a simple project that ensures that our public headers are
+ compatible with extremely strict compilation options.
+* `libgit2`
+ These tests exercise the core git functionality in libgit2 itself.
+* `resources`
+ These are the resources for the tests, including files and git
+ repositories.
+* `util`
+ These are tests of the common utility library.
+## Writing tests for libgit2
-* Write your modules and tests. Use good, meaningful names.
+libgit2 uses the [clar test framework](http://github.com/clar-test/clar), a
+C testing framework.
-* Make sure you actually build the tests by setting:
+The best resources for learning clar are [clar itself](https://github.com/clar-test/clar)
+and the existing tests within libgit2. In general:
- cmake -DBUILD_TESTS=ON build/
+* If you place a `.c` file into a test directory, it is eligible to contain
+test cases.
+* The function name for your test is important; test function names begin
+ with `test_`, followed by the folder path (underscore separated), two
+ underscores as a delimiter, then the test name. For example, a file
+ `merge/analysis.c` may contain a test `uptodate`:
-* Test:
+ ```
+ void test_merge_analysis__uptodate(void)
+ {
+ ...
+ }
+ ```
- ./build/libgit2_tests
+* You can run an individual test by passing `-s` to the test runner. Tests
+ are referred to by their function names; for example, the function
+ `test_merge_analysis__uptodate` is referred to as `merge::analysis::uptodate`.
+ To run only that function you can use the `-s` option on the test runner:
-* Make sure everything is fine.
+ ```
+ libgit2_tests -smerge::analysis::uptodate
+ ```
-* Send your pull request. That's it.
-
-
-Memory leak checks
-------------------
+## Memory leak checking
These are automatically run as part of CI, but if you want to check locally:
-#### Linux
+### Linux
Uses [`valgrind`](http://www.valgrind.org/):
@@ -38,7 +63,7 @@ $ valgrind --leak-check=full --show-reachable=yes --num-callers=50 --suppression
./libgit2_tests
```
-#### macOS
+### macOS
Uses [`leaks`](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/FindingLeaks.html), which requires XCode installed:
@@ -46,3 +71,13 @@ Uses [`leaks`](https://developer.apple.com/library/archive/documentation/Perform
$ MallocStackLogging=1 MallocScribble=1 MallocLogFile=/dev/null CLAR_AT_EXIT="leaks -quiet \$PPID" \
./libgit2_tests
```
+
+### Windows
+
+Build with the `WIN32_LEAKCHECK` option:
+
+```console
+$ cmake -DBUILD_TESTS=ON -DWIN32_LEAKCHECK=ON ..
+$ cmake --build .
+$ ./libgit2_tests
+```
diff --git a/tests/benchmarks/README.md b/tests/benchmarks/README.md
new file mode 100644
index 000000000..f66b27aea
--- /dev/null
+++ b/tests/benchmarks/README.md
@@ -0,0 +1,121 @@
+# libgit2 benchmarks
+
+This folder contains the individual benchmark tests for libgit2,
+meant for understanding the performance characteristics of libgit2,
+comparing your development code to the existing libgit2 code, or
+comparing libgit2 to the git reference implementation.
+
+## Running benchmark tests
+
+Benchmark tests can be run in several different ways: running all
+benchmarks, running one (or more) suite of benchmarks, or running a
+single individual benchmark. You can target either an individual
+version of a CLI, or you can A/B test a baseline CLI against a test
+CLI.
+
+### Specifying the command-line interface to test
+
+By default, the `git` in your path is benchmarked. Use the
+`-c` (or `--cli`) option to specify the command-line interface
+to test.
+
+Example: `libgit2_bench --cli git2_cli` will run the tests against
+`git2_cli`.
+
+### Running tests to compare two different implementations
+
+You can compare a baseline command-line interface against a test
+command-line interface using the `-b (or `--baseline-cli`) option.
+
+Example: `libgit2_bench --baseline-cli git --cli git2_cli` will
+run the tests against both `git` and `git2_cli`.
+
+### Running individual benchmark tests
+
+Similar to how a test suite or individual test is specified in
+[clar](https://github.com/clar-test/clar), the `-s` (or `--suite`)
+option may be used to specify the suite or individual test to run.
+Like clar, the suite and test name are separated by `::`, and like
+clar, this is a prefix match.
+
+Examples:
+* `libgit2_bench -shash_object` will run the tests in the
+ `hash_object` suite.
+* `libgit2_bench -shash_object::random_1kb` will run the
+ `hash_object::random_1kb` test.
+* `libgit2_bench -shash_object::random` will run all the tests that
+ begin with `hash_object::random`.
+
+## Writing benchmark tests
+
+Benchmark tests are meant to be easy to write. Each individual
+benchmark is a shell script that allows it to do set up (eg, creating
+or cloning a repository, creating temporary files, etc), then running
+benchmarks, then teardown.
+
+The `benchmark_helpers.sh` script provides many helpful utility
+functions to allow for cross-platform benchmarking, as well as a
+wrapper for `hyperfine` that is suited to testing libgit2.
+Note that the helper script must be included first, at the beginning
+of the benchmark test.
+
+### Benchmark example
+
+This simplistic example compares the speed of running the `git help`
+command in the baseline CLI to the test CLI.
+
+```bash
+#!/bin/bash -e
+
+# include the benchmark library
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+# run the "help" command; this will benchmark `git2_cli help`
+gitbench help
+```
+
+### Naming
+
+The filename of the benchmark itself is important. A benchmark's
+filename should be the name of the benchmark suite, followed by two
+underscores, followed by the name of the benchmark. For example,
+`hash-object__random_1kb` is the `random_1kb` test in the `hash-object`
+suite.
+
+### Options
+
+The `gitbench` function accepts several options.
+
+* `--sandbox `
+ The name of a test resource (in the `tests/resources` directory).
+ This will be copied as-is to the sandbox location before test
+ execution. This is copied _before_ the `prepare` script is run.
+ This option may be specified multiple times.
+* `--repository `
+ The name of a test resource repository (in the `tests/resources`
+ directory). This repository will be copied into a sandbox location
+ before test execution, and your test will run in this directory.
+ This is copied _before_ the `prepare` script is run.
+* `--prepare