diff --git a/.gitattributes b/.gitattributes
index 3d90b7d61f8..3788dc98358 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,4 @@
* text=auto
+ci/**/*.sh text eol=lf
+script/**/*.sh text eol=lf
tests/resources/** linguist-vendored
diff --git a/.github/actions/download-or-build-container/action.yml b/.github/actions/download-or-build-container/action.yml
new file mode 100644
index 00000000000..9c83a9836c3
--- /dev/null
+++ b/.github/actions/download-or-build-container/action.yml
@@ -0,0 +1,109 @@
+# Run a build step in a container or directly on the Actions runner
+name: Download or Build Container
+description: Download a container from the package registry, or build it if it's not found
+
+inputs:
+ container:
+ description: Container name
+ type: string
+ required: true
+ dockerfile:
+ description: Dockerfile
+ type: string
+ base:
+ description: Container base
+ type: string
+ registry:
+ description: Docker registry to read and publish to
+ type: string
+ default: ghcr.io
+ config-path:
+ description: Path to Dockerfiles
+ type: string
+ github_token:
+ description: GitHub Token
+ type: string
+
+runs:
+ using: 'composite'
+ steps:
+ - name: Download container
+ run: |
+ IMAGE_NAME="${{ inputs.container }}"
+ DOCKERFILE_PATH="${{ inputs.dockerfile }}"
+ DOCKER_REGISTRY="${{ inputs.registry }}"
+ DOCKERFILE_ROOT="${{ inputs.config-path }}"
+
+ if [ "${DOCKERFILE_PATH}" = "" ]; then
+ DOCKERFILE_PATH="${DOCKERFILE_ROOT}/${IMAGE_NAME}"
+ else
+ DOCKERFILE_PATH="${DOCKERFILE_ROOT}/${DOCKERFILE_PATH}"
+ fi
+
+ GIT_WORKTREE=$(cd "${GITHUB_ACTION_PATH}" && git rev-parse --show-toplevel)
+ echo "::: git worktree is ${GIT_WORKTREE}"
+ cd "${GIT_WORKTREE}"
+
+ DOCKER_CONTAINER="${GITHUB_REPOSITORY}/${IMAGE_NAME}"
+ DOCKER_REGISTRY_CONTAINER="${DOCKER_REGISTRY}/${DOCKER_CONTAINER}"
+
+ echo "dockerfile=${DOCKERFILE_PATH}" >> $GITHUB_ENV
+ echo "docker-container=${DOCKER_CONTAINER}" >> $GITHUB_ENV
+ echo "docker-registry-container=${DOCKER_REGISTRY_CONTAINER}" >> $GITHUB_ENV
+
+ # Identify the last git commit that touched the Dockerfiles
+ # Use this as a hash to identify the resulting docker containers
+ echo "::: dockerfile path is ${DOCKERFILE_PATH}"
+
+ DOCKER_SHA=$(git log -1 --pretty=format:"%h" -- "${DOCKERFILE_PATH}")
+ echo "docker-sha=${DOCKER_SHA}" >> $GITHUB_ENV
+
+ echo "::: docker sha is ${DOCKER_SHA}"
+
+ 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
+
+ if [ "${exists}" = "true" ]; then
+ echo "::: docker container exists in registry"
+ echo "docker-container-exists=true" >> $GITHUB_ENV
+ else
+ echo "::: docker container does not exist in registry"
+ echo "docker-container-exists=false" >> $GITHUB_ENV
+ fi
+ shell: bash
+ env:
+ GITHUB_TOKEN: ${{ inputs.github_token }}
+ - name: Create container
+ run: |
+ if [ "${{ inputs.base }}" != "" ]; then
+ BASE_ARG="--build-arg BASE=${{ inputs.base }}"
+ fi
+
+ GIT_WORKTREE=$(cd "${GITHUB_ACTION_PATH}" && git rev-parse --show-toplevel)
+ echo "::: git worktree is ${GIT_WORKTREE}"
+ cd "${GIT_WORKTREE}"
+
+ 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 }}
+ shell: bash
+ working-directory: source/${{ inputs.config-path }}
+ if: env.docker-container-exists != 'true'
+ - name: Publish container
+ run: |
+ docker push ${{ env.docker-registry-container-sha }}
+ docker push ${{ env.docker-registry-container-latest }}
+ shell: bash
+ if: env.docker-container-exists != 'true' && github.event_name != 'pull_request'
diff --git a/.github/actions/run-build/action.yml b/.github/actions/run-build/action.yml
new file mode 100644
index 00000000000..9afcfb11e72
--- /dev/null
+++ b/.github/actions/run-build/action.yml
@@ -0,0 +1,51 @@
+# Run a build step in a container or directly on the Actions runner
+name: Run Build Step
+description: Run a build step in a container or directly on the Actions runner
+
+inputs:
+ command:
+ description: Command to run
+ type: string
+ required: true
+ container:
+ description: Optional container to run in
+ type: string
+ container-version:
+ description: Version of the container to run
+ type: string
+ shell:
+ description: Shell to use
+ type: string
+ required: true
+ default: 'bash'
+
+runs:
+ using: 'composite'
+ steps:
+ - run: |
+ if [ -n "${{ inputs.container }}" ]; then
+ docker run \
+ --rm \
+ --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 \
+ -e CFLAGS \
+ -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 SKIP_PUSHOPTIONS_TESTS \
+ -e TSAN_OPTIONS \
+ -e UBSAN_OPTIONS \
+ ${{ inputs.container-version }} \
+ /bin/bash -c "${{ inputs.command }}"
+ else
+ ${{ inputs.command }}
+ fi
+ shell: ${{ inputs.shell != '' && inputs.shell || 'bash' }}
diff --git a/.github/release.yml b/.github/release.yml
index 79158f492d9..4d4e31860c2 100644
--- a/.github/release.yml
+++ b/.github/release.yml
@@ -3,6 +3,9 @@ changelog:
- title: New features
labels:
- feature
+ - title: Performance improvements
+ labels:
+ - performance
- title: Bug fixes
labels:
- bug
@@ -18,6 +21,15 @@ changelog:
- title: Documentation improvements
labels:
- documentation
+ - title: Platform compatibility fixes
+ labels:
+ - compatibility
+ - title: Git compatibility fixes
+ labels:
+ - git compatibility
+ - title: Dependency updates
+ labels:
+ - dependency
- title: Other changes
labels:
- '*'
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index bf2167464ec..6ee492ac443 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -6,10 +6,14 @@ on:
schedule:
- cron: '15 4 * * *'
+permissions:
+ contents: read
+
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).
+ # Run our benchmarks. We build a matrix with the various build
+ # targets and their details. Unlike our CI builds, we run these
+ # directly on the VM instead of in containers since we do not
+ # need the breadth of platform diversity.
build:
# Only run scheduled workflows on the main repository; prevents people
# from using build minutes on their forks.
@@ -27,7 +31,7 @@ jobs:
os: ubuntu-latest
setup-script: ubuntu
- name: "macOS"
- os: macos-11
+ os: macos-12
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
@@ -45,12 +49,12 @@ jobs:
id: windows
setup-script: win32
fail-fast: false
- name: "Build ${{ matrix.platform.name }}"
+ name: "Benchmark ${{ matrix.platform.name }}"
env: ${{ matrix.platform.env }}
runs-on: ${{ matrix.platform.os }}
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: source
fetch-depth: 0
@@ -72,11 +76,65 @@ jobs:
fi
mkdir benchmark && cd benchmark
- ../source/tests/benchmarks/benchmark.sh --baseline-cli "git" --cli "${GIT2_CLI}" --json benchmarks.json --zip benchmarks.zip
+ ../source/tests/benchmarks/benchmark.sh --baseline-cli "git" --cli "${GIT2_CLI}" --name libgit2 --json benchmarks.json --zip benchmarks.zip
shell: bash
- name: Upload results
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: benchmark-${{ matrix.platform.id }}
path: benchmark
if: always()
+
+ # Publish the results
+ publish:
+ name: Publish results
+ needs: [ build ]
+ if: ${{ always() && github.repository == 'libgit2/libgit2' }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out benchmark repository
+ uses: actions/checkout@v4
+ with:
+ repository: libgit2/benchmarks
+ path: site
+ fetch-depth: 0
+ ssh-key: ${{ secrets.BENCHMARKS_PUBLISH_KEY }}
+ - name: Download test results
+ uses: actions/download-artifact@v4
+ - name: Publish API
+ run: |
+ # Move today's benchmark run into the right place
+ for platform in linux macos windows; do
+ TIMESTAMP=$(jq .time.start < "benchmark-${platform}/benchmarks.json")
+ TIMESTAMP_LEN=$(echo -n ${TIMESTAMP} | wc -c | xargs)
+ DENOMINATOR=1
+ if [ "${TIMESTAMP_LEN}" = "19" ]; then
+ DENOMINATOR="1000000000"
+ elif [ "${TIMESTAMP_LEN}" = "13" ]; then
+ DENOMINATOR="1000"
+ else
+ echo "unknown timestamp"
+ exit 1
+ fi
+
+ if [[ "$(uname -s)" == "Darwin" ]]; then
+ DATE=$(date -R -r $(("${TIMESTAMP}/${DENOMINATOR}")) +"%Y-%m-%d")
+ else
+ DATE=$(date -d @$(("${TIMESTAMP}/${DENOMINATOR}")) +"%Y-%m-%d")
+ fi
+
+ mkdir -p "site/public/api/runs/${DATE}"
+ cp "benchmark-${platform}/benchmarks.json" "site/public/api/runs/${DATE}/${platform}.json"
+ done
+
+ (cd site && node scripts/aggregate.js)
+
+ (
+ cd site &&
+ git config user.name 'Benchmark Site Generation' &&
+ git config user.email 'libgit2@users.noreply.github.com' &&
+ git add . &&
+ git commit --allow-empty -m"benchmark update ${DATE}" &&
+ git push origin main
+ )
+ shell: bash
diff --git a/.github/workflows/build-containers.yml b/.github/workflows/build-containers.yml
new file mode 100644
index 00000000000..b52571c1811
--- /dev/null
+++ b/.github/workflows/build-containers.yml
@@ -0,0 +1,74 @@
+# Generate the containers that we use for builds.
+name: Build Containers
+
+on:
+ workflow_call:
+
+env:
+ docker-registry: ghcr.io
+ docker-config-path: source/ci/docker
+
+jobs:
+ # Build the docker container images that we will use for our Linux
+ # builds. This will identify the last commit to the repository that
+ # updated the docker images, and try to download the image tagged with
+ # that sha. If it does not exist, we'll do a docker build and push
+ # the image up to GitHub Packages for the actual CI/CD runs. We tag
+ # with both the sha and "latest" so that the subsequent runs need not
+ # know the sha. Only do this on CI builds (when the event is a "push")
+ # because PR builds from forks lack permission to write packages.
+ containers:
+ strategy:
+ matrix:
+ container:
+ - name: xenial
+ - name: bionic
+ - name: focal
+ - name: noble
+ - name: docurium
+ - name: bionic-x86
+ dockerfile: bionic
+ base: multiarch/ubuntu-core:x86-bionic
+ qemu: true
+ - name: bionic-arm32
+ dockerfile: bionic
+ base: multiarch/ubuntu-core:armhf-bionic
+ qemu: true
+ - name: bionic-arm64
+ dockerfile: bionic
+ base: multiarch/ubuntu-core:arm64-bionic
+ qemu: true
+ - name: centos7
+ - name: centos8
+ - name: fedora
+ runs-on: ubuntu-latest
+ name: "Create container: ${{ matrix.container.name }}"
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v4
+ with:
+ path: source
+ fetch-depth: 0
+ if: github.event_name != 'pull_request'
+ - name: Setup QEMU
+ run: docker run --rm --privileged multiarch/qemu-user-static:register --reset
+ if: matrix.container.qemu == true
+ - name: Download existing container
+ run: |
+ "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.container.name }}" "${{ matrix.container.dockerfile }}"
+ env:
+ DOCKER_REGISTRY: ${{ env.docker-registry }}
+ GITHUB_TOKEN: ${{ secrets.github_token }}
+ working-directory: ${{ env.docker-config-path }}
+ if: github.event_name != 'pull_request'
+ - name: Build and publish image
+ 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 }} .
+ 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 }}
+ working-directory: ${{ env.docker-config-path }}
+ if: github.event_name != 'pull_request' && env.docker-container-exists != 'true'
diff --git a/.github/workflows/experimental.yml b/.github/workflows/experimental.yml
new file mode 100644
index 00000000000..5bfea2c0028
--- /dev/null
+++ b/.github/workflows/experimental.yml
@@ -0,0 +1,118 @@
+# Validation builds for experimental features; these shouldn't be
+# required for pull request approval.
+name: Experimental Features
+
+on:
+ push:
+ branches: [ main, maint/* ]
+ pull_request:
+ branches: [ main, maint/* ]
+ workflow_dispatch:
+
+env:
+ docker-registry: ghcr.io
+ docker-config-path: ci/docker
+
+permissions:
+ contents: write
+ packages: write
+
+jobs:
+ # Run our CI/CD 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:
+ strategy:
+ matrix:
+ platform:
+ # All builds: experimental SHA256 support
+ - name: "Linux (SHA256, Xenial, Clang, OpenSSL)"
+ id: linux-sha256
+ os: ubuntu-latest
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON -DEXPERIMENTAL_SHA256=ON
+ - name: "macOS (SHA256)"
+ id: macos-sha256
+ os: macos-12
+ setup-script: osx
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON -DEXPERIMENTAL_SHA256=ON
+ CMAKE_GENERATOR: Ninja
+ PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (SHA256, amd64, Visual Studio)"
+ id: windows-sha256
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DEXPERIMENTAL_SHA256=ON
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ fail-fast: false
+ env: ${{ matrix.platform.env }}
+ runs-on: ${{ matrix.platform.os }}
+ name: "Build: ${{ matrix.platform.name }}"
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v4
+ with:
+ path: source
+ fetch-depth: 0
+ - name: Set up build environment
+ run: source/ci/setup-${{ matrix.platform.setup-script }}-build.sh
+ shell: bash
+ if: matrix.platform.setup-script != ''
+ - name: Setup QEMU
+ run: docker run --rm --privileged multiarch/qemu-user-static:register --reset
+ if: matrix.platform.container.qemu == true
+ - name: Set up container
+ uses: ./source/.github/actions/download-or-build-container
+ with:
+ registry: ${{ env.docker-registry }}
+ config-path: ${{ env.docker-config-path }}
+ container: ${{ matrix.platform.container.name }}
+ github_token: ${{ secrets.github_token }}
+ dockerfile: ${{ matrix.platform.container.dockerfile }}
+ if: matrix.platform.container.name != ''
+ - name: Prepare build
+ run: mkdir build
+ - name: Build
+ uses: ./source/.github/actions/run-build
+ with:
+ command: cd ${BUILD_WORKSPACE:-.}/build && ../source/ci/build.sh
+ container: ${{ matrix.platform.container.name }}
+ container-version: ${{ env.docker-registry-container-sha }}
+ shell: ${{ matrix.platform.shell }}
+ - name: Test
+ uses: ./source/.github/actions/run-build
+ with:
+ command: cd ${BUILD_WORKSPACE:-.}/build && ../source/ci/test.sh
+ container: ${{ matrix.platform.container.name }}
+ container-version: ${{ env.docker-registry-container-sha }}
+ shell: ${{ matrix.platform.shell }}
+ - name: Upload test results
+ uses: actions/upload-artifact@v4
+ if: success() || failure()
+ with:
+ name: test-results-${{ matrix.platform.id }}
+ path: build/results_*.xml
+
+ test_results:
+ name: Test results
+ needs: [ build ]
+ if: always()
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download test results
+ uses: actions/download-artifact@v3
+ - name: Generate test summary
+ uses: test-summary/action@v2
+ with:
+ paths: 'test-results-*/*.xml'
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 39cefdb192d..87e834f10db 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -11,144 +11,93 @@ on:
env:
docker-registry: ghcr.io
- docker-config-path: source/ci/docker
+ docker-config-path: ci/docker
-jobs:
- # Build the docker container images that we will use for our Linux
- # builds. This will identify the last commit to the repository that
- # updated the docker images, and try to download the image tagged with
- # that sha. If it does not exist, we'll do a docker build and push
- # the image up to GitHub Packages for the actual CI/CD runs. We tag
- # with both the sha and "latest" so that the subsequent runs need not
- # know the sha. Only do this on CI builds (when the event is a "push")
- # because PR builds from forks lack permission to write packages.
- containers:
- strategy:
- matrix:
- container:
- - name: xenial
- - name: bionic
- - name: focal
- - name: docurium
- - name: bionic-x86
- dockerfile: bionic
- base: multiarch/ubuntu-core:x86-bionic
- qemu: true
- - name: bionic-arm32
- dockerfile: bionic
- base: multiarch/ubuntu-core:armhf-bionic
- qemu: true
- - name: bionic-arm64
- dockerfile: bionic
- base: multiarch/ubuntu-core:arm64-bionic
- qemu: true
- - name: centos7
- - name: centos8
- runs-on: ubuntu-latest
- name: "Create container: ${{ matrix.container.name }}"
- steps:
- - name: Check out repository
- uses: actions/checkout@v3
- with:
- path: source
- fetch-depth: 0
- if: github.event_name != 'pull_request'
- - name: Setup QEMU
- run: docker run --rm --privileged multiarch/qemu-user-static:register --reset
- if: matrix.container.qemu == true
- - name: Download existing container
- run: |
- "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.container.name }}" "${{ matrix.container.dockerfile }}"
- env:
- DOCKER_REGISTRY: ${{ env.docker-registry }}
- GITHUB_TOKEN: ${{ secrets.github_token }}
- working-directory: ${{ env.docker-config-path }}
- if: github.event_name != 'pull_request'
- - name: Build and publish image
- 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 }} .
- 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 }}
- working-directory: ${{ env.docker-config-path }}
- if: github.event_name != 'pull_request' && env.docker-container-exists != 'true'
+permissions:
+ contents: write
+ packages: write
+jobs:
# Run our CI/CD 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:
- needs: [ containers ]
strategy:
matrix:
platform:
- - name: "Linux (Xenial, GCC, OpenSSL)"
- id: xenial-gcc-openssl
+ # All builds: core platforms
+ - name: "Linux (Noble, GCC, OpenSSL, libssh2)"
+ id: noble-gcc-openssl
+ os: ubuntu-latest
container:
- name: xenial
+ name: noble
env:
CC: gcc
CMAKE_GENERATOR: Ninja
- 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
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=libssh2 -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON
+ - name: "Linux (Noble, Clang, mbedTLS, OpenSSH)"
+ id: noble-clang-mbedtls
os: ubuntu-latest
- - name: Linux (Xenial, GCC, mbedTLS)
- id: xenial-gcc-mbedtls
container:
- name: xenial
+ name: noble
env:
- CC: gcc
+ CC: clang
+ CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=exec
CMAKE_GENERATOR: Ninja
- CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ - name: "Linux (Xenial, GCC, OpenSSL, OpenSSH)"
+ id: xenial-gcc-openssl
os: ubuntu-latest
- - name: "Linux (Xenial, Clang, OpenSSL)"
- id: xenial-clang-openssl
container:
name: xenial
env:
- CC: clang
+ CC: gcc
CMAKE_GENERATOR: Ninja
- CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=exec -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON
+ - name: "Linux (Xenial, Clang, mbedTLS, libssh2)"
+ id: xenial-gcc-mbedtls
os: ubuntu-latest
- - name: "Linux (Xenial, Clang, mbedTLS)"
- id: xenial-clang-mbedtls
container:
name: xenial
env:
CC: clang
- CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
CMAKE_GENERATOR: Ninja
- os: ubuntu-latest
+ CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=libssh2
- name: "macOS"
id: macos
- os: macos-11
+ os: macos-12
+ setup-script: osx
env:
CC: clang
CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
+ CMAKE_GENERATOR: Ninja
PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- setup-script: osx
- - name: "Windows (amd64, Visual Studio)"
+ - name: "Windows (amd64, Visual Studio, Schannel)"
id: windows-amd64-vs
os: windows-2019
+ setup-script: win32
env:
ARCH: amd64
CMAKE_GENERATOR: Visual Studio 16 2019
- CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON
+ CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel -DUSE_SSH=ON -DCMAKE_PREFIX_PATH=D:\Temp\libssh2
+ BUILD_PATH: C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin;D:\Temp\libssh2\bin
+ BUILD_TEMP: D:\Temp
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- - name: "Windows (x86, Visual Studio)"
+ - name: "Windows (x86, Visual Studio, WinHTTP)"
id: windows-x86-vs
os: windows-2019
+ setup-script: win32
env:
ARCH: x86
CMAKE_GENERATOR: Visual Studio 16 2019
- CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON
+ CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON -DCMAKE_PREFIX_PATH=D:\Temp\libssh2
+ BUILD_PATH: C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin;D:\Temp\libssh2\bin
+ BUILD_TEMP: D:\Temp
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- - name: "Windows (amd64, mingw)"
+ - name: "Windows (amd64, mingw, WinHTTP)"
id: windows-amd64-mingw
os: windows-2019
setup-script: mingw
@@ -160,26 +109,28 @@ jobs:
BUILD_PATH: D:\Temp\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- - name: "Windows (x86, mingw)"
+ - name: "Windows (x86, mingw, Schannel)"
id: windows-x86-mingw
os: windows-2019
setup-script: mingw
env:
ARCH: x86
CMAKE_GENERATOR: MinGW Makefiles
- CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel
BUILD_TEMP: D:\Temp
BUILD_PATH: D:\Temp\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- # Sanitizers
+ # All builds: sanitizers
- name: "Sanitizer (Memory)"
- id: memorysanitizer
+ id: sanitizer-memory
+ os: ubuntu-latest
+ setup-script: sanitizer
container:
- name: focal
+ name: noble
env:
- CC: clang-10
+ CC: clang
CFLAGS: -fsanitize=memory -fsanitize-memory-track-origins=2 -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local/msan -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON
CMAKE_GENERATOR: Ninja
@@ -187,73 +138,59 @@ jobs:
SKIP_NEGOTIATE_TESTS: true
ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
UBSAN_OPTIONS: print_stacktrace=1
+ - name: "Sanitizer (Address)"
+ id: sanitizer-address
os: ubuntu-latest
- - name: "Sanitizer (UndefinedBehavior)"
- id: ubsanitizer
+ setup-script: sanitizer
container:
- name: focal
+ name: noble
env:
- CC: clang-10
- CFLAGS: -fsanitize=undefined,nullability -fno-sanitize-recover=undefined,nullability -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
- CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON
+ CC: clang
+ CFLAGS: -fsanitize=address -ggdb -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON
CMAKE_GENERATOR: Ninja
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
UBSAN_OPTIONS: print_stacktrace=1
+ - name: "Sanitizer (UndefinedBehavior)"
+ id: sanitizer-ub
os: ubuntu-latest
- - name: "Sanitizer (Thread)"
- id: threadsanitizer
+ setup-script: sanitizer
container:
- name: focal
+ name: noble
env:
- CC: clang-10
- CFLAGS: -fsanitize=thread -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CC: clang
+ CFLAGS: -fsanitize=undefined,nullability -fno-sanitize-recover=undefined,nullability -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON
CMAKE_GENERATOR: Ninja
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
UBSAN_OPTIONS: print_stacktrace=1
- TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1
+ - name: "Sanitizer (Thread)"
+ id: sanitizer-thread
os: ubuntu-latest
-
- # Experimental: SHA256 support
- - name: "Linux (SHA256, Xenial, Clang, OpenSSL)"
- id: xenial-clang-openssl
+ setup-script: sanitizer
container:
- name: xenial
+ name: noble
env:
CC: clang
+ CFLAGS: -fsanitize=thread -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON
CMAKE_GENERATOR: Ninja
- CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
- os: ubuntu-latest
- - name: "macOS (SHA256)"
- id: macos
- os: macos-11
- env:
- CC: clang
- CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON -DEXPERIMENTAL_SHA256=ON
- PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
- setup-script: osx
- - name: "Windows (SHA256, amd64, Visual Studio)"
- id: windows-amd64-vs
- os: windows-2019
- env:
- ARCH: amd64
- CMAKE_GENERATOR: Visual Studio 16 2019
- CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DEXPERIMENTAL_SHA256=ON
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
+ ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ UBSAN_OPTIONS: print_stacktrace=1
+ TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1
fail-fast: false
env: ${{ matrix.platform.env }}
runs-on: ${{ matrix.platform.os }}
name: "Build: ${{ matrix.platform.name }}"
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: source
fetch-depth: 0
@@ -264,57 +201,33 @@ jobs:
- name: Setup QEMU
run: docker run --rm --privileged multiarch/qemu-user-static:register --reset
if: matrix.platform.container.qemu == true
- - name: Download container
- run: |
- "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.platform.container.name }}" "${{ matrix.platform.container.dockerfile }}"
- env:
- DOCKER_REGISTRY: ${{ env.docker-registry }}
- GITHUB_TOKEN: ${{ secrets.github_token }}
- working-directory: ${{ env.docker-config-path }}
+ - name: Set up container
+ uses: ./source/.github/actions/download-or-build-container
+ with:
+ registry: ${{ env.docker-registry }}
+ config-path: ${{ env.docker-config-path }}
+ container: ${{ matrix.platform.container.name }}
+ github_token: ${{ secrets.github_token }}
+ dockerfile: ${{ matrix.platform.container.dockerfile }}
if: matrix.platform.container.name != ''
- - name: Create container
- 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
- run: |
- export GITTEST_NEGOTIATE_PASSWORD="${{ secrets.GITTEST_NEGOTIATE_PASSWORD }}"
-
- if [ -n "${{ matrix.platform.container.name }}" ]; then
- mkdir build
- docker run \
- --rm \
- --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 \
- -e CFLAGS \
- -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 "cd build && ../source/ci/build.sh && ../source/ci/test.sh"
- else
- mkdir build
- cd build
- ../source/ci/build.sh
- ../source/ci/test.sh
- fi
- shell: bash
+ - name: Prepare build
+ run: mkdir build
+ - name: Build
+ uses: ./source/.github/actions/run-build
+ with:
+ command: cd ${BUILD_WORKSPACE:-.}/build && ../source/ci/build.sh
+ container: ${{ matrix.platform.container.name }}
+ container-version: ${{ env.docker-registry-container-sha }}
+ shell: ${{ matrix.platform.shell }}
+ - name: Test
+ uses: ./source/.github/actions/run-build
+ with:
+ command: cd ${BUILD_WORKSPACE:-.}/build && ../source/ci/test.sh
+ container: ${{ matrix.platform.container.name }}
+ container-version: ${{ env.docker-registry-container-sha }}
+ shell: ${{ matrix.platform.shell }}
- name: Upload test results
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
if: success() || failure()
with:
name: test-results-${{ matrix.platform.id }}
@@ -341,15 +254,22 @@ jobs:
# published to our documentation site.
documentation:
name: Generate documentation
- needs: [ containers ]
if: success() || failure()
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: source
fetch-depth: 0
+ - name: Set up container
+ uses: ./source/.github/actions/download-or-build-container
+ with:
+ registry: ${{ env.docker-registry }}
+ config-path: ${{ env.docker-config-path }}
+ container: docurium
+ github_token: ${{ secrets.github_token }}
+ dockerfile: ${{ matrix.platform.container.dockerfile }}
- name: Generate documentation
working-directory: source
run: |
@@ -365,7 +285,7 @@ jobs:
cm doc api.docurium
git checkout gh-pages
zip --exclude .git/\* --exclude .gitignore --exclude .gitattributes -r api-documentation.zip .
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
name: Upload artifact
with:
name: api-documentation
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 425a6e89ae0..28a06189d98 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -8,7 +8,11 @@ on:
env:
docker-registry: ghcr.io
- docker-config-path: source/ci/docker
+ docker-config-path: ci/docker
+
+permissions:
+ contents: read
+ packages: write
jobs:
# Run our nightly builds. We build a matrix with the various build
@@ -22,59 +26,112 @@ jobs:
strategy:
matrix:
platform:
- - name: Linux (Xenial, GCC, OpenSSL)
- container:
- name: xenial
- env:
- CC: gcc
- CMAKE_GENERATOR: Ninja
- CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ # All builds: core platforms
+ - name: "Linux (Noble, GCC, OpenSSL, libssh2)"
+ id: noble-gcc-openssl
os: ubuntu-latest
- - name: "Linux (Xenial, GCC, mbedTLS)"
container:
- name: xenial
+ name: noble
env:
CC: gcc
CMAKE_GENERATOR: Ninja
- CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=libssh2 -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON
+ - name: "Linux (Noble, Clang, mbedTLS, OpenSSH)"
+ id: noble-clang-mbedtls
os: ubuntu-latest
- - name: "Linux (Xenial, Clang, OpenSSL)"
container:
- name: xenial
+ name: noble
env:
CC: clang
+ CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=exec
CMAKE_GENERATOR: Ninja
- CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ - name: "Linux (Xenial, GCC, OpenSSL, OpenSSH)"
+ id: xenial-gcc-openssl
os: ubuntu-latest
- - name: "Linux (Xenial, Clang, mbedTLS)"
container:
name: xenial
env:
- CC: clang
- CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ CC: gcc
CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=exec -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON
+ - name: "Linux (Xenial, Clang, mbedTLS, libssh2)"
+ id: xenial-gcc-mbedtls
os: ubuntu-latest
- - name: "Linux (no threads)"
container:
name: xenial
env:
- CC: gcc
- CMAKE_OPTIONS: -DTHREADSAFE=OFF -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ CC: clang
CMAKE_GENERATOR: Ninja
- os: ubuntu-latest
- - name: "Linux (dynamically-loaded OpenSSL)"
- container:
- name: xenial
+ CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=libssh2
+ - name: "macOS"
+ id: macos
+ os: macos-12
+ setup-script: osx
env:
CC: clang
- CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
CMAKE_GENERATOR: Ninja
+ PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (amd64, Visual Studio, Schannel)"
+ id: windows-amd64-vs
+ os: windows-2019
+ setup-script: win32
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel -DUSE_SSH=ON -DCMAKE_PREFIX_PATH=D:\Temp\libssh2
+ BUILD_PATH: C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin;D:\Temp\libssh2\bin
+ BUILD_TEMP: D:\Temp
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (x86, Visual Studio, WinHTTP)"
+ id: windows-x86-vs
+ os: windows-2019
+ setup-script: win32
+ env:
+ ARCH: x86
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON -DCMAKE_PREFIX_PATH=D:\Temp\libssh2
+ BUILD_PATH: C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin;D:\Temp\libssh2\bin
+ BUILD_TEMP: D:\Temp
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (amd64, mingw, WinHTTP)"
+ id: windows-amd64-mingw
+ os: windows-2019
+ setup-script: mingw
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: MinGW Makefiles
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
+ BUILD_TEMP: D:\Temp
+ BUILD_PATH: D:\Temp\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (x86, mingw, Schannel)"
+ id: windows-x86-mingw
+ os: windows-2019
+ setup-script: mingw
+ env:
+ ARCH: x86
+ CMAKE_GENERATOR: MinGW Makefiles
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel
+ BUILD_TEMP: D:\Temp
+ BUILD_PATH: D:\Temp\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+
+ # All builds: sanitizers
+ - name: "Sanitizer (Memory)"
+ id: memorysanitizer
os: ubuntu-latest
- - name: "Linux (MemorySanitizer)"
+ setup-script: sanitizer
container:
- name: focal
+ name: noble
env:
- CC: clang-10
+ CC: clang-17
CFLAGS: -fsanitize=memory -fsanitize-memory-track-origins=2 -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local/msan -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON
CMAKE_GENERATOR: Ninja
@@ -82,60 +139,62 @@ jobs:
SKIP_NEGOTIATE_TESTS: true
ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
UBSAN_OPTIONS: print_stacktrace=1
+ - name: "Sanitizer (UndefinedBehavior)"
+ id: ubsanitizer
os: ubuntu-latest
- - name: "Linux (UndefinedBehaviorSanitizer)"
+ setup-script: sanitizer
container:
- name: focal
+ name: noble
env:
- CC: clang-10
+ CC: clang-17
CFLAGS: -fsanitize=undefined,nullability -fno-sanitize-recover=undefined,nullability -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
- CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON
CMAKE_GENERATOR: Ninja
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ UBSAN_OPTIONS: print_stacktrace=1
+ - name: "Sanitizer (Thread)"
+ id: threadsanitizer
os: ubuntu-latest
- - name: "Linux (ThreadSanitizer)"
+ setup-script: sanitizer
container:
- name: focal
+ name: noble
env:
- CC: clang-10
+ CC: clang-17
CFLAGS: -fsanitize=thread -fno-optimize-sibling-calls -fno-omit-frame-pointer
- CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON
CMAKE_GENERATOR: Ninja
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ UBSAN_OPTIONS: print_stacktrace=1
TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1
+
+ # Nightly builds: extended platforms
+ - name: "Linux (CentOS 7, OpenSSL)"
+ id: centos7-openssl
os: ubuntu-latest
- - name: "Linux (no mmap)"
- container:
- name: focal
- env:
- CC: clang-10
- CFLAGS: -DNO_MMAP
- CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local
- CMAKE_GENERATOR: Ninja
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
- os: ubuntu-latest
- - name: "Linux (CentOS 7)"
container:
name: centos7
env:
CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
SKIP_NEGOTIATE_TESTS: true
- os: ubuntu-latest
+ SKIP_PUSHOPTIONS_TESTS: true
- name: "Linux (CentOS 7, dynamically-loaded OpenSSL)"
+ id: centos7-dynamicopenssl
+ os: ubuntu-latest
container:
name: centos7
env:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
SKIP_NEGOTIATE_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
+ - name: "Linux (CentOS 8, OpenSSL)"
+ id: centos8-openssl
os: ubuntu-latest
- - name: "Linux (CentOS 8)"
container:
name: centos8
env:
@@ -143,8 +202,9 @@ jobs:
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
SKIP_NEGOTIATE_TESTS: true
SKIP_SSH_TESTS: true
- os: ubuntu-latest
- name: "Linux (CentOS 8, dynamically-loaded OpenSSL)"
+ id: centos8-dynamicopenssl
+ os: ubuntu-latest
container:
name: centos8
env:
@@ -152,64 +212,18 @@ jobs:
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
SKIP_NEGOTIATE_TESTS: true
SKIP_SSH_TESTS: true
- os: ubuntu-latest
- - name: "macOS"
- os: macos-11
- env:
- CC: clang
- CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
- PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
- setup-script: osx
- - name: "Windows (amd64, Visual Studio)"
- os: windows-2019
- env:
- ARCH: amd64
- CMAKE_GENERATOR: Visual Studio 16 2019
- CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
- - name: "Windows (no mmap)"
- os: windows-2019
- env:
- ARCH: amd64
- CMAKE_GENERATOR: Visual Studio 16 2019
- CFLAGS: -DNO_MMAP
- CMAKE_OPTIONS: -A x64 -DDEPRECATE_HARD=ON
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
- - name: "Windows (x86, Visual Studio)"
- os: windows-2019
- env:
ARCH: x86
- CMAKE_GENERATOR: Visual Studio 16 2019
- CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
- - name: "Windows (amd64, mingw)"
- os: windows-2019
- setup-script: mingw
- env:
- ARCH: amd64
- CMAKE_GENERATOR: MinGW Makefiles
- CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
- BUILD_TEMP: D:\Temp
- BUILD_PATH: D:\Temp\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
- - name: "Windows (x86, mingw)"
- os: windows-2019
- setup-script: mingw
+ - name: "Linux (Fedora, llhttp)"
+ id: fedora
+ os: ubuntu-latest
+ container:
+ name: fedora
env:
- ARCH: x86
- CMAKE_GENERATOR: MinGW Makefiles
- CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
- BUILD_TEMP: D:\Temp
- BUILD_PATH: D:\Temp\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
- SKIP_SSH_TESTS: true
- SKIP_NEGOTIATE_TESTS: true
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=pcre2 -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=libssh2 -DUSE_HTTP_PARSER=llhttp
- name: "Linux (Bionic, GCC, dynamically-loaded OpenSSL)"
+ id: bionic-gcc-dynamicopenssl
container:
name: bionic
dockerfile: bionic
@@ -218,8 +232,10 @@ jobs:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
os: ubuntu-latest
- name: "Linux (x86, Bionic, Clang, OpenSSL)"
+ id: bionic-x86-clang-openssl
container:
name: bionic-x86
dockerfile: bionic
@@ -229,8 +245,10 @@ jobs:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
os: ubuntu-latest
- name: "Linux (x86, Bionic, GCC, OpenSSL)"
+ id: bionic-x86-gcc-openssl
container:
name: bionic-x86
dockerfile: bionic
@@ -239,8 +257,10 @@ jobs:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
os: ubuntu-latest
- name: "Linux (arm32, Bionic, GCC, OpenSSL)"
+ id: bionic-arm32-gcc-openssl
container:
name: bionic-arm32
dockerfile: bionic
@@ -251,9 +271,11 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
SKIP_PROXY_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
GITTEST_FLAKY_STAT: true
os: ubuntu-latest
- name: "Linux (arm64, Bionic, GCC, OpenSSL)"
+ id: bionic-arm64-gcc-openssl
container:
name: bionic-arm64
dockerfile: bionic
@@ -264,14 +286,90 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
SKIP_PROXY_TESTS: true
+ SKIP_PUSHOPTIONS_TESTS: true
+ os: ubuntu-latest
+
+ # Nightly builds: ensure we fallback when missing core functionality
+ - name: "Linux (no threads)"
+ id: xenial-nothreads
+ os: ubuntu-latest
+ container:
+ name: xenial
+ env:
+ CC: gcc
+ CMAKE_OPTIONS: -DTHREADSAFE=OFF -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ CMAKE_GENERATOR: Ninja
+ SKIP_PUSHOPTIONS_TESTS: true
+ - name: "Linux (no mmap)"
+ id: noble-nommap
+ os: ubuntu-latest
+ container:
+ name: noble
+ env:
+ CC: gcc
+ CFLAGS: -DNO_MMAP
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (no mmap)"
+ id: windows-nommap
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CFLAGS: -DNO_MMAP
+ CMAKE_OPTIONS: -A x64 -DDEPRECATE_HARD=ON
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+
+ # Nightly builds: extended SSL support
+ - name: "Linux (dynamically-loaded OpenSSL)"
+ id: xenial-dynamicopenssl
+ os: ubuntu-latest
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ CMAKE_GENERATOR: Ninja
+
+ # All builds: experimental SHA256 support
+ - name: "Linux (SHA256, Xenial, Clang, OpenSSL)"
+ id: linux-sha256
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
os: ubuntu-latest
+ - name: "macOS (SHA256)"
+ id: macos-sha256
+ os: macos-12
+ setup-script: osx
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON -DEXPERIMENTAL_SHA256=ON
+ PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (SHA256, amd64, Visual Studio)"
+ id: windows-sha256
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DEXPERIMENTAL_SHA256=ON
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
fail-fast: false
- name: "Build ${{ matrix.platform.name }}"
env: ${{ matrix.platform.env }}
runs-on: ${{ matrix.platform.os }}
+ name: "Build ${{ matrix.platform.name }}"
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: source
fetch-depth: 0
@@ -282,47 +380,50 @@ jobs:
- name: Setup QEMU
run: docker run --rm --privileged multiarch/qemu-user-static:register --reset
if: matrix.platform.container.qemu == true
- - name: Download container
- run: |
- "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.platform.container.name }}" "${{ matrix.platform.container.dockerfile }}"
- env:
- DOCKER_REGISTRY: ${{ env.docker-registry }}
- GITHUB_TOKEN: ${{ secrets.github_token }}
- working-directory: ${{ env.docker-config-path }}
+ - name: Set up container
+ uses: ./source/.github/actions/download-or-build-container
+ with:
+ registry: ${{ env.docker-registry }}
+ config-path: ${{ env.docker-config-path }}
+ container: ${{ matrix.platform.container.name }}
+ github_token: ${{ secrets.github_token }}
+ dockerfile: ${{ matrix.platform.container.dockerfile }}
if: matrix.platform.container.name != ''
- - name: Create container
- run: docker build -t ${{ env.docker-registry-container-sha }} -f ${{ env.dockerfile }} .
- working-directory: ${{ env.docker-config-path }}
- if: matrix.platform.container.name != '' && env.docker-container-exists != 'true'
- - name: Build and test
- run: |
- export GITTEST_NEGOTIATE_PASSWORD="${{ secrets.GITTEST_NEGOTIATE_PASSWORD }}"
+ - name: Prepare build
+ run: mkdir build
+ - name: Build
+ uses: ./source/.github/actions/run-build
+ with:
+ command: cd ${BUILD_WORKSPACE:-.}/build && ../source/ci/build.sh
+ container: ${{ matrix.platform.container.name }}
+ container-version: ${{ env.docker-registry-container-sha }}
+ shell: ${{ matrix.platform.shell }}
+ - name: Test
+ uses: ./source/.github/actions/run-build
+ with:
+ command: cd ${BUILD_WORKSPACE:-.}/build && ../source/ci/test.sh
+ container: ${{ matrix.platform.container.name }}
+ container-version: ${{ env.docker-registry-container-sha }}
+ shell: ${{ matrix.platform.shell }}
+ - name: Upload test results
+ uses: actions/upload-artifact@v4
+ if: success() || failure()
+ with:
+ name: test-results-${{ matrix.platform.id }}
+ path: build/results_*.xml
- if [ -n "${{ matrix.platform.container.name }}" ]; then
- docker run \
- --rm \
- --user libgit2:libgit2 \
- -v "$(pwd)/source:/home/libgit2/source" \
- -w /home/libgit2 \
- -e ASAN_SYMBOLIZER_PATH \
- -e CC \
- -e CFLAGS \
- -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 \
- ${{ env.docker-registry-container-sha }} \
- /bin/bash -c "mkdir build && cd build && ../source/ci/build.sh && ../source/ci/test.sh"
- else
- mkdir build && cd build
- ../source/ci/build.sh
- ../source/ci/test.sh
- fi
- shell: bash
+ test_results:
+ name: Test results
+ needs: [ build ]
+ if: ${{ always() && github.repository == 'libgit2/libgit2' }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download test results
+ uses: actions/download-artifact@v3
+ - name: Generate test summary
+ uses: test-summary/action@v2
+ with:
+ paths: 'test-results-*/*.xml'
coverity:
# Only run scheduled workflows on the main repository; prevents people
@@ -333,17 +434,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: source
fetch-depth: 0
- - name: Download container
- run: |
- "${{ github.workspace }}/source/ci/getcontainer.sh" xenial
- env:
- DOCKER_REGISTRY: ${{ env.docker-registry }}
- GITHUB_TOKEN: ${{ secrets.github_token }}
- working-directory: ${{ env.docker-config-path }}
+ - name: Set up container
+ uses: ./source/.github/actions/download-or-build-container
+ with:
+ registry: ${{ env.docker-registry }}
+ config-path: ${{ env.docker-config-path }}
+ container: xenial
+ github_token: ${{ secrets.github_token }}
+ if: matrix.platform.container.name != ''
- name: Run Coverity
run: source/ci/coverity.sh
env:
@@ -354,11 +456,16 @@ jobs:
# from using build minutes on their forks.
if: github.repository == 'libgit2/libgit2'
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
name: CodeQL
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 0
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cf3b6224621..6e454816ef4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,7 +6,7 @@
cmake_minimum_required(VERSION 3.5.1)
-project(libgit2 VERSION "1.5.0" LANGUAGES C)
+project(libgit2 VERSION "1.8.4" LANGUAGES C)
# Add find modules to the path
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
@@ -30,12 +30,13 @@ option(USE_THREADS "Use threads for parallel processing when possibl
option(USE_NSEC "Support nanosecond precision file mtimes and ctimes" ON)
# Backend selection
-option(USE_SSH "Link with libssh2 to enable SSH support" OFF)
+option(USE_SSH "Enable SSH support. Can be set to a specific backend" 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" 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(USE_XDIFF "" CACHE STRING "Specifies the xdiff implementation; either system or builtin.")
set(REGEX_BACKEND "" CACHE STRING "Regular expression implementation. One of regcomp_l, pcre2, pcre, regcomp, or builtin.")
option(USE_BUNDLED_ZLIB "Use the bundled version of zlib. Can be set to one of Bundled(ON)/Chromium. The Chromium option requires a x86_64 processor with SSE4.2 and CLMUL" OFF)
@@ -82,12 +83,6 @@ if(MSVC)
option(WIN32_LEAKCHECK "Enable leak reporting via crtdbg" OFF)
endif()
-if(WIN32)
- # By default, libgit2 is built with WinHTTP. To use the built-in
- # HTTP transport, invoke CMake with the "-DUSE_WINHTTP=OFF" argument.
- option(USE_WINHTTP "Use Win32 WinHTTP routines" ON)
-endif()
-
if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif()
@@ -99,7 +94,7 @@ include(CheckLibraryExists)
include(CheckFunctionExists)
include(CheckSymbolExists)
include(CheckStructHasMember)
-include(CheckPrototypeDefinition)
+include(CheckPrototypeDefinitionSafe)
include(AddCFlagIfSupported)
include(FindPkgLibraries)
include(FindThreads)
diff --git a/COPYING b/COPYING
index 25c8d8c6b78..701792e9acb 100644
--- a/COPYING
+++ b/COPYING
@@ -365,7 +365,7 @@ Public License instead of this License.
The bundled ZLib code is licensed under the ZLib license:
-Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
+ (C) 1995-2022 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -1214,3 +1214,197 @@ 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.
+
+----------------------------------------------------------------------
+
+The bundled ntlmclient code is licensed under the MIT license:
+
+Copyright (c) Edward Thomson. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+----------------------------------------------------------------------
+
+Portions of this software derived from Team Explorer Everywhere:
+
+Copyright (c) Microsoft Corporation
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+---------------------------------------------------------------------------
+
+Portions of this software derived from the LLVM Compiler Infrastructure:
+
+Copyright (c) 2003-2016 University of Illinois at Urbana-Champaign.
+All rights reserved.
+
+Developed by:
+
+ LLVM Team
+
+ University of Illinois at Urbana-Champaign
+
+ http://llvm.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimers.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimers in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the names of the LLVM Team, University of Illinois at
+ Urbana-Champaign, nor the names of its contributors may be used to
+ endorse or promote products derived from this Software without specific
+ prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
+
+---------------------------------------------------------------------------
+
+Portions of this software derived from Unicode, Inc:
+
+Copyright 2001-2004 Unicode, Inc.
+
+Disclaimer
+
+This source code is provided as is by Unicode, Inc. No claims are
+made as to fitness for any particular purpose. No warranties of any
+kind are expressed or implied. The recipient agrees to determine
+applicability of information provided. If this file has been
+purchased on magnetic or optical media from Unicode, Inc., the
+sole remedy for any claim will be exchange of defective media
+within 90 days of receipt.
+
+Limitations on Rights to Redistribute This Code
+
+Unicode, Inc. hereby grants the right to freely use the information
+supplied in this file in the creation of products supporting the
+Unicode Standard, and to make copies of this file in any form
+for internal or external distribution as long as this notice
+remains attached.
+
+---------------------------------------------------------------------------
+
+Portions of this software derived from sheredom/utf8.h:
+
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+
+---------------------------------------------------------------------------
+
+Portions of this software derived from RFC 1320:
+
+Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD4 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+
+----------------------------------------------------------------------
+
+The bundled llhttp dependency is licensed under the MIT license:
+
+Copyright Fedor Indutny, 2018.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index c844ee4a1bb..77efdd4a688 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@ libgit2 - the Git linkable library
| Build Status | |
| ------------ | - |
| **main** branch CI builds | [](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush) |
-| **v1.5 branch** CI builds | [](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.5) |
-| **v1.4 branch** CI builds | [](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.4) |
+| **v1.8 branch** CI builds | [](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.8) |
+| **v1.7 branch** CI builds | [](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.7) |
| **Nightly** builds | [](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22Nightly+Build%22) [](https://scan.coverity.com/projects/639) |
`libgit2` is a portable, pure C implementation of the Git core methods
@@ -18,15 +18,16 @@ functionality into your application. Language bindings like
in your favorite language.
`libgit2` is used to power Git GUI clients like
-[GitKraken](https://gitkraken.com/) and [gmaster](https://gmaster.io/)
+[GitKraken](https://gitkraken.com/) and [GitButler](https://gitbutler.com/)
and on Git hosting providers like [GitHub](https://github.com/),
[GitLab](https://gitlab.com/) and
[Azure DevOps](https://azure.com/devops).
We perform the merge every time you click "merge pull request".
`libgit2` is licensed under a **very permissive license** (GPLv2 with a special
-Linking Exception). This basically means that you can link it (unmodified)
-with any kind of software without having to release its source code.
+Linking Exception). This means that you can link against the library with any
+kind of software without making that software fall under the GPL.
+Changes to libgit2 would still be covered under its GPL license.
Additionally, the example code has been released to the public domain (see the
[separate license](examples/COPYING) for more information).
@@ -67,7 +68,7 @@ But if you _do_ want to use libgit2 directly - because you're building
an application in C - then you may be able use an existing binary.
There are packages for the
[vcpkg](https://github.com/Microsoft/vcpkg) and
-[conan](https://conan.io/center/libgit2)
+[conan](https://conan.io/center/recipes/libgit2)
package managers. And libgit2 is available in
[Homebrew](https://formulae.brew.sh/formula/libgit2) and most Linux
distributions.
@@ -112,7 +113,7 @@ Getting Help
**Getting Help**
If you have questions about the library, please be sure to check out the
-[API documentation](http://libgit2.github.com/libgit2/). If you still have
+[API documentation](https://libgit2.org/libgit2/). If you still have
questions, reach out to us on Slack or post a question on
[StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) (with the `libgit2` tag).
@@ -395,7 +396,7 @@ Here are the bindings to libgit2 that are currently available:
* Pharo Smalltalk
* libgit2-pharo-bindings
* PHP
- * php-git
+ * php-git2
* Python
* pygit2
* R
diff --git a/ci/build.sh b/ci/build.sh
index 21a45af5f58..a9b66f6613f 100755
--- a/ci/build.sh
+++ b/ci/build.sh
@@ -13,16 +13,30 @@ BUILD_PATH=${BUILD_PATH:=$PATH}
CMAKE=$(which cmake)
CMAKE_GENERATOR=${CMAKE_GENERATOR:-Unix Makefiles}
+indent() { sed "s/^/ /"; }
+
+cygfullpath() {
+ result=$(echo "${1}" | tr \; \\n | while read -r element; do
+ if [ "${last}" != "" ]; then echo -n ":"; fi
+ echo -n $(cygpath "${element}")
+ last="${element}"
+ done)
+ if [ "${result}" = "" ]; then exit 1; fi
+ echo "${result}"
+}
+
if [[ "$(uname -s)" == MINGW* ]]; then
- BUILD_PATH=$(cygpath "$BUILD_PATH")
+ BUILD_PATH=$(cygfullpath "${BUILD_PATH}")
fi
-indent() { sed "s/^/ /"; }
echo "Source directory: ${SOURCE_DIR}"
echo "Build directory: ${BUILD_DIR}"
echo ""
+echo "Platform:"
+uname -s | indent
+
if [ "$(uname -s)" = "Darwin" ]; then
echo "macOS version:"
sw_vers | indent
@@ -40,13 +54,15 @@ echo "Kernel version:"
uname -a 2>&1 | indent
echo "CMake version:"
-env PATH="${BUILD_PATH}" "${CMAKE}" --version 2>&1 | indent
+env PATH="${BUILD_PATH}" "${CMAKE}" --version | head -1 2>&1 | indent
if test -n "${CC}"; then
echo "Compiler version:"
"${CC}" --version 2>&1 | indent
fi
echo "Environment:"
+echo "PATH=${BUILD_PATH}" | indent
+
if test -n "${CC}"; then
echo "CC=${CC}" | indent
fi
diff --git a/ci/docker/bionic b/ci/docker/bionic
index f1b69edefeb..f42c6d2aa0b 100644
--- a/ci/docker/bionic
+++ b/ci/docker/bionic
@@ -12,7 +12,6 @@ RUN apt-get update && \
libcurl4-openssl-dev \
libkrb5-dev \
libpcre3-dev \
- libssh2-1-dev \
libssl-dev \
libz-dev \
ninja-build \
@@ -37,7 +36,16 @@ RUN cd /tmp && \
cd .. && \
rm -rf mbedtls-mbedtls-2.16.2
-FROM mbedtls AS adduser
+FROM mbedtls AS libssh2
+RUN cd /tmp && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.11.0.tar.gz | tar -xz && \
+ cd libssh2-1.11.0 && \
+ CFLAGS=-fPIC cmake -G Ninja -DBUILD_SHARED_LIBS=ON . && \
+ ninja install && \
+ cd .. && \
+ rm -rf libssh2-1.11.0
+
+FROM libssh2 AS adduser
ARG UID=""
ARG GID=""
RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
diff --git a/ci/docker/centos7 b/ci/docker/centos7
index 28ed650811c..45c299d10bb 100644
--- a/ci/docker/centos7
+++ b/ci/docker/centos7
@@ -18,13 +18,13 @@ RUN yum install -y \
FROM yum AS libssh2
RUN cd /tmp && \
- curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.8.0.tar.gz | tar -xz && \
- cd libssh2-1.8.0 && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.11.0.tar.gz | tar -xz && \
+ cd libssh2-1.11.0 && \
./configure && \
make && \
make install && \
cd .. && \
- rm -rf libssh-1.8.0
+ rm -rf libssh-1.11.0
FROM libssh2 AS valgrind
RUN cd /tmp && \
diff --git a/ci/docker/centos8 b/ci/docker/centos8
index 81f0c3c7698..c2ac5f07af3 100644
--- a/ci/docker/centos8
+++ b/ci/docker/centos8
@@ -24,13 +24,13 @@ RUN yum install -y \
FROM yum AS libssh2
RUN cd /tmp && \
- curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.8.0.tar.gz | tar -xz && \
- cd libssh2-1.8.0 && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.11.0.tar.gz | tar -xz && \
+ cd libssh2-1.11.0 && \
./configure && \
make && \
make install && \
cd .. && \
- rm -rf libssh2-1.8.0
+ rm -rf libssh2-1.11.0
FROM libssh2 AS valgrind
RUN cd /tmp && \
diff --git a/ci/docker/fedora b/ci/docker/fedora
new file mode 100644
index 00000000000..1db3ceb78a0
--- /dev/null
+++ b/ci/docker/fedora
@@ -0,0 +1,52 @@
+ARG BASE=fedora:rawhide
+
+FROM ${BASE} AS stream
+RUN dnf -y distro-sync
+
+FROM stream AS yum
+RUN yum install -y \
+ which \
+ bzip2 \
+ git \
+ libarchive \
+ cmake \
+ gcc \
+ make \
+ openssl-devel \
+ openssh-server \
+ git-daemon \
+ java-1.8.0-openjdk-headless \
+ sudo \
+ python3 \
+ valgrind \
+ krb5-workstation \
+ krb5-libs \
+ krb5-devel \
+ pcre2-devel \
+ zlib-devel \
+ ninja-build \
+ llhttp-devel
+
+FROM yum AS libssh2
+RUN cd /tmp && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.11.0.tar.gz | tar -xz && \
+ cd libssh2-1.11.0 && \
+ ./configure && \
+ make && \
+ make install && \
+ cd .. && \
+ rm -rf libssh2-1.11.0
+
+FROM libssh2 AS adduser
+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
+RUN mkdir /var/run/sshd
+RUN echo "/usr/local/lib" > /etc/ld.so.conf.d/local && \
+ ldconfig
diff --git a/ci/docker/focal b/ci/docker/focal
index b3a402cb011..62f5b6301ae 100644
--- a/ci/docker/focal
+++ b/ci/docker/focal
@@ -53,7 +53,7 @@ RUN cd /tmp && \
cd libssh2-1.9.0 && \
mkdir build build-msan && \
cd build && \
- CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=Libgcrypt -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_PREFIX=/usr/local .. && \
+ CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_PREFIX=/usr/local .. && \
ninja install && \
cd ../build-msan && \
CC=clang-10 CFLAGS="-fPIC -fsanitize=memory -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer" LDFLAGS="-fsanitize=memory" cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=mbedTLS -DCMAKE_PREFIX_PATH=/usr/local/msan -DCMAKE_INSTALL_PREFIX=/usr/local/msan .. && \
diff --git a/ci/docker/noble b/ci/docker/noble
new file mode 100644
index 00000000000..05cd2768fe4
--- /dev/null
+++ b/ci/docker/noble
@@ -0,0 +1,88 @@
+ARG BASE=ubuntu:noble
+
+FROM ${BASE} AS apt
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ bzip2 \
+ clang \
+ cmake \
+ curl \
+ gcc \
+ git \
+ krb5-user \
+ libclang-rt-17-dev \
+ libcurl4-gnutls-dev \
+ libgcrypt20-dev \
+ libkrb5-dev \
+ libpcre3-dev \
+ libssl-dev \
+ libz-dev \
+ llvm-17 \
+ make \
+ ninja-build \
+ openjdk-8-jre-headless \
+ openssh-server \
+ openssl \
+ pkgconf \
+ python3 \
+ sudo \
+ valgrind \
+ && \
+ rm -rf /var/lib/apt/lists/* && \
+ mkdir /usr/local/msan
+
+FROM apt AS mbedtls
+RUN cd /tmp && \
+ curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.28.6.tar.gz | \
+ tar -xz && \
+ cd mbedtls-mbedtls-2.28.6 && \
+ scripts/config.pl unset MBEDTLS_AESNI_C && \
+ scripts/config.pl set MBEDTLS_MD4_C 1 && \
+ mkdir build build-msan && \
+ cd build && \
+ CC=clang-17 CFLAGS="-fPIC" cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_PREFIX=/usr/local .. && \
+ ninja install && \
+ cd ../build-msan && \
+ CC=clang-17 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-mbedtls-2.28.6
+
+FROM mbedtls AS libssh2
+RUN cd /tmp && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.11.0.tar.gz | tar -xz && \
+ cd libssh2-1.11.0 && \
+ mkdir build build-msan && \
+ cd build && \
+ CC=clang-17 CFLAGS="-fPIC" cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_PREFIX=/usr/local .. && \
+ ninja install && \
+ cd ../build-msan && \
+ CC=clang-17 CFLAGS="-fPIC -fsanitize=memory -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer" LDFLAGS="-fsanitize=memory" cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=mbedTLS -DCMAKE_PREFIX_PATH=/usr/local/msan -DCMAKE_INSTALL_PREFIX=/usr/local/msan .. && \
+ ninja install && \
+ cd .. && \
+ rm -rf libssh2-1.11.0
+
+FROM libssh2 AS valgrind
+RUN cd /tmp && \
+ curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.22.0.tar.bz2 | \
+ tar -xj && \
+ cd valgrind-3.22.0 && \
+ CC=clang-17 ./configure && \
+ make MAKEFLAGS="-j -l$(grep -c ^processor /proc/cpuinfo)" && \
+ make install && \
+ cd .. && \
+ rm -rf valgrind-3.22.0
+
+FROM valgrind AS adduser
+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 ldconfig
+RUN ldconfig
+
+FROM ldconfig AS configure
+RUN mkdir /var/run/sshd
diff --git a/ci/docker/xenial b/ci/docker/xenial
index f5fa5a315b2..793df4bda50 100644
--- a/ci/docker/xenial
+++ b/ci/docker/xenial
@@ -7,11 +7,13 @@ RUN apt-get update && \
clang \
cmake \
curl \
+ gettext \
gcc \
- git \
krb5-user \
libcurl4-gnutls-dev \
+ libexpat1-dev \
libgcrypt20-dev \
+ libintl-perl \
libkrb5-dev \
libpcre3-dev \
libssl-dev \
@@ -28,7 +30,17 @@ RUN apt-get update && \
&& \
rm -rf /var/lib/apt/lists/*
-FROM apt AS mbedtls
+FROM apt AS git
+RUN cd /tmp && \
+ curl --location --silent --show-error https://github.com/git/git/archive/refs/tags/v2.39.1.tar.gz | \
+ tar -xz && \
+ cd git-2.39.1 && \
+ make && \
+ make prefix=/usr install && \
+ cd .. && \
+ rm -rf git-2.39.1
+
+FROM git AS mbedtls
RUN cd /tmp && \
curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \
tar -xz && \
@@ -41,12 +53,12 @@ RUN cd /tmp && \
FROM mbedtls AS libssh2
RUN cd /tmp && \
- curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.8.2.tar.gz | tar -xz && \
- cd libssh2-1.8.2 && \
- CFLAGS=-fPIC cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=Libgcrypt . && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.11.0.tar.gz | tar -xz && \
+ cd libssh2-1.11.0 && \
+ CFLAGS=-fPIC cmake -G Ninja -DBUILD_SHARED_LIBS=ON . && \
ninja install && \
cd .. && \
- rm -rf libssh2-1.8.2
+ rm -rf libssh2-1.11.0
FROM libssh2 AS valgrind
RUN cd /tmp && \
diff --git a/ci/getcontainer.sh b/ci/getcontainer.sh
deleted file mode 100755
index 81d0c1d9266..00000000000
--- a/ci/getcontainer.sh
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/bin/bash
-
-set -e
-
-IMAGE_NAME=$1
-DOCKERFILE_PATH=$2
-
-if [ "${IMAGE_NAME}" = "" ]; then
- echo "usage: $0 image_name [dockerfile]"
- exit 1
-fi
-
-if [ "${DOCKERFILE_PATH}" = "" ]; then
- DOCKERFILE_PATH="${IMAGE_NAME}"
-fi
-
-if [ "${DOCKER_REGISTRY}" = "" ]; then
- echo "DOCKER_REGISTRY environment variable is unset."
- echo "Not running inside GitHub Actions or misconfigured?"
- exit 1
-fi
-
-DOCKER_CONTAINER="${GITHUB_REPOSITORY}/${IMAGE_NAME}"
-DOCKER_REGISTRY_CONTAINER="${DOCKER_REGISTRY}/${DOCKER_CONTAINER}"
-
-echo "dockerfile=${DOCKERFILE_PATH}" >> $GITHUB_ENV
-echo "docker-container=${DOCKER_CONTAINER}" >> $GITHUB_ENV
-echo "docker-registry-container=${DOCKER_REGISTRY_CONTAINER}" >> $GITHUB_ENV
-
-# Identify the last git commit that touched the Dockerfiles
-# Use this as a hash to identify the resulting docker containers
-DOCKER_SHA=$(git log -1 --pretty=format:"%h" -- "${DOCKERFILE_PATH}")
-echo "docker-sha=${DOCKER_SHA}" >> $GITHUB_ENV
-
-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
-
-if [ "${exists}" = "true" ]; then
- echo "docker-container-exists=true" >> $GITHUB_ENV
-else
- echo "docker-container-exists=false" >> $GITHUB_ENV
-fi
diff --git a/ci/setup-mingw-build.sh b/ci/setup-mingw-build.sh
index 3d72b24eb50..6c444f584e2 100755
--- a/ci/setup-mingw-build.sh
+++ b/ci/setup-mingw-build.sh
@@ -11,9 +11,9 @@ BUILD_TEMP=$(cygpath $BUILD_TEMP)
case "$ARCH" in
amd64)
- MINGW_URI="https://github.com/libgit2/ci-dependencies/releases/download/2021-05-04/mingw-x86_64-8.1.0-release-win32-sjlj-rt_v6-rev0.zip";;
+ MINGW_URI="https://github.com/libgit2/ci-dependencies/releases/download/2023-01-23/mingw-x86_64-8.1.0-release-win32-sjlj-rt_v6-rev0.zip";;
x86)
- MINGW_URI="https://github.com/libgit2/ci-dependencies/releases/download/2021-05-04/mingw-i686-8.1.0-release-win32-sjlj-rt_v6-rev0.zip";;
+ MINGW_URI="https://github.com/libgit2/ci-dependencies/releases/download/2023-01-23/mingw-i686-8.1.0-release-win32-sjlj-rt_v6-rev0.zip";;
esac
if [ -z "$MINGW_URI" ]; then
diff --git a/ci/setup-osx-build.sh b/ci/setup-osx-build.sh
index 0b95e7629e4..511d886cb17 100755
--- a/ci/setup-osx-build.sh
+++ b/ci/setup-osx-build.sh
@@ -3,6 +3,6 @@
set -ex
brew update
-brew install pkgconfig zlib curl openssl libssh2 ninja
+brew install pkgconfig libssh2 ninja
ln -s /Applications/Xcode.app/Contents/Developer/usr/lib/libLeaksAtExit.dylib /usr/local/lib
diff --git a/ci/setup-sanitizer-build.sh b/ci/setup-sanitizer-build.sh
new file mode 100755
index 00000000000..e4591f85bec
--- /dev/null
+++ b/ci/setup-sanitizer-build.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -ex
+
+# Linux updated its ASLR randomization in a way that is incompatible with
+# TSAN. See https://github.com/google/sanitizers/issues/1716
+sudo sysctl vm.mmap_rnd_bits=28
diff --git a/ci/setup-win32-build.sh b/ci/setup-win32-build.sh
new file mode 100755
index 00000000000..a8b81e5ef6e
--- /dev/null
+++ b/ci/setup-win32-build.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+set -ex
+
+echo "##############################################################################"
+echo "## Downloading libssh2"
+echo "##############################################################################"
+
+BUILD_TEMP=${BUILD_TEMP:=$TEMP}
+BUILD_TEMP=$(cygpath $BUILD_TEMP)
+
+case "$ARCH" in
+ amd64)
+ LIBSSH2_URI="https://github.com/libgit2/ci-dependencies/releases/download/2023-02-01/libssh2-20230201-amd64.zip";;
+ x86)
+ LIBSSH2_URI="https://github.com/libgit2/ci-dependencies/releases/download/2023-02-01-v2/libssh2-20230201-x86.zip";;
+esac
+
+if [ -z "$LIBSSH2_URI" ]; then
+ echo "No URL"
+ exit 1
+fi
+
+mkdir -p "$BUILD_TEMP"
+
+curl -s -L "$LIBSSH2_URI" -o "$BUILD_TEMP"/libssh2-"$ARCH".zip
+unzip -q "$BUILD_TEMP"/libssh2-"$ARCH".zip -d "$BUILD_TEMP"
diff --git a/ci/test.sh b/ci/test.sh
index 230daaaa0a4..98093c6ec5c 100755
--- a/ci/test.sh
+++ b/ci/test.sh
@@ -3,7 +3,14 @@
set -e
if [ -n "$SKIP_TESTS" ]; then
- exit 0
+ if [ -z "$SKIP_OFFLINE_TESTS" ]; then SKIP_OFFLINE_TESTS=1; fi
+ if [ -z "$SKIP_ONLINE_TESTS" ]; then SKIP_ONLINE_TESTS=1; fi
+ if [ -z "$SKIP_GITDAEMON_TESTS" ]; then SKIP_GITDAEMON_TESTS=1; fi
+ if [ -z "$SKIP_PROXY_TESTS" ]; then SKIP_PROXY_TESTS=1; fi
+ if [ -z "$SKIP_NTLM_TESTS" ]; then SKIP_NTLM_TESTS=1; fi
+ if [ -z "$SKIP_NEGOTIATE_TESTS" ]; then SKIP_NEGOTIATE_TESTS=1; fi
+ if [ -z "$SKIP_SSH_TESTS" ]; then SKIP_SSH_TESTS=1; fi
+ if [ -z "$SKIP_FUZZERS" ]; then SKIP_FUZZERS=1; fi
fi
# Windows doesn't run the NTLM tests properly (yet)
@@ -11,14 +18,36 @@ if [[ "$(uname -s)" == MINGW* ]]; then
SKIP_NTLM_TESTS=1
fi
+# older versions of git don't support push options
+if [ -z "$SKIP_PUSHOPTIONS_TESTS" ]; then
+ export GITTEST_PUSH_OPTIONS=true
+fi
+
SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
BUILD_DIR=$(pwd)
+BUILD_PATH=${BUILD_PATH:=$PATH}
+CTEST=$(which ctest)
TMPDIR=${TMPDIR:-/tmp}
USER=${USER:-$(whoami)}
+GITTEST_SSH_KEYTYPE=${GITTEST_SSH_KEYTYPE:="ecdsa"}
+
+HOME=`mktemp -d ${TMPDIR}/home.XXXXXXXX`
+export CLAR_HOMEDIR=${HOME}
+
SUCCESS=1
CONTINUE_ON_FAILURE=0
+should_run() {
+ eval "skip=\${SKIP_${1}}"
+ [ -z "$skip" \
+ -o "$skip" == "no" -o "$skip" == "NO" \
+ -o "$skip" == "n" -o "$skip" == "N" \
+ -o "$skip" == "false" -o "$skip" == "FALSE" \
+ -o "$skip" == "f" -o "$skip" == "F" \
+ -o "$skip" == "0" ]
+}
+
cleanup() {
echo "Cleaning up..."
@@ -32,6 +61,11 @@ cleanup() {
kill $GIT_NAMESPACE_PID
fi
+ if [ ! -z "$GIT_SHA256_PID" ]; then
+ echo "Stopping git daemon (sha256)..."
+ kill $GIT_SHA256_PID
+ fi
+
if [ ! -z "$PROXY_BASIC_PID" ]; then
echo "Stopping proxy (Basic)..."
kill $PROXY_BASIC_PID
@@ -68,11 +102,17 @@ run_test() {
echo ""
echo "Re-running flaky ${1} tests..."
echo ""
+
+ sleep 2
fi
RETURN_CODE=0
- CLAR_SUMMARY="${BUILD_DIR}/results_${1}.xml" ctest -V -R "^${1}$" || RETURN_CODE=$? && true
+ (
+ export PATH="${BUILD_PATH}"
+ export CLAR_SUMMARY="${BUILD_DIR}/results_${1}.xml"
+ "${CTEST}" -V -R "^${1}$"
+ ) || RETURN_CODE=$? && true
if [ "$RETURN_CODE" -eq 0 ]; then
FAILED=0
@@ -93,20 +133,43 @@ run_test() {
fi
}
+indent() { sed "s/^/ /"; }
+
+cygfullpath() {
+ result=$(echo "${1}" | tr \; \\n | while read -r element; do
+ if [ "${last}" != "" ]; then echo -n ":"; fi
+ echo -n $(cygpath "${element}")
+ last="${element}"
+ done)
+ if [ "${result}" = "" ]; then exit 1; fi
+ echo "${result}"
+}
+
+if [[ "$(uname -s)" == MINGW* ]]; then
+ BUILD_PATH=$(cygfullpath "$BUILD_PATH")
+fi
+
+
# Configure the test environment; run them early so that we're certain
# that they're started by the time we need them.
+echo "CTest version:"
+env PATH="${BUILD_PATH}" "${CTEST}" --version | head -1 2>&1 | indent
+
+echo ""
+
echo "##############################################################################"
echo "## Configuring test environment"
echo "##############################################################################"
echo ""
-if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
+if should_run "GITDAEMON_TESTS"; then
echo "Starting git daemon (standard)..."
GIT_STANDARD_DIR=`mktemp -d ${TMPDIR}/git_standard.XXXXXXXX`
- git init --bare "${GIT_STANDARD_DIR}/test.git" >/dev/null
+ cp -R "${SOURCE_DIR}/tests/resources/pushoptions.git" "${GIT_STANDARD_DIR}/test.git"
git daemon --listen=localhost --export-all --enable=receive-pack --base-path="${GIT_STANDARD_DIR}" "${GIT_STANDARD_DIR}" 2>/dev/null &
+
GIT_STANDARD_PID=$!
echo "Starting git daemon (namespace)..."
@@ -114,9 +177,15 @@ if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
cp -R "${SOURCE_DIR}/tests/resources/namespace.git" "${GIT_NAMESPACE_DIR}/namespace.git"
GIT_NAMESPACE="name1" git daemon --listen=localhost --port=9419 --export-all --enable=receive-pack --base-path="${GIT_NAMESPACE_DIR}" "${GIT_NAMESPACE_DIR}" &
GIT_NAMESPACE_PID=$!
+
+ echo "Starting git daemon (sha256)..."
+ GIT_SHA256_DIR=`mktemp -d ${TMPDIR}/git_sha256.XXXXXXXX`
+ cp -R "${SOURCE_DIR}/tests/resources/testrepo_256.git" "${GIT_SHA256_DIR}/testrepo_256.git"
+ git daemon --listen=localhost --port=9420 --export-all --enable=receive-pack --base-path="${GIT_SHA256_DIR}" "${GIT_SHA256_DIR}" &
+ GIT_SHA256_PID=$!
fi
-if [ -z "$SKIP_PROXY_TESTS" ]; then
+if should_run "PROXY_TESTS"; then
curl --location --silent --show-error https://github.com/ethomson/poxyproxy/releases/download/v0.7.0/poxyproxy-0.7.0.jar >poxyproxy.jar
echo "Starting HTTP proxy (Basic)..."
@@ -128,26 +197,27 @@ if [ -z "$SKIP_PROXY_TESTS" ]; then
PROXY_NTLM_PID=$!
fi
-if [ -z "$SKIP_NTLM_TESTS" -o -z "$SKIP_ONLINE_TESTS" ]; then
- curl --location --silent --show-error https://github.com/ethomson/poxygit/releases/download/v0.5.1/poxygit-0.5.1.jar >poxygit.jar
+if should_run "NTLM_TESTS" || should_run "ONLINE_TESTS"; then
+ curl --location --silent --show-error https://github.com/ethomson/poxygit/releases/download/v0.6.0/poxygit-0.6.0.jar >poxygit.jar
echo "Starting HTTP server..."
HTTP_DIR=`mktemp -d ${TMPDIR}/http.XXXXXXXX`
- git init --bare "${HTTP_DIR}/test.git"
+ cp -R "${SOURCE_DIR}/tests/resources/pushoptions.git" "${HTTP_DIR}/test.git"
+
java -jar poxygit.jar --address 127.0.0.1 --port 9000 --credentials foo:baz --quiet "${HTTP_DIR}" &
HTTP_PID=$!
fi
-if [ -z "$SKIP_SSH_TESTS" ]; then
+if should_run "SSH_TESTS"; then
echo "Starting SSH server..."
- HOME=`mktemp -d ${TMPDIR}/home.XXXXXXXX`
SSHD_DIR=`mktemp -d ${TMPDIR}/sshd.XXXXXXXX`
- git init --bare "${SSHD_DIR}/test.git" >/dev/null
+ cp -R "${SOURCE_DIR}/tests/resources/pushoptions.git" "${SSHD_DIR}/test.git"
+
cat >"${SSHD_DIR}/sshd_config" <<-EOF
Port 2222
ListenAddress 0.0.0.0
Protocol 2
- HostKey ${SSHD_DIR}/id_rsa
+ HostKey ${SSHD_DIR}/id_${GITTEST_SSH_KEYTYPE}
PidFile ${SSHD_DIR}/pid
AuthorizedKeysFile ${HOME}/.ssh/authorized_keys
LogLevel DEBUG
@@ -156,19 +226,21 @@ if [ -z "$SKIP_SSH_TESTS" ]; then
PubkeyAuthentication yes
ChallengeResponseAuthentication no
StrictModes no
+ HostCertificate ${SSHD_DIR}/id_${GITTEST_SSH_KEYTYPE}.pub
+ HostKey ${SSHD_DIR}/id_${GITTEST_SSH_KEYTYPE}
# Required here as sshd will simply close connection otherwise
UsePAM no
EOF
- ssh-keygen -t rsa -f "${SSHD_DIR}/id_rsa" -N "" -q
+ ssh-keygen -t "${GITTEST_SSH_KEYTYPE}" -f "${SSHD_DIR}/id_${GITTEST_SSH_KEYTYPE}" -N "" -q
/usr/sbin/sshd -f "${SSHD_DIR}/sshd_config" -E "${SSHD_DIR}/log"
# Set up keys
mkdir "${HOME}/.ssh"
- ssh-keygen -t rsa -f "${HOME}/.ssh/id_rsa" -N "" -q
- cat "${HOME}/.ssh/id_rsa.pub" >>"${HOME}/.ssh/authorized_keys"
+ ssh-keygen -t "${GITTEST_SSH_KEYTYPE}" -f "${HOME}/.ssh/id_${GITTEST_SSH_KEYTYPE}" -N "" -q
+ cat "${HOME}/.ssh/id_${GITTEST_SSH_KEYTYPE}.pub" >>"${HOME}/.ssh/authorized_keys"
while read algorithm key comment; do
echo "[localhost]:2222 $algorithm $key" >>"${HOME}/.ssh/known_hosts"
- done <"${SSHD_DIR}/id_rsa.pub"
+ done <"${SSHD_DIR}/id_${GITTEST_SSH_KEYTYPE}.pub"
# Append the github.com keys for the tests that don't override checks.
# We ask for ssh-rsa to test that the selection based off of known_hosts
@@ -187,7 +259,7 @@ fi
# Run the tests that do not require network connectivity.
-if [ -z "$SKIP_OFFLINE_TESTS" ]; then
+if should_run "OFFLINE_TESTS"; then
echo ""
echo "##############################################################################"
echo "## Running core tests"
@@ -218,7 +290,11 @@ if [ -n "$RUN_INVASIVE_TESTS" ]; then
unset GITTEST_INVASIVE_SPEED
fi
-if [ -z "$SKIP_ONLINE_TESTS" ]; then
+# the various network tests can fail due to network connectivity problems;
+# allow them to retry up to 5 times
+export GITTEST_FLAKY_RETRY=5
+
+if should_run "ONLINE_TESTS"; then
# Run the online tests. The "online" test suite only includes the
# default online tests that do not require additional configuration.
# The "proxy" and "ssh" test suites require further setup.
@@ -230,9 +306,13 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
export GITTEST_REMOTE_REDIRECT_INITIAL="http://localhost:9000/initial-redirect/libgit2/TestGitRepository"
export GITTEST_REMOTE_REDIRECT_SUBSEQUENT="http://localhost:9000/subsequent-redirect/libgit2/TestGitRepository"
+ export GITTEST_REMOTE_SPEED_SLOW="http://localhost:9000/speed-9600/test.git"
+ export GITTEST_REMOTE_SPEED_TIMESOUT="http://localhost:9000/speed-0.5/test.git"
run_test online
unset GITTEST_REMOTE_REDIRECT_INITIAL
unset GITTEST_REMOTE_REDIRECT_SUBSEQUENT
+ unset GITTEST_REMOTE_SPEED_SLOW
+ unset GITTEST_REMOTE_SPEED_TIMESOUT
# Run the online tests that immutably change global state separately
# to avoid polluting the test environment.
@@ -243,7 +323,7 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
run_test online_customcert
fi
-if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
+if should_run "GITDAEMON_TESTS"; then
echo ""
echo "Running gitdaemon (standard) tests"
echo ""
@@ -261,9 +341,17 @@ if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
run_test gitdaemon_namespace
unset GITTEST_REMOTE_URL
unset GITTEST_REMOTE_BRANCH
+
+ echo ""
+ echo "Running gitdaemon (sha256) tests"
+ echo ""
+
+ export GITTEST_REMOTE_URL="git://localhost:9420/testrepo_256.git"
+ run_test gitdaemon_sha256
+ unset GITTEST_REMOTE_URL
fi
-if [ -z "$SKIP_PROXY_TESTS" ]; then
+if should_run "PROXY_TESTS"; then
echo ""
echo "Running proxy tests (Basic authentication)"
echo ""
@@ -289,7 +377,7 @@ if [ -z "$SKIP_PROXY_TESTS" ]; then
unset GITTEST_REMOTE_PROXY_PASS
fi
-if [ -z "$SKIP_NTLM_TESTS" ]; then
+if should_run "NTLM_TESTS"; then
echo ""
echo "Running NTLM tests (IIS emulation)"
echo ""
@@ -315,7 +403,7 @@ if [ -z "$SKIP_NTLM_TESTS" ]; then
unset GITTEST_REMOTE_PASS
fi
-if [ -z "$SKIP_NEGOTIATE_TESTS" -a -n "$GITTEST_NEGOTIATE_PASSWORD" ]; then
+if should_run "NEGOTIATE_TESTS" && -n "$GITTEST_NEGOTIATE_PASSWORD" ; then
echo ""
echo "Running SPNEGO tests"
echo ""
@@ -348,13 +436,15 @@ if [ -z "$SKIP_NEGOTIATE_TESTS" -a -n "$GITTEST_NEGOTIATE_PASSWORD" ]; then
kdestroy -A
fi
-if [ -z "$SKIP_SSH_TESTS" ]; then
+if should_run "SSH_TESTS"; then
export GITTEST_REMOTE_USER=$USER
- export GITTEST_REMOTE_SSH_KEY="${HOME}/.ssh/id_rsa"
- export GITTEST_REMOTE_SSH_PUBKEY="${HOME}/.ssh/id_rsa.pub"
+ export GITTEST_REMOTE_SSH_KEY="${HOME}/.ssh/id_${GITTEST_SSH_KEYTYPE}"
+ export GITTEST_REMOTE_SSH_PUBKEY="${HOME}/.ssh/id_${GITTEST_SSH_KEYTYPE}.pub"
export GITTEST_REMOTE_SSH_PASSPHRASE=""
export GITTEST_REMOTE_SSH_FINGERPRINT="${SSH_FINGERPRINT}"
+ export GITTEST_SSH_CMD="ssh -i ${HOME}/.ssh/id_${GITTEST_SSH_KEYTYPE} -o UserKnownHostsFile=${HOME}/.ssh/known_hosts"
+
echo ""
echo "Running ssh tests"
echo ""
@@ -371,6 +461,8 @@ if [ -z "$SKIP_SSH_TESTS" ]; then
run_test ssh
unset GITTEST_REMOTE_URL
+ unset GITTEST_SSH_CMD
+
unset GITTEST_REMOTE_USER
unset GITTEST_REMOTE_SSH_KEY
unset GITTEST_REMOTE_SSH_PUBKEY
@@ -378,13 +470,15 @@ if [ -z "$SKIP_SSH_TESTS" ]; then
unset GITTEST_REMOTE_SSH_FINGERPRINT
fi
-if [ -z "$SKIP_FUZZERS" ]; then
+unset GITTEST_FLAKY_RETRY
+
+if should_run "FUZZERS"; then
echo ""
echo "##############################################################################"
echo "## Running fuzzers"
echo "##############################################################################"
- ctest -V -R 'fuzzer'
+ env PATH="${BUILD_PATH}" "${CTEST}" -V -R 'fuzzer'
fi
cleanup
diff --git a/cmake/CheckPrototypeDefinitionSafe.cmake b/cmake/CheckPrototypeDefinitionSafe.cmake
new file mode 100644
index 00000000000..f82603d3d72
--- /dev/null
+++ b/cmake/CheckPrototypeDefinitionSafe.cmake
@@ -0,0 +1,16 @@
+include(CheckPrototypeDefinition)
+
+function(check_prototype_definition_safe function prototype return header variable)
+ # temporarily save CMAKE_C_FLAGS and disable warnings about unused
+ # unused functions and parameters, otherwise they will always fail
+ # if ENABLE_WERROR is on
+ set(SAVED_CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
+
+ disable_warnings(unused-function)
+ disable_warnings(unused-parameter)
+
+ check_prototype_definition("${function}" "${prototype}" "${return}" "${header}" "${variable}")
+
+ # restore CMAKE_C_FLAGS
+ set(CMAKE_C_FLAGS "${SAVED_CMAKE_C_FLAGS}")
+endfunction()
diff --git a/cmake/ExperimentalFeatures.cmake b/cmake/ExperimentalFeatures.cmake
index 57a66e68daa..7eff40bdbc2 100644
--- a/cmake/ExperimentalFeatures.cmake
+++ b/cmake/ExperimentalFeatures.cmake
@@ -13,7 +13,7 @@ if(EXPERIMENTAL_SHA256)
set(EXPERIMENTAL 1)
set(GIT_EXPERIMENTAL_SHA256 1)
- add_compile_definitions(GIT_EXPERIMENTAL_SHA256)
+ add_definitions(-DGIT_EXPERIMENTAL_SHA256=1)
else()
add_feature_info("SHA256 API" OFF "experimental SHA256 APIs")
endif()
diff --git a/cmake/FindIconv.cmake b/cmake/FindIntlIconv.cmake
similarity index 100%
rename from cmake/FindIconv.cmake
rename to cmake/FindIntlIconv.cmake
diff --git a/cmake/FindLLHTTP.cmake b/cmake/FindLLHTTP.cmake
new file mode 100644
index 00000000000..a87d335d048
--- /dev/null
+++ b/cmake/FindLLHTTP.cmake
@@ -0,0 +1,39 @@
+# - Try to find llhttp
+#
+# Defines the following variables:
+#
+# LLHTTP_FOUND - system has llhttp
+# LLHTTP_INCLUDE_DIR - the llhttp include directory
+# LLHTTP_LIBRARIES - Link these to use llhttp
+# LLHTTP_VERSION_MAJOR - major version
+# LLHTTP_VERSION_MINOR - minor version
+# LLHTTP_VERSION_STRING - the version of llhttp found
+
+# Find the header and library
+find_path(LLHTTP_INCLUDE_DIR NAMES llhttp.h)
+find_library(LLHTTP_LIBRARY NAMES llhttp libllhttp)
+
+# Found the header, read version
+if(LLHTTP_INCLUDE_DIR AND EXISTS "${LLHTTP_INCLUDE_DIR}/llhttp.h")
+ file(READ "${LLHTTP_INCLUDE_DIR}/llhttp.h" LLHTTP_H)
+ if(LLHTTP_H)
+ string(REGEX REPLACE ".*#define[\t ]+LLHTTP_VERSION_MAJOR[\t ]+([0-9]+).*" "\\1" LLHTTP_VERSION_MAJOR "${LLHTTP_H}")
+ string(REGEX REPLACE ".*#define[\t ]+LLHTTP_VERSION_MINOR[\t ]+([0-9]+).*" "\\1" LLHTTP_VERSION_MINOR "${LLHTTP_H}")
+ set(LLHTTP_VERSION_STRING "${LLHTTP_VERSION_MAJOR}.${LLHTTP_VERSION_MINOR}")
+ endif()
+ unset(LLHTTP_H)
+endif()
+
+# Handle the QUIETLY and REQUIRED arguments and set LLHTTP_FOUND
+# to TRUE if all listed variables are TRUE
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(LLHTTP REQUIRED_VARS LLHTTP_INCLUDE_DIR LLHTTP_LIBRARY)
+
+# Hide advanced variables
+mark_as_advanced(LLHTTP_INCLUDE_DIR LLHTTP_LIBRARY)
+
+# Set standard variables
+if(LLHTTP_FOUND)
+ set(LLHTTP_LIBRARIES ${LLHTTP_LIBRARY})
+ set(LLHTTP_INCLUDE_DIRS ${LLHTTP_INCLUDE_DIR})
+endif()
diff --git a/cmake/SelectGSSAPI.cmake b/cmake/SelectGSSAPI.cmake
index 24e2d68b9a7..5bde11697df 100644
--- a/cmake/SelectGSSAPI.cmake
+++ b/cmake/SelectGSSAPI.cmake
@@ -29,7 +29,7 @@ if(USE_GSSAPI)
list(APPEND LIBGIT2_SYSTEM_LIBS ${GSSFRAMEWORK_LIBRARIES})
set(GIT_GSSFRAMEWORK 1)
- add_feature_info(SPNEGO GIT_GSSFRAMEWORK "SPNEGO authentication support (${USE_GSSAPI})")
+ add_feature_info(GSSAPI GIT_GSSFRAMEWORK "GSSAPI support for SPNEGO authentication (${USE_GSSAPI})")
elseif(USE_GSSAPI STREQUAL "gssapi")
if(NOT GSSAPI_FOUND)
message(FATAL_ERROR "Asked for gssapi GSS backend, but it wasn't found")
@@ -38,11 +38,11 @@ if(USE_GSSAPI)
list(APPEND LIBGIT2_SYSTEM_LIBS ${GSSAPI_LIBRARIES})
set(GIT_GSSAPI 1)
- add_feature_info(SPNEGO GIT_GSSAPI "SPNEGO authentication support (${USE_GSSAPI})")
+ add_feature_info(GSSAPI GIT_GSSAPI "GSSAPI support for SPNEGO authentication (${USE_GSSAPI})")
else()
message(FATAL_ERROR "Asked for backend ${USE_GSSAPI} but it wasn't found")
endif()
else()
set(GIT_GSSAPI 0)
- add_feature_info(SPNEGO NO "SPNEGO authentication support")
+ add_feature_info(GSSAPI NO "GSSAPI support for SPNEGO authentication")
endif()
diff --git a/cmake/SelectHTTPParser.cmake b/cmake/SelectHTTPParser.cmake
index 955aea3308c..4fc1f6968e6 100644
--- a/cmake/SelectHTTPParser.cmake
+++ b/cmake/SelectHTTPParser.cmake
@@ -1,19 +1,32 @@
# Optional external dependency: http-parser
-if(USE_HTTP_PARSER STREQUAL "system")
+if(USE_HTTP_PARSER STREQUAL "http-parser")
find_package(HTTPParser)
if(HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
list(APPEND LIBGIT2_SYSTEM_INCLUDES ${HTTP_PARSER_INCLUDE_DIRS})
list(APPEND LIBGIT2_SYSTEM_LIBS ${HTTP_PARSER_LIBRARIES})
list(APPEND LIBGIT2_PC_LIBS "-lhttp_parser")
- add_feature_info(http-parser ON "http-parser support (system)")
+ set(GIT_HTTPPARSER_HTTPPARSER 1)
+ add_feature_info(http-parser ON "using http-parser (system)")
else()
message(FATAL_ERROR "http-parser support was requested but not found")
endif()
+elseif(USE_HTTP_PARSER STREQUAL "llhttp")
+ find_package(LLHTTP)
+
+ if(LLHTTP_FOUND AND LLHTTP_VERSION_MAJOR EQUAL 9)
+ list(APPEND LIBGIT2_SYSTEM_INCLUDES ${LLHTTP_INCLUDE_DIRS})
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${LLHTTP_LIBRARIES})
+ list(APPEND LIBGIT2_PC_LIBS "-lllhttp")
+ set(GIT_HTTPPARSER_LLHTTP 1)
+ add_feature_info(http-parser ON "using llhttp (system)")
+ else()
+ message(FATAL_ERROR "llhttp support was requested but not found")
+ endif()
else()
- message(STATUS "http-parser version 2 was not found or disabled; using bundled 3rd-party sources.")
- add_subdirectory("${PROJECT_SOURCE_DIR}/deps/http-parser" "${PROJECT_BINARY_DIR}/deps/http-parser")
- list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/http-parser")
- list(APPEND LIBGIT2_DEPENDENCY_OBJECTS "$")
- add_feature_info(http-parser ON "http-parser support (bundled)")
+ add_subdirectory("${PROJECT_SOURCE_DIR}/deps/llhttp" "${PROJECT_BINARY_DIR}/deps/llhttp")
+ list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/llhttp")
+ list(APPEND LIBGIT2_DEPENDENCY_OBJECTS "$")
+ set(GIT_HTTPPARSER_BUILTIN 1)
+ add_feature_info(http-parser ON "using bundled parser")
endif()
diff --git a/cmake/SelectHTTPSBackend.cmake b/cmake/SelectHTTPSBackend.cmake
index 20221bf9f64..d293001f567 100644
--- a/cmake/SelectHTTPSBackend.cmake
+++ b/cmake/SelectHTTPSBackend.cmake
@@ -19,7 +19,7 @@ if(USE_HTTPS)
message(STATUS "Security framework is too old, falling back to OpenSSL")
set(USE_HTTPS "OpenSSL")
endif()
- elseif(USE_WINHTTP)
+ elseif(WIN32)
set(USE_HTTPS "WinHTTP")
elseif(OPENSSL_FOUND)
set(USE_HTTPS "OpenSSL")
@@ -55,6 +55,10 @@ if(USE_HTTPS)
set(GIT_OPENSSL 1)
list(APPEND LIBGIT2_SYSTEM_INCLUDES ${OPENSSL_INCLUDE_DIR})
list(APPEND LIBGIT2_SYSTEM_LIBS ${OPENSSL_LIBRARIES})
+ # Static OpenSSL (lib crypto.a) requires libdl, include it explicitly
+ if(LINK_WITH_STATIC_LIBRARIES STREQUAL ON)
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${CMAKE_DL_LIBS})
+ endif()
list(APPEND LIBGIT2_PC_LIBS ${OPENSSL_LDFLAGS})
list(APPEND LIBGIT2_PC_REQUIRES "openssl")
elseif(USE_HTTPS STREQUAL "mbedTLS")
@@ -106,8 +110,27 @@ if(USE_HTTPS)
# 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_HTTPS STREQUAL "Schannel")
+ set(GIT_SCHANNEL 1)
+
+ list(APPEND LIBGIT2_SYSTEM_LIBS "rpcrt4" "crypt32" "ole32")
+ list(APPEND LIBGIT2_PC_LIBS "-lrpcrt4" "-lcrypt32" "-lole32")
elseif(USE_HTTPS STREQUAL "WinHTTP")
- # WinHTTP setup was handled in the WinHTTP-specific block above
+ set(GIT_WINHTTP 1)
+
+ # Since MinGW does not come with headers or an import library for winhttp,
+ # we have to include a private header and generate our own import library
+ if(MINGW)
+ add_subdirectory("${PROJECT_SOURCE_DIR}/deps/winhttp" "${PROJECT_BINARY_DIR}/deps/winhttp")
+ list(APPEND LIBGIT2_SYSTEM_LIBS winhttp)
+ list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/winhttp")
+ else()
+ list(APPEND LIBGIT2_SYSTEM_LIBS "winhttp")
+ list(APPEND LIBGIT2_PC_LIBS "-lwinhttp")
+ endif()
+
+ list(APPEND LIBGIT2_SYSTEM_LIBS "rpcrt4" "crypt32" "ole32")
+ list(APPEND LIBGIT2_PC_LIBS "-lrpcrt4" "-lcrypt32" "-lole32")
elseif(USE_HTTPS STREQUAL "OpenSSL-Dynamic")
set(GIT_OPENSSL 1)
set(GIT_OPENSSL_DYNAMIC 1)
diff --git a/cmake/SelectHashes.cmake b/cmake/SelectHashes.cmake
index faf9e2ea31d..5c007e58749 100644
--- a/cmake/SelectHashes.cmake
+++ b/cmake/SelectHashes.cmake
@@ -13,6 +13,8 @@ if(USE_SHA1 STREQUAL ON)
elseif(USE_SHA1 STREQUAL "HTTPS")
if(USE_HTTPS STREQUAL "SecureTransport")
set(USE_SHA1 "CommonCrypto")
+ elseif(USE_HTTPS STREQUAL "Schannel")
+ set(USE_SHA1 "Win32")
elseif(USE_HTTPS STREQUAL "WinHTTP")
set(USE_SHA1 "Win32")
elseif(USE_HTTPS)
@@ -51,6 +53,8 @@ endif()
if(USE_SHA256 STREQUAL "HTTPS")
if(USE_HTTPS STREQUAL "SecureTransport")
set(USE_SHA256 "CommonCrypto")
+ elseif(USE_HTTPS STREQUAL "Schannel")
+ set(USE_SHA256 "Win32")
elseif(USE_HTTPS STREQUAL "WinHTTP")
set(USE_SHA256 "Win32")
elseif(USE_HTTPS)
diff --git a/cmake/SelectSSH.cmake b/cmake/SelectSSH.cmake
index 23dfc978521..079857f502b 100644
--- a/cmake/SelectSSH.cmake
+++ b/cmake/SelectSSH.cmake
@@ -1,6 +1,11 @@
-# Optional external dependency: libssh2
-if(USE_SSH)
+if(USE_SSH STREQUAL "exec")
+ set(GIT_SSH 1)
+ set(GIT_SSH_EXEC 1)
+
+ add_feature_info(SSH ON "using OpenSSH exec support")
+elseif(USE_SSH STREQUAL ON OR USE_SSH STREQUAL "libssh2")
find_pkglibraries(LIBSSH2 libssh2)
+
if(NOT LIBSSH2_FOUND)
find_package(LibSSH2)
set(LIBSSH2_INCLUDE_DIRS ${LIBSSH2_INCLUDE_DIR})
@@ -12,30 +17,28 @@ if(USE_SSH)
if(NOT LIBSSH2_FOUND)
message(FATAL_ERROR "LIBSSH2 not found. Set CMAKE_PREFIX_PATH if it is installed outside of the default search path.")
endif()
-endif()
-if(LIBSSH2_FOUND)
- set(GIT_SSH 1)
list(APPEND LIBGIT2_SYSTEM_INCLUDES ${LIBSSH2_INCLUDE_DIRS})
list(APPEND LIBGIT2_SYSTEM_LIBS ${LIBSSH2_LIBRARIES})
list(APPEND LIBGIT2_PC_LIBS ${LIBSSH2_LDFLAGS})
check_library_exists("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBSSH2_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
if(HAVE_LIBSSH2_MEMORY_CREDENTIALS)
- set(GIT_SSH_MEMORY_CREDENTIALS 1)
+ set(GIT_SSH_LIBSSH2_MEMORY_CREDENTIALS 1)
endif()
-else()
- message(STATUS "LIBSSH2 not found. Set CMAKE_PREFIX_PATH if it is installed outside of the default search path.")
-endif()
-if(WIN32 AND EMBED_SSH_PATH)
- file(GLOB SSH_SRC "${EMBED_SSH_PATH}/src/*.c")
- list(SORT SSH_SRC)
- list(APPEND LIBGIT2_DEPENDENCY_OBJECTS ${SSH_SRC})
+ if(WIN32 AND EMBED_SSH_PATH)
+ file(GLOB SSH_SRC "${EMBED_SSH_PATH}/src/*.c")
+ list(SORT SSH_SRC)
+ list(APPEND LIBGIT2_DEPENDENCY_OBJECTS ${SSH_SRC})
+
+ list(APPEND LIBGIT2_DEPENDENCY_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\"")
+ endif()
- list(APPEND LIBGIT2_DEPENDENCY_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)
+ set(GIT_SSH_LIBSSH2 1)
+ add_feature_info(SSH ON "using libssh2")
+else()
+ add_feature_info(SSH OFF "SSH transport support")
endif()
-
-add_feature_info(SSH GIT_SSH "SSH transport support")
diff --git a/cmake/SelectWinHTTP.cmake b/cmake/SelectWinHTTP.cmake
deleted file mode 100644
index 96e0bdbae54..00000000000
--- a/cmake/SelectWinHTTP.cmake
+++ /dev/null
@@ -1,17 +0,0 @@
-if(WIN32 AND USE_WINHTTP)
- set(GIT_WINHTTP 1)
-
- # Since MinGW does not come with headers or an import library for winhttp,
- # we have to include a private header and generate our own import library
- if(MINGW)
- add_subdirectory("${PROJECT_SOURCE_DIR}/deps/winhttp" "${PROJECT_BINARY_DIR}/deps/winhttp")
- list(APPEND LIBGIT2_SYSTEM_LIBS winhttp)
- list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/winhttp")
- else()
- list(APPEND LIBGIT2_SYSTEM_LIBS "winhttp")
- list(APPEND LIBGIT2_PC_LIBS "-lwinhttp")
- endif()
-
- list(APPEND LIBGIT2_SYSTEM_LIBS "rpcrt4" "crypt32" "ole32")
- list(APPEND LIBGIT2_PC_LIBS "-lrpcrt4" "-lcrypt32" "-lole32")
-endif()
diff --git a/cmake/SelectXdiff.cmake b/cmake/SelectXdiff.cmake
new file mode 100644
index 00000000000..9ab9f3f4f29
--- /dev/null
+++ b/cmake/SelectXdiff.cmake
@@ -0,0 +1,9 @@
+# Optional external dependency: xdiff
+if(USE_XDIFF STREQUAL "system")
+ message(FATAL_ERROR "external/system xdiff is not yet supported")
+else()
+ add_subdirectory("${PROJECT_SOURCE_DIR}/deps/xdiff" "${PROJECT_BINARY_DIR}/deps/xdiff")
+ list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/xdiff")
+ list(APPEND LIBGIT2_DEPENDENCY_OBJECTS "$")
+ add_feature_info(xdiff ON "xdiff support (bundled)")
+endif()
diff --git a/deps/http-parser/CMakeLists.txt b/deps/http-parser/CMakeLists.txt
deleted file mode 100644
index b9da2496f75..00000000000
--- a/deps/http-parser/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-file(GLOB SRC_HTTP "*.c" "*.h")
-list(SORT SRC_HTTP)
-
-add_library(http-parser OBJECT ${SRC_HTTP})
-
-enable_warnings(implicit-fallthrough=1)
diff --git a/deps/http-parser/COPYING b/deps/http-parser/COPYING
deleted file mode 100644
index 58010b38894..00000000000
--- a/deps/http-parser/COPYING
+++ /dev/null
@@ -1,23 +0,0 @@
-http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
-Igor Sysoev.
-
-Additional changes are licensed under the same terms as NGINX and
-copyright Joyent, Inc. and other Node contributors. All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to
-deal in the Software without restriction, including without limitation the
-rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE.
diff --git a/deps/http-parser/http_parser.c b/deps/http-parser/http_parser.c
deleted file mode 100644
index 1bcd330e5b5..00000000000
--- a/deps/http-parser/http_parser.c
+++ /dev/null
@@ -1,2182 +0,0 @@
-/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
- *
- * Additional changes are licensed under the same terms as NGINX and
- * copyright Joyent, Inc. and other Node contributors. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-#include "http_parser.h"
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifndef ULLONG_MAX
-# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
-#endif
-
-#ifndef MIN
-# define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
-#ifndef ARRAY_SIZE
-# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-#endif
-
-#ifndef BIT_AT
-# define BIT_AT(a, i) \
- (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
- (1 << ((unsigned int) (i) & 7))))
-#endif
-
-#ifndef ELEM_AT
-# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
-#endif
-
-#define SET_ERRNO(e) \
-do { \
- parser->http_errno = (e); \
-} while(0)
-
-
-/* Run the notify callback FOR, returning ER if it fails */
-#define CALLBACK_NOTIFY_(FOR, ER) \
-do { \
- assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
- \
- if (settings->on_##FOR) { \
- if (0 != settings->on_##FOR(parser)) { \
- SET_ERRNO(HPE_CB_##FOR); \
- } \
- \
- /* We either errored above or got paused; get out */ \
- if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \
- return (ER); \
- } \
- } \
-} while (0)
-
-/* Run the notify callback FOR and consume the current byte */
-#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1)
-
-/* Run the notify callback FOR and don't consume the current byte */
-#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data)
-
-/* Run data callback FOR with LEN bytes, returning ER if it fails */
-#define CALLBACK_DATA_(FOR, LEN, ER) \
-do { \
- assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
- \
- if (FOR##_mark) { \
- if (settings->on_##FOR) { \
- if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \
- SET_ERRNO(HPE_CB_##FOR); \
- } \
- \
- /* We either errored above or got paused; get out */ \
- if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \
- return (ER); \
- } \
- } \
- FOR##_mark = NULL; \
- } \
-} while (0)
-
-/* Run the data callback FOR and consume the current byte */
-#define CALLBACK_DATA(FOR) \
- CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
-
-/* Run the data callback FOR and don't consume the current byte */
-#define CALLBACK_DATA_NOADVANCE(FOR) \
- CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
-
-/* Set the mark FOR; non-destructive if mark is already set */
-#define MARK(FOR) \
-do { \
- if (!FOR##_mark) { \
- FOR##_mark = p; \
- } \
-} while (0)
-
-
-#define PROXY_CONNECTION "proxy-connection"
-#define CONNECTION "connection"
-#define CONTENT_LENGTH "content-length"
-#define TRANSFER_ENCODING "transfer-encoding"
-#define UPGRADE "upgrade"
-#define CHUNKED "chunked"
-#define KEEP_ALIVE "keep-alive"
-#define CLOSE "close"
-
-
-static const char *method_strings[] =
- {
-#define XX(num, name, string) #string,
- HTTP_METHOD_MAP(XX)
-#undef XX
- };
-
-
-/* Tokens as defined by rfc 2616. Also lowercases them.
- * token = 1*
- * separators = "(" | ")" | "<" | ">" | "@"
- * | "," | ";" | ":" | "\" | <">
- * | "/" | "[" | "]" | "?" | "="
- * | "{" | "}" | SP | HT
- */
-static const char tokens[256] = {
-/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
- 0, 0, 0, 0, 0, 0, 0, 0,
-/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
- 0, 0, 0, 0, 0, 0, 0, 0,
-/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
- 0, 0, 0, 0, 0, 0, 0, 0,
-/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
- 0, 0, 0, 0, 0, 0, 0, 0,
-/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
- 0, '!', 0, '#', '$', '%', '&', '\'',
-/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
- 0, 0, '*', '+', 0, '-', '.', 0,
-/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
- '0', '1', '2', '3', '4', '5', '6', '7',
-/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
- '8', '9', 0, 0, 0, 0, 0, 0,
-/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
- 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
-/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
- 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
-/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
- 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
-/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
- 'x', 'y', 'z', 0, 0, 0, '^', '_',
-/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
- '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
-/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
- 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
-/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
- 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
-/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
- 'x', 'y', 'z', 0, '|', 0, '~', 0 };
-
-
-static const int8_t unhex[256] =
- {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
- ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
- ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
- , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
- ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
- ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
- ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
- ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
- };
-
-
-#if HTTP_PARSER_STRICT
-# define T(v) 0
-#else
-# define T(v) v
-#endif
-
-
-static const uint8_t normal_url_char[32] = {
-/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
- 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
-/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
- 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
-/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
- 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
-/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
- 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
-/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
- 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
-/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
-/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
-/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
- 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
-
-#undef T
-
-enum state
- { s_dead = 1 /* important that this is > 0 */
-
- , s_start_req_or_res
- , s_res_or_resp_H
- , s_start_res
- , s_res_H
- , s_res_HT
- , s_res_HTT
- , s_res_HTTP
- , s_res_first_http_major
- , s_res_http_major
- , s_res_first_http_minor
- , s_res_http_minor
- , s_res_first_status_code
- , s_res_status_code
- , s_res_status
- , s_res_line_almost_done
-
- , s_start_req
-
- , s_req_method
- , s_req_spaces_before_url
- , s_req_schema
- , s_req_schema_slash
- , s_req_schema_slash_slash
- , s_req_server_start
- , s_req_server
- , s_req_server_with_at
- , s_req_path
- , s_req_query_string_start
- , s_req_query_string
- , s_req_fragment_start
- , s_req_fragment
- , s_req_http_start
- , s_req_http_H
- , s_req_http_HT
- , s_req_http_HTT
- , s_req_http_HTTP
- , s_req_first_http_major
- , s_req_http_major
- , s_req_first_http_minor
- , s_req_http_minor
- , s_req_line_almost_done
-
- , s_header_field_start
- , s_header_field
- , s_header_value_start
- , s_header_value
- , s_header_value_lws
-
- , s_header_almost_done
-
- , s_chunk_size_start
- , s_chunk_size
- , s_chunk_parameters
- , s_chunk_size_almost_done
-
- , s_headers_almost_done
- , s_headers_done
-
- /* Important: 's_headers_done' must be the last 'header' state. All
- * states beyond this must be 'body' states. It is used for overflow
- * checking. See the PARSING_HEADER() macro.
- */
-
- , s_chunk_data
- , s_chunk_data_almost_done
- , s_chunk_data_done
-
- , s_body_identity
- , s_body_identity_eof
-
- , s_message_done
- };
-
-
-#define PARSING_HEADER(state) (state <= s_headers_done)
-
-
-enum header_states
- { h_general = 0
- , h_C
- , h_CO
- , h_CON
-
- , h_matching_connection
- , h_matching_proxy_connection
- , h_matching_content_length
- , h_matching_transfer_encoding
- , h_matching_upgrade
-
- , h_connection
- , h_content_length
- , h_transfer_encoding
- , h_upgrade
-
- , h_matching_transfer_encoding_chunked
- , h_matching_connection_keep_alive
- , h_matching_connection_close
-
- , h_transfer_encoding_chunked
- , h_connection_keep_alive
- , h_connection_close
- };
-
-enum http_host_state
- {
- s_http_host_dead = 1
- , s_http_userinfo_start
- , s_http_userinfo
- , s_http_host_start
- , s_http_host_v6_start
- , s_http_host
- , s_http_host_v6
- , s_http_host_v6_end
- , s_http_host_port_start
- , s_http_host_port
-};
-
-/* Macros for character classes; depends on strict-mode */
-#define CR '\r'
-#define LF '\n'
-#define LOWER(c) (unsigned char)(c | 0x20)
-#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
-#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
-#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
-#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
-#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
- (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
- (c) == ')')
-#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
- (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
- (c) == '$' || (c) == ',')
-
-#if HTTP_PARSER_STRICT
-#define TOKEN(c) (tokens[(unsigned char)c])
-#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
-#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
-#else
-#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
-#define IS_URL_CHAR(c) \
- (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
-#define IS_HOST_CHAR(c) \
- (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
-#endif
-
-
-#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
-
-
-#if HTTP_PARSER_STRICT
-# define STRICT_CHECK(cond) \
-do { \
- if (cond) { \
- SET_ERRNO(HPE_STRICT); \
- goto error; \
- } \
-} while (0)
-# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
-#else
-# define STRICT_CHECK(cond)
-# define NEW_MESSAGE() start_state
-#endif
-
-
-/* Map errno values to strings for human-readable output */
-#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
-static struct {
- const char *name;
- const char *description;
-} http_strerror_tab[] = {
- HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
-};
-#undef HTTP_STRERROR_GEN
-
-int http_message_needs_eof(const http_parser *parser);
-
-/* Our URL parser.
- *
- * This is designed to be shared by http_parser_execute() for URL validation,
- * hence it has a state transition + byte-for-byte interface. In addition, it
- * is meant to be embedded in http_parser_parse_url(), which does the dirty
- * work of turning state transitions URL components for its API.
- *
- * This function should only be invoked with non-space characters. It is
- * assumed that the caller cares about (and can detect) the transition between
- * URL and non-URL states by looking for these.
- */
-static enum state
-parse_url_char(enum state s, const char ch)
-{
- if (ch == ' ' || ch == '\r' || ch == '\n') {
- return s_dead;
- }
-
-#if HTTP_PARSER_STRICT
- if (ch == '\t' || ch == '\f') {
- return s_dead;
- }
-#endif
-
- switch (s) {
- case s_req_spaces_before_url:
- /* Proxied requests are followed by scheme of an absolute URI (alpha).
- * All methods except CONNECT are followed by '/' or '*'.
- */
-
- if (ch == '/' || ch == '*') {
- return s_req_path;
- }
-
- /* The schema must start with an alpha character. After that, it may
- * consist of digits, '+', '-' or '.', followed by a ':'.
- */
- if (IS_ALPHA(ch)) {
- return s_req_schema;
- }
-
- break;
-
- case s_req_schema:
- if (IS_ALPHANUM(ch) || ch == '+' || ch == '-' || ch == '.') {
- return s;
- }
-
- if (ch == ':') {
- return s_req_schema_slash;
- }
-
- break;
-
- case s_req_schema_slash:
- if (ch == '/') {
- return s_req_schema_slash_slash;
- }
-
- break;
-
- case s_req_schema_slash_slash:
- if (ch == '/') {
- return s_req_server_start;
- }
-
- break;
-
- case s_req_server_with_at:
- if (ch == '@') {
- return s_dead;
- }
-
- /* FALLTHROUGH */
- case s_req_server_start:
- case s_req_server:
- if (ch == '/') {
- return s_req_path;
- }
-
- if (ch == '?') {
- return s_req_query_string_start;
- }
-
- if (ch == '@') {
- return s_req_server_with_at;
- }
-
- if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
- return s_req_server;
- }
-
- break;
-
- case s_req_path:
- if (IS_URL_CHAR(ch)) {
- return s;
- }
-
- switch (ch) {
- case '?':
- return s_req_query_string_start;
-
- case '#':
- return s_req_fragment_start;
- }
-
- break;
-
- case s_req_query_string_start:
- case s_req_query_string:
- if (IS_URL_CHAR(ch)) {
- return s_req_query_string;
- }
-
- switch (ch) {
- case '?':
- /* allow extra '?' in query string */
- return s_req_query_string;
-
- case '#':
- return s_req_fragment_start;
- }
-
- break;
-
- case s_req_fragment_start:
- if (IS_URL_CHAR(ch)) {
- return s_req_fragment;
- }
-
- switch (ch) {
- case '?':
- return s_req_fragment;
-
- case '#':
- return s;
- }
-
- break;
-
- case s_req_fragment:
- if (IS_URL_CHAR(ch)) {
- return s;
- }
-
- switch (ch) {
- case '?':
- case '#':
- return s;
- }
-
- break;
-
- default:
- break;
- }
-
- /* We should never fall out of the switch above unless there's an error */
- return s_dead;
-}
-
-size_t http_parser_execute (http_parser *parser,
- const http_parser_settings *settings,
- const char *data,
- size_t len)
-{
- char c, ch;
- int8_t unhex_val;
- const char *p = data;
- const char *header_field_mark = 0;
- const char *header_value_mark = 0;
- const char *url_mark = 0;
- const char *body_mark = 0;
-
- /* We're in an error state. Don't bother doing anything. */
- if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
- return 0;
- }
-
- if (len == 0) {
- switch (parser->state) {
- case s_body_identity_eof:
- /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
- * we got paused.
- */
- CALLBACK_NOTIFY_NOADVANCE(message_complete);
- return 0;
-
- case s_dead:
- case s_start_req_or_res:
- case s_start_res:
- case s_start_req:
- return 0;
-
- default:
- SET_ERRNO(HPE_INVALID_EOF_STATE);
- return 1;
- }
- }
-
-
- if (parser->state == s_header_field)
- header_field_mark = data;
- if (parser->state == s_header_value)
- header_value_mark = data;
- switch (parser->state) {
- case s_req_path:
- case s_req_schema:
- case s_req_schema_slash:
- case s_req_schema_slash_slash:
- case s_req_server_start:
- case s_req_server:
- case s_req_server_with_at:
- case s_req_query_string_start:
- case s_req_query_string:
- case s_req_fragment_start:
- case s_req_fragment:
- url_mark = data;
- break;
- }
-
- for (p=data; p != data + len; p++) {
- ch = *p;
-
- if (PARSING_HEADER(parser->state)) {
- ++parser->nread;
- /* Buffer overflow attack */
- if (parser->nread > HTTP_MAX_HEADER_SIZE) {
- SET_ERRNO(HPE_HEADER_OVERFLOW);
- goto error;
- }
- }
-
- reexecute_byte:
- switch (parser->state) {
-
- case s_dead:
- /* this state is used after a 'Connection: close' message
- * the parser will error out if it reads another message
- */
- if (ch == CR || ch == LF)
- break;
-
- SET_ERRNO(HPE_CLOSED_CONNECTION);
- goto error;
-
- case s_start_req_or_res:
- {
- if (ch == CR || ch == LF)
- break;
- parser->flags = 0;
- parser->content_length = ULLONG_MAX;
-
- if (ch == 'H') {
- parser->state = s_res_or_resp_H;
-
- CALLBACK_NOTIFY(message_begin);
- } else {
- parser->type = HTTP_REQUEST;
- parser->state = s_start_req;
- goto reexecute_byte;
- }
-
- break;
- }
-
- case s_res_or_resp_H:
- if (ch == 'T') {
- parser->type = HTTP_RESPONSE;
- parser->state = s_res_HT;
- } else {
- if (ch != 'E') {
- SET_ERRNO(HPE_INVALID_CONSTANT);
- goto error;
- }
-
- parser->type = HTTP_REQUEST;
- parser->method = HTTP_HEAD;
- parser->index = 2;
- parser->state = s_req_method;
- }
- break;
-
- case s_start_res:
- {
- parser->flags = 0;
- parser->content_length = ULLONG_MAX;
-
- switch (ch) {
- case 'H':
- parser->state = s_res_H;
- break;
-
- case CR:
- case LF:
- break;
-
- default:
- SET_ERRNO(HPE_INVALID_CONSTANT);
- goto error;
- }
-
- CALLBACK_NOTIFY(message_begin);
- break;
- }
-
- case s_res_H:
- STRICT_CHECK(ch != 'T');
- parser->state = s_res_HT;
- break;
-
- case s_res_HT:
- STRICT_CHECK(ch != 'T');
- parser->state = s_res_HTT;
- break;
-
- case s_res_HTT:
- STRICT_CHECK(ch != 'P');
- parser->state = s_res_HTTP;
- break;
-
- case s_res_HTTP:
- STRICT_CHECK(ch != '/');
- parser->state = s_res_first_http_major;
- break;
-
- case s_res_first_http_major:
- if (ch < '0' || ch > '9') {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- parser->http_major = ch - '0';
- parser->state = s_res_http_major;
- break;
-
- /* major HTTP version or dot */
- case s_res_http_major:
- {
- if (ch == '.') {
- parser->state = s_res_first_http_minor;
- break;
- }
-
- if (!IS_NUM(ch)) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- parser->http_major *= 10;
- parser->http_major += ch - '0';
-
- if (parser->http_major > 999) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- break;
- }
-
- /* first digit of minor HTTP version */
- case s_res_first_http_minor:
- if (!IS_NUM(ch)) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- parser->http_minor = ch - '0';
- parser->state = s_res_http_minor;
- break;
-
- /* minor HTTP version or end of request line */
- case s_res_http_minor:
- {
- if (ch == ' ') {
- parser->state = s_res_first_status_code;
- break;
- }
-
- if (!IS_NUM(ch)) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- parser->http_minor *= 10;
- parser->http_minor += ch - '0';
-
- if (parser->http_minor > 999) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- break;
- }
-
- case s_res_first_status_code:
- {
- if (!IS_NUM(ch)) {
- if (ch == ' ') {
- break;
- }
-
- SET_ERRNO(HPE_INVALID_STATUS);
- goto error;
- }
- parser->status_code = ch - '0';
- parser->state = s_res_status_code;
- break;
- }
-
- case s_res_status_code:
- {
- if (!IS_NUM(ch)) {
- switch (ch) {
- case ' ':
- parser->state = s_res_status;
- break;
- case CR:
- parser->state = s_res_line_almost_done;
- break;
- case LF:
- parser->state = s_header_field_start;
- break;
- default:
- SET_ERRNO(HPE_INVALID_STATUS);
- goto error;
- }
- break;
- }
-
- parser->status_code *= 10;
- parser->status_code += ch - '0';
-
- if (parser->status_code > 999) {
- SET_ERRNO(HPE_INVALID_STATUS);
- goto error;
- }
-
- break;
- }
-
- case s_res_status:
- /* the human readable status. e.g. "NOT FOUND"
- * we are not humans so just ignore this */
- if (ch == CR) {
- parser->state = s_res_line_almost_done;
- break;
- }
-
- if (ch == LF) {
- parser->state = s_header_field_start;
- break;
- }
- break;
-
- case s_res_line_almost_done:
- STRICT_CHECK(ch != LF);
- parser->state = s_header_field_start;
- break;
-
- case s_start_req:
- {
- if (ch == CR || ch == LF)
- break;
- parser->flags = 0;
- parser->content_length = ULLONG_MAX;
-
- if (!IS_ALPHA(ch)) {
- SET_ERRNO(HPE_INVALID_METHOD);
- goto error;
- }
-
- parser->method = (enum http_method) 0;
- parser->index = 1;
- switch (ch) {
- case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
- case 'D': parser->method = HTTP_DELETE; break;
- case 'G': parser->method = HTTP_GET; break;
- case 'H': parser->method = HTTP_HEAD; break;
- case 'L': parser->method = HTTP_LOCK; break;
- case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
- case 'N': parser->method = HTTP_NOTIFY; break;
- case 'O': parser->method = HTTP_OPTIONS; break;
- case 'P': parser->method = HTTP_POST;
- /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
- break;
- case 'R': parser->method = HTTP_REPORT; break;
- case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
- case 'T': parser->method = HTTP_TRACE; break;
- case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
- default:
- SET_ERRNO(HPE_INVALID_METHOD);
- goto error;
- }
- parser->state = s_req_method;
-
- CALLBACK_NOTIFY(message_begin);
-
- break;
- }
-
- case s_req_method:
- {
- const char *matcher;
- if (ch == '\0') {
- SET_ERRNO(HPE_INVALID_METHOD);
- goto error;
- }
-
- matcher = method_strings[parser->method];
- if (ch == ' ' && matcher[parser->index] == '\0') {
- parser->state = s_req_spaces_before_url;
- } else if (ch == matcher[parser->index]) {
- ; /* nada */
- } else if (parser->method == HTTP_CONNECT) {
- if (parser->index == 1 && ch == 'H') {
- parser->method = HTTP_CHECKOUT;
- } else if (parser->index == 2 && ch == 'P') {
- parser->method = HTTP_COPY;
- } else {
- goto error;
- }
- } else if (parser->method == HTTP_MKCOL) {
- if (parser->index == 1 && ch == 'O') {
- parser->method = HTTP_MOVE;
- } else if (parser->index == 1 && ch == 'E') {
- parser->method = HTTP_MERGE;
- } else if (parser->index == 1 && ch == '-') {
- parser->method = HTTP_MSEARCH;
- } else if (parser->index == 2 && ch == 'A') {
- parser->method = HTTP_MKACTIVITY;
- } else {
- goto error;
- }
- } else if (parser->method == HTTP_SUBSCRIBE) {
- if (parser->index == 1 && ch == 'E') {
- parser->method = HTTP_SEARCH;
- } else {
- goto error;
- }
- } else if (parser->index == 1 && parser->method == HTTP_POST) {
- if (ch == 'R') {
- parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
- } else if (ch == 'U') {
- parser->method = HTTP_PUT; /* or HTTP_PURGE */
- } else if (ch == 'A') {
- parser->method = HTTP_PATCH;
- } else {
- goto error;
- }
- } else if (parser->index == 2) {
- if (parser->method == HTTP_PUT) {
- if (ch == 'R') parser->method = HTTP_PURGE;
- } else if (parser->method == HTTP_UNLOCK) {
- if (ch == 'S') parser->method = HTTP_UNSUBSCRIBE;
- }
- } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
- parser->method = HTTP_PROPPATCH;
- } else {
- SET_ERRNO(HPE_INVALID_METHOD);
- goto error;
- }
-
- ++parser->index;
- break;
- }
-
- case s_req_spaces_before_url:
- {
- if (ch == ' ') break;
-
- MARK(url);
- if (parser->method == HTTP_CONNECT) {
- parser->state = s_req_server_start;
- }
-
- parser->state = parse_url_char((enum state)parser->state, ch);
- if (parser->state == s_dead) {
- SET_ERRNO(HPE_INVALID_URL);
- goto error;
- }
-
- break;
- }
-
- case s_req_schema:
- case s_req_schema_slash:
- case s_req_schema_slash_slash:
- case s_req_server_start:
- {
- switch (ch) {
- /* No whitespace allowed here */
- case ' ':
- case CR:
- case LF:
- SET_ERRNO(HPE_INVALID_URL);
- goto error;
- default:
- parser->state = parse_url_char((enum state)parser->state, ch);
- if (parser->state == s_dead) {
- SET_ERRNO(HPE_INVALID_URL);
- goto error;
- }
- }
-
- break;
- }
-
- case s_req_server:
- case s_req_server_with_at:
- case s_req_path:
- case s_req_query_string_start:
- case s_req_query_string:
- case s_req_fragment_start:
- case s_req_fragment:
- {
- switch (ch) {
- case ' ':
- parser->state = s_req_http_start;
- CALLBACK_DATA(url);
- break;
- case CR:
- case LF:
- parser->http_major = 0;
- parser->http_minor = 9;
- parser->state = (ch == CR) ?
- s_req_line_almost_done :
- s_header_field_start;
- CALLBACK_DATA(url);
- break;
- default:
- parser->state = parse_url_char((enum state)parser->state, ch);
- if (parser->state == s_dead) {
- SET_ERRNO(HPE_INVALID_URL);
- goto error;
- }
- }
- break;
- }
-
- case s_req_http_start:
- switch (ch) {
- case 'H':
- parser->state = s_req_http_H;
- break;
- case ' ':
- break;
- default:
- SET_ERRNO(HPE_INVALID_CONSTANT);
- goto error;
- }
- break;
-
- case s_req_http_H:
- STRICT_CHECK(ch != 'T');
- parser->state = s_req_http_HT;
- break;
-
- case s_req_http_HT:
- STRICT_CHECK(ch != 'T');
- parser->state = s_req_http_HTT;
- break;
-
- case s_req_http_HTT:
- STRICT_CHECK(ch != 'P');
- parser->state = s_req_http_HTTP;
- break;
-
- case s_req_http_HTTP:
- STRICT_CHECK(ch != '/');
- parser->state = s_req_first_http_major;
- break;
-
- /* first digit of major HTTP version */
- case s_req_first_http_major:
- if (ch < '1' || ch > '9') {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- parser->http_major = ch - '0';
- parser->state = s_req_http_major;
- break;
-
- /* major HTTP version or dot */
- case s_req_http_major:
- {
- if (ch == '.') {
- parser->state = s_req_first_http_minor;
- break;
- }
-
- if (!IS_NUM(ch)) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- parser->http_major *= 10;
- parser->http_major += ch - '0';
-
- if (parser->http_major > 999) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- break;
- }
-
- /* first digit of minor HTTP version */
- case s_req_first_http_minor:
- if (!IS_NUM(ch)) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- parser->http_minor = ch - '0';
- parser->state = s_req_http_minor;
- break;
-
- /* minor HTTP version or end of request line */
- case s_req_http_minor:
- {
- if (ch == CR) {
- parser->state = s_req_line_almost_done;
- break;
- }
-
- if (ch == LF) {
- parser->state = s_header_field_start;
- break;
- }
-
- /* XXX allow spaces after digit? */
-
- if (!IS_NUM(ch)) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- parser->http_minor *= 10;
- parser->http_minor += ch - '0';
-
- if (parser->http_minor > 999) {
- SET_ERRNO(HPE_INVALID_VERSION);
- goto error;
- }
-
- break;
- }
-
- /* end of request line */
- case s_req_line_almost_done:
- {
- if (ch != LF) {
- SET_ERRNO(HPE_LF_EXPECTED);
- goto error;
- }
-
- parser->state = s_header_field_start;
- break;
- }
-
- case s_header_field_start:
- {
- if (ch == CR) {
- parser->state = s_headers_almost_done;
- break;
- }
-
- if (ch == LF) {
- /* they might be just sending \n instead of \r\n so this would be
- * the second \n to denote the end of headers*/
- parser->state = s_headers_almost_done;
- goto reexecute_byte;
- }
-
- c = TOKEN(ch);
-
- if (!c) {
- SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
- goto error;
- }
-
- MARK(header_field);
-
- parser->index = 0;
- parser->state = s_header_field;
-
- switch (c) {
- case 'c':
- parser->header_state = h_C;
- break;
-
- case 'p':
- parser->header_state = h_matching_proxy_connection;
- break;
-
- case 't':
- parser->header_state = h_matching_transfer_encoding;
- break;
-
- case 'u':
- parser->header_state = h_matching_upgrade;
- break;
-
- default:
- parser->header_state = h_general;
- break;
- }
- break;
- }
-
- case s_header_field:
- {
- c = TOKEN(ch);
-
- if (c) {
- switch (parser->header_state) {
- case h_general:
- break;
-
- case h_C:
- parser->index++;
- parser->header_state = (c == 'o' ? h_CO : h_general);
- break;
-
- case h_CO:
- parser->index++;
- parser->header_state = (c == 'n' ? h_CON : h_general);
- break;
-
- case h_CON:
- parser->index++;
- switch (c) {
- case 'n':
- parser->header_state = h_matching_connection;
- break;
- case 't':
- parser->header_state = h_matching_content_length;
- break;
- default:
- parser->header_state = h_general;
- break;
- }
- break;
-
- /* connection */
-
- case h_matching_connection:
- parser->index++;
- if (parser->index > sizeof(CONNECTION)-1
- || c != CONNECTION[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(CONNECTION)-2) {
- parser->header_state = h_connection;
- }
- break;
-
- /* proxy-connection */
-
- case h_matching_proxy_connection:
- parser->index++;
- if (parser->index > sizeof(PROXY_CONNECTION)-1
- || c != PROXY_CONNECTION[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
- parser->header_state = h_connection;
- }
- break;
-
- /* content-length */
-
- case h_matching_content_length:
- parser->index++;
- if (parser->index > sizeof(CONTENT_LENGTH)-1
- || c != CONTENT_LENGTH[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
- parser->header_state = h_content_length;
- }
- break;
-
- /* transfer-encoding */
-
- case h_matching_transfer_encoding:
- parser->index++;
- if (parser->index > sizeof(TRANSFER_ENCODING)-1
- || c != TRANSFER_ENCODING[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
- parser->header_state = h_transfer_encoding;
- }
- break;
-
- /* upgrade */
-
- case h_matching_upgrade:
- parser->index++;
- if (parser->index > sizeof(UPGRADE)-1
- || c != UPGRADE[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(UPGRADE)-2) {
- parser->header_state = h_upgrade;
- }
- break;
-
- case h_connection:
- case h_content_length:
- case h_transfer_encoding:
- case h_upgrade:
- if (ch != ' ') parser->header_state = h_general;
- break;
-
- default:
- assert(0 && "Unknown header_state");
- break;
- }
- break;
- }
-
- if (ch == ':') {
- parser->state = s_header_value_start;
- CALLBACK_DATA(header_field);
- break;
- }
-
- if (ch == CR) {
- parser->state = s_header_almost_done;
- CALLBACK_DATA(header_field);
- break;
- }
-
- if (ch == LF) {
- parser->state = s_header_field_start;
- CALLBACK_DATA(header_field);
- break;
- }
-
- SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
- goto error;
- }
-
- case s_header_value_start:
- {
- if (ch == ' ' || ch == '\t') break;
-
- MARK(header_value);
-
- parser->state = s_header_value;
- parser->index = 0;
-
- if (ch == CR) {
- parser->header_state = h_general;
- parser->state = s_header_almost_done;
- CALLBACK_DATA(header_value);
- break;
- }
-
- if (ch == LF) {
- parser->state = s_header_field_start;
- CALLBACK_DATA(header_value);
- break;
- }
-
- c = LOWER(ch);
-
- switch (parser->header_state) {
- case h_upgrade:
- parser->flags |= F_UPGRADE;
- parser->header_state = h_general;
- break;
-
- case h_transfer_encoding:
- /* looking for 'Transfer-Encoding: chunked' */
- if ('c' == c) {
- parser->header_state = h_matching_transfer_encoding_chunked;
- } else {
- parser->header_state = h_general;
- }
- break;
-
- case h_content_length:
- if (!IS_NUM(ch)) {
- SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
- goto error;
- }
-
- parser->content_length = ch - '0';
- break;
-
- case h_connection:
- /* looking for 'Connection: keep-alive' */
- if (c == 'k') {
- parser->header_state = h_matching_connection_keep_alive;
- /* looking for 'Connection: close' */
- } else if (c == 'c') {
- parser->header_state = h_matching_connection_close;
- } else {
- parser->header_state = h_general;
- }
- break;
-
- default:
- parser->header_state = h_general;
- break;
- }
- break;
- }
-
- case s_header_value:
- {
-
- if (ch == CR) {
- parser->state = s_header_almost_done;
- CALLBACK_DATA(header_value);
- break;
- }
-
- if (ch == LF) {
- parser->state = s_header_almost_done;
- CALLBACK_DATA_NOADVANCE(header_value);
- goto reexecute_byte;
- }
-
- c = LOWER(ch);
-
- switch (parser->header_state) {
- case h_general:
- break;
-
- case h_connection:
- case h_transfer_encoding:
- assert(0 && "Shouldn't get here.");
- break;
-
- case h_content_length:
- {
- uint64_t t;
-
- if (ch == ' ') break;
-
- if (!IS_NUM(ch)) {
- SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
- goto error;
- }
-
- t = parser->content_length;
- t *= 10;
- t += ch - '0';
-
- /* Overflow? */
- if (t < parser->content_length || t == ULLONG_MAX) {
- SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
- goto error;
- }
-
- parser->content_length = t;
- break;
- }
-
- /* Transfer-Encoding: chunked */
- case h_matching_transfer_encoding_chunked:
- parser->index++;
- if (parser->index > sizeof(CHUNKED)-1
- || c != CHUNKED[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(CHUNKED)-2) {
- parser->header_state = h_transfer_encoding_chunked;
- }
- break;
-
- /* looking for 'Connection: keep-alive' */
- case h_matching_connection_keep_alive:
- parser->index++;
- if (parser->index > sizeof(KEEP_ALIVE)-1
- || c != KEEP_ALIVE[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
- parser->header_state = h_connection_keep_alive;
- }
- break;
-
- /* looking for 'Connection: close' */
- case h_matching_connection_close:
- parser->index++;
- if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(CLOSE)-2) {
- parser->header_state = h_connection_close;
- }
- break;
-
- case h_transfer_encoding_chunked:
- case h_connection_keep_alive:
- case h_connection_close:
- if (ch != ' ') parser->header_state = h_general;
- break;
-
- default:
- parser->state = s_header_value;
- parser->header_state = h_general;
- break;
- }
- break;
- }
-
- case s_header_almost_done:
- {
- STRICT_CHECK(ch != LF);
-
- parser->state = s_header_value_lws;
-
- switch (parser->header_state) {
- case h_connection_keep_alive:
- parser->flags |= F_CONNECTION_KEEP_ALIVE;
- break;
- case h_connection_close:
- parser->flags |= F_CONNECTION_CLOSE;
- break;
- case h_transfer_encoding_chunked:
- parser->flags |= F_CHUNKED;
- break;
- default:
- break;
- }
-
- break;
- }
-
- case s_header_value_lws:
- {
- if (ch == ' ' || ch == '\t')
- parser->state = s_header_value_start;
- else
- {
- parser->state = s_header_field_start;
- goto reexecute_byte;
- }
- break;
- }
-
- case s_headers_almost_done:
- {
- STRICT_CHECK(ch != LF);
-
- if (parser->flags & F_TRAILING) {
- /* End of a chunked request */
- parser->state = NEW_MESSAGE();
- CALLBACK_NOTIFY(message_complete);
- break;
- }
-
- parser->state = s_headers_done;
-
- /* Set this here so that on_headers_complete() callbacks can see it */
- parser->upgrade =
- (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT);
-
- /* Here we call the headers_complete callback. This is somewhat
- * different than other callbacks because if the user returns 1, we
- * will interpret that as saying that this message has no body. This
- * is needed for the annoying case of recieving a response to a HEAD
- * request.
- *
- * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
- * we have to simulate it by handling a change in errno below.
- */
- if (settings->on_headers_complete) {
- switch (settings->on_headers_complete(parser)) {
- case 0:
- break;
-
- case 1:
- parser->flags |= F_SKIPBODY;
- break;
-
- default:
- SET_ERRNO(HPE_CB_headers_complete);
- return p - data; /* Error */
- }
- }
-
- if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
- return p - data;
- }
-
- goto reexecute_byte;
- }
-
- case s_headers_done:
- {
- STRICT_CHECK(ch != LF);
-
- parser->nread = 0;
-
- /* Exit, the rest of the connect is in a different protocol. */
- if (parser->upgrade) {
- parser->state = NEW_MESSAGE();
- CALLBACK_NOTIFY(message_complete);
- return (p - data) + 1;
- }
-
- if (parser->flags & F_SKIPBODY) {
- parser->state = NEW_MESSAGE();
- CALLBACK_NOTIFY(message_complete);
- } else if (parser->flags & F_CHUNKED) {
- /* chunked encoding - ignore Content-Length header */
- parser->state = s_chunk_size_start;
- } else {
- if (parser->content_length == 0) {
- /* Content-Length header given but zero: Content-Length: 0\r\n */
- parser->state = NEW_MESSAGE();
- CALLBACK_NOTIFY(message_complete);
- } else if (parser->content_length != ULLONG_MAX) {
- /* Content-Length header given and non-zero */
- parser->state = s_body_identity;
- } else {
- if (parser->type == HTTP_REQUEST ||
- !http_message_needs_eof(parser)) {
- /* Assume content-length 0 - read the next */
- parser->state = NEW_MESSAGE();
- CALLBACK_NOTIFY(message_complete);
- } else {
- /* Read body until EOF */
- parser->state = s_body_identity_eof;
- }
- }
- }
-
- break;
- }
-
- case s_body_identity:
- {
- uint64_t to_read = MIN(parser->content_length,
- (uint64_t) ((data + len) - p));
-
- assert(parser->content_length != 0
- && parser->content_length != ULLONG_MAX);
-
- /* The difference between advancing content_length and p is because
- * the latter will automaticaly advance on the next loop iteration.
- * Further, if content_length ends up at 0, we want to see the last
- * byte again for our message complete callback.
- */
- MARK(body);
- parser->content_length -= to_read;
- p += to_read - 1;
-
- if (parser->content_length == 0) {
- parser->state = s_message_done;
-
- /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
- *
- * The alternative to doing this is to wait for the next byte to
- * trigger the data callback, just as in every other case. The
- * problem with this is that this makes it difficult for the test
- * harness to distinguish between complete-on-EOF and
- * complete-on-length. It's not clear that this distinction is
- * important for applications, but let's keep it for now.
- */
- CALLBACK_DATA_(body, p - body_mark + 1, p - data);
- goto reexecute_byte;
- }
-
- break;
- }
-
- /* read until EOF */
- case s_body_identity_eof:
- MARK(body);
- p = data + len - 1;
-
- break;
-
- case s_message_done:
- parser->state = NEW_MESSAGE();
- CALLBACK_NOTIFY(message_complete);
- break;
-
- case s_chunk_size_start:
- {
- assert(parser->nread == 1);
- assert(parser->flags & F_CHUNKED);
-
- unhex_val = unhex[(unsigned char)ch];
- if (unhex_val == -1) {
- SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
- goto error;
- }
-
- parser->content_length = unhex_val;
- parser->state = s_chunk_size;
- break;
- }
-
- case s_chunk_size:
- {
- uint64_t t;
-
- assert(parser->flags & F_CHUNKED);
-
- if (ch == CR) {
- parser->state = s_chunk_size_almost_done;
- break;
- }
-
- unhex_val = unhex[(unsigned char)ch];
-
- if (unhex_val == -1) {
- if (ch == ';' || ch == ' ') {
- parser->state = s_chunk_parameters;
- break;
- }
-
- SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
- goto error;
- }
-
- t = parser->content_length;
- t *= 16;
- t += unhex_val;
-
- /* Overflow? */
- if (t < parser->content_length || t == ULLONG_MAX) {
- SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
- goto error;
- }
-
- parser->content_length = t;
- break;
- }
-
- case s_chunk_parameters:
- {
- assert(parser->flags & F_CHUNKED);
- /* just ignore this. TODO check for overflow */
- if (ch == CR) {
- parser->state = s_chunk_size_almost_done;
- break;
- }
- break;
- }
-
- case s_chunk_size_almost_done:
- {
- assert(parser->flags & F_CHUNKED);
- STRICT_CHECK(ch != LF);
-
- parser->nread = 0;
-
- if (parser->content_length == 0) {
- parser->flags |= F_TRAILING;
- parser->state = s_header_field_start;
- } else {
- parser->state = s_chunk_data;
- }
- break;
- }
-
- case s_chunk_data:
- {
- uint64_t to_read = MIN(parser->content_length,
- (uint64_t) ((data + len) - p));
-
- assert(parser->flags & F_CHUNKED);
- assert(parser->content_length != 0
- && parser->content_length != ULLONG_MAX);
-
- /* See the explanation in s_body_identity for why the content
- * length and data pointers are managed this way.
- */
- MARK(body);
- parser->content_length -= to_read;
- p += to_read - 1;
-
- if (parser->content_length == 0) {
- parser->state = s_chunk_data_almost_done;
- }
-
- break;
- }
-
- case s_chunk_data_almost_done:
- assert(parser->flags & F_CHUNKED);
- assert(parser->content_length == 0);
- STRICT_CHECK(ch != CR);
- parser->state = s_chunk_data_done;
- CALLBACK_DATA(body);
- break;
-
- case s_chunk_data_done:
- assert(parser->flags & F_CHUNKED);
- STRICT_CHECK(ch != LF);
- parser->nread = 0;
- parser->state = s_chunk_size_start;
- break;
-
- default:
- assert(0 && "unhandled state");
- SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
- goto error;
- }
- }
-
- /* Run callbacks for any marks that we have leftover after we ran our of
- * bytes. There should be at most one of these set, so it's OK to invoke
- * them in series (unset marks will not result in callbacks).
- *
- * We use the NOADVANCE() variety of callbacks here because 'p' has already
- * overflowed 'data' and this allows us to correct for the off-by-one that
- * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
- * value that's in-bounds).
- */
-
- assert(((header_field_mark ? 1 : 0) +
- (header_value_mark ? 1 : 0) +
- (url_mark ? 1 : 0) +
- (body_mark ? 1 : 0)) <= 1);
-
- CALLBACK_DATA_NOADVANCE(header_field);
- CALLBACK_DATA_NOADVANCE(header_value);
- CALLBACK_DATA_NOADVANCE(url);
- CALLBACK_DATA_NOADVANCE(body);
-
- return len;
-
-error:
- if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
- SET_ERRNO(HPE_UNKNOWN);
- }
-
- return (p - data);
-}
-
-
-/* Does the parser need to see an EOF to find the end of the message? */
-int
-http_message_needs_eof (const http_parser *parser)
-{
- if (parser->type == HTTP_REQUEST) {
- return 0;
- }
-
- /* See RFC 2616 section 4.4 */
- if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
- parser->status_code == 204 || /* No Content */
- parser->status_code == 304 || /* Not Modified */
- parser->flags & F_SKIPBODY) { /* response to a HEAD request */
- return 0;
- }
-
- if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
- return 0;
- }
-
- return 1;
-}
-
-
-int
-http_should_keep_alive (const http_parser *parser)
-{
- if (parser->http_major > 0 && parser->http_minor > 0) {
- /* HTTP/1.1 */
- if (parser->flags & F_CONNECTION_CLOSE) {
- return 0;
- }
- } else {
- /* HTTP/1.0 or earlier */
- if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
- return 0;
- }
- }
-
- return !http_message_needs_eof(parser);
-}
-
-
-const char *
-http_method_str (enum http_method m)
-{
- return ELEM_AT(method_strings, m, "");
-}
-
-
-void
-http_parser_init (http_parser *parser, enum http_parser_type t)
-{
- void *data = parser->data; /* preserve application data */
- memset(parser, 0, sizeof(*parser));
- parser->data = data;
- parser->type = t;
- parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
- parser->http_errno = HPE_OK;
-}
-
-const char *
-http_errno_name(enum http_errno err) {
- assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
- return http_strerror_tab[err].name;
-}
-
-const char *
-http_errno_description(enum http_errno err) {
- assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
- return http_strerror_tab[err].description;
-}
-
-static enum http_host_state
-http_parse_host_char(enum http_host_state s, const char ch) {
- switch(s) {
- case s_http_userinfo:
- case s_http_userinfo_start:
- if (ch == '@') {
- return s_http_host_start;
- }
-
- if (IS_USERINFO_CHAR(ch)) {
- return s_http_userinfo;
- }
- break;
-
- case s_http_host_start:
- if (ch == '[') {
- return s_http_host_v6_start;
- }
-
- if (IS_HOST_CHAR(ch)) {
- return s_http_host;
- }
-
- break;
-
- case s_http_host:
- if (IS_HOST_CHAR(ch)) {
- return s_http_host;
- }
-
- /* FALLTHROUGH */
- case s_http_host_v6_end:
- if (ch == ':') {
- return s_http_host_port_start;
- }
-
- break;
-
- case s_http_host_v6:
- if (ch == ']') {
- return s_http_host_v6_end;
- }
-
- /* FALLTHROUGH */
- case s_http_host_v6_start:
- if (IS_HEX(ch) || ch == ':') {
- return s_http_host_v6;
- }
-
- break;
-
- case s_http_host_port:
- case s_http_host_port_start:
- if (IS_NUM(ch)) {
- return s_http_host_port;
- }
-
- break;
-
- default:
- break;
- }
- return s_http_host_dead;
-}
-
-static int
-http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
- enum http_host_state s;
-
- const char *p;
- size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
-
- if (buflen > UINT16_MAX)
- return 1;
-
- u->field_data[UF_HOST].len = 0;
-
- s = found_at ? s_http_userinfo_start : s_http_host_start;
-
- for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
- enum http_host_state new_s = http_parse_host_char(s, *p);
-
- if (new_s == s_http_host_dead) {
- return 1;
- }
-
- switch(new_s) {
- case s_http_host:
- if (s != s_http_host) {
- u->field_data[UF_HOST].off = (uint16_t)(p - buf);
- }
- u->field_data[UF_HOST].len++;
- break;
-
- case s_http_host_v6:
- if (s != s_http_host_v6) {
- u->field_data[UF_HOST].off = (uint16_t)(p - buf);
- }
- u->field_data[UF_HOST].len++;
- break;
-
- case s_http_host_port:
- if (s != s_http_host_port) {
- u->field_data[UF_PORT].off = (uint16_t)(p - buf);
- u->field_data[UF_PORT].len = 0;
- u->field_set |= (1 << UF_PORT);
- }
- u->field_data[UF_PORT].len++;
- break;
-
- case s_http_userinfo:
- if (s != s_http_userinfo) {
- u->field_data[UF_USERINFO].off = (uint16_t)(p - buf);
- u->field_data[UF_USERINFO].len = 0;
- u->field_set |= (1 << UF_USERINFO);
- }
- u->field_data[UF_USERINFO].len++;
- break;
-
- default:
- break;
- }
- s = new_s;
- }
-
- /* Make sure we don't end somewhere unexpected */
- switch (s) {
- case s_http_host_start:
- case s_http_host_v6_start:
- case s_http_host_v6:
- case s_http_userinfo:
- case s_http_userinfo_start:
- return 1;
- default:
- break;
- }
-
- return 0;
-}
-
-int
-http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
- struct http_parser_url *u)
-{
- enum state s;
- const char *p;
- enum http_parser_url_fields uf, old_uf;
- int found_at = 0;
-
- if (buflen > UINT16_MAX)
- return 1;
-
- u->port = u->field_set = 0;
- s = is_connect ? s_req_server_start : s_req_spaces_before_url;
- uf = old_uf = UF_MAX;
-
- for (p = buf; p < buf + buflen; p++) {
- s = parse_url_char(s, *p);
-
- /* Figure out the next field that we're operating on */
- switch (s) {
- case s_dead:
- return 1;
-
- /* Skip delimeters */
- case s_req_schema_slash:
- case s_req_schema_slash_slash:
- case s_req_server_start:
- case s_req_query_string_start:
- case s_req_fragment_start:
- continue;
-
- case s_req_schema:
- uf = UF_SCHEMA;
- break;
-
- case s_req_server_with_at:
- found_at = 1;
-
- /* FALLTROUGH */
- case s_req_server:
- uf = UF_HOST;
- break;
-
- case s_req_path:
- uf = UF_PATH;
- break;
-
- case s_req_query_string:
- uf = UF_QUERY;
- break;
-
- case s_req_fragment:
- uf = UF_FRAGMENT;
- break;
-
- default:
- assert(!"Unexpected state");
- return 1;
- }
-
- /* Nothing's changed; soldier on */
- if (uf == old_uf) {
- u->field_data[uf].len++;
- continue;
- }
-
- u->field_data[uf].off = (uint16_t)(p - buf);
- u->field_data[uf].len = 1;
-
- u->field_set |= (1 << uf);
- old_uf = uf;
- }
-
- /* host must be present if there is a schema */
- /* parsing http:///toto will fail */
- if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) {
- if (http_parse_host(buf, u, found_at) != 0) {
- return 1;
- }
- }
-
- /* CONNECT requests can only contain "hostname:port" */
- if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
- return 1;
- }
-
- if (u->field_set & (1 << UF_PORT)) {
- /* Don't bother with endp; we've already validated the string */
- unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
-
- /* Ports have a max value of 2^16 */
- if (v > 0xffff) {
- return 1;
- }
-
- u->port = (uint16_t) v;
- }
-
- return 0;
-}
-
-void
-http_parser_pause(http_parser *parser, int paused) {
- /* Users should only be pausing/unpausing a parser that is not in an error
- * state. In non-debug builds, there's not much that we can do about this
- * other than ignore it.
- */
- if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
- HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
- SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
- } else {
- assert(0 && "Attempting to pause parser in error state");
- }
-}
-
-int
-http_body_is_final(const struct http_parser *parser) {
- return parser->state == s_message_done;
-}
diff --git a/deps/http-parser/http_parser.h b/deps/http-parser/http_parser.h
deleted file mode 100644
index 67e1d95dd6e..00000000000
--- a/deps/http-parser/http_parser.h
+++ /dev/null
@@ -1,305 +0,0 @@
-/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-#ifndef http_parser_h
-#define http_parser_h
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define HTTP_PARSER_VERSION_MAJOR 2
-#define HTTP_PARSER_VERSION_MINOR 0
-
-#include
-#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
-#include
-typedef __int8 int8_t;
-typedef unsigned __int8 uint8_t;
-typedef __int16 int16_t;
-typedef unsigned __int16 uint16_t;
-typedef __int32 int32_t;
-typedef unsigned __int32 uint32_t;
-typedef __int64 int64_t;
-typedef unsigned __int64 uint64_t;
-typedef SIZE_T size_t;
-typedef SSIZE_T ssize_t;
-#elif defined(__sun) || defined(__sun__)
-#include
-#else
-#include
-#endif
-
-/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
- * faster
- */
-#ifndef HTTP_PARSER_STRICT
-# define HTTP_PARSER_STRICT 1
-#endif
-
-/* Maximium header size allowed */
-#define HTTP_MAX_HEADER_SIZE (80*1024)
-
-
-typedef struct http_parser http_parser;
-typedef struct http_parser_settings http_parser_settings;
-
-
-/* Callbacks should return non-zero to indicate an error. The parser will
- * then halt execution.
- *
- * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
- * returning '1' from on_headers_complete will tell the parser that it
- * should not expect a body. This is used when receiving a response to a
- * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
- * chunked' headers that indicate the presence of a body.
- *
- * http_data_cb does not return data chunks. It will be call arbitrarally
- * many times for each string. E.G. you might get 10 callbacks for "on_url"
- * each providing just a few characters more data.
- */
-typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
-typedef int (*http_cb) (http_parser*);
-
-
-/* Request Methods */
-#define HTTP_METHOD_MAP(XX) \
- XX(0, DELETE, DELETE) \
- XX(1, GET, GET) \
- XX(2, HEAD, HEAD) \
- XX(3, POST, POST) \
- XX(4, PUT, PUT) \
- /* pathological */ \
- XX(5, CONNECT, CONNECT) \
- XX(6, OPTIONS, OPTIONS) \
- XX(7, TRACE, TRACE) \
- /* webdav */ \
- XX(8, COPY, COPY) \
- XX(9, LOCK, LOCK) \
- XX(10, MKCOL, MKCOL) \
- XX(11, MOVE, MOVE) \
- XX(12, PROPFIND, PROPFIND) \
- XX(13, PROPPATCH, PROPPATCH) \
- XX(14, SEARCH, SEARCH) \
- XX(15, UNLOCK, UNLOCK) \
- /* subversion */ \
- XX(16, REPORT, REPORT) \
- XX(17, MKACTIVITY, MKACTIVITY) \
- XX(18, CHECKOUT, CHECKOUT) \
- XX(19, MERGE, MERGE) \
- /* upnp */ \
- XX(20, MSEARCH, M-SEARCH) \
- XX(21, NOTIFY, NOTIFY) \
- XX(22, SUBSCRIBE, SUBSCRIBE) \
- XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \
- /* RFC-5789 */ \
- XX(24, PATCH, PATCH) \
- XX(25, PURGE, PURGE) \
-
-enum http_method
- {
-#define XX(num, name, string) HTTP_##name = num,
- HTTP_METHOD_MAP(XX)
-#undef XX
- };
-
-
-enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
-
-
-/* Flag values for http_parser.flags field */
-enum flags
- { F_CHUNKED = 1 << 0
- , F_CONNECTION_KEEP_ALIVE = 1 << 1
- , F_CONNECTION_CLOSE = 1 << 2
- , F_TRAILING = 1 << 3
- , F_UPGRADE = 1 << 4
- , F_SKIPBODY = 1 << 5
- };
-
-
-/* Map for errno-related constants
- *
- * The provided argument should be a macro that takes 2 arguments.
- */
-#define HTTP_ERRNO_MAP(XX) \
- /* No error */ \
- XX(OK, "success") \
- \
- /* Callback-related errors */ \
- XX(CB_message_begin, "the on_message_begin callback failed") \
- XX(CB_url, "the on_url callback failed") \
- XX(CB_header_field, "the on_header_field callback failed") \
- XX(CB_header_value, "the on_header_value callback failed") \
- XX(CB_headers_complete, "the on_headers_complete callback failed") \
- XX(CB_body, "the on_body callback failed") \
- XX(CB_message_complete, "the on_message_complete callback failed") \
- \
- /* Parsing-related errors */ \
- XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
- XX(HEADER_OVERFLOW, \
- "too many header bytes seen; overflow detected") \
- XX(CLOSED_CONNECTION, \
- "data received after completed connection: close message") \
- XX(INVALID_VERSION, "invalid HTTP version") \
- XX(INVALID_STATUS, "invalid HTTP status code") \
- XX(INVALID_METHOD, "invalid HTTP method") \
- XX(INVALID_URL, "invalid URL") \
- XX(INVALID_HOST, "invalid host") \
- XX(INVALID_PORT, "invalid port") \
- XX(INVALID_PATH, "invalid path") \
- XX(INVALID_QUERY_STRING, "invalid query string") \
- XX(INVALID_FRAGMENT, "invalid fragment") \
- XX(LF_EXPECTED, "LF character expected") \
- XX(INVALID_HEADER_TOKEN, "invalid character in header") \
- XX(INVALID_CONTENT_LENGTH, \
- "invalid character in content-length header") \
- XX(INVALID_CHUNK_SIZE, \
- "invalid character in chunk size header") \
- XX(INVALID_CONSTANT, "invalid constant string") \
- XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
- XX(STRICT, "strict mode assertion failed") \
- XX(PAUSED, "parser is paused") \
- XX(UNKNOWN, "an unknown error occurred")
-
-
-/* Define HPE_* values for each errno value above */
-#define HTTP_ERRNO_GEN(n, s) HPE_##n,
-enum http_errno {
- HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
-};
-#undef HTTP_ERRNO_GEN
-
-
-/* Get an http_errno value from an http_parser */
-#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
-
-
-struct http_parser {
- /** PRIVATE **/
- unsigned char type : 2; /* enum http_parser_type */
- unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
- unsigned char state; /* enum state from http_parser.c */
- unsigned char header_state; /* enum header_state from http_parser.c */
- unsigned char index; /* index into current matcher */
-
- uint32_t nread; /* # bytes read in various scenarios */
- uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
-
- /** READ-ONLY **/
- unsigned short http_major;
- unsigned short http_minor;
- unsigned short status_code; /* responses only */
- unsigned char method; /* requests only */
- unsigned char http_errno : 7;
-
- /* 1 = Upgrade header was present and the parser has exited because of that.
- * 0 = No upgrade header present.
- * Should be checked when http_parser_execute() returns in addition to
- * error checking.
- */
- unsigned char upgrade : 1;
-
- /** PUBLIC **/
- void *data; /* A pointer to get hook to the "connection" or "socket" object */
-};
-
-
-struct http_parser_settings {
- http_cb on_message_begin;
- http_data_cb on_url;
- http_data_cb on_header_field;
- http_data_cb on_header_value;
- http_cb on_headers_complete;
- http_data_cb on_body;
- http_cb on_message_complete;
-};
-
-
-enum http_parser_url_fields
- { UF_SCHEMA = 0
- , UF_HOST = 1
- , UF_PORT = 2
- , UF_PATH = 3
- , UF_QUERY = 4
- , UF_FRAGMENT = 5
- , UF_USERINFO = 6
- , UF_MAX = 7
- };
-
-
-/* Result structure for http_parser_parse_url().
- *
- * Callers should index into field_data[] with UF_* values iff field_set
- * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
- * because we probably have padding left over), we convert any port to
- * a uint16_t.
- */
-struct http_parser_url {
- uint16_t field_set; /* Bitmask of (1 << UF_*) values */
- uint16_t port; /* Converted UF_PORT string */
-
- struct {
- uint16_t off; /* Offset into buffer in which field starts */
- uint16_t len; /* Length of run in buffer */
- } field_data[UF_MAX];
-};
-
-
-void http_parser_init(http_parser *parser, enum http_parser_type type);
-
-
-size_t http_parser_execute(http_parser *parser,
- const http_parser_settings *settings,
- const char *data,
- size_t len);
-
-
-/* If http_should_keep_alive() in the on_headers_complete or
- * on_message_complete callback returns 0, then this should be
- * the last message on the connection.
- * If you are the server, respond with the "Connection: close" header.
- * If you are the client, close the connection.
- */
-int http_should_keep_alive(const http_parser *parser);
-
-/* Returns a string version of the HTTP method. */
-const char *http_method_str(enum http_method m);
-
-/* Return a string name of the given error */
-const char *http_errno_name(enum http_errno err);
-
-/* Return a string description of the given error */
-const char *http_errno_description(enum http_errno err);
-
-/* Parse a URL; return nonzero on failure */
-int http_parser_parse_url(const char *buf, size_t buflen,
- int is_connect,
- struct http_parser_url *u);
-
-/* Pause or un-pause the parser; a nonzero value pauses */
-void http_parser_pause(http_parser *parser, int paused);
-
-/* Checks if this is the final chunk of the body. */
-int http_body_is_final(const http_parser *parser);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/deps/llhttp/CMakeLists.txt b/deps/llhttp/CMakeLists.txt
new file mode 100644
index 00000000000..6965335ab27
--- /dev/null
+++ b/deps/llhttp/CMakeLists.txt
@@ -0,0 +1,8 @@
+file(GLOB SRC_LLHTTP "*.c" "*.h")
+list(SORT SRC_LLHTTP)
+
+add_library(llhttp OBJECT ${SRC_LLHTTP})
+
+if(NOT MSVC)
+ set_source_files_properties(api.c http.c llhttp.c PROPERTIES COMPILE_FLAGS "-Wno-unused-parameter -Wno-missing-declarations")
+endif()
diff --git a/deps/llhttp/LICENSE-MIT b/deps/llhttp/LICENSE-MIT
new file mode 100644
index 00000000000..6c1512dd6bc
--- /dev/null
+++ b/deps/llhttp/LICENSE-MIT
@@ -0,0 +1,22 @@
+This software is licensed under the MIT License.
+
+Copyright Fedor Indutny, 2018.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/deps/llhttp/api.c b/deps/llhttp/api.c
new file mode 100644
index 00000000000..8c2ce3dc5c4
--- /dev/null
+++ b/deps/llhttp/api.c
@@ -0,0 +1,510 @@
+#include
+#include
+#include
+
+#include "llhttp.h"
+
+#define CALLBACK_MAYBE(PARSER, NAME) \
+ do { \
+ const llhttp_settings_t* settings; \
+ settings = (const llhttp_settings_t*) (PARSER)->settings; \
+ if (settings == NULL || settings->NAME == NULL) { \
+ err = 0; \
+ break; \
+ } \
+ err = settings->NAME((PARSER)); \
+ } while (0)
+
+#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \
+ do { \
+ const llhttp_settings_t* settings; \
+ settings = (const llhttp_settings_t*) (PARSER)->settings; \
+ if (settings == NULL || settings->NAME == NULL) { \
+ err = 0; \
+ break; \
+ } \
+ err = settings->NAME((PARSER), (START), (LEN)); \
+ if (err == -1) { \
+ err = HPE_USER; \
+ llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \
+ } \
+ } while (0)
+
+void llhttp_init(llhttp_t* parser, llhttp_type_t type,
+ const llhttp_settings_t* settings) {
+ llhttp__internal_init(parser);
+
+ parser->type = type;
+ parser->settings = (void*) settings;
+}
+
+
+#if defined(__wasm__)
+
+extern int wasm_on_message_begin(llhttp_t * p);
+extern int wasm_on_url(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_status(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_headers_complete(llhttp_t * p, int status_code,
+ uint8_t upgrade, int should_keep_alive);
+extern int wasm_on_body(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_message_complete(llhttp_t * p);
+
+static int wasm_on_headers_complete_wrap(llhttp_t* p) {
+ return wasm_on_headers_complete(p, p->status_code, p->upgrade,
+ llhttp_should_keep_alive(p));
+}
+
+const llhttp_settings_t wasm_settings = {
+ wasm_on_message_begin,
+ wasm_on_url,
+ wasm_on_status,
+ NULL,
+ NULL,
+ wasm_on_header_field,
+ wasm_on_header_value,
+ NULL,
+ NULL,
+ wasm_on_headers_complete_wrap,
+ wasm_on_body,
+ wasm_on_message_complete,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+
+llhttp_t* llhttp_alloc(llhttp_type_t type) {
+ llhttp_t* parser = malloc(sizeof(llhttp_t));
+ llhttp_init(parser, type, &wasm_settings);
+ return parser;
+}
+
+void llhttp_free(llhttp_t* parser) {
+ free(parser);
+}
+
+#endif // defined(__wasm__)
+
+/* Some getters required to get stuff from the parser */
+
+uint8_t llhttp_get_type(llhttp_t* parser) {
+ return parser->type;
+}
+
+uint8_t llhttp_get_http_major(llhttp_t* parser) {
+ return parser->http_major;
+}
+
+uint8_t llhttp_get_http_minor(llhttp_t* parser) {
+ return parser->http_minor;
+}
+
+uint8_t llhttp_get_method(llhttp_t* parser) {
+ return parser->method;
+}
+
+int llhttp_get_status_code(llhttp_t* parser) {
+ return parser->status_code;
+}
+
+uint8_t llhttp_get_upgrade(llhttp_t* parser) {
+ return parser->upgrade;
+}
+
+
+void llhttp_reset(llhttp_t* parser) {
+ llhttp_type_t type = parser->type;
+ const llhttp_settings_t* settings = parser->settings;
+ void* data = parser->data;
+ uint16_t lenient_flags = parser->lenient_flags;
+
+ llhttp__internal_init(parser);
+
+ parser->type = type;
+ parser->settings = (void*) settings;
+ parser->data = data;
+ parser->lenient_flags = lenient_flags;
+}
+
+
+llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
+ return llhttp__internal_execute(parser, data, data + len);
+}
+
+
+void llhttp_settings_init(llhttp_settings_t* settings) {
+ memset(settings, 0, sizeof(*settings));
+}
+
+
+llhttp_errno_t llhttp_finish(llhttp_t* parser) {
+ int err;
+
+ /* We're in an error state. Don't bother doing anything. */
+ if (parser->error != 0) {
+ return 0;
+ }
+
+ switch (parser->finish) {
+ case HTTP_FINISH_SAFE_WITH_CB:
+ CALLBACK_MAYBE(parser, on_message_complete);
+ if (err != HPE_OK) return err;
+
+ /* FALLTHROUGH */
+ case HTTP_FINISH_SAFE:
+ return HPE_OK;
+ case HTTP_FINISH_UNSAFE:
+ parser->reason = "Invalid EOF state";
+ return HPE_INVALID_EOF_STATE;
+ default:
+ abort();
+ }
+}
+
+
+void llhttp_pause(llhttp_t* parser) {
+ if (parser->error != HPE_OK) {
+ return;
+ }
+
+ parser->error = HPE_PAUSED;
+ parser->reason = "Paused";
+}
+
+
+void llhttp_resume(llhttp_t* parser) {
+ if (parser->error != HPE_PAUSED) {
+ return;
+ }
+
+ parser->error = 0;
+}
+
+
+void llhttp_resume_after_upgrade(llhttp_t* parser) {
+ if (parser->error != HPE_PAUSED_UPGRADE) {
+ return;
+ }
+
+ parser->error = 0;
+}
+
+
+llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
+ return parser->error;
+}
+
+
+const char* llhttp_get_error_reason(const llhttp_t* parser) {
+ return parser->reason;
+}
+
+
+void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
+ parser->reason = reason;
+}
+
+
+const char* llhttp_get_error_pos(const llhttp_t* parser) {
+ return parser->error_pos;
+}
+
+
+const char* llhttp_errno_name(llhttp_errno_t err) {
+#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
+ switch (err) {
+ HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+ default: abort();
+ }
+#undef HTTP_ERRNO_GEN
+}
+
+
+const char* llhttp_method_name(llhttp_method_t method) {
+#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING;
+ switch (method) {
+ HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN)
+ default: abort();
+ }
+#undef HTTP_METHOD_GEN
+}
+
+const char* llhttp_status_name(llhttp_status_t status) {
+#define HTTP_STATUS_GEN(NUM, NAME, STRING) case HTTP_STATUS_##NAME: return #STRING;
+ switch (status) {
+ HTTP_STATUS_MAP(HTTP_STATUS_GEN)
+ default: abort();
+ }
+#undef HTTP_STATUS_GEN
+}
+
+
+void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_HEADERS;
+ } else {
+ parser->lenient_flags &= ~LENIENT_HEADERS;
+ }
+}
+
+
+void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_CHUNKED_LENGTH;
+ } else {
+ parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH;
+ }
+}
+
+
+void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_KEEP_ALIVE;
+ } else {
+ parser->lenient_flags &= ~LENIENT_KEEP_ALIVE;
+ }
+}
+
+void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_TRANSFER_ENCODING;
+ } else {
+ parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING;
+ }
+}
+
+void llhttp_set_lenient_version(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_VERSION;
+ } else {
+ parser->lenient_flags &= ~LENIENT_VERSION;
+ }
+}
+
+void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE;
+ } else {
+ parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE;
+ }
+}
+
+void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR;
+ } else {
+ parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR;
+ }
+}
+
+void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
+ } else {
+ parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
+ }
+}
+
+void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_OPTIONAL_CR_BEFORE_LF;
+ } else {
+ parser->lenient_flags &= ~LENIENT_OPTIONAL_CR_BEFORE_LF;
+ }
+}
+
+void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE;
+ } else {
+ parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE;
+ }
+}
+
+/* Callbacks */
+
+
+int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_message_begin);
+ return err;
+}
+
+
+int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_url_complete);
+ return err;
+}
+
+
+int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_status_complete);
+ return err;
+}
+
+
+int llhttp__on_method(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_method, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_method_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_method_complete);
+ return err;
+}
+
+
+int llhttp__on_version(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_version, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_version_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_version_complete);
+ return err;
+}
+
+
+int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_header_field_complete);
+ return err;
+}
+
+
+int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_header_value_complete);
+ return err;
+}
+
+
+int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_headers_complete);
+ return err;
+}
+
+
+int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_message_complete);
+ return err;
+}
+
+
+int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_chunk_header);
+ return err;
+}
+
+
+int llhttp__on_chunk_extension_name(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_chunk_extension_name, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_chunk_extension_name_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_chunk_extension_name_complete);
+ return err;
+}
+
+
+int llhttp__on_chunk_extension_value(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_chunk_extension_value, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_chunk_extension_value_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_chunk_extension_value_complete);
+ return err;
+}
+
+
+int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_chunk_complete);
+ return err;
+}
+
+
+int llhttp__on_reset(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_reset);
+ return err;
+}
+
+
+/* Private */
+
+
+void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
+ const char* msg) {
+ if (p == endp) {
+ fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
+ s->flags, msg);
+ } else {
+ fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s,
+ s->type, s->flags, *p, msg);
+ }
+}
diff --git a/deps/llhttp/http.c b/deps/llhttp/http.c
new file mode 100644
index 00000000000..1ab91a55796
--- /dev/null
+++ b/deps/llhttp/http.c
@@ -0,0 +1,170 @@
+#include
+#ifndef LLHTTP__TEST
+# include "llhttp.h"
+#else
+# define llhttp_t llparse_t
+#endif /* */
+
+int llhttp_message_needs_eof(const llhttp_t* parser);
+int llhttp_should_keep_alive(const llhttp_t* parser);
+
+int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
+ const char* endp) {
+ /* Set this here so that on_headers_complete() callbacks can see it */
+ if ((parser->flags & F_UPGRADE) &&
+ (parser->flags & F_CONNECTION_UPGRADE)) {
+ /* For responses, "Upgrade: foo" and "Connection: upgrade" are
+ * mandatory only when it is a 101 Switching Protocols response,
+ * otherwise it is purely informational, to announce support.
+ */
+ parser->upgrade =
+ (parser->type == HTTP_REQUEST || parser->status_code == 101);
+ } else {
+ parser->upgrade = (parser->method == HTTP_CONNECT);
+ }
+ return 0;
+}
+
+
+/* Return values:
+ * 0 - No body, `restart`, message_complete
+ * 1 - CONNECT request, `restart`, message_complete, and pause
+ * 2 - chunk_size_start
+ * 3 - body_identity
+ * 4 - body_identity_eof
+ * 5 - invalid transfer-encoding for request
+ */
+int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
+ const char* endp) {
+ int hasBody;
+
+ hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;
+ if (
+ (parser->upgrade && (parser->method == HTTP_CONNECT ||
+ (parser->flags & F_SKIPBODY) || !hasBody)) ||
+ /* See RFC 2616 section 4.4 - 1xx e.g. Continue */
+ (parser->type == HTTP_RESPONSE && parser->status_code == 101)
+ ) {
+ /* Exit, the rest of the message is in a different protocol. */
+ return 1;
+ }
+
+ if (parser->type == HTTP_RESPONSE && parser->status_code == 100) {
+ /* No body, restart as the message is complete */
+ return 0;
+ }
+
+ /* See RFC 2616 section 4.4 */
+ if (
+ parser->flags & F_SKIPBODY || /* response to a HEAD request */
+ (
+ parser->type == HTTP_RESPONSE && (
+ parser->status_code == 102 || /* Processing */
+ parser->status_code == 103 || /* Early Hints */
+ parser->status_code == 204 || /* No Content */
+ parser->status_code == 304 /* Not Modified */
+ )
+ )
+ ) {
+ return 0;
+ } else if (parser->flags & F_CHUNKED) {
+ /* chunked encoding - ignore Content-Length header, prepare for a chunk */
+ return 2;
+ } else if (parser->flags & F_TRANSFER_ENCODING) {
+ if (parser->type == HTTP_REQUEST &&
+ (parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 &&
+ (parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 0) {
+ /* RFC 7230 3.3.3 */
+
+ /* If a Transfer-Encoding header field
+ * is present in a request and the chunked transfer coding is not
+ * the final encoding, the message body length cannot be determined
+ * reliably; the server MUST respond with the 400 (Bad Request)
+ * status code and then close the connection.
+ */
+ return 5;
+ } else {
+ /* RFC 7230 3.3.3 */
+
+ /* If a Transfer-Encoding header field is present in a response and
+ * the chunked transfer coding is not the final encoding, the
+ * message body length is determined by reading the connection until
+ * it is closed by the server.
+ */
+ return 4;
+ }
+ } else {
+ if (!(parser->flags & F_CONTENT_LENGTH)) {
+ if (!llhttp_message_needs_eof(parser)) {
+ /* Assume content-length 0 - read the next */
+ return 0;
+ } else {
+ /* Read body until EOF */
+ return 4;
+ }
+ } else if (parser->content_length == 0) {
+ /* Content-Length header given but zero: Content-Length: 0\r\n */
+ return 0;
+ } else {
+ /* Content-Length header given and non-zero */
+ return 3;
+ }
+ }
+}
+
+
+int llhttp__after_message_complete(llhttp_t* parser, const char* p,
+ const char* endp) {
+ int should_keep_alive;
+
+ should_keep_alive = llhttp_should_keep_alive(parser);
+ parser->finish = HTTP_FINISH_SAFE;
+ parser->flags = 0;
+
+ /* NOTE: this is ignored in loose parsing mode */
+ return should_keep_alive;
+}
+
+
+int llhttp_message_needs_eof(const llhttp_t* parser) {
+ if (parser->type == HTTP_REQUEST) {
+ return 0;
+ }
+
+ /* See RFC 2616 section 4.4 */
+ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
+ parser->status_code == 204 || /* No Content */
+ parser->status_code == 304 || /* Not Modified */
+ (parser->flags & F_SKIPBODY)) { /* response to a HEAD request */
+ return 0;
+ }
+
+ /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */
+ if ((parser->flags & F_TRANSFER_ENCODING) &&
+ (parser->flags & F_CHUNKED) == 0) {
+ return 1;
+ }
+
+ if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int llhttp_should_keep_alive(const llhttp_t* parser) {
+ if (parser->http_major > 0 && parser->http_minor > 0) {
+ /* HTTP/1.1 */
+ if (parser->flags & F_CONNECTION_CLOSE) {
+ return 0;
+ }
+ } else {
+ /* HTTP/1.0 or earlier */
+ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
+ return 0;
+ }
+ }
+
+ return !llhttp_message_needs_eof(parser);
+}
diff --git a/deps/llhttp/llhttp.c b/deps/llhttp/llhttp.c
new file mode 100644
index 00000000000..3ef3b817f3d
--- /dev/null
+++ b/deps/llhttp/llhttp.c
@@ -0,0 +1,10168 @@
+#include
+#include
+#include
+
+#ifdef __SSE4_2__
+ #ifdef _MSC_VER
+ #include
+ #else /* !_MSC_VER */
+ #include
+ #endif /* _MSC_VER */
+#endif /* __SSE4_2__ */
+
+#ifdef _MSC_VER
+ #define ALIGN(n) _declspec(align(n))
+#else /* !_MSC_VER */
+ #define ALIGN(n) __attribute__((aligned(n)))
+#endif /* _MSC_VER */
+
+#include "llhttp.h"
+
+typedef int (*llhttp__internal__span_cb)(
+ llhttp__internal_t*, const char*, const char*);
+
+static const unsigned char llparse_blob0[] = {
+ 'o', 'n'
+};
+static const unsigned char llparse_blob1[] = {
+ 'e', 'c', 't', 'i', 'o', 'n'
+};
+static const unsigned char llparse_blob2[] = {
+ 'l', 'o', 's', 'e'
+};
+static const unsigned char llparse_blob3[] = {
+ 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e'
+};
+static const unsigned char llparse_blob4[] = {
+ 'p', 'g', 'r', 'a', 'd', 'e'
+};
+static const unsigned char llparse_blob5[] = {
+ 'c', 'h', 'u', 'n', 'k', 'e', 'd'
+};
+#ifdef __SSE4_2__
+static const unsigned char ALIGN(16) llparse_blob6[] = {
+ 0x9, 0x9, ' ', '~', 0x80, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0
+};
+#endif /* __SSE4_2__ */
+#ifdef __SSE4_2__
+static const unsigned char ALIGN(16) llparse_blob7[] = {
+ '!', '!', '#', '\'', '*', '+', '-', '.', '0', '9', 'A',
+ 'Z', '^', 'z', '|', '|'
+};
+#endif /* __SSE4_2__ */
+#ifdef __SSE4_2__
+static const unsigned char ALIGN(16) llparse_blob8[] = {
+ '~', '~', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0
+};
+#endif /* __SSE4_2__ */
+static const unsigned char llparse_blob9[] = {
+ 'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h'
+};
+static const unsigned char llparse_blob10[] = {
+ 'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c',
+ 't', 'i', 'o', 'n'
+};
+static const unsigned char llparse_blob11[] = {
+ 'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c',
+ 'o', 'd', 'i', 'n', 'g'
+};
+static const unsigned char llparse_blob12[] = {
+ 'p', 'g', 'r', 'a', 'd', 'e'
+};
+static const unsigned char llparse_blob13[] = {
+ 'T', 'T', 'P', '/'
+};
+static const unsigned char llparse_blob14[] = {
+ 0xd, 0xa, 0xd, 0xa, 'S', 'M', 0xd, 0xa, 0xd, 0xa
+};
+static const unsigned char llparse_blob15[] = {
+ 'C', 'E', '/'
+};
+static const unsigned char llparse_blob16[] = {
+ 'T', 'S', 'P', '/'
+};
+static const unsigned char llparse_blob17[] = {
+ 'N', 'O', 'U', 'N', 'C', 'E'
+};
+static const unsigned char llparse_blob18[] = {
+ 'I', 'N', 'D'
+};
+static const unsigned char llparse_blob19[] = {
+ 'E', 'C', 'K', 'O', 'U', 'T'
+};
+static const unsigned char llparse_blob20[] = {
+ 'N', 'E', 'C', 'T'
+};
+static const unsigned char llparse_blob21[] = {
+ 'E', 'T', 'E'
+};
+static const unsigned char llparse_blob22[] = {
+ 'C', 'R', 'I', 'B', 'E'
+};
+static const unsigned char llparse_blob23[] = {
+ 'L', 'U', 'S', 'H'
+};
+static const unsigned char llparse_blob24[] = {
+ 'E', 'T'
+};
+static const unsigned char llparse_blob25[] = {
+ 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R'
+};
+static const unsigned char llparse_blob26[] = {
+ 'E', 'A', 'D'
+};
+static const unsigned char llparse_blob27[] = {
+ 'N', 'K'
+};
+static const unsigned char llparse_blob28[] = {
+ 'C', 'K'
+};
+static const unsigned char llparse_blob29[] = {
+ 'S', 'E', 'A', 'R', 'C', 'H'
+};
+static const unsigned char llparse_blob30[] = {
+ 'R', 'G', 'E'
+};
+static const unsigned char llparse_blob31[] = {
+ 'C', 'T', 'I', 'V', 'I', 'T', 'Y'
+};
+static const unsigned char llparse_blob32[] = {
+ 'L', 'E', 'N', 'D', 'A', 'R'
+};
+static const unsigned char llparse_blob33[] = {
+ 'V', 'E'
+};
+static const unsigned char llparse_blob34[] = {
+ 'O', 'T', 'I', 'F', 'Y'
+};
+static const unsigned char llparse_blob35[] = {
+ 'P', 'T', 'I', 'O', 'N', 'S'
+};
+static const unsigned char llparse_blob36[] = {
+ 'C', 'H'
+};
+static const unsigned char llparse_blob37[] = {
+ 'S', 'E'
+};
+static const unsigned char llparse_blob38[] = {
+ 'A', 'Y'
+};
+static const unsigned char llparse_blob39[] = {
+ 'S', 'T'
+};
+static const unsigned char llparse_blob40[] = {
+ 'I', 'N', 'D'
+};
+static const unsigned char llparse_blob41[] = {
+ 'A', 'T', 'C', 'H'
+};
+static const unsigned char llparse_blob42[] = {
+ 'G', 'E'
+};
+static const unsigned char llparse_blob43[] = {
+ 'U', 'E', 'R', 'Y'
+};
+static const unsigned char llparse_blob44[] = {
+ 'I', 'N', 'D'
+};
+static const unsigned char llparse_blob45[] = {
+ 'O', 'R', 'D'
+};
+static const unsigned char llparse_blob46[] = {
+ 'I', 'R', 'E', 'C', 'T'
+};
+static const unsigned char llparse_blob47[] = {
+ 'O', 'R', 'T'
+};
+static const unsigned char llparse_blob48[] = {
+ 'R', 'C', 'H'
+};
+static const unsigned char llparse_blob49[] = {
+ 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R'
+};
+static const unsigned char llparse_blob50[] = {
+ 'U', 'R', 'C', 'E'
+};
+static const unsigned char llparse_blob51[] = {
+ 'B', 'S', 'C', 'R', 'I', 'B', 'E'
+};
+static const unsigned char llparse_blob52[] = {
+ 'A', 'R', 'D', 'O', 'W', 'N'
+};
+static const unsigned char llparse_blob53[] = {
+ 'A', 'C', 'E'
+};
+static const unsigned char llparse_blob54[] = {
+ 'I', 'N', 'D'
+};
+static const unsigned char llparse_blob55[] = {
+ 'N', 'K'
+};
+static const unsigned char llparse_blob56[] = {
+ 'C', 'K'
+};
+static const unsigned char llparse_blob57[] = {
+ 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E'
+};
+static const unsigned char llparse_blob58[] = {
+ 'H', 'T', 'T', 'P', '/'
+};
+static const unsigned char llparse_blob59[] = {
+ 'A', 'D'
+};
+static const unsigned char llparse_blob60[] = {
+ 'T', 'P', '/'
+};
+
+enum llparse_match_status_e {
+ kMatchComplete,
+ kMatchPause,
+ kMatchMismatch
+};
+typedef enum llparse_match_status_e llparse_match_status_t;
+
+struct llparse_match_s {
+ llparse_match_status_t status;
+ const unsigned char* current;
+};
+typedef struct llparse_match_s llparse_match_t;
+
+static llparse_match_t llparse__match_sequence_to_lower(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp,
+ const unsigned char* seq, uint32_t seq_len) {
+ uint32_t index;
+ llparse_match_t res;
+
+ index = s->_index;
+ for (; p != endp; p++) {
+ unsigned char current;
+
+ current = ((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p));
+ if (current == seq[index]) {
+ if (++index == seq_len) {
+ res.status = kMatchComplete;
+ goto reset;
+ }
+ } else {
+ res.status = kMatchMismatch;
+ goto reset;
+ }
+ }
+ s->_index = index;
+ res.status = kMatchPause;
+ res.current = p;
+ return res;
+reset:
+ s->_index = 0;
+ res.current = p;
+ return res;
+}
+
+static llparse_match_t llparse__match_sequence_to_lower_unsafe(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp,
+ const unsigned char* seq, uint32_t seq_len) {
+ uint32_t index;
+ llparse_match_t res;
+
+ index = s->_index;
+ for (; p != endp; p++) {
+ unsigned char current;
+
+ current = ((*p) | 0x20);
+ if (current == seq[index]) {
+ if (++index == seq_len) {
+ res.status = kMatchComplete;
+ goto reset;
+ }
+ } else {
+ res.status = kMatchMismatch;
+ goto reset;
+ }
+ }
+ s->_index = index;
+ res.status = kMatchPause;
+ res.current = p;
+ return res;
+reset:
+ s->_index = 0;
+ res.current = p;
+ return res;
+}
+
+static llparse_match_t llparse__match_sequence_id(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp,
+ const unsigned char* seq, uint32_t seq_len) {
+ uint32_t index;
+ llparse_match_t res;
+
+ index = s->_index;
+ for (; p != endp; p++) {
+ unsigned char current;
+
+ current = *p;
+ if (current == seq[index]) {
+ if (++index == seq_len) {
+ res.status = kMatchComplete;
+ goto reset;
+ }
+ } else {
+ res.status = kMatchMismatch;
+ goto reset;
+ }
+ }
+ s->_index = index;
+ res.status = kMatchPause;
+ res.current = p;
+ return res;
+reset:
+ s->_index = 0;
+ res.current = p;
+ return res;
+}
+
+enum llparse_state_e {
+ s_error,
+ s_n_llhttp__internal__n_closed,
+ s_n_llhttp__internal__n_invoke_llhttp__after_message_complete,
+ s_n_llhttp__internal__n_pause_1,
+ s_n_llhttp__internal__n_invoke_is_equal_upgrade,
+ s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2,
+ s_n_llhttp__internal__n_chunk_data_almost_done_1,
+ s_n_llhttp__internal__n_chunk_data_almost_done,
+ s_n_llhttp__internal__n_consume_content_length,
+ s_n_llhttp__internal__n_span_start_llhttp__on_body,
+ s_n_llhttp__internal__n_invoke_is_equal_content_length,
+ s_n_llhttp__internal__n_chunk_size_almost_done,
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_9,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2,
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_10,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1,
+ s_n_llhttp__internal__n_chunk_extension_quoted_value_done,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2,
+ s_n_llhttp__internal__n_error_30,
+ s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair,
+ s_n_llhttp__internal__n_error_31,
+ s_n_llhttp__internal__n_chunk_extension_quoted_value,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3,
+ s_n_llhttp__internal__n_error_33,
+ s_n_llhttp__internal__n_chunk_extension_value,
+ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value,
+ s_n_llhttp__internal__n_error_34,
+ s_n_llhttp__internal__n_chunk_extension_name,
+ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name,
+ s_n_llhttp__internal__n_chunk_extensions,
+ s_n_llhttp__internal__n_chunk_size_otherwise,
+ s_n_llhttp__internal__n_chunk_size,
+ s_n_llhttp__internal__n_chunk_size_digit,
+ s_n_llhttp__internal__n_invoke_update_content_length_1,
+ s_n_llhttp__internal__n_consume_content_length_1,
+ s_n_llhttp__internal__n_span_start_llhttp__on_body_1,
+ s_n_llhttp__internal__n_eof,
+ s_n_llhttp__internal__n_span_start_llhttp__on_body_2,
+ s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete,
+ s_n_llhttp__internal__n_error_5,
+ s_n_llhttp__internal__n_headers_almost_done,
+ s_n_llhttp__internal__n_header_field_colon_discard_ws,
+ s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete,
+ s_n_llhttp__internal__n_span_start_llhttp__on_header_value,
+ s_n_llhttp__internal__n_header_value_discard_lws,
+ s_n_llhttp__internal__n_header_value_discard_ws_almost_done,
+ s_n_llhttp__internal__n_header_value_lws,
+ s_n_llhttp__internal__n_header_value_almost_done,
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_17,
+ s_n_llhttp__internal__n_header_value_lenient,
+ s_n_llhttp__internal__n_error_54,
+ s_n_llhttp__internal__n_header_value_otherwise,
+ s_n_llhttp__internal__n_header_value_connection_token,
+ s_n_llhttp__internal__n_header_value_connection_ws,
+ s_n_llhttp__internal__n_header_value_connection_1,
+ s_n_llhttp__internal__n_header_value_connection_2,
+ s_n_llhttp__internal__n_header_value_connection_3,
+ s_n_llhttp__internal__n_header_value_connection,
+ s_n_llhttp__internal__n_error_56,
+ s_n_llhttp__internal__n_error_57,
+ s_n_llhttp__internal__n_header_value_content_length_ws,
+ s_n_llhttp__internal__n_header_value_content_length,
+ s_n_llhttp__internal__n_error_59,
+ s_n_llhttp__internal__n_error_58,
+ s_n_llhttp__internal__n_header_value_te_token_ows,
+ s_n_llhttp__internal__n_header_value,
+ s_n_llhttp__internal__n_header_value_te_token,
+ s_n_llhttp__internal__n_header_value_te_chunked_last,
+ s_n_llhttp__internal__n_header_value_te_chunked,
+ s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1,
+ s_n_llhttp__internal__n_header_value_discard_ws,
+ s_n_llhttp__internal__n_invoke_load_header_state,
+ s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete,
+ s_n_llhttp__internal__n_header_field_general_otherwise,
+ s_n_llhttp__internal__n_header_field_general,
+ s_n_llhttp__internal__n_header_field_colon,
+ s_n_llhttp__internal__n_header_field_3,
+ s_n_llhttp__internal__n_header_field_4,
+ s_n_llhttp__internal__n_header_field_2,
+ s_n_llhttp__internal__n_header_field_1,
+ s_n_llhttp__internal__n_header_field_5,
+ s_n_llhttp__internal__n_header_field_6,
+ s_n_llhttp__internal__n_header_field_7,
+ s_n_llhttp__internal__n_header_field,
+ s_n_llhttp__internal__n_span_start_llhttp__on_header_field,
+ s_n_llhttp__internal__n_header_field_start,
+ s_n_llhttp__internal__n_headers_start,
+ s_n_llhttp__internal__n_url_to_http_09,
+ s_n_llhttp__internal__n_url_skip_to_http09,
+ s_n_llhttp__internal__n_url_skip_lf_to_http09_1,
+ s_n_llhttp__internal__n_url_skip_lf_to_http09,
+ s_n_llhttp__internal__n_req_pri_upgrade,
+ s_n_llhttp__internal__n_req_http_complete_crlf,
+ s_n_llhttp__internal__n_req_http_complete,
+ s_n_llhttp__internal__n_invoke_load_method_1,
+ s_n_llhttp__internal__n_invoke_llhttp__on_version_complete,
+ s_n_llhttp__internal__n_error_66,
+ s_n_llhttp__internal__n_error_73,
+ s_n_llhttp__internal__n_req_http_minor,
+ s_n_llhttp__internal__n_error_74,
+ s_n_llhttp__internal__n_req_http_dot,
+ s_n_llhttp__internal__n_error_75,
+ s_n_llhttp__internal__n_req_http_major,
+ s_n_llhttp__internal__n_span_start_llhttp__on_version,
+ s_n_llhttp__internal__n_req_http_start_1,
+ s_n_llhttp__internal__n_req_http_start_2,
+ s_n_llhttp__internal__n_req_http_start_3,
+ s_n_llhttp__internal__n_req_http_start,
+ s_n_llhttp__internal__n_url_to_http,
+ s_n_llhttp__internal__n_url_skip_to_http,
+ s_n_llhttp__internal__n_url_fragment,
+ s_n_llhttp__internal__n_span_end_stub_query_3,
+ s_n_llhttp__internal__n_url_query,
+ s_n_llhttp__internal__n_url_query_or_fragment,
+ s_n_llhttp__internal__n_url_path,
+ s_n_llhttp__internal__n_span_start_stub_path_2,
+ s_n_llhttp__internal__n_span_start_stub_path,
+ s_n_llhttp__internal__n_span_start_stub_path_1,
+ s_n_llhttp__internal__n_url_server_with_at,
+ s_n_llhttp__internal__n_url_server,
+ s_n_llhttp__internal__n_url_schema_delim_1,
+ s_n_llhttp__internal__n_url_schema_delim,
+ s_n_llhttp__internal__n_span_end_stub_schema,
+ s_n_llhttp__internal__n_url_schema,
+ s_n_llhttp__internal__n_url_start,
+ s_n_llhttp__internal__n_span_start_llhttp__on_url_1,
+ s_n_llhttp__internal__n_url_entry_normal,
+ s_n_llhttp__internal__n_span_start_llhttp__on_url,
+ s_n_llhttp__internal__n_url_entry_connect,
+ s_n_llhttp__internal__n_req_spaces_before_url,
+ s_n_llhttp__internal__n_req_first_space_before_url,
+ s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1,
+ s_n_llhttp__internal__n_after_start_req_2,
+ s_n_llhttp__internal__n_after_start_req_3,
+ s_n_llhttp__internal__n_after_start_req_1,
+ s_n_llhttp__internal__n_after_start_req_4,
+ s_n_llhttp__internal__n_after_start_req_6,
+ s_n_llhttp__internal__n_after_start_req_8,
+ s_n_llhttp__internal__n_after_start_req_9,
+ s_n_llhttp__internal__n_after_start_req_7,
+ s_n_llhttp__internal__n_after_start_req_5,
+ s_n_llhttp__internal__n_after_start_req_12,
+ s_n_llhttp__internal__n_after_start_req_13,
+ s_n_llhttp__internal__n_after_start_req_11,
+ s_n_llhttp__internal__n_after_start_req_10,
+ s_n_llhttp__internal__n_after_start_req_14,
+ s_n_llhttp__internal__n_after_start_req_17,
+ s_n_llhttp__internal__n_after_start_req_16,
+ s_n_llhttp__internal__n_after_start_req_15,
+ s_n_llhttp__internal__n_after_start_req_18,
+ s_n_llhttp__internal__n_after_start_req_20,
+ s_n_llhttp__internal__n_after_start_req_21,
+ s_n_llhttp__internal__n_after_start_req_19,
+ s_n_llhttp__internal__n_after_start_req_23,
+ s_n_llhttp__internal__n_after_start_req_24,
+ s_n_llhttp__internal__n_after_start_req_26,
+ s_n_llhttp__internal__n_after_start_req_28,
+ s_n_llhttp__internal__n_after_start_req_29,
+ s_n_llhttp__internal__n_after_start_req_27,
+ s_n_llhttp__internal__n_after_start_req_25,
+ s_n_llhttp__internal__n_after_start_req_30,
+ s_n_llhttp__internal__n_after_start_req_22,
+ s_n_llhttp__internal__n_after_start_req_31,
+ s_n_llhttp__internal__n_after_start_req_32,
+ s_n_llhttp__internal__n_after_start_req_35,
+ s_n_llhttp__internal__n_after_start_req_36,
+ s_n_llhttp__internal__n_after_start_req_34,
+ s_n_llhttp__internal__n_after_start_req_37,
+ s_n_llhttp__internal__n_after_start_req_38,
+ s_n_llhttp__internal__n_after_start_req_42,
+ s_n_llhttp__internal__n_after_start_req_43,
+ s_n_llhttp__internal__n_after_start_req_41,
+ s_n_llhttp__internal__n_after_start_req_40,
+ s_n_llhttp__internal__n_after_start_req_39,
+ s_n_llhttp__internal__n_after_start_req_45,
+ s_n_llhttp__internal__n_after_start_req_44,
+ s_n_llhttp__internal__n_after_start_req_33,
+ s_n_llhttp__internal__n_after_start_req_46,
+ s_n_llhttp__internal__n_after_start_req_49,
+ s_n_llhttp__internal__n_after_start_req_50,
+ s_n_llhttp__internal__n_after_start_req_51,
+ s_n_llhttp__internal__n_after_start_req_52,
+ s_n_llhttp__internal__n_after_start_req_48,
+ s_n_llhttp__internal__n_after_start_req_47,
+ s_n_llhttp__internal__n_after_start_req_55,
+ s_n_llhttp__internal__n_after_start_req_57,
+ s_n_llhttp__internal__n_after_start_req_58,
+ s_n_llhttp__internal__n_after_start_req_56,
+ s_n_llhttp__internal__n_after_start_req_54,
+ s_n_llhttp__internal__n_after_start_req_59,
+ s_n_llhttp__internal__n_after_start_req_60,
+ s_n_llhttp__internal__n_after_start_req_53,
+ s_n_llhttp__internal__n_after_start_req_62,
+ s_n_llhttp__internal__n_after_start_req_63,
+ s_n_llhttp__internal__n_after_start_req_61,
+ s_n_llhttp__internal__n_after_start_req_66,
+ s_n_llhttp__internal__n_after_start_req_68,
+ s_n_llhttp__internal__n_after_start_req_69,
+ s_n_llhttp__internal__n_after_start_req_67,
+ s_n_llhttp__internal__n_after_start_req_70,
+ s_n_llhttp__internal__n_after_start_req_65,
+ s_n_llhttp__internal__n_after_start_req_64,
+ s_n_llhttp__internal__n_after_start_req,
+ s_n_llhttp__internal__n_span_start_llhttp__on_method_1,
+ s_n_llhttp__internal__n_res_line_almost_done,
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_30,
+ s_n_llhttp__internal__n_res_status,
+ s_n_llhttp__internal__n_span_start_llhttp__on_status,
+ s_n_llhttp__internal__n_res_status_code_otherwise,
+ s_n_llhttp__internal__n_res_status_code_digit_3,
+ s_n_llhttp__internal__n_res_status_code_digit_2,
+ s_n_llhttp__internal__n_res_status_code_digit_1,
+ s_n_llhttp__internal__n_res_after_version,
+ s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1,
+ s_n_llhttp__internal__n_error_89,
+ s_n_llhttp__internal__n_error_103,
+ s_n_llhttp__internal__n_res_http_minor,
+ s_n_llhttp__internal__n_error_104,
+ s_n_llhttp__internal__n_res_http_dot,
+ s_n_llhttp__internal__n_error_105,
+ s_n_llhttp__internal__n_res_http_major,
+ s_n_llhttp__internal__n_span_start_llhttp__on_version_1,
+ s_n_llhttp__internal__n_start_res,
+ s_n_llhttp__internal__n_invoke_llhttp__on_method_complete,
+ s_n_llhttp__internal__n_req_or_res_method_2,
+ s_n_llhttp__internal__n_invoke_update_type_1,
+ s_n_llhttp__internal__n_req_or_res_method_3,
+ s_n_llhttp__internal__n_req_or_res_method_1,
+ s_n_llhttp__internal__n_req_or_res_method,
+ s_n_llhttp__internal__n_span_start_llhttp__on_method,
+ s_n_llhttp__internal__n_start_req_or_res,
+ s_n_llhttp__internal__n_invoke_load_type,
+ s_n_llhttp__internal__n_invoke_update_finish,
+ s_n_llhttp__internal__n_start,
+};
+typedef enum llparse_state_e llparse_state_t;
+
+int llhttp__on_method(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_url(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_version(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_header_field(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_header_value(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_body(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_chunk_extension_name(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_chunk_extension_value(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_status(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_load_initial_message_completed(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->initial_message_completed;
+}
+
+int llhttp__on_reset(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_update_finish(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->finish = 2;
+ return 0;
+}
+
+int llhttp__on_message_begin(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_load_type(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->type;
+}
+
+int llhttp__internal__c_store_method(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ state->method = match;
+ return 0;
+}
+
+int llhttp__on_method_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_is_equal_method(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->method == 5;
+}
+
+int llhttp__internal__c_update_http_major(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->http_major = 0;
+ return 0;
+}
+
+int llhttp__internal__c_update_http_minor(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->http_minor = 9;
+ return 0;
+}
+
+int llhttp__on_url_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_test_lenient_flags(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 1) == 1;
+}
+
+int llhttp__internal__c_test_lenient_flags_1(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 256) == 256;
+}
+
+int llhttp__internal__c_test_flags(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->flags & 128) == 128;
+}
+
+int llhttp__on_chunk_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_message_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_is_equal_upgrade(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->upgrade == 1;
+}
+
+int llhttp__after_message_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_update_content_length(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->content_length = 0;
+ return 0;
+}
+
+int llhttp__internal__c_update_initial_message_completed(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->initial_message_completed = 1;
+ return 0;
+}
+
+int llhttp__internal__c_update_finish_1(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->finish = 0;
+ return 0;
+}
+
+int llhttp__internal__c_test_lenient_flags_2(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 4) == 4;
+}
+
+int llhttp__internal__c_test_lenient_flags_3(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 32) == 32;
+}
+
+int llhttp__before_headers_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_headers_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__after_headers_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_mul_add_content_length(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ /* Multiplication overflow */
+ if (state->content_length > 0xffffffffffffffffULL / 16) {
+ return 1;
+ }
+
+ state->content_length *= 16;
+
+ /* Addition overflow */
+ if (match >= 0) {
+ if (state->content_length > 0xffffffffffffffffULL - match) {
+ return 1;
+ }
+ } else {
+ if (state->content_length < 0ULL - match) {
+ return 1;
+ }
+ }
+ state->content_length += match;
+ return 0;
+}
+
+int llhttp__internal__c_test_lenient_flags_4(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 512) == 512;
+}
+
+int llhttp__on_chunk_header(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_is_equal_content_length(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->content_length == 0;
+}
+
+int llhttp__internal__c_test_lenient_flags_7(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 128) == 128;
+}
+
+int llhttp__internal__c_or_flags(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 128;
+ return 0;
+}
+
+int llhttp__internal__c_test_lenient_flags_8(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 64) == 64;
+}
+
+int llhttp__on_chunk_extension_name_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_chunk_extension_value_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_update_finish_3(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->finish = 1;
+ return 0;
+}
+
+int llhttp__internal__c_or_flags_1(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 64;
+ return 0;
+}
+
+int llhttp__internal__c_update_upgrade(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->upgrade = 1;
+ return 0;
+}
+
+int llhttp__internal__c_store_header_state(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ state->header_state = match;
+ return 0;
+}
+
+int llhttp__on_header_field_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_load_header_state(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->header_state;
+}
+
+int llhttp__internal__c_test_flags_4(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->flags & 512) == 512;
+}
+
+int llhttp__internal__c_test_lenient_flags_22(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 2) == 2;
+}
+
+int llhttp__internal__c_or_flags_5(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 1;
+ return 0;
+}
+
+int llhttp__internal__c_update_header_state(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->header_state = 1;
+ return 0;
+}
+
+int llhttp__on_header_value_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_or_flags_6(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 2;
+ return 0;
+}
+
+int llhttp__internal__c_or_flags_7(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 4;
+ return 0;
+}
+
+int llhttp__internal__c_or_flags_8(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 8;
+ return 0;
+}
+
+int llhttp__internal__c_update_header_state_3(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->header_state = 6;
+ return 0;
+}
+
+int llhttp__internal__c_update_header_state_1(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->header_state = 0;
+ return 0;
+}
+
+int llhttp__internal__c_update_header_state_6(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->header_state = 5;
+ return 0;
+}
+
+int llhttp__internal__c_update_header_state_7(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->header_state = 7;
+ return 0;
+}
+
+int llhttp__internal__c_test_flags_2(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->flags & 32) == 32;
+}
+
+int llhttp__internal__c_mul_add_content_length_1(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ /* Multiplication overflow */
+ if (state->content_length > 0xffffffffffffffffULL / 10) {
+ return 1;
+ }
+
+ state->content_length *= 10;
+
+ /* Addition overflow */
+ if (match >= 0) {
+ if (state->content_length > 0xffffffffffffffffULL - match) {
+ return 1;
+ }
+ } else {
+ if (state->content_length < 0ULL - match) {
+ return 1;
+ }
+ }
+ state->content_length += match;
+ return 0;
+}
+
+int llhttp__internal__c_or_flags_17(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 32;
+ return 0;
+}
+
+int llhttp__internal__c_test_flags_3(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->flags & 8) == 8;
+}
+
+int llhttp__internal__c_test_lenient_flags_20(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 8) == 8;
+}
+
+int llhttp__internal__c_or_flags_18(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 512;
+ return 0;
+}
+
+int llhttp__internal__c_and_flags(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags &= -9;
+ return 0;
+}
+
+int llhttp__internal__c_update_header_state_8(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->header_state = 8;
+ return 0;
+}
+
+int llhttp__internal__c_or_flags_20(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 16;
+ return 0;
+}
+
+int llhttp__internal__c_load_method(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->method;
+}
+
+int llhttp__internal__c_store_http_major(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ state->http_major = match;
+ return 0;
+}
+
+int llhttp__internal__c_store_http_minor(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ state->http_minor = match;
+ return 0;
+}
+
+int llhttp__internal__c_test_lenient_flags_24(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 16) == 16;
+}
+
+int llhttp__on_version_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_load_http_major(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->http_major;
+}
+
+int llhttp__internal__c_load_http_minor(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->http_minor;
+}
+
+int llhttp__internal__c_update_status_code(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->status_code = 0;
+ return 0;
+}
+
+int llhttp__internal__c_mul_add_status_code(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ /* Multiplication overflow */
+ if (state->status_code > 0xffff / 10) {
+ return 1;
+ }
+
+ state->status_code *= 10;
+
+ /* Addition overflow */
+ if (match >= 0) {
+ if (state->status_code > 0xffff - match) {
+ return 1;
+ }
+ } else {
+ if (state->status_code < 0 - match) {
+ return 1;
+ }
+ }
+ state->status_code += match;
+ return 0;
+}
+
+int llhttp__on_status_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_update_type(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->type = 1;
+ return 0;
+}
+
+int llhttp__internal__c_update_type_1(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->type = 2;
+ return 0;
+}
+
+int llhttp__internal_init(llhttp__internal_t* state) {
+ memset(state, 0, sizeof(*state));
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start;
+ return 0;
+}
+
+static llparse_state_t llhttp__internal__run(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ int match;
+ switch ((llparse_state_t) (intptr_t) state->_current) {
+ case s_n_llhttp__internal__n_closed:
+ s_n_llhttp__internal__n_closed: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_closed;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_closed;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_closed;
+ }
+ default: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_3;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: {
+ switch (llhttp__after_message_complete(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_update_content_length;
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_finish_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_pause_1:
+ s_n_llhttp__internal__n_pause_1: {
+ state->error = 0x16;
+ state->reason = "Pause on CONNECT/Upgrade";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_is_equal_upgrade:
+ s_n_llhttp__internal__n_invoke_is_equal_upgrade: {
+ switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;
+ default:
+ goto s_n_llhttp__internal__n_pause_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2:
+ s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: {
+ switch (llhttp__on_message_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_is_equal_upgrade;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_13;
+ default:
+ goto s_n_llhttp__internal__n_error_38;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_data_almost_done_1:
+ s_n_llhttp__internal__n_chunk_data_almost_done_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_data_almost_done_1;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_data_almost_done:
+ s_n_llhttp__internal__n_chunk_data_almost_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_data_almost_done;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_6;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_data_almost_done_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_consume_content_length:
+ s_n_llhttp__internal__n_consume_content_length: {
+ size_t avail;
+ uint64_t need;
+
+ avail = endp - p;
+ need = state->content_length;
+ if (avail >= need) {
+ p += need;
+ state->content_length = 0;
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_body;
+ }
+
+ state->content_length -= avail;
+ return s_n_llhttp__internal__n_consume_content_length;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_body:
+ s_n_llhttp__internal__n_span_start_llhttp__on_body: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_body;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_body;
+ goto s_n_llhttp__internal__n_consume_content_length;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_is_equal_content_length:
+ s_n_llhttp__internal__n_invoke_is_equal_content_length: {
+ switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_body;
+ default:
+ goto s_n_llhttp__internal__n_invoke_or_flags;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_size_almost_done:
+ s_n_llhttp__internal__n_chunk_size_almost_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_size_almost_done;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_8;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_test_lenient_flags_9:
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_9: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ default:
+ goto s_n_llhttp__internal__n_error_20;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete: {
+ switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_9;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_5;
+ default:
+ goto s_n_llhttp__internal__n_error_19;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1: {
+ switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_6;
+ default:
+ goto s_n_llhttp__internal__n_error_21;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2: {
+ switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_chunk_extensions;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_7;
+ default:
+ goto s_n_llhttp__internal__n_error_22;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_test_lenient_flags_10:
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_10: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ default:
+ goto s_n_llhttp__internal__n_error_25;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete: {
+ switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_10;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_8;
+ default:
+ goto s_n_llhttp__internal__n_error_24;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1: {
+ switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_9;
+ default:
+ goto s_n_llhttp__internal__n_error_26;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_extension_quoted_value_done:
+ s_n_llhttp__internal__n_chunk_extension_quoted_value_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_extension_quoted_value_done;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_11;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ }
+ case ';': {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extensions;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_29;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2: {
+ switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_chunk_extension_quoted_value_done;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_10;
+ default:
+ goto s_n_llhttp__internal__n_error_27;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_30:
+ s_n_llhttp__internal__n_error_30: {
+ state->error = 0x2;
+ state->reason = "Invalid quoted-pair in chunk extensions quoted value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair:
+ s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extension_quoted_value;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_31:
+ s_n_llhttp__internal__n_error_31: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk extensions quoted value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_extension_quoted_value:
+ s_n_llhttp__internal__n_chunk_extension_quoted_value: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_extension_quoted_value;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extension_quoted_value;
+ }
+ case 2: {
+ p++;
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2;
+ }
+ case 3: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3: {
+ switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_chunk_extensions;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_11;
+ default:
+ goto s_n_llhttp__internal__n_error_32;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_33:
+ s_n_llhttp__internal__n_error_33: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk extensions value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_extension_value:
+ s_n_llhttp__internal__n_chunk_extension_value: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 4, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 5, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_extension_value;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1;
+ }
+ case 3: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extension_value;
+ }
+ case 4: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extension_quoted_value;
+ }
+ case 5: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value:
+ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_chunk_extension_value;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_34:
+ s_n_llhttp__internal__n_error_34: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk extensions name";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_extension_name:
+ s_n_llhttp__internal__n_chunk_extension_name: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 0, 5, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_extension_name;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1;
+ }
+ case 3: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extension_name;
+ }
+ case 4: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2;
+ }
+ case 5: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name:
+ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_chunk_extension_name;
+ goto s_n_llhttp__internal__n_chunk_extension_name;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_extensions:
+ s_n_llhttp__internal__n_chunk_extensions: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_extensions;
+ }
+ switch (*p) {
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_error_17;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_error_18;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_size_otherwise:
+ s_n_llhttp__internal__n_chunk_size_otherwise: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_size_otherwise;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4;
+ }
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_5;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4;
+ }
+ case ';': {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extensions;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_35;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_size:
+ s_n_llhttp__internal__n_chunk_size: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_size;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'A': {
+ p++;
+ match = 10;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'B': {
+ p++;
+ match = 11;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'C': {
+ p++;
+ match = 12;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'D': {
+ p++;
+ match = 13;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'E': {
+ p++;
+ match = 14;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'F': {
+ p++;
+ match = 15;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'a': {
+ p++;
+ match = 10;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'b': {
+ p++;
+ match = 11;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'c': {
+ p++;
+ match = 12;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'd': {
+ p++;
+ match = 13;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'e': {
+ p++;
+ match = 14;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'f': {
+ p++;
+ match = 15;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_chunk_size_otherwise;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_size_digit:
+ s_n_llhttp__internal__n_chunk_size_digit: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_size_digit;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'A': {
+ p++;
+ match = 10;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'B': {
+ p++;
+ match = 11;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'C': {
+ p++;
+ match = 12;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'D': {
+ p++;
+ match = 13;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'E': {
+ p++;
+ match = 14;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'F': {
+ p++;
+ match = 15;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'a': {
+ p++;
+ match = 10;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'b': {
+ p++;
+ match = 11;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'c': {
+ p++;
+ match = 12;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'd': {
+ p++;
+ match = 13;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'e': {
+ p++;
+ match = 14;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'f': {
+ p++;
+ match = 15;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_37;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_update_content_length_1:
+ s_n_llhttp__internal__n_invoke_update_content_length_1: {
+ switch (llhttp__internal__c_update_content_length(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_chunk_size_digit;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_consume_content_length_1:
+ s_n_llhttp__internal__n_consume_content_length_1: {
+ size_t avail;
+ uint64_t need;
+
+ avail = endp - p;
+ need = state->content_length;
+ if (avail >= need) {
+ p += need;
+ state->content_length = 0;
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1;
+ }
+
+ state->content_length -= avail;
+ return s_n_llhttp__internal__n_consume_content_length_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_body_1:
+ s_n_llhttp__internal__n_span_start_llhttp__on_body_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_body_1;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_body;
+ goto s_n_llhttp__internal__n_consume_content_length_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_eof:
+ s_n_llhttp__internal__n_eof: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_eof;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_eof;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_body_2:
+ s_n_llhttp__internal__n_span_start_llhttp__on_body_2: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_body_2;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_body;
+ goto s_n_llhttp__internal__n_eof;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: {
+ switch (llhttp__after_headers_complete(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1;
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_update_content_length_1;
+ case 3:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1;
+ case 4:
+ goto s_n_llhttp__internal__n_invoke_update_finish_3;
+ case 5:
+ goto s_n_llhttp__internal__n_error_39;
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_5:
+ s_n_llhttp__internal__n_error_5: {
+ state->error = 0xa;
+ state->reason = "Invalid header field char";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_headers_almost_done:
+ s_n_llhttp__internal__n_headers_almost_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_headers_almost_done;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_flags_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_12;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_colon_discard_ws:
+ s_n_llhttp__internal__n_header_field_colon_discard_ws: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_colon_discard_ws;
+ }
+ switch (*p) {
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_colon_discard_ws;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_field_colon;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: {
+ switch (llhttp__on_header_value_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_header_field_start;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_18;
+ default:
+ goto s_n_llhttp__internal__n_error_48;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_header_value:
+ s_n_llhttp__internal__n_span_start_llhttp__on_header_value: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_header_value;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_header_value;
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_discard_lws:
+ s_n_llhttp__internal__n_header_value_discard_lws: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_discard_lws;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_load_header_state_1;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_discard_ws_almost_done:
+ s_n_llhttp__internal__n_header_value_discard_ws_almost_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_discard_ws_almost_done;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_discard_lws;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_16;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_lws:
+ s_n_llhttp__internal__n_header_value_lws: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_lws;
+ }
+ switch (*p) {
+ case 9: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18;
+ }
+ case ' ': {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_load_header_state_5;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_almost_done:
+ s_n_llhttp__internal__n_header_value_almost_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_almost_done;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_lws;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_53;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_test_lenient_flags_17:
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_17: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_value_almost_done;
+ default:
+ goto s_n_llhttp__internal__n_error_51;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_lenient:
+ s_n_llhttp__internal__n_header_value_lenient: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_lenient;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5;
+ }
+ default: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_lenient;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_54:
+ s_n_llhttp__internal__n_error_54: {
+ state->error = 0xa;
+ state->reason = "Invalid header value char";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_otherwise:
+ s_n_llhttp__internal__n_header_value_otherwise: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_19;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_connection_token:
+ s_n_llhttp__internal__n_header_value_connection_token: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ case 2: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_connection_ws:
+ s_n_llhttp__internal__n_header_value_connection_ws: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_connection_ws;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection_ws;
+ }
+ case ',': {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_load_header_state_6;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_5;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_connection_1:
+ s_n_llhttp__internal__n_header_value_connection_1: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_connection_1;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob2, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_update_header_state_3;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_value_connection_1;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_connection_2:
+ s_n_llhttp__internal__n_header_value_connection_2: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_connection_2;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob3, 9);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_update_header_state_6;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_value_connection_2;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_connection_3:
+ s_n_llhttp__internal__n_header_value_connection_3: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_connection_3;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob4, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_update_header_state_7;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_value_connection_3;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_connection:
+ s_n_llhttp__internal__n_header_value_connection: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_connection;
+ }
+ switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection;
+ }
+ case 'c': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection_1;
+ }
+ case 'k': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection_2;
+ }
+ case 'u': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection_3;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_56:
+ s_n_llhttp__internal__n_error_56: {
+ state->error = 0xb;
+ state->reason = "Content-Length overflow";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_57:
+ s_n_llhttp__internal__n_error_57: {
+ state->error = 0xb;
+ state->reason = "Invalid character in Content-Length";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_content_length_ws:
+ s_n_llhttp__internal__n_header_value_content_length_ws: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_content_length_ws;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_invoke_or_flags_17;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_invoke_or_flags_17;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_content_length_ws;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_content_length:
+ s_n_llhttp__internal__n_header_value_content_length: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_content_length;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_value_content_length_ws;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_59:
+ s_n_llhttp__internal__n_error_59: {
+ state->error = 0xf;
+ state->reason = "Invalid `Transfer-Encoding` header value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_58:
+ s_n_llhttp__internal__n_error_58: {
+ state->error = 0xf;
+ state->reason = "Invalid `Transfer-Encoding` header value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_te_token_ows:
+ s_n_llhttp__internal__n_header_value_te_token_ows: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_te_token_ows;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_te_token_ows;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_te_token_ows;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_value_te_chunked;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value:
+ s_n_llhttp__internal__n_header_value: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value;
+ }
+ #ifdef __SSE4_2__
+ if (endp - p >= 16) {
+ __m128i ranges;
+ __m128i input;
+ int avail;
+ int match_len;
+
+ /* Load input */
+ input = _mm_loadu_si128((__m128i const*) p);
+ ranges = _mm_loadu_si128((__m128i const*) llparse_blob6);
+
+ /* Find first character that does not match `ranges` */
+ match_len = _mm_cmpestri(ranges, 6,
+ input, 16,
+ _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |
+ _SIDD_NEGATIVE_POLARITY);
+
+ if (match_len != 0) {
+ p += match_len;
+ goto s_n_llhttp__internal__n_header_value;
+ }
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ #endif /* __SSE4_2__ */
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_te_token:
+ s_n_llhttp__internal__n_header_value_te_token: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_te_token;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_te_token;
+ }
+ case 2: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_te_token_ows;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_9;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_te_chunked_last:
+ s_n_llhttp__internal__n_header_value_te_chunked_last: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_te_chunked_last;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_8;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_8;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_te_chunked_last;
+ }
+ case ',': {
+ goto s_n_llhttp__internal__n_invoke_load_type_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_value_te_token;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_te_chunked:
+ s_n_llhttp__internal__n_header_value_te_chunked: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_te_chunked;
+ }
+ match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 7);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_te_chunked_last;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_value_te_chunked;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_header_value_te_token;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1:
+ s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_header_value;
+ goto s_n_llhttp__internal__n_invoke_load_header_state_3;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_discard_ws:
+ s_n_llhttp__internal__n_header_value_discard_ws: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_14;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_load_header_state:
+ s_n_llhttp__internal__n_invoke_load_header_state: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_test_flags_4;
+ case 3:
+ goto s_n_llhttp__internal__n_invoke_test_flags_5;
+ default:
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: {
+ switch (llhttp__on_header_field_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_load_header_state;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_19;
+ default:
+ goto s_n_llhttp__internal__n_error_45;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_general_otherwise:
+ s_n_llhttp__internal__n_header_field_general_otherwise: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_general_otherwise;
+ }
+ switch (*p) {
+ case ':': {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_62;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_general:
+ s_n_llhttp__internal__n_header_field_general: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_general;
+ }
+ #ifdef __SSE4_2__
+ if (endp - p >= 16) {
+ __m128i ranges;
+ __m128i input;
+ int avail;
+ int match_len;
+
+ /* Load input */
+ input = _mm_loadu_si128((__m128i const*) p);
+ ranges = _mm_loadu_si128((__m128i const*) llparse_blob7);
+
+ /* Find first character that does not match `ranges` */
+ match_len = _mm_cmpestri(ranges, 16,
+ input, 16,
+ _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |
+ _SIDD_NEGATIVE_POLARITY);
+
+ if (match_len != 0) {
+ p += match_len;
+ goto s_n_llhttp__internal__n_header_field_general;
+ }
+ ranges = _mm_loadu_si128((__m128i const*) llparse_blob8);
+
+ /* Find first character that does not match `ranges` */
+ match_len = _mm_cmpestri(ranges, 2,
+ input, 16,
+ _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |
+ _SIDD_NEGATIVE_POLARITY);
+
+ if (match_len != 0) {
+ p += match_len;
+ goto s_n_llhttp__internal__n_header_field_general;
+ }
+ goto s_n_llhttp__internal__n_header_field_general_otherwise;
+ }
+ #endif /* __SSE4_2__ */
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_general;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_field_general_otherwise;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_colon:
+ s_n_llhttp__internal__n_header_field_colon: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_colon;
+ }
+ switch (*p) {
+ case ' ': {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_13;
+ }
+ case ':': {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_10;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_3:
+ s_n_llhttp__internal__n_header_field_3: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_3;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob1, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_header_state;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_field_3;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_4:
+ s_n_llhttp__internal__n_header_field_4: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_4;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob9, 10);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_header_state;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_field_4;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_2:
+ s_n_llhttp__internal__n_header_field_2: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_2;
+ }
+ switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) {
+ case 'n': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_3;
+ }
+ case 't': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_4;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_1:
+ s_n_llhttp__internal__n_header_field_1: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_1;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob0, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_2;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_field_1;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_5:
+ s_n_llhttp__internal__n_header_field_5: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_5;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob10, 15);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_header_state;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_field_5;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_6:
+ s_n_llhttp__internal__n_header_field_6: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_6;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob11, 16);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_store_header_state;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_field_6;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_7:
+ s_n_llhttp__internal__n_header_field_7: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_7;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob12, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_store_header_state;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_field_7;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field:
+ s_n_llhttp__internal__n_header_field: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field;
+ }
+ switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) {
+ case 'c': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_1;
+ }
+ case 'p': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_5;
+ }
+ case 't': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_6;
+ }
+ case 'u': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_7;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_header_field:
+ s_n_llhttp__internal__n_span_start_llhttp__on_header_field: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_header_field;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_header_field;
+ goto s_n_llhttp__internal__n_header_field;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_start:
+ s_n_llhttp__internal__n_header_field_start: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_start;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_1;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_headers_almost_done;
+ }
+ case ':': {
+ goto s_n_llhttp__internal__n_error_44;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_headers_start:
+ s_n_llhttp__internal__n_headers_start: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_headers_start;
+ }
+ switch (*p) {
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_field_start;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_to_http_09:
+ s_n_llhttp__internal__n_url_to_http_09: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_to_http_09;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_http_major;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_skip_to_http09:
+ s_n_llhttp__internal__n_url_skip_to_http09: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_skip_to_http09;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ default: {
+ p++;
+ goto s_n_llhttp__internal__n_url_to_http_09;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_skip_lf_to_http09_1:
+ s_n_llhttp__internal__n_url_skip_lf_to_http09_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_skip_lf_to_http09_1;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_url_to_http_09;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_63;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_skip_lf_to_http09:
+ s_n_llhttp__internal__n_url_skip_lf_to_http09: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_url_skip_lf_to_http09_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_63;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_pri_upgrade:
+ s_n_llhttp__internal__n_req_pri_upgrade: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_pri_upgrade;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 10);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_error_71;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_req_pri_upgrade;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_72;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_complete_crlf:
+ s_n_llhttp__internal__n_req_http_complete_crlf: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_complete_crlf;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_headers_start;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_26;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_complete:
+ s_n_llhttp__internal__n_req_http_complete: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_complete;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_25;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_req_http_complete_crlf;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_70;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_load_method_1:
+ s_n_llhttp__internal__n_invoke_load_method_1: {
+ switch (llhttp__internal__c_load_method(state, p, endp)) {
+ case 34:
+ goto s_n_llhttp__internal__n_req_pri_upgrade;
+ default:
+ goto s_n_llhttp__internal__n_req_http_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__on_version_complete: {
+ switch (llhttp__on_version_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_load_method_1;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_21;
+ default:
+ goto s_n_llhttp__internal__n_error_67;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_66:
+ s_n_llhttp__internal__n_error_66: {
+ state->error = 0x9;
+ state->reason = "Invalid HTTP version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_73:
+ s_n_llhttp__internal__n_error_73: {
+ state->error = 0x9;
+ state->reason = "Invalid minor version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_minor:
+ s_n_llhttp__internal__n_req_http_minor: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_minor;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_2;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_74:
+ s_n_llhttp__internal__n_error_74: {
+ state->error = 0x9;
+ state->reason = "Expected dot";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_dot:
+ s_n_llhttp__internal__n_req_http_dot: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_dot;
+ }
+ switch (*p) {
+ case '.': {
+ p++;
+ goto s_n_llhttp__internal__n_req_http_minor;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_3;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_75:
+ s_n_llhttp__internal__n_error_75: {
+ state->error = 0x9;
+ state->reason = "Invalid major version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_major:
+ s_n_llhttp__internal__n_req_http_major: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_major;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_4;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_version:
+ s_n_llhttp__internal__n_span_start_llhttp__on_version: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_version;
+ goto s_n_llhttp__internal__n_req_http_major;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_start_1:
+ s_n_llhttp__internal__n_req_http_start_1: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_start_1;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob13, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_load_method;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_req_http_start_1;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_78;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_start_2:
+ s_n_llhttp__internal__n_req_http_start_2: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_start_2;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_load_method_2;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_req_http_start_2;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_78;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_start_3:
+ s_n_llhttp__internal__n_req_http_start_3: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_start_3;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_load_method_3;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_req_http_start_3;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_78;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_start:
+ s_n_llhttp__internal__n_req_http_start: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_start;
+ }
+ switch (*p) {
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_req_http_start;
+ }
+ case 'H': {
+ p++;
+ goto s_n_llhttp__internal__n_req_http_start_1;
+ }
+ case 'I': {
+ p++;
+ goto s_n_llhttp__internal__n_req_http_start_2;
+ }
+ case 'R': {
+ p++;
+ goto s_n_llhttp__internal__n_req_http_start_3;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_78;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_to_http:
+ s_n_llhttp__internal__n_url_to_http: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_to_http;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_skip_to_http:
+ s_n_llhttp__internal__n_url_skip_to_http: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_skip_to_http;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ default: {
+ p++;
+ goto s_n_llhttp__internal__n_url_to_http;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_fragment:
+ s_n_llhttp__internal__n_url_fragment: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_fragment;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6;
+ }
+ case 3: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7;
+ }
+ case 4: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8;
+ }
+ case 5: {
+ p++;
+ goto s_n_llhttp__internal__n_url_fragment;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_79;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_end_stub_query_3:
+ s_n_llhttp__internal__n_span_end_stub_query_3: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_end_stub_query_3;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_url_fragment;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_query:
+ s_n_llhttp__internal__n_url_query: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_query;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9;
+ }
+ case 3: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10;
+ }
+ case 4: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11;
+ }
+ case 5: {
+ p++;
+ goto s_n_llhttp__internal__n_url_query;
+ }
+ case 6: {
+ goto s_n_llhttp__internal__n_span_end_stub_query_3;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_80;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_query_or_fragment:
+ s_n_llhttp__internal__n_url_query_or_fragment: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_query_or_fragment;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 10: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4;
+ }
+ case ' ': {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5;
+ }
+ case '#': {
+ p++;
+ goto s_n_llhttp__internal__n_url_fragment;
+ }
+ case '?': {
+ p++;
+ goto s_n_llhttp__internal__n_url_query;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_81;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_path:
+ s_n_llhttp__internal__n_url_path: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_path;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ p++;
+ goto s_n_llhttp__internal__n_url_path;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_url_query_or_fragment;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_stub_path_2:
+ s_n_llhttp__internal__n_span_start_stub_path_2: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_stub_path_2;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_url_path;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_stub_path:
+ s_n_llhttp__internal__n_span_start_stub_path: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_stub_path;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_url_path;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_stub_path_1:
+ s_n_llhttp__internal__n_span_start_stub_path_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_stub_path_1;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_url_path;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_server_with_at:
+ s_n_llhttp__internal__n_url_server_with_at: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7,
+ 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5,
+ 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_server_with_at;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12;
+ }
+ case 3: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13;
+ }
+ case 4: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14;
+ }
+ case 5: {
+ p++;
+ goto s_n_llhttp__internal__n_url_server;
+ }
+ case 6: {
+ goto s_n_llhttp__internal__n_span_start_stub_path_1;
+ }
+ case 7: {
+ p++;
+ goto s_n_llhttp__internal__n_url_query;
+ }
+ case 8: {
+ p++;
+ goto s_n_llhttp__internal__n_error_82;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_83;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_server:
+ s_n_llhttp__internal__n_url_server: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7,
+ 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5,
+ 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_server;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url;
+ }
+ case 3: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1;
+ }
+ case 4: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2;
+ }
+ case 5: {
+ p++;
+ goto s_n_llhttp__internal__n_url_server;
+ }
+ case 6: {
+ goto s_n_llhttp__internal__n_span_start_stub_path;
+ }
+ case 7: {
+ p++;
+ goto s_n_llhttp__internal__n_url_query;
+ }
+ case 8: {
+ p++;
+ goto s_n_llhttp__internal__n_url_server_with_at;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_84;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_schema_delim_1:
+ s_n_llhttp__internal__n_url_schema_delim_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_schema_delim_1;
+ }
+ switch (*p) {
+ case '/': {
+ p++;
+ goto s_n_llhttp__internal__n_url_server;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_85;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_schema_delim:
+ s_n_llhttp__internal__n_url_schema_delim: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_schema_delim;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case '/': {
+ p++;
+ goto s_n_llhttp__internal__n_url_schema_delim_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_85;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_end_stub_schema:
+ s_n_llhttp__internal__n_span_end_stub_schema: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_end_stub_schema;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_url_schema_delim;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_schema:
+ s_n_llhttp__internal__n_url_schema: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_schema;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_stub_schema;
+ }
+ case 3: {
+ p++;
+ goto s_n_llhttp__internal__n_url_schema;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_86;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_start:
+ s_n_llhttp__internal__n_url_start: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_start;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_start_stub_path_2;
+ }
+ case 3: {
+ goto s_n_llhttp__internal__n_url_schema;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_87;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_url_1:
+ s_n_llhttp__internal__n_span_start_llhttp__on_url_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_url_1;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_url;
+ goto s_n_llhttp__internal__n_url_start;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_entry_normal:
+ s_n_llhttp__internal__n_url_entry_normal: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_entry_normal;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_url:
+ s_n_llhttp__internal__n_span_start_llhttp__on_url: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_url;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_url;
+ goto s_n_llhttp__internal__n_url_server;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_entry_connect:
+ s_n_llhttp__internal__n_url_entry_connect: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_entry_connect;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_url;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_spaces_before_url:
+ s_n_llhttp__internal__n_req_spaces_before_url: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_spaces_before_url;
+ }
+ switch (*p) {
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_req_spaces_before_url;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_is_equal_method;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_first_space_before_url:
+ s_n_llhttp__internal__n_req_first_space_before_url: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_first_space_before_url;
+ }
+ switch (*p) {
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_req_spaces_before_url;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_88;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1:
+ s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1: {
+ switch (llhttp__on_method_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_req_first_space_before_url;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_26;
+ default:
+ goto s_n_llhttp__internal__n_error_107;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_2:
+ s_n_llhttp__internal__n_after_start_req_2: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_2;
+ }
+ switch (*p) {
+ case 'L': {
+ p++;
+ match = 19;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_3:
+ s_n_llhttp__internal__n_after_start_req_3: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_3;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 36;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_3;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_1:
+ s_n_llhttp__internal__n_after_start_req_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_1;
+ }
+ switch (*p) {
+ case 'C': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_2;
+ }
+ case 'N': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_3;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_4:
+ s_n_llhttp__internal__n_after_start_req_4: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_4;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 16;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_4;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_6:
+ s_n_llhttp__internal__n_after_start_req_6: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_6;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 22;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_6;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_8:
+ s_n_llhttp__internal__n_after_start_req_8: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_8;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_8;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_9:
+ s_n_llhttp__internal__n_after_start_req_9: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_9;
+ }
+ switch (*p) {
+ case 'Y': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_7:
+ s_n_llhttp__internal__n_after_start_req_7: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_7;
+ }
+ switch (*p) {
+ case 'N': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_8;
+ }
+ case 'P': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_9;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_5:
+ s_n_llhttp__internal__n_after_start_req_5: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_5;
+ }
+ switch (*p) {
+ case 'H': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_6;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_7;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_12:
+ s_n_llhttp__internal__n_after_start_req_12: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_12;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_12;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_13:
+ s_n_llhttp__internal__n_after_start_req_13: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_13;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 5);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 35;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_13;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_11:
+ s_n_llhttp__internal__n_after_start_req_11: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_11;
+ }
+ switch (*p) {
+ case 'L': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_12;
+ }
+ case 'S': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_13;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_10:
+ s_n_llhttp__internal__n_after_start_req_10: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_10;
+ }
+ switch (*p) {
+ case 'E': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_11;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_14:
+ s_n_llhttp__internal__n_after_start_req_14: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_14;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 45;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_14;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_17:
+ s_n_llhttp__internal__n_after_start_req_17: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_17;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 9);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 41;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_17;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_16:
+ s_n_llhttp__internal__n_after_start_req_16: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_16;
+ }
+ switch (*p) {
+ case '_': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_17;
+ }
+ default: {
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_15:
+ s_n_llhttp__internal__n_after_start_req_15: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_15;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_16;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_15;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_18:
+ s_n_llhttp__internal__n_after_start_req_18: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_18;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_18;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_20:
+ s_n_llhttp__internal__n_after_start_req_20: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_20;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 31;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_20;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_21:
+ s_n_llhttp__internal__n_after_start_req_21: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_21;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_21;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_19:
+ s_n_llhttp__internal__n_after_start_req_19: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_19;
+ }
+ switch (*p) {
+ case 'I': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_20;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_21;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_23:
+ s_n_llhttp__internal__n_after_start_req_23: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_23;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 24;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_23;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_24:
+ s_n_llhttp__internal__n_after_start_req_24: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_24;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 23;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_24;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_26:
+ s_n_llhttp__internal__n_after_start_req_26: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_26;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 7);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 21;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_26;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_28:
+ s_n_llhttp__internal__n_after_start_req_28: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_28;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 30;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_28;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_29:
+ s_n_llhttp__internal__n_after_start_req_29: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_29;
+ }
+ switch (*p) {
+ case 'L': {
+ p++;
+ match = 10;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_27:
+ s_n_llhttp__internal__n_after_start_req_27: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_27;
+ }
+ switch (*p) {
+ case 'A': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_28;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_29;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_25:
+ s_n_llhttp__internal__n_after_start_req_25: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_25;
+ }
+ switch (*p) {
+ case 'A': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_26;
+ }
+ case 'C': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_27;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_30:
+ s_n_llhttp__internal__n_after_start_req_30: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_30;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 11;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_30;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_22:
+ s_n_llhttp__internal__n_after_start_req_22: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_22;
+ }
+ switch (*p) {
+ case '-': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_23;
+ }
+ case 'E': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_24;
+ }
+ case 'K': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_25;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_30;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_31:
+ s_n_llhttp__internal__n_after_start_req_31: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_31;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 5);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 25;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_31;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_32:
+ s_n_llhttp__internal__n_after_start_req_32: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_32;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_32;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_35:
+ s_n_llhttp__internal__n_after_start_req_35: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_35;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 28;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_35;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_36:
+ s_n_llhttp__internal__n_after_start_req_36: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_36;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 39;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_36;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_34:
+ s_n_llhttp__internal__n_after_start_req_34: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_34;
+ }
+ switch (*p) {
+ case 'T': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_35;
+ }
+ case 'U': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_36;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_37:
+ s_n_llhttp__internal__n_after_start_req_37: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_37;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 38;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_37;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_38:
+ s_n_llhttp__internal__n_after_start_req_38: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_38;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_38;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_42:
+ s_n_llhttp__internal__n_after_start_req_42: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_42;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 12;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_42;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_43:
+ s_n_llhttp__internal__n_after_start_req_43: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_43;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 13;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_43;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_41:
+ s_n_llhttp__internal__n_after_start_req_41: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_41;
+ }
+ switch (*p) {
+ case 'F': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_42;
+ }
+ case 'P': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_43;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_40:
+ s_n_llhttp__internal__n_after_start_req_40: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_40;
+ }
+ switch (*p) {
+ case 'P': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_41;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_39:
+ s_n_llhttp__internal__n_after_start_req_39: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_39;
+ }
+ switch (*p) {
+ case 'I': {
+ p++;
+ match = 34;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_40;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_45:
+ s_n_llhttp__internal__n_after_start_req_45: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_45;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 29;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_45;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_44:
+ s_n_llhttp__internal__n_after_start_req_44: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_44;
+ }
+ switch (*p) {
+ case 'R': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_45;
+ }
+ case 'T': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_33:
+ s_n_llhttp__internal__n_after_start_req_33: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_33;
+ }
+ switch (*p) {
+ case 'A': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_34;
+ }
+ case 'L': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_37;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_38;
+ }
+ case 'R': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_39;
+ }
+ case 'U': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_44;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_46:
+ s_n_llhttp__internal__n_after_start_req_46: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_46;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 46;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_46;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_49:
+ s_n_llhttp__internal__n_after_start_req_49: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_49;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 17;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_49;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_50:
+ s_n_llhttp__internal__n_after_start_req_50: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_50;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 44;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_50;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_51:
+ s_n_llhttp__internal__n_after_start_req_51: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_51;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 5);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 43;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_51;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_52:
+ s_n_llhttp__internal__n_after_start_req_52: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_52;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 20;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_52;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_48:
+ s_n_llhttp__internal__n_after_start_req_48: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_48;
+ }
+ switch (*p) {
+ case 'B': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_49;
+ }
+ case 'C': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_50;
+ }
+ case 'D': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_51;
+ }
+ case 'P': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_52;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_47:
+ s_n_llhttp__internal__n_after_start_req_47: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_47;
+ }
+ switch (*p) {
+ case 'E': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_48;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_55:
+ s_n_llhttp__internal__n_after_start_req_55: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_55;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 14;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_55;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_57:
+ s_n_llhttp__internal__n_after_start_req_57: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_57;
+ }
+ switch (*p) {
+ case 'P': {
+ p++;
+ match = 37;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_58:
+ s_n_llhttp__internal__n_after_start_req_58: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_58;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 9);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 42;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_58;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_56:
+ s_n_llhttp__internal__n_after_start_req_56: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_56;
+ }
+ switch (*p) {
+ case 'U': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_57;
+ }
+ case '_': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_58;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_54:
+ s_n_llhttp__internal__n_after_start_req_54: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_54;
+ }
+ switch (*p) {
+ case 'A': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_55;
+ }
+ case 'T': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_56;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_59:
+ s_n_llhttp__internal__n_after_start_req_59: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_59;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 33;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_59;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_60:
+ s_n_llhttp__internal__n_after_start_req_60: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_60;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob51, 7);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 26;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_60;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_53:
+ s_n_llhttp__internal__n_after_start_req_53: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_53;
+ }
+ switch (*p) {
+ case 'E': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_54;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_59;
+ }
+ case 'U': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_60;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_62:
+ s_n_llhttp__internal__n_after_start_req_62: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_62;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob52, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 40;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_62;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_63:
+ s_n_llhttp__internal__n_after_start_req_63: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_63;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob53, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_63;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_61:
+ s_n_llhttp__internal__n_after_start_req_61: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_61;
+ }
+ switch (*p) {
+ case 'E': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_62;
+ }
+ case 'R': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_63;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_66:
+ s_n_llhttp__internal__n_after_start_req_66: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_66;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob54, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 18;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_66;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_68:
+ s_n_llhttp__internal__n_after_start_req_68: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_68;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob55, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 32;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_68;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_69:
+ s_n_llhttp__internal__n_after_start_req_69: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_69;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob56, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 15;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_69;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_67:
+ s_n_llhttp__internal__n_after_start_req_67: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_67;
+ }
+ switch (*p) {
+ case 'I': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_68;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_69;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_70:
+ s_n_llhttp__internal__n_after_start_req_70: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_70;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob57, 8);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 27;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_70;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_65:
+ s_n_llhttp__internal__n_after_start_req_65: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_65;
+ }
+ switch (*p) {
+ case 'B': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_66;
+ }
+ case 'L': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_67;
+ }
+ case 'S': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_70;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_64:
+ s_n_llhttp__internal__n_after_start_req_64: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_64;
+ }
+ switch (*p) {
+ case 'N': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_65;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req:
+ s_n_llhttp__internal__n_after_start_req: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req;
+ }
+ switch (*p) {
+ case 'A': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_1;
+ }
+ case 'B': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_4;
+ }
+ case 'C': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_5;
+ }
+ case 'D': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_10;
+ }
+ case 'F': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_14;
+ }
+ case 'G': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_15;
+ }
+ case 'H': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_18;
+ }
+ case 'L': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_19;
+ }
+ case 'M': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_22;
+ }
+ case 'N': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_31;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_32;
+ }
+ case 'P': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_33;
+ }
+ case 'Q': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_46;
+ }
+ case 'R': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_47;
+ }
+ case 'S': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_53;
+ }
+ case 'T': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_61;
+ }
+ case 'U': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_64;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_method_1:
+ s_n_llhttp__internal__n_span_start_llhttp__on_method_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_method_1;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_method;
+ goto s_n_llhttp__internal__n_after_start_req;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_line_almost_done:
+ s_n_llhttp__internal__n_res_line_almost_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_line_almost_done;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_29;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_test_lenient_flags_30:
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_30: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;
+ default:
+ goto s_n_llhttp__internal__n_error_94;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_status:
+ s_n_llhttp__internal__n_res_status: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_status;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_status;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1;
+ }
+ default: {
+ p++;
+ goto s_n_llhttp__internal__n_res_status;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_status:
+ s_n_llhttp__internal__n_span_start_llhttp__on_status: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_status;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_status;
+ goto s_n_llhttp__internal__n_res_status;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_status_code_otherwise:
+ s_n_llhttp__internal__n_res_status_code_otherwise: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_status_code_otherwise;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_28;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_res_line_almost_done;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_status;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_95;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_status_code_digit_3:
+ s_n_llhttp__internal__n_res_status_code_digit_3: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_status_code_digit_3;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_97;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_status_code_digit_2:
+ s_n_llhttp__internal__n_res_status_code_digit_2: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_status_code_digit_2;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_99;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_status_code_digit_1:
+ s_n_llhttp__internal__n_res_status_code_digit_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_status_code_digit_1;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_101;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_after_version:
+ s_n_llhttp__internal__n_res_after_version: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_after_version;
+ }
+ switch (*p) {
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_update_status_code;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_102;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1:
+ s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1: {
+ switch (llhttp__on_version_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_res_after_version;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_25;
+ default:
+ goto s_n_llhttp__internal__n_error_90;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_89:
+ s_n_llhttp__internal__n_error_89: {
+ state->error = 0x9;
+ state->reason = "Invalid HTTP version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_103:
+ s_n_llhttp__internal__n_error_103: {
+ state->error = 0x9;
+ state->reason = "Invalid minor version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_http_minor:
+ s_n_llhttp__internal__n_res_http_minor: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_http_minor;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_7;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_104:
+ s_n_llhttp__internal__n_error_104: {
+ state->error = 0x9;
+ state->reason = "Expected dot";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_http_dot:
+ s_n_llhttp__internal__n_res_http_dot: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_http_dot;
+ }
+ switch (*p) {
+ case '.': {
+ p++;
+ goto s_n_llhttp__internal__n_res_http_minor;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_8;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_105:
+ s_n_llhttp__internal__n_error_105: {
+ state->error = 0x9;
+ state->reason = "Invalid major version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_http_major:
+ s_n_llhttp__internal__n_res_http_major: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_http_major;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_9;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_version_1:
+ s_n_llhttp__internal__n_span_start_llhttp__on_version_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_version_1;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_version;
+ goto s_n_llhttp__internal__n_res_http_major;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_start_res:
+ s_n_llhttp__internal__n_start_res: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_start_res;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob58, 5);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_start_res;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_109;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__on_method_complete: {
+ switch (llhttp__on_method_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_req_first_space_before_url;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_23;
+ default:
+ goto s_n_llhttp__internal__n_error_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_or_res_method_2:
+ s_n_llhttp__internal__n_req_or_res_method_2: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_or_res_method_2;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob59, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_method;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_req_or_res_method_2;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_106;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_update_type_1:
+ s_n_llhttp__internal__n_invoke_update_type_1: {
+ switch (llhttp__internal__c_update_type_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_or_res_method_3:
+ s_n_llhttp__internal__n_req_or_res_method_3: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_or_res_method_3;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob60, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_req_or_res_method_3;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_106;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_or_res_method_1:
+ s_n_llhttp__internal__n_req_or_res_method_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_or_res_method_1;
+ }
+ switch (*p) {
+ case 'E': {
+ p++;
+ goto s_n_llhttp__internal__n_req_or_res_method_2;
+ }
+ case 'T': {
+ p++;
+ goto s_n_llhttp__internal__n_req_or_res_method_3;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_106;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_or_res_method:
+ s_n_llhttp__internal__n_req_or_res_method: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_or_res_method;
+ }
+ switch (*p) {
+ case 'H': {
+ p++;
+ goto s_n_llhttp__internal__n_req_or_res_method_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_106;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_method:
+ s_n_llhttp__internal__n_span_start_llhttp__on_method: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_method;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_method;
+ goto s_n_llhttp__internal__n_req_or_res_method;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_start_req_or_res:
+ s_n_llhttp__internal__n_start_req_or_res: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_start_req_or_res;
+ }
+ switch (*p) {
+ case 'H': {
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_method;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_type_2;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_load_type:
+ s_n_llhttp__internal__n_invoke_load_type: {
+ switch (llhttp__internal__c_load_type(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1;
+ case 2:
+ goto s_n_llhttp__internal__n_start_res;
+ default:
+ goto s_n_llhttp__internal__n_start_req_or_res;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_update_finish:
+ s_n_llhttp__internal__n_invoke_update_finish: {
+ switch (llhttp__internal__c_update_finish(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_start:
+ s_n_llhttp__internal__n_start: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_start;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_start;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_start;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_load_initial_message_completed;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ default:
+ /* UNREACHABLE */
+ abort();
+ }
+ s_n_llhttp__internal__n_error_2: {
+ state->error = 0x7;
+ state->reason = "Invalid characters in url";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_finish_2: {
+ switch (llhttp__internal__c_update_finish_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_start;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_initial_message_completed: {
+ switch (llhttp__internal__c_update_initial_message_completed(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_finish_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_content_length: {
+ switch (llhttp__internal__c_update_content_length(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_initial_message_completed;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_8: {
+ state->error = 0x5;
+ state->reason = "Data after `Connection: close`";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_3: {
+ switch (llhttp__internal__c_test_lenient_flags_3(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_closed;
+ default:
+ goto s_n_llhttp__internal__n_error_8;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_2: {
+ switch (llhttp__internal__c_test_lenient_flags_2(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_update_initial_message_completed;
+ default:
+ goto s_n_llhttp__internal__n_closed;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_finish_1: {
+ switch (llhttp__internal__c_update_finish_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_13: {
+ state->error = 0x15;
+ state->reason = "on_message_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_38: {
+ state->error = 0x12;
+ state->reason = "`on_message_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_15: {
+ state->error = 0x15;
+ state->reason = "on_chunk_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_40: {
+ state->error = 0x14;
+ state->reason = "`on_chunk_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: {
+ switch (llhttp__on_chunk_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_15;
+ default:
+ goto s_n_llhttp__internal__n_error_40;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_2: {
+ state->error = 0x15;
+ state->reason = "on_message_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_9: {
+ state->error = 0x12;
+ state->reason = "`on_message_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: {
+ switch (llhttp__on_message_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_pause_1;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_2;
+ default:
+ goto s_n_llhttp__internal__n_error_9;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_36: {
+ state->error = 0xc;
+ state->reason = "Chunk size overflow";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_10: {
+ state->error = 0xc;
+ state->reason = "Invalid character in chunk size";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_4: {
+ switch (llhttp__internal__c_test_lenient_flags_4(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_chunk_size_otherwise;
+ default:
+ goto s_n_llhttp__internal__n_error_10;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_3: {
+ state->error = 0x15;
+ state->reason = "on_chunk_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length_1;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_14: {
+ state->error = 0x14;
+ state->reason = "`on_chunk_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: {
+ switch (llhttp__on_chunk_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_update_content_length_1;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_3;
+ default:
+ goto s_n_llhttp__internal__n_error_14;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_13: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after chunk data";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_6: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;
+ default:
+ goto s_n_llhttp__internal__n_error_13;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_15: {
+ state->error = 0x2;
+ state->reason = "Expected LF after chunk data";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_7: {
+ switch (llhttp__internal__c_test_lenient_flags_7(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;
+ default:
+ goto s_n_llhttp__internal__n_error_15;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_body: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_body(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_chunk_data_almost_done;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags: {
+ switch (llhttp__internal__c_or_flags(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_field_start;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_4: {
+ state->error = 0x15;
+ state->reason = "on_chunk_header pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_12: {
+ state->error = 0x13;
+ state->reason = "`on_chunk_header` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: {
+ switch (llhttp__on_chunk_header(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_is_equal_content_length;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_4;
+ default:
+ goto s_n_llhttp__internal__n_error_12;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_16: {
+ state->error = 0x2;
+ state->reason = "Expected LF after chunk size";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_8: {
+ switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header;
+ default:
+ goto s_n_llhttp__internal__n_error_16;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_11: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after chunk size";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_5: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ default:
+ goto s_n_llhttp__internal__n_error_11;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_17: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk extensions";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_18: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk extensions";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_20: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after chunk extension name";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_5: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_name pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_9;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_19: {
+ state->error = 0x22;
+ state->reason = "`on_chunk_extension_name` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_name(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_6: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_name pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_21: {
+ state->error = 0x22;
+ state->reason = "`on_chunk_extension_name` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_name(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_7: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_name pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_22: {
+ state->error = 0x22;
+ state->reason = "`on_chunk_extension_name` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_name(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_25: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after chunk extension value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_8: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_value pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_10;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_24: {
+ state->error = 0x23;
+ state->reason = "`on_chunk_extension_value` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_9: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_value pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_26: {
+ state->error = 0x23;
+ state->reason = "`on_chunk_extension_value` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_28: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after chunk extension value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_11: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ default:
+ goto s_n_llhttp__internal__n_error_28;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_29: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk extensions quote value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_10: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_value pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_quoted_value_done;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_27: {
+ state->error = 0x23;
+ state->reason = "`on_chunk_extension_value` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_30;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_30;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_31;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_31;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_11: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_value pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_32: {
+ state->error = 0x23;
+ state->reason = "`on_chunk_extension_value` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_33;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_33;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_12: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_name pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_value;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_23: {
+ state->error = 0x22;
+ state->reason = "`on_chunk_extension_name` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3: {
+ switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_chunk_extension_value;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_12;
+ default:
+ goto s_n_llhttp__internal__n_error_23;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_name(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_name(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_34;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_34;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_35: {
+ state->error = 0xc;
+ state->reason = "Invalid character in chunk size";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_mul_add_content_length: {
+ switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) {
+ case 1:
+ goto s_n_llhttp__internal__n_error_36;
+ default:
+ goto s_n_llhttp__internal__n_chunk_size;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_37: {
+ state->error = 0xc;
+ state->reason = "Invalid character in chunk size";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_body_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_body(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_finish_3: {
+ switch (llhttp__internal__c_update_finish_3(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_39: {
+ state->error = 0xf;
+ state->reason = "Request has invalid `Transfer-Encoding`";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause: {
+ state->error = 0x15;
+ state->reason = "on_message_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_7: {
+ state->error = 0x12;
+ state->reason = "`on_message_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: {
+ switch (llhttp__on_message_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;
+ case 21:
+ goto s_n_llhttp__internal__n_pause;
+ default:
+ goto s_n_llhttp__internal__n_error_7;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_1: {
+ switch (llhttp__internal__c_or_flags_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_2: {
+ switch (llhttp__internal__c_or_flags_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_upgrade: {
+ switch (llhttp__internal__c_update_upgrade(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_or_flags_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_14: {
+ state->error = 0x15;
+ state->reason = "Paused by on_headers_complete";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_6: {
+ state->error = 0x11;
+ state->reason = "User callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: {
+ switch (llhttp__on_headers_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_or_flags_1;
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_update_upgrade;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_14;
+ default:
+ goto s_n_llhttp__internal__n_error_6;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: {
+ switch (llhttp__before_headers_complete(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_flags: {
+ switch (llhttp__internal__c_test_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1;
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_1: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_test_flags;
+ default:
+ goto s_n_llhttp__internal__n_error_5;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_17: {
+ state->error = 0x15;
+ state->reason = "on_chunk_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_42: {
+ state->error = 0x14;
+ state->reason = "`on_chunk_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2: {
+ switch (llhttp__on_chunk_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_17;
+ default:
+ goto s_n_llhttp__internal__n_error_42;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_3: {
+ switch (llhttp__internal__c_or_flags_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_4: {
+ switch (llhttp__internal__c_or_flags_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_upgrade_1: {
+ switch (llhttp__internal__c_update_upgrade(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_or_flags_4;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_16: {
+ state->error = 0x15;
+ state->reason = "Paused by on_headers_complete";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_41: {
+ state->error = 0x11;
+ state->reason = "User callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1: {
+ switch (llhttp__on_headers_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_or_flags_3;
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_update_upgrade_1;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_16;
+ default:
+ goto s_n_llhttp__internal__n_error_41;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1: {
+ switch (llhttp__before_headers_complete(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_flags_1: {
+ switch (llhttp__internal__c_test_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2;
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_43: {
+ state->error = 0x2;
+ state->reason = "Expected LF after headers";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_12: {
+ switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_test_flags_1;
+ default:
+ goto s_n_llhttp__internal__n_error_43;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_44: {
+ state->error = 0xa;
+ state->reason = "Invalid header token";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_field: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_field(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_5;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_5;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_13: {
+ switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_field_colon_discard_ws;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_60: {
+ state->error = 0xb;
+ state->reason = "Content-Length can't be present with Transfer-Encoding";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_47: {
+ state->error = 0xa;
+ state->reason = "Invalid header value char";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_15: {
+ switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ default:
+ goto s_n_llhttp__internal__n_error_47;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_49: {
+ state->error = 0xb;
+ state->reason = "Empty Content-Length";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_18: {
+ state->error = 0x15;
+ state->reason = "on_header_value_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_48: {
+ state->error = 0x1d;
+ state->reason = "`on_header_value_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state: {
+ switch (llhttp__internal__c_update_header_state(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_5: {
+ switch (llhttp__internal__c_or_flags_5(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_6: {
+ switch (llhttp__internal__c_or_flags_6(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_7: {
+ switch (llhttp__internal__c_or_flags_7(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_8: {
+ switch (llhttp__internal__c_or_flags_8(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_header_state_2: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 5:
+ goto s_n_llhttp__internal__n_invoke_or_flags_5;
+ case 6:
+ goto s_n_llhttp__internal__n_invoke_or_flags_6;
+ case 7:
+ goto s_n_llhttp__internal__n_invoke_or_flags_7;
+ case 8:
+ goto s_n_llhttp__internal__n_invoke_or_flags_8;
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_header_state_1: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 2:
+ goto s_n_llhttp__internal__n_error_49;
+ default:
+ goto s_n_llhttp__internal__n_invoke_load_header_state_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_46: {
+ state->error = 0xa;
+ state->reason = "Invalid header value char";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_14: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_value_discard_lws;
+ default:
+ goto s_n_llhttp__internal__n_error_46;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_50: {
+ state->error = 0x2;
+ state->reason = "Expected LF after CR";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_16: {
+ switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_value_discard_lws;
+ default:
+ goto s_n_llhttp__internal__n_error_50;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_1: {
+ switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_header_state_4: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 8:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_1;
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_52: {
+ state->error = 0xa;
+ state->reason = "Unexpected whitespace after header value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_18: {
+ switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_load_header_state_4;
+ default:
+ goto s_n_llhttp__internal__n_error_52;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_2: {
+ switch (llhttp__internal__c_update_header_state(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_9: {
+ switch (llhttp__internal__c_or_flags_5(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_10: {
+ switch (llhttp__internal__c_or_flags_6(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_11: {
+ switch (llhttp__internal__c_or_flags_7(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_12: {
+ switch (llhttp__internal__c_or_flags_8(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_header_state_5: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 5:
+ goto s_n_llhttp__internal__n_invoke_or_flags_9;
+ case 6:
+ goto s_n_llhttp__internal__n_invoke_or_flags_10;
+ case 7:
+ goto s_n_llhttp__internal__n_invoke_or_flags_11;
+ case 8:
+ goto s_n_llhttp__internal__n_invoke_or_flags_12;
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_53: {
+ state->error = 0x3;
+ state->reason = "Missing expected LF after header value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_51: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after header value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_17;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_17;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_header_value_almost_done;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_header_value_almost_done;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_header_value_almost_done;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_54;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_54;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_19: {
+ switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_value_lenient;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_4: {
+ switch (llhttp__internal__c_update_header_state(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_13: {
+ switch (llhttp__internal__c_or_flags_5(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_4;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_14: {
+ switch (llhttp__internal__c_or_flags_6(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_4;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_15: {
+ switch (llhttp__internal__c_or_flags_7(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_4;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_16: {
+ switch (llhttp__internal__c_or_flags_8(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_header_state_6: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 5:
+ goto s_n_llhttp__internal__n_invoke_or_flags_13;
+ case 6:
+ goto s_n_llhttp__internal__n_invoke_or_flags_14;
+ case 7:
+ goto s_n_llhttp__internal__n_invoke_or_flags_15;
+ case 8:
+ goto s_n_llhttp__internal__n_invoke_or_flags_16;
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_5: {
+ switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_3: {
+ switch (llhttp__internal__c_update_header_state_3(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_6: {
+ switch (llhttp__internal__c_update_header_state_6(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_7: {
+ switch (llhttp__internal__c_update_header_state_7(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_56;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_56;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_mul_add_content_length_1: {
+ switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) {
+ case 1:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6;
+ default:
+ goto s_n_llhttp__internal__n_header_value_content_length;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_17: {
+ switch (llhttp__internal__c_or_flags_17(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_57;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_57;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_55: {
+ state->error = 0x4;
+ state->reason = "Duplicate Content-Length";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_flags_2: {
+ switch (llhttp__internal__c_test_flags_2(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_header_value_content_length;
+ default:
+ goto s_n_llhttp__internal__n_error_55;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_59;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_59;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_8: {
+ switch (llhttp__internal__c_update_header_state_8(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_58;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_58;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_20: {
+ switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8;
+ default:
+ goto s_n_llhttp__internal__n_header_value_te_chunked;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_type_1: {
+ switch (llhttp__internal__c_load_type(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_20;
+ default:
+ goto s_n_llhttp__internal__n_header_value_te_chunked;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_9: {
+ switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_and_flags: {
+ switch (llhttp__internal__c_and_flags(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_te_chunked;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_19: {
+ switch (llhttp__internal__c_or_flags_18(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_and_flags;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_21: {
+ switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9;
+ default:
+ goto s_n_llhttp__internal__n_invoke_or_flags_19;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_type_2: {
+ switch (llhttp__internal__c_load_type(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_21;
+ default:
+ goto s_n_llhttp__internal__n_invoke_or_flags_19;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_18: {
+ switch (llhttp__internal__c_or_flags_18(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_and_flags;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_flags_3: {
+ switch (llhttp__internal__c_test_flags_3(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_load_type_2;
+ default:
+ goto s_n_llhttp__internal__n_invoke_or_flags_18;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_20: {
+ switch (llhttp__internal__c_or_flags_20(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_9;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_header_state_3: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_value_connection;
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_test_flags_2;
+ case 3:
+ goto s_n_llhttp__internal__n_invoke_test_flags_3;
+ case 4:
+ goto s_n_llhttp__internal__n_invoke_or_flags_20;
+ default:
+ goto s_n_llhttp__internal__n_header_value;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_22: {
+ switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_error_60;
+ default:
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_flags_4: {
+ switch (llhttp__internal__c_test_flags_4(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_22;
+ default:
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_61: {
+ state->error = 0xf;
+ state->reason = "Transfer-Encoding can't be present with Content-Length";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_23: {
+ switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_error_61;
+ default:
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_flags_5: {
+ switch (llhttp__internal__c_test_flags_2(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_23;
+ default:
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_19: {
+ state->error = 0x15;
+ state->reason = "on_header_field_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_header_state;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_45: {
+ state->error = 0x1c;
+ state->reason = "`on_header_field_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_field(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_field(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_62: {
+ state->error = 0xa;
+ state->reason = "Invalid header token";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_10: {
+ switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_field_general;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_header_state: {
+ switch (llhttp__internal__c_store_header_state(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_header_field_colon;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_11: {
+ switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_field_general;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_4: {
+ state->error = 0x1e;
+ state->reason = "Unexpected space after start line";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags: {
+ switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_field_start;
+ default:
+ goto s_n_llhttp__internal__n_error_4;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_20: {
+ state->error = 0x15;
+ state->reason = "on_url_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_3: {
+ state->error = 0x1a;
+ state->reason = "`on_url_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_url_complete: {
+ switch (llhttp__on_url_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_headers_start;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_20;
+ default:
+ goto s_n_llhttp__internal__n_error_3;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_http_minor: {
+ switch (llhttp__internal__c_update_http_minor(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_http_major: {
+ switch (llhttp__internal__c_update_http_major(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_http_minor;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_3: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_63: {
+ state->error = 0x7;
+ state->reason = "Expected CRLF";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_4: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_71: {
+ state->error = 0x17;
+ state->reason = "Pause on PRI/Upgrade";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_72: {
+ state->error = 0x9;
+ state->reason = "Expected HTTP/2 Connection Preface";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_69: {
+ state->error = 0x2;
+ state->reason = "Expected CRLF after version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_26: {
+ switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_headers_start;
+ default:
+ goto s_n_llhttp__internal__n_error_69;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_68: {
+ state->error = 0x9;
+ state->reason = "Expected CRLF after version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_25: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_req_http_complete_crlf;
+ default:
+ goto s_n_llhttp__internal__n_error_68;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_70: {
+ state->error = 0x9;
+ state->reason = "Expected CRLF after version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_21: {
+ state->error = 0x15;
+ state->reason = "on_version_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_1;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_67: {
+ state->error = 0x21;
+ state->reason = "`on_version_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_66;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_66;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_minor: {
+ switch (llhttp__internal__c_load_http_minor(state, p, endp)) {
+ case 9:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_minor_1: {
+ switch (llhttp__internal__c_load_http_minor(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;
+ case 1:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_minor_2: {
+ switch (llhttp__internal__c_load_http_minor(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_major: {
+ switch (llhttp__internal__c_load_http_major(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_load_http_minor;
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_load_http_minor_1;
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_load_http_minor_2;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_24: {
+ switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;
+ default:
+ goto s_n_llhttp__internal__n_invoke_load_http_major;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_http_minor: {
+ switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_24;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_73;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_73;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_3: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_74;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_74;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_http_major: {
+ switch (llhttp__internal__c_store_http_major(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_req_http_dot;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_4: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_75;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_75;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_65: {
+ state->error = 0x8;
+ state->reason = "Invalid method for HTTP/x.x request";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_method: {
+ switch (llhttp__internal__c_load_method(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 1:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 2:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 3:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 4:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 5:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 6:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 7:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 8:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 9:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 10:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 11:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 12:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 13:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 14:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 15:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 16:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 17:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 18:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 19:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 20:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 21:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 22:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 23:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 24:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 25:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 26:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 27:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 28:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 29:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 30:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 31:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 32:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 33:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 34:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 46:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ default:
+ goto s_n_llhttp__internal__n_error_65;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_78: {
+ state->error = 0x8;
+ state->reason = "Expected HTTP/";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_76: {
+ state->error = 0x8;
+ state->reason = "Expected SOURCE method for ICE/x.x request";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_method_2: {
+ switch (llhttp__internal__c_load_method(state, p, endp)) {
+ case 33:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ default:
+ goto s_n_llhttp__internal__n_error_76;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_77: {
+ state->error = 0x8;
+ state->reason = "Invalid method for RTSP/x.x request";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_method_3: {
+ switch (llhttp__internal__c_load_method(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 3:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 6:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 35:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 36:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 37:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 38:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 39:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 40:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 41:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 42:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 43:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 44:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 45:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ default:
+ goto s_n_llhttp__internal__n_error_77;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_22: {
+ state->error = 0x15;
+ state->reason = "on_url_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_http_start;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_64: {
+ state->error = 0x1a;
+ state->reason = "`on_url_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1: {
+ switch (llhttp__on_url_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_req_http_start;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_22;
+ default:
+ goto s_n_llhttp__internal__n_error_64;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_5: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_6: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_7: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_8: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_79: {
+ state->error = 0x7;
+ state->reason = "Invalid char in url fragment start";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_9: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_10: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_11: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_80: {
+ state->error = 0x7;
+ state->reason = "Invalid char in url query";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_81: {
+ state->error = 0x7;
+ state->reason = "Invalid char in url path";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_12: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_13: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_14: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_82: {
+ state->error = 0x7;
+ state->reason = "Double @ in url";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_83: {
+ state->error = 0x7;
+ state->reason = "Unexpected char in url server";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_84: {
+ state->error = 0x7;
+ state->reason = "Unexpected char in url server";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_85: {
+ state->error = 0x7;
+ state->reason = "Unexpected char in url schema";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_86: {
+ state->error = 0x7;
+ state->reason = "Unexpected char in url schema";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_87: {
+ state->error = 0x7;
+ state->reason = "Unexpected start char in url";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_is_equal_method: {
+ switch (llhttp__internal__c_is_equal_method(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_url_entry_normal;
+ default:
+ goto s_n_llhttp__internal__n_url_entry_connect;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_88: {
+ state->error = 0x6;
+ state->reason = "Expected space after method";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_26: {
+ state->error = 0x15;
+ state->reason = "on_method_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_107: {
+ state->error = 0x20;
+ state->reason = "`on_method_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_method_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_method(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_method_1: {
+ switch (llhttp__internal__c_store_method(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_method_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_108: {
+ state->error = 0x6;
+ state->reason = "Invalid method encountered";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_100: {
+ state->error = 0xd;
+ state->reason = "Invalid status code";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_98: {
+ state->error = 0xd;
+ state->reason = "Invalid status code";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_96: {
+ state->error = 0xd;
+ state->reason = "Invalid status code";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_24: {
+ state->error = 0x15;
+ state->reason = "on_status_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_92: {
+ state->error = 0x1b;
+ state->reason = "`on_status_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_status_complete: {
+ switch (llhttp__on_status_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_headers_start;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_24;
+ default:
+ goto s_n_llhttp__internal__n_error_92;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_91: {
+ state->error = 0xd;
+ state->reason = "Invalid response status";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_28: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;
+ default:
+ goto s_n_llhttp__internal__n_error_91;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_93: {
+ state->error = 0x2;
+ state->reason = "Expected LF after CR";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_29: {
+ switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;
+ default:
+ goto s_n_llhttp__internal__n_error_93;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_94: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after response line";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_status: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_status(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_30;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_30;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_status_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_status(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_res_line_almost_done;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_95: {
+ state->error = 0xd;
+ state->reason = "Invalid response status";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_mul_add_status_code_2: {
+ switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {
+ case 1:
+ goto s_n_llhttp__internal__n_error_96;
+ default:
+ goto s_n_llhttp__internal__n_res_status_code_otherwise;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_97: {
+ state->error = 0xd;
+ state->reason = "Invalid status code";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_mul_add_status_code_1: {
+ switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {
+ case 1:
+ goto s_n_llhttp__internal__n_error_98;
+ default:
+ goto s_n_llhttp__internal__n_res_status_code_digit_3;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_99: {
+ state->error = 0xd;
+ state->reason = "Invalid status code";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_mul_add_status_code: {
+ switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {
+ case 1:
+ goto s_n_llhttp__internal__n_error_100;
+ default:
+ goto s_n_llhttp__internal__n_res_status_code_digit_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_101: {
+ state->error = 0xd;
+ state->reason = "Invalid status code";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_status_code: {
+ switch (llhttp__internal__c_update_status_code(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_res_status_code_digit_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_102: {
+ state->error = 0x9;
+ state->reason = "Expected space after version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_25: {
+ state->error = 0x15;
+ state->reason = "on_version_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_version;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_90: {
+ state->error = 0x21;
+ state->reason = "`on_version_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_6: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_5: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_89;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_89;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_minor_3: {
+ switch (llhttp__internal__c_load_http_minor(state, p, endp)) {
+ case 9:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_minor_4: {
+ switch (llhttp__internal__c_load_http_minor(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;
+ case 1:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_minor_5: {
+ switch (llhttp__internal__c_load_http_minor(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_major_1: {
+ switch (llhttp__internal__c_load_http_major(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_load_http_minor_3;
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_load_http_minor_4;
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_load_http_minor_5;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_27: {
+ switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;
+ default:
+ goto s_n_llhttp__internal__n_invoke_load_http_major_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_http_minor_1: {
+ switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_27;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_7: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_103;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_103;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_8: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_104;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_104;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_http_major_1: {
+ switch (llhttp__internal__c_store_http_major(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_res_http_dot;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_9: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_105;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_105;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_109: {
+ state->error = 0x8;
+ state->reason = "Expected HTTP/";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_23: {
+ state->error = 0x15;
+ state->reason = "on_method_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_1: {
+ state->error = 0x20;
+ state->reason = "`on_method_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_method: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_method(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_type: {
+ switch (llhttp__internal__c_update_type(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_method;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_method: {
+ switch (llhttp__internal__c_store_method(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_type;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_106: {
+ state->error = 0x8;
+ state->reason = "Invalid word encountered";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_method_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_method(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_type_1;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_update_type_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_type_2: {
+ switch (llhttp__internal__c_update_type(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_27: {
+ state->error = 0x15;
+ state->reason = "on_message_begin pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error: {
+ state->error = 0x10;
+ state->reason = "`on_message_begin` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: {
+ switch (llhttp__on_message_begin(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_load_type;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_27;
+ default:
+ goto s_n_llhttp__internal__n_error;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_28: {
+ state->error = 0x15;
+ state->reason = "on_reset pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_finish;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_110: {
+ state->error = 0x1f;
+ state->reason = "`on_reset` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_reset: {
+ switch (llhttp__on_reset(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_update_finish;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_28;
+ default:
+ goto s_n_llhttp__internal__n_error_110;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_initial_message_completed: {
+ switch (llhttp__internal__c_load_initial_message_completed(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_reset;
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_finish;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+}
+
+int llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) {
+ llparse_state_t next;
+
+ /* check lingering errors */
+ if (state->error != 0) {
+ return state->error;
+ }
+
+ /* restart spans */
+ if (state->_span_pos0 != NULL) {
+ state->_span_pos0 = (void*) p;
+ }
+
+ next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp);
+ if (next == s_error) {
+ return state->error;
+ }
+ state->_current = (void*) (intptr_t) next;
+
+ /* execute spans */
+ if (state->_span_pos0 != NULL) {
+ int error;
+
+ error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp);
+ if (error != 0) {
+ state->error = error;
+ state->error_pos = endp;
+ return error;
+ }
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/deps/llhttp/llhttp.h b/deps/llhttp/llhttp.h
new file mode 100644
index 00000000000..26f01c32cc7
--- /dev/null
+++ b/deps/llhttp/llhttp.h
@@ -0,0 +1,897 @@
+
+#ifndef INCLUDE_LLHTTP_H_
+#define INCLUDE_LLHTTP_H_
+
+#define LLHTTP_VERSION_MAJOR 9
+#define LLHTTP_VERSION_MINOR 2
+#define LLHTTP_VERSION_PATCH 1
+
+#ifndef INCLUDE_LLHTTP_ITSELF_H_
+#define INCLUDE_LLHTTP_ITSELF_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+typedef struct llhttp__internal_s llhttp__internal_t;
+struct llhttp__internal_s {
+ int32_t _index;
+ void* _span_pos0;
+ void* _span_cb0;
+ int32_t error;
+ const char* reason;
+ const char* error_pos;
+ void* data;
+ void* _current;
+ uint64_t content_length;
+ uint8_t type;
+ uint8_t method;
+ uint8_t http_major;
+ uint8_t http_minor;
+ uint8_t header_state;
+ uint16_t lenient_flags;
+ uint8_t upgrade;
+ uint8_t finish;
+ uint16_t flags;
+ uint16_t status_code;
+ uint8_t initial_message_completed;
+ void* settings;
+};
+
+int llhttp__internal_init(llhttp__internal_t* s);
+int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+#endif /* INCLUDE_LLHTTP_ITSELF_H_ */
+
+
+#ifndef LLLLHTTP_C_HEADERS_
+#define LLLLHTTP_C_HEADERS_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum llhttp_errno {
+ HPE_OK = 0,
+ HPE_INTERNAL = 1,
+ HPE_STRICT = 2,
+ HPE_CR_EXPECTED = 25,
+ HPE_LF_EXPECTED = 3,
+ HPE_UNEXPECTED_CONTENT_LENGTH = 4,
+ HPE_UNEXPECTED_SPACE = 30,
+ HPE_CLOSED_CONNECTION = 5,
+ HPE_INVALID_METHOD = 6,
+ HPE_INVALID_URL = 7,
+ HPE_INVALID_CONSTANT = 8,
+ HPE_INVALID_VERSION = 9,
+ HPE_INVALID_HEADER_TOKEN = 10,
+ HPE_INVALID_CONTENT_LENGTH = 11,
+ HPE_INVALID_CHUNK_SIZE = 12,
+ HPE_INVALID_STATUS = 13,
+ HPE_INVALID_EOF_STATE = 14,
+ HPE_INVALID_TRANSFER_ENCODING = 15,
+ HPE_CB_MESSAGE_BEGIN = 16,
+ HPE_CB_HEADERS_COMPLETE = 17,
+ HPE_CB_MESSAGE_COMPLETE = 18,
+ HPE_CB_CHUNK_HEADER = 19,
+ HPE_CB_CHUNK_COMPLETE = 20,
+ HPE_PAUSED = 21,
+ HPE_PAUSED_UPGRADE = 22,
+ HPE_PAUSED_H2_UPGRADE = 23,
+ HPE_USER = 24,
+ HPE_CB_URL_COMPLETE = 26,
+ HPE_CB_STATUS_COMPLETE = 27,
+ HPE_CB_METHOD_COMPLETE = 32,
+ HPE_CB_VERSION_COMPLETE = 33,
+ HPE_CB_HEADER_FIELD_COMPLETE = 28,
+ HPE_CB_HEADER_VALUE_COMPLETE = 29,
+ HPE_CB_CHUNK_EXTENSION_NAME_COMPLETE = 34,
+ HPE_CB_CHUNK_EXTENSION_VALUE_COMPLETE = 35,
+ HPE_CB_RESET = 31
+};
+typedef enum llhttp_errno llhttp_errno_t;
+
+enum llhttp_flags {
+ F_CONNECTION_KEEP_ALIVE = 0x1,
+ F_CONNECTION_CLOSE = 0x2,
+ F_CONNECTION_UPGRADE = 0x4,
+ F_CHUNKED = 0x8,
+ F_UPGRADE = 0x10,
+ F_CONTENT_LENGTH = 0x20,
+ F_SKIPBODY = 0x40,
+ F_TRAILING = 0x80,
+ F_TRANSFER_ENCODING = 0x200
+};
+typedef enum llhttp_flags llhttp_flags_t;
+
+enum llhttp_lenient_flags {
+ LENIENT_HEADERS = 0x1,
+ LENIENT_CHUNKED_LENGTH = 0x2,
+ LENIENT_KEEP_ALIVE = 0x4,
+ LENIENT_TRANSFER_ENCODING = 0x8,
+ LENIENT_VERSION = 0x10,
+ LENIENT_DATA_AFTER_CLOSE = 0x20,
+ LENIENT_OPTIONAL_LF_AFTER_CR = 0x40,
+ LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80,
+ LENIENT_OPTIONAL_CR_BEFORE_LF = 0x100,
+ LENIENT_SPACES_AFTER_CHUNK_SIZE = 0x200
+};
+typedef enum llhttp_lenient_flags llhttp_lenient_flags_t;
+
+enum llhttp_type {
+ HTTP_BOTH = 0,
+ HTTP_REQUEST = 1,
+ HTTP_RESPONSE = 2
+};
+typedef enum llhttp_type llhttp_type_t;
+
+enum llhttp_finish {
+ HTTP_FINISH_SAFE = 0,
+ HTTP_FINISH_SAFE_WITH_CB = 1,
+ HTTP_FINISH_UNSAFE = 2
+};
+typedef enum llhttp_finish llhttp_finish_t;
+
+enum llhttp_method {
+ HTTP_DELETE = 0,
+ HTTP_GET = 1,
+ HTTP_HEAD = 2,
+ HTTP_POST = 3,
+ HTTP_PUT = 4,
+ HTTP_CONNECT = 5,
+ HTTP_OPTIONS = 6,
+ HTTP_TRACE = 7,
+ HTTP_COPY = 8,
+ HTTP_LOCK = 9,
+ HTTP_MKCOL = 10,
+ HTTP_MOVE = 11,
+ HTTP_PROPFIND = 12,
+ HTTP_PROPPATCH = 13,
+ HTTP_SEARCH = 14,
+ HTTP_UNLOCK = 15,
+ HTTP_BIND = 16,
+ HTTP_REBIND = 17,
+ HTTP_UNBIND = 18,
+ HTTP_ACL = 19,
+ HTTP_REPORT = 20,
+ HTTP_MKACTIVITY = 21,
+ HTTP_CHECKOUT = 22,
+ HTTP_MERGE = 23,
+ HTTP_MSEARCH = 24,
+ HTTP_NOTIFY = 25,
+ HTTP_SUBSCRIBE = 26,
+ HTTP_UNSUBSCRIBE = 27,
+ HTTP_PATCH = 28,
+ HTTP_PURGE = 29,
+ HTTP_MKCALENDAR = 30,
+ HTTP_LINK = 31,
+ HTTP_UNLINK = 32,
+ HTTP_SOURCE = 33,
+ HTTP_PRI = 34,
+ HTTP_DESCRIBE = 35,
+ HTTP_ANNOUNCE = 36,
+ HTTP_SETUP = 37,
+ HTTP_PLAY = 38,
+ HTTP_PAUSE = 39,
+ HTTP_TEARDOWN = 40,
+ HTTP_GET_PARAMETER = 41,
+ HTTP_SET_PARAMETER = 42,
+ HTTP_REDIRECT = 43,
+ HTTP_RECORD = 44,
+ HTTP_FLUSH = 45,
+ HTTP_QUERY = 46
+};
+typedef enum llhttp_method llhttp_method_t;
+
+enum llhttp_status {
+ HTTP_STATUS_CONTINUE = 100,
+ HTTP_STATUS_SWITCHING_PROTOCOLS = 101,
+ HTTP_STATUS_PROCESSING = 102,
+ HTTP_STATUS_EARLY_HINTS = 103,
+ HTTP_STATUS_RESPONSE_IS_STALE = 110,
+ HTTP_STATUS_REVALIDATION_FAILED = 111,
+ HTTP_STATUS_DISCONNECTED_OPERATION = 112,
+ HTTP_STATUS_HEURISTIC_EXPIRATION = 113,
+ HTTP_STATUS_MISCELLANEOUS_WARNING = 199,
+ HTTP_STATUS_OK = 200,
+ HTTP_STATUS_CREATED = 201,
+ HTTP_STATUS_ACCEPTED = 202,
+ HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203,
+ HTTP_STATUS_NO_CONTENT = 204,
+ HTTP_STATUS_RESET_CONTENT = 205,
+ HTTP_STATUS_PARTIAL_CONTENT = 206,
+ HTTP_STATUS_MULTI_STATUS = 207,
+ HTTP_STATUS_ALREADY_REPORTED = 208,
+ HTTP_STATUS_TRANSFORMATION_APPLIED = 214,
+ HTTP_STATUS_IM_USED = 226,
+ HTTP_STATUS_MISCELLANEOUS_PERSISTENT_WARNING = 299,
+ HTTP_STATUS_MULTIPLE_CHOICES = 300,
+ HTTP_STATUS_MOVED_PERMANENTLY = 301,
+ HTTP_STATUS_FOUND = 302,
+ HTTP_STATUS_SEE_OTHER = 303,
+ HTTP_STATUS_NOT_MODIFIED = 304,
+ HTTP_STATUS_USE_PROXY = 305,
+ HTTP_STATUS_SWITCH_PROXY = 306,
+ HTTP_STATUS_TEMPORARY_REDIRECT = 307,
+ HTTP_STATUS_PERMANENT_REDIRECT = 308,
+ HTTP_STATUS_BAD_REQUEST = 400,
+ HTTP_STATUS_UNAUTHORIZED = 401,
+ HTTP_STATUS_PAYMENT_REQUIRED = 402,
+ HTTP_STATUS_FORBIDDEN = 403,
+ HTTP_STATUS_NOT_FOUND = 404,
+ HTTP_STATUS_METHOD_NOT_ALLOWED = 405,
+ HTTP_STATUS_NOT_ACCEPTABLE = 406,
+ HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
+ HTTP_STATUS_REQUEST_TIMEOUT = 408,
+ HTTP_STATUS_CONFLICT = 409,
+ HTTP_STATUS_GONE = 410,
+ HTTP_STATUS_LENGTH_REQUIRED = 411,
+ HTTP_STATUS_PRECONDITION_FAILED = 412,
+ HTTP_STATUS_PAYLOAD_TOO_LARGE = 413,
+ HTTP_STATUS_URI_TOO_LONG = 414,
+ HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,
+ HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416,
+ HTTP_STATUS_EXPECTATION_FAILED = 417,
+ HTTP_STATUS_IM_A_TEAPOT = 418,
+ HTTP_STATUS_PAGE_EXPIRED = 419,
+ HTTP_STATUS_ENHANCE_YOUR_CALM = 420,
+ HTTP_STATUS_MISDIRECTED_REQUEST = 421,
+ HTTP_STATUS_UNPROCESSABLE_ENTITY = 422,
+ HTTP_STATUS_LOCKED = 423,
+ HTTP_STATUS_FAILED_DEPENDENCY = 424,
+ HTTP_STATUS_TOO_EARLY = 425,
+ HTTP_STATUS_UPGRADE_REQUIRED = 426,
+ HTTP_STATUS_PRECONDITION_REQUIRED = 428,
+ HTTP_STATUS_TOO_MANY_REQUESTS = 429,
+ HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430,
+ HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
+ HTTP_STATUS_LOGIN_TIMEOUT = 440,
+ HTTP_STATUS_NO_RESPONSE = 444,
+ HTTP_STATUS_RETRY_WITH = 449,
+ HTTP_STATUS_BLOCKED_BY_PARENTAL_CONTROL = 450,
+ HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451,
+ HTTP_STATUS_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460,
+ HTTP_STATUS_INVALID_X_FORWARDED_FOR = 463,
+ HTTP_STATUS_REQUEST_HEADER_TOO_LARGE = 494,
+ HTTP_STATUS_SSL_CERTIFICATE_ERROR = 495,
+ HTTP_STATUS_SSL_CERTIFICATE_REQUIRED = 496,
+ HTTP_STATUS_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497,
+ HTTP_STATUS_INVALID_TOKEN = 498,
+ HTTP_STATUS_CLIENT_CLOSED_REQUEST = 499,
+ HTTP_STATUS_INTERNAL_SERVER_ERROR = 500,
+ HTTP_STATUS_NOT_IMPLEMENTED = 501,
+ HTTP_STATUS_BAD_GATEWAY = 502,
+ HTTP_STATUS_SERVICE_UNAVAILABLE = 503,
+ HTTP_STATUS_GATEWAY_TIMEOUT = 504,
+ HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,
+ HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506,
+ HTTP_STATUS_INSUFFICIENT_STORAGE = 507,
+ HTTP_STATUS_LOOP_DETECTED = 508,
+ HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509,
+ HTTP_STATUS_NOT_EXTENDED = 510,
+ HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511,
+ HTTP_STATUS_WEB_SERVER_UNKNOWN_ERROR = 520,
+ HTTP_STATUS_WEB_SERVER_IS_DOWN = 521,
+ HTTP_STATUS_CONNECTION_TIMEOUT = 522,
+ HTTP_STATUS_ORIGIN_IS_UNREACHABLE = 523,
+ HTTP_STATUS_TIMEOUT_OCCURED = 524,
+ HTTP_STATUS_SSL_HANDSHAKE_FAILED = 525,
+ HTTP_STATUS_INVALID_SSL_CERTIFICATE = 526,
+ HTTP_STATUS_RAILGUN_ERROR = 527,
+ HTTP_STATUS_SITE_IS_OVERLOADED = 529,
+ HTTP_STATUS_SITE_IS_FROZEN = 530,
+ HTTP_STATUS_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561,
+ HTTP_STATUS_NETWORK_READ_TIMEOUT = 598,
+ HTTP_STATUS_NETWORK_CONNECT_TIMEOUT = 599
+};
+typedef enum llhttp_status llhttp_status_t;
+
+#define HTTP_ERRNO_MAP(XX) \
+ XX(0, OK, OK) \
+ XX(1, INTERNAL, INTERNAL) \
+ XX(2, STRICT, STRICT) \
+ XX(25, CR_EXPECTED, CR_EXPECTED) \
+ XX(3, LF_EXPECTED, LF_EXPECTED) \
+ XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
+ XX(30, UNEXPECTED_SPACE, UNEXPECTED_SPACE) \
+ XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
+ XX(6, INVALID_METHOD, INVALID_METHOD) \
+ XX(7, INVALID_URL, INVALID_URL) \
+ XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
+ XX(9, INVALID_VERSION, INVALID_VERSION) \
+ XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
+ XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
+ XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
+ XX(13, INVALID_STATUS, INVALID_STATUS) \
+ XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
+ XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \
+ XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
+ XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
+ XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
+ XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
+ XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
+ XX(21, PAUSED, PAUSED) \
+ XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \
+ XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \
+ XX(24, USER, USER) \
+ XX(26, CB_URL_COMPLETE, CB_URL_COMPLETE) \
+ XX(27, CB_STATUS_COMPLETE, CB_STATUS_COMPLETE) \
+ XX(32, CB_METHOD_COMPLETE, CB_METHOD_COMPLETE) \
+ XX(33, CB_VERSION_COMPLETE, CB_VERSION_COMPLETE) \
+ XX(28, CB_HEADER_FIELD_COMPLETE, CB_HEADER_FIELD_COMPLETE) \
+ XX(29, CB_HEADER_VALUE_COMPLETE, CB_HEADER_VALUE_COMPLETE) \
+ XX(34, CB_CHUNK_EXTENSION_NAME_COMPLETE, CB_CHUNK_EXTENSION_NAME_COMPLETE) \
+ XX(35, CB_CHUNK_EXTENSION_VALUE_COMPLETE, CB_CHUNK_EXTENSION_VALUE_COMPLETE) \
+ XX(31, CB_RESET, CB_RESET) \
+
+
+#define HTTP_METHOD_MAP(XX) \
+ XX(0, DELETE, DELETE) \
+ XX(1, GET, GET) \
+ XX(2, HEAD, HEAD) \
+ XX(3, POST, POST) \
+ XX(4, PUT, PUT) \
+ XX(5, CONNECT, CONNECT) \
+ XX(6, OPTIONS, OPTIONS) \
+ XX(7, TRACE, TRACE) \
+ XX(8, COPY, COPY) \
+ XX(9, LOCK, LOCK) \
+ XX(10, MKCOL, MKCOL) \
+ XX(11, MOVE, MOVE) \
+ XX(12, PROPFIND, PROPFIND) \
+ XX(13, PROPPATCH, PROPPATCH) \
+ XX(14, SEARCH, SEARCH) \
+ XX(15, UNLOCK, UNLOCK) \
+ XX(16, BIND, BIND) \
+ XX(17, REBIND, REBIND) \
+ XX(18, UNBIND, UNBIND) \
+ XX(19, ACL, ACL) \
+ XX(20, REPORT, REPORT) \
+ XX(21, MKACTIVITY, MKACTIVITY) \
+ XX(22, CHECKOUT, CHECKOUT) \
+ XX(23, MERGE, MERGE) \
+ XX(24, MSEARCH, M-SEARCH) \
+ XX(25, NOTIFY, NOTIFY) \
+ XX(26, SUBSCRIBE, SUBSCRIBE) \
+ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
+ XX(28, PATCH, PATCH) \
+ XX(29, PURGE, PURGE) \
+ XX(30, MKCALENDAR, MKCALENDAR) \
+ XX(31, LINK, LINK) \
+ XX(32, UNLINK, UNLINK) \
+ XX(33, SOURCE, SOURCE) \
+ XX(46, QUERY, QUERY) \
+
+
+#define RTSP_METHOD_MAP(XX) \
+ XX(1, GET, GET) \
+ XX(3, POST, POST) \
+ XX(6, OPTIONS, OPTIONS) \
+ XX(35, DESCRIBE, DESCRIBE) \
+ XX(36, ANNOUNCE, ANNOUNCE) \
+ XX(37, SETUP, SETUP) \
+ XX(38, PLAY, PLAY) \
+ XX(39, PAUSE, PAUSE) \
+ XX(40, TEARDOWN, TEARDOWN) \
+ XX(41, GET_PARAMETER, GET_PARAMETER) \
+ XX(42, SET_PARAMETER, SET_PARAMETER) \
+ XX(43, REDIRECT, REDIRECT) \
+ XX(44, RECORD, RECORD) \
+ XX(45, FLUSH, FLUSH) \
+
+
+#define HTTP_ALL_METHOD_MAP(XX) \
+ XX(0, DELETE, DELETE) \
+ XX(1, GET, GET) \
+ XX(2, HEAD, HEAD) \
+ XX(3, POST, POST) \
+ XX(4, PUT, PUT) \
+ XX(5, CONNECT, CONNECT) \
+ XX(6, OPTIONS, OPTIONS) \
+ XX(7, TRACE, TRACE) \
+ XX(8, COPY, COPY) \
+ XX(9, LOCK, LOCK) \
+ XX(10, MKCOL, MKCOL) \
+ XX(11, MOVE, MOVE) \
+ XX(12, PROPFIND, PROPFIND) \
+ XX(13, PROPPATCH, PROPPATCH) \
+ XX(14, SEARCH, SEARCH) \
+ XX(15, UNLOCK, UNLOCK) \
+ XX(16, BIND, BIND) \
+ XX(17, REBIND, REBIND) \
+ XX(18, UNBIND, UNBIND) \
+ XX(19, ACL, ACL) \
+ XX(20, REPORT, REPORT) \
+ XX(21, MKACTIVITY, MKACTIVITY) \
+ XX(22, CHECKOUT, CHECKOUT) \
+ XX(23, MERGE, MERGE) \
+ XX(24, MSEARCH, M-SEARCH) \
+ XX(25, NOTIFY, NOTIFY) \
+ XX(26, SUBSCRIBE, SUBSCRIBE) \
+ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
+ XX(28, PATCH, PATCH) \
+ XX(29, PURGE, PURGE) \
+ XX(30, MKCALENDAR, MKCALENDAR) \
+ XX(31, LINK, LINK) \
+ XX(32, UNLINK, UNLINK) \
+ XX(33, SOURCE, SOURCE) \
+ XX(34, PRI, PRI) \
+ XX(35, DESCRIBE, DESCRIBE) \
+ XX(36, ANNOUNCE, ANNOUNCE) \
+ XX(37, SETUP, SETUP) \
+ XX(38, PLAY, PLAY) \
+ XX(39, PAUSE, PAUSE) \
+ XX(40, TEARDOWN, TEARDOWN) \
+ XX(41, GET_PARAMETER, GET_PARAMETER) \
+ XX(42, SET_PARAMETER, SET_PARAMETER) \
+ XX(43, REDIRECT, REDIRECT) \
+ XX(44, RECORD, RECORD) \
+ XX(45, FLUSH, FLUSH) \
+ XX(46, QUERY, QUERY) \
+
+
+#define HTTP_STATUS_MAP(XX) \
+ XX(100, CONTINUE, CONTINUE) \
+ XX(101, SWITCHING_PROTOCOLS, SWITCHING_PROTOCOLS) \
+ XX(102, PROCESSING, PROCESSING) \
+ XX(103, EARLY_HINTS, EARLY_HINTS) \
+ XX(110, RESPONSE_IS_STALE, RESPONSE_IS_STALE) \
+ XX(111, REVALIDATION_FAILED, REVALIDATION_FAILED) \
+ XX(112, DISCONNECTED_OPERATION, DISCONNECTED_OPERATION) \
+ XX(113, HEURISTIC_EXPIRATION, HEURISTIC_EXPIRATION) \
+ XX(199, MISCELLANEOUS_WARNING, MISCELLANEOUS_WARNING) \
+ XX(200, OK, OK) \
+ XX(201, CREATED, CREATED) \
+ XX(202, ACCEPTED, ACCEPTED) \
+ XX(203, NON_AUTHORITATIVE_INFORMATION, NON_AUTHORITATIVE_INFORMATION) \
+ XX(204, NO_CONTENT, NO_CONTENT) \
+ XX(205, RESET_CONTENT, RESET_CONTENT) \
+ XX(206, PARTIAL_CONTENT, PARTIAL_CONTENT) \
+ XX(207, MULTI_STATUS, MULTI_STATUS) \
+ XX(208, ALREADY_REPORTED, ALREADY_REPORTED) \
+ XX(214, TRANSFORMATION_APPLIED, TRANSFORMATION_APPLIED) \
+ XX(226, IM_USED, IM_USED) \
+ XX(299, MISCELLANEOUS_PERSISTENT_WARNING, MISCELLANEOUS_PERSISTENT_WARNING) \
+ XX(300, MULTIPLE_CHOICES, MULTIPLE_CHOICES) \
+ XX(301, MOVED_PERMANENTLY, MOVED_PERMANENTLY) \
+ XX(302, FOUND, FOUND) \
+ XX(303, SEE_OTHER, SEE_OTHER) \
+ XX(304, NOT_MODIFIED, NOT_MODIFIED) \
+ XX(305, USE_PROXY, USE_PROXY) \
+ XX(306, SWITCH_PROXY, SWITCH_PROXY) \
+ XX(307, TEMPORARY_REDIRECT, TEMPORARY_REDIRECT) \
+ XX(308, PERMANENT_REDIRECT, PERMANENT_REDIRECT) \
+ XX(400, BAD_REQUEST, BAD_REQUEST) \
+ XX(401, UNAUTHORIZED, UNAUTHORIZED) \
+ XX(402, PAYMENT_REQUIRED, PAYMENT_REQUIRED) \
+ XX(403, FORBIDDEN, FORBIDDEN) \
+ XX(404, NOT_FOUND, NOT_FOUND) \
+ XX(405, METHOD_NOT_ALLOWED, METHOD_NOT_ALLOWED) \
+ XX(406, NOT_ACCEPTABLE, NOT_ACCEPTABLE) \
+ XX(407, PROXY_AUTHENTICATION_REQUIRED, PROXY_AUTHENTICATION_REQUIRED) \
+ XX(408, REQUEST_TIMEOUT, REQUEST_TIMEOUT) \
+ XX(409, CONFLICT, CONFLICT) \
+ XX(410, GONE, GONE) \
+ XX(411, LENGTH_REQUIRED, LENGTH_REQUIRED) \
+ XX(412, PRECONDITION_FAILED, PRECONDITION_FAILED) \
+ XX(413, PAYLOAD_TOO_LARGE, PAYLOAD_TOO_LARGE) \
+ XX(414, URI_TOO_LONG, URI_TOO_LONG) \
+ XX(415, UNSUPPORTED_MEDIA_TYPE, UNSUPPORTED_MEDIA_TYPE) \
+ XX(416, RANGE_NOT_SATISFIABLE, RANGE_NOT_SATISFIABLE) \
+ XX(417, EXPECTATION_FAILED, EXPECTATION_FAILED) \
+ XX(418, IM_A_TEAPOT, IM_A_TEAPOT) \
+ XX(419, PAGE_EXPIRED, PAGE_EXPIRED) \
+ XX(420, ENHANCE_YOUR_CALM, ENHANCE_YOUR_CALM) \
+ XX(421, MISDIRECTED_REQUEST, MISDIRECTED_REQUEST) \
+ XX(422, UNPROCESSABLE_ENTITY, UNPROCESSABLE_ENTITY) \
+ XX(423, LOCKED, LOCKED) \
+ XX(424, FAILED_DEPENDENCY, FAILED_DEPENDENCY) \
+ XX(425, TOO_EARLY, TOO_EARLY) \
+ XX(426, UPGRADE_REQUIRED, UPGRADE_REQUIRED) \
+ XX(428, PRECONDITION_REQUIRED, PRECONDITION_REQUIRED) \
+ XX(429, TOO_MANY_REQUESTS, TOO_MANY_REQUESTS) \
+ XX(430, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL) \
+ XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_HEADER_FIELDS_TOO_LARGE) \
+ XX(440, LOGIN_TIMEOUT, LOGIN_TIMEOUT) \
+ XX(444, NO_RESPONSE, NO_RESPONSE) \
+ XX(449, RETRY_WITH, RETRY_WITH) \
+ XX(450, BLOCKED_BY_PARENTAL_CONTROL, BLOCKED_BY_PARENTAL_CONTROL) \
+ XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, UNAVAILABLE_FOR_LEGAL_REASONS) \
+ XX(460, CLIENT_CLOSED_LOAD_BALANCED_REQUEST, CLIENT_CLOSED_LOAD_BALANCED_REQUEST) \
+ XX(463, INVALID_X_FORWARDED_FOR, INVALID_X_FORWARDED_FOR) \
+ XX(494, REQUEST_HEADER_TOO_LARGE, REQUEST_HEADER_TOO_LARGE) \
+ XX(495, SSL_CERTIFICATE_ERROR, SSL_CERTIFICATE_ERROR) \
+ XX(496, SSL_CERTIFICATE_REQUIRED, SSL_CERTIFICATE_REQUIRED) \
+ XX(497, HTTP_REQUEST_SENT_TO_HTTPS_PORT, HTTP_REQUEST_SENT_TO_HTTPS_PORT) \
+ XX(498, INVALID_TOKEN, INVALID_TOKEN) \
+ XX(499, CLIENT_CLOSED_REQUEST, CLIENT_CLOSED_REQUEST) \
+ XX(500, INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR) \
+ XX(501, NOT_IMPLEMENTED, NOT_IMPLEMENTED) \
+ XX(502, BAD_GATEWAY, BAD_GATEWAY) \
+ XX(503, SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE) \
+ XX(504, GATEWAY_TIMEOUT, GATEWAY_TIMEOUT) \
+ XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP_VERSION_NOT_SUPPORTED) \
+ XX(506, VARIANT_ALSO_NEGOTIATES, VARIANT_ALSO_NEGOTIATES) \
+ XX(507, INSUFFICIENT_STORAGE, INSUFFICIENT_STORAGE) \
+ XX(508, LOOP_DETECTED, LOOP_DETECTED) \
+ XX(509, BANDWIDTH_LIMIT_EXCEEDED, BANDWIDTH_LIMIT_EXCEEDED) \
+ XX(510, NOT_EXTENDED, NOT_EXTENDED) \
+ XX(511, NETWORK_AUTHENTICATION_REQUIRED, NETWORK_AUTHENTICATION_REQUIRED) \
+ XX(520, WEB_SERVER_UNKNOWN_ERROR, WEB_SERVER_UNKNOWN_ERROR) \
+ XX(521, WEB_SERVER_IS_DOWN, WEB_SERVER_IS_DOWN) \
+ XX(522, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT) \
+ XX(523, ORIGIN_IS_UNREACHABLE, ORIGIN_IS_UNREACHABLE) \
+ XX(524, TIMEOUT_OCCURED, TIMEOUT_OCCURED) \
+ XX(525, SSL_HANDSHAKE_FAILED, SSL_HANDSHAKE_FAILED) \
+ XX(526, INVALID_SSL_CERTIFICATE, INVALID_SSL_CERTIFICATE) \
+ XX(527, RAILGUN_ERROR, RAILGUN_ERROR) \
+ XX(529, SITE_IS_OVERLOADED, SITE_IS_OVERLOADED) \
+ XX(530, SITE_IS_FROZEN, SITE_IS_FROZEN) \
+ XX(561, IDENTITY_PROVIDER_AUTHENTICATION_ERROR, IDENTITY_PROVIDER_AUTHENTICATION_ERROR) \
+ XX(598, NETWORK_READ_TIMEOUT, NETWORK_READ_TIMEOUT) \
+ XX(599, NETWORK_CONNECT_TIMEOUT, NETWORK_CONNECT_TIMEOUT) \
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+#endif /* LLLLHTTP_C_HEADERS_ */
+
+
+#ifndef INCLUDE_LLHTTP_API_H_
+#define INCLUDE_LLHTTP_API_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include
+
+#define LLHTTP_EXPORT
+
+typedef llhttp__internal_t llhttp_t;
+typedef struct llhttp_settings_s llhttp_settings_t;
+
+typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
+typedef int (*llhttp_cb)(llhttp_t*);
+
+struct llhttp_settings_s {
+ /* Possible return values 0, -1, `HPE_PAUSED` */
+ llhttp_cb on_message_begin;
+
+ /* Possible return values 0, -1, HPE_USER */
+ llhttp_data_cb on_url;
+ llhttp_data_cb on_status;
+ llhttp_data_cb on_method;
+ llhttp_data_cb on_version;
+ llhttp_data_cb on_header_field;
+ llhttp_data_cb on_header_value;
+ llhttp_data_cb on_chunk_extension_name;
+ llhttp_data_cb on_chunk_extension_value;
+
+ /* Possible return values:
+ * 0 - Proceed normally
+ * 1 - Assume that request/response has no body, and proceed to parsing the
+ * next message
+ * 2 - Assume absence of body (as above) and make `llhttp_execute()` return
+ * `HPE_PAUSED_UPGRADE`
+ * -1 - Error
+ * `HPE_PAUSED`
+ */
+ llhttp_cb on_headers_complete;
+
+ /* Possible return values 0, -1, HPE_USER */
+ llhttp_data_cb on_body;
+
+ /* Possible return values 0, -1, `HPE_PAUSED` */
+ llhttp_cb on_message_complete;
+ llhttp_cb on_url_complete;
+ llhttp_cb on_status_complete;
+ llhttp_cb on_method_complete;
+ llhttp_cb on_version_complete;
+ llhttp_cb on_header_field_complete;
+ llhttp_cb on_header_value_complete;
+ llhttp_cb on_chunk_extension_name_complete;
+ llhttp_cb on_chunk_extension_value_complete;
+
+ /* When on_chunk_header is called, the current chunk length is stored
+ * in parser->content_length.
+ * Possible return values 0, -1, `HPE_PAUSED`
+ */
+ llhttp_cb on_chunk_header;
+ llhttp_cb on_chunk_complete;
+ llhttp_cb on_reset;
+};
+
+/* Initialize the parser with specific type and user settings.
+ *
+ * NOTE: lifetime of `settings` has to be at least the same as the lifetime of
+ * the `parser` here. In practice, `settings` has to be either a static
+ * variable or be allocated with `malloc`, `new`, etc.
+ */
+LLHTTP_EXPORT
+void llhttp_init(llhttp_t* parser, llhttp_type_t type,
+ const llhttp_settings_t* settings);
+
+LLHTTP_EXPORT
+llhttp_t* llhttp_alloc(llhttp_type_t type);
+
+LLHTTP_EXPORT
+void llhttp_free(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_type(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_http_major(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_http_minor(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_method(llhttp_t* parser);
+
+LLHTTP_EXPORT
+int llhttp_get_status_code(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_upgrade(llhttp_t* parser);
+
+/* Reset an already initialized parser back to the start state, preserving the
+ * existing parser type, callback settings, user data, and lenient flags.
+ */
+LLHTTP_EXPORT
+void llhttp_reset(llhttp_t* parser);
+
+/* Initialize the settings object */
+LLHTTP_EXPORT
+void llhttp_settings_init(llhttp_settings_t* settings);
+
+/* Parse full or partial request/response, invoking user callbacks along the
+ * way.
+ *
+ * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
+ * interrupts, and such errno is returned from `llhttp_execute()`. If
+ * `HPE_PAUSED` was used as a errno, the execution can be resumed with
+ * `llhttp_resume()` call.
+ *
+ * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
+ * is returned after fully parsing the request/response. If the user wishes to
+ * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
+ *
+ * NOTE: if this function ever returns a non-pause type error, it will continue
+ * to return the same error upon each successive call up until `llhttp_init()`
+ * is called.
+ */
+LLHTTP_EXPORT
+llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
+
+/* This method should be called when the other side has no further bytes to
+ * send (e.g. shutdown of readable side of the TCP connection.)
+ *
+ * Requests without `Content-Length` and other messages might require treating
+ * all incoming bytes as the part of the body, up to the last byte of the
+ * connection. This method will invoke `on_message_complete()` callback if the
+ * request was terminated safely. Otherwise a error code would be returned.
+ */
+LLHTTP_EXPORT
+llhttp_errno_t llhttp_finish(llhttp_t* parser);
+
+/* Returns `1` if the incoming message is parsed until the last byte, and has
+ * to be completed by calling `llhttp_finish()` on EOF
+ */
+LLHTTP_EXPORT
+int llhttp_message_needs_eof(const llhttp_t* parser);
+
+/* Returns `1` if there might be any other messages following the last that was
+ * successfully parsed.
+ */
+LLHTTP_EXPORT
+int llhttp_should_keep_alive(const llhttp_t* parser);
+
+/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
+ * appropriate error reason.
+ *
+ * Important: do not call this from user callbacks! User callbacks must return
+ * `HPE_PAUSED` if pausing is required.
+ */
+LLHTTP_EXPORT
+void llhttp_pause(llhttp_t* parser);
+
+/* Might be called to resume the execution after the pause in user's callback.
+ * See `llhttp_execute()` above for details.
+ *
+ * Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
+ */
+LLHTTP_EXPORT
+void llhttp_resume(llhttp_t* parser);
+
+/* Might be called to resume the execution after the pause in user's callback.
+ * See `llhttp_execute()` above for details.
+ *
+ * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
+ */
+LLHTTP_EXPORT
+void llhttp_resume_after_upgrade(llhttp_t* parser);
+
+/* Returns the latest return error */
+LLHTTP_EXPORT
+llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
+
+/* Returns the verbal explanation of the latest returned error.
+ *
+ * Note: User callback should set error reason when returning the error. See
+ * `llhttp_set_error_reason()` for details.
+ */
+LLHTTP_EXPORT
+const char* llhttp_get_error_reason(const llhttp_t* parser);
+
+/* Assign verbal description to the returned error. Must be called in user
+ * callbacks right before returning the errno.
+ *
+ * Note: `HPE_USER` error code might be useful in user callbacks.
+ */
+LLHTTP_EXPORT
+void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
+
+/* Returns the pointer to the last parsed byte before the returned error. The
+ * pointer is relative to the `data` argument of `llhttp_execute()`.
+ *
+ * Note: this method might be useful for counting the number of parsed bytes.
+ */
+LLHTTP_EXPORT
+const char* llhttp_get_error_pos(const llhttp_t* parser);
+
+/* Returns textual name of error code */
+LLHTTP_EXPORT
+const char* llhttp_errno_name(llhttp_errno_t err);
+
+/* Returns textual name of HTTP method */
+LLHTTP_EXPORT
+const char* llhttp_method_name(llhttp_method_t method);
+
+/* Returns textual name of HTTP status */
+LLHTTP_EXPORT
+const char* llhttp_status_name(llhttp_status_t status);
+
+/* Enables/disables lenient header value parsing (disabled by default).
+ *
+ * Lenient parsing disables header value token checks, extending llhttp's
+ * protocol support to highly non-compliant clients/server. No
+ * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
+ * lenient parsing is "on".
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_headers(llhttp_t* parser, int enabled);
+
+
+/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and
+ * `Content-Length` headers (disabled by default).
+ *
+ * Normally `llhttp` would error when `Transfer-Encoding` is present in
+ * conjunction with `Content-Length`. This error is important to prevent HTTP
+ * request smuggling, but may be less desirable for small number of cases
+ * involving legacy servers.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);
+
+
+/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0
+ * requests responses.
+ *
+ * Normally `llhttp` would error on (in strict mode) or discard (in loose mode)
+ * the HTTP request/response after the request/response with `Connection: close`
+ * and `Content-Length`. This is important to prevent cache poisoning attacks,
+ * but might interact badly with outdated and insecure clients. With this flag
+ * the extra request/response will be parsed normally.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * poisoning attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of `Transfer-Encoding` header.
+ *
+ * Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value
+ * and another value after it (either in a single header or in multiple
+ * headers whose value are internally joined using `, `).
+ * This is mandated by the spec to reliably determine request body size and thus
+ * avoid request smuggling.
+ * With this flag the extra value will be parsed normally.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of HTTP version.
+ *
+ * Normally `llhttp` would error when the HTTP version in the request or status line
+ * is not `0.9`, `1.0`, `1.1` or `2.0`.
+ * With this flag the invalid value will be parsed normally.
+ *
+ * **Enabling this flag can pose a security issue since you will allow unsupported
+ * HTTP versions. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_version(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of additional data received after a message ends
+ * and keep-alive is disabled.
+ *
+ * Normally `llhttp` would error when additional unexpected data is received if the message
+ * contains the `Connection` header with `close` value.
+ * With this flag the extra data will discarded without throwing an error.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * poisoning attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of incomplete CRLF sequences.
+ *
+ * Normally `llhttp` would error when a CR is not followed by LF when terminating the
+ * request line, the status line, the headers or a chunk header.
+ * With this flag only a CR is required to terminate such sections.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled);
+
+/*
+ * Enables/disables lenient handling of line separators.
+ *
+ * Normally `llhttp` would error when a LF is not preceded by CR when terminating the
+ * request line, the status line, the headers, a chunk header or a chunk data.
+ * With this flag only a LF is required to terminate such sections.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of chunks not separated via CRLF.
+ *
+ * Normally `llhttp` would error when after a chunk data a CRLF is missing before
+ * starting a new chunk.
+ * With this flag the new chunk can start immediately after the previous one.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of spaces after chunk size.
+ *
+ * Normally `llhttp` would error when after a chunk size is followed by one or more
+ * spaces are present instead of a CRLF or `;`.
+ * With this flag this check is disabled.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+#endif /* INCLUDE_LLHTTP_API_H_ */
+
+
+#endif /* INCLUDE_LLHTTP_H_ */
diff --git a/deps/ntlmclient/CMakeLists.txt b/deps/ntlmclient/CMakeLists.txt
index 9c5e23d6002..f1f5de162a0 100644
--- a/deps/ntlmclient/CMakeLists.txt
+++ b/deps/ntlmclient/CMakeLists.txt
@@ -20,16 +20,18 @@ if(USE_HTTPS STREQUAL "SecureTransport")
set_source_files_properties("crypt_commoncrypto.c" COMPILE_FLAGS "-Wno-deprecated")
elseif(USE_HTTPS STREQUAL "OpenSSL")
add_definitions(-DCRYPT_OPENSSL)
+ add_definitions(-DOPENSSL_API_COMPAT=0x10100000L)
include_directories(${OPENSSL_INCLUDE_DIR})
set(SRC_NTLMCLIENT_CRYPTO "crypt_openssl.c" "crypt_openssl.h")
elseif(USE_HTTPS STREQUAL "OpenSSL-Dynamic")
add_definitions(-DCRYPT_OPENSSL)
add_definitions(-DCRYPT_OPENSSL_DYNAMIC)
+ add_definitions(-DOPENSSL_API_COMPAT=0x10100000L)
set(SRC_NTLMCLIENT_CRYPTO "crypt_openssl.c" "crypt_openssl.h")
elseif(USE_HTTPS STREQUAL "mbedTLS")
add_definitions(-DCRYPT_MBEDTLS)
include_directories(${MBEDTLS_INCLUDE_DIR})
- set(SRC_NTLMCLIENT_CRYPTO "crypt_mbedtls.c" "crypt_mbedtls.h")
+ set(SRC_NTLMCLIENT_CRYPTO "crypt_mbedtls.c" "crypt_mbedtls.h" "crypt_builtin_md4.c")
else()
message(FATAL_ERROR "Unable to use libgit2's HTTPS backend (${USE_HTTPS}) for NTLM crypto")
endif()
diff --git a/deps/ntlmclient/crypt_builtin_md4.c b/deps/ntlmclient/crypt_builtin_md4.c
new file mode 100644
index 00000000000..de9a85cafaa
--- /dev/null
+++ b/deps/ntlmclient/crypt_builtin_md4.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) Edward Thomson. All rights reserved.
+ *
+ * This file is part of ntlmclient, distributed under the MIT license.
+ * For full terms and copyright information, and for third-party
+ * copyright information, see the included LICENSE.txt file.
+ */
+
+#include
+#include
+
+#include "ntlm.h"
+#include "crypt.h"
+
+/*
+ * Below is the MD4 code from RFC 1320, with minor modifications
+ * to make it compile on a modern compiler. It is included since
+ * many system crypto libraries lack MD4, sensibly.
+ */
+
+/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm
+ */
+
+/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD4 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef uint16_t UINT2;
+
+/* UINT4 defines a four byte word */
+typedef uint32_t UINT4;
+
+#define MD4_memcpy memcpy
+#define MD4_memset memset
+
+/* MD4 context. */
+typedef struct {
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD4_CTX;
+
+/* Constants for MD4Transform routine.
+ */
+#define S11 3
+#define S12 7
+#define S13 11
+#define S14 19
+#define S21 3
+#define S22 5
+#define S23 9
+#define S24 13
+#define S31 3
+#define S32 9
+#define S33 11
+#define S34 15
+
+static void MD4Transform(UINT4 [4], const unsigned char [64]);
+static void Encode (unsigned char *, UINT4 *, unsigned int);
+static void Decode (UINT4 *, const unsigned char *, unsigned int);
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G and H are basic MD4 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s) { \
+ (a) += F ((b), (c), (d)) + (x); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+#define GG(a, b, c, d, x, s) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+#define HH(a, b, c, d, x, s) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+
+/* MD4 initialization. Begins an MD4 operation, writing a new context.
+ */
+static void MD4Init (MD4_CTX *context)
+{
+ context->count[0] = context->count[1] = 0;
+
+ /* Load magic initialization constants.
+ */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD4 block update operation. Continues an MD4 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+static void MD4Update (MD4_CTX *context, const unsigned char *input, unsigned int inputLen)
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3))
+ < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.
+ */
+ if (inputLen >= partLen) {
+ MD4_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD4Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD4Transform (context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ MD4_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)&input[i],
+ inputLen-i);
+}
+
+/* MD4 finalization. Ends an MD4 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+static void MD4Final (unsigned char digest[16], MD4_CTX *context)
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.
+ */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD4Update (context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD4Update (context, bits, 8);
+ /* Store state in digest */
+ Encode (digest, context->state, 16);
+
+ /* Zeroize sensitive information.
+ */
+ MD4_memset ((POINTER)context, 0, sizeof (*context));
+}
+
+/* MD4 basic transformation. Transforms state based on block.
+ */
+static void MD4Transform (UINT4 state[4], const unsigned char block[64])
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11); /* 1 */
+ FF (d, a, b, c, x[ 1], S12); /* 2 */
+ FF (c, d, a, b, x[ 2], S13); /* 3 */
+ FF (b, c, d, a, x[ 3], S14); /* 4 */
+ FF (a, b, c, d, x[ 4], S11); /* 5 */
+ FF (d, a, b, c, x[ 5], S12); /* 6 */
+ FF (c, d, a, b, x[ 6], S13); /* 7 */
+ FF (b, c, d, a, x[ 7], S14); /* 8 */
+ FF (a, b, c, d, x[ 8], S11); /* 9 */
+ FF (d, a, b, c, x[ 9], S12); /* 10 */
+ FF (c, d, a, b, x[10], S13); /* 11 */
+ FF (b, c, d, a, x[11], S14); /* 12 */
+ FF (a, b, c, d, x[12], S11); /* 13 */
+ FF (d, a, b, c, x[13], S12); /* 14 */
+ FF (c, d, a, b, x[14], S13); /* 15 */
+ FF (b, c, d, a, x[15], S14); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 0], S21); /* 17 */
+ GG (d, a, b, c, x[ 4], S22); /* 18 */
+ GG (c, d, a, b, x[ 8], S23); /* 19 */
+ GG (b, c, d, a, x[12], S24); /* 20 */
+ GG (a, b, c, d, x[ 1], S21); /* 21 */
+ GG (d, a, b, c, x[ 5], S22); /* 22 */
+ GG (c, d, a, b, x[ 9], S23); /* 23 */
+ GG (b, c, d, a, x[13], S24); /* 24 */
+ GG (a, b, c, d, x[ 2], S21); /* 25 */
+ GG (d, a, b, c, x[ 6], S22); /* 26 */
+ GG (c, d, a, b, x[10], S23); /* 27 */
+ GG (b, c, d, a, x[14], S24); /* 28 */
+ GG (a, b, c, d, x[ 3], S21); /* 29 */
+ GG (d, a, b, c, x[ 7], S22); /* 30 */
+ GG (c, d, a, b, x[11], S23); /* 31 */
+ GG (b, c, d, a, x[15], S24); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 0], S31); /* 33 */
+ HH (d, a, b, c, x[ 8], S32); /* 34 */
+ HH (c, d, a, b, x[ 4], S33); /* 35 */
+ HH (b, c, d, a, x[12], S34); /* 36 */
+ HH (a, b, c, d, x[ 2], S31); /* 37 */
+ HH (d, a, b, c, x[10], S32); /* 38 */
+ HH (c, d, a, b, x[ 6], S33); /* 39 */
+ HH (b, c, d, a, x[14], S34); /* 40 */
+ HH (a, b, c, d, x[ 1], S31); /* 41 */
+ HH (d, a, b, c, x[ 9], S32); /* 42 */
+ HH (c, d, a, b, x[ 5], S33); /* 43 */
+ HH (b, c, d, a, x[13], S34); /* 44 */
+ HH (a, b, c, d, x[ 3], S31); /* 45 */
+ HH (d, a, b, c, x[11], S32); /* 46 */
+ HH (c, d, a, b, x[ 7], S33); /* 47 */
+ HH (b, c, d, a, x[15], S34); /* 48 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information.
+ */
+ MD4_memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+static void Encode (unsigned char *output, UINT4 *input, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+static void Decode (UINT4 *output, const unsigned char *input, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+bool ntlm_md4_digest(
+ unsigned char out[CRYPT_MD4_DIGESTSIZE],
+ ntlm_client *ntlm,
+ const unsigned char *in,
+ size_t in_len)
+{
+ MD4_CTX ctx;
+
+ NTLM_UNUSED(ntlm);
+
+ if (in_len > UINT_MAX)
+ return false;
+
+ MD4Init(&ctx);
+ MD4Update(&ctx, in, (unsigned int)in_len);
+ MD4Final (out, &ctx);
+
+ return true;
+}
diff --git a/deps/ntlmclient/crypt_commoncrypto.c b/deps/ntlmclient/crypt_commoncrypto.c
index 4ff57edd29a..3c20469f58d 100644
--- a/deps/ntlmclient/crypt_commoncrypto.c
+++ b/deps/ntlmclient/crypt_commoncrypto.c
@@ -59,11 +59,12 @@ bool ntlm_des_encrypt(
ntlm_des_block *plaintext,
ntlm_des_block *key)
{
+ CCCryptorStatus result;
size_t written;
NTLM_UNUSED(ntlm);
- CCCryptorStatus result = CCCrypt(kCCEncrypt,
+ result = CCCrypt(kCCEncrypt,
kCCAlgorithmDES, kCCOptionECBMode,
key, sizeof(ntlm_des_block), NULL,
plaintext, sizeof(ntlm_des_block),
diff --git a/deps/ntlmclient/crypt_mbedtls.c b/deps/ntlmclient/crypt_mbedtls.c
index 6283c3eec08..4bbb878015d 100644
--- a/deps/ntlmclient/crypt_mbedtls.c
+++ b/deps/ntlmclient/crypt_mbedtls.c
@@ -12,7 +12,6 @@
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/des.h"
#include "mbedtls/entropy.h"
-#include "mbedtls/md4.h"
#include "ntlm.h"
#include "crypt.h"
@@ -88,25 +87,6 @@ bool ntlm_des_encrypt(
return success;
}
-bool ntlm_md4_digest(
- unsigned char out[CRYPT_MD4_DIGESTSIZE],
- ntlm_client *ntlm,
- const unsigned char *in,
- size_t in_len)
-{
- mbedtls_md4_context ctx;
-
- NTLM_UNUSED(ntlm);
-
- mbedtls_md4_init(&ctx);
- mbedtls_md4_starts(&ctx);
- mbedtls_md4_update(&ctx, in, in_len);
- mbedtls_md4_finish(&ctx, out);
- mbedtls_md4_free(&ctx);
-
- return true;
-}
-
bool ntlm_hmac_md5_init(
ntlm_client *ntlm,
const unsigned char *key,
diff --git a/deps/ntlmclient/ntlm.c b/deps/ntlmclient/ntlm.c
index ad4de5de56e..6094a4a3484 100644
--- a/deps/ntlmclient/ntlm.c
+++ b/deps/ntlmclient/ntlm.c
@@ -988,9 +988,9 @@ static inline bool generate_lm_hash(
keystr2_len = (password_len > 7) ? MIN(14, password_len) - 7 : 0;
for (i = 0; i < keystr1_len; i++)
- keystr1[i] = (unsigned char)toupper(password[i]);
+ keystr1[i] = (unsigned char)toupper((unsigned char)password[i]);
for (i = 0; i < keystr2_len; i++)
- keystr2[i] = (unsigned char)toupper(password[i+7]);
+ keystr2[i] = (unsigned char)toupper((unsigned char)password[i+7]);
/* DES encrypt the LM constant using the password as the key */
des_key_from_password(&key1, keystr1, keystr1_len);
diff --git a/deps/ntlmclient/unicode_builtin.c b/deps/ntlmclient/unicode_builtin.c
index e2ee0abf71e..6d398b7c9f8 100644
--- a/deps/ntlmclient/unicode_builtin.c
+++ b/deps/ntlmclient/unicode_builtin.c
@@ -372,13 +372,13 @@ static inline bool unicode_builtin_encoding_convert(
goto done;
}
+ out_len = out_start - out;
+
if ((new_out = realloc(out, out_size)) == NULL) {
ntlm_client_set_errmsg(ntlm, "out of memory");
goto done;
}
- out_len = out_start - out;
-
out = new_out;
out_start = new_out + out_len;
out_end = out + out_size;
diff --git a/deps/pcre/LICENCE b/deps/pcre/LICENCE
index 57a544814c8..803b4119e50 100644
--- a/deps/pcre/LICENCE
+++ b/deps/pcre/LICENCE
@@ -19,13 +19,13 @@ THE BASIC LIBRARY FUNCTIONS
---------------------------
Written by: Philip Hazel
-Email local part: ph10
-Email domain: cam.ac.uk
+Email local part: Philip.Hazel
+Email domain: gmail.com
University of Cambridge Computing Service,
Cambridge, England.
-Copyright (c) 1997-2020 University of Cambridge
+Copyright (c) 1997-2021 University of Cambridge
All rights reserved.
@@ -36,7 +36,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Email domain: freemail.hu
-Copyright(c) 2010-2020 Zoltan Herczeg
+Copyright(c) 2010-2021 Zoltan Herczeg
All rights reserved.
@@ -47,7 +47,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Email domain: freemail.hu
-Copyright(c) 2009-2020 Zoltan Herczeg
+Copyright(c) 2009-2021 Zoltan Herczeg
All rights reserved.
diff --git a/deps/pcre/pcre.h b/deps/pcre/pcre.h
index 5b147b9f8b5..821b50e88a9 100644
--- a/deps/pcre/pcre.h
+++ b/deps/pcre/pcre.h
@@ -42,9 +42,9 @@ POSSIBILITY OF SUCH DAMAGE.
/* The current PCRE version information. */
#define PCRE_MAJOR 8
-#define PCRE_MINOR 44
+#define PCRE_MINOR 45
#define PCRE_PRERELEASE
-#define PCRE_DATE 2020-02-12
+#define PCRE_DATE 2021-06-15
#define PCRE_EXP_DECL extern
diff --git a/deps/pcre/pcre_compile.c b/deps/pcre/pcre_compile.c
index a3f20fd35d0..43f852f4627 100644
--- a/deps/pcre/pcre_compile.c
+++ b/deps/pcre/pcre_compile.c
@@ -6,7 +6,7 @@
and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
- Copyright (c) 1997-2020 University of Cambridge
+ Copyright (c) 1997-2021 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -9104,6 +9104,8 @@ pcre_uchar cworkspace[COMPILE_WORK_SIZE];
similar way to cworkspace, it can be expanded using malloc() if necessary. */
named_group named_groups[NAMED_GROUP_LIST_SIZE];
+cd->named_groups = named_groups;
+cd->named_group_list_size = NAMED_GROUP_LIST_SIZE;
/* Set this early so that early errors get offset 0. */
@@ -9377,8 +9379,6 @@ cd->hwm = cworkspace;
cd->iscondassert = FALSE;
cd->start_workspace = cworkspace;
cd->workspace_size = COMPILE_WORK_SIZE;
-cd->named_groups = named_groups;
-cd->named_group_list_size = NAMED_GROUP_LIST_SIZE;
cd->start_pattern = (const pcre_uchar *)pattern;
cd->end_pattern = (const pcre_uchar *)(pattern + STRLEN_UC((const pcre_uchar *)pattern));
cd->req_varyopt = 0;
@@ -9489,6 +9489,7 @@ if (cd->names_found > 0)
add_name(cd, ng->name, ng->length, ng->number);
if (cd->named_group_list_size > NAMED_GROUP_LIST_SIZE)
(PUBL(free))((void *)cd->named_groups);
+ cd->named_group_list_size = 0; /* So we don't free it twice */
}
/* Set up a starting, non-extracting bracket, then compile the expression. On
@@ -9639,6 +9640,8 @@ if (errorcode != 0)
{
(PUBL(free))(re);
PCRE_EARLY_ERROR_RETURN:
+ if (cd->named_group_list_size > NAMED_GROUP_LIST_SIZE)
+ (PUBL(free))((void *)cd->named_groups);
*erroroffset = (int)(ptr - (const pcre_uchar *)pattern);
PCRE_EARLY_ERROR_RETURN2:
*errorptr = find_error_text(errorcode);
diff --git a/deps/pcre/pcre_exec.c b/deps/pcre/pcre_exec.c
index 3fd58cbe31c..5b96954fcd9 100644
--- a/deps/pcre/pcre_exec.c
+++ b/deps/pcre/pcre_exec.c
@@ -6,7 +6,7 @@
and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
- Copyright (c) 1997-2018 University of Cambridge
+ Copyright (c) 1997-2021 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -758,7 +758,7 @@ for (;;)
md->mark = NULL; /* In case previously set by assertion */
RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, md,
eptrb, RM55);
- if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) &&
+ if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT || rrc == MATCH_KETRPOS) &&
md->mark == NULL) md->mark = ecode + 2;
/* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an
diff --git a/deps/xdiff/CMakeLists.txt b/deps/xdiff/CMakeLists.txt
new file mode 100644
index 00000000000..743ac636f0a
--- /dev/null
+++ b/deps/xdiff/CMakeLists.txt
@@ -0,0 +1,28 @@
+
+file(GLOB SRC_XDIFF "*.c" "*.h")
+list(SORT SRC_XDIFF)
+
+add_library(xdiff OBJECT ${SRC_XDIFF})
+target_include_directories(xdiff SYSTEM PRIVATE
+ "${PROJECT_SOURCE_DIR}/include"
+ "${PROJECT_SOURCE_DIR}/src/util"
+ "${PROJECT_BINARY_DIR}/src/util"
+ ${LIBGIT2_SYSTEM_INCLUDES}
+ ${LIBGIT2_DEPENDENCY_INCLUDES})
+
+# 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(xdiffi.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xemit.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xhistogram.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xmerge.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xutils.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xpatience.c PROPERTIES COMPILE_FLAGS -WX-)
+else()
+ set_source_files_properties(xdiffi.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
+ set_source_files_properties(xemit.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
+ set_source_files_properties(xhistogram.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
+ set_source_files_properties(xutils.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
+ set_source_files_properties(xpatience.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
+endif()
diff --git a/src/libgit2/xdiff/git-xdiff.h b/deps/xdiff/git-xdiff.h
similarity index 91%
rename from src/libgit2/xdiff/git-xdiff.h
rename to deps/xdiff/git-xdiff.h
index b75dba8195a..1450ab3dd21 100644
--- a/src/libgit2/xdiff/git-xdiff.h
+++ b/deps/xdiff/git-xdiff.h
@@ -27,11 +27,14 @@
# endif
#endif
+#define XDL_UNUSED GIT_UNUSED_ARG
+
#define xdl_malloc(x) git__malloc(x)
+#define xdl_calloc(n, sz) git__calloc(n, sz)
#define xdl_free(ptr) git__free(ptr)
#define xdl_realloc(ptr, x) git__realloc(ptr, x)
-#define XDL_BUG(msg) GIT_ASSERT(msg)
+#define XDL_BUG(msg) GIT_ASSERT(!msg)
#define xdl_regex_t git_regexp
#define xdl_regmatch_t git_regmatch
diff --git a/src/libgit2/xdiff/xdiff.h b/deps/xdiff/xdiff.h
similarity index 100%
rename from src/libgit2/xdiff/xdiff.h
rename to deps/xdiff/xdiff.h
diff --git a/src/libgit2/xdiff/xdiffi.c b/deps/xdiff/xdiffi.c
similarity index 97%
rename from src/libgit2/xdiff/xdiffi.c
rename to deps/xdiff/xdiffi.c
index af31b7f4b39..ea36143af2c 100644
--- a/src/libgit2/xdiff/xdiffi.c
+++ b/deps/xdiff/xdiffi.c
@@ -315,16 +315,19 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
long *kvd, *kvdf, *kvdb;
xdalgoenv_t xenv;
diffdata_t dd1, dd2;
+ int res;
- if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF)
- return xdl_do_patience_diff(mf1, mf2, xpp, xe);
-
- if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
- return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
+ if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0)
+ return -1;
- if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
+ if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF) {
+ res = xdl_do_patience_diff(xpp, xe);
+ goto out;
+ }
- return -1;
+ if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF) {
+ res = xdl_do_histogram_diff(xpp, xe);
+ goto out;
}
/*
@@ -334,7 +337,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
* One is to store the forward path and one to store the backward path.
*/
ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;
- if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) {
+ if (!XDL_ALLOC_ARRAY(kvd, 2 * ndiags + 2)) {
xdl_free_env(xe);
return -1;
@@ -359,17 +362,15 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
dd2.rchg = xe->xdf2.rchg;
dd2.rindex = xe->xdf2.rindex;
- if (xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,
- kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0, &xenv) < 0) {
-
- xdl_free(kvd);
- xdl_free_env(xe);
- return -1;
- }
-
+ res = xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,
+ kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0,
+ &xenv);
xdl_free(kvd);
+ out:
+ if (res < 0)
+ xdl_free_env(xe);
- return 0;
+ return res;
}
@@ -972,7 +973,7 @@ void xdl_free_script(xdchange_t *xscr) {
}
}
-static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+static int xdl_call_hunk_func(xdfenv_t *xe XDL_UNUSED, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg)
{
xdchange_t *xch, *xche;
diff --git a/src/libgit2/xdiff/xdiffi.h b/deps/xdiff/xdiffi.h
similarity index 90%
rename from src/libgit2/xdiff/xdiffi.h
rename to deps/xdiff/xdiffi.h
index 8f1c7c8b044..126c9d8ff4e 100644
--- a/src/libgit2/xdiff/xdiffi.h
+++ b/deps/xdiff/xdiffi.h
@@ -56,9 +56,7 @@ int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr);
void xdl_free_script(xdchange_t *xscr);
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg);
-int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
- xdfenv_t *env);
-int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
- xdfenv_t *env);
+int xdl_do_patience_diff(xpparam_t const *xpp, xdfenv_t *env);
+int xdl_do_histogram_diff(xpparam_t const *xpp, xdfenv_t *env);
#endif /* #if !defined(XDIFFI_H) */
diff --git a/src/libgit2/xdiff/xemit.c b/deps/xdiff/xemit.c
similarity index 98%
rename from src/libgit2/xdiff/xemit.c
rename to deps/xdiff/xemit.c
index 1cbf2b9829e..75f0fe49866 100644
--- a/src/libgit2/xdiff/xemit.c
+++ b/deps/xdiff/xemit.c
@@ -65,7 +65,7 @@ xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
*xscr = xch;
}
- if (*xscr == NULL)
+ if (!*xscr)
return NULL;
lxch = *xscr;
@@ -95,7 +95,7 @@ xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
}
-static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)
+static long def_ff(const char *rec, long len, char *buf, long sz)
{
if (len > 0 &&
(isalpha((unsigned char)*rec) || /* identifier? */
@@ -117,7 +117,7 @@ static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,
const char *rec;
long len = xdl_get_rec(xdf, ri, &rec);
if (!xecfg->find_func)
- return def_ff(rec, len, buf, sz, xecfg->find_func_priv);
+ return def_ff(rec, len, buf, sz);
return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);
}
diff --git a/src/libgit2/xdiff/xemit.h b/deps/xdiff/xemit.h
similarity index 100%
rename from src/libgit2/xdiff/xemit.h
rename to deps/xdiff/xemit.h
diff --git a/src/libgit2/xdiff/xhistogram.c b/deps/xdiff/xhistogram.c
similarity index 92%
rename from src/libgit2/xdiff/xhistogram.c
rename to deps/xdiff/xhistogram.c
index 80794748b0d..16a8fe2f3f3 100644
--- a/src/libgit2/xdiff/xhistogram.c
+++ b/deps/xdiff/xhistogram.c
@@ -251,7 +251,7 @@ static int find_lcs(xpparam_t const *xpp, xdfenv_t *env,
int line1, int count1, int line2, int count2)
{
int b_ptr;
- int sz, ret = -1;
+ int ret = -1;
struct histindex index;
memset(&index, 0, sizeof(index));
@@ -265,23 +265,16 @@ static int find_lcs(xpparam_t const *xpp, xdfenv_t *env,
index.rcha.head = NULL;
index.table_bits = xdl_hashbits(count1);
- sz = index.records_size = 1 << index.table_bits;
- sz *= sizeof(struct record *);
- if (!(index.records = (struct record **) xdl_malloc(sz)))
+ index.records_size = 1 << index.table_bits;
+ if (!XDL_CALLOC_ARRAY(index.records, index.records_size))
goto cleanup;
- memset(index.records, 0, sz);
- sz = index.line_map_size = count1;
- sz *= sizeof(struct record *);
- if (!(index.line_map = (struct record **) xdl_malloc(sz)))
+ index.line_map_size = count1;
+ if (!XDL_CALLOC_ARRAY(index.line_map, index.line_map_size))
goto cleanup;
- memset(index.line_map, 0, sz);
- sz = index.line_map_size;
- sz *= sizeof(unsigned int);
- if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz)))
+ if (!XDL_CALLOC_ARRAY(index.next_ptrs, index.line_map_size))
goto cleanup;
- memset(index.next_ptrs, 0, sz);
/* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */
if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0)
@@ -369,12 +362,8 @@ static int histogram_diff(xpparam_t const *xpp, xdfenv_t *env,
return result;
}
-int xdl_do_histogram_diff(mmfile_t *file1, mmfile_t *file2,
- xpparam_t const *xpp, xdfenv_t *env)
+int xdl_do_histogram_diff(xpparam_t const *xpp, xdfenv_t *env)
{
- if (xdl_prepare_env(file1, file2, xpp, env) < 0)
- return -1;
-
return histogram_diff(xpp, env,
env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1,
env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1);
diff --git a/src/libgit2/xdiff/xinclude.h b/deps/xdiff/xinclude.h
similarity index 100%
rename from src/libgit2/xdiff/xinclude.h
rename to deps/xdiff/xinclude.h
diff --git a/src/libgit2/xdiff/xmacros.h b/deps/xdiff/xmacros.h
similarity index 72%
rename from src/libgit2/xdiff/xmacros.h
rename to deps/xdiff/xmacros.h
index 2809a28ca96..8487bb396fa 100644
--- a/src/libgit2/xdiff/xmacros.h
+++ b/deps/xdiff/xmacros.h
@@ -34,7 +34,6 @@
#define XDL_ADDBITS(v,b) ((v) + ((v) >> (b)))
#define XDL_MASKBITS(b) ((1UL << (b)) - 1)
#define XDL_HASHLONG(v,b) (XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b))
-#define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0)
#define XDL_LE32_PUT(p, v) \
do { \
unsigned char *__p = (unsigned char *) (p); \
@@ -50,5 +49,23 @@ do { \
((unsigned long) __p[2]) << 16 | ((unsigned long) __p[3]) << 24; \
} while (0)
+/* Allocate an array of nr elements, returns NULL on failure */
+#define XDL_ALLOC_ARRAY(p, nr) \
+ ((p) = SIZE_MAX / sizeof(*(p)) >= (size_t)(nr) \
+ ? xdl_malloc((nr) * sizeof(*(p))) \
+ : NULL)
+
+/* Allocate an array of nr zeroed out elements, returns NULL on failure */
+#define XDL_CALLOC_ARRAY(p, nr) ((p) = xdl_calloc(nr, sizeof(*(p))))
+
+/*
+ * Ensure array p can accommodate at least nr elements, growing the
+ * array and updating alloc (which is the number of allocated
+ * elements) as necessary. Frees p and returns -1 on failure, returns
+ * 0 on success
+ */
+#define XDL_ALLOC_GROW(p, nr, alloc) \
+ (-!((nr) <= (alloc) || \
+ ((p) = xdl_alloc_grow_helper((p), (nr), &(alloc), sizeof(*(p))))))
#endif /* #if !defined(XMACROS_H) */
diff --git a/src/libgit2/xdiff/xmerge.c b/deps/xdiff/xmerge.c
similarity index 97%
rename from src/libgit2/xdiff/xmerge.c
rename to deps/xdiff/xmerge.c
index 433e2d74157..6ebf73a933a 100644
--- a/src/libgit2/xdiff/xmerge.c
+++ b/deps/xdiff/xmerge.c
@@ -684,42 +684,42 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
xmparam_t const *xmp, mmbuffer_t *result)
{
- xdchange_t *xscr1, *xscr2;
+ xdchange_t *xscr1 = NULL, *xscr2 = NULL;
xdfenv_t xe1, xe2;
- int status;
+ int status = -1;
xpparam_t const *xpp = &xmp->xpp;
result->ptr = NULL;
result->size = 0;
- if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0) {
+ if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0)
return -1;
- }
- if (xdl_do_diff(orig, mf2, xpp, &xe2) < 0) {
- xdl_free_env(&xe1);
- return -1;
- }
+
+ if (xdl_do_diff(orig, mf2, xpp, &xe2) < 0)
+ goto free_xe1; /* avoid double free of xe2 */
+
if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 ||
xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 ||
- xdl_build_script(&xe1, &xscr1) < 0) {
- xdl_free_env(&xe1);
- return -1;
- }
+ xdl_build_script(&xe1, &xscr1) < 0)
+ goto out;
+
if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 ||
xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 ||
- xdl_build_script(&xe2, &xscr2) < 0) {
- xdl_free_script(xscr1);
- xdl_free_env(&xe1);
- xdl_free_env(&xe2);
- return -1;
- }
- status = 0;
+ xdl_build_script(&xe2, &xscr2) < 0)
+ goto out;
+
if (!xscr1) {
result->ptr = xdl_malloc(mf2->size);
+ if (!result->ptr)
+ goto out;
+ status = 0;
memcpy(result->ptr, mf2->ptr, mf2->size);
result->size = mf2->size;
} else if (!xscr2) {
result->ptr = xdl_malloc(mf1->size);
+ if (!result->ptr)
+ goto out;
+ status = 0;
memcpy(result->ptr, mf1->ptr, mf1->size);
result->size = mf1->size;
} else {
@@ -727,11 +727,13 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
&xe2, xscr2,
xmp, result);
}
+ out:
xdl_free_script(xscr1);
xdl_free_script(xscr2);
- xdl_free_env(&xe1);
xdl_free_env(&xe2);
+ free_xe1:
+ xdl_free_env(&xe1);
return status;
}
diff --git a/src/libgit2/xdiff/xpatience.c b/deps/xdiff/xpatience.c
similarity index 88%
rename from src/libgit2/xdiff/xpatience.c
rename to deps/xdiff/xpatience.c
index c5d48e80aef..a2d8955537f 100644
--- a/src/libgit2/xdiff/xpatience.c
+++ b/deps/xdiff/xpatience.c
@@ -69,7 +69,6 @@ struct hashmap {
} *entries, *first, *last;
/* were common records found? */
unsigned long has_matches;
- mmfile_t *file1, *file2;
xdfenv_t *env;
xpparam_t const *xpp;
};
@@ -139,23 +138,17 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,
*
* It is assumed that env has been prepared using xdl_prepare().
*/
-static int fill_hashmap(mmfile_t *file1, mmfile_t *file2,
- xpparam_t const *xpp, xdfenv_t *env,
+static int fill_hashmap(xpparam_t const *xpp, xdfenv_t *env,
struct hashmap *result,
int line1, int count1, int line2, int count2)
{
- result->file1 = file1;
- result->file2 = file2;
result->xpp = xpp;
result->env = env;
/* We know exactly how large we want the hash map */
result->alloc = count1 * 2;
- result->entries = (struct entry *)
- xdl_malloc(result->alloc * sizeof(struct entry));
- if (!result->entries)
+ if (!XDL_CALLOC_ARRAY(result->entries, result->alloc))
return -1;
- memset(result->entries, 0, result->alloc * sizeof(struct entry));
/* First, fill with entries from the first file */
while (count1--)
@@ -198,9 +191,9 @@ static int binary_search(struct entry **sequence, int longest,
* item per sequence length: the sequence with the smallest last
* element (in terms of line2).
*/
-static struct entry *find_longest_common_sequence(struct hashmap *map)
+static int find_longest_common_sequence(struct hashmap *map, struct entry **res)
{
- struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *));
+ struct entry **sequence;
int longest = 0, i;
struct entry *entry;
@@ -211,6 +204,9 @@ static struct entry *find_longest_common_sequence(struct hashmap *map)
*/
int anchor_i = -1;
+ if (!XDL_ALLOC_ARRAY(sequence, map->nr))
+ return -1;
+
for (entry = map->first; entry; entry = entry->next) {
if (!entry->line2 || entry->line2 == NON_UNIQUE)
continue;
@@ -230,8 +226,9 @@ static struct entry *find_longest_common_sequence(struct hashmap *map)
/* No common unique lines were found */
if (!longest) {
+ *res = NULL;
xdl_free(sequence);
- return NULL;
+ return 0;
}
/* Iterate starting at the last element, adjusting the "next" members */
@@ -241,8 +238,9 @@ static struct entry *find_longest_common_sequence(struct hashmap *map)
entry->previous->next = entry;
entry = entry->previous;
}
+ *res = entry;
xdl_free(sequence);
- return entry;
+ return 0;
}
static int match(struct hashmap *map, int line1, int line2)
@@ -252,8 +250,7 @@ static int match(struct hashmap *map, int line1, int line2)
return record1->ha == record2->ha;
}
-static int patience_diff(mmfile_t *file1, mmfile_t *file2,
- xpparam_t const *xpp, xdfenv_t *env,
+static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,
int line1, int count1, int line2, int count2);
static int walk_common_sequence(struct hashmap *map, struct entry *first,
@@ -284,8 +281,7 @@ static int walk_common_sequence(struct hashmap *map, struct entry *first,
/* Recurse */
if (next1 > line1 || next2 > line2) {
- if (patience_diff(map->file1, map->file2,
- map->xpp, map->env,
+ if (patience_diff(map->xpp, map->env,
line1, next1 - line1,
line2, next2 - line2))
return -1;
@@ -324,8 +320,7 @@ static int fall_back_to_classic_diff(struct hashmap *map,
*
* This function assumes that env was prepared with xdl_prepare_env().
*/
-static int patience_diff(mmfile_t *file1, mmfile_t *file2,
- xpparam_t const *xpp, xdfenv_t *env,
+static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,
int line1, int count1, int line2, int count2)
{
struct hashmap map;
@@ -344,7 +339,7 @@ static int patience_diff(mmfile_t *file1, mmfile_t *file2,
}
memset(&map, 0, sizeof(map));
- if (fill_hashmap(file1, file2, xpp, env, &map,
+ if (fill_hashmap(xpp, env, &map,
line1, count1, line2, count2))
return -1;
@@ -358,25 +353,21 @@ static int patience_diff(mmfile_t *file1, mmfile_t *file2,
return 0;
}
- first = find_longest_common_sequence(&map);
+ result = find_longest_common_sequence(&map, &first);
+ if (result)
+ goto out;
if (first)
result = walk_common_sequence(&map, first,
line1, count1, line2, count2);
else
result = fall_back_to_classic_diff(&map,
line1, count1, line2, count2);
-
+ out:
xdl_free(map.entries);
return result;
}
-int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2,
- xpparam_t const *xpp, xdfenv_t *env)
+int xdl_do_patience_diff(xpparam_t const *xpp, xdfenv_t *env)
{
- if (xdl_prepare_env(file1, file2, xpp, env) < 0)
- return -1;
-
- /* environment is cleaned up in xdl_diff() */
- return patience_diff(file1, file2, xpp, env,
- 1, env->xdf1.nrec, 1, env->xdf2.nrec);
+ return patience_diff(xpp, env, 1, env->xdf1.nrec, 1, env->xdf2.nrec);
}
diff --git a/src/libgit2/xdiff/xprepare.c b/deps/xdiff/xprepare.c
similarity index 90%
rename from src/libgit2/xdiff/xprepare.c
rename to deps/xdiff/xprepare.c
index 4527a4a07c4..c84549f6c50 100644
--- a/src/libgit2/xdiff/xprepare.c
+++ b/deps/xdiff/xprepare.c
@@ -78,15 +78,14 @@ static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
return -1;
}
- if (!(cf->rchash = (xdlclass_t **) xdl_malloc(cf->hsize * sizeof(xdlclass_t *)))) {
+ if (!XDL_CALLOC_ARRAY(cf->rchash, cf->hsize)) {
xdl_cha_free(&cf->ncha);
return -1;
}
- memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *));
cf->alloc = size;
- if (!(cf->rcrecs = (xdlclass_t **) xdl_malloc(cf->alloc * sizeof(xdlclass_t *)))) {
+ if (!XDL_ALLOC_ARRAY(cf->rcrecs, cf->alloc)) {
xdl_free(cf->rchash);
xdl_cha_free(&cf->ncha);
@@ -112,7 +111,6 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t
long hi;
char const *line;
xdlclass_t *rcrec;
- xdlclass_t **rcrecs;
line = rec->ptr;
hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);
@@ -128,14 +126,8 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t
return -1;
}
rcrec->idx = cf->count++;
- if (cf->count > cf->alloc) {
- cf->alloc *= 2;
- if (!(rcrecs = (xdlclass_t **) xdl_realloc(cf->rcrecs, cf->alloc * sizeof(xdlclass_t *)))) {
-
+ if (XDL_ALLOC_GROW(cf->rcrecs, cf->count, cf->alloc))
return -1;
- }
- cf->rcrecs = rcrecs;
- }
cf->rcrecs[rcrec->idx] = rcrec;
rcrec->line = line;
rcrec->size = rec->size;
@@ -164,7 +156,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
unsigned long hav;
char const *blk, *cur, *top, *prev;
xrecord_t *crec;
- xrecord_t **recs, **rrecs;
+ xrecord_t **recs;
xrecord_t **rhash;
unsigned long *ha;
char *rchg;
@@ -178,26 +170,21 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
goto abort;
- if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
+ if (!XDL_ALLOC_ARRAY(recs, narec))
goto abort;
hbits = xdl_hashbits((unsigned int) narec);
hsize = 1 << hbits;
- if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
+ if (!XDL_CALLOC_ARRAY(rhash, hsize))
goto abort;
- memset(rhash, 0, hsize * sizeof(xrecord_t *));
nrec = 0;
- if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
+ if ((cur = blk = xdl_mmfile_first(mf, &bsize))) {
for (top = blk + bsize; cur < top; ) {
prev = cur;
hav = xdl_hash_record(&cur, top, xpp->flags);
- if (nrec >= narec) {
- narec *= 2;
- if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *))))
- goto abort;
- recs = rrecs;
- }
+ if (XDL_ALLOC_GROW(recs, nrec + 1, narec))
+ goto abort;
if (!(crec = xdl_cha_alloc(&xdf->rcha)))
goto abort;
crec->ptr = prev;
@@ -209,15 +196,14 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
}
}
- if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char))))
+ if (!XDL_CALLOC_ARRAY(rchg, nrec + 2))
goto abort;
- memset(rchg, 0, (nrec + 2) * sizeof(char));
if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
(XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {
- if (!(rindex = xdl_malloc((nrec + 1) * sizeof(*rindex))))
+ if (!XDL_ALLOC_ARRAY(rindex, nrec + 1))
goto abort;
- if (!(ha = xdl_malloc((nrec + 1) * sizeof(*ha))))
+ if (!XDL_ALLOC_ARRAY(ha, nrec + 1))
goto abort;
}
@@ -383,11 +369,8 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd
xdlclass_t *rcrec;
char *dis, *dis1, *dis2;
- if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) {
-
+ if (!XDL_CALLOC_ARRAY(dis, xdf1->nrec + xdf2->nrec + 2))
return -1;
- }
- memset(dis, 0, xdf1->nrec + xdf2->nrec + 2);
dis1 = dis;
dis2 = dis1 + xdf1->nrec + 1;
diff --git a/src/libgit2/xdiff/xprepare.h b/deps/xdiff/xprepare.h
similarity index 100%
rename from src/libgit2/xdiff/xprepare.h
rename to deps/xdiff/xprepare.h
diff --git a/src/libgit2/xdiff/xtypes.h b/deps/xdiff/xtypes.h
similarity index 100%
rename from src/libgit2/xdiff/xtypes.h
rename to deps/xdiff/xtypes.h
diff --git a/src/libgit2/xdiff/xutils.c b/deps/xdiff/xutils.c
similarity index 96%
rename from src/libgit2/xdiff/xutils.c
rename to deps/xdiff/xutils.c
index cfa6e2220ff..9e36f24875d 100644
--- a/src/libgit2/xdiff/xutils.c
+++ b/deps/xdiff/xutils.c
@@ -122,7 +122,7 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {
long nl = 0, size, tsize = 0;
char const *data, *cur, *top;
- if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) {
+ if ((cur = data = xdl_mmfile_first(mf, &size))) {
for (top = data + size; nl < sample && cur < top; ) {
nl++;
if (!(cur = memchr(cur, '\n', top - cur)))
@@ -432,3 +432,20 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
return 0;
}
+
+void* xdl_alloc_grow_helper(void *p, long nr, long *alloc, size_t size)
+{
+ void *tmp = NULL;
+ size_t n = ((LONG_MAX - 16) / 2 >= *alloc) ? 2 * *alloc + 16 : LONG_MAX;
+ if (nr > n)
+ n = nr;
+ if (SIZE_MAX / size >= n)
+ tmp = xdl_realloc(p, n * size);
+ if (tmp) {
+ *alloc = n;
+ } else {
+ xdl_free(p);
+ *alloc = 0;
+ }
+ return tmp;
+}
diff --git a/src/libgit2/xdiff/xutils.h b/deps/xdiff/xutils.h
similarity index 93%
rename from src/libgit2/xdiff/xutils.h
rename to deps/xdiff/xutils.h
index fba7bae03c7..fd0bba94e8b 100644
--- a/src/libgit2/xdiff/xutils.h
+++ b/deps/xdiff/xutils.h
@@ -42,6 +42,7 @@ int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
int line1, int count1, int line2, int count2);
-
+/* Do not call this function, use XDL_ALLOC_GROW instead */
+void* xdl_alloc_grow_helper(void* p, long nr, long* alloc, size_t size);
#endif /* #if !defined(XUTILS_H) */
diff --git a/deps/zlib/CMakeLists.txt b/deps/zlib/CMakeLists.txt
index 435877d869d..078ce69b32e 100644
--- a/deps/zlib/CMakeLists.txt
+++ b/deps/zlib/CMakeLists.txt
@@ -1,5 +1,10 @@
disable_warnings(implicit-fallthrough)
-add_definitions(-DNO_VIZ -DSTDC -DNO_GZIP)
+add_definitions(-DNO_VIZ -DSTDC -DNO_GZIP -DHAVE_SYS_TYPES_H -DHAVE_STDINT_H -DHAVE_STDDEF_H)
+
+if(MINGW OR MSYS)
+ add_definitions(-DZ_HAVE_UNISTD_H -D_LFS64_LARGEFILE -D_LARGEFILE64_SOURCE=1)
+endif()
+
file(GLOB SRC_ZLIB "*.c" "*.h")
list(SORT SRC_ZLIB)
include_directories(".")
diff --git a/deps/zlib/COPYING b/deps/zlib/COPYING
deleted file mode 100644
index e2e86d76968..00000000000
--- a/deps/zlib/COPYING
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any
-damages arising from the use of this software.
-
-Permission is granted to anyone to use this software for any
-purpose, including commercial applications, and to alter it and
-redistribute it freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you
- must not claim that you wrote the original software. If you
- use this software in a product, an acknowledgment in the
- product documentation would be appreciated but is not
- required.
-
-2. Altered source versions must be plainly marked as such, and
- must not be misrepresented as being the original software.
-
-3. This notice may not be removed or altered from any source
- distribution.
-
-Jean-loup Gailly Mark Adler
-
-The data format used by the zlib library is described by RFCs
-(Request for Comments) 1950 to 1952 in the files rfc1950 (zlib
-format), rfc1951 (deflate format) and rfc1952 (gzip format).
diff --git a/deps/zlib/LICENSE b/deps/zlib/LICENSE
new file mode 100644
index 00000000000..ab8ee6f7142
--- /dev/null
+++ b/deps/zlib/LICENSE
@@ -0,0 +1,22 @@
+Copyright notice:
+
+ (C) 1995-2022 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
diff --git a/deps/zlib/adler32.c b/deps/zlib/adler32.c
index d0be4380a39..04b81d29bad 100644
--- a/deps/zlib/adler32.c
+++ b/deps/zlib/adler32.c
@@ -7,8 +7,6 @@
#include "zutil.h"
-local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2));
-
#define BASE 65521U /* largest prime smaller than 65536 */
#define NMAX 5552
/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
@@ -60,11 +58,7 @@ local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2));
#endif
/* ========================================================================= */
-uLong ZEXPORT adler32_z(adler, buf, len)
- uLong adler;
- const Bytef *buf;
- z_size_t len;
-{
+uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, z_size_t len) {
unsigned long sum2;
unsigned n;
@@ -131,20 +125,12 @@ uLong ZEXPORT adler32_z(adler, buf, len)
}
/* ========================================================================= */
-uLong ZEXPORT adler32(adler, buf, len)
- uLong adler;
- const Bytef *buf;
- uInt len;
-{
+uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) {
return adler32_z(adler, buf, len);
}
/* ========================================================================= */
-local uLong adler32_combine_(adler1, adler2, len2)
- uLong adler1;
- uLong adler2;
- z_off64_t len2;
-{
+local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2) {
unsigned long sum1;
unsigned long sum2;
unsigned rem;
@@ -169,18 +155,10 @@ local uLong adler32_combine_(adler1, adler2, len2)
}
/* ========================================================================= */
-uLong ZEXPORT adler32_combine(adler1, adler2, len2)
- uLong adler1;
- uLong adler2;
- z_off_t len2;
-{
+uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2) {
return adler32_combine_(adler1, adler2, len2);
}
-uLong ZEXPORT adler32_combine64(adler1, adler2, len2)
- uLong adler1;
- uLong adler2;
- z_off64_t len2;
-{
+uLong ZEXPORT adler32_combine64(uLong adler1, uLong adler2, z_off64_t len2) {
return adler32_combine_(adler1, adler2, len2);
}
diff --git a/deps/zlib/crc32.c b/deps/zlib/crc32.c
index 25cb7a009e1..6c38f5c04c6 100644
--- a/deps/zlib/crc32.c
+++ b/deps/zlib/crc32.c
@@ -98,10 +98,6 @@
# endif
#endif
-/* Local functions. */
-local z_crc_t multmodp OF((z_crc_t a, z_crc_t b));
-local z_crc_t x2nmodp OF((z_off64_t n, unsigned k));
-
/* If available, use the ARM processor CRC32 instruction. */
#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) && W == 8
# define ARMCRC32
@@ -114,12 +110,7 @@ local z_crc_t x2nmodp OF((z_off64_t n, unsigned k));
instruction, if one is available. This assumes that word_t is either 32 bits
or 64 bits.
*/
-
-local z_word_t byte_swap(z_word_t word);
-
-local z_word_t byte_swap(word)
- z_word_t word;
-{
+local z_word_t byte_swap(z_word_t word) {
# if W == 8
return
(word & 0xff00000000000000) >> 56 |
@@ -140,24 +131,77 @@ local z_word_t byte_swap(word)
}
#endif
+#ifdef DYNAMIC_CRC_TABLE
+/* =========================================================================
+ * Table of powers of x for combining CRC-32s, filled in by make_crc_table()
+ * below.
+ */
+ local z_crc_t FAR x2n_table[32];
+#else
+/* =========================================================================
+ * Tables for byte-wise and braided CRC-32 calculations, and a table of powers
+ * of x for combining CRC-32s, all made by make_crc_table().
+ */
+# include "crc32.h"
+#endif
+
/* CRC polynomial. */
#define POLY 0xedb88320 /* p(x) reflected, with x^32 implied */
-#ifdef DYNAMIC_CRC_TABLE
+/*
+ Return a(x) multiplied by b(x) modulo p(x), where p(x) is the CRC polynomial,
+ reflected. For speed, this requires that a not be zero.
+ */
+local z_crc_t multmodp(z_crc_t a, z_crc_t b) {
+ z_crc_t m, p;
+
+ m = (z_crc_t)1 << 31;
+ p = 0;
+ for (;;) {
+ if (a & m) {
+ p ^= b;
+ if ((a & (m - 1)) == 0)
+ break;
+ }
+ m >>= 1;
+ b = b & 1 ? (b >> 1) ^ POLY : b >> 1;
+ }
+ return p;
+}
+
+/*
+ Return x^(n * 2^k) modulo p(x). Requires that x2n_table[] has been
+ initialized.
+ */
+local z_crc_t x2nmodp(z_off64_t n, unsigned k) {
+ z_crc_t p;
+
+ p = (z_crc_t)1 << 31; /* x^0 == 1 */
+ while (n) {
+ if (n & 1)
+ p = multmodp(x2n_table[k & 31], p);
+ n >>= 1;
+ k++;
+ }
+ return p;
+}
+#ifdef DYNAMIC_CRC_TABLE
+/* =========================================================================
+ * Build the tables for byte-wise and braided CRC-32 calculations, and a table
+ * of powers of x for combining CRC-32s.
+ */
local z_crc_t FAR crc_table[256];
-local z_crc_t FAR x2n_table[32];
-local void make_crc_table OF((void));
#ifdef W
local z_word_t FAR crc_big_table[256];
local z_crc_t FAR crc_braid_table[W][256];
local z_word_t FAR crc_braid_big_table[W][256];
- local void braid OF((z_crc_t [][256], z_word_t [][256], int, int));
+ local void braid(z_crc_t [][256], z_word_t [][256], int, int);
#endif
#ifdef MAKECRCH
- local void write_table OF((FILE *, const z_crc_t FAR *, int));
- local void write_table32hi OF((FILE *, const z_word_t FAR *, int));
- local void write_table64 OF((FILE *, const z_word_t FAR *, int));
+ local void write_table(FILE *, const z_crc_t FAR *, int);
+ local void write_table32hi(FILE *, const z_word_t FAR *, int);
+ local void write_table64(FILE *, const z_word_t FAR *, int);
#endif /* MAKECRCH */
/*
@@ -170,7 +214,6 @@ local void make_crc_table OF((void));
/* Definition of once functionality. */
typedef struct once_s once_t;
-local void once OF((once_t *, void (*)(void)));
/* Check for the availability of atomics. */
#if defined(__STDC__) && __STDC_VERSION__ >= 201112L && \
@@ -190,10 +233,7 @@ struct once_s {
invoke once() at the same time. The state must be a once_t initialized with
ONCE_INIT.
*/
-local void once(state, init)
- once_t *state;
- void (*init)(void);
-{
+local void once(once_t *state, void (*init)(void)) {
if (!atomic_load(&state->done)) {
if (atomic_flag_test_and_set(&state->begun))
while (!atomic_load(&state->done))
@@ -216,10 +256,7 @@ struct once_s {
/* Test and set. Alas, not atomic, but tries to minimize the period of
vulnerability. */
-local int test_and_set OF((int volatile *));
-local int test_and_set(flag)
- int volatile *flag;
-{
+local int test_and_set(int volatile *flag) {
int was;
was = *flag;
@@ -228,10 +265,7 @@ local int test_and_set(flag)
}
/* Run the provided init() function once. This is not thread-safe. */
-local void once(state, init)
- once_t *state;
- void (*init)(void);
-{
+local void once(once_t *state, void (*init)(void)) {
if (!state->done) {
if (test_and_set(&state->begun))
while (!state->done)
@@ -273,8 +307,7 @@ local once_t made = ONCE_INIT;
combinations of CRC register values and incoming bytes.
*/
-local void make_crc_table()
-{
+local void make_crc_table(void) {
unsigned i, j, n;
z_crc_t p;
@@ -441,11 +474,7 @@ local void make_crc_table()
Write the 32-bit values in table[0..k-1] to out, five per line in
hexadecimal separated by commas.
*/
-local void write_table(out, table, k)
- FILE *out;
- const z_crc_t FAR *table;
- int k;
-{
+local void write_table(FILE *out, const z_crc_t FAR *table, int k) {
int n;
for (n = 0; n < k; n++)
@@ -458,11 +487,7 @@ local void write_table(out, table, k)
Write the high 32-bits of each value in table[0..k-1] to out, five per line
in hexadecimal separated by commas.
*/
-local void write_table32hi(out, table, k)
-FILE *out;
-const z_word_t FAR *table;
-int k;
-{
+local void write_table32hi(FILE *out, const z_word_t FAR *table, int k) {
int n;
for (n = 0; n < k; n++)
@@ -478,11 +503,7 @@ int k;
bits. If not, then the type cast and format string can be adjusted
accordingly.
*/
-local void write_table64(out, table, k)
- FILE *out;
- const z_word_t FAR *table;
- int k;
-{
+local void write_table64(FILE *out, const z_word_t FAR *table, int k) {
int n;
for (n = 0; n < k; n++)
@@ -492,8 +513,7 @@ local void write_table64(out, table, k)
}
/* Actually do the deed. */
-int main()
-{
+int main(void) {
make_crc_table();
return 0;
}
@@ -505,12 +525,7 @@ int main()
Generate the little and big-endian braid tables for the given n and z_word_t
size w. Each array must have room for w blocks of 256 elements.
*/
-local void braid(ltl, big, n, w)
- z_crc_t ltl[][256];
- z_word_t big[][256];
- int n;
- int w;
-{
+local void braid(z_crc_t ltl[][256], z_word_t big[][256], int n, int w) {
int k;
z_crc_t i, p, q;
for (k = 0; k < w; k++) {
@@ -525,69 +540,13 @@ local void braid(ltl, big, n, w)
}
#endif
-#else /* !DYNAMIC_CRC_TABLE */
-/* ========================================================================
- * Tables for byte-wise and braided CRC-32 calculations, and a table of powers
- * of x for combining CRC-32s, all made by make_crc_table().
- */
-#include "crc32.h"
#endif /* DYNAMIC_CRC_TABLE */
-/* ========================================================================
- * Routines used for CRC calculation. Some are also required for the table
- * generation above.
- */
-
-/*
- Return a(x) multiplied by b(x) modulo p(x), where p(x) is the CRC polynomial,
- reflected. For speed, this requires that a not be zero.
- */
-local z_crc_t multmodp(a, b)
- z_crc_t a;
- z_crc_t b;
-{
- z_crc_t m, p;
-
- m = (z_crc_t)1 << 31;
- p = 0;
- for (;;) {
- if (a & m) {
- p ^= b;
- if ((a & (m - 1)) == 0)
- break;
- }
- m >>= 1;
- b = b & 1 ? (b >> 1) ^ POLY : b >> 1;
- }
- return p;
-}
-
-/*
- Return x^(n * 2^k) modulo p(x). Requires that x2n_table[] has been
- initialized.
- */
-local z_crc_t x2nmodp(n, k)
- z_off64_t n;
- unsigned k;
-{
- z_crc_t p;
-
- p = (z_crc_t)1 << 31; /* x^0 == 1 */
- while (n) {
- if (n & 1)
- p = multmodp(x2n_table[k & 31], p);
- n >>= 1;
- k++;
- }
- return p;
-}
-
/* =========================================================================
* This function can be used by asm versions of crc32(), and to force the
* generation of the CRC tables in a threaded application.
*/
-const z_crc_t FAR * ZEXPORT get_crc_table()
-{
+const z_crc_t FAR * ZEXPORT get_crc_table(void) {
#ifdef DYNAMIC_CRC_TABLE
once(&made, make_crc_table);
#endif /* DYNAMIC_CRC_TABLE */
@@ -613,11 +572,8 @@ const z_crc_t FAR * ZEXPORT get_crc_table()
#define Z_BATCH_ZEROS 0xa10d3d0c /* computed from Z_BATCH = 3990 */
#define Z_BATCH_MIN 800 /* fewest words in a final batch */
-unsigned long ZEXPORT crc32_z(crc, buf, len)
- unsigned long crc;
- const unsigned char FAR *buf;
- z_size_t len;
-{
+unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf,
+ z_size_t len) {
z_crc_t val;
z_word_t crc1, crc2;
const z_word_t *word;
@@ -633,7 +589,7 @@ unsigned long ZEXPORT crc32_z(crc, buf, len)
#endif /* DYNAMIC_CRC_TABLE */
/* Pre-condition the CRC */
- crc ^= 0xffffffff;
+ crc = (~crc) & 0xffffffff;
/* Compute the CRC up to a word boundary. */
while (len && ((z_size_t)buf & 7) != 0) {
@@ -648,8 +604,8 @@ unsigned long ZEXPORT crc32_z(crc, buf, len)
len &= 7;
/* Do three interleaved CRCs to realize the throughput of one crc32x
- instruction per cycle. Each CRC is calcuated on Z_BATCH words. The three
- CRCs are combined into a single CRC after each set of batches. */
+ instruction per cycle. Each CRC is calculated on Z_BATCH words. The
+ three CRCs are combined into a single CRC after each set of batches. */
while (num >= 3 * Z_BATCH) {
crc1 = 0;
crc2 = 0;
@@ -712,26 +668,19 @@ unsigned long ZEXPORT crc32_z(crc, buf, len)
#ifdef W
-local z_crc_t crc_word(z_word_t data);
-local z_word_t crc_word_big(z_word_t data);
-
/*
Return the CRC of the W bytes in the word_t data, taking the
least-significant byte of the word as the first byte of data, without any pre
or post conditioning. This is used to combine the CRCs of each braid.
*/
-local z_crc_t crc_word(data)
- z_word_t data;
-{
+local z_crc_t crc_word(z_word_t data) {
int k;
for (k = 0; k < W; k++)
data = (data >> 8) ^ crc_table[data & 0xff];
return (z_crc_t)data;
}
-local z_word_t crc_word_big(data)
- z_word_t data;
-{
+local z_word_t crc_word_big(z_word_t data) {
int k;
for (k = 0; k < W; k++)
data = (data << 8) ^
@@ -742,11 +691,8 @@ local z_word_t crc_word_big(data)
#endif
/* ========================================================================= */
-unsigned long ZEXPORT crc32_z(crc, buf, len)
- unsigned long crc;
- const unsigned char FAR *buf;
- z_size_t len;
-{
+unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf,
+ z_size_t len) {
/* Return initial CRC, if requested. */
if (buf == Z_NULL) return 0;
@@ -755,7 +701,7 @@ unsigned long ZEXPORT crc32_z(crc, buf, len)
#endif /* DYNAMIC_CRC_TABLE */
/* Pre-condition the CRC */
- crc ^= 0xffffffff;
+ crc = (~crc) & 0xffffffff;
#ifdef W
@@ -778,8 +724,8 @@ unsigned long ZEXPORT crc32_z(crc, buf, len)
words = (z_word_t const *)buf;
/* Do endian check at execution time instead of compile time, since ARM
- processors can change the endianess at execution time. If the
- compiler knows what the endianess will be, it can optimize out the
+ processors can change the endianness at execution time. If the
+ compiler knows what the endianness will be, it can optimize out the
check and the unused branch. */
endian = 1;
if (*(unsigned char *)&endian) {
@@ -1066,39 +1012,26 @@ unsigned long ZEXPORT crc32_z(crc, buf, len)
#endif
/* ========================================================================= */
-unsigned long ZEXPORT crc32(crc, buf, len)
- unsigned long crc;
- const unsigned char FAR *buf;
- uInt len;
-{
+unsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf,
+ uInt len) {
return crc32_z(crc, buf, len);
}
/* ========================================================================= */
-uLong ZEXPORT crc32_combine64(crc1, crc2, len2)
- uLong crc1;
- uLong crc2;
- z_off64_t len2;
-{
+uLong ZEXPORT crc32_combine64(uLong crc1, uLong crc2, z_off64_t len2) {
#ifdef DYNAMIC_CRC_TABLE
once(&made, make_crc_table);
#endif /* DYNAMIC_CRC_TABLE */
- return multmodp(x2nmodp(len2, 3), crc1) ^ crc2;
+ return multmodp(x2nmodp(len2, 3), crc1) ^ (crc2 & 0xffffffff);
}
/* ========================================================================= */
-uLong ZEXPORT crc32_combine(crc1, crc2, len2)
- uLong crc1;
- uLong crc2;
- z_off_t len2;
-{
- return crc32_combine64(crc1, crc2, len2);
+uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2) {
+ return crc32_combine64(crc1, crc2, (z_off64_t)len2);
}
/* ========================================================================= */
-uLong ZEXPORT crc32_combine_gen64(len2)
- z_off64_t len2;
-{
+uLong ZEXPORT crc32_combine_gen64(z_off64_t len2) {
#ifdef DYNAMIC_CRC_TABLE
once(&made, make_crc_table);
#endif /* DYNAMIC_CRC_TABLE */
@@ -1106,17 +1039,11 @@ uLong ZEXPORT crc32_combine_gen64(len2)
}
/* ========================================================================= */
-uLong ZEXPORT crc32_combine_gen(len2)
- z_off_t len2;
-{
- return crc32_combine_gen64(len2);
+uLong ZEXPORT crc32_combine_gen(z_off_t len2) {
+ return crc32_combine_gen64((z_off64_t)len2);
}
/* ========================================================================= */
-uLong ZEXPORT crc32_combine_op(crc1, crc2, op)
- uLong crc1;
- uLong crc2;
- uLong op;
-{
- return multmodp(op, crc1) ^ crc2;
+uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op) {
+ return multmodp(op, crc1) ^ (crc2 & 0xffffffff);
}
diff --git a/deps/zlib/deflate.c b/deps/zlib/deflate.c
index feacd78327d..bd011751920 100644
--- a/deps/zlib/deflate.c
+++ b/deps/zlib/deflate.c
@@ -1,5 +1,5 @@
/* deflate.c -- compress data using the deflation algorithm
- * Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler
+ * Copyright (C) 1995-2023 Jean-loup Gailly and Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@@ -52,7 +52,7 @@
#include "deflate.h"
const char deflate_copyright[] =
- " deflate 1.2.12 Copyright 1995-2022 Jean-loup Gailly and Mark Adler ";
+ " deflate 1.3 Copyright 1995-2023 Jean-loup Gailly and Mark Adler ";
/*
If you use the zlib library in a product, an acknowledgment is welcome
in the documentation of your product. If for some reason you cannot
@@ -60,9 +60,6 @@ const char deflate_copyright[] =
copyright string in the executable of your product.
*/
-/* ===========================================================================
- * Function prototypes.
- */
typedef enum {
need_more, /* block not completed, need more input or more output */
block_done, /* block flush performed */
@@ -70,35 +67,16 @@ typedef enum {
finish_done /* finish done, accept no more input or output */
} block_state;
-typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+typedef block_state (*compress_func)(deflate_state *s, int flush);
/* Compression function. Returns the block state after the call. */
-local int deflateStateCheck OF((z_streamp strm));
-local void slide_hash OF((deflate_state *s));
-local void fill_window OF((deflate_state *s));
-local block_state deflate_stored OF((deflate_state *s, int flush));
-local block_state deflate_fast OF((deflate_state *s, int flush));
+local block_state deflate_stored(deflate_state *s, int flush);
+local block_state deflate_fast(deflate_state *s, int flush);
#ifndef FASTEST
-local block_state deflate_slow OF((deflate_state *s, int flush));
-#endif
-local block_state deflate_rle OF((deflate_state *s, int flush));
-local block_state deflate_huff OF((deflate_state *s, int flush));
-local void lm_init OF((deflate_state *s));
-local void putShortMSB OF((deflate_state *s, uInt b));
-local void flush_pending OF((z_streamp strm));
-local unsigned read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
-#ifdef ASMV
-# pragma message("Assembler code may have bugs -- use at your own risk")
- void match_init OF((void)); /* asm code initialization */
- uInt longest_match OF((deflate_state *s, IPos cur_match));
-#else
-local uInt longest_match OF((deflate_state *s, IPos cur_match));
-#endif
-
-#ifdef ZLIB_DEBUG
-local void check_match OF((deflate_state *s, IPos start, IPos match,
- int length));
+local block_state deflate_slow(deflate_state *s, int flush);
#endif
+local block_state deflate_rle(deflate_state *s, int flush);
+local block_state deflate_huff(deflate_state *s, int flush);
/* ===========================================================================
* Local data
@@ -160,7 +138,7 @@ local const config configuration_table[10] = {
* characters, so that a running hash key can be computed from the previous
* key instead of complete recalculation each time.
*/
-#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask)
+#define UPDATE_HASH(s,h,c) (h = (((h) << s->hash_shift) ^ (c)) & s->hash_mask)
/* ===========================================================================
@@ -191,9 +169,9 @@ local const config configuration_table[10] = {
*/
#define CLEAR_HASH(s) \
do { \
- s->head[s->hash_size-1] = NIL; \
+ s->head[s->hash_size - 1] = NIL; \
zmemzero((Bytef *)s->head, \
- (unsigned)(s->hash_size-1)*sizeof(*s->head)); \
+ (unsigned)(s->hash_size - 1)*sizeof(*s->head)); \
} while (0)
/* ===========================================================================
@@ -203,12 +181,10 @@ local const config configuration_table[10] = {
*/
#if defined(__has_feature)
# if __has_feature(memory_sanitizer)
-__attribute__((no_sanitize("memory")))
+ __attribute__((no_sanitize("memory")))
# endif
#endif
-local void slide_hash(s)
- deflate_state *s;
-{
+local void slide_hash(deflate_state *s) {
unsigned n, m;
Posf *p;
uInt wsize = s->w_size;
@@ -232,30 +208,177 @@ local void slide_hash(s)
#endif
}
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read. All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local unsigned read_buf(z_streamp strm, Bytef *buf, unsigned size) {
+ unsigned len = strm->avail_in;
+
+ if (len > size) len = size;
+ if (len == 0) return 0;
+
+ strm->avail_in -= len;
+
+ zmemcpy(buf, strm->next_in, len);
+ if (strm->state->wrap == 1) {
+ strm->adler = adler32(strm->adler, buf, len);
+ }
+#ifdef GZIP
+ else if (strm->state->wrap == 2) {
+ strm->adler = crc32(strm->adler, buf, len);
+ }
+#endif
+ strm->next_in += len;
+ strm->total_in += len;
+
+ return len;
+}
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or avail_in == 0; reads are
+ * performed for at least two bytes (required for the zip translate_eol
+ * option -- not supported here).
+ */
+local void fill_window(deflate_state *s) {
+ unsigned n;
+ unsigned more; /* Amount of free space at the end of the window. */
+ uInt wsize = s->w_size;
+
+ Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");
+
+ do {
+ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+ /* Deal with !@#$% 64K limit: */
+ if (sizeof(int) <= 2) {
+ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+ more = wsize;
+
+ } else if (more == (unsigned)(-1)) {
+ /* Very unlikely, but possible on 16 bit machine if
+ * strstart == 0 && lookahead == 1 (input done a byte at time)
+ */
+ more--;
+ }
+ }
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ if (s->strstart >= wsize + MAX_DIST(s)) {
+
+ zmemcpy(s->window, s->window + wsize, (unsigned)wsize - more);
+ s->match_start -= wsize;
+ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
+ s->block_start -= (long) wsize;
+ if (s->insert > s->strstart)
+ s->insert = s->strstart;
+ slide_hash(s);
+ more += wsize;
+ }
+ if (s->strm->avail_in == 0) break;
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the BIG_MEM or MMAP case (not yet supported),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ Assert(more >= 2, "more < 2");
+
+ n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
+ s->lookahead += n;
+
+ /* Initialize the hash value now that we have some input: */
+ if (s->lookahead + s->insert >= MIN_MATCH) {
+ uInt str = s->strstart - s->insert;
+ s->ins_h = s->window[str];
+ UPDATE_HASH(s, s->ins_h, s->window[str + 1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ while (s->insert) {
+ UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
+#ifndef FASTEST
+ s->prev[str & s->w_mask] = s->head[s->ins_h];
+#endif
+ s->head[s->ins_h] = (Pos)str;
+ str++;
+ s->insert--;
+ if (s->lookahead + s->insert < MIN_MATCH)
+ break;
+ }
+ }
+ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+ * but this is not important since only literal bytes will be emitted.
+ */
+
+ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+
+ /* If the WIN_INIT bytes after the end of the current data have never been
+ * written, then zero those bytes in order to avoid memory check reports of
+ * the use of uninitialized (or uninitialised as Julian writes) bytes by
+ * the longest match routines. Update the high water mark for the next
+ * time through here. WIN_INIT is set to MAX_MATCH since the longest match
+ * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
+ */
+ if (s->high_water < s->window_size) {
+ ulg curr = s->strstart + (ulg)(s->lookahead);
+ ulg init;
+
+ if (s->high_water < curr) {
+ /* Previous high water mark below current data -- zero WIN_INIT
+ * bytes or up to end of window, whichever is less.
+ */
+ init = s->window_size - curr;
+ if (init > WIN_INIT)
+ init = WIN_INIT;
+ zmemzero(s->window + curr, (unsigned)init);
+ s->high_water = curr + init;
+ }
+ else if (s->high_water < (ulg)curr + WIN_INIT) {
+ /* High water mark at or above current data, but below current data
+ * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
+ * to end of window, whichever is less.
+ */
+ init = (ulg)curr + WIN_INIT - s->high_water;
+ if (init > s->window_size - s->high_water)
+ init = s->window_size - s->high_water;
+ zmemzero(s->window + s->high_water, (unsigned)init);
+ s->high_water += init;
+ }
+ }
+
+ Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
+ "not enough room for search");
+}
+
/* ========================================================================= */
-int ZEXPORT deflateInit_(strm, level, version, stream_size)
- z_streamp strm;
- int level;
- const char *version;
- int stream_size;
-{
+int ZEXPORT deflateInit_(z_streamp strm, int level, const char *version,
+ int stream_size) {
return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
Z_DEFAULT_STRATEGY, version, stream_size);
/* To do: ignore strm->next_in if we use it as window */
}
/* ========================================================================= */
-int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
- version, stream_size)
- z_streamp strm;
- int level;
- int method;
- int windowBits;
- int memLevel;
- int strategy;
- const char *version;
- int stream_size;
-{
+int ZEXPORT deflateInit2_(z_streamp strm, int level, int method,
+ int windowBits, int memLevel, int strategy,
+ const char *version, int stream_size) {
deflate_state *s;
int wrap = 1;
static const char my_version[] = ZLIB_VERSION;
@@ -290,6 +413,8 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
if (windowBits < 0) { /* suppress zlib wrapper */
wrap = 0;
+ if (windowBits < -15)
+ return Z_STREAM_ERROR;
windowBits = -windowBits;
}
#ifdef GZIP
@@ -319,7 +444,7 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
s->hash_bits = (uInt)memLevel + 7;
s->hash_size = 1 << s->hash_bits;
s->hash_mask = s->hash_size - 1;
- s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+ s->hash_shift = ((s->hash_bits + MIN_MATCH-1) / MIN_MATCH);
s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
@@ -345,11 +470,11 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
* sym_buf value to read moves forward three bytes. From that symbol, up to
* 31 bits are written to pending_buf. The closest the written pending_buf
* bits gets to the next sym_buf symbol to read is just before the last
- * code is written. At that time, 31*(n-2) bits have been written, just
- * after 24*(n-2) bits have been consumed from sym_buf. sym_buf starts at
- * 8*n bits into pending_buf. (Note that the symbol buffer fills when n-1
+ * code is written. At that time, 31*(n - 2) bits have been written, just
+ * after 24*(n - 2) bits have been consumed from sym_buf. sym_buf starts at
+ * 8*n bits into pending_buf. (Note that the symbol buffer fills when n - 1
* symbols are written.) The closest the writing gets to what is unread is
- * then n+14 bits. Here n is lit_bufsize, which is 16384 by default, and
+ * then n + 14 bits. Here n is lit_bufsize, which is 16384 by default, and
* can range from 128 to 32768.
*
* Therefore, at a minimum, there are 142 bits of space between what is
@@ -395,9 +520,7 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
/* =========================================================================
* Check for a valid deflate stream state. Return 0 if ok, 1 if not.
*/
-local int deflateStateCheck (strm)
- z_streamp strm;
-{
+local int deflateStateCheck(z_streamp strm) {
deflate_state *s;
if (strm == Z_NULL ||
strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0)
@@ -418,11 +541,8 @@ local int deflateStateCheck (strm)
}
/* ========================================================================= */
-int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
- z_streamp strm;
- const Bytef *dictionary;
- uInt dictLength;
-{
+int ZEXPORT deflateSetDictionary(z_streamp strm, const Bytef *dictionary,
+ uInt dictLength) {
deflate_state *s;
uInt str, n;
int wrap;
@@ -487,11 +607,8 @@ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
}
/* ========================================================================= */
-int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength)
- z_streamp strm;
- Bytef *dictionary;
- uInt *dictLength;
-{
+int ZEXPORT deflateGetDictionary(z_streamp strm, Bytef *dictionary,
+ uInt *dictLength) {
deflate_state *s;
uInt len;
@@ -509,9 +626,7 @@ int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength)
}
/* ========================================================================= */
-int ZEXPORT deflateResetKeep (strm)
- z_streamp strm;
-{
+int ZEXPORT deflateResetKeep(z_streamp strm) {
deflate_state *s;
if (deflateStateCheck(strm)) {
@@ -546,10 +661,32 @@ int ZEXPORT deflateResetKeep (strm)
return Z_OK;
}
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init(deflate_state *s) {
+ s->window_size = (ulg)2L*s->w_size;
+
+ CLEAR_HASH(s);
+
+ /* Set the default configuration parameters:
+ */
+ s->max_lazy_match = configuration_table[s->level].max_lazy;
+ s->good_match = configuration_table[s->level].good_length;
+ s->nice_match = configuration_table[s->level].nice_length;
+ s->max_chain_length = configuration_table[s->level].max_chain;
+
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->lookahead = 0;
+ s->insert = 0;
+ s->match_length = s->prev_length = MIN_MATCH-1;
+ s->match_available = 0;
+ s->ins_h = 0;
+}
+
/* ========================================================================= */
-int ZEXPORT deflateReset (strm)
- z_streamp strm;
-{
+int ZEXPORT deflateReset(z_streamp strm) {
int ret;
ret = deflateResetKeep(strm);
@@ -559,10 +696,7 @@ int ZEXPORT deflateReset (strm)
}
/* ========================================================================= */
-int ZEXPORT deflateSetHeader (strm, head)
- z_streamp strm;
- gz_headerp head;
-{
+int ZEXPORT deflateSetHeader(z_streamp strm, gz_headerp head) {
if (deflateStateCheck(strm) || strm->state->wrap != 2)
return Z_STREAM_ERROR;
strm->state->gzhead = head;
@@ -570,11 +704,7 @@ int ZEXPORT deflateSetHeader (strm, head)
}
/* ========================================================================= */
-int ZEXPORT deflatePending (strm, pending, bits)
- unsigned *pending;
- int *bits;
- z_streamp strm;
-{
+int ZEXPORT deflatePending(z_streamp strm, unsigned *pending, int *bits) {
if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
if (pending != Z_NULL)
*pending = strm->state->pending;
@@ -584,11 +714,7 @@ int ZEXPORT deflatePending (strm, pending, bits)
}
/* ========================================================================= */
-int ZEXPORT deflatePrime (strm, bits, value)
- z_streamp strm;
- int bits;
- int value;
-{
+int ZEXPORT deflatePrime(z_streamp strm, int bits, int value) {
deflate_state *s;
int put;
@@ -611,11 +737,7 @@ int ZEXPORT deflatePrime (strm, bits, value)
}
/* ========================================================================= */
-int ZEXPORT deflateParams(strm, level, strategy)
- z_streamp strm;
- int level;
- int strategy;
-{
+int ZEXPORT deflateParams(z_streamp strm, int level, int strategy) {
deflate_state *s;
compress_func func;
@@ -660,13 +782,8 @@ int ZEXPORT deflateParams(strm, level, strategy)
}
/* ========================================================================= */
-int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
- z_streamp strm;
- int good_length;
- int max_lazy;
- int nice_length;
- int max_chain;
-{
+int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy,
+ int nice_length, int max_chain) {
deflate_state *s;
if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
@@ -679,36 +796,47 @@ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
}
/* =========================================================================
- * For the default windowBits of 15 and memLevel of 8, this function returns
- * a close to exact, as well as small, upper bound on the compressed size.
- * They are coded as constants here for a reason--if the #define's are
- * changed, then this function needs to be changed as well. The return
- * value for 15 and 8 only works for those exact settings.
+ * For the default windowBits of 15 and memLevel of 8, this function returns a
+ * close to exact, as well as small, upper bound on the compressed size. This
+ * is an expansion of ~0.03%, plus a small constant.
*
- * For any setting other than those defaults for windowBits and memLevel,
- * the value returned is a conservative worst case for the maximum expansion
- * resulting from using fixed blocks instead of stored blocks, which deflate
- * can emit on compressed data for some combinations of the parameters.
+ * For any setting other than those defaults for windowBits and memLevel, one
+ * of two worst case bounds is returned. This is at most an expansion of ~4% or
+ * ~13%, plus a small constant.
*
- * This function could be more sophisticated to provide closer upper bounds for
- * every combination of windowBits and memLevel. But even the conservative
- * upper bound of about 14% expansion does not seem onerous for output buffer
- * allocation.
+ * Both the 0.03% and 4% derive from the overhead of stored blocks. The first
+ * one is for stored blocks of 16383 bytes (memLevel == 8), whereas the second
+ * is for stored blocks of 127 bytes (the worst case memLevel == 1). The
+ * expansion results from five bytes of header for each stored block.
+ *
+ * The larger expansion of 13% results from a window size less than or equal to
+ * the symbols buffer size (windowBits <= memLevel + 7). In that case some of
+ * the data being compressed may have slid out of the sliding window, impeding
+ * a stored block from being emitted. Then the only choice is a fixed or
+ * dynamic block, where a fixed block limits the maximum expansion to 9 bits
+ * per 8-bit byte, plus 10 bits for every block. The smallest block size for
+ * which this can occur is 255 (memLevel == 2).
+ *
+ * Shifts are used to approximate divisions, for speed.
*/
-uLong ZEXPORT deflateBound(strm, sourceLen)
- z_streamp strm;
- uLong sourceLen;
-{
+uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen) {
deflate_state *s;
- uLong complen, wraplen;
+ uLong fixedlen, storelen, wraplen;
+
+ /* upper bound for fixed blocks with 9-bit literals and length 255
+ (memLevel == 2, which is the lowest that may not use stored blocks) --
+ ~13% overhead plus a small constant */
+ fixedlen = sourceLen + (sourceLen >> 3) + (sourceLen >> 8) +
+ (sourceLen >> 9) + 4;
- /* conservative upper bound for compressed data */
- complen = sourceLen +
- ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;
+ /* upper bound for stored blocks with length 127 (memLevel == 1) --
+ ~4% overhead plus a small constant */
+ storelen = sourceLen + (sourceLen >> 5) + (sourceLen >> 7) +
+ (sourceLen >> 11) + 7;
- /* if can't get parameters, return conservative bound plus zlib wrapper */
+ /* if can't get parameters, return larger bound plus a zlib wrapper */
if (deflateStateCheck(strm))
- return complen + 6;
+ return (fixedlen > storelen ? fixedlen : storelen) + 6;
/* compute wrapper length */
s = strm->state;
@@ -745,11 +873,13 @@ uLong ZEXPORT deflateBound(strm, sourceLen)
wraplen = 6;
}
- /* if not default parameters, return conservative bound */
+ /* if not default parameters, return one of the conservative bounds */
if (s->w_bits != 15 || s->hash_bits != 8 + 7)
- return complen + wraplen;
+ return (s->w_bits <= s->hash_bits && s->level ? fixedlen : storelen) +
+ wraplen;
- /* default settings: return tight bound for that case */
+ /* default settings: return tight bound for that case -- ~0.03% overhead
+ plus a small constant */
return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
(sourceLen >> 25) + 13 - 6 + wraplen;
}
@@ -759,10 +889,7 @@ uLong ZEXPORT deflateBound(strm, sourceLen)
* IN assertion: the stream state is correct and there is enough room in
* pending_buf.
*/
-local void putShortMSB (s, b)
- deflate_state *s;
- uInt b;
-{
+local void putShortMSB(deflate_state *s, uInt b) {
put_byte(s, (Byte)(b >> 8));
put_byte(s, (Byte)(b & 0xff));
}
@@ -773,9 +900,7 @@ local void putShortMSB (s, b)
* applications may wish to modify it to avoid allocating a large
* strm->next_out buffer and copying into it. (See also read_buf()).
*/
-local void flush_pending(strm)
- z_streamp strm;
-{
+local void flush_pending(z_streamp strm) {
unsigned len;
deflate_state *s = strm->state;
@@ -806,10 +931,7 @@ local void flush_pending(strm)
} while (0)
/* ========================================================================= */
-int ZEXPORT deflate (strm, flush)
- z_streamp strm;
- int flush;
-{
+int ZEXPORT deflate(z_streamp strm, int flush) {
int old_flush; /* value of flush param for previous deflate call */
deflate_state *s;
@@ -861,7 +983,7 @@ int ZEXPORT deflate (strm, flush)
s->status = BUSY_STATE;
if (s->status == INIT_STATE) {
/* zlib header */
- uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+ uInt header = (Z_DEFLATED + ((s->w_bits - 8) << 4)) << 8;
uInt level_flags;
if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
@@ -1121,9 +1243,7 @@ int ZEXPORT deflate (strm, flush)
}
/* ========================================================================= */
-int ZEXPORT deflateEnd (strm)
- z_streamp strm;
-{
+int ZEXPORT deflateEnd(z_streamp strm) {
int status;
if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
@@ -1147,11 +1267,10 @@ int ZEXPORT deflateEnd (strm)
* To simplify the source, this is not supported for 16-bit MSDOS (which
* doesn't have enough memory anyway to duplicate compression states).
*/
-int ZEXPORT deflateCopy (dest, source)
- z_streamp dest;
- z_streamp source;
-{
+int ZEXPORT deflateCopy(z_streamp dest, z_streamp source) {
#ifdef MAXSEG_64K
+ (void)dest;
+ (void)source;
return Z_STREAM_ERROR;
#else
deflate_state *ds;
@@ -1199,71 +1318,6 @@ int ZEXPORT deflateCopy (dest, source)
#endif /* MAXSEG_64K */
}
-/* ===========================================================================
- * Read a new buffer from the current input stream, update the adler32
- * and total number of bytes read. All deflate() input goes through
- * this function so some applications may wish to modify it to avoid
- * allocating a large strm->next_in buffer and copying from it.
- * (See also flush_pending()).
- */
-local unsigned read_buf(strm, buf, size)
- z_streamp strm;
- Bytef *buf;
- unsigned size;
-{
- unsigned len = strm->avail_in;
-
- if (len > size) len = size;
- if (len == 0) return 0;
-
- strm->avail_in -= len;
-
- zmemcpy(buf, strm->next_in, len);
- if (strm->state->wrap == 1) {
- strm->adler = adler32(strm->adler, buf, len);
- }
-#ifdef GZIP
- else if (strm->state->wrap == 2) {
- strm->adler = crc32(strm->adler, buf, len);
- }
-#endif
- strm->next_in += len;
- strm->total_in += len;
-
- return len;
-}
-
-/* ===========================================================================
- * Initialize the "longest match" routines for a new zlib stream
- */
-local void lm_init (s)
- deflate_state *s;
-{
- s->window_size = (ulg)2L*s->w_size;
-
- CLEAR_HASH(s);
-
- /* Set the default configuration parameters:
- */
- s->max_lazy_match = configuration_table[s->level].max_lazy;
- s->good_match = configuration_table[s->level].good_length;
- s->nice_match = configuration_table[s->level].nice_length;
- s->max_chain_length = configuration_table[s->level].max_chain;
-
- s->strstart = 0;
- s->block_start = 0L;
- s->lookahead = 0;
- s->insert = 0;
- s->match_length = s->prev_length = MIN_MATCH-1;
- s->match_available = 0;
- s->ins_h = 0;
-#ifndef FASTEST
-#ifdef ASMV
- match_init(); /* initialize the asm code */
-#endif
-#endif
-}
-
#ifndef FASTEST
/* ===========================================================================
* Set match_start to the longest match starting at the given string and
@@ -1274,14 +1328,7 @@ local void lm_init (s)
* string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
* OUT assertion: the match length is not greater than s->lookahead.
*/
-#ifndef ASMV
-/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
- * match.S. The code will be functionally equivalent.
- */
-local uInt longest_match(s, cur_match)
- deflate_state *s;
- IPos cur_match; /* current match */
-{
+local uInt longest_match(deflate_state *s, IPos cur_match) {
unsigned chain_length = s->max_chain_length;/* max hash chain length */
register Bytef *scan = s->window + s->strstart; /* current string */
register Bytef *match; /* matched string */
@@ -1302,10 +1349,10 @@ local uInt longest_match(s, cur_match)
*/
register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
register ush scan_start = *(ushf*)scan;
- register ush scan_end = *(ushf*)(scan+best_len-1);
+ register ush scan_end = *(ushf*)(scan + best_len - 1);
#else
register Bytef *strend = s->window + s->strstart + MAX_MATCH;
- register Byte scan_end1 = scan[best_len-1];
+ register Byte scan_end1 = scan[best_len - 1];
register Byte scan_end = scan[best_len];
#endif
@@ -1323,7 +1370,8 @@ local uInt longest_match(s, cur_match)
*/
if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead;
- Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+ Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
+ "need lookahead");
do {
Assert(cur_match < s->strstart, "no future");
@@ -1341,43 +1389,44 @@ local uInt longest_match(s, cur_match)
/* This code assumes sizeof(unsigned short) == 2. Do not use
* UNALIGNED_OK if your compiler uses a different size.
*/
- if (*(ushf*)(match+best_len-1) != scan_end ||
+ if (*(ushf*)(match + best_len - 1) != scan_end ||
*(ushf*)match != scan_start) continue;
/* It is not necessary to compare scan[2] and match[2] since they are
* always equal when the other bytes match, given that the hash keys
* are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
- * strstart+3, +5, ... up to strstart+257. We check for insufficient
+ * strstart + 3, + 5, up to strstart + 257. We check for insufficient
* lookahead only every 4th comparison; the 128th check will be made
- * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+ * at strstart + 257. If MAX_MATCH-2 is not a multiple of 8, it is
* necessary to put more guard bytes at the end of the window, or
* to check more often for insufficient lookahead.
*/
Assert(scan[2] == match[2], "scan[2]?");
scan++, match++;
do {
- } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
- *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
- *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
- *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ } while (*(ushf*)(scan += 2) == *(ushf*)(match += 2) &&
+ *(ushf*)(scan += 2) == *(ushf*)(match += 2) &&
+ *(ushf*)(scan += 2) == *(ushf*)(match += 2) &&
+ *(ushf*)(scan += 2) == *(ushf*)(match += 2) &&
scan < strend);
/* The funny "do {}" generates better code on most compilers */
- /* Here, scan <= window+strstart+257 */
- Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ /* Here, scan <= window + strstart + 257 */
+ Assert(scan <= s->window + (unsigned)(s->window_size - 1),
+ "wild scan");
if (*scan == *match) scan++;
- len = (MAX_MATCH - 1) - (int)(strend-scan);
+ len = (MAX_MATCH - 1) - (int)(strend - scan);
scan = strend - (MAX_MATCH-1);
#else /* UNALIGNED_OK */
- if (match[best_len] != scan_end ||
- match[best_len-1] != scan_end1 ||
- *match != *scan ||
- *++match != scan[1]) continue;
+ if (match[best_len] != scan_end ||
+ match[best_len - 1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1]) continue;
- /* The check at best_len-1 can be removed because it will be made
+ /* The check at best_len - 1 can be removed because it will be made
* again later. (This heuristic is not always a win.)
* It is not necessary to compare scan[2] and match[2] since they
* are always equal when the other bytes match, given that
@@ -1387,7 +1436,7 @@ local uInt longest_match(s, cur_match)
Assert(*scan == *match, "match[2]?");
/* We check for insufficient lookahead only every 8th comparison;
- * the 256th check will be made at strstart+258.
+ * the 256th check will be made at strstart + 258.
*/
do {
} while (*++scan == *++match && *++scan == *++match &&
@@ -1396,7 +1445,8 @@ local uInt longest_match(s, cur_match)
*++scan == *++match && *++scan == *++match &&
scan < strend);
- Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ Assert(scan <= s->window + (unsigned)(s->window_size - 1),
+ "wild scan");
len = MAX_MATCH - (int)(strend - scan);
scan = strend - MAX_MATCH;
@@ -1408,9 +1458,9 @@ local uInt longest_match(s, cur_match)
best_len = len;
if (len >= nice_match) break;
#ifdef UNALIGNED_OK
- scan_end = *(ushf*)(scan+best_len-1);
+ scan_end = *(ushf*)(scan + best_len - 1);
#else
- scan_end1 = scan[best_len-1];
+ scan_end1 = scan[best_len - 1];
scan_end = scan[best_len];
#endif
}
@@ -1420,17 +1470,13 @@ local uInt longest_match(s, cur_match)
if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
return s->lookahead;
}
-#endif /* ASMV */
#else /* FASTEST */
/* ---------------------------------------------------------------------------
* Optimized version for FASTEST only
*/
-local uInt longest_match(s, cur_match)
- deflate_state *s;
- IPos cur_match; /* current match */
-{
+local uInt longest_match(deflate_state *s, IPos cur_match) {
register Bytef *scan = s->window + s->strstart; /* current string */
register Bytef *match; /* matched string */
register int len; /* length of current match */
@@ -1441,7 +1487,8 @@ local uInt longest_match(s, cur_match)
*/
Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
- Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+ Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
+ "need lookahead");
Assert(cur_match < s->strstart, "no future");
@@ -1451,7 +1498,7 @@ local uInt longest_match(s, cur_match)
*/
if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
- /* The check at best_len-1 can be removed because it will be made
+ /* The check at best_len - 1 can be removed because it will be made
* again later. (This heuristic is not always a win.)
* It is not necessary to compare scan[2] and match[2] since they
* are always equal when the other bytes match, given that
@@ -1461,7 +1508,7 @@ local uInt longest_match(s, cur_match)
Assert(*scan == *match, "match[2]?");
/* We check for insufficient lookahead only every 8th comparison;
- * the 256th check will be made at strstart+258.
+ * the 256th check will be made at strstart + 258.
*/
do {
} while (*++scan == *++match && *++scan == *++match &&
@@ -1470,7 +1517,7 @@ local uInt longest_match(s, cur_match)
*++scan == *++match && *++scan == *++match &&
scan < strend);
- Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ Assert(scan <= s->window + (unsigned)(s->window_size - 1), "wild scan");
len = MAX_MATCH - (int)(strend - scan);
@@ -1490,11 +1537,7 @@ local uInt longest_match(s, cur_match)
/* ===========================================================================
* Check that the match at match_start is indeed a match.
*/
-local void check_match(s, start, match, length)
- deflate_state *s;
- IPos start, match;
- int length;
-{
+local void check_match(deflate_state *s, IPos start, IPos match, int length) {
/* check that the match is indeed a match */
if (zmemcmp(s->window + match,
s->window + start, length) != EQUAL) {
@@ -1506,7 +1549,7 @@ local void check_match(s, start, match, length)
z_error("invalid match");
}
if (z_verbose > 1) {
- fprintf(stderr,"\\[%d,%d]", start-match, length);
+ fprintf(stderr,"\\[%d,%d]", start - match, length);
do { putc(s->window[start++], stderr); } while (--length != 0);
}
}
@@ -1514,137 +1557,6 @@ local void check_match(s, start, match, length)
# define check_match(s, start, match, length)
#endif /* ZLIB_DEBUG */
-/* ===========================================================================
- * Fill the window when the lookahead becomes insufficient.
- * Updates strstart and lookahead.
- *
- * IN assertion: lookahead < MIN_LOOKAHEAD
- * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
- * At least one byte has been read, or avail_in == 0; reads are
- * performed for at least two bytes (required for the zip translate_eol
- * option -- not supported here).
- */
-local void fill_window(s)
- deflate_state *s;
-{
- unsigned n;
- unsigned more; /* Amount of free space at the end of the window. */
- uInt wsize = s->w_size;
-
- Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");
-
- do {
- more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
-
- /* Deal with !@#$% 64K limit: */
- if (sizeof(int) <= 2) {
- if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
- more = wsize;
-
- } else if (more == (unsigned)(-1)) {
- /* Very unlikely, but possible on 16 bit machine if
- * strstart == 0 && lookahead == 1 (input done a byte at time)
- */
- more--;
- }
- }
-
- /* If the window is almost full and there is insufficient lookahead,
- * move the upper half to the lower one to make room in the upper half.
- */
- if (s->strstart >= wsize+MAX_DIST(s)) {
-
- zmemcpy(s->window, s->window+wsize, (unsigned)wsize - more);
- s->match_start -= wsize;
- s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
- s->block_start -= (long) wsize;
- if (s->insert > s->strstart)
- s->insert = s->strstart;
- slide_hash(s);
- more += wsize;
- }
- if (s->strm->avail_in == 0) break;
-
- /* If there was no sliding:
- * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
- * more == window_size - lookahead - strstart
- * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
- * => more >= window_size - 2*WSIZE + 2
- * In the BIG_MEM or MMAP case (not yet supported),
- * window_size == input_size + MIN_LOOKAHEAD &&
- * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
- * Otherwise, window_size == 2*WSIZE so more >= 2.
- * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
- */
- Assert(more >= 2, "more < 2");
-
- n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
- s->lookahead += n;
-
- /* Initialize the hash value now that we have some input: */
- if (s->lookahead + s->insert >= MIN_MATCH) {
- uInt str = s->strstart - s->insert;
- s->ins_h = s->window[str];
- UPDATE_HASH(s, s->ins_h, s->window[str + 1]);
-#if MIN_MATCH != 3
- Call UPDATE_HASH() MIN_MATCH-3 more times
-#endif
- while (s->insert) {
- UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
-#ifndef FASTEST
- s->prev[str & s->w_mask] = s->head[s->ins_h];
-#endif
- s->head[s->ins_h] = (Pos)str;
- str++;
- s->insert--;
- if (s->lookahead + s->insert < MIN_MATCH)
- break;
- }
- }
- /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
- * but this is not important since only literal bytes will be emitted.
- */
-
- } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
-
- /* If the WIN_INIT bytes after the end of the current data have never been
- * written, then zero those bytes in order to avoid memory check reports of
- * the use of uninitialized (or uninitialised as Julian writes) bytes by
- * the longest match routines. Update the high water mark for the next
- * time through here. WIN_INIT is set to MAX_MATCH since the longest match
- * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
- */
- if (s->high_water < s->window_size) {
- ulg curr = s->strstart + (ulg)(s->lookahead);
- ulg init;
-
- if (s->high_water < curr) {
- /* Previous high water mark below current data -- zero WIN_INIT
- * bytes or up to end of window, whichever is less.
- */
- init = s->window_size - curr;
- if (init > WIN_INIT)
- init = WIN_INIT;
- zmemzero(s->window + curr, (unsigned)init);
- s->high_water = curr + init;
- }
- else if (s->high_water < (ulg)curr + WIN_INIT) {
- /* High water mark at or above current data, but below current data
- * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
- * to end of window, whichever is less.
- */
- init = (ulg)curr + WIN_INIT - s->high_water;
- if (init > s->window_size - s->high_water)
- init = s->window_size - s->high_water;
- zmemzero(s->window + s->high_water, (unsigned)init);
- s->high_water += init;
- }
- }
-
- Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
- "not enough room for search");
-}
-
/* ===========================================================================
* Flush the current block, with given end-of-file flag.
* IN assertion: strstart is set to the end of the current match.
@@ -1685,12 +1597,9 @@ local void fill_window(s)
*
* deflate_stored() is written to minimize the number of times an input byte is
* copied. It is most efficient with large input and output buffers, which
- * maximizes the opportunites to have a single copy from next_in to next_out.
+ * maximizes the opportunities to have a single copy from next_in to next_out.
*/
-local block_state deflate_stored(s, flush)
- deflate_state *s;
- int flush;
-{
+local block_state deflate_stored(deflate_state *s, int flush) {
/* Smallest worthy block size when not flushing or finishing. By default
* this is 32K. This can be as small as 507 bytes for memLevel == 1. For
* large input and output buffers, the stored block size will be larger.
@@ -1874,10 +1783,7 @@ local block_state deflate_stored(s, flush)
* new strings in the dictionary only for unmatched strings or for short
* matches. It is used only for the fast compression options.
*/
-local block_state deflate_fast(s, flush)
- deflate_state *s;
- int flush;
-{
+local block_state deflate_fast(deflate_state *s, int flush) {
IPos hash_head; /* head of the hash chain */
int bflush; /* set if current block must be flushed */
@@ -1895,7 +1801,7 @@ local block_state deflate_fast(s, flush)
if (s->lookahead == 0) break; /* flush the current block */
}
- /* Insert the string window[strstart .. strstart+2] in the
+ /* Insert the string window[strstart .. strstart + 2] in the
* dictionary, and set hash_head to the head of the hash chain:
*/
hash_head = NIL;
@@ -1943,7 +1849,7 @@ local block_state deflate_fast(s, flush)
s->strstart += s->match_length;
s->match_length = 0;
s->ins_h = s->window[s->strstart];
- UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart + 1]);
#if MIN_MATCH != 3
Call UPDATE_HASH() MIN_MATCH-3 more times
#endif
@@ -1954,7 +1860,7 @@ local block_state deflate_fast(s, flush)
} else {
/* No match, output a literal byte */
Tracevv((stderr,"%c", s->window[s->strstart]));
- _tr_tally_lit (s, s->window[s->strstart], bflush);
+ _tr_tally_lit(s, s->window[s->strstart], bflush);
s->lookahead--;
s->strstart++;
}
@@ -1976,10 +1882,7 @@ local block_state deflate_fast(s, flush)
* evaluation for matches: a match is finally adopted only if there is
* no better match at the next window position.
*/
-local block_state deflate_slow(s, flush)
- deflate_state *s;
- int flush;
-{
+local block_state deflate_slow(deflate_state *s, int flush) {
IPos hash_head; /* head of hash chain */
int bflush; /* set if current block must be flushed */
@@ -1998,7 +1901,7 @@ local block_state deflate_slow(s, flush)
if (s->lookahead == 0) break; /* flush the current block */
}
- /* Insert the string window[strstart .. strstart+2] in the
+ /* Insert the string window[strstart .. strstart + 2] in the
* dictionary, and set hash_head to the head of the hash chain:
*/
hash_head = NIL;
@@ -2040,17 +1943,17 @@ local block_state deflate_slow(s, flush)
uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
/* Do not insert strings in hash table beyond this. */
- check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+ check_match(s, s->strstart - 1, s->prev_match, s->prev_length);
- _tr_tally_dist(s, s->strstart -1 - s->prev_match,
+ _tr_tally_dist(s, s->strstart - 1 - s->prev_match,
s->prev_length - MIN_MATCH, bflush);
/* Insert in hash table all strings up to the end of the match.
- * strstart-1 and strstart are already inserted. If there is not
+ * strstart - 1 and strstart are already inserted. If there is not
* enough lookahead, the last two strings are not inserted in
* the hash table.
*/
- s->lookahead -= s->prev_length-1;
+ s->lookahead -= s->prev_length - 1;
s->prev_length -= 2;
do {
if (++s->strstart <= max_insert) {
@@ -2068,8 +1971,8 @@ local block_state deflate_slow(s, flush)
* single literal. If there was a match but the current match
* is longer, truncate the previous match to a single literal.
*/
- Tracevv((stderr,"%c", s->window[s->strstart-1]));
- _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ Tracevv((stderr,"%c", s->window[s->strstart - 1]));
+ _tr_tally_lit(s, s->window[s->strstart - 1], bflush);
if (bflush) {
FLUSH_BLOCK_ONLY(s, 0);
}
@@ -2087,8 +1990,8 @@ local block_state deflate_slow(s, flush)
}
Assert (flush != Z_NO_FLUSH, "no flush?");
if (s->match_available) {
- Tracevv((stderr,"%c", s->window[s->strstart-1]));
- _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ Tracevv((stderr,"%c", s->window[s->strstart - 1]));
+ _tr_tally_lit(s, s->window[s->strstart - 1], bflush);
s->match_available = 0;
}
s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
@@ -2107,10 +2010,7 @@ local block_state deflate_slow(s, flush)
* one. Do not maintain a hash table. (It will be regenerated if this run of
* deflate switches away from Z_RLE.)
*/
-local block_state deflate_rle(s, flush)
- deflate_state *s;
- int flush;
-{
+local block_state deflate_rle(deflate_state *s, int flush) {
int bflush; /* set if current block must be flushed */
uInt prev; /* byte at distance one to match */
Bytef *scan, *strend; /* scan goes up to strend for length of run */
@@ -2145,7 +2045,8 @@ local block_state deflate_rle(s, flush)
if (s->match_length > s->lookahead)
s->match_length = s->lookahead;
}
- Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
+ Assert(scan <= s->window + (uInt)(s->window_size - 1),
+ "wild scan");
}
/* Emit match if have run of MIN_MATCH or longer, else emit literal */
@@ -2160,7 +2061,7 @@ local block_state deflate_rle(s, flush)
} else {
/* No match, output a literal byte */
Tracevv((stderr,"%c", s->window[s->strstart]));
- _tr_tally_lit (s, s->window[s->strstart], bflush);
+ _tr_tally_lit(s, s->window[s->strstart], bflush);
s->lookahead--;
s->strstart++;
}
@@ -2180,10 +2081,7 @@ local block_state deflate_rle(s, flush)
* For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table.
* (It will be regenerated if this run of deflate switches away from Huffman.)
*/
-local block_state deflate_huff(s, flush)
- deflate_state *s;
- int flush;
-{
+local block_state deflate_huff(deflate_state *s, int flush) {
int bflush; /* set if current block must be flushed */
for (;;) {
@@ -2200,7 +2098,7 @@ local block_state deflate_huff(s, flush)
/* Output a literal byte */
s->match_length = 0;
Tracevv((stderr,"%c", s->window[s->strstart]));
- _tr_tally_lit (s, s->window[s->strstart], bflush);
+ _tr_tally_lit(s, s->window[s->strstart], bflush);
s->lookahead--;
s->strstart++;
if (bflush) FLUSH_BLOCK(s, 0);
diff --git a/deps/zlib/deflate.h b/deps/zlib/deflate.h
index 1a06cd5f25d..8696791429f 100644
--- a/deps/zlib/deflate.h
+++ b/deps/zlib/deflate.h
@@ -291,14 +291,14 @@ typedef struct internal_state {
memory checker errors from longest match routines */
/* in trees.c */
-void ZLIB_INTERNAL _tr_init OF((deflate_state *s));
-int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
-void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf,
- ulg stored_len, int last));
-void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s));
-void ZLIB_INTERNAL _tr_align OF((deflate_state *s));
-void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,
- ulg stored_len, int last));
+void ZLIB_INTERNAL _tr_init(deflate_state *s);
+int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc);
+void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf,
+ ulg stored_len, int last);
+void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s);
+void ZLIB_INTERNAL _tr_align(deflate_state *s);
+void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf,
+ ulg stored_len, int last);
#define d_code(dist) \
((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
diff --git a/deps/zlib/gzguts.h b/deps/zlib/gzguts.h
index 57faf37165a..f9375047e8c 100644
--- a/deps/zlib/gzguts.h
+++ b/deps/zlib/gzguts.h
@@ -7,9 +7,8 @@
# ifndef _LARGEFILE_SOURCE
# define _LARGEFILE_SOURCE 1
# endif
-# ifdef _FILE_OFFSET_BITS
-# undef _FILE_OFFSET_BITS
-# endif
+# undef _FILE_OFFSET_BITS
+# undef _TIME_BITS
#endif
#ifdef HAVE_HIDDEN
@@ -119,8 +118,8 @@
/* gz* functions always use library allocation functions */
#ifndef STDC
- extern voidp malloc OF((uInt size));
- extern void free OF((voidpf ptr));
+ extern voidp malloc(uInt size);
+ extern void free(voidpf ptr);
#endif
/* get errno and strerror definition */
@@ -138,10 +137,10 @@
/* provide prototypes for these when building zlib without LFS */
#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0
- ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
- ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
- ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
- ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
+ ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *);
+ ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int);
+ ZEXTERN z_off64_t ZEXPORT gztell64(gzFile);
+ ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile);
#endif
/* default memLevel */
@@ -203,9 +202,9 @@ typedef struct {
typedef gz_state FAR *gz_statep;
/* shared functions */
-void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *));
+void ZLIB_INTERNAL gz_error(gz_statep, int, const char *);
#if defined UNDER_CE
-char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error));
+char ZLIB_INTERNAL *gz_strwinerror(DWORD error);
#endif
/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t
@@ -214,6 +213,6 @@ char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error));
#ifdef INT_MAX
# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX)
#else
-unsigned ZLIB_INTERNAL gz_intmax OF((void));
+unsigned ZLIB_INTERNAL gz_intmax(void);
# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax())
#endif
diff --git a/deps/zlib/infback.c b/deps/zlib/infback.c
index a390c58e816..e7b25b307a3 100644
--- a/deps/zlib/infback.c
+++ b/deps/zlib/infback.c
@@ -15,9 +15,6 @@
#include "inflate.h"
#include "inffast.h"
-/* function prototypes */
-local void fixedtables OF((struct inflate_state FAR *state));
-
/*
strm provides memory allocation functions in zalloc and zfree, or
Z_NULL to use the library memory allocation functions.
@@ -25,13 +22,9 @@ local void fixedtables OF((struct inflate_state FAR *state));
windowBits is in the range 8..15, and window is a user-supplied
window and output buffer that is 2**windowBits bytes.
*/
-int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size)
-z_streamp strm;
-int windowBits;
-unsigned char FAR *window;
-const char *version;
-int stream_size;
-{
+int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits,
+ unsigned char FAR *window, const char *version,
+ int stream_size) {
struct inflate_state FAR *state;
if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
@@ -66,6 +59,7 @@ int stream_size;
state->window = window;
state->wnext = 0;
state->whave = 0;
+ state->sane = 1;
return Z_OK;
}
@@ -79,9 +73,7 @@ int stream_size;
used for threaded applications, since the rewriting of the tables and virgin
may not be thread-safe.
*/
-local void fixedtables(state)
-struct inflate_state FAR *state;
-{
+local void fixedtables(struct inflate_state FAR *state) {
#ifdef BUILDFIXED
static int virgin = 1;
static code *lenfix, *distfix;
@@ -247,13 +239,8 @@ struct inflate_state FAR *state;
inflateBack() can also return Z_STREAM_ERROR if the input parameters
are not correct, i.e. strm is Z_NULL or the state was not initialized.
*/
-int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc)
-z_streamp strm;
-in_func in;
-void FAR *in_desc;
-out_func out;
-void FAR *out_desc;
-{
+int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc,
+ out_func out, void FAR *out_desc) {
struct inflate_state FAR *state;
z_const unsigned char FAR *next; /* next input */
unsigned char FAR *put; /* next output */
@@ -605,33 +592,33 @@ void FAR *out_desc;
break;
case DONE:
- /* inflate stream terminated properly -- write leftover output */
+ /* inflate stream terminated properly */
ret = Z_STREAM_END;
- if (left < state->wsize) {
- if (out(out_desc, state->window, state->wsize - left))
- ret = Z_BUF_ERROR;
- }
goto inf_leave;
case BAD:
ret = Z_DATA_ERROR;
goto inf_leave;
- default: /* can't happen, but makes compilers happy */
+ default:
+ /* can't happen, but makes compilers happy */
ret = Z_STREAM_ERROR;
goto inf_leave;
}
- /* Return unused input */
+ /* Write leftover output and return unused input */
inf_leave:
+ if (left < state->wsize) {
+ if (out(out_desc, state->window, state->wsize - left) &&
+ ret == Z_STREAM_END)
+ ret = Z_BUF_ERROR;
+ }
strm->next_in = next;
strm->avail_in = have;
return ret;
}
-int ZEXPORT inflateBackEnd(strm)
-z_streamp strm;
-{
+int ZEXPORT inflateBackEnd(z_streamp strm) {
if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
return Z_STREAM_ERROR;
ZFREE(strm, strm->state);
diff --git a/deps/zlib/inffast.c b/deps/zlib/inffast.c
index 1fec7f363fa..9354676e786 100644
--- a/deps/zlib/inffast.c
+++ b/deps/zlib/inffast.c
@@ -47,10 +47,7 @@
requires strm->avail_out >= 258 for each loop to avoid checking for
output space.
*/
-void ZLIB_INTERNAL inflate_fast(strm, start)
-z_streamp strm;
-unsigned start; /* inflate()'s starting value for strm->avail_out */
-{
+void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) {
struct inflate_state FAR *state;
z_const unsigned char FAR *in; /* local strm->next_in */
z_const unsigned char FAR *last; /* have enough input while in < last */
diff --git a/deps/zlib/inffast.h b/deps/zlib/inffast.h
index e5c1aa4ca8c..49c6d156c5c 100644
--- a/deps/zlib/inffast.h
+++ b/deps/zlib/inffast.h
@@ -8,4 +8,4 @@
subject to change. Applications should only use zlib.h.
*/
-void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start));
+void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start);
diff --git a/deps/zlib/inflate.c b/deps/zlib/inflate.c
index 7be8c63662a..b0757a9b249 100644
--- a/deps/zlib/inflate.c
+++ b/deps/zlib/inflate.c
@@ -91,20 +91,7 @@
# endif
#endif
-/* function prototypes */
-local int inflateStateCheck OF((z_streamp strm));
-local void fixedtables OF((struct inflate_state FAR *state));
-local int updatewindow OF((z_streamp strm, const unsigned char FAR *end,
- unsigned copy));
-#ifdef BUILDFIXED
- void makefixed OF((void));
-#endif
-local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf,
- unsigned len));
-
-local int inflateStateCheck(strm)
-z_streamp strm;
-{
+local int inflateStateCheck(z_streamp strm) {
struct inflate_state FAR *state;
if (strm == Z_NULL ||
strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0)
@@ -116,9 +103,7 @@ z_streamp strm;
return 0;
}
-int ZEXPORT inflateResetKeep(strm)
-z_streamp strm;
-{
+int ZEXPORT inflateResetKeep(z_streamp strm) {
struct inflate_state FAR *state;
if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
@@ -142,9 +127,7 @@ z_streamp strm;
return Z_OK;
}
-int ZEXPORT inflateReset(strm)
-z_streamp strm;
-{
+int ZEXPORT inflateReset(z_streamp strm) {
struct inflate_state FAR *state;
if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
@@ -155,10 +138,7 @@ z_streamp strm;
return inflateResetKeep(strm);
}
-int ZEXPORT inflateReset2(strm, windowBits)
-z_streamp strm;
-int windowBits;
-{
+int ZEXPORT inflateReset2(z_streamp strm, int windowBits) {
int wrap;
struct inflate_state FAR *state;
@@ -168,6 +148,8 @@ int windowBits;
/* extract wrap request from windowBits parameter */
if (windowBits < 0) {
+ if (windowBits < -15)
+ return Z_STREAM_ERROR;
wrap = 0;
windowBits = -windowBits;
}
@@ -193,12 +175,8 @@ int windowBits;
return inflateReset(strm);
}
-int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
-z_streamp strm;
-int windowBits;
-const char *version;
-int stream_size;
-{
+int ZEXPORT inflateInit2_(z_streamp strm, int windowBits,
+ const char *version, int stream_size) {
int ret;
struct inflate_state FAR *state;
@@ -237,22 +215,17 @@ int stream_size;
return ret;
}
-int ZEXPORT inflateInit_(strm, version, stream_size)
-z_streamp strm;
-const char *version;
-int stream_size;
-{
+int ZEXPORT inflateInit_(z_streamp strm, const char *version,
+ int stream_size) {
return inflateInit2_(strm, DEF_WBITS, version, stream_size);
}
-int ZEXPORT inflatePrime(strm, bits, value)
-z_streamp strm;
-int bits;
-int value;
-{
+int ZEXPORT inflatePrime(z_streamp strm, int bits, int value) {
struct inflate_state FAR *state;
if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
+ if (bits == 0)
+ return Z_OK;
state = (struct inflate_state FAR *)strm->state;
if (bits < 0) {
state->hold = 0;
@@ -276,9 +249,7 @@ int value;
used for threaded applications, since the rewriting of the tables and virgin
may not be thread-safe.
*/
-local void fixedtables(state)
-struct inflate_state FAR *state;
-{
+local void fixedtables(struct inflate_state FAR *state) {
#ifdef BUILDFIXED
static int virgin = 1;
static code *lenfix, *distfix;
@@ -340,7 +311,7 @@ struct inflate_state FAR *state;
a.out > inffixed.h
*/
-void makefixed()
+void makefixed(void)
{
unsigned low, size;
struct inflate_state state;
@@ -394,11 +365,7 @@ void makefixed()
output will fall in the output data, making match copies simpler and faster.
The advantage may be dependent on the size of the processor's data caches.
*/
-local int updatewindow(strm, end, copy)
-z_streamp strm;
-const Bytef *end;
-unsigned copy;
-{
+local int updatewindow(z_streamp strm, const Bytef *end, unsigned copy) {
struct inflate_state FAR *state;
unsigned dist;
@@ -620,10 +587,7 @@ unsigned copy;
will return Z_BUF_ERROR if it has not reached the end of the stream.
*/
-int ZEXPORT inflate(strm, flush)
-z_streamp strm;
-int flush;
-{
+int ZEXPORT inflate(z_streamp strm, int flush) {
struct inflate_state FAR *state;
z_const unsigned char FAR *next; /* next input */
unsigned char FAR *put; /* next output */
@@ -764,8 +728,9 @@ int flush;
if (copy > have) copy = have;
if (copy) {
if (state->head != Z_NULL &&
- state->head->extra != Z_NULL) {
- len = state->head->extra_len - state->length;
+ state->head->extra != Z_NULL &&
+ (len = state->head->extra_len - state->length) <
+ state->head->extra_max) {
zmemcpy(state->head->extra + len, next,
len + copy > state->head->extra_max ?
state->head->extra_max - len : copy);
@@ -1298,9 +1263,7 @@ int flush;
return ret;
}
-int ZEXPORT inflateEnd(strm)
-z_streamp strm;
-{
+int ZEXPORT inflateEnd(z_streamp strm) {
struct inflate_state FAR *state;
if (inflateStateCheck(strm))
return Z_STREAM_ERROR;
@@ -1312,11 +1275,8 @@ z_streamp strm;
return Z_OK;
}
-int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength)
-z_streamp strm;
-Bytef *dictionary;
-uInt *dictLength;
-{
+int ZEXPORT inflateGetDictionary(z_streamp strm, Bytef *dictionary,
+ uInt *dictLength) {
struct inflate_state FAR *state;
/* check state */
@@ -1335,11 +1295,8 @@ uInt *dictLength;
return Z_OK;
}
-int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
-z_streamp strm;
-const Bytef *dictionary;
-uInt dictLength;
-{
+int ZEXPORT inflateSetDictionary(z_streamp strm, const Bytef *dictionary,
+ uInt dictLength) {
struct inflate_state FAR *state;
unsigned long dictid;
int ret;
@@ -1370,10 +1327,7 @@ uInt dictLength;
return Z_OK;
}
-int ZEXPORT inflateGetHeader(strm, head)
-z_streamp strm;
-gz_headerp head;
-{
+int ZEXPORT inflateGetHeader(z_streamp strm, gz_headerp head) {
struct inflate_state FAR *state;
/* check state */
@@ -1398,11 +1352,8 @@ gz_headerp head;
called again with more data and the *have state. *have is initialized to
zero for the first call.
*/
-local unsigned syncsearch(have, buf, len)
-unsigned FAR *have;
-const unsigned char FAR *buf;
-unsigned len;
-{
+local unsigned syncsearch(unsigned FAR *have, const unsigned char FAR *buf,
+ unsigned len) {
unsigned got;
unsigned next;
@@ -1421,9 +1372,7 @@ unsigned len;
return next;
}
-int ZEXPORT inflateSync(strm)
-z_streamp strm;
-{
+int ZEXPORT inflateSync(z_streamp strm) {
unsigned len; /* number of bytes to look at or looked at */
int flags; /* temporary to save header status */
unsigned long in, out; /* temporary to save total_in and total_out */
@@ -1479,9 +1428,7 @@ z_streamp strm;
block. When decompressing, PPP checks that at the end of input packet,
inflate is waiting for these length bytes.
*/
-int ZEXPORT inflateSyncPoint(strm)
-z_streamp strm;
-{
+int ZEXPORT inflateSyncPoint(z_streamp strm) {
struct inflate_state FAR *state;
if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
@@ -1489,10 +1436,7 @@ z_streamp strm;
return state->mode == STORED && state->bits == 0;
}
-int ZEXPORT inflateCopy(dest, source)
-z_streamp dest;
-z_streamp source;
-{
+int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) {
struct inflate_state FAR *state;
struct inflate_state FAR *copy;
unsigned char FAR *window;
@@ -1536,10 +1480,7 @@ z_streamp source;
return Z_OK;
}
-int ZEXPORT inflateUndermine(strm, subvert)
-z_streamp strm;
-int subvert;
-{
+int ZEXPORT inflateUndermine(z_streamp strm, int subvert) {
struct inflate_state FAR *state;
if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
@@ -1554,10 +1495,7 @@ int subvert;
#endif
}
-int ZEXPORT inflateValidate(strm, check)
-z_streamp strm;
-int check;
-{
+int ZEXPORT inflateValidate(z_streamp strm, int check) {
struct inflate_state FAR *state;
if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
@@ -1569,9 +1507,7 @@ int check;
return Z_OK;
}
-long ZEXPORT inflateMark(strm)
-z_streamp strm;
-{
+long ZEXPORT inflateMark(z_streamp strm) {
struct inflate_state FAR *state;
if (inflateStateCheck(strm))
@@ -1582,9 +1518,7 @@ z_streamp strm;
(state->mode == MATCH ? state->was - state->length : 0));
}
-unsigned long ZEXPORT inflateCodesUsed(strm)
-z_streamp strm;
-{
+unsigned long ZEXPORT inflateCodesUsed(z_streamp strm) {
struct inflate_state FAR *state;
if (inflateStateCheck(strm)) return (unsigned long)-1;
state = (struct inflate_state FAR *)strm->state;
diff --git a/deps/zlib/inftrees.c b/deps/zlib/inftrees.c
index 09462a740b1..8a208c2daa8 100644
--- a/deps/zlib/inftrees.c
+++ b/deps/zlib/inftrees.c
@@ -1,5 +1,5 @@
/* inftrees.c -- generate Huffman trees for efficient decoding
- * Copyright (C) 1995-2022 Mark Adler
+ * Copyright (C) 1995-2023 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@@ -9,7 +9,7 @@
#define MAXBITS 15
const char inflate_copyright[] =
- " inflate 1.2.12 Copyright 1995-2022 Mark Adler ";
+ " inflate 1.3 Copyright 1995-2023 Mark Adler ";
/*
If you use the zlib library in a product, an acknowledgment is welcome
in the documentation of your product. If for some reason you cannot
@@ -29,14 +29,9 @@ const char inflate_copyright[] =
table index bits. It will differ if the request is greater than the
longest code or if it is less than the shortest code.
*/
-int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work)
-codetype type;
-unsigned short FAR *lens;
-unsigned codes;
-code FAR * FAR *table;
-unsigned FAR *bits;
-unsigned short FAR *work;
-{
+int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens,
+ unsigned codes, code FAR * FAR *table,
+ unsigned FAR *bits, unsigned short FAR *work) {
unsigned len; /* a code's length in bits */
unsigned sym; /* index of code symbols */
unsigned min, max; /* minimum and maximum code lengths */
@@ -62,7 +57,7 @@ unsigned short FAR *work;
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
static const unsigned short lext[31] = { /* Length codes 257..285 extra */
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
- 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 199, 202};
+ 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 198, 203};
static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
diff --git a/deps/zlib/inftrees.h b/deps/zlib/inftrees.h
index baa53a0b1a1..a10712d8cb5 100644
--- a/deps/zlib/inftrees.h
+++ b/deps/zlib/inftrees.h
@@ -38,7 +38,7 @@ typedef struct {
/* Maximum size of the dynamic table. The maximum number of code structures is
1444, which is the sum of 852 for literal/length codes and 592 for distance
codes. These values were found by exhaustive searches using the program
- examples/enough.c found in the zlib distribtution. The arguments to that
+ examples/enough.c found in the zlib distribution. The arguments to that
program are the number of symbols, the initial root table size, and the
maximum bit length of a code. "enough 286 9 15" for literal/length codes
returns returns 852, and "enough 30 6 15" for distance codes returns 592.
@@ -57,6 +57,6 @@ typedef enum {
DISTS
} codetype;
-int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens,
- unsigned codes, code FAR * FAR *table,
- unsigned FAR *bits, unsigned short FAR *work));
+int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens,
+ unsigned codes, code FAR * FAR *table,
+ unsigned FAR *bits, unsigned short FAR *work);
diff --git a/deps/zlib/trees.c b/deps/zlib/trees.c
index 8b438cce4f3..8dbdc40bacc 100644
--- a/deps/zlib/trees.c
+++ b/deps/zlib/trees.c
@@ -122,39 +122,116 @@ struct static_tree_desc_s {
int max_length; /* max bit length for the codes */
};
-local const static_tree_desc static_l_desc =
+#ifdef NO_INIT_GLOBAL_POINTERS
+# define TCONST
+#else
+# define TCONST const
+#endif
+
+local TCONST static_tree_desc static_l_desc =
{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
-local const static_tree_desc static_d_desc =
+local TCONST static_tree_desc static_d_desc =
{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
-local const static_tree_desc static_bl_desc =
+local TCONST static_tree_desc static_bl_desc =
{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
/* ===========================================================================
- * Local (static) routines in this file.
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
*/
+#define put_short(s, w) { \
+ put_byte(s, (uch)((w) & 0xff)); \
+ put_byte(s, (uch)((ush)(w) >> 8)); \
+}
-local void tr_static_init OF((void));
-local void init_block OF((deflate_state *s));
-local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
-local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
-local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
-local void build_tree OF((deflate_state *s, tree_desc *desc));
-local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
-local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
-local int build_bl_tree OF((deflate_state *s));
-local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
- int blcodes));
-local void compress_block OF((deflate_state *s, const ct_data *ltree,
- const ct_data *dtree));
-local int detect_data_type OF((deflate_state *s));
-local unsigned bi_reverse OF((unsigned code, int len));
-local void bi_windup OF((deflate_state *s));
-local void bi_flush OF((deflate_state *s));
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(unsigned code, int len) {
+ register unsigned res = 0;
+ do {
+ res |= code & 1;
+ code >>= 1, res <<= 1;
+ } while (--len > 0);
+ return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(deflate_state *s) {
+ if (s->bi_valid == 16) {
+ put_short(s, s->bi_buf);
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ } else if (s->bi_valid >= 8) {
+ put_byte(s, (Byte)s->bi_buf);
+ s->bi_buf >>= 8;
+ s->bi_valid -= 8;
+ }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(deflate_state *s) {
+ if (s->bi_valid > 8) {
+ put_short(s, s->bi_buf);
+ } else if (s->bi_valid > 0) {
+ put_byte(s, (Byte)s->bi_buf);
+ }
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+#ifdef ZLIB_DEBUG
+ s->bits_sent = (s->bits_sent + 7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+local void gen_codes(ct_data *tree, int max_code, ushf *bl_count) {
+ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+ unsigned code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ code = (code + bl_count[bits - 1]) << 1;
+ next_code[bits] = (ush)code;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert (code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1,
+ "inconsistent bit counts");
+ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].Len;
+ if (len == 0) continue;
+ /* Now reverse the bits */
+ tree[n].Code = (ush)bi_reverse(next_code[len]++, len);
+
+ Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len] - 1));
+ }
+}
#ifdef GEN_TREES_H
-local void gen_trees_header OF((void));
+local void gen_trees_header(void);
#endif
#ifndef ZLIB_DEBUG
@@ -167,33 +244,18 @@ local void gen_trees_header OF((void));
send_bits(s, tree[c].Code, tree[c].Len); }
#endif
-/* ===========================================================================
- * Output a short LSB first on the stream.
- * IN assertion: there is enough room in pendingBuf.
- */
-#define put_short(s, w) { \
- put_byte(s, (uch)((w) & 0xff)); \
- put_byte(s, (uch)((ush)(w) >> 8)); \
-}
-
/* ===========================================================================
* Send a value on a given number of bits.
* IN assertion: length <= 16 and value fits in length bits.
*/
#ifdef ZLIB_DEBUG
-local void send_bits OF((deflate_state *s, int value, int length));
-
-local void send_bits(s, value, length)
- deflate_state *s;
- int value; /* value to send */
- int length; /* number of bits */
-{
+local void send_bits(deflate_state *s, int value, int length) {
Tracevv((stderr," l %2d v %4x ", length, value));
Assert(length > 0 && length <= 15, "invalid length");
s->bits_sent += (ulg)length;
/* If not enough room in bi_buf, use (valid) bits from bi_buf and
- * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+ * (16 - bi_valid) bits from value, leaving (width - (16 - bi_valid))
* unused bits in value.
*/
if (s->bi_valid > (int)Buf_size - length) {
@@ -229,8 +291,7 @@ local void send_bits(s, value, length)
/* ===========================================================================
* Initialize the various 'constant' tables.
*/
-local void tr_static_init()
-{
+local void tr_static_init(void) {
#if defined(GEN_TREES_H) || !defined(STDC)
static int static_init_done = 0;
int n; /* iterates over tree elements */
@@ -256,7 +317,7 @@ local void tr_static_init()
length = 0;
for (code = 0; code < LENGTH_CODES-1; code++) {
base_length[code] = length;
- for (n = 0; n < (1< dist code (0..29) */
dist = 0;
for (code = 0 ; code < 16; code++) {
base_dist[code] = dist;
- for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */
for ( ; code < D_CODES; code++) {
base_dist[code] = dist << 7;
- for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+ for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) {
_dist_code[256 + dist++] = (uch)code;
}
}
- Assert (dist == 256, "tr_static_init: 256+dist != 512");
+ Assert (dist == 256, "tr_static_init: 256 + dist != 512");
/* Construct the codes of the static literal tree */
for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
@@ -312,7 +373,7 @@ local void tr_static_init()
}
/* ===========================================================================
- * Genererate the file trees.h describing the static trees.
+ * Generate the file trees.h describing the static trees.
*/
#ifdef GEN_TREES_H
# ifndef ZLIB_DEBUG
@@ -321,10 +382,9 @@ local void tr_static_init()
# define SEPARATOR(i, last, width) \
((i) == (last)? "\n};\n\n" : \
- ((i) % (width) == (width)-1 ? ",\n" : ", "))
+ ((i) % (width) == (width) - 1 ? ",\n" : ", "))
-void gen_trees_header()
-{
+void gen_trees_header(void) {
FILE *header = fopen("trees.h", "w");
int i;
@@ -373,12 +433,26 @@ void gen_trees_header()
}
#endif /* GEN_TREES_H */
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(deflate_state *s) {
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
+ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
+ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+ s->dyn_ltree[END_BLOCK].Freq = 1;
+ s->opt_len = s->static_len = 0L;
+ s->sym_next = s->matches = 0;
+}
+
/* ===========================================================================
* Initialize the tree data structures for a new zlib stream.
*/
-void ZLIB_INTERNAL _tr_init(s)
- deflate_state *s;
-{
+void ZLIB_INTERNAL _tr_init(deflate_state *s) {
tr_static_init();
s->l_desc.dyn_tree = s->dyn_ltree;
@@ -401,24 +475,6 @@ void ZLIB_INTERNAL _tr_init(s)
init_block(s);
}
-/* ===========================================================================
- * Initialize a new block.
- */
-local void init_block(s)
- deflate_state *s;
-{
- int n; /* iterates over tree elements */
-
- /* Initialize the trees. */
- for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
- for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
- for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
-
- s->dyn_ltree[END_BLOCK].Freq = 1;
- s->opt_len = s->static_len = 0L;
- s->sym_next = s->matches = 0;
-}
-
#define SMALLEST 1
/* Index within the heap array of least frequent node in the Huffman tree */
@@ -448,17 +504,13 @@ local void init_block(s)
* when the heap property is re-established (each father smaller than its
* two sons).
*/
-local void pqdownheap(s, tree, k)
- deflate_state *s;
- ct_data *tree; /* the tree to restore */
- int k; /* node to move down */
-{
+local void pqdownheap(deflate_state *s, ct_data *tree, int k) {
int v = s->heap[k];
int j = k << 1; /* left son of k */
while (j <= s->heap_len) {
/* Set j to the smallest of the two sons: */
if (j < s->heap_len &&
- smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+ smaller(tree, s->heap[j + 1], s->heap[j], s->depth)) {
j++;
}
/* Exit if v is smaller than both sons */
@@ -483,10 +535,7 @@ local void pqdownheap(s, tree, k)
* The length opt_len is updated; static_len is also updated if stree is
* not null.
*/
-local void gen_bitlen(s, desc)
- deflate_state *s;
- tree_desc *desc; /* the tree descriptor */
-{
+local void gen_bitlen(deflate_state *s, tree_desc *desc) {
ct_data *tree = desc->dyn_tree;
int max_code = desc->max_code;
const ct_data *stree = desc->stat_desc->static_tree;
@@ -507,7 +556,7 @@ local void gen_bitlen(s, desc)
*/
tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
- for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+ for (h = s->heap_max + 1; h < HEAP_SIZE; h++) {
n = s->heap[h];
bits = tree[tree[n].Dad].Len + 1;
if (bits > max_length) bits = max_length, overflow++;
@@ -518,7 +567,7 @@ local void gen_bitlen(s, desc)
s->bl_count[bits]++;
xbits = 0;
- if (n >= base) xbits = extra[n-base];
+ if (n >= base) xbits = extra[n - base];
f = tree[n].Freq;
s->opt_len += (ulg)f * (unsigned)(bits + xbits);
if (stree) s->static_len += (ulg)f * (unsigned)(stree[n].Len + xbits);
@@ -530,10 +579,10 @@ local void gen_bitlen(s, desc)
/* Find the first bit length which could increase: */
do {
- bits = max_length-1;
+ bits = max_length - 1;
while (s->bl_count[bits] == 0) bits--;
- s->bl_count[bits]--; /* move one leaf down the tree */
- s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+ s->bl_count[bits]--; /* move one leaf down the tree */
+ s->bl_count[bits + 1] += 2; /* move one overflow item as its brother */
s->bl_count[max_length]--;
/* The brother of the overflow item also moves one step up,
* but this does not affect bl_count[max_length]
@@ -561,48 +610,9 @@ local void gen_bitlen(s, desc)
}
}
-/* ===========================================================================
- * Generate the codes for a given tree and bit counts (which need not be
- * optimal).
- * IN assertion: the array bl_count contains the bit length statistics for
- * the given tree and the field len is set for all tree elements.
- * OUT assertion: the field code is set for all tree elements of non
- * zero code length.
- */
-local void gen_codes (tree, max_code, bl_count)
- ct_data *tree; /* the tree to decorate */
- int max_code; /* largest code with non zero frequency */
- ushf *bl_count; /* number of codes at each bit length */
-{
- ush next_code[MAX_BITS+1]; /* next code value for each bit length */
- unsigned code = 0; /* running code value */
- int bits; /* bit index */
- int n; /* code index */
-
- /* The distribution counts are first used to generate the code values
- * without bit reversal.
- */
- for (bits = 1; bits <= MAX_BITS; bits++) {
- code = (code + bl_count[bits-1]) << 1;
- next_code[bits] = (ush)code;
- }
- /* Check that the bit counts in bl_count are consistent. The last code
- * must be all ones.
- */
- Assert (code + bl_count[MAX_BITS]-1 == (1<
+#endif
/* ===========================================================================
* Construct one Huffman tree and assigns the code bit strings and lengths.
@@ -612,10 +622,7 @@ local void gen_codes (tree, max_code, bl_count)
* and corresponding code. The length opt_len is updated; static_len is
* also updated if stree is not null. The field max_code is set.
*/
-local void build_tree(s, desc)
- deflate_state *s;
- tree_desc *desc; /* the tree descriptor */
-{
+local void build_tree(deflate_state *s, tree_desc *desc) {
ct_data *tree = desc->dyn_tree;
const ct_data *stree = desc->stat_desc->static_tree;
int elems = desc->stat_desc->elems;
@@ -624,7 +631,7 @@ local void build_tree(s, desc)
int node; /* new node being created */
/* Construct the initial heap, with least frequent element in
- * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n + 1].
* heap[0] is not used.
*/
s->heap_len = 0, s->heap_max = HEAP_SIZE;
@@ -652,7 +659,7 @@ local void build_tree(s, desc)
}
desc->max_code = max_code;
- /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ /* The elements heap[heap_len/2 + 1 .. heap_len] are leaves of the tree,
* establish sub-heaps of increasing lengths:
*/
for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
@@ -700,11 +707,7 @@ local void build_tree(s, desc)
* Scan a literal or distance tree to determine the frequencies of the codes
* in the bit length tree.
*/
-local void scan_tree (s, tree, max_code)
- deflate_state *s;
- ct_data *tree; /* the tree to be scanned */
- int max_code; /* and its largest code of non zero frequency */
-{
+local void scan_tree(deflate_state *s, ct_data *tree, int max_code) {
int n; /* iterates over all tree elements */
int prevlen = -1; /* last emitted length */
int curlen; /* length of current code */
@@ -714,10 +717,10 @@ local void scan_tree (s, tree, max_code)
int min_count = 4; /* min repeat count */
if (nextlen == 0) max_count = 138, min_count = 3;
- tree[max_code+1].Len = (ush)0xffff; /* guard */
+ tree[max_code + 1].Len = (ush)0xffff; /* guard */
for (n = 0; n <= max_code; n++) {
- curlen = nextlen; nextlen = tree[n+1].Len;
+ curlen = nextlen; nextlen = tree[n + 1].Len;
if (++count < max_count && curlen == nextlen) {
continue;
} else if (count < min_count) {
@@ -745,11 +748,7 @@ local void scan_tree (s, tree, max_code)
* Send a literal or distance tree in compressed form, using the codes in
* bl_tree.
*/
-local void send_tree (s, tree, max_code)
- deflate_state *s;
- ct_data *tree; /* the tree to be scanned */
- int max_code; /* and its largest code of non zero frequency */
-{
+local void send_tree(deflate_state *s, ct_data *tree, int max_code) {
int n; /* iterates over all tree elements */
int prevlen = -1; /* last emitted length */
int curlen; /* length of current code */
@@ -758,11 +757,11 @@ local void send_tree (s, tree, max_code)
int max_count = 7; /* max repeat count */
int min_count = 4; /* min repeat count */
- /* tree[max_code+1].Len = -1; */ /* guard already set */
+ /* tree[max_code + 1].Len = -1; */ /* guard already set */
if (nextlen == 0) max_count = 138, min_count = 3;
for (n = 0; n <= max_code; n++) {
- curlen = nextlen; nextlen = tree[n+1].Len;
+ curlen = nextlen; nextlen = tree[n + 1].Len;
if (++count < max_count && curlen == nextlen) {
continue;
} else if (count < min_count) {
@@ -773,13 +772,13 @@ local void send_tree (s, tree, max_code)
send_code(s, curlen, s->bl_tree); count--;
}
Assert(count >= 3 && count <= 6, " 3_6?");
- send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count - 3, 2);
} else if (count <= 10) {
- send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count - 3, 3);
} else {
- send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count - 11, 7);
}
count = 0; prevlen = curlen;
if (nextlen == 0) {
@@ -796,9 +795,7 @@ local void send_tree (s, tree, max_code)
* Construct the Huffman tree for the bit lengths and return the index in
* bl_order of the last bit length code to send.
*/
-local int build_bl_tree(s)
- deflate_state *s;
-{
+local int build_bl_tree(deflate_state *s) {
int max_blindex; /* index of last bit length code of non zero freq */
/* Determine the bit length frequencies for literal and distance trees */
@@ -807,8 +804,8 @@ local int build_bl_tree(s)
/* Build the bit length tree: */
build_tree(s, (tree_desc *)(&(s->bl_desc)));
- /* opt_len now includes the length of the tree representations, except
- * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ /* opt_len now includes the length of the tree representations, except the
+ * lengths of the bit lengths codes and the 5 + 5 + 4 bits for the counts.
*/
/* Determine the number of bit length codes to send. The pkzip format
@@ -819,7 +816,7 @@ local int build_bl_tree(s)
if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
}
/* Update opt_len to include the bit length tree and counts */
- s->opt_len += 3*((ulg)max_blindex+1) + 5+5+4;
+ s->opt_len += 3*((ulg)max_blindex + 1) + 5 + 5 + 4;
Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
s->opt_len, s->static_len));
@@ -831,42 +828,36 @@ local int build_bl_tree(s)
* lengths of the bit length codes, the literal tree and the distance tree.
* IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
*/
-local void send_all_trees(s, lcodes, dcodes, blcodes)
- deflate_state *s;
- int lcodes, dcodes, blcodes; /* number of codes for each tree */
-{
+local void send_all_trees(deflate_state *s, int lcodes, int dcodes,
+ int blcodes) {
int rank; /* index in bl_order */
Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
"too many codes");
Tracev((stderr, "\nbl counts: "));
- send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
- send_bits(s, dcodes-1, 5);
- send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
+ send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(s, dcodes - 1, 5);
+ send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */
for (rank = 0; rank < blcodes; rank++) {
Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
}
Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
- send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+ send_tree(s, (ct_data *)s->dyn_ltree, lcodes - 1); /* literal tree */
Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
- send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+ send_tree(s, (ct_data *)s->dyn_dtree, dcodes - 1); /* distance tree */
Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
}
/* ===========================================================================
* Send a stored block
*/
-void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last)
- deflate_state *s;
- charf *buf; /* input block */
- ulg stored_len; /* length of input block */
- int last; /* one if this is the last block for a file */
-{
- send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */
+void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf,
+ ulg stored_len, int last) {
+ send_bits(s, (STORED_BLOCK<<1) + last, 3); /* send block type */
bi_windup(s); /* align on byte boundary */
put_short(s, (ush)stored_len);
put_short(s, (ush)~stored_len);
@@ -877,16 +868,14 @@ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last)
s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
s->compressed_len += (stored_len + 4) << 3;
s->bits_sent += 2*16;
- s->bits_sent += stored_len<<3;
+ s->bits_sent += stored_len << 3;
#endif
}
/* ===========================================================================
* Flush the bits in the bit buffer to pending output (leaves at most 7 bits)
*/
-void ZLIB_INTERNAL _tr_flush_bits(s)
- deflate_state *s;
-{
+void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s) {
bi_flush(s);
}
@@ -894,9 +883,7 @@ void ZLIB_INTERNAL _tr_flush_bits(s)
* Send one empty static block to give enough lookahead for inflate.
* This takes 10 bits, of which 7 may remain in the bit buffer.
*/
-void ZLIB_INTERNAL _tr_align(s)
- deflate_state *s;
-{
+void ZLIB_INTERNAL _tr_align(deflate_state *s) {
send_bits(s, STATIC_TREES<<1, 3);
send_code(s, END_BLOCK, static_ltree);
#ifdef ZLIB_DEBUG
@@ -905,16 +892,99 @@ void ZLIB_INTERNAL _tr_align(s)
bi_flush(s);
}
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(deflate_state *s, const ct_data *ltree,
+ const ct_data *dtree) {
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned sx = 0; /* running index in sym_buf */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (s->sym_next != 0) do {
+ dist = s->sym_buf[sx++] & 0xff;
+ dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8;
+ lc = s->sym_buf[sx++];
+ if (dist == 0) {
+ send_code(s, lc, ltree); /* send a literal byte */
+ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = _length_code[lc];
+ send_code(s, code + LITERALS + 1, ltree); /* send length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= base_length[code];
+ send_bits(s, lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ Assert (code < D_CODES, "bad d_code");
+
+ send_code(s, code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= (unsigned)base_dist[code];
+ send_bits(s, dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and sym_buf is ok: */
+ Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow");
+
+ } while (sx < s->sym_next);
+
+ send_code(s, END_BLOCK, ltree);
+}
+
+/* ===========================================================================
+ * Check if the data type is TEXT or BINARY, using the following algorithm:
+ * - TEXT if the two conditions below are satisfied:
+ * a) There are no non-portable control characters belonging to the
+ * "block list" (0..6, 14..25, 28..31).
+ * b) There is at least one printable character belonging to the
+ * "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
+ * - BINARY otherwise.
+ * - The following partially-portable control characters form a
+ * "gray list" that is ignored in this detection algorithm:
+ * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
+ * IN assertion: the fields Freq of dyn_ltree are set.
+ */
+local int detect_data_type(deflate_state *s) {
+ /* block_mask is the bit mask of block-listed bytes
+ * set bits 0..6, 14..25, and 28..31
+ * 0xf3ffc07f = binary 11110011111111111100000001111111
+ */
+ unsigned long block_mask = 0xf3ffc07fUL;
+ int n;
+
+ /* Check for non-textual ("block-listed") bytes. */
+ for (n = 0; n <= 31; n++, block_mask >>= 1)
+ if ((block_mask & 1) && (s->dyn_ltree[n].Freq != 0))
+ return Z_BINARY;
+
+ /* Check for textual ("allow-listed") bytes. */
+ if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0
+ || s->dyn_ltree[13].Freq != 0)
+ return Z_TEXT;
+ for (n = 32; n < LITERALS; n++)
+ if (s->dyn_ltree[n].Freq != 0)
+ return Z_TEXT;
+
+ /* There are no "block-listed" or "allow-listed" bytes:
+ * this stream either is empty or has tolerated ("gray-listed") bytes only.
+ */
+ return Z_BINARY;
+}
+
/* ===========================================================================
* Determine the best encoding for the current block: dynamic trees, static
* trees or store, and write out the encoded block.
*/
-void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
- deflate_state *s;
- charf *buf; /* input block, or NULL if too old */
- ulg stored_len; /* length of input block */
- int last; /* one if this is the last block for a file */
-{
+void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf,
+ ulg stored_len, int last) {
ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
int max_blindex = 0; /* index of last bit length code of non zero freq */
@@ -943,14 +1013,17 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
max_blindex = build_bl_tree(s);
/* Determine the best encoding. Compute the block lengths in bytes. */
- opt_lenb = (s->opt_len+3+7)>>3;
- static_lenb = (s->static_len+3+7)>>3;
+ opt_lenb = (s->opt_len + 3 + 7) >> 3;
+ static_lenb = (s->static_len + 3 + 7) >> 3;
Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
s->sym_next / 3));
- if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+#ifndef FORCE_STATIC
+ if (static_lenb <= opt_lenb || s->strategy == Z_FIXED)
+#endif
+ opt_lenb = static_lenb;
} else {
Assert(buf != (char*)0, "lost buf");
@@ -960,7 +1033,7 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
#ifdef FORCE_STORED
if (buf != (char*)0) { /* force stored block */
#else
- if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+ if (stored_len + 4 <= opt_lenb && buf != (char*)0) {
/* 4: two words for the lengths */
#endif
/* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
@@ -971,21 +1044,17 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
*/
_tr_stored_block(s, buf, stored_len, last);
-#ifdef FORCE_STATIC
- } else if (static_lenb >= 0) { /* force static trees */
-#else
- } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) {
-#endif
- send_bits(s, (STATIC_TREES<<1)+last, 3);
+ } else if (static_lenb == opt_lenb) {
+ send_bits(s, (STATIC_TREES<<1) + last, 3);
compress_block(s, (const ct_data *)static_ltree,
(const ct_data *)static_dtree);
#ifdef ZLIB_DEBUG
s->compressed_len += 3 + s->static_len;
#endif
} else {
- send_bits(s, (DYN_TREES<<1)+last, 3);
- send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
- max_blindex+1);
+ send_bits(s, (DYN_TREES<<1) + last, 3);
+ send_all_trees(s, s->l_desc.max_code + 1, s->d_desc.max_code + 1,
+ max_blindex + 1);
compress_block(s, (const ct_data *)s->dyn_ltree,
(const ct_data *)s->dyn_dtree);
#ifdef ZLIB_DEBUG
@@ -1004,19 +1073,15 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
s->compressed_len += 7; /* align on byte boundary */
#endif
}
- Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
- s->compressed_len-7*last));
+ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len >> 3,
+ s->compressed_len - 7*last));
}
/* ===========================================================================
* Save the match info and tally the frequency counts. Return true if
* the current block must be flushed.
*/
-int ZLIB_INTERNAL _tr_tally (s, dist, lc)
- deflate_state *s;
- unsigned dist; /* distance of matched string */
- unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
-{
+int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc) {
s->sym_buf[s->sym_next++] = (uch)dist;
s->sym_buf[s->sym_next++] = (uch)(dist >> 8);
s->sym_buf[s->sym_next++] = (uch)lc;
@@ -1031,152 +1096,8 @@ int ZLIB_INTERNAL _tr_tally (s, dist, lc)
(ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
(ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
- s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
+ s->dyn_ltree[_length_code[lc] + LITERALS + 1].Freq++;
s->dyn_dtree[d_code(dist)].Freq++;
}
return (s->sym_next == s->sym_end);
}
-
-/* ===========================================================================
- * Send the block data compressed using the given Huffman trees
- */
-local void compress_block(s, ltree, dtree)
- deflate_state *s;
- const ct_data *ltree; /* literal tree */
- const ct_data *dtree; /* distance tree */
-{
- unsigned dist; /* distance of matched string */
- int lc; /* match length or unmatched char (if dist == 0) */
- unsigned sx = 0; /* running index in sym_buf */
- unsigned code; /* the code to send */
- int extra; /* number of extra bits to send */
-
- if (s->sym_next != 0) do {
- dist = s->sym_buf[sx++] & 0xff;
- dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8;
- lc = s->sym_buf[sx++];
- if (dist == 0) {
- send_code(s, lc, ltree); /* send a literal byte */
- Tracecv(isgraph(lc), (stderr," '%c' ", lc));
- } else {
- /* Here, lc is the match length - MIN_MATCH */
- code = _length_code[lc];
- send_code(s, code+LITERALS+1, ltree); /* send the length code */
- extra = extra_lbits[code];
- if (extra != 0) {
- lc -= base_length[code];
- send_bits(s, lc, extra); /* send the extra length bits */
- }
- dist--; /* dist is now the match distance - 1 */
- code = d_code(dist);
- Assert (code < D_CODES, "bad d_code");
-
- send_code(s, code, dtree); /* send the distance code */
- extra = extra_dbits[code];
- if (extra != 0) {
- dist -= (unsigned)base_dist[code];
- send_bits(s, dist, extra); /* send the extra distance bits */
- }
- } /* literal or match pair ? */
-
- /* Check that the overlay between pending_buf and sym_buf is ok: */
- Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow");
-
- } while (sx < s->sym_next);
-
- send_code(s, END_BLOCK, ltree);
-}
-
-/* ===========================================================================
- * Check if the data type is TEXT or BINARY, using the following algorithm:
- * - TEXT if the two conditions below are satisfied:
- * a) There are no non-portable control characters belonging to the
- * "block list" (0..6, 14..25, 28..31).
- * b) There is at least one printable character belonging to the
- * "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
- * - BINARY otherwise.
- * - The following partially-portable control characters form a
- * "gray list" that is ignored in this detection algorithm:
- * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
- * IN assertion: the fields Freq of dyn_ltree are set.
- */
-local int detect_data_type(s)
- deflate_state *s;
-{
- /* block_mask is the bit mask of block-listed bytes
- * set bits 0..6, 14..25, and 28..31
- * 0xf3ffc07f = binary 11110011111111111100000001111111
- */
- unsigned long block_mask = 0xf3ffc07fUL;
- int n;
-
- /* Check for non-textual ("block-listed") bytes. */
- for (n = 0; n <= 31; n++, block_mask >>= 1)
- if ((block_mask & 1) && (s->dyn_ltree[n].Freq != 0))
- return Z_BINARY;
-
- /* Check for textual ("allow-listed") bytes. */
- if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0
- || s->dyn_ltree[13].Freq != 0)
- return Z_TEXT;
- for (n = 32; n < LITERALS; n++)
- if (s->dyn_ltree[n].Freq != 0)
- return Z_TEXT;
-
- /* There are no "block-listed" or "allow-listed" bytes:
- * this stream either is empty or has tolerated ("gray-listed") bytes only.
- */
- return Z_BINARY;
-}
-
-/* ===========================================================================
- * Reverse the first len bits of a code, using straightforward code (a faster
- * method would use a table)
- * IN assertion: 1 <= len <= 15
- */
-local unsigned bi_reverse(code, len)
- unsigned code; /* the value to invert */
- int len; /* its bit length */
-{
- register unsigned res = 0;
- do {
- res |= code & 1;
- code >>= 1, res <<= 1;
- } while (--len > 0);
- return res >> 1;
-}
-
-/* ===========================================================================
- * Flush the bit buffer, keeping at most 7 bits in it.
- */
-local void bi_flush(s)
- deflate_state *s;
-{
- if (s->bi_valid == 16) {
- put_short(s, s->bi_buf);
- s->bi_buf = 0;
- s->bi_valid = 0;
- } else if (s->bi_valid >= 8) {
- put_byte(s, (Byte)s->bi_buf);
- s->bi_buf >>= 8;
- s->bi_valid -= 8;
- }
-}
-
-/* ===========================================================================
- * Flush the bit buffer and align the output on a byte boundary
- */
-local void bi_windup(s)
- deflate_state *s;
-{
- if (s->bi_valid > 8) {
- put_short(s, s->bi_buf);
- } else if (s->bi_valid > 0) {
- put_byte(s, (Byte)s->bi_buf);
- }
- s->bi_buf = 0;
- s->bi_valid = 0;
-#ifdef ZLIB_DEBUG
- s->bits_sent = (s->bits_sent+7) & ~7;
-#endif
-}
diff --git a/deps/zlib/zconf.h b/deps/zlib/zconf.h
index 5e1d68a004e..fb76ffe312a 100644
--- a/deps/zlib/zconf.h
+++ b/deps/zlib/zconf.h
@@ -38,6 +38,9 @@
# define crc32 z_crc32
# define crc32_combine z_crc32_combine
# define crc32_combine64 z_crc32_combine64
+# define crc32_combine_gen z_crc32_combine_gen
+# define crc32_combine_gen64 z_crc32_combine_gen64
+# define crc32_combine_op z_crc32_combine_op
# define crc32_z z_crc32_z
# define deflate z_deflate
# define deflateBound z_deflateBound
@@ -238,7 +241,11 @@
#endif
#ifdef Z_SOLO
- typedef unsigned long z_size_t;
+# ifdef _WIN64
+ typedef unsigned long long z_size_t;
+# else
+ typedef unsigned long z_size_t;
+# endif
#else
# define z_longlong long long
# if defined(NO_SIZE_T)
@@ -349,6 +356,9 @@
# ifdef FAR
# undef FAR
# endif
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
# include
/* No need for _export, use ZLIB.DEF instead. */
/* For complete Windows compatibility, use WINAPI, not __stdcall. */
@@ -467,11 +477,18 @@ typedef uLong FAR uLongf;
# undef _LARGEFILE64_SOURCE
#endif
-#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)
-# define Z_HAVE_UNISTD_H
+#ifndef Z_HAVE_UNISTD_H
+# ifdef __WATCOMC__
+# define Z_HAVE_UNISTD_H
+# endif
+#endif
+#ifndef Z_HAVE_UNISTD_H
+# if defined(_LARGEFILE64_SOURCE) && !defined(_WIN32)
+# define Z_HAVE_UNISTD_H
+# endif
#endif
#ifndef Z_SOLO
-# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE)
+# if defined(Z_HAVE_UNISTD_H)
# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */
# ifdef VMS
# include /* for off_t */
@@ -507,7 +524,7 @@ typedef uLong FAR uLongf;
#if !defined(_WIN32) && defined(Z_LARGE64)
# define z_off64_t off64_t
#else
-# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)
+# if defined(_WIN32) && !defined(__GNUC__)
# define z_off64_t __int64
# else
# define z_off64_t z_off_t
diff --git a/deps/zlib/zlib.h b/deps/zlib/zlib.h
index d074d8398a6..6b7244f9943 100644
--- a/deps/zlib/zlib.h
+++ b/deps/zlib/zlib.h
@@ -1,7 +1,7 @@
/* zlib.h -- interface of the 'zlib' general purpose compression library
- version 1.2.12, March 11th, 2022
+ version 1.3, August 18th, 2023
- Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler
+ Copyright (C) 1995-2023 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -37,11 +37,11 @@
extern "C" {
#endif
-#define ZLIB_VERSION "1.2.12"
-#define ZLIB_VERNUM 0x12c0
+#define ZLIB_VERSION "1.3"
+#define ZLIB_VERNUM 0x1300
#define ZLIB_VER_MAJOR 1
-#define ZLIB_VER_MINOR 2
-#define ZLIB_VER_REVISION 12
+#define ZLIB_VER_MINOR 3
+#define ZLIB_VER_REVISION 0
#define ZLIB_VER_SUBREVISION 0
/*
@@ -78,8 +78,8 @@ extern "C" {
even in the case of corrupted input.
*/
-typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
-typedef void (*free_func) OF((voidpf opaque, voidpf address));
+typedef voidpf (*alloc_func)(voidpf opaque, uInt items, uInt size);
+typedef void (*free_func)(voidpf opaque, voidpf address);
struct internal_state;
@@ -217,7 +217,7 @@ typedef gz_header FAR *gz_headerp;
/* basic functions */
-ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+ZEXTERN const char * ZEXPORT zlibVersion(void);
/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
If the first character differs, the library code actually used is not
compatible with the zlib.h header file used by the application. This check
@@ -225,12 +225,12 @@ ZEXTERN const char * ZEXPORT zlibVersion OF((void));
*/
/*
-ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+ZEXTERN int ZEXPORT deflateInit(z_streamp strm, int level);
Initializes the internal stream state for compression. The fields
zalloc, zfree and opaque must be initialized before by the caller. If
zalloc and zfree are set to Z_NULL, deflateInit updates them to use default
- allocation functions.
+ allocation functions. total_in, total_out, adler, and msg are initialized.
The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
1 gives best speed, 9 gives best compression, 0 gives no compression at all
@@ -247,7 +247,7 @@ ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
*/
-ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+ZEXTERN int ZEXPORT deflate(z_streamp strm, int flush);
/*
deflate compresses as much data as possible, and stops when the input
buffer becomes empty or the output buffer becomes full. It may introduce
@@ -276,7 +276,7 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
== 0), or after each call of deflate(). If deflate returns Z_OK and with
zero avail_out, it must be called again after making room in the output
buffer because there might be more output pending. See deflatePending(),
- which can be used if desired to determine whether or not there is more ouput
+ which can be used if desired to determine whether or not there is more output
in that case.
Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
@@ -320,8 +320,8 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
with the same value of the flush parameter and more output space (updated
avail_out), until the flush is complete (deflate returns with non-zero
avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
- avail_out is greater than six to avoid repeated flush markers due to
- avail_out == 0 on return.
+ avail_out is greater than six when the flush marker begins, in order to avoid
+ repeated flush markers upon calling deflate() again when avail_out == 0.
If the parameter flush is set to Z_FINISH, pending input is processed,
pending output is flushed and deflate returns with Z_STREAM_END if there was
@@ -360,7 +360,7 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
*/
-ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+ZEXTERN int ZEXPORT deflateEnd(z_streamp strm);
/*
All dynamically allocated data structures for this stream are freed.
This function discards any unprocessed input and does not flush any pending
@@ -375,7 +375,7 @@ ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
/*
-ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+ZEXTERN int ZEXPORT inflateInit(z_streamp strm);
Initializes the internal stream state for decompression. The fields
next_in, avail_in, zalloc, zfree and opaque must be initialized before by
@@ -383,7 +383,8 @@ ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
read or consumed. The allocation of a sliding window will be deferred to
the first call of inflate (if the decompression does not complete on the
first call). If zalloc and zfree are set to Z_NULL, inflateInit updates
- them to use default allocation functions.
+ them to use default allocation functions. total_in, total_out, adler, and
+ msg are initialized.
inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
@@ -397,7 +398,7 @@ ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
*/
-ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+ZEXTERN int ZEXPORT inflate(z_streamp strm, int flush);
/*
inflate decompresses as much data as possible, and stops when the input
buffer becomes empty or the output buffer becomes full. It may introduce
@@ -517,7 +518,7 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
*/
-ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+ZEXTERN int ZEXPORT inflateEnd(z_streamp strm);
/*
All dynamically allocated data structures for this stream are freed.
This function discards any unprocessed input and does not flush any pending
@@ -535,12 +536,12 @@ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
*/
/*
-ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
- int level,
- int method,
- int windowBits,
- int memLevel,
- int strategy));
+ZEXTERN int ZEXPORT deflateInit2(z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy);
This is another version of deflateInit with more compression options. The
fields zalloc, zfree and opaque must be initialized before by the caller.
@@ -607,9 +608,9 @@ ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
compression: this will be done by deflate().
*/
-ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
- const Bytef *dictionary,
- uInt dictLength));
+ZEXTERN int ZEXPORT deflateSetDictionary(z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength);
/*
Initializes the compression dictionary from the given byte sequence
without producing any compressed output. When using the zlib format, this
@@ -651,16 +652,16 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
not perform any compression: this will be done by deflate().
*/
-ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm,
- Bytef *dictionary,
- uInt *dictLength));
+ZEXTERN int ZEXPORT deflateGetDictionary(z_streamp strm,
+ Bytef *dictionary,
+ uInt *dictLength);
/*
Returns the sliding dictionary being maintained by deflate. dictLength is
set to the number of bytes in the dictionary, and that many bytes are copied
to dictionary. dictionary must have enough space, where 32768 bytes is
always enough. If deflateGetDictionary() is called with dictionary equal to
Z_NULL, then only the dictionary length is returned, and nothing is copied.
- Similary, if dictLength is Z_NULL, then it is not set.
+ Similarly, if dictLength is Z_NULL, then it is not set.
deflateGetDictionary() may return a length less than the window size, even
when more than the window size in input has been provided. It may return up
@@ -673,8 +674,8 @@ ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm,
stream state is inconsistent.
*/
-ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
- z_streamp source));
+ZEXTERN int ZEXPORT deflateCopy(z_streamp dest,
+ z_streamp source);
/*
Sets the destination stream as a complete copy of the source stream.
@@ -691,20 +692,20 @@ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
destination.
*/
-ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+ZEXTERN int ZEXPORT deflateReset(z_streamp strm);
/*
This function is equivalent to deflateEnd followed by deflateInit, but
does not free and reallocate the internal compression state. The stream
will leave the compression level and any other attributes that may have been
- set unchanged.
+ set unchanged. total_in, total_out, adler, and msg are initialized.
deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
stream state was inconsistent (such as zalloc or state being Z_NULL).
*/
-ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
- int level,
- int strategy));
+ZEXTERN int ZEXPORT deflateParams(z_streamp strm,
+ int level,
+ int strategy);
/*
Dynamically update the compression level and compression strategy. The
interpretation of level and strategy is as in deflateInit2(). This can be
@@ -729,7 +730,7 @@ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
Then no more input data should be provided before the deflateParams() call.
If this is done, the old level and strategy will be applied to the data
compressed before deflateParams(), and the new level and strategy will be
- applied to the the data compressed after deflateParams().
+ applied to the data compressed after deflateParams().
deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream
state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if
@@ -740,11 +741,11 @@ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
retried with more output space.
*/
-ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
- int good_length,
- int max_lazy,
- int nice_length,
- int max_chain));
+ZEXTERN int ZEXPORT deflateTune(z_streamp strm,
+ int good_length,
+ int max_lazy,
+ int nice_length,
+ int max_chain);
/*
Fine tune deflate's internal compression parameters. This should only be
used by someone who understands the algorithm used by zlib's deflate for
@@ -757,8 +758,8 @@ ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
*/
-ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
- uLong sourceLen));
+ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm,
+ uLong sourceLen);
/*
deflateBound() returns an upper bound on the compressed size after
deflation of sourceLen bytes. It must be called after deflateInit() or
@@ -772,9 +773,9 @@ ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
than Z_FINISH or Z_NO_FLUSH are used.
*/
-ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm,
- unsigned *pending,
- int *bits));
+ZEXTERN int ZEXPORT deflatePending(z_streamp strm,
+ unsigned *pending,
+ int *bits);
/*
deflatePending() returns the number of bytes and bits of output that have
been generated, but not yet provided in the available output. The bytes not
@@ -787,9 +788,9 @@ ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm,
stream state was inconsistent.
*/
-ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
- int bits,
- int value));
+ZEXTERN int ZEXPORT deflatePrime(z_streamp strm,
+ int bits,
+ int value);
/*
deflatePrime() inserts bits in the deflate output stream. The intent
is that this function is used to start off the deflate output with the bits
@@ -804,8 +805,8 @@ ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
source stream state was inconsistent.
*/
-ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
- gz_headerp head));
+ZEXTERN int ZEXPORT deflateSetHeader(z_streamp strm,
+ gz_headerp head);
/*
deflateSetHeader() provides gzip header information for when a gzip
stream is requested by deflateInit2(). deflateSetHeader() may be called
@@ -821,16 +822,17 @@ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
gzip file" and give up.
If deflateSetHeader is not used, the default gzip header has text false,
- the time set to zero, and os set to 255, with no extra, name, or comment
- fields. The gzip header is returned to the default state by deflateReset().
+ the time set to zero, and os set to the current operating system, with no
+ extra, name, or comment fields. The gzip header is returned to the default
+ state by deflateReset().
deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
stream state was inconsistent.
*/
/*
-ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
- int windowBits));
+ZEXTERN int ZEXPORT inflateInit2(z_streamp strm,
+ int windowBits);
This is another version of inflateInit with an extra parameter. The
fields next_in, avail_in, zalloc, zfree and opaque must be initialized
@@ -883,9 +885,9 @@ ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
deferred until inflate() is called.
*/
-ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
- const Bytef *dictionary,
- uInt dictLength));
+ZEXTERN int ZEXPORT inflateSetDictionary(z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength);
/*
Initializes the decompression dictionary from the given uncompressed byte
sequence. This function must be called immediately after a call of inflate,
@@ -906,22 +908,22 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
inflate().
*/
-ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm,
- Bytef *dictionary,
- uInt *dictLength));
+ZEXTERN int ZEXPORT inflateGetDictionary(z_streamp strm,
+ Bytef *dictionary,
+ uInt *dictLength);
/*
Returns the sliding dictionary being maintained by inflate. dictLength is
set to the number of bytes in the dictionary, and that many bytes are copied
to dictionary. dictionary must have enough space, where 32768 bytes is
always enough. If inflateGetDictionary() is called with dictionary equal to
Z_NULL, then only the dictionary length is returned, and nothing is copied.
- Similary, if dictLength is Z_NULL, then it is not set.
+ Similarly, if dictLength is Z_NULL, then it is not set.
inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the
stream state is inconsistent.
*/
-ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+ZEXTERN int ZEXPORT inflateSync(z_streamp strm);
/*
Skips invalid compressed data until a possible full flush point (see above
for the description of deflate with Z_FULL_FLUSH) can be found, or until all
@@ -940,8 +942,8 @@ ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
input each time, until success or end of the input data.
*/
-ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
- z_streamp source));
+ZEXTERN int ZEXPORT inflateCopy(z_streamp dest,
+ z_streamp source);
/*
Sets the destination stream as a complete copy of the source stream.
@@ -956,18 +958,19 @@ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
destination.
*/
-ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+ZEXTERN int ZEXPORT inflateReset(z_streamp strm);
/*
This function is equivalent to inflateEnd followed by inflateInit,
but does not free and reallocate the internal decompression state. The
stream will keep attributes that may have been set by inflateInit2.
+ total_in, total_out, adler, and msg are initialized.
inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
stream state was inconsistent (such as zalloc or state being Z_NULL).
*/
-ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm,
- int windowBits));
+ZEXTERN int ZEXPORT inflateReset2(z_streamp strm,
+ int windowBits);
/*
This function is the same as inflateReset, but it also permits changing
the wrap and window size requests. The windowBits parameter is interpreted
@@ -980,9 +983,9 @@ ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm,
the windowBits parameter is invalid.
*/
-ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
- int bits,
- int value));
+ZEXTERN int ZEXPORT inflatePrime(z_streamp strm,
+ int bits,
+ int value);
/*
This function inserts bits in the inflate input stream. The intent is
that this function is used to start inflating at a bit position in the
@@ -1001,7 +1004,7 @@ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
stream state was inconsistent.
*/
-ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm));
+ZEXTERN long ZEXPORT inflateMark(z_streamp strm);
/*
This function returns two values, one in the lower 16 bits of the return
value, and the other in the remaining upper bits, obtained by shifting the
@@ -1029,8 +1032,8 @@ ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm));
source stream state was inconsistent.
*/
-ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
- gz_headerp head));
+ZEXTERN int ZEXPORT inflateGetHeader(z_streamp strm,
+ gz_headerp head);
/*
inflateGetHeader() requests that gzip header information be stored in the
provided gz_header structure. inflateGetHeader() may be called after
@@ -1070,8 +1073,8 @@ ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
*/
/*
-ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
- unsigned char FAR *window));
+ZEXTERN int ZEXPORT inflateBackInit(z_streamp strm, int windowBits,
+ unsigned char FAR *window);
Initialize the internal stream state for decompression using inflateBack()
calls. The fields zalloc, zfree and opaque in strm must be initialized
@@ -1091,13 +1094,13 @@ ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
the version of the header file.
*/
-typedef unsigned (*in_func) OF((void FAR *,
- z_const unsigned char FAR * FAR *));
-typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
+typedef unsigned (*in_func)(void FAR *,
+ z_const unsigned char FAR * FAR *);
+typedef int (*out_func)(void FAR *, unsigned char FAR *, unsigned);
-ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
- in_func in, void FAR *in_desc,
- out_func out, void FAR *out_desc));
+ZEXTERN int ZEXPORT inflateBack(z_streamp strm,
+ in_func in, void FAR *in_desc,
+ out_func out, void FAR *out_desc);
/*
inflateBack() does a raw inflate with a single call using a call-back
interface for input and output. This is potentially more efficient than
@@ -1165,7 +1168,7 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
cannot return Z_OK.
*/
-ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
+ZEXTERN int ZEXPORT inflateBackEnd(z_streamp strm);
/*
All memory allocated by inflateBackInit() is freed.
@@ -1173,7 +1176,7 @@ ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
state was inconsistent.
*/
-ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
+ZEXTERN uLong ZEXPORT zlibCompileFlags(void);
/* Return flags indicating compile-time options.
Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
@@ -1226,8 +1229,8 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
you need special options.
*/
-ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
- const Bytef *source, uLong sourceLen));
+ZEXTERN int ZEXPORT compress(Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen);
/*
Compresses the source buffer into the destination buffer. sourceLen is
the byte length of the source buffer. Upon entry, destLen is the total size
@@ -1241,9 +1244,9 @@ ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
buffer.
*/
-ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
- const Bytef *source, uLong sourceLen,
- int level));
+ZEXTERN int ZEXPORT compress2(Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen,
+ int level);
/*
Compresses the source buffer into the destination buffer. The level
parameter has the same meaning as in deflateInit. sourceLen is the byte
@@ -1257,15 +1260,15 @@ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
Z_STREAM_ERROR if the level parameter is invalid.
*/
-ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
+ZEXTERN uLong ZEXPORT compressBound(uLong sourceLen);
/*
compressBound() returns an upper bound on the compressed size after
compress() or compress2() on sourceLen bytes. It would be used before a
compress() or compress2() call to allocate the destination buffer.
*/
-ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
- const Bytef *source, uLong sourceLen));
+ZEXTERN int ZEXPORT uncompress(Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen);
/*
Decompresses the source buffer into the destination buffer. sourceLen is
the byte length of the source buffer. Upon entry, destLen is the total size
@@ -1282,8 +1285,8 @@ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
buffer with the uncompressed data up to that point.
*/
-ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen,
- const Bytef *source, uLong *sourceLen));
+ZEXTERN int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong *sourceLen);
/*
Same as uncompress, except that sourceLen is a pointer, where the
length of the source is *sourceLen. On return, *sourceLen is the number of
@@ -1302,7 +1305,7 @@ ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen,
typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */
/*
-ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
+ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode);
Open the gzip (.gz) file at path for reading and decompressing, or
compressing and writing. The mode parameter is as in fopen ("rb" or "wb")
@@ -1339,7 +1342,7 @@ ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
file could not be opened.
*/
-ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
+ZEXTERN gzFile ZEXPORT gzdopen(int fd, const char *mode);
/*
Associate a gzFile with the file descriptor fd. File descriptors are
obtained from calls like open, dup, creat, pipe or fileno (if the file has
@@ -1362,7 +1365,7 @@ ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
will not detect if fd is invalid (unless fd is -1).
*/
-ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
+ZEXTERN int ZEXPORT gzbuffer(gzFile file, unsigned size);
/*
Set the internal buffer size used by this library's functions for file to
size. The default buffer size is 8192 bytes. This function must be called
@@ -1378,7 +1381,7 @@ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
too late.
*/
-ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+ZEXTERN int ZEXPORT gzsetparams(gzFile file, int level, int strategy);
/*
Dynamically update the compression level and strategy for file. See the
description of deflateInit2 for the meaning of these parameters. Previously
@@ -1389,7 +1392,7 @@ ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
or Z_MEM_ERROR if there is a memory allocation error.
*/
-ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+ZEXTERN int ZEXPORT gzread(gzFile file, voidp buf, unsigned len);
/*
Read and decompress up to len uncompressed bytes from file into buf. If
the input file is not in gzip format, gzread copies the given number of
@@ -1419,8 +1422,8 @@ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
Z_STREAM_ERROR.
*/
-ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems,
- gzFile file));
+ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems,
+ gzFile file);
/*
Read and decompress up to nitems items of size size from file into buf,
otherwise operating as gzread() does. This duplicates the interface of
@@ -1437,22 +1440,22 @@ ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems,
In the event that the end of file is reached and only a partial item is
available at the end, i.e. the remaining uncompressed data length is not a
- multiple of size, then the final partial item is nevetheless read into buf
+ multiple of size, then the final partial item is nevertheless read into buf
and the end-of-file flag is set. The length of the partial item read is not
provided, but could be inferred from the result of gztell(). This behavior
is the same as the behavior of fread() implementations in common libraries,
but it prevents the direct use of gzfread() to read a concurrently written
- file, reseting and retrying on end-of-file, when size is not 1.
+ file, resetting and retrying on end-of-file, when size is not 1.
*/
-ZEXTERN int ZEXPORT gzwrite OF((gzFile file, voidpc buf, unsigned len));
+ZEXTERN int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len);
/*
Compress and write the len uncompressed bytes at buf to file. gzwrite
returns the number of uncompressed bytes written or 0 in case of error.
*/
-ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size,
- z_size_t nitems, gzFile file));
+ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size,
+ z_size_t nitems, gzFile file);
/*
Compress and write nitems items of size size from buf to file, duplicating
the interface of stdio's fwrite(), with size_t request and return types. If
@@ -1465,7 +1468,7 @@ ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size,
is returned, and the error state is set to Z_STREAM_ERROR.
*/
-ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...));
+ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...);
/*
Convert, format, compress, and write the arguments (...) to file under
control of the string format, as in fprintf. gzprintf returns the number of
@@ -1480,7 +1483,7 @@ ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...));
This can be determined using zlibCompileFlags().
*/
-ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s);
/*
Compress and write the given null-terminated string s to file, excluding
the terminating null character.
@@ -1488,7 +1491,7 @@ ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
gzputs returns the number of characters written, or -1 in case of error.
*/
-ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len);
/*
Read and decompress bytes from file into buf, until len-1 characters are
read, or until a newline character is read and transferred to buf, or an
@@ -1502,13 +1505,13 @@ ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
buf are indeterminate.
*/
-ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
+ZEXTERN int ZEXPORT gzputc(gzFile file, int c);
/*
Compress and write c, converted to an unsigned char, into file. gzputc
returns the value that was written, or -1 in case of error.
*/
-ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
+ZEXTERN int ZEXPORT gzgetc(gzFile file);
/*
Read and decompress one byte from file. gzgetc returns this byte or -1
in case of end of file or error. This is implemented as a macro for speed.
@@ -1517,7 +1520,7 @@ ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
points to has been clobbered or not.
*/
-ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
+ZEXTERN int ZEXPORT gzungetc(int c, gzFile file);
/*
Push c back onto the stream for file to be read as the first character on
the next read. At least one character of push-back is always allowed.
@@ -1529,7 +1532,7 @@ ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
gzseek() or gzrewind().
*/
-ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
+ZEXTERN int ZEXPORT gzflush(gzFile file, int flush);
/*
Flush all pending output to file. The parameter flush is as in the
deflate() function. The return value is the zlib error number (see function
@@ -1545,8 +1548,8 @@ ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
*/
/*
-ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
- z_off_t offset, int whence));
+ZEXTERN z_off_t ZEXPORT gzseek(gzFile file,
+ z_off_t offset, int whence);
Set the starting position to offset relative to whence for the next gzread
or gzwrite on file. The offset represents a number of bytes in the
@@ -1564,7 +1567,7 @@ ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
would be before the current position.
*/
-ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
+ZEXTERN int ZEXPORT gzrewind(gzFile file);
/*
Rewind file. This function is supported only for reading.
@@ -1572,7 +1575,7 @@ ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
*/
/*
-ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
+ZEXTERN z_off_t ZEXPORT gztell(gzFile file);
Return the starting position for the next gzread or gzwrite on file.
This position represents a number of bytes in the uncompressed data stream,
@@ -1583,7 +1586,7 @@ ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
*/
/*
-ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
+ZEXTERN z_off_t ZEXPORT gzoffset(gzFile file);
Return the current compressed (actual) read or write offset of file. This
offset includes the count of bytes that precede the gzip stream, for example
@@ -1592,7 +1595,7 @@ ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
be used for a progress indicator. On error, gzoffset() returns -1.
*/
-ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+ZEXTERN int ZEXPORT gzeof(gzFile file);
/*
Return true (1) if the end-of-file indicator for file has been set while
reading, false (0) otherwise. Note that the end-of-file indicator is set
@@ -1607,7 +1610,7 @@ ZEXTERN int ZEXPORT gzeof OF((gzFile file));
has grown since the previous end of file was detected.
*/
-ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
+ZEXTERN int ZEXPORT gzdirect(gzFile file);
/*
Return true (1) if file is being copied directly while reading, or false
(0) if file is a gzip stream being decompressed.
@@ -1628,7 +1631,7 @@ ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
gzip file reading and decompression, which may not be desired.)
*/
-ZEXTERN int ZEXPORT gzclose OF((gzFile file));
+ZEXTERN int ZEXPORT gzclose(gzFile file);
/*
Flush all pending output for file, if necessary, close file and
deallocate the (de)compression state. Note that once file is closed, you
@@ -1641,8 +1644,8 @@ ZEXTERN int ZEXPORT gzclose OF((gzFile file));
last read ended in the middle of a gzip stream, or Z_OK on success.
*/
-ZEXTERN int ZEXPORT gzclose_r OF((gzFile file));
-ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
+ZEXTERN int ZEXPORT gzclose_r(gzFile file);
+ZEXTERN int ZEXPORT gzclose_w(gzFile file);
/*
Same as gzclose(), but gzclose_r() is only for use when reading, and
gzclose_w() is only for use when writing or appending. The advantage to
@@ -1653,7 +1656,7 @@ ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
zlib library.
*/
-ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+ZEXTERN const char * ZEXPORT gzerror(gzFile file, int *errnum);
/*
Return the error message for the last error which occurred on file.
errnum is set to zlib error number. If an error occurred in the file system
@@ -1669,7 +1672,7 @@ ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
functions above that do not distinguish those cases in their return values.
*/
-ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
+ZEXTERN void ZEXPORT gzclearerr(gzFile file);
/*
Clear the error and end-of-file flags for file. This is analogous to the
clearerr() function in stdio. This is useful for continuing to read a gzip
@@ -1686,7 +1689,7 @@ ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
library.
*/
-ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+ZEXTERN uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len);
/*
Update a running Adler-32 checksum with the bytes buf[0..len-1] and
return the updated checksum. An Adler-32 value is in the range of a 32-bit
@@ -1706,15 +1709,15 @@ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
if (adler != original_adler) error();
*/
-ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf,
- z_size_t len));
+ZEXTERN uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf,
+ z_size_t len);
/*
Same as adler32(), but with a size_t length.
*/
/*
-ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
- z_off_t len2));
+ZEXTERN uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2,
+ z_off_t len2);
Combine two Adler-32 checksums into one. For two sequences of bytes, seq1
and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
@@ -1724,7 +1727,7 @@ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
negative, the result has no meaning or utility.
*/
-ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
+ZEXTERN uLong ZEXPORT crc32(uLong crc, const Bytef *buf, uInt len);
/*
Update a running CRC-32 with the bytes buf[0..len-1] and return the
updated CRC-32. A CRC-32 value is in the range of a 32-bit unsigned integer.
@@ -1742,14 +1745,14 @@ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
if (crc != original_crc) error();
*/
-ZEXTERN uLong ZEXPORT crc32_z OF((uLong crc, const Bytef *buf,
- z_size_t len));
+ZEXTERN uLong ZEXPORT crc32_z(uLong crc, const Bytef *buf,
+ z_size_t len);
/*
Same as crc32(), but with a size_t length.
*/
/*
-ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
+ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2);
Combine two CRC-32 check values into one. For two sequences of bytes,
seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
@@ -1759,13 +1762,13 @@ ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
*/
/*
-ZEXTERN uLong ZEXPORT crc32_combine_gen OF((z_off_t len2));
+ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t len2);
Return the operator corresponding to length len2, to be used with
crc32_combine_op().
*/
-ZEXTERN uLong ZEXPORT crc32_combine_op OF((uLong crc1, uLong crc2, uLong op));
+ZEXTERN uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op);
/*
Give the same result as crc32_combine(), using op in place of len2. op is
is generated from len2 by crc32_combine_gen(). This will be faster than
@@ -1778,20 +1781,20 @@ ZEXTERN uLong ZEXPORT crc32_combine_op OF((uLong crc1, uLong crc2, uLong op));
/* deflateInit and inflateInit are macros to allow checking the zlib version
* and the compiler's view of z_stream:
*/
-ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
- const char *version, int stream_size));
-ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
- const char *version, int stream_size));
-ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
- int windowBits, int memLevel,
- int strategy, const char *version,
- int stream_size));
-ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
- const char *version, int stream_size));
-ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
- unsigned char FAR *window,
- const char *version,
- int stream_size));
+ZEXTERN int ZEXPORT deflateInit_(z_streamp strm, int level,
+ const char *version, int stream_size);
+ZEXTERN int ZEXPORT inflateInit_(z_streamp strm,
+ const char *version, int stream_size);
+ZEXTERN int ZEXPORT deflateInit2_(z_streamp strm, int level, int method,
+ int windowBits, int memLevel,
+ int strategy, const char *version,
+ int stream_size);
+ZEXTERN int ZEXPORT inflateInit2_(z_streamp strm, int windowBits,
+ const char *version, int stream_size);
+ZEXTERN int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits,
+ unsigned char FAR *window,
+ const char *version,
+ int stream_size);
#ifdef Z_PREFIX_SET
# define z_deflateInit(strm, level) \
deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))
@@ -1836,7 +1839,7 @@ struct gzFile_s {
unsigned char *next;
z_off64_t pos;
};
-ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */
+ZEXTERN int ZEXPORT gzgetc_(gzFile file); /* backward compatibility */
#ifdef Z_PREFIX_SET
# undef z_gzgetc
# define z_gzgetc(g) \
@@ -1853,13 +1856,13 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */
* without large file support, _LFS64_LARGEFILE must also be true
*/
#ifdef Z_LARGE64
- ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
- ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
- ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
- ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
- ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t));
- ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t));
- ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off64_t));
+ ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *);
+ ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int);
+ ZEXTERN z_off64_t ZEXPORT gztell64(gzFile);
+ ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile);
+ ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t);
+ ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t);
+ ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t);
#endif
#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64)
@@ -1881,53 +1884,50 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */
# define crc32_combine_gen crc32_combine_gen64
# endif
# ifndef Z_LARGE64
- ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
- ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int));
- ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile));
- ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile));
- ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
- ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
- ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off_t));
+ ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *);
+ ZEXTERN z_off_t ZEXPORT gzseek64(gzFile, z_off_t, int);
+ ZEXTERN z_off_t ZEXPORT gztell64(gzFile);
+ ZEXTERN z_off_t ZEXPORT gzoffset64(gzFile);
+ ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t);
+ ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t);
+ ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t);
# endif
#else
- ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *));
- ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int));
- ZEXTERN z_off_t ZEXPORT gztell OF((gzFile));
- ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile));
- ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
- ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t));
- ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
- ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t));
- ZEXTERN uLong ZEXPORT crc32_combine_gen OF((z_off_t));
- ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off64_t));
+ ZEXTERN gzFile ZEXPORT gzopen(const char *, const char *);
+ ZEXTERN z_off_t ZEXPORT gzseek(gzFile, z_off_t, int);
+ ZEXTERN z_off_t ZEXPORT gztell(gzFile);
+ ZEXTERN z_off_t ZEXPORT gzoffset(gzFile);
+ ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t);
+ ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t);
+ ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t);
#endif
#else /* Z_SOLO */
- ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
- ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
- ZEXTERN uLong ZEXPORT crc32_combine_gen OF((z_off_t));
+ ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t);
+ ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t);
+ ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t);
#endif /* !Z_SOLO */
/* undocumented functions */
-ZEXTERN const char * ZEXPORT zError OF((int));
-ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp));
-ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void));
-ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int));
-ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int));
-ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp));
-ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp));
-ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp));
+ZEXTERN const char * ZEXPORT zError(int);
+ZEXTERN int ZEXPORT inflateSyncPoint(z_streamp);
+ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table(void);
+ZEXTERN int ZEXPORT inflateUndermine(z_streamp, int);
+ZEXTERN int ZEXPORT inflateValidate(z_streamp, int);
+ZEXTERN unsigned long ZEXPORT inflateCodesUsed(z_streamp);
+ZEXTERN int ZEXPORT inflateResetKeep(z_streamp);
+ZEXTERN int ZEXPORT deflateResetKeep(z_streamp);
#if defined(_WIN32) && !defined(Z_SOLO)
-ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path,
- const char *mode));
+ZEXTERN gzFile ZEXPORT gzopen_w(const wchar_t *path,
+ const char *mode);
#endif
#if defined(STDC) || defined(Z_HAVE_STDARG_H)
# ifndef Z_SOLO
-ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file,
- const char *format,
- va_list va));
+ZEXTERN int ZEXPORTVA gzvprintf(gzFile file,
+ const char *format,
+ va_list va);
# endif
#endif
diff --git a/deps/zlib/zutil.c b/deps/zlib/zutil.c
index dcab28a0d51..b1c5d2d3c6d 100644
--- a/deps/zlib/zutil.c
+++ b/deps/zlib/zutil.c
@@ -24,13 +24,11 @@ z_const char * const z_errmsg[10] = {
};
-const char * ZEXPORT zlibVersion()
-{
+const char * ZEXPORT zlibVersion(void) {
return ZLIB_VERSION;
}
-uLong ZEXPORT zlibCompileFlags()
-{
+uLong ZEXPORT zlibCompileFlags(void) {
uLong flags;
flags = 0;
@@ -61,9 +59,11 @@ uLong ZEXPORT zlibCompileFlags()
#ifdef ZLIB_DEBUG
flags += 1 << 8;
#endif
+ /*
#if defined(ASMV) || defined(ASMINF)
flags += 1 << 9;
#endif
+ */
#ifdef ZLIB_WINAPI
flags += 1 << 10;
#endif
@@ -119,9 +119,7 @@ uLong ZEXPORT zlibCompileFlags()
# endif
int ZLIB_INTERNAL z_verbose = verbose;
-void ZLIB_INTERNAL z_error (m)
- char *m;
-{
+void ZLIB_INTERNAL z_error(char *m) {
fprintf(stderr, "%s\n", m);
exit(1);
}
@@ -130,9 +128,7 @@ void ZLIB_INTERNAL z_error (m)
/* exported to allow conversion of error code to string for compress() and
* uncompress()
*/
-const char * ZEXPORT zError(err)
- int err;
-{
+const char * ZEXPORT zError(int err) {
return ERR_MSG(err);
}
@@ -146,22 +142,14 @@ const char * ZEXPORT zError(err)
#ifndef HAVE_MEMCPY
-void ZLIB_INTERNAL zmemcpy(dest, source, len)
- Bytef* dest;
- const Bytef* source;
- uInt len;
-{
+void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len) {
if (len == 0) return;
do {
*dest++ = *source++; /* ??? to be unrolled */
} while (--len != 0);
}
-int ZLIB_INTERNAL zmemcmp(s1, s2, len)
- const Bytef* s1;
- const Bytef* s2;
- uInt len;
-{
+int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len) {
uInt j;
for (j = 0; j < len; j++) {
@@ -170,10 +158,7 @@ int ZLIB_INTERNAL zmemcmp(s1, s2, len)
return 0;
}
-void ZLIB_INTERNAL zmemzero(dest, len)
- Bytef* dest;
- uInt len;
-{
+void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len) {
if (len == 0) return;
do {
*dest++ = 0; /* ??? to be unrolled */
@@ -214,8 +199,7 @@ local ptr_table table[MAX_PTR];
* a protected system like OS/2. Use Microsoft C instead.
*/
-voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size)
-{
+voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) {
voidpf buf;
ulg bsize = (ulg)items*size;
@@ -240,8 +224,7 @@ voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size)
return buf;
}
-void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
-{
+void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) {
int n;
(void)opaque;
@@ -277,14 +260,12 @@ void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
# define _hfree hfree
#endif
-voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size)
-{
+voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, uInt items, uInt size) {
(void)opaque;
return _halloc((long)items, size);
}
-void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
-{
+void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) {
(void)opaque;
_hfree(ptr);
}
@@ -297,25 +278,18 @@ void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
#ifndef MY_ZCALLOC /* Any system without a special alloc function */
#ifndef STDC
-extern voidp malloc OF((uInt size));
-extern voidp calloc OF((uInt items, uInt size));
-extern void free OF((voidpf ptr));
+extern voidp malloc(uInt size);
+extern voidp calloc(uInt items, uInt size);
+extern void free(voidpf ptr);
#endif
-voidpf ZLIB_INTERNAL zcalloc (opaque, items, size)
- voidpf opaque;
- unsigned items;
- unsigned size;
-{
+voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) {
(void)opaque;
return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
(voidpf)calloc(items, size);
}
-void ZLIB_INTERNAL zcfree (opaque, ptr)
- voidpf opaque;
- voidpf ptr;
-{
+void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) {
(void)opaque;
free(ptr);
}
diff --git a/deps/zlib/zutil.h b/deps/zlib/zutil.h
index d9a20ae1bf4..902a304cc2d 100644
--- a/deps/zlib/zutil.h
+++ b/deps/zlib/zutil.h
@@ -191,8 +191,9 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
/* provide prototypes for these when building zlib without LFS */
#if !defined(_WIN32) && \
(!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0)
- ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
- ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
+ ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t);
+ ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t);
+ ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t);
#endif
/* common defaults */
@@ -231,16 +232,16 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
# define zmemzero(dest, len) memset(dest, 0, len)
# endif
#else
- void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
- int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
- void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len));
+ void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len);
+ int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len);
+ void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len);
#endif
/* Diagnostic functions */
#ifdef ZLIB_DEBUG
# include
extern int ZLIB_INTERNAL z_verbose;
- extern void ZLIB_INTERNAL z_error OF((char *m));
+ extern void ZLIB_INTERNAL z_error(char *m);
# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
# define Trace(x) {if (z_verbose>=0) fprintf x ;}
# define Tracev(x) {if (z_verbose>0) fprintf x ;}
@@ -257,9 +258,9 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
#endif
#ifndef Z_SOLO
- voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items,
- unsigned size));
- void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr));
+ voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items,
+ unsigned size);
+ void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr);
#endif
#define ZALLOC(strm, items, size) \
diff --git a/docs/changelog.md b/docs/changelog.md
index 989800a11db..02bad65e17c 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -1,3 +1,733 @@
+v1.8.4
+------
+
+We erroneously shipped v1.8.3 without actually including the change
+in v1.8.2. This release re-re-introduces the pre-v1.8.0 `commit`
+constness behavior.
+
+## What's Changed
+
+### Bug fixes
+
+* Fix constness issue introduced in #6716 by @ethomson in https://github.com/libgit2/libgit2/pull/6829
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.8.3...v1.8.4
+
+v1.8.3
+------
+
+This release fixes a bug introduced in v1.8.1 for users of the legacy
+[Node.js http-parser](https://github.com/nodejs/http-parser)
+dependency.
+
+## What's Changed
+
+### Bug fixes
+
+* http: Backport on_status initialize fix for http-parser by @ethomson in https://github.com/libgit2/libgit2/pull/6931
+
+v1.8.2
+------
+
+This release reverts a const-correctness change introduced in
+v1.8.0 for the `git_commit_create` functions. We now retain the
+const-behavior for the `commits` arguments from prior to v1.8.0.
+
+This change was meant to resolve compatibility issues with bindings
+and downstream users.
+
+## What's Changed
+
+### New features
+
+* Introduce a stricter debugging allocator for testing by @ethomson in https://github.com/libgit2/libgit2/pull/6811
+
+### Bug fixes
+
+* Fix constness issue introduced in #6716 by @ethomson in https://github.com/libgit2/libgit2/pull/6829
+
+### Build and CI improvements
+
+* README: add experimental builds to ci table by @ethomson in https://github.com/libgit2/libgit2/pull/6816
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.8.1...v1.8.2
+
+v1.8.1
+------
+
+This release primarily includes straightforward bugfixes, as well as
+new functionality to have more control over the HTTP User-Agent header.
+However, there is an API change from v1.8 that was required for
+improved compatibility.
+
+In v1.8, libgit2 introduced the `report_unchanged ` member in the
+`git_fetch_options` structure. We mistakenly introduced this as a
+bitfield, which is not suitable for our public API. To correct this
+mistake, we have _removed_ the `report_unchanged ` member. To support
+the report unchanged tips option, users can set the `update_fetchhead`
+member to include the `GIT_REMOTE_UPDATE_REPORT_UNCHANGED` value.
+
+The libgit2 projects regrets the API change, but this was required to
+support cross-platform compatibility.
+
+## What's Changed
+
+### New features
+
+* Allow more control over the user-agent by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6788
+
+### Bug fixes
+
+* commit: Fix git_commit_create_from_stage without author and
+ committer by @florianpircher in
+ https://github.com/libgit2/libgit2/pull/6781
+* process.c: fix environ for macOS by @barracuda156 in
+ https://github.com/libgit2/libgit2/pull/6792
+* Bounds check for pack index read by @ConradIrwin in
+ https://github.com/libgit2/libgit2/pull/6796
+* transport: provide a useful error message during cancellation
+ by @ethomson in https://github.com/libgit2/libgit2/pull/6802
+* transport: support sha256 oids by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6803
+* Revparse: Correctly accept ref with '@' at the end by @csware in
+ https://github.com/libgit2/libgit2/pull/6809
+* remote: drop bitfields in git_remote_fetch_options by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6806
+* examples: fix memory leak in for-each-ref.c by @qaqland in
+ https://github.com/libgit2/libgit2/pull/6808
+* xdiff: use proper free function by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6810
+* rand: avoid uninitialized loadavg warnings by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6812
+* cli: include alloca on illumos / solaris / sunos by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6813
+* Update git_array allocator to obey strict aliasing rules
+ by @ethomson in https://github.com/libgit2/libgit2/pull/6814
+* tree: avoid mixed signedness comparison by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6815
+
+### Build and CI improvements
+
+* ci: update nightly workflows by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6773
+* ci: give all nightly builds a unique id by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6782
+* cmake: remove workaround that isn't compatible with Windows on
+ ARM by @hackhaslam in https://github.com/libgit2/libgit2/pull/6794
+
+### Documentation improvements
+
+* Docs meta-updates by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6787
+
+### Dependency updates
+
+* Enable llhttp for HTTP parsing by @sgallagher in
+ https://github.com/libgit2/libgit2/pull/6713
+
+## New Contributors
+
+* @florianpircher made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6781
+* @barracuda156 made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6792
+* @sgallagher made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6713
+* @ConradIrwin made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6796
+* @qaqland made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6808
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.8.0...v1.8.1
+
+v1.8
+----
+
+This is release v1.8.0, "Das Fliegende Klassenzimmer". This release
+includes optional, experimental support for invoking OpenSSH to fetch
+and push, an easier mechanism to perform the default behavior of
+`git commit`, and has many improvements for worktrees. This release
+also includes many other new features and bugfixes.
+
+## Major changes
+
+* **Executable SSH (OpenSSH) support**
+ libgit2 can now invoke the command-line OpenSSH to fetch from and push
+ to remotes over SSH. This support takes the place of libssh2 support.
+ To use it, configure libgit2 with `cmake -DUSE_SSH=exec`, and please
+ report any problems that you discover. By @ethomson in
+ https://github.com/libgit2/libgit2/pull/6617
+
+* **Simplified commit creation**
+ The `git_commit_create_from_stage` API was introduced to allow users to
+ better emulate the behavior of `git commit` without needing to provide
+ unnecessary information. The current state of the index is committed to
+ the current branch. By @ethomson in
+ https://github.com/libgit2/libgit2/pull/6716
+
+* **Worktree improvements**
+ A number of worktree improvements have been made for better
+ compatibility with core git. First, libgit2 now understands per-worktree
+ references, thanks to @csware in
+ https://github.com/libgit2/libgit2/pull/6387. Worktree-specific
+ configuration is now supported, thanks to @vermiculus in
+ https://github.com/libgit2/libgit2/pull/6202. And improved compatibility
+ with `git worktree add` is now supported, thanks to @herrerog in
+ https://github.com/libgit2/libgit2/pull/5319.
+
+## Breaking changes
+
+* **Adding `WORKTREE` configuration level** (ABI breaking change)
+ To support worktree configurations at the appropriate level (higher
+ priority than local configuration, but lower priority than app-specific
+ configuration), the `GIT_CONFIG_LEVEL_WORKTREE` level was introduced at
+ priority 6. `GIT_CONFIG_LEVEL_APP` now begins at priority 7.
+
+* **Changes to `git_config_entry`** (ABI breaking change)
+ The `git_config_entry` structure now contains information about the
+ `backend_type` and `origin_path`. The unused `payload` value has been
+ removed.
+
+* **`git_push_options` includes remote push options** (ABI breaking change)
+ The `git_push_options` structure now contains a value for remote push
+ options.
+
+## Other changes
+
+### New features
+
+* config: provide an "origin" for config entries by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6615
+* cli: add a `git config` command by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6616
+* Add OpenSSH support by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6617
+* remote: optionally report unchanged tips by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6645
+* Support setting oid type for in-memory repositories by @kcsaul in
+ https://github.com/libgit2/libgit2/pull/6671
+* cli: add `index-pack` command by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6681
+* Add `git_repository_commit_parents` to identify the parents of the next
+ commit given the repository state by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6707
+* commit: introduce `git_commit_create_from_stage` by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6716
+* set SSH timeout by @vafada in
+ https://github.com/libgit2/libgit2/pull/6721
+* Implement push options on push by @russell in
+ https://github.com/libgit2/libgit2/pull/6439
+* Support index.skipHash true config by @parnic in
+ https://github.com/libgit2/libgit2/pull/6738
+* worktree: mimic 'git worktree add' behavior. by @herrerog in
+ https://github.com/libgit2/libgit2/pull/5319
+* Support the extension for worktree-specific config by @vermiculus in
+ https://github.com/libgit2/libgit2/pull/6202
+* Separate config reader and writer backend priorities (for worktree
+ configs) by @ethomson in https://github.com/libgit2/libgit2/pull/6756
+* fetch: enable deepening/shortening shallow clones by @kempniu in
+ https://github.com/libgit2/libgit2/pull/6662
+
+### Bug fixes
+
+* repository: make cleanup safe for re-use with grafts by @carlosmn in
+ https://github.com/libgit2/libgit2/pull/6600
+* fix: Add missing include for `oidarray`. by @dvzrv in
+ https://github.com/libgit2/libgit2/pull/6608
+* ssh: fix `known_hosts` leak in `_git_ssh_setup_conn` by @steven9724 in
+ https://github.com/libgit2/libgit2/pull/6599
+* proxy: Return an error for invalid proxy URLs instead of crashing by
+ @lrm29 in https://github.com/libgit2/libgit2/pull/6597
+* errors: refactoring - never return `NULL` in `git_error_last()` by
+ @ethomson in https://github.com/libgit2/libgit2/pull/6625
+* Reject potential option injections over ssh by @carlosmn in
+ https://github.com/libgit2/libgit2/pull/6636
+* remote: fix memory leak in `git_remote_download()` by @7Ji in
+ https://github.com/libgit2/libgit2/pull/6651
+* git2: Fix crash when called w/o parameters by @csware in
+ https://github.com/libgit2/libgit2/pull/6673
+* Avoid macro redefinition of `ENABLE_INTSAFE_SIGNED_FUNCTIONS` by @csware
+ in https://github.com/libgit2/libgit2/pull/6666
+* util: suppress some uninitialized variable warnings by @boretrk in
+ https://github.com/libgit2/libgit2/pull/6659
+* push: set generic error in `push_negotiation` cb by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6675
+* process: test `/usr/bin/false` on BSDs by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6677
+* clone: don't mix up "http://url" with "http:/url" when figuring out if we
+ should do a local clone by @boretrk in
+ https://github.com/libgit2/libgit2/pull/6361
+* Several compatibility fixes by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6678
+* Git blame buffer gives the wrong result in many cases where there are
+ by @thosey in https://github.com/libgit2/libgit2/pull/6572
+* Fix 'path cannot exist in repository' during diff for in-memory repository
+ by @kcsaul in https://github.com/libgit2/libgit2/pull/6683
+* process: don't try to close the status by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6693
+* Minor bug fixes by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6695
+* Bypass shallow clone support for in-memory repositories by @kcsaul in
+ https://github.com/libgit2/libgit2/pull/6684
+* examples: use `unsigned` int for bitfields by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6699
+* Fix some bugs caught by UBscan by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6700
+* `git_diff_find_similar` doesn't always remove unmodified deltas by @yori
+ in https://github.com/libgit2/libgit2/pull/6642
+* httpclient: clear `client->parser.data` after use by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6705
+* Do not normalize `safe.directory` paths by @csware in
+ https://github.com/libgit2/libgit2/pull/6668
+* clone: don't swallow error in `should_checkout` by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6727
+* Correct index add directory/file conflict detection by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6729
+* Correct `git_revparse_single` and add revparse fuzzing by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6730
+* config: properly delete or rename section containing multivars by
+ @samueltardieu in https://github.com/libgit2/libgit2/pull/6723
+* revparse: ensure bare '@' is truly bare by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6742
+* repo: ensure we can initialize win32 paths by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6743
+* Swap `GIT_DIFF_LINE_(ADD|DEL)_EOFNL` to match other Diffs by @xphoniex in
+ https://github.com/libgit2/libgit2/pull/6240
+* diff: fix test for SHA256 support in `diff_from_buffer` by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6745
+* http: support empty http.proxy config setting by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6744
+* More `safe.directory` improvements by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6739
+* Ensure that completely ignored diff is empty by @ethomson in
+ https://github.com/libgit2/libgit2/pull/5893
+* Fix broken regexp that matches submodule names containing ".path" by
+ @csware in https://github.com/libgit2/libgit2/pull/6749
+* Fix memory leaks by @csware in
+ https://github.com/libgit2/libgit2/pull/6748
+* Make `refdb_fs` (hopefully) fully aware of per worktree refs by @csware in
+ https://github.com/libgit2/libgit2/pull/6387
+* fix log example by @albfan in https://github.com/libgit2/libgit2/pull/6359
+* fetch: fail on depth for local transport by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6757
+* Fix message trailer parsing by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6761
+* config: correct fetching the `HIGHEST_LEVEL` config by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6766
+* Avoid some API breaking changes in v1.8 by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6768
+
+### Build and CI improvements
+
+* meta: update version numbers to v1.8 by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6596
+* Revert "CMake: Search for ssh2 instead of libssh2." by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6619
+* cmake: fix openssl build on win32 by @lazka in
+ https://github.com/libgit2/libgit2/pull/6626
+* ci: retry flaky online tests by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6628
+* ci: update to macOS 12 by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6629
+* Use `#!/bin/bash` for script with bash-specific commands by @roehling in
+ https://github.com/libgit2/libgit2/pull/6581
+* ci: overwrite nonsense in `/usr/local` during macOS setup by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6664
+* release: add a compatibility label by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6676
+* actions: set permissions by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6680
+* cmake: rename FindIconv to avoid collision with cmake by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6682
+* ci: allow workflows to read and write packages by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6687
+* ci: allow workflows to push changes by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6688
+* tests: remove test for strcasecmp by @boretrk in
+ https://github.com/libgit2/libgit2/pull/6691
+* CI fixes by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6694
+* ci: improvements to prepare for Cygwin support by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6696
+* Yet more CI improvements by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6697
+* Fix nightly builds by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6709
+* Benchmarks: add a site to view results by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6715
+* `GIT_RAND_GETENTROPY`: do not include `sys/random.h` by @semarie in
+ https://github.com/libgit2/libgit2/pull/6736
+* add dl to `LIBGIT2_SYSTEM_LIBS` by @christopherfujino in
+ https://github.com/libgit2/libgit2/pull/6631
+* meta: add dependency tag to release.yml by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6740
+* CI: fix our nightlies by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6751
+* trace: Re-enable tests as tracing is now enabled by default by @lrm29 in
+ https://github.com/libgit2/libgit2/pull/6752
+* tests: don't free an unininitialized repo by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6763
+* ci: reduce ASLR randomization for TSAN by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6764
+* packbuilder: adjust nondeterministic tests by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6762
+* Allow libgit2 to be compiled with mbedtls3. by @adamharrison in
+ https://github.com/libgit2/libgit2/pull/6759
+* build: update to latest actions versions by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6765
+* ctype: cast characters to unsigned when classifying characters by
+ @boretrk in https://github.com/libgit2/libgit2/pull/6679 and
+ @ethomson in https://github.com/libgit2/libgit2/pull/6770
+* valgrind: suppress OpenSSL warnings by @ethomson in https://github.com/libgit2/libgit2/pull/6769
+
+### Documentation improvements
+
+* README.md: Fix link to conan packages by @lrm29 in
+ https://github.com/libgit2/libgit2/pull/6621
+* README: replace gmaster with GitButler by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6692
+* blame example: Fix support for line range in CLI by @wetneb in
+ https://github.com/libgit2/libgit2/pull/6638
+* Support authentication in push example by @pluehne in
+ https://github.com/libgit2/libgit2/pull/5904
+* docs: fix mistake in attr.h by @DavHau in
+ https://github.com/libgit2/libgit2/pull/6714
+* Fix broken links by @csware in
+ https://github.com/libgit2/libgit2/pull/6747
+
+### Platform compatibility fixes
+
+* stransport: macOS: replace `errSSLNetworkTimeout`, with hard-coded
+ value by @mascguy in https://github.com/libgit2/libgit2/pull/6610
+
+### Git compatibility fixes
+
+* Do not trim dots from usernames by @georgthegreat in
+ https://github.com/libgit2/libgit2/pull/6657
+* merge: fix incorrect rename detection for empty files by @herrerog in
+ https://github.com/libgit2/libgit2/pull/6717
+
+### Dependency updates
+
+* zlib: upgrade bundled zlib to v1.3 by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6698
+* ntlmclient: update to latest upstream ntlmclient by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6704
+
+## New Contributors
+
+* @dvzrv made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6608
+* @mascguy made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6610
+* @steven9724 made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6599
+* @lazka made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6626
+* @roehling made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6581
+* @7Ji made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6651
+* @kempniu made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6662
+* @thosey made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6572
+* @wetneb made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6638
+* @yori made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6642
+* @pluehne made their first contribution in
+ https://github.com/libgit2/libgit2/pull/5904
+* @DavHau made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6714
+* @vafada made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6721
+* @semarie made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6736
+* @christopherfujino made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6631
+* @parnic made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6738
+* @samueltardieu made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6723
+* @xphoniex made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6240
+* @adamharrison made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6759
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.7.0...v1.8.0
+
+v1.7
+----
+
+This is release v1.7.0, "Kleine Raupe Nimmersatt". This release adds
+shallow clone support, completes the experimental SHA256 support,
+adds Schannel support for Windows, and includes many other new
+features and bugfixes.
+
+## Major changes
+
+* **Shallow clone support**
+ libgit2 now supports shallow clone and shallow repositories, thanks
+ to a significant investment from many community members -- hundreds
+ of commits by many contributors.
+
+ * Shallow (#6396) with some fixes from review by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6557
+ * Shallow Clone Support by @lya001 in
+ https://github.com/libgit2/libgit2/pull/6396
+ * Shallow support v2 by @pks-t in
+ https://github.com/libgit2/libgit2/pull/5254
+
+* **SHA256 support**
+ libgit2 should now support SHA256 repositories using the
+ `extensions.objectFormat` configuration option when the library is
+ built with `EXPERIMENTAL_SHA256=ON`. Users are encouraged to begin
+ testing their applications with this option and provide bug reports
+ and feedback. This _is_ a breaking API change; SHA256 support will
+ be enabled by default in libgit2 v2.0.
+
+ * sha256: less hardcoded SHA1 types and lengths by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6549
+ * Support SHA256 in git_repository_wrap_odb by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6556
+
+* **Schannel and SSPI for Windows**
+ libgit2 now supports the Windows Schannel and SSPI APIs for HTTPS
+ support on Windows, when configured with `USE_HTTPS=Schannel`.
+ Setting this option will not use the existing WinHTTP support, but
+ will use libgit2's standard HTTP client stack with Windows TLS
+ primitives. Windows users are encouraged to begin testing their
+ applications with this option and provide bug reports and feedback.
+ This will be enabled by default in a future version of libgit2.
+
+ * Introduce Schannel and SSPI for Windows by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6533
+
+## Breaking changes
+
+* **Simplify custom pluggable allocator** (System API / ABI breaking change)
+ The `git_allocator` structure (configurable by the
+ `GIT_OPT_SET_ALLOCATOR` option) now only contains `gmalloc`,
+ `grealloc` and `gfree` members. This simplifies both the work needed
+ by an implementer _and_ allows more flexibility and correctness in
+ libgit2 itself, especially during out-of-memory situations and
+ errors during bootstrapping.
+
+ * tests: add allocator with limited number of bytes by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6563
+
+## Other changes
+
+### New features
+* repo: honor environment variables for more scenarios by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6544
+* Introduce timeouts on sockets by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6535
+
+### Performance improvements
+* midx: do not try to look at every object in the index by @carlosmn in
+ https://github.com/libgit2/libgit2/pull/6585
+* Partial fix for #6532: insert-by-date order. by @arroz in
+ https://github.com/libgit2/libgit2/pull/6539
+
+### Bug fixes
+* repo: don't allow repeated extensions by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6505
+* config: return `GIT_ENOTFOUND` for missing programdata by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6547
+* Fix missing oid type for "fake" repositories by @oreiche in
+ https://github.com/libgit2/libgit2/pull/6554
+* Thread-local storage: handle failure cases by @ethomson in
+ https://github.com/libgit2/libgit2/pull/5722
+* midx: allow unknown chunk ids in multi-pack index files by @carlosmn in
+ https://github.com/libgit2/libgit2/pull/6583
+* pack: cast the number of objects to size_t by @carlosmn in
+ https://github.com/libgit2/libgit2/pull/6584
+* Fixes #6344: git_branch_move now renames the reflog instead of deleting
+ by @arroz in https://github.com/libgit2/libgit2/pull/6345
+* #6576 git_diff_index_to_workdir reverse now loads untracked content by
+ @arroz in https://github.com/libgit2/libgit2/pull/6577
+
+### Build and CI improvements
+* meta: the main branch is now v1.7.0 by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6516
+* xdiff: move xdiff to 'deps' by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6482
+* util: detect all possible qsort_r and qsort_s variants by
+ @DimitryAndric in https://github.com/libgit2/libgit2/pull/6555
+* Work around -Werror problems when detecting qsort variants by
+ @DimitryAndric in https://github.com/libgit2/libgit2/pull/6558
+* actions: simplify execution with composite action by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6488
+* CMake: Search for ssh2 instead of libssh2. by @Faless in
+ https://github.com/libgit2/libgit2/pull/6586
+
+### Documentation improvements
+* docs: fix IRC server from freenode to libera by @vincenzopalazzo in
+ https://github.com/libgit2/libgit2/pull/6590
+
+### Dependency upgrades
+* Update xdiff to git 2.40.1's version by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6561
+* deps: update pcre to 8.45 by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6593
+
+## New Contributors
+* @oreiche made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6554
+* @DimitryAndric made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6555
+* @vincenzopalazzo made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6590
+* @Faless made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6586
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.6.3...v1.7.0
+
+v1.6.3
+------
+
+## What's Changed
+
+### Bug fixes
+
+* odb: restore `git_odb_open` by @ethomson in https://github.com/libgit2/libgit2/pull/6520
+* Ensure that `git_index_add_all` handles ignored directories by @ethomson in https://github.com/libgit2/libgit2/pull/6521
+* pack: use 64 bits for the number of objects by @carlosmn in https://github.com/libgit2/libgit2/pull/6530
+
+### Build and CI improvements
+
+* Remove unused wditer variable by @georgthegreat in https://github.com/libgit2/libgit2/pull/6518
+* fs_path: let root run the ownership tests by @ethomson in https://github.com/libgit2/libgit2/pull/6513
+* sysdir: Do not declare win32 functions on non-win32 platforms by @Batchyx in https://github.com/libgit2/libgit2/pull/6527
+* cmake: don't include `include/git2` by @ethomson in https://github.com/libgit2/libgit2/pull/6529
+
+## New Contributors
+* @georgthegreat made their first contribution in https://github.com/libgit2/libgit2/pull/6518
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.6.2...v1.6.3
+
+v1.6.2
+------
+
+## What's Changed
+### Bug fixes
+
+* remote: always populate old id in update tips by @ethomson in https://github.com/libgit2/libgit2/pull/6506
+ The update tips callback would not always be properly provided with an empty (`0000000...`) OID for new refs.
+
+* Revert #6503 by @ethomson in https://github.com/libgit2/libgit2/pull/6511
+ The certificate callback added port information for callbacks in #6503, but the format was ambiguous with IPv6 addresses. Revert this change temporarily.
+
+* Add `git_odb_backend_loose` back by @ethomson in https://github.com/libgit2/libgit2/pull/6512
+ During SHA256 refactoring, the `git_odb_backend_loose` API was accidentally removed. Add it back.
+
+* meta: configure pkg-config .pc correctly by @ethomson in https://github.com/libgit2/libgit2/pull/6514
+ During SHA256 refactoring, the pkg-config `.pc` file was erroneously renamed to `git2` instead of `libgit2`. Repair this.
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.6.1...v1.6.2
+
+v1.6
+----
+
+This is release v1.6.1, "Hubbeliges Krokodil". This release adds experimental SHA256 support and includes many new features and bugfixes. This release replaces libgit2 v1.6.0, which did not correctly update its version number(s).
+
+## What's Changed
+
+### New features
+
+* **Support for bare repositories with SHA256 support (experimental)** by @ethomson in https://github.com/libgit2/libgit2/pull/6191
+ You can configure experimental SHA256 support in libgit2 with `cmake -DEXPERIMENTAL_SHA256=ON` during project setup. This is useful for considering future integrations, work on clients, and work on language bindings. At present, working with bare repositories should largely work, including remote operations. But many pieces of functionality - including working with the index - are not yet supported. As a result, **libgit2 with SHA256 support should not be used in production or released with package distribution.**
+
+* **Support the notion of a home directory separately from global configuration directory** by @ethomson in https://github.com/libgit2/libgit2/pull/6455 and https://github.com/libgit2/libgit2/pull/6456
+ Callers and language bindings can now configure the home directory that libgit2 uses for file lookups (eg, the `.ssh` directory). This configuration is separate from the git global configuration path.
+
+* **stash: partial stash specific files** by @gitkraken-jacobw in https://github.com/libgit2/libgit2/pull/6330
+ A stash can be created with only specific files, using a pathspec. This is similar to the `git stash push` command.
+
+* **push: revparse refspec source, so you can push things that are not refs** by @sven-of-cord in https://github.com/libgit2/libgit2/pull/6362
+ Pushes can be performed using refspecs instead of only references.
+
+* **Support OpenSSL3** by @ethomson in https://github.com/libgit2/libgit2/pull/6464 and https://github.com/libgit2/libgit2/pull/6471
+ OpenSSL 3 is now supported, both when compiled directly and dynamically loaded.
+
+### Bug fixes
+* winhttp: support long custom headers by @kcsaul in https://github.com/libgit2/libgit2/pull/6363
+* Fix memory leak by @csware in https://github.com/libgit2/libgit2/pull/6382
+* Don't fail the whole clone if you can't find a default branch by @torvalds in https://github.com/libgit2/libgit2/pull/6369
+* #6366: When a worktree is missing, return `GIT_ENOTFOUND`. by @arroz in https://github.com/libgit2/libgit2/pull/6395
+* commit-graph: only verify csum on `git_commit_graph_open()`. by @derrickstolee in https://github.com/libgit2/libgit2/pull/6420
+* Ignore missing 'safe.directory' config during ownership checks by @kcsaul in https://github.com/libgit2/libgit2/pull/6408
+* Fix leak in `git_tag_create_from_buffer` by @julianmesa-gitkraken in https://github.com/libgit2/libgit2/pull/6421
+* http: Update httpclient options when reusing an existing connection. by @slackner in https://github.com/libgit2/libgit2/pull/6416
+* Add support for `safe.directory *` by @csware in https://github.com/libgit2/libgit2/pull/6429
+* URL parsing for google-compatible URLs by @ethomson in https://github.com/libgit2/libgit2/pull/6326
+* Fixes #6433: `git_submodule_update` fails to update configured but missing submodule by @tagesuhu in https://github.com/libgit2/libgit2/pull/6434
+* transport: fix capabilities calculation by @russell in https://github.com/libgit2/libgit2/pull/6435
+* push: use resolved oid as the source by @ethomson in https://github.com/libgit2/libgit2/pull/6452
+* Use `git_clone__submodule` to avoid file checks in workdir by @abizjak in https://github.com/libgit2/libgit2/pull/6444
+* #6422: handle dangling symbolic refs gracefully by @arroz in https://github.com/libgit2/libgit2/pull/6423
+* `diff_file`: Fix crash when freeing a patch representing an empty untracked file by @jorio in https://github.com/libgit2/libgit2/pull/6475
+* clone: clean up options on failure by @ethomson in https://github.com/libgit2/libgit2/pull/6479
+* stash: update strarray usage by @ethomson in https://github.com/libgit2/libgit2/pull/6487
+* #6491: Sets `oid_type` on repos open with `git_repository_open_bare` by @arroz in https://github.com/libgit2/libgit2/pull/6492
+* Handle Win32 shares by @ethomson in https://github.com/libgit2/libgit2/pull/6493
+* Make failure to connect to ssh-agent non-fatal by @fxcoudert in https://github.com/libgit2/libgit2/pull/6497
+* odb: don't unconditionally add `oid_type` to stream by @ethomson in https://github.com/libgit2/libgit2/pull/6499
+* Pass hostkey & port to host verify callback by @fxcoudert in https://github.com/libgit2/libgit2/pull/6503
+
+### Code cleanups
+* meta: update version number to v1.6.0-alpha by @ethomson in https://github.com/libgit2/libgit2/pull/6352
+* sha256: indirection for experimental functions by @ethomson in https://github.com/libgit2/libgit2/pull/6354
+* Delete `create.c.bak` by @lrm29 in https://github.com/libgit2/libgit2/pull/6398
+* Support non-cmake builds with an in-tree `experimental.h` by @ethomson in https://github.com/libgit2/libgit2/pull/6405
+
+### Build and CI improvements
+* tests: skip flaky-ass googlesource tests by @ethomson in https://github.com/libgit2/libgit2/pull/6353
+* clar: remove ftrunacte from libgit2 tests by @boretrk in https://github.com/libgit2/libgit2/pull/6357
+* CI Improvements by @ethomson in https://github.com/libgit2/libgit2/pull/6403
+* fix compile on Windows with `-DWIN32_LEAN_AND_MEAN` by @christoph-cullmann in https://github.com/libgit2/libgit2/pull/6373
+* Fixes #6365 : Uppercase windows.h include fails build in case-sensitive OS by @Vinz2008 in https://github.com/libgit2/libgit2/pull/6377
+* ci: update version numbers of actions by @ethomson in https://github.com/libgit2/libgit2/pull/6448
+* thread: avoid warnings when building without threads by @ethomson in https://github.com/libgit2/libgit2/pull/6432
+* src: hide unused hmac() prototype by @0-wiz-0 in https://github.com/libgit2/libgit2/pull/6458
+* tests: update clar test runner by @ethomson in https://github.com/libgit2/libgit2/pull/6459
+* ci: always create test summaries, even on failure by @ethomson in https://github.com/libgit2/libgit2/pull/6460
+* Fix build failure with `-DEMBED_SSH_PATH` by @vicr123 in https://github.com/libgit2/libgit2/pull/6374
+* Define correct `off64_t` for AIX by @bzEq in https://github.com/libgit2/libgit2/pull/6376
+* Fix some warnings in main by @ethomson in https://github.com/libgit2/libgit2/pull/6480
+* strarray: remove deprecated declaration by @ethomson in https://github.com/libgit2/libgit2/pull/6486
+* tests: always unset `HTTP_PROXY` before starting tests by @ethomson in https://github.com/libgit2/libgit2/pull/6498
+
+### Documentation improvements
+* add 2-clause BSD license to COPYING by @martinvonz in https://github.com/libgit2/libgit2/pull/6413
+* Add new PHP bindings project to language bindings section of README.md by @RogerGee in https://github.com/libgit2/libgit2/pull/6473
+* README: clarify the linking exception by @ethomson in https://github.com/libgit2/libgit2/pull/6494
+* Correct the definition of "empty" in the docs for `git_repository_is_empty` by @timrogers in https://github.com/libgit2/libgit2/pull/6500
+
+## New Contributors
+* @christoph-cullmann made their first contribution in https://github.com/libgit2/libgit2/pull/6373
+* @Vinz2008 made their first contribution in https://github.com/libgit2/libgit2/pull/6377
+* @torvalds made their first contribution in https://github.com/libgit2/libgit2/pull/6369
+* @derrickstolee made their first contribution in https://github.com/libgit2/libgit2/pull/6420
+* @julianmesa-gitkraken made their first contribution in https://github.com/libgit2/libgit2/pull/6421
+* @slackner made their first contribution in https://github.com/libgit2/libgit2/pull/6416
+* @martinvonz made their first contribution in https://github.com/libgit2/libgit2/pull/6413
+* @tagesuhu made their first contribution in https://github.com/libgit2/libgit2/pull/6434
+* @russell made their first contribution in https://github.com/libgit2/libgit2/pull/6435
+* @sven-of-cord made their first contribution in https://github.com/libgit2/libgit2/pull/6362
+* @0-wiz-0 made their first contribution in https://github.com/libgit2/libgit2/pull/6458
+* @abizjak made their first contribution in https://github.com/libgit2/libgit2/pull/6444
+* @vicr123 made their first contribution in https://github.com/libgit2/libgit2/pull/6374
+* @bzEq made their first contribution in https://github.com/libgit2/libgit2/pull/6376
+* @gitkraken-jacobw made their first contribution in https://github.com/libgit2/libgit2/pull/6330
+* @fxcoudert made their first contribution in https://github.com/libgit2/libgit2/pull/6497
+* @timrogers made their first contribution in https://github.com/libgit2/libgit2/pull/6500
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.5.0...v1.6.0
+
v1.5
----
diff --git a/docs/contributing.md b/docs/contributing.md
index 03e0017093b..382d9555ef5 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -24,9 +24,8 @@ by the following licenses:
## Discussion & Chat
-We hang out in the
-[`#libgit2`](http://webchat.freenode.net/?channels=#libgit2)) channel on
-irc.freenode.net.
+We hang out in the [#libgit2](https://web.libera.chat/#libgit2) channel on
+[libera](https://libera.chat).
Also, feel free to open an
[Issue](https://github.com/libgit2/libgit2/issues/new) to start a discussion
diff --git a/examples/README.md b/examples/README.md
index 769c4b2678a..0f1f253877a 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -15,8 +15,8 @@ so there are no restrictions on their use.
For annotated HTML versions, see the "Examples" section of:
- http://libgit2.github.com/libgit2
+ https://libgit2.org/libgit2
such as:
- http://libgit2.github.com/libgit2/ex/HEAD/general.html
+ https://libgit2.org/libgit2/ex/HEAD/general.html
diff --git a/examples/args.h b/examples/args.h
index d626f98c8a7..4db0493bb2d 100644
--- a/examples/args.h
+++ b/examples/args.h
@@ -8,7 +8,7 @@ struct args_info {
int argc;
char **argv;
int pos;
- int opts_done : 1; /**< Did we see a -- separator */
+ unsigned int opts_done : 1; /**< Did we see a -- separator */
};
#define ARGS_INFO_INIT { argc, argv, 0, 0 }
#define ARGS_CURRENT(args) args->argv[args->pos]
diff --git a/examples/blame.c b/examples/blame.c
index 77087a5771a..0996e7a1fdb 100644
--- a/examples/blame.c
+++ b/examples/blame.c
@@ -47,6 +47,10 @@ int lg2_blame(git_repository *repo, int argc, char *argv[])
if (o.M) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
if (o.C) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
if (o.F) blameopts.flags |= GIT_BLAME_FIRST_PARENT;
+ if (o.start_line && o.end_line) {
+ blameopts.min_line = o.start_line;
+ blameopts.max_line = o.end_line;
+ }
/**
* The commit range comes in "committish" form. Use the rev-parse API to
diff --git a/examples/checkout.c b/examples/checkout.c
index ac7b7422d7b..82567cdc432 100644
--- a/examples/checkout.c
+++ b/examples/checkout.c
@@ -35,9 +35,9 @@
*/
typedef struct {
- int force : 1;
- int progress : 1;
- int perf : 1;
+ unsigned int force : 1;
+ unsigned int progress : 1;
+ unsigned int perf : 1;
} checkout_options;
static void print_usage(void)
diff --git a/examples/diff.c b/examples/diff.c
index a9fb5d4428a..80c5200e908 100644
--- a/examples/diff.c
+++ b/examples/diff.c
@@ -188,9 +188,17 @@ static void compute_diff_no_index(git_diff **diff, struct diff_options *o) {
check_lg2(
git_patch_to_buf(&buf, patch),
"patch to buf", NULL);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ check_lg2(
+ git_diff_from_buffer(diff, buf.ptr, buf.size, NULL),
+ "diff from patch", NULL);
+#else
check_lg2(
git_diff_from_buffer(diff, buf.ptr, buf.size),
"diff from patch", NULL);
+#endif
+
git_patch_free(patch);
git_buf_dispose(&buf);
free(file1_str);
diff --git a/examples/for-each-ref.c b/examples/for-each-ref.c
index a0706741a3f..f745bc38cb9 100644
--- a/examples/for-each-ref.c
+++ b/examples/for-each-ref.c
@@ -25,6 +25,8 @@ static int show_ref(git_reference *ref, void *data)
git_object_type2string(git_object_type(obj)),
git_reference_name(ref));
+ git_object_free(obj);
+ git_reference_free(ref);
if (resolved)
git_reference_free(resolved);
return 0;
diff --git a/examples/general.c b/examples/general.c
index 7f44cd78680..0275f84a24e 100644
--- a/examples/general.c
+++ b/examples/general.c
@@ -31,8 +31,8 @@
* Git Internals that you will need to know to work with Git at this level,
* check out [Chapter 10][pg] of the Pro Git book.
*
- * [lg]: http://libgit2.github.com
- * [ap]: http://libgit2.github.com/libgit2
+ * [lg]: https://libgit2.org
+ * [ap]: https://libgit2.org/libgit2
* [pg]: https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain
*/
@@ -97,7 +97,7 @@ int lg2_general(git_repository *repo, int argc, char** argv)
*
* (Try running this program against tests/resources/testrepo.git.)
*
- * [me]: http://libgit2.github.com/libgit2/#HEAD/group/repository
+ * [me]: https://libgit2.org/libgit2/#HEAD/group/repository
*/
repo_path = (argc > 1) ? argv[1] : "/opt/libgit2-test/.git";
@@ -173,7 +173,7 @@ static void oid_parsing(git_oid *oid)
* working with raw objects, we'll need to get this structure from the
* repository.
*
- * [odb]: http://libgit2.github.com/libgit2/#HEAD/group/odb
+ * [odb]: https://libgit2.org/libgit2/#HEAD/group/odb
*/
static void object_database(git_repository *repo, git_oid *oid)
{
@@ -262,7 +262,7 @@ static void object_database(git_repository *repo, git_oid *oid)
* of them here. You can read about the other ones in the [commit API
* docs][cd].
*
- * [cd]: http://libgit2.github.com/libgit2/#HEAD/group/commit
+ * [cd]: https://libgit2.org/libgit2/#HEAD/group/commit
*/
static void commit_writing(git_repository *repo)
{
@@ -347,7 +347,7 @@ static void commit_writing(git_repository *repo)
* data in the commit - the author (name, email, datetime), committer
* (same), tree, message, encoding and parent(s).
*
- * [pco]: http://libgit2.github.com/libgit2/#HEAD/group/commit
+ * [pco]: https://libgit2.org/libgit2/#HEAD/group/commit
*/
static void commit_parsing(git_repository *repo)
{
@@ -418,7 +418,7 @@ static void commit_parsing(git_repository *repo)
* functions very similarly to the commit lookup, parsing and creation
* methods, since the objects themselves are very similar.
*
- * [tm]: http://libgit2.github.com/libgit2/#HEAD/group/tag
+ * [tm]: https://libgit2.org/libgit2/#HEAD/group/tag
*/
static void tag_parsing(git_repository *repo)
{
@@ -472,7 +472,7 @@ static void tag_parsing(git_repository *repo)
* object type in Git, but a useful structure for parsing and traversing
* tree entries.
*
- * [tp]: http://libgit2.github.com/libgit2/#HEAD/group/tree
+ * [tp]: https://libgit2.org/libgit2/#HEAD/group/tree
*/
static void tree_parsing(git_repository *repo)
{
@@ -536,7 +536,7 @@ static void tree_parsing(git_repository *repo)
* from disk and writing it to the db and getting the oid back so you
* don't have to do all those steps yourself.
*
- * [ba]: http://libgit2.github.com/libgit2/#HEAD/group/blob
+ * [ba]: https://libgit2.org/libgit2/#HEAD/group/blob
*/
static void blob_parsing(git_repository *repo)
{
@@ -578,7 +578,7 @@ static void blob_parsing(git_repository *repo)
* that were ancestors of (reachable from) a given starting point. This
* can allow you to create `git log` type functionality.
*
- * [rw]: http://libgit2.github.com/libgit2/#HEAD/group/revwalk
+ * [rw]: https://libgit2.org/libgit2/#HEAD/group/revwalk
*/
static void revwalking(git_repository *repo)
{
@@ -643,7 +643,7 @@ static void revwalking(git_repository *repo)
* The [index file API][gi] allows you to read, traverse, update and write
* the Git index file (sometimes thought of as the staging area).
*
- * [gi]: http://libgit2.github.com/libgit2/#HEAD/group/index
+ * [gi]: https://libgit2.org/libgit2/#HEAD/group/index
*/
static void index_walking(git_repository *repo)
{
@@ -687,7 +687,7 @@ static void index_walking(git_repository *repo)
* references such as branches, tags and remote references (everything in
* the .git/refs directory).
*
- * [ref]: http://libgit2.github.com/libgit2/#HEAD/group/reference
+ * [ref]: https://libgit2.org/libgit2/#HEAD/group/reference
*/
static void reference_listing(git_repository *repo)
{
@@ -740,7 +740,7 @@ static void reference_listing(git_repository *repo)
* The [config API][config] allows you to list and update config values
* in any of the accessible config file locations (system, global, local).
*
- * [config]: http://libgit2.github.com/libgit2/#HEAD/group/config
+ * [config]: https://libgit2.org/libgit2/#HEAD/group/config
*/
static void config_files(const char *repo_path, git_repository* repo)
{
diff --git a/examples/index-pack.c b/examples/index-pack.c
index df37dd0c45e..0f8234c7591 100644
--- a/examples/index-pack.c
+++ b/examples/index-pack.c
@@ -28,11 +28,18 @@ int lg2_index_pack(git_repository *repo, int argc, char **argv)
return EXIT_FAILURE;
}
- if (git_indexer_new(&idx, ".", 0, NULL, NULL) < 0) {
+#ifdef GIT_EXPERIMENTAL_SHA256
+ error = git_indexer_new(&idx, ".", git_repository_oid_type(repo), NULL);
+#else
+ error = git_indexer_new(&idx, ".", 0, NULL, NULL);
+#endif
+
+ if (error < 0) {
puts("bad idx");
return -1;
}
+
if ((fd = open(argv[1], 0)) < 0) {
perror("open");
return -1;
diff --git a/examples/log.c b/examples/log.c
index 4b0a95dcd0d..62a6eb5858f 100644
--- a/examples/log.c
+++ b/examples/log.c
@@ -50,6 +50,7 @@ static int add_revision(struct log_state *s, const char *revstr);
/** log_options holds other command line options that affect log output */
struct log_options {
int show_diff;
+ int show_oneline;
int show_log_size;
int skip, limit;
int min_parents, max_parents;
@@ -81,9 +82,11 @@ int lg2_log(git_repository *repo, int argc, char *argv[])
git_commit *commit = NULL;
git_pathspec *ps = NULL;
+ memset(&s, 0, sizeof(s));
+
/** Parse arguments and set up revwalker. */
- last_arg = parse_options(&s, &opt, argc, argv);
s.repo = repo;
+ last_arg = parse_options(&s, &opt, argc, argv);
diffopts.pathspec.strings = &argv[last_arg];
diffopts.pathspec.count = argc - last_arg;
@@ -335,34 +338,45 @@ static void print_commit(git_commit *commit, struct log_options *opts)
const char *scan, *eol;
git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
- printf("commit %s\n", buf);
- if (opts->show_log_size) {
- printf("log size %d\n", (int)strlen(git_commit_message(commit)));
- }
+ if (opts->show_oneline) {
+ printf("%s ", buf);
+ } else {
+ printf("commit %s\n", buf);
- if ((count = (int)git_commit_parentcount(commit)) > 1) {
- printf("Merge:");
- for (i = 0; i < count; ++i) {
- git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
- printf(" %s", buf);
+ if (opts->show_log_size) {
+ printf("log size %d\n", (int)strlen(git_commit_message(commit)));
+ }
+
+ if ((count = (int)git_commit_parentcount(commit)) > 1) {
+ printf("Merge:");
+ for (i = 0; i < count; ++i) {
+ git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
+ printf(" %s", buf);
+ }
+ printf("\n");
}
- printf("\n");
- }
- if ((sig = git_commit_author(commit)) != NULL) {
- printf("Author: %s <%s>\n", sig->name, sig->email);
- print_time(&sig->when, "Date: ");
+ if ((sig = git_commit_author(commit)) != NULL) {
+ printf("Author: %s <%s>\n", sig->name, sig->email);
+ print_time(&sig->when, "Date: ");
+ }
+ printf("\n");
}
- printf("\n");
for (scan = git_commit_message(commit); scan && *scan; ) {
for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */;
- printf(" %.*s\n", (int)(eol - scan), scan);
+ if (opts->show_oneline)
+ printf("%.*s\n", (int)(eol - scan), scan);
+ else
+ printf(" %.*s\n", (int)(eol - scan), scan);
scan = *eol ? eol + 1 : NULL;
+ if (opts->show_oneline)
+ break;
}
- printf("\n");
+ if (!opts->show_oneline)
+ printf("\n");
}
/** Helper to find how many files in a commit changed from its nth parent. */
@@ -407,8 +421,6 @@ static int parse_options(
struct log_state *s, struct log_options *opt, int argc, char **argv)
{
struct args_info args = ARGS_INFO_INIT;
-
- memset(s, 0, sizeof(*s));
s->sorting = GIT_SORT_TIME;
memset(opt, 0, sizeof(*opt));
@@ -424,7 +436,7 @@ static int parse_options(
else
/** Try failed revision parse as filename. */
break;
- } else if (!match_arg_separator(&args)) {
+ } else if (match_arg_separator(&args)) {
break;
}
else if (!strcmp(a, "--date-order"))
@@ -474,6 +486,8 @@ static int parse_options(
opt->show_diff = 1;
else if (!strcmp(a, "--log-size"))
opt->show_log_size = 1;
+ else if (!strcmp(a, "--oneline"))
+ opt->show_oneline = 1;
else
usage("Unsupported argument", a);
}
diff --git a/examples/merge.c b/examples/merge.c
index 460c06a2567..7a76912cd24 100644
--- a/examples/merge.c
+++ b/examples/merge.c
@@ -30,7 +30,7 @@ struct merge_options {
git_annotated_commit **annotated;
size_t annotated_count;
- int no_commit : 1;
+ unsigned int no_commit : 1;
};
static void print_usage(void)
diff --git a/examples/push.c b/examples/push.c
index bcf307607b0..5113eed394b 100644
--- a/examples/push.c
+++ b/examples/push.c
@@ -32,6 +32,7 @@
/** Entry point for this command */
int lg2_push(git_repository *repo, int argc, char **argv) {
git_push_options options;
+ git_remote_callbacks callbacks;
git_remote* remote = NULL;
char *refspec = "refs/heads/master";
const git_strarray refspecs = {
@@ -47,7 +48,11 @@ int lg2_push(git_repository *repo, int argc, char **argv) {
check_lg2(git_remote_lookup(&remote, repo, "origin" ), "Unable to lookup remote", NULL);
+ check_lg2(git_remote_init_callbacks(&callbacks, GIT_REMOTE_CALLBACKS_VERSION), "Error initializing remote callbacks", NULL);
+ callbacks.credentials = cred_acquire_cb;
+
check_lg2(git_push_options_init(&options, GIT_PUSH_OPTIONS_VERSION ), "Error initializing push", NULL);
+ options.callbacks = callbacks;
check_lg2(git_remote_push(remote, &refspecs, &options), "Error pushing", NULL);
diff --git a/examples/show-index.c b/examples/show-index.c
index fb797e04beb..0a5e7d1a2e4 100644
--- a/examples/show-index.c
+++ b/examples/show-index.c
@@ -30,7 +30,11 @@ int lg2_show_index(git_repository *repo, int argc, char **argv)
dirlen = strlen(dir);
if (dirlen > 5 && strcmp(dir + dirlen - 5, "index") == 0) {
+#ifdef GIT_EXPERIMENTAL_SHA256
+ check_lg2(git_index_open(&index, dir, GIT_OID_SHA1), "could not open index", dir);
+#else
check_lg2(git_index_open(&index, dir), "could not open index", dir);
+#endif
} else {
check_lg2(git_repository_open_ext(&repo, dir, 0, NULL), "could not open repository", dir);
check_lg2(git_repository_index(&index, repo), "could not open repository index", NULL);
diff --git a/fuzzers/CMakeLists.txt b/fuzzers/CMakeLists.txt
index a2c19ed408a..01f0f51997f 100644
--- a/fuzzers/CMakeLists.txt
+++ b/fuzzers/CMakeLists.txt
@@ -12,10 +12,13 @@ foreach(fuzz_target_src ${SRC_FUZZERS})
string(REPLACE ".c" "" fuzz_target_name ${fuzz_target_src})
string(REPLACE "_fuzzer" "" fuzz_name ${fuzz_target_name})
- set(${fuzz_target_name}_SOURCES ${fuzz_target_src} ${LIBGIT2_OBJECTS})
+ set(${fuzz_target_name}_SOURCES
+ ${fuzz_target_src} "fuzzer_utils.c" ${LIBGIT2_OBJECTS})
+
if(USE_STANDALONE_FUZZERS)
list(APPEND ${fuzz_target_name}_SOURCES "standalone_driver.c")
endif()
+
add_executable(${fuzz_target_name} ${${fuzz_target_name}_SOURCES})
set_target_properties(${fuzz_target_name} PROPERTIES C_STANDARD 90)
diff --git a/fuzzers/config_file_fuzzer.c b/fuzzers/config_file_fuzzer.c
index 890adbfc528..763036960b4 100644
--- a/fuzzers/config_file_fuzzer.c
+++ b/fuzzers/config_file_fuzzer.c
@@ -43,7 +43,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
goto out;
}
- if ((err = git_config_backend_from_string(&backend, (const char*)data, size)) != 0) {
+ if ((err = git_config_backend_from_string(&backend, (const char*)data, size, NULL)) != 0) {
goto out;
}
if ((err = git_config_add_backend(cfg, backend, 0, NULL, 0)) != 0) {
diff --git a/fuzzers/corpora/revparse/head b/fuzzers/corpora/revparse/head
new file mode 100644
index 00000000000..e5517e4c5b4
--- /dev/null
+++ b/fuzzers/corpora/revparse/head
@@ -0,0 +1 @@
+HEAD
\ No newline at end of file
diff --git a/fuzzers/corpora/revparse/revat b/fuzzers/corpora/revparse/revat
new file mode 100644
index 00000000000..382ffc0ba32
--- /dev/null
+++ b/fuzzers/corpora/revparse/revat
@@ -0,0 +1 @@
+xxxxxxxxxxxxxxxx@
\ No newline at end of file
diff --git a/fuzzers/download_refs_fuzzer.c b/fuzzers/download_refs_fuzzer.c
index ff95cd107ef..c2b80ccb1f2 100644
--- a/fuzzers/download_refs_fuzzer.c
+++ b/fuzzers/download_refs_fuzzer.c
@@ -16,6 +16,7 @@
#include "futils.h"
#include "standalone_driver.h"
+#include "fuzzer_utils.h"
#define UNUSED(x) (void)(x)
@@ -157,33 +158,10 @@ static int fuzzer_transport_cb(git_transport **out, git_remote *owner, void *par
return git_transport_smart(out, owner, &def);
}
-static void fuzzer_git_abort(const char *op)
-{
- const git_error *err = git_error_last();
- fprintf(stderr, "unexpected libgit error: %s: %s\n",
- op, err ? err->message : "");
- abort();
-}
-
int LLVMFuzzerInitialize(int *argc, char ***argv)
{
-#if defined(_WIN32)
- char tmpdir[MAX_PATH], path[MAX_PATH];
-
- if (GetTempPath((DWORD)sizeof(tmpdir), tmpdir) == 0)
- abort();
-
- if (GetTempFileName(tmpdir, "lg2", 1, path) == 0)
- abort();
-
- if (git_futils_mkdir(path, 0700, 0) < 0)
- abort();
-#else
- char path[] = "/tmp/git2.XXXXXX";
-
- if (mkdtemp(path) != path)
- abort();
-#endif
+ UNUSED(argc);
+ UNUSED(argv);
if (git_libgit2_init() < 0)
abort();
@@ -191,12 +169,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv)
if (git_libgit2_opts(GIT_OPT_SET_PACK_MAX_OBJECTS, 10000000) < 0)
abort();
- UNUSED(argc);
- UNUSED(argv);
-
- if (git_repository_init(&repo, path, 1) < 0)
- fuzzer_git_abort("git_repository_init");
-
+ repo = fuzzer_repo_init();
return 0;
}
diff --git a/fuzzers/fuzzer_utils.c b/fuzzers/fuzzer_utils.c
new file mode 100644
index 00000000000..cde5065da03
--- /dev/null
+++ b/fuzzers/fuzzer_utils.c
@@ -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.
+ */
+
+#include
+#include
+#include
+
+#include "git2.h"
+#include "futils.h"
+
+#include "fuzzer_utils.h"
+
+void fuzzer_git_abort(const char *op)
+{
+ const git_error *err = git_error_last();
+ fprintf(stderr, "unexpected libgit error: %s: %s\n",
+ op, err ? err->message : "");
+ abort();
+}
+
+git_repository *fuzzer_repo_init(void)
+{
+ git_repository *repo;
+
+#if defined(_WIN32)
+ char tmpdir[MAX_PATH], path[MAX_PATH];
+
+ if (GetTempPath((DWORD)sizeof(tmpdir), tmpdir) == 0)
+ abort();
+
+ if (GetTempFileName(tmpdir, "lg2", 1, path) == 0)
+ abort();
+
+ if (git_futils_mkdir(path, 0700, 0) < 0)
+ abort();
+#else
+ char path[] = "/tmp/git2.XXXXXX";
+
+ if (mkdtemp(path) != path)
+ abort();
+#endif
+
+ if (git_repository_init(&repo, path, 1) < 0)
+ fuzzer_git_abort("git_repository_init");
+
+ return repo;
+}
diff --git a/src/libgit2/transports/ssh.h b/fuzzers/fuzzer_utils.h
similarity index 58%
rename from src/libgit2/transports/ssh.h
rename to fuzzers/fuzzer_utils.h
index d3e741f1d9e..6b67c9a611d 100644
--- a/src/libgit2/transports/ssh.h
+++ b/fuzzers/fuzzer_utils.h
@@ -4,11 +4,11 @@
* 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_transports_ssh_h__
-#define INCLUDE_transports_ssh_h__
-#include "common.h"
+#ifndef INCLUDE_fuzzer_utils_h__
+#define INCLUDE_fuzzer_utils_h__
-int git_transport_ssh_global_init(void);
+extern void fuzzer_git_abort(const char *op);
+extern git_repository *fuzzer_repo_init(void);
#endif
diff --git a/fuzzers/objects_fuzzer.c b/fuzzers/objects_fuzzer.c
index 51b4a1ed415..7294e9b35aa 100644
--- a/fuzzers/objects_fuzzer.c
+++ b/fuzzers/objects_fuzzer.c
@@ -39,7 +39,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
* to do.
*/
for (i = 0; i < ARRAY_SIZE(types); i++) {
- if (git_object__from_raw(&object, (const char *) data, size, types[i]) < 0)
+ if (git_object__from_raw(&object, (const char *) data, size, types[i], GIT_OID_SHA1) < 0)
continue;
git_object_free(object);
object = NULL;
diff --git a/fuzzers/packfile_fuzzer.c b/fuzzers/packfile_fuzzer.c
index 52a813760a4..aeba9575c3e 100644
--- a/fuzzers/packfile_fuzzer.c
+++ b/fuzzers/packfile_fuzzer.c
@@ -67,6 +67,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
git_str path = GIT_STR_INIT;
git_oid oid;
bool append_hash = false;
+ int error;
if (size == 0)
return 0;
@@ -82,7 +83,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
abort();
}
- if (git_indexer_new(&indexer, ".", 0, odb, NULL) < 0) {
+#ifdef GIT_EXPERIMENTAL_SHA256
+ error = git_indexer_new(&indexer, ".", GIT_OID_SHA1, NULL);
+#else
+ error = git_indexer_new(&indexer, ".", 0, odb, NULL);
+#endif
+
+ if (error < 0) {
fprintf(stderr, "Failed to create the indexer: %s\n",
git_error_last()->message);
abort();
diff --git a/fuzzers/revparse_fuzzer.c b/fuzzers/revparse_fuzzer.c
new file mode 100644
index 00000000000..37c22e2221c
--- /dev/null
+++ b/fuzzers/revparse_fuzzer.c
@@ -0,0 +1,52 @@
+/*
+ * libgit2 revparse fuzzer target.
+ *
+ * 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.h"
+
+#include "standalone_driver.h"
+#include "fuzzer_utils.h"
+
+#define UNUSED(x) (void)(x)
+
+static git_repository *repo;
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+ UNUSED(argc);
+ UNUSED(argv);
+
+ if (git_libgit2_init() < 0)
+ abort();
+
+ if (git_libgit2_opts(GIT_OPT_SET_PACK_MAX_OBJECTS, 10000000) < 0)
+ abort();
+
+ repo = fuzzer_repo_init();
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ git_object *obj = NULL;
+ char *c;
+
+ if ((c = calloc(1, size + 1)) == NULL)
+ abort();
+
+ memcpy(c, data, size);
+
+ git_revparse_single(&obj, repo, c);
+ git_object_free(obj);
+ free(c);
+
+ return 0;
+}
diff --git a/include/git2/attr.h b/include/git2/attr.h
index 0c838727d14..69929b3dfc6 100644
--- a/include/git2/attr.h
+++ b/include/git2/attr.h
@@ -116,14 +116,12 @@ GIT_EXTERN(git_attr_value_t) git_attr_value(const char *attr);
*/
#define GIT_ATTR_CHECK_FILE_THEN_INDEX 0
#define GIT_ATTR_CHECK_INDEX_THEN_FILE 1
-#define GIT_ATTR_CHECK_INDEX_ONLY 2
+#define GIT_ATTR_CHECK_INDEX_ONLY 2
/**
* Check attribute flags: controlling extended attribute behavior.
*
* Normally, attribute checks include looking in the /etc (or system
- * equivalent) directory for a `gitattributes` file. Passing this
- * flag will cause attribute checks to ignore that file.
* equivalent) directory for a `gitattributes` file. Passing the
* `GIT_ATTR_CHECK_NO_SYSTEM` flag will cause attribute checks to
* ignore that file.
diff --git a/include/git2/commit.h b/include/git2/commit.h
index 67170cb9c83..88c21e0c973 100644
--- a/include/git2/commit.h
+++ b/include/git2/commit.h
@@ -394,6 +394,49 @@ GIT_EXTERN(int) git_commit_create_v(
size_t parent_count,
...);
+typedef struct {
+ unsigned int version;
+
+ /**
+ * Flags for creating the commit.
+ *
+ * If `allow_empty_commit` is specified, a commit with no changes
+ * from the prior commit (and "empty" commit) is allowed. Otherwise,
+ * commit creation will be stopped.
+ */
+ unsigned int allow_empty_commit : 1;
+
+ /** The commit author, or NULL for the default. */
+ const git_signature *author;
+
+ /** The committer, or NULL for the default. */
+ const git_signature *committer;
+
+ /** Encoding for the commit message; leave NULL for default. */
+ const char *message_encoding;
+} git_commit_create_options;
+
+#define GIT_COMMIT_CREATE_OPTIONS_VERSION 1
+#define GIT_COMMIT_CREATE_OPTIONS_INIT { GIT_COMMIT_CREATE_OPTIONS_VERSION }
+
+/**
+ * Commits the staged changes in the repository; this is a near analog to
+ * `git commit -m message`.
+ *
+ * By default, empty commits are not allowed.
+ *
+ * @param id pointer to store the new commit's object id
+ * @param repo repository to commit changes in
+ * @param message the commit message
+ * @param opts options for creating the commit
+ * @return 0 on success, GIT_EUNCHANGED if there were no changes to commit, or an error code
+ */
+GIT_EXTERN(int) git_commit_create_from_stage(
+ git_oid *id,
+ git_repository *repo,
+ const char *message,
+ const git_commit_create_options *opts);
+
/**
* Amend an existing commit by replacing only non-NULL values.
*
@@ -541,6 +584,24 @@ typedef int (*git_commit_create_cb)(
const git_commit *parents[],
void *payload);
+/** An array of commits returned from the library */
+typedef struct git_commitarray {
+ git_commit *const *commits;
+ size_t count;
+} git_commitarray;
+
+/**
+ * Free the commits contained in a commit array. This method should
+ * be called on `git_commitarray` objects that were provided by the
+ * library. Not doing so will result in a memory leak.
+ *
+ * This does not free the `git_commitarray` itself, since the library
+ * will never allocate that object directly itself.
+ *
+ * @param array The git_commitarray that contains commits to free
+ */
+GIT_EXTERN(void) git_commitarray_dispose(git_commitarray *array);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/common.h b/include/git2/common.h
index ccf66334a66..b7cf20b31c9 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -222,7 +222,15 @@ typedef enum {
GIT_OPT_GET_EXTENSIONS,
GIT_OPT_SET_EXTENSIONS,
GIT_OPT_GET_OWNER_VALIDATION,
- GIT_OPT_SET_OWNER_VALIDATION
+ GIT_OPT_SET_OWNER_VALIDATION,
+ GIT_OPT_GET_HOMEDIR,
+ GIT_OPT_SET_HOMEDIR,
+ GIT_OPT_SET_SERVER_CONNECT_TIMEOUT,
+ GIT_OPT_GET_SERVER_CONNECT_TIMEOUT,
+ GIT_OPT_SET_SERVER_TIMEOUT,
+ GIT_OPT_GET_SERVER_TIMEOUT,
+ GIT_OPT_SET_USER_AGENT_PRODUCT,
+ GIT_OPT_GET_USER_AGENT_PRODUCT
} git_libgit2_opt_t;
/**
@@ -331,11 +339,35 @@ typedef enum {
*
* * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent)
*
- * > Set the value of the User-Agent header. This value will be
- * > appended to "git/1.0", for compatibility with other git clients.
+ * > Set the value of the comment section of the User-Agent header.
+ * > This can be information about your product and its version.
+ * > By default this is "libgit2" followed by the libgit2 version.
* >
- * > - `user_agent` is the value that will be delivered as the
- * > User-Agent header on HTTP requests.
+ * > This value will be appended to User-Agent _product_, which
+ * > is typically set to "git/2.0".
+ * >
+ * > Set to the empty string ("") to not send any information in the
+ * > comment section, or set to NULL to restore the default.
+ *
+ * * opts(GIT_OPT_GET_USER_AGENT, git_buf *out)
+ *
+ * > Get the value of the User-Agent header.
+ * > The User-Agent is written to the `out` buffer.
+ *
+ * * opts(GIT_OPT_SET_USER_AGENT_PRODUCT, const char *user_agent_product)
+ *
+ * > Set the value of the product portion of the User-Agent header.
+ * > This defaults to "git/2.0", for compatibility with other git
+ * > clients. It is recommended to keep this as git/ for
+ * > compatibility with servers that do user-agent detection.
+ * >
+ * > Set to the empty string ("") to not send any user-agent string,
+ * > or set to NULL to restore the default.
+ *
+ * * opts(GIT_OPT_GET_USER_AGENT_PRODUCT, git_buf *out)
+ *
+ * > Get the value of the User-Agent product header.
+ * > The User-Agent product is written to the `out` buffer.
*
* * opts(GIT_OPT_SET_WINDOWS_SHAREMODE, unsigned long value)
*
@@ -371,11 +403,6 @@ typedef enum {
* >
* > - `ciphers` is the list of ciphers that are eanbled.
*
- * * opts(GIT_OPT_GET_USER_AGENT, git_buf *out)
- *
- * > Get the value of the User-Agent header.
- * > The User-Agent is written to the `out` buffer.
- *
* * opts(GIT_OPT_ENABLE_OFS_DELTA, int enabled)
*
* > Enable or disable the use of "offset deltas" when creating packfiles,
@@ -468,6 +495,34 @@ typedef enum {
* > Set that repository directories should be owned by the current
* > user. The default is to validate ownership.
*
+ * opts(GIT_OPT_GET_HOMEDIR, git_buf *out)
+ * > Gets the current user's home directory, as it will be used
+ * > for file lookups. The path is written to the `out` buffer.
+ *
+ * opts(GIT_OPT_SET_HOMEDIR, const char *path)
+ * > Sets the directory used as the current user's home directory,
+ * > for file lookups.
+ * >
+ * > - `path` directory of home directory.
+ *
+ * opts(GIT_OPT_GET_SERVER_CONNECT_TIMEOUT, int *timeout)
+ * > Gets the timeout (in milliseconds) to attempt connections to
+ * > a remote server.
+ *
+ * opts(GIT_OPT_SET_SERVER_CONNECT_TIMEOUT, int timeout)
+ * > Sets the timeout (in milliseconds) to attempt connections to
+ * > a remote server. Set to 0 to use the system default. Note that
+ * > this may not be able to be configured longer than the system
+ * > default, typically 75 seconds.
+ *
+ * opts(GIT_OPT_GET_SERVER_TIMEOUT, int *timeout)
+ * > Gets the timeout (in milliseconds) for reading from and writing
+ * > to a remote server.
+ *
+ * opts(GIT_OPT_SET_SERVER_TIMEOUT, int timeout)
+ * > Sets the timeout (in milliseconds) for reading from and writing
+ * > to a remote server. Set to 0 to use the system default.
+ *
* @param option Option key
* @param ... value to set the option
* @return 0 on success, <0 on failure
diff --git a/include/git2/config.h b/include/git2/config.h
index cfeb2214938..32361431326 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -22,8 +22,19 @@ GIT_BEGIN_DECL
/**
* Priority level of a config file.
+ *
* These priority levels correspond to the natural escalation logic
- * (from higher to lower) when searching for config entries in git.git.
+ * (from higher to lower) when reading or searching for config entries
+ * in git.git. Meaning that for the same key, the configuration in
+ * the local configuration is preferred over the configuration in
+ * the system configuration file.
+ *
+ * Callers can add their own custom configuration, beginning at the
+ * `GIT_CONFIG_LEVEL_APP` level.
+ *
+ * Writes, by default, occur in the highest priority level backend
+ * that is writable. This ordering can be overridden with
+ * `git_config_set_writeorder`.
*
* git_config_open_default() and git_repository_config() honor those
* priority levels as well.
@@ -48,7 +59,9 @@ typedef enum {
*/
GIT_CONFIG_LEVEL_LOCAL = 5,
- GIT_CONFIG_LEVEL_WORKTREE = 6,
+ /** Worktree specific configuration file; $GIT_DIR/config.worktree
+ */
+ GIT_CONFIG_LEVEL_WORKTREE = 6,
/** Application specific configuration file; freely defined by applications
*/
@@ -64,12 +77,32 @@ typedef enum {
* An entry in a configuration file
*/
typedef struct git_config_entry {
- const char *name; /**< Name of the entry (normalised) */
- const char *value; /**< String value of the entry */
- unsigned int include_depth; /**< Depth of includes where this variable was found */
- git_config_level_t level; /**< Which config file this was found in */
- void GIT_CALLBACK(free)(struct git_config_entry *entry); /**< Free function for this entry */
- void *payload; /**< Opaque value for the free function. Do not read or write */
+ /** Name of the configuration entry (normalized) */
+ const char *name;
+
+ /** Literal (string) value of the entry */
+ const char *value;
+
+ /** The type of backend that this entry exists in (eg, "file") */
+ const char *backend_type;
+
+ /**
+ * The path to the origin of this entry. For config files, this is
+ * the path to the file.
+ */
+ const char *origin_path;
+
+ /** Depth of includes where this variable was found */
+ unsigned int include_depth;
+
+ /** Configuration level for the file this was found in */
+ git_config_level_t level;
+
+ /**
+ * Free function for this entry; for internal purposes. Callers
+ * should call `git_config_entry_free` to free data.
+ */
+ void GIT_CALLBACK(free)(struct git_config_entry *entry);
} git_config_entry;
/**
@@ -278,6 +311,11 @@ GIT_EXTERN(int) git_config_open_level(
*/
GIT_EXTERN(int) git_config_open_global(git_config **out, git_config *config);
+GIT_EXTERN(int) git_config_set_writeorder(
+ git_config *cfg,
+ git_config_level_t *levels,
+ size_t len);
+
/**
* Create a snapshot of the configuration
*
diff --git a/include/git2/diff.h b/include/git2/diff.h
index ab56f9781ab..dde30181b77 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -428,6 +428,22 @@ typedef struct {
*/
uint32_t interhunk_lines;
+ /**
+ * The object ID type to emit in diffs; this is used by functions
+ * that operate without a repository - namely `git_diff_buffers`,
+ * or `git_diff_blobs` and `git_diff_blob_to_buffer` when one blob
+ * is `NULL`.
+ *
+ * This may be omitted (set to `0`). If a repository is available,
+ * the object ID format of the repository will be used. If no
+ * repository is available then the default is `GIT_OID_SHA`.
+ *
+ * If this is specified and a repository is available, then the
+ * specified `oid_type` must match the repository's object ID
+ * format.
+ */
+ git_oid_t oid_type;
+
/**
* The abbreviation length to use when formatting object ids.
* Defaults to the value of 'core.abbrev' from the config, or 7 if unset.
@@ -1164,9 +1180,8 @@ GIT_EXTERN(int) git_diff_to_buf(
/**@}*/
-
/*
- * Misc
+ * Low-level file comparison, invoking callbacks per difference.
*/
/**
@@ -1282,6 +1297,25 @@ GIT_EXTERN(int) git_diff_buffers(
git_diff_line_cb line_cb,
void *payload);
+/* Patch file parsing. */
+
+/**
+ * Options for parsing a diff / patch file.
+ */
+typedef struct {
+ unsigned int version;
+ git_oid_t oid_type;
+} git_diff_parse_options;
+
+/* The current version of the diff parse options structure */
+#define GIT_DIFF_PARSE_OPTIONS_VERSION 1
+
+/* Stack initializer for diff parse options. Alternatively use
+ * `git_diff_parse_options_init` programmatic initialization.
+ */
+#define GIT_DIFF_PARSE_OPTIONS_INIT \
+ { GIT_DIFF_PARSE_OPTIONS_VERSION, GIT_OID_DEFAULT }
+
/**
* Read the contents of a git patch file into a `git_diff` object.
*
@@ -1304,7 +1338,11 @@ GIT_EXTERN(int) git_diff_buffers(
GIT_EXTERN(int) git_diff_from_buffer(
git_diff **out,
const char *content,
- size_t content_len);
+ size_t content_len
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_diff_parse_options *opts
+#endif
+ );
/**
* This is an opaque structure which is allocated by `git_diff_get_stats`.
diff --git a/include/git2/email.h b/include/git2/email.h
index 20393653e9f..3389353e796 100644
--- a/include/git2/email.h
+++ b/include/git2/email.h
@@ -8,6 +8,7 @@
#define INCLUDE_git_email_h__
#include "common.h"
+#include "diff.h"
/**
* @file git2/email.h
diff --git a/include/git2/errors.h b/include/git2/errors.h
index a61964bbb12..52fa5f0720d 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -19,20 +19,20 @@ GIT_BEGIN_DECL
/** Generic return codes */
typedef enum {
- GIT_OK = 0, /**< No error */
+ GIT_OK = 0, /**< No error */
- GIT_ERROR = -1, /**< Generic error */
- GIT_ENOTFOUND = -3, /**< Requested object could not be found */
- GIT_EEXISTS = -4, /**< Object exists preventing operation */
- GIT_EAMBIGUOUS = -5, /**< More than one object matches */
- GIT_EBUFS = -6, /**< Output buffer too short to hold data */
+ GIT_ERROR = -1, /**< Generic error */
+ GIT_ENOTFOUND = -3, /**< Requested object could not be found */
+ GIT_EEXISTS = -4, /**< Object exists preventing operation */
+ GIT_EAMBIGUOUS = -5, /**< More than one object matches */
+ GIT_EBUFS = -6, /**< Output buffer too short to hold data */
/**
* GIT_EUSER is a special error that is never generated by libgit2
* code. You can return it from a callback (e.g to stop an iteration)
* to know that it was generated by the callback and not by libgit2.
*/
- GIT_EUSER = -7,
+ GIT_EUSER = -7,
GIT_EBAREREPO = -8, /**< Operation not allowed on bare repository */
GIT_EUNBORNBRANCH = -9, /**< HEAD refers to branch with no commits */
@@ -58,7 +58,11 @@ typedef enum {
GIT_EMISMATCH = -33, /**< Hashsum mismatch in object */
GIT_EINDEXDIRTY = -34, /**< Unsaved changes in the index would be overwritten */
GIT_EAPPLYFAIL = -35, /**< Patch application failed */
- GIT_EOWNER = -36 /**< The object is not owned by the current user */
+ GIT_EOWNER = -36, /**< The object is not owned by the current user */
+ GIT_TIMEOUT = -37, /**< The operation timed out */
+ GIT_EUNCHANGED = -38, /**< There were no changes */
+ GIT_ENOTSUPPORTED = -39, /**< An option is not supported */
+ GIT_EREADONLY = -40 /**< The subject is read-only */
} git_error_code;
/**
@@ -109,70 +113,30 @@ typedef enum {
GIT_ERROR_WORKTREE,
GIT_ERROR_SHA,
GIT_ERROR_HTTP,
- GIT_ERROR_INTERNAL
+ GIT_ERROR_INTERNAL,
+ GIT_ERROR_GRAFTS
} git_error_t;
/**
* Return the last `git_error` object that was generated for the
* current thread.
*
- * The default behaviour of this function is to return NULL if no previous error has occurred.
- * However, libgit2's error strings are not cleared aggressively, so a prior
- * (unrelated) error may be returned. This can be avoided by only calling
- * this function if the prior call to a libgit2 API returned an error.
+ * This function will never return NULL.
*
- * @return A git_error object.
- */
-GIT_EXTERN(const git_error *) git_error_last(void);
-
-/**
- * Clear the last library error that occurred for this thread.
- */
-GIT_EXTERN(void) git_error_clear(void);
-
-/**
- * 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
- * will never need to call this function -- actually, calling it in most
- * circumstances (for example, calling from within a callback function)
- * will just end up having the value overwritten by libgit2 internals.
+ * Callers should not rely on this to determine whether an error has
+ * occurred. For error checking, callers should examine the return
+ * codes of libgit2 functions.
*
- * This error message is stored in thread-local storage and only applies
- * to the particular thread that this libgit2 call is made from.
+ * This call can only reliably report error messages when an error
+ * has occurred. (It may contain stale information if it is called
+ * after a different function that succeeds.)
*
- * @param error_class One of the `git_error_t` enum above describing the
- * general subsystem that is responsible for the error.
- * @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.
+ * The memory for this object is managed by libgit2. It should not
+ * be freed.
*
- * @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);
-
-/**
- * Set the error message to a special value for memory allocation failure.
- *
- * The normal `git_error_set_str()` function attempts to `strdup()` the
- * string that is passed in. This is not a good idea when the error in
- * question is a memory allocation failure. That circumstance has a
- * special setter function that sets the error string to a known and
- * statically allocated internal value.
+ * @return A git_error object.
*/
-GIT_EXTERN(void) git_error_set_oom(void);
+GIT_EXTERN(const git_error *) git_error_last(void);
/** @} */
GIT_END_DECL
diff --git a/include/git2/index.h b/include/git2/index.h
index c82d50cd71f..585db04093f 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -186,7 +186,12 @@ typedef enum {
* @param index_path the path to the index file in disk
* @return 0 or an error code
*/
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_index_open(git_index **out, const char *index_path, git_oid_t oid_type);
+#else
GIT_EXTERN(int) git_index_open(git_index **out, const char *index_path);
+#endif
/**
* Create an in-memory index object.
@@ -199,7 +204,11 @@ GIT_EXTERN(int) git_index_open(git_index **out, const char *index_path);
* @param out the pointer for the new index
* @return 0 or an error code
*/
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_index_new(git_index **out, git_oid_t oid_type);
+#else
GIT_EXTERN(int) git_index_new(git_index **out);
+#endif
/**
* Free an existing index object.
diff --git a/include/git2/indexer.h b/include/git2/indexer.h
index ffe9bf366d5..630eef93456 100644
--- a/include/git2/indexer.h
+++ b/include/git2/indexer.h
@@ -62,6 +62,19 @@ typedef int GIT_CALLBACK(git_indexer_progress_cb)(const git_indexer_progress *st
typedef struct git_indexer_options {
unsigned int version;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ /** permissions to use creating packfile or 0 for defaults */
+ unsigned int mode;
+
+ /**
+ * object database from which to read base objects when
+ * fixing thin packs. This can be NULL if there are no thin
+ * packs; if a thin pack is encountered, an error will be
+ * returned if there are bases missing.
+ */
+ git_odb *odb;
+#endif
+
/** progress_cb function to call with progress information */
git_indexer_progress_cb progress_cb;
@@ -87,6 +100,21 @@ GIT_EXTERN(int) git_indexer_options_init(
git_indexer_options *opts,
unsigned int version);
+#ifdef GIT_EXPERIMENTAL_SHA256
+/**
+ * Create a new indexer instance
+ *
+ * @param out where to store the indexer instance
+ * @param path to the directory where the packfile should be stored
+ * @param oid_type the oid type to use for objects
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_indexer_new(
+ git_indexer **out,
+ const char *path,
+ git_oid_t oid_type,
+ git_indexer_options *opts);
+#else
/**
* Create a new indexer instance
*
@@ -106,6 +134,7 @@ GIT_EXTERN(int) git_indexer_new(
unsigned int mode,
git_odb *odb,
git_indexer_options *opts);
+#endif
/**
* Add data to the indexer
diff --git a/include/git2/object.h b/include/git2/object.h
index 5610a476f49..6384aaa6e94 100644
--- a/include/git2/object.h
+++ b/include/git2/object.h
@@ -225,6 +225,7 @@ GIT_EXTERN(int) git_object_peel(
*/
GIT_EXTERN(int) git_object_dup(git_object **dest, git_object *source);
+#ifdef GIT_EXPERIMENTAL_SHA256
/**
* Analyzes a buffer of raw object content and determines its validity.
* Tree, commit, and tag objects will be parsed and ensured that they
@@ -238,14 +239,39 @@ GIT_EXTERN(int) git_object_dup(git_object **dest, git_object *source);
* @param valid Output pointer to set with validity of the object content
* @param buf The contents to validate
* @param len The length of the buffer
- * @param type The type of the object in the buffer
+ * @param object_type The type of the object in the buffer
+ * @param oid_type The object ID type for the OIDs in the given buffer
* @return 0 on success or an error code
*/
GIT_EXTERN(int) git_object_rawcontent_is_valid(
int *valid,
const char *buf,
size_t len,
- git_object_t type);
+ git_object_t object_type,
+ git_oid_t oid_type);
+#else
+/**
+ * Analyzes a buffer of raw object content and determines its validity.
+ * Tree, commit, and tag objects will be parsed and ensured that they
+ * are valid, parseable content. (Blobs are always valid by definition.)
+ * An error message will be set with an informative message if the object
+ * is not valid.
+ *
+ * @warning This function is experimental and its signature may change in
+ * the future.
+ *
+ * @param valid Output pointer to set with validity of the object content
+ * @param buf The contents to validate
+ * @param len The length of the buffer
+ * @param object_type The type of the object in the buffer
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_object_rawcontent_is_valid(
+ int *valid,
+ const char *buf,
+ size_t len,
+ git_object_t object_type);
+#endif
/** @} */
GIT_END_DECL
diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h
index a31d1b78291..12dd0fd38a3 100644
--- a/include/git2/odb_backend.h
+++ b/include/git2/odb_backend.h
@@ -24,6 +24,26 @@ GIT_BEGIN_DECL
* Constructors for in-box ODB backends.
*/
+/** Options for configuring a packfile object backend. */
+typedef struct {
+ unsigned int version; /**< version for the struct */
+
+ /**
+ * Type of object IDs to use for this object database, or
+ * 0 for default (currently SHA1).
+ */
+ git_oid_t oid_type;
+} git_odb_backend_pack_options;
+
+/* The current version of the diff options structure */
+#define GIT_ODB_BACKEND_PACK_OPTIONS_VERSION 1
+
+/* Stack initializer for odb pack backend options. Alternatively use
+ * `git_odb_backend_pack_options_init` programmatic initialization.
+ */
+#define GIT_ODB_BACKEND_PACK_OPTIONS_INIT \
+ { GIT_ODB_BACKEND_PACK_OPTIONS_VERSION }
+
/**
* Create a backend for the packfiles.
*
@@ -32,7 +52,38 @@ GIT_BEGIN_DECL
*
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_dir);
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_odb_backend_pack(
+ git_odb_backend **out,
+ const char *objects_dir,
+ const git_odb_backend_pack_options *opts);
+#else
+GIT_EXTERN(int) git_odb_backend_pack(
+ git_odb_backend **out,
+ const char *objects_dir);
+#endif
+
+/**
+ * Create a backend out of a single packfile
+ *
+ * This can be useful for inspecting the contents of a single
+ * packfile.
+ *
+ * @param out location to store the odb backend pointer
+ * @param index_file path to the packfile's .idx file
+ *
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_odb_backend_one_pack(
+ git_odb_backend **out,
+ const char *index_file,
+ const git_odb_backend_pack_options *opts);
+#else
+GIT_EXTERN(int) git_odb_backend_one_pack(
+ git_odb_backend **out,
+ const char *index_file);
+#endif
typedef enum {
GIT_ODB_BACKEND_LOOSE_FSYNC = (1 << 0)
@@ -100,19 +151,6 @@ GIT_EXTERN(int) git_odb_backend_loose(
unsigned int file_mode);
#endif
-/**
- * Create a backend out of a single packfile
- *
- * This can be useful for inspecting the contents of a single
- * packfile.
- *
- * @param out location to store the odb backend pointer
- * @param index_file path to the packfile's .idx file
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file);
-
/** Streaming mode */
typedef enum {
GIT_STREAM_RDONLY = (1 << 1),
@@ -133,7 +171,9 @@ struct git_odb_stream {
unsigned int mode;
void *hash_ctx;
+#ifdef GIT_EXPERIMENTAL_SHA256
git_oid_t oid_type;
+#endif
git_object_size_t declared_size;
git_object_size_t received_bytes;
diff --git a/include/git2/oid.h b/include/git2/oid.h
index 399b7b9071b..35b43ef183a 100644
--- a/include/git2/oid.h
+++ b/include/git2/oid.h
@@ -225,7 +225,7 @@ GIT_EXTERN(int) git_oid_pathfmt(char *out, const git_oid *id);
* concurrent calls of the function.
*
* @param oid The oid structure to format
- * @return the c-string
+ * @return the c-string or NULL on failure
*/
GIT_EXTERN(char *) git_oid_tostr_s(const git_oid *oid);
diff --git a/include/git2/refspec.h b/include/git2/refspec.h
index eaf7747465c..e7087132b04 100644
--- a/include/git2/refspec.h
+++ b/include/git2/refspec.h
@@ -58,7 +58,7 @@ GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
* Get the refspec's string
*
* @param refspec the refspec
- * @returns the refspec's original string
+ * @return the refspec's original string
*/
GIT_EXTERN(const char *) git_refspec_string(const git_refspec *refspec);
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 8c9c26f3fd5..5505f6c358d 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -76,6 +76,17 @@ typedef enum {
GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC = (1 << 1)
} git_remote_create_flags;
+/**
+ * How to handle reference updates.
+ */
+typedef enum {
+ /* Write the fetch results to FETCH_HEAD. */
+ GIT_REMOTE_UPDATE_FETCHHEAD = (1 << 0),
+
+ /* Report unchanged tips in the update_tips callback. */
+ GIT_REMOTE_UPDATE_REPORT_UNCHANGED = (1 << 1)
+} git_remote_update_flags;
+
/**
* Remote creation options structure
*
@@ -702,6 +713,15 @@ typedef enum {
GIT_REMOTE_DOWNLOAD_TAGS_ALL
} git_remote_autotag_option_t;
+/** Constants for fetch depth (shallowness of fetch). */
+typedef enum {
+ /** The fetch is "full" (not shallow). This is the default. */
+ GIT_FETCH_DEPTH_FULL = 0,
+
+ /** The fetch should "unshallow" and fetch missing data. */
+ GIT_FETCH_DEPTH_UNSHALLOW = 2147483647
+} git_fetch_depth_t;
+
/**
* Fetch options structure.
*
@@ -724,10 +744,9 @@ typedef struct {
git_fetch_prune_t prune;
/**
- * Whether to write the results to FETCH_HEAD. Defaults to
- * on. Leave this default in order to behave like git.
+ * How to handle reference updates; see `git_remote_update_flags`.
*/
- int update_fetchhead;
+ unsigned int update_fetchhead;
/**
* Determines how to behave regarding tags on the remote, such
@@ -743,6 +762,15 @@ typedef struct {
*/
git_proxy_options proxy_opts;
+ /**
+ * Depth of the fetch to perform, or `GIT_FETCH_DEPTH_FULL`
+ * (or `0`) for full history, or `GIT_FETCH_DEPTH_UNSHALLOW`
+ * to "unshallow" a shallow repository.
+ *
+ * The default is full (`GIT_FETCH_DEPTH_FULL` or `0`).
+ */
+ int depth;
+
/**
* Whether to allow off-site redirects. If this is not
* specified, the `http.followRedirects` configuration setting
@@ -757,8 +785,13 @@ typedef struct {
} git_fetch_options;
#define GIT_FETCH_OPTIONS_VERSION 1
-#define GIT_FETCH_OPTIONS_INIT { GIT_FETCH_OPTIONS_VERSION, GIT_REMOTE_CALLBACKS_INIT, GIT_FETCH_PRUNE_UNSPECIFIED, 1, \
- GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED, GIT_PROXY_OPTIONS_INIT }
+#define GIT_FETCH_OPTIONS_INIT { \
+ GIT_FETCH_OPTIONS_VERSION, \
+ GIT_REMOTE_CALLBACKS_INIT, \
+ GIT_FETCH_PRUNE_UNSPECIFIED, \
+ GIT_REMOTE_UPDATE_FETCHHEAD, \
+ GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED, \
+ GIT_PROXY_OPTIONS_INIT }
/**
* Initialize git_fetch_options structure
@@ -812,6 +845,11 @@ typedef struct {
* Extra headers for this push operation
*/
git_strarray custom_headers;
+
+ /**
+ * "Push options" to deliver to the remote.
+ */
+ git_strarray remote_push_options;
} git_push_options;
#define GIT_PUSH_OPTIONS_VERSION 1
@@ -983,7 +1021,7 @@ GIT_EXTERN(int) git_remote_upload(
* the name of the remote (or its url, for in-memory remotes). This
* parameter is ignored when pushing.
* @param callbacks pointer to the callback structure to use or NULL
- * @param update_fetchhead whether to write to FETCH_HEAD. Pass 1 to behave like git.
+ * @param update_flags the git_remote_update_flags for these tips.
* @param download_tags what the behaviour for downloading tags is for this fetch. This is
* ignored for push. This must be the same value passed to `git_remote_download()`.
* @return 0 or an error code
@@ -991,7 +1029,7 @@ GIT_EXTERN(int) git_remote_upload(
GIT_EXTERN(int) git_remote_update_tips(
git_remote *remote,
const git_remote_callbacks *callbacks,
- int update_fetchhead,
+ unsigned int update_flags,
git_remote_autotag_option_t download_tags,
const char *reflog_message);
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 71a685d151e..0afda72d402 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -11,6 +11,7 @@
#include "types.h"
#include "oid.h"
#include "buffer.h"
+#include "commit.h"
/**
* @file git2/repository.h
@@ -56,9 +57,19 @@ GIT_EXTERN(int) git_repository_open_from_worktree(git_repository **out, git_work
*
* @param out pointer to the repo
* @param odb the object database to wrap
+ * @param oid_type the oid type of the object database
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_repository_wrap_odb(git_repository **out, git_odb *odb);
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_repository_wrap_odb(
+ git_repository **out,
+ git_odb *odb,
+ git_oid_t oid_type);
+#else
+GIT_EXTERN(int) git_repository_wrap_odb(
+ git_repository **out,
+ git_odb *odb);
+#endif
/**
* Look for a git repository and copy its path in the given buffer.
@@ -351,6 +362,15 @@ typedef struct {
* pointing to this URL.
*/
const char *origin_url;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ /**
+ *
+ * Type of object IDs to use for this repository, or 0 for
+ * default (currently SHA1).
+ */
+ git_oid_t oid_type;
+#endif
} git_repository_init_options;
#define GIT_REPOSITORY_INIT_OPTIONS_VERSION 1
@@ -456,7 +476,9 @@ GIT_EXTERN(int) git_repository_head_unborn(git_repository *repo);
* Check if a repository is empty
*
* An empty repository has just been initialized and contains no references
- * apart from HEAD, which must be pointing to the unborn master branch.
+ * apart from HEAD, which must be pointing to the unborn master branch,
+ * or the branch specified for the repository in the `init.defaultBranch`
+ * configuration variable.
*
* @param repo Repo to test
* @return 1 if the repository is empty, 0 if it isn't, error code
@@ -477,12 +499,12 @@ typedef enum {
GIT_REPOSITORY_ITEM_PACKED_REFS,
GIT_REPOSITORY_ITEM_REMOTES,
GIT_REPOSITORY_ITEM_CONFIG,
- GIT_REPOSITORY_ITEM_WORKTREE_CONFIG,
GIT_REPOSITORY_ITEM_INFO,
GIT_REPOSITORY_ITEM_HOOKS,
GIT_REPOSITORY_ITEM_LOGS,
GIT_REPOSITORY_ITEM_MODULES,
GIT_REPOSITORY_ITEM_WORKTREES,
+ GIT_REPOSITORY_ITEM_WORKTREE_CONFIG,
GIT_REPOSITORY_ITEM__LAST
} git_repository_item_t;
@@ -526,7 +548,7 @@ GIT_EXTERN(const char *) git_repository_workdir(const git_repository *repo);
/**
* Get the path of the shared common directory for this repository.
- *
+ *
* If the repository is bare, it is the root directory for the repository.
* If the repository is a worktree, it is the parent repo's gitdir.
* Otherwise, it is the gitdir.
@@ -950,6 +972,25 @@ GIT_EXTERN(int) git_repository_ident(const char **name, const char **email, cons
*/
GIT_EXTERN(int) git_repository_set_ident(git_repository *repo, const char *name, const char *email);
+/**
+ * Gets the object type used by this repository.
+ *
+ * @param repo the repository
+ * @return the object id type
+ */
+GIT_EXTERN(git_oid_t) git_repository_oid_type(git_repository *repo);
+
+/**
+ * Gets the parents of the next commit, given the current repository state.
+ * Generally, this is the HEAD commit, except when performing a merge, in
+ * which case it is two or more commits.
+ *
+ * @param commits a `git_commitarray` that will contain the commit parents
+ * @param repo the repository
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_repository_commit_parents(git_commitarray *commits, git_repository *repo);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/stash.h b/include/git2/stash.h
index 32e6f95764c..dcfc013dc4e 100644
--- a/include/git2/stash.h
+++ b/include/git2/stash.h
@@ -44,7 +44,12 @@ typedef enum {
* All ignored files are also stashed and then cleaned up from
* the working directory
*/
- GIT_STASH_INCLUDE_IGNORED = (1 << 2)
+ GIT_STASH_INCLUDE_IGNORED = (1 << 2),
+
+ /**
+ * All changes in the index and working directory are left intact
+ */
+ GIT_STASH_KEEP_ALL = (1 << 3)
} git_stash_flags;
/**
@@ -52,15 +57,10 @@ typedef enum {
*
* @param out Object id of the commit containing the stashed state.
* This commit is also the target of the direct reference refs/stash.
- *
* @param repo The owning repository.
- *
* @param stasher The identity of the person performing the stashing.
- *
* @param message Optional description along with the stashed state.
- *
* @param flags Flags to control the stashing process. (see GIT_STASH_* above)
- *
* @return 0 on success, GIT_ENOTFOUND where there's nothing to stash,
* or error code.
*/
@@ -71,6 +71,60 @@ GIT_EXTERN(int) git_stash_save(
const char *message,
uint32_t flags);
+/**
+ * Stash save options structure
+ *
+ * Initialize with `GIT_STASH_SAVE_OPTIONS_INIT`. Alternatively, you can
+ * use `git_stash_save_options_init`.
+ *
+ */
+typedef struct git_stash_save_options {
+ unsigned int version;
+
+ /** Flags to control the stashing process. (see GIT_STASH_* above) */
+ uint32_t flags;
+
+ /** The identity of the person performing the stashing. */
+ const git_signature *stasher;
+
+ /** Optional description along with the stashed state. */
+ const char *message;
+
+ /** Optional paths that control which files are stashed. */
+ git_strarray paths;
+} git_stash_save_options;
+
+#define GIT_STASH_SAVE_OPTIONS_VERSION 1
+#define GIT_STASH_SAVE_OPTIONS_INIT { GIT_STASH_SAVE_OPTIONS_VERSION }
+
+/**
+ * Initialize git_stash_save_options structure
+ *
+ * Initializes a `git_stash_save_options` with default values. Equivalent to
+ * creating an instance with `GIT_STASH_SAVE_OPTIONS_INIT`.
+ *
+ * @param opts The `git_stash_save_options` struct to initialize.
+ * @param version The struct version; pass `GIT_STASH_SAVE_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_stash_save_options_init(
+ git_stash_save_options *opts, unsigned int version);
+
+/**
+ * Save the local modifications to a new stash, with options.
+ *
+ * @param out Object id of the commit containing the stashed state.
+ * This commit is also the target of the direct reference refs/stash.
+ * @param repo The owning repository.
+ * @param opts The stash options.
+ * @return 0 on success, GIT_ENOTFOUND where there's nothing to stash,
+ * or error code.
+ */
+GIT_EXTERN(int) git_stash_save_with_opts(
+ git_oid *out,
+ git_repository *repo,
+ const git_stash_save_options *opts);
+
/** Stash application flags. */
typedef enum {
GIT_STASH_APPLY_DEFAULT = 0,
diff --git a/include/git2/strarray.h b/include/git2/strarray.h
index 0f657e6c57d..03d93f8fbbc 100644
--- a/include/git2/strarray.h
+++ b/include/git2/strarray.h
@@ -36,19 +36,6 @@ typedef struct git_strarray {
*/
GIT_EXTERN(void) git_strarray_dispose(git_strarray *array);
-/**
- * Copy a string array object from source to target.
- *
- * Note: target is overwritten and hence should be empty, otherwise its
- * contents are leaked. Call git_strarray_free() if necessary.
- *
- * @param tgt target
- * @param src source
- * @return 0 on success, < 0 on allocation failure
- */
-GIT_EXTERN(int) git_strarray_copy(git_strarray *tgt, const git_strarray *src);
-
-
/** @} */
GIT_END_DECL
diff --git a/include/git2/sys/alloc.h b/include/git2/sys/alloc.h
index c295807bcf2..e7f85b890c8 100644
--- a/include/git2/sys/alloc.h
+++ b/include/git2/sys/alloc.h
@@ -24,28 +24,6 @@ typedef struct {
/** Allocate `n` bytes of memory */
void * GIT_CALLBACK(gmalloc)(size_t n, const char *file, int line);
- /**
- * Allocate memory for an array of `nelem` elements, where each element
- * has a size of `elsize`. Returned memory shall be initialized to
- * all-zeroes
- */
- void * GIT_CALLBACK(gcalloc)(size_t nelem, size_t elsize, const char *file, int line);
-
- /** Allocate memory for the string `str` and duplicate its contents. */
- char * GIT_CALLBACK(gstrdup)(const char *str, const char *file, int line);
-
- /**
- * Equivalent to the `gstrdup` function, but only duplicating at most
- * `n + 1` bytes
- */
- char * GIT_CALLBACK(gstrndup)(const char *str, size_t n, const char *file, int line);
-
- /**
- * Equivalent to `gstrndup`, but will always duplicate exactly `n` bytes
- * of `str`. Thus, out of bounds reads at `str` may happen.
- */
- char * GIT_CALLBACK(gsubstrdup)(const char *str, size_t n, const char *file, int line);
-
/**
* This function shall deallocate the old object `ptr` and return a
* pointer to a new object that has the size specified by `size`. In
@@ -53,18 +31,6 @@ typedef struct {
*/
void * GIT_CALLBACK(grealloc)(void *ptr, size_t size, const char *file, int line);
- /**
- * This function shall be equivalent to `grealloc`, but allocating
- * `neleme * elsize` bytes.
- */
- void * GIT_CALLBACK(greallocarray)(void *ptr, size_t nelem, size_t elsize, const char *file, int line);
-
- /**
- * This function shall allocate a new array of `nelem` elements, where
- * each element has a size of `elsize` bytes.
- */
- void * GIT_CALLBACK(gmallocarray)(size_t nelem, size_t elsize, const char *file, int line);
-
/**
* This function shall free the memory pointed to by `ptr`. In case
* `ptr` is `NULL`, this shall be a no-op.
diff --git a/include/git2/sys/commit_graph.h b/include/git2/sys/commit_graph.h
index 823c7ed579b..06e045fcd2b 100644
--- a/include/git2/sys/commit_graph.h
+++ b/include/git2/sys/commit_graph.h
@@ -28,7 +28,13 @@ GIT_BEGIN_DECL
* @param objects_dir the path to a git objects directory.
* @return Zero on success; -1 on failure.
*/
-GIT_EXTERN(int) git_commit_graph_open(git_commit_graph **cgraph_out, const char *objects_dir);
+GIT_EXTERN(int) git_commit_graph_open(
+ git_commit_graph **cgraph_out,
+ const char *objects_dir
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ );
/**
* Frees commit-graph data. This should only be called when memory allocated
@@ -50,7 +56,11 @@ GIT_EXTERN(void) git_commit_graph_free(git_commit_graph *cgraph);
*/
GIT_EXTERN(int) git_commit_graph_writer_new(
git_commit_graph_writer **out,
- const char *objects_info_dir);
+ const char *objects_info_dir
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ );
/**
* Free the commit-graph writer and its resources.
diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h
index 0a9005e35d4..75d20758b84 100644
--- a/include/git2/sys/config.h
+++ b/include/git2/sys/config.h
@@ -125,6 +125,57 @@ GIT_EXTERN(int) git_config_add_backend(
const git_repository *repo,
int force);
+/** Options for in-memory configuration backends. */
+typedef struct {
+ unsigned int version;
+
+ /**
+ * The type of this backend (eg, "command line"). If this is
+ * NULL, then this will be "in-memory".
+ */
+ const char *backend_type;
+
+ /**
+ * The path to the origin; if this is NULL then it will be
+ * left unset in the resulting configuration entries.
+ */
+ const char *origin_path;
+} git_config_backend_memory_options;
+
+#define GIT_CONFIG_BACKEND_MEMORY_OPTIONS_VERSION 1
+#define GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT { GIT_CONFIG_BACKEND_MEMORY_OPTIONS_VERSION }
+
+
+/**
+ * Create an in-memory configuration backend from a string in standard
+ * git configuration file format.
+ *
+ * @param out the new backend
+ * @param cfg the configuration that is to be parsed
+ * @param len the length of the string pointed to by `cfg`
+ * @param opts the options to initialize this backend with, or NULL
+ */
+extern int git_config_backend_from_string(
+ git_config_backend **out,
+ const char *cfg,
+ size_t len,
+ git_config_backend_memory_options *opts);
+
+/**
+ * Create an in-memory configuration backend from a list of name/value
+ * pairs.
+ *
+ * @param out the new backend
+ * @param values the configuration values to set (in "key=value" format)
+ * @param len the length of the values array
+ * @param opts the options to initialize this backend with, or NULL
+ */
+extern int git_config_backend_from_values(
+ git_config_backend **out,
+ const char **values,
+ size_t len,
+ git_config_backend_memory_options *opts);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/sys/email.h b/include/git2/sys/email.h
index 6f4a2866209..5029f9a532c 100644
--- a/include/git2/sys/email.h
+++ b/include/git2/sys/email.h
@@ -7,6 +7,11 @@
#ifndef INCLUDE_sys_git_email_h__
#define INCLUDE_sys_git_email_h__
+#include "git2/common.h"
+#include "git2/diff.h"
+#include "git2/email.h"
+#include "git2/types.h"
+
/**
* @file git2/sys/email.h
* @brief Advanced git email creation routines
diff --git a/include/git2/sys/errors.h b/include/git2/sys/errors.h
new file mode 100644
index 00000000000..3ae121524d5
--- /dev/null
+++ b/include/git2/sys/errors.h
@@ -0,0 +1,66 @@
+/*
+ * 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_sys_git_errors_h__
+#define INCLUDE_sys_git_errors_h__
+
+#include "git2/common.h"
+
+GIT_BEGIN_DECL
+
+/**
+ * Clear the last library error that occurred for this thread.
+ */
+GIT_EXTERN(void) git_error_clear(void);
+
+/**
+ * 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
+ * will never need to call this function -- actually, calling it in most
+ * circumstances (for example, calling from within a callback function)
+ * will just end up having the value overwritten by libgit2 internals.
+ *
+ * This error message is stored in thread-local storage and only applies
+ * to the particular thread that this libgit2 call is made from.
+ *
+ * @param error_class One of the `git_error_t` enum above describing the
+ * general subsystem that is responsible for the error.
+ * @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);
+
+/**
+ * Set the error message to a special value for memory allocation failure.
+ *
+ * The normal `git_error_set_str()` function attempts to `strdup()` the
+ * string that is passed in. This is not a good idea when the error in
+ * question is a memory allocation failure. That circumstance has a
+ * special setter function that sets the error string to a known and
+ * statically allocated internal value.
+ */
+GIT_EXTERN(void) git_error_set_oom(void);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/sys/midx.h b/include/git2/sys/midx.h
index e3d7498298c..3a87484d2b5 100644
--- a/include/git2/sys/midx.h
+++ b/include/git2/sys/midx.h
@@ -29,7 +29,11 @@ GIT_BEGIN_DECL
*/
GIT_EXTERN(int) git_midx_writer_new(
git_midx_writer **out,
- const char *pack_dir);
+ const char *pack_dir
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ );
/**
* Free the multi-pack-index writer and its resources.
diff --git a/include/git2/sys/remote.h b/include/git2/sys/remote.h
index 0eae9234deb..58950e1ec77 100644
--- a/include/git2/sys/remote.h
+++ b/include/git2/sys/remote.h
@@ -20,12 +20,18 @@
GIT_BEGIN_DECL
+/**
+ * A remote's capabilities.
+ */
typedef enum {
/** Remote supports fetching an advertised object by ID. */
GIT_REMOTE_CAPABILITY_TIP_OID = (1 << 0),
/** Remote supports fetching an individual reachable object. */
GIT_REMOTE_CAPABILITY_REACHABLE_OID = (1 << 1),
+
+ /** Remote supports push options. */
+ GIT_REMOTE_CAPABILITY_PUSH_OPTIONS = (1 << 2),
} git_remote_capability_t;
/**
diff --git a/include/git2/sys/repository.h b/include/git2/sys/repository.h
index 892be669266..080a404c413 100644
--- a/include/git2/sys/repository.h
+++ b/include/git2/sys/repository.h
@@ -9,6 +9,7 @@
#include "git2/common.h"
#include "git2/types.h"
+#include "git2/oid.h"
/**
* @file git2/sys/repository.h
@@ -32,7 +33,11 @@ GIT_BEGIN_DECL
* @param out The blank repository
* @return 0 on success, or an error code
*/
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_repository_new(git_repository **out, git_oid_t oid_type);
+#else
GIT_EXTERN(int) git_repository_new(git_repository **out);
+#endif
/**
* Reset all the internal state in a repository.
diff --git a/include/git2/sys/stream.h b/include/git2/sys/stream.h
index e0e03a2d764..3277088c99c 100644
--- a/include/git2/sys/stream.h
+++ b/include/git2/sys/stream.h
@@ -29,8 +29,22 @@ GIT_BEGIN_DECL
typedef struct git_stream {
int version;
- int encrypted;
- int proxy_support;
+ unsigned int encrypted : 1,
+ proxy_support : 1;
+
+ /**
+ * Timeout for read and write operations; can be set to `0` to
+ * block indefinitely.
+ */
+ int timeout;
+
+ /**
+ * Timeout to connect to the remote server; can be set to `0`
+ * to use the system defaults. This can be shorter than the
+ * system default - often 75 seconds - but cannot be longer.
+ */
+ int connect_timeout;
+
int GIT_CALLBACK(connect)(struct git_stream *);
int GIT_CALLBACK(certificate)(git_cert **, struct git_stream *);
int GIT_CALLBACK(set_proxy)(struct git_stream *, const git_proxy_options *proxy_opts);
diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h
index 930c281e23a..1f9cd932d5b 100644
--- a/include/git2/sys/transport.h
+++ b/include/git2/sys/transport.h
@@ -13,6 +13,7 @@ extern "C" {
#endif
#include "git2/net.h"
+#include "git2/oidarray.h"
#include "git2/proxy.h"
#include "git2/remote.h"
#include "git2/strarray.h"
@@ -29,6 +30,14 @@ extern "C" {
GIT_BEGIN_DECL
+typedef struct {
+ const git_remote_head * const *refs;
+ size_t refs_len;
+ git_oid *shallow_roots;
+ size_t shallow_roots_len;
+ int depth;
+} git_fetch_negotiation;
+
struct git_transport {
unsigned int version; /**< The struct version */
@@ -61,6 +70,18 @@ struct git_transport {
unsigned int *capabilities,
git_transport *transport);
+#ifdef GIT_EXPERIMENTAL_SHA256
+ /**
+ * Gets the object type for the remote repository.
+ *
+ * This function may be called after a successful call to
+ * `connect()`.
+ */
+ int GIT_CALLBACK(oid_type)(
+ git_oid_t *object_type,
+ git_transport *transport);
+#endif
+
/**
* Get the list of available references in the remote repository.
*
@@ -88,8 +109,17 @@ struct git_transport {
int GIT_CALLBACK(negotiate_fetch)(
git_transport *transport,
git_repository *repo,
- const git_remote_head * const *refs,
- size_t count);
+ const git_fetch_negotiation *fetch_data);
+
+ /**
+ * Return the shallow roots of the remote.
+ *
+ * This function may be called after a successful call to
+ * `negotiate_fetch`.
+ */
+ int GIT_CALLBACK(shallow_roots)(
+ git_oidarray *out,
+ git_transport *transport);
/**
* Start downloading the packfile from the remote repository.
diff --git a/include/git2/version.h b/include/git2/version.h
index 09f4ae3e4df..65fd199cf09 100644
--- a/include/git2/version.h
+++ b/include/git2/version.h
@@ -11,16 +11,16 @@
* The version string for libgit2. This string follows semantic
* versioning (v2) guidelines.
*/
-#define LIBGIT2_VERSION "1.6.0-alpha"
+#define LIBGIT2_VERSION "1.8.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 6
+#define LIBGIT2_VER_MINOR 8
/** The revision ("teeny") version number for this version of libgit2. */
-#define LIBGIT2_VER_REVISION 0
+#define LIBGIT2_VER_REVISION 4
/** The Windows DLL patch number for this version of libgit2. */
#define LIBGIT2_VER_PATCH 0
@@ -31,9 +31,13 @@
* a prerelease name like "beta" or "rc1". For final releases, this will
* be `NULL`.
*/
-#define LIBGIT2_VER_PRERELEASE "alpha"
+#define LIBGIT2_VER_PRERELEASE NULL
-/** The library ABI soversion for this version of libgit2. */
-#define LIBGIT2_SOVERSION "1.6"
+/**
+ * The library ABI soversion for this version of libgit2. This should
+ * only be changed when the library has a breaking ABI change, and so
+ * may trail the library's version number.
+ */
+#define LIBGIT2_SOVERSION "1.8"
#endif
diff --git a/include/git2/worktree.h b/include/git2/worktree.h
index bb024dc940e..a6e5d17c4b1 100644
--- a/include/git2/worktree.h
+++ b/include/git2/worktree.h
@@ -11,6 +11,7 @@
#include "buffer.h"
#include "types.h"
#include "strarray.h"
+#include "checkout.h"
/**
* @file git2/worktrees.h
@@ -85,8 +86,9 @@ GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt);
typedef struct git_worktree_add_options {
unsigned int version;
- int lock; /**< lock newly created worktree */
- git_reference *ref; /**< reference to use for the new worktree HEAD */
+ int lock; /**< lock newly created worktree */
+ int checkout_existing; /**< allow checkout of existing branch matching worktree name */
+ git_reference *ref; /**< reference to use for the new worktree HEAD */
/**
* Options for the checkout.
@@ -95,7 +97,8 @@ typedef struct git_worktree_add_options {
} git_worktree_add_options;
#define GIT_WORKTREE_ADD_OPTIONS_VERSION 1
-#define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0,NULL,GIT_CHECKOUT_OPTIONS_INIT}
+#define GIT_WORKTREE_ADD_OPTIONS_INIT { GIT_WORKTREE_ADD_OPTIONS_VERSION, \
+ 0, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT }
/**
* Initialize git_worktree_add_options structure
@@ -237,7 +240,9 @@ GIT_EXTERN(int) git_worktree_prune_options_init(
*
* If the worktree is not valid and not locked or if the above
* flags have been passed in, this function will return a
- * positive value.
+ * positive value. If the worktree is not prunable, an error
+ * message will be set (visible in `giterr_last`) with details about
+ * why.
*
* @param wt Worktree to check.
* @param opts The prunable options.
diff --git a/package.json b/package.json
index 8ef46d7610c..6b8106c43a8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "libgit2",
- "version": "1.6.0-alpha",
+ "version": "1.8.4",
"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/script/valgrind.sh b/script/valgrind.sh
index b5deed2b06e..aacd767a7c8 100755
--- a/script/valgrind.sh
+++ b/script/valgrind.sh
@@ -1,2 +1,2 @@
#!/bin/bash
-exec valgrind --leak-check=full --show-reachable=yes --error-exitcode=125 --num-callers=50 --suppressions="$(dirname "${BASH_SOURCE[0]}")/valgrind.supp" "$@"
+exec valgrind --leak-check=full --show-reachable=yes --child-silent-after-fork=yes --error-exitcode=125 --num-callers=50 --suppressions="$(dirname "${BASH_SOURCE[0]}")/valgrind.supp" "$@"
diff --git a/script/valgrind.supp b/script/valgrind.supp
index 8c4549f62be..79e8378f07e 100644
--- a/script/valgrind.supp
+++ b/script/valgrind.supp
@@ -80,6 +80,13 @@
fun:__check_pf
}
+{
+ ignore-glibc-getaddrinfo-fn
+ Memcheck:Leak
+ ...
+ fun:getaddrinfo
+}
+
{
ignore-curl-global-init
Memcheck:Leak
@@ -191,6 +198,16 @@
...
}
+{
+ ignore-openssl-undefined-in-connect
+ Memcheck:Cond
+ ...
+ obj:*libcrypto.so*
+ ...
+ fun:openssl_connect
+ ...
+}
+
{
ignore-libssh2-rsa-sha1-sign
Memcheck:Leak
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e108b2e79ce..ed3f4a51427 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -41,8 +41,8 @@ include(SelectHTTPSBackend)
include(SelectHashes)
include(SelectHTTPParser)
include(SelectRegex)
+include(SelectXdiff)
include(SelectSSH)
-include(SelectWinHTTP)
include(SelectZlib)
#
@@ -58,20 +58,43 @@ 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)
+# old-style FreeBSD qsort_r() has the 'context' parameter as the first argument
+# of the comparison function:
+check_prototype_definition_safe(qsort_r
+ "void (qsort_r)(void *base, size_t nmemb, size_t size, void *context, int (*compar)(void *, const void *, const void *))"
+ "" "stdlib.h" GIT_QSORT_BSD)
+
+# GNU or POSIX qsort_r() has the 'context' parameter as the last argument of the
+# comparison function:
+check_prototype_definition_safe(qsort_r
+ "void (qsort_r)(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *context)"
+ "" "stdlib.h" GIT_QSORT_GNU)
+
+# C11 qsort_s() has the 'context' parameter as the last argument of the
+# comparison function, and returns an error status:
+check_prototype_definition_safe(qsort_s
+ "errno_t (qsort_s)(void *base, rsize_t nmemb, rsize_t size, int (*compar)(const void *, const void *, void *), void *context)"
+ "0" "stdlib.h" GIT_QSORT_C11)
+
+# MSC qsort_s() has the 'context' parameter as the first argument of the
+# comparison function, and as the last argument of qsort_s():
+check_prototype_definition_safe(qsort_s
+ "void (qsort_s)(void *base, size_t num, size_t width, int (*compare )(void *, const void *, const void *), void *context)"
+ "" "stdlib.h" GIT_QSORT_MSC)
-check_prototype_definition(qsort_r
- "void qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg)"
- "" "stdlib.h" GIT_QSORT_R_GNU)
+# random / entropy data
-check_function_exists(qsort_s GIT_QSORT_S)
+check_symbol_exists(getentropy unistd.h GIT_RAND_GETENTROPY)
+check_symbol_exists(getloadavg stdlib.h GIT_RAND_GETLOADAVG)
-# random / entropy data
+# poll
-check_function_exists(getentropy GIT_RAND_GETENTROPY)
-check_function_exists(getloadavg GIT_RAND_GETLOADAVG)
+if(WIN32)
+ set(GIT_IO_WSAPOLL 1)
+else()
+ check_symbol_exists(poll poll.h GIT_IO_POLL)
+ check_symbol_exists(select sys/select.h GIT_IO_SELECT)
+endif()
# determine architecture of the machine
@@ -112,7 +135,8 @@ endif()
# platform libraries
if(WIN32)
- list(APPEND LIBGIT2_SYSTEM_LIBS ws2_32)
+ list(APPEND LIBGIT2_SYSTEM_LIBS "ws2_32" "secur32")
+ list(APPEND LIBGIT2_PC_LIBS "-lws2_32" "-lsecur32")
endif()
if(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
@@ -160,7 +184,7 @@ add_feature_info(ntlmclient GIT_NTLM "NTLM authentication support for Unix")
# iconv
if(USE_ICONV)
- find_package(Iconv)
+ find_package(IntlIconv)
endif()
if(ICONV_FOUND)
set(GIT_USE_ICONV 1)
diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt
index 46f4d63b504..97797e33bd9 100644
--- a/src/cli/CMakeLists.txt
+++ b/src/cli/CMakeLists.txt
@@ -1,10 +1,11 @@
set(CLI_INCLUDES
"${libgit2_BINARY_DIR}/src/util"
"${libgit2_BINARY_DIR}/include"
- "${libgit2_BINARY_DIR}/include/git2"
"${libgit2_SOURCE_DIR}/src/util"
"${libgit2_SOURCE_DIR}/src/cli"
- "${libgit2_SOURCE_DIR}/include")
+ "${libgit2_SOURCE_DIR}/include"
+ "${LIBGIT2_DEPENDENCY_INCLUDES}"
+ "${LIBGIT2_SYSTEM_INCLUDES}")
if(WIN32 AND NOT CYGWIN)
file(GLOB CLI_SRC_OS win32/*.c)
diff --git a/src/cli/cmd.c b/src/cli/cmd.c
index 2a7e71cdbcb..0b1fafb4423 100644
--- a/src/cli/cmd.c
+++ b/src/cli/cmd.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "cli.h"
+#include "common.h"
#include "cmd.h"
const cli_cmd_spec *cli_cmd_spec_byname(const char *name)
diff --git a/src/cli/cmd.h b/src/cli/cmd.h
index 8b1a1b38fd7..bd881223db9 100644
--- a/src/cli/cmd.h
+++ b/src/cli/cmd.h
@@ -27,7 +27,9 @@ 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_config(int argc, char **argv);
extern int cmd_hash_object(int argc, char **argv);
extern int cmd_help(int argc, char **argv);
+extern int cmd_index_pack(int argc, char **argv);
#endif /* CLI_cmd_h__ */
diff --git a/src/cli/cmd_cat_file.c b/src/cli/cmd_cat_file.c
index fb53a722ba2..90ee6033e8c 100644
--- a/src/cli/cmd_cat_file.c
+++ b/src/cli/cmd_cat_file.c
@@ -6,7 +6,7 @@
*/
#include
-#include "cli.h"
+#include "common.h"
#include "cmd.h"
#define COMMAND_NAME "cat-file"
@@ -24,9 +24,7 @@ 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_COMMON_OPT,
{ CLI_OPT_TYPE_SWITCH, NULL, 't', &display, DISPLAY_TYPE,
CLI_OPT_USAGE_REQUIRED, NULL, "display the type of the object" },
@@ -139,6 +137,7 @@ static int print_pretty(git_object *object)
int cmd_cat_file(int argc, char **argv)
{
+ cli_repository_open_options open_opts = { argv + 1, argc - 1};
git_repository *repo = NULL;
git_object *object = NULL;
git_object_t type;
@@ -153,7 +152,7 @@ int cmd_cat_file(int argc, char **argv)
return 0;
}
- if (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0)
+ if (cli_repository_open(&repo, &open_opts) < 0)
return cli_error_git();
if ((giterr = git_revparse_single(&object, repo, object_spec)) < 0) {
diff --git a/src/cli/cmd_clone.c b/src/cli/cmd_clone.c
index a382b5875df..7d9736fc72a 100644
--- a/src/cli/cmd_clone.c
+++ b/src/cli/cmd_clone.c
@@ -7,7 +7,7 @@
#include
#include
-#include "cli.h"
+#include "common.h"
#include "cmd.h"
#include "error.h"
#include "sighandler.h"
@@ -18,15 +18,13 @@
#define COMMAND_NAME "clone"
-static char *branch, *remote_path, *local_path;
+static char *branch, *remote_path, *local_path, *depth;
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_COMMON_OPT,
{ CLI_OPT_TYPE_SWITCH, "quiet", 'q', &quiet, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "display the type of the object" },
@@ -36,6 +34,8 @@ static const cli_opt_spec opts[] = {
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_VALUE, "depth", 0, &depth, 0,
+ CLI_OPT_USAGE_DEFAULT, "depth", "commit depth to check out " },
{ CLI_OPT_TYPE_LITERAL },
{ CLI_OPT_TYPE_ARG, "repository", 0, &remote_path, 0,
CLI_OPT_USAGE_REQUIRED, "repository", "repository path" },
@@ -71,6 +71,22 @@ static char *compute_local_path(const char *orig_path)
return local_path;
}
+static int compute_depth(const char *depth)
+{
+ int64_t i;
+ const char *endptr;
+
+ if (!depth)
+ return 0;
+
+ if (git__strntol64(&i, depth, strlen(depth), &endptr, 10) < 0 || i < 0 || i > INT_MAX || *endptr) {
+ fprintf(stderr, "fatal: depth '%s' is not valid.\n", depth);
+ exit(128);
+ }
+
+ return (int)i;
+}
+
static bool validate_local_path(const char *path)
{
if (!git_fs_path_exists(path))
@@ -127,11 +143,9 @@ int cmd_clone(int argc, char **argv)
goto done;
}
- if (bare)
- clone_opts.bare = 1;
-
- if (branch)
- clone_opts.checkout_branch = branch;
+ clone_opts.bare = !!bare;
+ clone_opts.checkout_branch = branch;
+ clone_opts.fetch_opts.depth = compute_depth(depth);
if (!checkout)
clone_opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
diff --git a/src/cli/cmd_config.c b/src/cli/cmd_config.c
new file mode 100644
index 00000000000..6b9d373cee6
--- /dev/null
+++ b/src/cli/cmd_config.c
@@ -0,0 +1,242 @@
+/*
+ * 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 "common.h"
+#include "cmd.h"
+
+#define COMMAND_NAME "config"
+
+typedef enum {
+ ACTION_NONE = 0,
+ ACTION_GET,
+ ACTION_ADD,
+ ACTION_REPLACE_ALL,
+ ACTION_LIST
+} action_t;
+
+static action_t action = ACTION_NONE;
+static int show_origin;
+static int show_scope;
+static int show_help;
+static int null_separator;
+static int config_level;
+static char *config_filename;
+static char *name, *value, *value_pattern;
+
+static const cli_opt_spec opts[] = {
+ CLI_COMMON_OPT, \
+
+ { CLI_OPT_TYPE_SWITCH, "null", 'z', &null_separator, 1,
+ 0, NULL, "use NUL as a separator" },
+
+ { CLI_OPT_TYPE_SWITCH, "system", 0, &config_level, GIT_CONFIG_LEVEL_SYSTEM,
+ 0, NULL, "read/write to system configuration" },
+ { CLI_OPT_TYPE_SWITCH, "global", 0, &config_level, GIT_CONFIG_LEVEL_GLOBAL,
+ CLI_OPT_USAGE_CHOICE, NULL, "read/write to global configuration" },
+ { CLI_OPT_TYPE_SWITCH, "local", 0, &config_level, GIT_CONFIG_LEVEL_LOCAL,
+ CLI_OPT_USAGE_CHOICE, NULL, "read/write to local configuration" },
+ { CLI_OPT_TYPE_VALUE, "file", 0, &config_filename, 0,
+ CLI_OPT_USAGE_CHOICE, "filename", "read/write to specified configuration file" },
+
+ { CLI_OPT_TYPE_SWITCH, "get", 0, &action, ACTION_GET,
+ CLI_OPT_USAGE_REQUIRED, NULL, "get a configuration value" },
+ { CLI_OPT_TYPE_SWITCH, "add", 0, &action, ACTION_ADD,
+ CLI_OPT_USAGE_CHOICE, NULL, "add a configuration value" },
+ { CLI_OPT_TYPE_SWITCH, "replace-all", 0, &action, ACTION_REPLACE_ALL,
+ CLI_OPT_USAGE_CHOICE, NULL, "add a configuration value, replacing any old values" },
+ { CLI_OPT_TYPE_SWITCH, "list", 'l', &action, ACTION_LIST,
+ CLI_OPT_USAGE_CHOICE | CLI_OPT_USAGE_SHOW_LONG,
+ NULL, "list all configuration entries" },
+ { CLI_OPT_TYPE_SWITCH, "show-origin", 0, &show_origin, 1,
+ 0, NULL, "show origin of configuration" },
+ { CLI_OPT_TYPE_SWITCH, "show-scope", 0, &show_scope, 1,
+ 0, NULL, "show scope of configuration" },
+ { CLI_OPT_TYPE_ARG, "name", 0, &name, 0,
+ 0, "name", "name of configuration entry" },
+ { CLI_OPT_TYPE_ARG, "value", 0, &value, 0,
+ 0, "value", "value of configuration entry" },
+ { CLI_OPT_TYPE_ARG, "regexp", 0, &value_pattern, 0,
+ 0, "regexp", "regular expression of values to replace" },
+ { 0 },
+};
+
+static void print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Query and set configuration options.\n");
+ printf("\n");
+
+ printf("Options:\n");
+
+ cli_opt_help_fprint(stdout, opts);
+}
+
+static int get_config(git_config *config)
+{
+ git_buf value = GIT_BUF_INIT;
+ char sep = null_separator ? '\0' : '\n';
+ int error;
+
+ error = git_config_get_string_buf(&value, config, name);
+
+ if (error && error != GIT_ENOTFOUND)
+ return cli_error_git();
+
+ else if (error == GIT_ENOTFOUND)
+ return 1;
+
+ printf("%s%c", value.ptr, sep);
+ return 0;
+}
+
+static int add_config(git_config *config)
+{
+ if (git_config_set_multivar(config, name, "$^", value) < 0)
+ return cli_error_git();
+
+ return 0;
+}
+
+static int replace_all_config(git_config *config)
+{
+ if (git_config_set_multivar(config, name, value_pattern ? value_pattern : ".*", value) < 0)
+ return cli_error_git();
+
+ return 0;
+}
+
+static const char *level_name(git_config_level_t level)
+{
+ switch (level) {
+ case GIT_CONFIG_LEVEL_PROGRAMDATA:
+ return "programdata";
+ case GIT_CONFIG_LEVEL_SYSTEM:
+ return "system";
+ case GIT_CONFIG_LEVEL_XDG:
+ return "global";
+ case GIT_CONFIG_LEVEL_GLOBAL:
+ return "global";
+ case GIT_CONFIG_LEVEL_LOCAL:
+ return "local";
+ case GIT_CONFIG_LEVEL_APP:
+ return "command";
+ default:
+ return "unknown";
+ }
+}
+
+static int list_config(git_config *config)
+{
+ git_config_iterator *iterator;
+ git_config_entry *entry;
+ char data_separator = null_separator ? '\0' : '\t';
+ char kv_separator = null_separator ? '\n' : '=';
+ char entry_separator = null_separator ? '\0' : '\n';
+ int error;
+
+ if (git_config_iterator_new(&iterator, config) < 0)
+ return cli_error_git();
+
+ while ((error = git_config_next(&entry, iterator)) == 0) {
+ if (show_scope)
+ printf("%s%c",
+ level_name(entry->level),
+ data_separator);
+
+ if (show_origin)
+ printf("%s%s%s%c",
+ entry->backend_type ? entry->backend_type : "",
+ entry->backend_type && entry->origin_path ? ":" : "",
+ entry->origin_path ? entry->origin_path : "",
+ data_separator);
+
+ printf("%s%c%s%c", entry->name, kv_separator, entry->value,
+ entry_separator);
+ }
+
+ if (error != GIT_ITEROVER)
+ return cli_error_git();
+
+ git_config_iterator_free(iterator);
+ return 0;
+}
+
+int cmd_config(int argc, char **argv)
+{
+ git_repository *repo = NULL;
+ git_config *config = NULL;
+ cli_repository_open_options open_opts = { argv + 1, argc - 1};
+ cli_opt invalid_opt;
+ 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 (config_filename) {
+ if (git_config_new(&config) < 0 ||
+ git_config_add_file_ondisk(config, config_filename,
+ GIT_CONFIG_LEVEL_APP, NULL, 0) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+ } else {
+ if (cli_repository_open(&repo, &open_opts) < 0 ||
+ git_repository_config(&config, repo) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ if (config_level &&
+ git_config_open_level(&config, config, config_level) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+ }
+
+ switch (action) {
+ case ACTION_ADD:
+ if (!name || !value || value_pattern)
+ ret = cli_error_usage("%s --add requires two arguments", COMMAND_NAME);
+ else
+ ret = add_config(config);
+ break;
+ case ACTION_REPLACE_ALL:
+ if (!name || !value)
+ ret = cli_error_usage("%s --replace-all requires two or three arguments", COMMAND_NAME);
+ else
+ ret = replace_all_config(config);
+ break;
+ case ACTION_GET:
+ if (!name)
+ ret = cli_error_usage("%s --get requires an argument", COMMAND_NAME);
+ else
+ ret = get_config(config);
+ break;
+ case ACTION_LIST:
+ if (name)
+ ret = cli_error_usage("%s --list does not take an argument", COMMAND_NAME);
+ else
+ ret = list_config(config);
+ break;
+ default:
+ ret = cli_error_usage("unknown action");
+ }
+
+done:
+ git_config_free(config);
+ git_repository_free(repo);
+ return ret;
+}
diff --git a/src/cli/cmd_hash_object.c b/src/cli/cmd_hash_object.c
index a64db8823a7..741debbeb2f 100644
--- a/src/cli/cmd_hash_object.c
+++ b/src/cli/cmd_hash_object.c
@@ -6,7 +6,7 @@
*/
#include
-#include "cli.h"
+#include "common.h"
#include "cmd.h"
#include "futils.h"
@@ -19,9 +19,7 @@ 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_COMMON_OPT,
{ CLI_OPT_TYPE_VALUE, NULL, 't', &type_name, 0,
CLI_OPT_USAGE_DEFAULT, "type", "the type of object to hash (default: \"blob\")" },
@@ -49,26 +47,37 @@ static void print_help(void)
cli_opt_help_fprint(stdout, opts);
}
-static int hash_buf(git_odb *odb, git_str *buf, git_object_t type)
+static int hash_buf(
+ git_odb *odb,
+ git_str *buf,
+ git_object_t object_type,
+ git_oid_t oid_type)
{
git_oid oid;
if (!literally) {
int valid = 0;
- if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, type) < 0 || !valid)
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, object_type, oid_type) < 0 || !valid)
+ return cli_error_git();
+#else
+ GIT_UNUSED(oid_type);
+
+ if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, object_type) < 0 || !valid)
return cli_error_git();
+#endif
}
if (write_object) {
- if (git_odb_write(&oid, odb, buf->ptr, buf->size, type) < 0)
+ if (git_odb_write(&oid, odb, buf->ptr, buf->size, object_type) < 0)
return cli_error_git();
} else {
#ifdef GIT_EXPERIMENTAL_SHA256
- if (git_odb_hash(&oid, buf->ptr, buf->size, type, GIT_OID_SHA1) < 0)
+ if (git_odb_hash(&oid, buf->ptr, buf->size, object_type, GIT_OID_SHA1) < 0)
return cli_error_git();
#else
- if (git_odb_hash(&oid, buf->ptr, buf->size, type) < 0)
+ if (git_odb_hash(&oid, buf->ptr, buf->size, object_type) < 0)
return cli_error_git();
#endif
}
@@ -81,11 +90,13 @@ static int hash_buf(git_odb *odb, git_str *buf, git_object_t type)
int cmd_hash_object(int argc, char **argv)
{
+ cli_repository_open_options open_opts = { argv + 1, argc - 1};
git_repository *repo = NULL;
git_odb *odb = NULL;
+ git_oid_t oid_type;
git_str buf = GIT_STR_INIT;
cli_opt invalid_opt;
- git_object_t type = GIT_OBJECT_BLOB;
+ git_object_t object_type = GIT_OBJECT_BLOB;
char **filename;
int ret = 0;
@@ -97,16 +108,18 @@ int cmd_hash_object(int argc, char **argv)
return 0;
}
- if (type_name && (type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID)
+ if (type_name && (object_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 ||
+ (cli_repository_open(&repo, &open_opts) < 0 ||
git_repository_odb(&odb, repo) < 0)) {
ret = cli_error_git();
goto done;
}
+ oid_type = git_repository_oid_type(repo);
+
/*
* TODO: we're reading blobs, we shouldn't pull them all into main
* memory, we should just stream them into the odb instead.
@@ -118,7 +131,7 @@ int cmd_hash_object(int argc, char **argv)
goto done;
}
- if ((ret = hash_buf(odb, &buf, type)) != 0)
+ if ((ret = hash_buf(odb, &buf, object_type, oid_type)) != 0)
goto done;
} else {
for (filename = filenames; *filename; filename++) {
@@ -127,7 +140,7 @@ int cmd_hash_object(int argc, char **argv)
goto done;
}
- if ((ret = hash_buf(odb, &buf, type)) != 0)
+ if ((ret = hash_buf(odb, &buf, object_type, oid_type)) != 0)
goto done;
}
}
diff --git a/src/cli/cmd_help.c b/src/cli/cmd_help.c
index 7ee9822427c..5e877e06dbf 100644
--- a/src/cli/cmd_help.c
+++ b/src/cli/cmd_help.c
@@ -7,7 +7,7 @@
#include
#include
-#include "cli.h"
+#include "common.h"
#include "cmd.h"
#define COMMAND_NAME "help"
@@ -16,8 +16,8 @@ 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_COMMON_OPT,
+
{ CLI_OPT_TYPE_ARG, "command", 0, &command, 0,
CLI_OPT_USAGE_DEFAULT, "command", "the command to show help for" },
{ 0 },
diff --git a/src/cli/cmd_index_pack.c b/src/cli/cmd_index_pack.c
new file mode 100644
index 00000000000..09685c5d4db
--- /dev/null
+++ b/src/cli/cmd_index_pack.c
@@ -0,0 +1,114 @@
+/*
+ * 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 "common.h"
+#include "cmd.h"
+#include "progress.h"
+
+#define COMMAND_NAME "index-pack"
+
+#define BUFFER_SIZE (1024 * 1024)
+
+static int show_help, verbose, read_stdin;
+static char *filename;
+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, "verbose", 'v', &verbose, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "display progress output" },
+
+ { CLI_OPT_TYPE_LITERAL },
+
+ { CLI_OPT_TYPE_SWITCH, "stdin", 0, &read_stdin, 1,
+ CLI_OPT_USAGE_REQUIRED, NULL, "read from stdin" },
+ { CLI_OPT_TYPE_ARG, "pack-file", 0, &filename, 0,
+ CLI_OPT_USAGE_CHOICE, "pack-file", "packfile path" },
+
+ { 0 },
+};
+
+static void print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Indexes a packfile and writes the index to disk.\n");
+ printf("\n");
+
+ printf("Options:\n");
+
+ cli_opt_help_fprint(stdout, opts);
+}
+
+int cmd_index_pack(int argc, char **argv)
+{
+ cli_opt invalid_opt;
+ git_indexer *idx = NULL;
+ git_indexer_options idx_opts = GIT_INDEXER_OPTIONS_INIT;
+ git_indexer_progress stats = {0};
+ char buf[BUFFER_SIZE];
+ ssize_t read_len;
+ int fd, ret;
+
+ 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 (verbose) {
+ idx_opts.progress_cb = cli_progress_indexer;
+ idx_opts.progress_cb_payload = &progress;
+ }
+
+ if (read_stdin) {
+ fd = fileno(stdin);
+ } else if ((fd = p_open(filename, O_RDONLY)) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ ret = git_indexer_new(&idx, ".", GIT_OID_SHA1, &idx_opts);
+#else
+ ret = git_indexer_new(&idx, ".", 0, NULL, &idx_opts);
+#endif
+
+ if (ret < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ while ((read_len = p_read(fd, buf, sizeof(buf))) > 0) {
+ if (git_indexer_append(idx, buf, (size_t)read_len, &stats) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+ }
+
+ if (git_indexer_commit(idx, &stats) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ cli_progress_finish(&progress);
+
+done:
+ if (!read_stdin && fd >= 0)
+ p_close(fd);
+
+ cli_progress_dispose(&progress);
+ git_indexer_free(idx);
+ return ret;
+}
diff --git a/src/cli/common.c b/src/cli/common.c
new file mode 100644
index 00000000000..60b0358662b
--- /dev/null
+++ b/src/cli/common.c
@@ -0,0 +1,126 @@
+/*
+ * 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 "vector.h"
+
+#include "common.h"
+#include "error.h"
+
+static int parse_option(cli_opt *opt, void *data)
+{
+ git_str kv = GIT_STR_INIT, env = GIT_STR_INIT;
+ git_vector *cmdline_config = data;
+ int error = 0;
+
+ if (opt->spec && opt->spec->alias == 'c') {
+ if (git_str_puts(&kv, opt->value) < 0) {
+ error = cli_error_git();
+ goto done;
+ }
+ }
+
+ else if (opt->spec && !strcmp(opt->spec->name, "config-env")) {
+ char *val = strchr(opt->value, '=');
+
+ if (val == NULL || *(val + 1) == '\0') {
+ error = cli_error("invalid config format: '%s'", opt->value);
+ goto done;
+ }
+
+ if (git_str_put(&kv, opt->value, (val - opt->value)) < 0) {
+ error = cli_error_git();
+ goto done;
+ }
+
+ val++;
+
+ if ((error = git__getenv(&env, val)) == GIT_ENOTFOUND) {
+ error = cli_error("missing environment variable '%s' for configuration '%s'", val, kv.ptr);
+ goto done;
+ } else if (error) {
+ error = cli_error_git();
+ goto done;
+ }
+
+ if (git_str_putc(&kv, '=') < 0 ||
+ git_str_puts(&kv, env.ptr) < 0) {
+ error = cli_error_git();
+ goto done;
+ }
+ }
+
+ if (kv.size > 0 &&
+ git_vector_insert(cmdline_config, git_str_detach(&kv)) < 0)
+ error = cli_error_git();
+
+done:
+ git_str_dispose(&env);
+ git_str_dispose(&kv);
+ return error;
+}
+
+static int parse_common_options(
+ git_repository *repo,
+ cli_repository_open_options *opts)
+{
+ cli_opt_spec common_opts[] = {
+ { CLI_COMMON_OPT_CONFIG },
+ { CLI_COMMON_OPT_CONFIG_ENV },
+ { 0 }
+ };
+ git_config_backend_memory_options config_opts =
+ GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT;
+ git_vector cmdline = GIT_VECTOR_INIT;
+ git_config *config = NULL;
+ git_config_backend *backend = NULL;
+ int error = 0;
+
+ config_opts.backend_type = "command line";
+
+ if ((error = cli_opt_foreach(common_opts, opts->args,
+ opts->args_len, CLI_OPT_PARSE_GNU, parse_option,
+ &cmdline)) < 0)
+ goto done;
+
+ if (git_vector_length(&cmdline) == 0)
+ goto done;
+
+ if (git_repository_config(&config, repo) < 0 ||
+ git_config_backend_from_values(&backend,
+ (const char **)cmdline.contents, cmdline.length,
+ &config_opts) < 0 ||
+ git_config_add_backend(config, backend, GIT_CONFIG_LEVEL_APP,
+ repo, 0) < 0)
+ error = cli_error_git();
+
+done:
+ if (error && backend)
+ backend->free(backend);
+ git_config_free(config);
+ git_vector_free_deep(&cmdline);
+ return error;
+}
+
+int cli_repository_open(
+ git_repository **out,
+ cli_repository_open_options *opts)
+{
+ git_repository *repo;
+
+ if (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0)
+ return -1;
+
+ if (opts && parse_common_options(repo, opts) < 0)
+ return -1;
+
+ *out = repo;
+ return 0;
+}
diff --git a/src/cli/common.h b/src/cli/common.h
new file mode 100644
index 00000000000..3aed8ad8a42
--- /dev/null
+++ b/src/cli/common.h
@@ -0,0 +1,66 @@
+/*
+ * 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_common_h__
+#define CLI_common_h__
+
+#define PROGRAM_NAME "git2"
+
+#include "git2_util.h"
+
+#include "error.h"
+#include "opt.h"
+#include "opt_usage.h"
+
+/*
+ * Common command arguments.
+ */
+
+#define CLI_COMMON_OPT_HELP \
+ CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1, \
+ CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING
+#define CLI_COMMON_OPT_CONFIG \
+ CLI_OPT_TYPE_VALUE, NULL, 'c', NULL, 0, \
+ CLI_OPT_USAGE_HIDDEN
+#define CLI_COMMON_OPT_CONFIG_ENV \
+ CLI_OPT_TYPE_VALUE, "config-env", 0, NULL, 0, \
+ CLI_OPT_USAGE_HIDDEN
+
+#define CLI_COMMON_OPT \
+ { CLI_COMMON_OPT_HELP }, \
+ { CLI_COMMON_OPT_CONFIG }, \
+ { CLI_COMMON_OPT_CONFIG_ENV }
+
+typedef struct {
+ char **args;
+ int args_len;
+} cli_repository_open_options;
+
+extern int cli_repository_open(
+ git_repository **out,
+ cli_repository_open_options *opts);
+
+/*
+ * Common command arguments.
+ */
+
+#define CLI_COMMON_OPT_HELP \
+ CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1, \
+ CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING
+#define CLI_COMMON_OPT_CONFIG \
+ CLI_OPT_TYPE_VALUE, NULL, 'c', NULL, 0, \
+ CLI_OPT_USAGE_HIDDEN
+#define CLI_COMMON_OPT_CONFIG_ENV \
+ CLI_OPT_TYPE_VALUE, "config-env", 0, NULL, 0, \
+ CLI_OPT_USAGE_HIDDEN
+
+#define CLI_COMMON_OPT \
+ { CLI_COMMON_OPT_HELP }, \
+ { CLI_COMMON_OPT_CONFIG }, \
+ { CLI_COMMON_OPT_CONFIG_ENV }
+
+#endif /* CLI_common_h__ */
diff --git a/src/cli/error.h b/src/cli/error.h
index cce7a54c093..abf8a5160d1 100644
--- a/src/cli/error.h
+++ b/src/cli/error.h
@@ -8,7 +8,7 @@
#ifndef CLI_error_h__
#define CLI_error_h__
-#include "cli.h"
+#include "common.h"
#include
#define CLI_EXIT_OK 0
diff --git a/src/cli/main.c b/src/cli/main.c
index cbfc50eec35..c7a6fcfce26 100644
--- a/src/cli/main.c
+++ b/src/cli/main.c
@@ -7,7 +7,7 @@
#include
#include
-#include "cli.h"
+#include "common.h"
#include "cmd.h"
static int show_help = 0;
@@ -16,8 +16,12 @@ 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, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "display help information" },
+ { CLI_OPT_TYPE_VALUE, NULL, 'c', NULL, 0,
+ CLI_OPT_USAGE_DEFAULT, "key=value", "add configuration value" },
+ { CLI_OPT_TYPE_VALUE, "config-env", 0, NULL, 0,
+ CLI_OPT_USAGE_DEFAULT, "key=value", "set configuration value to environment variable" },
{ 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,
@@ -30,19 +34,40 @@ const cli_opt_spec cli_common_opts[] = {
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" },
+ { "config", cmd_config, "View or set configuration values " },
{ "hash-object", cmd_hash_object, "Hash a raw object and product its object ID" },
{ "help", cmd_help, "Display help information" },
+ { "index-pack", cmd_index_pack, "Create an index for a packfile" },
{ NULL }
};
+/*
+ * Reorder the argv as it was given, since git has the notion of global
+ * options (like `--help` or `-c key=val`) that we want to pass to the
+ * subcommand, and that can appear early in the arguments, before the
+ * command name. Put the command-name in argv[1] to allow easier parsing.
+ */
+static void reorder_args(char **argv, size_t first)
+{
+ char *tmp;
+ size_t i;
+
+ if (first == 1)
+ return;
+
+ tmp = argv[first];
+
+ for (i = first; i > 1; i--)
+ argv[i] = argv[i - 1];
+
+ argv[1] = tmp;
+}
+
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) {
@@ -66,8 +91,7 @@ int main(int argc, char **argv)
* remaining arguments as args for the command itself.
*/
if (command) {
- args = &argv[optparser.idx];
- args_len = (int)(argc - optparser.idx);
+ reorder_args(argv, optparser.idx);
break;
}
}
@@ -77,19 +101,9 @@ int main(int argc, char **argv)
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 (!command) {
+ ret = cmd_help(argc, argv);
+ goto done;
}
if ((cmd = cli_cmd_spec_byname(command)) == NULL) {
@@ -98,7 +112,7 @@ int main(int argc, char **argv)
goto done;
}
- ret = cmd->fn(args_len, args);
+ ret = cmd->fn(argc - 1, &argv[1]);
done:
git_libgit2_shutdown();
diff --git a/src/cli/opt.c b/src/cli/opt.c
index 62a3430d16e..9242e2203b4 100644
--- a/src/cli/opt.c
+++ b/src/cli/opt.c
@@ -10,7 +10,7 @@
* 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
+ * ./rename.pl cli_opt --filename=opt --include=common.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
*/
#include
@@ -19,7 +19,11 @@
#include
#include
-#include "cli.h"
+#if defined(__sun) || defined(__illumos__)
+# include
+#endif
+
+#include "common.h"
#include "opt.h"
#ifdef _WIN32
@@ -73,7 +77,7 @@ GIT_INLINE(const cli_opt_spec *) spec_for_long(
/* Handle --option=value arguments */
if (spec->type == CLI_OPT_TYPE_VALUE &&
- eql &&
+ spec->name && eql &&
strncmp(arg, spec->name, eql_pos) == 0 &&
spec->name[eql_pos] == '\0') {
*has_value = 1;
@@ -575,6 +579,28 @@ cli_opt_status_t cli_opt_parse(
return validate_required(opt, specs, given_specs);
}
+int cli_opt_foreach(
+ const cli_opt_spec specs[],
+ char **args,
+ size_t args_len,
+ unsigned int flags,
+ int (*callback)(cli_opt *, void *),
+ void *callback_data)
+{
+ cli_opt_parser parser;
+ cli_opt opt;
+ int ret;
+
+ cli_opt_parser_init(&parser, specs, args, args_len, flags);
+
+ while (cli_opt_parser_next(&opt, &parser)) {
+ if ((ret = callback(&opt, callback_data)) != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
static int spec_name_fprint(FILE *file, const cli_opt_spec *spec)
{
int error;
diff --git a/src/cli/opt.h b/src/cli/opt.h
index 6c1d4603ecd..226f74db8bc 100644
--- a/src/cli/opt.h
+++ b/src/cli/opt.h
@@ -10,7 +10,7 @@
* 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
+ * ./rename.pl cli_opt --filename=opt --include=common.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
*/
#ifndef CLI_opt_h__
@@ -275,8 +275,8 @@ typedef struct cli_opt_parser {
size_t arg_idx;
size_t in_args;
size_t in_short;
- int needs_sort : 1,
- in_literal : 1;
+ unsigned int needs_sort : 1,
+ in_literal : 1;
} cli_opt_parser;
/**
@@ -300,6 +300,24 @@ cli_opt_status_t cli_opt_parse(
size_t args_len,
unsigned int flags);
+/**
+ * Quickly executes the given callback for each argument.
+ *
+ * @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
+ * @param callback The callback to invoke for each specified option
+ * @param callback_data Data to be provided to the callback
+ */
+int cli_opt_foreach(
+ const cli_opt_spec specs[],
+ char **args,
+ size_t args_len,
+ unsigned int flags,
+ int (*callback)(cli_opt *, void *),
+ void *callback_data);
+
/**
* Initializes a parser that parses the given arguments according to the
* given specifications.
diff --git a/src/cli/opt_usage.c b/src/cli/opt_usage.c
index 478b416316d..8374f5151a7 100644
--- a/src/cli/opt_usage.c
+++ b/src/cli/opt_usage.c
@@ -5,7 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "cli.h"
+#include "common.h"
#include "str.h"
static int print_spec_name(git_str *out, const cli_opt_spec *spec)
diff --git a/src/cli/progress.c b/src/cli/progress.c
index ba52655e75e..d975b0954ac 100644
--- a/src/cli/progress.c
+++ b/src/cli/progress.c
@@ -15,10 +15,10 @@
/*
* 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.
+ * avoiding too much noise on the screen. (In milliseconds.)
*/
-#define PROGRESS_UPDATE_TIME 0.10
-#define THROUGHPUT_UPDATE_TIME 1.00
+#define PROGRESS_UPDATE_TIME 60
+#define THROUGHPUT_UPDATE_TIME 500
#define is_nl(c) ((c) == '\r' || (c) == '\n')
@@ -54,7 +54,7 @@ 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();
+ uint64_t now = git_time_monotonic();
size_t i;
/* Avoid spamming the console with progress updates */
@@ -191,20 +191,21 @@ static int fetch_receiving(
{
char *recv_units[] = { "B", "KiB", "MiB", "GiB", "TiB", NULL };
char *rate_units[] = { "B/s", "KiB/s", "MiB/s", "GiB/s", "TiB/s", NULL };
+ uint64_t now, elapsed;
- double now, recv_len, rate, elapsed;
+ double recv_len, rate;
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();
+ progress->action_start = git_time_monotonic();
if (done && progress->action_finish)
now = progress->action_finish;
else if (done)
- progress->action_finish = now = git__timer();
+ progress->action_finish = now = git_time_monotonic();
else
- now = git__timer();
+ now = git_time_monotonic();
if (progress->throughput_update &&
now - progress->throughput_update < THROUGHPUT_UPDATE_TIME) {
@@ -241,7 +242,21 @@ static int fetch_receiving(
done ? ", done." : "");
}
-static int fetch_resolving(
+static int indexer_indexing(
+ cli_progress *progress,
+ const git_indexer_progress *stats)
+{
+ bool done = (stats->received_objects == stats->total_objects);
+
+ return progress_printf(progress, false,
+ "Indexing objects: %3d%% (%d/%d)%s\r",
+ percent(stats->received_objects, stats->total_objects),
+ stats->received_objects,
+ stats->total_objects,
+ done ? ", done." : "");
+}
+
+static int indexer_resolving(
cli_progress *progress,
const git_indexer_progress *stats)
{
@@ -282,7 +297,42 @@ int cli_progress_fetch_transfer(const git_indexer_progress *stats, void *payload
/* fall through */
case CLI_PROGRESS_RESOLVING:
- error = fetch_resolving(progress, stats);
+ error = indexer_resolving(progress, stats);
+ break;
+
+ default:
+ /* should not be reached */
+ GIT_ASSERT(!"unexpected progress state");
+ }
+
+ return error;
+}
+
+int cli_progress_indexer(
+ 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_INDEXING;
+ /* fall through */
+
+ case CLI_PROGRESS_INDEXING:
+ if ((error = indexer_indexing(progress, stats)) < 0)
+ break;
+
+ if (stats->indexed_deltas == stats->total_deltas)
+ break;
+
+ progress_complete(progress);
+ progress->action = CLI_PROGRESS_RESOLVING;
+ /* fall through */
+
+ case CLI_PROGRESS_RESOLVING:
+ error = indexer_resolving(progress, stats);
break;
default:
diff --git a/src/cli/progress.h b/src/cli/progress.h
index 7a445ec2966..f08d68f19e4 100644
--- a/src/cli/progress.h
+++ b/src/cli/progress.h
@@ -22,6 +22,7 @@
typedef enum {
CLI_PROGRESS_NONE,
CLI_PROGRESS_RECEIVING,
+ CLI_PROGRESS_INDEXING,
CLI_PROGRESS_RESOLVING,
CLI_PROGRESS_CHECKING_OUT
} cli_progress_t;
@@ -30,11 +31,11 @@ typedef struct {
cli_progress_t action;
/* Actions may time themselves (eg fetch) but are not required to */
- double action_start;
- double action_finish;
+ uint64_t action_start;
+ uint64_t action_finish;
/* Last console update, avoid too frequent updates. */
- double last_update;
+ uint64_t last_update;
/* Accumulators for partial output and deferred updates. */
git_str sideband;
@@ -42,7 +43,7 @@ typedef struct {
git_str deferred;
/* Last update about throughput */
- double throughput_update;
+ uint64_t throughput_update;
double throughput_bytes;
} cli_progress;
@@ -74,6 +75,17 @@ extern int cli_progress_fetch_transfer(
const git_indexer_progress *stats,
void *payload);
+/**
+ * Prints indexer progress to the console. Suitable for a
+ * `progress_cb` callback for `git_indexer_options`.
+ *
+ * @param stats The indexer stats
+ * @param payload A pointer to the cli_progress
+ */
+extern int cli_progress_indexer(
+ const git_indexer_progress *stats,
+ void *payload);
+
/**
* Prints checkout progress to the console. Suitable for a
* `progress_cb` callback for `git_checkout_options`.
diff --git a/src/cli/unix/sighandler.c b/src/cli/unix/sighandler.c
index 6b4982d48f2..05ac8672cad 100644
--- a/src/cli/unix/sighandler.c
+++ b/src/cli/unix/sighandler.c
@@ -8,7 +8,8 @@
#include
#include
#include "git2_util.h"
-#include "cli.h"
+#include "common.h"
+#include "sighandler.h"
static void (*interrupt_handler)(void) = NULL;
diff --git a/src/cli/win32/precompiled.h b/src/cli/win32/precompiled.h
index b0309b864ad..031370e87e7 100644
--- a/src/cli/win32/precompiled.h
+++ b/src/cli/win32/precompiled.h
@@ -1,3 +1,3 @@
#include
-#include "cli.h"
+#include "common.h"
diff --git a/src/cli/win32/sighandler.c b/src/cli/win32/sighandler.c
index cc0b6464033..05a67fb14fe 100644
--- a/src/cli/win32/sighandler.c
+++ b/src/cli/win32/sighandler.c
@@ -8,7 +8,7 @@
#include "git2_util.h"
#include
-#include "cli.h"
+#include "sighandler.h"
static void (*interrupt_handler)(void) = NULL;
diff --git a/src/libgit2/CMakeLists.txt b/src/libgit2/CMakeLists.txt
index 3462b795e22..bc7cb5b3597 100644
--- a/src/libgit2/CMakeLists.txt
+++ b/src/libgit2/CMakeLists.txt
@@ -10,21 +10,10 @@ include(PkgBuildConfig)
set(LIBGIT2_INCLUDES
"${PROJECT_BINARY_DIR}/src/util"
"${PROJECT_BINARY_DIR}/include"
- "${PROJECT_BINARY_DIR}/include/git2"
"${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"
@@ -35,8 +24,7 @@ target_sources(libgit2 PRIVATE ${SRC_H})
file(GLOB SRC_GIT2 *.c *.h
streams/*.c streams/*.h
- transports/*.c transports/*.h
- xdiff/*.c xdiff/*.h)
+ transports/*.c transports/*.h)
list(SORT SRC_GIT2)
target_sources(libgit2 PRIVATE ${SRC_GIT2})
@@ -50,25 +38,9 @@ if(APPLE)
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})
+list(APPEND LIBGIT2_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES})
target_include_directories(libgit2 PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${PROJECT_SOURCE_DIR}/include)
target_include_directories(libgit2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
@@ -86,18 +58,13 @@ set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE)
add_library(libgit2package ${SRC_RC} ${LIBGIT2_OBJECTS})
target_link_libraries(libgit2package ${LIBGIT2_SYSTEM_LIBS})
+target_include_directories(libgit2package SYSTEM PRIVATE ${LIBGIT2_INCLUDES})
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)
@@ -111,10 +78,10 @@ if(SONAME)
endif()
endif()
-pkg_build_config(NAME "${LIBGIT2_FILENAME}"
+pkg_build_config(NAME "lib${LIBGIT2_FILENAME}"
VERSION ${libgit2_VERSION}
DESCRIPTION "The git library, take 2"
- LIBS_SELF git2
+ LIBS_SELF ${LIBGIT2_FILENAME}
PRIVATE_LIBS ${LIBGIT2_PC_LIBS}
REQUIRES ${LIBGIT2_PC_REQUIRES})
diff --git a/src/libgit2/annotated_commit.c b/src/libgit2/annotated_commit.c
index 7bd8b6077f9..c5c8ace789d 100644
--- a/src/libgit2/annotated_commit.c
+++ b/src/libgit2/annotated_commit.c
@@ -39,8 +39,8 @@ static int annotated_commit_init(
if ((error = git_commit_dup(&annotated_commit->commit, commit)) < 0)
goto done;
- git_oid_fmt(annotated_commit->id_str, git_commit_id(commit));
- annotated_commit->id_str[GIT_OID_SHA1_HEXSIZE] = '\0';
+ git_oid_tostr(annotated_commit->id_str, GIT_OID_MAX_HEXSIZE + 1,
+ git_commit_id(commit));
if (!description)
description = annotated_commit->id_str;
diff --git a/src/libgit2/annotated_commit.h b/src/libgit2/annotated_commit.h
index c87eaa8057b..1f805fe9bda 100644
--- a/src/libgit2/annotated_commit.h
+++ b/src/libgit2/annotated_commit.h
@@ -41,7 +41,7 @@ struct git_annotated_commit {
const char *ref_name;
const char *remote_url;
- char id_str[GIT_OID_SHA1_HEXSIZE+1];
+ char id_str[GIT_OID_MAX_HEXSIZE + 1];
};
extern int git_annotated_commit_from_head(git_annotated_commit **out,
diff --git a/src/libgit2/apply.c b/src/libgit2/apply.c
index 18304da4df7..6b55b812fa4 100644
--- a/src/libgit2/apply.c
+++ b/src/libgit2/apply.c
@@ -19,6 +19,7 @@
#include "zstream.h"
#include "reader.h"
#include "index.h"
+#include "repository.h"
#include "apply.h"
typedef struct {
@@ -644,7 +645,7 @@ int git_apply_to_tree(
* put the current tree into the postimage as-is - the diff will
* replace any entries contained therein
*/
- if ((error = git_index_new(&postimage)) < 0 ||
+ if ((error = git_index__new(&postimage, repo->oid_type)) < 0 ||
(error = git_index_read_tree(postimage, preimage)) < 0 ||
(error = git_reader_for_index(&post_reader, repo, postimage)) < 0)
goto done;
@@ -851,8 +852,8 @@ int git_apply(
* having the full repo index, so we will limit our checkout
* to only write these files that were affected by the diff.
*/
- if ((error = git_index_new(&preimage)) < 0 ||
- (error = git_index_new(&postimage)) < 0 ||
+ if ((error = git_index__new(&preimage, repo->oid_type)) < 0 ||
+ (error = git_index__new(&postimage, repo->oid_type)) < 0 ||
(error = git_reader_for_index(&post_reader, repo, postimage)) < 0)
goto done;
diff --git a/src/libgit2/attr.c b/src/libgit2/attr.c
index 1623b1d4570..1db90b59c7e 100644
--- a/src/libgit2/attr.c
+++ b/src/libgit2/attr.c
@@ -424,9 +424,13 @@ static int attr_setup(
goto out;
if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
- (error = preload_attr_source(repo, attr_session, &index_source)) < 0)
+ (error = preload_attr_source(repo, attr_session, &index_source)) < 0) {
+ if (error != GIT_ENOTFOUND)
goto out;
+ error = 0;
+ }
+
if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) &&
(error = preload_attr_source(repo, attr_session, &head_source)) < 0)
goto out;
diff --git a/src/libgit2/attrcache.c b/src/libgit2/attrcache.c
index b16d95c3c02..405944ed156 100644
--- a/src/libgit2/attrcache.c
+++ b/src/libgit2/attrcache.c
@@ -300,7 +300,7 @@ static int attr_cache__lookup_path(
/* expand leading ~/ as needed */
if (cfgval && cfgval[0] == '~' && cfgval[1] == '/') {
- if (! (error = git_sysdir_expand_global_file(&buf, &cfgval[2])))
+ if (! (error = git_sysdir_expand_homedir_file(&buf, &cfgval[2])))
*out = git_str_detach(&buf);
} else if (cfgval) {
*out = git__strdup(cfgval);
diff --git a/src/libgit2/blame.c b/src/libgit2/blame.c
index b70cd615ec8..2ed7d2011f7 100644
--- a/src/libgit2/blame.c
+++ b/src/libgit2/blame.c
@@ -60,10 +60,11 @@ static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line)
}
static git_blame_hunk *new_hunk(
- size_t start,
- size_t lines,
- size_t orig_start,
- const char *path)
+ size_t start,
+ size_t lines,
+ size_t orig_start,
+ const char *path,
+ git_blame *blame)
{
git_blame_hunk *hunk = git__calloc(1, sizeof(git_blame_hunk));
if (!hunk) return NULL;
@@ -72,8 +73,8 @@ static git_blame_hunk *new_hunk(
hunk->final_start_line_number = start;
hunk->orig_start_line_number = orig_start;
hunk->orig_path = path ? git__strdup(path) : NULL;
- git_oid_clear(&hunk->orig_commit_id, GIT_OID_SHA1);
- git_oid_clear(&hunk->final_commit_id, GIT_OID_SHA1);
+ git_oid_clear(&hunk->orig_commit_id, blame->repository->oid_type);
+ git_oid_clear(&hunk->final_commit_id, blame->repository->oid_type);
return hunk;
}
@@ -86,13 +87,14 @@ static void free_hunk(git_blame_hunk *hunk)
git__free(hunk);
}
-static git_blame_hunk *dup_hunk(git_blame_hunk *hunk)
+static git_blame_hunk *dup_hunk(git_blame_hunk *hunk, git_blame *blame)
{
git_blame_hunk *newhunk = new_hunk(
hunk->final_start_line_number,
hunk->lines_in_hunk,
hunk->orig_start_line_number,
- hunk->orig_path);
+ hunk->orig_path,
+ blame);
if (!newhunk)
return NULL;
@@ -115,12 +117,12 @@ static git_blame_hunk *dup_hunk(git_blame_hunk *hunk)
static void shift_hunks_by(git_vector *v, size_t start_line, int shift_by)
{
size_t i;
-
- if (!git_vector_bsearch2(&i, v, hunk_byfinalline_search_cmp, &start_line)) {
- for (; i < v->length; i++) {
- git_blame_hunk *hunk = (git_blame_hunk*)v->contents[i];
- hunk->final_start_line_number += shift_by;
+ for (i = 0; i < v->length; i++) {
+ git_blame_hunk *hunk = (git_blame_hunk*)v->contents[i];
+ if(hunk->final_start_line_number < start_line){
+ continue;
}
+ hunk->final_start_line_number += shift_by;
}
}
@@ -237,7 +239,8 @@ static git_blame_hunk *split_hunk_in_vector(
git_vector *vec,
git_blame_hunk *hunk,
size_t rel_line,
- bool return_new)
+ bool return_new,
+ git_blame *blame)
{
size_t new_line_count;
git_blame_hunk *nh;
@@ -250,8 +253,9 @@ static git_blame_hunk *split_hunk_in_vector(
}
new_line_count = hunk->lines_in_hunk - rel_line;
- nh = new_hunk(hunk->final_start_line_number + rel_line, new_line_count,
- hunk->orig_start_line_number + rel_line, hunk->orig_path);
+ nh = new_hunk(hunk->final_start_line_number + rel_line,
+ new_line_count, hunk->orig_start_line_number + rel_line,
+ hunk->orig_path, blame);
if (!nh)
return NULL;
@@ -304,7 +308,8 @@ static int index_blob_lines(git_blame *blame)
static git_blame_hunk *hunk_from_entry(git_blame__entry *e, git_blame *blame)
{
git_blame_hunk *h = new_hunk(
- e->lno+1, e->num_lines, e->s_lno+1, e->suspect->path);
+ e->lno+1, e->num_lines, e->s_lno+1, e->suspect->path,
+ blame);
if (!h)
return NULL;
@@ -439,20 +444,21 @@ static int buffer_hunk_cb(
GIT_UNUSED(delta);
- wedge_line = (hunk->old_lines == 0) ? hunk->new_start : hunk->old_start;
+ wedge_line = (hunk->new_start >= hunk->old_start || hunk->old_lines==0) ? hunk->new_start : hunk->old_start;
blame->current_diff_line = wedge_line;
-
blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byline(blame, wedge_line);
if (!blame->current_hunk) {
/* Line added at the end of the file */
- blame->current_hunk = new_hunk(wedge_line, 0, wedge_line, blame->path);
+ blame->current_hunk = new_hunk(wedge_line, 0, wedge_line,
+ blame->path, blame);
+ blame->current_diff_line++;
GIT_ERROR_CHECK_ALLOC(blame->current_hunk);
-
git_vector_insert(&blame->hunks, blame->current_hunk);
} else if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){
/* If this hunk doesn't start between existing hunks, split a hunk up so it does */
blame->current_hunk = split_hunk_in_vector(&blame->hunks, blame->current_hunk,
- wedge_line - blame->current_hunk->orig_start_line_number, true);
+ wedge_line - blame->current_hunk->final_start_line_number, true,
+ blame);
GIT_ERROR_CHECK_ALLOC(blame->current_hunk);
}
@@ -477,13 +483,12 @@ static int buffer_line_cb(
hunk_ends_at_or_before_line(blame->current_hunk, blame->current_diff_line)) {
/* Append to the current buffer-blame hunk */
blame->current_hunk->lines_in_hunk++;
- shift_hunks_by(&blame->hunks, blame->current_diff_line+1, 1);
+ shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
} else {
/* Create a new buffer-blame hunk with this line */
shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
- blame->current_hunk = new_hunk(blame->current_diff_line, 1, 0, blame->path);
+ blame->current_hunk = new_hunk(blame->current_diff_line, 1, 0, blame->path, blame);
GIT_ERROR_CHECK_ALLOC(blame->current_hunk);
-
git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL);
}
blame->current_diff_line++;
@@ -491,15 +496,16 @@ static int buffer_line_cb(
if (line->origin == GIT_DIFF_LINE_DELETION) {
/* Trim the line from the current hunk; remove it if it's now empty */
- size_t shift_base = blame->current_diff_line + blame->current_hunk->lines_in_hunk+1;
+ size_t shift_base = blame->current_diff_line + blame->current_hunk->lines_in_hunk;
if (--(blame->current_hunk->lines_in_hunk) == 0) {
size_t i;
- shift_base--;
+ size_t i_next;
if (!git_vector_search2(&i, &blame->hunks, ptrs_equal_cmp, blame->current_hunk)) {
git_vector_remove(&blame->hunks, i);
free_hunk(blame->current_hunk);
- blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byindex(blame, (uint32_t)i);
+ i_next = min( i , blame->hunks.length -1);
+ blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byindex(blame, (uint32_t)i_next);
}
}
shift_hunks_by(&blame->hunks, shift_base, -1);
@@ -529,7 +535,7 @@ int git_blame_buffer(
/* Duplicate all of the hunk structures in the reference blame */
git_vector_foreach(&reference->hunks, i, hunk) {
- git_blame_hunk *h = dup_hunk(hunk);
+ git_blame_hunk *h = dup_hunk(hunk, blame);
GIT_ERROR_CHECK_ALLOC(h);
git_vector_insert(&blame->hunks, h);
diff --git a/src/libgit2/blame_git.c b/src/libgit2/blame_git.c
index 2504b338ac5..69897b3860c 100644
--- a/src/libgit2/blame_git.c
+++ b/src/libgit2/blame_git.c
@@ -9,7 +9,6 @@
#include "commit.h"
#include "blob.h"
-#include "xdiff/xinclude.h"
#include "diff_xdiff.h"
/*
diff --git a/src/libgit2/blob.c b/src/libgit2/blob.c
index b1680d3a80c..5cfd7474b71 100644
--- a/src/libgit2/blob.c
+++ b/src/libgit2/blob.c
@@ -52,11 +52,12 @@ void git_blob__free(void *_blob)
git__free(blob);
}
-int git_blob__parse_raw(void *_blob, const char *data, size_t size)
+int git_blob__parse_raw(void *_blob, const char *data, size_t size, git_oid_t oid_type)
{
git_blob *blob = (git_blob *) _blob;
GIT_ASSERT_ARG(blob);
+ GIT_UNUSED(oid_type);
blob->raw = 1;
blob->data.raw.data = data;
@@ -64,11 +65,12 @@ int git_blob__parse_raw(void *_blob, const char *data, size_t size)
return 0;
}
-int git_blob__parse(void *_blob, git_odb_object *odb_obj)
+int git_blob__parse(void *_blob, git_odb_object *odb_obj, git_oid_t oid_type)
{
git_blob *blob = (git_blob *) _blob;
GIT_ASSERT_ARG(blob);
+ GIT_UNUSED(oid_type);
git_cached_obj_incref((git_cached_obj *)odb_obj);
blob->raw = 0;
diff --git a/src/libgit2/blob.h b/src/libgit2/blob.h
index 9a5dda2254e..d6c9dd99b07 100644
--- a/src/libgit2/blob.h
+++ b/src/libgit2/blob.h
@@ -36,8 +36,8 @@ struct git_blob {
} while(0)
void git_blob__free(void *blob);
-int git_blob__parse(void *blob, git_odb_object *obj);
-int git_blob__parse_raw(void *blob, const char *data, size_t size);
+int git_blob__parse(void *blob, git_odb_object *obj, git_oid_t oid_type);
+int git_blob__parse_raw(void *blob, const char *data, size_t size, git_oid_t oid_type);
int git_blob__getbuf(git_str *buffer, git_blob *blob);
extern int git_blob__create_from_paths(
diff --git a/src/libgit2/branch.c b/src/libgit2/branch.c
index 4cbd1e26f7a..9a31c9c6ffc 100644
--- a/src/libgit2/branch.c
+++ b/src/libgit2/branch.c
@@ -134,9 +134,9 @@ int git_branch_create(
const git_commit *commit,
int force)
{
- char commit_id[GIT_OID_SHA1_HEXSIZE + 1];
+ char commit_id[GIT_OID_MAX_HEXSIZE + 1];
- git_oid_tostr(commit_id, GIT_OID_SHA1_HEXSIZE + 1, git_commit_id(commit));
+ git_oid_tostr(commit_id, GIT_OID_MAX_HEXSIZE + 1, git_commit_id(commit));
return create_branch(ref_out, repository, branch_name, commit, commit_id, force);
}
diff --git a/src/libgit2/cherrypick.c b/src/libgit2/cherrypick.c
index 04812b1c6f5..3ef42d5e78e 100644
--- a/src/libgit2/cherrypick.c
+++ b/src/libgit2/cherrypick.c
@@ -106,10 +106,10 @@ static int cherrypick_state_cleanup(git_repository *repo)
static int cherrypick_seterr(git_commit *commit, const char *fmt)
{
- char commit_oidstr[GIT_OID_SHA1_HEXSIZE + 1];
+ char commit_oidstr[GIT_OID_MAX_HEXSIZE + 1];
git_error_set(GIT_ERROR_CHERRYPICK, fmt,
- git_oid_tostr(commit_oidstr, GIT_OID_SHA1_HEXSIZE + 1, git_commit_id(commit)));
+ git_oid_tostr(commit_oidstr, GIT_OID_MAX_HEXSIZE + 1, git_commit_id(commit)));
return -1;
}
@@ -173,7 +173,7 @@ int git_cherrypick(
git_cherrypick_options opts;
git_reference *our_ref = NULL;
git_commit *our_commit = NULL;
- char commit_oidstr[GIT_OID_SHA1_HEXSIZE + 1];
+ char commit_oidstr[GIT_OID_MAX_HEXSIZE + 1];
const char *commit_msg, *commit_summary;
git_str their_label = GIT_STR_INIT;
git_index *index = NULL;
diff --git a/src/libgit2/clone.c b/src/libgit2/clone.c
index 9aadb0f17a7..53651765920 100644
--- a/src/libgit2/clone.c
+++ b/src/libgit2/clone.c
@@ -22,6 +22,7 @@
#include "fs_path.h"
#include "repository.h"
#include "odb.h"
+#include "net.h"
static int clone_local_into(git_repository *repo, git_remote *remote, const git_fetch_options *fetch_opts, const git_checkout_options *co_opts, const char *branch, int link);
@@ -336,8 +337,9 @@ static int create_and_configure_origin(
git_remote_create_cb remote_create = options->remote_cb;
void *payload = options->remote_cb_payload;
- /* If the path exists and is a dir, the url should be the absolute path */
- if (git_fs_path_root(url) < 0 && git_fs_path_exists(url) && git_fs_path_isdir(url)) {
+ /* If the path is local and exists it should be the absolute path. */
+ if (!git_net_str_is_url(url) && git_fs_path_root(url) < 0 &&
+ git_fs_path_exists(url)) {
if (p_realpath(url, buf) == NULL)
return -1;
@@ -360,25 +362,29 @@ static int create_and_configure_origin(
return error;
}
-static bool should_checkout(
+static int should_checkout(
+ bool *out,
git_repository *repo,
bool is_bare,
const git_checkout_options *opts)
{
- if (is_bare)
- return false;
+ int error;
- if (!opts)
- return false;
+ if (!opts || is_bare || opts->checkout_strategy == GIT_CHECKOUT_NONE) {
+ *out = 0;
+ return 0;
+ }
- if (opts->checkout_strategy == GIT_CHECKOUT_NONE)
- return false;
+ if ((error = git_repository_head_unborn(repo)) < 0)
+ return error;
- return !git_repository_head_unborn(repo);
+ *out = !error;
+ return 0;
}
static int checkout_branch(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const char *reflog_message)
{
+ bool checkout;
int error;
if (branch)
@@ -387,18 +393,32 @@ static int checkout_branch(git_repository *repo, git_remote *remote, const git_c
else
error = update_head_to_remote(repo, remote, reflog_message);
- if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
+ if (error < 0)
+ return error;
+
+ if ((error = should_checkout(&checkout, repo, git_repository_is_bare(repo), co_opts)) < 0)
+ return error;
+
+ if (checkout)
error = git_checkout_head(repo, co_opts);
return error;
}
-static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch_options *opts, const git_checkout_options *co_opts, const char *branch, const git_strarray *refspecs)
+static int clone_into(
+ git_repository *repo,
+ git_remote *_remote,
+ const git_fetch_options *opts,
+ const git_checkout_options *co_opts,
+ const char *branch,
+ const git_strarray *refspecs)
{
int error;
git_str reflog_message = GIT_STR_INIT;
+ git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
git_fetch_options fetch_opts;
git_remote *remote;
+ git_oid_t oid_type;
GIT_ASSERT_ARG(repo);
GIT_ASSERT_ARG(_remote);
@@ -413,9 +433,28 @@ static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch
memcpy(&fetch_opts, opts, sizeof(git_fetch_options));
fetch_opts.update_fetchhead = 0;
- fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
+
+ if (!opts->depth)
+ fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
+
+ if ((error = git_remote_connect_options__from_fetch_opts(&connect_opts, remote, &fetch_opts)) < 0)
+ goto cleanup;
+
git_str_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
+ /*
+ * Connect to the server so that we can identify the remote
+ * object format.
+ */
+
+ if ((error = git_remote_connect_ext(remote, GIT_DIRECTION_FETCH,
+ &connect_opts)) < 0)
+ goto cleanup;
+
+ if ((error = git_remote_oid_type(&oid_type, remote)) < 0 ||
+ (error = git_repository__set_objectformat(repo, oid_type)) < 0)
+ goto cleanup;
+
if ((error = git_remote_fetch(remote, refspecs, &fetch_opts, git_str_cstr(&reflog_message))) != 0)
goto cleanup;
@@ -423,6 +462,7 @@ static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch
cleanup:
git_remote_free(remote);
+ git_remote_connect_options_dispose(&connect_opts);
git_str_dispose(&reflog_message);
return error;
@@ -431,26 +471,25 @@ static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch
int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t local)
{
git_str fromurl = GIT_STR_INIT;
- const char *path = url_or_path;
- bool is_url, is_local;
+ bool is_local;
if (local == GIT_CLONE_NO_LOCAL)
return 0;
- if ((is_url = git_fs_path_is_local_file_url(url_or_path)) != 0) {
- if (git_fs_path_fromurl(&fromurl, url_or_path) < 0) {
- is_local = -1;
- goto done;
- }
+ if (git_net_str_is_url(url_or_path)) {
+ /* If GIT_CLONE_LOCAL_AUTO is specified, any url should be treated as remote */
+ if (local == GIT_CLONE_LOCAL_AUTO ||
+ !git_fs_path_is_local_file_url(url_or_path))
+ return 0;
- path = fromurl.ptr;
+ if (git_fs_path_fromurl(&fromurl, url_or_path) == 0)
+ is_local = git_fs_path_isdir(git_str_cstr(&fromurl));
+ else
+ is_local = -1;
+ git_str_dispose(&fromurl);
+ } else {
+ is_local = git_fs_path_isdir(url_or_path);
}
-
- is_local = (!is_url || local != GIT_CLONE_LOCAL_AUTO) &&
- git_fs_path_isdir(path);
-
-done:
- git_str_dispose(&fromurl);
return is_local;
}
@@ -515,15 +554,15 @@ static int git__clone(
}
if (error != 0) {
- git_error_state last_error = {0};
- git_error_state_capture(&last_error, error);
+ git_error *last_error;
+ git_error_save(&last_error);
git_repository_free(repo);
repo = NULL;
(void)git_futils_rmdir_r(local_path, NULL, rmdir_flags);
- git_error_state_restore(&last_error);
+ git_error_restore(last_error);
}
*out = repo;
diff --git a/src/libgit2/commit.c b/src/libgit2/commit.c
index 114ae6fc92b..10df43623d2 100644
--- a/src/libgit2/commit.c
+++ b/src/libgit2/commit.c
@@ -22,6 +22,7 @@
#include "object.h"
#include "array.h"
#include "oidarray.h"
+#include "grafts.h"
void git_commit__free(void *_commit)
{
@@ -390,7 +391,11 @@ int git_commit_amend(
return error;
}
-static int commit_parse(git_commit *commit, const char *data, size_t size, unsigned int flags)
+static int commit_parse(
+ git_commit *commit,
+ const char *data,
+ size_t size,
+ git_commit__parse_options *opts)
{
const char *buffer_start = data, *buffer;
const char *buffer_end = buffer_start + size;
@@ -401,6 +406,7 @@ static int commit_parse(git_commit *commit, const char *data, size_t size, unsig
GIT_ASSERT_ARG(commit);
GIT_ASSERT_ARG(data);
+ GIT_ASSERT_ARG(opts);
buffer = buffer_start;
@@ -409,32 +415,29 @@ static int commit_parse(git_commit *commit, const char *data, size_t size, unsig
GIT_ERROR_CHECK_ARRAY(commit->parent_ids);
/* The tree is always the first field */
- if (!(flags & GIT_COMMIT_PARSE_QUICK)) {
+ if (!(opts->flags & GIT_COMMIT_PARSE_QUICK)) {
if (git_object__parse_oid_header(&commit->tree_id,
&buffer, buffer_end, "tree ",
- GIT_OID_SHA1) < 0)
+ opts->oid_type) < 0)
goto bad_buffer;
} else {
- size_t tree_len = strlen("tree ") + GIT_OID_SHA1_HEXSIZE + 1;
+ size_t tree_len = strlen("tree ") + git_oid_hexsize(opts->oid_type) + 1;
+
if (buffer + tree_len > buffer_end)
goto bad_buffer;
buffer += tree_len;
}
- /*
- * TODO: commit grafts!
- */
-
while (git_object__parse_oid_header(&parent_id,
&buffer, buffer_end, "parent ",
- GIT_OID_SHA1) == 0) {
+ opts->oid_type) == 0) {
git_oid *new_id = git_array_alloc(commit->parent_ids);
GIT_ERROR_CHECK_ALLOC(new_id);
git_oid_cpy(new_id, &parent_id);
}
- if (!(flags & GIT_COMMIT_PARSE_QUICK)) {
+ if (!opts || !(opts->flags & GIT_COMMIT_PARSE_QUICK)) {
commit->author = git__malloc(sizeof(git_signature));
GIT_ERROR_CHECK_ALLOC(commit->author);
@@ -458,7 +461,7 @@ static int commit_parse(git_commit *commit, const char *data, size_t size, unsig
if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n')) < 0)
return error;
- if (flags & GIT_COMMIT_PARSE_QUICK)
+ if (opts && opts->flags & GIT_COMMIT_PARSE_QUICK)
return 0;
/* Parse add'l header entries */
@@ -503,19 +506,64 @@ static int commit_parse(git_commit *commit, const char *data, size_t size, unsig
return GIT_EINVALID;
}
-int git_commit__parse_raw(void *commit, const char *data, size_t size)
+int git_commit__parse(
+ void *commit,
+ git_odb_object *odb_obj,
+ git_oid_t oid_type)
{
- return commit_parse(commit, data, size, 0);
+ git_commit__parse_options parse_options = {0};
+ parse_options.oid_type = oid_type;
+
+ return git_commit__parse_ext(commit, odb_obj, &parse_options);
}
-int git_commit__parse_ext(git_commit *commit, git_odb_object *odb_obj, unsigned int flags)
+int git_commit__parse_raw(
+ void *commit,
+ const char *data,
+ size_t size,
+ git_oid_t oid_type)
{
- return commit_parse(commit, git_odb_object_data(odb_obj), git_odb_object_size(odb_obj), flags);
+ git_commit__parse_options parse_options = {0};
+ parse_options.oid_type = oid_type;
+
+ return commit_parse(commit, data, size, &parse_options);
}
-int git_commit__parse(void *_commit, git_odb_object *odb_obj)
+static int assign_commit_parents_from_graft(git_commit *commit, git_commit_graft *graft) {
+ size_t idx;
+ git_oid *oid;
+
+ git_array_clear(commit->parent_ids);
+ git_array_init_to_size(commit->parent_ids, git_array_size(graft->parents));
+ git_array_foreach(graft->parents, idx, oid) {
+ git_oid *id = git_array_alloc(commit->parent_ids);
+ GIT_ERROR_CHECK_ALLOC(id);
+
+ git_oid_cpy(id, oid);
+ }
+
+ return 0;
+}
+
+int git_commit__parse_ext(
+ git_commit *commit,
+ git_odb_object *odb_obj,
+ git_commit__parse_options *parse_opts)
{
- return git_commit__parse_ext(_commit, odb_obj, 0);
+ git_repository *repo = git_object_owner((git_object *)commit);
+ git_commit_graft *graft;
+ int error;
+
+ if ((error = commit_parse(commit, git_odb_object_data(odb_obj),
+ git_odb_object_size(odb_obj), parse_opts)) < 0)
+ return error;
+
+ /* Perform necessary grafts */
+ if (git_grafts_get(&graft, repo->grafts, git_odb_object_id(odb_obj)) != 0 &&
+ git_grafts_get(&graft, repo->shallow_grafts, git_odb_object_id(odb_obj)) != 0)
+ return 0;
+
+ return assign_commit_parents_from_graft(commit, graft);
}
#define GIT_COMMIT_GETTER(_rvalue, _name, _return, _invalid) \
@@ -985,11 +1033,14 @@ int git_commit_create_with_signature(
git_str commit = GIT_STR_INIT;
git_commit *parsed;
git_array_oid_t parents = GIT_ARRAY_INIT;
+ git_commit__parse_options parse_opts = {0};
+
+ parse_opts.oid_type = repo->oid_type;
/* The first step is to verify that all the tree and parents exist */
parsed = git__calloc(1, sizeof(git_commit));
GIT_ERROR_CHECK_ALLOC(parsed);
- if (commit_parse(parsed, commit_content, strlen(commit_content), 0) < 0) {
+ if (commit_parse(parsed, commit_content, strlen(commit_content), &parse_opts) < 0) {
error = -1;
goto cleanup;
}
@@ -1035,6 +1086,83 @@ int git_commit_create_with_signature(
return error;
}
+int git_commit_create_from_stage(
+ git_oid *out,
+ git_repository *repo,
+ const char *message,
+ const git_commit_create_options *given_opts)
+{
+ git_commit_create_options opts = GIT_COMMIT_CREATE_OPTIONS_INIT;
+ git_signature *default_signature = NULL;
+ const git_signature *author, *committer;
+ git_index *index = NULL;
+ git_diff *diff = NULL;
+ git_oid tree_id;
+ git_tree *head_tree = NULL, *tree = NULL;
+ git_commitarray parents = { 0 };
+ int error = -1;
+
+ GIT_ASSERT_ARG(out && repo);
+
+ if (given_opts)
+ memcpy(&opts, given_opts, sizeof(git_commit_create_options));
+
+ author = opts.author;
+ committer = opts.committer;
+
+ if (!author || !committer) {
+ if (git_signature_default(&default_signature, repo) < 0)
+ goto done;
+
+ if (!author)
+ author = default_signature;
+
+ if (!committer)
+ committer = default_signature;
+ }
+
+ if (git_repository_index(&index, repo) < 0)
+ goto done;
+
+ if (!opts.allow_empty_commit) {
+ error = git_repository_head_tree(&head_tree, repo);
+
+ if (error && error != GIT_EUNBORNBRANCH)
+ goto done;
+
+ error = -1;
+
+ if (git_diff_tree_to_index(&diff, repo, head_tree, index, NULL) < 0)
+ goto done;
+
+ if (git_diff_num_deltas(diff) == 0) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "no changes are staged for commit");
+ error = GIT_EUNCHANGED;
+ goto done;
+ }
+ }
+
+ if (git_index_write_tree(&tree_id, index) < 0 ||
+ git_tree_lookup(&tree, repo, &tree_id) < 0 ||
+ git_repository_commit_parents(&parents, repo) < 0)
+ goto done;
+
+ error = git_commit_create(out, repo, "HEAD", author, committer,
+ opts.message_encoding, message,
+ tree, parents.count,
+ (const git_commit **)parents.commits);
+
+done:
+ git_commitarray_dispose(&parents);
+ git_signature_free(default_signature);
+ git_tree_free(tree);
+ git_tree_free(head_tree);
+ git_diff_free(diff);
+ git_index_free(index);
+ return error;
+}
+
int git_commit_committer_with_mailmap(
git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
{
@@ -1046,3 +1174,18 @@ int git_commit_author_with_mailmap(
{
return git_mailmap_resolve_signature(out, mailmap, commit->author);
}
+
+void git_commitarray_dispose(git_commitarray *array)
+{
+ size_t i;
+
+ if (array == NULL)
+ return;
+
+ for (i = 0; i < array->count; i++)
+ git_commit_free(array->commits[i]);
+
+ git__free((git_commit **)array->commits);
+
+ memset(array, 0, sizeof(*array));
+}
diff --git a/src/libgit2/commit.h b/src/libgit2/commit.h
index 7a2454e6192..c25fee327b8 100644
--- a/src/libgit2/commit.h
+++ b/src/libgit2/commit.h
@@ -33,6 +33,16 @@ struct git_commit {
char *body;
};
+typedef struct {
+ git_oid_t oid_type;
+ unsigned int flags;
+} git_commit__parse_options;
+
+typedef enum {
+ /** Only parse parents and committer info */
+ GIT_COMMIT_PARSE_QUICK = (1 << 0)
+} git_commit__parse_flags;
+
int git_commit__header_field(
git_str *out,
const git_commit *commit,
@@ -56,14 +66,22 @@ int git_commit__create_buffer(
size_t parent_count,
const git_commit *parents[]);
-void git_commit__free(void *commit);
-int git_commit__parse(void *commit, git_odb_object *obj);
-int git_commit__parse_raw(void *commit, const char *data, size_t size);
+int git_commit__parse(
+ void *commit,
+ git_odb_object *obj,
+ git_oid_t oid_type);
-typedef enum {
- GIT_COMMIT_PARSE_QUICK = (1 << 0) /**< Only parse parents and committer info */
-} git_commit__parse_flags;
+int git_commit__parse_raw(
+ void *commit,
+ const char *data,
+ size_t size,
+ git_oid_t oid_type);
-int git_commit__parse_ext(git_commit *commit, git_odb_object *odb_obj, unsigned int flags);
+int git_commit__parse_ext(
+ git_commit *commit,
+ git_odb_object *odb_obj,
+ git_commit__parse_options *parse_opts);
+
+void git_commit__free(void *commit);
#endif
diff --git a/src/libgit2/commit_graph.c b/src/libgit2/commit_graph.c
index 9554fe85551..4edd7110640 100644
--- a/src/libgit2/commit_graph.c
+++ b/src/libgit2/commit_graph.c
@@ -138,19 +138,22 @@ static int commit_graph_parse_oid_lookup(
struct git_commit_graph_chunk *chunk_oid_lookup)
{
uint32_t i;
- unsigned char *oid, *prev_oid, zero_oid[GIT_OID_SHA1_SIZE] = {0};
+ unsigned char *oid, *prev_oid, zero_oid[GIT_OID_MAX_SIZE] = {0};
+ size_t oid_size;
+
+ oid_size = git_oid_size(file->oid_type);
if (chunk_oid_lookup->offset == 0)
return commit_graph_error("missing OID Lookup chunk");
if (chunk_oid_lookup->length == 0)
return commit_graph_error("empty OID Lookup chunk");
- if (chunk_oid_lookup->length != file->num_commits * GIT_OID_SHA1_SIZE)
+ if (chunk_oid_lookup->length != file->num_commits * oid_size)
return commit_graph_error("OID Lookup chunk has wrong length");
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_SHA1_SIZE) {
- if (git_oid_raw_cmp(prev_oid, oid, GIT_OID_SHA1_SIZE) >= 0)
+ for (i = 0; i < file->num_commits; ++i, oid += oid_size) {
+ if (git_oid_raw_cmp(prev_oid, oid, oid_size) >= 0)
return commit_graph_error("OID Lookup index is non-monotonic");
prev_oid = oid;
}
@@ -163,11 +166,13 @@ static int commit_graph_parse_commit_data(
const unsigned char *data,
struct git_commit_graph_chunk *chunk_commit_data)
{
+ size_t oid_size = git_oid_size(file->oid_type);
+
if (chunk_commit_data->offset == 0)
return commit_graph_error("missing Commit Data chunk");
if (chunk_commit_data->length == 0)
return commit_graph_error("empty Commit Data chunk");
- if (chunk_commit_data->length != file->num_commits * (GIT_OID_SHA1_SIZE + 16))
+ if (chunk_commit_data->length != file->num_commits * (oid_size + 16))
return commit_graph_error("Commit Data chunk has wrong length");
file->commit_data = data + chunk_commit_data->offset;
@@ -209,7 +214,9 @@ int git_commit_graph_file_parse(
GIT_ASSERT_ARG(file);
- if (size < sizeof(struct git_commit_graph_header) + GIT_OID_SHA1_SIZE)
+ checksum_size = git_oid_size(file->oid_type);
+
+ if (size < sizeof(struct git_commit_graph_header) + checksum_size)
return commit_graph_error("commit-graph is too short");
hdr = ((struct git_commit_graph_header *)data);
@@ -226,8 +233,7 @@ int git_commit_graph_file_parse(
* headers, and a special zero chunk.
*/
last_chunk_offset = sizeof(struct git_commit_graph_header) + (1 + hdr->chunks) * 12;
- trailer_offset = size - GIT_OID_SHA1_SIZE;
- checksum_size = GIT_HASH_SHA1_SIZE;
+ trailer_offset = size - checksum_size;
if (trailer_offset < last_chunk_offset)
return commit_graph_error("wrong commit-graph size");
@@ -295,25 +301,35 @@ int git_commit_graph_file_parse(
return 0;
}
-int git_commit_graph_new(git_commit_graph **cgraph_out, const char *objects_dir, bool open_file)
+int git_commit_graph_new(
+ git_commit_graph **cgraph_out,
+ const char *objects_dir,
+ bool open_file,
+ git_oid_t oid_type)
{
git_commit_graph *cgraph = NULL;
int error = 0;
GIT_ASSERT_ARG(cgraph_out);
GIT_ASSERT_ARG(objects_dir);
+ GIT_ASSERT_ARG(oid_type);
cgraph = git__calloc(1, sizeof(git_commit_graph));
GIT_ERROR_CHECK_ALLOC(cgraph);
+ cgraph->oid_type = oid_type;
+
error = git_str_joinpath(&cgraph->filename, objects_dir, "info/commit-graph");
if (error < 0)
goto error;
if (open_file) {
- error = git_commit_graph_file_open(&cgraph->file, git_str_cstr(&cgraph->filename));
+ error = git_commit_graph_file_open(&cgraph->file,
+ git_str_cstr(&cgraph->filename), oid_type);
+
if (error < 0)
goto error;
+
cgraph->checked = 1;
}
@@ -326,14 +342,18 @@ int git_commit_graph_new(git_commit_graph **cgraph_out, const char *objects_dir,
}
int git_commit_graph_validate(git_commit_graph *cgraph) {
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
- size_t checksum_size = GIT_HASH_SHA1_SIZE;
- size_t trailer_offset = cgraph->file->graph_map.len - checksum_size;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ git_hash_algorithm_t checksum_type;
+ size_t checksum_size, trailer_offset;
+
+ checksum_type = git_oid_algorithm(cgraph->oid_type);
+ checksum_size = git_hash_size(checksum_type);
+ trailer_offset = cgraph->file->graph_map.len - checksum_size;
if (cgraph->file->graph_map.len < checksum_size)
return commit_graph_error("map length too small");
- if (git_hash_buf(checksum, cgraph->file->graph_map.data, trailer_offset, GIT_HASH_ALGORITHM_SHA1) < 0)
+ if (git_hash_buf(checksum, cgraph->file->graph_map.data, trailer_offset, checksum_type) < 0)
return commit_graph_error("could not calculate signature");
if (memcmp(checksum, cgraph->file->checksum, checksum_size) != 0)
return commit_graph_error("index signature mismatch");
@@ -341,16 +361,32 @@ int git_commit_graph_validate(git_commit_graph *cgraph) {
return 0;
}
-int git_commit_graph_open(git_commit_graph **cgraph_out, const char *objects_dir)
+int git_commit_graph_open(
+ git_commit_graph **cgraph_out,
+ const char *objects_dir
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ )
{
- int error = git_commit_graph_new(cgraph_out, objects_dir, true);
- if (!error) {
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_oid_t oid_type = GIT_OID_SHA1;
+#endif
+ int error;
+
+ error = git_commit_graph_new(cgraph_out, objects_dir, true,
+ oid_type);
+
+ if (!error)
return git_commit_graph_validate(*cgraph_out);
- }
+
return error;
}
-int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *path)
+int git_commit_graph_file_open(
+ git_commit_graph_file **file_out,
+ const char *path,
+ git_oid_t oid_type)
{
git_commit_graph_file *file;
git_file fd = -1;
@@ -379,6 +415,8 @@ int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *pat
file = git__calloc(1, sizeof(git_commit_graph_file));
GIT_ERROR_CHECK_ALLOC(file);
+ file->oid_type = oid_type;
+
error = git_futils_mmap_ro(&file->graph_map, fd, 0, cgraph_size);
p_close(fd);
if (error < 0) {
@@ -395,7 +433,9 @@ int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *pat
return 0;
}
-int git_commit_graph_get_file(git_commit_graph_file **file_out, git_commit_graph *cgraph)
+int git_commit_graph_get_file(
+ git_commit_graph_file **file_out,
+ git_commit_graph *cgraph)
{
if (!cgraph->checked) {
int error = 0;
@@ -405,7 +445,8 @@ int git_commit_graph_get_file(git_commit_graph_file **file_out, git_commit_graph
cgraph->checked = 1;
/* Best effort */
- error = git_commit_graph_file_open(&result, git_str_cstr(&cgraph->filename));
+ error = git_commit_graph_file_open(&result,
+ git_str_cstr(&cgraph->filename), cgraph->oid_type);
if (error < 0)
return error;
@@ -441,6 +482,7 @@ static int git_commit_graph_entry_get_byindex(
size_t pos)
{
const unsigned char *commit_data;
+ size_t oid_size = git_oid_size(file->oid_type);
GIT_ASSERT_ARG(e);
GIT_ASSERT_ARG(file);
@@ -450,15 +492,15 @@ static int git_commit_graph_entry_get_byindex(
return GIT_ENOTFOUND;
}
- commit_data = file->commit_data + pos * (GIT_OID_SHA1_SIZE + 4 * sizeof(uint32_t));
- git_oid__fromraw(&e->tree_oid, commit_data, GIT_OID_SHA1);
- e->parent_indices[0] = ntohl(*((uint32_t *)(commit_data + GIT_OID_SHA1_SIZE)));
+ commit_data = file->commit_data + pos * (oid_size + 4 * sizeof(uint32_t));
+ git_oid__fromraw(&e->tree_oid, commit_data, file->oid_type);
+ e->parent_indices[0] = ntohl(*((uint32_t *)(commit_data + oid_size)));
e->parent_indices[1] = ntohl(
- *((uint32_t *)(commit_data + GIT_OID_SHA1_SIZE + sizeof(uint32_t))));
+ *((uint32_t *)(commit_data + oid_size + sizeof(uint32_t))));
e->parent_count = (e->parent_indices[0] != GIT_COMMIT_GRAPH_MISSING_PARENT)
+ (e->parent_indices[1] != GIT_COMMIT_GRAPH_MISSING_PARENT);
- e->generation = ntohl(*((uint32_t *)(commit_data + GIT_OID_SHA1_SIZE + 2 * sizeof(uint32_t))));
- e->commit_time = ntohl(*((uint32_t *)(commit_data + GIT_OID_SHA1_SIZE + 3 * sizeof(uint32_t))));
+ e->generation = ntohl(*((uint32_t *)(commit_data + oid_size + 2 * sizeof(uint32_t))));
+ e->commit_time = ntohl(*((uint32_t *)(commit_data + oid_size + 3 * sizeof(uint32_t))));
e->commit_time |= (e->generation & UINT64_C(0x3)) << UINT64_C(32);
e->generation >>= 2u;
@@ -485,7 +527,7 @@ static int git_commit_graph_entry_get_byindex(
}
}
- git_oid__fromraw(&e->sha1, &file->oid_lookup[pos * GIT_OID_SHA1_SIZE], GIT_OID_SHA1);
+ git_oid__fromraw(&e->sha1, &file->oid_lookup[pos * oid_size], file->oid_type);
return 0;
}
@@ -494,8 +536,8 @@ bool git_commit_graph_file_needs_refresh(const git_commit_graph_file *file, cons
git_file fd = -1;
struct stat st;
ssize_t bytes_read;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
- size_t checksum_size = GIT_HASH_SHA1_SIZE;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ size_t checksum_size = git_oid_size(file->oid_type);
/* TODO: properly open the file without access time using O_NOATIME */
fd = git_futils_open_ro(path);
@@ -530,35 +572,40 @@ int git_commit_graph_entry_find(
int pos, found = 0;
uint32_t hi, lo;
const unsigned char *current = NULL;
+ size_t oid_size, oid_hexsize;
GIT_ASSERT_ARG(e);
GIT_ASSERT_ARG(file);
GIT_ASSERT_ARG(short_oid);
+ oid_size = git_oid_size(file->oid_type);
+ oid_hexsize = git_oid_hexsize(file->oid_type);
+
hi = ntohl(file->oid_fanout[(int)short_oid->id[0]]);
lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(file->oid_fanout[(int)short_oid->id[0] - 1]));
- pos = git_pack__lookup_sha1(file->oid_lookup, GIT_OID_SHA1_SIZE, lo, hi, short_oid->id);
+ pos = git_pack__lookup_id(file->oid_lookup, oid_size, lo, hi,
+ short_oid->id, file->oid_type);
if (pos >= 0) {
/* An object matching exactly the oid was found */
found = 1;
- current = file->oid_lookup + (pos * GIT_OID_SHA1_SIZE);
+ current = file->oid_lookup + (pos * oid_size);
} 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 * GIT_OID_SHA1_SIZE);
+ current = file->oid_lookup + (pos * oid_size);
if (!git_oid_raw_ncmp(short_oid->id, current, len))
found = 1;
}
}
- if (found && len != GIT_OID_SHA1_HEXSIZE && pos + 1 < (int)file->num_commits) {
+ if (found && len != oid_hexsize && pos + 1 < (int)file->num_commits) {
/* Check for ambiguousity */
- const unsigned char *next = current + GIT_OID_SHA1_SIZE;
+ const unsigned char *next = current + oid_size;
if (!git_oid_raw_ncmp(short_oid->id, next, len))
found = 2;
@@ -637,11 +684,27 @@ static int packed_commit__cmp(const void *a_, const void *b_)
return git_oid_cmp(&a->sha1, &b->sha1);
}
-int git_commit_graph_writer_new(git_commit_graph_writer **out, const char *objects_info_dir)
+int git_commit_graph_writer_new(
+ git_commit_graph_writer **out,
+ const char *objects_info_dir
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ )
{
- git_commit_graph_writer *w = git__calloc(1, sizeof(git_commit_graph_writer));
+ git_commit_graph_writer *w;
+
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_oid_t oid_type = GIT_OID_SHA1;
+#endif
+
+ GIT_ASSERT_ARG(out && objects_info_dir && oid_type);
+
+ w = git__calloc(1, sizeof(git_commit_graph_writer));
GIT_ERROR_CHECK_ALLOC(w);
+ w->oid_type = oid_type;
+
if (git_str_sets(&w->objects_info_dir, objects_info_dir) < 0) {
git__free(w);
return -1;
@@ -726,7 +789,8 @@ int git_commit_graph_writer_add_index_file(
if (error < 0)
goto cleanup;
- error = git_mwindow_get_pack(&p, idx_path);
+ /* TODO: SHA256 */
+ error = git_mwindow_get_pack(&p, idx_path, 0);
if (error < 0)
goto cleanup;
@@ -992,8 +1056,9 @@ static int commit_graph_write(
off64_t offset;
git_str oid_lookup = GIT_STR_INIT, commit_data = GIT_STR_INIT,
extra_edge_list = GIT_STR_INIT;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
- size_t checksum_size;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ git_hash_algorithm_t checksum_type;
+ size_t checksum_size, oid_size;
git_hash_ctx ctx;
struct commit_graph_write_hash_context hash_cb_data = {0};
@@ -1006,8 +1071,11 @@ static int commit_graph_write(
hash_cb_data.cb_data = cb_data;
hash_cb_data.ctx = &ctx;
- checksum_size = GIT_HASH_SHA1_SIZE;
- error = git_hash_ctx_init(&ctx, GIT_HASH_ALGORITHM_SHA1);
+ oid_size = git_oid_size(w->oid_type);
+ checksum_type = git_oid_algorithm(w->oid_type);
+ checksum_size = git_hash_size(checksum_type);
+
+ error = git_hash_ctx_init(&ctx, checksum_type);
if (error < 0)
return error;
cb_data = &hash_cb_data;
@@ -1034,7 +1102,7 @@ static int commit_graph_write(
git_vector_foreach (&w->commits, i, packed_commit) {
error = git_str_put(&oid_lookup,
(const char *)&packed_commit->sha1.id,
- GIT_OID_SHA1_SIZE);
+ oid_size);
if (error < 0)
goto cleanup;
@@ -1051,7 +1119,7 @@ static int commit_graph_write(
error = git_str_put(&commit_data,
(const char *)&packed_commit->tree_oid.id,
- GIT_OID_SHA1_SIZE);
+ oid_size);
if (error < 0)
goto cleanup;
diff --git a/src/libgit2/commit_graph.h b/src/libgit2/commit_graph.h
index 517abb2390b..ecf4379bdb6 100644
--- a/src/libgit2/commit_graph.h
+++ b/src/libgit2/commit_graph.h
@@ -30,6 +30,9 @@
typedef struct git_commit_graph_file {
git_map graph_map;
+ /* The type of object IDs in the commit graph file. */
+ git_oid_t oid_type;
+
/* The OID Fanout table. */
const uint32_t *oid_fanout;
/* The total number of commits in the graph. */
@@ -84,10 +87,10 @@ typedef struct git_commit_graph_entry {
/* The index within the Extra Edge List of any parent after the first two. */
size_t extra_parents_index;
- /* The SHA-1 hash of the root tree of the commit. */
+ /* The object ID of the root tree of the commit. */
git_oid tree_oid;
- /* The SHA-1 hash of the requested commit. */
+ /* The object ID hash of the requested commit. */
git_oid sha1;
} git_commit_graph_entry;
@@ -99,18 +102,28 @@ struct git_commit_graph {
/* The underlying commit-graph file. */
git_commit_graph_file *file;
+ /* The object ID types in the commit graph. */
+ git_oid_t oid_type;
+
/* Whether the commit-graph file was already checked for validity. */
bool checked;
};
/** Create a new commit-graph, optionally opening the underlying file. */
-int git_commit_graph_new(git_commit_graph **cgraph_out, const char *objects_dir, bool open_file);
+int git_commit_graph_new(
+ git_commit_graph **cgraph_out,
+ const char *objects_dir,
+ bool open_file,
+ git_oid_t oid_type);
/** Validate the checksum of a commit graph */
int git_commit_graph_validate(git_commit_graph *cgraph);
/** Open and validate a commit-graph file. */
-int git_commit_graph_file_open(git_commit_graph_file **file_out, const char *path);
+int git_commit_graph_file_open(
+ git_commit_graph_file **file_out,
+ const char *path,
+ git_oid_t oid_type);
/*
* Attempt to get the git_commit_graph's commit-graph file. This object is
@@ -134,6 +147,9 @@ struct git_commit_graph_writer {
*/
git_str objects_info_dir;
+ /* The object ID type of the commit graph. */
+ git_oid_t oid_type;
+
/* The list of packed commits. */
git_vector commits;
};
diff --git a/src/libgit2/commit_list.c b/src/libgit2/commit_list.c
index 511d7291f00..7f00c483fac 100644
--- a/src/libgit2/commit_list.c
+++ b/src/libgit2/commit_list.c
@@ -43,13 +43,18 @@ int git_commit_list_time_cmp(const void *a, const void *b)
return 0;
}
-git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p)
-{
+git_commit_list *git_commit_list_create(git_commit_list_node *item, git_commit_list *next) {
git_commit_list *new_list = git__malloc(sizeof(git_commit_list));
if (new_list != NULL) {
new_list->item = item;
- new_list->next = *list_p;
+ new_list->next = next;
}
+ return new_list;
+}
+
+git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p)
+{
+ git_commit_list *new_list = git_commit_list_create(item, *list_p);
*list_p = new_list;
return new_list;
}
@@ -124,13 +129,17 @@ static int commit_quick_parse(
{
git_oid *parent_oid;
git_commit *commit;
+ git_commit__parse_options parse_opts = {
+ walk->repo->oid_type,
+ GIT_COMMIT_PARSE_QUICK
+ };
size_t i;
commit = git__calloc(1, sizeof(*commit));
GIT_ERROR_CHECK_ALLOC(commit);
commit->object.repo = walk->repo;
- if (git_commit__parse_ext(commit, obj, GIT_COMMIT_PARSE_QUICK) < 0) {
+ if (git_commit__parse_ext(commit, obj, &parse_opts) < 0) {
git__free(commit);
return -1;
}
@@ -172,7 +181,9 @@ int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit)
if (cgraph_file) {
git_commit_graph_entry e;
- error = git_commit_graph_entry_find(&e, cgraph_file, &commit->oid, GIT_OID_SHA1_SIZE);
+ error = git_commit_graph_entry_find(&e, cgraph_file,
+ &commit->oid, git_oid_size(walk->repo->oid_type));
+
if (error == 0 && git__is_uint16(e.parent_count)) {
size_t i;
commit->generation = (uint32_t)e.generation;
diff --git a/src/libgit2/commit_list.h b/src/libgit2/commit_list.h
index aad39f3513b..e2dbd2aaed3 100644
--- a/src/libgit2/commit_list.h
+++ b/src/libgit2/commit_list.h
@@ -49,6 +49,7 @@ git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk);
int git_commit_list_generation_cmp(const void *a, const void *b);
int git_commit_list_time_cmp(const void *a, const void *b);
void git_commit_list_free(git_commit_list **list_p);
+git_commit_list *git_commit_list_create(git_commit_list_node *item, git_commit_list *next);
git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p);
git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p);
int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit);
diff --git a/src/libgit2/config.c b/src/libgit2/config.c
index ba3537bc910..1e4e17597d6 100644
--- a/src/libgit2/config.c
+++ b/src/libgit2/config.c
@@ -22,6 +22,32 @@
#include
+/*
+ * A refcounted instance of a config_backend that can be shared across
+ * a configuration instance, any snapshots, and individual configuration
+ * levels (from `git_config_open_level`).
+ */
+typedef struct {
+ git_refcount rc;
+ git_config_backend *backend;
+} backend_instance;
+
+/*
+ * An entry in the readers or writers vector in the configuration.
+ * This is kept separate from the refcounted instance so that different
+ * views of the configuration can have different notions of levels or
+ * write orders.
+ *
+ * (eg, a standard configuration has a priority ordering of writers, a
+ * snapshot has *no* writers, and an individual level has a single
+ * writer.)
+ */
+typedef struct {
+ backend_instance *instance;
+ git_config_level_t level;
+ int write_order;
+} backend_entry;
+
void git_config_entry_free(git_config_entry *entry)
{
if (!entry)
@@ -30,75 +56,75 @@ void git_config_entry_free(git_config_entry *entry)
entry->free(entry);
}
-typedef struct {
- git_refcount rc;
-
- git_config_backend *backend;
- git_config_level_t level;
-} backend_internal;
-
-static void backend_internal_free(backend_internal *internal)
+static void backend_instance_free(backend_instance *instance)
{
git_config_backend *backend;
- backend = internal->backend;
+ backend = instance->backend;
backend->free(backend);
- git__free(internal);
+ git__free(instance);
}
-static void config_free(git_config *cfg)
+static void config_free(git_config *config)
{
size_t i;
- backend_internal *internal;
+ backend_entry *entry;
- for (i = 0; i < cfg->backends.length; ++i) {
- internal = git_vector_get(&cfg->backends, i);
- GIT_REFCOUNT_DEC(internal, backend_internal_free);
+ git_vector_foreach(&config->readers, i, entry) {
+ GIT_REFCOUNT_DEC(entry->instance, backend_instance_free);
+ git__free(entry);
}
- git_vector_free(&cfg->backends);
-
- git__memzero(cfg, sizeof(*cfg));
- git__free(cfg);
+ git_vector_free(&config->readers);
+ git_vector_free(&config->writers);
+ git__free(config);
}
-void git_config_free(git_config *cfg)
+void git_config_free(git_config *config)
{
- if (cfg == NULL)
+ if (config == NULL)
return;
- GIT_REFCOUNT_DEC(cfg, config_free);
+ GIT_REFCOUNT_DEC(config, config_free);
}
-static int config_backend_cmp(const void *a, const void *b)
+static int reader_cmp(const void *_a, const void *_b)
{
- const backend_internal *bk_a = (const backend_internal *)(a);
- const backend_internal *bk_b = (const backend_internal *)(b);
+ const backend_entry *a = _a;
+ const backend_entry *b = _b;
- return bk_b->level - bk_a->level;
+ return b->level - a->level;
}
-int git_config_new(git_config **out)
+static int writer_cmp(const void *_a, const void *_b)
{
- git_config *cfg;
+ const backend_entry *a = _a;
+ const backend_entry *b = _b;
- cfg = git__malloc(sizeof(git_config));
- GIT_ERROR_CHECK_ALLOC(cfg);
+ return b->write_order - a->write_order;
+}
- memset(cfg, 0x0, sizeof(git_config));
+int git_config_new(git_config **out)
+{
+ git_config *config;
+
+ config = git__calloc(1, sizeof(git_config));
+ GIT_ERROR_CHECK_ALLOC(config);
- if (git_vector_init(&cfg->backends, 3, config_backend_cmp) < 0) {
- git__free(cfg);
+ if (git_vector_init(&config->readers, 8, reader_cmp) < 0 ||
+ git_vector_init(&config->writers, 8, writer_cmp) < 0) {
+ config_free(config);
return -1;
}
- *out = cfg;
- GIT_REFCOUNT_INC(cfg);
+ GIT_REFCOUNT_INC(config);
+
+ *out = config;
return 0;
}
int git_config_add_file_ondisk(
- git_config *cfg,
+ git_config *config,
const char *path,
git_config_level_t level,
const git_repository *repo,
@@ -108,7 +134,7 @@ int git_config_add_file_ondisk(
struct stat st;
int res;
- GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(config);
GIT_ASSERT_ARG(path);
res = p_stat(path, &st);
@@ -120,7 +146,7 @@ int git_config_add_file_ondisk(
if (git_config_backend_from_file(&file, path) < 0)
return -1;
- if ((res = git_config_add_backend(cfg, file, level, repo, force)) < 0) {
+ if ((res = git_config_add_backend(config, file, level, repo, force)) < 0) {
/*
* free manually; the file is not owned by the config
* instance yet and will not be freed on cleanup
@@ -154,7 +180,7 @@ int git_config_snapshot(git_config **out, git_config *in)
{
int error = 0;
size_t i;
- backend_internal *internal;
+ backend_entry *entry;
git_config *config;
*out = NULL;
@@ -162,18 +188,20 @@ int git_config_snapshot(git_config **out, git_config *in)
if (git_config_new(&config) < 0)
return -1;
- git_vector_foreach(&in->backends, i, internal) {
+ git_vector_foreach(&in->readers, i, entry) {
git_config_backend *b;
- if ((error = internal->backend->snapshot(&b, internal->backend)) < 0)
+ if ((error = entry->instance->backend->snapshot(&b, entry->instance->backend)) < 0)
break;
- if ((error = git_config_add_backend(config, b, internal->level, NULL, 0)) < 0) {
+ if ((error = git_config_add_backend(config, b, entry->level, NULL, 0)) < 0) {
b->free(b);
break;
}
}
+ git_config_set_writeorder(config, NULL, 0);
+
if (error < 0)
git_config_free(config);
else
@@ -183,141 +211,162 @@ int git_config_snapshot(git_config **out, git_config *in)
}
static int find_backend_by_level(
- backend_internal **out,
- const git_config *cfg,
+ backend_instance **out,
+ const git_config *config,
git_config_level_t level)
{
- int pos = -1;
- backend_internal *internal;
+ backend_entry *entry, *found = NULL;
size_t i;
- /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config backend
- * which has the highest level. As config backends are stored in a vector
- * sorted by decreasing order of level, getting the backend at position 0
- * will do the job.
+ /*
+ * when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the
+ * config backend which has the highest level. As config backends
+ * are stored in a vector sorted by decreasing order of level,
+ * getting the backend at position 0 will do the job.
*/
if (level == GIT_CONFIG_HIGHEST_LEVEL) {
- pos = 0;
+ found = git_vector_get(&config->readers, 0);
} else {
- git_vector_foreach(&cfg->backends, i, internal) {
- if (internal->level == level)
- pos = (int)i;
+ git_vector_foreach(&config->readers, i, entry) {
+ if (entry->level == level) {
+ found = entry;
+ break;
+ }
}
}
- if (pos == -1) {
+ if (!found) {
git_error_set(GIT_ERROR_CONFIG,
- "no configuration exists for the given level '%i'", (int)level);
+ "no configuration exists for the given level '%d'", level);
return GIT_ENOTFOUND;
}
- *out = git_vector_get(&cfg->backends, pos);
-
+ *out = found->instance;
return 0;
}
-static int duplicate_level(void **old_raw, void *new_raw)
+static int duplicate_level(void **_old, void *_new)
{
- backend_internal **old = (backend_internal **)old_raw;
+ backend_entry **old = (backend_entry **)_old;
- GIT_UNUSED(new_raw);
+ GIT_UNUSED(_new);
- git_error_set(GIT_ERROR_CONFIG, "there already exists a configuration for the given level (%i)", (int)(*old)->level);
+ git_error_set(GIT_ERROR_CONFIG, "configuration at level %d already exists", (*old)->level);
return GIT_EEXISTS;
}
static void try_remove_existing_backend(
- git_config *cfg,
+ git_config *config,
git_config_level_t level)
{
- int pos = -1;
- backend_internal *internal;
+ backend_entry *entry, *found = NULL;
size_t i;
- git_vector_foreach(&cfg->backends, i, internal) {
- if (internal->level == level)
- pos = (int)i;
+ git_vector_foreach(&config->readers, i, entry) {
+ if (entry->level == level) {
+ git_vector_remove(&config->readers, i);
+ found = entry;
+ break;
+ }
}
- if (pos == -1)
+ if (!found)
return;
- internal = git_vector_get(&cfg->backends, pos);
-
- if (git_vector_remove(&cfg->backends, pos) < 0)
- return;
+ git_vector_foreach(&config->writers, i, entry) {
+ if (entry->level == level) {
+ git_vector_remove(&config->writers, i);
+ break;
+ }
+ }
- GIT_REFCOUNT_DEC(internal, backend_internal_free);
+ GIT_REFCOUNT_DEC(found->instance, backend_instance_free);
+ git__free(found);
}
-static int git_config__add_internal(
- git_config *cfg,
- backend_internal *internal,
+static int git_config__add_instance(
+ git_config *config,
+ backend_instance *instance,
git_config_level_t level,
int force)
{
+ backend_entry *entry;
int result;
/* delete existing config backend for level if it exists */
if (force)
- try_remove_existing_backend(cfg, level);
+ try_remove_existing_backend(config, level);
- if ((result = git_vector_insert_sorted(&cfg->backends,
- internal, &duplicate_level)) < 0)
- return result;
+ entry = git__malloc(sizeof(backend_entry));
+ GIT_ERROR_CHECK_ALLOC(entry);
- git_vector_sort(&cfg->backends);
- internal->backend->cfg = cfg;
+ entry->instance = instance;
+ entry->level = level;
+ entry->write_order = level;
- GIT_REFCOUNT_INC(internal);
+ if ((result = git_vector_insert_sorted(&config->readers,
+ entry, &duplicate_level)) < 0 ||
+ (result = git_vector_insert_sorted(&config->writers,
+ entry, NULL)) < 0) {
+ git__free(entry);
+ return result;
+ }
+
+ GIT_REFCOUNT_INC(entry->instance);
return 0;
}
-int git_config_open_global(git_config **cfg_out, git_config *cfg)
+int git_config_open_global(git_config **out, git_config *config)
{
- if (!git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_XDG))
+ int error;
+
+ error = git_config_open_level(out, config, GIT_CONFIG_LEVEL_XDG);
+
+ if (error == 0)
return 0;
+ else if (error != GIT_ENOTFOUND)
+ return error;
- return git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_GLOBAL);
+ return git_config_open_level(out, config, GIT_CONFIG_LEVEL_GLOBAL);
}
int git_config_open_level(
- git_config **cfg_out,
- const git_config *cfg_parent,
+ git_config **out,
+ const git_config *parent,
git_config_level_t level)
{
- git_config *cfg;
- backend_internal *internal;
+ git_config *config;
+ backend_instance *instance;
int res;
- if ((res = find_backend_by_level(&internal, cfg_parent, level)) < 0)
+ if ((res = find_backend_by_level(&instance, parent, level)) < 0)
return res;
- if ((res = git_config_new(&cfg)) < 0)
+ if ((res = git_config_new(&config)) < 0)
return res;
- if ((res = git_config__add_internal(cfg, internal, level, true)) < 0) {
- git_config_free(cfg);
+ if ((res = git_config__add_instance(config, instance, level, true)) < 0) {
+ git_config_free(config);
return res;
}
- *cfg_out = cfg;
+ *out = config;
return 0;
}
int git_config_add_backend(
- git_config *cfg,
+ git_config *config,
git_config_backend *backend,
git_config_level_t level,
const git_repository *repo,
int force)
{
- backend_internal *internal;
+ backend_instance *instance;
int result;
- GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(config);
GIT_ASSERT_ARG(backend);
GIT_ERROR_CHECK_VERSION(backend, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
@@ -325,22 +374,50 @@ int git_config_add_backend(
if ((result = backend->open(backend, level, repo)) < 0)
return result;
- internal = git__malloc(sizeof(backend_internal));
- GIT_ERROR_CHECK_ALLOC(internal);
+ instance = git__calloc(1, sizeof(backend_instance));
+ GIT_ERROR_CHECK_ALLOC(instance);
- memset(internal, 0x0, sizeof(backend_internal));
+ instance->backend = backend;
+ instance->backend->cfg = config;
- internal->backend = backend;
- internal->level = level;
-
- if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) {
- git__free(internal);
+ if ((result = git_config__add_instance(config, instance, level, force)) < 0) {
+ git__free(instance);
return result;
}
return 0;
}
+int git_config_set_writeorder(
+ git_config *config,
+ git_config_level_t *levels,
+ size_t len)
+{
+ backend_entry *entry;
+ size_t i, j;
+
+ GIT_ASSERT(len < INT_MAX);
+
+ git_vector_foreach(&config->readers, i, entry) {
+ bool found = false;
+
+ for (j = 0; j < len; j++) {
+ if (levels[j] == entry->level) {
+ entry->write_order = (int)j;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ entry->write_order = -1;
+ }
+
+ git_vector_sort(&config->writers);
+
+ return 0;
+}
+
/*
* Loop over all the variables
*/
@@ -348,37 +425,20 @@ int git_config_add_backend(
typedef struct {
git_config_iterator parent;
git_config_iterator *current;
- const git_config *cfg;
+ const git_config *config;
git_regexp regex;
size_t i;
} all_iter;
-static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
-{
- backend_internal *internal;
-
- for (; i > 0; --i) {
- internal = git_vector_get(&cfg->backends, i - 1);
- if (!internal || !internal->backend)
- continue;
-
- *out = i;
- return 0;
- }
-
- return -1;
-}
-
-static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
+static int all_iter_next(git_config_entry **out, git_config_iterator *_iter)
{
all_iter *iter = (all_iter *) _iter;
- backend_internal *internal;
+ backend_entry *entry;
git_config_backend *backend;
- size_t i;
int error = 0;
if (iter->current != NULL &&
- (error = iter->current->next(entry, iter->current)) == 0) {
+ (error = iter->current->next(out, iter->current)) == 0) {
return 0;
}
@@ -386,12 +446,14 @@ static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
return error;
do {
- if (find_next_backend(&i, iter->cfg, iter->i) < 0)
+ if (iter->i == 0)
return GIT_ITEROVER;
- internal = git_vector_get(&iter->cfg->backends, i - 1);
- backend = internal->backend;
- iter->i = i - 1;
+ entry = git_vector_get(&iter->config->readers, iter->i - 1);
+ GIT_ASSERT(entry && entry->instance && entry->instance->backend);
+
+ backend = entry->instance->backend;
+ iter->i--;
if (iter->current)
iter->current->free(iter->current);
@@ -404,7 +466,7 @@ static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
if (error < 0)
return error;
- error = iter->current->next(entry, iter->current);
+ error = iter->current->next(out, iter->current);
/* If this backend is empty, then keep going */
if (error == GIT_ITEROVER)
continue;
@@ -423,7 +485,7 @@ static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_it
/*
* We use the "normal" function to grab the next one across
- * backends and then apply the regex
+ * readers and then apply the regex
*/
while ((error = all_iter_next(entry, _iter)) == 0) {
/* skip non-matching keys if regexp was provided */
@@ -455,7 +517,7 @@ static void all_iter_glob_free(git_config_iterator *_iter)
all_iter_free(_iter);
}
-int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
+int git_config_iterator_new(git_config_iterator **out, const git_config *config)
{
all_iter *iter;
@@ -465,21 +527,21 @@ int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
iter->parent.free = all_iter_free;
iter->parent.next = all_iter_next;
- iter->i = cfg->backends.length;
- iter->cfg = cfg;
+ iter->i = config->readers.length;
+ iter->config = config;
*out = (git_config_iterator *) iter;
return 0;
}
-int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp)
+int git_config_iterator_glob_new(git_config_iterator **out, const git_config *config, const char *regexp)
{
all_iter *iter;
int result;
if (regexp == NULL)
- return git_config_iterator_new(out, cfg);
+ return git_config_iterator_new(out, config);
iter = git__calloc(1, sizeof(all_iter));
GIT_ERROR_CHECK_ALLOC(iter);
@@ -491,8 +553,8 @@ int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cf
iter->parent.next = all_iter_glob_next;
iter->parent.free = all_iter_glob_free;
- iter->i = cfg->backends.length;
- iter->cfg = cfg;
+ iter->i = config->readers.length;
+ iter->config = config;
*out = (git_config_iterator *) iter;
@@ -500,9 +562,9 @@ int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cf
}
int git_config_foreach(
- const git_config *cfg, git_config_foreach_cb cb, void *payload)
+ const git_config *config, git_config_foreach_cb cb, void *payload)
{
- return git_config_foreach_match(cfg, NULL, cb, payload);
+ return git_config_foreach_match(config, NULL, cb, payload);
}
int git_config_backend_foreach_match(
@@ -548,7 +610,7 @@ int git_config_backend_foreach_match(
}
int git_config_foreach_match(
- const git_config *cfg,
+ const git_config *config,
const char *regexp,
git_config_foreach_cb cb,
void *payload)
@@ -557,7 +619,7 @@ int git_config_foreach_match(
git_config_iterator *iter;
git_config_entry *entry;
- if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0)
+ if ((error = git_config_iterator_glob_new(&iter, config, regexp)) < 0)
return error;
while (!(error = git_config_next(&entry, iter))) {
@@ -579,92 +641,59 @@ int git_config_foreach_match(
* Setters
**************/
-typedef enum {
- BACKEND_USE_SET,
- BACKEND_USE_DELETE
-} backend_use;
-
-static const char *uses[] = {
- "set",
- "delete"
-};
-
-static int get_backend_for_use(git_config_backend **out,
- git_config *cfg, const char *name, backend_use use)
-{
+ static backend_instance *get_writer_instance(git_config *config)
+ {
+ backend_entry *entry;
size_t i;
- backend_internal *backend;
- git_config_entry *entry;
- *out = NULL;
+ git_vector_foreach(&config->writers, i, entry) {
+ if (entry->instance->backend->readonly)
+ continue;
- if (git_vector_length(&cfg->backends) == 0) {
- git_error_set(GIT_ERROR_CONFIG,
- "cannot %s value for '%s' when no config backends exist",
- uses[use], name);
- return GIT_ENOTFOUND;
+ if (entry->write_order < 0)
+ continue;
+
+ return entry->instance;
}
- git_vector_foreach(&cfg->backends, i, backend) {
- if (!backend->backend->readonly) {
- *out = backend->backend;
- entry = NULL;
-
- /* If this backend has an entry for this config, use it. */
- backend->backend->get(backend->backend, name, &entry);
- if (entry != NULL) {
- git_config_entry_free(entry);
- return 0;
- }
-
- /* Don't use worktree config as an eager implied destination. */
- if (backend->level == GIT_CONFIG_LEVEL_WORKTREE) {
- continue;
- }
-
- /* The original behavior was to return the first writeable backend found. */
- return 0;
- }
- }
-
- if (*out) {
- return 0;
- }
-
- git_error_set(GIT_ERROR_CONFIG,
- "cannot %s value for '%s' when all config backends are readonly",
- uses[use], name);
- return GIT_ENOTFOUND;
+ return NULL;
+ }
+
+static git_config_backend *get_writer(git_config *config)
+{
+ backend_instance *instance = get_writer_instance(config);
+
+ return instance ? instance->backend : NULL;
}
-int git_config_delete_entry(git_config *cfg, const char *name)
+int git_config_delete_entry(git_config *config, const char *name)
{
git_config_backend *backend;
- if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
- return GIT_ENOTFOUND;
+ if ((backend = get_writer(config)) == NULL)
+ return GIT_EREADONLY;
return backend->del(backend, name);
}
-int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
+int git_config_set_int64(git_config *config, const char *name, int64_t value)
{
char str_value[32]; /* All numbers should fit in here */
p_snprintf(str_value, sizeof(str_value), "%" PRId64, value);
- return git_config_set_string(cfg, name, str_value);
+ return git_config_set_string(config, name, str_value);
}
-int git_config_set_int32(git_config *cfg, const char *name, int32_t value)
+int git_config_set_int32(git_config *config, const char *name, int32_t value)
{
- return git_config_set_int64(cfg, name, (int64_t)value);
+ return git_config_set_int64(config, name, (int64_t)value);
}
-int git_config_set_bool(git_config *cfg, const char *name, int value)
+int git_config_set_bool(git_config *config, const char *name, int value)
{
- return git_config_set_string(cfg, name, value ? "true" : "false");
+ return git_config_set_string(config, name, value ? "true" : "false");
}
-int git_config_set_string(git_config *cfg, const char *name, const char *value)
+int git_config_set_string(git_config *config, const char *name, const char *value)
{
int error;
git_config_backend *backend;
@@ -674,13 +703,15 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
return -1;
}
- if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_SET) < 0)
- return GIT_ENOTFOUND;
+ if ((backend = get_writer(config)) == NULL) {
+ git_error_set(GIT_ERROR_CONFIG, "cannot set '%s': the configuration is read-only", name);
+ return GIT_EREADONLY;
+ }
error = backend->set(backend, name, value);
- if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
- git_repository__configmap_lookup_cache_clear(GIT_REFCOUNT_OWNER(cfg));
+ if (!error && GIT_REFCOUNT_OWNER(config) != NULL)
+ git_repository__configmap_lookup_cache_clear(GIT_REFCOUNT_OWNER(config));
return error;
}
@@ -734,16 +765,17 @@ enum {
static int get_entry(
git_config_entry **out,
- const git_config *cfg,
+ const git_config *config,
const char *name,
bool normalize_name,
int want_errors)
{
+ backend_entry *entry;
+ git_config_backend *backend;
int res = GIT_ENOTFOUND;
const char *key = name;
char *normalized = NULL;
size_t i;
- backend_internal *internal;
*out = NULL;
@@ -754,11 +786,12 @@ static int get_entry(
}
res = GIT_ENOTFOUND;
- git_vector_foreach(&cfg->backends, i, internal) {
- if (!internal || !internal->backend)
- continue;
+ git_vector_foreach(&config->readers, i, entry) {
+ GIT_ASSERT(entry->instance && entry->instance->backend);
+
+ backend = entry->instance->backend;
+ res = backend->get(backend, key, out);
- res = internal->backend->get(internal->backend, key, out);
if (res != GIT_ENOTFOUND)
break;
}
@@ -766,9 +799,9 @@ static int get_entry(
git__free(normalized);
cleanup:
- if (res == GIT_ENOTFOUND)
+ if (res == GIT_ENOTFOUND) {
res = (want_errors > GET_ALL_ERRORS) ? 0 : config_error_notfound(name);
- else if (res && (want_errors == GET_NO_ERRORS)) {
+ } else if (res && (want_errors == GET_NO_ERRORS)) {
git_error_clear();
res = 0;
}
@@ -777,24 +810,24 @@ static int get_entry(
}
int git_config_get_entry(
- git_config_entry **out, const git_config *cfg, const char *name)
+ git_config_entry **out, const git_config *config, const char *name)
{
- return get_entry(out, cfg, name, true, GET_ALL_ERRORS);
+ return get_entry(out, config, name, true, GET_ALL_ERRORS);
}
int git_config__lookup_entry(
git_config_entry **out,
- const git_config *cfg,
+ const git_config *config,
const char *key,
bool no_errors)
{
return get_entry(
- out, cfg, key, false, no_errors ? GET_NO_ERRORS : GET_NO_MISSING);
+ out, config, key, false, no_errors ? GET_NO_ERRORS : GET_NO_MISSING);
}
int git_config_get_mapped(
int *out,
- const git_config *cfg,
+ const git_config *config,
const char *name,
const git_configmap *maps,
size_t map_n)
@@ -802,7 +835,7 @@ int git_config_get_mapped(
git_config_entry *entry;
int ret;
- if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ if ((ret = get_entry(&entry, config, name, true, GET_ALL_ERRORS)) < 0)
return ret;
ret = git_config_lookup_map_value(out, maps, map_n, entry->value);
@@ -811,12 +844,12 @@ int git_config_get_mapped(
return ret;
}
-int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name)
+int git_config_get_int64(int64_t *out, const git_config *config, const char *name)
{
git_config_entry *entry;
int ret;
- if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ if ((ret = get_entry(&entry, config, name, true, GET_ALL_ERRORS)) < 0)
return ret;
ret = git_config_parse_int64(out, entry->value);
@@ -825,12 +858,12 @@ int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name)
return ret;
}
-int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name)
+int git_config_get_int32(int32_t *out, const git_config *config, const char *name)
{
git_config_entry *entry;
int ret;
- if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ if ((ret = get_entry(&entry, config, name, true, GET_ALL_ERRORS)) < 0)
return ret;
ret = git_config_parse_int32(out, entry->value);
@@ -839,12 +872,12 @@ int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name)
return ret;
}
-int git_config_get_bool(int *out, const git_config *cfg, const char *name)
+int git_config_get_bool(int *out, const git_config *config, const char *name)
{
git_config_entry *entry;
int ret;
- if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ if ((ret = get_entry(&entry, config, name, true, GET_ALL_ERRORS)) < 0)
return ret;
ret = git_config_parse_bool(out, entry->value);
@@ -853,16 +886,15 @@ int git_config_get_bool(int *out, const git_config *cfg, const char *name)
return ret;
}
-static int is_readonly(const git_config *cfg)
+static int is_readonly(const git_config *config)
{
+ backend_entry *entry;
size_t i;
- backend_internal *internal;
- git_vector_foreach(&cfg->backends, i, internal) {
- if (!internal || !internal->backend)
- continue;
+ git_vector_foreach(&config->writers, i, entry) {
+ GIT_ASSERT(entry->instance && entry->instance->backend);
- if (!internal->backend->readonly)
+ if (!entry->instance->backend->readonly)
return 0;
}
@@ -880,7 +912,7 @@ static int git_config__parse_path(git_str *out, const char *value)
return -1;
}
- return git_sysdir_expand_global_file(out, value[1] ? &value[2] : NULL);
+ return git_sysdir_expand_homedir_file(out, value[1] ? &value[2] : NULL);
}
return git_str_sets(out, value);
@@ -893,21 +925,21 @@ int git_config_parse_path(git_buf *out, const char *value)
int git_config_get_path(
git_buf *out,
- const git_config *cfg,
+ const git_config *config,
const char *name)
{
- GIT_BUF_WRAP_PRIVATE(out, git_config__get_path, cfg, name);
+ GIT_BUF_WRAP_PRIVATE(out, git_config__get_path, config, name);
}
int git_config__get_path(
git_str *out,
- const git_config *cfg,
+ const git_config *config,
const char *name)
{
git_config_entry *entry;
int error;
- if ((error = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ if ((error = get_entry(&entry, config, name, true, GET_ALL_ERRORS)) < 0)
return error;
error = git_config__parse_path(out, entry->value);
@@ -917,17 +949,17 @@ int git_config__get_path(
}
int git_config_get_string(
- const char **out, const git_config *cfg, const char *name)
+ const char **out, const git_config *config, const char *name)
{
git_config_entry *entry;
int ret;
- if (!is_readonly(cfg)) {
+ if (!is_readonly(config)) {
git_error_set(GIT_ERROR_CONFIG, "get_string called on a live config object");
return -1;
}
- ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
+ ret = get_entry(&entry, config, name, true, GET_ALL_ERRORS);
*out = !ret ? (entry->value ? entry->value : "") : NULL;
git_config_entry_free(entry);
@@ -936,22 +968,22 @@ int git_config_get_string(
}
int git_config_get_string_buf(
- git_buf *out, const git_config *cfg, const char *name)
+ git_buf *out, const git_config *config, const char *name)
{
- GIT_BUF_WRAP_PRIVATE(out, git_config__get_string_buf, cfg, name);
+ GIT_BUF_WRAP_PRIVATE(out, git_config__get_string_buf, config, name);
}
int git_config__get_string_buf(
- git_str *out, const git_config *cfg, const char *name)
+ git_str *out, const git_config *config, const char *name)
{
git_config_entry *entry;
int ret;
const char *str;
GIT_ASSERT_ARG(out);
- GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(config);
- ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
+ ret = get_entry(&entry, config, name, true, GET_ALL_ERRORS);
str = !ret ? (entry->value ? entry->value : "") : NULL;
if (str)
@@ -963,12 +995,12 @@ int git_config__get_string_buf(
}
char *git_config__get_string_force(
- const git_config *cfg, const char *key, const char *fallback_value)
+ const git_config *config, const char *key, const char *fallback_value)
{
git_config_entry *entry;
char *ret;
- get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
+ get_entry(&entry, config, key, false, GET_NO_ERRORS);
ret = (entry && entry->value) ? git__strdup(entry->value) : fallback_value ? git__strdup(fallback_value) : NULL;
git_config_entry_free(entry);
@@ -976,12 +1008,12 @@ char *git_config__get_string_force(
}
int git_config__get_bool_force(
- const git_config *cfg, const char *key, int fallback_value)
+ const git_config *config, const char *key, int fallback_value)
{
int val = fallback_value;
git_config_entry *entry;
- get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
+ get_entry(&entry, config, key, false, GET_NO_ERRORS);
if (entry && git_config_parse_bool(&val, entry->value) < 0)
git_error_clear();
@@ -991,12 +1023,12 @@ int git_config__get_bool_force(
}
int git_config__get_int_force(
- const git_config *cfg, const char *key, int fallback_value)
+ const git_config *config, const char *key, int fallback_value)
{
int32_t val = (int32_t)fallback_value;
git_config_entry *entry;
- get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
+ get_entry(&entry, config, key, false, GET_NO_ERRORS);
if (entry && git_config_parse_int32(&val, entry->value) < 0)
git_error_clear();
@@ -1006,14 +1038,14 @@ int git_config__get_int_force(
}
int git_config_get_multivar_foreach(
- const git_config *cfg, const char *name, const char *regexp,
+ const git_config *config, const char *name, const char *regexp,
git_config_foreach_cb cb, void *payload)
{
int err, found;
git_config_iterator *iter;
git_config_entry *entry;
- if ((err = git_config_multivar_iterator_new(&iter, cfg, name, regexp)) < 0)
+ if ((err = git_config_multivar_iterator_new(&iter, config, name, regexp)) < 0)
return err;
found = 0;
@@ -1075,13 +1107,13 @@ static void multivar_iter_free(git_config_iterator *_iter)
git__free(iter);
}
-int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp)
+int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *config, const char *name, const char *regexp)
{
multivar_iter *iter = NULL;
git_config_iterator *inner = NULL;
int error;
- if ((error = git_config_iterator_new(&inner, cfg)) < 0)
+ if ((error = git_config_iterator_new(&inner, config)) < 0)
return error;
iter = git__calloc(1, sizeof(multivar_iter));
@@ -1112,22 +1144,24 @@ int git_config_multivar_iterator_new(git_config_iterator **out, const git_config
return error;
}
-int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
+int git_config_set_multivar(git_config *config, const char *name, const char *regexp, const char *value)
{
git_config_backend *backend;
- if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
- return GIT_ENOTFOUND;
+ if ((backend = get_writer(config)) == NULL) {
+ git_error_set(GIT_ERROR_CONFIG, "cannot set '%s': the configuration is read-only", name);
+ return GIT_EREADONLY;
+ }
return backend->set_multivar(backend, name, regexp, value);
}
-int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp)
+int git_config_delete_multivar(git_config *config, const char *name, const char *regexp)
{
git_config_backend *backend;
- if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
- return GIT_ENOTFOUND;
+ if ((backend = get_writer(config)) == NULL)
+ return GIT_EREADONLY;
return backend->del_multivar(backend, name, regexp);
}
@@ -1194,9 +1228,12 @@ int git_config__find_programdata(git_str *path)
GIT_FS_PATH_OWNER_CURRENT_USER |
GIT_FS_PATH_OWNER_ADMINISTRATOR;
bool is_safe;
+ int error;
+
+ if ((error = git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA)) < 0)
+ return error;
- if (git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA) < 0 ||
- git_fs_path_owner_is(&is_safe, path->ptr, owner_level) < 0)
+ if (git_fs_path_owner_is(&is_safe, path->ptr, owner_level) < 0)
return -1;
if (!is_safe) {
@@ -1235,79 +1272,77 @@ int git_config__global_location(git_str *buf)
int git_config_open_default(git_config **out)
{
int error;
- git_config *cfg = NULL;
+ git_config *config = NULL;
git_str buf = GIT_STR_INIT;
- if ((error = git_config_new(&cfg)) < 0)
+ if ((error = git_config_new(&config)) < 0)
return error;
if (!git_config__find_global(&buf) ||
!git_config__global_location(&buf)) {
- error = git_config_add_file_ondisk(cfg, buf.ptr,
+ error = git_config_add_file_ondisk(config, buf.ptr,
GIT_CONFIG_LEVEL_GLOBAL, NULL, 0);
}
if (!error && !git_config__find_xdg(&buf))
- error = git_config_add_file_ondisk(cfg, buf.ptr,
+ error = git_config_add_file_ondisk(config, buf.ptr,
GIT_CONFIG_LEVEL_XDG, NULL, 0);
if (!error && !git_config__find_system(&buf))
- error = git_config_add_file_ondisk(cfg, buf.ptr,
+ error = git_config_add_file_ondisk(config, buf.ptr,
GIT_CONFIG_LEVEL_SYSTEM, NULL, 0);
if (!error && !git_config__find_programdata(&buf))
- error = git_config_add_file_ondisk(cfg, buf.ptr,
+ error = git_config_add_file_ondisk(config, buf.ptr,
GIT_CONFIG_LEVEL_PROGRAMDATA, NULL, 0);
git_str_dispose(&buf);
if (error) {
- git_config_free(cfg);
- cfg = NULL;
+ git_config_free(config);
+ config = NULL;
}
- *out = cfg;
+ *out = config;
return error;
}
-int git_config_lock(git_transaction **out, git_config *cfg)
+int git_config_lock(git_transaction **out, git_config *config)
{
+ backend_instance *instance;
int error;
- git_config_backend *backend;
- backend_internal *internal;
- GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(config);
- internal = git_vector_get(&cfg->backends, 0);
- if (!internal || !internal->backend) {
- git_error_set(GIT_ERROR_CONFIG, "cannot lock; the config has no backends");
- return -1;
+ if ((instance = get_writer_instance(config)) == NULL) {
+ git_error_set(GIT_ERROR_CONFIG, "cannot lock: the configuration is read-only");
+ return GIT_EREADONLY;
}
- backend = internal->backend;
- if ((error = backend->lock(backend)) < 0)
+ if ((error = instance->backend->lock(instance->backend)) < 0 ||
+ (error = git_transaction_config_new(out, config, instance)) < 0)
return error;
- return git_transaction_config_new(out, cfg);
+ GIT_REFCOUNT_INC(instance);
+ return 0;
}
-int git_config_unlock(git_config *cfg, int commit)
+int git_config_unlock(
+ git_config *config,
+ void *data,
+ int commit)
{
- git_config_backend *backend;
- backend_internal *internal;
+ backend_instance *instance = data;
+ int error;
- GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(config && data);
+ GIT_UNUSED(config);
- internal = git_vector_get(&cfg->backends, 0);
- if (!internal || !internal->backend) {
- git_error_set(GIT_ERROR_CONFIG, "cannot lock; the config has no backends");
- return -1;
- }
-
- backend = internal->backend;
+ error = instance->backend->unlock(instance->backend, commit);
+ GIT_REFCOUNT_DEC(instance, backend_instance_free);
- return backend->unlock(backend, commit);
+ return error;
}
/***********
@@ -1464,7 +1499,7 @@ static int normalize_section(char *start, char *end)
for (scan = start; *scan; ++scan) {
if (end && scan >= end)
break;
- if (isalnum(*scan))
+ if (git__isalnum(*scan))
*scan = (char)git__tolower(*scan);
else if (*scan != '-' || scan == start)
return GIT_EINVALIDSPEC;
@@ -1526,19 +1561,32 @@ static int rename_config_entries_cb(
int error = 0;
struct rename_data *data = (struct rename_data *)payload;
size_t base_len = git_str_len(data->name);
+ git_str value = GIT_STR_INIT;
+
+ if (base_len > 0) {
+ if ((error = git_str_puts(data->name,
+ entry->name + data->old_len)) < 0 ||
+ (error = git_config_set_multivar(
+ data->config, git_str_cstr(data->name), "^$",
+ entry->value)) < 0)
+ goto cleanup;
+ }
- if (base_len > 0 &&
- !(error = git_str_puts(data->name, entry->name + data->old_len)))
- {
- error = git_config_set_string(
- data->config, git_str_cstr(data->name), entry->value);
+ git_str_putc(&value, '^');
+ git_str_puts_escape_regex(&value, entry->value);
+ git_str_putc(&value, '$');
- git_str_truncate(data->name, base_len);
+ if (git_str_oom(&value)) {
+ error = -1;
+ goto cleanup;
}
- if (!error)
- error = git_config_delete_entry(data->config, entry->name);
+ error = git_config_delete_multivar(
+ data->config, entry->name, git_str_cstr(&value));
+ cleanup:
+ git_str_truncate(data->name, base_len);
+ git_str_dispose(&value);
return error;
}
diff --git a/src/libgit2/config.h b/src/libgit2/config.h
index 01b84b157f7..5003cbf9c1f 100644
--- a/src/libgit2/config.h
+++ b/src/libgit2/config.h
@@ -24,7 +24,8 @@
struct git_config {
git_refcount rc;
- git_vector backends;
+ git_vector readers;
+ git_vector writers;
};
extern int git_config__global_location(git_str *buf);
@@ -94,17 +95,21 @@ int git_config_lookup_map_enum(git_configmap_t *type_out,
size_t map_n, int enum_val);
/**
- * Unlock the backend with the highest priority
+ * Unlock the given backend that was previously locked.
*
* Unlocking will allow other writers to update the configuration
* file. Optionally, any changes performed since the lock will be
* applied to the configuration.
*
- * @param cfg the configuration
+ * @param config the config instance
+ * @param data the config data passed to git_transaction_new
* @param commit boolean which indicates whether to commit any changes
* done since locking
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_config_unlock(git_config *cfg, int commit);
+GIT_EXTERN(int) git_config_unlock(
+ git_config *config,
+ void *data,
+ int commit);
#endif
diff --git a/src/libgit2/config_backend.h b/src/libgit2/config_backend.h
index dbb19051484..37d25abe146 100644
--- a/src/libgit2/config_backend.h
+++ b/src/libgit2/config_backend.h
@@ -37,15 +37,6 @@ extern int git_config_backend_from_file(git_config_backend **out, const char *pa
*/
extern int git_config_backend_snapshot(git_config_backend **out, git_config_backend *source);
-/**
- * Create an in-memory configuration file backend
- *
- * @param out the new backend
- * @param cfg the configuration that is to be parsed
- * @param len the length of the string pointed to by `cfg`
- */
-extern int git_config_backend_from_string(git_config_backend **out, const char *cfg, size_t len);
-
GIT_INLINE(int) git_config_backend_open(git_config_backend *cfg, unsigned int level, const git_repository *repo)
{
return cfg->open(cfg, level, repo);
diff --git a/src/libgit2/config_entries.c b/src/libgit2/config_entries.c
deleted file mode 100644
index 66aae096d2d..00000000000
--- a/src/libgit2/config_entries.c
+++ /dev/null
@@ -1,237 +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 "config_entries.h"
-
-typedef struct config_entry_list {
- struct config_entry_list *next;
- struct config_entry_list *last;
- git_config_entry *entry;
-} config_entry_list;
-
-typedef struct {
- git_config_entry *entry;
- bool multivar;
-} config_entry_map_head;
-
-typedef struct config_entries_iterator {
- git_config_iterator parent;
- git_config_entries *entries;
- config_entry_list *head;
-} config_entries_iterator;
-
-struct git_config_entries {
- git_refcount rc;
- git_strmap *map;
- config_entry_list *list;
-};
-
-int git_config_entries_new(git_config_entries **out)
-{
- git_config_entries *entries;
- int error;
-
- entries = git__calloc(1, sizeof(git_config_entries));
- GIT_ERROR_CHECK_ALLOC(entries);
- GIT_REFCOUNT_INC(entries);
-
- if ((error = git_strmap_new(&entries->map)) < 0)
- git__free(entries);
- else
- *out = entries;
-
- return error;
-}
-
-int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry)
-{
- git_config_entry *duplicated;
- int error;
-
- duplicated = git__calloc(1, sizeof(git_config_entry));
- GIT_ERROR_CHECK_ALLOC(duplicated);
-
- duplicated->name = git__strdup(entry->name);
- GIT_ERROR_CHECK_ALLOC(duplicated->name);
-
- if (entry->value) {
- duplicated->value = git__strdup(entry->value);
- GIT_ERROR_CHECK_ALLOC(duplicated->value);
- }
- duplicated->level = entry->level;
- duplicated->include_depth = entry->include_depth;
-
- if ((error = git_config_entries_append(entries, duplicated)) < 0)
- goto out;
-
-out:
- if (error && duplicated) {
- git__free((char *) duplicated->name);
- git__free((char *) duplicated->value);
- git__free(duplicated);
- }
- return error;
-}
-
-int git_config_entries_dup(git_config_entries **out, git_config_entries *entries)
-{
- git_config_entries *result = NULL;
- config_entry_list *head;
- int error;
-
- if ((error = git_config_entries_new(&result)) < 0)
- goto out;
-
- for (head = entries->list; head; head = head->next)
- if ((git_config_entries_dup_entry(result, head->entry)) < 0)
- goto out;
-
- *out = result;
- result = NULL;
-
-out:
- git_config_entries_free(result);
- return error;
-}
-
-void git_config_entries_incref(git_config_entries *entries)
-{
- GIT_REFCOUNT_INC(entries);
-}
-
-static void config_entries_free(git_config_entries *entries)
-{
- config_entry_list *list = NULL, *next;
- config_entry_map_head *head;
-
- git_strmap_foreach_value(entries->map, head,
- git__free((char *) head->entry->name); git__free(head)
- );
- git_strmap_free(entries->map);
-
- list = entries->list;
- while (list != NULL) {
- next = list->next;
- git__free((char *) list->entry->value);
- git__free(list->entry);
- git__free(list);
- list = next;
- }
-
- git__free(entries);
-}
-
-void git_config_entries_free(git_config_entries *entries)
-{
- if (entries)
- GIT_REFCOUNT_DEC(entries, config_entries_free);
-}
-
-int git_config_entries_append(git_config_entries *entries, git_config_entry *entry)
-{
- config_entry_list *list_head;
- config_entry_map_head *map_head;
-
- if ((map_head = git_strmap_get(entries->map, entry->name)) != NULL) {
- map_head->multivar = true;
- /*
- * This is a micro-optimization for configuration files
- * with a lot of same keys. As for multivars the entry's
- * key will be the same for all entries, we can just free
- * all except the first entry's name and just re-use it.
- */
- git__free((char *) entry->name);
- entry->name = map_head->entry->name;
- } else {
- map_head = git__calloc(1, sizeof(*map_head));
- if ((git_strmap_set(entries->map, entry->name, map_head)) < 0)
- return -1;
- }
- map_head->entry = entry;
-
- list_head = git__calloc(1, sizeof(config_entry_list));
- GIT_ERROR_CHECK_ALLOC(list_head);
- list_head->entry = entry;
-
- if (entries->list)
- entries->list->last->next = list_head;
- else
- entries->list = list_head;
- entries->list->last = list_head;
-
- return 0;
-}
-
-int git_config_entries_get(git_config_entry **out, git_config_entries *entries, const char *key)
-{
- config_entry_map_head *entry;
- if ((entry = git_strmap_get(entries->map, key)) == NULL)
- return GIT_ENOTFOUND;
- *out = entry->entry;
- return 0;
-}
-
-int git_config_entries_get_unique(git_config_entry **out, git_config_entries *entries, const char *key)
-{
- config_entry_map_head *entry;
-
- if ((entry = git_strmap_get(entries->map, key)) == NULL)
- return GIT_ENOTFOUND;
-
- if (entry->multivar) {
- git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being a multivar");
- return -1;
- }
-
- if (entry->entry->include_depth) {
- git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being included");
- return -1;
- }
-
- *out = entry->entry;
-
- return 0;
-}
-
-static void config_iterator_free(git_config_iterator *iter)
-{
- config_entries_iterator *it = (config_entries_iterator *) iter;
- git_config_entries_free(it->entries);
- git__free(it);
-}
-
-static int config_iterator_next(
- git_config_entry **entry,
- git_config_iterator *iter)
-{
- config_entries_iterator *it = (config_entries_iterator *) iter;
-
- if (!it->head)
- return GIT_ITEROVER;
-
- *entry = it->head->entry;
- it->head = it->head->next;
-
- return 0;
-}
-
-int git_config_entries_iterator_new(git_config_iterator **out, git_config_entries *entries)
-{
- config_entries_iterator *it;
-
- it = git__calloc(1, sizeof(config_entries_iterator));
- GIT_ERROR_CHECK_ALLOC(it);
- it->parent.next = config_iterator_next;
- it->parent.free = config_iterator_free;
- it->head = entries->list;
- it->entries = entries;
-
- git_config_entries_incref(entries);
- *out = &it->parent;
-
- return 0;
-}
diff --git a/src/libgit2/config_entries.h b/src/libgit2/config_entries.h
deleted file mode 100644
index 832379e7466..00000000000
--- a/src/libgit2/config_entries.h
+++ /dev/null
@@ -1,24 +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 "common.h"
-
-#include "git2/sys/config.h"
-#include "config.h"
-
-typedef struct git_config_entries git_config_entries;
-
-int git_config_entries_new(git_config_entries **out);
-int git_config_entries_dup(git_config_entries **out, git_config_entries *entries);
-int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry);
-void git_config_entries_incref(git_config_entries *entries);
-void git_config_entries_free(git_config_entries *entries);
-/* Add or append the new config option */
-int git_config_entries_append(git_config_entries *entries, git_config_entry *entry);
-int git_config_entries_get(git_config_entry **out, git_config_entries *entries, const char *key);
-int git_config_entries_get_unique(git_config_entry **out, git_config_entries *entries, const char *key);
-int git_config_entries_iterator_new(git_config_iterator **out, git_config_entries *entries);
diff --git a/src/libgit2/config_file.c b/src/libgit2/config_file.c
index 66fcb8ae295..340e85691ed 100644
--- a/src/libgit2/config_file.c
+++ b/src/libgit2/config_file.c
@@ -13,7 +13,7 @@
#include "array.h"
#include "str.h"
#include "config_backend.h"
-#include "config_entries.h"
+#include "config_list.h"
#include "config_parse.h"
#include "filebuf.h"
#include "regexp.h"
@@ -24,9 +24,11 @@
/* Max depth for [include] directives */
#define MAX_INCLUDE_DEPTH 10
+#define CONFIG_FILE_TYPE "file"
+
typedef struct config_file {
git_futils_filestamp stamp;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
+ unsigned char checksum[GIT_HASH_SHA256_SIZE];
char *path;
git_array_t(struct config_file) includes;
} config_file;
@@ -34,7 +36,7 @@ typedef struct config_file {
typedef struct {
git_config_backend parent;
git_mutex values_mutex;
- git_config_entries *entries;
+ git_config_list *config_list;
const git_repository *repo;
git_config_level_t level;
@@ -50,13 +52,13 @@ typedef struct {
typedef struct {
const git_repository *repo;
config_file *file;
- git_config_entries *entries;
+ git_config_list *config_list;
git_config_level_t level;
unsigned int depth;
} config_file_parse_data;
-static int config_file_read(git_config_entries *entries, const git_repository *repo, config_file *file, git_config_level_t level, int depth);
-static int config_file_read_buffer(git_config_entries *entries, const git_repository *repo, config_file *file, git_config_level_t level, int depth, const char *buf, size_t buflen);
+static int config_file_read(git_config_list *config_list, const git_repository *repo, config_file *file, git_config_level_t level, int depth);
+static int config_file_read_buffer(git_config_list *config_list, const git_repository *repo, config_file *file, git_config_level_t level, int depth, const char *buf, size_t buflen);
static int config_file_write(config_file_backend *cfg, const char *orig_key, const char *key, const git_regexp *preg, const char *value);
static char *escape_value(const char *ptr);
@@ -65,7 +67,7 @@ static char *escape_value(const char *ptr);
* refcount. This is its own function to make sure we use the mutex to
* avoid the map pointer from changing under us.
*/
-static int config_file_entries_take(git_config_entries **out, config_file_backend *b)
+static int config_file_take_list(git_config_list **out, config_file_backend *b)
{
int error;
@@ -74,8 +76,8 @@ static int config_file_entries_take(git_config_entries **out, config_file_backen
return error;
}
- git_config_entries_incref(b->entries);
- *out = b->entries;
+ git_config_list_incref(b->config_list);
+ *out = b->config_list;
git_mutex_unlock(&b->values_mutex);
@@ -106,7 +108,7 @@ static int config_file_open(git_config_backend *cfg, git_config_level_t level, c
b->level = level;
b->repo = repo;
- if ((res = git_config_entries_new(&b->entries)) < 0)
+ if ((res = git_config_list_new(&b->config_list)) < 0)
return res;
if (!git_fs_path_exists(b->file.path))
@@ -121,9 +123,9 @@ static int config_file_open(git_config_backend *cfg, git_config_level_t level, c
if (p_access(b->file.path, R_OK) < 0)
return GIT_ENOTFOUND;
- if (res < 0 || (res = config_file_read(b->entries, repo, &b->file, level, 0)) < 0) {
- git_config_entries_free(b->entries);
- b->entries = NULL;
+ if (res < 0 || (res = config_file_read(b->config_list, repo, &b->file, level, 0)) < 0) {
+ git_config_list_free(b->config_list);
+ b->config_list = NULL;
}
return res;
@@ -133,7 +135,7 @@ static int config_file_is_modified(int *modified, config_file *file)
{
config_file *include;
git_str buf = GIT_STR_INIT;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
+ unsigned char checksum[GIT_HASH_SHA256_SIZE];
uint32_t i;
int error = 0;
@@ -145,10 +147,10 @@ static int config_file_is_modified(int *modified, config_file *file)
if ((error = git_futils_readbuffer(&buf, file->path)) < 0)
goto out;
- if ((error = git_hash_buf(checksum, buf.ptr, buf.size, GIT_HASH_ALGORITHM_SHA1)) < 0)
+ if ((error = git_hash_buf(checksum, buf.ptr, buf.size, GIT_HASH_ALGORITHM_SHA256)) < 0)
goto out;
- if (memcmp(checksum, file->checksum, GIT_HASH_SHA1_SIZE) != 0) {
+ if (memcmp(checksum, file->checksum, GIT_HASH_SHA256_SIZE) != 0) {
*modified = 1;
goto out;
}
@@ -175,10 +177,10 @@ static void config_file_clear_includes(config_file_backend *cfg)
git_array_clear(cfg->file.includes);
}
-static int config_file_set_entries(git_config_backend *cfg, git_config_entries *entries)
+static int config_file_set_entries(git_config_backend *cfg, git_config_list *config_list)
{
config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
- git_config_entries *old = NULL;
+ git_config_list *old = NULL;
int error;
if (b->parent.readonly) {
@@ -191,40 +193,40 @@ static int config_file_set_entries(git_config_backend *cfg, git_config_entries *
goto out;
}
- old = b->entries;
- b->entries = entries;
+ old = b->config_list;
+ b->config_list = config_list;
git_mutex_unlock(&b->values_mutex);
out:
- git_config_entries_free(old);
+ git_config_list_free(old);
return error;
}
static int config_file_refresh_from_buffer(git_config_backend *cfg, const char *buf, size_t buflen)
{
config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
- git_config_entries *entries = NULL;
+ git_config_list *config_list = NULL;
int error;
config_file_clear_includes(b);
- if ((error = git_config_entries_new(&entries)) < 0 ||
- (error = config_file_read_buffer(entries, b->repo, &b->file,
+ if ((error = git_config_list_new(&config_list)) < 0 ||
+ (error = config_file_read_buffer(config_list, b->repo, &b->file,
b->level, 0, buf, buflen)) < 0 ||
- (error = config_file_set_entries(cfg, entries)) < 0)
+ (error = config_file_set_entries(cfg, config_list)) < 0)
goto out;
- entries = NULL;
+ config_list = NULL;
out:
- git_config_entries_free(entries);
+ git_config_list_free(config_list);
return error;
}
static int config_file_refresh(git_config_backend *cfg)
{
config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
- git_config_entries *entries = NULL;
+ git_config_list *config_list = NULL;
int error, modified;
if (cfg->readonly)
@@ -238,14 +240,14 @@ static int config_file_refresh(git_config_backend *cfg)
config_file_clear_includes(b);
- if ((error = git_config_entries_new(&entries)) < 0 ||
- (error = config_file_read(entries, b->repo, &b->file, b->level, 0)) < 0 ||
- (error = config_file_set_entries(cfg, entries)) < 0)
+ if ((error = git_config_list_new(&config_list)) < 0 ||
+ (error = config_file_read(config_list, b->repo, &b->file, b->level, 0)) < 0 ||
+ (error = config_file_set_entries(cfg, config_list)) < 0)
goto out;
- entries = NULL;
+ config_list = NULL;
out:
- git_config_entries_free(entries);
+ git_config_list_free(config_list);
return (error == GIT_ENOTFOUND) ? 0 : error;
}
@@ -258,7 +260,7 @@ static void config_file_free(git_config_backend *_backend)
return;
config_file_clear(&backend->file);
- git_config_entries_free(backend->entries);
+ git_config_list_free(backend->config_list);
git_mutex_free(&backend->values_mutex);
git__free(backend);
}
@@ -268,19 +270,19 @@ static int config_file_iterator(
struct git_config_backend *backend)
{
config_file_backend *b = GIT_CONTAINER_OF(backend, config_file_backend, parent);
- git_config_entries *dupped = NULL, *entries = NULL;
+ git_config_list *dupped = NULL, *config_list = NULL;
int error;
if ((error = config_file_refresh(backend)) < 0 ||
- (error = config_file_entries_take(&entries, b)) < 0 ||
- (error = git_config_entries_dup(&dupped, entries)) < 0 ||
- (error = git_config_entries_iterator_new(iter, dupped)) < 0)
+ (error = config_file_take_list(&config_list, b)) < 0 ||
+ (error = git_config_list_dup(&dupped, config_list)) < 0 ||
+ (error = git_config_list_iterator_new(iter, dupped)) < 0)
goto out;
out:
- /* Let iterator delete duplicated entries when it's done */
- git_config_entries_free(entries);
- git_config_entries_free(dupped);
+ /* Let iterator delete duplicated config_list when it's done */
+ git_config_list_free(config_list);
+ git_config_list_free(dupped);
return error;
}
@@ -292,24 +294,24 @@ static int config_file_snapshot(git_config_backend **out, git_config_backend *ba
static int config_file_set(git_config_backend *cfg, const char *name, const char *value)
{
config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
- git_config_entries *entries;
- git_config_entry *existing;
+ git_config_list *config_list;
+ git_config_list_entry *existing;
char *key, *esc_value = NULL;
int error;
if ((error = git_config__normalize_name(name, &key)) < 0)
return error;
- if ((error = config_file_entries_take(&entries, b)) < 0)
+ if ((error = config_file_take_list(&config_list, b)) < 0)
return error;
/* Check whether we'd be modifying an included or multivar key */
- if ((error = git_config_entries_get_unique(&existing, entries, key)) < 0) {
+ if ((error = git_config_list_get_unique(&existing, config_list, key)) < 0) {
if (error != GIT_ENOTFOUND)
goto out;
error = 0;
- } else if ((!existing->value && !value) ||
- (existing->value && value && !strcmp(existing->value, value))) {
+ } else if ((!existing->base.value && !value) ||
+ (existing->base.value && value && !strcmp(existing->base.value, value))) {
/* don't update if old and new values already match */
error = 0;
goto out;
@@ -325,43 +327,34 @@ static int config_file_set(git_config_backend *cfg, const char *name, const char
goto out;
out:
- git_config_entries_free(entries);
+ git_config_list_free(config_list);
git__free(esc_value);
git__free(key);
return error;
}
-/* release the map containing the entry as an equivalent to freeing it */
-static void config_file_entry_free(git_config_entry *entry)
-{
- git_config_entries *entries = (git_config_entries *) entry->payload;
- git_config_entries_free(entries);
-}
-
/*
* Internal function that actually gets the value in string form
*/
static int config_file_get(git_config_backend *cfg, const char *key, git_config_entry **out)
{
config_file_backend *h = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
- git_config_entries *entries = NULL;
- git_config_entry *entry;
+ git_config_list *config_list = NULL;
+ git_config_list_entry *entry;
int error = 0;
if (!h->parent.readonly && ((error = config_file_refresh(cfg)) < 0))
return error;
- if ((error = config_file_entries_take(&entries, h)) < 0)
+ if ((error = config_file_take_list(&config_list, h)) < 0)
return error;
- if ((error = (git_config_entries_get(&entry, entries, key))) < 0) {
- git_config_entries_free(entries);
+ if ((error = (git_config_list_get(&entry, config_list, key))) < 0) {
+ git_config_list_free(config_list);
return error;
}
- entry->free = config_file_entry_free;
- entry->payload = entries;
- *out = entry;
+ *out = &entry->base;
return 0;
}
@@ -396,29 +389,29 @@ static int config_file_set_multivar(
static int config_file_delete(git_config_backend *cfg, const char *name)
{
config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
- git_config_entries *entries = NULL;
- git_config_entry *entry;
+ git_config_list *config_list = NULL;
+ git_config_list_entry *entry;
char *key = NULL;
int error;
if ((error = git_config__normalize_name(name, &key)) < 0)
goto out;
- if ((error = config_file_entries_take(&entries, b)) < 0)
+ if ((error = config_file_take_list(&config_list, b)) < 0)
goto out;
/* Check whether we'd be modifying an included or multivar key */
- if ((error = git_config_entries_get_unique(&entry, entries, key)) < 0) {
+ if ((error = git_config_list_get_unique(&entry, config_list, key)) < 0) {
if (error == GIT_ENOTFOUND)
git_error_set(GIT_ERROR_CONFIG, "could not find key '%s' to delete", name);
goto out;
}
- if ((error = config_file_write(b, name, entry->name, NULL, NULL)) < 0)
+ if ((error = config_file_write(b, name, entry->base.name, NULL, NULL)) < 0)
goto out;
out:
- git_config_entries_free(entries);
+ git_config_list_free(config_list);
git__free(key);
return error;
}
@@ -426,8 +419,8 @@ static int config_file_delete(git_config_backend *cfg, const char *name)
static int config_file_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
{
config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
- git_config_entries *entries = NULL;
- git_config_entry *entry = NULL;
+ git_config_list *config_list = NULL;
+ git_config_list_entry *entry = NULL;
git_regexp preg = GIT_REGEX_INIT;
char *key = NULL;
int result;
@@ -435,10 +428,10 @@ static int config_file_delete_multivar(git_config_backend *cfg, const char *name
if ((result = git_config__normalize_name(name, &key)) < 0)
goto out;
- if ((result = config_file_entries_take(&entries, b)) < 0)
+ if ((result = config_file_take_list(&config_list, b)) < 0)
goto out;
- if ((result = git_config_entries_get(&entry, entries, key)) < 0) {
+ if ((result = git_config_list_get(&entry, config_list, key)) < 0) {
if (result == GIT_ENOTFOUND)
git_error_set(GIT_ERROR_CONFIG, "could not find key '%s' to delete", name);
goto out;
@@ -451,7 +444,7 @@ static int config_file_delete_multivar(git_config_backend *cfg, const char *name
goto out;
out:
- git_config_entries_free(entries);
+ git_config_list_free(config_list);
git__free(key);
git_regexp_dispose(&preg);
return result;
@@ -528,7 +521,7 @@ static int included_path(git_str *out, const char *dir, const char *path)
{
/* From the user's home */
if (path[0] == '~' && path[1] == '/')
- return git_sysdir_expand_global_file(out, &path[1]);
+ return git_sysdir_expand_homedir_file(out, &path[1]);
return git_fs_path_join_unrooted(out, path, dir, NULL);
}
@@ -591,7 +584,7 @@ static int parse_include(config_file_parse_data *parse_data, const char *file)
git_array_init(include->includes);
include->path = git_str_detach(&path);
- result = config_file_read(parse_data->entries, parse_data->repo, include,
+ result = config_file_read(parse_data->config_list, parse_data->repo, include,
parse_data->level, parse_data->depth+1);
if (result == GIT_ENOTFOUND) {
@@ -616,7 +609,7 @@ static int do_match_gitdir(
git_fs_path_dirname_r(&pattern, cfg_file);
git_str_joinpath(&pattern, pattern.ptr, condition + 2);
} else if (condition[0] == '~' && git_fs_path_is_dirsep(condition[1]))
- git_sysdir_expand_global_file(&pattern, condition + 1);
+ git_sysdir_expand_homedir_file(&pattern, condition + 1);
else if (!git_fs_path_is_absolute(condition))
git_str_joinpath(&pattern, "**", condition);
else
@@ -776,7 +769,7 @@ static int read_on_variable(
{
config_file_parse_data *parse_data = (config_file_parse_data *)data;
git_str buf = GIT_STR_INIT;
- git_config_entry *entry;
+ git_config_list_entry *entry;
const char *c;
int result = 0;
@@ -799,30 +792,45 @@ static int read_on_variable(
if (git_str_oom(&buf))
return -1;
- entry = git__calloc(1, sizeof(git_config_entry));
+ entry = git__calloc(1, sizeof(git_config_list_entry));
GIT_ERROR_CHECK_ALLOC(entry);
- entry->name = git_str_detach(&buf);
- entry->value = var_value ? git__strdup(var_value) : NULL;
- entry->level = parse_data->level;
- entry->include_depth = parse_data->depth;
- if ((result = git_config_entries_append(parse_data->entries, entry)) < 0)
+ entry->base.name = git_str_detach(&buf);
+ GIT_ERROR_CHECK_ALLOC(entry->base.name);
+
+ if (var_value) {
+ entry->base.value = git__strdup(var_value);
+ GIT_ERROR_CHECK_ALLOC(entry->base.value);
+ }
+
+ entry->base.backend_type = git_config_list_add_string(parse_data->config_list, CONFIG_FILE_TYPE);
+ GIT_ERROR_CHECK_ALLOC(entry->base.backend_type);
+
+ entry->base.origin_path = git_config_list_add_string(parse_data->config_list, parse_data->file->path);
+ GIT_ERROR_CHECK_ALLOC(entry->base.origin_path);
+
+ entry->base.level = parse_data->level;
+ entry->base.include_depth = parse_data->depth;
+ entry->base.free = git_config_list_entry_free;
+ entry->config_list = parse_data->config_list;
+
+ if ((result = git_config_list_append(parse_data->config_list, entry)) < 0)
return result;
result = 0;
/* Add or append the new config option */
- if (!git__strcmp(entry->name, "include.path"))
- result = parse_include(parse_data, entry->value);
- else if (!git__prefixcmp(entry->name, "includeif.") &&
- !git__suffixcmp(entry->name, ".path"))
- result = parse_conditional_include(parse_data, entry->name, entry->value);
+ if (!git__strcmp(entry->base.name, "include.path"))
+ result = parse_include(parse_data, entry->base.value);
+ else if (!git__prefixcmp(entry->base.name, "includeif.") &&
+ !git__suffixcmp(entry->base.name, ".path"))
+ result = parse_conditional_include(parse_data, entry->base.name, entry->base.value);
return result;
}
static int config_file_read_buffer(
- git_config_entries *entries,
+ git_config_list *config_list,
const git_repository *repo,
config_file *file,
git_config_level_t level,
@@ -851,7 +859,7 @@ static int config_file_read_buffer(
parse_data.repo = repo;
parse_data.file = file;
- parse_data.entries = entries;
+ parse_data.config_list = config_list;
parse_data.level = level;
parse_data.depth = depth;
@@ -862,7 +870,7 @@ static int config_file_read_buffer(
}
static int config_file_read(
- git_config_entries *entries,
+ git_config_list *config_list,
const git_repository *repo,
config_file *file,
git_config_level_t level,
@@ -881,10 +889,10 @@ static int config_file_read(
goto out;
git_futils_filestamp_set_from_stat(&file->stamp, &st);
- if ((error = git_hash_buf(file->checksum, contents.ptr, contents.size, GIT_HASH_ALGORITHM_SHA1)) < 0)
+ if ((error = git_hash_buf(file->checksum, contents.ptr, contents.size, GIT_HASH_ALGORITHM_SHA256)) < 0)
goto out;
- if ((error = config_file_read_buffer(entries, repo, file, level, depth,
+ if ((error = config_file_read_buffer(config_list, repo, file, level, depth,
contents.ptr, contents.size)) < 0)
goto out;
@@ -1116,7 +1124,12 @@ static int write_on_eof(
/*
* This is pretty much the parsing, except we write out anything we don't have
*/
-static int config_file_write(config_file_backend *cfg, const char *orig_key, const char *key, const git_regexp *preg, const char *value)
+static int config_file_write(
+ config_file_backend *cfg,
+ const char *orig_key,
+ const char *key,
+ const git_regexp *preg,
+ const char *value)
{
char *orig_section = NULL, *section = NULL, *orig_name, *name, *ldot;
@@ -1131,8 +1144,9 @@ static int config_file_write(config_file_backend *cfg, const char *orig_key, con
if (cfg->locked) {
error = git_str_puts(&contents, git_str_cstr(&cfg->locked_content) == NULL ? "" : git_str_cstr(&cfg->locked_content));
} else {
- if ((error = git_filebuf_open(&file, cfg->file.path, GIT_FILEBUF_HASH_CONTENTS,
- GIT_CONFIG_FILE_MODE)) < 0)
+ if ((error = git_filebuf_open(&file, cfg->file.path,
+ GIT_FILEBUF_HASH_SHA256,
+ GIT_CONFIG_FILE_MODE)) < 0)
goto done;
/* We need to read in our own config file */
diff --git a/src/libgit2/config_list.c b/src/libgit2/config_list.c
new file mode 100644
index 00000000000..0b7a4f3600a
--- /dev/null
+++ b/src/libgit2/config_list.c
@@ -0,0 +1,288 @@
+/*
+ * 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 "config_list.h"
+
+typedef struct config_entry_list {
+ struct config_entry_list *next;
+ struct config_entry_list *last;
+ git_config_list_entry *entry;
+} config_entry_list;
+
+typedef struct {
+ git_config_list_entry *entry;
+ bool multivar;
+} config_entry_map_head;
+
+typedef struct config_list_iterator {
+ git_config_iterator parent;
+ git_config_list *list;
+ config_entry_list *head;
+} config_list_iterator;
+
+struct git_config_list {
+ git_refcount rc;
+
+ /* Interned strings - paths to config files or backend types */
+ git_strmap *strings;
+
+ /* Config entries */
+ git_strmap *map;
+ config_entry_list *entries;
+};
+
+int git_config_list_new(git_config_list **out)
+{
+ git_config_list *config_list;
+
+ config_list = git__calloc(1, sizeof(git_config_list));
+ GIT_ERROR_CHECK_ALLOC(config_list);
+ GIT_REFCOUNT_INC(config_list);
+
+ if (git_strmap_new(&config_list->strings) < 0 ||
+ git_strmap_new(&config_list->map) < 0) {
+ git_strmap_free(config_list->strings);
+ git_strmap_free(config_list->map);
+ git__free(config_list);
+
+ return -1;
+ }
+
+ *out = config_list;
+ return 0;
+}
+
+int git_config_list_dup_entry(git_config_list *config_list, const git_config_entry *entry)
+{
+ git_config_list_entry *duplicated;
+ int error;
+
+ duplicated = git__calloc(1, sizeof(git_config_list_entry));
+ GIT_ERROR_CHECK_ALLOC(duplicated);
+
+ duplicated->base.name = git__strdup(entry->name);
+ GIT_ERROR_CHECK_ALLOC(duplicated->base.name);
+
+ if (entry->value) {
+ duplicated->base.value = git__strdup(entry->value);
+ GIT_ERROR_CHECK_ALLOC(duplicated->base.value);
+ }
+
+ duplicated->base.backend_type = git_config_list_add_string(config_list, entry->backend_type);
+ GIT_ERROR_CHECK_ALLOC(duplicated->base.backend_type);
+
+ if (entry->origin_path) {
+ duplicated->base.origin_path = git_config_list_add_string(config_list, entry->origin_path);
+ GIT_ERROR_CHECK_ALLOC(duplicated->base.origin_path);
+ }
+
+ duplicated->base.level = entry->level;
+ duplicated->base.include_depth = entry->include_depth;
+ duplicated->base.free = git_config_list_entry_free;
+ duplicated->config_list = config_list;
+
+ if ((error = git_config_list_append(config_list, duplicated)) < 0)
+ goto out;
+
+out:
+ if (error && duplicated) {
+ git__free((char *) duplicated->base.name);
+ git__free((char *) duplicated->base.value);
+ git__free(duplicated);
+ }
+ return error;
+}
+
+int git_config_list_dup(git_config_list **out, git_config_list *config_list)
+{
+ git_config_list *result = NULL;
+ config_entry_list *head;
+ int error;
+
+ if ((error = git_config_list_new(&result)) < 0)
+ goto out;
+
+ for (head = config_list->entries; head; head = head->next)
+ if ((git_config_list_dup_entry(result, &head->entry->base)) < 0)
+ goto out;
+
+ *out = result;
+ result = NULL;
+
+out:
+ git_config_list_free(result);
+ return error;
+}
+
+void git_config_list_incref(git_config_list *config_list)
+{
+ GIT_REFCOUNT_INC(config_list);
+}
+
+static void config_list_free(git_config_list *config_list)
+{
+ config_entry_list *entry_list = NULL, *next;
+ config_entry_map_head *head;
+ char *str;
+
+ git_strmap_foreach_value(config_list->strings, str, {
+ git__free(str);
+ });
+ git_strmap_free(config_list->strings);
+
+ git_strmap_foreach_value(config_list->map, head, {
+ git__free((char *) head->entry->base.name);
+ git__free(head);
+ });
+ git_strmap_free(config_list->map);
+
+ entry_list = config_list->entries;
+ while (entry_list != NULL) {
+ next = entry_list->next;
+ git__free((char *) entry_list->entry->base.value);
+ git__free(entry_list->entry);
+ git__free(entry_list);
+ entry_list = next;
+ }
+
+ git__free(config_list);
+}
+
+void git_config_list_free(git_config_list *config_list)
+{
+ if (config_list)
+ GIT_REFCOUNT_DEC(config_list, config_list_free);
+}
+
+int git_config_list_append(git_config_list *config_list, git_config_list_entry *entry)
+{
+ config_entry_list *list_head;
+ config_entry_map_head *map_head;
+
+ if ((map_head = git_strmap_get(config_list->map, entry->base.name)) != NULL) {
+ map_head->multivar = true;
+ /*
+ * This is a micro-optimization for configuration files
+ * with a lot of same keys. As for multivars the entry's
+ * key will be the same for all list, we can just free
+ * all except the first entry's name and just re-use it.
+ */
+ git__free((char *) entry->base.name);
+ entry->base.name = map_head->entry->base.name;
+ } else {
+ map_head = git__calloc(1, sizeof(*map_head));
+ if ((git_strmap_set(config_list->map, entry->base.name, map_head)) < 0)
+ return -1;
+ }
+ map_head->entry = entry;
+
+ list_head = git__calloc(1, sizeof(config_entry_list));
+ GIT_ERROR_CHECK_ALLOC(list_head);
+ list_head->entry = entry;
+
+ if (config_list->entries)
+ config_list->entries->last->next = list_head;
+ else
+ config_list->entries = list_head;
+ config_list->entries->last = list_head;
+
+ return 0;
+}
+
+int git_config_list_get(git_config_list_entry **out, git_config_list *config_list, const char *key)
+{
+ config_entry_map_head *entry;
+
+ if ((entry = git_strmap_get(config_list->map, key)) == NULL)
+ return GIT_ENOTFOUND;
+
+ *out = entry->entry;
+ return 0;
+}
+
+int git_config_list_get_unique(git_config_list_entry **out, git_config_list *config_list, const char *key)
+{
+ config_entry_map_head *entry;
+
+ if ((entry = git_strmap_get(config_list->map, key)) == NULL)
+ return GIT_ENOTFOUND;
+
+ if (entry->multivar) {
+ git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being a multivar");
+ return -1;
+ }
+
+ if (entry->entry->base.include_depth) {
+ git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being included");
+ return -1;
+ }
+
+ *out = entry->entry;
+ return 0;
+}
+
+static void config_iterator_free(git_config_iterator *iter)
+{
+ config_list_iterator *it = (config_list_iterator *) iter;
+ git_config_list_free(it->list);
+ git__free(it);
+}
+
+static int config_iterator_next(
+ git_config_entry **entry,
+ git_config_iterator *iter)
+{
+ config_list_iterator *it = (config_list_iterator *) iter;
+
+ if (!it->head)
+ return GIT_ITEROVER;
+
+ *entry = &it->head->entry->base;
+ it->head = it->head->next;
+
+ return 0;
+}
+
+int git_config_list_iterator_new(git_config_iterator **out, git_config_list *config_list)
+{
+ config_list_iterator *it;
+
+ it = git__calloc(1, sizeof(config_list_iterator));
+ GIT_ERROR_CHECK_ALLOC(it);
+ it->parent.next = config_iterator_next;
+ it->parent.free = config_iterator_free;
+ it->head = config_list->entries;
+ it->list = config_list;
+
+ git_config_list_incref(config_list);
+ *out = &it->parent;
+
+ return 0;
+}
+
+/* release the map containing the entry as an equivalent to freeing it */
+void git_config_list_entry_free(git_config_entry *e)
+{
+ git_config_list_entry *entry = (git_config_list_entry *)e;
+ git_config_list_free(entry->config_list);
+}
+
+const char *git_config_list_add_string(
+ git_config_list *config_list,
+ const char *str)
+{
+ const char *s;
+
+ if ((s = git_strmap_get(config_list->strings, str)) != NULL)
+ return s;
+
+ if ((s = git__strdup(str)) == NULL ||
+ git_strmap_set(config_list->strings, s, (void *)s) < 0)
+ return NULL;
+
+ return s;
+}
diff --git a/src/libgit2/config_list.h b/src/libgit2/config_list.h
new file mode 100644
index 00000000000..023bca1e5ca
--- /dev/null
+++ b/src/libgit2/config_list.h
@@ -0,0 +1,32 @@
+/*
+ * 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 "common.h"
+
+#include "git2/sys/config.h"
+#include "config.h"
+
+typedef struct git_config_list git_config_list;
+
+typedef struct {
+ git_config_entry base;
+ git_config_list *config_list;
+} git_config_list_entry;
+
+int git_config_list_new(git_config_list **out);
+int git_config_list_dup(git_config_list **out, git_config_list *list);
+int git_config_list_dup_entry(git_config_list *list, const git_config_entry *entry);
+void git_config_list_incref(git_config_list *list);
+void git_config_list_free(git_config_list *list);
+/* Add or append the new config option */
+int git_config_list_append(git_config_list *list, git_config_list_entry *entry);
+int git_config_list_get(git_config_list_entry **out, git_config_list *list, const char *key);
+int git_config_list_get_unique(git_config_list_entry **out, git_config_list *list, const char *key);
+int git_config_list_iterator_new(git_config_iterator **out, git_config_list *list);
+const char *git_config_list_add_string(git_config_list *list, const char *str);
+
+void git_config_list_entry_free(git_config_entry *entry);
diff --git a/src/libgit2/config_mem.c b/src/libgit2/config_mem.c
index 560229cf534..406aa83e6e1 100644
--- a/src/libgit2/config_mem.c
+++ b/src/libgit2/config_mem.c
@@ -9,16 +9,29 @@
#include "config_backend.h"
#include "config_parse.h"
-#include "config_entries.h"
+#include "config_list.h"
+#include "strlist.h"
typedef struct {
git_config_backend parent;
- git_config_entries *entries;
+
+ char *backend_type;
+ char *origin_path;
+
+ git_config_list *config_list;
+
+ /* Configuration data in the config file format */
git_str cfg;
+
+ /* Array of key=value pairs */
+ char **values;
+ size_t values_len;
} config_memory_backend;
typedef struct {
- git_config_entries *entries;
+ const char *backend_type;
+ const char *origin_path;
+ git_config_list *config_list;
git_config_level_t level;
} config_memory_parse_data;
@@ -39,7 +52,7 @@ static int read_variable_cb(
{
config_memory_parse_data *parse_data = (config_memory_parse_data *) payload;
git_str buf = GIT_STR_INIT;
- git_config_entry *entry;
+ git_config_list_entry *entry;
const char *c;
int result;
@@ -62,35 +75,46 @@ static int read_variable_cb(
if (git_str_oom(&buf))
return -1;
- entry = git__calloc(1, sizeof(git_config_entry));
+ entry = git__calloc(1, sizeof(git_config_list_entry));
GIT_ERROR_CHECK_ALLOC(entry);
- entry->name = git_str_detach(&buf);
- entry->value = var_value ? git__strdup(var_value) : NULL;
- entry->level = parse_data->level;
- entry->include_depth = 0;
-
- if ((result = git_config_entries_append(parse_data->entries, entry)) < 0)
+ entry->base.name = git_str_detach(&buf);
+ entry->base.value = var_value ? git__strdup(var_value) : NULL;
+ entry->base.level = parse_data->level;
+ entry->base.include_depth = 0;
+ entry->base.backend_type = parse_data->backend_type;
+ entry->base.origin_path = parse_data->origin_path;
+ entry->base.free = git_config_list_entry_free;
+ entry->config_list = parse_data->config_list;
+
+ if ((result = git_config_list_append(parse_data->config_list, entry)) < 0)
return result;
return result;
}
-static int config_memory_open(git_config_backend *backend, git_config_level_t level, const git_repository *repo)
+static int parse_config(
+ config_memory_backend *memory_backend,
+ git_config_level_t level)
{
- config_memory_backend *memory_backend = (config_memory_backend *) backend;
git_config_parser parser = GIT_PARSE_CTX_INIT;
config_memory_parse_data parse_data;
int error;
- GIT_UNUSED(repo);
-
- if ((error = git_config_parser_init(&parser, "in-memory", memory_backend->cfg.ptr,
- memory_backend->cfg.size)) < 0)
+ if ((error = git_config_parser_init(&parser, "in-memory",
+ memory_backend->cfg.ptr, memory_backend->cfg.size)) < 0)
goto out;
- parse_data.entries = memory_backend->entries;
+
+ parse_data.backend_type = git_config_list_add_string(
+ memory_backend->config_list, memory_backend->backend_type);
+ parse_data.origin_path = memory_backend->origin_path ?
+ git_config_list_add_string(memory_backend->config_list,
+ memory_backend->origin_path) :
+ NULL;
+ parse_data.config_list = memory_backend->config_list;
parse_data.level = level;
- if ((error = git_config_parse(&parser, NULL, read_variable_cb, NULL, NULL, &parse_data)) < 0)
+ if ((error = git_config_parse(&parser, NULL, read_variable_cb,
+ NULL, NULL, &parse_data)) < 0)
goto out;
out:
@@ -98,10 +122,85 @@ static int config_memory_open(git_config_backend *backend, git_config_level_t le
return error;
}
+static int parse_values(
+ config_memory_backend *memory_backend,
+ git_config_level_t level)
+{
+ git_config_list_entry *entry;
+ const char *eql, *backend_type, *origin_path;
+ size_t name_len, i;
+
+ backend_type = git_config_list_add_string(
+ memory_backend->config_list, memory_backend->backend_type);
+ GIT_ERROR_CHECK_ALLOC(backend_type);
+
+ origin_path = memory_backend->origin_path ?
+ git_config_list_add_string(memory_backend->config_list,
+ memory_backend->origin_path) :
+ NULL;
+
+ for (i = 0; i < memory_backend->values_len; i++) {
+ eql = strchr(memory_backend->values[i], '=');
+ name_len = eql - memory_backend->values[i];
+
+ if (name_len == 0) {
+ git_error_set(GIT_ERROR_CONFIG, "empty config key");
+ return -1;
+ }
+
+ entry = git__calloc(1, sizeof(git_config_list_entry));
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ entry->base.name = git__strndup(memory_backend->values[i], name_len);
+ GIT_ERROR_CHECK_ALLOC(entry->base.name);
+
+ if (eql) {
+ entry->base.value = git__strdup(eql + 1);
+ GIT_ERROR_CHECK_ALLOC(entry->base.value);
+ }
+
+ entry->base.level = level;
+ entry->base.include_depth = 0;
+ entry->base.backend_type = backend_type;
+ entry->base.origin_path = origin_path;
+ entry->base.free = git_config_list_entry_free;
+ entry->config_list = memory_backend->config_list;
+
+ if (git_config_list_append(memory_backend->config_list, entry) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int config_memory_open(git_config_backend *backend, git_config_level_t level, const git_repository *repo)
+{
+ config_memory_backend *memory_backend = (config_memory_backend *) backend;
+
+ GIT_UNUSED(repo);
+
+ if (memory_backend->cfg.size > 0 &&
+ parse_config(memory_backend, level) < 0)
+ return -1;
+
+ if (memory_backend->values_len > 0 &&
+ parse_values(memory_backend, level) < 0)
+ return -1;
+
+ return 0;
+}
+
static int config_memory_get(git_config_backend *backend, const char *key, git_config_entry **out)
{
config_memory_backend *memory_backend = (config_memory_backend *) backend;
- return git_config_entries_get(out, memory_backend->entries, key);
+ git_config_list_entry *entry;
+ int error;
+
+ if ((error = git_config_list_get(&entry, memory_backend->config_list, key)) != 0)
+ return error;
+
+ *out = &entry->base;
+ return 0;
}
static int config_memory_iterator(
@@ -109,18 +208,18 @@ static int config_memory_iterator(
git_config_backend *backend)
{
config_memory_backend *memory_backend = (config_memory_backend *) backend;
- git_config_entries *entries;
+ git_config_list *config_list;
int error;
- if ((error = git_config_entries_dup(&entries, memory_backend->entries)) < 0)
+ if ((error = git_config_list_dup(&config_list, memory_backend->config_list)) < 0)
goto out;
- if ((error = git_config_entries_iterator_new(iter, entries)) < 0)
+ if ((error = git_config_list_iterator_new(iter, config_list)) < 0)
goto out;
out:
- /* Let iterator delete duplicated entries when it's done */
- git_config_entries_free(entries);
+ /* Let iterator delete duplicated config_list when it's done */
+ git_config_list_free(config_list);
return error;
}
@@ -177,28 +276,24 @@ static void config_memory_free(git_config_backend *_backend)
if (backend == NULL)
return;
- git_config_entries_free(backend->entries);
+ git__free(backend->origin_path);
+ git__free(backend->backend_type);
+ git_config_list_free(backend->config_list);
+ git_strlist_free(backend->values, backend->values_len);
git_str_dispose(&backend->cfg);
git__free(backend);
}
-int git_config_backend_from_string(git_config_backend **out, const char *cfg, size_t len)
+static config_memory_backend *config_backend_new(
+ git_config_backend_memory_options *opts)
{
config_memory_backend *backend;
- backend = git__calloc(1, sizeof(config_memory_backend));
- GIT_ERROR_CHECK_ALLOC(backend);
+ if ((backend = git__calloc(1, sizeof(config_memory_backend))) == NULL)
+ return NULL;
- if (git_config_entries_new(&backend->entries) < 0) {
- git__free(backend);
- return -1;
- }
-
- if (git_str_set(&backend->cfg, cfg, len) < 0) {
- git_config_entries_free(backend->entries);
- git__free(backend);
- return -1;
- }
+ if (git_config_list_new(&backend->config_list) < 0)
+ goto on_error;
backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
backend->parent.readonly = 1;
@@ -214,7 +309,66 @@ int git_config_backend_from_string(git_config_backend **out, const char *cfg, si
backend->parent.snapshot = git_config_backend_snapshot;
backend->parent.free = config_memory_free;
+ backend->backend_type = git__strdup(opts && opts->backend_type ?
+ opts->backend_type : "in-memory");
+
+ if (backend->backend_type == NULL)
+ goto on_error;
+
+ if (opts && opts->origin_path &&
+ (backend->origin_path = git__strdup(opts->origin_path)) == NULL)
+ goto on_error;
+
+ return backend;
+
+on_error:
+ git_config_list_free(backend->config_list);
+ git__free(backend->origin_path);
+ git__free(backend->backend_type);
+ git__free(backend);
+ return NULL;
+}
+
+int git_config_backend_from_string(
+ git_config_backend **out,
+ const char *cfg,
+ size_t len,
+ git_config_backend_memory_options *opts)
+{
+ config_memory_backend *backend;
+
+ if ((backend = config_backend_new(opts)) == NULL)
+ return -1;
+
+ if (git_str_set(&backend->cfg, cfg, len) < 0) {
+ git_config_list_free(backend->config_list);
+ git__free(backend);
+ return -1;
+ }
+
*out = (git_config_backend *)backend;
+ return 0;
+}
+
+int git_config_backend_from_values(
+ git_config_backend **out,
+ const char **values,
+ size_t len,
+ git_config_backend_memory_options *opts)
+{
+ config_memory_backend *backend;
+ if ((backend = config_backend_new(opts)) == NULL)
+ return -1;
+
+ if (git_strlist_copy(&backend->values, values, len) < 0) {
+ git_config_list_free(backend->config_list);
+ git__free(backend);
+ return -1;
+ }
+
+ backend->values_len = len;
+
+ *out = (git_config_backend *)backend;
return 0;
}
diff --git a/src/libgit2/config_parse.c b/src/libgit2/config_parse.c
index 06931368e7b..7f933db4885 100644
--- a/src/libgit2/config_parse.c
+++ b/src/libgit2/config_parse.c
@@ -25,9 +25,9 @@ static void set_parse_error(git_config_parser *reader, int col, const char *erro
}
-GIT_INLINE(int) config_keychar(int c)
+GIT_INLINE(int) config_keychar(char c)
{
- return isalnum(c) || c == '-';
+ return git__isalnum(c) || c == '-';
}
static int strip_comments(char *line, int in_quotes)
@@ -158,9 +158,10 @@ static int parse_subsection_header(git_config_parser *reader, const char *line,
static int parse_section_header(git_config_parser *reader, char **section_out)
{
char *name, *name_end;
- int name_length, c, pos;
+ int name_length, pos;
int result;
char *line;
+ char c;
size_t line_len;
git_parse_advance_ws(&reader->ctx);
@@ -279,8 +280,7 @@ static int skip_bom(git_parse_ctx *parser)
*/
/* '\"' -> '"' etc */
-static int unescape_line(
- char **out, bool *is_multi, const char *ptr, int quote_count)
+static int unescape_line(char **out, bool *is_multi, const char *ptr, int *quote_count)
{
char *str, *fixed, *esc;
size_t ptr_len = strlen(ptr), alloc_len;
@@ -296,7 +296,8 @@ static int unescape_line(
while (*ptr != '\0') {
if (*ptr == '"') {
- quote_count++;
+ if (quote_count)
+ (*quote_count)++;
} else if (*ptr != '\\') {
*fixed++ = *ptr;
} else {
@@ -358,7 +359,7 @@ static int parse_multiline_variable(git_config_parser *reader, git_str *value, i
goto next;
if ((error = unescape_line(&proc_line, &multiline,
- line, in_quotes)) < 0)
+ line, &in_quotes)) < 0)
goto out;
/* Add this line to the multiline var */
@@ -382,7 +383,7 @@ static int parse_multiline_variable(git_config_parser *reader, git_str *value, i
GIT_INLINE(bool) is_namechar(char c)
{
- return isalnum(c) || c == '-';
+ return git__isalnum(c) || c == '-';
}
static int parse_name(
@@ -445,7 +446,7 @@ static int parse_variable(git_config_parser *reader, char **var_name, char **var
while (git__isspace(value_start[0]))
value_start++;
- if ((error = unescape_line(&value, &multiline, value_start, 0)) < 0)
+ if ((error = unescape_line(&value, &multiline, value_start, NULL)) < 0)
goto out;
if (multiline) {
diff --git a/src/libgit2/config_snapshot.c b/src/libgit2/config_snapshot.c
index e295d2f7f28..d8b8733a9fb 100644
--- a/src/libgit2/config_snapshot.c
+++ b/src/libgit2/config_snapshot.c
@@ -8,12 +8,12 @@
#include "config_backend.h"
#include "config.h"
-#include "config_entries.h"
+#include "config_list.h"
typedef struct {
git_config_backend parent;
git_mutex values_mutex;
- git_config_entries *entries;
+ git_config_list *config_list;
git_config_backend *source;
} config_snapshot_backend;
@@ -28,31 +28,24 @@ static int config_snapshot_iterator(
struct git_config_backend *backend)
{
config_snapshot_backend *b = GIT_CONTAINER_OF(backend, config_snapshot_backend, parent);
- git_config_entries *entries = NULL;
+ git_config_list *config_list = NULL;
int error;
- if ((error = git_config_entries_dup(&entries, b->entries)) < 0 ||
- (error = git_config_entries_iterator_new(iter, entries)) < 0)
+ if ((error = git_config_list_dup(&config_list, b->config_list)) < 0 ||
+ (error = git_config_list_iterator_new(iter, config_list)) < 0)
goto out;
out:
- /* Let iterator delete duplicated entries when it's done */
- git_config_entries_free(entries);
+ /* Let iterator delete duplicated config_list when it's done */
+ git_config_list_free(config_list);
return error;
}
-/* release the map containing the entry as an equivalent to freeing it */
-static void config_snapshot_entry_free(git_config_entry *entry)
-{
- git_config_entries *entries = (git_config_entries *) entry->payload;
- git_config_entries_free(entries);
-}
-
static int config_snapshot_get(git_config_backend *cfg, const char *key, git_config_entry **out)
{
config_snapshot_backend *b = GIT_CONTAINER_OF(cfg, config_snapshot_backend, parent);
- git_config_entries *entries = NULL;
- git_config_entry *entry;
+ git_config_list *config_list = NULL;
+ git_config_list_entry *entry;
int error = 0;
if (git_mutex_lock(&b->values_mutex) < 0) {
@@ -60,19 +53,16 @@ static int config_snapshot_get(git_config_backend *cfg, const char *key, git_con
return -1;
}
- entries = b->entries;
- git_config_entries_incref(entries);
+ config_list = b->config_list;
+ git_config_list_incref(config_list);
git_mutex_unlock(&b->values_mutex);
- if ((error = (git_config_entries_get(&entry, entries, key))) < 0) {
- git_config_entries_free(entries);
+ if ((error = (git_config_list_get(&entry, config_list, key))) < 0) {
+ git_config_list_free(config_list);
return error;
}
- entry->free = config_snapshot_entry_free;
- entry->payload = entries;
- *out = entry;
-
+ *out = &entry->base;
return 0;
}
@@ -135,7 +125,7 @@ static void config_snapshot_free(git_config_backend *_backend)
if (backend == NULL)
return;
- git_config_entries_free(backend->entries);
+ git_config_list_free(backend->config_list);
git_mutex_free(&backend->values_mutex);
git__free(backend);
}
@@ -143,7 +133,7 @@ static void config_snapshot_free(git_config_backend *_backend)
static int config_snapshot_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
{
config_snapshot_backend *b = GIT_CONTAINER_OF(cfg, config_snapshot_backend, parent);
- git_config_entries *entries = NULL;
+ git_config_list *config_list = NULL;
git_config_iterator *it = NULL;
git_config_entry *entry;
int error;
@@ -152,12 +142,12 @@ static int config_snapshot_open(git_config_backend *cfg, git_config_level_t leve
GIT_UNUSED(level);
GIT_UNUSED(repo);
- if ((error = git_config_entries_new(&entries)) < 0 ||
+ if ((error = git_config_list_new(&config_list)) < 0 ||
(error = b->source->iterator(&it, b->source)) < 0)
goto out;
while ((error = git_config_next(&entry, it)) == 0)
- if ((error = git_config_entries_dup_entry(entries, entry)) < 0)
+ if ((error = git_config_list_dup_entry(config_list, entry)) < 0)
goto out;
if (error < 0) {
@@ -166,12 +156,12 @@ static int config_snapshot_open(git_config_backend *cfg, git_config_level_t leve
error = 0;
}
- b->entries = entries;
+ b->config_list = config_list;
out:
git_config_iterator_free(it);
if (error)
- git_config_entries_free(entries);
+ git_config_list_free(config_list);
return error;
}
diff --git a/src/libgit2/describe.c b/src/libgit2/describe.c
index 20a171aa58d..04453472330 100644
--- a/src/libgit2/describe.c
+++ b/src/libgit2/describe.c
@@ -8,7 +8,6 @@
#include "common.h"
#include "git2/describe.h"
-#include "git2/strarray.h"
#include "git2/diff.h"
#include "git2/status.h"
@@ -19,6 +18,7 @@
#include "refs.h"
#include "repository.h"
#include "revwalk.h"
+#include "strarray.h"
#include "tag.h"
#include "vector.h"
#include "wildmatch.h"
@@ -363,12 +363,15 @@ static int find_unique_abbrev_size(
size_t size = abbreviated_size;
git_odb *odb;
git_oid dummy;
+ size_t hexsize;
int error;
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
return error;
- while (size < GIT_OID_SHA1_HEXSIZE) {
+ hexsize = git_oid_hexsize(repo->oid_type);
+
+ while (size < hexsize) {
if ((error = git_odb_exists_prefix(&dummy, odb, oid_in, size)) == 0) {
*out = (int) size;
return 0;
@@ -383,7 +386,7 @@ static int find_unique_abbrev_size(
}
/* If we didn't find any shorter prefix, we have to do the whole thing */
- *out = GIT_OID_SHA1_HEXSIZE;
+ *out = (int)hexsize;
return 0;
}
@@ -397,7 +400,7 @@ static int show_suffix(
{
int error, size = 0;
- char hex_oid[GIT_OID_SHA1_HEXSIZE];
+ char hex_oid[GIT_OID_MAX_HEXSIZE];
if ((error = find_unique_abbrev_size(&size, repo, id, abbrev_size)) < 0)
return error;
@@ -414,7 +417,7 @@ static int show_suffix(
#define MAX_CANDIDATES_TAGS FLAG_BITS - 1
static int describe_not_found(const git_oid *oid, const char *message_format) {
- char oid_str[GIT_OID_SHA1_HEXSIZE + 1];
+ char oid_str[GIT_OID_MAX_HEXSIZE + 1];
git_oid_tostr(oid_str, sizeof(oid_str), oid);
git_error_set(GIT_ERROR_DESCRIBE, message_format, oid_str);
@@ -525,7 +528,7 @@ static int describe(
if (annotated_cnt && (git_pqueue_size(&list) == 0)) {
/*
if (debug) {
- char oid_str[GIT_OID_SHA1_HEXSIZE + 1];
+ char oid_str[GIT_OID_MAX_HEXSIZE + 1];
git_oid_tostr(oid_str, sizeof(oid_str), &c->oid);
fprintf(stderr, "finished search at %s\n", oid_str);
@@ -592,7 +595,7 @@ static int describe(
"head", "lightweight", "annotated",
};
- char oid_str[GIT_OID_SHA1_HEXSIZE + 1];
+ char oid_str[GIT_OID_MAX_HEXSIZE + 1];
if (debug) {
for (cur_match = 0; cur_match < match_cnt; cur_match++) {
@@ -816,7 +819,7 @@ static int git_describe__format(
/* If we didn't find *any* tags, we fall back to the commit's id */
if (result->fallback_to_id) {
- char hex_oid[GIT_OID_SHA1_HEXSIZE + 1] = {0};
+ char hex_oid[GIT_OID_MAX_HEXSIZE + 1] = {0};
int size = 0;
if ((error = find_unique_abbrev_size(
diff --git a/src/libgit2/diff.c b/src/libgit2/diff.c
index 20a18c4b9b0..db12ccd6809 100644
--- a/src/libgit2/diff.c
+++ b/src/libgit2/diff.c
@@ -19,8 +19,10 @@
#include "git2/email.h"
struct patch_id_args {
+ git_diff *diff;
git_hash_ctx ctx;
git_oid result;
+ git_oid_t oid_type;
int first_file;
};
@@ -280,17 +282,19 @@ int git_diff_find_options_init(
return 0;
}
-static int flush_hunk(git_oid *result, git_hash_ctx *ctx)
+static int flush_hunk(git_oid *result, struct patch_id_args *args)
{
+ git_hash_ctx *ctx = &args->ctx;
git_oid hash;
unsigned short carry = 0;
- int error, i;
+ size_t i;
+ int error;
if ((error = git_hash_final(hash.id, ctx)) < 0 ||
(error = git_hash_init(ctx)) < 0)
return error;
- for (i = 0; i < GIT_OID_SHA1_SIZE; i++) {
+ for (i = 0; i < git_oid_size(args->oid_type); i++) {
carry += result->id[i] + hash.id[i];
result->id[i] = (unsigned char)carry;
carry >>= 8;
@@ -338,7 +342,7 @@ static int diff_patchid_print_callback_to_buf(
if (line->origin == GIT_DIFF_LINE_FILE_HDR &&
!args->first_file &&
- (error = flush_hunk(&args->result, &args->ctx) < 0))
+ (error = flush_hunk(&args->result, args) < 0))
goto out;
if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0)
@@ -362,14 +366,19 @@ int git_diff_patchid_options_init(git_diff_patchid_options *opts, unsigned int v
int git_diff_patchid(git_oid *out, git_diff *diff, git_diff_patchid_options *opts)
{
struct patch_id_args args;
+ git_hash_algorithm_t algorithm;
int error;
GIT_ERROR_CHECK_VERSION(
opts, GIT_DIFF_PATCHID_OPTIONS_VERSION, "git_diff_patchid_options");
+ algorithm = git_oid_algorithm(diff->opts.oid_type);
+
memset(&args, 0, sizeof(args));
+ args.diff = diff;
args.first_file = 1;
- if ((error = git_hash_ctx_init(&args.ctx, GIT_HASH_ALGORITHM_SHA1)) < 0)
+ args.oid_type = diff->opts.oid_type;
+ if ((error = git_hash_ctx_init(&args.ctx, algorithm)) < 0)
goto out;
if ((error = git_diff_print(diff,
@@ -378,11 +387,11 @@ int git_diff_patchid(git_oid *out, git_diff *diff, git_diff_patchid_options *opt
&args)) < 0)
goto out;
- if ((error = (flush_hunk(&args.result, &args.ctx))) < 0)
+ if ((error = (flush_hunk(&args.result, &args))) < 0)
goto out;
#ifdef GIT_EXPERIMENTAL_SHA256
- args.result.type = GIT_OID_SHA1;
+ args.result.type = diff->opts.oid_type;
#endif
git_oid_cpy(out, &args.result);
diff --git a/src/libgit2/diff.h b/src/libgit2/diff.h
index 2cc35e65b7c..f21b2764505 100644
--- a/src/libgit2/diff.h
+++ b/src/libgit2/diff.h
@@ -30,15 +30,15 @@ typedef enum {
} git_diff_origin_t;
struct git_diff {
- git_refcount rc;
+ git_refcount rc;
git_repository *repo;
- git_attr_session attrsession;
+ git_attr_session attrsession;
git_diff_origin_t type;
- git_diff_options opts;
- git_vector deltas; /* vector of git_diff_delta */
+ git_diff_options opts;
+ git_vector deltas; /* vector of git_diff_delta */
git_pool pool;
- git_iterator_t old_src;
- git_iterator_t new_src;
+ git_iterator_t old_src;
+ git_iterator_t new_src;
git_diff_perfdata perf;
int (*strcomp)(const char *, const char *);
diff --git a/src/libgit2/diff_file.c b/src/libgit2/diff_file.c
index 5f433665852..a792834caca 100644
--- a/src/libgit2/diff_file.c
+++ b/src/libgit2/diff_file.c
@@ -112,7 +112,7 @@ int git_diff_file_content__init_from_diff(
case GIT_DELTA_DELETED:
has_data = use_old; break;
case GIT_DELTA_UNTRACKED:
- has_data = !use_old &&
+ has_data = (use_old == (diff->opts.flags & GIT_DIFF_REVERSE)) &&
(diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0;
break;
case GIT_DELTA_UNREADABLE:
@@ -144,7 +144,7 @@ int git_diff_file_content__init_from_src(
if (!src->blob && !src->buf) {
fc->flags |= GIT_DIFF_FLAG__NO_DATA;
- git_oid_clear(&fc->file->id, GIT_OID_SHA1);
+ git_oid_clear(&fc->file->id, opts->oid_type);
} else {
fc->flags |= GIT_DIFF_FLAG__LOADED;
fc->file->flags |= GIT_DIFF_FLAG_VALID_ID;
@@ -154,7 +154,7 @@ int git_diff_file_content__init_from_src(
git_blob_dup((git_blob **)&fc->blob, (git_blob *) src->blob);
fc->file->size = git_blob_rawsize(src->blob);
git_oid_cpy(&fc->file->id, git_blob_id(src->blob));
- fc->file->id_abbrev = GIT_OID_SHA1_HEXSIZE;
+ fc->file->id_abbrev = (uint16_t)git_oid_hexsize(repo->oid_type);
fc->map.len = (size_t)fc->file->size;
fc->map.data = (char *)git_blob_rawcontent(src->blob);
@@ -162,10 +162,10 @@ int git_diff_file_content__init_from_src(
fc->flags |= GIT_DIFF_FLAG__FREE_BLOB;
} else {
int error;
- if ((error = git_odb__hash(&fc->file->id, src->buf, src->buflen, GIT_OBJECT_BLOB, GIT_OID_SHA1)) < 0)
+ if ((error = git_odb__hash(&fc->file->id, src->buf, src->buflen, GIT_OBJECT_BLOB, opts->oid_type)) < 0)
return error;
fc->file->size = src->buflen;
- fc->file->id_abbrev = GIT_OID_SHA1_HEXSIZE;
+ fc->file->id_abbrev = (uint16_t)git_oid_hexsize(opts->oid_type);
fc->map.len = src->buflen;
fc->map.data = (char *)src->buf;
@@ -178,7 +178,7 @@ int git_diff_file_content__init_from_src(
static int diff_file_content_commit_to_str(
git_diff_file_content *fc, bool check_status)
{
- char oid[GIT_OID_SHA1_HEXSIZE+1];
+ char oid[GIT_OID_MAX_HEXSIZE+1];
git_str content = GIT_STR_INIT;
const char *status = "";
@@ -348,6 +348,13 @@ static int diff_file_content_load_workdir_file(
goto cleanup;
}
+ /* if file is empty, don't attempt to mmap or readbuffer */
+ if (fc->file->size == 0) {
+ fc->map.len = 0;
+ fc->map.data = git_str__initstr;
+ goto cleanup;
+ }
+
if ((diff_opts->flags & GIT_DIFF_SHOW_BINARY) == 0 &&
diff_file_content_binary_by_size(fc))
goto cleanup;
@@ -413,7 +420,7 @@ static int diff_file_content_load_workdir(
if (!error && (fc->file->flags & GIT_DIFF_FLAG_VALID_ID) == 0) {
error = git_odb__hash(
&fc->file->id, fc->map.data, fc->map.len,
- GIT_OBJECT_BLOB, GIT_OID_SHA1);
+ GIT_OBJECT_BLOB, diff_opts->oid_type);
fc->file->flags |= GIT_DIFF_FLAG_VALID_ID;
}
diff --git a/src/libgit2/diff_generate.c b/src/libgit2/diff_generate.c
index c0e146b3b85..9e2b8995604 100644
--- a/src/libgit2/diff_generate.c
+++ b/src/libgit2/diff_generate.c
@@ -61,8 +61,8 @@ static git_diff_delta *diff_delta__alloc(
}
delta->status = status;
- git_oid_clear(&delta->old_file.id, GIT_OID_SHA1);
- git_oid_clear(&delta->new_file.id, GIT_OID_SHA1);
+ git_oid_clear(&delta->old_file.id, diff->base.opts.oid_type);
+ git_oid_clear(&delta->new_file.id, diff->base.opts.oid_type);
return delta;
}
@@ -149,10 +149,13 @@ static int diff_delta__from_one(
const git_index_entry *entry = nitem;
bool has_old = false;
git_diff_delta *delta;
+ git_oid_t oid_type;
const char *matched_pathspec;
GIT_ASSERT_ARG((oitem != NULL) ^ (nitem != NULL));
+ oid_type = diff->base.opts.oid_type;
+
if (oitem) {
entry = oitem;
has_old = true;
@@ -186,21 +189,24 @@ static int diff_delta__from_one(
GIT_ASSERT(status != GIT_DELTA_MODIFIED);
delta->nfiles = 1;
+ git_oid_clear(&delta->old_file.id, diff->base.opts.oid_type);
+ git_oid_clear(&delta->new_file.id, diff->base.opts.oid_type);
+
if (has_old) {
delta->old_file.mode = entry->mode;
delta->old_file.size = entry->file_size;
delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
git_oid_cpy(&delta->old_file.id, &entry->id);
- git_oid_clear(&delta->new_file.id, GIT_OID_SHA1);
- delta->old_file.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+ git_oid_clear(&delta->new_file.id, oid_type);
+ delta->old_file.id_abbrev = (uint16_t)git_oid_hexsize(oid_type);
delta->old_file.skip_worktree = (0 != (entry->flags_extended & GIT_INDEX_ENTRY_SKIP_WORKTREE));
} else /* ADDED, IGNORED, UNTRACKED */ {
delta->new_file.mode = entry->mode;
delta->new_file.size = entry->file_size;
delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS;
- git_oid_clear(&delta->old_file.id, GIT_OID_SHA1);
+ git_oid_clear(&delta->old_file.id, oid_type);
git_oid_cpy(&delta->new_file.id, &entry->id);
- delta->new_file.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+ delta->new_file.id_abbrev = (uint16_t)git_oid_hexsize(oid_type);
delta->new_file.skip_worktree = (0 != (entry->flags_extended & GIT_INDEX_ENTRY_SKIP_WORKTREE));
}
@@ -227,6 +233,9 @@ static int diff_delta__from_two(
const git_oid *old_id = &old_entry->id;
git_diff_delta *delta;
const char *canonical_path = old_entry->path;
+ git_oid_t oid_type;
+
+ oid_type = diff->base.opts.oid_type;
if (status == GIT_DELTA_UNMODIFIED &&
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
@@ -259,14 +268,14 @@ static int diff_delta__from_two(
delta->old_file.size = old_entry->file_size;
delta->old_file.mode = old_mode;
git_oid_cpy(&delta->old_file.id, old_id);
- delta->old_file.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+ delta->old_file.id_abbrev = (uint16_t)git_oid_hexsize(oid_type);
delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID |
GIT_DIFF_FLAG_EXISTS;
}
if (!git_index_entry_is_conflict(new_entry)) {
git_oid_cpy(&delta->new_file.id, new_id);
- delta->new_file.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+ delta->new_file.id_abbrev = (uint16_t)git_oid_hexsize(oid_type);
delta->new_file.size = new_entry->file_size;
delta->new_file.mode = new_mode;
delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
@@ -495,6 +504,14 @@ static int diff_generated_apply_options(
return -1;
}
+ if (!diff->base.opts.oid_type) {
+ diff->base.opts.oid_type = repo->oid_type;
+ } else if (diff->base.opts.oid_type != repo->oid_type) {
+ git_error_set(GIT_ERROR_INVALID,
+ "specified object ID type does not match repository object ID type");
+ return -1;
+ }
+
/* flag INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES))
diff->base.opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
@@ -608,7 +625,7 @@ int git_diff__oid_for_file(
entry.mode = mode;
entry.file_size = (uint32_t)size;
entry.path = (char *)path;
- git_oid_clear(&entry.id, GIT_OID_SHA1);
+ git_oid_clear(&entry.id, diff->opts.oid_type);
return git_diff__oid_for_entry(out, diff, &entry, mode, NULL);
}
@@ -629,7 +646,7 @@ int git_diff__oid_for_entry(
GIT_ASSERT(d->type == GIT_DIFF_TYPE_GENERATED);
diff = (git_diff_generated *)d;
- git_oid_clear(out, GIT_OID_SHA1);
+ git_oid_clear(out, diff->base.opts.oid_type);
if (git_repository_workdir_path(&full_path, diff->base.repo, entry.path) < 0)
return -1;
@@ -665,7 +682,8 @@ int git_diff__oid_for_entry(
git_error_clear();
}
} else if (S_ISLNK(mode)) {
- error = git_odb__hashlink(out, full_path.ptr, GIT_OID_SHA1);
+ error = git_odb__hashlink(out, full_path.ptr,
+ diff->base.opts.oid_type);
diff->base.perf.oid_calculations++;
} else if (!git__is_sizet(entry.file_size)) {
git_error_set(GIT_ERROR_NOMEMORY, "file size overflow (for 32-bits) on '%s'",
@@ -681,7 +699,8 @@ int git_diff__oid_for_entry(
else {
error = git_odb__hashfd_filtered(
out, fd, (size_t)entry.file_size,
- GIT_OBJECT_BLOB, GIT_OID_SHA1, fl);
+ GIT_OBJECT_BLOB, diff->base.opts.oid_type,
+ fl);
p_close(fd);
diff->base.perf.oid_calculations++;
}
@@ -790,7 +809,7 @@ static int maybe_modified(
git_diff_generated *diff,
diff_in_progress *info)
{
- git_oid noid = GIT_OID_SHA1_ZERO;
+ git_oid noid;
git_delta_t status = GIT_DELTA_MODIFIED;
const git_index_entry *oitem = info->oitem;
const git_index_entry *nitem = info->nitem;
@@ -801,6 +820,8 @@ static int maybe_modified(
const char *matched_pathspec;
int error = 0;
+ git_oid_clear(&noid, diff->base.opts.oid_type);
+
if (!diff_pathspec_match(&matched_pathspec, diff, oitem))
return 0;
@@ -1724,11 +1745,11 @@ int git_diff__commit(
*out = NULL;
if ((parents = git_commit_parentcount(commit)) > 1) {
- char commit_oidstr[GIT_OID_SHA1_HEXSIZE + 1];
+ char commit_oidstr[GIT_OID_MAX_HEXSIZE + 1];
error = -1;
git_error_set(GIT_ERROR_INVALID, "commit %s is a merge commit",
- git_oid_tostr(commit_oidstr, GIT_OID_SHA1_HEXSIZE + 1, git_commit_id(commit)));
+ git_oid_tostr(commit_oidstr, GIT_OID_MAX_HEXSIZE + 1, git_commit_id(commit)));
goto on_error;
}
diff --git a/src/libgit2/diff_parse.c b/src/libgit2/diff_parse.c
index 75e41a5443b..04603969e40 100644
--- a/src/libgit2/diff_parse.c
+++ b/src/libgit2/diff_parse.c
@@ -29,7 +29,7 @@ static void diff_parsed_free(git_diff *d)
git__free(diff);
}
-static git_diff_parsed *diff_parsed_alloc(void)
+static git_diff_parsed *diff_parsed_alloc(git_oid_t oid_type)
{
git_diff_parsed *diff;
@@ -51,6 +51,7 @@ static git_diff_parsed *diff_parsed_alloc(void)
}
diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE;
+ diff->base.opts.oid_type = oid_type;
if (git_pool_init(&diff->base.pool, 1) < 0 ||
git_vector_init(&diff->patches, 0, NULL) < 0 ||
@@ -67,19 +68,34 @@ static git_diff_parsed *diff_parsed_alloc(void)
int git_diff_from_buffer(
git_diff **out,
const char *content,
- size_t content_len)
+ size_t content_len
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_diff_parse_options *opts
+#endif
+ )
{
git_diff_parsed *diff;
git_patch *patch;
git_patch_parse_ctx *ctx = NULL;
+ git_patch_options patch_opts = GIT_PATCH_OPTIONS_INIT;
+ git_oid_t oid_type;
int error = 0;
*out = NULL;
- diff = diff_parsed_alloc();
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid_type = (opts && opts->oid_type) ? opts->oid_type :
+ GIT_OID_DEFAULT;
+#else
+ oid_type = GIT_OID_DEFAULT;
+#endif
+
+ patch_opts.oid_type = oid_type;
+
+ diff = diff_parsed_alloc(oid_type);
GIT_ERROR_CHECK_ALLOC(diff);
- ctx = git_patch_parse_ctx_init(content, content_len, NULL);
+ ctx = git_patch_parse_ctx_init(content, content_len, &patch_opts);
GIT_ERROR_CHECK_ALLOC(ctx);
while (ctx->parse_ctx.remain_len) {
diff --git a/src/libgit2/diff_print.c b/src/libgit2/diff_print.c
index 3077e11e157..daeefca50ca 100644
--- a/src/libgit2/diff_print.c
+++ b/src/libgit2/diff_print.c
@@ -29,6 +29,8 @@ typedef struct {
const char *new_prefix;
uint32_t flags;
int id_strlen;
+ unsigned int sent_file_header;
+ git_oid_t oid_type;
int (*strcomp)(const char *, const char *);
} diff_print_info;
@@ -46,6 +48,8 @@ static int diff_print_info_init__common(
pi->payload = payload;
pi->buf = out;
+ GIT_ASSERT(pi->oid_type);
+
if (!pi->id_strlen) {
if (!repo)
pi->id_strlen = GIT_ABBREV_DEFAULT;
@@ -53,8 +57,9 @@ static int diff_print_info_init__common(
return -1;
}
- if (pi->id_strlen > GIT_OID_SHA1_HEXSIZE)
- pi->id_strlen = GIT_OID_SHA1_HEXSIZE;
+ if (pi->id_strlen > 0 &&
+ (size_t)pi->id_strlen > git_oid_hexsize(pi->oid_type))
+ pi->id_strlen = (int)git_oid_hexsize(pi->oid_type);
memset(&pi->line, 0, sizeof(pi->line));
pi->line.old_lineno = -1;
@@ -78,6 +83,7 @@ static int diff_print_info_init_fromdiff(
if (diff) {
pi->flags = diff->opts.flags;
+ pi->oid_type = diff->opts.oid_type;
pi->id_strlen = diff->opts.id_abbrev;
pi->old_prefix = diff->opts.old_prefix;
pi->new_prefix = diff->opts.new_prefix;
@@ -101,6 +107,7 @@ static int diff_print_info_init_frompatch(
memset(pi, 0, sizeof(diff_print_info));
pi->flags = patch->diff_opts.flags;
+ pi->oid_type = patch->diff_opts.oid_type;
pi->id_strlen = patch->diff_opts.id_abbrev;
pi->old_prefix = patch->diff_opts.old_prefix;
pi->new_prefix = patch->diff_opts.new_prefix;
@@ -212,7 +219,10 @@ static int diff_print_one_raw(
git_str *out = pi->buf;
int id_abbrev;
char code = git_diff_status_char(delta->status);
- char start_oid[GIT_OID_SHA1_HEXSIZE+1], end_oid[GIT_OID_SHA1_HEXSIZE+1];
+ char start_oid[GIT_OID_MAX_HEXSIZE + 1],
+ end_oid[GIT_OID_MAX_HEXSIZE + 1];
+ size_t oid_hexsize;
+ bool id_is_abbrev;
GIT_UNUSED(progress);
@@ -231,12 +241,21 @@ static int diff_print_one_raw(
return -1;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+ GIT_ASSERT(delta->old_file.id.type == delta->new_file.id.type);
+ oid_hexsize = git_oid_hexsize(delta->old_file.id.type);
+#else
+ oid_hexsize = GIT_OID_SHA1_HEXSIZE;
+#endif
+
+ id_is_abbrev = (pi->id_strlen > 0 &&
+ (size_t)pi->id_strlen <= oid_hexsize);
+
git_oid_tostr(start_oid, pi->id_strlen + 1, &delta->old_file.id);
git_oid_tostr(end_oid, pi->id_strlen + 1, &delta->new_file.id);
- git_str_printf(
- out, (pi->id_strlen <= GIT_OID_SHA1_HEXSIZE) ?
- ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c",
+ git_str_printf(out,
+ id_is_abbrev ? ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c",
delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
if (delta->similarity > 0)
@@ -273,7 +292,8 @@ static int diff_print_oid_range(
git_str *out, const git_diff_delta *delta, int id_strlen,
bool print_index)
{
- char start_oid[GIT_OID_SHA1_HEXSIZE+1], end_oid[GIT_OID_SHA1_HEXSIZE+1];
+ char start_oid[GIT_OID_MAX_HEXSIZE + 1],
+ end_oid[GIT_OID_MAX_HEXSIZE + 1];
if (delta->old_file.mode &&
id_strlen > delta->old_file.id_abbrev) {
@@ -560,6 +580,30 @@ static int diff_print_patch_file_binary(
return error;
}
+GIT_INLINE(int) should_force_header(const git_diff_delta *delta)
+{
+ if (delta->old_file.mode != delta->new_file.mode)
+ return 1;
+
+ if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED)
+ return 1;
+
+ return 0;
+}
+
+GIT_INLINE(int) flush_file_header(const git_diff_delta *delta, diff_print_info *pi)
+{
+ if (pi->sent_file_header)
+ return 0;
+
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_str_cstr(pi->buf);
+ pi->line.content_len = git_str_len(pi->buf);
+ pi->sent_file_header = 1;
+
+ return pi->print_cb(delta, NULL, &pi->line, pi->payload);
+}
+
static int diff_print_patch_file(
const git_diff_delta *delta, float progress, void *data)
{
@@ -590,15 +634,22 @@ static int diff_print_patch_file(
(pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
return 0;
+ pi->sent_file_header = 0;
+
if ((error = git_diff_delta__format_file_header(pi->buf, delta, oldpfx, newpfx,
id_strlen, print_index)) < 0)
return error;
- pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
- pi->line.content = git_str_cstr(pi->buf);
- pi->line.content_len = git_str_len(pi->buf);
+ /*
+ * pi->buf now contains the file header data. Go ahead and send it
+ * if there's useful data in there, like similarity. Otherwise, we
+ * should queue it to send when we see the first hunk. This prevents
+ * us from sending a header when all hunks were ignored.
+ */
+ if (should_force_header(delta) && (error = flush_file_header(delta, pi)) < 0)
+ return error;
- return pi->print_cb(delta, NULL, &pi->line, pi->payload);
+ return 0;
}
static int diff_print_patch_binary(
@@ -613,6 +664,9 @@ static int diff_print_patch_binary(
pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
int error;
+ if ((error = flush_file_header(delta, pi)) < 0)
+ return error;
+
git_str_clear(pi->buf);
if ((error = diff_print_patch_file_binary(
@@ -632,10 +686,14 @@ static int diff_print_patch_hunk(
void *data)
{
diff_print_info *pi = data;
+ int error;
if (S_ISDIR(d->new_file.mode))
return 0;
+ if ((error = flush_file_header(d, pi)) < 0)
+ return error;
+
pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
pi->line.content = h->header;
pi->line.content_len = h->header_len;
@@ -650,10 +708,14 @@ static int diff_print_patch_line(
void *data)
{
diff_print_info *pi = data;
+ int error;
if (S_ISDIR(delta->new_file.mode))
return 0;
+ if ((error = flush_file_header(delta, pi)) < 0)
+ return error;
+
return pi->print_cb(delta, hunk, line, pi->payload);
}
diff --git a/src/libgit2/diff_tform.c b/src/libgit2/diff_tform.c
index 8c0c1b7fc5e..9fa3cef8358 100644
--- a/src/libgit2/diff_tform.c
+++ b/src/libgit2/diff_tform.c
@@ -364,7 +364,7 @@ static int insert_delete_side_of_split(
memset(&deleted->new_file, 0, sizeof(deleted->new_file));
deleted->new_file.path = deleted->old_file.path;
deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
- git_oid_clear(&deleted->new_file.id, GIT_OID_SHA1);
+ git_oid_clear(&deleted->new_file.id, diff->opts.oid_type);
return git_vector_insert(onto, deleted);
}
@@ -398,7 +398,7 @@ static int apply_splits_and_deletes(
memset(&delta->old_file, 0, sizeof(delta->old_file));
delta->old_file.path = delta->new_file.path;
delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
- git_oid_clear(&delta->old_file.id, GIT_OID_SHA1);
+ git_oid_clear(&delta->old_file.id, diff->opts.oid_type);
}
/* clean up delta before inserting into new list */
@@ -653,6 +653,23 @@ static int calc_self_similarity(
return 0;
}
+static void handle_non_blob(
+ git_diff *diff,
+ const git_diff_find_options *opts,
+ size_t delta_idx)
+{
+ git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx);
+
+ /* skip things that are blobs */
+ if (GIT_MODE_ISBLOB(delta->old_file.mode))
+ return;
+
+ /* honor "remove unmodified" flag for non-blobs (eg submodules) */
+ if (delta->status == GIT_DELTA_UNMODIFIED &&
+ FLAG_SET(opts, GIT_DIFF_FIND_REMOVE_UNMODIFIED))
+ delta->flags |= GIT_DIFF_FLAG__TO_DELETE;
+}
+
static bool is_rename_target(
git_diff *diff,
const git_diff_find_options *opts,
@@ -810,7 +827,8 @@ int git_diff_find_similar(
git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
size_t num_deltas, num_srcs = 0, num_tgts = 0;
size_t tried_srcs = 0, tried_tgts = 0;
- size_t num_rewrites = 0, num_updates = 0, num_bumped = 0;
+ size_t num_rewrites = 0, num_updates = 0, num_bumped = 0,
+ num_to_delete = 0;
size_t sigcache_size;
void **sigcache = NULL; /* cache of similarity metric file signatures */
diff_find_match *tgt2src = NULL;
@@ -844,6 +862,8 @@ int git_diff_find_similar(
* mark them for splitting if break-rewrites is enabled
*/
git_vector_foreach(&diff->deltas, t, tgt) {
+ handle_non_blob(diff, &opts, t);
+
if (is_rename_source(diff, &opts, t, sigcache))
++num_srcs;
@@ -852,11 +872,14 @@ int git_diff_find_similar(
if ((tgt->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0)
num_rewrites++;
+
+ if ((tgt->flags & GIT_DIFF_FLAG__TO_DELETE) != 0)
+ num_to_delete++;
}
- /* if there are no candidate srcs or tgts, we're done */
+ /* If there are no candidate srcs or tgts, no need to find matches */
if (!num_srcs || !num_tgts)
- goto cleanup;
+ goto split_and_delete;
src2tgt = git__calloc(num_deltas, sizeof(diff_find_match));
GIT_ERROR_CHECK_ALLOC(src2tgt);
@@ -997,7 +1020,7 @@ int git_diff_find_similar(
memset(&src->new_file, 0, sizeof(src->new_file));
src->new_file.path = src->old_file.path;
src->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
- git_oid_clear(&src->new_file.id, GIT_OID_SHA1);
+ git_oid_clear(&src->new_file.id, diff->opts.oid_type);
num_updates++;
@@ -1023,7 +1046,7 @@ int git_diff_find_similar(
memset(&src->old_file, 0, sizeof(src->old_file));
src->old_file.path = src->new_file.path;
src->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
- git_oid_clear(&src->old_file.id, GIT_OID_SHA1);
+ git_oid_clear(&src->old_file.id, diff->opts.oid_type);
src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
num_rewrites--;
@@ -1093,15 +1116,20 @@ int git_diff_find_similar(
}
}
+split_and_delete:
/*
* Actually split and delete entries as needed
*/
- if (num_rewrites > 0 || num_updates > 0)
+ if (num_rewrites > 0 || num_updates > 0 || num_to_delete > 0) {
+ size_t apply_len = diff->deltas.length -
+ num_rewrites - num_to_delete;
+
error = apply_splits_and_deletes(
- diff, diff->deltas.length - num_rewrites,
+ diff, apply_len,
FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES) &&
!FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY));
+ }
cleanup:
git__free(tgt2src);
diff --git a/src/libgit2/diff_xdiff.h b/src/libgit2/diff_xdiff.h
index 9b303e9dc45..327dc7c4ab3 100644
--- a/src/libgit2/diff_xdiff.h
+++ b/src/libgit2/diff_xdiff.h
@@ -10,7 +10,7 @@
#include "common.h"
#include "diff.h"
-#include "xdiff/xdiff.h"
+#include "xdiff.h"
#include "patch_generate.h"
/* xdiff cannot cope with large files. these files should not be passed to
diff --git a/src/libgit2/email.c b/src/libgit2/email.c
index 0a75021c8db..8a10a12b75f 100644
--- a/src/libgit2/email.c
+++ b/src/libgit2/email.c
@@ -130,11 +130,12 @@ static int append_header(
const git_signature *author,
git_email_create_options *opts)
{
- char id[GIT_OID_SHA1_HEXSIZE];
+ char id[GIT_OID_MAX_HEXSIZE + 1];
int error;
- if ((error = git_oid_fmt(id, commit_id)) < 0 ||
- (error = git_str_printf(out, "From %.*s %s\n", GIT_OID_SHA1_HEXSIZE, id, EMAIL_TIMESTAMP)) < 0 ||
+ git_oid_tostr(id, GIT_OID_MAX_HEXSIZE + 1, commit_id);
+
+ if ((error = git_str_printf(out, "From %s %s\n", id, EMAIL_TIMESTAMP)) < 0 ||
(error = git_str_printf(out, "From: %s <%s>\n", author->name, author->email)) < 0 ||
(error = append_date(out, &author->when)) < 0 ||
(error = append_subject(out, patch_idx, patch_count, summary, opts)) < 0)
diff --git a/src/libgit2/errors.c b/src/libgit2/errors.c
deleted file mode 100644
index 3614b9ce5f5..00000000000
--- a/src/libgit2/errors.c
+++ /dev/null
@@ -1,238 +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 "common.h"
-
-#include "threadstate.h"
-#include "posix.h"
-#include "str.h"
-#include "libgit2.h"
-
-/********************************************
- * New error handling
- ********************************************/
-
-static git_error g_git_oom_error = {
- "Out of memory",
- GIT_ERROR_NOMEMORY
-};
-
-static git_error g_git_uninitialized_error = {
- "libgit2 has not been initialized; you must call git_libgit2_init",
- GIT_ERROR_INVALID
-};
-
-static void set_error_from_buffer(int error_class)
-{
- git_error *error = &GIT_THREADSTATE->error_t;
- git_str *buf = &GIT_THREADSTATE->error_buf;
-
- error->message = buf->ptr;
- error->klass = error_class;
-
- GIT_THREADSTATE->last_error = error;
-}
-
-static void set_error(int error_class, char *string)
-{
- git_str *buf = &GIT_THREADSTATE->error_buf;
-
- git_str_clear(buf);
- if (string) {
- git_str_puts(buf, string);
- git__free(string);
- }
-
- set_error_from_buffer(error_class);
-}
-
-void git_error_set_oom(void)
-{
- GIT_THREADSTATE->last_error = &g_git_oom_error;
-}
-
-void git_error_set(int error_class, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- git_error_vset(error_class, fmt, ap);
- va_end(ap);
-}
-
-void git_error_vset(int error_class, const char *fmt, va_list ap)
-{
-#ifdef GIT_WIN32
- DWORD win32_error_code = (error_class == GIT_ERROR_OS) ? GetLastError() : 0;
-#endif
- int error_code = (error_class == GIT_ERROR_OS) ? errno : 0;
- git_str *buf = &GIT_THREADSTATE->error_buf;
-
- git_str_clear(buf);
- if (fmt) {
- git_str_vprintf(buf, fmt, ap);
- if (error_class == GIT_ERROR_OS)
- git_str_PUTS(buf, ": ");
- }
-
- if (error_class == GIT_ERROR_OS) {
-#ifdef GIT_WIN32
- char * win32_error = git_win32_get_error_message(win32_error_code);
- if (win32_error) {
- git_str_puts(buf, win32_error);
- git__free(win32_error);
-
- SetLastError(0);
- }
- else
-#endif
- if (error_code)
- git_str_puts(buf, strerror(error_code));
-
- if (error_code)
- errno = 0;
- }
-
- if (!git_str_oom(buf))
- set_error_from_buffer(error_class);
-}
-
-int git_error_set_str(int error_class, const char *string)
-{
- git_str *buf = &GIT_THREADSTATE->error_buf;
-
- GIT_ASSERT_ARG(string);
-
- git_str_clear(buf);
- git_str_puts(buf, string);
-
- if (git_str_oom(buf))
- return -1;
-
- set_error_from_buffer(error_class);
- return 0;
-}
-
-void git_error_clear(void)
-{
- if (GIT_THREADSTATE->last_error != NULL) {
- set_error(0, NULL);
- GIT_THREADSTATE->last_error = NULL;
- }
-
- errno = 0;
-#ifdef GIT_WIN32
- SetLastError(0);
-#endif
-}
-
-const git_error *git_error_last(void)
-{
- /* If the library is not initialized, return a static error. */
- if (!git_libgit2_init_count())
- return &g_git_uninitialized_error;
-
- return GIT_THREADSTATE->last_error;
-}
-
-int git_error_state_capture(git_error_state *state, int error_code)
-{
- git_error *error = GIT_THREADSTATE->last_error;
- git_str *error_buf = &GIT_THREADSTATE->error_buf;
-
- memset(state, 0, sizeof(git_error_state));
-
- if (!error_code)
- return 0;
-
- state->error_code = error_code;
- state->oom = (error == &g_git_oom_error);
-
- if (error) {
- state->error_msg.klass = error->klass;
-
- if (state->oom)
- state->error_msg.message = g_git_oom_error.message;
- else
- state->error_msg.message = git_str_detach(error_buf);
- }
-
- git_error_clear();
- return error_code;
-}
-
-int git_error_state_restore(git_error_state *state)
-{
- int ret = 0;
-
- git_error_clear();
-
- if (state && state->error_msg.message) {
- if (state->oom)
- git_error_set_oom();
- else
- set_error(state->error_msg.klass, state->error_msg.message);
-
- ret = state->error_code;
- memset(state, 0, sizeof(git_error_state));
- }
-
- return ret;
-}
-
-void git_error_state_free(git_error_state *state)
-{
- if (!state)
- return;
-
- if (!state->oom)
- git__free(state->error_msg.message);
-
- memset(state, 0, sizeof(git_error_state));
-}
-
-int git_error_system_last(void)
-{
-#ifdef GIT_WIN32
- return GetLastError();
-#else
- return errno;
-#endif
-}
-
-void git_error_system_set(int code)
-{
-#ifdef GIT_WIN32
- SetLastError(code);
-#else
- errno = code;
-#endif
-}
-
-/* Deprecated error values and functions */
-
-#ifndef GIT_DEPRECATE_HARD
-const git_error *giterr_last(void)
-{
- return git_error_last();
-}
-
-void giterr_clear(void)
-{
- git_error_clear();
-}
-
-void giterr_set_str(int error_class, const char *string)
-{
- git_error_set_str(error_class, string);
-}
-
-void giterr_set_oom(void)
-{
- git_error_set_oom();
-}
-#endif
diff --git a/src/libgit2/fetch.c b/src/libgit2/fetch.c
index 5c2fee61737..8e2660f2172 100644
--- a/src/libgit2/fetch.c
+++ b/src/libgit2/fetch.c
@@ -17,9 +17,9 @@
#include "remote.h"
#include "refspec.h"
#include "pack.h"
-#include "netops.h"
#include "repository.h"
#include "refs.h"
+#include "transports/smart.h"
static int maybe_want(git_remote *remote, git_remote_head *head, git_refspec *tagspec, git_remote_autotag_option_t tagopt)
{
@@ -59,8 +59,12 @@ static int mark_local(git_remote *remote)
return -1;
git_vector_foreach(&remote->refs, i, head) {
- /* If we have the object, mark it so we don't ask for it */
- if (git_odb_exists(odb, &head->oid))
+ /* If we have the object, mark it so we don't ask for it.
+ However if we are unshallowing or changing history
+ depth, we need to ask for it even though the head
+ exists locally. */
+ if (remote->nego.depth == GIT_FETCH_DEPTH_FULL &&
+ git_odb_exists(odb, &head->oid))
head->local = 1;
else
remote->need_pack = 1;
@@ -76,7 +80,7 @@ static int maybe_want_oid(git_remote *remote, git_refspec *spec)
oid_head = git__calloc(1, sizeof(git_remote_head));
GIT_ERROR_CHECK_ALLOC(oid_head);
- git_oid__fromstr(&oid_head->oid, spec->src, GIT_OID_SHA1);
+ git_oid__fromstr(&oid_head->oid, spec->src, remote->repo->oid_type);
if (spec->dst) {
oid_head->name = git__strdup(spec->dst);
@@ -95,7 +99,6 @@ static int filter_wants(git_remote *remote, const git_fetch_options *opts)
git_remote_head **heads;
git_refspec tagspec, head, *spec;
int error = 0;
- git_odb *odb;
size_t i, heads_len;
unsigned int remote_caps;
unsigned int oid_mask = GIT_REMOTE_CAPABILITY_TIP_OID |
@@ -126,9 +129,6 @@ static int filter_wants(git_remote *remote, const git_fetch_options *opts)
goto cleanup;
}
- if ((error = git_repository_odb__weakptr(&odb, remote->repo)) < 0)
- goto cleanup;
-
if ((error = git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote)) < 0 ||
(error = git_remote_capabilities(&remote_caps, remote)) < 0)
goto cleanup;
@@ -141,7 +141,7 @@ static int filter_wants(git_remote *remote, const git_fetch_options *opts)
/* Handle explicitly specified OID specs */
git_vector_foreach(&remote->active_refspecs, i, spec) {
- if (!git_oid__is_hexstr(spec->src, GIT_OID_SHA1))
+ if (!git_oid__is_hexstr(spec->src, remote->repo->oid_type))
continue;
if (!(remote_caps & oid_mask)) {
@@ -170,9 +170,15 @@ static int filter_wants(git_remote *remote, const git_fetch_options *opts)
int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts)
{
git_transport *t = remote->transport;
+ int error;
remote->need_pack = 0;
+ if (opts) {
+ GIT_ASSERT_ARG(opts->depth >= 0);
+ remote->nego.depth = opts->depth;
+ }
+
if (filter_wants(remote, opts) < 0)
return -1;
@@ -184,20 +190,40 @@ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts)
* Now we have everything set up so we can start tell the
* server what we want and what we have.
*/
- return t->negotiate_fetch(t,
+ remote->nego.refs = (const git_remote_head * const *)remote->refs.contents;
+ remote->nego.refs_len = remote->refs.length;
+
+ if (git_repository__shallow_roots(&remote->nego.shallow_roots,
+ &remote->nego.shallow_roots_len,
+ remote->repo) < 0)
+ return -1;
+
+ error = t->negotiate_fetch(t,
remote->repo,
- (const git_remote_head * const *)remote->refs.contents,
- remote->refs.length);
+ &remote->nego);
+
+ git__free(remote->nego.shallow_roots);
+
+ return error;
}
int git_fetch_download_pack(git_remote *remote)
{
+ git_oidarray shallow_roots = { NULL };
git_transport *t = remote->transport;
+ int error;
if (!remote->need_pack)
return 0;
- return t->download_pack(t, remote->repo, &remote->stats);
+ if ((error = t->download_pack(t, remote->repo, &remote->stats)) != 0 ||
+ (error = t->shallow_roots(&shallow_roots, t)) != 0)
+ return error;
+
+ error = git_repository__shallow_roots_write(remote->repo, &shallow_roots);
+
+ git_oidarray_dispose(&shallow_roots);
+ return error;
}
int git_fetch_options_init(git_fetch_options *opts, unsigned int version)
diff --git a/src/libgit2/fetch.h b/src/libgit2/fetch.h
index 10b6731f0a2..493366dedf1 100644
--- a/src/libgit2/fetch.h
+++ b/src/libgit2/fetch.h
@@ -11,8 +11,6 @@
#include "git2/remote.h"
-#include "netops.h"
-
int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts);
int git_fetch_download_pack(git_remote *remote);
diff --git a/src/libgit2/fetchhead.c b/src/libgit2/fetchhead.c
index 0ebfe5c439b..2f276e5265e 100644
--- a/src/libgit2/fetchhead.c
+++ b/src/libgit2/fetchhead.c
@@ -105,15 +105,14 @@ static int fetchhead_ref_write(
git_filebuf *file,
git_fetchhead_ref *fetchhead_ref)
{
- char oid[GIT_OID_SHA1_HEXSIZE + 1];
+ char oid[GIT_OID_MAX_HEXSIZE + 1];
const char *type, *name;
int head = 0;
GIT_ASSERT_ARG(file);
GIT_ASSERT_ARG(fetchhead_ref);
- git_oid_fmt(oid, &fetchhead_ref->oid);
- oid[GIT_OID_SHA1_HEXSIZE] = '\0';
+ git_oid_tostr(oid, GIT_OID_MAX_HEXSIZE + 1, &fetchhead_ref->oid);
if (git__prefixcmp(fetchhead_ref->ref_name, GIT_REFS_HEADS_DIR) == 0) {
type = "branch ";
@@ -174,7 +173,8 @@ static int fetchhead_ref_parse(
git_str *ref_name,
const char **remote_url,
char *line,
- size_t line_num)
+ size_t line_num,
+ git_oid_t oid_type)
{
char *oid_str, *is_merge_str, *desc, *name = NULL;
const char *type = NULL;
@@ -196,13 +196,13 @@ static int fetchhead_ref_parse(
*is_merge = 1;
}
- if (strlen(oid_str) != GIT_OID_SHA1_HEXSIZE) {
+ if (strlen(oid_str) != git_oid_hexsize(oid_type)) {
git_error_set(GIT_ERROR_FETCHHEAD,
"invalid object ID in FETCH_HEAD line %"PRIuZ, line_num);
return -1;
}
- if (git_oid__fromstr(oid, oid_str, GIT_OID_SHA1) < 0) {
+ if (git_oid__fromstr(oid, oid_str, oid_type) < 0) {
const git_error *oid_err = git_error_last();
const char *err_msg = oid_err ? oid_err->message : "invalid object ID";
@@ -269,7 +269,8 @@ static int fetchhead_ref_parse(
return error;
}
-int git_repository_fetchhead_foreach(git_repository *repo,
+int git_repository_fetchhead_foreach(
+ git_repository *repo,
git_repository_fetchhead_foreach_cb cb,
void *payload)
{
@@ -296,8 +297,9 @@ int git_repository_fetchhead_foreach(git_repository *repo,
while ((line = git__strsep(&buffer, "\n")) != NULL) {
++line_num;
- if ((error = fetchhead_ref_parse(
- &oid, &is_merge, &name, &remote_url, line, line_num)) < 0)
+ if ((error = fetchhead_ref_parse(&oid, &is_merge, &name,
+ &remote_url, line, line_num,
+ repo->oid_type)) < 0)
goto done;
if (git_str_len(&name) > 0)
diff --git a/src/libgit2/filter.c b/src/libgit2/filter.c
index 80a3cae67bc..fdfc409a287 100644
--- a/src/libgit2/filter.c
+++ b/src/libgit2/filter.c
@@ -908,7 +908,7 @@ static int buffered_stream_close(git_writestream *s)
{
struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
git_str *writebuf;
- git_error_state error_state = {0};
+ git_error *last_error;
int error;
GIT_ASSERT_ARG(buffered_stream);
@@ -946,9 +946,9 @@ static int buffered_stream_close(git_writestream *s)
} else {
/* close stream before erroring out taking care
* to preserve the original error */
- git_error_state_capture(&error_state, error);
+ git_error_save(&last_error);
buffered_stream->target->close(buffered_stream->target);
- git_error_state_restore(&error_state);
+ git_error_restore(last_error);
return error;
}
diff --git a/src/libgit2/git2.rc b/src/libgit2/git2.rc
index d273afd7066..b94ecafd774 100644
--- a/src/libgit2/git2.rc
+++ b/src/libgit2/git2.rc
@@ -10,7 +10,7 @@
#endif
#ifndef LIBGIT2_COMMENTS
-# define LIBGIT2_COMMENTS "For more information visit http://libgit2.github.com/"
+# define LIBGIT2_COMMENTS "For more information visit https://libgit2.org/"
#endif
#ifdef __GNUC__
diff --git a/src/libgit2/grafts.c b/src/libgit2/grafts.c
new file mode 100644
index 00000000000..1d9373a56f6
--- /dev/null
+++ b/src/libgit2/grafts.c
@@ -0,0 +1,272 @@
+/*
+ * 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 "grafts.h"
+
+#include "futils.h"
+#include "oid.h"
+#include "oidarray.h"
+#include "parse.h"
+
+struct git_grafts {
+ /* Map of `git_commit_graft`s */
+ git_oidmap *commits;
+
+ /* Type of object IDs */
+ git_oid_t oid_type;
+
+ /* File backing the graft. NULL if it's an in-memory graft */
+ char *path;
+ unsigned char path_checksum[GIT_HASH_SHA256_SIZE];
+};
+
+int git_grafts_new(git_grafts **out, git_oid_t oid_type)
+{
+ git_grafts *grafts;
+
+ GIT_ASSERT_ARG(out && oid_type);
+
+ grafts = git__calloc(1, sizeof(*grafts));
+ GIT_ERROR_CHECK_ALLOC(grafts);
+
+ if ((git_oidmap_new(&grafts->commits)) < 0) {
+ git__free(grafts);
+ return -1;
+ }
+
+ grafts->oid_type = oid_type;
+
+ *out = grafts;
+ return 0;
+}
+
+int git_grafts_open(
+ git_grafts **out,
+ const char *path,
+ git_oid_t oid_type)
+{
+ git_grafts *grafts = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(out && path && oid_type);
+
+ if ((error = git_grafts_new(&grafts, oid_type)) < 0)
+ goto error;
+
+ grafts->path = git__strdup(path);
+ GIT_ERROR_CHECK_ALLOC(grafts->path);
+
+ if ((error = git_grafts_refresh(grafts)) < 0)
+ goto error;
+
+ *out = grafts;
+
+error:
+ if (error < 0)
+ git_grafts_free(grafts);
+
+ return error;
+}
+
+int git_grafts_open_or_refresh(
+ git_grafts **out,
+ const char *path,
+ git_oid_t oid_type)
+{
+ GIT_ASSERT_ARG(out && path && oid_type);
+
+ return *out ? git_grafts_refresh(*out) : git_grafts_open(out, path, oid_type);
+}
+
+void git_grafts_free(git_grafts *grafts)
+{
+ if (!grafts)
+ return;
+ git__free(grafts->path);
+ git_grafts_clear(grafts);
+ git_oidmap_free(grafts->commits);
+ git__free(grafts);
+}
+
+void git_grafts_clear(git_grafts *grafts)
+{
+ git_commit_graft *graft;
+
+ if (!grafts)
+ return;
+
+ git_oidmap_foreach_value(grafts->commits, graft, {
+ git__free(graft->parents.ptr);
+ git__free(graft);
+ });
+
+ git_oidmap_clear(grafts->commits);
+}
+
+int git_grafts_refresh(git_grafts *grafts)
+{
+ git_str contents = GIT_STR_INIT;
+ int error, updated = 0;
+
+ GIT_ASSERT_ARG(grafts);
+
+ if (!grafts->path)
+ return 0;
+
+ if ((error = git_futils_readbuffer_updated(&contents, grafts->path,
+ grafts->path_checksum, &updated)) < 0) {
+
+ if (error == GIT_ENOTFOUND) {
+ git_grafts_clear(grafts);
+ error = 0;
+ }
+
+ goto cleanup;
+ }
+
+ if (!updated) {
+ goto cleanup;
+ }
+
+ if ((error = git_grafts_parse(grafts, contents.ptr, contents.size)) < 0)
+ goto cleanup;
+
+cleanup:
+ git_str_dispose(&contents);
+ return error;
+}
+
+int git_grafts_parse(git_grafts *grafts, const char *buf, size_t len)
+{
+ git_array_oid_t parents = GIT_ARRAY_INIT;
+ git_parse_ctx parser;
+ int error;
+
+ git_grafts_clear(grafts);
+
+ if ((error = git_parse_ctx_init(&parser, buf, len)) < 0)
+ goto error;
+
+ for (; parser.remain_len; git_parse_advance_line(&parser)) {
+ git_oid graft_oid;
+
+ if ((error = git_parse_advance_oid(&graft_oid, &parser, grafts->oid_type)) < 0) {
+ git_error_set(GIT_ERROR_GRAFTS, "invalid graft OID at line %" PRIuZ, parser.line_num);
+ goto error;
+ }
+
+ while (parser.line_len && git_parse_advance_expected(&parser, "\n", 1) != 0) {
+ git_oid *id = git_array_alloc(parents);
+ GIT_ERROR_CHECK_ALLOC(id);
+
+ if ((error = git_parse_advance_expected(&parser, " ", 1)) < 0 ||
+ (error = git_parse_advance_oid(id, &parser, grafts->oid_type)) < 0) {
+ git_error_set(GIT_ERROR_GRAFTS, "invalid parent OID at line %" PRIuZ, parser.line_num);
+ goto error;
+ }
+ }
+
+ if ((error = git_grafts_add(grafts, &graft_oid, parents)) < 0)
+ goto error;
+
+ git_array_clear(parents);
+ }
+
+error:
+ git_array_clear(parents);
+ return error;
+}
+
+int git_grafts_add(git_grafts *grafts, const git_oid *oid, git_array_oid_t parents)
+{
+ git_commit_graft *graft;
+ git_oid *parent_oid;
+ int error;
+ size_t i;
+
+ GIT_ASSERT_ARG(grafts && oid);
+
+ graft = git__calloc(1, sizeof(*graft));
+ GIT_ERROR_CHECK_ALLOC(graft);
+
+ git_array_init_to_size(graft->parents, git_array_size(parents));
+ git_array_foreach(parents, i, parent_oid) {
+ git_oid *id = git_array_alloc(graft->parents);
+ GIT_ERROR_CHECK_ALLOC(id);
+
+ git_oid_cpy(id, parent_oid);
+ }
+ git_oid_cpy(&graft->oid, oid);
+
+ if ((error = git_grafts_remove(grafts, &graft->oid)) < 0 && error != GIT_ENOTFOUND)
+ goto cleanup;
+
+ if ((error = git_oidmap_set(grafts->commits, &graft->oid, graft)) < 0)
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ git_array_clear(graft->parents);
+ git__free(graft);
+ return error;
+}
+
+int git_grafts_remove(git_grafts *grafts, const git_oid *oid)
+{
+ git_commit_graft *graft;
+ int error;
+
+ GIT_ASSERT_ARG(grafts && oid);
+
+ if ((graft = git_oidmap_get(grafts->commits, oid)) == NULL)
+ return GIT_ENOTFOUND;
+
+ if ((error = git_oidmap_delete(grafts->commits, oid)) < 0)
+ return error;
+
+ git__free(graft->parents.ptr);
+ git__free(graft);
+
+ return 0;
+}
+
+int git_grafts_get(git_commit_graft **out, git_grafts *grafts, const git_oid *oid)
+{
+ GIT_ASSERT_ARG(out && grafts && oid);
+ if ((*out = git_oidmap_get(grafts->commits, oid)) == NULL)
+ return GIT_ENOTFOUND;
+ return 0;
+}
+
+int git_grafts_oids(git_oid **out, size_t *out_len, git_grafts *grafts)
+{
+ git_array_oid_t array = GIT_ARRAY_INIT;
+ const git_oid *oid;
+ size_t existing, i = 0;
+
+ GIT_ASSERT_ARG(out && grafts);
+
+ if ((existing = git_oidmap_size(grafts->commits)) > 0)
+ git_array_init_to_size(array, existing);
+
+ while (git_oidmap_iterate(NULL, grafts->commits, &i, &oid) == 0) {
+ git_oid *cpy = git_array_alloc(array);
+ GIT_ERROR_CHECK_ALLOC(cpy);
+ git_oid_cpy(cpy, oid);
+ }
+
+ *out = array.ptr;
+ *out_len = array.size;
+
+ return 0;
+}
+
+size_t git_grafts_size(git_grafts *grafts)
+{
+ return git_oidmap_size(grafts->commits);
+}
diff --git a/src/libgit2/grafts.h b/src/libgit2/grafts.h
new file mode 100644
index 00000000000..394867fd6cc
--- /dev/null
+++ b/src/libgit2/grafts.h
@@ -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.
+ */
+#ifndef INCLUDE_graft_h__
+#define INCLUDE_graft_h__
+
+#include "common.h"
+#include "oidarray.h"
+#include "oidmap.h"
+
+/** graft commit */
+typedef struct {
+ git_oid oid;
+ git_array_oid_t parents;
+} git_commit_graft;
+
+typedef struct git_grafts git_grafts;
+
+int git_grafts_new(git_grafts **out, git_oid_t oid_type);
+int git_grafts_open(git_grafts **out, const char *path, git_oid_t oid_type);
+int git_grafts_open_or_refresh(git_grafts **out, const char *path, git_oid_t oid_type);
+void git_grafts_free(git_grafts *grafts);
+void git_grafts_clear(git_grafts *grafts);
+
+int git_grafts_refresh(git_grafts *grafts);
+int git_grafts_parse(git_grafts *grafts, const char *buf, size_t len);
+int git_grafts_add(git_grafts *grafts, const git_oid *oid, git_array_oid_t parents);
+int git_grafts_remove(git_grafts *grafts, const git_oid *oid);
+int git_grafts_get(git_commit_graft **out, git_grafts *grafts, const git_oid *oid);
+int git_grafts_oids(git_oid **out, size_t *out_len, git_grafts *grafts);
+size_t git_grafts_size(git_grafts *grafts);
+
+#endif
diff --git a/src/libgit2/ident.c b/src/libgit2/ident.c
index bf9a4998e55..97110c66410 100644
--- a/src/libgit2/ident.c
+++ b/src/libgit2/ident.c
@@ -42,7 +42,7 @@ static int ident_find_id(
static int ident_insert_id(
git_str *to, const git_str *from, const git_filter_source *src)
{
- char oid[GIT_OID_SHA1_HEXSIZE+1];
+ char oid[GIT_OID_MAX_HEXSIZE + 1];
const char *id_start, *id_end, *from_end = from->ptr + from->size;
size_t need_size;
@@ -57,7 +57,7 @@ static int ident_insert_id(
return GIT_PASSTHROUGH;
need_size = (size_t)(id_start - from->ptr) +
- 5 /* "$Id: " */ + GIT_OID_SHA1_HEXSIZE + 2 /* " $" */ +
+ 5 /* "$Id: " */ + GIT_OID_MAX_HEXSIZE + 2 /* " $" */ +
(size_t)(from_end - id_end);
if (git_str_grow(to, need_size) < 0)
@@ -65,7 +65,7 @@ static int ident_insert_id(
git_str_set(to, from->ptr, (size_t)(id_start - from->ptr));
git_str_put(to, "$Id: ", 5);
- git_str_put(to, oid, GIT_OID_SHA1_HEXSIZE);
+ git_str_puts(to, oid);
git_str_put(to, " $", 2);
git_str_put(to, id_end, (size_t)(from_end - id_end));
diff --git a/src/libgit2/index.c b/src/libgit2/index.c
index d61c4bcfdcf..3a54ded450a 100644
--- a/src/libgit2/index.c
+++ b/src/libgit2/index.c
@@ -34,8 +34,6 @@ static int index_apply_to_wd_diff(git_index *index, int action, const git_strarr
unsigned int flags,
git_index_matched_path_cb cb, void *payload);
-#define minimal_entry_size (offsetof(struct entry_short, path))
-
static const size_t INDEX_HEADER_SIZE = 12;
static const unsigned int INDEX_VERSION_NUMBER_DEFAULT = 2;
@@ -67,7 +65,7 @@ struct entry_time {
uint32_t nanoseconds;
};
-struct entry_short {
+struct entry_common {
struct entry_time ctime;
struct entry_time mtime;
uint32_t dev;
@@ -76,25 +74,35 @@ struct entry_short {
uint32_t uid;
uint32_t gid;
uint32_t file_size;
- unsigned char oid[GIT_OID_SHA1_SIZE];
- uint16_t flags;
- char path[1]; /* arbitrary length */
};
-struct entry_long {
- struct entry_time ctime;
- struct entry_time mtime;
- uint32_t dev;
- uint32_t ino;
- uint32_t mode;
- uint32_t uid;
- uint32_t gid;
- uint32_t file_size;
- unsigned char oid[GIT_OID_SHA1_SIZE];
- uint16_t flags;
- uint16_t flags_extended;
- char path[1]; /* arbitrary length */
-};
+#define entry_short(oid_size) \
+ struct { \
+ struct entry_common common; \
+ unsigned char oid[oid_size]; \
+ uint16_t flags; \
+ char path[1]; /* arbitrary length */ \
+ }
+
+#define entry_long(oid_size) \
+ struct { \
+ struct entry_common common; \
+ unsigned char oid[oid_size]; \
+ uint16_t flags; \
+ uint16_t flags_extended; \
+ char path[1]; /* arbitrary length */ \
+ }
+
+typedef entry_short(GIT_OID_SHA1_SIZE) index_entry_short_sha1;
+typedef entry_long(GIT_OID_SHA1_SIZE) index_entry_long_sha1;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+typedef entry_short(GIT_OID_SHA256_SIZE) index_entry_short_sha256;
+typedef entry_long(GIT_OID_SHA256_SIZE) index_entry_long_sha256;
+#endif
+
+#undef entry_short
+#undef entry_long
struct entry_srch_key {
const char *path;
@@ -117,12 +125,12 @@ struct reuc_entry_internal {
bool git_index__enforce_unsaved_safety = false;
/* local declarations */
-static int read_extension(size_t *read_len, git_index *index, const char *buffer, size_t buffer_size);
+static int read_extension(size_t *read_len, git_index *index, size_t checksum_size, const char *buffer, size_t buffer_size);
static int read_header(struct index_header *dest, const void *buffer);
static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
static bool is_index_extended(git_index *index);
-static int write_index(unsigned char checksum[GIT_HASH_SHA1_SIZE], size_t *checksum_size, git_index *index, git_filebuf *file);
+static int write_index(unsigned char checksum[GIT_HASH_MAX_SIZE], size_t *checksum_size, git_index *index, git_filebuf *file);
static void index_entry_free(git_index_entry *entry);
static void index_entry_reuc_free(git_index_reuc_entry *reuc);
@@ -403,7 +411,10 @@ void git_index__set_ignore_case(git_index *index, bool ignore_case)
git_vector_sort(&index->reuc);
}
-int git_index_open(git_index **index_out, const char *index_path)
+int git_index__open(
+ git_index **index_out,
+ const char *index_path,
+ git_oid_t oid_type)
{
git_index *index;
int error = -1;
@@ -413,6 +424,8 @@ int git_index_open(git_index **index_out, const char *index_path)
index = git__calloc(1, sizeof(git_index));
GIT_ERROR_CHECK_ALLOC(index);
+ index->oid_type = oid_type;
+
if (git_pool_init(&index->tree_pool, 1) < 0)
goto fail;
@@ -453,10 +466,34 @@ int git_index_open(git_index **index_out, const char *index_path)
return error;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_index_open(git_index **index_out, const char *index_path, git_oid_t oid_type)
+{
+ return git_index__open(index_out, index_path, oid_type);
+}
+#else
+int git_index_open(git_index **index_out, const char *index_path)
+{
+ return git_index__open(index_out, index_path, GIT_OID_SHA1);
+}
+#endif
+
+int git_index__new(git_index **out, git_oid_t oid_type)
+{
+ return git_index__open(out, NULL, oid_type);
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_index_new(git_index **out, git_oid_t oid_type)
+{
+ return git_index__new(out, oid_type);
+}
+#else
int git_index_new(git_index **out)
{
- return git_index_open(out, NULL);
+ return git_index__new(out, GIT_OID_SHA1);
}
+#endif
static void index_free(git_index *index)
{
@@ -622,8 +659,8 @@ static int compare_checksum(git_index *index)
{
int fd;
ssize_t bytes_read;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
- size_t checksum_size = GIT_HASH_SHA1_SIZE;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ size_t checksum_size = git_oid_size(index->oid_type);
if ((fd = p_open(index->index_file_path, O_RDONLY)) < 0)
return fd;
@@ -1154,10 +1191,13 @@ static int has_dir_name(git_index *index,
size_t len, pos;
for (;;) {
- if (*--slash == '/')
- break;
+ slash--;
+
if (slash <= entry->path)
return 0;
+
+ if (*slash == '/')
+ break;
}
len = slash - name;
@@ -1578,15 +1618,17 @@ int git_index_add_bypath(git_index *index, const char *path)
if (ret == GIT_EDIRECTORY) {
git_submodule *sm;
- git_error_state err;
+ git_error *last_error;
- git_error_state_capture(&err, ret);
+ git_error_save(&last_error);
ret = git_submodule_lookup(&sm, INDEX_OWNER(index), path);
- if (ret == GIT_ENOTFOUND)
- return git_error_state_restore(&err);
+ if (ret == GIT_ENOTFOUND) {
+ git_error_restore(last_error);
+ return GIT_EDIRECTORY;
+ }
- git_error_state_free(&err);
+ git_error_free(last_error);
/*
* EEXISTS means that there is a repository at that path, but it's not known
@@ -2312,6 +2354,7 @@ static int index_error_invalid(const char *message)
static int read_reuc(git_index *index, const char *buffer, size_t size)
{
const char *endptr;
+ size_t oid_size = git_oid_size(index->oid_type);
size_t len;
int i;
@@ -2360,16 +2403,16 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
for (i = 0; i < 3; i++) {
if (!lost->mode[i])
continue;
- if (size < GIT_OID_SHA1_SIZE) {
+ if (size < oid_size) {
index_entry_reuc_free(lost);
return index_error_invalid("reading reuc entry oid");
}
- if (git_oid__fromraw(&lost->oid[i], (const unsigned char *) buffer, GIT_OID_SHA1) < 0)
+ if (git_oid__fromraw(&lost->oid[i], (const unsigned char *) buffer, index->oid_type) < 0)
return -1;
- size -= GIT_OID_SHA1_SIZE;
- buffer += GIT_OID_SHA1_SIZE;
+ size -= oid_size;
+ buffer += oid_size;
}
/* entry was read successfully - insert into reuc vector */
@@ -2439,73 +2482,157 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size
return 0;
}
-static size_t index_entry_size(size_t path_len, size_t varint_len, uint32_t flags)
+GIT_INLINE(size_t) index_entry_path_offset(
+ git_oid_t oid_type,
+ uint32_t flags)
{
+ if (oid_type == GIT_OID_SHA1)
+ return (flags & GIT_INDEX_ENTRY_EXTENDED) ?
+ offsetof(index_entry_long_sha1, path) :
+ offsetof(index_entry_short_sha1, path);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ else if (oid_type == GIT_OID_SHA256)
+ return (flags & GIT_INDEX_ENTRY_EXTENDED) ?
+ offsetof(index_entry_long_sha256, path) :
+ offsetof(index_entry_short_sha256, path);
+#endif
+
+ git_error_set(GIT_ERROR_INTERNAL, "invalid oid type");
+ return 0;
+}
+
+GIT_INLINE(size_t) index_entry_flags_offset(git_oid_t oid_type)
+{
+ if (oid_type == GIT_OID_SHA1)
+ return offsetof(index_entry_long_sha1, flags_extended);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ else if (oid_type == GIT_OID_SHA256)
+ return offsetof(index_entry_long_sha256, flags_extended);
+#endif
+
+ git_error_set(GIT_ERROR_INTERNAL, "invalid oid type");
+ return 0;
+}
+
+static size_t index_entry_size(
+ size_t path_len,
+ size_t varint_len,
+ git_oid_t oid_type,
+ uint32_t flags)
+{
+ size_t offset, size;
+
+ if (!(offset = index_entry_path_offset(oid_type, flags)))
+ return 0;
+
if (varint_len) {
- if (flags & GIT_INDEX_ENTRY_EXTENDED)
- return offsetof(struct entry_long, path) + path_len + 1 + varint_len;
- else
- return offsetof(struct entry_short, path) + path_len + 1 + varint_len;
+ if (GIT_ADD_SIZET_OVERFLOW(&size, offset, path_len) ||
+ GIT_ADD_SIZET_OVERFLOW(&size, size, 1) ||
+ GIT_ADD_SIZET_OVERFLOW(&size, size, varint_len))
+ return 0;
} else {
-#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7)
- if (flags & GIT_INDEX_ENTRY_EXTENDED)
- return entry_size(struct entry_long, path_len);
- else
- return entry_size(struct entry_short, path_len);
-#undef entry_size
+ if (GIT_ADD_SIZET_OVERFLOW(&size, offset, path_len) ||
+ GIT_ADD_SIZET_OVERFLOW(&size, size, 8))
+ return 0;
+
+ size &= ~7;
}
+
+ return size;
}
static int read_entry(
git_index_entry **out,
size_t *out_size,
git_index *index,
+ size_t checksum_size,
const void *buffer,
size_t buffer_size,
const char *last)
{
- size_t path_length, entry_size;
+ size_t path_length, path_offset, entry_size;
const char *path_ptr;
- struct entry_short source;
+ struct entry_common *source_common;
+ index_entry_short_sha1 source_sha1;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ index_entry_short_sha256 source_sha256;
+#endif
git_index_entry entry = {{0}};
bool compressed = index->version >= INDEX_VERSION_NUMBER_COMP;
char *tmp_path = NULL;
- size_t checksum_size = GIT_HASH_SHA1_SIZE;
+
+ size_t minimal_entry_size = index_entry_path_offset(index->oid_type, 0);
if (checksum_size + minimal_entry_size > buffer_size)
return -1;
/* buffer is not guaranteed to be aligned */
- memcpy(&source, buffer, sizeof(struct entry_short));
-
- entry.ctime.seconds = (git_time_t)ntohl(source.ctime.seconds);
- entry.ctime.nanoseconds = ntohl(source.ctime.nanoseconds);
- entry.mtime.seconds = (git_time_t)ntohl(source.mtime.seconds);
- entry.mtime.nanoseconds = ntohl(source.mtime.nanoseconds);
- entry.dev = ntohl(source.dev);
- entry.ino = ntohl(source.ino);
- entry.mode = ntohl(source.mode);
- entry.uid = ntohl(source.uid);
- entry.gid = ntohl(source.gid);
- entry.file_size = ntohl(source.file_size);
- entry.flags = ntohs(source.flags);
-
- if (git_oid__fromraw(&entry.id, source.oid, GIT_OID_SHA1) < 0)
+ switch (index->oid_type) {
+ case GIT_OID_SHA1:
+ source_common = &source_sha1.common;
+ memcpy(&source_sha1, buffer, sizeof(source_sha1));
+ break;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ source_common = &source_sha256.common;
+ memcpy(&source_sha256, buffer, sizeof(source_sha256));
+ break;
+#endif
+ default:
+ GIT_ASSERT(!"invalid oid type");
+ }
+
+ entry.ctime.seconds = (git_time_t)ntohl(source_common->ctime.seconds);
+ entry.ctime.nanoseconds = ntohl(source_common->ctime.nanoseconds);
+ entry.mtime.seconds = (git_time_t)ntohl(source_common->mtime.seconds);
+ entry.mtime.nanoseconds = ntohl(source_common->mtime.nanoseconds);
+ entry.dev = ntohl(source_common->dev);
+ entry.ino = ntohl(source_common->ino);
+ entry.mode = ntohl(source_common->mode);
+ entry.uid = ntohl(source_common->uid);
+ entry.gid = ntohl(source_common->gid);
+ entry.file_size = ntohl(source_common->file_size);
+
+ switch (index->oid_type) {
+ case GIT_OID_SHA1:
+ if (git_oid__fromraw(&entry.id, source_sha1.oid,
+ GIT_OID_SHA1) < 0)
+ return -1;
+ entry.flags = ntohs(source_sha1.flags);
+ break;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ if (git_oid__fromraw(&entry.id, source_sha256.oid,
+ GIT_OID_SHA256) < 0)
+ return -1;
+ entry.flags = ntohs(source_sha256.flags);
+ break;
+#endif
+ default:
+ GIT_ASSERT(!"invalid oid type");
+ }
+
+ if (!(path_offset = index_entry_path_offset(index->oid_type, entry.flags)))
return -1;
+
if (entry.flags & GIT_INDEX_ENTRY_EXTENDED) {
uint16_t flags_raw;
size_t flags_offset;
- flags_offset = offsetof(struct entry_long, flags_extended);
- memcpy(&flags_raw, (const char *) buffer + flags_offset,
- sizeof(flags_raw));
+ if (!(flags_offset = index_entry_flags_offset(index->oid_type)))
+ return -1;
+
+ memcpy(&flags_raw, (const char *)buffer + flags_offset, sizeof(flags_raw));
flags_raw = ntohs(flags_raw);
memcpy(&entry.flags_extended, &flags_raw, sizeof(flags_raw));
- path_ptr = (const char *) buffer + offsetof(struct entry_long, path);
- } else
- path_ptr = (const char *) buffer + offsetof(struct entry_short, path);
+ path_ptr = (const char *)buffer + path_offset;
+ } else {
+ path_ptr = (const char *)buffer + path_offset;
+ }
if (!compressed) {
path_length = entry.flags & GIT_INDEX_ENTRY_NAMEMASK;
@@ -2517,12 +2644,12 @@ static int read_entry(
path_end = memchr(path_ptr, '\0', buffer_size);
if (path_end == NULL)
- return -1;
+ return index_error_invalid("invalid path name");
path_length = path_end - path_ptr;
}
- entry_size = index_entry_size(path_length, 0, entry.flags);
+ entry_size = index_entry_size(path_length, 0, index->oid_type, entry.flags);
entry.path = (char *)path_ptr;
} else {
size_t varint_len, last_len, prefix_len, suffix_len, path_len;
@@ -2548,15 +2675,18 @@ static int read_entry(
memcpy(tmp_path, last, prefix_len);
memcpy(tmp_path + prefix_len, path_ptr + varint_len, suffix_len + 1);
- entry_size = index_entry_size(suffix_len, varint_len, entry.flags);
+
+ entry_size = index_entry_size(suffix_len, varint_len, index->oid_type, entry.flags);
entry.path = tmp_path;
}
if (entry_size == 0)
return -1;
- if (checksum_size + entry_size > buffer_size)
+ if (checksum_size + entry_size > buffer_size) {
+ git_error_set(GIT_ERROR_INTERNAL, "invalid index checksum");
return -1;
+ }
if (index_entry_dup(out, index, &entry) < 0) {
git__free(tmp_path);
@@ -2585,11 +2715,10 @@ static int read_header(struct index_header *dest, const void *buffer)
return 0;
}
-static int read_extension(size_t *read_len, git_index *index, const char *buffer, size_t buffer_size)
+static int read_extension(size_t *read_len, git_index *index, size_t checksum_size, const char *buffer, size_t buffer_size)
{
struct index_extension dest;
size_t total_size;
- size_t checksum_size = GIT_HASH_SHA1_SIZE;
/* buffer is not guaranteed to be aligned */
memcpy(&dest, buffer, sizeof(struct index_extension));
@@ -2608,7 +2737,7 @@ static int read_extension(size_t *read_len, git_index *index, const char *buffer
if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') {
/* tree cache */
if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) {
- if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size, &index->tree_pool) < 0)
+ if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size, index->oid_type, &index->tree_pool) < 0)
return -1;
} else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
if (read_reuc(index, buffer + 8, dest.extension_size) < 0)
@@ -2636,8 +2765,9 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
int error = 0;
unsigned int i;
struct index_header header = { 0 };
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
- size_t checksum_size = GIT_HASH_SHA1_SIZE;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ unsigned char zero_checksum[GIT_HASH_MAX_SIZE] = { 0 };
+ size_t checksum_size = git_hash_size(git_oid_algorithm(index->oid_type));
const char *last = NULL;
const char *empty = "";
@@ -2652,9 +2782,12 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
if (buffer_size < INDEX_HEADER_SIZE + checksum_size)
return index_error_invalid("insufficient buffer space");
- /* Precalculate the SHA1 of the files's contents -- we'll match it to
- * the provided SHA1 in the footer */
- git_hash_buf(checksum, buffer, buffer_size - checksum_size, GIT_HASH_ALGORITHM_SHA1);
+ /*
+ * Precalculate the hash of the files's contents -- we'll match
+ * it to the provided checksum in the footer.
+ */
+ git_hash_buf(checksum, buffer, buffer_size - checksum_size,
+ git_oid_algorithm(index->oid_type));
/* Parse header */
if ((error = read_header(&header, buffer)) < 0)
@@ -2676,7 +2809,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
git_index_entry *entry = NULL;
size_t entry_size;
- if ((error = read_entry(&entry, &entry_size, index, buffer, buffer_size, last)) < 0) {
+ if ((error = read_entry(&entry, &entry_size, index, checksum_size, buffer, buffer_size, last)) < 0) {
error = index_error_invalid("invalid entry");
goto done;
}
@@ -2707,7 +2840,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
while (buffer_size > checksum_size) {
size_t extension_size;
- if ((error = read_extension(&extension_size, index, buffer, buffer_size)) < 0) {
+ if ((error = read_extension(&extension_size, index, checksum_size, buffer, buffer_size)) < 0) {
goto done;
}
@@ -2720,8 +2853,14 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
goto done;
}
- /* 160-bit SHA-1 over the content of the index file before this checksum. */
- if (memcmp(checksum, buffer, checksum_size) != 0) {
+ /*
+ * SHA-1 or SHA-256 (depending on the repository's object format)
+ * over the content of the index file before this checksum.
+ * Note: checksum may be 0 if the index was written by a client
+ * where index.skipHash was set to true.
+ */
+ if (memcmp(zero_checksum, buffer, checksum_size) != 0 &&
+ memcmp(checksum, buffer, checksum_size) != 0) {
error = index_error_invalid(
"calculated checksum does not match expected");
goto done;
@@ -2760,16 +2899,40 @@ static bool is_index_extended(git_index *index)
return (extended > 0);
}
-static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const char *last)
+static int write_disk_entry(
+ git_index *index,
+ git_filebuf *file,
+ git_index_entry *entry,
+ const char *last)
{
void *mem = NULL;
- struct entry_short ondisk;
- size_t path_len, disk_size;
+ struct entry_common *ondisk_common;
+ size_t path_len, path_offset, disk_size;
int varint_len = 0;
char *path;
const char *path_start = entry->path;
size_t same_len = 0;
+ index_entry_short_sha1 ondisk_sha1;
+ index_entry_long_sha1 ondisk_ext_sha1;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ index_entry_short_sha256 ondisk_sha256;
+ index_entry_long_sha256 ondisk_ext_sha256;
+#endif
+
+ switch (index->oid_type) {
+ case GIT_OID_SHA1:
+ ondisk_common = &ondisk_sha1.common;
+ break;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ ondisk_common = &ondisk_sha256.common;
+ break;
+#endif
+ default:
+ GIT_ASSERT(!"invalid oid type");
+ }
+
path_len = ((struct entry_internal *)entry)->pathlen;
if (last) {
@@ -2786,9 +2949,9 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha
varint_len = git_encode_varint(NULL, 0, strlen(last) - same_len);
}
- disk_size = index_entry_size(path_len, varint_len, entry->flags);
+ disk_size = index_entry_size(path_len, varint_len, index->oid_type, entry->flags);
- if (git_filebuf_reserve(file, &mem, disk_size) < 0)
+ if (!disk_size || git_filebuf_reserve(file, &mem, disk_size) < 0)
return -1;
memset(mem, 0x0, disk_size);
@@ -2803,35 +2966,77 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha
*
* In 2038 I will be either too dead or too rich to care about this
*/
- ondisk.ctime.seconds = htonl((uint32_t)entry->ctime.seconds);
- ondisk.mtime.seconds = htonl((uint32_t)entry->mtime.seconds);
- ondisk.ctime.nanoseconds = htonl(entry->ctime.nanoseconds);
- ondisk.mtime.nanoseconds = htonl(entry->mtime.nanoseconds);
- ondisk.dev = htonl(entry->dev);
- ondisk.ino = htonl(entry->ino);
- ondisk.mode = htonl(entry->mode);
- ondisk.uid = htonl(entry->uid);
- ondisk.gid = htonl(entry->gid);
- ondisk.file_size = htonl((uint32_t)entry->file_size);
- git_oid_raw_cpy(ondisk.oid, entry->id.id, GIT_OID_SHA1_SIZE);
- ondisk.flags = htons(entry->flags);
+ ondisk_common->ctime.seconds = htonl((uint32_t)entry->ctime.seconds);
+ ondisk_common->mtime.seconds = htonl((uint32_t)entry->mtime.seconds);
+ ondisk_common->ctime.nanoseconds = htonl(entry->ctime.nanoseconds);
+ ondisk_common->mtime.nanoseconds = htonl(entry->mtime.nanoseconds);
+ ondisk_common->dev = htonl(entry->dev);
+ ondisk_common->ino = htonl(entry->ino);
+ ondisk_common->mode = htonl(entry->mode);
+ ondisk_common->uid = htonl(entry->uid);
+ ondisk_common->gid = htonl(entry->gid);
+ ondisk_common->file_size = htonl((uint32_t)entry->file_size);
+
+ switch (index->oid_type) {
+ case GIT_OID_SHA1:
+ git_oid_raw_cpy(ondisk_sha1.oid, entry->id.id, GIT_OID_SHA1_SIZE);
+ ondisk_sha1.flags = htons(entry->flags);
+ break;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ git_oid_raw_cpy(ondisk_sha256.oid, entry->id.id, GIT_OID_SHA256_SIZE);
+ ondisk_sha256.flags = htons(entry->flags);
+ break;
+#endif
+ default:
+ GIT_ASSERT(!"invalid oid type");
+ }
+
+ path_offset = index_entry_path_offset(index->oid_type, entry->flags);
if (entry->flags & GIT_INDEX_ENTRY_EXTENDED) {
- const size_t path_offset = offsetof(struct entry_long, path);
- struct entry_long ondisk_ext;
- memcpy(&ondisk_ext, &ondisk, sizeof(struct entry_short));
- ondisk_ext.flags_extended = htons(entry->flags_extended &
+ struct entry_common *ondisk_ext;
+ uint16_t flags_extended = htons(entry->flags_extended &
GIT_INDEX_ENTRY_EXTENDED_FLAGS);
- memcpy(mem, &ondisk_ext, path_offset);
- path = (char *)mem + path_offset;
- disk_size -= path_offset;
+
+ switch (index->oid_type) {
+ case GIT_OID_SHA1:
+ memcpy(&ondisk_ext_sha1, &ondisk_sha1,
+ sizeof(index_entry_short_sha1));
+ ondisk_ext_sha1.flags_extended = flags_extended;
+ ondisk_ext = &ondisk_ext_sha1.common;
+ break;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ memcpy(&ondisk_ext_sha256, &ondisk_sha256,
+ sizeof(index_entry_short_sha256));
+ ondisk_ext_sha256.flags_extended = flags_extended;
+ ondisk_ext = &ondisk_ext_sha256.common;
+ break;
+#endif
+ default:
+ GIT_ASSERT(!"invalid oid type");
+ }
+
+ memcpy(mem, ondisk_ext, path_offset);
} else {
- const size_t path_offset = offsetof(struct entry_short, path);
- memcpy(mem, &ondisk, path_offset);
- path = (char *)mem + path_offset;
- disk_size -= path_offset;
+ switch (index->oid_type) {
+ case GIT_OID_SHA1:
+ memcpy(mem, &ondisk_sha1, path_offset);
+ break;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ memcpy(mem, &ondisk_sha256, path_offset);
+ break;
+#endif
+ default:
+ GIT_ASSERT(!"invalid oid type");
+ }
}
+ path = (char *)mem + path_offset;
+ disk_size -= path_offset;
+
if (last) {
varint_len = git_encode_varint((unsigned char *) path,
disk_size, strlen(last) - same_len);
@@ -2883,7 +3088,7 @@ static int write_entries(git_index *index, git_filebuf *file)
last = "";
git_vector_foreach(entries, i, entry) {
- if ((error = write_disk_entry(file, entry, last)) < 0)
+ if ((error = write_disk_entry(index, file, entry, last)) < 0)
break;
if (index->version >= INDEX_VERSION_NUMBER_COMP)
last = entry->path;
@@ -2961,8 +3166,9 @@ static int write_name_extension(git_index *index, git_filebuf *file)
return error;
}
-static int create_reuc_extension_data(git_str *reuc_buf, git_index_reuc_entry *reuc)
+static int create_reuc_extension_data(git_str *reuc_buf, git_index *index, git_index_reuc_entry *reuc)
{
+ size_t oid_size = git_oid_size(index->oid_type);
int i;
int error = 0;
@@ -2976,7 +3182,7 @@ static int create_reuc_extension_data(git_str *reuc_buf, git_index_reuc_entry *r
}
for (i = 0; i < 3; i++) {
- if (reuc->mode[i] && (error = git_str_put(reuc_buf, (char *)&reuc->oid[i].id, GIT_OID_SHA1_SIZE)) < 0)
+ if (reuc->mode[i] && (error = git_str_put(reuc_buf, (char *)&reuc->oid[i].id, oid_size)) < 0)
return error;
}
@@ -2993,7 +3199,7 @@ static int write_reuc_extension(git_index *index, git_filebuf *file)
int error = 0;
git_vector_foreach(out, i, reuc) {
- if ((error = create_reuc_extension_data(&reuc_buf, reuc)) < 0)
+ if ((error = create_reuc_extension_data(&reuc_buf, index, reuc)) < 0)
goto done;
}
@@ -3042,7 +3248,7 @@ static void clear_uptodate(git_index *index)
}
static int write_index(
- unsigned char checksum[GIT_HASH_SHA1_SIZE],
+ unsigned char checksum[GIT_HASH_MAX_SIZE],
size_t *checksum_size,
git_index *index,
git_filebuf *file)
@@ -3054,7 +3260,9 @@ static int write_index(
GIT_ASSERT_ARG(index);
GIT_ASSERT_ARG(file);
- *checksum_size = GIT_HASH_SHA1_SIZE;
+ GIT_ASSERT(index->oid_type);
+
+ *checksum_size = git_hash_size(git_oid_algorithm(index->oid_type));
if (index->version <= INDEX_VERSION_NUMBER_EXT) {
is_extended = is_index_extended(index);
@@ -3235,7 +3443,7 @@ int git_index_read_tree(git_index *index, const git_tree *tree)
if (error < 0)
return error;
- error = git_tree_cache_read_tree(&index->tree, tree, &index->tree_pool);
+ error = git_tree_cache_read_tree(&index->tree, tree, index->oid_type, &index->tree_pool);
return error;
}
@@ -3423,7 +3631,6 @@ int git_index_add_all(
{
int error;
git_repository *repo;
- git_iterator *wditer = NULL;
git_pathspec ps;
bool no_fnmatch = (flags & GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH) != 0;
@@ -3449,7 +3656,6 @@ int git_index_add_all(
git_error_set_after_callback(error);
cleanup:
- git_iterator_free(wditer);
git_pathspec__clear(&ps);
return error;
@@ -3545,7 +3751,8 @@ static int index_apply_to_wd_diff(git_index *index, int action, const git_strarr
opts.skip_sparse_files = 1;
if (flags & GIT_INDEX_ADD_FORCE)
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED |
+ GIT_DIFF_RECURSE_IGNORED_DIRS;
}
if (flags & GIT_INDEX_UPDATE_INDEX)
opts.flags |= GIT_DIFF_UPDATE_INDEX;
@@ -3705,19 +3912,23 @@ int git_indexwriter_init(
git_indexwriter *writer,
git_index *index)
{
- int error;
+ int filebuf_hash, error;
GIT_REFCOUNT_INC(index);
writer->index = index;
+ filebuf_hash = git_filebuf_hash_flags(git_oid_algorithm(index->oid_type));
+ GIT_ASSERT(filebuf_hash);
+
if (!index->index_file_path)
return create_index_error(-1,
"failed to write index: The index is in-memory only");
- if ((error = git_filebuf_open(
- &writer->file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_INDEX_FILE_MODE)) < 0) {
-
+ if ((error = git_filebuf_open(&writer->file,
+ index->index_file_path,
+ git_filebuf_hash_flags(filebuf_hash),
+ GIT_INDEX_FILE_MODE)) < 0) {
if (error == GIT_ELOCKED)
git_error_set(GIT_ERROR_INDEX, "the index is locked; this might be due to a concurrent or crashed process");
@@ -3749,7 +3960,7 @@ int git_indexwriter_init_for_operation(
int git_indexwriter_commit(git_indexwriter *writer)
{
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
size_t checksum_size;
int error;
diff --git a/src/libgit2/index.h b/src/libgit2/index.h
index 71bb096f738..53c29977df7 100644
--- a/src/libgit2/index.h
+++ b/src/libgit2/index.h
@@ -27,7 +27,7 @@ struct git_index {
char *index_file_path;
git_futils_filestamp stamp;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
git_vector entries;
git_idxmap *entries_map;
@@ -35,6 +35,8 @@ struct git_index {
git_vector deleted; /* deleted entries if readers > 0 */
git_atomic32 readers; /* number of active iterators */
+ git_oid_t oid_type;
+
unsigned int on_disk:1;
unsigned int ignore_case:1;
unsigned int distrust_filemode:1;
@@ -141,6 +143,17 @@ GIT_INLINE(unsigned char *) git_index__checksum(git_index *index)
return index->checksum;
}
+/* SHA256-aware internal functions */
+
+extern int git_index__new(
+ git_index **index_out,
+ git_oid_t oid_type);
+
+extern int git_index__open(
+ git_index **index_out,
+ const char *index_path,
+ git_oid_t oid_type);
+
/* Copy the current entries vector *and* increment the index refcount.
* Call `git_index__release_snapshot` when done.
*/
diff --git a/src/libgit2/indexer.c b/src/libgit2/indexer.c
index 62bb70393b8..e559a194235 100644
--- a/src/libgit2/indexer.c
+++ b/src/libgit2/indexer.c
@@ -42,6 +42,7 @@ struct git_indexer {
have_delta :1,
do_fsync :1,
do_verify :1;
+ git_oid_t oid_type;
struct git_pack_header hdr;
struct git_pack_file *pack;
unsigned int mode;
@@ -55,8 +56,8 @@ struct git_indexer {
git_vector deltas;
unsigned int fanout[256];
git_hash_ctx hash_ctx;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
- char name[(GIT_HASH_SHA1_SIZE * 2) + 1];
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ char name[(GIT_HASH_MAX_SIZE * 2) + 1];
git_indexer_progress_cb progress_cb;
void *progress_payload;
char objbuf[8*1024];
@@ -68,7 +69,7 @@ struct git_indexer {
git_odb *odb;
/* Fields for calculating the packfile trailer (hash of everything before it) */
- char inbuf[GIT_OID_SHA1_SIZE];
+ char inbuf[GIT_HASH_MAX_SIZE];
size_t inbuf_len;
git_hash_ctx trailer;
};
@@ -136,17 +137,33 @@ int git_indexer_init_options(git_indexer_options *opts, unsigned int version)
}
#endif
-int git_indexer_new(
- git_indexer **out,
- const char *prefix,
- unsigned int mode,
- git_odb *odb,
- git_indexer_options *in_opts)
+GIT_INLINE(git_hash_algorithm_t) indexer_hash_algorithm(git_indexer *idx)
+{
+ switch (idx->oid_type) {
+ case GIT_OID_SHA1:
+ return GIT_HASH_ALGORITHM_SHA1;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ return GIT_HASH_ALGORITHM_SHA256;
+#endif
+ }
+
+ return GIT_HASH_ALGORITHM_NONE;
+}
+
+static int indexer_new(
+ git_indexer **out,
+ const char *prefix,
+ git_oid_t oid_type,
+ unsigned int mode,
+ git_odb *odb,
+ git_indexer_options *in_opts)
{
git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
git_indexer *idx;
git_str path = GIT_STR_INIT, tmp_path = GIT_STR_INIT;
static const char suff[] = "/pack";
+ git_hash_algorithm_t checksum_type;
int error, fd = -1;
if (in_opts)
@@ -154,14 +171,17 @@ int git_indexer_new(
idx = git__calloc(1, sizeof(git_indexer));
GIT_ERROR_CHECK_ALLOC(idx);
+ idx->oid_type = oid_type;
idx->odb = odb;
idx->progress_cb = opts.progress_cb;
idx->progress_payload = opts.progress_cb_payload;
idx->mode = mode ? mode : GIT_PACK_FILE_MODE;
git_str_init(&idx->entry_data, 0);
- if ((error = git_hash_ctx_init(&idx->hash_ctx, GIT_HASH_ALGORITHM_SHA1)) < 0 ||
- (error = git_hash_ctx_init(&idx->trailer, GIT_HASH_ALGORITHM_SHA1)) < 0 ||
+ checksum_type = indexer_hash_algorithm(idx);
+
+ if ((error = git_hash_ctx_init(&idx->hash_ctx, checksum_type)) < 0 ||
+ (error = git_hash_ctx_init(&idx->trailer, checksum_type)) < 0 ||
(error = git_oidmap_new(&idx->expected_oids)) < 0)
goto cleanup;
@@ -179,7 +199,7 @@ int git_indexer_new(
if (fd < 0)
goto cleanup;
- error = git_packfile_alloc(&idx->pack, git_str_cstr(&tmp_path));
+ error = git_packfile_alloc(&idx->pack, git_str_cstr(&tmp_path), oid_type);
git_str_dispose(&tmp_path);
if (error < 0)
@@ -208,6 +228,33 @@ int git_indexer_new(
return -1;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_indexer_new(
+ git_indexer **out,
+ const char *prefix,
+ git_oid_t oid_type,
+ git_indexer_options *opts)
+{
+ return indexer_new(
+ out,
+ prefix,
+ oid_type,
+ opts ? opts->mode : 0,
+ opts ? opts->odb : NULL,
+ opts);
+}
+#else
+int git_indexer_new(
+ git_indexer **out,
+ const char *prefix,
+ unsigned int mode,
+ git_odb *odb,
+ git_indexer_options *opts)
+{
+ return indexer_new(out, prefix, GIT_OID_SHA1, mode, odb, opts);
+}
+#endif
+
void git_indexer__set_fsync(git_indexer *idx, int do_fsync)
{
idx->do_fsync = !!do_fsync;
@@ -272,7 +319,7 @@ static int advance_delta_offset(git_indexer *idx, git_object_t type)
GIT_ASSERT_ARG(type == GIT_OBJECT_REF_DELTA || type == GIT_OBJECT_OFS_DELTA);
if (type == GIT_OBJECT_REF_DELTA) {
- idx->off += GIT_OID_SHA1_SIZE;
+ idx->off += git_oid_size(idx->oid_type);
} else {
off64_t base_off;
int error = get_delta_base(&base_off, idx->pack, &w, &idx->off, type, idx->entry_start);
@@ -356,7 +403,7 @@ static int check_object_connectivity(git_indexer *idx, const git_rawobj *obj)
obj->type != GIT_OBJECT_TAG)
return 0;
- if (git_object__from_raw(&object, obj->data, obj->len, obj->type) < 0) {
+ if (git_object__from_raw(&object, obj->data, obj->len, obj->type, idx->oid_type) < 0) {
/*
* parse_raw returns EINVALID on invalid data; downgrade
* that to a normal -1 error code.
@@ -446,7 +493,7 @@ static int store_object(git_indexer *idx)
}
#ifdef GIT_EXPERIMENTAL_SHA256
- oid.type = GIT_OID_SHA1;
+ oid.type = idx->oid_type;
#endif
entry_size = idx->off - entry_start;
@@ -468,16 +515,22 @@ static int store_object(git_indexer *idx)
goto on_error;
}
- git_oid_cpy(&pentry->sha1, &oid);
+ git_oid_cpy(&pentry->id, &oid);
pentry->offset = entry_start;
- if (git_oidmap_exists(idx->pack->idx_cache, &pentry->sha1)) {
- git_error_set(GIT_ERROR_INDEXER, "duplicate object %s found in pack", git_oid_tostr_s(&pentry->sha1));
+ if (git_oidmap_exists(idx->pack->idx_cache, &pentry->id)) {
+ const char *idstr = git_oid_tostr_s(&pentry->id);
+
+ if (!idstr)
+ git_error_set(GIT_ERROR_INDEXER, "failed to parse object id");
+ else
+ git_error_set(GIT_ERROR_INDEXER, "duplicate object %s found in pack", idstr);
+
git__free(pentry);
goto on_error;
}
- if ((error = git_oidmap_set(idx->pack->idx_cache, &pentry->sha1, pentry)) < 0) {
+ if ((error = git_oidmap_set(idx->pack->idx_cache, &pentry->id, pentry)) < 0) {
git__free(pentry);
git_error_set_oom();
goto on_error;
@@ -522,8 +575,8 @@ static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_ent
pentry->offset = entry_start;
- if (git_oidmap_exists(idx->pack->idx_cache, &pentry->sha1) ||
- git_oidmap_set(idx->pack->idx_cache, &pentry->sha1, pentry) < 0) {
+ if (git_oidmap_exists(idx->pack->idx_cache, &pentry->id) ||
+ git_oidmap_set(idx->pack->idx_cache, &pentry->id, pentry) < 0) {
git_error_set(GIT_ERROR_INDEXER, "cannot insert object into pack");
return -1;
}
@@ -549,7 +602,7 @@ static int hash_and_save(git_indexer *idx, git_rawobj *obj, off64_t entry_start)
entry = git__calloc(1, sizeof(*entry));
GIT_ERROR_CHECK_ALLOC(entry);
- if (git_odb__hashobj(&oid, obj, GIT_OID_SHA1) < 0) {
+ if (git_odb__hashobj(&oid, obj, idx->oid_type) < 0) {
git_error_set(GIT_ERROR_INDEXER, "failed to hash object");
goto on_error;
}
@@ -557,7 +610,7 @@ static int hash_and_save(git_indexer *idx, git_rawobj *obj, off64_t entry_start)
pentry = git__calloc(1, sizeof(struct git_pack_entry));
GIT_ERROR_CHECK_ALLOC(pentry);
- git_oid_cpy(&pentry->sha1, &oid);
+ git_oid_cpy(&pentry->id, &oid);
git_oid_cpy(&entry->oid, &oid);
entry->crc = crc32(0L, Z_NULL, 0);
@@ -583,34 +636,38 @@ static int do_progress_callback(git_indexer *idx, git_indexer_progress *stats)
return 0;
}
-/* Hash everything but the last 20B of input */
+/* Hash everything but the checksum trailer */
static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
{
size_t to_expell, to_keep;
+ size_t oid_size = git_oid_size(idx->oid_type);
if (size == 0)
return;
- /* Easy case, dump the buffer and the data minus the last 20 bytes */
- if (size >= GIT_OID_SHA1_SIZE) {
+ /*
+ * Easy case, dump the buffer and the data minus the trailing
+ * checksum (SHA1 or SHA256).
+ */
+ if (size >= oid_size) {
git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
- git_hash_update(&idx->trailer, data, size - GIT_OID_SHA1_SIZE);
+ git_hash_update(&idx->trailer, data, size - oid_size);
- data += size - GIT_OID_SHA1_SIZE;
- memcpy(idx->inbuf, data, GIT_OID_SHA1_SIZE);
- idx->inbuf_len = GIT_OID_SHA1_SIZE;
+ data += size - oid_size;
+ memcpy(idx->inbuf, data, oid_size);
+ idx->inbuf_len = oid_size;
return;
}
/* We can just append */
- if (idx->inbuf_len + size <= GIT_OID_SHA1_SIZE) {
+ if (idx->inbuf_len + size <= oid_size) {
memcpy(idx->inbuf + idx->inbuf_len, data, size);
idx->inbuf_len += size;
return;
}
/* We need to partially drain the buffer and then append */
- to_keep = GIT_OID_SHA1_SIZE - size;
+ to_keep = oid_size - size;
to_expell = idx->inbuf_len - to_keep;
git_hash_update(&idx->trailer, idx->inbuf, to_expell);
@@ -729,12 +786,14 @@ static int read_stream_object(git_indexer *idx, git_indexer_progress *stats)
{
git_packfile_stream *stream = &idx->stream;
off64_t entry_start = idx->off;
- size_t entry_size;
+ size_t oid_size, entry_size;
git_object_t type;
git_mwindow *w = NULL;
int error;
- if (idx->pack->mwf.size <= idx->off + 20)
+ oid_size = git_oid_size(idx->oid_type);
+
+ if (idx->pack->mwf.size <= idx->off + (long long)oid_size)
return GIT_EBUFS;
if (!idx->have_stream) {
@@ -905,7 +964,7 @@ static int index_path(git_str *path, git_indexer *idx, const char *suffix)
slash--;
if (git_str_grow(path, slash + 1 + strlen(prefix) +
- GIT_OID_SHA1_HEXSIZE + strlen(suffix) + 1) < 0)
+ git_oid_hexsize(idx->oid_type) + strlen(suffix) + 1) < 0)
return -1;
git_str_truncate(path, slash);
@@ -922,7 +981,7 @@ static int index_path(git_str *path, git_indexer *idx, const char *suffix)
*/
static int seek_back_trailer(git_indexer *idx)
{
- idx->pack->mwf.size -= GIT_OID_SHA1_SIZE;
+ idx->pack->mwf.size -= git_oid_size(idx->oid_type);
return git_mwindow_free_all(&idx->pack->mwf);
}
@@ -931,15 +990,17 @@ static int inject_object(git_indexer *idx, git_oid *id)
git_odb_object *obj = NULL;
struct entry *entry = NULL;
struct git_pack_entry *pentry = NULL;
- unsigned char empty_checksum[GIT_HASH_SHA1_SIZE] = {0};
+ unsigned char empty_checksum[GIT_HASH_MAX_SIZE] = {0};
unsigned char hdr[64];
git_str buf = GIT_STR_INIT;
off64_t entry_start;
const void *data;
size_t len, hdr_len;
- size_t checksum_size = GIT_HASH_SHA1_SIZE;
+ size_t checksum_size;
int error;
+ checksum_size = git_hash_size(indexer_hash_algorithm(idx));
+
if ((error = seek_back_trailer(idx)) < 0)
goto cleanup;
@@ -982,12 +1043,12 @@ static int inject_object(git_indexer *idx, git_oid *id)
if ((error = append_to_pack(idx, empty_checksum, checksum_size)) < 0)
goto cleanup;
- idx->pack->mwf.size += GIT_OID_SHA1_SIZE;
+ idx->pack->mwf.size += git_oid_size(idx->oid_type);
pentry = git__calloc(1, sizeof(struct git_pack_entry));
GIT_ERROR_CHECK_ALLOC(pentry);
- git_oid_cpy(&pentry->sha1, id);
+ git_oid_cpy(&pentry->id, id);
git_oid_cpy(&entry->oid, id);
idx->off = entry_start + hdr_len + len;
@@ -1045,13 +1106,13 @@ static int fix_thin_pack(git_indexer *idx, git_indexer_progress *stats)
}
/* curpos now points to the base information, which is an OID */
- base_info = git_mwindow_open(&idx->pack->mwf, &w, curpos, GIT_OID_SHA1_SIZE, &left);
+ base_info = git_mwindow_open(&idx->pack->mwf, &w, curpos, git_oid_size(idx->oid_type), &left);
if (base_info == NULL) {
git_error_set(GIT_ERROR_INDEXER, "failed to map delta information");
return -1;
}
- git_oid__fromraw(&base, base_info, GIT_OID_SHA1);
+ git_oid__fromraw(&base, base_info, idx->oid_type);
git_mwindow_close(&w);
if (has_entry(idx, &base))
@@ -1173,10 +1234,11 @@ int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
struct git_pack_idx_header hdr;
git_str filename = GIT_STR_INIT;
struct entry *entry;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
git_filebuf index_file = {0};
void *packfile_trailer;
- size_t checksum_size = GIT_HASH_SHA1_SIZE;
+ size_t checksum_size;
+ int filebuf_hash;
bool mismatch;
if (!idx->parsed_header) {
@@ -1184,6 +1246,10 @@ int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
return -1;
}
+ checksum_size = git_hash_size(indexer_hash_algorithm(idx));
+ filebuf_hash = git_filebuf_hash_flags(indexer_hash_algorithm(idx));
+ GIT_ASSERT(checksum_size);
+
/* Test for this before resolve_deltas(), as it plays with idx->off */
if (idx->off + (ssize_t)checksum_size < idx->pack->mwf.size) {
git_error_set(GIT_ERROR_INDEXER, "unexpected data at the end of the pack");
@@ -1256,8 +1322,7 @@ int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
return -1;
if (git_filebuf_open(&index_file, filename.ptr,
- GIT_FILEBUF_HASH_CONTENTS |
- (idx->do_fsync ? GIT_FILEBUF_FSYNC : 0),
+ filebuf_hash | (idx->do_fsync ? GIT_FILEBUF_FSYNC : 0),
idx->mode) < 0)
goto on_error;
@@ -1274,7 +1339,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.id, GIT_OID_SHA1_SIZE);
+ git_filebuf_write(&index_file, &entry->oid.id, git_oid_size(idx->oid_type));
}
/* Write out the CRC32 values */
diff --git a/src/libgit2/iterator.c b/src/libgit2/iterator.c
index 57206f2805e..09240c92ccf 100644
--- a/src/libgit2/iterator.c
+++ b/src/libgit2/iterator.c
@@ -31,6 +31,8 @@
static void iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
{
+ int (*vector_cmp)(const void *a, const void *b);
+
if (ignore_case)
iter->flags |= GIT_ITERATOR_IGNORE_CASE;
else
@@ -41,7 +43,9 @@ static void iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
iter->prefixcomp = ignore_case ? git__prefixcmp_icase : git__prefixcmp;
iter->entry_srch = ignore_case ? git_index_entry_isrch : git_index_entry_srch;
- git_vector_set_cmp(&iter->pathlist, (git_vector_cmp)iter->strcomp);
+ vector_cmp = ignore_case ? git__strcasecmp_cb : git__strcmp_cb;
+
+ git_vector_set_cmp(&iter->pathlist, vector_cmp);
}
static int iterator_range_init(
@@ -301,6 +305,7 @@ typedef enum {
static iterator_pathlist_search_t iterator_pathlist_search(
git_iterator *iter, const char *path, size_t path_len)
{
+ int (*vector_cmp)(const void *a, const void *b);
const char *p;
size_t idx;
int error;
@@ -310,8 +315,10 @@ static iterator_pathlist_search_t iterator_pathlist_search(
git_vector_sort(&iter->pathlist);
- error = git_vector_bsearch2(&idx, &iter->pathlist,
- (git_vector_cmp)iter->strcomp, path);
+ vector_cmp = (iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0 ?
+ git__strcasecmp_cb : git__strcmp_cb;
+
+ error = git_vector_bsearch2(&idx, &iter->pathlist, vector_cmp, path);
/* the given path was found in the pathlist. since the pathlist only
* matches directories when they're suffixed with a '/', analyze the
@@ -1092,6 +1099,8 @@ typedef struct {
git_index *index;
git_vector index_snapshot;
+ git_oid_t oid_type;
+
git_array_t(filesystem_iterator_frame) frames;
git_ignores ignores;
@@ -1330,7 +1339,7 @@ static int filesystem_iterator_entry_hash(
int error;
if (S_ISDIR(entry->st.st_mode)) {
- memset(&entry->id, 0, GIT_OID_SHA1_SIZE);
+ memset(&entry->id, 0, git_oid_size(iter->oid_type));
return 0;
}
@@ -1340,7 +1349,7 @@ static int filesystem_iterator_entry_hash(
if (!(error = git_str_joinpath(&fullpath, iter->root, entry->path)) &&
!(error = git_path_validate_str_length(iter->base.repo, &fullpath)))
- error = git_odb__hashfile(&entry->id, fullpath.ptr, GIT_OBJECT_BLOB, GIT_OID_SHA1);
+ error = git_odb__hashfile(&entry->id, fullpath.ptr, GIT_OBJECT_BLOB, iter->oid_type);
git_str_dispose(&fullpath);
return error;
@@ -1589,7 +1598,7 @@ static void filesystem_iterator_set_current(
if (iter->base.flags & GIT_ITERATOR_INCLUDE_HASH)
git_oid_cpy(&iter->entry.id, &entry->id);
else
- git_oid_clear(&iter->entry.id, GIT_OID_SHA1);
+ git_oid_clear(&iter->entry.id, iter->oid_type);
iter->entry.path = entry->path;
@@ -2127,6 +2136,8 @@ static int iterator_for_filesystem(
(iterator__flag(&iter->base, PRECOMPOSE_UNICODE) ?
GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE : 0);
+ iter->oid_type = options->oid_type;
+
if ((error = filesystem_iterator_init(iter)) < 0)
goto on_error;
@@ -2141,10 +2152,15 @@ static int iterator_for_filesystem(
int git_iterator_for_filesystem(
git_iterator **out,
const char *root,
- git_iterator_options *options)
+ git_iterator_options *given_opts)
{
+ git_iterator_options options = GIT_ITERATOR_OPTIONS_INIT;
+
+ if (given_opts)
+ memcpy(&options, given_opts, sizeof(git_iterator_options));
+
return iterator_for_filesystem(out,
- NULL, root, NULL, NULL, GIT_ITERATOR_FS, options);
+ NULL, root, NULL, NULL, GIT_ITERATOR_FS, &options);
}
int git_iterator_for_workdir_ext(
@@ -2178,6 +2194,12 @@ int git_iterator_for_workdir_ext(
if (sparse_checkout_enabled == true)
options.flags |= GIT_ITERATOR_HONOR_SPARSE;
+ if (!options.oid_type)
+ options.oid_type = repo->oid_type;
+ else if (options.oid_type != repo->oid_type)
+ git_error_set(GIT_ERROR_INVALID,
+ "specified object ID type does not match repository object ID type");
+
return iterator_for_filesystem(out,
repo, repo_workdir, index, tree, GIT_ITERATOR_WORKDIR, &options);
}
diff --git a/src/libgit2/iterator.h b/src/libgit2/iterator.h
index 493e28f45ac..43b48b6f98d 100644
--- a/src/libgit2/iterator.h
+++ b/src/libgit2/iterator.h
@@ -63,6 +63,9 @@ typedef struct {
/* flags, from above */
unsigned int flags;
+
+ /* oid type - necessary for non-workdir filesystem iterators */
+ git_oid_t oid_type;
} git_iterator_options;
#define GIT_ITERATOR_OPTIONS_INIT {0}
diff --git a/src/libgit2/libgit2.c b/src/libgit2/libgit2.c
index 2fda0722ece..1b6f1a1f846 100644
--- a/src/libgit2/libgit2.c
+++ b/src/libgit2/libgit2.c
@@ -5,63 +5,32 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "libgit2.h"
-
#include
#include "alloc.h"
#include "buf.h"
-#include "cache.h"
#include "common.h"
#include "filter.h"
#include "hash.h"
-#include "index.h"
#include "merge_driver.h"
#include "pool.h"
#include "mwindow.h"
-#include "object.h"
-#include "odb.h"
+#include "oid.h"
#include "rand.h"
-#include "refs.h"
#include "runtime.h"
+#include "settings.h"
#include "sysdir.h"
#include "thread.h"
-#include "threadstate.h"
#include "git2/global.h"
#include "streams/registry.h"
#include "streams/mbedtls.h"
#include "streams/openssl.h"
-#include "transports/smart.h"
-#include "transports/http.h"
-#include "transports/ssh.h"
+#include "streams/socket.h"
+#include "transports/ssh_libssh2.h"
#ifdef GIT_WIN32
# include "win32/w32_leakcheck.h"
#endif
-/* Declarations for tuneable settings */
-extern size_t git_mwindow__window_size;
-extern size_t git_mwindow__mapped_limit;
-extern size_t git_mwindow__file_limit;
-extern size_t git_indexer__max_objects;
-extern bool git_disable_pack_keep_file_checks;
-extern int git_odb__packed_priority;
-extern int git_odb__loose_priority;
-
-char *git__user_agent;
-char *git__ssl_ciphers;
-
-static void libgit2_settings_global_shutdown(void)
-{
- git__free(git__user_agent);
- git__free(git__ssl_ciphers);
- git_repository__free_extensions();
-}
-
-static int git_libgit2_settings_global_init(void)
-{
- return git_runtime_shutdown_register(libgit2_settings_global_shutdown);
-}
-
int git_libgit2_init(void)
{
static git_runtime_init_fn init_fns[] = {
@@ -69,30 +38,27 @@ int git_libgit2_init(void)
git_win32_leakcheck_global_init,
#endif
git_allocator_global_init,
- git_threadstate_global_init,
+ git_error_global_init,
git_threads_global_init,
+ git_oid_global_init,
git_rand_global_init,
git_hash_global_init,
git_sysdir_global_init,
git_filter_global_init,
git_merge_driver_global_init,
- git_transport_ssh_global_init,
+ git_transport_ssh_libssh2_global_init,
git_stream_registry_global_init,
+ git_socket_stream_global_init,
git_openssl_stream_global_init,
git_mbedtls_stream_global_init,
git_mwindow_global_init,
git_pool_global_init,
- git_libgit2_settings_global_init
+ git_settings_global_init
};
return git_runtime_init(init_fns, ARRAY_SIZE(init_fns));
}
-int git_libgit2_init_count(void)
-{
- return git_runtime_init_count();
-}
-
int git_libgit2_shutdown(void)
{
return git_runtime_shutdown();
@@ -121,305 +87,11 @@ int git_libgit2_features(void)
#ifdef GIT_HTTPS
| GIT_FEATURE_HTTPS
#endif
-#if defined(GIT_SSH)
+#ifdef GIT_SSH
| GIT_FEATURE_SSH
#endif
-#if defined(GIT_USE_NSEC)
+#ifdef GIT_USE_NSEC
| GIT_FEATURE_NSEC
#endif
;
}
-
-static int config_level_to_sysdir(int *out, int config_level)
-{
- switch (config_level) {
- case GIT_CONFIG_LEVEL_SYSTEM:
- *out = GIT_SYSDIR_SYSTEM;
- return 0;
- case GIT_CONFIG_LEVEL_XDG:
- *out = GIT_SYSDIR_XDG;
- return 0;
- case GIT_CONFIG_LEVEL_GLOBAL:
- *out = GIT_SYSDIR_GLOBAL;
- return 0;
- case GIT_CONFIG_LEVEL_PROGRAMDATA:
- *out = GIT_SYSDIR_PROGRAMDATA;
- return 0;
- default:
- break;
- }
-
- git_error_set(
- GIT_ERROR_INVALID, "invalid config path selector %d", config_level);
- return -1;
-}
-
-const char *git_libgit2__user_agent(void)
-{
- return git__user_agent;
-}
-
-const char *git_libgit2__ssl_ciphers(void)
-{
- return git__ssl_ciphers;
-}
-
-int git_libgit2_opts(int key, ...)
-{
- int error = 0;
- va_list ap;
-
- va_start(ap, key);
-
- switch (key) {
- case GIT_OPT_SET_MWINDOW_SIZE:
- git_mwindow__window_size = va_arg(ap, size_t);
- break;
-
- case GIT_OPT_GET_MWINDOW_SIZE:
- *(va_arg(ap, size_t *)) = git_mwindow__window_size;
- break;
-
- case GIT_OPT_SET_MWINDOW_MAPPED_LIMIT:
- git_mwindow__mapped_limit = va_arg(ap, size_t);
- break;
-
- case GIT_OPT_GET_MWINDOW_MAPPED_LIMIT:
- *(va_arg(ap, size_t *)) = git_mwindow__mapped_limit;
- break;
-
- case GIT_OPT_SET_MWINDOW_FILE_LIMIT:
- git_mwindow__file_limit = va_arg(ap, size_t);
- break;
-
- case GIT_OPT_GET_MWINDOW_FILE_LIMIT:
- *(va_arg(ap, size_t *)) = git_mwindow__file_limit;
- break;
-
- case GIT_OPT_GET_SEARCH_PATH:
- {
- int sysdir = va_arg(ap, int);
- git_buf *out = va_arg(ap, git_buf *);
- git_str str = GIT_STR_INIT;
- const git_str *tmp;
- int level;
-
- if ((error = git_buf_tostr(&str, out)) < 0 ||
- (error = config_level_to_sysdir(&level, sysdir)) < 0 ||
- (error = git_sysdir_get(&tmp, level)) < 0 ||
- (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
- break;
-
- error = git_buf_fromstr(out, &str);
- }
- break;
-
- case GIT_OPT_SET_SEARCH_PATH:
- {
- int level;
-
- if ((error = config_level_to_sysdir(&level, va_arg(ap, int))) >= 0)
- error = git_sysdir_set(level, va_arg(ap, const char *));
- }
- break;
-
- case GIT_OPT_SET_CACHE_OBJECT_LIMIT:
- {
- git_object_t type = (git_object_t)va_arg(ap, int);
- size_t size = va_arg(ap, size_t);
- error = git_cache_set_max_object_size(type, size);
- break;
- }
-
- case GIT_OPT_SET_CACHE_MAX_SIZE:
- git_cache__max_storage = va_arg(ap, ssize_t);
- break;
-
- case GIT_OPT_ENABLE_CACHING:
- git_cache__enabled = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_GET_CACHED_MEMORY:
- *(va_arg(ap, ssize_t *)) = git_cache__current_storage.val;
- *(va_arg(ap, ssize_t *)) = git_cache__max_storage;
- break;
-
- case GIT_OPT_GET_TEMPLATE_PATH:
- {
- git_buf *out = va_arg(ap, git_buf *);
- git_str str = GIT_STR_INIT;
- const git_str *tmp;
-
- if ((error = git_buf_tostr(&str, out)) < 0 ||
- (error = git_sysdir_get(&tmp, GIT_SYSDIR_TEMPLATE)) < 0 ||
- (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
- break;
-
- error = git_buf_fromstr(out, &str);
- }
- break;
-
- case GIT_OPT_SET_TEMPLATE_PATH:
- error = git_sysdir_set(GIT_SYSDIR_TEMPLATE, va_arg(ap, const char *));
- break;
-
- case GIT_OPT_SET_SSL_CERT_LOCATIONS:
-#ifdef GIT_OPENSSL
- {
- const char *file = va_arg(ap, const char *);
- const char *path = va_arg(ap, const char *);
- error = git_openssl__set_cert_location(file, path);
- }
-#elif defined(GIT_MBEDTLS)
- {
- const char *file = va_arg(ap, const char *);
- const char *path = va_arg(ap, const char *);
- error = git_mbedtls__set_cert_location(file, path);
- }
-#else
- git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support certificate locations");
- error = -1;
-#endif
- break;
- case GIT_OPT_SET_USER_AGENT:
- git__free(git__user_agent);
- git__user_agent = git__strdup(va_arg(ap, const char *));
- if (!git__user_agent) {
- git_error_set_oom();
- error = -1;
- }
-
- break;
-
- case GIT_OPT_ENABLE_STRICT_OBJECT_CREATION:
- git_object__strict_input_validation = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION:
- git_reference__enable_symbolic_ref_target_validation = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_SET_SSL_CIPHERS:
-#if (GIT_OPENSSL || GIT_MBEDTLS)
- {
- git__free(git__ssl_ciphers);
- git__ssl_ciphers = git__strdup(va_arg(ap, const char *));
- if (!git__ssl_ciphers) {
- git_error_set_oom();
- error = -1;
- }
- }
-#else
- git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support custom ciphers");
- error = -1;
-#endif
- break;
-
- case GIT_OPT_GET_USER_AGENT:
- {
- git_buf *out = va_arg(ap, git_buf *);
- git_str str = GIT_STR_INIT;
-
- if ((error = git_buf_tostr(&str, out)) < 0 ||
- (error = git_str_puts(&str, git__user_agent)) < 0)
- break;
-
- error = git_buf_fromstr(out, &str);
- }
- break;
-
- case GIT_OPT_ENABLE_OFS_DELTA:
- git_smart__ofs_delta_enabled = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_ENABLE_FSYNC_GITDIR:
- git_repository__fsync_gitdir = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_GET_WINDOWS_SHAREMODE:
-#ifdef GIT_WIN32
- *(va_arg(ap, unsigned long *)) = git_win32__createfile_sharemode;
-#endif
- break;
-
- case GIT_OPT_SET_WINDOWS_SHAREMODE:
-#ifdef GIT_WIN32
- git_win32__createfile_sharemode = va_arg(ap, unsigned long);
-#endif
- break;
-
- case GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION:
- git_odb__strict_hash_verification = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_SET_ALLOCATOR:
- error = git_allocator_setup(va_arg(ap, git_allocator *));
- break;
-
- case GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY:
- git_index__enforce_unsaved_safety = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_SET_PACK_MAX_OBJECTS:
- git_indexer__max_objects = va_arg(ap, size_t);
- break;
-
- case GIT_OPT_GET_PACK_MAX_OBJECTS:
- *(va_arg(ap, size_t *)) = git_indexer__max_objects;
- break;
-
- case GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS:
- git_disable_pack_keep_file_checks = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE:
- git_http__expect_continue = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_SET_ODB_PACKED_PRIORITY:
- git_odb__packed_priority = va_arg(ap, int);
- break;
-
- case GIT_OPT_SET_ODB_LOOSE_PRIORITY:
- git_odb__loose_priority = va_arg(ap, int);
- break;
-
- case GIT_OPT_SET_EXTENSIONS:
- {
- const char **extensions = va_arg(ap, const char **);
- size_t len = va_arg(ap, size_t);
- error = git_repository__set_extensions(extensions, len);
- }
- break;
-
- case GIT_OPT_GET_EXTENSIONS:
- {
- git_strarray *out = va_arg(ap, git_strarray *);
- char **extensions;
- size_t len;
-
- if ((error = git_repository__extensions(&extensions, &len)) < 0)
- break;
-
- out->strings = extensions;
- out->count = len;
- }
- break;
-
- case GIT_OPT_GET_OWNER_VALIDATION:
- *(va_arg(ap, int *)) = git_repository__validate_ownership;
- break;
-
- case GIT_OPT_SET_OWNER_VALIDATION:
- git_repository__validate_ownership = (va_arg(ap, int) != 0);
- break;
-
- default:
- git_error_set(GIT_ERROR_INVALID, "invalid option key");
- error = -1;
- }
-
- va_end(ap);
-
- return error;
-}
diff --git a/src/libgit2/libgit2.h b/src/libgit2/libgit2.h
deleted file mode 100644
index a898367ae37..00000000000
--- a/src/libgit2/libgit2.h
+++ /dev/null
@@ -1,15 +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_libgit2_h__
-#define INCLUDE_libgit2_h__
-
-extern int git_libgit2_init_count(void);
-
-extern const char *git_libgit2__user_agent(void);
-extern const char *git_libgit2__ssl_ciphers(void);
-
-#endif
diff --git a/src/libgit2/merge.c b/src/libgit2/merge.c
index df2cefb2930..21e5ef6a8f9 100644
--- a/src/libgit2/merge.c
+++ b/src/libgit2/merge.c
@@ -611,13 +611,13 @@ int git_repository_mergehead_foreach(
buffer = merge_head_file.ptr;
while ((line = git__strsep(&buffer, "\n")) != NULL) {
- if (strlen(line) != GIT_OID_SHA1_HEXSIZE) {
+ if (strlen(line) != git_oid_hexsize(repo->oid_type)) {
git_error_set(GIT_ERROR_INVALID, "unable to parse OID - invalid length");
error = -1;
goto cleanup;
}
- if ((error = git_oid__fromstr(&oid, line, GIT_OID_SHA1)) < 0)
+ if ((error = git_oid__fromstr(&oid, line, repo->oid_type)) < 0)
goto cleanup;
if ((error = cb(&oid, payload)) != 0) {
@@ -1061,7 +1061,7 @@ static int index_entry_similarity_calc(
const git_merge_options *opts)
{
git_blob *blob;
- git_diff_file diff_file = { GIT_OID_SHA1_ZERO };
+ git_diff_file diff_file;
git_object_size_t blobsize;
int error;
@@ -1070,6 +1070,8 @@ static int index_entry_similarity_calc(
*out = NULL;
+ git_oid_clear(&diff_file.id, repo->oid_type);
+
if ((error = git_blob_lookup(&blob, repo, &entry->id)) < 0)
return error;
@@ -1223,6 +1225,13 @@ static int merge_diff_mark_similarity_exact(
if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry))
continue;
+ /*
+ * Ignore empty files because it has always the same blob sha1
+ * and will lead to incorrect matches between all entries.
+ */
+ if (git_oid_equal(&conflict_src->ancestor_entry.id, &git_oid__empty_blob_sha1))
+ continue;
+
if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) {
error = deletes_by_oid_enqueue(ours_deletes_by_oid, &diff_list->pool, &conflict_src->ancestor_entry.id, i);
if (error < 0)
@@ -1997,8 +2006,11 @@ static int index_update_reuc(git_index *index, git_merge_diff_list *diff_list)
return 0;
}
-static int index_from_diff_list(git_index **out,
- git_merge_diff_list *diff_list, bool skip_reuc)
+static int index_from_diff_list(
+ git_index **out,
+ git_merge_diff_list *diff_list,
+ git_oid_t oid_type,
+ bool skip_reuc)
{
git_index *index;
size_t i;
@@ -2007,7 +2019,7 @@ static int index_from_diff_list(git_index **out,
*out = NULL;
- if ((error = git_index_new(&index)) < 0)
+ if ((error = git_index__new(&index, oid_type)) < 0)
return error;
if ((error = git_index__fill(index, &diff_list->staged)) < 0)
@@ -2157,7 +2169,7 @@ int git_merge__iterators(
}
}
- error = index_from_diff_list(out, diff_list,
+ error = index_from_diff_list(out, diff_list, repo->oid_type,
(opts.flags & GIT_MERGE_SKIP_REUC));
done:
@@ -2200,8 +2212,8 @@ int git_merge_trees(
result = our_tree;
if (result) {
- if ((error = git_index_new(out)) == 0)
- error = git_index_read_tree(*out, result);
+ if ((error = git_index__new(out, repo->oid_type)) == 0)
+ error = git_index_read_tree(*out, result);
return error;
}
diff --git a/src/libgit2/merge_file.c b/src/libgit2/merge_file.c
index 732a047b1c9..ffe83cf2a3b 100644
--- a/src/libgit2/merge_file.c
+++ b/src/libgit2/merge_file.c
@@ -19,8 +19,6 @@
#include "git2/index.h"
#include "git2/merge.h"
-#include "xdiff/xdiff.h"
-
/* only examine the first 8000 bytes for binaryness.
* https://github.com/git/git/blob/77bd3ea9f54f1584147b594abc04c26ca516d987/xdiff-interface.c#L197
*/
diff --git a/src/libgit2/midx.c b/src/libgit2/midx.c
index 51b2d6cc7ae..71bbb1d0eaf 100644
--- a/src/libgit2/midx.c
+++ b/src/libgit2/midx.c
@@ -114,23 +114,16 @@ static int midx_parse_oid_lookup(
const unsigned char *data,
struct git_midx_chunk *chunk_oid_lookup)
{
- uint32_t i;
- unsigned char *oid, *prev_oid, zero_oid[GIT_OID_SHA1_SIZE] = {0};
+ size_t oid_size = git_oid_size(idx->oid_type);
if (chunk_oid_lookup->offset == 0)
return midx_error("missing OID Lookup chunk");
if (chunk_oid_lookup->length == 0)
return midx_error("empty OID Lookup chunk");
- if (chunk_oid_lookup->length != idx->num_objects * GIT_OID_SHA1_SIZE)
+ if (chunk_oid_lookup->length != idx->num_objects * oid_size)
return midx_error("OID Lookup chunk has wrong length");
- 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_SHA1_SIZE) {
- if (git_oid_raw_cmp(prev_oid, oid, GIT_OID_SHA1_SIZE) >= 0)
- return midx_error("OID Lookup index is non-monotonic");
- prev_oid = oid;
- }
+ idx->oid_lookup = (unsigned char *)(data + chunk_oid_lookup->offset);
return 0;
}
@@ -178,17 +171,20 @@ int git_midx_parse(
struct git_midx_chunk *last_chunk;
uint32_t i;
off64_t last_chunk_offset, chunk_offset, trailer_offset;
- size_t checksum_size;
+ size_t checksum_size, oid_size;
int error;
struct git_midx_chunk chunk_packfile_names = {0},
chunk_oid_fanout = {0},
chunk_oid_lookup = {0},
chunk_object_offsets = {0},
- chunk_object_large_offsets = {0};
+ chunk_object_large_offsets = {0},
+ chunk_unknown = {0};
GIT_ASSERT_ARG(idx);
- if (size < sizeof(struct git_midx_header) + GIT_OID_SHA1_SIZE)
+ oid_size = git_oid_size(idx->oid_type);
+
+ if (size < sizeof(struct git_midx_header) + oid_size)
return midx_error("multi-pack index is too short");
hdr = ((struct git_midx_header *)data);
@@ -209,7 +205,7 @@ int git_midx_parse(
sizeof(struct git_midx_header) +
(1 + hdr->chunks) * 12;
- checksum_size = GIT_HASH_SHA1_SIZE;
+ checksum_size = oid_size;
trailer_offset = size - checksum_size;
if (trailer_offset < last_chunk_offset)
@@ -261,7 +257,9 @@ int git_midx_parse(
break;
default:
- return midx_error("unrecognized chunk ID");
+ chunk_unknown.offset = last_chunk_offset;
+ last_chunk = &chunk_unknown;
+ break;
}
}
last_chunk->length = (size_t)(trailer_offset - last_chunk_offset);
@@ -287,8 +285,9 @@ int git_midx_parse(
}
int git_midx_open(
- git_midx_file **idx_out,
- const char *path)
+ git_midx_file **idx_out,
+ const char *path,
+ git_oid_t oid_type)
{
git_midx_file *idx;
git_file fd = -1;
@@ -296,6 +295,8 @@ int git_midx_open(
struct stat st;
int error;
+ GIT_ASSERT_ARG(idx_out && path && oid_type);
+
/* TODO: properly open the file without access time using O_NOATIME */
fd = git_futils_open_ro(path);
if (fd < 0)
@@ -317,6 +318,8 @@ int git_midx_open(
idx = git__calloc(1, sizeof(git_midx_file));
GIT_ERROR_CHECK_ALLOC(idx);
+ idx->oid_type = oid_type;
+
error = git_str_sets(&idx->filename, path);
if (error < 0)
return error;
@@ -344,7 +347,7 @@ bool git_midx_needs_refresh(
git_file fd = -1;
struct stat st;
ssize_t bytes_read;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
size_t checksum_size;
/* TODO: properly open the file without access time using O_NOATIME */
@@ -364,8 +367,8 @@ bool git_midx_needs_refresh(
return true;
}
- checksum_size = GIT_HASH_SHA1_SIZE;
- bytes_read = p_pread(fd, checksum, checksum_size, st.st_size - GIT_OID_SHA1_SIZE);
+ checksum_size = git_oid_size(idx->oid_type);
+ bytes_read = p_pread(fd, checksum, checksum_size, st.st_size - checksum_size);
p_close(fd);
if (bytes_read != (ssize_t)checksum_size)
@@ -381,7 +384,7 @@ int git_midx_entry_find(
size_t len)
{
int pos, found = 0;
- size_t pack_index;
+ size_t pack_index, oid_size, oid_hexsize;
uint32_t hi, lo;
unsigned char *current = NULL;
const unsigned char *object_offset;
@@ -389,30 +392,33 @@ int git_midx_entry_find(
GIT_ASSERT_ARG(idx);
+ oid_size = git_oid_size(idx->oid_type);
+ oid_hexsize = git_oid_hexsize(idx->oid_type);
+
hi = ntohl(idx->oid_fanout[(int)short_oid->id[0]]);
lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(idx->oid_fanout[(int)short_oid->id[0] - 1]));
- pos = git_pack__lookup_sha1(idx->oid_lookup, GIT_OID_SHA1_SIZE, lo, hi, short_oid->id);
+ pos = git_pack__lookup_id(idx->oid_lookup, oid_size, lo, hi, short_oid->id, idx->oid_type);
if (pos >= 0) {
/* An object matching exactly the oid was found */
found = 1;
- current = idx->oid_lookup + (pos * GIT_OID_SHA1_SIZE);
+ current = idx->oid_lookup + (pos * oid_size);
} 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 * GIT_OID_SHA1_SIZE);
+ current = idx->oid_lookup + (pos * oid_size);
if (!git_oid_raw_ncmp(short_oid->id, current, len))
found = 1;
}
}
- if (found && len != GIT_OID_SHA1_HEXSIZE && pos + 1 < (int)idx->num_objects) {
+ if (found && len != oid_hexsize && pos + 1 < (int)idx->num_objects) {
/* Check for ambiguousity */
- const unsigned char *next = current + GIT_OID_SHA1_SIZE;
+ const unsigned char *next = current + oid_size;
if (!git_oid_raw_ncmp(short_oid->id, next, len))
found = 2;
@@ -443,7 +449,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__fromraw(&e->sha1, current, GIT_OID_SHA1);
+ git_oid__fromraw(&e->sha1, current, idx->oid_type);
return 0;
}
@@ -453,13 +459,15 @@ int git_midx_foreach_entry(
void *data)
{
git_oid oid;
- size_t i;
+ size_t oid_size, i;
int error;
GIT_ASSERT_ARG(idx);
+ oid_size = git_oid_size(idx->oid_type);
+
for (i = 0; i < idx->num_objects; ++i) {
- if ((error = git_oid__fromraw(&oid, &idx->oid_lookup[i * GIT_OID_SHA1_SIZE], GIT_OID_SHA1)) < 0)
+ if ((error = git_oid__fromraw(&oid, &idx->oid_lookup[i * oid_size], idx->oid_type)) < 0)
return error;
if ((error = cb(&oid, data)) != 0)
@@ -501,9 +509,21 @@ static int packfile__cmp(const void *a_, const void *b_)
int git_midx_writer_new(
git_midx_writer **out,
- const char *pack_dir)
+ const char *pack_dir
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ )
{
- git_midx_writer *w = git__calloc(1, sizeof(git_midx_writer));
+ git_midx_writer *w;
+
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_oid_t oid_type = GIT_OID_SHA1;
+#endif
+
+ GIT_ASSERT_ARG(out && pack_dir && oid_type);
+
+ w = git__calloc(1, sizeof(git_midx_writer));
GIT_ERROR_CHECK_ALLOC(w);
if (git_str_sets(&w->pack_dir, pack_dir) < 0) {
@@ -518,6 +538,8 @@ int git_midx_writer_new(
return -1;
}
+ w->oid_type = oid_type;
+
*out = w;
return 0;
}
@@ -549,7 +571,8 @@ int git_midx_writer_add(
if (error < 0)
return error;
- error = git_mwindow_get_pack(&p, git_str_cstr(&idx_path_buf));
+ /* TODO: SHA256 */
+ error = git_mwindow_get_pack(&p, git_str_cstr(&idx_path_buf), 0);
git_str_dispose(&idx_path_buf);
if (error < 0)
return error;
@@ -661,12 +684,13 @@ static int midx_write(
oid_lookup = GIT_STR_INIT,
object_offsets = GIT_STR_INIT,
object_large_offsets = GIT_STR_INIT;
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
- size_t checksum_size;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ size_t checksum_size, oid_size;
git_midx_entry *entry;
object_entry_array_t object_entries_array = GIT_ARRAY_INIT;
git_vector object_entries = GIT_VECTOR_INIT;
git_hash_ctx ctx;
+ git_hash_algorithm_t checksum_type;
struct midx_write_hash_context hash_cb_data = {0};
hdr.signature = htonl(MIDX_SIGNATURE);
@@ -678,10 +702,14 @@ static int midx_write(
hash_cb_data.cb_data = cb_data;
hash_cb_data.ctx = &ctx;
- checksum_size = GIT_HASH_SHA1_SIZE;
- error = git_hash_ctx_init(&ctx, GIT_HASH_ALGORITHM_SHA1);
- if (error < 0)
+ oid_size = git_oid_size(w->oid_type);
+ checksum_type = git_oid_algorithm(w->oid_type);
+ checksum_size = git_hash_size(checksum_type);
+ GIT_ASSERT(oid_size && checksum_type && checksum_size);
+
+ if ((error = git_hash_ctx_init(&ctx, checksum_type)) < 0)
return error;
+
cb_data = &hash_cb_data;
write_cb = midx_write_hash;
@@ -748,7 +776,9 @@ static int midx_write(
/* Fill the OID Lookup table. */
git_vector_foreach (&object_entries, i, entry) {
- error = git_str_put(&oid_lookup, (char *)&entry->sha1.id, GIT_OID_SHA1_SIZE);
+ error = git_str_put(&oid_lookup,
+ (char *)&entry->sha1.id, oid_size);
+
if (error < 0)
goto cleanup;
}
diff --git a/src/libgit2/midx.h b/src/libgit2/midx.h
index bcb0d9a0abd..5107f69cba3 100644
--- a/src/libgit2/midx.h
+++ b/src/libgit2/midx.h
@@ -51,8 +51,14 @@ typedef struct git_midx_file {
/* The number of entries in the Object Large Offsets table. Each entry has an 8-byte with an offset */
size_t num_object_large_offsets;
- /* The trailer of the file. Contains the SHA1-checksum of the whole file. */
- unsigned char checksum[GIT_HASH_SHA1_SIZE];
+ /*
+ * The trailer of the file. Contains the checksum of the whole
+ * file, in the repository's object format hash.
+ */
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+
+ /* The type of object IDs in the midx. */
+ git_oid_t oid_type;
/* something like ".git/objects/pack/multi-pack-index". */
git_str filename;
@@ -82,11 +88,15 @@ struct git_midx_writer {
/* The list of `git_pack_file`s. */
git_vector packs;
+
+ /* The object ID type of the writer. */
+ git_oid_t oid_type;
};
int git_midx_open(
git_midx_file **idx_out,
- const char *path);
+ const char *path,
+ git_oid_t oid_type);
bool git_midx_needs_refresh(
const git_midx_file *idx,
const char *path);
diff --git a/src/libgit2/mwindow.c b/src/libgit2/mwindow.c
index ad649490ad4..b8295d9d119 100644
--- a/src/libgit2/mwindow.c
+++ b/src/libgit2/mwindow.c
@@ -61,7 +61,10 @@ int git_mwindow_global_init(void)
return git_runtime_shutdown_register(git_mwindow_global_shutdown);
}
-int git_mwindow_get_pack(struct git_pack_file **out, const char *path)
+int git_mwindow_get_pack(
+ struct git_pack_file **out,
+ const char *path,
+ git_oid_t oid_type)
{
struct git_pack_file *pack;
char *packname;
@@ -86,7 +89,7 @@ int git_mwindow_get_pack(struct git_pack_file **out, const char *path)
}
/* If we didn't find it, we need to create it */
- if ((error = git_packfile_alloc(&pack, path)) < 0) {
+ if ((error = git_packfile_alloc(&pack, path, oid_type)) < 0) {
git_mutex_unlock(&git__mwindow_mutex);
return error;
}
diff --git a/src/libgit2/mwindow.h b/src/libgit2/mwindow.h
index e32ab99d4e9..8e6df2613b7 100644
--- a/src/libgit2/mwindow.h
+++ b/src/libgit2/mwindow.h
@@ -48,7 +48,10 @@ void git_mwindow_close(git_mwindow **w_cursor);
extern int git_mwindow_global_init(void);
struct git_pack_file; /* just declaration to avoid cyclical includes */
-int git_mwindow_get_pack(struct git_pack_file **out, const char *path);
+int git_mwindow_get_pack(
+ struct git_pack_file **out,
+ const char *path,
+ git_oid_t oid_type);
int git_mwindow_put_pack(struct git_pack_file *pack);
#endif
diff --git a/src/libgit2/netops.c b/src/libgit2/netops.c
deleted file mode 100644
index 00640c600ee..00000000000
--- a/src/libgit2/netops.c
+++ /dev/null
@@ -1,124 +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 "netops.h"
-
-#include
-#include "git2/errors.h"
-
-#include "posix.h"
-#include "str.h"
-#include "runtime.h"
-
-int gitno_recv(gitno_buffer *buf)
-{
- return buf->recv(buf);
-}
-
-void gitno_buffer_setup_callback(
- gitno_buffer *buf,
- char *data,
- size_t len,
- int (*recv)(gitno_buffer *buf), void *cb_data)
-{
- memset(data, 0x0, len);
- buf->data = data;
- buf->len = len;
- buf->offset = 0;
- buf->recv = recv;
- buf->cb_data = cb_data;
-}
-
-static int recv_stream(gitno_buffer *buf)
-{
- git_stream *io = (git_stream *) buf->cb_data;
- size_t readlen = buf->len - buf->offset;
- ssize_t ret;
-
- readlen = min(readlen, INT_MAX);
-
- ret = git_stream_read(io, buf->data + buf->offset, (int)readlen);
- if (ret < 0)
- return -1;
-
- buf->offset += ret;
- return (int)ret;
-}
-
-void gitno_buffer_setup_fromstream(git_stream *st, gitno_buffer *buf, char *data, size_t len)
-{
- memset(data, 0x0, len);
- buf->data = data;
- buf->len = len;
- buf->offset = 0;
- buf->recv = recv_stream;
- buf->cb_data = st;
-}
-
-/* Consume up to ptr and move the rest of the buffer to the beginning */
-int gitno_consume(gitno_buffer *buf, const char *ptr)
-{
- size_t consumed;
-
- GIT_ASSERT(ptr - buf->data >= 0);
- GIT_ASSERT(ptr - buf->data <= (int) buf->len);
-
- consumed = ptr - buf->data;
-
- memmove(buf->data, ptr, buf->offset - consumed);
- memset(buf->data + buf->offset, 0x0, buf->len - buf->offset);
- buf->offset -= consumed;
-
- return 0;
-}
-
-/* Consume const bytes and move the rest of the buffer to the beginning */
-void gitno_consume_n(gitno_buffer *buf, size_t cons)
-{
- memmove(buf->data, buf->data + cons, buf->len - buf->offset);
- memset(buf->data + cons, 0x0, buf->len - buf->offset);
- buf->offset -= cons;
-}
-
-/* Match host names according to RFC 2818 rules */
-int gitno__match_host(const char *pattern, const char *host)
-{
- for (;;) {
- char c = git__tolower(*pattern++);
-
- if (c == '\0')
- return *host ? -1 : 0;
-
- if (c == '*') {
- c = *pattern;
- /* '*' at the end matches everything left */
- if (c == '\0')
- return 0;
-
- /*
- * We've found a pattern, so move towards the next matching
- * char. The '.' is handled specially because wildcards aren't
- * allowed to cross subdomains.
- */
-
- while(*host) {
- char h = git__tolower(*host);
- if (c == h)
- return gitno__match_host(pattern, host++);
- if (h == '.')
- return gitno__match_host(pattern, host);
- host++;
- }
- return -1;
- }
-
- if (c != git__tolower(*host++))
- return -1;
- }
-
- return -1;
-}
diff --git a/src/libgit2/netops.h b/src/libgit2/netops.h
deleted file mode 100644
index 56f96853459..00000000000
--- a/src/libgit2/netops.h
+++ /dev/null
@@ -1,68 +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_netops_h__
-#define INCLUDE_netops_h__
-
-#include "common.h"
-
-#include "posix.h"
-#include "stream.h"
-#include "net.h"
-
-#ifdef GIT_OPENSSL
-# include "streams/openssl.h"
-#endif
-
-typedef struct gitno_ssl {
-#ifdef GIT_OPENSSL
- SSL *ssl;
-#else
- size_t dummy;
-#endif
-} gitno_ssl;
-
-/* Represents a socket that may or may not be using SSL */
-typedef struct gitno_socket {
- GIT_SOCKET socket;
- gitno_ssl ssl;
-} gitno_socket;
-
-typedef struct gitno_buffer {
- char *data;
- size_t len;
- size_t offset;
- int (*recv)(struct gitno_buffer *buffer);
- void *cb_data;
-} gitno_buffer;
-
-/* Flags to gitno_connect */
-enum {
- /* Attempt to create an SSL connection. */
- GITNO_CONNECT_SSL = 1
-};
-
-/**
- * Check if the name in a cert matches the wanted hostname
- *
- * Check if a pattern from a certificate matches the hostname we
- * wanted to connect to according to RFC2818 rules (which specifies
- * HTTP over TLS). Mainly, an asterisk matches anything, but is
- * limited to a single url component.
- *
- * Note that this does not set an error message. It expects the user
- * to provide the message for the user.
- */
-int gitno__match_host(const char *pattern, const char *host);
-
-void gitno_buffer_setup_fromstream(git_stream *st, gitno_buffer *buf, char *data, size_t len);
-void gitno_buffer_setup_callback(gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data);
-int gitno_recv(gitno_buffer *buf);
-
-int gitno_consume(gitno_buffer *buf, const char *ptr);
-void gitno_consume_n(gitno_buffer *buf, size_t cons);
-
-#endif
diff --git a/src/libgit2/notes.c b/src/libgit2/notes.c
index 1b1935330e9..13ca3824bf1 100644
--- a/src/libgit2/notes.c
+++ b/src/libgit2/notes.c
@@ -460,7 +460,7 @@ int git_note_commit_read(
{
int error;
git_tree *tree = NULL;
- char target[GIT_OID_SHA1_HEXSIZE + 1];
+ char target[GIT_OID_MAX_HEXSIZE + 1];
git_oid_tostr(target, sizeof(target), oid);
@@ -507,7 +507,7 @@ int git_note_commit_create(
{
int error;
git_tree *tree = NULL;
- char target[GIT_OID_SHA1_HEXSIZE + 1];
+ char target[GIT_OID_MAX_HEXSIZE + 1];
git_oid_tostr(target, sizeof(target), oid);
@@ -578,7 +578,7 @@ int git_note_commit_remove(
{
int error;
git_tree *tree = NULL;
- char target[GIT_OID_SHA1_HEXSIZE + 1];
+ char target[GIT_OID_MAX_HEXSIZE + 1];
git_oid_tostr(target, sizeof(target), oid);
@@ -665,8 +665,9 @@ void git_note_free(git_note *note)
}
static int process_entry_path(
- const char *entry_path,
- git_oid *annotated_object_id)
+ git_oid *annotated_object_id,
+ git_note_iterator *it,
+ const char *entry_path)
{
int error = 0;
size_t i = 0, j = 0, len;
@@ -698,12 +699,12 @@ static int process_entry_path(
buf.ptr[j] = '\0';
buf.size = j;
- if (j != GIT_OID_SHA1_HEXSIZE) {
+ if (j != git_oid_hexsize(it->repo->oid_type)) {
/* This is not a note entry */
goto cleanup;
}
- error = git_oid__fromstr(annotated_object_id, buf.ptr, GIT_OID_SHA1);
+ error = git_oid__fromstr(annotated_object_id, buf.ptr, it->repo->oid_type);
cleanup:
git_str_dispose(&buf);
@@ -799,7 +800,7 @@ int git_note_next(
git_oid_cpy(note_id, &item->id);
- if ((error = process_entry_path(item->path, annotated_id)) < 0)
+ if ((error = process_entry_path(annotated_id, it, item->path)) < 0)
return error;
if ((error = git_iterator_advance(NULL, it)) < 0 && error != GIT_ITEROVER)
diff --git a/src/libgit2/object.c b/src/libgit2/object.c
index d45465678f1..70e28c2c034 100644
--- a/src/libgit2/object.c
+++ b/src/libgit2/object.c
@@ -27,8 +27,8 @@ typedef struct {
const char *str; /* type name string */
size_t size; /* size in bytes of the object structure */
- int (*parse)(void *self, git_odb_object *obj);
- int (*parse_raw)(void *self, const char *data, size_t size);
+ int (*parse)(void *self, git_odb_object *obj, git_oid_t oid_type);
+ int (*parse_raw)(void *self, const char *data, size_t size, git_oid_t oid_type);
void (*free)(void *self);
} git_object_def;
@@ -60,7 +60,8 @@ int git_object__from_raw(
git_object **object_out,
const char *data,
size_t size,
- git_object_t type)
+ git_object_t object_type,
+ git_oid_t oid_type)
{
git_object_def *def;
git_object *object;
@@ -71,12 +72,15 @@ int git_object__from_raw(
*object_out = NULL;
/* Validate type match */
- if (type != GIT_OBJECT_BLOB && type != GIT_OBJECT_TREE && type != GIT_OBJECT_COMMIT && type != GIT_OBJECT_TAG) {
+ if (object_type != GIT_OBJECT_BLOB &&
+ object_type != GIT_OBJECT_TREE &&
+ object_type != GIT_OBJECT_COMMIT &&
+ object_type != GIT_OBJECT_TAG) {
git_error_set(GIT_ERROR_INVALID, "the requested type is invalid");
return GIT_ENOTFOUND;
}
- if ((object_size = git_object__size(type)) == 0) {
+ if ((object_size = git_object__size(object_type)) == 0) {
git_error_set(GIT_ERROR_INVALID, "the requested type is invalid");
return GIT_ENOTFOUND;
}
@@ -85,15 +89,15 @@ int git_object__from_raw(
object = git__calloc(1, object_size);
GIT_ERROR_CHECK_ALLOC(object);
object->cached.flags = GIT_CACHE_STORE_PARSED;
- object->cached.type = type;
- if ((error = git_odb__hash(&object->cached.oid, data, size, type, GIT_OID_SHA1)) < 0)
+ object->cached.type = object_type;
+ if ((error = git_odb__hash(&object->cached.oid, data, size, object_type, oid_type)) < 0)
return error;
/* Parse raw object data */
- def = &git_objects_table[type];
+ def = &git_objects_table[object_type];
GIT_ASSERT(def->free && def->parse_raw);
- if ((error = def->parse_raw(object, data, size)) < 0) {
+ if ((error = def->parse_raw(object, data, size, oid_type)) < 0) {
def->free(object);
return error;
}
@@ -104,15 +108,13 @@ int git_object__from_raw(
return 0;
}
-int git_object__from_odb_object(
+int git_object__init_from_odb_object(
git_object **object_out,
git_repository *repo,
git_odb_object *odb_obj,
git_object_t type)
{
- int error;
size_t object_size;
- git_object_def *def;
git_object *object = NULL;
GIT_ASSERT_ARG(object_out);
@@ -139,11 +141,28 @@ int git_object__from_odb_object(
object->cached.size = odb_obj->cached.size;
object->repo = repo;
+ *object_out = object;
+ return 0;
+}
+
+int git_object__from_odb_object(
+ git_object **object_out,
+ git_repository *repo,
+ git_odb_object *odb_obj,
+ git_object_t type)
+{
+ int error;
+ git_object_def *def;
+ git_object *object = NULL;
+
+ if ((error = git_object__init_from_odb_object(&object, repo, odb_obj, type)) < 0)
+ return error;
+
/* Parse raw object data */
def = &git_objects_table[odb_obj->cached.type];
GIT_ASSERT(def->free && def->parse);
- if ((error = def->parse(object, odb_obj)) < 0) {
+ if ((error = def->parse(object, odb_obj, repo->oid_type)) < 0) {
/*
* parse returns EINVALID on invalid data; downgrade
* that to a normal -1 error code.
@@ -177,6 +196,7 @@ int git_object_lookup_prefix(
git_object *object = NULL;
git_odb *odb = NULL;
git_odb_object *odb_obj = NULL;
+ size_t oid_hexsize;
int error = 0;
GIT_ASSERT_ARG(repo);
@@ -192,10 +212,12 @@ int git_object_lookup_prefix(
if (error < 0)
return error;
- if (len > GIT_OID_SHA1_HEXSIZE)
- len = GIT_OID_SHA1_HEXSIZE;
+ oid_hexsize = git_oid_hexsize(repo->oid_type);
+
+ if (len > oid_hexsize)
+ len = oid_hexsize;
- if (len == GIT_OID_SHA1_HEXSIZE) {
+ if (len == oid_hexsize) {
git_cached_obj *cached = NULL;
/* We want to match the full id : we can first look up in the cache,
@@ -229,8 +251,9 @@ int git_object_lookup_prefix(
error = git_odb_read(&odb_obj, odb, id);
}
} else {
- git_oid short_oid = GIT_OID_SHA1_ZERO;
+ git_oid short_oid;
+ git_oid_clear(&short_oid, repo->oid_type);
git_oid__cpy_prefix(&short_oid, id, len);
/* If len < GIT_OID_SHA1_HEXSIZE (a strict short oid was given), we have
@@ -258,7 +281,9 @@ int git_object_lookup_prefix(
}
int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_object_t type) {
- return git_object_lookup_prefix(object_out, repo, id, GIT_OID_SHA1_HEXSIZE, type);
+ GIT_ASSERT_ARG(repo);
+ return git_object_lookup_prefix(object_out,
+ repo, id, git_oid_hexsize(repo->oid_type), type);
}
void git_object_free(git_object *object)
@@ -357,12 +382,11 @@ static int dereference_object(git_object **dereferenced, git_object *obj)
static int peel_error(int error, const git_oid *oid, git_object_t type)
{
const char *type_name;
- char hex_oid[GIT_OID_SHA1_HEXSIZE + 1];
+ char hex_oid[GIT_OID_MAX_HEXSIZE + 1];
type_name = git_object_type2string(type);
- git_oid_fmt(hex_oid, oid);
- hex_oid[GIT_OID_SHA1_HEXSIZE] = '\0';
+ git_oid_nfmt(hex_oid, GIT_OID_MAX_HEXSIZE + 1, oid);
git_error_set(GIT_ERROR_OBJECT, "the git_object of id '%s' can not be "
"successfully peeled into a %s (git_object_t=%i).", hex_oid, type_name, type);
@@ -500,31 +524,36 @@ int git_object_lookup_bypath(
static int git_object__short_id(git_str *out, const git_object *obj)
{
git_repository *repo;
- int len = GIT_ABBREV_DEFAULT, error;
- git_oid id = GIT_OID_SHA1_ZERO;
+ git_oid id;
git_odb *odb;
+ size_t oid_hexsize;
+ int len = GIT_ABBREV_DEFAULT, error;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(obj);
repo = git_object_owner(obj);
+ git_oid_clear(&id, repo->oid_type);
+ oid_hexsize = git_oid_hexsize(repo->oid_type);
+
if ((error = git_repository__configmap_lookup(&len, repo, GIT_CONFIGMAP_ABBREV)) < 0)
return error;
+ if (len < 0 || (size_t)len > oid_hexsize) {
+ git_error_set(GIT_ERROR_CONFIG, "invalid oid abbreviation setting: '%d'", len);
+ return -1;
+ }
+
if ((error = git_repository_odb(&odb, repo)) < 0)
return error;
- while (len < GIT_OID_SHA1_HEXSIZE) {
+ while ((size_t)len < oid_hexsize) {
/* set up short oid */
memcpy(&id.id, &obj->cached.oid.id, (len + 1) / 2);
if (len & 1)
id.id[len / 2] &= 0xf0;
-#ifdef GIT_EXPERIMENTAL_SHA256
- id.type = GIT_OID_SHA1;
-#endif
-
error = git_odb_exists_prefix(NULL, odb, &id, len);
if (error != GIT_EAMBIGUOUS)
break;
@@ -576,21 +605,29 @@ int git_object_rawcontent_is_valid(
int *valid,
const char *buf,
size_t len,
- git_object_t type)
+ git_object_t object_type
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ )
{
git_object *obj = NULL;
int error;
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_oid_t oid_type = GIT_OID_SHA1;
+#endif
+
GIT_ASSERT_ARG(valid);
GIT_ASSERT_ARG(buf);
/* Blobs are always valid; don't bother parsing. */
- if (type == GIT_OBJECT_BLOB) {
+ if (object_type == GIT_OBJECT_BLOB) {
*valid = 1;
return 0;
}
- error = git_object__from_raw(&obj, buf, len, type);
+ error = git_object__from_raw(&obj, buf, len, object_type, oid_type);
git_object_free(obj);
if (error == 0) {
@@ -611,7 +648,7 @@ int git_object__parse_oid_header(
const char *header,
git_oid_t oid_type)
{
- const size_t sha_len = GIT_OID_SHA1_HEXSIZE;
+ const size_t sha_len = git_oid_hexsize(oid_type);
const size_t header_len = strlen(header);
const char *buffer = *buffer_out;
diff --git a/src/libgit2/object.h b/src/libgit2/object.h
index 980e8627e47..b6c604c8178 100644
--- a/src/libgit2/object.h
+++ b/src/libgit2/object.h
@@ -33,6 +33,13 @@ int git_object__from_raw(
git_object **object_out,
const char *data,
size_t size,
+ git_object_t object_type,
+ git_oid_t oid_type);
+
+int git_object__init_from_odb_object(
+ git_object **object_out,
+ git_repository *repo,
+ git_odb_object *odb_obj,
git_object_t type);
int git_object__from_odb_object(
diff --git a/src/libgit2/odb.c b/src/libgit2/odb.c
index aa2dd3cb27a..fec1e45b9d4 100644
--- a/src/libgit2/odb.c
+++ b/src/libgit2/odb.c
@@ -684,6 +684,7 @@ int git_odb__add_default_backends(
ino_t inode;
git_odb_backend *loose, *packed;
git_odb_backend_loose_options loose_opts = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
+ git_odb_backend_pack_options pack_opts = GIT_ODB_BACKEND_PACK_OPTIONS_INIT;
/* TODO: inodes are not really relevant on Win32, so we need to find
* a cross-platform workaround for this */
@@ -722,6 +723,7 @@ int git_odb__add_default_backends(
loose_opts.flags |= GIT_ODB_BACKEND_LOOSE_FSYNC;
loose_opts.oid_type = db->options.oid_type;
+ pack_opts.oid_type = db->options.oid_type;
/* add the loose object backend */
if (git_odb__backend_loose(&loose, objects_dir, &loose_opts) < 0 ||
@@ -729,15 +731,25 @@ int git_odb__add_default_backends(
return -1;
/* add the packed file backend */
- if (git_odb_backend_pack(&packed, objects_dir) < 0 ||
- add_backend_internal(db, packed, git_odb__packed_priority, as_alternates, inode) < 0)
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (git_odb_backend_pack(&packed, objects_dir, &pack_opts) < 0)
+ return -1;
+#else
+ GIT_UNUSED(pack_opts);
+
+ if (git_odb_backend_pack(&packed, objects_dir) < 0)
+ return -1;
+#endif
+
+ if (add_backend_internal(db, packed, git_odb__packed_priority, as_alternates, inode) < 0)
return -1;
if (git_mutex_lock(&db->lock) < 0) {
git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
return -1;
}
- if (!db->cgraph && git_commit_graph_new(&db->cgraph, objects_dir, false) < 0) {
+ if (!db->cgraph &&
+ git_commit_graph_new(&db->cgraph, objects_dir, false, db->options.oid_type) < 0) {
git_mutex_unlock(&db->lock);
return -1;
}
@@ -845,6 +857,25 @@ int git_odb__open(
return 0;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+
+int git_odb_open(
+ git_odb **out,
+ const char *objects_dir,
+ const git_odb_options *opts)
+{
+ return git_odb__open(out, objects_dir, opts);
+}
+
+#else
+
+int git_odb_open(git_odb **out, const char *objects_dir)
+{
+ return git_odb__open(out, objects_dir, NULL);
+}
+
+#endif
+
int git_odb__set_caps(git_odb *odb, int caps)
{
if (caps == GIT_ODB_CAP_FROM_OWNER) {
@@ -1463,11 +1494,16 @@ static int read_prefix_1(git_odb_object **out, git_odb *db,
if (found && git_oid__cmp(&full_oid, &found_full_oid)) {
git_str buf = GIT_STR_INIT;
+ const char *idstr;
- git_str_printf(&buf, "multiple matches for prefix: %s",
- git_oid_tostr_s(&full_oid));
- git_str_printf(&buf, " %s",
- git_oid_tostr_s(&found_full_oid));
+ if ((idstr = git_oid_tostr_s(&full_oid)) == NULL) {
+ git_str_puts(&buf, "failed to parse object id");
+ } else {
+ git_str_printf(&buf, "multiple matches for prefix: %s", idstr);
+
+ if ((idstr = git_oid_tostr_s(&found_full_oid)) != NULL)
+ git_str_printf(&buf, " %s", idstr);
+ }
error = git_odb__error_ambiguous(buf.ptr);
git_str_dispose(&buf);
@@ -1693,7 +1729,9 @@ int git_odb_open_wstream(
(error = hash_header(ctx, size, type)) < 0)
goto done;
+#ifdef GIT_EXPERIMENTAL_SHA256
(*stream)->oid_type = db->options.oid_type;
+#endif
(*stream)->hash_ctx = ctx;
(*stream)->declared_size = size;
(*stream)->received_bytes = 0;
diff --git a/src/libgit2/odb_loose.c b/src/libgit2/odb_loose.c
index d1abbe2333e..51195d35778 100644
--- a/src/libgit2/odb_loose.c
+++ b/src/libgit2/odb_loose.c
@@ -1204,3 +1204,37 @@ int git_odb__backend_loose(
*backend_out = (git_odb_backend *)backend;
return 0;
}
+
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_odb_backend_loose(
+ git_odb_backend **backend_out,
+ const char *objects_dir,
+ git_odb_backend_loose_options *opts)
+{
+ return git_odb__backend_loose(backend_out, objects_dir, opts);
+}
+#else
+int git_odb_backend_loose(
+ git_odb_backend **backend_out,
+ const char *objects_dir,
+ int compression_level,
+ int do_fsync,
+ unsigned int dir_mode,
+ unsigned int file_mode)
+{
+ git_odb_backend_loose_flag_t flags = 0;
+ git_odb_backend_loose_options opts = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
+
+ if (do_fsync)
+ flags |= GIT_ODB_BACKEND_LOOSE_FSYNC;
+
+ opts.flags = flags;
+ opts.compression_level = compression_level;
+ opts.dir_mode = dir_mode;
+ opts.file_mode = file_mode;
+ opts.oid_type = GIT_OID_DEFAULT;
+
+ return git_odb__backend_loose(backend_out, objects_dir, &opts);
+}
+#endif
diff --git a/src/libgit2/odb_pack.c b/src/libgit2/odb_pack.c
index 49a655b4484..fc533ae83e2 100644
--- a/src/libgit2/odb_pack.c
+++ b/src/libgit2/odb_pack.c
@@ -26,6 +26,7 @@
struct pack_backend {
git_odb_backend parent;
+ git_odb_backend_pack_options opts;
git_midx_file *midx;
git_vector midx_packs;
git_vector packs;
@@ -95,24 +96,24 @@ struct pack_writepack {
* --------------------------------------------------
*
* # pack_backend__exists / pack_backend__exists_prefix
- * | Check if the given SHA1 oid (or a SHA1 oid prefix) exists in any of the
+ * | Check if the given oid (or an oid prefix) exists in any of the
* | packs that have been loaded for our ODB.
* |
* |-# pack_entry_find / pack_entry_find_prefix
- * | If there is a multi-pack-index present, search the SHA1 oid in that
+ * | If there is a multi-pack-index present, search the oid in that
* | index first. If it is not found there, iterate through all the unindexed
* | packs that have been preloaded (starting by the pack where the latest
* | object was found) to try to find the OID in one of them.
* |
* |-# git_midx_entry_find
- * | Search for the SHA1 oid in the multi-pack-index. See
+ * | Search for the oid in the multi-pack-index. See
* |
* | for specifics on the multi-pack-index format and how do we find
* | entries in it.
* |
* |-# git_pack_entry_find
- * | Check the index of an individual unindexed pack to see if the SHA1
- * | OID can be found. If we can find the offset to that SHA1 inside of the
+ * | Check the index of an individual unindexed pack to see if the
+ * | OID can be found. If we can find the offset to that inside of the
* | index, that means the object is contained inside of the packfile and
* | we can stop searching. Before returning, we verify that the
* | packfile behind the index we are searching still exists on disk.
@@ -141,13 +142,13 @@ struct pack_writepack {
* --------------------------------------------------
*
* # pack_backend__read / pack_backend__read_prefix
- * | Check if the given SHA1 oid (or a SHA1 oid prefix) exists in any of the
+ * | Check if the given oid (or an oid prefix) exists in any of the
* | packs that have been loaded for our ODB. If it does, open the packfile and
* | read from it.
* |
* |-# git_packfile_unpack
* Armed with a packfile and the offset within it, we can finally unpack
- * the object pointed at by the SHA1 oid. This involves mmapping part of
+ * the object pointed at by the oid. This involves mmapping part of
* the `.pack` file, and uncompressing the object within it (if it is
* stored in the undelfitied representation), or finding a base object and
* applying some deltas to its uncompressed representation (if it is stored
@@ -177,7 +178,7 @@ static int pack_entry_find(struct git_pack_entry *e,
* a prefix of an identifier.
* Sets GIT_EAMBIGUOUS if short oid is ambiguous.
* This method assumes that len is between
- * GIT_OID_MINPREFIXLEN and GIT_OID_SHA1_HEXSIZE.
+ * GIT_OID_MINPREFIXLEN and the hexsize for the hash type.
*/
static int pack_entry_find_prefix(
struct git_pack_entry *e,
@@ -251,7 +252,7 @@ static int packfile_load__cb(void *data, git_str *path)
if (git_vector_search2(NULL, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0)
return 0;
- error = git_mwindow_get_pack(&pack, path->ptr);
+ error = git_mwindow_get_pack(&pack, path->ptr, backend->opts.oid_type);
/* ignore missing .pack file as git does */
if (error == GIT_ENOTFOUND) {
@@ -270,33 +271,34 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen
{
struct git_pack_file *last_found = backend->last_found, *p;
git_midx_entry midx_entry;
+ size_t oid_hexsize = git_oid_hexsize(backend->opts.oid_type);
size_t i;
if (backend->midx &&
- git_midx_entry_find(&midx_entry, backend->midx, oid, GIT_OID_SHA1_HEXSIZE) == 0 &&
+ git_midx_entry_find(&midx_entry, backend->midx, oid, oid_hexsize) == 0 &&
midx_entry.pack_index < git_vector_length(&backend->midx_packs)) {
e->offset = midx_entry.offset;
- git_oid_cpy(&e->sha1, &midx_entry.sha1);
+ git_oid_cpy(&e->id, &midx_entry.sha1);
e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index);
return 0;
}
if (last_found &&
- git_pack_entry_find(e, last_found, oid, GIT_OID_SHA1_HEXSIZE) == 0)
+ git_pack_entry_find(e, last_found, oid, oid_hexsize) == 0)
return 0;
git_vector_foreach(&backend->packs, i, p) {
if (p == last_found)
continue;
- if (git_pack_entry_find(e, p, oid, GIT_OID_SHA1_HEXSIZE) == 0) {
+ if (git_pack_entry_find(e, p, oid, oid_hexsize) == 0) {
backend->last_found = p;
return 0;
}
}
return git_odb__error_notfound(
- "failed to find pack entry", oid, GIT_OID_SHA1_HEXSIZE);
+ "failed to find pack entry", oid, oid_hexsize);
}
static int pack_entry_find_prefix(
@@ -307,20 +309,26 @@ static int pack_entry_find_prefix(
{
int error;
size_t i;
- git_oid found_full_oid = GIT_OID_SHA1_ZERO;
+ git_oid found_full_oid;
bool found = false;
struct git_pack_file *last_found = backend->last_found, *p;
git_midx_entry midx_entry;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_oid_clear(&found_full_oid, short_oid->type);
+#else
+ git_oid_clear(&found_full_oid, GIT_OID_SHA1);
+#endif
+
if (backend->midx) {
error = git_midx_entry_find(&midx_entry, backend->midx, short_oid, len);
if (error == GIT_EAMBIGUOUS)
return error;
if (!error && midx_entry.pack_index < git_vector_length(&backend->midx_packs)) {
e->offset = midx_entry.offset;
- git_oid_cpy(&e->sha1, &midx_entry.sha1);
+ git_oid_cpy(&e->id, &midx_entry.sha1);
e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index);
- git_oid_cpy(&found_full_oid, &e->sha1);
+ git_oid_cpy(&found_full_oid, &e->id);
found = true;
}
}
@@ -330,9 +338,9 @@ static int pack_entry_find_prefix(
if (error == GIT_EAMBIGUOUS)
return error;
if (!error) {
- if (found && git_oid_cmp(&e->sha1, &found_full_oid))
+ if (found && git_oid_cmp(&e->id, &found_full_oid))
return git_odb__error_ambiguous("found multiple pack entries");
- git_oid_cpy(&found_full_oid, &e->sha1);
+ git_oid_cpy(&found_full_oid, &e->id);
found = true;
}
}
@@ -345,9 +353,9 @@ static int pack_entry_find_prefix(
if (error == GIT_EAMBIGUOUS)
return error;
if (!error) {
- if (found && git_oid_cmp(&e->sha1, &found_full_oid))
+ if (found && git_oid_cmp(&e->id, &found_full_oid))
return git_odb__error_ambiguous("found multiple pack entries");
- git_oid_cpy(&found_full_oid, &e->sha1);
+ git_oid_cpy(&found_full_oid, &e->id);
found = true;
backend->last_found = p;
}
@@ -425,7 +433,10 @@ static int process_multi_pack_index_pack(
}
/* Pack was not found. Allocate a new one. */
- error = git_mwindow_get_pack(&pack, git_str_cstr(&pack_path));
+ error = git_mwindow_get_pack(
+ &pack,
+ git_str_cstr(&pack_path),
+ backend->opts.oid_type);
git_str_dispose(&pack_path);
if (error < 0)
return error;
@@ -468,7 +479,9 @@ static int refresh_multi_pack_index(struct pack_backend *backend)
}
}
- error = git_midx_open(&backend->midx, git_str_cstr(&midx_path));
+ error = git_midx_open(&backend->midx, git_str_cstr(&midx_path),
+ backend->opts.oid_type);
+
git_str_dispose(&midx_path);
if (error < 0)
return error;
@@ -596,32 +609,33 @@ static int pack_backend__read_prefix(
void **buffer_p,
size_t *len_p,
git_object_t *type_p,
- git_odb_backend *backend,
+ git_odb_backend *_backend,
const git_oid *short_oid,
size_t len)
{
+ struct pack_backend *backend = (struct pack_backend *)_backend;
int error = 0;
if (len < GIT_OID_MINPREFIXLEN)
error = git_odb__error_ambiguous("prefix length too short");
- else if (len >= GIT_OID_SHA1_HEXSIZE) {
+ else if (len >= git_oid_hexsize(backend->opts.oid_type)) {
/* We can fall back to regular read method */
- error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
+ error = pack_backend__read(buffer_p, len_p, type_p, _backend, short_oid);
if (!error)
git_oid_cpy(out_oid, short_oid);
} else {
struct git_pack_entry e;
git_rawobj raw = {NULL};
- if ((error = pack_entry_find_prefix(
- &e, (struct pack_backend *)backend, short_oid, len)) == 0 &&
- (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
+ if ((error = pack_entry_find_prefix(&e,
+ backend, short_oid, len)) == 0 &&
+ (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
{
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
- git_oid_cpy(out_oid, &e.sha1);
+ git_oid_cpy(out_oid, &e.id);
}
}
@@ -642,7 +656,7 @@ static int pack_backend__exists_prefix(
struct git_pack_entry e = {0};
error = pack_entry_find_prefix(&e, pb, short_id, len);
- git_oid_cpy(out, &e.sha1);
+ git_oid_cpy(out, &e.id);
return error;
}
@@ -712,6 +726,7 @@ static int pack_backend__writepack(struct git_odb_writepack **out,
git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
struct pack_backend *backend;
struct pack_writepack *writepack;
+ int error;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(_backend);
@@ -726,11 +741,20 @@ static int pack_backend__writepack(struct git_odb_writepack **out,
writepack = git__calloc(1, sizeof(struct pack_writepack));
GIT_ERROR_CHECK_ALLOC(writepack);
- if (git_indexer_new(&writepack->indexer,
- backend->pack_folder, 0, odb, &opts) < 0) {
- git__free(writepack);
+#ifdef GIT_EXPERIMENTAL_SHA256
+ opts.odb = odb;
+
+ error = git_indexer_new(&writepack->indexer,
+ backend->pack_folder,
+ backend->opts.oid_type,
+ &opts);
+#else
+ error = git_indexer_new(&writepack->indexer,
+ backend->pack_folder, 0, odb, &opts);
+#endif
+
+ if (error < 0)
return -1;
- }
writepack->parent.backend = _backend;
writepack->parent.append = pack_backend__writepack_append;
@@ -776,7 +800,12 @@ static int pack_backend__writemidx(git_odb_backend *_backend)
backend = (struct pack_backend *)_backend;
- error = git_midx_writer_new(&w, backend->pack_folder);
+ error = git_midx_writer_new(&w, backend->pack_folder
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , backend->opts.oid_type
+#endif
+ );
+
if (error < 0)
return error;
@@ -840,7 +869,10 @@ static void pack_backend__free(git_odb_backend *_backend)
git__free(backend);
}
-static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
+static int pack_backend__alloc(
+ struct pack_backend **out,
+ size_t initial_size,
+ const git_odb_backend_pack_options *opts)
{
struct pack_backend *backend = git__calloc(1, sizeof(struct pack_backend));
GIT_ERROR_CHECK_ALLOC(backend);
@@ -849,12 +881,19 @@ static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
git__free(backend);
return -1;
}
+
if (git_vector_init(&backend->packs, initial_size, packfile_sort__cb) < 0) {
git_vector_free(&backend->midx_packs);
git__free(backend);
return -1;
}
+ if (opts)
+ memcpy(&backend->opts, opts, sizeof(git_odb_backend_pack_options));
+
+ if (!backend->opts.oid_type)
+ backend->opts.oid_type = GIT_OID_DEFAULT;
+
backend->parent.version = GIT_ODB_BACKEND_VERSION;
backend->parent.read = &pack_backend__read;
@@ -873,17 +912,31 @@ static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
return 0;
}
-int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_odb_backend_one_pack(
+ git_odb_backend **backend_out,
+ const char *idx,
+ const git_odb_backend_pack_options *opts)
+#else
+int git_odb_backend_one_pack(
+ git_odb_backend **backend_out,
+ const char *idx)
+#endif
{
struct pack_backend *backend = NULL;
struct git_pack_file *packfile = NULL;
- if (pack_backend__alloc(&backend, 1) < 0)
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_odb_backend_pack_options *opts = NULL;
+#endif
+
+ git_oid_t oid_type = opts ? opts->oid_type : 0;
+
+ if (pack_backend__alloc(&backend, 1, opts) < 0)
return -1;
- if (git_mwindow_get_pack(&packfile, idx) < 0 ||
- git_vector_insert(&backend->packs, packfile) < 0)
- {
+ if (git_mwindow_get_pack(&packfile, idx, oid_type) < 0 ||
+ git_vector_insert(&backend->packs, packfile) < 0) {
pack_backend__free((git_odb_backend *)backend);
return -1;
}
@@ -892,18 +945,30 @@ int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
return 0;
}
-int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_odb_backend_pack(
+ git_odb_backend **backend_out,
+ const char *objects_dir,
+ const git_odb_backend_pack_options *opts)
+#else
+int git_odb_backend_pack(
+ git_odb_backend **backend_out,
+ const char *objects_dir)
+#endif
{
int error = 0;
struct pack_backend *backend = NULL;
git_str path = GIT_STR_INIT;
- if (pack_backend__alloc(&backend, 8) < 0)
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_odb_backend_pack_options *opts = NULL;
+#endif
+
+ if (pack_backend__alloc(&backend, 8, opts) < 0)
return -1;
if (!(error = git_str_joinpath(&path, objects_dir, "pack")) &&
- git_fs_path_isdir(git_str_cstr(&path)))
- {
+ git_fs_path_isdir(git_str_cstr(&path))) {
backend->pack_folder = git_str_detach(&path);
error = pack_backend__refresh((git_odb_backend *)backend);
}
diff --git a/src/libgit2/oid.c b/src/libgit2/oid.c
index 6cc21641d70..2bb7a6f6bc4 100644
--- a/src/libgit2/oid.c
+++ b/src/libgit2/oid.c
@@ -9,7 +9,7 @@
#include "git2/oid.h"
#include "repository.h"
-#include "threadstate.h"
+#include "runtime.h"
#include
#include
@@ -153,9 +153,42 @@ int git_oid_pathfmt(char *str, const git_oid *oid)
return 0;
}
+static git_tlsdata_key thread_str_key;
+
+static void GIT_SYSTEM_CALL thread_str_free(void *s)
+{
+ char *str = (char *)s;
+ git__free(str);
+}
+
+static void thread_str_global_shutdown(void)
+{
+ char *str = git_tlsdata_get(thread_str_key);
+ git_tlsdata_set(thread_str_key, NULL);
+
+ git__free(str);
+ git_tlsdata_dispose(thread_str_key);
+}
+
+int git_oid_global_init(void)
+{
+ if (git_tlsdata_init(&thread_str_key, thread_str_free) != 0)
+ return -1;
+
+ return git_runtime_shutdown_register(thread_str_global_shutdown);
+}
+
char *git_oid_tostr_s(const git_oid *oid)
{
- char *str = GIT_THREADSTATE->oid_fmt;
+ char *str;
+
+ if ((str = git_tlsdata_get(thread_str_key)) == NULL) {
+ if ((str = git__malloc(GIT_OID_MAX_HEXSIZE + 1)) == NULL)
+ return NULL;
+
+ git_tlsdata_set(thread_str_key, str);
+ }
+
git_oid_nfmt(str, git_oid_hexsize(git_oid_type(oid)) + 1, oid);
return str;
}
diff --git a/src/libgit2/oid.h b/src/libgit2/oid.h
index d775e180b4e..f25a899a681 100644
--- a/src/libgit2/oid.h
+++ b/src/libgit2/oid.h
@@ -66,6 +66,47 @@ GIT_INLINE(size_t) git_oid_hexsize(git_oid_t type)
return 0;
}
+GIT_INLINE(const char *) git_oid_type_name(git_oid_t type)
+{
+ switch (type) {
+ case GIT_OID_SHA1:
+ return "sha1";
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ return "sha256";
+#endif
+ }
+
+ return "unknown";
+}
+
+GIT_INLINE(git_oid_t) git_oid_type_fromstr(const char *name)
+{
+ if (strcmp(name, "sha1") == 0)
+ return GIT_OID_SHA1;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (strcmp(name, "sha256") == 0)
+ return GIT_OID_SHA256;
+#endif
+
+ return 0;
+}
+
+GIT_INLINE(git_oid_t) git_oid_type_fromstrn(const char *name, size_t len)
+{
+ if (len == CONST_STRLEN("sha1") && strncmp(name, "sha1", len) == 0)
+ return GIT_OID_SHA1;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (len == CONST_STRLEN("sha256") && strncmp(name, "sha256", len) == 0)
+ return GIT_OID_SHA256;
+#endif
+
+ return 0;
+}
+
GIT_INLINE(git_hash_algorithm_t) git_oid_algorithm(git_oid_t type)
{
switch (type) {
@@ -229,4 +270,6 @@ int git_oid__fromstrn(
int git_oid__fromraw(git_oid *out, const unsigned char *raw, git_oid_t type);
+int git_oid_global_init(void);
+
#endif
diff --git a/src/libgit2/oidarray.c b/src/libgit2/oidarray.c
index 583017c4ee2..37f67756aa5 100644
--- a/src/libgit2/oidarray.c
+++ b/src/libgit2/oidarray.c
@@ -15,10 +15,17 @@ void git_oidarray_dispose(git_oidarray *arr)
git__free(arr->ids);
}
-void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array)
+void git_oidarray__from_array(git_oidarray *out, const git_array_oid_t *array)
{
- arr->count = array->size;
- arr->ids = array->ptr;
+ out->count = array->size;
+ out->ids = array->ptr;
+}
+
+void git_oidarray__to_array(git_array_oid_t *out, const git_oidarray *array)
+{
+ out->ptr = array->ids;
+ out->size = array->count;
+ out->asize = array->count;
}
void git_oidarray__reverse(git_oidarray *arr)
@@ -33,6 +40,45 @@ void git_oidarray__reverse(git_oidarray *arr)
}
}
+int git_oidarray__add(git_array_oid_t *arr, git_oid *id)
+{
+ git_oid *add, *iter;
+ size_t i;
+
+ git_array_foreach(*arr, i, iter) {
+ if (git_oid_cmp(iter, id) == 0)
+ return 0;
+ }
+
+ if ((add = git_array_alloc(*arr)) == NULL)
+ return -1;
+
+ git_oid_cpy(add, id);
+ return 0;
+}
+
+bool git_oidarray__remove(git_array_oid_t *arr, git_oid *id)
+{
+ bool found = false;
+ size_t remain, i;
+ git_oid *iter;
+
+ git_array_foreach(*arr, i, iter) {
+ if (git_oid_cmp(iter, id) == 0) {
+ arr->size--;
+ remain = arr->size - i;
+
+ if (remain > 0)
+ memmove(&arr->ptr[i], &arr->ptr[i+1], remain * sizeof(git_oid));
+
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
+
#ifndef GIT_DEPRECATE_HARD
void git_oidarray_free(git_oidarray *arr)
diff --git a/src/libgit2/oidarray.h b/src/libgit2/oidarray.h
index eed3a109120..8f1543a32e0 100644
--- a/src/libgit2/oidarray.h
+++ b/src/libgit2/oidarray.h
@@ -15,6 +15,10 @@
typedef git_array_t(git_oid) git_array_oid_t;
extern void git_oidarray__reverse(git_oidarray *arr);
-extern void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array);
+extern void git_oidarray__from_array(git_oidarray *out, const git_array_oid_t *array);
+extern void git_oidarray__to_array(git_array_oid_t *out, const git_oidarray *array);
+
+int git_oidarray__add(git_array_oid_t *arr, git_oid *id);
+bool git_oidarray__remove(git_array_oid_t *arr, git_oid *id);
#endif
diff --git a/src/libgit2/pack-objects.c b/src/libgit2/pack-objects.c
index 068231649ad..b2d80cba954 100644
--- a/src/libgit2/pack-objects.c
+++ b/src/libgit2/pack-objects.c
@@ -11,7 +11,6 @@
#include "zstream.h"
#include "delta.h"
#include "iterator.h"
-#include "netops.h"
#include "pack.h"
#include "thread.h"
#include "tree.h"
@@ -127,6 +126,7 @@ static int packbuilder_config(git_packbuilder *pb)
int git_packbuilder_new(git_packbuilder **out, git_repository *repo)
{
+ git_hash_algorithm_t hash_algorithm;
git_packbuilder *pb;
*out = NULL;
@@ -134,6 +134,11 @@ int git_packbuilder_new(git_packbuilder **out, git_repository *repo)
pb = git__calloc(1, sizeof(*pb));
GIT_ERROR_CHECK_ALLOC(pb);
+ pb->oid_type = repo->oid_type;
+
+ hash_algorithm = git_oid_algorithm(pb->oid_type);
+ GIT_ASSERT(hash_algorithm);
+
if (git_oidmap_new(&pb->object_ix) < 0 ||
git_oidmap_new(&pb->walk_objects) < 0 ||
git_pool_init(&pb->object_pool, sizeof(struct walk_object)) < 0)
@@ -142,7 +147,7 @@ int git_packbuilder_new(git_packbuilder **out, git_repository *repo)
pb->repo = repo;
pb->nr_threads = 1; /* do not spawn any thread by default */
- if (git_hash_ctx_init(&pb->ctx, GIT_HASH_ALGORITHM_SHA1) < 0 ||
+ if (git_hash_ctx_init(&pb->ctx, hash_algorithm) < 0 ||
git_zstream_init(&pb->zstream, GIT_ZSTREAM_DEFLATE) < 0 ||
git_repository_odb(&pb->odb, repo) < 0 ||
packbuilder_config(pb) < 0)
@@ -249,10 +254,10 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
pb->done = false;
if (pb->progress_cb) {
- double current_time = git__timer();
- double elapsed = current_time - pb->last_progress_report_time;
+ uint64_t current_time = git_time_monotonic();
+ uint64_t elapsed = current_time - pb->last_progress_report_time;
- if (elapsed < 0 || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ if (elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
pb->last_progress_report_time = current_time;
ret = pb->progress_cb(
@@ -315,9 +320,11 @@ static int write_object(
git_object_t type;
unsigned char hdr[10], *zbuf = NULL;
void *data = NULL;
- size_t hdr_len, zbuf_len = COMPRESS_BUFLEN, data_len;
+ size_t hdr_len, zbuf_len = COMPRESS_BUFLEN, data_len, oid_size;
int error;
+ oid_size = git_oid_size(pb->oid_type);
+
/*
* If we have a delta base, let's use the delta to save space.
* Otherwise load the whole object. 'data' ends up pointing to
@@ -347,8 +354,8 @@ static int write_object(
goto done;
if (type == GIT_OBJECT_REF_DELTA) {
- if ((error = write_cb(po->delta->id.id, GIT_OID_SHA1_SIZE, cb_data)) < 0 ||
- (error = git_hash_update(&pb->ctx, po->delta->id.id, GIT_OID_SHA1_SIZE)) < 0)
+ if ((error = write_cb(po->delta->id.id, oid_size, cb_data)) < 0 ||
+ (error = git_hash_update(&pb->ctx, po->delta->id.id, oid_size)) < 0)
goto done;
}
@@ -668,7 +675,7 @@ static int write_pack(git_packbuilder *pb,
if ((error = git_hash_final(entry_oid.id, &pb->ctx)) < 0)
goto done;
- error = write_cb(entry_oid.id, GIT_OID_SHA1_SIZE, cb_data);
+ error = write_cb(entry_oid.id, git_oid_size(pb->oid_type), cb_data);
done:
/* if callback cancelled writing, we must still free delta_data */
@@ -926,10 +933,10 @@ static int report_delta_progress(
int ret;
if (pb->progress_cb) {
- double current_time = git__timer();
- double elapsed = current_time - pb->last_progress_report_time;
+ uint64_t current_time = git_time_monotonic();
+ uint64_t elapsed = current_time - pb->last_progress_report_time;
- if (force || elapsed < 0 || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ if (force || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
pb->last_progress_report_time = current_time;
ret = pb->progress_cb(
@@ -1407,7 +1414,18 @@ int git_packbuilder_write(
opts.progress_cb = progress_cb;
opts.progress_cb_payload = progress_cb_payload;
- if ((error = git_indexer_new(&indexer, path, mode, pb->odb, &opts)) < 0)
+ /* TODO: SHA256 */
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ opts.mode = mode;
+ opts.odb = pb->odb;
+
+ error = git_indexer_new(&indexer, path, GIT_OID_SHA1, &opts);
+#else
+ error = git_indexer_new(&indexer, path, mode, pb->odb, &opts);
+#endif
+
+ if (error < 0)
goto cleanup;
if (!git_repository__configmap_lookup(&t, pb->repo, GIT_CONFIGMAP_FSYNCOBJECTFILES) && t)
diff --git a/src/libgit2/pack-objects.h b/src/libgit2/pack-objects.h
index 2faa3ec7f51..bbc8b9430ee 100644
--- a/src/libgit2/pack-objects.h
+++ b/src/libgit2/pack-objects.h
@@ -13,7 +13,6 @@
#include "str.h"
#include "hash.h"
#include "oidmap.h"
-#include "netops.h"
#include "zstream.h"
#include "pool.h"
#include "indexer.h"
@@ -56,6 +55,8 @@ struct git_packbuilder {
git_repository *repo; /* associated repository */
git_odb *odb; /* associated object database */
+ git_oid_t oid_type;
+
git_hash_ctx ctx;
git_zstream zstream;
@@ -94,7 +95,9 @@ struct git_packbuilder {
git_packbuilder_progress progress_cb;
void *progress_cb_payload;
- double last_progress_report_time; /* the time progress was last reported */
+
+ /* the time progress was last reported, in millisecond ticks */
+ uint64_t last_progress_report_time;
bool done;
};
diff --git a/src/libgit2/pack.c b/src/libgit2/pack.c
index d428729eae1..1ff0eb0c9d9 100644
--- a/src/libgit2/pack.c
+++ b/src/libgit2/pack.c
@@ -32,7 +32,7 @@ static int packfile_unpack_compressed(
* Throws GIT_EAMBIGUOUSOIDPREFIX if short oid
* is ambiguous within the pack.
* This method assumes that len is between
- * GIT_OID_MINPREFIXLEN and GIT_OID_SHA1_HEXSIZE.
+ * GIT_OID_MINPREFIXLEN and the oid type's hexsize.
*/
static int pack_entry_find_offset(
off64_t *offset_out,
@@ -186,9 +186,9 @@ static int cache_add(
static void pack_index_free(struct git_pack_file *p)
{
- if (p->oids) {
- git__free(p->oids);
- p->oids = NULL;
+ if (p->ids) {
+ git__free(p->ids);
+ p->ids = NULL;
}
if (p->index_map.data) {
git_futils_mmap_free(&p->index_map);
@@ -200,11 +200,12 @@ static void pack_index_free(struct git_pack_file *p)
static int pack_index_check_locked(const char *path, struct git_pack_file *p)
{
struct git_pack_idx_header *hdr;
- uint32_t version, nr, i, *index;
+ uint32_t version, nr = 0, i, *index;
void *idx_map;
size_t idx_size;
struct stat st;
int error;
+
/* TODO: properly open the file without access time using O_NOATIME */
git_file fd = git_futils_open_ro(path);
if (fd < 0)
@@ -218,8 +219,7 @@ static int pack_index_check_locked(const char *path, struct git_pack_file *p)
if (!S_ISREG(st.st_mode) ||
!git__is_sizet(st.st_size) ||
- (idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20)
- {
+ (idx_size = (size_t)st.st_size) < (size_t)((4 * 256) + (p->oid_size * 2))) {
p_close(fd);
git_error_set(GIT_ERROR_ODB, "invalid pack index '%s'", path);
return -1;
@@ -242,10 +242,10 @@ static int pack_index_check_locked(const char *path, struct git_pack_file *p)
return packfile_error("unsupported index version");
}
- } else
+ } else {
version = 1;
+ }
- nr = 0;
index = idx_map;
if (version > 1)
@@ -264,11 +264,11 @@ static int pack_index_check_locked(const char *path, struct git_pack_file *p)
/*
* Total size:
* - 256 index entries 4 bytes each
- * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
- * - 20-byte SHA1 of the packfile
- * - 20-byte SHA1 file checksum
+ * - 24/36-byte entries * nr (20/32 byte SHA + 4-byte offset)
+ * - 20/32-byte SHA of the packfile
+ * - 20/32-byte SHA file checksum
*/
- if (idx_size != 4*256 + nr * 24 + 20 + 20) {
+ if (idx_size != (4 * 256 + ((uint64_t) nr * (p->oid_size + 4)) + (p->oid_size * 2))) {
git_futils_mmap_free(&p->index_map);
return packfile_error("index is corrupted");
}
@@ -277,17 +277,17 @@ static int pack_index_check_locked(const char *path, struct git_pack_file *p)
* Minimum size:
* - 8 bytes of header
* - 256 index entries 4 bytes each
- * - 20-byte sha1 entry * nr
+ * - 20/32-byte SHA entry * nr
* - 4-byte crc entry * nr
* - 4-byte offset entry * nr
- * - 20-byte SHA1 of the packfile
- * - 20-byte SHA1 file checksum
+ * - 20/32-byte SHA of the packfile
+ * - 20/32-byte SHA file checksum
* And after the 4-byte offset table might be a
* variable sized table containing 8-byte entries
* for offsets larger than 2^31.
*/
- unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
- unsigned long max_size = min_size;
+ uint64_t min_size = 8 + (4 * 256) + ((uint64_t)nr * (p->oid_size + 4 + 4)) + (p->oid_size * 2);
+ uint64_t max_size = min_size;
if (nr)
max_size += (nr - 1)*8;
@@ -365,12 +365,12 @@ static unsigned char *pack_window_open(
* Don't allow a negative offset, as that means we've wrapped
* around.
*/
- if (offset > (p->mwf.size - 20))
+ if (offset > (p->mwf.size - p->oid_size))
goto cleanup;
if (offset < 0)
goto cleanup;
- pack_data = git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
+ pack_data = git_mwindow_open(&p->mwf, w_cursor, offset, p->oid_size, left);
cleanup:
git_mutex_unlock(&p->mwf.lock);
@@ -473,13 +473,13 @@ int git_packfile_unpack_header(
return error;
}
- /* pack_window_open() assures us we have [base, base + 20) available
- * as a range that we can look at at. (Its actually the hash
- * size that is assured.) With our object header encoding
- * the maximum deflated object size is 2^137, which is just
- * insane, so we know won't exceed what we have been given.
+ /* pack_window_open() assures us we have [base, base + oid_size)
+ * available as a range that we can look at at. (It's actually
+ * the hash size that is assured.) With our object header
+ * encoding the maximum deflated object size is 2^137, which is
+ * just insane, so we know won't exceed what we have been given.
*/
- base = git_mwindow_open(&p->mwf, w_curs, *curpos, 20, &left);
+ base = git_mwindow_open(&p->mwf, w_curs, *curpos, p->oid_size, &left);
git_mutex_unlock(&p->lock);
git_mutex_unlock(&p->mwf.lock);
if (base == NULL)
@@ -977,11 +977,12 @@ int get_delta_base(
/* Assumption: the only reason this would fail is because the file is too small */
if (base_info == NULL)
return GIT_EBUFS;
- /* pack_window_open() assured us we have [base_info, base_info + 20)
- * as a range that we can look at without walking off the
- * end of the mapped window. Its actually the hash size
- * that is assured. An OFS_DELTA longer than the hash size
- * is stupid, as then a REF_DELTA would be smaller to store.
+ /* pack_window_open() assured us we have
+ * [base_info, base_info + oid_size) as a range that we can look
+ * at without walking off the end of the mapped window. Its
+ * actually the hash size that is assured. An OFS_DELTA longer
+ * than the hash size is stupid, as then a REF_DELTA would be
+ * smaller to store.
*/
if (type == GIT_OBJECT_OFS_DELTA) {
unsigned used = 0;
@@ -1002,7 +1003,7 @@ int get_delta_base(
*curpos += used;
} else if (type == GIT_OBJECT_REF_DELTA) {
git_oid base_oid;
- git_oid__fromraw(&base_oid, base_info, GIT_OID_SHA1);
+ git_oid__fromraw(&base_oid, base_info, p->oid_type);
/* If we have the cooperative cache, search in it first */
if (p->has_cache) {
@@ -1012,7 +1013,7 @@ int get_delta_base(
if (entry->offset == 0)
return packfile_error("delta offset is zero");
- *curpos += 20;
+ *curpos += p->oid_size;
*delta_base_out = entry->offset;
return 0;
} else {
@@ -1025,9 +1026,9 @@ int get_delta_base(
}
/* The base entry _must_ be in the same pack */
- if (pack_entry_find_offset(&base_offset, &unused, p, &base_oid, GIT_OID_SHA1_HEXSIZE) < 0)
+ if (pack_entry_find_offset(&base_offset, &unused, p, &base_oid, p->oid_hexsize) < 0)
return packfile_error("base entry delta is not in the same pack");
- *curpos += 20;
+ *curpos += p->oid_size;
} else
return packfile_error("unknown object type");
@@ -1070,7 +1071,7 @@ void git_packfile_free(struct git_pack_file *p, bool unlink_packfile)
pack_index_free(p);
- git__free(p->bad_object_sha1);
+ git__free(p->bad_object_ids);
git_mutex_free(&p->bases.lock);
git_mutex_free(&p->mwf.lock);
@@ -1083,8 +1084,8 @@ static int packfile_open_locked(struct git_pack_file *p)
{
struct stat st;
struct git_pack_header hdr;
- unsigned char sha1[GIT_OID_SHA1_SIZE];
- unsigned char *idx_sha1;
+ unsigned char checksum[GIT_OID_MAX_SIZE];
+ unsigned char *idx_checksum;
if (pack_index_open_locked(p) < 0)
return git_odb__error_notfound("failed to open packfile", NULL, 0);
@@ -1131,12 +1132,13 @@ 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, GIT_OID_SHA1_SIZE, p->mwf.size - GIT_OID_SHA1_SIZE) < 0)
+ p_pread(p->mwf.fd, checksum, p->oid_size, p->mwf.size - p->oid_size) < 0)
goto cleanup;
- idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40;
+ idx_checksum = ((unsigned char *)p->index_map.data) +
+ p->index_map.len - (p->oid_size * 2);
- if (git_oid_raw_cmp(sha1, idx_sha1, GIT_OID_SHA1_SIZE) != 0)
+ if (git_oid_raw_cmp(checksum, idx_checksum, p->oid_size) != 0)
goto cleanup;
if (git_mwindow_file_register(&p->mwf) < 0)
@@ -1171,7 +1173,10 @@ int git_packfile__name(char **out, const char *path)
return 0;
}
-int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
+int git_packfile_alloc(
+ struct git_pack_file **pack_out,
+ const char *path,
+ git_oid_t oid_type)
{
struct stat st;
struct git_pack_file *p;
@@ -1219,6 +1224,9 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
p->pack_local = 1;
p->mtime = (git_time_t)st.st_mtime;
p->index_version = -1;
+ p->oid_type = oid_type ? oid_type : GIT_OID_DEFAULT;
+ p->oid_size = (unsigned int)git_oid_size(p->oid_type);
+ p->oid_hexsize = (unsigned int)git_oid_hexsize(p->oid_type);
if (git_mutex_init(&p->lock) < 0) {
git_error_set(GIT_ERROR_OS, "failed to initialize packfile mutex");
@@ -1260,20 +1268,20 @@ static off64_t nth_packed_object_offset_locked(struct git_pack_file *p, uint32_t
end = index + p->index_map.len;
index += 4 * 256;
if (p->index_version == 1)
- return ntohl(*((uint32_t *)(index + 24 * n)));
+ return ntohl(*((uint32_t *)(index + (p->oid_size + 4) * (size_t) n)));
- index += 8 + p->num_objects * (20 + 4);
+ index += 8 + (size_t) p->num_objects * (p->oid_size + 4);
off32 = ntohl(*((uint32_t *)(index + 4 * n)));
if (!(off32 & 0x80000000))
return off32;
- index += p->num_objects * 4 + (off32 & 0x7fffffff) * 8;
+ index += (size_t) p->num_objects * 4 + (off32 & 0x7fffffff) * 8;
/* Make sure we're not being sent out of bounds */
if (index >= end - 8)
return -1;
return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
- ntohl(*((uint32_t *)(index + 4)));
+ ntohl(*((uint32_t *)(index + 4)));
}
static int git__memcmp4(const void *a, const void *b) {
@@ -1312,7 +1320,7 @@ int git_pack_foreach_entry(
index += 4 * 256;
- if (p->oids == NULL) {
+ if (p->ids == NULL) {
git_vector offsets, oids;
if ((error = git_vector_init(&oids, p->num_objects, NULL))) {
@@ -1326,22 +1334,25 @@ int git_pack_foreach_entry(
}
if (p->index_version > 1) {
- const unsigned char *off = index + 24 * p->num_objects;
+ const unsigned char *off = index +
+ (p->oid_size + 4) * p->num_objects;
+
for (i = 0; i < p->num_objects; i++)
git_vector_insert(&offsets, (void*)&off[4 * i]);
+
git_vector_sort(&offsets);
git_vector_foreach(&offsets, i, current)
git_vector_insert(&oids, (void*)&index[5 * (current - off)]);
} else {
for (i = 0; i < p->num_objects; i++)
- git_vector_insert(&offsets, (void*)&index[24 * i]);
+ git_vector_insert(&offsets, (void*)&index[(p->oid_size + 4) * i]);
git_vector_sort(&offsets);
git_vector_foreach(&offsets, i, current)
git_vector_insert(&oids, (void*)¤t[4]);
}
git_vector_free(&offsets);
- p->oids = (unsigned char **)git_vector_detach(NULL, NULL, &oids);
+ p->ids = (unsigned char **)git_vector_detach(NULL, NULL, &oids);
}
/*
@@ -1362,7 +1373,7 @@ int git_pack_foreach_entry(
git_array_clear(oids);
GIT_ERROR_CHECK_ALLOC(oid);
}
- git_oid__fromraw(oid, p->oids[i], GIT_OID_SHA1);
+ git_oid__fromraw(oid, p->ids[i], p->oid_type);
}
git_mutex_unlock(&p->lock);
@@ -1412,10 +1423,13 @@ int git_pack_foreach_entry_offset(
/* all offsets should have been validated by pack_index_check_locked */
if (p->index_version > 1) {
- const unsigned char *offsets = index + 24 * p->num_objects;
+ const unsigned char *offsets = index +
+ (p->oid_size + 4) * p->num_objects;
const unsigned char *large_offset_ptr;
- const unsigned char *large_offsets = index + 28 * p->num_objects;
- const unsigned char *large_offsets_end = ((const unsigned char *)p->index_map.data) + p->index_map.len - 20;
+ const unsigned char *large_offsets = index +
+ (p->oid_size + 8) * p->num_objects;
+ const unsigned char *large_offsets_end = ((const unsigned char *)p->index_map.data) + p->index_map.len - p->oid_size;
+
for (i = 0; i < p->num_objects; i++) {
current_offset = ntohl(*(const uint32_t *)(offsets + 4 * i));
if (current_offset & 0x80000000) {
@@ -1428,7 +1442,7 @@ int git_pack_foreach_entry_offset(
ntohl(*((uint32_t *)(large_offset_ptr + 4)));
}
- git_oid__fromraw(¤t_oid, (index + 20 * i), GIT_OID_SHA1);
+ git_oid__fromraw(¤t_oid, (index + p->oid_size * i), p->oid_type);
if ((error = cb(¤t_oid, current_offset, data)) != 0) {
error = git_error_set_after_callback(error);
goto cleanup;
@@ -1436,8 +1450,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));
- git_oid__fromraw(¤t_oid, (index + 24 * i + 4), GIT_OID_SHA1);
+ current_offset = ntohl(*(const uint32_t *)(index + (p->oid_size + 4) * i));
+ git_oid__fromraw(¤t_oid, (index + (p->oid_size + 4) * i + 4), p->oid_type);
if ((error = cb(¤t_oid, current_offset, data)) != 0) {
error = git_error_set_after_callback(error);
goto cleanup;
@@ -1450,14 +1464,20 @@ int git_pack_foreach_entry_offset(
return error;
}
-int git_pack__lookup_sha1(const void *oid_lookup_table, size_t stride, unsigned lo,
- unsigned hi, const unsigned char *oid_prefix)
+int git_pack__lookup_id(
+ const void *oid_lookup_table,
+ size_t stride,
+ unsigned lo,
+ unsigned hi,
+ const unsigned char *oid_prefix,
+ const git_oid_t oid_type)
{
const unsigned char *base = oid_lookup_table;
+ size_t oid_size = git_oid_size(oid_type);
while (lo < hi) {
unsigned mi = (lo + hi) / 2;
- int cmp = git_oid_raw_cmp(base + mi * stride, oid_prefix, GIT_OID_SHA1_SIZE);
+ int cmp = git_oid_raw_cmp(base + mi * stride, oid_prefix, oid_size);
if (!cmp)
return mi;
@@ -1479,6 +1499,7 @@ static int pack_entry_find_offset(
size_t len)
{
const uint32_t *level1_ofs;
+ size_t ofs_delta = 0;
const unsigned char *index;
unsigned hi, lo, stride;
int pos, found = 0;
@@ -1504,17 +1525,23 @@ static int pack_entry_find_offset(
if (p->index_version > 1) {
level1_ofs += 2;
+ ofs_delta = 2;
index += 8;
}
+ if ((size_t)short_oid->id[0] + ofs_delta >= p->index_map.len) {
+ git_error_set(GIT_ERROR_INTERNAL, "internal error: p->short_oid->[0] out of bounds");
+ goto cleanup;
+ }
+
index += 4 * 256;
hi = ntohl(level1_ofs[(int)short_oid->id[0]]);
lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)short_oid->id[0] - 1]));
if (p->index_version > 1) {
- stride = 20;
+ stride = p->oid_size;
} else {
- stride = 24;
+ stride = p->oid_size + 4;
index += 4;
}
@@ -1523,7 +1550,8 @@ static int pack_entry_find_offset(
short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);
#endif
- pos = git_pack__lookup_sha1(index, stride, lo, hi, short_oid->id);
+ pos = git_pack__lookup_id(index, stride, lo, hi,
+ short_oid->id, p->oid_type);
if (pos >= 0) {
/* An object matching exactly the oid was found */
@@ -1541,7 +1569,9 @@ static int pack_entry_find_offset(
}
}
- if (found && len != GIT_OID_SHA1_HEXSIZE && pos + 1 < (int)p->num_objects) {
+ if (found &&
+ len != p->oid_hexsize &&
+ pos + 1 < (int)p->num_objects) {
/* Check for ambiguousity */
const unsigned char *next = current + stride;
@@ -1566,13 +1596,13 @@ static int pack_entry_find_offset(
}
*offset_out = offset;
- git_oid__fromraw(found_oid, current, GIT_OID_SHA1);
+ git_oid__fromraw(found_oid, current, p->oid_type);
#ifdef INDEX_DEBUG_LOOKUP
{
- unsigned char hex_sha1[GIT_OID_SHA1_HEXSIZE + 1];
+ char hex_sha1[p->oid_hexsize + 1];
git_oid_fmt(hex_sha1, found_oid);
- hex_sha1[GIT_OID_SHA1_HEXSIZE] = '\0';
+ hex_sha1[p->oid_hexsize] = '\0';
printf("found lo=%d %s\n", lo, hex_sha1);
}
#endif
@@ -1594,10 +1624,10 @@ int git_pack_entry_find(
GIT_ASSERT_ARG(p);
- if (len == GIT_OID_SHA1_HEXSIZE && p->num_bad_objects) {
+ if (len == p->oid_hexsize && p->num_bad_objects) {
unsigned i;
for (i = 0; i < p->num_bad_objects; i++)
- if (git_oid__cmp(short_oid, &p->bad_object_sha1[i]) == 0)
+ if (git_oid__cmp(short_oid, &p->bad_object_ids[i]) == 0)
return packfile_error("bad object found in packfile");
}
@@ -1630,6 +1660,6 @@ int git_pack_entry_find(
e->offset = offset;
e->p = p;
- git_oid_cpy(&e->sha1, &found_oid);
+ git_oid_cpy(&e->id, &found_oid);
return 0;
}
diff --git a/src/libgit2/pack.h b/src/libgit2/pack.h
index d90588f7951..1a9eb14b295 100644
--- a/src/libgit2/pack.h
+++ b/src/libgit2/pack.h
@@ -99,13 +99,19 @@ struct git_pack_file {
uint32_t num_objects;
uint32_t num_bad_objects;
- git_oid *bad_object_sha1; /* array of git_oid */
+ git_oid *bad_object_ids; /* array of git_oid */
+
+ git_oid_t oid_type;
+ unsigned oid_hexsize:7,
+ oid_size:6,
+ pack_local:1,
+ pack_keep:1,
+ has_cache:1;
int index_version;
git_time_t mtime;
- unsigned pack_local:1, pack_keep:1, has_cache:1;
git_oidmap *idx_cache;
- unsigned char **oids;
+ unsigned char **ids;
git_pack_cache bases; /* delta base cache */
@@ -116,21 +122,26 @@ struct git_pack_file {
};
/**
- * Return the position where an OID (or a prefix) would be inserted within the
- * OID Lookup Table of an .idx file. This performs binary search between the lo
- * and hi indices.
+ * Return the position where an OID (or a prefix) would be inserted within
+ * the OID Lookup Table of an .idx file. This performs binary search
+ * between the lo and hi indices.
*
- * The stride parameter is provided because .idx files version 1 store the OIDs
- * interleaved with the 4-byte file offsets of the objects within the .pack
- * file (stride = 24), whereas files with version 2 store them in a contiguous
- * flat array (stride = 20).
+ * The stride parameter is provided because .idx files version 1 store the
+ * OIDs interleaved with the 4-byte file offsets of the objects within the
+ * .pack file (stride = oid_size + 4), whereas files with version 2 store
+ * them in a contiguous flat array (stride = oid_size).
*/
-int git_pack__lookup_sha1(const void *oid_lookup_table, size_t stride, unsigned lo,
- unsigned hi, const unsigned char *oid_prefix);
+int git_pack__lookup_id(
+ const void *id_lookup_table,
+ size_t stride,
+ unsigned lo,
+ unsigned hi,
+ const unsigned char *id_prefix,
+ const git_oid_t oid_type);
struct git_pack_entry {
off64_t offset;
- git_oid sha1;
+ git_oid id;
struct git_pack_file *p;
};
@@ -174,12 +185,15 @@ int get_delta_base(
off64_t delta_obj_offset);
void git_packfile_free(struct git_pack_file *p, bool unlink_packfile);
-int git_packfile_alloc(struct git_pack_file **pack_out, const char *path);
+int git_packfile_alloc(
+ struct git_pack_file **pack_out,
+ const char *path,
+ git_oid_t oid_type);
int git_pack_entry_find(
struct git_pack_entry *e,
struct git_pack_file *p,
- const git_oid *short_oid,
+ const git_oid *short_id,
size_t len);
int git_pack_foreach_entry(
struct git_pack_file *p,
diff --git a/src/libgit2/parse.c b/src/libgit2/parse.c
index 55d3cb10ec9..9eb86a3f584 100644
--- a/src/libgit2/parse.c
+++ b/src/libgit2/parse.c
@@ -102,13 +102,16 @@ int git_parse_advance_digit(int64_t *out, git_parse_ctx *ctx, int base)
return 0;
}
-int git_parse_advance_oid(git_oid *out, git_parse_ctx *ctx)
+int git_parse_advance_oid(git_oid *out, git_parse_ctx *ctx, git_oid_t oid_type)
{
- if (ctx->line_len < GIT_OID_SHA1_HEXSIZE)
+ size_t oid_hexsize = git_oid_hexsize(oid_type);
+ GIT_ASSERT(oid_hexsize);
+
+ if (ctx->line_len < oid_hexsize)
return -1;
- if ((git_oid__fromstrn(out, ctx->line, GIT_OID_SHA1_HEXSIZE, GIT_OID_SHA1)) < 0)
+ if ((git_oid__fromstrn(out, ctx->line, oid_hexsize, oid_type)) < 0)
return -1;
- git_parse_advance_chars(ctx, GIT_OID_SHA1_HEXSIZE);
+ git_parse_advance_chars(ctx, oid_hexsize);
return 0;
}
diff --git a/src/libgit2/parse.h b/src/libgit2/parse.h
index 0ecb7c103b9..beef1de12fb 100644
--- a/src/libgit2/parse.h
+++ b/src/libgit2/parse.h
@@ -50,7 +50,7 @@ int git_parse_advance_expected(
int git_parse_advance_ws(git_parse_ctx *ctx);
int git_parse_advance_nl(git_parse_ctx *ctx);
int git_parse_advance_digit(int64_t *out, git_parse_ctx *ctx, int base);
-int git_parse_advance_oid(git_oid *out, git_parse_ctx *ctx);
+int git_parse_advance_oid(git_oid *out, git_parse_ctx *ctx, git_oid_t oid_type);
enum GIT_PARSE_PEEK_FLAGS {
GIT_PARSE_PEEK_SKIP_WHITESPACE = (1 << 0)
diff --git a/src/libgit2/patch.h b/src/libgit2/patch.h
index 1e1471ed613..86328e886e7 100644
--- a/src/libgit2/patch.h
+++ b/src/libgit2/patch.h
@@ -59,9 +59,15 @@ typedef struct {
* This prefix will be removed when looking for files. The default is 1.
*/
uint32_t prefix_len;
+
+ /**
+ * The type of object IDs in the patch file. The default is
+ * `GIT_OID_DEFAULT`.
+ */
+ git_oid_t oid_type;
} git_patch_options;
-#define GIT_PATCH_OPTIONS_INIT { 1 }
+#define GIT_PATCH_OPTIONS_INIT { 1, GIT_OID_DEFAULT }
extern int git_patch__to_buf(git_str *out, git_patch *patch);
extern void git_patch_free(git_patch *patch);
diff --git a/src/libgit2/patch_generate.c b/src/libgit2/patch_generate.c
index bc598fea870..079bc53ae9b 100644
--- a/src/libgit2/patch_generate.c
+++ b/src/libgit2/patch_generate.c
@@ -81,7 +81,8 @@ static void patch_generated_init_common(git_patch_generated *patch)
static int patch_generated_normalize_options(
git_diff_options *out,
- const git_diff_options *opts)
+ const git_diff_options *opts,
+ git_repository *repo)
{
if (opts) {
GIT_ERROR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
@@ -91,6 +92,23 @@ static int patch_generated_normalize_options(
memcpy(out, &default_opts, sizeof(git_diff_options));
}
+ if (repo && opts && opts->oid_type && repo->oid_type != opts->oid_type) {
+ /*
+ * This limitation feels unnecessary - we should consider
+ * allowing users to generate diffs with a different object
+ * ID format than the repository.
+ */
+ git_error_set(GIT_ERROR_INVALID,
+ "specified object ID type does not match repository object ID type");
+ return -1;
+ } else if (repo) {
+ out->oid_type = repo->oid_type;
+ } else if (opts && opts->oid_type) {
+ out->oid_type = opts->oid_type;
+ } else {
+ out->oid_type = GIT_OID_DEFAULT;
+ }
+
out->old_prefix = opts && opts->old_prefix ?
git__strdup(opts->old_prefix) :
git__strdup(DIFF_OLD_PREFIX_DEFAULT);
@@ -118,7 +136,7 @@ static int patch_generated_init(
patch->delta_index = delta_index;
if ((error = patch_generated_normalize_options(
- &patch->base.diff_opts, &diff->opts)) < 0 ||
+ &patch->base.diff_opts, &diff->opts, diff->repo)) < 0 ||
(error = git_diff_file_content__init_from_diff(
&patch->ofile, diff, patch->base.delta, true)) < 0 ||
(error = git_diff_file_content__init_from_diff(
@@ -449,7 +467,7 @@ static int patch_generated_from_sources(
git_xdiff_output *xo,
git_diff_file_content_src *oldsrc,
git_diff_file_content_src *newsrc,
- const git_diff_options *opts)
+ const git_diff_options *given_opts)
{
int error = 0;
git_repository *repo =
@@ -457,11 +475,12 @@ static int patch_generated_from_sources(
newsrc->blob ? git_blob_owner(newsrc->blob) : NULL;
git_diff_file *lfile = &pd->delta.old_file, *rfile = &pd->delta.new_file;
git_diff_file_content *ldata = &pd->patch.ofile, *rdata = &pd->patch.nfile;
+ git_diff_options *opts = &pd->patch.base.diff_opts;
- if ((error = patch_generated_normalize_options(&pd->patch.base.diff_opts, opts)) < 0)
+ if ((error = patch_generated_normalize_options(opts, given_opts, repo)) < 0)
return error;
- if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
+ if ((opts->flags & GIT_DIFF_REVERSE) != 0) {
void *tmp = lfile; lfile = rfile; rfile = tmp;
tmp = ldata; ldata = rdata; rdata = tmp;
}
diff --git a/src/libgit2/patch_parse.c b/src/libgit2/patch_parse.c
index ffdb99231a2..04f2a582ab1 100644
--- a/src/libgit2/patch_parse.c
+++ b/src/libgit2/patch_parse.c
@@ -166,15 +166,19 @@ static int parse_header_oid(
uint16_t *oid_len,
git_patch_parse_ctx *ctx)
{
- size_t len;
+ size_t hexsize, len;
+
+ hexsize = git_oid_hexsize(ctx->opts.oid_type);
- for (len = 0; len < ctx->parse_ctx.line_len && len < GIT_OID_SHA1_HEXSIZE; len++) {
+ for (len = 0;
+ len < ctx->parse_ctx.line_len && len < hexsize;
+ len++) {
if (!git__isxdigit(ctx->parse_ctx.line[len]))
break;
}
- if (len < GIT_OID_MINPREFIXLEN || len > GIT_OID_SHA1_HEXSIZE ||
- git_oid__fromstrn(oid, ctx->parse_ctx.line, len, GIT_OID_SHA1) < 0)
+ if (len < GIT_OID_MINPREFIXLEN || len > hexsize ||
+ git_oid__fromstrn(oid, ctx->parse_ctx.line, len, ctx->opts.oid_type) < 0)
return git_parse_err("invalid hex formatted object id at line %"PRIuZ,
ctx->parse_ctx.line_num);
@@ -558,9 +562,9 @@ static int parse_hunk_header(
static int eof_for_origin(int origin) {
if (origin == GIT_DIFF_LINE_ADDITION)
- return GIT_DIFF_LINE_ADD_EOFNL;
- if (origin == GIT_DIFF_LINE_DELETION)
return GIT_DIFF_LINE_DEL_EOFNL;
+ if (origin == GIT_DIFF_LINE_DELETION)
+ return GIT_DIFF_LINE_ADD_EOFNL;
return GIT_DIFF_LINE_CONTEXT_EOFNL;
}
@@ -1065,12 +1069,14 @@ static int check_patch(git_patch_parsed *patch)
return git_parse_err("patch with no hunks");
if (delta->status == GIT_DELTA_ADDED) {
- git_oid_clear(&delta->old_file.id, GIT_OID_SHA1);
+ git_oid_clear(&delta->old_file.id,
+ patch->base.diff_opts.oid_type);
delta->old_file.id_abbrev = 0;
}
if (delta->status == GIT_DELTA_DELETED) {
- git_oid_clear(&delta->new_file.id, GIT_OID_SHA1);
+ git_oid_clear(&delta->new_file.id,
+ patch->base.diff_opts.oid_type);
delta->new_file.id_abbrev = 0;
}
@@ -1187,11 +1193,13 @@ int git_patch_parse(
patch->base.delta->status = GIT_DELTA_MODIFIED;
patch->base.delta->nfiles = 2;
+ patch->base.diff_opts.oid_type = ctx->opts.oid_type;
+
start = ctx->parse_ctx.remain_len;
if ((error = parse_patch_header(patch, ctx)) < 0 ||
- (error = parse_patch_body(patch, ctx)) < 0 ||
- (error = check_patch(patch)) < 0)
+ (error = parse_patch_body(patch, ctx)) < 0 ||
+ (error = check_patch(patch)) < 0)
goto done;
used = start - ctx->parse_ctx.remain_len;
diff --git a/src/libgit2/path.c b/src/libgit2/path.c
index a19340efe6f..4b584fb8056 100644
--- a/src/libgit2/path.c
+++ b/src/libgit2/path.c
@@ -202,7 +202,7 @@ GIT_INLINE(size_t) common_prefix_icase(const char *str, size_t len, const char *
{
size_t count = 0;
- while (len > 0 && tolower(*str) == tolower(*prefix)) {
+ while (len > 0 && git__tolower(*str) == git__tolower(*prefix)) {
count++;
str++;
prefix++;
diff --git a/src/libgit2/push.c b/src/libgit2/push.c
index e256818707a..e065858826a 100644
--- a/src/libgit2/push.c
+++ b/src/libgit2/push.c
@@ -68,6 +68,14 @@ int git_push_new(git_push **out, git_remote *remote, const git_push_options *opt
return -1;
}
+ if (git_vector_init(&p->remote_push_options, 0, git__strcmp_cb) < 0) {
+ git_vector_free(&p->status);
+ git_vector_free(&p->specs);
+ git_vector_free(&p->updates);
+ git__free(p);
+ return -1;
+ }
+
*out = p;
return 0;
}
@@ -118,8 +126,8 @@ static int parse_refspec(git_push *push, push_spec **spec, const char *str)
s = git__calloc(1, sizeof(*s));
GIT_ERROR_CHECK_ALLOC(s);
- git_oid_clear(&s->loid, GIT_OID_SHA1);
- git_oid_clear(&s->roid, GIT_OID_SHA1);
+ git_oid_clear(&s->loid, push->repo->oid_type);
+ git_oid_clear(&s->roid, push->repo->oid_type);
if (git_refspec__parse(&s->refspec, str, false) < 0) {
git_error_set(GIT_ERROR_INVALID, "invalid refspec %s", str);
@@ -444,10 +452,21 @@ static int do_push(git_push *push)
if ((error = calculate_work(push)) < 0)
goto on_error;
- if (callbacks && callbacks->push_negotiation &&
- (error = callbacks->push_negotiation((const git_push_update **) push->updates.contents,
- push->updates.length, callbacks->payload)) < 0)
- goto on_error;
+ if (callbacks && callbacks->push_negotiation) {
+ git_error_clear();
+
+ error = callbacks->push_negotiation(
+ (const git_push_update **) push->updates.contents,
+ push->updates.length, callbacks->payload);
+
+ if (error < 0) {
+ git_error_set_after_callback_function(error,
+ "push_negotiation");
+ goto on_error;
+ }
+
+ error = 0;
+ }
if ((error = queue_objects(push)) < 0 ||
(error = transport->push(transport, push)) < 0)
@@ -479,12 +498,24 @@ static int filter_refs(git_remote *remote)
int git_push_finish(git_push *push)
{
int error;
+ unsigned int remote_caps;
if (!git_remote_connected(push->remote)) {
git_error_set(GIT_ERROR_NET, "remote is disconnected");
return -1;
}
+ if ((error = git_remote_capabilities(&remote_caps, push->remote)) < 0) {
+ git_error_set(GIT_ERROR_INVALID, "remote capabilities not available");
+ return -1;
+ }
+
+ if (git_vector_length(&push->remote_push_options) > 0 &&
+ !(remote_caps & GIT_REMOTE_CAPABILITY_PUSH_OPTIONS)) {
+ git_error_set(GIT_ERROR_INVALID, "push-options not supported by remote");
+ return -1;
+ }
+
if ((error = filter_refs(push->remote)) < 0 ||
(error = do_push(push)) < 0)
return error;
@@ -528,6 +559,7 @@ void git_push_free(git_push *push)
push_spec *spec;
push_status *status;
git_push_update *update;
+ char *option;
unsigned int i;
if (push == NULL)
@@ -550,6 +582,11 @@ void git_push_free(git_push *push)
}
git_vector_free(&push->updates);
+ git_vector_foreach(&push->remote_push_options, i, option) {
+ git__free(option);
+ }
+ git_vector_free(&push->remote_push_options);
+
git__free(push);
}
diff --git a/src/libgit2/push.h b/src/libgit2/push.h
index fc72e845eee..40a1823e45b 100644
--- a/src/libgit2/push.h
+++ b/src/libgit2/push.h
@@ -34,6 +34,7 @@ struct git_push {
git_vector specs;
git_vector updates;
bool report_status;
+ git_vector remote_push_options;
/* report-status */
bool unpack_ok;
diff --git a/src/libgit2/reader.c b/src/libgit2/reader.c
index be29bb41c11..df2b2807f55 100644
--- a/src/libgit2/reader.c
+++ b/src/libgit2/reader.c
@@ -125,7 +125,7 @@ static int workdir_reader_read(
goto done;
if (out_id || reader->index) {
- if ((error = git_odb__hash(&id, out->ptr, out->size, GIT_OBJECT_BLOB, GIT_OID_SHA1)) < 0)
+ if ((error = git_odb__hash(&id, out->ptr, out->size, GIT_OBJECT_BLOB, reader->repo->oid_type)) < 0)
goto done;
}
diff --git a/src/libgit2/rebase.c b/src/libgit2/rebase.c
index 1970d5ddc60..77e442e981d 100644
--- a/src/libgit2/rebase.c
+++ b/src/libgit2/rebase.c
@@ -65,6 +65,9 @@ struct git_rebase {
git_rebase_t type;
char *state_path;
+ /* Temporary buffer for paths within the state path. */
+ git_str state_filename;
+
unsigned int head_detached:1,
inmemory:1,
quiet:1,
@@ -134,33 +137,42 @@ static int rebase_state_type(
GIT_INLINE(int) rebase_readfile(
git_str *out,
- git_str *state_path,
+ git_rebase *rebase,
const char *filename)
{
- size_t state_path_len = state_path->size;
+ /*
+ * `rebase->state_filename` is a temporary buffer to avoid
+ * unnecessary allocations and copies of `rebase->state_path`.
+ * At the start and end of this function it always contains the
+ * contents of `rebase->state_path` itself.
+ */
+ size_t state_path_len = rebase->state_filename.size;
int error;
git_str_clear(out);
- if ((error = git_str_joinpath(state_path, state_path->ptr, filename)) < 0 ||
- (error = git_futils_readbuffer(out, state_path->ptr)) < 0)
+ if ((error = git_str_joinpath(&rebase->state_filename, rebase->state_filename.ptr, filename)) < 0 ||
+ (error = git_futils_readbuffer(out, rebase->state_filename.ptr)) < 0)
goto done;
git_str_rtrim(out);
done:
- git_str_truncate(state_path, state_path_len);
+ git_str_truncate(&rebase->state_filename, state_path_len);
return error;
}
GIT_INLINE(int) rebase_readint(
- size_t *out, git_str *asc_out, git_str *state_path, const char *filename)
+ size_t *out,
+ git_str *asc_out,
+ git_rebase *rebase,
+ const char *filename)
{
int32_t num;
const char *eol;
int error = 0;
- if ((error = rebase_readfile(asc_out, state_path, filename)) < 0)
+ if ((error = rebase_readfile(asc_out, rebase, filename)) < 0)
return error;
if (git__strntol32(&num, asc_out->ptr, asc_out->size, &eol, 10) < 0 || num < 0 || *eol) {
@@ -174,15 +186,18 @@ GIT_INLINE(int) rebase_readint(
}
GIT_INLINE(int) rebase_readoid(
- git_oid *out, git_str *str_out, git_str *state_path, const char *filename)
+ git_oid *out,
+ git_str *str_out,
+ git_rebase *rebase,
+ const char *filename)
{
int error;
- if ((error = rebase_readfile(str_out, state_path, filename)) < 0)
+ if ((error = rebase_readfile(str_out, rebase, filename)) < 0)
return error;
- if (str_out->size != GIT_OID_SHA1_HEXSIZE ||
- git_oid__fromstr(out, str_out->ptr, GIT_OID_SHA1) < 0) {
+ if (str_out->size != git_oid_hexsize(rebase->repo->oid_type) ||
+ git_oid__fromstr(out, str_out->ptr, rebase->repo->oid_type) < 0) {
git_error_set(GIT_ERROR_REBASE, "the file '%s' contains an invalid object ID", filename);
return -1;
}
@@ -213,17 +228,14 @@ static git_rebase_operation *rebase_operation_alloc(
static int rebase_open_merge(git_rebase *rebase)
{
- git_str state_path = GIT_STR_INIT, buf = GIT_STR_INIT, cmt = GIT_STR_INIT;
+ git_str buf = GIT_STR_INIT, cmt = GIT_STR_INIT;
git_oid id;
git_rebase_operation *operation;
size_t i, msgnum = 0, end;
int error;
- if ((error = git_str_puts(&state_path, rebase->state_path)) < 0)
- goto done;
-
/* Read 'msgnum' if it exists (otherwise, let msgnum = 0) */
- if ((error = rebase_readint(&msgnum, &buf, &state_path, MSGNUM_FILE)) < 0 &&
+ if ((error = rebase_readint(&msgnum, &buf, rebase, MSGNUM_FILE)) < 0 &&
error != GIT_ENOTFOUND)
goto done;
@@ -233,11 +245,11 @@ static int rebase_open_merge(git_rebase *rebase)
}
/* Read 'end' */
- if ((error = rebase_readint(&end, &buf, &state_path, END_FILE)) < 0)
+ if ((error = rebase_readint(&end, &buf, rebase, END_FILE)) < 0)
goto done;
/* Read 'current' if it exists */
- if ((error = rebase_readoid(&id, &buf, &state_path, CURRENT_FILE)) < 0 &&
+ if ((error = rebase_readoid(&id, &buf, rebase, CURRENT_FILE)) < 0 &&
error != GIT_ENOTFOUND)
goto done;
@@ -249,7 +261,7 @@ static int rebase_open_merge(git_rebase *rebase)
git_str_clear(&cmt);
if ((error = git_str_printf(&cmt, "cmt.%" PRIuZ, (i+1))) < 0 ||
- (error = rebase_readoid(&id, &buf, &state_path, cmt.ptr)) < 0)
+ (error = rebase_readoid(&id, &buf, rebase, cmt.ptr)) < 0)
goto done;
operation = rebase_operation_alloc(rebase, GIT_REBASE_OPERATION_PICK, &id, NULL);
@@ -257,14 +269,13 @@ static int rebase_open_merge(git_rebase *rebase)
}
/* Read 'onto_name' */
- if ((error = rebase_readfile(&buf, &state_path, ONTO_NAME_FILE)) < 0)
+ if ((error = rebase_readfile(&buf, rebase, ONTO_NAME_FILE)) < 0)
goto done;
rebase->onto_name = git_str_detach(&buf);
done:
git_str_dispose(&cmt);
- git_str_dispose(&state_path);
git_str_dispose(&buf);
return error;
@@ -308,9 +319,9 @@ int git_rebase_open(
const git_rebase_options *given_opts)
{
git_rebase *rebase;
- git_str path = GIT_STR_INIT, orig_head_name = GIT_STR_INIT,
- orig_head_id = GIT_STR_INIT, onto_id = GIT_STR_INIT;
- size_t state_path_len;
+ git_str orig_head_name = GIT_STR_INIT,
+ orig_head_id = GIT_STR_INIT,
+ onto_id = GIT_STR_INIT;
int error;
GIT_ASSERT_ARG(repo);
@@ -332,13 +343,10 @@ int git_rebase_open(
goto done;
}
- if ((error = git_str_puts(&path, rebase->state_path)) < 0)
+ if ((error = git_str_puts(&rebase->state_filename, rebase->state_path)) < 0)
goto done;
- state_path_len = git_str_len(&path);
-
- if ((error = git_str_joinpath(&path, path.ptr, HEAD_NAME_FILE)) < 0 ||
- (error = git_futils_readbuffer(&orig_head_name, path.ptr)) < 0)
+ if ((error = rebase_readfile(&orig_head_name, rebase, HEAD_NAME_FILE)) < 0)
goto done;
git_str_rtrim(&orig_head_name);
@@ -346,36 +354,16 @@ int git_rebase_open(
if (strcmp(ORIG_DETACHED_HEAD, orig_head_name.ptr) == 0)
rebase->head_detached = 1;
- git_str_truncate(&path, state_path_len);
-
- if ((error = git_str_joinpath(&path, path.ptr, ORIG_HEAD_FILE)) < 0)
- goto done;
-
- if (!git_fs_path_isfile(path.ptr)) {
+ if ((error = rebase_readoid(&rebase->orig_head_id, &orig_head_id, rebase, ORIG_HEAD_FILE)) < 0) {
/* Previous versions of git.git used 'head' here; support that. */
- git_str_truncate(&path, state_path_len);
+ if (error == GIT_ENOTFOUND)
+ error = rebase_readoid(&rebase->orig_head_id, &orig_head_id, rebase, HEAD_FILE);
- if ((error = git_str_joinpath(&path, path.ptr, HEAD_FILE)) < 0)
+ if (error < 0)
goto done;
}
- if ((error = git_futils_readbuffer(&orig_head_id, path.ptr)) < 0)
- goto done;
-
- git_str_rtrim(&orig_head_id);
-
- if ((error = git_oid__fromstr(&rebase->orig_head_id, orig_head_id.ptr, GIT_OID_SHA1)) < 0)
- goto done;
-
- git_str_truncate(&path, state_path_len);
-
- if ((error = git_str_joinpath(&path, path.ptr, ONTO_FILE)) < 0 ||
- (error = git_futils_readbuffer(&onto_id, path.ptr)) < 0)
- goto done;
-
- git_str_rtrim(&onto_id);
-
- if ((error = git_oid__fromstr(&rebase->onto_id, onto_id.ptr, GIT_OID_SHA1)) < 0)
+ if ((error = rebase_readoid(&rebase->onto_id, &onto_id, rebase, ONTO_FILE)) < 0)
goto done;
if (!rebase->head_detached)
@@ -403,7 +391,6 @@ int git_rebase_open(
else
git_rebase_free(rebase);
- git_str_dispose(&path);
git_str_dispose(&orig_head_name);
git_str_dispose(&orig_head_id);
git_str_dispose(&onto_id);
@@ -453,13 +440,13 @@ static const char *rebase_onto_name(const git_annotated_commit *onto)
static int rebase_setupfiles_merge(git_rebase *rebase)
{
git_str commit_filename = GIT_STR_INIT;
- char id_str[GIT_OID_SHA1_HEXSIZE];
+ char id_str[GIT_OID_MAX_HEXSIZE + 1];
git_rebase_operation *operation;
size_t i;
int error = 0;
if ((error = rebase_setupfile(rebase, END_FILE, 0, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 ||
- (error = rebase_setupfile(rebase, ONTO_NAME_FILE, 0, "%s\n", rebase->onto_name)) < 0)
+ (error = rebase_setupfile(rebase, ONTO_NAME_FILE, 0, "%s\n", rebase->onto_name)) < 0)
goto done;
for (i = 0; i < git_array_size(rebase->operations); i++) {
@@ -468,10 +455,9 @@ static int rebase_setupfiles_merge(git_rebase *rebase)
git_str_clear(&commit_filename);
git_str_printf(&commit_filename, CMT_FILE_FMT, i+1);
- git_oid_fmt(id_str, &operation->id);
+ git_oid_tostr(id_str, GIT_OID_MAX_HEXSIZE + 1, &operation->id);
- if ((error = rebase_setupfile(rebase, commit_filename.ptr, 0,
- "%.*s\n", GIT_OID_SHA1_HEXSIZE, id_str)) < 0)
+ if ((error = rebase_setupfile(rebase, commit_filename.ptr, 0, "%s\n", id_str)) < 0)
goto done;
}
@@ -482,11 +468,11 @@ static int rebase_setupfiles_merge(git_rebase *rebase)
static int rebase_setupfiles(git_rebase *rebase)
{
- char onto[GIT_OID_SHA1_HEXSIZE], orig_head[GIT_OID_SHA1_HEXSIZE];
+ char onto[GIT_OID_MAX_HEXSIZE + 1], orig_head[GIT_OID_MAX_HEXSIZE + 1];
const char *orig_head_name;
- git_oid_fmt(onto, &rebase->onto_id);
- git_oid_fmt(orig_head, &rebase->orig_head_id);
+ git_oid_tostr(onto, GIT_OID_MAX_HEXSIZE + 1, &rebase->onto_id);
+ git_oid_tostr(orig_head, GIT_OID_MAX_HEXSIZE + 1, &rebase->orig_head_id);
if (p_mkdir(rebase->state_path, REBASE_DIR_MODE) < 0) {
git_error_set(GIT_ERROR_OS, "failed to create rebase directory '%s'", rebase->state_path);
@@ -498,8 +484,8 @@ static int rebase_setupfiles(git_rebase *rebase)
if (git_repository__set_orig_head(rebase->repo, &rebase->orig_head_id) < 0 ||
rebase_setupfile(rebase, HEAD_NAME_FILE, 0, "%s\n", orig_head_name) < 0 ||
- rebase_setupfile(rebase, ONTO_FILE, 0, "%.*s\n", GIT_OID_SHA1_HEXSIZE, onto) < 0 ||
- rebase_setupfile(rebase, ORIG_HEAD_FILE, 0, "%.*s\n", GIT_OID_SHA1_HEXSIZE, orig_head) < 0 ||
+ rebase_setupfile(rebase, ONTO_FILE, 0, "%s\n", onto) < 0 ||
+ rebase_setupfile(rebase, ORIG_HEAD_FILE, 0, "%s\n", orig_head) < 0 ||
rebase_setupfile(rebase, QUIET_FILE, 0, rebase->quiet ? "t\n" : "\n") < 0)
return -1;
@@ -644,7 +630,8 @@ static int rebase_init_merge(
GIT_UNUSED(upstream);
- if ((error = git_str_joinpath(&state_path, repo->gitdir, REBASE_MERGE_DIR)) < 0)
+ if ((error = git_str_joinpath(&state_path, repo->gitdir, REBASE_MERGE_DIR)) < 0 ||
+ (error = git_str_put(&rebase->state_filename, state_path.ptr, state_path.size)) < 0)
goto done;
rebase->state_path = git_str_detach(&state_path);
@@ -814,7 +801,7 @@ static int rebase_next_merge(
git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
git_rebase_operation *operation;
git_checkout_options checkout_opts;
- char current_idstr[GIT_OID_SHA1_HEXSIZE];
+ char current_idstr[GIT_OID_MAX_HEXSIZE + 1];
unsigned int parent_count;
int error;
@@ -837,13 +824,13 @@ static int rebase_next_merge(
goto done;
}
- git_oid_fmt(current_idstr, &operation->id);
+ git_oid_tostr(current_idstr, GIT_OID_MAX_HEXSIZE + 1, &operation->id);
normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit);
if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 ||
(error = rebase_setupfile(rebase, MSGNUM_FILE, 0, "%" PRIuZ "\n", rebase->current+1)) < 0 ||
- (error = rebase_setupfile(rebase, CURRENT_FILE, 0, "%.*s\n", GIT_OID_SHA1_HEXSIZE, current_idstr)) < 0 ||
+ (error = rebase_setupfile(rebase, CURRENT_FILE, 0, "%s\n", current_idstr)) < 0 ||
(error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0 ||
(error = git_merge__check_result(rebase->repo, index)) < 0 ||
(error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 ||
@@ -1103,7 +1090,7 @@ static int rebase_commit_merge(
git_reference *head = NULL;
git_commit *head_commit = NULL, *commit = NULL;
git_index *index = NULL;
- char old_idstr[GIT_OID_SHA1_HEXSIZE], new_idstr[GIT_OID_SHA1_HEXSIZE];
+ char old_idstr[GIT_OID_MAX_HEXSIZE + 1], new_idstr[GIT_OID_MAX_HEXSIZE + 1];
int error;
operation = git_array_get(rebase->operations, rebase->current);
@@ -1119,11 +1106,11 @@ static int rebase_commit_merge(
rebase->repo, NULL, "HEAD", git_commit_id(commit), "rebase")) < 0)
goto done;
- git_oid_fmt(old_idstr, &operation->id);
- git_oid_fmt(new_idstr, git_commit_id(commit));
+ git_oid_tostr(old_idstr, GIT_OID_MAX_HEXSIZE + 1, &operation->id);
+ git_oid_tostr(new_idstr, GIT_OID_MAX_HEXSIZE + 1, git_commit_id(commit));
if ((error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND,
- "%.*s %.*s\n", GIT_OID_SHA1_HEXSIZE, old_idstr, GIT_OID_SHA1_HEXSIZE, new_idstr)) < 0)
+ "%s %s\n", old_idstr, new_idstr)) < 0)
goto done;
git_oid_cpy(commit_id, git_commit_id(commit));
@@ -1306,7 +1293,9 @@ static int rebase_copy_notes(
git_rebase *rebase,
const git_signature *committer)
{
- git_str path = GIT_STR_INIT, rewritten = GIT_STR_INIT, notes_ref = GIT_STR_INIT;
+ git_str path = GIT_STR_INIT,
+ rewritten = GIT_STR_INIT,
+ notes_ref = GIT_STR_INIT;
char *pair_list, *fromstr, *tostr, *end;
git_oid from, to;
unsigned int linenum = 1;
@@ -1342,10 +1331,10 @@ static int rebase_copy_notes(
tostr = end+1;
*end = '\0';
- if (strlen(fromstr) != GIT_OID_SHA1_HEXSIZE ||
- strlen(tostr) != GIT_OID_SHA1_HEXSIZE ||
- git_oid__fromstr(&from, fromstr, GIT_OID_SHA1) < 0 ||
- git_oid__fromstr(&to, tostr, GIT_OID_SHA1) < 0)
+ if (strlen(fromstr) != git_oid_hexsize(rebase->repo->oid_type) ||
+ strlen(tostr) != git_oid_hexsize(rebase->repo->oid_type) ||
+ git_oid__fromstr(&from, fromstr, rebase->repo->oid_type) < 0 ||
+ git_oid__fromstr(&to, tostr, rebase->repo->oid_type) < 0)
goto on_error;
if ((error = rebase_copy_note(rebase, notes_ref.ptr, &from, &to, committer)) < 0)
@@ -1373,17 +1362,15 @@ static int return_to_orig_head(git_rebase *rebase)
git_reference *terminal_ref = NULL, *branch_ref = NULL, *head_ref = NULL;
git_commit *terminal_commit = NULL;
git_str branch_msg = GIT_STR_INIT, head_msg = GIT_STR_INIT;
- char onto[GIT_OID_SHA1_HEXSIZE];
+ char onto[GIT_OID_MAX_HEXSIZE + 1];
int error = 0;
- git_oid_fmt(onto, &rebase->onto_id);
+ git_oid_tostr(onto, GIT_OID_MAX_HEXSIZE + 1, &rebase->onto_id);
if ((error = git_str_printf(&branch_msg,
- "rebase finished: %s onto %.*s",
- rebase->orig_head_name, GIT_OID_SHA1_HEXSIZE, onto)) == 0 &&
+ "rebase finished: %s onto %s", rebase->orig_head_name, onto)) == 0 &&
(error = git_str_printf(&head_msg,
- "rebase finished: returning to %s",
- rebase->orig_head_name)) == 0 &&
+ "rebase finished: returning to %s", rebase->orig_head_name)) == 0 &&
(error = git_repository_head(&terminal_ref, rebase->repo)) == 0 &&
(error = git_reference_peel((git_object **)&terminal_commit,
terminal_ref, GIT_OBJECT_COMMIT)) == 0 &&
@@ -1475,6 +1462,7 @@ void git_rebase_free(git_rebase *rebase)
git__free(rebase->onto_name);
git__free(rebase->orig_head_name);
git__free(rebase->state_path);
+ git_str_dispose(&rebase->state_filename);
git_array_clear(rebase->operations);
git__free((char *)rebase->options.rewrite_notes_ref);
git__free(rebase);
diff --git a/src/libgit2/refdb_fs.c b/src/libgit2/refdb_fs.c
index 11ad8694664..4af42ed742f 100644
--- a/src/libgit2/refdb_fs.c
+++ b/src/libgit2/refdb_fs.c
@@ -60,15 +60,17 @@ typedef struct refdb_fs_backend {
/* path to common objects' directory */
char *commonpath;
- git_sortedcache *refcache;
+ git_oid_t oid_type;
+
+ unsigned int fsync : 1,
+ sorted : 1;
int peeling_mode;
git_iterator_flag_t iterator_flags;
uint32_t direach_flags;
- int fsync;
+ git_sortedcache *refcache;
git_map packed_refs_map;
git_mutex prlock; /* protect packed_refs_map */
git_futils_filestamp packed_refs_stamp;
- bool sorted;
} refdb_fs_backend;
static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name);
@@ -113,6 +115,7 @@ static int packed_reload(refdb_fs_backend *backend)
{
int error;
git_str packedrefs = GIT_STR_INIT;
+ size_t oid_hexsize = git_oid_hexsize(backend->oid_type);
char *scan, *eof, *eol;
if (!backend->gitpath)
@@ -158,9 +161,9 @@ static int packed_reload(refdb_fs_backend *backend)
/* parse " \n" */
- if (git_oid__fromstr(&oid, scan, GIT_OID_SHA1) < 0)
+ if (git_oid__fromstr(&oid, scan, backend->oid_type) < 0)
goto parse_failed;
- scan += GIT_OID_SHA1_HEXSIZE;
+ scan += oid_hexsize;
if (*scan++ != ' ')
goto parse_failed;
@@ -179,9 +182,9 @@ static int packed_reload(refdb_fs_backend *backend)
/* look for optional "^\n" */
if (*scan == '^') {
- if (git_oid__fromstr(&oid, scan + 1, GIT_OID_SHA1) < 0)
+ if (git_oid__fromstr(&oid, scan + 1, backend->oid_type) < 0)
goto parse_failed;
- scan += GIT_OID_SHA1_HEXSIZE + 1;
+ scan += oid_hexsize + 1;
if (scan < eof) {
if (!(eol = strchr(scan, '\n')))
@@ -214,19 +217,23 @@ static int packed_reload(refdb_fs_backend *backend)
}
static int loose_parse_oid(
- git_oid *oid, const char *filename, git_str *file_content)
+ git_oid *oid,
+ const char *filename,
+ git_str *file_content,
+ git_oid_t oid_type)
{
const char *str = git_str_cstr(file_content);
+ size_t oid_hexsize = git_oid_hexsize(oid_type);
- if (git_str_len(file_content) < GIT_OID_SHA1_HEXSIZE)
+ if (git_str_len(file_content) < oid_hexsize)
goto corrupted;
/* we need to get 40 OID characters from the file */
- if (git_oid__fromstr(oid, str, GIT_OID_SHA1) < 0)
+ if (git_oid__fromstr(oid, str, oid_type) < 0)
goto corrupted;
/* If the file is longer than 40 chars, the 41st must be a space */
- str += GIT_OID_SHA1_HEXSIZE;
+ str += oid_hexsize;
if (*str == '\0' || git__isspace(*str))
return 0;
@@ -266,7 +273,7 @@ static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name)
goto done;
/* parse OID from file */
- if ((error = loose_parse_oid(&oid, name, &ref_file)) < 0)
+ if ((error = loose_parse_oid(&oid, name, &ref_file, backend->oid_type)) < 0)
goto done;
if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
@@ -403,7 +410,9 @@ static const char *loose_parse_symbolic(git_str *file_content)
static bool is_per_worktree_ref(const char *ref_name)
{
return git__prefixcmp(ref_name, "refs/") != 0 ||
- git__prefixcmp(ref_name, "refs/bisect/") == 0;
+ git__prefixcmp(ref_name, "refs/bisect/") == 0 ||
+ git__prefixcmp(ref_name, "refs/worktree/") == 0 ||
+ git__prefixcmp(ref_name, "refs/rewritten/") == 0;
}
static int loose_lookup(
@@ -437,7 +446,7 @@ static int loose_lookup(
} else {
git_oid oid;
- if (!(error = loose_parse_oid(&oid, ref_name, &ref_file)) &&
+ if (!(error = loose_parse_oid(&oid, ref_name, &ref_file, backend->oid_type)) &&
out != NULL)
*out = git_reference__alloc(ref_name, &oid, NULL);
}
@@ -615,19 +624,24 @@ static const char *end_of_record(const char *p, const char *end)
return p;
}
-static int
-cmp_record_to_refname(const char *rec, size_t data_end, const char *ref_name)
+static int cmp_record_to_refname(
+ const char *rec,
+ size_t data_end,
+ const char *ref_name,
+ git_oid_t oid_type)
{
const size_t ref_len = strlen(ref_name);
int cmp_val;
const char *end;
+ size_t oid_hexsize = git_oid_hexsize(oid_type);
+
+ rec += oid_hexsize + 1; /* + space */
- rec += GIT_OID_SHA1_HEXSIZE + 1; /* + space */
- if (data_end < GIT_OID_SHA1_HEXSIZE + 3) {
- /* an incomplete (corrupt) record is treated as less than ref_name */
+ /* an incomplete (corrupt) record is treated as less than ref_name */
+ if (data_end < oid_hexsize + 3)
return -1;
- }
- data_end -= GIT_OID_SHA1_HEXSIZE + 1;
+
+ data_end -= oid_hexsize + 1;
end = memchr(rec, '\n', data_end);
if (end)
@@ -675,6 +689,7 @@ static int packed_lookup(
{
int error = 0;
const char *left, *right, *data_end;
+ size_t oid_hexsize = git_oid_hexsize(backend->oid_type);
if ((error = packed_map_check(backend)) < 0)
return error;
@@ -698,7 +713,7 @@ static int packed_lookup(
mid = left + (right - left) / 2;
rec = start_of_record(left, mid);
- compare = cmp_record_to_refname(rec, data_end - rec, ref_name);
+ compare = cmp_record_to_refname(rec, data_end - rec, ref_name, backend->oid_type);
if (compare < 0) {
left = end_of_record(mid, right);
@@ -708,11 +723,11 @@ static int packed_lookup(
const char *eol;
git_oid oid, peel, *peel_ptr = NULL;
- if (data_end - rec < GIT_OID_SHA1_HEXSIZE ||
- git_oid__fromstr(&oid, rec, GIT_OID_SHA1) < 0) {
+ if (data_end - rec < (long)oid_hexsize ||
+ git_oid__fromstr(&oid, rec, backend->oid_type) < 0) {
goto parse_failed;
}
- rec += GIT_OID_SHA1_HEXSIZE + 1;
+ rec += oid_hexsize + 1;
if (!(eol = memchr(rec, '\n', data_end - rec))) {
goto parse_failed;
}
@@ -724,8 +739,8 @@ static int packed_lookup(
if (*rec == '^') {
rec++;
- if (data_end - rec < GIT_OID_SHA1_HEXSIZE ||
- git_oid__fromstr(&peel, rec, GIT_OID_SHA1) < 0) {
+ if (data_end - rec < (long)oid_hexsize ||
+ git_oid__fromstr(&peel, rec, backend->oid_type) < 0) {
goto parse_failed;
}
peel_ptr = &peel;
@@ -792,80 +807,149 @@ static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
git__free(iter);
}
-static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
+struct iter_load_context {
+ refdb_fs_backend *backend;
+ refdb_fs_iter *iter;
+
+ /*
+ * If we have a glob with a prefix (eg `refs/heads/ *`) then we can
+ * optimize our prefix to avoid walking refs that we know won't
+ * match. This is that prefix.
+ */
+ const char *ref_prefix;
+ size_t ref_prefix_len;
+
+ /* Temporary variables to avoid unnecessary allocations */
+ git_str ref_name;
+ git_str path;
+};
+
+static void iter_load_optimize_prefix(struct iter_load_context *ctx)
{
- int error = 0;
- git_str path = GIT_STR_INIT;
- git_iterator *fsit = NULL;
- git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT;
- const git_index_entry *entry = NULL;
- const char *ref_prefix = GIT_REFS_DIR;
- size_t ref_prefix_len = strlen(ref_prefix);
+ const char *pos, *last_sep = NULL;
- if (!backend->commonpath) /* do nothing if no commonpath for loose refs */
- return 0;
+ if (!ctx->iter->glob)
+ return;
- fsit_opts.flags = backend->iterator_flags;
-
- if (iter->glob) {
- const char *last_sep = NULL;
- const char *pos;
- for (pos = iter->glob; *pos; ++pos) {
- switch (*pos) {
- case '?':
- case '*':
- case '[':
- case '\\':
- break;
- case '/':
- last_sep = pos;
- /* FALLTHROUGH */
- default:
- continue;
- }
+ for (pos = ctx->iter->glob; *pos; pos++) {
+ switch (*pos) {
+ case '?':
+ case '*':
+ case '[':
+ case '\\':
break;
+ case '/':
+ last_sep = pos;
+ /* FALLTHROUGH */
+ default:
+ continue;
}
- if (last_sep) {
- ref_prefix = iter->glob;
- ref_prefix_len = (last_sep - ref_prefix) + 1;
- }
+ break;
}
- if ((error = git_str_puts(&path, backend->commonpath)) < 0 ||
- (error = git_str_put(&path, ref_prefix, ref_prefix_len)) < 0) {
- git_str_dispose(&path);
- return error;
+ if (last_sep) {
+ ctx->ref_prefix = ctx->iter->glob;
+ ctx->ref_prefix_len = (last_sep - ctx->ref_prefix) + 1;
}
+}
+
+static int iter_load_paths(
+ struct iter_load_context *ctx,
+ const char *root_path,
+ bool worktree)
+{
+ git_iterator *fsit = NULL;
+ git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT;
+ const git_index_entry *entry;
+ int error = 0;
+
+ fsit_opts.flags = ctx->backend->iterator_flags;
+
+ git_str_clear(&ctx->path);
+ git_str_puts(&ctx->path, root_path);
+ git_str_put(&ctx->path, ctx->ref_prefix, ctx->ref_prefix_len);
- if ((error = git_iterator_for_filesystem(&fsit, path.ptr, &fsit_opts)) < 0) {
- git_str_dispose(&path);
- return (iter->glob && error == GIT_ENOTFOUND)? 0 : error;
+ fsit_opts.flags = ctx->backend->iterator_flags;
+ fsit_opts.oid_type = ctx->backend->oid_type;
+
+ if ((error = git_iterator_for_filesystem(&fsit, ctx->path.ptr, &fsit_opts)) < 0) {
+ /*
+ * Subdirectories - either glob provided or per-worktree refs - need
+ * not exist.
+ */
+ if ((worktree || ctx->iter->glob) && error == GIT_ENOTFOUND)
+ error = 0;
+
+ goto done;
}
- error = git_str_sets(&path, ref_prefix);
+ git_str_clear(&ctx->ref_name);
+ git_str_put(&ctx->ref_name, ctx->ref_prefix, ctx->ref_prefix_len);
- while (!error && !git_iterator_advance(&entry, fsit)) {
- const char *ref_name;
+ while (git_iterator_advance(&entry, fsit) == 0) {
char *ref_dup;
- git_str_truncate(&path, ref_prefix_len);
- git_str_puts(&path, entry->path);
- ref_name = git_str_cstr(&path);
+ git_str_truncate(&ctx->ref_name, ctx->ref_prefix_len);
+ git_str_puts(&ctx->ref_name, entry->path);
+
+ if (worktree) {
+ if (!is_per_worktree_ref(ctx->ref_name.ptr))
+ continue;
+ } else {
+ if (git_repository_is_worktree(ctx->backend->repo) &&
+ is_per_worktree_ref(ctx->ref_name.ptr))
+ continue;
+ }
- if (git__suffixcmp(ref_name, ".lock") == 0 ||
- (iter->glob && wildmatch(iter->glob, ref_name, 0) != 0))
+ if (git__suffixcmp(ctx->ref_name.ptr, ".lock") == 0)
continue;
- ref_dup = git_pool_strdup(&iter->pool, ref_name);
- if (!ref_dup)
- error = -1;
- else
- error = git_vector_insert(&iter->loose, ref_dup);
+ if (ctx->iter->glob && wildmatch(ctx->iter->glob, ctx->ref_name.ptr, 0))
+ continue;
+
+ ref_dup = git_pool_strdup(&ctx->iter->pool, ctx->ref_name.ptr);
+ GIT_ERROR_CHECK_ALLOC(ref_dup);
+
+ if ((error = git_vector_insert(&ctx->iter->loose, ref_dup)) < 0)
+ goto done;
}
+done:
git_iterator_free(fsit);
- git_str_dispose(&path);
+ return error;
+}
+
+#define iter_load_context_init(b, i) { b, i, GIT_REFS_DIR, CONST_STRLEN(GIT_REFS_DIR) }
+#define iter_load_context_dispose(ctx) do { \
+ git_str_dispose(&((ctx)->path)); \
+ git_str_dispose(&((ctx)->ref_name)); \
+} while(0)
+
+static int iter_load_loose_paths(
+ refdb_fs_backend *backend,
+ refdb_fs_iter *iter)
+{
+ struct iter_load_context ctx = iter_load_context_init(backend, iter);
+
+ int error = 0;
+
+ if (!backend->commonpath)
+ return 0;
+
+ iter_load_optimize_prefix(&ctx);
+
+ if ((error = iter_load_paths(&ctx,
+ backend->commonpath, false)) < 0)
+ goto done;
+
+ if (git_repository_is_worktree(backend->repo)) {
+ if ((error = iter_load_paths(&ctx,
+ backend->gitpath, true)) < 0)
+ goto done;
+ }
+done:
+ iter_load_context_dispose(&ctx);
return error;
}
@@ -1108,7 +1192,7 @@ static int loose_commit(git_filebuf *file, const git_reference *ref)
GIT_ASSERT_ARG(ref);
if (ref->type == GIT_REFERENCE_DIRECT) {
- char oid[GIT_OID_SHA1_HEXSIZE + 1];
+ char oid[GIT_OID_MAX_HEXSIZE + 1];
git_oid_nfmt(oid, sizeof(oid), &ref->target.oid);
git_filebuf_printf(file, "%s\n", oid);
@@ -1224,7 +1308,7 @@ static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref)
*/
static int packed_write_ref(struct packref *ref, git_filebuf *file)
{
- char oid[GIT_OID_SHA1_HEXSIZE + 1];
+ char oid[GIT_OID_MAX_HEXSIZE + 1];
git_oid_nfmt(oid, sizeof(oid), &ref->oid);
/*
@@ -1238,7 +1322,7 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file)
* The required peels have already been loaded into `ref->peel_target`.
*/
if (ref->flags & PACKREF_HAS_PEEL) {
- char peel[GIT_OID_SHA1_HEXSIZE + 1];
+ char peel[GIT_OID_MAX_HEXSIZE + 1];
git_oid_nfmt(peel, sizeof(peel), &ref->peel);
if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
@@ -1302,7 +1386,7 @@ static int packed_remove_loose(refdb_fs_backend *backend)
continue;
/* Figure out the current id; if we find a bad ref file, skip it so we can do the rest */
- if (loose_parse_oid(¤t_id, lock.path_original, &ref_content) < 0)
+ if (loose_parse_oid(¤t_id, lock.path_original, &ref_content, backend->oid_type) < 0)
continue;
/* If the ref moved since we packed it, we must not delete it */
@@ -1769,7 +1853,7 @@ static int refdb_fs_backend__rename(
(error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0)
return error;
- if ((error = refdb_fs_backend__delete(_backend, old_name, NULL, NULL)) < 0) {
+ if ((error = loose_lock(&file, backend, old->name)) < 0) {
git_reference_free(old);
return error;
}
@@ -1777,32 +1861,33 @@ static int refdb_fs_backend__rename(
new = git_reference__realloc(&old, new_name);
if (!new) {
git_reference_free(old);
+ git_filebuf_cleanup(&file);
return -1;
}
- if ((error = loose_lock(&file, backend, new->name)) < 0) {
+ if ((error = refdb_fs_backend__delete_tail(_backend, &file, old_name, NULL, NULL)) < 0) {
git_reference_free(new);
+ git_filebuf_cleanup(&file);
return error;
}
- /* Try to rename the refog; it's ok if the old doesn't exist */
- error = refdb_reflog_fs__rename(_backend, old_name, new_name);
- if (((error == 0) || (error == GIT_ENOTFOUND)) &&
- ((error = reflog_append(backend, new, git_reference_target(new), NULL, who, message)) < 0)) {
+ if ((error = loose_lock(&file, backend, new_name)) < 0) {
git_reference_free(new);
- git_filebuf_cleanup(&file);
return error;
}
- if (error < 0) {
+ /* Try to rename the refog; it's ok if the old doesn't exist */
+ error = refdb_reflog_fs__rename(_backend, old_name, new_name);
+ if (((error == 0) || (error == GIT_ENOTFOUND)) &&
+ ((error = reflog_append(backend, new, git_reference_target(new), NULL, who, message)) < 0)) {
git_reference_free(new);
git_filebuf_cleanup(&file);
return error;
}
-
if ((error = loose_commit(&file, new)) < 0 || out == NULL) {
git_reference_free(new);
+ git_filebuf_cleanup(&file);
return error;
}
@@ -1891,7 +1976,10 @@ static char *setup_namespace(git_repository *repo, const char *in)
return out;
}
-static int reflog_alloc(git_reflog **reflog, const char *name)
+static int reflog_alloc(
+ git_reflog **reflog,
+ const char *name,
+ git_oid_t oid_type)
{
git_reflog *log;
@@ -1903,6 +1991,8 @@ static int reflog_alloc(git_reflog **reflog, const char *name)
log->ref_name = git__strdup(name);
GIT_ERROR_CHECK_ALLOC(log->ref_name);
+ log->oid_type = oid_type;
+
if (git_vector_init(&log->entries, 0, NULL) < 0) {
git__free(log->ref_name);
git__free(log);
@@ -1931,9 +2021,9 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
entry->committer = git__calloc(1, sizeof(*entry->committer));
GIT_ERROR_CHECK_ALLOC(entry->committer);
- if (git_parse_advance_oid(&entry->oid_old, &parser) < 0 ||
+ if (git_parse_advance_oid(&entry->oid_old, &parser, log->oid_type) < 0 ||
git_parse_advance_expected(&parser, " ", 1) < 0 ||
- git_parse_advance_oid(&entry->oid_cur, &parser) < 0)
+ git_parse_advance_oid(&entry->oid_cur, &parser, log->oid_type) < 0)
goto next;
sig = parser.line;
@@ -2032,7 +2122,10 @@ static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *nam
return has_reflog(backend->repo, name);
}
-static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
+static int refdb_reflog_fs__read(
+ git_reflog **out,
+ git_refdb_backend *_backend,
+ const char *name)
{
int error = -1;
git_str log_path = GIT_STR_INIT;
@@ -2048,7 +2141,7 @@ static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend,
backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
repo = backend->repo;
- if (reflog_alloc(&log, name) < 0)
+ if (reflog_alloc(&log, name, backend->oid_type) < 0)
return -1;
if (reflog_path(&log_path, repo, name) < 0)
@@ -2086,11 +2179,11 @@ static int serialize_reflog_entry(
const git_signature *committer,
const char *msg)
{
- char raw_old[GIT_OID_SHA1_HEXSIZE+1];
- char raw_new[GIT_OID_SHA1_HEXSIZE+1];
+ char raw_old[GIT_OID_MAX_HEXSIZE + 1];
+ char raw_new[GIT_OID_MAX_HEXSIZE + 1];
- git_oid_tostr(raw_old, GIT_OID_SHA1_HEXSIZE+1, oid_old);
- git_oid_tostr(raw_new, GIT_OID_SHA1_HEXSIZE+1, oid_new);
+ git_oid_tostr(raw_old, GIT_OID_MAX_HEXSIZE + 1, oid_old);
+ git_oid_tostr(raw_new, GIT_OID_MAX_HEXSIZE + 1, oid_new);
git_str_clear(buf);
@@ -2189,10 +2282,16 @@ static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflo
}
/* Append to the reflog, must be called under reference lock */
-static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *who, const char *message)
+static int reflog_append(
+ refdb_fs_backend *backend,
+ const git_reference *ref,
+ const git_oid *old,
+ const git_oid *new,
+ const git_signature *who,
+ const char *message)
{
int error, is_symbolic, open_flags;
- git_oid old_id = GIT_OID_SHA1_ZERO, new_id = GIT_OID_SHA1_ZERO;
+ git_oid old_id, new_id;
git_str buf = GIT_STR_INIT, path = GIT_STR_INIT;
git_repository *repo = backend->repo;
@@ -2206,6 +2305,9 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co
/* From here on is_symbolic also means that it's HEAD */
+ git_oid_clear(&old_id, backend->oid_type);
+ git_oid_clear(&new_id, backend->oid_type);
+
if (old) {
git_oid_cpy(&old_id, old);
} else {
@@ -2368,7 +2470,12 @@ static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name
if ((error = reflog_path(&path, backend->repo, name)) < 0)
goto out;
- if (!git_fs_path_exists(path.ptr))
+ /*
+ * If a reference was moved downwards, eg refs/heads/br2 -> refs/heads/br2/new-name,
+ * refs/heads/br2 does exist but it's a directory. That's a valid situation.
+ * Proceed only if it's a file.
+ */
+ if (!git_fs_path_isfile(path.ptr))
goto out;
if ((error = p_unlink(path.ptr)) < 0)
@@ -2402,6 +2509,7 @@ int git_refdb_backend_fs(
goto fail;
backend->repo = repository;
+ backend->oid_type = repository->oid_type;
if (repository->gitdir) {
backend->gitpath = setup_namespace(repository, repository->gitdir);
diff --git a/src/libgit2/reflog.c b/src/libgit2/reflog.c
index eb0b7e48c1f..86d4355e336 100644
--- a/src/libgit2/reflog.c
+++ b/src/libgit2/reflog.c
@@ -71,7 +71,11 @@ int git_reflog_write(git_reflog *reflog)
return db->backend->reflog_write(db->backend, reflog);
}
-int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_signature *committer, const char *msg)
+int git_reflog_append(
+ git_reflog *reflog,
+ const git_oid *new_oid,
+ const git_signature *committer,
+ const char *msg)
{
const git_reflog_entry *previous;
git_reflog_entry *entry;
@@ -104,7 +108,7 @@ int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_sign
previous = git_reflog_entry_byindex(reflog, 0);
if (previous == NULL)
- git_oid__fromstr(&entry->oid_old, GIT_OID_SHA1_HEXZERO, GIT_OID_SHA1);
+ git_oid_clear(&entry->oid_old, reflog->oid_type);
else
git_oid_cpy(&entry->oid_old, &previous->oid_cur);
@@ -218,12 +222,8 @@ int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry)
/* If the oldest entry has just been removed... */
if (idx == entrycount - 1) {
- git_oid zero = GIT_OID_SHA1_ZERO;
-
/* ...clear the oid_old member of the "new" oldest entry */
- if (git_oid_cpy(&entry->oid_old, &zero) < 0)
- return -1;
-
+ git_oid_clear(&entry->oid_old, reflog->oid_type);
return 0;
}
diff --git a/src/libgit2/reflog.h b/src/libgit2/reflog.h
index 50d1056ed7e..bc98785981a 100644
--- a/src/libgit2/reflog.h
+++ b/src/libgit2/reflog.h
@@ -16,8 +16,6 @@
#define GIT_REFLOG_DIR_MODE 0777
#define GIT_REFLOG_FILE_MODE 0666
-#define GIT_REFLOG_SIZE_MIN (2*GIT_OID_SHA1_HEXSIZE+2+17)
-
struct git_reflog_entry {
git_oid oid_old;
git_oid oid_cur;
@@ -30,6 +28,7 @@ struct git_reflog_entry {
struct git_reflog {
git_refdb *db;
char *ref_name;
+ git_oid_t oid_type;
git_vector entries;
};
diff --git a/src/libgit2/refs.c b/src/libgit2/refs.c
index 8e4abaccc1f..c1ed04d233a 100644
--- a/src/libgit2/refs.c
+++ b/src/libgit2/refs.c
@@ -72,6 +72,7 @@ git_reference *git_reference__alloc(
const git_oid *oid,
const git_oid *peel)
{
+ git_oid_t oid_type;
git_reference *ref;
GIT_ASSERT_ARG_WITH_RETVAL(name, NULL);
@@ -84,10 +85,16 @@ git_reference *git_reference__alloc(
ref->type = GIT_REFERENCE_DIRECT;
git_oid_cpy(&ref->target.oid, oid);
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid_type = oid->type;
+#else
+ oid_type = GIT_OID_SHA1;
+#endif
+
if (peel != NULL)
git_oid_cpy(&ref->peel, peel);
else
- git_oid_clear(&ref->peel, GIT_OID_SHA1);
+ git_oid_clear(&ref->peel, oid_type);
return ref;
}
@@ -1073,6 +1080,12 @@ int git_reference_cmp(
return git_oid__cmp(&ref1->target.oid, &ref2->target.oid);
}
+int git_reference__cmp_cb(const void *a, const void *b)
+{
+ return git_reference_cmp(
+ (const git_reference *)a, (const git_reference *)b);
+}
+
/*
* Starting with the reference given by `ref_name`, follows symbolic
* references until a direct reference is found and updated the OID
diff --git a/src/libgit2/refs.h b/src/libgit2/refs.h
index cb888bf8f49..588af82fe40 100644
--- a/src/libgit2/refs.h
+++ b/src/libgit2/refs.h
@@ -92,6 +92,12 @@ int git_reference__is_tag(const char *ref_name);
int git_reference__is_note(const char *ref_name);
const char *git_reference__shorthand(const char *name);
+/*
+ * A `git_reference_cmp` wrapper suitable for passing to generic
+ * comparators, like `vector_cmp` / `tsort` / etc.
+ */
+int git_reference__cmp_cb(const void *a, const void *b);
+
/**
* Lookup a reference by name and try to resolve to an OID.
*
diff --git a/src/libgit2/remote.c b/src/libgit2/remote.c
index 02d271d7d9d..8b486ea3550 100644
--- a/src/libgit2/remote.c
+++ b/src/libgit2/remote.c
@@ -17,11 +17,13 @@
#include "fetchhead.h"
#include "push.h"
#include "proxy.h"
+#include "strarray.h"
#include "git2/config.h"
#include "git2/types.h"
#include "git2/oid.h"
#include "git2/net.h"
+#include "transports/smart.h"
#define CONFIG_URL_FMT "remote.%s.url"
#define CONFIG_PUSHURL_FMT "remote.%s.pushurl"
@@ -1026,6 +1028,24 @@ int git_remote_capabilities(unsigned int *out, git_remote *remote)
return remote->transport->capabilities(out, remote->transport);
}
+int git_remote_oid_type(git_oid_t *out, git_remote *remote)
+{
+ GIT_ASSERT_ARG(remote);
+
+ if (!remote->transport) {
+ git_error_set(GIT_ERROR_NET, "this remote has never connected");
+ *out = 0;
+ return -1;
+ }
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ return remote->transport->oid_type(out, remote->transport);
+#else
+ *out = GIT_OID_SHA1;
+ return 0;
+#endif
+}
+
static int lookup_config(char **out, git_config *cfg, const char *name)
{
git_config_entry *ce = NULL;
@@ -1225,24 +1245,6 @@ static int ls_to_vector(git_vector *out, git_remote *remote)
return 0;
}
-#define copy_opts(out, in) \
- if (in) { \
- (out)->callbacks = (in)->callbacks; \
- (out)->proxy_opts = (in)->proxy_opts; \
- (out)->custom_headers = (in)->custom_headers; \
- (out)->follow_redirects = (in)->follow_redirects; \
- }
-
-GIT_INLINE(int) connect_opts_from_fetch_opts(
- git_remote_connect_options *out,
- git_remote *remote,
- const git_fetch_options *fetch_opts)
-{
- git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
- copy_opts(&tmp, fetch_opts);
- return git_remote_connect_options_normalize(out, remote->repo, &tmp);
-}
-
static int connect_or_reset_options(
git_remote *remote,
int direction,
@@ -1330,13 +1332,18 @@ int git_remote_download(
return -1;
}
- if (connect_opts_from_fetch_opts(&connect_opts, remote, opts) < 0)
+ if (git_remote_connect_options__from_fetch_opts(&connect_opts,
+ remote, opts) < 0)
return -1;
if ((error = connect_or_reset_options(remote, GIT_DIRECTION_FETCH, &connect_opts)) < 0)
return error;
- return git_remote__download(remote, refspecs, opts);
+ error = git_remote__download(remote, refspecs, opts);
+
+ git_remote_connect_options_dispose(&connect_opts);
+
+ return error;
}
int git_remote_fetch(
@@ -1345,11 +1352,14 @@ int git_remote_fetch(
const git_fetch_options *opts,
const char *reflog_message)
{
- int error, update_fetchhead = 1;
git_remote_autotag_option_t tagopt = remote->download_tags;
bool prune = false;
git_str reflog_msg_buf = GIT_STR_INIT;
git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ unsigned int capabilities;
+ git_oid_t oid_type;
+ unsigned int update_flags = GIT_REMOTE_UPDATE_FETCHHEAD;
+ int error;
GIT_ASSERT_ARG(remote);
@@ -1358,17 +1368,22 @@ int git_remote_fetch(
return -1;
}
- if (connect_opts_from_fetch_opts(&connect_opts, remote, opts) < 0)
+ if (git_remote_connect_options__from_fetch_opts(&connect_opts,
+ remote, opts) < 0)
return -1;
if ((error = connect_or_reset_options(remote, GIT_DIRECTION_FETCH, &connect_opts)) < 0)
return error;
if (opts) {
- update_fetchhead = opts->update_fetchhead;
+ update_flags = opts->update_fetchhead;
tagopt = opts->download_tags;
}
+ if ((error = git_remote_capabilities(&capabilities, remote)) < 0 ||
+ (error = git_remote_oid_type(&oid_type, remote)) < 0)
+ return error;
+
/* Connect and download everything */
error = git_remote__download(remote, refspecs, opts);
@@ -1388,8 +1403,14 @@ int git_remote_fetch(
}
/* Create "remote/foo" branches for all remote branches */
- error = git_remote_update_tips(remote, &connect_opts.callbacks, update_fetchhead, tagopt, git_str_cstr(&reflog_msg_buf));
+ error = git_remote_update_tips(remote,
+ &connect_opts.callbacks,
+ update_flags,
+ tagopt,
+ git_str_cstr(&reflog_msg_buf));
+
git_str_dispose(&reflog_msg_buf);
+
if (error < 0)
goto done;
@@ -1622,7 +1643,10 @@ int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks)
const git_refspec *spec;
const char *refname;
int error;
- git_oid zero_id = GIT_OID_SHA1_ZERO;
+ git_oid zero_id;
+
+ GIT_ASSERT(remote && remote->repo);
+ git_oid_clear(&zero_id, remote->repo->oid_type);
if (callbacks)
GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
@@ -1727,6 +1751,9 @@ static int update_ref(
git_oid old_id;
int error;
+ GIT_ASSERT(remote && remote->repo);
+ git_oid_clear(&old_id, remote->repo->oid_type);
+
error = git_reference_name_to_id(&old_id, remote->repo, ref_name);
if (error < 0 && error != GIT_ENOTFOUND)
@@ -1758,6 +1785,7 @@ static int update_one_tip(
git_refspec *spec,
git_remote_head *head,
git_refspec *tagspec,
+ unsigned int update_flags,
git_remote_autotag_option_t tagopt,
const char *log_message,
const git_remote_callbacks *callbacks)
@@ -1765,11 +1793,13 @@ static int update_one_tip(
git_odb *odb;
git_str refname = GIT_STR_INIT;
git_reference *ref = NULL;
- bool autotag = false;
+ bool autotag = false, updated = false;
git_oid old;
int valid;
int error;
+ GIT_ASSERT(remote && remote->repo);
+
if ((error = git_repository_odb__weakptr(&odb, remote->repo)) < 0)
goto done;
@@ -1830,28 +1860,28 @@ static int update_one_tip(
}
if (error == GIT_ENOTFOUND) {
- git_oid_clear(&old, GIT_OID_SHA1);
+ git_oid_clear(&old, remote->repo->oid_type);
error = 0;
if (autotag && (error = git_vector_insert(update_heads, head)) < 0)
goto done;
}
- if (!git_oid__cmp(&old, &head->oid))
- goto done;
+ if ((updated = !git_oid_equal(&old, &head->oid))) {
+ /* In autotag mode, don't overwrite any locally-existing tags */
+ error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag,
+ log_message);
- /* In autotag mode, don't overwrite any locally-existing tags */
- error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag,
- log_message);
+ if (error < 0) {
+ if (error == GIT_EEXISTS)
+ error = 0;
- if (error < 0) {
- if (error == GIT_EEXISTS)
- error = 0;
-
- goto done;
+ goto done;
+ }
}
if (callbacks && callbacks->update_tips != NULL &&
+ (updated || (update_flags & GIT_REMOTE_UPDATE_REPORT_UNCHANGED)) &&
(error = callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload)) < 0)
git_error_set_after_callback_function(error, "git_remote_fetch");
@@ -1864,7 +1894,7 @@ static int update_one_tip(
static int update_tips_for_spec(
git_remote *remote,
const git_remote_callbacks *callbacks,
- int update_fetchhead,
+ unsigned int update_flags,
git_remote_autotag_option_t tagopt,
git_refspec *spec,
git_vector *refs,
@@ -1876,7 +1906,7 @@ static int update_tips_for_spec(
int error = 0;
size_t i;
- GIT_ASSERT_ARG(remote);
+ GIT_ASSERT_ARG(remote && remote->repo);
if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
return -1;
@@ -1887,15 +1917,18 @@ static int update_tips_for_spec(
/* Update tips based on the remote heads */
git_vector_foreach(refs, i, head) {
- if (update_one_tip(&update_heads, remote, spec, head, &tagspec, tagopt, log_message, callbacks) < 0)
+ if (update_one_tip(&update_heads,
+ remote, spec, head, &tagspec,
+ update_flags, tagopt, log_message,
+ callbacks) < 0)
goto on_error;
}
/* Handle specified oid sources */
- if (git_oid__is_hexstr(spec->src, GIT_OID_SHA1)) {
+ if (git_oid__is_hexstr(spec->src, remote->repo->oid_type)) {
git_oid id;
- if ((error = git_oid__fromstr(&id, spec->src, GIT_OID_SHA1)) < 0)
+ if ((error = git_oid__fromstr(&id, spec->src, remote->repo->oid_type)) < 0)
goto on_error;
if (spec->dst &&
@@ -1909,7 +1942,7 @@ static int update_tips_for_spec(
goto on_error;
}
- if (update_fetchhead &&
+ if ((update_flags & GIT_REMOTE_UPDATE_FETCHHEAD) &&
(error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
goto on_error;
@@ -2040,11 +2073,11 @@ static int truncate_fetch_head(const char *gitdir)
}
int git_remote_update_tips(
- git_remote *remote,
- const git_remote_callbacks *callbacks,
- int update_fetchhead,
- git_remote_autotag_option_t download_tags,
- const char *reflog_message)
+ git_remote *remote,
+ const git_remote_callbacks *callbacks,
+ unsigned int update_flags,
+ git_remote_autotag_option_t download_tags,
+ const char *reflog_message)
{
git_refspec *spec, tagspec;
git_vector refs = GIT_VECTOR_INIT;
@@ -2073,7 +2106,7 @@ int git_remote_update_tips(
goto out;
if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
- if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, &tagspec, &refs, reflog_message)) < 0)
+ if ((error = update_tips_for_spec(remote, callbacks, update_flags, tagopt, &tagspec, &refs, reflog_message)) < 0)
goto out;
}
@@ -2081,7 +2114,7 @@ int git_remote_update_tips(
if (spec->push)
continue;
- if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, spec, &refs, reflog_message)) < 0)
+ if ((error = update_tips_for_spec(remote, callbacks, update_flags, tagopt, spec, &refs, reflog_message)) < 0)
goto out;
}
@@ -2896,16 +2929,6 @@ int git_remote__default_branch(git_str *out, git_remote *remote)
return error;
}
-GIT_INLINE(int) connect_opts_from_push_opts(
- git_remote_connect_options *out,
- git_remote *remote,
- const git_push_options *push_opts)
-{
- git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
- copy_opts(&tmp, push_opts);
- return git_remote_connect_options_normalize(out, remote->repo, &tmp);
-}
-
int git_remote_upload(
git_remote *remote,
const git_strarray *refspecs,
@@ -2924,7 +2947,8 @@ int git_remote_upload(
return -1;
}
- if ((error = connect_opts_from_push_opts(&connect_opts, remote, opts)) < 0)
+ if ((error = git_remote_connect_options__from_push_opts(
+ &connect_opts, remote, opts)) < 0)
goto cleanup;
if ((error = connect_or_reset_options(remote, GIT_DIRECTION_PUSH, &connect_opts)) < 0)
@@ -2958,6 +2982,15 @@ int git_remote_upload(
}
}
+ if (opts && opts->remote_push_options.count > 0)
+ for (i = 0; i < opts->remote_push_options.count; ++i) {
+ char *optstr = git__strdup(opts->remote_push_options.strings[i]);
+ GIT_ERROR_CHECK_ALLOC(optstr);
+
+ if ((error = git_vector_insert(&push->remote_push_options, optstr)) < 0)
+ goto cleanup;
+ }
+
if ((error = git_push_finish(push)) < 0)
goto cleanup;
@@ -2985,7 +3018,8 @@ int git_remote_push(
return -1;
}
- if (connect_opts_from_push_opts(&connect_opts, remote, opts) < 0)
+ if (git_remote_connect_options__from_push_opts(&connect_opts,
+ remote, opts) < 0)
return -1;
if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
diff --git a/src/libgit2/remote.h b/src/libgit2/remote.h
index 41ee58e0fd5..9e089be38c5 100644
--- a/src/libgit2/remote.h
+++ b/src/libgit2/remote.h
@@ -17,6 +17,7 @@
#include "refspec.h"
#include "vector.h"
#include "net.h"
+#include "proxy.h"
#define GIT_REMOTE_ORIGIN "origin"
@@ -37,6 +38,7 @@ struct git_remote {
git_remote_autotag_option_t download_tags;
int prune_refs;
int passed_refspecs;
+ git_fetch_negotiation nego;
};
int git_remote__urlfordirection(git_str *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks);
@@ -56,5 +58,44 @@ int git_remote_connect_options_normalize(
const git_remote_connect_options *src);
int git_remote_capabilities(unsigned int *out, git_remote *remote);
+int git_remote_oid_type(git_oid_t *out, git_remote *remote);
+
+
+#define git_remote_connect_options__copy_opts(out, in) \
+ if (in) { \
+ (out)->callbacks = (in)->callbacks; \
+ (out)->proxy_opts = (in)->proxy_opts; \
+ (out)->custom_headers = (in)->custom_headers; \
+ (out)->follow_redirects = (in)->follow_redirects; \
+ }
+
+GIT_INLINE(int) git_remote_connect_options__from_fetch_opts(
+ git_remote_connect_options *out,
+ git_remote *remote,
+ const git_fetch_options *fetch_opts)
+{
+ git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ git_remote_connect_options__copy_opts(&tmp, fetch_opts);
+ return git_remote_connect_options_normalize(out, remote->repo, &tmp);
+}
+
+GIT_INLINE(int) git_remote_connect_options__from_push_opts(
+ git_remote_connect_options *out,
+ git_remote *remote,
+ const git_push_options *push_opts)
+{
+ git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ git_remote_connect_options__copy_opts(&tmp, push_opts);
+ return git_remote_connect_options_normalize(out, remote->repo, &tmp);
+}
+
+#undef git_remote_connect_options__copy_opts
+
+GIT_INLINE(void) git_remote_connect_options__dispose(
+ git_remote_connect_options *opts)
+{
+ git_proxy_options_dispose(&opts->proxy_opts);
+ git_strarray_dispose(&opts->custom_headers);
+}
#endif
diff --git a/src/libgit2/repository.c b/src/libgit2/repository.c
index a6bd7b914e3..8e449a6df09 100644
--- a/src/libgit2/repository.c
+++ b/src/libgit2/repository.c
@@ -15,6 +15,7 @@
#include "buf.h"
#include "common.h"
#include "commit.h"
+#include "grafts.h"
#include "tag.h"
#include "blob.h"
#include "futils.h"
@@ -57,17 +58,18 @@ static const struct {
{ GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "packed-refs", false },
{ GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "remotes", true },
{ GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "config", false },
- { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "config.worktree", false },
{ GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "info", true },
{ GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "hooks", true },
{ GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "logs", true },
{ GIT_REPOSITORY_ITEM_GITDIR, GIT_REPOSITORY_ITEM__LAST, "modules", true },
- { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "worktrees", true }
+ { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "worktrees", true },
+ { GIT_REPOSITORY_ITEM_GITDIR, GIT_REPOSITORY_ITEM_GITDIR, "config.worktree", false }
};
static int check_repositoryformatversion(int *version, git_config *config);
static int check_extensions(git_config *config, int version);
-static int load_global_config(git_config **config);
+static int load_global_config(git_config **config, bool use_env);
+static int load_objectformat(git_repository *repo, git_config *config);
#define GIT_COMMONDIR_FILE "commondir"
#define GIT_GITDIR_FILE "gitdir"
@@ -76,8 +78,8 @@ static int load_global_config(git_config **config);
#define GIT_BRANCH_DEFAULT "master"
-#define GIT_REPO_VERSION 0
-#define GIT_REPO_MAX_VERSION 1
+#define GIT_REPO_VERSION_DEFAULT 0
+#define GIT_REPO_VERSION_MAX 1
git_str git_repository__reserved_names_win32[] = {
{ DOT_GIT, 0, CONST_STRLEN(DOT_GIT) },
@@ -151,6 +153,10 @@ int git_repository__cleanup(git_repository *repo)
git_repository_submodule_cache_clear(repo);
git_cache_clear(&repo->objects);
git_attr_cache_flush(repo);
+ git_grafts_free(repo->grafts);
+ repo->grafts = NULL;
+ git_grafts_free(repo->shallow_grafts);
+ repo->shallow_grafts = NULL;
set_config(repo, NULL);
set_index(repo, NULL);
@@ -191,11 +197,23 @@ void git_repository_free(git_repository *repo)
}
/* Check if we have a separate commondir (e.g. we have a worktree) */
-static int lookup_commondir(bool *separate, git_str *commondir, git_str *repository_path)
+static int lookup_commondir(
+ bool *separate,
+ git_str *commondir,
+ git_str *repository_path,
+ uint32_t flags)
{
- git_str common_link = GIT_STR_INIT;
+ git_str common_link = GIT_STR_INIT;
int error;
+ /* Environment variable overrides configuration */
+ if ((flags & GIT_REPOSITORY_OPEN_FROM_ENV)) {
+ error = git__getenv(commondir, "GIT_COMMON_DIR");
+
+ if (!error || error != GIT_ENOTFOUND)
+ goto done;
+ }
+
/*
* If there's no commondir file, the repository path is the
* common path, but it needs a trailing slash.
@@ -222,12 +240,11 @@ static int lookup_commondir(bool *separate, git_str *commondir, git_str *reposit
git_str_swap(commondir, &common_link);
}
- git_str_dispose(&common_link);
-
/* Make sure the commondir path always has a trailing slash */
error = git_fs_path_prettify_dir(commondir, commondir->ptr, NULL);
done:
+ git_str_dispose(&common_link);
return error;
}
@@ -241,7 +258,7 @@ GIT_INLINE(int) validate_repo_path(git_str *path)
*/
static size_t suffix_len =
CONST_STRLEN("objects/pack/pack-.pack.lock") +
- GIT_OID_SHA1_HEXSIZE;
+ GIT_OID_MAX_HEXSIZE;
return git_fs_path_validate_str_length_with_suffix(
path, suffix_len);
@@ -252,14 +269,19 @@ GIT_INLINE(int) validate_repo_path(git_str *path)
*
* Open a repository object from its path
*/
-static int is_valid_repository_path(bool *out, git_str *repository_path, git_str *common_path)
+static int is_valid_repository_path(
+ bool *out,
+ git_str *repository_path,
+ git_str *common_path,
+ uint32_t flags)
{
bool separate_commondir = false;
int error;
*out = false;
- if ((error = lookup_commondir(&separate_commondir, common_path, repository_path)) < 0)
+ if ((error = lookup_commondir(&separate_commondir,
+ common_path, repository_path, flags)) < 0)
return error;
/* Ensure HEAD file exists */
@@ -307,7 +329,7 @@ static git_repository *repository_alloc(void)
return NULL;
}
-int git_repository_new(git_repository **out)
+int git_repository__new(git_repository **out, git_oid_t oid_type)
{
git_repository *repo;
@@ -316,10 +338,23 @@ int git_repository_new(git_repository **out)
repo->is_bare = 1;
repo->is_worktree = 0;
+ repo->oid_type = oid_type;
return 0;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_repository_new(git_repository **out, git_oid_t oid_type)
+{
+ return git_repository__new(out, oid_type);
+}
+#else
+int git_repository_new(git_repository** out)
+{
+ return git_repository__new(out, GIT_OID_SHA1);
+}
+#endif
+
static int load_config_data(git_repository *repo, const git_config *config)
{
int is_bare;
@@ -337,19 +372,42 @@ static int load_config_data(git_repository *repo, const git_config *config)
return 0;
}
-static int load_workdir(git_repository *repo, git_config *config, git_str *parent_path)
+static int load_workdir(
+ git_repository *repo,
+ git_config *config,
+ git_str *parent_path)
{
- int error;
- git_config_entry *ce;
+ git_config_entry *ce = NULL;
git_str worktree = GIT_STR_INIT;
git_str path = GIT_STR_INIT;
+ git_str workdir_env = GIT_STR_INIT;
+ const char *value = NULL;
+ int error;
if (repo->is_bare)
return 0;
- if ((error = git_config__lookup_entry(
- &ce, config, "core.worktree", false)) < 0)
- return error;
+ /* Environment variables are preferred */
+ if (repo->use_env) {
+ error = git__getenv(&workdir_env, "GIT_WORK_TREE");
+
+ if (error == 0)
+ value = workdir_env.ptr;
+ else if (error == GIT_ENOTFOUND)
+ error = 0;
+ else
+ goto cleanup;
+ }
+
+ /* Examine configuration values if necessary */
+ if (!value) {
+ if ((error = git_config__lookup_entry(&ce, config,
+ "core.worktree", false)) < 0)
+ return error;
+
+ if (ce && ce->value)
+ value = ce->value;
+ }
if (repo->is_worktree) {
char *gitlink = git_worktree__read_link(repo->gitdir, GIT_GITDIR_FILE);
@@ -367,17 +425,21 @@ static int load_workdir(git_repository *repo, git_config *config, git_str *paren
}
repo->workdir = git_str_detach(&worktree);
- }
- else if (ce && ce->value) {
- if ((error = git_fs_path_prettify_dir(
- &worktree, ce->value, repo->gitdir)) < 0)
+ } else if (value) {
+ if (!*value) {
+ git_error_set(GIT_ERROR_NET, "working directory cannot be set to empty path");
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = git_fs_path_prettify_dir(&worktree,
+ value, repo->gitdir)) < 0)
goto cleanup;
repo->workdir = git_str_detach(&worktree);
- }
- else if (parent_path && git_fs_path_isdir(parent_path->ptr))
+ } else if (parent_path && git_fs_path_isdir(parent_path->ptr)) {
repo->workdir = git_str_detach(parent_path);
- else {
+ } else {
if (git_fs_path_dirname_r(&worktree, repo->gitdir) < 0 ||
git_fs_path_to_dir(&worktree) < 0) {
error = -1;
@@ -388,8 +450,10 @@ static int load_workdir(git_repository *repo, git_config *config, git_str *paren
}
GIT_ERROR_CHECK_ALLOC(repo->workdir);
+
cleanup:
git_str_dispose(&path);
+ git_str_dispose(&workdir_env);
git_config_entry_free(ce);
return error;
}
@@ -495,20 +559,65 @@ typedef struct {
static int validate_ownership_cb(const git_config_entry *entry, void *payload)
{
validate_ownership_data *data = payload;
+ const char *test_path;
- if (strcmp(entry->value, "") == 0)
+ if (strcmp(entry->value, "") == 0) {
*data->is_safe = false;
-
- if (strcmp(entry->value, "*") == 0)
- *data->is_safe = true;
- else if (git_fs_path_prettify_dir(&data->tmp, entry->value, NULL) == 0 &&
- strcmp(data->tmp.ptr, data->repo_path) == 0)
+ } else if (strcmp(entry->value, "*") == 0) {
*data->is_safe = true;
+ } else {
+ if (git_str_sets(&data->tmp, entry->value) < 0)
+ return -1;
+
+ if (!git_fs_path_is_root(data->tmp.ptr)) {
+ /* Input must not have trailing backslash. */
+ if (!data->tmp.size ||
+ data->tmp.ptr[data->tmp.size - 1] == '/')
+ return 0;
+
+ if (git_fs_path_to_dir(&data->tmp) < 0)
+ return -1;
+ }
+
+ test_path = data->tmp.ptr;
+
+ /*
+ * Git - and especially, Git for Windows - does some
+ * truly bizarre things with paths that start with a
+ * forward slash; and expects you to escape that with
+ * `%(prefix)`. This syntax generally means to add the
+ * prefix that Git was installed to (eg `/usr/local`)
+ * unless it's an absolute path, in which case the
+ * leading `%(prefix)/` is just removed. And Git for
+ * Windows expects you to use this syntax for absolute
+ * Unix-style paths (in "Git Bash" or Windows Subsystem
+ * for Linux).
+ *
+ * Worse, the behavior used to be that a leading `/` was
+ * not absolute. It would indicate that Git for Windows
+ * should add the prefix. So `//` is required for absolute
+ * Unix-style paths. Yes, this is truly horrifying.
+ *
+ * Emulate that behavior, I guess, but only for absolute
+ * paths. We won't deal with the Git install prefix. Also,
+ * give WSL users an escape hatch where they don't have to
+ * think about this and can use the literal path that the
+ * filesystem APIs provide (`//wsl.localhost/...`).
+ */
+ if (strncmp(test_path, "%(prefix)//", strlen("%(prefix)//")) == 0)
+ test_path += strlen("%(prefix)/");
+
+ if (strcmp(test_path, data->repo_path) == 0)
+ *data->is_safe = true;
+ }
return 0;
}
-static int validate_ownership_config(bool *is_safe, const char *path)
+static int validate_ownership_config(
+ bool *is_safe,
+ const char *path,
+ bool use_env)
{
validate_ownership_data ownership_data = {
path, GIT_STR_INIT, is_safe
@@ -516,7 +625,7 @@ static int validate_ownership_config(bool *is_safe, const char *path)
git_config *config;
int error;
- if (load_global_config(&config) != 0)
+ if (load_global_config(&config, use_env) != 0)
return 0;
error = git_config_get_multivar_foreach(config,
@@ -547,6 +656,9 @@ static int validate_ownership_path(bool *is_safe, const char *path)
if (error == GIT_ENOTFOUND) {
*is_safe = true;
error = 0;
+ } else if (error == GIT_EINVALID) {
+ *is_safe = false;
+ error = 0;
}
return error;
@@ -587,13 +699,17 @@ static int validate_ownership(git_repository *repo)
}
if (is_safe ||
- (error = validate_ownership_config(&is_safe, validation_paths[0])) < 0)
+ (error = validate_ownership_config(
+ &is_safe, validation_paths[0], repo->use_env)) < 0)
goto done;
if (!is_safe) {
+ size_t path_len = git_fs_path_is_root(path) ?
+ strlen(path) : git_fs_path_dirlen(path);
+
git_error_set(GIT_ERROR_CONFIG,
- "repository path '%s' is not owned by current user",
- path);
+ "repository path '%.*s' is not owned by current user",
+ (int)min(path_len, INT_MAX), path);
error = GIT_EOWNER;
}
@@ -601,14 +717,28 @@ static int validate_ownership(git_repository *repo)
return error;
}
-static int find_repo(
- git_str *gitdir_path,
- git_str *workdir_path,
- git_str *gitlink_path,
- git_str *commondir_path,
+struct repo_paths {
+ git_str gitdir;
+ git_str workdir;
+ git_str gitlink;
+ git_str commondir;
+};
+
+#define REPO_PATHS_INIT { GIT_STR_INIT }
+
+GIT_INLINE(void) repo_paths_dispose(struct repo_paths *paths)
+{
+ git_str_dispose(&paths->gitdir);
+ git_str_dispose(&paths->workdir);
+ git_str_dispose(&paths->gitlink);
+ git_str_dispose(&paths->commondir);
+}
+
+static int find_repo_traverse(
+ struct repo_paths *out,
const char *start_path,
- uint32_t flags,
- const char *ceiling_dirs)
+ const char *ceiling_dirs,
+ uint32_t flags)
{
git_str path = GIT_STR_INIT;
git_str repo_link = GIT_STR_INIT;
@@ -620,19 +750,23 @@ static int find_repo(
size_t ceiling_offset = 0;
int error;
- git_str_clear(gitdir_path);
+ git_str_clear(&out->gitdir);
- error = git_fs_path_prettify(&path, start_path, NULL);
- if (error < 0)
+ if ((error = git_fs_path_prettify(&path, start_path, NULL)) < 0)
return error;
- /* in_dot_git toggles each loop:
+ /*
+ * In each loop we look first for a `.git` dir within the
+ * directory, then to see if the directory itself is a repo.
+ *
+ * In other words: if we start in /a/b/c, then we look at:
* /a/b/c/.git, /a/b/c, /a/b/.git, /a/b, /a/.git, /a
- * With GIT_REPOSITORY_OPEN_BARE or GIT_REPOSITORY_OPEN_NO_DOTGIT, we
- * assume we started with /a/b/c.git and don't append .git the first
- * time through.
- * min_iterations indicates the number of iterations left before going
- * further counts as a search. */
+ *
+ * With GIT_REPOSITORY_OPEN_BARE or GIT_REPOSITORY_OPEN_NO_DOTGIT,
+ * we assume we started with /a/b/c.git and don't append .git the
+ * first time through. min_iterations indicates the number of
+ * iterations left before going further counts as a search.
+ */
if (flags & (GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
in_dot_git = true;
min_iterations = 1;
@@ -659,48 +793,51 @@ static int find_repo(
break;
if (S_ISDIR(st.st_mode)) {
- if ((error = is_valid_repository_path(&is_valid, &path, &common_link)) < 0)
+ if ((error = is_valid_repository_path(&is_valid, &path, &common_link, flags)) < 0)
goto out;
if (is_valid) {
if ((error = git_fs_path_to_dir(&path)) < 0 ||
- (error = git_str_set(gitdir_path, path.ptr, path.size)) < 0)
+ (error = git_str_set(&out->gitdir, path.ptr, path.size)) < 0)
goto out;
- if (gitlink_path)
- if ((error = git_str_attach(gitlink_path, git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0)) < 0)
- goto out;
- if (commondir_path)
- git_str_swap(&common_link, commondir_path);
+ if ((error = git_str_attach(&out->gitlink, git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0)) < 0)
+ goto out;
+
+ git_str_swap(&common_link, &out->commondir);
break;
}
} else if (S_ISREG(st.st_mode) && git__suffixcmp(path.ptr, "/" DOT_GIT) == 0) {
if ((error = read_gitfile(&repo_link, path.ptr)) < 0 ||
- (error = is_valid_repository_path(&is_valid, &repo_link, &common_link)) < 0)
+ (error = is_valid_repository_path(&is_valid, &repo_link, &common_link, flags)) < 0)
goto out;
if (is_valid) {
- git_str_swap(gitdir_path, &repo_link);
+ git_str_swap(&out->gitdir, &repo_link);
- if (gitlink_path)
- if ((error = git_str_put(gitlink_path, path.ptr, path.size)) < 0)
- goto out;
- if (commondir_path)
- git_str_swap(&common_link, commondir_path);
+ if ((error = git_str_put(&out->gitlink, path.ptr, path.size)) < 0)
+ goto out;
+
+ git_str_swap(&common_link, &out->commondir);
}
break;
}
}
- /* Move up one directory. If we're in_dot_git, we'll search the
- * parent itself next. If we're !in_dot_git, we'll search .git
- * in the parent directory next (added at the top of the loop). */
+ /*
+ * Move up one directory. If we're in_dot_git, we'll
+ * search the parent itself next. If we're !in_dot_git,
+ * we'll search .git in the parent directory next (added
+ * at the top of the loop).
+ */
if ((error = git_fs_path_dirname_r(&path, path.ptr)) < 0)
goto out;
- /* Once we've checked the directory (and .git if applicable),
- * find the ceiling for a search. */
+ /*
+ * Once we've checked the directory (and .git if
+ * applicable), find the ceiling for a search.
+ */
if (min_iterations && (--min_iterations == 0))
ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
@@ -710,29 +847,176 @@ static int find_repo(
break;
}
- if (workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
- if (!git_str_len(gitdir_path))
- git_str_clear(workdir_path);
- else if ((error = git_fs_path_dirname_r(workdir_path, path.ptr)) < 0 ||
- (error = git_fs_path_to_dir(workdir_path)) < 0)
+ if (!(flags & GIT_REPOSITORY_OPEN_BARE)) {
+ if (!git_str_len(&out->gitdir))
+ git_str_clear(&out->workdir);
+ else if ((error = git_fs_path_dirname_r(&out->workdir, path.ptr)) < 0 ||
+ (error = git_fs_path_to_dir(&out->workdir)) < 0)
goto out;
}
- /* If we didn't find the repository, and we don't have any other error
- * to report, report that. */
- if (!git_str_len(gitdir_path)) {
- git_error_set(GIT_ERROR_REPOSITORY, "could not find repository from '%s'", start_path);
+ /* If we didn't find the repository, and we don't have any other
+ * error to report, report that. */
+ if (!git_str_len(&out->gitdir)) {
+ git_error_set(GIT_ERROR_REPOSITORY, "could not find repository at '%s'", start_path);
error = GIT_ENOTFOUND;
goto out;
}
out:
+ if (error)
+ repo_paths_dispose(out);
+
git_str_dispose(&path);
git_str_dispose(&repo_link);
git_str_dispose(&common_link);
return error;
}
+static int load_grafts(git_repository *repo)
+{
+ git_str path = GIT_STR_INIT;
+ int error;
+
+ /* refresh if they've both been opened previously */
+ if (repo->grafts && repo->shallow_grafts) {
+ if ((error = git_grafts_refresh(repo->grafts)) < 0 ||
+ (error = git_grafts_refresh(repo->shallow_grafts)) < 0)
+ return error;
+ }
+
+ /* resolve info path, which may not be found for inmemory repository */
+ if ((error = git_repository__item_path(&path, repo, GIT_REPOSITORY_ITEM_INFO)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ return error;
+
+ /* create empty/inmemory grafts for inmemory repository */
+ if (!repo->grafts && (error = git_grafts_new(&repo->grafts, repo->oid_type)) < 0)
+ return error;
+
+ if (!repo->shallow_grafts && (error = git_grafts_new(&repo->shallow_grafts, repo->oid_type)) < 0)
+ return error;
+
+ return 0;
+ }
+
+ /* load grafts from disk */
+ if ((error = git_str_joinpath(&path, path.ptr, "grafts")) < 0 ||
+ (error = git_grafts_open_or_refresh(&repo->grafts, path.ptr, repo->oid_type)) < 0)
+ goto error;
+
+ git_str_clear(&path);
+
+ if ((error = git_str_joinpath(&path, repo->gitdir, "shallow")) < 0 ||
+ (error = git_grafts_open_or_refresh(&repo->shallow_grafts, path.ptr, repo->oid_type)) < 0)
+ goto error;
+
+error:
+ git_str_dispose(&path);
+ return error;
+}
+
+static int find_repo(
+ struct repo_paths *out,
+ const char *start_path,
+ const char *ceiling_dirs,
+ uint32_t flags)
+{
+ bool use_env = !!(flags & GIT_REPOSITORY_OPEN_FROM_ENV);
+ git_str gitdir_buf = GIT_STR_INIT,
+ ceiling_dirs_buf = GIT_STR_INIT,
+ across_fs_buf = GIT_STR_INIT;
+ int error;
+
+ if (use_env && !start_path) {
+ error = git__getenv(&gitdir_buf, "GIT_DIR");
+
+ if (!error) {
+ start_path = gitdir_buf.ptr;
+ flags |= GIT_REPOSITORY_OPEN_NO_SEARCH;
+ flags |= GIT_REPOSITORY_OPEN_NO_DOTGIT;
+ } else if (error == GIT_ENOTFOUND) {
+ start_path = ".";
+ } else {
+ goto done;
+ }
+ }
+
+ if (use_env && !ceiling_dirs) {
+ error = git__getenv(&ceiling_dirs_buf,
+ "GIT_CEILING_DIRECTORIES");
+
+ if (!error)
+ ceiling_dirs = ceiling_dirs_buf.ptr;
+ else if (error != GIT_ENOTFOUND)
+ goto done;
+ }
+
+ if (use_env) {
+ error = git__getenv(&across_fs_buf,
+ "GIT_DISCOVERY_ACROSS_FILESYSTEM");
+
+ if (!error) {
+ int across_fs = 0;
+
+ if ((error = git_config_parse_bool(&across_fs,
+ git_str_cstr(&across_fs_buf))) < 0)
+ goto done;
+
+ if (across_fs)
+ flags |= GIT_REPOSITORY_OPEN_CROSS_FS;
+ } else if (error != GIT_ENOTFOUND) {
+ goto done;
+ }
+ }
+
+ error = find_repo_traverse(out, start_path, ceiling_dirs, flags);
+
+done:
+ git_str_dispose(&gitdir_buf);
+ git_str_dispose(&ceiling_dirs_buf);
+ git_str_dispose(&across_fs_buf);
+
+ return error;
+}
+
+static int obtain_config_and_set_oid_type(
+ git_config **config_ptr,
+ git_repository *repo)
+{
+ int error;
+ git_config *config = NULL;
+ int version = 0;
+
+ /*
+ * We'd like to have the config, but git doesn't particularly
+ * care if it's not there, so we need to deal with that.
+ */
+
+ error = git_repository_config_snapshot(&config, repo);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto out;
+
+ if (config &&
+ (error = check_repositoryformatversion(&version, config)) < 0)
+ goto out;
+
+ if ((error = check_extensions(config, version)) < 0)
+ goto out;
+
+ if (version > 0) {
+ if ((error = load_objectformat(repo, config)) < 0)
+ goto out;
+ } else {
+ repo->oid_type = GIT_OID_DEFAULT;
+ }
+
+out:
+ *config_ptr = config;
+
+ return error;
+}
+
int git_repository_open_bare(
git_repository **repo_ptr,
const char *bare_path)
@@ -741,9 +1025,10 @@ int git_repository_open_bare(
git_repository *repo = NULL;
bool is_valid;
int error;
+ git_config *config;
if ((error = git_fs_path_prettify_dir(&path, bare_path, NULL)) < 0 ||
- (error = is_valid_repository_path(&is_valid, &path, &common_path)) < 0)
+ (error = is_valid_repository_path(&is_valid, &path, &common_path, 0)) < 0)
return error;
if (!is_valid) {
@@ -766,177 +1051,33 @@ int git_repository_open_bare(
repo->is_worktree = 0;
repo->workdir = NULL;
+ if ((error = obtain_config_and_set_oid_type(&config, repo)) < 0)
+ goto cleanup;
+
*repo_ptr = repo;
- return 0;
+
+cleanup:
+ git_config_free(config);
+
+ return error;
}
-static int _git_repository_open_ext_from_env(
- git_repository **out,
- const char *start_path)
+static int repo_load_namespace(git_repository *repo)
{
- git_repository *repo = NULL;
- git_index *index = NULL;
- git_odb *odb = NULL;
- git_str dir_buf = GIT_STR_INIT;
- git_str ceiling_dirs_buf = GIT_STR_INIT;
- git_str across_fs_buf = GIT_STR_INIT;
- git_str index_file_buf = GIT_STR_INIT;
git_str namespace_buf = GIT_STR_INIT;
- git_str object_dir_buf = GIT_STR_INIT;
- git_str alts_buf = GIT_STR_INIT;
- git_str work_tree_buf = GIT_STR_INIT;
- git_str common_dir_buf = GIT_STR_INIT;
- const char *ceiling_dirs = NULL;
- unsigned flags = 0;
int error;
- if (!start_path) {
- error = git__getenv(&dir_buf, "GIT_DIR");
- if (error == GIT_ENOTFOUND) {
- git_error_clear();
- start_path = ".";
- } else if (error < 0)
- goto error;
- else {
- start_path = git_str_cstr(&dir_buf);
- flags |= GIT_REPOSITORY_OPEN_NO_SEARCH;
- flags |= GIT_REPOSITORY_OPEN_NO_DOTGIT;
- }
- }
-
- error = git__getenv(&ceiling_dirs_buf, "GIT_CEILING_DIRECTORIES");
- if (error == GIT_ENOTFOUND)
- git_error_clear();
- else if (error < 0)
- goto error;
- else
- ceiling_dirs = git_str_cstr(&ceiling_dirs_buf);
-
- error = git__getenv(&across_fs_buf, "GIT_DISCOVERY_ACROSS_FILESYSTEM");
- if (error == GIT_ENOTFOUND)
- git_error_clear();
- else if (error < 0)
- goto error;
- else {
- int across_fs = 0;
- error = git_config_parse_bool(&across_fs, git_str_cstr(&across_fs_buf));
- if (error < 0)
- goto error;
- if (across_fs)
- flags |= GIT_REPOSITORY_OPEN_CROSS_FS;
- }
-
- error = git__getenv(&index_file_buf, "GIT_INDEX_FILE");
- if (error == GIT_ENOTFOUND)
- git_error_clear();
- else if (error < 0)
- goto error;
- else {
- error = git_index_open(&index, git_str_cstr(&index_file_buf));
- if (error < 0)
- goto error;
- }
+ if (!repo->use_env)
+ return 0;
error = git__getenv(&namespace_buf, "GIT_NAMESPACE");
- if (error == GIT_ENOTFOUND)
- git_error_clear();
- else if (error < 0)
- goto error;
-
- error = git__getenv(&object_dir_buf, "GIT_OBJECT_DIRECTORY");
- if (error == GIT_ENOTFOUND)
- git_error_clear();
- else if (error < 0)
- goto error;
- else {
- error = git_odb__open(&odb, git_str_cstr(&object_dir_buf), NULL);
- if (error < 0)
- goto error;
- }
-
- error = git__getenv(&work_tree_buf, "GIT_WORK_TREE");
- if (error == GIT_ENOTFOUND)
- git_error_clear();
- else if (error < 0)
- goto error;
- else {
- git_error_set(GIT_ERROR_INVALID, "GIT_WORK_TREE unimplemented");
- error = GIT_ERROR;
- goto error;
- }
-
- error = git__getenv(&work_tree_buf, "GIT_COMMON_DIR");
- if (error == GIT_ENOTFOUND)
- git_error_clear();
- else if (error < 0)
- goto error;
- else {
- git_error_set(GIT_ERROR_INVALID, "GIT_COMMON_DIR unimplemented");
- error = GIT_ERROR;
- goto error;
- }
-
- error = git_repository_open_ext(&repo, start_path, flags, ceiling_dirs);
- if (error < 0)
- goto error;
-
- if (odb)
- git_repository_set_odb(repo, odb);
-
- error = git__getenv(&alts_buf, "GIT_ALTERNATE_OBJECT_DIRECTORIES");
- if (error == GIT_ENOTFOUND) {
- git_error_clear();
- error = 0;
- } else if (error < 0)
- goto error;
- else {
- const char *end;
- char *alt, *sep;
- if (!odb) {
- error = git_repository_odb(&odb, repo);
- if (error < 0)
- goto error;
- }
-
- end = git_str_cstr(&alts_buf) + git_str_len(&alts_buf);
- for (sep = alt = alts_buf.ptr; sep != end; alt = sep+1) {
- for (sep = alt; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++)
- ;
- if (*sep)
- *sep = '\0';
- error = git_odb_add_disk_alternate(odb, alt);
- if (error < 0)
- goto error;
- }
- }
-
- if (git_str_len(&namespace_buf)) {
- error = git_repository_set_namespace(repo, git_str_cstr(&namespace_buf));
- if (error < 0)
- goto error;
- }
- git_repository_set_index(repo, index);
+ if (error == 0)
+ repo->namespace = git_str_detach(&namespace_buf);
+ else if (error != GIT_ENOTFOUND)
+ return error;
- if (out) {
- *out = repo;
- goto success;
- }
-error:
- git_repository_free(repo);
-success:
- git_odb_free(odb);
- git_index_free(index);
- git_str_dispose(&common_dir_buf);
- git_str_dispose(&work_tree_buf);
- git_str_dispose(&alts_buf);
- git_str_dispose(&object_dir_buf);
- git_str_dispose(&namespace_buf);
- git_str_dispose(&index_file_buf);
- git_str_dispose(&across_fs_buf);
- git_str_dispose(&ceiling_dirs_buf);
- git_str_dispose(&dir_buf);
- return error;
+ return 0;
}
static int repo_is_worktree(unsigned *out, const git_repository *repo)
@@ -968,22 +1109,16 @@ int git_repository_open_ext(
unsigned int flags,
const char *ceiling_dirs)
{
- int error;
- unsigned is_worktree;
- git_str gitdir = GIT_STR_INIT, workdir = GIT_STR_INIT,
- gitlink = GIT_STR_INIT, commondir = GIT_STR_INIT;
+ struct repo_paths paths = { GIT_STR_INIT };
git_repository *repo = NULL;
git_config *config = NULL;
- int version = 0;
-
- if (flags & GIT_REPOSITORY_OPEN_FROM_ENV)
- return _git_repository_open_ext_from_env(repo_ptr, start_path);
+ unsigned is_worktree;
+ int error;
if (repo_ptr)
*repo_ptr = NULL;
- error = find_repo(
- &gitdir, &workdir, &gitlink, &commondir, start_path, flags, ceiling_dirs);
+ error = find_repo(&paths, start_path, ceiling_dirs, flags);
if (error < 0 || !repo_ptr)
goto cleanup;
@@ -991,35 +1126,30 @@ int git_repository_open_ext(
repo = repository_alloc();
GIT_ERROR_CHECK_ALLOC(repo);
- repo->gitdir = git_str_detach(&gitdir);
+ repo->use_env = !!(flags & GIT_REPOSITORY_OPEN_FROM_ENV);
+
+ repo->gitdir = git_str_detach(&paths.gitdir);
GIT_ERROR_CHECK_ALLOC(repo->gitdir);
- if (gitlink.size) {
- repo->gitlink = git_str_detach(&gitlink);
+ if (paths.gitlink.size) {
+ repo->gitlink = git_str_detach(&paths.gitlink);
GIT_ERROR_CHECK_ALLOC(repo->gitlink);
}
- if (commondir.size) {
- repo->commondir = git_str_detach(&commondir);
+ if (paths.commondir.size) {
+ repo->commondir = git_str_detach(&paths.commondir);
GIT_ERROR_CHECK_ALLOC(repo->commondir);
}
if ((error = repo_is_worktree(&is_worktree, repo)) < 0)
goto cleanup;
- repo->is_worktree = is_worktree;
-
- /*
- * We'd like to have the config, but git doesn't particularly
- * care if it's not there, so we need to deal with that.
- */
- error = git_repository_config_snapshot(&config, repo);
- if (error < 0 && error != GIT_ENOTFOUND)
- goto cleanup;
+ repo->is_worktree = is_worktree;
- if (config && (error = check_repositoryformatversion(&version, config)) < 0)
+ error = obtain_config_and_set_oid_type(&config, repo);
+ if (error < 0)
goto cleanup;
- if ((error = check_extensions(config, version)) < 0)
+ if ((error = load_grafts(repo)) < 0)
goto cleanup;
if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0) {
@@ -1027,10 +1157,13 @@ int git_repository_open_ext(
} else {
if (config &&
((error = load_config_data(repo, config)) < 0 ||
- (error = load_workdir(repo, config, &workdir)) < 0))
+ (error = load_workdir(repo, config, &paths.workdir)) < 0))
goto cleanup;
}
+ if ((error = repo_load_namespace(repo)) < 0)
+ goto cleanup;
+
/*
* Ensure that the git directory and worktree are
* owned by the current user.
@@ -1040,10 +1173,7 @@ int git_repository_open_ext(
goto cleanup;
cleanup:
- git_str_dispose(&gitdir);
- git_str_dispose(&workdir);
- git_str_dispose(&gitlink);
- git_str_dispose(&commondir);
+ repo_paths_dispose(&paths);
git_config_free(config);
if (error < 0)
@@ -1092,30 +1222,74 @@ int git_repository_open_from_worktree(git_repository **repo_out, git_worktree *w
return err;
}
-int git_repository_wrap_odb(git_repository **repo_out, git_odb *odb)
+int git_repository__wrap_odb(
+ git_repository **out,
+ git_odb *odb,
+ git_oid_t oid_type)
{
git_repository *repo;
repo = repository_alloc();
GIT_ERROR_CHECK_ALLOC(repo);
+ repo->oid_type = oid_type;
+
git_repository_set_odb(repo, odb);
- *repo_out = repo;
+ *out = repo;
return 0;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_repository_wrap_odb(
+ git_repository **out,
+ git_odb *odb,
+ git_oid_t oid_type)
+{
+ return git_repository__wrap_odb(out, odb, oid_type);
+}
+#else
+int git_repository_wrap_odb(git_repository **out, git_odb *odb)
+{
+ return git_repository__wrap_odb(out, odb, GIT_OID_DEFAULT);
+}
+#endif
+
int git_repository_discover(
git_buf *out,
const char *start_path,
int across_fs,
const char *ceiling_dirs)
{
+ struct repo_paths paths = { GIT_STR_INIT };
uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;
+ int error;
GIT_ASSERT_ARG(start_path);
- GIT_BUF_WRAP_PRIVATE(out, find_repo, NULL, NULL, NULL, start_path, flags, ceiling_dirs);
+ if ((error = find_repo(&paths, start_path, ceiling_dirs, flags)) == 0)
+ error = git_buf_fromstr(out, &paths.gitdir);
+
+ repo_paths_dispose(&paths);
+ return error;
+}
+
+static int has_config_worktree(bool *out, git_config *cfg)
+{
+ int worktreeconfig = 0, error;
+
+ *out = false;
+
+ error = git_config_get_bool(&worktreeconfig, cfg, "extensions.worktreeconfig");
+
+ if (error == 0)
+ *out = worktreeconfig;
+ else if (error == GIT_ENOTFOUND)
+ *out = false;
+ else
+ return error;
+
+ return 0;
}
static int load_config(
@@ -1126,9 +1300,11 @@ static int load_config(
const char *system_config_path,
const char *programdata_path)
{
- int error;
git_str config_path = GIT_STR_INIT;
git_config *cfg = NULL;
+ git_config_level_t write_order;
+ bool has_worktree;
+ int error;
GIT_ASSERT_ARG(out);
@@ -1142,11 +1318,13 @@ static int load_config(
if (error && error != GIT_ENOTFOUND)
goto on_error;
- if ((error = git_repository__item_path(&config_path, repo, GIT_REPOSITORY_ITEM_WORKTREE_CONFIG)) == 0)
- error = git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_WORKTREE, repo, 0);
+ if ((error = has_config_worktree(&has_worktree, cfg)) == 0 &&
+ has_worktree &&
+ (error = git_repository__item_path(&config_path, repo, GIT_REPOSITORY_ITEM_WORKTREE_CONFIG)) == 0)
+ error = git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_WORKTREE, repo, 0);
- if (error && error != GIT_ENOTFOUND)
- goto on_error;
+ if (error && error != GIT_ENOTFOUND)
+ goto on_error;
git_str_dispose(&config_path);
}
@@ -1175,21 +1353,67 @@ static int load_config(
error != GIT_ENOTFOUND)
goto on_error;
- git_error_clear(); /* clear any lingering ENOTFOUND errors */
+ git_error_clear(); /* clear any lingering ENOTFOUND errors */
+
+ write_order = GIT_CONFIG_LEVEL_LOCAL;
+
+ if ((error = git_config_set_writeorder(cfg, &write_order, 1)) < 0)
+ goto on_error;
+
+ *out = cfg;
+ return 0;
+
+on_error:
+ git_str_dispose(&config_path);
+ git_config_free(cfg);
+ *out = NULL;
+ return error;
+}
+
+static const char *path_unless_empty(git_str *buf)
+{
+ return git_str_len(buf) > 0 ? git_str_cstr(buf) : NULL;
+}
+
+GIT_INLINE(int) config_path_system(git_str *out, bool use_env)
+{
+ if (use_env) {
+ git_str no_system_buf = GIT_STR_INIT;
+ int no_system = 0;
+ int error;
+
+ error = git__getenv(&no_system_buf, "GIT_CONFIG_NOSYSTEM");
+
+ if (error && error != GIT_ENOTFOUND)
+ return error;
+
+ error = git_config_parse_bool(&no_system, no_system_buf.ptr);
+ git_str_dispose(&no_system_buf);
- *out = cfg;
- return 0;
+ if (no_system)
+ return 0;
-on_error:
- git_str_dispose(&config_path);
- git_config_free(cfg);
- *out = NULL;
- return error;
+ error = git__getenv(out, "GIT_CONFIG_SYSTEM");
+
+ if (error == 0 || error != GIT_ENOTFOUND)
+ return 0;
+ }
+
+ git_config__find_system(out);
+ return 0;
}
-static const char *path_unless_empty(git_str *buf)
+GIT_INLINE(int) config_path_global(git_str *out, bool use_env)
{
- return git_str_len(buf) > 0 ? git_str_cstr(buf) : NULL;
+ if (use_env) {
+ int error = git__getenv(out, "GIT_CONFIG_GLOBAL");
+
+ if (error == 0 || error != GIT_ENOTFOUND)
+ return 0;
+ }
+
+ git_config__find_global(out);
+ return 0;
}
int git_repository_config__weakptr(git_config **out, git_repository *repo)
@@ -1197,27 +1421,35 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo)
int error = 0;
if (repo->_config == NULL) {
+ git_str system_buf = GIT_STR_INIT;
git_str global_buf = GIT_STR_INIT;
git_str xdg_buf = GIT_STR_INIT;
- git_str system_buf = GIT_STR_INIT;
git_str programdata_buf = GIT_STR_INIT;
+ bool use_env = repo->use_env;
git_config *config;
- git_config__find_global(&global_buf);
- git_config__find_xdg(&xdg_buf);
- git_config__find_system(&system_buf);
- git_config__find_programdata(&programdata_buf);
+ if (!(error = config_path_system(&system_buf, use_env)) &&
+ !(error = config_path_global(&global_buf, use_env))) {
+ git_config__find_xdg(&xdg_buf);
+ git_config__find_programdata(&programdata_buf);
+ }
- /* If there is no global file, open a backend for it anyway */
- if (git_str_len(&global_buf) == 0)
- git_config__global_location(&global_buf);
+ if (!error) {
+ /*
+ * If there is no global file, open a backend
+ * for it anyway.
+ */
+ if (git_str_len(&global_buf) == 0)
+ git_config__global_location(&global_buf);
+
+ error = load_config(
+ &config, repo,
+ path_unless_empty(&global_buf),
+ path_unless_empty(&xdg_buf),
+ path_unless_empty(&system_buf),
+ path_unless_empty(&programdata_buf));
+ }
- error = load_config(
- &config, repo,
- path_unless_empty(&global_buf),
- path_unless_empty(&xdg_buf),
- path_unless_empty(&system_buf),
- path_unless_empty(&programdata_buf));
if (!error) {
GIT_REFCOUNT_OWN(config, repo);
@@ -1266,6 +1498,56 @@ int git_repository_set_config(git_repository *repo, git_config *config)
return 0;
}
+static int repository_odb_path(git_str *out, git_repository *repo)
+{
+ int error = GIT_ENOTFOUND;
+
+ if (repo->use_env)
+ error = git__getenv(out, "GIT_OBJECT_DIRECTORY");
+
+ if (error == GIT_ENOTFOUND)
+ error = git_repository__item_path(out, repo,
+ GIT_REPOSITORY_ITEM_OBJECTS);
+
+ return error;
+}
+
+static int repository_odb_alternates(
+ git_odb *odb,
+ git_repository *repo)
+{
+ git_str alternates = GIT_STR_INIT;
+ char *sep, *alt;
+ int error;
+
+ if (!repo->use_env)
+ return 0;
+
+ error = git__getenv(&alternates, "GIT_ALTERNATE_OBJECT_DIRECTORIES");
+
+ if (error != 0)
+ return (error == GIT_ENOTFOUND) ? 0 : error;
+
+ alt = alternates.ptr;
+
+ while (*alt) {
+ sep = strchr(alt, GIT_PATH_LIST_SEPARATOR);
+
+ if (sep)
+ *sep = '\0';
+
+ error = git_odb_add_disk_alternate(odb, alt);
+
+ if (sep)
+ alt = sep + 1;
+ else
+ break;
+ }
+
+ git_str_dispose(&alternates);
+ return 0;
+}
+
int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
{
int error = 0;
@@ -1276,11 +1558,14 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
*out = git_atomic_load(repo->_odb);
if (*out == NULL) {
git_str odb_path = GIT_STR_INIT;
+ git_odb_options odb_opts = GIT_ODB_OPTIONS_INIT;
git_odb *odb;
- if ((error = git_repository__item_path(&odb_path, repo,
- GIT_REPOSITORY_ITEM_OBJECTS)) < 0 ||
- (error = git_odb__new(&odb, NULL)) < 0)
+ odb_opts.oid_type = repo->oid_type;
+
+ if ((error = repository_odb_path(&odb_path, repo)) < 0 ||
+ (error = git_odb__new(&odb, &odb_opts)) < 0 ||
+ (error = repository_odb_alternates(odb, repo)) < 0)
return error;
GIT_REFCOUNT_OWN(odb, repo);
@@ -1364,6 +1649,20 @@ int git_repository_set_refdb(git_repository *repo, git_refdb *refdb)
return 0;
}
+static int repository_index_path(git_str *out, git_repository *repo)
+{
+ int error = GIT_ENOTFOUND;
+
+ if (repo->use_env)
+ error = git__getenv(out, "GIT_INDEX_FILE");
+
+ if (error == GIT_ENOTFOUND)
+ error = git_repository__item_path(out, repo,
+ GIT_REPOSITORY_ITEM_INDEX);
+
+ return error;
+}
+
int git_repository_index__weakptr(git_index **out, git_repository *repo)
{
int error = 0;
@@ -1375,10 +1674,11 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo)
git_str index_path = GIT_STR_INIT;
git_index *index;
- if ((error = git_str_joinpath(&index_path, repo->gitdir, GIT_INDEX_FILE)) < 0)
+ if ((error = repository_index_path(&index_path, repo)) < 0)
return error;
- error = git_index_open(&index, index_path.ptr);
+ error = git_index__open(&index, index_path.ptr, repo->oid_type);
+
if (!error) {
GIT_REFCOUNT_OWN(index, repo);
@@ -1414,6 +1714,22 @@ int git_repository_set_index(git_repository *repo, git_index *index)
return 0;
}
+int git_repository_grafts__weakptr(git_grafts **out, git_repository *repo)
+{
+ GIT_ASSERT_ARG(out && repo);
+ GIT_ASSERT(repo->grafts);
+ *out = repo->grafts;
+ return 0;
+}
+
+int git_repository_shallow_grafts__weakptr(git_grafts **out, git_repository *repo)
+{
+ GIT_ASSERT_ARG(out && repo);
+ GIT_ASSERT(repo->shallow_grafts);
+ *out = repo->shallow_grafts;
+ return 0;
+}
+
int git_repository_set_namespace(git_repository *repo, const char *namespace)
{
git__free(repo->namespace);
@@ -1538,6 +1854,7 @@ static int check_repositoryformatversion(int *version, git_config *config)
int error;
error = git_config_get_int32(version, config, "core.repositoryformatversion");
+
/* git ignores this if the config variable isn't there */
if (error == GIT_ENOTFOUND)
return 0;
@@ -1545,10 +1862,15 @@ static int check_repositoryformatversion(int *version, git_config *config)
if (error < 0)
return -1;
- if (GIT_REPO_MAX_VERSION < *version) {
+ if (*version < 0) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "invalid repository version %d", *version);
+ }
+
+ if (GIT_REPO_VERSION_MAX < *version) {
git_error_set(GIT_ERROR_REPOSITORY,
"unsupported repository version %d; only versions up to %d are supported",
- *version, GIT_REPO_MAX_VERSION);
+ *version, GIT_REPO_VERSION_MAX);
return -1;
}
@@ -1557,10 +1879,11 @@ static int check_repositoryformatversion(int *version, git_config *config)
static const char *builtin_extensions[] = {
"noop",
- "worktreeconfig",
+ "objectformat",
+ "worktreeconfig",
};
-static git_vector user_extensions = GIT_VECTOR_INIT;
+static git_vector user_extensions = { 0, git__strcmp_cb };
static int check_valid_extension(const git_config_entry *entry, void *payload)
{
@@ -1621,6 +1944,79 @@ static int check_extensions(git_config *config, int version)
return git_config_foreach_match(config, "^extensions\\.", check_valid_extension, NULL);
}
+static int load_objectformat(git_repository *repo, git_config *config)
+{
+ git_config_entry *entry = NULL;
+ int error;
+
+ if ((error = git_config_get_entry(&entry, config, "extensions.objectformat")) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ repo->oid_type = GIT_OID_DEFAULT;
+ git_error_clear();
+ error = 0;
+ }
+
+ goto done;
+ }
+
+ if ((repo->oid_type = git_oid_type_fromstr(entry->value)) == 0) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "unknown object format '%s'", entry->value);
+ error = GIT_EINVALID;
+ }
+
+done:
+ git_config_entry_free(entry);
+ return error;
+}
+
+int git_repository__set_objectformat(
+ git_repository *repo,
+ git_oid_t oid_type)
+{
+ git_config *cfg;
+
+ /*
+ * Older clients do not necessarily understand the
+ * `objectformat` extension, even when it's set to an
+ * object format that they understand (SHA1). Do not set
+ * the objectformat extension unless we're not using the
+ * default object format.
+ */
+ if (oid_type == GIT_OID_DEFAULT)
+ return 0;
+
+ if (!git_repository_is_empty(repo) && repo->oid_type != oid_type) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "cannot change object id type of existing repository");
+ return -1;
+ }
+
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
+ return -1;
+
+ if (git_config_set_int32(cfg,
+ "core.repositoryformatversion", 1) < 0 ||
+ git_config_set_string(cfg, "extensions.objectformat",
+ git_oid_type_name(oid_type)) < 0)
+ return -1;
+
+ /*
+ * During repo init, we may create some backends with the
+ * default oid type. Clear them so that we create them with
+ * the proper oid type.
+ */
+ if (repo->oid_type != oid_type) {
+ set_index(repo, NULL);
+ set_odb(repo, NULL);
+ set_refdb(repo, NULL);
+
+ repo->oid_type = oid_type;
+ }
+
+ return 0;
+}
+
int git_repository__extensions(char ***out, size_t *out_len)
{
git_vector extensions;
@@ -1628,7 +2024,7 @@ int git_repository__extensions(char ***out, size_t *out_len)
char *extension;
size_t i, j;
- if (git_vector_init(&extensions, 8, NULL) < 0)
+ if (git_vector_init(&extensions, 8, git__strcmp_cb) < 0)
return -1;
for (i = 0; i < ARRAY_SIZE(builtin_extensions); i++) {
@@ -1660,21 +2056,49 @@ int git_repository__extensions(char ***out, size_t *out_len)
return -1;
}
+ git_vector_sort(&extensions);
+
*out = (char **)git_vector_detach(out_len, NULL, &extensions);
return 0;
}
+static int dup_ext_err(void **old, void *extension)
+{
+ GIT_UNUSED(old);
+ GIT_UNUSED(extension);
+ return GIT_EEXISTS;
+}
+
int git_repository__set_extensions(const char **extensions, size_t len)
{
char *extension;
- size_t i;
+ size_t i, j;
+ int error;
git_repository__free_extensions();
for (i = 0; i < len; i++) {
- if ((extension = git__strdup(extensions[i])) == NULL ||
- git_vector_insert(&user_extensions, extension) < 0)
+ bool is_builtin = false;
+
+ for (j = 0; j < ARRAY_SIZE(builtin_extensions); j++) {
+ if (strcmp(builtin_extensions[j], extensions[i]) == 0) {
+ is_builtin = true;
+ break;
+ }
+ }
+
+ if (is_builtin)
+ continue;
+
+ if ((extension = git__strdup(extensions[i])) == NULL)
return -1;
+
+ if ((error = git_vector_insert_sorted(&user_extensions, extension, dup_ext_err)) < 0) {
+ git__free(extension);
+
+ if (error != GIT_EEXISTS)
+ return -1;
+ }
}
return 0;
@@ -1743,7 +2167,7 @@ static bool is_filesystem_case_insensitive(const char *gitdir_path)
* Return a configuration object with only the global and system
* configurations; no repository-level configuration.
*/
-static int load_global_config(git_config **config)
+static int load_global_config(git_config **config, bool use_env)
{
git_str global_buf = GIT_STR_INIT;
git_str xdg_buf = GIT_STR_INIT;
@@ -1751,16 +2175,17 @@ static int load_global_config(git_config **config)
git_str programdata_buf = GIT_STR_INIT;
int error;
- git_config__find_global(&global_buf);
- git_config__find_xdg(&xdg_buf);
- git_config__find_system(&system_buf);
- git_config__find_programdata(&programdata_buf);
+ if (!(error = config_path_system(&system_buf, use_env)) &&
+ !(error = config_path_global(&global_buf, use_env))) {
+ git_config__find_xdg(&xdg_buf);
+ git_config__find_programdata(&programdata_buf);
- error = load_config(config, NULL,
- path_unless_empty(&global_buf),
- path_unless_empty(&xdg_buf),
- path_unless_empty(&system_buf),
- path_unless_empty(&programdata_buf));
+ error = load_config(config, NULL,
+ path_unless_empty(&global_buf),
+ path_unless_empty(&xdg_buf),
+ path_unless_empty(&system_buf),
+ path_unless_empty(&programdata_buf));
+ }
git_str_dispose(&global_buf);
git_str_dispose(&xdg_buf);
@@ -1770,7 +2195,7 @@ static int load_global_config(git_config **config)
return error;
}
-static bool are_symlinks_supported(const char *wd_path)
+static bool are_symlinks_supported(const char *wd_path, bool use_env)
{
git_config *config = NULL;
int symlinks = 0;
@@ -1783,10 +2208,12 @@ static bool are_symlinks_supported(const char *wd_path)
* _not_ set, then we do not test or enable symlink support.
*/
#ifdef GIT_WIN32
- if (load_global_config(&config) < 0 ||
+ if (load_global_config(&config, use_env) < 0 ||
git_config_get_bool(&symlinks, config, "core.symlinks") < 0 ||
!symlinks)
goto done;
+#else
+ GIT_UNUSED(use_env);
#endif
if (!(symlinks = git_fs_path_supports_symlinks(wd_path)))
@@ -1859,7 +2286,8 @@ static int repo_init_fs_configs(
const char *cfg_path,
const char *repo_dir,
const char *work_dir,
- bool update_ignorecase)
+ bool update_ignorecase,
+ bool use_env)
{
int error = 0;
@@ -1870,7 +2298,7 @@ static int repo_init_fs_configs(
cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0)
return error;
- if (!are_symlinks_supported(work_dir)) {
+ if (!are_symlinks_supported(work_dir, use_env)) {
if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0)
return error;
} else if (git_config_delete_entry(cfg, "core.symlinks") < 0)
@@ -1899,19 +2327,22 @@ static int repo_init_config(
const char *repo_dir,
const char *work_dir,
uint32_t flags,
- uint32_t mode)
+ uint32_t mode,
+ git_oid_t oid_type)
{
int error = 0;
git_str cfg_path = GIT_STR_INIT, worktree_path = GIT_STR_INIT;
git_config *config = NULL;
bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
- int version = 0;
+ bool use_env = ((flags & GIT_REPOSITORY_OPEN_FROM_ENV) != 0);
+ int version = GIT_REPO_VERSION_DEFAULT;
if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0)
goto cleanup;
- if (is_reinit && (error = check_repositoryformatversion(&version, config)) < 0)
+ if (is_reinit &&
+ (error = check_repositoryformatversion(&version, config)) < 0)
goto cleanup;
if ((error = check_extensions(config, version)) < 0)
@@ -1922,10 +2353,11 @@ static int repo_init_config(
goto cleanup; } while (0)
SET_REPO_CONFIG(bool, "core.bare", is_bare);
- SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
+ SET_REPO_CONFIG(int32, "core.repositoryformatversion", version);
if ((error = repo_init_fs_configs(
- config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0)
+ config, cfg_path.ptr, repo_dir, work_dir,
+ !is_reinit, use_env)) < 0)
goto cleanup;
if (!is_bare) {
@@ -1955,6 +2387,11 @@ static int repo_init_config(
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
}
+ if (oid_type != GIT_OID_DEFAULT) {
+ SET_REPO_CONFIG(int32, "core.repositoryformatversion", 1);
+ SET_REPO_CONFIG(string, "extensions.objectformat", git_oid_type_name(oid_type));
+ }
+
cleanup:
git_str_dispose(&cfg_path);
git_str_dispose(&worktree_path);
@@ -1984,8 +2421,8 @@ int git_repository_reinit_filesystem(git_repository *repo, int recurse)
const char *repo_dir = git_repository_path(repo);
if (!(error = repo_local_config(&config, &path, repo, repo_dir)))
- error = repo_init_fs_configs(
- config, path.ptr, repo_dir, git_repository_workdir(repo), true);
+ error = repo_init_fs_configs(config, path.ptr, repo_dir,
+ git_repository_workdir(repo), true, repo->use_env);
git_config_free(config);
git_str_dispose(&path);
@@ -2275,6 +2712,8 @@ static int repo_init_directories(
if (git_str_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0)
return -1;
+ git_fs_path_mkposix(repo_path->ptr);
+
has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0);
if (has_dotgit)
opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT;
@@ -2435,6 +2874,7 @@ int git_repository_init_ext(
common_path = GIT_STR_INIT;
const char *wd;
bool is_valid;
+ git_oid_t oid_type = GIT_OID_DEFAULT;
int error;
GIT_ASSERT_ARG(out);
@@ -2443,12 +2883,17 @@ int git_repository_init_ext(
GIT_ERROR_CHECK_VERSION(opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, "git_repository_init_options");
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (opts->oid_type)
+ oid_type = opts->oid_type;
+#endif
+
if ((error = repo_init_directories(&repo_path, &wd_path, given_repo, opts)) < 0)
goto out;
wd = (opts->flags & GIT_REPOSITORY_INIT_BARE) ? NULL : git_str_cstr(&wd_path);
- if ((error = is_valid_repository_path(&is_valid, &repo_path, &common_path)) < 0)
+ if ((error = is_valid_repository_path(&is_valid, &repo_path, &common_path, opts->flags)) < 0)
goto out;
if (is_valid) {
@@ -2461,13 +2906,13 @@ int git_repository_init_ext(
opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
- if ((error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode)) < 0)
+ if ((error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode, oid_type)) < 0)
goto out;
/* TODO: reinitialize the templates */
} else {
if ((error = repo_init_structure(repo_path.ptr, wd, opts)) < 0 ||
- (error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode)) < 0 ||
+ (error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode, oid_type)) < 0 ||
(error = repo_init_head(repo_path.ptr, opts->initial_head)) < 0)
goto out;
}
@@ -2836,14 +3281,18 @@ int git_repository_set_workdir(
if (git_fs_path_prettify_dir(&path, workdir, NULL) < 0)
return -1;
- if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0)
+ if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0) {
+ git_str_dispose(&path);
return 0;
+ }
if (update_gitlink) {
git_config *config;
- if (git_repository_config__weakptr(&config, repo) < 0)
+ if (git_repository_config__weakptr(&config, repo) < 0) {
+ git_str_dispose(&path);
return -1;
+ }
error = repo_write_gitlink(path.ptr, git_repository_path(repo), false);
@@ -2865,6 +3314,7 @@ int git_repository_set_workdir(
git__free(old_workdir);
}
+ git_str_dispose(&path);
return error;
}
@@ -2907,6 +3357,25 @@ int git_repository_set_bare(git_repository *repo)
return 0;
}
+int git_repository_head_commit(git_commit **commit, git_repository *repo)
+{
+ git_reference *head;
+ git_object *obj;
+ int error;
+
+ if ((error = git_repository_head(&head, repo)) < 0)
+ return error;
+
+ if ((error = git_reference_peel(&obj, head, GIT_OBJECT_COMMIT)) < 0)
+ goto cleanup;
+
+ *commit = (git_commit *)obj;
+
+cleanup:
+ git_reference_free(head);
+ return error;
+}
+
int git_repository_head_tree(git_tree **tree, git_repository *repo)
{
git_reference *head;
@@ -2930,14 +3399,14 @@ int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head
{
git_filebuf file = GIT_FILEBUF_INIT;
git_str file_path = GIT_STR_INIT;
- char orig_head_str[GIT_OID_SHA1_HEXSIZE];
+ char orig_head_str[GIT_OID_MAX_HEXSIZE];
int error = 0;
git_oid_fmt(orig_head_str, orig_head);
if ((error = git_str_joinpath(&file_path, repo->gitdir, GIT_ORIG_HEAD_FILE)) == 0 &&
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_MERGE_FILE_MODE)) == 0 &&
- (error = git_filebuf_printf(&file, "%.*s\n", GIT_OID_SHA1_HEXSIZE, orig_head_str)) == 0)
+ (error = git_filebuf_printf(&file, "%.*s\n", (int)git_oid_hexsize(repo->oid_type), orig_head_str)) == 0)
error = git_filebuf_commit(&file);
if (error < 0)
@@ -3050,7 +3519,7 @@ int git_repository_hashfile(
goto cleanup;
}
- error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, GIT_OID_SHA1, fl);
+ error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, repo->oid_type, fl);
cleanup:
if (fd >= 0)
@@ -3063,12 +3532,18 @@ int git_repository_hashfile(
static int checkout_message(git_str *out, git_reference *old, const char *new)
{
+ const char *idstr;
+
git_str_puts(out, "checkout: moving from ");
- if (git_reference_type(old) == GIT_REFERENCE_SYMBOLIC)
+ if (git_reference_type(old) == GIT_REFERENCE_SYMBOLIC) {
git_str_puts(out, git_reference__shorthand(git_reference_symbolic_target(old)));
- else
- git_str_puts(out, git_oid_tostr_s(git_reference_target(old)));
+ } else {
+ if ((idstr = git_oid_tostr_s(git_reference_target(old))) == NULL)
+ return -1;
+
+ git_str_puts(out, idstr);
+ }
git_str_puts(out, " to ");
@@ -3104,8 +3579,11 @@ static int detach(git_repository *repo, const git_oid *id, const char *new)
if ((error = git_object_peel(&peeled, object, GIT_OBJECT_COMMIT)) < 0)
goto cleanup;
- if (new == NULL)
- new = git_oid_tostr_s(git_object_id(peeled));
+ if (new == NULL &&
+ (new = git_oid_tostr_s(git_object_id(peeled))) == NULL) {
+ error = -1;
+ goto cleanup;
+ }
if ((error = checkout_message(&log_message, current, new)) < 0)
goto cleanup;
@@ -3193,6 +3671,7 @@ int git_repository_detach_head(git_repository *repo)
git_reference *old_head = NULL, *new_head = NULL, *current = NULL;
git_object *object = NULL;
git_str log_message = GIT_STR_INIT;
+ const char *idstr;
int error;
GIT_ASSERT_ARG(repo);
@@ -3206,7 +3685,12 @@ int git_repository_detach_head(git_repository *repo)
if ((error = git_object_lookup(&object, repo, git_reference_target(old_head), GIT_OBJECT_COMMIT)) < 0)
goto cleanup;
- if ((error = checkout_message(&log_message, current, git_oid_tostr_s(git_object_id(object)))) < 0)
+ if ((idstr = git_oid_tostr_s(git_object_id(object))) == NULL) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = checkout_message(&log_message, current, idstr)) < 0)
goto cleanup;
error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head),
@@ -3312,6 +3796,66 @@ int git_repository_state_cleanup(git_repository *repo)
return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
}
+int git_repository__shallow_roots(
+ git_oid **out,
+ size_t *out_len,
+ git_repository *repo)
+{
+ int error = 0;
+
+ if (!repo->shallow_grafts && (error = load_grafts(repo)) < 0)
+ return error;
+
+ if ((error = git_grafts_refresh(repo->shallow_grafts)) < 0)
+ return error;
+
+ if ((error = git_grafts_oids(out, out_len, repo->shallow_grafts)) < 0)
+ return error;
+
+ return 0;
+}
+
+int git_repository__shallow_roots_write(git_repository *repo, git_oidarray *roots)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str path = GIT_STR_INIT;
+ char oid_str[GIT_OID_MAX_HEXSIZE + 1];
+ size_t i;
+ int filebuf_hash, error = 0;
+
+ GIT_ASSERT_ARG(repo);
+
+ filebuf_hash = git_filebuf_hash_flags(git_oid_algorithm(repo->oid_type));
+ GIT_ASSERT(filebuf_hash);
+
+ if ((error = git_str_joinpath(&path, repo->gitdir, "shallow")) < 0)
+ goto on_error;
+
+ if ((error = git_filebuf_open(&file, git_str_cstr(&path), filebuf_hash, 0666)) < 0)
+ goto on_error;
+
+ for (i = 0; i < roots->count; i++) {
+ git_oid_tostr(oid_str, sizeof(oid_str), &roots->ids[i]);
+ git_filebuf_write(&file, oid_str, git_oid_hexsize(repo->oid_type));
+ git_filebuf_write(&file, "\n", 1);
+ }
+
+ git_filebuf_commit(&file);
+
+ if ((error = load_grafts(repo)) < 0) {
+ error = -1;
+ goto on_error;
+ }
+
+ if (!roots->count)
+ remove(path.ptr);
+
+on_error:
+ git_str_dispose(&path);
+
+ return error;
+}
+
int git_repository_is_shallow(git_repository *repo)
{
git_str path = GIT_STR_INIT;
@@ -3331,6 +3875,7 @@ int git_repository_is_shallow(git_repository *repo)
if (error < 0)
return error;
+
return st.st_size == 0 ? 0 : 1;
}
@@ -3397,3 +3942,70 @@ int git_repository_submodule_cache_clear(git_repository *repo)
repo->submodule_cache = NULL;
return error;
}
+
+git_oid_t git_repository_oid_type(git_repository *repo)
+{
+ return repo ? repo->oid_type : 0;
+}
+
+struct mergehead_data {
+ git_repository *repo;
+ git_vector *parents;
+};
+
+static int insert_mergehead(const git_oid *oid, void *payload)
+{
+ git_commit *commit;
+ struct mergehead_data *data = (struct mergehead_data *)payload;
+
+ if (git_commit_lookup(&commit, data->repo, oid) < 0)
+ return -1;
+
+ return git_vector_insert(data->parents, commit);
+}
+
+int git_repository_commit_parents(git_commitarray *out, git_repository *repo)
+{
+ git_commit *first_parent = NULL, *commit;
+ git_reference *head_ref = NULL;
+ git_vector parents = GIT_VECTOR_INIT;
+ struct mergehead_data data;
+ size_t i;
+ int error;
+
+ GIT_ASSERT_ARG(out && repo);
+
+ out->count = 0;
+ out->commits = NULL;
+
+ error = git_revparse_ext((git_object **)&first_parent, &head_ref, repo, "HEAD");
+
+ if (error != 0) {
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+
+ goto done;
+ }
+
+ if ((error = git_vector_insert(&parents, first_parent)) < 0)
+ goto done;
+
+ data.repo = repo;
+ data.parents = &parents;
+
+ error = git_repository_mergehead_foreach(repo, insert_mergehead, &data);
+
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+ else if (error != 0)
+ goto done;
+
+ out->commits = (git_commit **)git_vector_detach(&out->count, NULL, &parents);
+
+done:
+ git_vector_foreach(&parents, i, commit)
+ git__free(commit);
+
+ git_reference_free(head_ref);
+ return error;
+}
diff --git a/src/libgit2/repository.h b/src/libgit2/repository.h
index 81d7daece14..4d0c6aa9f3f 100644
--- a/src/libgit2/repository.h
+++ b/src/libgit2/repository.h
@@ -24,6 +24,7 @@
#include "attrcache.h"
#include "submodule.h"
#include "diff_driver.h"
+#include "grafts.h"
#define DOT_GIT ".git"
#define GIT_DIR DOT_GIT "/"
@@ -154,11 +155,16 @@ struct git_repository {
git_array_t(git_str) reserved_names;
- unsigned is_bare:1;
- unsigned is_worktree:1;
+ unsigned use_env:1,
+ is_bare:1,
+ is_worktree:1;
+ git_oid_t oid_type;
unsigned int lru_counter;
+ git_grafts *grafts;
+ git_grafts *shallow_grafts;
+
git_atomic32 attr_session_key;
intptr_t configmap_cache[GIT_CONFIGMAP_CACHE_MAX];
@@ -170,6 +176,7 @@ GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
return repo->attrcache;
}
+int git_repository_head_commit(git_commit **commit, git_repository *repo);
int git_repository_head_tree(git_tree **tree, git_repository *repo);
int git_repository_create_head(const char *git_dir, const char *ref_name);
@@ -190,6 +197,13 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo);
int git_repository_odb__weakptr(git_odb **out, git_repository *repo);
int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo);
int git_repository_index__weakptr(git_index **out, git_repository *repo);
+int git_repository_grafts__weakptr(git_grafts **out, git_repository *repo);
+int git_repository_shallow_grafts__weakptr(git_grafts **out, git_repository *repo);
+
+int git_repository__wrap_odb(
+ git_repository **out,
+ git_odb *odb,
+ git_oid_t oid_type);
/*
* Configuration map cache
@@ -241,6 +255,9 @@ extern size_t git_repository__reserved_names_posix_len;
bool git_repository__reserved_names(
git_str **out, size_t *outlen, git_repository *repo, bool include_ntfs);
+int git_repository__shallow_roots(git_oid **out, size_t *out_len, git_repository *repo);
+int git_repository__shallow_roots_write(git_repository *repo, git_oidarray *roots);
+
/*
* The default branch for the repository; the `init.defaultBranch`
* configuration option, if set, or `master` if it is not.
@@ -259,4 +276,15 @@ int git_repository__extensions(char ***out, size_t *out_len);
int git_repository__set_extensions(const char **extensions, size_t len);
void git_repository__free_extensions(void);
+/*
+ * Set the object format (OID type) for a repository; this will set
+ * both the configuration and the internal value for the oid type.
+ */
+int git_repository__set_objectformat(
+ git_repository *repo,
+ git_oid_t oid_type);
+
+/* SHA256-aware internal functions */
+int git_repository__new(git_repository **out, git_oid_t oid_type);
+
#endif
diff --git a/src/libgit2/reset.c b/src/libgit2/reset.c
index 9574819cb16..605c4afd5e2 100644
--- a/src/libgit2/reset.c
+++ b/src/libgit2/reset.c
@@ -188,9 +188,9 @@ int git_reset(
git_reset_t reset_type,
const git_checkout_options *checkout_opts)
{
- char to[GIT_OID_SHA1_HEXSIZE + 1];
+ char to[GIT_OID_MAX_HEXSIZE + 1];
- git_oid_tostr(to, GIT_OID_SHA1_HEXSIZE + 1, git_object_id(target));
+ git_oid_tostr(to, GIT_OID_MAX_HEXSIZE + 1, git_object_id(target));
return reset(repo, target, to, reset_type, checkout_opts);
}
diff --git a/src/libgit2/revert.c b/src/libgit2/revert.c
index 1106dfe2f2c..4a31ad40a1a 100644
--- a/src/libgit2/revert.c
+++ b/src/libgit2/revert.c
@@ -107,12 +107,10 @@ static int revert_state_cleanup(git_repository *repo)
static int revert_seterr(git_commit *commit, const char *fmt)
{
- char commit_oidstr[GIT_OID_SHA1_HEXSIZE + 1];
+ char commit_id[GIT_OID_MAX_HEXSIZE + 1];
- git_oid_fmt(commit_oidstr, git_commit_id(commit));
- commit_oidstr[GIT_OID_SHA1_HEXSIZE] = '\0';
-
- git_error_set(GIT_ERROR_REVERT, fmt, commit_oidstr);
+ git_oid_tostr(commit_id, GIT_OID_MAX_HEXSIZE + 1, git_commit_id(commit));
+ git_error_set(GIT_ERROR_REVERT, fmt, commit_id);
return -1;
}
@@ -176,7 +174,7 @@ int git_revert(
git_revert_options opts;
git_reference *our_ref = NULL;
git_commit *our_commit = NULL;
- char commit_oidstr[GIT_OID_SHA1_HEXSIZE + 1];
+ char commit_id[GIT_OID_MAX_HEXSIZE + 1];
const char *commit_msg;
git_str their_label = GIT_STR_INIT;
git_index *index = NULL;
@@ -191,19 +189,18 @@ int git_revert(
if ((error = git_repository__ensure_not_bare(repo, "revert")) < 0)
return error;
- git_oid_fmt(commit_oidstr, git_commit_id(commit));
- commit_oidstr[GIT_OID_SHA1_HEXSIZE] = '\0';
+ git_oid_tostr(commit_id, GIT_OID_MAX_HEXSIZE + 1, git_commit_id(commit));
if ((commit_msg = git_commit_summary(commit)) == NULL) {
error = -1;
goto on_error;
}
- if ((error = git_str_printf(&their_label, "parent of %.7s... %s", commit_oidstr, commit_msg)) < 0 ||
+ if ((error = git_str_printf(&their_label, "parent of %.7s... %s", commit_id, commit_msg)) < 0 ||
(error = revert_normalize_opts(repo, &opts, given_opts, git_str_cstr(&their_label))) < 0 ||
(error = git_indexwriter_init_for_operation(&indexwriter, repo, &opts.checkout_opts.checkout_strategy)) < 0 ||
- (error = write_revert_head(repo, commit_oidstr)) < 0 ||
- (error = write_merge_msg(repo, commit_oidstr, commit_msg)) < 0 ||
+ (error = write_revert_head(repo, commit_id)) < 0 ||
+ (error = write_merge_msg(repo, commit_id, commit_msg)) < 0 ||
(error = git_repository_head(&our_ref, repo)) < 0 ||
(error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJECT_COMMIT)) < 0 ||
(error = git_revert_commit(&index, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
diff --git a/src/libgit2/revparse.c b/src/libgit2/revparse.c
index 2ed28215957..9083e7a3cdc 100644
--- a/src/libgit2/revparse.c
+++ b/src/libgit2/revparse.c
@@ -15,21 +15,28 @@
#include "git2.h"
-static int maybe_sha_or_abbrev(git_object **out, git_repository *repo, const char *spec, size_t speclen)
+static int maybe_sha_or_abbrev(
+ git_object **out,
+ git_repository *repo,
+ const char *spec,
+ size_t speclen)
{
git_oid oid;
- if (git_oid__fromstrn(&oid, spec, speclen, GIT_OID_SHA1) < 0)
+ if (git_oid__fromstrn(&oid, spec, speclen, repo->oid_type) < 0)
return GIT_ENOTFOUND;
return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJECT_ANY);
}
-static int maybe_sha(git_object **out, git_repository *repo, const char *spec)
+static int maybe_sha(
+ git_object **out,
+ git_repository *repo,
+ const char *spec)
{
size_t speclen = strlen(spec);
- if (speclen != GIT_OID_SHA1_HEXSIZE)
+ if (speclen != git_oid_hexsize(repo->oid_type))
return GIT_ENOTFOUND;
return maybe_sha_or_abbrev(out, repo, spec, speclen);
@@ -110,8 +117,8 @@ static int revparse_lookup_object(
if (error != GIT_ENOTFOUND)
return error;
- if ((strlen(spec) < GIT_OID_SHA1_HEXSIZE) &&
- ((error = maybe_abbrev(object_out, repo, spec)) != GIT_ENOTFOUND))
+ if ((strlen(spec) < git_oid_hexsize(repo->oid_type)) &&
+ ((error = maybe_abbrev(object_out, repo, spec)) != GIT_ENOTFOUND))
return error;
if ((error = maybe_describe(object_out, repo, spec)) != GIT_ENOTFOUND)
@@ -268,7 +275,16 @@ static int retrieve_revobject_from_reflog(git_object **out, git_reference **base
int error = -1;
if (*base_ref == NULL) {
- if ((error = git_reference_dwim(&ref, repo, identifier)) < 0)
+ /*
+ * When HEAD@{n} is specified, do not use dwim, which would resolve the
+ * reference (to the current branch that HEAD is pointing to).
+ */
+ if (position > 0 && strcmp(identifier, GIT_HEAD_FILE) == 0)
+ error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE);
+ else
+ error = git_reference_dwim(&ref, repo, identifier);
+
+ if (error < 0)
return error;
} else {
ref = *base_ref;
@@ -685,6 +701,7 @@ static int revparse(
git_object *base_rev = NULL;
bool should_return_reference = true;
+ bool parsed = false;
GIT_ASSERT_ARG(object_out);
GIT_ASSERT_ARG(reference_out);
@@ -694,7 +711,7 @@ static int revparse(
*object_out = NULL;
*reference_out = NULL;
- while (spec[pos]) {
+ while (!parsed && spec[pos]) {
switch (spec[pos]) {
case '^':
should_return_reference = false;
@@ -799,8 +816,10 @@ static int revparse(
if (temp_object != NULL)
base_rev = temp_object;
break;
- } else if (spec[pos+1] == '\0') {
+ } else if (spec[pos + 1] == '\0' && !pos) {
spec = "HEAD";
+ identifier_len = 4;
+ parsed = true;
break;
}
/* fall through */
@@ -916,7 +935,7 @@ int git_revparse(
* allowed.
*/
if (!git__strcmp(spec, "..")) {
- git_error_set(GIT_ERROR_INVALID, "Invalid pattern '..'");
+ git_error_set(GIT_ERROR_INVALID, "invalid pattern '..'");
return GIT_EINVALIDSPEC;
}
diff --git a/src/libgit2/revwalk.c b/src/libgit2/revwalk.c
index 553e0497a14..4ea6fae8ffb 100644
--- a/src/libgit2/revwalk.c
+++ b/src/libgit2/revwalk.c
@@ -83,8 +83,13 @@ int git_revwalk__push_commit(git_revwalk *walk, const git_oid *oid, const git_re
commit->uninteresting = opts->uninteresting;
list = walk->user_input;
- if ((opts->insert_by_date &&
- git_commit_list_insert_by_date(commit, &list) == NULL) ||
+
+ /* To insert by date, we need to parse so we know the date. */
+ if (opts->insert_by_date && ((error = git_commit_list_parse(walk, commit)) < 0))
+ return error;
+
+ if ((opts->insert_by_date == 0 ||
+ git_commit_list_insert_by_date(commit, &list) == NULL) &&
git_commit_list_insert(commit, &list) == NULL) {
git_error_set_oom();
return -1;
@@ -121,8 +126,12 @@ int git_revwalk__push_ref(git_revwalk *walk, const char *refname, const git_revw
{
git_oid oid;
- if (git_reference_name_to_id(&oid, walk->repo, refname) < 0)
+ int error = git_reference_name_to_id(&oid, walk->repo, refname);
+ if (opts->from_glob && (error == GIT_ENOTFOUND || error == GIT_EINVALIDSPEC || error == GIT_EPEEL)) {
+ return 0;
+ } else if (error < 0) {
return -1;
+ }
return git_revwalk__push_commit(walk, &oid, opts);
}
@@ -605,7 +614,7 @@ static int sort_in_topological_order(git_commit_list **out, git_revwalk *walk, g
static int prepare_walk(git_revwalk *walk)
{
int error = 0;
- git_commit_list *list, *commits = NULL;
+ git_commit_list *list, *commits = NULL, *commits_last = NULL;
git_commit_list_node *next;
/* If there were no pushes, we know that the walk is already over */
@@ -614,6 +623,12 @@ static int prepare_walk(git_revwalk *walk)
return GIT_ITEROVER;
}
+ /*
+ * This is a bit convoluted, but necessary to maintain the order of
+ * the commits. This is especially important in situations where
+ * git_revwalk__push_glob is called with a git_revwalk__push_options
+ * setting insert_by_date = 1, which is critical for fetch negotiation.
+ */
for (list = walk->user_input; list; list = list->next) {
git_commit_list_node *commit = list->item;
if ((error = git_commit_list_parse(walk, commit)) < 0)
@@ -623,8 +638,19 @@ static int prepare_walk(git_revwalk *walk)
mark_parents_uninteresting(commit);
if (!commit->seen) {
+ git_commit_list *new_list = NULL;
+ if ((new_list = git_commit_list_create(commit, NULL)) == NULL) {
+ git_error_set_oom();
+ return -1;
+ }
+
commit->seen = 1;
- git_commit_list_insert(commit, &commits);
+ if (commits_last == NULL)
+ commits = new_list;
+ else
+ commits_last->next = new_list;
+
+ commits_last = new_list;
}
}
diff --git a/src/libgit2/settings.c b/src/libgit2/settings.c
new file mode 100644
index 00000000000..4a41830b8fd
--- /dev/null
+++ b/src/libgit2/settings.c
@@ -0,0 +1,456 @@
+/*
+ * 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 "settings.h"
+
+#include
+#include "alloc.h"
+#include "buf.h"
+#include "cache.h"
+#include "common.h"
+#include "filter.h"
+#include "grafts.h"
+#include "hash.h"
+#include "index.h"
+#include "merge_driver.h"
+#include "pool.h"
+#include "mwindow.h"
+#include "object.h"
+#include "odb.h"
+#include "rand.h"
+#include "refs.h"
+#include "runtime.h"
+#include "sysdir.h"
+#include "thread.h"
+#include "git2/global.h"
+#include "streams/registry.h"
+#include "streams/mbedtls.h"
+#include "streams/openssl.h"
+#include "streams/socket.h"
+#include "transports/smart.h"
+#include "transports/http.h"
+#include "transports/ssh_libssh2.h"
+
+#ifdef GIT_WIN32
+# include "win32/w32_leakcheck.h"
+#endif
+
+/* Declarations for tuneable settings */
+extern size_t git_mwindow__window_size;
+extern size_t git_mwindow__mapped_limit;
+extern size_t git_mwindow__file_limit;
+extern size_t git_indexer__max_objects;
+extern bool git_disable_pack_keep_file_checks;
+extern int git_odb__packed_priority;
+extern int git_odb__loose_priority;
+extern int git_socket_stream__connect_timeout;
+extern int git_socket_stream__timeout;
+
+char *git__user_agent;
+char *git__user_agent_product;
+char *git__ssl_ciphers;
+
+static void settings_global_shutdown(void)
+{
+ git__free(git__user_agent);
+ git__free(git__user_agent_product);
+
+ git__free(git__ssl_ciphers);
+ git_repository__free_extensions();
+}
+
+int git_settings_global_init(void)
+{
+ return git_runtime_shutdown_register(settings_global_shutdown);
+}
+
+static int config_level_to_sysdir(int *out, int config_level)
+{
+ switch (config_level) {
+ case GIT_CONFIG_LEVEL_SYSTEM:
+ *out = GIT_SYSDIR_SYSTEM;
+ return 0;
+ case GIT_CONFIG_LEVEL_XDG:
+ *out = GIT_SYSDIR_XDG;
+ return 0;
+ case GIT_CONFIG_LEVEL_GLOBAL:
+ *out = GIT_SYSDIR_GLOBAL;
+ return 0;
+ case GIT_CONFIG_LEVEL_PROGRAMDATA:
+ *out = GIT_SYSDIR_PROGRAMDATA;
+ return 0;
+ default:
+ break;
+ }
+
+ git_error_set(
+ GIT_ERROR_INVALID, "invalid config path selector %d", config_level);
+ return -1;
+}
+
+const char *git_settings__user_agent_product(void)
+{
+ return git__user_agent_product ? git__user_agent_product :
+ "git/2.0";
+}
+
+const char *git_settings__user_agent(void)
+{
+ return git__user_agent ? git__user_agent :
+ "libgit2 " LIBGIT2_VERSION;
+}
+
+int git_libgit2_opts(int key, ...)
+{
+ int error = 0;
+ va_list ap;
+
+ va_start(ap, key);
+
+ switch (key) {
+ case GIT_OPT_SET_MWINDOW_SIZE:
+ git_mwindow__window_size = va_arg(ap, size_t);
+ break;
+
+ case GIT_OPT_GET_MWINDOW_SIZE:
+ *(va_arg(ap, size_t *)) = git_mwindow__window_size;
+ break;
+
+ case GIT_OPT_SET_MWINDOW_MAPPED_LIMIT:
+ git_mwindow__mapped_limit = va_arg(ap, size_t);
+ break;
+
+ case GIT_OPT_GET_MWINDOW_MAPPED_LIMIT:
+ *(va_arg(ap, size_t *)) = git_mwindow__mapped_limit;
+ break;
+
+ case GIT_OPT_SET_MWINDOW_FILE_LIMIT:
+ git_mwindow__file_limit = va_arg(ap, size_t);
+ break;
+
+ case GIT_OPT_GET_MWINDOW_FILE_LIMIT:
+ *(va_arg(ap, size_t *)) = git_mwindow__file_limit;
+ break;
+
+ case GIT_OPT_GET_SEARCH_PATH:
+ {
+ int sysdir = va_arg(ap, int);
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+ const git_str *tmp;
+ int level;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = config_level_to_sysdir(&level, sysdir)) < 0 ||
+ (error = git_sysdir_get(&tmp, level)) < 0 ||
+ (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_SET_SEARCH_PATH:
+ {
+ int level;
+
+ if ((error = config_level_to_sysdir(&level, va_arg(ap, int))) >= 0)
+ error = git_sysdir_set(level, va_arg(ap, const char *));
+ }
+ break;
+
+ case GIT_OPT_SET_CACHE_OBJECT_LIMIT:
+ {
+ git_object_t type = (git_object_t)va_arg(ap, int);
+ size_t size = va_arg(ap, size_t);
+ error = git_cache_set_max_object_size(type, size);
+ break;
+ }
+
+ case GIT_OPT_SET_CACHE_MAX_SIZE:
+ git_cache__max_storage = va_arg(ap, ssize_t);
+ break;
+
+ case GIT_OPT_ENABLE_CACHING:
+ git_cache__enabled = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_GET_CACHED_MEMORY:
+ *(va_arg(ap, ssize_t *)) = git_cache__current_storage.val;
+ *(va_arg(ap, ssize_t *)) = git_cache__max_storage;
+ break;
+
+ case GIT_OPT_GET_TEMPLATE_PATH:
+ {
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+ const git_str *tmp;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_sysdir_get(&tmp, GIT_SYSDIR_TEMPLATE)) < 0 ||
+ (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_SET_TEMPLATE_PATH:
+ error = git_sysdir_set(GIT_SYSDIR_TEMPLATE, va_arg(ap, const char *));
+ break;
+
+ case GIT_OPT_SET_SSL_CERT_LOCATIONS:
+#ifdef GIT_OPENSSL
+ {
+ const char *file = va_arg(ap, const char *);
+ const char *path = va_arg(ap, const char *);
+ error = git_openssl__set_cert_location(file, path);
+ }
+#elif defined(GIT_MBEDTLS)
+ {
+ const char *file = va_arg(ap, const char *);
+ const char *path = va_arg(ap, const char *);
+ error = git_mbedtls__set_cert_location(file, path);
+ }
+#else
+ git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support certificate locations");
+ error = -1;
+#endif
+ break;
+
+ case GIT_OPT_SET_USER_AGENT:
+ {
+ const char *new_agent = va_arg(ap, const char *);
+
+ git__free(git__user_agent);
+
+ if (new_agent) {
+ git__user_agent= git__strdup(new_agent);
+
+ if (!git__user_agent)
+ error = -1;
+ } else {
+ git__user_agent = NULL;
+ }
+ }
+ break;
+
+ case GIT_OPT_GET_USER_AGENT:
+ {
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_str_puts(&str, git_settings__user_agent())) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_SET_USER_AGENT_PRODUCT:
+ {
+ const char *new_agent = va_arg(ap, const char *);
+
+ git__free(git__user_agent_product);
+
+ if (new_agent) {
+ git__user_agent_product = git__strdup(new_agent);
+
+ if (!git__user_agent_product)
+ error = -1;
+ } else {
+ git__user_agent_product = NULL;
+ }
+ }
+ break;
+
+ case GIT_OPT_GET_USER_AGENT_PRODUCT:
+ {
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_str_puts(&str, git_settings__user_agent_product())) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_ENABLE_STRICT_OBJECT_CREATION:
+ git_object__strict_input_validation = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION:
+ git_reference__enable_symbolic_ref_target_validation = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_SET_SSL_CIPHERS:
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+ {
+ git__free(git__ssl_ciphers);
+ git__ssl_ciphers = git__strdup(va_arg(ap, const char *));
+ if (!git__ssl_ciphers) {
+ git_error_set_oom();
+ error = -1;
+ }
+ }
+#else
+ git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support custom ciphers");
+ error = -1;
+#endif
+ break;
+
+ case GIT_OPT_ENABLE_OFS_DELTA:
+ git_smart__ofs_delta_enabled = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_ENABLE_FSYNC_GITDIR:
+ git_repository__fsync_gitdir = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_GET_WINDOWS_SHAREMODE:
+#ifdef GIT_WIN32
+ *(va_arg(ap, unsigned long *)) = git_win32__createfile_sharemode;
+#endif
+ break;
+
+ case GIT_OPT_SET_WINDOWS_SHAREMODE:
+#ifdef GIT_WIN32
+ git_win32__createfile_sharemode = va_arg(ap, unsigned long);
+#endif
+ break;
+
+ case GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION:
+ git_odb__strict_hash_verification = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_SET_ALLOCATOR:
+ error = git_allocator_setup(va_arg(ap, git_allocator *));
+ break;
+
+ case GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY:
+ git_index__enforce_unsaved_safety = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_SET_PACK_MAX_OBJECTS:
+ git_indexer__max_objects = va_arg(ap, size_t);
+ break;
+
+ case GIT_OPT_GET_PACK_MAX_OBJECTS:
+ *(va_arg(ap, size_t *)) = git_indexer__max_objects;
+ break;
+
+ case GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS:
+ git_disable_pack_keep_file_checks = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE:
+ git_http__expect_continue = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_SET_ODB_PACKED_PRIORITY:
+ git_odb__packed_priority = va_arg(ap, int);
+ break;
+
+ case GIT_OPT_SET_ODB_LOOSE_PRIORITY:
+ git_odb__loose_priority = va_arg(ap, int);
+ break;
+
+ case GIT_OPT_SET_EXTENSIONS:
+ {
+ const char **extensions = va_arg(ap, const char **);
+ size_t len = va_arg(ap, size_t);
+ error = git_repository__set_extensions(extensions, len);
+ }
+ break;
+
+ case GIT_OPT_GET_EXTENSIONS:
+ {
+ git_strarray *out = va_arg(ap, git_strarray *);
+ char **extensions;
+ size_t len;
+
+ if ((error = git_repository__extensions(&extensions, &len)) < 0)
+ break;
+
+ out->strings = extensions;
+ out->count = len;
+ }
+ break;
+
+ case GIT_OPT_GET_OWNER_VALIDATION:
+ *(va_arg(ap, int *)) = git_repository__validate_ownership;
+ break;
+
+ case GIT_OPT_SET_OWNER_VALIDATION:
+ git_repository__validate_ownership = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_GET_HOMEDIR:
+ {
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+ const git_str *tmp;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_sysdir_get(&tmp, GIT_SYSDIR_HOME)) < 0 ||
+ (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_SET_HOMEDIR:
+ error = git_sysdir_set(GIT_SYSDIR_HOME, va_arg(ap, const char *));
+ break;
+
+ case GIT_OPT_GET_SERVER_CONNECT_TIMEOUT:
+ *(va_arg(ap, int *)) = git_socket_stream__connect_timeout;
+ break;
+
+ case GIT_OPT_SET_SERVER_CONNECT_TIMEOUT:
+ {
+ int timeout = va_arg(ap, int);
+
+ if (timeout < 0) {
+ git_error_set(GIT_ERROR_INVALID, "invalid connect timeout");
+ error = -1;
+ } else {
+ git_socket_stream__connect_timeout = timeout;
+ }
+ }
+ break;
+
+ case GIT_OPT_GET_SERVER_TIMEOUT:
+ *(va_arg(ap, int *)) = git_socket_stream__timeout;
+ break;
+
+ case GIT_OPT_SET_SERVER_TIMEOUT:
+ {
+ int timeout = va_arg(ap, int);
+
+ if (timeout < 0) {
+ git_error_set(GIT_ERROR_INVALID, "invalid timeout");
+ error = -1;
+ } else {
+ git_socket_stream__timeout = timeout;
+ }
+ }
+ break;
+
+ default:
+ git_error_set(GIT_ERROR_INVALID, "invalid option key");
+ error = -1;
+ }
+
+ va_end(ap);
+
+ return error;
+}
diff --git a/src/libgit2/settings.h b/src/libgit2/settings.h
index dc42ce93952..292936676aa 100644
--- a/src/libgit2/settings.h
+++ b/src/libgit2/settings.h
@@ -4,8 +4,12 @@
* 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_settings_h__
+#define INCLUDE_settings_h__
extern int git_settings_global_init(void);
-extern const char *git_libgit2__user_agent(void);
-extern const char *git_libgit2__ssl_ciphers(void);
+extern const char *git_settings__user_agent(void);
+extern const char *git_settings__user_agent_product(void);
+
+#endif
diff --git a/src/libgit2/signature.c b/src/libgit2/signature.c
index 5d6ab572cbf..12d2b5f8d50 100644
--- a/src/libgit2/signature.c
+++ b/src/libgit2/signature.c
@@ -43,7 +43,6 @@ static bool contains_angle_brackets(const char *input)
static bool is_crud(unsigned char c)
{
return c <= 32 ||
- c == '.' ||
c == ',' ||
c == ':' ||
c == ';' ||
diff --git a/src/libgit2/stash.c b/src/libgit2/stash.c
index 5fc01ac36dd..b49e95cdb21 100644
--- a/src/libgit2/stash.c
+++ b/src/libgit2/stash.c
@@ -25,6 +25,7 @@
#include "merge.h"
#include "diff.h"
#include "diff_generate.h"
+#include "strarray.h"
static int create_error(int error, const char *msg)
{
@@ -193,6 +194,30 @@ static int stash_to_index(
return git_index_add(index, &entry);
}
+static int stash_update_index_from_paths(
+ git_repository *repo,
+ git_index *index,
+ const git_strarray *paths)
+{
+ unsigned int status_flags;
+ size_t i;
+ int error = 0;
+
+ for (i = 0; i < paths->count; i++) {
+ git_status_file(&status_flags, repo, paths->strings[i]);
+
+ if (status_flags & (GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_DELETED)) {
+ if ((error = git_index_remove(index, paths->strings[i], 0)) < 0)
+ return error;
+ } else {
+ if ((error = stash_to_index(repo, index, paths->strings[i])) < 0)
+ return error;
+ }
+ }
+
+ return error;
+}
+
static int stash_update_index_from_diff(
git_repository *repo,
git_index *index,
@@ -259,7 +284,7 @@ static int build_untracked_tree(
struct stash_update_rules data = {0};
int error;
- if ((error = git_index_new(&i_index)) < 0)
+ if ((error = git_index__new(&i_index, repo->oid_type)) < 0)
goto cleanup;
if (flags & GIT_STASH_INCLUDE_UNTRACKED) {
@@ -388,26 +413,81 @@ static int build_workdir_tree(
return error;
}
-static int commit_worktree(
+static int build_stash_commit_from_tree(
git_oid *w_commit_oid,
git_repository *repo,
const git_signature *stasher,
const char *message,
git_commit *i_commit,
git_commit *b_commit,
- git_commit *u_commit)
+ git_commit *u_commit,
+ const git_tree *tree)
{
const git_commit *parents[] = { NULL, NULL, NULL };
- git_index *i_index = NULL, *r_index = NULL;
- git_tree *w_tree = NULL;
- int error = 0, ignorecase;
parents[0] = b_commit;
parents[1] = i_commit;
parents[2] = u_commit;
+ return git_commit_create(
+ w_commit_oid,
+ repo,
+ NULL,
+ stasher,
+ stasher,
+ NULL,
+ message,
+ tree,
+ u_commit ? 3 : 2,
+ parents);
+}
+
+static int build_stash_commit_from_index(
+ git_oid *w_commit_oid,
+ git_repository *repo,
+ const git_signature *stasher,
+ const char *message,
+ git_commit *i_commit,
+ git_commit *b_commit,
+ git_commit *u_commit,
+ git_index *index)
+{
+ git_tree *tree;
+ int error;
+
+ if ((error = build_tree_from_index(&tree, repo, index)) < 0)
+ goto cleanup;
+
+ error = build_stash_commit_from_tree(
+ w_commit_oid,
+ repo,
+ stasher,
+ message,
+ i_commit,
+ b_commit,
+ u_commit,
+ tree);
+
+cleanup:
+ git_tree_free(tree);
+ return error;
+}
+
+static int commit_worktree(
+ git_oid *w_commit_oid,
+ git_repository *repo,
+ const git_signature *stasher,
+ const char *message,
+ git_commit *i_commit,
+ git_commit *b_commit,
+ git_commit *u_commit)
+{
+ git_index *i_index = NULL, *r_index = NULL;
+ git_tree *w_tree = NULL;
+ int error = 0, ignorecase;
+
if ((error = git_repository_index(&r_index, repo) < 0) ||
- (error = git_index_new(&i_index)) < 0 ||
+ (error = git_index__new(&i_index, repo->oid_type)) < 0 ||
(error = git_index__fill(i_index, &r_index->entries) < 0) ||
(error = git_repository__configmap_lookup(&ignorecase, repo, GIT_CONFIGMAP_IGNORECASE)) < 0)
goto cleanup;
@@ -417,17 +497,16 @@ static int commit_worktree(
if ((error = build_workdir_tree(&w_tree, repo, i_index, b_commit)) < 0)
goto cleanup;
- error = git_commit_create(
+ error = build_stash_commit_from_tree(
w_commit_oid,
repo,
- NULL,
- stasher,
stasher,
- NULL,
message,
- w_tree,
- u_commit ? 3 : 2,
- parents);
+ i_commit,
+ b_commit,
+ u_commit,
+ w_tree
+ );
cleanup:
git_tree_free(w_tree);
@@ -520,6 +599,54 @@ static int ensure_there_are_changes_to_stash(git_repository *repo, uint32_t flag
return error;
}
+static int has_changes_cb(
+ const char *path,
+ unsigned int status,
+ void *payload)
+{
+ GIT_UNUSED(path);
+ GIT_UNUSED(status);
+ GIT_UNUSED(payload);
+
+ if (status == GIT_STATUS_CURRENT)
+ return GIT_ENOTFOUND;
+
+ return 0;
+}
+
+static int ensure_there_are_changes_to_stash_paths(
+ git_repository *repo,
+ uint32_t flags,
+ const git_strarray *paths)
+{
+ int error;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH;
+
+ if (flags & GIT_STASH_INCLUDE_UNTRACKED)
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ if (flags & GIT_STASH_INCLUDE_IGNORED)
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+
+ git_strarray_copy(&opts.pathspec, paths);
+
+ error = git_status_foreach_ext(repo, &opts, has_changes_cb, NULL);
+
+ git_strarray_dispose(&opts.pathspec);
+
+ if (error == GIT_ENOTFOUND)
+ return create_error(GIT_ENOTFOUND, "one of the files does not have any changes to stash.");
+
+ return error;
+}
+
static int reset_index_and_workdir(git_repository *repo, git_commit *commit, uint32_t flags)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
@@ -540,14 +667,36 @@ int git_stash_save(
const char *message,
uint32_t flags)
{
- git_index *index = NULL;
+ git_stash_save_options opts = GIT_STASH_SAVE_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(stasher);
+
+ opts.stasher = stasher;
+ opts.message = message;
+ opts.flags = flags;
+
+ return git_stash_save_with_opts(out, repo, &opts);
+}
+
+int git_stash_save_with_opts(
+ git_oid *out,
+ git_repository *repo,
+ const git_stash_save_options *opts)
+{
+ git_index *index = NULL, *paths_index = NULL;
git_commit *b_commit = NULL, *i_commit = NULL, *u_commit = NULL;
git_str msg = GIT_STR_INIT;
+ git_tree *tree = NULL;
+ git_reference *head = NULL;
+ bool has_paths = false;
+
int error;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(repo);
- GIT_ASSERT_ARG(stasher);
+ GIT_ASSERT_ARG(opts && opts->stasher);
+
+ has_paths = opts->paths.count > 0;
if ((error = git_repository__ensure_not_bare(repo, "stash save")) < 0)
return error;
@@ -555,44 +704,63 @@ int git_stash_save(
if ((error = retrieve_base_commit_and_message(&b_commit, &msg, repo)) < 0)
goto cleanup;
- if ((error = ensure_there_are_changes_to_stash(repo, flags)) < 0)
+ if (!has_paths &&
+ (error = ensure_there_are_changes_to_stash(repo, opts->flags)) < 0)
+ goto cleanup;
+ else if (has_paths &&
+ (error = ensure_there_are_changes_to_stash_paths(
+ repo, opts->flags, &opts->paths)) < 0)
goto cleanup;
if ((error = git_repository_index(&index, repo)) < 0)
goto cleanup;
- if ((error = commit_index(&i_commit, repo, index, stasher,
+ if ((error = commit_index(&i_commit, repo, index, opts->stasher,
git_str_cstr(&msg), b_commit)) < 0)
goto cleanup;
- if ((flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) &&
- (error = commit_untracked(&u_commit, repo, stasher,
- git_str_cstr(&msg), i_commit, flags)) < 0)
+ if ((opts->flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) &&
+ (error = commit_untracked(&u_commit, repo, opts->stasher,
+ git_str_cstr(&msg), i_commit, opts->flags)) < 0)
goto cleanup;
- if ((error = prepare_worktree_commit_message(&msg, message)) < 0)
+ if ((error = prepare_worktree_commit_message(&msg, opts->message)) < 0)
goto cleanup;
- if ((error = commit_worktree(out, repo, stasher, git_str_cstr(&msg),
- i_commit, b_commit, u_commit)) < 0)
- goto cleanup;
+ if (!has_paths) {
+ if ((error = commit_worktree(out, repo, opts->stasher, git_str_cstr(&msg),
+ i_commit, b_commit, u_commit)) < 0)
+ goto cleanup;
+ } else {
+ if ((error = git_index__new(&paths_index, repo->oid_type)) < 0 ||
+ (error = retrieve_head(&head, repo)) < 0 ||
+ (error = git_reference_peel((git_object**)&tree, head, GIT_OBJECT_TREE)) < 0 ||
+ (error = git_index_read_tree(paths_index, tree)) < 0 ||
+ (error = stash_update_index_from_paths(repo, paths_index, &opts->paths)) < 0 ||
+ (error = build_stash_commit_from_index(out, repo, opts->stasher, git_str_cstr(&msg),
+ i_commit, b_commit, u_commit, paths_index)) < 0)
+ goto cleanup;
+ }
git_str_rtrim(&msg);
if ((error = update_reflog(out, repo, git_str_cstr(&msg))) < 0)
goto cleanup;
- if ((error = reset_index_and_workdir(repo, (flags & GIT_STASH_KEEP_INDEX) ? i_commit : b_commit,
- flags)) < 0)
+ if (!(opts->flags & GIT_STASH_KEEP_ALL) &&
+ (error = reset_index_and_workdir(repo,
+ (opts->flags & GIT_STASH_KEEP_INDEX) ? i_commit : b_commit,opts->flags)) < 0)
goto cleanup;
cleanup:
-
git_str_dispose(&msg);
git_commit_free(i_commit);
git_commit_free(b_commit);
git_commit_free(u_commit);
+ git_tree_free(tree);
+ git_reference_free(head);
git_index_free(index);
+ git_index_free(paths_index);
return error;
}
@@ -777,6 +945,13 @@ int git_stash_apply_options_init(git_stash_apply_options *opts, unsigned int ver
return 0;
}
+int git_stash_save_options_init(git_stash_save_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_stash_save_options, GIT_STASH_SAVE_OPTIONS_INIT);
+ return 0;
+}
+
#ifndef GIT_DEPRECATE_HARD
int git_stash_apply_init_options(git_stash_apply_options *opts, unsigned int version)
{
@@ -828,6 +1003,7 @@ static int stage_new_file(const git_index_entry **entries, void *data)
static int stage_new_files(
git_index **out,
+ git_repository *repo,
git_tree *parent_tree,
git_tree *tree)
{
@@ -836,7 +1012,7 @@ static int stage_new_files(
git_index *index = NULL;
int error;
- if ((error = git_index_new(&index)) < 0 ||
+ if ((error = git_index__new(&index, repo->oid_type)) < 0 ||
(error = git_iterator_for_tree(
&iterators[0], parent_tree, &iterator_options)) < 0 ||
(error = git_iterator_for_tree(
@@ -920,10 +1096,10 @@ int git_stash_apply(
* previously unstaged contents are staged, not the previously staged.)
*/
} else if ((opts.flags & GIT_STASH_APPLY_REINSTATE_INDEX) == 0) {
- if ((error = stage_new_files(
- &stash_adds, stash_parent_tree, stash_tree)) < 0 ||
- (error = merge_indexes(
- &unstashed_index, repo, stash_parent_tree, repo_index, stash_adds)) < 0)
+ if ((error = stage_new_files(&stash_adds, repo,
+ stash_parent_tree, stash_tree)) < 0 ||
+ (error = merge_indexes(&unstashed_index, repo,
+ stash_parent_tree, repo_index, stash_adds)) < 0)
goto cleanup;
}
diff --git a/src/libgit2/strarray.c b/src/libgit2/strarray.c
index 2f9b77cc280..25e75f02ad9 100644
--- a/src/libgit2/strarray.c
+++ b/src/libgit2/strarray.c
@@ -8,6 +8,7 @@
#include "util.h"
#include "common.h"
+#include "strarray.h"
int git_strarray_copy(git_strarray *tgt, const git_strarray *src)
{
diff --git a/src/libgit2/strarray.h b/src/libgit2/strarray.h
new file mode 100644
index 00000000000..1984805357a
--- /dev/null
+++ b/src/libgit2/strarray.h
@@ -0,0 +1,25 @@
+/*
+ * 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_strarray_h__
+#define INCLUDE_strarray_h__
+
+#include "common.h"
+#include "git2/strarray.h"
+
+/**
+ * Copy a string array object from source to target.
+ *
+ * Note: target is overwritten and hence should be empty, otherwise its
+ * contents are leaked. Call git_strarray_free() if necessary.
+ *
+ * @param tgt target
+ * @param src source
+ * @return 0 on success, < 0 on allocation failure
+ */
+extern int git_strarray_copy(git_strarray *tgt, const git_strarray *src);
+
+#endif
diff --git a/src/libgit2/streams/mbedtls.c b/src/libgit2/streams/mbedtls.c
index 0cf5c8af1fb..1b2780706c6 100644
--- a/src/libgit2/streams/mbedtls.c
+++ b/src/libgit2/streams/mbedtls.c
@@ -14,7 +14,6 @@
#include "runtime.h"
#include "stream.h"
#include "streams/socket.h"
-#include "netops.h"
#include "git2/transport.h"
#include "util.h"
@@ -33,7 +32,6 @@
# endif
#endif
-#include
#include
#include
#include
@@ -44,9 +42,15 @@
#define GIT_SSL_DEFAULT_CIPHERS "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-DSS-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-DSS-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-DSS-WITH-AES-128-CBC-SHA256:TLS-DHE-DSS-WITH-AES-256-CBC-SHA256:TLS-DHE-DSS-WITH-AES-128-CBC-SHA:TLS-DHE-DSS-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-AES-128-GCM-SHA256:TLS-RSA-WITH-AES-256-GCM-SHA384:TLS-RSA-WITH-AES-128-CBC-SHA256:TLS-RSA-WITH-AES-256-CBC-SHA256:TLS-RSA-WITH-AES-128-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA"
#define GIT_SSL_DEFAULT_CIPHERS_COUNT 30
-static mbedtls_ssl_config *git__ssl_conf;
static int ciphers_list[GIT_SSL_DEFAULT_CIPHERS_COUNT];
-static mbedtls_entropy_context *mbedtls_entropy;
+
+static bool initialized = false;
+static mbedtls_ssl_config mbedtls_config;
+static mbedtls_ctr_drbg_context mbedtls_rng;
+static mbedtls_entropy_context mbedtls_entropy;
+
+static bool has_ca_chain = false;
+static mbedtls_x509_crt mbedtls_ca_chain;
/**
* This function aims to clean-up the SSL context which
@@ -54,19 +58,16 @@ static mbedtls_entropy_context *mbedtls_entropy;
*/
static void shutdown_ssl(void)
{
- if (git__ssl_conf) {
- mbedtls_x509_crt_free(git__ssl_conf->ca_chain);
- git__free(git__ssl_conf->ca_chain);
- mbedtls_ctr_drbg_free(git__ssl_conf->p_rng);
- git__free(git__ssl_conf->p_rng);
- mbedtls_ssl_config_free(git__ssl_conf);
- git__free(git__ssl_conf);
- git__ssl_conf = NULL;
+ if (has_ca_chain) {
+ mbedtls_x509_crt_free(&mbedtls_ca_chain);
+ has_ca_chain = false;
}
- if (mbedtls_entropy) {
- mbedtls_entropy_free(mbedtls_entropy);
- git__free(mbedtls_entropy);
- mbedtls_entropy = NULL;
+
+ if (initialized) {
+ mbedtls_ctr_drbg_free(&mbedtls_rng);
+ mbedtls_ssl_config_free(&mbedtls_config);
+ mbedtls_entropy_free(&mbedtls_entropy);
+ initialized = false;
}
}
@@ -75,32 +76,33 @@ int git_mbedtls_stream_global_init(void)
int loaded = 0;
char *crtpath = GIT_DEFAULT_CERT_LOCATION;
struct stat statbuf;
- mbedtls_ctr_drbg_context *ctr_drbg = NULL;
size_t ciphers_known = 0;
char *cipher_name = NULL;
char *cipher_string = NULL;
char *cipher_string_tmp = NULL;
- git__ssl_conf = git__malloc(sizeof(mbedtls_ssl_config));
- GIT_ERROR_CHECK_ALLOC(git__ssl_conf);
+ mbedtls_ssl_config_init(&mbedtls_config);
+ mbedtls_entropy_init(&mbedtls_entropy);
+ mbedtls_ctr_drbg_init(&mbedtls_rng);
- mbedtls_ssl_config_init(git__ssl_conf);
- if (mbedtls_ssl_config_defaults(git__ssl_conf,
- MBEDTLS_SSL_IS_CLIENT,
- MBEDTLS_SSL_TRANSPORT_STREAM,
- MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
+ if (mbedtls_ssl_config_defaults(&mbedtls_config,
+ MBEDTLS_SSL_IS_CLIENT,
+ MBEDTLS_SSL_TRANSPORT_STREAM,
+ MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
git_error_set(GIT_ERROR_SSL, "failed to initialize mbedTLS");
goto cleanup;
}
- /* configure TLSv1 */
- mbedtls_ssl_conf_min_version(git__ssl_conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0);
+ /* configure TLSv1.1 */
+#ifdef MBEDTLS_SSL_MINOR_VERSION_2
+ mbedtls_ssl_conf_min_version(&mbedtls_config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_2);
+#endif
/* verify_server_cert is responsible for making the check.
* OPTIONAL because REQUIRED drops the certificate as soon as the check
* is made, so we can never see the certificate and override it. */
- mbedtls_ssl_conf_authmode(git__ssl_conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
+ mbedtls_ssl_conf_authmode(&mbedtls_config, MBEDTLS_SSL_VERIFY_OPTIONAL);
/* set the list of allowed ciphersuites */
ciphers_known = 0;
@@ -124,42 +126,33 @@ int git_mbedtls_stream_global_init(void)
git_error_set(GIT_ERROR_SSL, "no cipher could be enabled");
goto cleanup;
}
- mbedtls_ssl_conf_ciphersuites(git__ssl_conf, ciphers_list);
+ mbedtls_ssl_conf_ciphersuites(&mbedtls_config, ciphers_list);
/* Seeding the random number generator */
- mbedtls_entropy = git__malloc(sizeof(mbedtls_entropy_context));
- GIT_ERROR_CHECK_ALLOC(mbedtls_entropy);
-
- mbedtls_entropy_init(mbedtls_entropy);
-
- ctr_drbg = git__malloc(sizeof(mbedtls_ctr_drbg_context));
- GIT_ERROR_CHECK_ALLOC(ctr_drbg);
- mbedtls_ctr_drbg_init(ctr_drbg);
-
- if (mbedtls_ctr_drbg_seed(ctr_drbg,
- mbedtls_entropy_func,
- mbedtls_entropy, NULL, 0) != 0) {
+ if (mbedtls_ctr_drbg_seed(&mbedtls_rng, mbedtls_entropy_func,
+ &mbedtls_entropy, NULL, 0) != 0) {
git_error_set(GIT_ERROR_SSL, "failed to initialize mbedTLS entropy pool");
goto cleanup;
}
- mbedtls_ssl_conf_rng(git__ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg);
+ mbedtls_ssl_conf_rng(&mbedtls_config, mbedtls_ctr_drbg_random, &mbedtls_rng);
/* load default certificates */
if (crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
loaded = (git_mbedtls__set_cert_location(crtpath, NULL) == 0);
+
if (!loaded && crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
loaded = (git_mbedtls__set_cert_location(NULL, crtpath) == 0);
+ initialized = true;
+
return git_runtime_shutdown_register(shutdown_ssl);
cleanup:
- mbedtls_ctr_drbg_free(ctr_drbg);
- git__free(ctr_drbg);
- mbedtls_ssl_config_free(git__ssl_conf);
- git__free(git__ssl_conf);
- git__ssl_conf = NULL;
+ mbedtls_ctr_drbg_free(&mbedtls_rng);
+ mbedtls_ssl_config_free(&mbedtls_config);
+ mbedtls_entropy_free(&mbedtls_entropy);
return -1;
}
@@ -193,7 +186,7 @@ static int ssl_set_error(mbedtls_ssl_context *ssl, int error)
break;
case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:
- git_error_set(GIT_ERROR_SSL, "SSL error: %#04x [%x] - %s", error, ssl->session_negotiate->verify_result, errbuf);
+ git_error_set(GIT_ERROR_SSL, "SSL error: %#04x [%x] - %s", error, mbedtls_ssl_get_verify_result(ssl), errbuf);
ret = GIT_ECERTIFICATE;
break;
@@ -375,7 +368,7 @@ static int mbedtls_stream_wrap(
st->ssl = git__malloc(sizeof(mbedtls_ssl_context));
GIT_ERROR_CHECK_ALLOC(st->ssl);
mbedtls_ssl_init(st->ssl);
- if (mbedtls_ssl_setup(st->ssl, git__ssl_conf)) {
+ if (mbedtls_ssl_setup(st->ssl, &mbedtls_config)) {
git_error_set(GIT_ERROR_SSL, "failed to create ssl object");
error = -1;
goto out_err;
@@ -442,30 +435,30 @@ int git_mbedtls__set_cert_location(const char *file, const char *path)
{
int ret = 0;
char errbuf[512];
- mbedtls_x509_crt *cacert;
GIT_ASSERT_ARG(file || path);
- cacert = git__malloc(sizeof(mbedtls_x509_crt));
- GIT_ERROR_CHECK_ALLOC(cacert);
+ if (has_ca_chain)
+ mbedtls_x509_crt_free(&mbedtls_ca_chain);
+
+ mbedtls_x509_crt_init(&mbedtls_ca_chain);
- mbedtls_x509_crt_init(cacert);
if (file)
- ret = mbedtls_x509_crt_parse_file(cacert, file);
+ ret = mbedtls_x509_crt_parse_file(&mbedtls_ca_chain, file);
+
if (ret >= 0 && path)
- ret = mbedtls_x509_crt_parse_path(cacert, path);
+ ret = mbedtls_x509_crt_parse_path(&mbedtls_ca_chain, path);
+
/* mbedtls_x509_crt_parse_path returns the number of invalid certs on success */
if (ret < 0) {
- mbedtls_x509_crt_free(cacert);
- git__free(cacert);
+ mbedtls_x509_crt_free(&mbedtls_ca_chain);
mbedtls_strerror( ret, errbuf, 512 );
git_error_set(GIT_ERROR_SSL, "failed to load CA certificates: %#04x - %s", ret, errbuf);
return -1;
}
- mbedtls_x509_crt_free(git__ssl_conf->ca_chain);
- git__free(git__ssl_conf->ca_chain);
- mbedtls_ssl_conf_ca_chain(git__ssl_conf, cacert, NULL);
+ mbedtls_ssl_conf_ca_chain(&mbedtls_config, &mbedtls_ca_chain, NULL);
+ has_ca_chain = true;
return 0;
}
diff --git a/src/libgit2/streams/openssl.c b/src/libgit2/streams/openssl.c
index 89c96780c14..7cb8f7f927c 100644
--- a/src/libgit2/streams/openssl.c
+++ b/src/libgit2/streams/openssl.c
@@ -18,8 +18,8 @@
#include "settings.h"
#include "posix.h"
#include "stream.h"
+#include "net.h"
#include "streams/socket.h"
-#include "netops.h"
#include "git2/transport.h"
#include "git2/sys/openssl.h"
@@ -36,6 +36,8 @@
# include
#endif
+extern char *git__ssl_ciphers;
+
SSL_CTX *git__ssl_ctx;
#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
@@ -70,14 +72,14 @@ static void *git_openssl_malloc(size_t bytes, const char *file, int line)
GIT_UNUSED(line);
return git__calloc(1, bytes);
}
-
+
static void *git_openssl_realloc(void *mem, size_t size, const char *file, int line)
{
GIT_UNUSED(file);
GIT_UNUSED(line);
return git__realloc(mem, size);
}
-
+
static void git_openssl_free(void *mem, const char *file, int line)
{
GIT_UNUSED(file);
@@ -105,7 +107,7 @@ static void git_openssl_free(void *mem)
static int openssl_init(void)
{
long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
- const char *ciphers = git_libgit2__ssl_ciphers();
+ const char *ciphers = git__ssl_ciphers;
#ifdef VALGRIND
static bool allocators_initialized = false;
#endif
@@ -198,7 +200,7 @@ static int openssl_ensure_initialized(void)
if ((error = git_openssl_stream_dynamic_init()) == 0)
error = openssl_init();
- openssl_initialized = true;
+ openssl_initialized = !error;
}
error |= git_mutex_unlock(&openssl_mutex);
@@ -357,15 +359,10 @@ static int ssl_teardown(SSL *ssl)
return ret;
}
-static int check_host_name(const char *name, const char *host)
+static bool check_host_name(const char *host, const char *name)
{
- if (!strcasecmp(name, host))
- return 0;
-
- if (gitno__match_host(name, host) < 0)
- return -1;
-
- return 0;
+ return !strcasecmp(host, name) ||
+ git_net_hostname_matches_cert(host, name);
}
static int verify_server_cert(SSL *ssl, const char *host)
@@ -425,10 +422,7 @@ static int verify_server_cert(SSL *ssl, const char *host)
if (memchr(name, '\0', namelen))
continue;
- if (check_host_name(name, host) < 0)
- matched = 0;
- else
- matched = 1;
+ matched = !!check_host_name(host, name);
} else if (type == GEN_IPADD) {
/* Here name isn't so much a name but a binary representation of the IP */
matched = addr && !!memcmp(name, addr, namelen);
@@ -481,7 +475,7 @@ static int verify_server_cert(SSL *ssl, const char *host)
goto cert_fail_name;
}
- if (check_host_name((char *)peer_cn, host) < 0)
+ if (!check_host_name(host, (char *)peer_cn))
goto cert_fail_name;
goto cleanup;
diff --git a/src/libgit2/streams/openssl_dynamic.c b/src/libgit2/streams/openssl_dynamic.c
index da16b6ed70c..222c1099d6b 100644
--- a/src/libgit2/streams/openssl_dynamic.c
+++ b/src/libgit2/streams/openssl_dynamic.c
@@ -91,7 +91,7 @@ int (*sk_num)(const void *sk);
void *(*sk_value)(const void *sk, int i);
void (*sk_free)(void *sk);
-void *openssl_handle;
+static void *openssl_handle;
GIT_INLINE(void *) openssl_sym(int *err, const char *name, bool required)
{
@@ -125,7 +125,8 @@ int git_openssl_stream_dynamic_init(void)
(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) {
+ (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.3", RTLD_NOW)) == NULL) {
git_error_set(GIT_ERROR_SSL, "could not load ssl libraries");
return -1;
}
@@ -175,7 +176,6 @@ int git_openssl_stream_dynamic_init(void)
SSL_connect = (int (*)(SSL *))openssl_sym(&err, "SSL_connect", true);
SSL_ctrl = (long (*)(SSL *, int, long, void *))openssl_sym(&err, "SSL_ctrl", true);
- SSL_get_peer_certificate = (X509 *(*)(const SSL *))openssl_sym(&err, "SSL_get_peer_certificate", true);
SSL_library_init = (int (*)(void))openssl_sym(&err, "SSL_library_init", false);
SSL_free = (void (*)(SSL *))openssl_sym(&err, "SSL_free", true);
SSL_get_error = (int (*)(SSL *, int))openssl_sym(&err, "SSL_get_error", true);
@@ -187,6 +187,10 @@ int git_openssl_stream_dynamic_init(void)
SSL_shutdown = (int (*)(SSL *ssl))openssl_sym(&err, "SSL_shutdown", true);
SSL_write = (int (*)(SSL *, const void *, int))openssl_sym(&err, "SSL_write", true);
+ if (!(SSL_get_peer_certificate = (X509 *(*)(const SSL *))openssl_sym(&err, "SSL_get_peer_certificate", false))) {
+ SSL_get_peer_certificate = (X509 *(*)(const SSL *))openssl_sym(&err, "SSL_get1_peer_certificate", true);
+ }
+
SSL_CTX_ctrl = (long (*)(SSL_CTX *, int, long, void *))openssl_sym(&err, "SSL_CTX_ctrl", true);
SSL_CTX_free = (void (*)(SSL_CTX *))openssl_sym(&err, "SSL_CTX_free", true);
SSL_CTX_new = (SSL_CTX *(*)(const SSL_METHOD *))openssl_sym(&err, "SSL_CTX_new", true);
diff --git a/src/libgit2/streams/schannel.c b/src/libgit2/streams/schannel.c
new file mode 100644
index 00000000000..f096158193c
--- /dev/null
+++ b/src/libgit2/streams/schannel.c
@@ -0,0 +1,715 @@
+/*
+ * 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 "streams/schannel.h"
+
+#ifdef GIT_SCHANNEL
+
+#define SECURITY_WIN32
+
+#include
+#include
+#include
+
+#include "stream.h"
+#include "streams/socket.h"
+
+#ifndef SP_PROT_TLS1_2_CLIENT
+# define SP_PROT_TLS1_2_CLIENT 2048
+#endif
+
+#ifndef SP_PROT_TLS1_3_CLIENT
+# define SP_PROT_TLS1_3_CLIENT 8192
+#endif
+
+#ifndef SECBUFFER_ALERT
+# define SECBUFFER_ALERT 17
+#endif
+
+#define READ_BLOCKSIZE (16 * 1024)
+
+typedef enum {
+ STATE_NONE = 0,
+ STATE_CRED = 1,
+ STATE_CONTEXT = 2,
+ STATE_CERTIFICATE = 3
+} schannel_state;
+
+typedef struct {
+ git_stream parent;
+ git_stream *io;
+ int owned;
+ bool connected;
+ wchar_t *host_w;
+
+ schannel_state state;
+
+ CredHandle cred;
+ CtxtHandle context;
+ SecPkgContext_StreamSizes stream_sizes;
+
+ CERT_CONTEXT *certificate;
+ const CERT_CHAIN_CONTEXT *cert_chain;
+ git_cert_x509 x509;
+
+ git_str plaintext_in;
+ git_str ciphertext_in;
+} schannel_stream;
+
+static int connect_context(schannel_stream *st)
+{
+ SCHANNEL_CRED cred = { 0 };
+ SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
+ DWORD context_flags;
+ static size_t MAX_RETRIES = 1024;
+ size_t retries;
+ ssize_t read_len;
+ int error = 0;
+
+ if (st->owned && (error = git_stream_connect(st->io)) < 0)
+ return error;
+
+ cred.dwVersion = SCHANNEL_CRED_VERSION;
+ cred.dwFlags = SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ SCH_CRED_IGNORE_REVOCATION_OFFLINE |
+ SCH_CRED_MANUAL_CRED_VALIDATION |
+ SCH_CRED_NO_DEFAULT_CREDS |
+ SCH_CRED_NO_SERVERNAME_CHECK;
+ cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT |
+ SP_PROT_TLS1_3_CLIENT;
+
+ if (AcquireCredentialsHandleW(NULL, SCHANNEL_NAME_W,
+ SECPKG_CRED_OUTBOUND, NULL, &cred, NULL,
+ NULL, &st->cred, NULL) != SEC_E_OK) {
+ git_error_set(GIT_ERROR_OS, "could not acquire credentials handle");
+ return -1;
+ }
+
+ st->state = STATE_CRED;
+
+ context_flags = ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_STREAM;
+
+ for (retries = 0; retries < MAX_RETRIES; retries++) {
+ SecBuffer input_buf[] = {
+ { (unsigned long)st->ciphertext_in.size,
+ SECBUFFER_TOKEN,
+ st->ciphertext_in.size ? st->ciphertext_in.ptr : NULL },
+ { 0, SECBUFFER_EMPTY, NULL }
+ };
+ SecBuffer output_buf[] = { { 0, SECBUFFER_TOKEN, NULL },
+ { 0, SECBUFFER_ALERT, NULL } };
+
+ SecBufferDesc input_buf_desc = { SECBUFFER_VERSION, 2, input_buf };
+ SecBufferDesc output_buf_desc = { SECBUFFER_VERSION, 2, output_buf };
+
+ status = InitializeSecurityContextW(&st->cred,
+ retries ? &st->context : NULL, st->host_w,
+ context_flags, 0, 0, retries ? &input_buf_desc : NULL, 0,
+ retries ? NULL : &st->context, &output_buf_desc,
+ &context_flags, NULL);
+
+ if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED) {
+ st->state = STATE_CONTEXT;
+
+ if (output_buf[0].cbBuffer > 0) {
+ error = git_stream__write_full(st->io,
+ output_buf[0].pvBuffer,
+ output_buf[0].cbBuffer, 0);
+
+ FreeContextBuffer(output_buf[0].pvBuffer);
+ }
+
+ /* handle any leftover, unprocessed data */
+ if (input_buf[1].BufferType == SECBUFFER_EXTRA) {
+ GIT_ASSERT(st->ciphertext_in.size > input_buf[1].cbBuffer);
+
+ git_str_consume_bytes(&st->ciphertext_in,
+ st->ciphertext_in.size - input_buf[1].cbBuffer);
+ } else {
+ git_str_clear(&st->ciphertext_in);
+ }
+
+ if (error < 0 || status == SEC_E_OK)
+ break;
+ } else if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ /* we need additional data from the client; */
+ if (git_str_grow_by(&st->ciphertext_in, READ_BLOCKSIZE) < 0) {
+ error = -1;
+ break;
+ }
+
+ if ((read_len = git_stream_read(st->io,
+ st->ciphertext_in.ptr + st->ciphertext_in.size,
+ (st->ciphertext_in.asize - st->ciphertext_in.size))) < 0) {
+ error = -1;
+ break;
+ }
+
+ GIT_ASSERT((size_t)read_len <=
+ st->ciphertext_in.asize - st->ciphertext_in.size);
+ st->ciphertext_in.size += read_len;
+ } else {
+ git_error_set(GIT_ERROR_OS,
+ "could not initialize security context");
+ error = -1;
+ break;
+ }
+
+ GIT_ASSERT(st->ciphertext_in.size < ULONG_MAX);
+ }
+
+ if (retries == MAX_RETRIES) {
+ git_error_set(GIT_ERROR_SSL,
+ "could not initialize security context: too many retries");
+ error = -1;
+ }
+
+ if (!error) {
+ if (QueryContextAttributesW(&st->context,
+ SECPKG_ATTR_STREAM_SIZES,
+ &st->stream_sizes) != SEC_E_OK) {
+ git_error_set(GIT_ERROR_SSL,
+ "could not query stream sizes");
+ error = -1;
+ }
+ }
+
+ return error;
+}
+
+static int set_certificate_lookup_error(DWORD status)
+{
+ switch (status) {
+ case CERT_TRUST_IS_NOT_TIME_VALID:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate is expired or not yet valid");
+ break;
+ case CERT_TRUST_IS_REVOKED:
+ git_error_set(GIT_ERROR_SSL, "certificate is revoked");
+ break;
+ case CERT_TRUST_IS_NOT_SIGNATURE_VALID:
+ case CERT_TRUST_IS_NOT_VALID_FOR_USAGE:
+ case CERT_TRUST_INVALID_EXTENSION:
+ case CERT_TRUST_INVALID_POLICY_CONSTRAINTS:
+ case CERT_TRUST_INVALID_BASIC_CONSTRAINTS:
+ case CERT_TRUST_INVALID_NAME_CONSTRAINTS:
+ case CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT:
+ case CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT:
+ case CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT:
+ case CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT:
+ case CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY:
+ case CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT:
+ git_error_set(GIT_ERROR_SSL, "certificate is not valid");
+ break;
+ case CERT_TRUST_IS_UNTRUSTED_ROOT:
+ case CERT_TRUST_IS_CYCLIC:
+ case CERT_TRUST_IS_EXPLICIT_DISTRUST:
+ git_error_set(GIT_ERROR_SSL, "certificate is not trusted");
+ break;
+ case CERT_TRUST_REVOCATION_STATUS_UNKNOWN:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate revocation status could not be verified");
+ break;
+ case CERT_TRUST_IS_OFFLINE_REVOCATION:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate revocation is offline or stale");
+ break;
+ case CERT_TRUST_HAS_WEAK_SIGNATURE:
+ git_error_set(GIT_ERROR_SSL, "certificate has a weak signature");
+ break;
+ default:
+ git_error_set(GIT_ERROR_SSL,
+ "unknown certificate lookup failure: %d", status);
+ return -1;
+ }
+
+ return GIT_ECERTIFICATE;
+}
+
+static int set_certificate_validation_error(DWORD status)
+{
+ switch (status) {
+ case TRUST_E_CERT_SIGNATURE:
+ git_error_set(GIT_ERROR_SSL,
+ "the certificate cannot be verified");
+ break;
+ case CRYPT_E_REVOKED:
+ git_error_set(GIT_ERROR_SSL,
+ "the certificate or signature has been revoked");
+ break;
+ case CERT_E_UNTRUSTEDROOT:
+ git_error_set(GIT_ERROR_SSL,
+ "the certificate root is not trusted");
+ break;
+ case CERT_E_UNTRUSTEDTESTROOT:
+ git_error_set(GIT_ERROR_SSL,
+ "the certificate root is a test certificate");
+ break;
+ case CERT_E_CHAINING:
+ git_error_set(GIT_ERROR_SSL,
+ "the certificate chain is invalid");
+ break;
+ case CERT_E_WRONG_USAGE:
+ case CERT_E_PURPOSE:
+ git_error_set(GIT_ERROR_SSL,
+ "the certificate is not valid for this usage");
+ break;
+ case CERT_E_EXPIRED:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate is expired or not yet valid");
+ break;
+ case CERT_E_INVALID_NAME:
+ case CERT_E_CN_NO_MATCH:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate is not valid for this hostname");
+ break;
+ case CERT_E_INVALID_POLICY:
+ case TRUST_E_BASIC_CONSTRAINTS:
+ case CERT_E_CRITICAL:
+ case CERT_E_VALIDITYPERIODNESTING:
+ git_error_set(GIT_ERROR_SSL, "certificate is not valid");
+ break;
+ case CRYPT_E_NO_REVOCATION_CHECK:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate revocation status could not be verified");
+ break;
+ case CRYPT_E_REVOCATION_OFFLINE:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate revocation is offline or stale");
+ break;
+ case CERT_E_ROLE:
+ git_error_set(GIT_ERROR_SSL, "certificate authority is not valid");
+ break;
+ default:
+ git_error_set(GIT_ERROR_SSL,
+ "unknown certificate policy checking failure: %d",
+ status);
+ return -1;
+ }
+
+ return GIT_ECERTIFICATE;
+}
+
+static int check_certificate(schannel_stream* st)
+{
+ CERT_CHAIN_PARA cert_chain_parameters;
+ SSL_EXTRA_CERT_CHAIN_POLICY_PARA ssl_policy_parameters;
+ CERT_CHAIN_POLICY_PARA cert_policy_parameters =
+ { sizeof(CERT_CHAIN_POLICY_PARA), 0, &ssl_policy_parameters };
+ CERT_CHAIN_POLICY_STATUS cert_policy_status;
+
+ memset(&cert_chain_parameters, 0, sizeof(CERT_CHAIN_PARA));
+ cert_chain_parameters.cbSize = sizeof(CERT_CHAIN_PARA);
+
+ if (QueryContextAttributesW(&st->context,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &st->certificate) != SEC_E_OK) {
+ git_error_set(GIT_ERROR_OS,
+ "could not query remote certificate context");
+ return -1;
+ }
+
+ /* TODO: do we really want to do revokcation checking ? */
+ if (!CertGetCertificateChain(NULL, st->certificate, NULL,
+ st->certificate->hCertStore, &cert_chain_parameters,
+ CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
+ NULL, &st->cert_chain)) {
+ git_error_set(GIT_ERROR_OS, "could not query remote certificate chain");
+ CertFreeCertificateContext(st->certificate);
+ return -1;
+ }
+
+ st->state = STATE_CERTIFICATE;
+
+ /* Set up the x509 certificate data for future callbacks */
+
+ st->x509.parent.cert_type = GIT_CERT_X509;
+ st->x509.data = st->certificate->pbCertEncoded;
+ st->x509.len = st->certificate->cbCertEncoded;
+
+ /* Handle initial certificate validation */
+
+ if (st->cert_chain->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR)
+ return set_certificate_lookup_error(st->cert_chain->TrustStatus.dwErrorStatus);
+
+ ssl_policy_parameters.cbSize = sizeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
+ ssl_policy_parameters.dwAuthType = AUTHTYPE_SERVER;
+ ssl_policy_parameters.pwszServerName = st->host_w;
+
+ if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL,
+ st->cert_chain, &cert_policy_parameters,
+ &cert_policy_status)) {
+ git_error_set(GIT_ERROR_OS, "could not verify certificate chain policy");
+ return -1;
+ }
+
+ if (cert_policy_status.dwError != SEC_E_OK)
+ return set_certificate_validation_error(cert_policy_status.dwError);
+
+ return 0;
+}
+
+static int schannel_connect(git_stream *stream)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+ int error;
+
+ GIT_ASSERT(st->state == STATE_NONE);
+
+ if ((error = connect_context(st)) < 0 ||
+ (error = check_certificate(st)) < 0)
+ return error;
+
+ st->connected = 1;
+ return 0;
+}
+
+static int schannel_certificate(git_cert **out, git_stream *stream)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+
+ *out = &st->x509.parent;
+ return 0;
+}
+
+static int schannel_set_proxy(
+ git_stream *stream,
+ const git_proxy_options *proxy_options)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+ return git_stream_set_proxy(st->io, proxy_options);
+}
+
+static ssize_t schannel_write(
+ git_stream *stream,
+ const char *data,
+ size_t data_len,
+ int flags)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+ SecBuffer encrypt_buf[3];
+ SecBufferDesc encrypt_buf_desc = { SECBUFFER_VERSION, 3, encrypt_buf };
+ git_str ciphertext_out = GIT_STR_INIT;
+ ssize_t total_len = 0;
+
+ GIT_UNUSED(flags);
+
+ if (data_len > SSIZE_MAX)
+ data_len = SSIZE_MAX;
+
+ git_str_init(&ciphertext_out,
+ st->stream_sizes.cbHeader +
+ st->stream_sizes.cbMaximumMessage +
+ st->stream_sizes.cbTrailer);
+
+ while (data_len > 0) {
+ size_t message_len = min(data_len, st->stream_sizes.cbMaximumMessage);
+ size_t ciphertext_len, ciphertext_written = 0;
+
+ encrypt_buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ encrypt_buf[0].cbBuffer = st->stream_sizes.cbHeader;
+ encrypt_buf[0].pvBuffer = ciphertext_out.ptr;
+
+ encrypt_buf[1].BufferType = SECBUFFER_DATA;
+ encrypt_buf[1].cbBuffer = (unsigned long)message_len;
+ encrypt_buf[1].pvBuffer =
+ ciphertext_out.ptr + st->stream_sizes.cbHeader;
+
+ encrypt_buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ encrypt_buf[2].cbBuffer = st->stream_sizes.cbTrailer;
+ encrypt_buf[2].pvBuffer =
+ ciphertext_out.ptr + st->stream_sizes.cbHeader +
+ message_len;
+
+ memcpy(ciphertext_out.ptr + st->stream_sizes.cbHeader, data, message_len);
+
+ if (EncryptMessage(&st->context, 0, &encrypt_buf_desc, 0) != SEC_E_OK) {
+ git_error_set(GIT_ERROR_OS, "could not encrypt tls message");
+ total_len = -1;
+ goto done;
+ }
+
+ ciphertext_len = encrypt_buf[0].cbBuffer +
+ encrypt_buf[1].cbBuffer +
+ encrypt_buf[2].cbBuffer;
+
+ while (ciphertext_written < ciphertext_len) {
+ ssize_t chunk_len = git_stream_write(st->io,
+ ciphertext_out.ptr + ciphertext_written,
+ ciphertext_len - ciphertext_written, 0);
+
+ if (chunk_len < 0) {
+ total_len = -1;
+ goto done;
+ }
+
+ ciphertext_len -= chunk_len;
+ ciphertext_written += chunk_len;
+ }
+
+ total_len += message_len;
+
+ data += message_len;
+ data_len -= message_len;
+ }
+
+done:
+ git_str_dispose(&ciphertext_out);
+ return total_len;
+}
+
+static ssize_t schannel_read(git_stream *stream, void *_data, size_t data_len)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+ char *data = (char *)_data;
+ SecBuffer decrypt_buf[4];
+ SecBufferDesc decrypt_buf_desc = { SECBUFFER_VERSION, 4, decrypt_buf };
+ SECURITY_STATUS status;
+ ssize_t chunk_len, total_len = 0;
+
+ if (data_len > SSIZE_MAX)
+ data_len = SSIZE_MAX;
+
+ /*
+ * Loop until we have some bytes to return - we may have decrypted
+ * bytes queued or ciphertext from the wire that we can decrypt and
+ * return. Return any queued bytes if they're available to avoid a
+ * network read, which may block. We may return less than the
+ * caller requested, and they can retry for an actual network
+ */
+ while ((size_t)total_len < data_len) {
+ if (st->plaintext_in.size > 0) {
+ size_t copy_len = min(st->plaintext_in.size, data_len);
+
+ memcpy(data, st->plaintext_in.ptr, copy_len);
+ git_str_consume_bytes(&st->plaintext_in, copy_len);
+
+ data += copy_len;
+ data_len -= copy_len;
+
+ total_len += copy_len;
+
+ continue;
+ }
+
+ if (st->ciphertext_in.size > 0) {
+ decrypt_buf[0].BufferType = SECBUFFER_DATA;
+ decrypt_buf[0].cbBuffer = (unsigned long)min(st->ciphertext_in.size, ULONG_MAX);
+ decrypt_buf[0].pvBuffer = st->ciphertext_in.ptr;
+
+ decrypt_buf[1].BufferType = SECBUFFER_EMPTY;
+ decrypt_buf[1].cbBuffer = 0;
+ decrypt_buf[1].pvBuffer = NULL;
+
+ decrypt_buf[2].BufferType = SECBUFFER_EMPTY;
+ decrypt_buf[2].cbBuffer = 0;
+ decrypt_buf[2].pvBuffer = NULL;
+
+ decrypt_buf[3].BufferType = SECBUFFER_EMPTY;
+ decrypt_buf[3].cbBuffer = 0;
+ decrypt_buf[3].pvBuffer = NULL;
+
+ status = DecryptMessage(&st->context, &decrypt_buf_desc, 0, NULL);
+
+ if (status == SEC_E_OK) {
+ GIT_ASSERT(decrypt_buf[0].BufferType == SECBUFFER_STREAM_HEADER);
+ GIT_ASSERT(decrypt_buf[1].BufferType == SECBUFFER_DATA);
+ GIT_ASSERT(decrypt_buf[2].BufferType == SECBUFFER_STREAM_TRAILER);
+
+ if (git_str_put(&st->plaintext_in, decrypt_buf[1].pvBuffer, decrypt_buf[1].cbBuffer) < 0) {
+ total_len = -1;
+ goto done;
+ }
+
+ if (decrypt_buf[3].BufferType == SECBUFFER_EXTRA) {
+ git_str_consume_bytes(&st->ciphertext_in, (st->ciphertext_in.size - decrypt_buf[3].cbBuffer));
+ } else {
+ git_str_clear(&st->ciphertext_in);
+ }
+
+ continue;
+ } else if (status == SEC_E_CONTEXT_EXPIRED) {
+ break;
+ } else if (status != SEC_E_INCOMPLETE_MESSAGE) {
+ git_error_set(GIT_ERROR_SSL, "could not decrypt tls message");
+ total_len = -1;
+ goto done;
+ }
+ }
+
+ if (total_len != 0)
+ break;
+
+ if (git_str_grow_by(&st->ciphertext_in, READ_BLOCKSIZE) < 0) {
+ total_len = -1;
+ goto done;
+ }
+
+ if ((chunk_len = git_stream_read(st->io, st->ciphertext_in.ptr + st->ciphertext_in.size, st->ciphertext_in.asize - st->ciphertext_in.size)) < 0) {
+ total_len = -1;
+ goto done;
+ }
+
+ st->ciphertext_in.size += chunk_len;
+ }
+
+done:
+ return total_len;
+}
+
+static int schannel_close(git_stream *stream)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+ int error = 0;
+
+ if (st->connected) {
+ SecBuffer shutdown_buf;
+ SecBufferDesc shutdown_buf_desc =
+ { SECBUFFER_VERSION, 1, &shutdown_buf };
+ DWORD shutdown_message = SCHANNEL_SHUTDOWN, shutdown_flags;
+
+ shutdown_buf.BufferType = SECBUFFER_TOKEN;
+ shutdown_buf.cbBuffer = sizeof(DWORD);
+ shutdown_buf.pvBuffer = &shutdown_message;
+
+ if (ApplyControlToken(&st->context, &shutdown_buf_desc) != SEC_E_OK) {
+ git_error_set(GIT_ERROR_SSL, "could not shutdown stream");
+ error = -1;
+ }
+
+ shutdown_buf.BufferType = SECBUFFER_TOKEN;
+ shutdown_buf.cbBuffer = 0;
+ shutdown_buf.pvBuffer = NULL;
+
+ shutdown_flags = ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_STREAM;
+
+ if (InitializeSecurityContext(&st->cred, &st->context,
+ NULL, shutdown_flags, 0, 0,
+ &shutdown_buf_desc, 0, NULL,
+ &shutdown_buf_desc, &shutdown_flags,
+ NULL) == SEC_E_OK) {
+ if (shutdown_buf.cbBuffer > 0) {
+ if (git_stream__write_full(st->io,
+ shutdown_buf.pvBuffer,
+ shutdown_buf.cbBuffer, 0) < 0)
+ error = -1;
+
+ FreeContextBuffer(shutdown_buf.pvBuffer);
+ }
+ }
+ }
+
+ st->connected = false;
+
+ if (st->owned && git_stream_close(st->io) < 0)
+ error = -1;
+
+ return error;
+}
+
+static void schannel_free(git_stream *stream)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+
+ if (st->state >= STATE_CERTIFICATE) {
+ CertFreeCertificateContext(st->certificate);
+ CertFreeCertificateChain(st->cert_chain);
+ }
+
+ if (st->state >= STATE_CONTEXT)
+ DeleteSecurityContext(&st->context);
+
+ if (st->state >= STATE_CRED)
+ FreeCredentialsHandle(&st->cred);
+
+ st->state = STATE_NONE;
+
+ git_str_dispose(&st->ciphertext_in);
+ git_str_dispose(&st->plaintext_in);
+
+ git__free(st->host_w);
+
+ if (st->owned)
+ git_stream_free(st->io);
+
+ git__free(st);
+}
+
+static int schannel_stream_wrap(
+ git_stream **out,
+ git_stream *in,
+ const char *host,
+ int owned)
+{
+ schannel_stream *st;
+
+ st = git__calloc(1, sizeof(schannel_stream));
+ GIT_ERROR_CHECK_ALLOC(st);
+
+ st->io = in;
+ st->owned = owned;
+
+ if (git_utf8_to_16_alloc(&st->host_w, host) < 0) {
+ git__free(st);
+ return -1;
+ }
+
+ st->parent.version = GIT_STREAM_VERSION;
+ st->parent.encrypted = 1;
+ st->parent.proxy_support = git_stream_supports_proxy(st->io);
+ st->parent.connect = schannel_connect;
+ st->parent.certificate = schannel_certificate;
+ st->parent.set_proxy = schannel_set_proxy;
+ st->parent.read = schannel_read;
+ st->parent.write = schannel_write;
+ st->parent.close = schannel_close;
+ st->parent.free = schannel_free;
+
+ *out = (git_stream *)st;
+ return 0;
+}
+
+extern int git_schannel_stream_new(
+ git_stream **out,
+ const char *host,
+ const char *port)
+{
+ git_stream *stream;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+ GIT_ASSERT_ARG(port);
+
+ if ((error = git_socket_stream_new(&stream, host, port)) < 0)
+ return error;
+
+ if ((error = schannel_stream_wrap(out, stream, host, 1)) < 0) {
+ git_stream_close(stream);
+ git_stream_free(stream);
+ }
+
+ return error;
+}
+
+extern int git_schannel_stream_wrap(
+ git_stream **out,
+ git_stream *in,
+ const char *host)
+{
+ return schannel_stream_wrap(out, in, host, 0);
+}
+
+#endif
diff --git a/src/libgit2/streams/schannel.h b/src/libgit2/streams/schannel.h
new file mode 100644
index 00000000000..3584970d1f8
--- /dev/null
+++ b/src/libgit2/streams/schannel.h
@@ -0,0 +1,28 @@
+/*
+ * 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_steams_schannel_h__
+#define INCLUDE_steams_schannel_h__
+
+#include "common.h"
+
+#include "git2/sys/stream.h"
+
+#ifdef GIT_SCHANNEL
+
+extern int git_schannel_stream_new(
+ git_stream **out,
+ const char *host,
+ const char *port);
+
+extern int git_schannel_stream_wrap(
+ git_stream **out,
+ git_stream *in,
+ const char *host);
+
+#endif
+
+#endif
diff --git a/src/libgit2/streams/socket.c b/src/libgit2/streams/socket.c
index 9415fe89237..a463312fd85 100644
--- a/src/libgit2/streams/socket.c
+++ b/src/libgit2/streams/socket.c
@@ -8,26 +8,29 @@
#include "streams/socket.h"
#include "posix.h"
-#include "netops.h"
#include "registry.h"
+#include "runtime.h"
#include "stream.h"
#ifndef _WIN32
-# include
-# include
-# include
-# include
-# include
-# include
-# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
#else
-# include
-# include
-# ifdef _MSC_VER
-# pragma comment(lib, "ws2_32")
-# endif
+# include
+# include
+# ifdef _MSC_VER
+# pragma comment(lib, "ws2_32")
+# endif
#endif
+int git_socket_stream__connect_timeout = 0;
+int git_socket_stream__timeout = 0;
+
#ifdef GIT_WIN32
static void net_set_error(const char *str)
{
@@ -54,11 +57,8 @@ static int close_socket(GIT_SOCKET s)
return 0;
#ifdef GIT_WIN32
- if (SOCKET_ERROR == closesocket(s))
- return -1;
-
- if (0 != WSACleanup()) {
- git_error_set(GIT_ERROR_OS, "winsock cleanup failed");
+ if (closesocket(s) != 0) {
+ net_set_error("could not close socket");
return -1;
}
@@ -69,38 +69,119 @@ static int close_socket(GIT_SOCKET s)
}
-static int socket_connect(git_stream *stream)
+static int set_nonblocking(GIT_SOCKET s)
{
- struct addrinfo *info = NULL, *p;
- struct addrinfo hints;
- git_socket_stream *st = (git_socket_stream *) stream;
- GIT_SOCKET s = INVALID_SOCKET;
- int ret;
-
#ifdef GIT_WIN32
- /* on win32, the WSA context needs to be initialized
- * before any socket calls can be performed */
- WSADATA wsd;
+ unsigned long nonblocking = 1;
- if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
- git_error_set(GIT_ERROR_OS, "winsock init failed");
+ if (ioctlsocket(s, FIONBIO, &nonblocking) != 0) {
+ net_set_error("could not set socket non-blocking");
return -1;
}
+#else
+ int flags;
+
+ if ((flags = fcntl(s, F_GETFL, 0)) == -1) {
+ net_set_error("could not query socket flags");
+ return -1;
+ }
+
+ flags |= O_NONBLOCK;
- if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
- WSACleanup();
- git_error_set(GIT_ERROR_OS, "winsock init failed");
+ if (fcntl(s, F_SETFL, flags) != 0) {
+ net_set_error("could not set socket non-blocking");
return -1;
}
#endif
+ return 0;
+}
+
+/* Promote a sockerr to an errno for our error handling routines */
+static int handle_sockerr(GIT_SOCKET socket)
+{
+ int sockerr;
+ socklen_t errlen = sizeof(sockerr);
+
+ if (getsockopt(socket, SOL_SOCKET, SO_ERROR,
+ (void *)&sockerr, &errlen) < 0)
+ return -1;
+
+ if (sockerr == ETIMEDOUT)
+ return GIT_TIMEOUT;
+
+ errno = sockerr;
+ return -1;
+}
+
+GIT_INLINE(bool) connect_would_block(int error)
+{
+#ifdef GIT_WIN32
+ if (error == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
+ return true;
+#endif
+
+ if (error == -1 && errno == EINPROGRESS)
+ return true;
+
+ return false;
+}
+
+static int connect_with_timeout(
+ GIT_SOCKET socket,
+ const struct sockaddr *address,
+ socklen_t address_len,
+ int timeout)
+{
+ struct pollfd fd;
+ int error;
+
+ if (timeout && (error = set_nonblocking(socket)) < 0)
+ return error;
+
+ error = connect(socket, address, address_len);
+
+ if (error == 0 || !connect_would_block(error))
+ return error;
+
+ fd.fd = socket;
+ fd.events = POLLOUT;
+ fd.revents = 0;
+
+ error = p_poll(&fd, 1, timeout);
+
+ if (error == 0) {
+ return GIT_TIMEOUT;
+ } else if (error != 1) {
+ return -1;
+ } else if ((fd.revents & (POLLPRI | POLLHUP | POLLERR))) {
+ return handle_sockerr(socket);
+ } else if ((fd.revents & POLLOUT) != POLLOUT) {
+ git_error_set(GIT_ERROR_NET,
+ "unknown error while polling for connect: %d",
+ fd.revents);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int socket_connect(git_stream *stream)
+{
+ git_socket_stream *st = (git_socket_stream *) stream;
+ GIT_SOCKET s = INVALID_SOCKET;
+ struct addrinfo *info = NULL, *p;
+ struct addrinfo hints;
+ int error;
+
memset(&hints, 0x0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
- if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
+ if ((error = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
git_error_set(GIT_ERROR_NET,
- "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
+ "failed to resolve address for %s: %s",
+ st->host, p_gai_strerror(error));
return -1;
}
@@ -110,48 +191,115 @@ static int socket_connect(git_stream *stream)
if (s == INVALID_SOCKET)
continue;
- if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
+ error = connect_with_timeout(s, p->ai_addr,
+ (socklen_t)p->ai_addrlen,
+ st->parent.connect_timeout);
+
+ if (error == 0)
break;
/* If we can't connect, try the next one */
close_socket(s);
s = INVALID_SOCKET;
+
+ if (error == GIT_TIMEOUT)
+ break;
}
/* Oops, we couldn't connect to any address */
- if (s == INVALID_SOCKET && p == NULL) {
- git_error_set(GIT_ERROR_OS, "failed to connect to %s", st->host);
- p_freeaddrinfo(info);
- return -1;
+ if (s == INVALID_SOCKET) {
+ if (error == GIT_TIMEOUT)
+ git_error_set(GIT_ERROR_NET, "failed to connect to %s: Operation timed out", st->host);
+ else
+ git_error_set(GIT_ERROR_OS, "failed to connect to %s", st->host);
+ error = -1;
+ goto done;
}
+ if (st->parent.timeout && !st->parent.connect_timeout &&
+ (error = set_nonblocking(s)) < 0)
+ return error;
+
st->s = s;
+ error = 0;
+
+done:
p_freeaddrinfo(info);
- return 0;
+ return error;
}
-static ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags)
+static ssize_t socket_write(
+ git_stream *stream,
+ const char *data,
+ size_t len,
+ int flags)
{
git_socket_stream *st = (git_socket_stream *) stream;
- ssize_t written;
+ struct pollfd fd;
+ ssize_t ret;
+
+ GIT_ASSERT(flags == 0);
+ GIT_UNUSED(flags);
- errno = 0;
+ ret = p_send(st->s, data, len, 0);
- if ((written = p_send(st->s, data, len, flags)) < 0) {
- net_set_error("error sending data");
+ if (st->parent.timeout && ret < 0 &&
+ (errno == EAGAIN || errno != EWOULDBLOCK)) {
+ fd.fd = st->s;
+ fd.events = POLLOUT;
+ fd.revents = 0;
+
+ ret = p_poll(&fd, 1, st->parent.timeout);
+
+ if (ret == 1) {
+ ret = p_send(st->s, data, len, 0);
+ } else if (ret == 0) {
+ git_error_set(GIT_ERROR_NET,
+ "could not write to socket: timed out");
+ return GIT_TIMEOUT;
+ }
+ }
+
+ if (ret < 0) {
+ net_set_error("error receiving data from socket");
return -1;
}
- return written;
+ return ret;
}
-static ssize_t socket_read(git_stream *stream, void *data, size_t len)
+static ssize_t socket_read(
+ git_stream *stream,
+ void *data,
+ size_t len)
{
- ssize_t ret;
git_socket_stream *st = (git_socket_stream *) stream;
+ struct pollfd fd;
+ ssize_t ret;
- if ((ret = p_recv(st->s, data, len, 0)) < 0)
- net_set_error("error receiving socket data");
+ ret = p_recv(st->s, data, len, 0);
+
+ if (st->parent.timeout && ret < 0 &&
+ (errno == EAGAIN || errno != EWOULDBLOCK)) {
+ fd.fd = st->s;
+ fd.events = POLLIN;
+ fd.revents = 0;
+
+ ret = p_poll(&fd, 1, st->parent.timeout);
+
+ if (ret == 1) {
+ ret = p_recv(st->s, data, len, 0);
+ } else if (ret == 0) {
+ git_error_set(GIT_ERROR_NET,
+ "could not read from socket: timed out");
+ return GIT_TIMEOUT;
+ }
+ }
+
+ if (ret < 0) {
+ net_set_error("error receiving data from socket");
+ return -1;
+ }
return ret;
}
@@ -199,6 +347,8 @@ static int default_socket_stream_new(
}
st->parent.version = GIT_STREAM_VERSION;
+ st->parent.timeout = git_socket_stream__timeout;
+ st->parent.connect_timeout = git_socket_stream__connect_timeout;
st->parent.connect = socket_connect;
st->parent.write = socket_write;
st->parent.read = socket_read;
@@ -237,3 +387,42 @@ int git_socket_stream_new(
return init(out, host, port);
}
+
+#ifdef GIT_WIN32
+
+static void socket_stream_global_shutdown(void)
+{
+ WSACleanup();
+}
+
+int git_socket_stream_global_init(void)
+{
+ WORD winsock_version;
+ WSADATA wsa_data;
+
+ winsock_version = MAKEWORD(2, 2);
+
+ if (WSAStartup(winsock_version, &wsa_data) != 0) {
+ git_error_set(GIT_ERROR_OS, "could not initialize Windows Socket Library");
+ return -1;
+ }
+
+ if (LOBYTE(wsa_data.wVersion) != 2 ||
+ HIBYTE(wsa_data.wVersion) != 2) {
+ git_error_set(GIT_ERROR_SSL, "Windows Socket Library does not support Winsock 2.2");
+ return -1;
+ }
+
+ return git_runtime_shutdown_register(socket_stream_global_shutdown);
+}
+
+#else
+
+#include "stream.h"
+
+int git_socket_stream_global_init(void)
+{
+ return 0;
+}
+
+ #endif
diff --git a/src/libgit2/streams/socket.h b/src/libgit2/streams/socket.h
index 3235f31679c..73e8de099a6 100644
--- a/src/libgit2/streams/socket.h
+++ b/src/libgit2/streams/socket.h
@@ -9,7 +9,7 @@
#include "common.h"
-#include "netops.h"
+#include "stream.h"
typedef struct {
git_stream parent;
@@ -20,4 +20,6 @@ typedef struct {
extern int git_socket_stream_new(git_stream **out, const char *host, const char *port);
+extern int git_socket_stream_global_init(void);
+
#endif
diff --git a/src/libgit2/streams/stransport.c b/src/libgit2/streams/stransport.c
index 3f31d2541c7..7a3585e246b 100644
--- a/src/libgit2/streams/stransport.c
+++ b/src/libgit2/streams/stransport.c
@@ -44,6 +44,7 @@ typedef struct {
git_stream parent;
git_stream *io;
int owned;
+ int error;
SSLContextRef ctx;
CFDataRef der_data;
git_cert_x509 cert_info;
@@ -61,7 +62,10 @@ static int stransport_connect(git_stream *stream)
return error;
ret = SSLHandshake(st->ctx);
- if (ret != errSSLServerAuthCompleted) {
+
+ if (ret != errSSLServerAuthCompleted && st->error != 0)
+ return -1;
+ else if (ret != errSSLServerAuthCompleted) {
git_error_set(GIT_ERROR_SSL, "unexpected return value from ssl handshake %d", (int)ret);
return -1;
}
@@ -147,10 +151,20 @@ static int stransport_set_proxy(
*/
static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len)
{
- git_stream *io = (git_stream *) conn;
+ stransport_stream *st = (stransport_stream *)conn;
+ git_stream *io = st->io;
+ OSStatus ret;
- if (git_stream__write_full(io, data, *len, 0) < 0)
- return -36; /* "ioErr" from MacErrors.h which is not available on iOS */
+ st->error = 0;
+
+ ret = git_stream__write_full(io, data, *len, 0);
+
+ if (ret < 0) {
+ st->error = ret;
+ return (ret == GIT_TIMEOUT) ?
+ -9853 /* errSSLNetworkTimeout */:
+ -36 /* ioErr */;
+ }
return noErr;
}
@@ -164,8 +178,12 @@ static ssize_t stransport_write(git_stream *stream, const char *data, size_t len
GIT_UNUSED(flags);
data_len = min(len, SSIZE_MAX);
- if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr)
+ if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr) {
+ if (st->error == GIT_TIMEOUT)
+ return GIT_TIMEOUT;
+
return stransport_error(ret);
+ }
GIT_ASSERT(processed < SSIZE_MAX);
return (ssize_t)processed;
@@ -182,18 +200,24 @@ static ssize_t stransport_write(git_stream *stream, const char *data, size_t len
*/
static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len)
{
- git_stream *io = (git_stream *) conn;
+ stransport_stream *st = (stransport_stream *)conn;
+ git_stream *io = st->io;
OSStatus error = noErr;
size_t off = 0;
ssize_t ret;
+ st->error = 0;
+
do {
ret = git_stream_read(io, data + off, *len - off);
+
if (ret < 0) {
- error = -36; /* "ioErr" from MacErrors.h which is not available on iOS */
+ st->error = ret;
+ error = (ret == GIT_TIMEOUT) ?
+ -9853 /* errSSLNetworkTimeout */:
+ -36 /* ioErr */;
break;
- }
- if (ret == 0) {
+ } else if (ret == 0) {
error = errSSLClosedGraceful;
break;
}
@@ -207,12 +231,16 @@ static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len)
static ssize_t stransport_read(git_stream *stream, void *data, size_t len)
{
- stransport_stream *st = (stransport_stream *) stream;
+ stransport_stream *st = (stransport_stream *)stream;
size_t processed;
OSStatus ret;
- if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr)
+ if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr) {
+ if (st->error == GIT_TIMEOUT)
+ return GIT_TIMEOUT;
+
return stransport_error(ret);
+ }
return processed;
}
@@ -269,7 +297,7 @@ static int stransport_wrap(
}
if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr ||
- (ret = SSLSetConnection(st->ctx, st->io)) != noErr ||
+ (ret = SSLSetConnection(st->ctx, st)) != noErr ||
(ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr ||
(ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr ||
(ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr ||
diff --git a/src/libgit2/streams/tls.c b/src/libgit2/streams/tls.c
index e063a33f99a..246ac9ca793 100644
--- a/src/libgit2/streams/tls.c
+++ b/src/libgit2/streams/tls.c
@@ -13,6 +13,7 @@
#include "streams/mbedtls.h"
#include "streams/openssl.h"
#include "streams/stransport.h"
+#include "streams/schannel.h"
int git_tls_stream_new(git_stream **out, const char *host, const char *port)
{
@@ -33,6 +34,8 @@ int git_tls_stream_new(git_stream **out, const char *host, const char *port)
init = git_openssl_stream_new;
#elif defined(GIT_MBEDTLS)
init = git_mbedtls_stream_new;
+#elif defined(GIT_SCHANNEL)
+ init = git_schannel_stream_new;
#endif
} else {
return error;
@@ -63,6 +66,8 @@ int git_tls_stream_wrap(git_stream **out, git_stream *in, const char *host)
wrap = git_openssl_stream_wrap;
#elif defined(GIT_MBEDTLS)
wrap = git_mbedtls_stream_wrap;
+#elif defined(GIT_SCHANNEL)
+ wrap = git_schannel_stream_wrap;
#endif
}
diff --git a/src/libgit2/submodule.c b/src/libgit2/submodule.c
index 95ea84fc233..830d41c7d22 100644
--- a/src/libgit2/submodule.c
+++ b/src/libgit2/submodule.c
@@ -196,7 +196,7 @@ static void free_submodule_names(git_strmap *names)
*/
static int load_submodule_names(git_strmap **out, git_repository *repo, git_config *cfg)
{
- const char *key = "submodule\\..*\\.path";
+ const char *key = "^submodule\\..*\\.path$";
git_config_iterator *iter = NULL;
git_config_entry *entry;
git_str buf = GIT_STR_INIT;
@@ -332,7 +332,7 @@ int git_submodule__lookup_with_cache(
/* If it's not configured or we're looking by path */
if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
git_config_backend *mods;
- const char *pattern = "submodule\\..*\\.path";
+ const char *pattern = "^submodule\\..*\\.path$";
git_str path = GIT_STR_INIT;
fbp_data data = { NULL, NULL };
diff --git a/src/libgit2/submodule.h b/src/libgit2/submodule.h
index 7fa98248620..40b7b70f777 100644
--- a/src/libgit2/submodule.h
+++ b/src/libgit2/submodule.h
@@ -69,9 +69,9 @@
* - `repo` is the parent repository that contains this submodule.
* - `flags` after for internal use, tracking where this submodule has been
* found (head, index, config, workdir) and known status info, etc.
- * - `head_oid` is the SHA1 for the submodule path in the repo HEAD.
- * - `index_oid` is the SHA1 for the submodule recorded in the index.
- * - `wd_oid` is the SHA1 for the HEAD of the checked out submodule.
+ * - `head_oid` is the oid for the submodule path in the repo HEAD.
+ * - `index_oid` is the oid for the submodule recorded in the index.
+ * - `wd_oid` is the oid for the HEAD of the checked out submodule.
*
* If the submodule has been added to .gitmodules but not yet git added,
* then the `index_oid` will be zero but still marked valid. If the
diff --git a/src/libgit2/sysdir.c b/src/libgit2/sysdir.c
index 450cb509b81..7838a6789c5 100644
--- a/src/libgit2/sysdir.c
+++ b/src/libgit2/sysdir.c
@@ -12,16 +12,262 @@
#include "fs_path.h"
#include
#if GIT_WIN32
-#include "win32/findfile.h"
+# include "fs_path.h"
+# include "win32/path_w32.h"
+# include "win32/utf-conv.h"
#else
-#include
-#include
+# include
+# include
#endif
+#ifdef GIT_WIN32
+# define REG_GITFORWINDOWS_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
+# define REG_GITFORWINDOWS_KEY_WOW64 L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
+
+static int expand_win32_path(git_win32_path dest, const wchar_t *src)
+{
+ DWORD len = ExpandEnvironmentStringsW(src, dest, GIT_WIN_PATH_UTF16);
+
+ if (!len || len > GIT_WIN_PATH_UTF16)
+ return -1;
+
+ return 0;
+}
+
+static int win32_path_to_utf8(git_str *dest, const wchar_t *src)
+{
+ git_win32_utf8_path utf8_path;
+
+ if (git_win32_path_to_utf8(utf8_path, src) < 0) {
+ git_error_set(GIT_ERROR_OS, "unable to convert path to UTF-8");
+ return -1;
+ }
+
+ /* Convert backslashes to forward slashes */
+ git_fs_path_mkposix(utf8_path);
+
+ return git_str_sets(dest, utf8_path);
+}
+
+static git_win32_path mock_registry;
+static bool mock_registry_set;
+
+extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir)
+{
+ if (!mock_sysdir) {
+ mock_registry[0] = L'\0';
+ mock_registry_set = false;
+ } else {
+ size_t len = wcslen(mock_sysdir);
+
+ if (len > GIT_WIN_PATH_MAX) {
+ git_error_set(GIT_ERROR_INVALID, "mock path too long");
+ return -1;
+ }
+
+ wcscpy(mock_registry, mock_sysdir);
+ mock_registry_set = true;
+ }
+
+ return 0;
+}
+
+static int lookup_registry_key(
+ git_win32_path out,
+ const HKEY hive,
+ const wchar_t* key,
+ const wchar_t *value)
+{
+ HKEY hkey;
+ DWORD type, size;
+ int error = GIT_ENOTFOUND;
+
+ /*
+ * Registry data may not be NUL terminated, provide room to do
+ * it ourselves.
+ */
+ size = (DWORD)((sizeof(git_win32_path) - 1) * sizeof(wchar_t));
+
+ if (RegOpenKeyExW(hive, key, 0, KEY_READ, &hkey) != 0)
+ return GIT_ENOTFOUND;
+
+ if (RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)out, &size) == 0 &&
+ type == REG_SZ &&
+ size > 0 &&
+ size < sizeof(git_win32_path)) {
+ size_t wsize = size / sizeof(wchar_t);
+ size_t len = wsize - 1;
+
+ if (out[wsize - 1] != L'\0') {
+ len = wsize;
+ out[wsize] = L'\0';
+ }
+
+ if (out[len - 1] == L'\\')
+ out[len - 1] = L'\0';
+
+ if (_waccess(out, F_OK) == 0)
+ error = 0;
+ }
+
+ RegCloseKey(hkey);
+ return error;
+}
+
+static int find_sysdir_in_registry(git_win32_path out)
+{
+ if (mock_registry_set) {
+ if (mock_registry[0] == L'\0')
+ return GIT_ENOTFOUND;
+
+ wcscpy(out, mock_registry);
+ return 0;
+ }
+
+ if (lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
+ lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0 ||
+ lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
+ lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0)
+ return 0;
+
+ return GIT_ENOTFOUND;
+}
+
+static int find_sysdir_in_path(git_win32_path out)
+{
+ size_t out_len;
+
+ if (git_win32_path_find_executable(out, L"git.exe") < 0 &&
+ git_win32_path_find_executable(out, L"git.cmd") < 0)
+ return GIT_ENOTFOUND;
+
+ out_len = wcslen(out);
+
+ /* Trim the file name */
+ if (out_len <= CONST_STRLEN(L"git.exe"))
+ return GIT_ENOTFOUND;
+
+ out_len -= CONST_STRLEN(L"git.exe");
+
+ if (out_len && out[out_len - 1] == L'\\')
+ out_len--;
+
+ /*
+ * Git for Windows usually places the command in a 'bin' or
+ * 'cmd' directory, trim that.
+ */
+ if (out_len >= CONST_STRLEN(L"\\bin") &&
+ wcsncmp(&out[out_len - CONST_STRLEN(L"\\bin")], L"\\bin", CONST_STRLEN(L"\\bin")) == 0)
+ out_len -= CONST_STRLEN(L"\\bin");
+ else if (out_len >= CONST_STRLEN(L"\\cmd") &&
+ wcsncmp(&out[out_len - CONST_STRLEN(L"\\cmd")], L"\\cmd", CONST_STRLEN(L"\\cmd")) == 0)
+ out_len -= CONST_STRLEN(L"\\cmd");
+
+ if (!out_len)
+ return GIT_ENOTFOUND;
+
+ out[out_len] = L'\0';
+ return 0;
+}
+
+static int find_win32_dirs(
+ git_str *out,
+ const wchar_t* tmpl[])
+{
+ git_win32_path path16;
+ git_str buf = GIT_STR_INIT;
+
+ git_str_clear(out);
+
+ for (; *tmpl != NULL; tmpl++) {
+ if (!expand_win32_path(path16, *tmpl) &&
+ path16[0] != L'%' &&
+ !_waccess(path16, F_OK)) {
+ win32_path_to_utf8(&buf, path16);
+
+ if (buf.size)
+ git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
+ }
+ }
+
+ git_str_dispose(&buf);
+
+ return (git_str_oom(out) ? -1 : 0);
+}
+
+static int append_subdir(git_str *out, git_str *path, const char *subdir)
+{
+ static const char* architecture_roots[] = {
+ "",
+ "mingw64",
+ "mingw32",
+ NULL
+ };
+ const char **root;
+ size_t orig_path_len = path->size;
+
+ for (root = architecture_roots; *root; root++) {
+ if ((*root[0] && git_str_joinpath(path, path->ptr, *root) < 0) ||
+ git_str_joinpath(path, path->ptr, subdir) < 0)
+ return -1;
+
+ if (git_fs_path_exists(path->ptr) &&
+ git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, path->ptr) < 0)
+ return -1;
+
+ git_str_truncate(path, orig_path_len);
+ }
+
+ return 0;
+}
+
+int git_win32__find_system_dirs(git_str *out, const char *subdir)
+{
+ git_win32_path pathdir, regdir;
+ git_str path8 = GIT_STR_INIT;
+ bool has_pathdir, has_regdir;
+ int error;
+
+ has_pathdir = (find_sysdir_in_path(pathdir) == 0);
+ has_regdir = (find_sysdir_in_registry(regdir) == 0);
+
+ if (!has_pathdir && !has_regdir)
+ return 0;
+
+ /*
+ * Usually the git in the path is the same git in the registry,
+ * in this case there's no need to duplicate the paths.
+ */
+ if (has_pathdir && has_regdir && wcscmp(pathdir, regdir) == 0)
+ has_regdir = false;
+
+ if (has_pathdir) {
+ if ((error = win32_path_to_utf8(&path8, pathdir)) < 0 ||
+ (error = append_subdir(out, &path8, subdir)) < 0)
+ goto done;
+ }
+
+ if (has_regdir) {
+ if ((error = win32_path_to_utf8(&path8, regdir)) < 0 ||
+ (error = append_subdir(out, &path8, subdir)) < 0)
+ goto done;
+ }
+
+done:
+ git_str_dispose(&path8);
+ return error;
+}
+#endif /* WIN32 */
+
static int git_sysdir_guess_programdata_dirs(git_str *out)
{
#ifdef GIT_WIN32
- return git_win32__find_programdata_dirs(out);
+ static const wchar_t *programdata_tmpls[2] = {
+ L"%PROGRAMDATA%\\Git",
+ NULL,
+ };
+
+ return find_win32_dirs(out, programdata_tmpls);
#else
git_str_clear(out);
return 0;
@@ -75,10 +321,17 @@ static int get_passwd_home(git_str *out, uid_t uid)
}
#endif
-static int git_sysdir_guess_global_dirs(git_str *out)
+static int git_sysdir_guess_home_dirs(git_str *out)
{
#ifdef GIT_WIN32
- return git_win32__find_global_dirs(out);
+ static const wchar_t *global_tmpls[4] = {
+ L"%HOME%\\",
+ L"%HOMEDRIVE%%HOMEPATH%\\",
+ L"%USERPROFILE%\\",
+ NULL,
+ };
+
+ return find_win32_dirs(out, global_tmpls);
#else
int error;
uid_t uid, euid;
@@ -114,10 +367,25 @@ static int git_sysdir_guess_global_dirs(git_str *out)
#endif
}
+static int git_sysdir_guess_global_dirs(git_str *out)
+{
+ return git_sysdir_guess_home_dirs(out);
+}
+
static int git_sysdir_guess_xdg_dirs(git_str *out)
{
#ifdef GIT_WIN32
- return git_win32__find_xdg_dirs(out);
+ static const wchar_t *global_tmpls[7] = {
+ L"%XDG_CONFIG_HOME%\\git",
+ L"%APPDATA%\\git",
+ L"%LOCALAPPDATA%\\git",
+ L"%HOME%\\.config\\git",
+ L"%HOMEDRIVE%%HOMEPATH%\\.config\\git",
+ L"%USERPROFILE%\\.config\\git",
+ NULL,
+ };
+
+ return find_win32_dirs(out, global_tmpls);
#else
git_str env = GIT_STR_INIT;
int error;
@@ -171,6 +439,7 @@ static struct git_sysdir__dir git_sysdir__dirs[] = {
{ GIT_STR_INIT, git_sysdir_guess_xdg_dirs },
{ GIT_STR_INIT, git_sysdir_guess_programdata_dirs },
{ GIT_STR_INIT, git_sysdir_guess_template_dirs },
+ { GIT_STR_INIT, git_sysdir_guess_home_dirs }
};
static void git_sysdir_global_shutdown(void)
@@ -350,6 +619,12 @@ int git_sysdir_find_template_dir(git_str *path)
path, NULL, GIT_SYSDIR_TEMPLATE, "template");
}
+int git_sysdir_find_homedir(git_str *path)
+{
+ return git_sysdir_find_in_dirlist(
+ path, NULL, GIT_SYSDIR_HOME, "home directory");
+}
+
int git_sysdir_expand_global_file(git_str *path, const char *filename)
{
int error;
@@ -361,3 +636,15 @@ int git_sysdir_expand_global_file(git_str *path, const char *filename)
return error;
}
+
+int git_sysdir_expand_homedir_file(git_str *path, const char *filename)
+{
+ int error;
+
+ if ((error = git_sysdir_find_homedir(path)) == 0) {
+ if (filename)
+ error = git_str_joinpath(path, path->ptr, filename);
+ }
+
+ return error;
+}
diff --git a/src/libgit2/sysdir.h b/src/libgit2/sysdir.h
index 568f27940c8..03f59e1de81 100644
--- a/src/libgit2/sysdir.h
+++ b/src/libgit2/sysdir.h
@@ -57,10 +57,22 @@ extern int git_sysdir_find_programdata_file(git_str *path, const char *filename)
extern int git_sysdir_find_template_dir(git_str *path);
/**
- * Expand the name of a "global" file (i.e. one in a user's home
- * directory). Unlike `find_global_file` (above), this makes no
- * attempt to check for the existence of the file, and is useful if
- * you want the full path regardless of existence.
+ * Find the home directory. On Windows, this will look at the `HOME`,
+ * `HOMEPATH`, and `USERPROFILE` environment variables (in that order)
+ * and return the first path that is set and exists. On other systems,
+ * this will simply return the contents of the `HOME` environment variable.
+ *
+ * @param path buffer to write the full path into
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_sysdir_find_homedir(git_str *path);
+
+/**
+ * Expand the name of a "global" file -- by default inside the user's
+ * home directory, but can be overridden by the user configuration.
+ * Unlike `find_global_file` (above), this makes no attempt to check
+ * for the existence of the file, and is useful if you want the full
+ * path regardless of existence.
*
* @param path buffer to write the full path into
* @param filename name of file in the home directory
@@ -68,13 +80,25 @@ extern int git_sysdir_find_template_dir(git_str *path);
*/
extern int git_sysdir_expand_global_file(git_str *path, const char *filename);
+/**
+ * Expand the name of a file in the user's home directory. This
+ * function makes no attempt to check for the existence of the file,
+ * and is useful if you want the full path regardless of existence.
+ *
+ * @param path buffer to write the full path into
+ * @param filename name of file in the home directory
+ * @return 0 on success or -1 on error
+ */
+extern int git_sysdir_expand_homedir_file(git_str *path, const char *filename);
+
typedef enum {
- GIT_SYSDIR_SYSTEM = 0,
- GIT_SYSDIR_GLOBAL = 1,
- GIT_SYSDIR_XDG = 2,
+ GIT_SYSDIR_SYSTEM = 0,
+ GIT_SYSDIR_GLOBAL = 1,
+ GIT_SYSDIR_XDG = 2,
GIT_SYSDIR_PROGRAMDATA = 3,
- GIT_SYSDIR_TEMPLATE = 4,
- GIT_SYSDIR__MAX = 5
+ GIT_SYSDIR_TEMPLATE = 4,
+ GIT_SYSDIR_HOME = 5,
+ GIT_SYSDIR__MAX = 6
} git_sysdir_t;
/**
@@ -110,4 +134,12 @@ extern int git_sysdir_set(git_sysdir_t which, const char *paths);
*/
extern int git_sysdir_reset(void);
+#ifdef GIT_WIN32
+/** Sets the registry system dir to a mock; for testing. */
+extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir);
+
+/** Find the given system dir; for testing. */
+extern int git_win32__find_system_dirs(git_str *out, const char *subdir);
+#endif
+
#endif
diff --git a/src/libgit2/tag.c b/src/libgit2/tag.c
index 0a90e393cc9..562ec13eaed 100644
--- a/src/libgit2/tag.c
+++ b/src/libgit2/tag.c
@@ -65,7 +65,11 @@ static int tag_error(const char *str)
return GIT_EINVALID;
}
-static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
+static int tag_parse(
+ git_tag *tag,
+ const char *buffer,
+ const char *buffer_end,
+ git_oid_t oid_type)
{
static const char *tag_types[] = {
NULL, "commit\n", "tree\n", "blob\n", "tag\n"
@@ -76,7 +80,7 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
int error;
if (git_object__parse_oid_header(&tag->target,
- &buffer, buffer_end, "object ", GIT_OID_SHA1) < 0)
+ &buffer, buffer_end, "object ", oid_type) < 0)
return tag_error("object field invalid");
if (buffer + 5 >= buffer_end)
@@ -161,18 +165,25 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
return 0;
}
-int git_tag__parse_raw(void *_tag, const char *data, size_t size)
+int git_tag__parse_raw(
+ void *_tag,
+ const char *data,
+ size_t size,
+ git_oid_t oid_type)
{
- return tag_parse(_tag, data, data + size);
+ return tag_parse(_tag, data, data + size, oid_type);
}
-int git_tag__parse(void *_tag, git_odb_object *odb_obj)
+int git_tag__parse(
+ void *_tag,
+ git_odb_object *odb_obj,
+ git_oid_t oid_type)
{
git_tag *tag = _tag;
const char *buffer = git_odb_object_data(odb_obj);
const char *buffer_end = buffer + git_odb_object_size(odb_obj);
- return tag_parse(tag, buffer, buffer_end);
+ return tag_parse(tag, buffer, buffer_end, oid_type);
}
static int retrieve_tag_reference(
@@ -374,7 +385,7 @@ int git_tag_create_from_buffer(git_oid *oid, git_repository *repo, const char *b
return -1;
/* validate the buffer */
- if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0)
+ if (tag_parse(&tag, buffer, buffer + strlen(buffer), repo->oid_type) < 0)
return -1;
/* validate the target */
diff --git a/src/libgit2/tag.h b/src/libgit2/tag.h
index 76ae1508eaa..fdaaa463ccf 100644
--- a/src/libgit2/tag.h
+++ b/src/libgit2/tag.h
@@ -25,7 +25,7 @@ struct git_tag {
};
void git_tag__free(void *tag);
-int git_tag__parse(void *tag, git_odb_object *obj);
-int git_tag__parse_raw(void *tag, const char *data, size_t size);
+int git_tag__parse(void *tag, git_odb_object *obj, git_oid_t oid_type);
+int git_tag__parse_raw(void *tag, const char *data, size_t size, git_oid_t oid_type);
#endif
diff --git a/src/libgit2/threadstate.c b/src/libgit2/threadstate.c
deleted file mode 100644
index 9e3ef581849..00000000000
--- a/src/libgit2/threadstate.c
+++ /dev/null
@@ -1,84 +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 "threadstate.h"
-#include "runtime.h"
-
-/**
- * Handle the thread-local state
- *
- * `git_threadstate_global_init` will be called as part
- * of `git_libgit2_init` (which itself must be called
- * before calling any other function in the library).
- *
- * This function allocates a TLS index to store the per-
- * thread state.
- *
- * Any internal method that requires thread-local state
- * will then call `git_threadstate_get()` which returns a
- * pointer to the thread-local state structure; this
- * structure is lazily allocated on each thread.
- *
- * This mechanism will register a shutdown handler
- * (`git_threadstate_global_shutdown`) which will free the
- * TLS index. This shutdown handler will be called by
- * `git_libgit2_shutdown`.
- */
-
-static git_tlsdata_key tls_key;
-
-static void threadstate_dispose(git_threadstate *threadstate)
-{
- if (!threadstate)
- return;
-
- if (threadstate->error_t.message != git_str__initstr)
- git__free(threadstate->error_t.message);
- threadstate->error_t.message = NULL;
-}
-
-static void GIT_SYSTEM_CALL threadstate_free(void *threadstate)
-{
- threadstate_dispose(threadstate);
- git__free(threadstate);
-}
-
-static void git_threadstate_global_shutdown(void)
-{
- git_threadstate *threadstate;
-
- threadstate = git_tlsdata_get(tls_key);
- git_tlsdata_set(tls_key, NULL);
-
- threadstate_dispose(threadstate);
- git__free(threadstate);
-
- git_tlsdata_dispose(tls_key);
-}
-
-int git_threadstate_global_init(void)
-{
- if (git_tlsdata_init(&tls_key, &threadstate_free) != 0)
- return -1;
-
- return git_runtime_shutdown_register(git_threadstate_global_shutdown);
-}
-
-git_threadstate *git_threadstate_get(void)
-{
- git_threadstate *threadstate;
-
- if ((threadstate = git_tlsdata_get(tls_key)) != NULL)
- return threadstate;
-
- if ((threadstate = git__calloc(1, sizeof(git_threadstate))) == NULL ||
- git_str_init(&threadstate->error_buf, 0) < 0)
- return NULL;
-
- git_tlsdata_set(tls_key, threadstate);
- return threadstate;
-}
diff --git a/src/libgit2/threadstate.h b/src/libgit2/threadstate.h
deleted file mode 100644
index f9e7ba7bfe0..00000000000
--- a/src/libgit2/threadstate.h
+++ /dev/null
@@ -1,24 +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_threadstate_h__
-#define INCLUDE_threadstate_h__
-
-#include "common.h"
-
-typedef struct {
- git_error *last_error;
- git_error error_t;
- git_str error_buf;
- char oid_fmt[GIT_OID_SHA1_HEXSIZE+1];
-} git_threadstate;
-
-extern int git_threadstate_global_init(void);
-extern git_threadstate *git_threadstate_get(void);
-
-#define GIT_THREADSTATE (git_threadstate_get())
-
-#endif
diff --git a/src/libgit2/trailer.c b/src/libgit2/trailer.c
index 4761c9922f2..c7579fb3b97 100644
--- a/src/libgit2/trailer.c
+++ b/src/libgit2/trailer.c
@@ -24,7 +24,7 @@ static const char *const git_generated_prefixes[] = {
static int is_blank_line(const char *str)
{
const char *s = str;
- while (*s && *s != '\n' && isspace(*s))
+ while (*s && *s != '\n' && git__isspace(*s))
s++;
return !*s || *s == '\n';
}
@@ -93,7 +93,7 @@ static bool find_separator(size_t *out, const char *line, const char *separators
return true;
}
- if (!whitespace_found && (isalnum(*c) || *c == '-'))
+ if (!whitespace_found && (git__isalnum(*c) || *c == '-'))
continue;
if (c != line && (*c == ' ' || *c == '\t')) {
whitespace_found = 1;
@@ -158,7 +158,7 @@ static size_t find_patch_start(const char *str)
const char *s;
for (s = str; *s; s = next_line(s)) {
- if (git__prefixcmp(s, "---") == 0)
+ if (git__prefixcmp(s, "---") == 0 && git__isspace(s[3]))
return s - str;
}
@@ -233,12 +233,12 @@ static size_t find_trailer_start(const char *buf, size_t len)
}
find_separator(&separator_pos, bol, TRAILER_SEPARATORS);
- if (separator_pos >= 1 && !isspace(bol[0])) {
+ if (separator_pos >= 1 && !git__isspace(bol[0])) {
trailer_lines++;
possible_continuation_lines = 0;
if (recognized_prefix)
continue;
- } else if (isspace(bol[0]))
+ } else if (git__isspace(bol[0]))
possible_continuation_lines++;
else {
non_trailer_lines++;
@@ -323,7 +323,7 @@ int git_message_trailers(git_message_trailer_array *trailer_arr, const char *mes
goto ret;
}
- if (isalnum(*ptr) || *ptr == '-') {
+ if (git__isalnum(*ptr) || *ptr == '-') {
/* legal key character */
NEXT(S_KEY);
}
diff --git a/src/libgit2/transaction.c b/src/libgit2/transaction.c
index ccffa9984cc..963416196b4 100644
--- a/src/libgit2/transaction.c
+++ b/src/libgit2/transaction.c
@@ -49,12 +49,16 @@ struct git_transaction {
git_repository *repo;
git_refdb *db;
git_config *cfg;
+ void *cfg_data;
git_strmap *locks;
git_pool pool;
};
-int git_transaction_config_new(git_transaction **out, git_config *cfg)
+int git_transaction_config_new(
+ git_transaction **out,
+ git_config *cfg,
+ void *data)
{
git_transaction *tx;
@@ -66,6 +70,8 @@ int git_transaction_config_new(git_transaction **out, git_config *cfg)
tx->type = TRANSACTION_CONFIG;
tx->cfg = cfg;
+ tx->cfg_data = data;
+
*out = tx;
return 0;
}
@@ -333,8 +339,9 @@ int git_transaction_commit(git_transaction *tx)
GIT_ASSERT_ARG(tx);
if (tx->type == TRANSACTION_CONFIG) {
- error = git_config_unlock(tx->cfg, true);
+ error = git_config_unlock(tx->cfg, tx->cfg_data, true);
tx->cfg = NULL;
+ tx->cfg_data = NULL;
return error;
}
@@ -369,10 +376,8 @@ void git_transaction_free(git_transaction *tx)
return;
if (tx->type == TRANSACTION_CONFIG) {
- if (tx->cfg) {
- git_config_unlock(tx->cfg, false);
- git_config_free(tx->cfg);
- }
+ if (tx->cfg)
+ git_config_unlock(tx->cfg, tx->cfg_data, false);
git__free(tx);
return;
diff --git a/src/libgit2/transaction.h b/src/libgit2/transaction.h
index 780c068303e..cb26017ae9f 100644
--- a/src/libgit2/transaction.h
+++ b/src/libgit2/transaction.h
@@ -9,6 +9,9 @@
#include "common.h"
-int git_transaction_config_new(git_transaction **out, git_config *cfg);
+int git_transaction_config_new(
+ git_transaction **out,
+ git_config *cfg,
+ void *data);
#endif
diff --git a/src/libgit2/transport.c b/src/libgit2/transport.c
index 640ccacaee3..c61d0a68b7e 100644
--- a/src/libgit2/transport.c
+++ b/src/libgit2/transport.c
@@ -22,6 +22,7 @@ typedef struct transport_definition {
static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1, NULL };
static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0, NULL };
+
#ifdef GIT_SSH
static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0, NULL };
#endif
@@ -33,11 +34,13 @@ static transport_definition transports[] = {
{ "http://", git_transport_smart, &http_subtransport_definition },
{ "https://", git_transport_smart, &http_subtransport_definition },
{ "file://", git_transport_local, NULL },
+
#ifdef GIT_SSH
{ "ssh://", git_transport_smart, &ssh_subtransport_definition },
{ "ssh+git://", git_transport_smart, &ssh_subtransport_definition },
{ "git+ssh://", git_transport_smart, &ssh_subtransport_definition },
#endif
+
{ NULL, 0, 0 }
};
diff --git a/src/libgit2/transports/auth.h b/src/libgit2/transports/auth.h
index 64680cc5358..9f6f8fd3b2d 100644
--- a/src/libgit2/transports/auth.h
+++ b/src/libgit2/transports/auth.h
@@ -9,8 +9,7 @@
#define INCLUDE_transports_auth_h__
#include "common.h"
-
-#include "netops.h"
+#include "net.h"
typedef enum {
GIT_HTTP_AUTH_BASIC = 1,
diff --git a/src/libgit2/transports/auth_negotiate.c b/src/libgit2/transports/auth_gssapi.c
similarity index 79%
rename from src/libgit2/transports/auth_negotiate.c
rename to src/libgit2/transports/auth_gssapi.c
index 6380504be7e..5005538411b 100644
--- a/src/libgit2/transports/auth_negotiate.c
+++ b/src/libgit2/transports/auth_gssapi.c
@@ -20,13 +20,13 @@
#include
#endif
-static gss_OID_desc negotiate_oid_spnego =
+static gss_OID_desc gssapi_oid_spnego =
{ 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
-static gss_OID_desc negotiate_oid_krb5 =
+static gss_OID_desc gssapi_oid_krb5 =
{ 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
-static gss_OID negotiate_oids[] =
- { &negotiate_oid_spnego, &negotiate_oid_krb5, NULL };
+static gss_OID gssapi_oids[] =
+ { &gssapi_oid_spnego, &gssapi_oid_krb5, NULL };
typedef struct {
git_http_auth_context parent;
@@ -36,9 +36,9 @@ typedef struct {
char *challenge;
gss_ctx_id_t gss_context;
gss_OID oid;
-} http_auth_negotiate_context;
+} http_auth_gssapi_context;
-static void negotiate_err_set(
+static void gssapi_err_set(
OM_uint32 status_major,
OM_uint32 status_minor,
const char *message)
@@ -58,11 +58,11 @@ static void negotiate_err_set(
}
}
-static int negotiate_set_challenge(
+static int gssapi_set_challenge(
git_http_auth_context *c,
const char *challenge)
{
- http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+ http_auth_gssapi_context *ctx = (http_auth_gssapi_context *)c;
GIT_ASSERT_ARG(ctx);
GIT_ASSERT_ARG(challenge);
@@ -76,7 +76,7 @@ static int negotiate_set_challenge(
return 0;
}
-static void negotiate_context_dispose(http_auth_negotiate_context *ctx)
+static void gssapi_context_dispose(http_auth_gssapi_context *ctx)
{
OM_uint32 status_minor;
@@ -92,12 +92,12 @@ static void negotiate_context_dispose(http_auth_negotiate_context *ctx)
ctx->challenge = NULL;
}
-static int negotiate_next_token(
+static int gssapi_next_token(
git_str *buf,
git_http_auth_context *c,
git_credential *cred)
{
- http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+ http_auth_gssapi_context *ctx = (http_auth_gssapi_context *)c;
OM_uint32 status_major, status_minor;
gss_buffer_desc target_buffer = GSS_C_EMPTY_BUFFER,
input_token = GSS_C_EMPTY_BUFFER,
@@ -126,7 +126,7 @@ static int negotiate_next_token(
GSS_C_NT_HOSTBASED_SERVICE, &server);
if (GSS_ERROR(status_major)) {
- negotiate_err_set(status_major, status_minor,
+ gssapi_err_set(status_major, status_minor,
"could not parse principal");
error = -1;
goto done;
@@ -152,10 +152,10 @@ static int negotiate_next_token(
input_token.length = input_buf.size;
input_token_ptr = &input_token;
} else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
- negotiate_context_dispose(ctx);
+ gssapi_context_dispose(ctx);
}
- mech = &negotiate_oid_spnego;
+ mech = &gssapi_oid_spnego;
status_major = gss_init_sec_context(
&status_minor,
@@ -173,14 +173,14 @@ static int negotiate_next_token(
NULL);
if (GSS_ERROR(status_major)) {
- negotiate_err_set(status_major, status_minor, "negotiate failure");
+ gssapi_err_set(status_major, status_minor, "negotiate failure");
error = -1;
goto done;
}
/* This message merely told us auth was complete; we do not respond. */
if (status_major == GSS_S_COMPLETE) {
- negotiate_context_dispose(ctx);
+ gssapi_context_dispose(ctx);
ctx->complete = 1;
goto done;
}
@@ -204,20 +204,20 @@ static int negotiate_next_token(
return error;
}
-static int negotiate_is_complete(git_http_auth_context *c)
+static int gssapi_is_complete(git_http_auth_context *c)
{
- http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+ http_auth_gssapi_context *ctx = (http_auth_gssapi_context *)c;
GIT_ASSERT_ARG(ctx);
return (ctx->complete == 1);
}
-static void negotiate_context_free(git_http_auth_context *c)
+static void gssapi_context_free(git_http_auth_context *c)
{
- http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+ http_auth_gssapi_context *ctx = (http_auth_gssapi_context *)c;
- negotiate_context_dispose(ctx);
+ gssapi_context_dispose(ctx);
ctx->configured = 0;
ctx->complete = 0;
@@ -226,8 +226,8 @@ static void negotiate_context_free(git_http_auth_context *c)
git__free(ctx);
}
-static int negotiate_init_context(
- http_auth_negotiate_context *ctx,
+static int gssapi_init_context(
+ http_auth_gssapi_context *ctx,
const git_net_url *url)
{
OM_uint32 status_major, status_minor;
@@ -239,13 +239,13 @@ static int negotiate_init_context(
status_major = gss_indicate_mechs(&status_minor, &mechanism_list);
if (GSS_ERROR(status_major)) {
- negotiate_err_set(status_major, status_minor,
+ gssapi_err_set(status_major, status_minor,
"could not query mechanisms");
return -1;
}
if (mechanism_list) {
- for (oid = negotiate_oids; *oid; oid++) {
+ for (oid = gssapi_oids; *oid; oid++) {
for (i = 0; i < mechanism_list->count; i++) {
item = &mechanism_list->elements[i];
@@ -285,14 +285,14 @@ int git_http_auth_negotiate(
git_http_auth_context **out,
const git_net_url *url)
{
- http_auth_negotiate_context *ctx;
+ http_auth_gssapi_context *ctx;
*out = NULL;
- ctx = git__calloc(1, sizeof(http_auth_negotiate_context));
+ ctx = git__calloc(1, sizeof(http_auth_gssapi_context));
GIT_ERROR_CHECK_ALLOC(ctx);
- if (negotiate_init_context(ctx, url) < 0) {
+ if (gssapi_init_context(ctx, url) < 0) {
git__free(ctx);
return -1;
}
@@ -300,10 +300,10 @@ int git_http_auth_negotiate(
ctx->parent.type = GIT_HTTP_AUTH_NEGOTIATE;
ctx->parent.credtypes = GIT_CREDENTIAL_DEFAULT;
ctx->parent.connection_affinity = 1;
- ctx->parent.set_challenge = negotiate_set_challenge;
- ctx->parent.next_token = negotiate_next_token;
- ctx->parent.is_complete = negotiate_is_complete;
- ctx->parent.free = negotiate_context_free;
+ ctx->parent.set_challenge = gssapi_set_challenge;
+ ctx->parent.next_token = gssapi_next_token;
+ ctx->parent.is_complete = gssapi_is_complete;
+ ctx->parent.free = gssapi_context_free;
*out = (git_http_auth_context *)ctx;
diff --git a/src/libgit2/transports/auth_negotiate.h b/src/libgit2/transports/auth_negotiate.h
index 34aff295b13..4360785c555 100644
--- a/src/libgit2/transports/auth_negotiate.h
+++ b/src/libgit2/transports/auth_negotiate.h
@@ -12,7 +12,7 @@
#include "git2.h"
#include "auth.h"
-#if defined(GIT_GSSAPI) || defined(GIT_GSSFRAMEWORK)
+#if defined(GIT_GSSAPI) || defined(GIT_GSSFRAMEWORK) || defined(GIT_WIN32)
extern int git_http_auth_negotiate(
git_http_auth_context **out,
diff --git a/src/libgit2/transports/auth_ntlm.h b/src/libgit2/transports/auth_ntlm.h
index 40689498cae..33406ae94c3 100644
--- a/src/libgit2/transports/auth_ntlm.h
+++ b/src/libgit2/transports/auth_ntlm.h
@@ -13,7 +13,7 @@
/* NTLM requires a full request/challenge/response */
#define GIT_AUTH_STEPS_NTLM 2
-#ifdef GIT_NTLM
+#if defined(GIT_NTLM) || defined(GIT_WIN32)
#if defined(GIT_OPENSSL)
# define CRYPT_OPENSSL
diff --git a/src/libgit2/transports/auth_ntlm.c b/src/libgit2/transports/auth_ntlmclient.c
similarity index 88%
rename from src/libgit2/transports/auth_ntlm.c
rename to src/libgit2/transports/auth_ntlmclient.c
index f49ce101a56..6f26a6179c6 100644
--- a/src/libgit2/transports/auth_ntlm.c
+++ b/src/libgit2/transports/auth_ntlmclient.c
@@ -23,7 +23,7 @@ typedef struct {
bool complete;
} http_auth_ntlm_context;
-static int ntlm_set_challenge(
+static int ntlmclient_set_challenge(
git_http_auth_context *c,
const char *challenge)
{
@@ -40,7 +40,7 @@ static int ntlm_set_challenge(
return 0;
}
-static int ntlm_set_credentials(http_auth_ntlm_context *ctx, git_credential *_cred)
+static int ntlmclient_set_credentials(http_auth_ntlm_context *ctx, git_credential *_cred)
{
git_credential_userpass_plaintext *cred;
const char *sep, *username;
@@ -76,7 +76,7 @@ static int ntlm_set_credentials(http_auth_ntlm_context *ctx, git_credential *_cr
return error;
}
-static int ntlm_next_token(
+static int ntlmclient_next_token(
git_str *buf,
git_http_auth_context *c,
git_credential *cred)
@@ -104,7 +104,7 @@ static int ntlm_next_token(
*/
ctx->complete = true;
- if (cred && ntlm_set_credentials(ctx, cred) != 0)
+ if (cred && ntlmclient_set_credentials(ctx, cred) != 0)
goto done;
if (challenge_len < 4) {
@@ -162,7 +162,7 @@ static int ntlm_next_token(
return error;
}
-static int ntlm_is_complete(git_http_auth_context *c)
+static int ntlmclient_is_complete(git_http_auth_context *c)
{
http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
@@ -170,7 +170,7 @@ static int ntlm_is_complete(git_http_auth_context *c)
return (ctx->complete == true);
}
-static void ntlm_context_free(git_http_auth_context *c)
+static void ntlmclient_context_free(git_http_auth_context *c)
{
http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
@@ -179,7 +179,7 @@ static void ntlm_context_free(git_http_auth_context *c)
git__free(ctx);
}
-static int ntlm_init_context(
+static int ntlmclient_init_context(
http_auth_ntlm_context *ctx,
const git_net_url *url)
{
@@ -206,7 +206,7 @@ int git_http_auth_ntlm(
ctx = git__calloc(1, sizeof(http_auth_ntlm_context));
GIT_ERROR_CHECK_ALLOC(ctx);
- if (ntlm_init_context(ctx, url) < 0) {
+ if (ntlmclient_init_context(ctx, url) < 0) {
git__free(ctx);
return -1;
}
@@ -214,10 +214,10 @@ int git_http_auth_ntlm(
ctx->parent.type = GIT_HTTP_AUTH_NTLM;
ctx->parent.credtypes = GIT_CREDENTIAL_USERPASS_PLAINTEXT;
ctx->parent.connection_affinity = 1;
- ctx->parent.set_challenge = ntlm_set_challenge;
- ctx->parent.next_token = ntlm_next_token;
- ctx->parent.is_complete = ntlm_is_complete;
- ctx->parent.free = ntlm_context_free;
+ ctx->parent.set_challenge = ntlmclient_set_challenge;
+ ctx->parent.next_token = ntlmclient_next_token;
+ ctx->parent.is_complete = ntlmclient_is_complete;
+ ctx->parent.free = ntlmclient_context_free;
*out = (git_http_auth_context *)ctx;
diff --git a/src/libgit2/transports/auth_sspi.c b/src/libgit2/transports/auth_sspi.c
new file mode 100644
index 00000000000..f8269365d7f
--- /dev/null
+++ b/src/libgit2/transports/auth_sspi.c
@@ -0,0 +1,341 @@
+/*
+ * 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 "auth_ntlm.h"
+#include "auth_negotiate.h"
+
+#ifdef GIT_WIN32
+
+#define SECURITY_WIN32
+
+#include "git2.h"
+#include "auth.h"
+#include "git2/sys/credential.h"
+
+#include
+#include
+
+typedef struct {
+ git_http_auth_context parent;
+ wchar_t *target;
+
+ const char *package_name;
+ size_t package_name_len;
+ wchar_t *package_name_w;
+ SecPkgInfoW *package_info;
+ SEC_WINNT_AUTH_IDENTITY_W identity;
+ CredHandle cred;
+ CtxtHandle context;
+
+ int has_identity : 1,
+ has_credentials : 1,
+ has_context : 1,
+ complete : 1;
+ git_str challenge;
+} http_auth_sspi_context;
+
+static void sspi_reset_context(http_auth_sspi_context *ctx)
+{
+ if (ctx->has_identity) {
+ git__free(ctx->identity.User);
+ git__free(ctx->identity.Domain);
+ git__free(ctx->identity.Password);
+
+ memset(&ctx->identity, 0, sizeof(SEC_WINNT_AUTH_IDENTITY_W));
+
+ ctx->has_identity = 0;
+ }
+
+ if (ctx->has_credentials) {
+ FreeCredentialsHandle(&ctx->cred);
+ memset(&ctx->cred, 0, sizeof(CredHandle));
+
+ ctx->has_credentials = 0;
+ }
+
+ if (ctx->has_context) {
+ DeleteSecurityContext(&ctx->context);
+ memset(&ctx->context, 0, sizeof(CtxtHandle));
+
+ ctx->has_context = 0;
+ }
+
+ ctx->complete = 0;
+
+ git_str_dispose(&ctx->challenge);
+}
+
+static int sspi_set_challenge(
+ git_http_auth_context *c,
+ const char *challenge)
+{
+ http_auth_sspi_context *ctx = (http_auth_sspi_context *)c;
+ size_t challenge_len = strlen(challenge);
+
+ git_str_clear(&ctx->challenge);
+
+ if (strncmp(challenge, ctx->package_name, ctx->package_name_len) != 0) {
+ git_error_set(GIT_ERROR_NET, "invalid %s challenge from server", ctx->package_name);
+ return -1;
+ }
+
+ /*
+ * A package type indicator without a base64 payload indicates the
+ * mechanism; it's not an actual challenge. Ignore it.
+ */
+ if (challenge[ctx->package_name_len] == 0) {
+ return 0;
+ } else if (challenge[ctx->package_name_len] != ' ') {
+ git_error_set(GIT_ERROR_NET, "invalid %s challenge from server", ctx->package_name);
+ return -1;
+ }
+
+ if (git_str_decode_base64(&ctx->challenge,
+ challenge + (ctx->package_name_len + 1),
+ challenge_len - (ctx->package_name_len + 1)) < 0) {
+ git_error_set(GIT_ERROR_NET, "invalid %s challenge from server", ctx->package_name);
+ return -1;
+ }
+
+ GIT_ASSERT(ctx->challenge.size <= ULONG_MAX);
+ return 0;
+}
+
+static int create_identity(
+ SEC_WINNT_AUTH_IDENTITY_W **out,
+ http_auth_sspi_context *ctx,
+ git_credential *cred)
+{
+ git_credential_userpass_plaintext *userpass;
+ wchar_t *username = NULL, *domain = NULL, *password = NULL;
+ int username_len = 0, domain_len = 0, password_len = 0;
+ const char *sep;
+
+ if (cred->credtype == GIT_CREDENTIAL_DEFAULT) {
+ *out = NULL;
+ return 0;
+ }
+
+ if (cred->credtype != GIT_CREDENTIAL_USERPASS_PLAINTEXT) {
+ git_error_set(GIT_ERROR_NET, "unknown credential type: %d", cred->credtype);
+ return -1;
+ }
+
+ userpass = (git_credential_userpass_plaintext *)cred;
+
+ if ((sep = strchr(userpass->username, '\\')) != NULL) {
+ GIT_ASSERT(sep - userpass->username < INT_MAX);
+
+ username_len = git_utf8_to_16_alloc(&username, sep + 1);
+ domain_len = git_utf8_to_16_alloc_with_len(&domain,
+ userpass->username, (int)(sep - userpass->username));
+ } else {
+ username_len = git_utf8_to_16_alloc(&username,
+ userpass->username);
+ }
+
+ password_len = git_utf8_to_16_alloc(&password, userpass->password);
+
+ if (username_len < 0 || domain_len < 0 || password_len < 0) {
+ git__free(username);
+ git__free(domain);
+ git__free(password);
+ return -1;
+ }
+
+ ctx->identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+ ctx->identity.User = username;
+ ctx->identity.UserLength = (unsigned long)username_len;
+ ctx->identity.Password = password;
+ ctx->identity.PasswordLength = (unsigned long)password_len;
+ ctx->identity.Domain = domain;
+ ctx->identity.DomainLength = (unsigned long)domain_len;
+
+ ctx->has_identity = 1;
+
+ *out = &ctx->identity;
+
+ return 0;
+}
+
+static int sspi_next_token(
+ git_str *buf,
+ git_http_auth_context *c,
+ git_credential *cred)
+{
+ http_auth_sspi_context *ctx = (http_auth_sspi_context *)c;
+ SEC_WINNT_AUTH_IDENTITY_W *identity = NULL;
+ TimeStamp timestamp;
+ DWORD context_flags;
+ SecBuffer input_buf = { 0, SECBUFFER_TOKEN, NULL };
+ SecBuffer output_buf = { 0, SECBUFFER_TOKEN, NULL };
+ SecBufferDesc input_buf_desc = { SECBUFFER_VERSION, 1, &input_buf };
+ SecBufferDesc output_buf_desc = { SECBUFFER_VERSION, 1, &output_buf };
+ SECURITY_STATUS status;
+
+ if (ctx->complete)
+ sspi_reset_context(ctx);
+
+ if (!ctx->has_context) {
+ if (create_identity(&identity, ctx, cred) < 0)
+ return -1;
+
+ status = AcquireCredentialsHandleW(NULL, ctx->package_name_w,
+ SECPKG_CRED_BOTH, NULL, identity, NULL,
+ NULL, &ctx->cred, ×tamp);
+
+ if (status != SEC_E_OK) {
+ git_error_set(GIT_ERROR_OS, "could not acquire credentials");
+ return -1;
+ }
+
+ ctx->has_credentials = 1;
+ }
+
+ context_flags = ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_MUTUAL_AUTH;
+
+ if (ctx->challenge.size > 0) {
+ input_buf.BufferType = SECBUFFER_TOKEN;
+ input_buf.cbBuffer = (unsigned long)ctx->challenge.size;
+ input_buf.pvBuffer = ctx->challenge.ptr;
+ }
+
+ status = InitializeSecurityContextW(&ctx->cred,
+ ctx->has_context ? &ctx->context : NULL,
+ ctx->target,
+ context_flags,
+ 0,
+ SECURITY_NETWORK_DREP,
+ ctx->has_context ? &input_buf_desc : NULL,
+ 0,
+ ctx->has_context ? NULL : &ctx->context,
+ &output_buf_desc,
+ &context_flags,
+ NULL);
+
+ if (status == SEC_I_COMPLETE_AND_CONTINUE ||
+ status == SEC_I_COMPLETE_NEEDED)
+ status = CompleteAuthToken(&ctx->context, &output_buf_desc);
+
+ if (status == SEC_E_OK) {
+ ctx->complete = 1;
+ } else if (status != SEC_I_CONTINUE_NEEDED) {
+ git_error_set(GIT_ERROR_OS, "could not initialize security context");
+ return -1;
+ }
+
+ ctx->has_context = 1;
+ git_str_clear(&ctx->challenge);
+
+ if (output_buf.cbBuffer > 0) {
+ git_str_put(buf, ctx->package_name, ctx->package_name_len);
+ git_str_putc(buf, ' ');
+ git_str_encode_base64(buf, output_buf.pvBuffer, output_buf.cbBuffer);
+
+ FreeContextBuffer(output_buf.pvBuffer);
+
+ if (git_str_oom(buf))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int sspi_is_complete(git_http_auth_context *c)
+{
+ http_auth_sspi_context *ctx = (http_auth_sspi_context *)c;
+
+ return ctx->complete;
+}
+
+static void sspi_context_free(git_http_auth_context *c)
+{
+ http_auth_sspi_context *ctx = (http_auth_sspi_context *)c;
+
+ sspi_reset_context(ctx);
+
+ FreeContextBuffer(ctx->package_info);
+ git__free(ctx->target);
+ git__free(ctx);
+}
+
+static int sspi_init_context(
+ git_http_auth_context **out,
+ git_http_auth_t type,
+ const git_net_url *url)
+{
+ http_auth_sspi_context *ctx;
+ git_str target = GIT_STR_INIT;
+
+ *out = NULL;
+
+ ctx = git__calloc(1, sizeof(http_auth_sspi_context));
+ GIT_ERROR_CHECK_ALLOC(ctx);
+
+ switch (type) {
+ case GIT_HTTP_AUTH_NTLM:
+ ctx->package_name = "NTLM";
+ ctx->package_name_len = CONST_STRLEN("NTLM");
+ ctx->package_name_w = L"NTLM";
+ ctx->parent.credtypes = GIT_CREDENTIAL_USERPASS_PLAINTEXT |
+ GIT_CREDENTIAL_DEFAULT;
+ break;
+ case GIT_HTTP_AUTH_NEGOTIATE:
+ ctx->package_name = "Negotiate";
+ ctx->package_name_len = CONST_STRLEN("Negotiate");
+ ctx->package_name_w = L"Negotiate";
+ ctx->parent.credtypes = GIT_CREDENTIAL_DEFAULT;
+ break;
+ default:
+ git_error_set(GIT_ERROR_NET, "unknown SSPI auth type: %d", ctx->parent.type);
+ git__free(ctx);
+ return -1;
+ }
+
+ if (QuerySecurityPackageInfoW(ctx->package_name_w, &ctx->package_info) != SEC_E_OK) {
+ git_error_set(GIT_ERROR_OS, "could not query security package");
+ git__free(ctx);
+ return -1;
+ }
+
+ if (git_str_printf(&target, "http/%s", url->host) < 0 ||
+ git_utf8_to_16_alloc(&ctx->target, target.ptr) < 0) {
+ FreeContextBuffer(ctx->package_info);
+ git__free(ctx);
+ return -1;
+ }
+
+ ctx->parent.type = type;
+ ctx->parent.connection_affinity = 1;
+ ctx->parent.set_challenge = sspi_set_challenge;
+ ctx->parent.next_token = sspi_next_token;
+ ctx->parent.is_complete = sspi_is_complete;
+ ctx->parent.free = sspi_context_free;
+
+ *out = (git_http_auth_context *)ctx;
+
+ git_str_dispose(&target);
+ return 0;
+}
+
+int git_http_auth_negotiate(
+ git_http_auth_context **out,
+ const git_net_url *url)
+{
+ return sspi_init_context(out, GIT_HTTP_AUTH_NEGOTIATE, url);
+}
+
+int git_http_auth_ntlm(
+ git_http_auth_context **out,
+ const git_net_url *url)
+{
+ return sspi_init_context(out, GIT_HTTP_AUTH_NTLM, url);
+}
+
+#endif /* GIT_WIN32 */
diff --git a/src/libgit2/transports/credential.c b/src/libgit2/transports/credential.c
index 6e00b028243..b47bd63a198 100644
--- a/src/libgit2/transports/credential.c
+++ b/src/libgit2/transports/credential.c
@@ -204,7 +204,7 @@ int git_credential_ssh_key_memory_new(
const char *privatekey,
const char *passphrase)
{
-#ifdef GIT_SSH_MEMORY_CREDENTIALS
+#ifdef GIT_SSH_LIBSSH2_MEMORY_CREDENTIALS
return git_credential_ssh_key_type_new(
cred,
username,
diff --git a/src/libgit2/transports/git.c b/src/libgit2/transports/git.c
index 591e2ab0352..53611f2a7a6 100644
--- a/src/libgit2/transports/git.c
+++ b/src/libgit2/transports/git.c
@@ -7,7 +7,7 @@
#include "common.h"
-#include "netops.h"
+#include "net.h"
#include "stream.h"
#include "streams/socket.h"
#include "git2/sys/transport.h"
@@ -95,22 +95,21 @@ static int git_proto_stream_read(
size_t buf_size,
size_t *bytes_read)
{
- int error;
git_proto_stream *s = (git_proto_stream *)stream;
- gitno_buffer buf;
+ ssize_t ret;
+ int error;
*bytes_read = 0;
if (!s->sent_command && (error = send_command(s)) < 0)
return error;
- gitno_buffer_setup_fromstream(s->io, &buf, buffer, buf_size);
+ ret = git_stream_read(s->io, buffer, min(buf_size, INT_MAX));
- if ((error = gitno_recv(&buf)) < 0)
- return error;
-
- *bytes_read = buf.offset;
+ if (ret < 0)
+ return -1;
+ *bytes_read = (size_t)ret;
return 0;
}
diff --git a/src/libgit2/transports/http.c b/src/libgit2/transports/http.c
index 5a0edd5df53..8d6f0e9ef8e 100644
--- a/src/libgit2/transports/http.c
+++ b/src/libgit2/transports/http.c
@@ -9,9 +9,7 @@
#if !defined(GIT_WINHTTP) && !defined(__EMSCRIPTEN__)
-#include "http_parser.h"
#include "net.h"
-#include "netops.h"
#include "remote.h"
#include "smart.h"
#include "auth.h"
@@ -335,10 +333,16 @@ static int lookup_proxy(
return 0;
}
- if (!proxy ||
- (error = git_net_url_parse(&transport->proxy.url, proxy)) < 0)
+ if (!proxy || !*proxy ||
+ (error = git_net_url_parse_http(&transport->proxy.url, proxy)) < 0)
goto done;
+ if (!git_net_url_valid(&transport->proxy.url)) {
+ git_error_set(GIT_ERROR_HTTP, "invalid URL: '%s'", proxy);
+ error = -1;
+ goto done;
+ }
+
*out_use = true;
done:
diff --git a/src/libgit2/transports/http.h b/src/libgit2/transports/http.h
index 8e8e7226ed2..7410202a820 100644
--- a/src/libgit2/transports/http.h
+++ b/src/libgit2/transports/http.h
@@ -15,14 +15,4 @@
extern bool git_http__expect_continue;
-GIT_INLINE(int) git_http__user_agent(git_str *buf)
-{
- const char *ua = git_libgit2__user_agent();
-
- if (!ua)
- ua = "libgit2 " LIBGIT2_VERSION;
-
- return git_str_printf(buf, "git/2.0 (%s)", ua);
-}
-
#endif
diff --git a/src/libgit2/transports/httpclient.c b/src/libgit2/transports/httpclient.c
index 0ad6cd4dcb9..a0c4002e806 100644
--- a/src/libgit2/transports/httpclient.c
+++ b/src/libgit2/transports/httpclient.c
@@ -7,7 +7,7 @@
#include "common.h"
#include "git2.h"
-#include "http_parser.h"
+
#include "vector.h"
#include "trace.h"
#include "httpclient.h"
@@ -21,6 +21,7 @@
#include "streams/socket.h"
#include "streams/tls.h"
#include "auth.h"
+#include "httpparser.h"
static git_http_auth_scheme auth_schemes[] = {
{ GIT_HTTP_AUTH_NEGOTIATE, "Negotiate", GIT_CREDENTIAL_DEFAULT, git_http_auth_negotiate },
@@ -108,7 +109,7 @@ struct git_http_client {
git_http_server_t current_server;
http_client_state state;
- http_parser parser;
+ git_http_parser parser;
git_http_server server;
git_http_server proxy;
@@ -154,7 +155,7 @@ void git_http_response_dispose(git_http_response *response)
memset(response, 0, sizeof(git_http_response));
}
-static int on_header_complete(http_parser *parser)
+static int on_header_complete(git_http_parser *parser)
{
http_parser_context *ctx = (http_parser_context *) parser->data;
git_http_client *client = ctx->client;
@@ -219,7 +220,7 @@ static int on_header_complete(http_parser *parser)
return 0;
}
-static int on_header_field(http_parser *parser, const char *str, size_t len)
+static int on_header_field(git_http_parser *parser, const char *str, size_t len)
{
http_parser_context *ctx = (http_parser_context *) parser->data;
@@ -254,7 +255,7 @@ static int on_header_field(http_parser *parser, const char *str, size_t len)
return 0;
}
-static int on_header_value(http_parser *parser, const char *str, size_t len)
+static int on_header_value(git_http_parser *parser, const char *str, size_t len)
{
http_parser_context *ctx = (http_parser_context *) parser->data;
@@ -342,7 +343,7 @@ static int resend_needed(git_http_client *client, git_http_response *response)
return 0;
}
-static int on_headers_complete(http_parser *parser)
+static int on_headers_complete(git_http_parser *parser)
{
http_parser_context *ctx = (http_parser_context *) parser->data;
@@ -364,8 +365,8 @@ static int on_headers_complete(http_parser *parser)
return ctx->parse_status = PARSE_STATUS_ERROR;
}
- ctx->response->status = parser->status_code;
- ctx->client->keepalive = http_should_keep_alive(parser);
+ ctx->response->status = git_http_parser_status_code(parser);
+ ctx->client->keepalive = git_http_parser_keep_alive(parser);
/* Prepare for authentication */
collect_authinfo(&ctx->response->server_auth_schemetypes,
@@ -378,18 +379,15 @@ static int on_headers_complete(http_parser *parser)
ctx->response->resend_credentials = resend_needed(ctx->client,
ctx->response);
- /* Stop parsing. */
- http_parser_pause(parser, 1);
-
if (ctx->response->content_type || ctx->response->chunked)
ctx->client->state = READING_BODY;
else
ctx->client->state = DONE;
- return 0;
+ return git_http_parser_pause(parser);
}
-static int on_body(http_parser *parser, const char *buf, size_t len)
+static int on_body(git_http_parser *parser, const char *buf, size_t len)
{
http_parser_context *ctx = (http_parser_context *) parser->data;
size_t max_len;
@@ -411,7 +409,7 @@ static int on_body(http_parser *parser, const char *buf, size_t len)
return 0;
}
-static int on_message_complete(http_parser *parser)
+static int on_message_complete(git_http_parser *parser)
{
http_parser_context *ctx = (http_parser_context *) parser->data;
@@ -651,6 +649,30 @@ static int puts_host_and_port(git_str *buf, git_net_url *url, bool force_port)
return git_str_oom(buf) ? -1 : 0;
}
+static int append_user_agent(git_str *buf)
+{
+ const char *product = git_settings__user_agent_product();
+ const char *comment = git_settings__user_agent();
+
+ GIT_ASSERT(product && comment);
+
+ if (!*product)
+ return 0;
+
+ git_str_puts(buf, "User-Agent: ");
+ git_str_puts(buf, product);
+
+ if (*comment) {
+ git_str_puts(buf, " (");
+ git_str_puts(buf, comment);
+ git_str_puts(buf, ")");
+ }
+
+ git_str_puts(buf, "\r\n");
+
+ return git_str_oom(buf) ? -1 : 0;
+}
+
static int generate_connect_request(
git_http_client *client,
git_http_request *request)
@@ -665,9 +687,7 @@ static int generate_connect_request(
puts_host_and_port(buf, &client->server.url, true);
git_str_puts(buf, " HTTP/1.1\r\n");
- git_str_puts(buf, "User-Agent: ");
- git_http__user_agent(buf);
- git_str_puts(buf, "\r\n");
+ append_user_agent(buf);
git_str_puts(buf, "Host: ");
puts_host_and_port(buf, &client->server.url, true);
@@ -711,9 +731,7 @@ static int generate_request(
git_str_puts(buf, " HTTP/1.1\r\n");
- git_str_puts(buf, "User-Agent: ");
- git_http__user_agent(buf);
- git_str_puts(buf, "\r\n");
+ append_user_agent(buf);
git_str_puts(buf, "Host: ");
puts_host_and_port(buf, request->url, false);
@@ -768,25 +786,37 @@ static int check_certificate(
void *cert_cb_payload)
{
git_cert *cert;
- git_error_state last_error = {0};
+ git_error *last_error;
int error;
if ((error = git_stream_certificate(&cert, stream)) < 0)
return error;
- git_error_state_capture(&last_error, GIT_ECERTIFICATE);
+ /*
+ * Allow callers to set an error - but save ours and clear
+ * it, so that we can detect if they set one and restore it
+ * if we need to.
+ */
+ git_error_save(&last_error);
+ git_error_clear();
error = cert_cb(cert, is_valid, url->host, cert_cb_payload);
- if (error == GIT_PASSTHROUGH && !is_valid)
- return git_error_state_restore(&last_error);
- else if (error == GIT_PASSTHROUGH)
- error = 0;
- else if (error && !git_error_last())
- git_error_set(GIT_ERROR_HTTP,
- "user rejected certificate for %s", url->host);
+ if (error == GIT_PASSTHROUGH) {
+ error = is_valid ? 0 : -1;
- git_error_state_free(&last_error);
+ if (error) {
+ git_error_restore(last_error);
+ last_error = NULL;
+ }
+ } else if (error) {
+ if (!git_error_exists())
+ git_error_set(GIT_ERROR_HTTP,
+ "user rejected certificate for %s",
+ url->host);
+ }
+
+ git_error_free(last_error);
return error;
}
@@ -837,6 +867,11 @@ GIT_INLINE(int) server_setup_from_url(
git_http_server *server,
git_net_url *url)
{
+ GIT_ASSERT_ARG(url);
+ GIT_ASSERT_ARG(url->scheme);
+ GIT_ASSERT_ARG(url->host);
+ GIT_ASSERT_ARG(url->port);
+
if (!server->url.scheme || strcmp(server->url.scheme, url->scheme) ||
!server->url.host || strcmp(server->url.host, url->host) ||
!server->url.port || strcmp(server->url.port, url->port)) {
@@ -859,9 +894,29 @@ GIT_INLINE(int) server_setup_from_url(
return 0;
}
+static bool parser_settings_initialized;
+static git_http_parser_settings parser_settings;
+
+GIT_INLINE(git_http_parser_settings *) http_client_parser_settings(void)
+{
+ if (!parser_settings_initialized) {
+ parser_settings.on_header_field = on_header_field;
+ parser_settings.on_header_value = on_header_value;
+ parser_settings.on_headers_complete = on_headers_complete;
+ parser_settings.on_body = on_body;
+ parser_settings.on_message_complete = on_message_complete;
+
+ parser_settings_initialized = true;
+ }
+
+ return &parser_settings;
+}
+
static void reset_parser(git_http_client *client)
{
- http_parser_init(&client->parser, HTTP_RESPONSE);
+ git_http_parser_init(&client->parser,
+ GIT_HTTP_PARSER_RESPONSE,
+ http_client_parser_settings());
}
static int setup_hosts(
@@ -1104,27 +1159,9 @@ GIT_INLINE(int) client_read(git_http_client *client)
return (int)read_len;
}
-static bool parser_settings_initialized;
-static http_parser_settings parser_settings;
-
-GIT_INLINE(http_parser_settings *) http_client_parser_settings(void)
-{
- if (!parser_settings_initialized) {
- parser_settings.on_header_field = on_header_field;
- parser_settings.on_header_value = on_header_value;
- parser_settings.on_headers_complete = on_headers_complete;
- parser_settings.on_body = on_body;
- parser_settings.on_message_complete = on_message_complete;
-
- parser_settings_initialized = true;
- }
-
- return &parser_settings;
-}
-
GIT_INLINE(int) client_read_and_parse(git_http_client *client)
{
- http_parser *parser = &client->parser;
+ git_http_parser *parser = &client->parser;
http_parser_context *ctx = (http_parser_context *) parser->data;
unsigned char http_errno;
int read_len;
@@ -1138,11 +1175,10 @@ GIT_INLINE(int) client_read_and_parse(git_http_client *client)
if (!client->read_buf.size && (read_len = client_read(client)) < 0)
return read_len;
- parsed_len = http_parser_execute(parser,
- http_client_parser_settings(),
+ parsed_len = git_http_parser_execute(parser,
client->read_buf.ptr,
client->read_buf.size);
- http_errno = client->parser.http_errno;
+ http_errno = git_http_parser_errno(parser);
if (parsed_len > INT_MAX) {
git_error_set(GIT_ERROR_HTTP, "unexpectedly large parse");
@@ -1161,26 +1197,29 @@ GIT_INLINE(int) client_read_and_parse(git_http_client *client)
* (This can happen in response to an expect/continue request,
* where the server gives you a 100 and 200 simultaneously.)
*/
- if (http_errno == HPE_PAUSED) {
+ if (http_errno == GIT_HTTP_PARSER_PAUSED) {
+ size_t additional_size;
+
+ git_http_parser_resume(parser);
+
/*
- * http-parser has a "feature" where it will not deliver the
- * final byte when paused in a callback. Consume that byte.
- * https://github.com/nodejs/http-parser/issues/97
+ * http-parser has a "feature" where it will not deliver
+ * the final byte when paused in a callback. Consume
+ * that byte.
*/
- GIT_ASSERT(client->read_buf.size > parsed_len);
+ if ((additional_size = git_http_parser_remain_after_pause(parser)) > 0) {
+ GIT_ASSERT((client->read_buf.size - parsed_len) >= additional_size);
- http_parser_pause(parser, 0);
-
- parsed_len += http_parser_execute(parser,
- http_client_parser_settings(),
- client->read_buf.ptr + parsed_len,
- 1);
+ parsed_len += git_http_parser_execute(parser,
+ client->read_buf.ptr + parsed_len,
+ additional_size);
+ }
}
/* Most failures will be reported in http_errno */
- else if (parser->http_errno != HPE_OK) {
+ else if (git_http_parser_errno(parser) != GIT_HTTP_PARSER_OK) {
git_error_set(GIT_ERROR_HTTP, "http parser error: %s",
- http_errno_description(http_errno));
+ git_http_parser_errmsg(parser, http_errno));
return -1;
}
@@ -1188,7 +1227,7 @@ GIT_INLINE(int) client_read_and_parse(git_http_client *client)
else if (parsed_len != client->read_buf.size) {
git_error_set(GIT_ERROR_HTTP,
"http parser did not consume entire buffer: %s",
- http_errno_description(http_errno));
+ git_http_parser_errmsg(parser, http_errno));
return -1;
}
@@ -1227,7 +1266,7 @@ static void complete_response_body(git_http_client *client)
/* If there was an error, just close the connection. */
if (client_read_and_parse(client) < 0 ||
- parser_context.error != HPE_OK ||
+ parser_context.error != GIT_HTTP_PARSER_OK ||
(parser_context.parse_status != PARSE_STATUS_OK &&
parser_context.parse_status != PARSE_STATUS_NO_OUTPUT)) {
git_error_clear();
@@ -1235,6 +1274,7 @@ static void complete_response_body(git_http_client *client)
}
done:
+ client->parser.data = NULL;
git_str_clear(&client->read_buf);
}
@@ -1424,6 +1464,7 @@ int git_http_client_read_response(
done:
git_str_dispose(&parser_context.parse_header_name);
git_str_dispose(&parser_context.parse_header_value);
+ client->parser.data = NULL;
return error;
}
@@ -1479,6 +1520,8 @@ int git_http_client_read_body(
if (error < 0)
client->connected = 0;
+ client->parser.data = NULL;
+
return error;
}
@@ -1501,7 +1544,7 @@ int git_http_client_skip_body(git_http_client *client)
do {
error = client_read_and_parse(client);
- if (parser_context.error != HPE_OK ||
+ if (parser_context.error != GIT_HTTP_PARSER_OK ||
(parser_context.parse_status != PARSE_STATUS_OK &&
parser_context.parse_status != PARSE_STATUS_NO_OUTPUT)) {
git_error_set(GIT_ERROR_HTTP,
@@ -1513,6 +1556,8 @@ int git_http_client_skip_body(git_http_client *client)
if (error < 0)
client->connected = 0;
+ client->parser.data = NULL;
+
return error;
}
diff --git a/src/libgit2/transports/httpparser.c b/src/libgit2/transports/httpparser.c
new file mode 100644
index 00000000000..84833e61737
--- /dev/null
+++ b/src/libgit2/transports/httpparser.c
@@ -0,0 +1,128 @@
+/*
+ * 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 "httpparser.h"
+
+#include
+
+#if defined(GIT_HTTPPARSER_HTTPPARSER)
+
+#include "http_parser.h"
+
+static int on_message_begin(http_parser *p)
+{
+ git_http_parser *parser = (git_http_parser *)p;
+ return parser->settings.on_message_begin(parser);
+}
+
+static int on_url(http_parser *p, const char *str, size_t len)
+{
+ git_http_parser *parser = (git_http_parser *)p;
+ return parser->settings.on_url(parser, str, len);
+}
+
+static int on_header_field(http_parser *p, const char *str, size_t len)
+{
+ git_http_parser *parser = (git_http_parser *)p;
+ return parser->settings.on_header_field(parser, str, len);
+}
+
+static int on_header_value(http_parser *p, const char *str, size_t len)
+{
+ git_http_parser *parser = (git_http_parser *)p;
+ return parser->settings.on_header_value(parser, str, len);
+}
+
+static int on_headers_complete(http_parser *p)
+{
+ git_http_parser *parser = (git_http_parser *)p;
+ return parser->settings.on_headers_complete(parser);
+}
+
+static int on_body(http_parser *p, const char *buf, size_t len)
+{
+ git_http_parser *parser = (git_http_parser *)p;
+ return parser->settings.on_body(parser, buf, len);
+}
+
+static int on_message_complete(http_parser *p)
+{
+ git_http_parser *parser = (git_http_parser *)p;
+ return parser->settings.on_message_complete(parser);
+}
+
+void git_http_parser_init(
+ git_http_parser *parser,
+ git_http_parser_t type,
+ git_http_parser_settings *settings)
+{
+ http_parser_init(&parser->parser, (enum http_parser_type)type);
+ memcpy(&parser->settings, settings, sizeof(git_http_parser_settings));
+}
+
+size_t git_http_parser_execute(
+ git_http_parser *parser,
+ const char *data,
+ size_t len)
+{
+ struct http_parser_settings settings_proxy;
+
+ memset(&settings_proxy, 0, sizeof(struct http_parser_settings));
+
+ settings_proxy.on_message_begin = parser->settings.on_message_begin ? on_message_begin : NULL;
+ settings_proxy.on_url = parser->settings.on_url ? on_url : NULL;
+ settings_proxy.on_header_field = parser->settings.on_header_field ? on_header_field : NULL;
+ settings_proxy.on_header_value = parser->settings.on_header_value ? on_header_value : NULL;
+ settings_proxy.on_headers_complete = parser->settings.on_headers_complete ? on_headers_complete : NULL;
+ settings_proxy.on_body = parser->settings.on_body ? on_body : NULL;
+ settings_proxy.on_message_complete = parser->settings.on_message_complete ? on_message_complete : NULL;
+
+ return http_parser_execute(&parser->parser, &settings_proxy, data, len);
+}
+
+#elif defined(GIT_HTTPPARSER_LLHTTP) || defined(GIT_HTTPPARSER_BUILTIN)
+
+# include
+
+size_t git_http_parser_execute(
+ git_http_parser *parser,
+ const char* data,
+ size_t len)
+{
+ llhttp_errno_t error;
+ size_t parsed_len;
+
+ /*
+ * Unlike http_parser, which returns the number of parsed
+ * bytes in the _execute() call, llhttp returns an error
+ * code.
+ */
+
+ if (data == NULL || len == 0)
+ error = llhttp_finish(parser);
+ else
+ error = llhttp_execute(parser, data, len);
+
+ parsed_len = len;
+
+ /*
+ * Adjust number of parsed bytes in case of error.
+ */
+ if (error != HPE_OK) {
+ parsed_len = llhttp_get_error_pos(parser) - data;
+
+ /* This isn't a real pause, just a way to stop parsing early. */
+ if (error == HPE_PAUSED_UPGRADE)
+ llhttp_resume_after_upgrade(parser);
+ }
+
+ return parsed_len;
+}
+
+#else
+# error unknown http-parser
+#endif
diff --git a/src/libgit2/transports/httpparser.h b/src/libgit2/transports/httpparser.h
new file mode 100644
index 00000000000..1fe0dcf6406
--- /dev/null
+++ b/src/libgit2/transports/httpparser.h
@@ -0,0 +1,99 @@
+/*
+ * 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_transports_httpparser_h__
+#define INCLUDE_transports_httpparser_h__
+
+#include "git2_util.h"
+
+#if defined(GIT_HTTPPARSER_HTTPPARSER)
+
+# include
+
+typedef enum {
+ GIT_HTTP_PARSER_OK = HPE_OK,
+ GIT_HTTP_PARSER_PAUSED = HPE_PAUSED,
+} git_http_parser_error_t;
+
+typedef enum {
+ GIT_HTTP_PARSER_REQUEST = HTTP_REQUEST,
+ GIT_HTTP_PARSER_RESPONSE = HTTP_RESPONSE,
+} git_http_parser_t;
+
+typedef struct git_http_parser git_http_parser;
+
+typedef struct {
+ int (*on_message_begin)(git_http_parser *);
+ int (*on_url)(git_http_parser *, const char *, size_t);
+ int (*on_header_field)(git_http_parser *, const char *, size_t);
+ int (*on_header_value)(git_http_parser *, const char *, size_t);
+ int (*on_headers_complete)(git_http_parser *);
+ int (*on_body)(git_http_parser *, const char *, size_t);
+ int (*on_message_complete)(git_http_parser *);
+} git_http_parser_settings;
+
+struct git_http_parser {
+ http_parser parser;
+ git_http_parser_settings settings;
+ void *data;
+};
+
+void git_http_parser_init(
+ git_http_parser *parser,
+ git_http_parser_t type,
+ git_http_parser_settings *settings);
+
+size_t git_http_parser_execute(
+ git_http_parser *parser,
+ const char *data,
+ size_t len);
+
+# define git_http_parser_status_code(parser) parser->parser.status_code
+# define git_http_parser_keep_alive(parser) http_should_keep_alive(&parser->parser)
+# define git_http_parser_pause(parser) (http_parser_pause(&parser->parser, 1), 0)
+# define git_http_parser_resume(parser) http_parser_pause(&parser->parser, 0)
+# define git_http_parser_remain_after_pause(parser) 1
+# define git_http_parser_errno(parser) parser->parser.http_errno
+# define git_http_parser_errmsg(parser, errno) http_errno_description(errno)
+
+#elif defined(GIT_HTTPPARSER_LLHTTP) || defined(GIT_HTTPPARSER_BUILTIN)
+
+# include
+
+typedef enum {
+ GIT_HTTP_PARSER_OK = HPE_OK,
+ GIT_HTTP_PARSER_PAUSED = HPE_PAUSED,
+} git_http_parser_error_t;
+
+typedef enum {
+ GIT_HTTP_PARSER_REQUEST = HTTP_REQUEST,
+ GIT_HTTP_PARSER_RESPONSE = HTTP_RESPONSE,
+} git_http_parser_t;
+
+typedef llhttp_t git_http_parser;
+typedef llhttp_settings_t git_http_parser_settings;
+
+# define git_http_parser_init(parser, direction, settings) llhttp_init(parser, (llhttp_type_t)direction, settings)
+
+size_t git_http_parser_execute(
+ git_http_parser *parser,
+ const char *data,
+ size_t len);
+
+# define git_http_parser_status_code(parser) parser->status_code
+# define git_http_parser_keep_alive(parser) llhttp_should_keep_alive(parser)
+# define git_http_parser_pause(parser) (llhttp_pause(parser), GIT_HTTP_PARSER_PAUSED)
+# define git_http_parser_resume(parser) llhttp_resume(parser)
+# define git_http_parser_remain_after_pause(parser) 0
+# define git_http_parser_errno(parser) parser->error
+# define git_http_parser_errmsg(parser, errno) llhttp_get_error_reason(parser)
+
+#else
+# error unknown http-parser
+#endif
+
+#endif
diff --git a/src/libgit2/transports/local.c b/src/libgit2/transports/local.c
index 6c754a034a4..68ff1c1c12c 100644
--- a/src/libgit2/transports/local.c
+++ b/src/libgit2/transports/local.c
@@ -266,6 +266,17 @@ static int local_capabilities(unsigned int *capabilities, git_transport *transpo
return 0;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+static int local_oid_type(git_oid_t *out, git_transport *transport)
+{
+ transport_local *t = (transport_local *)transport;
+
+ *out = t->repo->oid_type;
+
+ return 0;
+}
+#endif
+
static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_local *t = (transport_local *)transport;
@@ -284,15 +295,18 @@ static int local_ls(const git_remote_head ***out, size_t *size, git_transport *t
static int local_negotiate_fetch(
git_transport *transport,
git_repository *repo,
- const git_remote_head * const *refs,
- size_t count)
+ const git_fetch_negotiation *wants)
{
transport_local *t = (transport_local*)transport;
git_remote_head *rhead;
unsigned int i;
- GIT_UNUSED(refs);
- GIT_UNUSED(count);
+ GIT_UNUSED(wants);
+
+ if (wants->depth) {
+ git_error_set(GIT_ERROR_NET, "shallow fetch is not supported by the local transport");
+ return GIT_ENOTSUPPORTED;
+ }
/* Fill in the loids */
git_vector_foreach(&t->refs, i, rhead) {
@@ -311,6 +325,16 @@ static int local_negotiate_fetch(
return 0;
}
+static int local_shallow_roots(
+ git_oidarray *out,
+ git_transport *transport)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(transport);
+
+ return 0;
+}
+
static int local_push_update_remote_ref(
git_repository *remote_repo,
const char *lref,
@@ -434,7 +458,7 @@ static int local_push(
default:
last = git_error_last();
- if (last && last->message)
+ if (last->klass != GIT_ERROR_NONE)
status->msg = git__strdup(last->message);
else
status->msg = git__strdup("Unspecified error encountered");
@@ -732,7 +756,11 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param)
t->parent.connect = local_connect;
t->parent.set_connect_opts = local_set_connect_opts;
t->parent.capabilities = local_capabilities;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ t->parent.oid_type = local_oid_type;
+#endif
t->parent.negotiate_fetch = local_negotiate_fetch;
+ t->parent.shallow_roots = local_shallow_roots;
t->parent.download_pack = local_download_pack;
t->parent.push = local_push;
t->parent.close = local_close;
diff --git a/src/libgit2/transports/smart.c b/src/libgit2/transports/smart.c
index 7f57dba2a42..be0cb7b05e4 100644
--- a/src/libgit2/transports/smart.c
+++ b/src/libgit2/transports/smart.c
@@ -13,30 +13,42 @@
#include "refspec.h"
#include "proxy.h"
-static int git_smart__recv_cb(gitno_buffer *buf)
+int git_smart__recv(transport_smart *t)
{
- transport_smart *t = (transport_smart *) buf->cb_data;
- size_t old_len, bytes_read;
- int error;
+ size_t bytes_read;
+ int ret;
+ GIT_ASSERT_ARG(t);
GIT_ASSERT(t->current_stream);
- old_len = buf->offset;
+ if (git_staticstr_remain(&t->buffer) == 0) {
+ git_error_set(GIT_ERROR_NET, "out of buffer space");
+ return -1;
+ }
+
+ ret = t->current_stream->read(t->current_stream,
+ git_staticstr_offset(&t->buffer),
+ git_staticstr_remain(&t->buffer),
+ &bytes_read);
- if ((error = t->current_stream->read(t->current_stream, buf->data + buf->offset, buf->len - buf->offset, &bytes_read)) < 0)
- return error;
+ if (ret < 0)
+ return ret;
- buf->offset += bytes_read;
+ GIT_ASSERT(bytes_read <= INT_MAX);
+ GIT_ASSERT(bytes_read <= git_staticstr_remain(&t->buffer));
+
+ git_staticstr_increase(&t->buffer, bytes_read);
if (t->packetsize_cb && !t->cancelled.val) {
- error = t->packetsize_cb(bytes_read, t->packetsize_payload);
- if (error) {
+ ret = t->packetsize_cb(bytes_read, t->packetsize_payload);
+
+ if (ret) {
git_atomic32_set(&t->cancelled, 1);
return GIT_EUSER;
}
}
- return (int)(buf->offset - old_len);
+ return (int)bytes_read;
}
GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransport)
@@ -54,6 +66,12 @@ GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransp
return -1;
}
+ git__free(t->caps.object_format);
+ t->caps.object_format = NULL;
+
+ git__free(t->caps.agent);
+ t->caps.agent = NULL;
+
return 0;
}
@@ -149,8 +167,6 @@ static int git_smart__connect(
/* Save off the current stream (i.e. socket) that we are working with */
t->current_stream = stream;
- gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
-
/* 2 flushes for RPC; 1 for stateful */
if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0)
return error;
@@ -233,6 +249,9 @@ static int git_smart__capabilities(unsigned int *capabilities, git_transport *tr
*capabilities = 0;
+ if (t->caps.push_options)
+ *capabilities |= GIT_REMOTE_CAPABILITY_PUSH_OPTIONS;
+
if (t->caps.want_tip_sha1)
*capabilities |= GIT_REMOTE_CAPABILITY_TIP_OID;
@@ -242,6 +261,30 @@ static int git_smart__capabilities(unsigned int *capabilities, git_transport *tr
return 0;
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+static int git_smart__oid_type(git_oid_t *out, git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ *out = 0;
+
+ if (t->caps.object_format == NULL) {
+ *out = GIT_OID_DEFAULT;
+ } else {
+ *out = git_oid_type_fromstr(t->caps.object_format);
+
+ if (!*out) {
+ git_error_set(GIT_ERROR_INVALID,
+ "unknown object format '%s'",
+ t->caps.object_format);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+#endif
+
static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
@@ -283,8 +326,6 @@ int git_smart__negotiation_step(git_transport *transport, void *data, size_t len
if ((error = stream->write(stream, (const char *)data, len)) < 0)
return error;
- gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
-
return 0;
}
@@ -309,8 +350,6 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream
/* Save off the current stream (i.e. socket) that we are working with */
t->current_stream = *stream;
- gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
-
return 0;
}
@@ -334,17 +373,27 @@ static int git_smart__close(git_transport *transport)
git_vector *common = &t->common;
unsigned int i;
git_pkt *p;
+ git_smart_service_t service;
int ret;
git_smart_subtransport_stream *stream;
const char flush[] = "0000";
+ if (t->direction == GIT_DIRECTION_FETCH) {
+ service = GIT_SERVICE_UPLOADPACK;
+ } else if (t->direction == GIT_DIRECTION_PUSH) {
+ service = GIT_SERVICE_RECEIVEPACK;
+ } else {
+ git_error_set(GIT_ERROR_NET, "invalid direction");
+ return -1;
+ }
+
/*
* If we're still connected at this point and not using RPC,
* we should say goodbye by sending a flush, or git-daemon
* will complain that we disconnected unexpectedly.
*/
if (t->connected && !t->rpc &&
- !t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) {
+ !t->wrapped->action(&stream, t->wrapped, t->url, service)) {
t->current_stream->write(t->current_stream, flush, 4);
}
@@ -386,6 +435,10 @@ static void git_smart__free(git_transport *transport)
git_remote_connect_options_dispose(&t->connect_opts);
+ git_array_dispose(t->shallow_roots);
+
+ git__free(t->caps.object_format);
+ git__free(t->caps.agent);
git__free(t);
}
@@ -452,9 +505,13 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
t->parent.connect = git_smart__connect;
t->parent.set_connect_opts = git_smart__set_connect_opts;
t->parent.capabilities = git_smart__capabilities;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ t->parent.oid_type = git_smart__oid_type;
+#endif
t->parent.close = git_smart__close;
t->parent.free = git_smart__free;
t->parent.negotiate_fetch = git_smart__negotiate_fetch;
+ t->parent.shallow_roots = git_smart__shallow_roots;
t->parent.download_pack = git_smart__download_pack;
t->parent.push = git_smart__push;
t->parent.ls = git_smart__ls;
@@ -464,20 +521,16 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
t->owner = owner;
t->rpc = definition->rpc;
- if (git_vector_init(&t->refs, 16, ref_name_cmp) < 0) {
- git__free(t);
- return -1;
- }
-
- if (git_vector_init(&t->heads, 16, ref_name_cmp) < 0) {
+ if (git_vector_init(&t->refs, 16, ref_name_cmp) < 0 ||
+ git_vector_init(&t->heads, 16, ref_name_cmp) < 0 ||
+ definition->callback(&t->wrapped, &t->parent, definition->param) < 0) {
+ git_vector_free(&t->refs);
+ git_vector_free(&t->heads);
git__free(t);
return -1;
}
- if (definition->callback(&t->wrapped, &t->parent, definition->param) < 0) {
- git__free(t);
- return -1;
- }
+ git_staticstr_init(&t->buffer, GIT_SMART_BUFFER_SIZE);
*out = (git_transport *) t;
return 0;
diff --git a/src/libgit2/transports/smart.h b/src/libgit2/transports/smart.h
index 9323d6c444e..c987d93b53d 100644
--- a/src/libgit2/transports/smart.h
+++ b/src/libgit2/transports/smart.h
@@ -11,11 +11,14 @@
#include "git2.h"
#include "vector.h"
-#include "netops.h"
#include "push.h"
#include "str.h"
+#include "oidarray.h"
+#include "staticstr.h"
#include "git2/sys/transport.h"
+#define GIT_SMART_BUFFER_SIZE 65536
+
#define GIT_SIDE_BAND_DATA 1
#define GIT_SIDE_BAND_PROGRESS 2
#define GIT_SIDE_BAND_ERROR 3
@@ -32,6 +35,10 @@
#define GIT_CAP_SYMREF "symref"
#define GIT_CAP_WANT_TIP_SHA1 "allow-tip-sha1-in-want"
#define GIT_CAP_WANT_REACHABLE_SHA1 "allow-reachable-sha1-in-want"
+#define GIT_CAP_SHALLOW "shallow"
+#define GIT_CAP_OBJECT_FORMAT "object-format="
+#define GIT_CAP_AGENT "agent="
+#define GIT_CAP_PUSH_OPTIONS "push-options"
extern bool git_smart__ofs_delta_enabled;
@@ -48,7 +55,9 @@ typedef enum {
GIT_PKT_PROGRESS,
GIT_PKT_OK,
GIT_PKT_NG,
- GIT_PKT_UNPACK
+ GIT_PKT_UNPACK,
+ GIT_PKT_SHALLOW,
+ GIT_PKT_UNSHALLOW
} git_pkt_type;
/* Used for multi_ack and multi_ack_detailed */
@@ -120,6 +129,11 @@ typedef struct {
int unpack_ok;
} git_pkt_unpack;
+typedef struct {
+ git_pkt_type type;
+ git_oid oid;
+} git_pkt_shallow;
+
typedef struct transport_smart_caps {
unsigned int common:1,
ofs_delta:1,
@@ -132,7 +146,11 @@ typedef struct transport_smart_caps {
report_status:1,
thin_pack:1,
want_tip_sha1:1,
- want_reachable_sha1:1;
+ want_reachable_sha1:1,
+ shallow:1,
+ push_options:1;
+ char *object_format;
+ char *agent;
} transport_smart_caps;
typedef int (*packetsize_cb)(size_t received, void *payload);
@@ -149,14 +167,14 @@ typedef struct {
git_vector refs;
git_vector heads;
git_vector common;
+ git_array_oid_t shallow_roots;
git_atomic32 cancelled;
packetsize_cb packetsize_cb;
void *packetsize_payload;
unsigned rpc : 1,
have_refs : 1,
connected : 1;
- gitno_buffer buffer;
- char buffer_data[65536];
+ git_staticstr_with_size(GIT_SMART_BUFFER_SIZE) buffer;
} transport_smart;
/* smart_protocol.c */
@@ -167,8 +185,9 @@ int git_smart__push(git_transport *transport, git_push *push);
int git_smart__negotiate_fetch(
git_transport *transport,
git_repository *repo,
- const git_remote_head * const *refs,
- size_t count);
+ const git_fetch_negotiation *wants);
+
+int git_smart__shallow_roots(git_oidarray *out, git_transport *transport);
int git_smart__download_pack(
git_transport *transport,
@@ -176,17 +195,24 @@ int git_smart__download_pack(
git_indexer_progress *stats);
/* smart.c */
+int git_smart__recv(transport_smart *t);
+
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out);
int git_smart__update_heads(transport_smart *t, git_vector *symrefs);
/* smart_pkt.c */
-int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, size_t linelen);
+typedef struct {
+ git_oid_t oid_type;
+ unsigned int seen_capabilities: 1;
+} git_pkt_parse_data;
+
+int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, size_t linelen, git_pkt_parse_data *data);
int git_pkt_buffer_flush(git_str *buf);
int git_pkt_send_flush(GIT_SOCKET s);
int git_pkt_buffer_done(git_str *buf);
-int git_pkt_buffer_wants(const git_remote_head * const *refs, size_t count, transport_smart_caps *caps, git_str *buf);
+int git_pkt_buffer_wants(const git_fetch_negotiation *wants, transport_smart_caps *caps, git_str *buf);
int git_pkt_buffer_have(git_oid *oid, git_str *buf);
void git_pkt_free(git_pkt *pkt);
diff --git a/src/libgit2/transports/smart_pkt.c b/src/libgit2/transports/smart_pkt.c
index e679819fa15..7ea8676e966 100644
--- a/src/libgit2/transports/smart_pkt.c
+++ b/src/libgit2/transports/smart_pkt.c
@@ -9,7 +9,6 @@
#include "smart.h"
#include "util.h"
-#include "netops.h"
#include "posix.h"
#include "str.h"
#include "oid.h"
@@ -21,11 +20,14 @@
#include
-#define PKT_LEN_SIZE 4
-static const char pkt_done_str[] = "0009done\n";
-static const char pkt_flush_str[] = "0000";
-static const char pkt_have_prefix[] = "0032have ";
-static const char pkt_want_prefix[] = "0032want ";
+#define PKT_DONE_STR "0009done\n"
+#define PKT_FLUSH_STR "0000"
+#define PKT_HAVE_PREFIX "have "
+#define PKT_WANT_PREFIX "want "
+
+#define PKT_LEN_SIZE 4
+#define PKT_MAX_SIZE 0xffff
+#define PKT_MAX_WANTLEN (PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) + GIT_OID_MAX_HEXSIZE + 1)
static int flush_pkt(git_pkt **out)
{
@@ -41,9 +43,16 @@ static int flush_pkt(git_pkt **out)
}
/* the rest of the line will be useful for multi_ack and multi_ack_detailed */
-static int ack_pkt(git_pkt **out, const char *line, size_t len)
+static int ack_pkt(
+ git_pkt **out,
+ const char *line,
+ size_t len,
+ git_pkt_parse_data *data)
{
git_pkt_ack *pkt;
+ size_t oid_hexsize = git_oid_hexsize(data->oid_type);
+
+ GIT_ASSERT(data && data->oid_type);
pkt = git__calloc(1, sizeof(git_pkt_ack));
GIT_ERROR_CHECK_ALLOC(pkt);
@@ -54,11 +63,11 @@ static int ack_pkt(git_pkt **out, const char *line, size_t len)
line += 4;
len -= 4;
- if (len < GIT_OID_SHA1_HEXSIZE ||
- git_oid__fromstr(&pkt->oid, line, GIT_OID_SHA1) < 0)
+ if (len < oid_hexsize ||
+ git_oid__fromstr(&pkt->oid, line, data->oid_type) < 0)
goto out_err;
- line += GIT_OID_SHA1_HEXSIZE;
- len -= GIT_OID_SHA1_HEXSIZE;
+ line += oid_hexsize;
+ len -= oid_hexsize;
if (len && line[0] == ' ') {
line++;
@@ -212,26 +221,88 @@ static int sideband_error_pkt(git_pkt **out, const char *line, size_t len)
return 0;
}
+static int set_data(
+ git_pkt_parse_data *data,
+ const char *line,
+ size_t len)
+{
+ const char *caps, *format_str = NULL, *eos;
+ size_t format_len;
+ git_oid_t remote_oid_type;
+
+ GIT_ASSERT_ARG(data);
+
+ if ((caps = memchr(line, '\0', len)) != NULL &&
+ len > (size_t)((caps - line) + 1)) {
+ caps++;
+
+ if (strncmp(caps, "object-format=", CONST_STRLEN("object-format=")) == 0)
+ format_str = caps + CONST_STRLEN("object-format=");
+ else if ((format_str = strstr(caps, " object-format=")) != NULL)
+ format_str += CONST_STRLEN(" object-format=");
+ }
+
+ if (format_str) {
+ if ((eos = strchr(format_str, ' ')) == NULL)
+ eos = strchr(format_str, '\0');
+
+ GIT_ASSERT(eos);
+
+ format_len = eos - format_str;
+
+ if ((remote_oid_type = git_oid_type_fromstrn(format_str, format_len)) == 0) {
+ git_error_set(GIT_ERROR_INVALID, "unknown remote object format '%.*s'", (int)format_len, format_str);
+ return -1;
+ }
+ } else {
+ remote_oid_type = GIT_OID_SHA1;
+ }
+
+ if (!data->oid_type) {
+ data->oid_type = remote_oid_type;
+ } else if (data->oid_type != remote_oid_type) {
+ git_error_set(GIT_ERROR_INVALID,
+ "the local object format '%s' does not match the remote object format '%s'",
+ git_oid_type_name(data->oid_type),
+ git_oid_type_name(remote_oid_type));
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* Parse an other-ref line.
*/
-static int ref_pkt(git_pkt **out, const char *line, size_t len)
+static int ref_pkt(
+ git_pkt **out,
+ const char *line,
+ size_t len,
+ git_pkt_parse_data *data)
{
git_pkt_ref *pkt;
- size_t alloclen;
+ size_t alloclen, oid_hexsize;
pkt = git__calloc(1, sizeof(git_pkt_ref));
GIT_ERROR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_REF;
- if (len < GIT_OID_SHA1_HEXSIZE ||
- git_oid__fromstr(&pkt->head.oid, line, GIT_OID_SHA1) < 0)
+ /* Determine OID type from capabilities */
+ if (!data->seen_capabilities && set_data(data, line, len) < 0)
+ return -1;
+
+ GIT_ASSERT(data->oid_type);
+ oid_hexsize = git_oid_hexsize(data->oid_type);
+
+ if (len < oid_hexsize ||
+ git_oid__fromstr(&pkt->head.oid, line, data->oid_type) < 0)
goto out_err;
- line += GIT_OID_SHA1_HEXSIZE;
- len -= GIT_OID_SHA1_HEXSIZE;
+ line += oid_hexsize;
+ len -= oid_hexsize;
if (git__prefixncmp(line, len, " "))
goto out_err;
+
line++;
len--;
@@ -248,8 +319,14 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len)
memcpy(pkt->head.name, line, len);
pkt->head.name[len] = '\0';
- if (strlen(pkt->head.name) < len)
- pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
+ if (strlen(pkt->head.name) < len) {
+ if (!data->seen_capabilities)
+ pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
+ else
+ goto out_err;
+ }
+
+ data->seen_capabilities = 1;
*out = (git_pkt *)pkt;
return 0;
@@ -366,6 +443,84 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len)
return 0;
}
+static int shallow_pkt(
+ git_pkt **out,
+ const char *line,
+ size_t len,
+ git_pkt_parse_data *data)
+{
+ git_pkt_shallow *pkt;
+ size_t oid_hexsize = git_oid_hexsize(data->oid_type);
+
+ GIT_ASSERT(data && data->oid_type);
+
+ pkt = git__calloc(1, sizeof(git_pkt_shallow));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_SHALLOW;
+
+ if (git__prefixncmp(line, len, "shallow "))
+ goto out_err;
+
+ line += 8;
+ len -= 8;
+
+ if (len != oid_hexsize)
+ goto out_err;
+
+ git_oid__fromstr(&pkt->oid, line, data->oid_type);
+ line += oid_hexsize + 1;
+ len -= oid_hexsize + 1;
+
+ *out = (git_pkt *)pkt;
+
+ return 0;
+
+out_err:
+ git_error_set(GIT_ERROR_NET, "invalid packet line");
+ git__free(pkt);
+ return -1;
+}
+
+static int unshallow_pkt(
+ git_pkt **out,
+ const char *line,
+ size_t len,
+ git_pkt_parse_data *data)
+{
+ git_pkt_shallow *pkt;
+ size_t oid_hexsize = git_oid_hexsize(data->oid_type);
+
+ GIT_ASSERT(data && data->oid_type);
+
+ pkt = git__calloc(1, sizeof(git_pkt_shallow));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_UNSHALLOW;
+
+ if (git__prefixncmp(line, len, "unshallow "))
+ goto out_err;
+
+ line += 10;
+ len -= 10;
+
+ if (len != oid_hexsize)
+ goto out_err;
+
+ git_oid__fromstr(&pkt->oid, line, data->oid_type);
+ line += oid_hexsize + 1;
+ len -= oid_hexsize + 1;
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
+
+out_err:
+ git_error_set(GIT_ERROR_NET, "invalid packet line");
+ git__free(pkt);
+ return -1;
+}
+
static int parse_len(size_t *out, const char *line, size_t linelen)
{
char num[PKT_LEN_SIZE + 1];
@@ -381,10 +536,10 @@ static int parse_len(size_t *out, const char *line, size_t linelen)
num[PKT_LEN_SIZE] = '\0';
for (i = 0; i < PKT_LEN_SIZE; ++i) {
- if (!isxdigit(num[i])) {
+ if (!git__isxdigit(num[i])) {
/* Make sure there are no special characters before passing to error message */
for (k = 0; k < PKT_LEN_SIZE; ++k) {
- if(!isprint(num[k])) {
+ if(!git__isprint(num[k])) {
num[k] = '.';
}
}
@@ -418,7 +573,11 @@ static int parse_len(size_t *out, const char *line, size_t linelen)
*/
int git_pkt_parse_line(
- git_pkt **pkt, const char **endptr, const char *line, size_t linelen)
+ git_pkt **pkt,
+ const char **endptr,
+ const char *line,
+ size_t linelen,
+ git_pkt_parse_data *data)
{
int error;
size_t len;
@@ -479,7 +638,7 @@ int git_pkt_parse_line(
else if (*line == GIT_SIDE_BAND_ERROR)
error = sideband_error_pkt(pkt, line, len);
else if (!git__prefixncmp(line, len, "ACK"))
- error = ack_pkt(pkt, line, len);
+ error = ack_pkt(pkt, line, len, data);
else if (!git__prefixncmp(line, len, "NAK"))
error = nak_pkt(pkt);
else if (!git__prefixncmp(line, len, "ERR"))
@@ -492,8 +651,12 @@ int git_pkt_parse_line(
error = ng_pkt(pkt, line, len);
else if (!git__prefixncmp(line, len, "unpack"))
error = unpack_pkt(pkt, line, len);
+ else if (!git__prefixcmp(line, "shallow"))
+ error = shallow_pkt(pkt, line, len, data);
+ else if (!git__prefixcmp(line, "unshallow"))
+ error = unshallow_pkt(pkt, line, len, data);
else
- error = ref_pkt(pkt, line, len);
+ error = ref_pkt(pkt, line, len, data);
*endptr = line + len;
@@ -527,14 +690,21 @@ void git_pkt_free(git_pkt *pkt)
int git_pkt_buffer_flush(git_str *buf)
{
- return git_str_put(buf, pkt_flush_str, strlen(pkt_flush_str));
+ return git_str_put(buf, PKT_FLUSH_STR, CONST_STRLEN(PKT_FLUSH_STR));
}
-static int buffer_want_with_caps(const git_remote_head *head, transport_smart_caps *caps, git_str *buf)
+static int buffer_want_with_caps(
+ const git_remote_head *head,
+ transport_smart_caps *caps,
+ git_oid_t oid_type,
+ git_str *buf)
{
git_str str = GIT_STR_INIT;
- char oid[GIT_OID_SHA1_HEXSIZE +1] = {0};
- size_t len;
+ char oid[GIT_OID_MAX_HEXSIZE];
+ size_t oid_hexsize, len;
+
+ oid_hexsize = git_oid_hexsize(oid_type);
+ git_oid_fmt(oid, &head->oid);
/* Prefer multi_ack_detailed */
if (caps->multi_ack_detailed)
@@ -557,22 +727,26 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
if (caps->ofs_delta)
git_str_puts(&str, GIT_CAP_OFS_DELTA " ");
+ if (caps->shallow)
+ git_str_puts(&str, GIT_CAP_SHALLOW " ");
+
if (git_str_oom(&str))
return -1;
- len = strlen("XXXXwant ") + GIT_OID_SHA1_HEXSIZE + 1 /* NUL */ +
- git_str_len(&str) + 1 /* LF */;
-
- if (len > 0xffff) {
+ if (str.size > (PKT_MAX_SIZE - (PKT_MAX_WANTLEN + 1))) {
git_error_set(GIT_ERROR_NET,
- "tried to produce packet with invalid length %" PRIuZ, len);
+ "tried to produce packet with invalid caps length %" PRIuZ, str.size);
return -1;
}
+ len = PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) +
+ oid_hexsize + 1 /* NUL */ +
+ git_str_len(&str) + 1 /* LF */;
+
git_str_grow_by(buf, len);
- git_oid_fmt(oid, &head->oid);
git_str_printf(buf,
- "%04xwant %s %s\n", (unsigned int)len, oid, git_str_cstr(&str));
+ "%04x%s%.*s %s\n", (unsigned int)len, PKT_WANT_PREFIX,
+ (int)oid_hexsize, oid, git_str_cstr(&str));
git_str_dispose(&str);
GIT_ERROR_CHECK_ALLOC_STR(buf);
@@ -586,38 +760,81 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
*/
int git_pkt_buffer_wants(
- const git_remote_head * const *refs,
- size_t count,
+ const git_fetch_negotiation *wants,
transport_smart_caps *caps,
git_str *buf)
{
- size_t i = 0;
const git_remote_head *head;
+ char oid[GIT_OID_MAX_HEXSIZE];
+ git_oid_t oid_type;
+ size_t oid_hexsize, want_len, i = 0;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid_type = wants->refs_len > 0 ? wants->refs[0]->oid.type : GIT_OID_SHA1;
+#else
+ oid_type = GIT_OID_SHA1;
+#endif
+
+ oid_hexsize = git_oid_hexsize(oid_type);
+
+ want_len = PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) +
+ oid_hexsize + 1 /* LF */;
if (caps->common) {
- for (; i < count; ++i) {
- head = refs[i];
+ for (; i < wants->refs_len; ++i) {
+ head = wants->refs[i];
if (!head->local)
break;
}
- if (buffer_want_with_caps(refs[i], caps, buf) < 0)
+ if (buffer_want_with_caps(wants->refs[i], caps, oid_type, buf) < 0)
return -1;
i++;
}
- for (; i < count; ++i) {
- char oid[GIT_OID_SHA1_HEXSIZE];
+ for (; i < wants->refs_len; ++i) {
+ head = wants->refs[i];
- head = refs[i];
if (head->local)
continue;
git_oid_fmt(oid, &head->oid);
- git_str_put(buf, pkt_want_prefix, strlen(pkt_want_prefix));
- git_str_put(buf, oid, GIT_OID_SHA1_HEXSIZE);
- git_str_putc(buf, '\n');
+
+ git_str_printf(buf, "%04x%s%.*s\n",
+ (unsigned int)want_len, PKT_WANT_PREFIX,
+ (int)oid_hexsize, oid);
+
+ if (git_str_oom(buf))
+ return -1;
+ }
+
+ /* Tell the server about our shallow objects */
+ for (i = 0; i < wants->shallow_roots_len; i++) {
+ char oid[GIT_OID_MAX_HEXSIZE + 1];
+ git_str shallow_buf = GIT_STR_INIT;
+
+ git_oid_tostr(oid, GIT_OID_MAX_HEXSIZE + 1, &wants->shallow_roots[i]);
+ git_str_puts(&shallow_buf, "shallow ");
+ git_str_puts(&shallow_buf, oid);
+ git_str_putc(&shallow_buf, '\n');
+
+ git_str_printf(buf, "%04x%s", (unsigned int)git_str_len(&shallow_buf) + 4, git_str_cstr(&shallow_buf));
+
+ git_str_dispose(&shallow_buf);
+
+ if (git_str_oom(buf))
+ return -1;
+ }
+
+ if (wants->depth > 0) {
+ git_str deepen_buf = GIT_STR_INIT;
+
+ git_str_printf(&deepen_buf, "deepen %d\n", wants->depth);
+ git_str_printf(buf,"%04x%s", (unsigned int)git_str_len(&deepen_buf) + 4, git_str_cstr(&deepen_buf));
+
+ git_str_dispose(&deepen_buf);
+
if (git_str_oom(buf))
return -1;
}
@@ -627,14 +844,27 @@ int git_pkt_buffer_wants(
int git_pkt_buffer_have(git_oid *oid, git_str *buf)
{
- char oidhex[GIT_OID_SHA1_HEXSIZE + 1];
-
- memset(oidhex, 0x0, sizeof(oidhex));
- git_oid_fmt(oidhex, oid);
- return git_str_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);
+ char oid_str[GIT_OID_MAX_HEXSIZE];
+ git_oid_t oid_type;
+ size_t oid_hexsize, have_len;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid_type = oid->type;
+#else
+ oid_type = GIT_OID_SHA1;
+#endif
+
+ oid_hexsize = git_oid_hexsize(oid_type);
+ have_len = PKT_LEN_SIZE + CONST_STRLEN(PKT_HAVE_PREFIX) +
+ oid_hexsize + 1 /* LF */;
+
+ git_oid_fmt(oid_str, oid);
+ return git_str_printf(buf, "%04x%s%.*s\n",
+ (unsigned int)have_len, PKT_HAVE_PREFIX,
+ (int)oid_hexsize, oid_str);
}
int git_pkt_buffer_done(git_str *buf)
{
- return git_str_puts(buf, pkt_done_str);
+ return git_str_put(buf, PKT_DONE_STR, CONST_STRLEN(PKT_DONE_STR));
}
diff --git a/src/libgit2/transports/smart_protocol.c b/src/libgit2/transports/smart_protocol.c
index 88c208fb5d0..df1c190c30e 100644
--- a/src/libgit2/transports/smart_protocol.c
+++ b/src/libgit2/transports/smart_protocol.c
@@ -27,11 +27,11 @@ bool git_smart__ofs_delta_enabled = true;
int git_smart__store_refs(transport_smart *t, int flushes)
{
- gitno_buffer *buf = &t->buffer;
git_vector *refs = &t->refs;
int error, flush = 0, recvd;
const char *line_end = NULL;
git_pkt *pkt = NULL;
+ git_pkt_parse_data pkt_parse_data = { 0 };
size_t i;
/* Clear existing refs in case git_remote_connect() is called again
@@ -44,8 +44,10 @@ int git_smart__store_refs(transport_smart *t, int flushes)
pkt = NULL;
do {
- if (buf->offset > 0)
- error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset);
+ if (t->buffer.len > 0)
+ error = git_pkt_parse_line(&pkt, &line_end,
+ t->buffer.data, t->buffer.len,
+ &pkt_parse_data);
else
error = GIT_EBUFS;
@@ -53,19 +55,18 @@ int git_smart__store_refs(transport_smart *t, int flushes)
return error;
if (error == GIT_EBUFS) {
- if ((recvd = gitno_recv(buf)) < 0)
+ if ((recvd = git_smart__recv(t)) < 0)
return recvd;
if (recvd == 0) {
- git_error_set(GIT_ERROR_NET, "early EOF");
+ git_error_set(GIT_ERROR_NET, "could not read refs from remote repository");
return GIT_EEOF;
}
continue;
}
- if (gitno_consume(buf, line_end) < 0)
- return -1;
+ git_staticstr_consume(&t->buffer, line_end);
if (pkt->type == GIT_PKT_ERR) {
git_error_set(GIT_ERROR_NET, "remote error: %s", ((git_pkt_err *)pkt)->error);
@@ -133,9 +134,12 @@ static int append_symref(const char **out, git_vector *symrefs, const char *ptr)
return -1;
}
-int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
+int git_smart__detect_caps(
+ git_pkt_ref *pkt,
+ transport_smart_caps *caps,
+ git_vector *symrefs)
{
- const char *ptr;
+ const char *ptr, *start;
/* No refs or capabilities, odd but not a problem */
if (pkt == NULL || pkt->capabilities == NULL)
@@ -190,6 +194,12 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec
continue;
}
+ if (!git__prefixcmp(ptr, GIT_CAP_PUSH_OPTIONS)) {
+ caps->common = caps->push_options = 1;
+ ptr += strlen(GIT_CAP_PUSH_OPTIONS);
+ continue;
+ }
+
if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) {
caps->common = caps->thin_pack = 1;
ptr += strlen(GIT_CAP_THIN_PACK);
@@ -217,6 +227,34 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec
continue;
}
+ if (!git__prefixcmp(ptr, GIT_CAP_OBJECT_FORMAT)) {
+ ptr += strlen(GIT_CAP_OBJECT_FORMAT);
+
+ start = ptr;
+ ptr = strchr(ptr, ' ');
+
+ if ((caps->object_format = git__strndup(start, (ptr - start))) == NULL)
+ return -1;
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_AGENT)) {
+ ptr += strlen(GIT_CAP_AGENT);
+
+ start = ptr;
+ ptr = strchr(ptr, ' ');
+
+ if ((caps->agent = git__strndup(start, (ptr - start))) == NULL)
+ return -1;
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_SHALLOW)) {
+ caps->common = caps->shallow = 1;
+ ptr += strlen(GIT_CAP_SHALLOW);
+ continue;
+ }
+
/* We don't know this capability, so skip it */
ptr = strchr(ptr, ' ');
}
@@ -224,15 +262,23 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec
return 0;
}
-static int recv_pkt(git_pkt **out_pkt, git_pkt_type *out_type, gitno_buffer *buf)
+static int recv_pkt(
+ git_pkt **out_pkt,
+ git_pkt_type *out_type,
+ transport_smart *t)
{
- const char *ptr = buf->data, *line_end = ptr;
+ const char *ptr = t->buffer.data, *line_end = ptr;
git_pkt *pkt = NULL;
+ git_pkt_parse_data pkt_parse_data = { 0 };
int error = 0, ret;
+ pkt_parse_data.oid_type = t->owner->repo->oid_type;
+ pkt_parse_data.seen_capabilities = 1;
+
do {
- if (buf->offset > 0)
- error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset);
+ if (t->buffer.len > 0)
+ error = git_pkt_parse_line(&pkt, &line_end, ptr,
+ t->buffer.len, &pkt_parse_data);
else
error = GIT_EBUFS;
@@ -242,16 +288,15 @@ static int recv_pkt(git_pkt **out_pkt, git_pkt_type *out_type, gitno_buffer *buf
if (error < 0 && error != GIT_EBUFS)
return error;
- if ((ret = gitno_recv(buf)) < 0) {
+ if ((ret = git_smart__recv(t)) < 0) {
return ret;
} else if (ret == 0) {
- git_error_set(GIT_ERROR_NET, "early EOF");
+ git_error_set(GIT_ERROR_NET, "could not read from remote repository");
return GIT_EEOF;
}
} while (error);
- if (gitno_consume(buf, line_end) < 0)
- return -1;
+ git_staticstr_consume(&t->buffer, line_end);
if (out_type != NULL)
*out_type = pkt->type;
@@ -266,11 +311,10 @@ static int recv_pkt(git_pkt **out_pkt, git_pkt_type *out_type, gitno_buffer *buf
static int store_common(transport_smart *t)
{
git_pkt *pkt = NULL;
- gitno_buffer *buf = &t->buffer;
int error;
do {
- if ((error = recv_pkt(&pkt, NULL, buf)) < 0)
+ if ((error = recv_pkt(&pkt, NULL, t)) < 0)
return error;
if (pkt->type != GIT_PKT_ACK) {
@@ -287,7 +331,7 @@ static int store_common(transport_smart *t)
return 0;
}
-static int wait_while_ack(gitno_buffer *buf)
+static int wait_while_ack(transport_smart *t)
{
int error;
git_pkt *pkt = NULL;
@@ -296,7 +340,7 @@ static int wait_while_ack(gitno_buffer *buf)
while (1) {
git_pkt_free(pkt);
- if ((error = recv_pkt(&pkt, NULL, buf)) < 0)
+ if ((error = recv_pkt(&pkt, NULL, t)) < 0)
return error;
if (pkt->type == GIT_PKT_NAK)
@@ -317,11 +361,51 @@ static int wait_while_ack(gitno_buffer *buf)
return 0;
}
-int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count)
+static int cap_not_sup_err(const char *cap_name)
+{
+ git_error_set(GIT_ERROR_NET, "server doesn't support %s", cap_name);
+ return GIT_EINVALID;
+}
+
+/* Disables server capabilities we're not interested in */
+static int setup_caps(
+ transport_smart_caps *caps,
+ const git_fetch_negotiation *wants)
+{
+ if (wants->depth > 0) {
+ if (!caps->shallow)
+ return cap_not_sup_err(GIT_CAP_SHALLOW);
+ } else {
+ caps->shallow = 0;
+ }
+
+ return 0;
+}
+
+static int setup_shallow_roots(
+ git_array_oid_t *out,
+ const git_fetch_negotiation *wants)
+{
+ git_array_clear(*out);
+
+ if (wants->shallow_roots_len > 0) {
+ git_array_init_to_size(*out, wants->shallow_roots_len);
+ GIT_ERROR_CHECK_ALLOC(out->ptr);
+
+ memcpy(out->ptr, wants->shallow_roots,
+ sizeof(git_oid) * wants->shallow_roots_len);
+ }
+
+ return 0;
+}
+
+int git_smart__negotiate_fetch(
+ git_transport *transport,
+ git_repository *repo,
+ const git_fetch_negotiation *wants)
{
transport_smart *t = (transport_smart *)transport;
git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
- gitno_buffer *buf = &t->buffer;
git_str data = GIT_STR_INIT;
git_revwalk *walk = NULL;
int error = -1;
@@ -329,7 +413,11 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
unsigned int i;
git_oid oid;
- if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
+ if ((error = setup_caps(&t->caps, wants)) < 0 ||
+ (error = setup_shallow_roots(&t->shallow_roots, wants)) < 0)
+ return error;
+
+ if ((error = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
return error;
if ((error = git_revwalk_new(&walk, repo)) < 0)
@@ -339,6 +427,37 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
if ((error = git_revwalk__push_glob(walk, "refs/*", &opts)) < 0)
goto on_error;
+ if (wants->depth > 0) {
+ git_pkt_shallow *pkt;
+
+ if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
+ goto on_error;
+
+ while ((error = recv_pkt((git_pkt **)&pkt, NULL, t)) == 0) {
+ bool complete = false;
+
+ if (pkt->type == GIT_PKT_SHALLOW) {
+ error = git_oidarray__add(&t->shallow_roots, &pkt->oid);
+ } else if (pkt->type == GIT_PKT_UNSHALLOW) {
+ git_oidarray__remove(&t->shallow_roots, &pkt->oid);
+ } else if (pkt->type == GIT_PKT_FLUSH) {
+ /* Server is done, stop processing shallow oids */
+ complete = true;
+ } else {
+ git_error_set(GIT_ERROR_NET, "unexpected packet type");
+ error = -1;
+ }
+
+ git_pkt_free((git_pkt *) pkt);
+
+ if (complete || error < 0)
+ break;
+ }
+
+ if (error < 0)
+ goto on_error;
+ }
+
/*
* Our support for ACK extensions is simply to parse them. On
* the first ACK we will accept that as enough common
@@ -379,7 +498,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
if ((error = store_common(t)) < 0)
goto on_error;
} else {
- if ((error = recv_pkt(NULL, &pkt_type, buf)) < 0)
+ if ((error = recv_pkt(NULL, &pkt_type, t)) < 0)
goto on_error;
if (pkt_type == GIT_PKT_ACK) {
@@ -401,7 +520,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
git_pkt_ack *pkt;
unsigned int j;
- if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
+ if ((error = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
goto on_error;
git_vector_foreach(&t->common, j, pkt) {
@@ -421,7 +540,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
git_pkt_ack *pkt;
unsigned int j;
- if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
+ if ((error = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
goto on_error;
git_vector_foreach(&t->common, j, pkt) {
@@ -439,10 +558,11 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
goto on_error;
if (t->cancelled.val) {
- git_error_set(GIT_ERROR_NET, "The fetch was cancelled by the user");
+ git_error_set(GIT_ERROR_NET, "the fetch was cancelled");
error = GIT_EUSER;
goto on_error;
}
+
if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
goto on_error;
@@ -451,7 +571,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
/* Now let's eat up whatever the server gives us */
if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
- if ((error = recv_pkt(NULL, &pkt_type, buf)) < 0)
+ if ((error = recv_pkt(NULL, &pkt_type, t)) < 0)
return error;
if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
@@ -459,7 +579,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
return -1;
}
} else {
- error = wait_while_ack(buf);
+ error = wait_while_ack(t);
}
return error;
@@ -470,7 +590,29 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
return error;
}
-static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_indexer_progress *stats)
+int git_smart__shallow_roots(git_oidarray *out, git_transport *transport)
+{
+ transport_smart *t = (transport_smart *)transport;
+ size_t len;
+
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&len, t->shallow_roots.size, sizeof(git_oid));
+
+ out->count = t->shallow_roots.size;
+
+ if (len) {
+ out->ids = git__malloc(len);
+ memcpy(out->ids, t->shallow_roots.ptr, len);
+ } else {
+ out->ids = NULL;
+ }
+
+ return 0;
+}
+
+static int no_sideband(
+ transport_smart *t,
+ struct git_odb_writepack *writepack,
+ git_indexer_progress *stats)
{
int recvd;
@@ -480,12 +622,12 @@ static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack,
return GIT_EUSER;
}
- if (writepack->append(writepack, buf->data, buf->offset, stats) < 0)
+ if (writepack->append(writepack, t->buffer.data, t->buffer.len, stats) < 0)
return -1;
- gitno_consume_n(buf, buf->offset);
+ git_staticstr_clear(&t->buffer);
- if ((recvd = gitno_recv(buf)) < 0)
+ if ((recvd = git_smart__recv(t)) < 0)
return recvd;
} while(recvd > 0);
@@ -527,7 +669,6 @@ int git_smart__download_pack(
git_indexer_progress *stats)
{
transport_smart *t = (transport_smart *)transport;
- gitno_buffer *buf = &t->buffer;
git_odb *odb;
struct git_odb_writepack *writepack = NULL;
int error = 0;
@@ -546,9 +687,10 @@ int git_smart__download_pack(
t->packetsize_payload = &npp;
/* We might have something in the buffer already from negotiate_fetch */
- if (t->buffer.offset > 0 && !t->cancelled.val)
- if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
+ if (t->buffer.len > 0 && !t->cancelled.val) {
+ if (t->packetsize_cb(t->buffer.len, t->packetsize_payload))
git_atomic32_set(&t->cancelled, 1);
+ }
}
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
@@ -561,7 +703,7 @@ int git_smart__download_pack(
* check which one belongs there.
*/
if (!t->caps.side_band && !t->caps.side_band_64k) {
- error = no_sideband(t, writepack, buf, stats);
+ error = no_sideband(t, writepack, stats);
goto done;
}
@@ -575,7 +717,7 @@ int git_smart__download_pack(
goto done;
}
- if ((error = recv_pkt(&pkt, NULL, buf)) >= 0) {
+ if ((error = recv_pkt(&pkt, NULL, t)) >= 0) {
/* Check cancellation after network call */
if (t->cancelled.val) {
git_error_clear();
@@ -642,33 +784,54 @@ int git_smart__download_pack(
static int gen_pktline(git_str *buf, git_push *push)
{
push_spec *spec;
+ char *option;
size_t i, len;
- char old_id[GIT_OID_SHA1_HEXSIZE+1], new_id[GIT_OID_SHA1_HEXSIZE+1];
-
- old_id[GIT_OID_SHA1_HEXSIZE] = '\0'; new_id[GIT_OID_SHA1_HEXSIZE] = '\0';
+ char old_id[GIT_OID_MAX_HEXSIZE + 1], new_id[GIT_OID_MAX_HEXSIZE + 1];
+ size_t old_id_len, new_id_len;
git_vector_foreach(&push->specs, i, spec) {
- len = 2*GIT_OID_SHA1_HEXSIZE + 7 + strlen(spec->refspec.dst);
+ len = strlen(spec->refspec.dst) + 7;
if (i == 0) {
- ++len; /* '\0' */
+ /* Need a leading \0 */
+ ++len;
+
if (push->report_status)
len += strlen(GIT_CAP_REPORT_STATUS) + 1;
+
+ if (git_vector_length(&push->remote_push_options) > 0)
+ len += strlen(GIT_CAP_PUSH_OPTIONS) + 1;
+
len += strlen(GIT_CAP_SIDE_BAND_64K) + 1;
}
+ old_id_len = git_oid_hexsize(git_oid_type(&spec->roid));
+ new_id_len = git_oid_hexsize(git_oid_type(&spec->loid));
+
+ len += (old_id_len + new_id_len);
+
git_oid_fmt(old_id, &spec->roid);
+ old_id[old_id_len] = '\0';
+
git_oid_fmt(new_id, &spec->loid);
+ new_id[new_id_len] = '\0';
- git_str_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->refspec.dst);
+ git_str_printf(buf, "%04"PRIxZ"%.*s %.*s %s", len,
+ (int)old_id_len, old_id, (int)new_id_len, new_id,
+ spec->refspec.dst);
if (i == 0) {
git_str_putc(buf, '\0');
+
/* Core git always starts their capabilities string with a space */
if (push->report_status) {
git_str_putc(buf, ' ');
git_str_printf(buf, GIT_CAP_REPORT_STATUS);
}
+ if (git_vector_length(&push->remote_push_options) > 0) {
+ git_str_putc(buf, ' ');
+ git_str_printf(buf, GIT_CAP_PUSH_OPTIONS);
+ }
git_str_putc(buf, ' ');
git_str_printf(buf, GIT_CAP_SIDE_BAND_64K);
}
@@ -676,6 +839,13 @@ static int gen_pktline(git_str *buf, git_push *push)
git_str_putc(buf, '\n');
}
+ if (git_vector_length(&push->remote_push_options) > 0) {
+ git_str_printf(buf, "0000");
+ git_vector_foreach(&push->remote_push_options, i, option) {
+ git_str_printf(buf, "%04"PRIxZ"%s", strlen(option) + 4 , option);
+ }
+ }
+
git_str_puts(buf, "0000");
return git_str_oom(buf) ? -1 : 0;
}
@@ -723,6 +893,7 @@ static int add_push_report_pkt(git_push *push, git_pkt *pkt)
static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, git_str *data_pkt_buf)
{
git_pkt *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
const char *line, *line_end = NULL;
size_t line_len;
int error;
@@ -741,7 +912,7 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt,
}
while (line_len > 0) {
- error = git_pkt_parse_line(&pkt, &line_end, line, line_len);
+ error = git_pkt_parse_line(&pkt, &line_end, line, line_len, &pkt_parse_data);
if (error == GIT_EBUFS) {
/* Buffer the data when the inner packet is split
@@ -777,15 +948,17 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt,
static int parse_report(transport_smart *transport, git_push *push)
{
git_pkt *pkt = NULL;
+ git_pkt_parse_data pkt_parse_data = { 0 };
const char *line_end = NULL;
- gitno_buffer *buf = &transport->buffer;
int error, recvd;
git_str data_pkt_buf = GIT_STR_INIT;
for (;;) {
- if (buf->offset > 0)
+ if (transport->buffer.len > 0)
error = git_pkt_parse_line(&pkt, &line_end,
- buf->data, buf->offset);
+ transport->buffer.data,
+ transport->buffer.len,
+ &pkt_parse_data);
else
error = GIT_EBUFS;
@@ -795,22 +968,20 @@ static int parse_report(transport_smart *transport, git_push *push)
}
if (error == GIT_EBUFS) {
- if ((recvd = gitno_recv(buf)) < 0) {
+ if ((recvd = git_smart__recv(transport)) < 0) {
error = recvd;
goto done;
}
if (recvd == 0) {
- git_error_set(GIT_ERROR_NET, "early EOF");
+ git_error_set(GIT_ERROR_NET, "could not read report from remote repository");
error = GIT_EEOF;
goto done;
}
continue;
}
- if (gitno_consume(buf, line_end) < 0)
- return -1;
-
+ git_staticstr_consume(&transport->buffer, line_end);
error = 0;
switch (pkt->type) {
@@ -975,7 +1146,7 @@ struct push_packbuilder_payload
git_push_transfer_progress_cb cb;
void *cb_payload;
size_t last_bytes;
- double last_progress_report_time;
+ uint64_t last_progress_report_time;
};
static int stream_thunk(void *buf, size_t size, void *data)
@@ -987,11 +1158,11 @@ static int stream_thunk(void *buf, size_t size, void *data)
return error;
if (payload->cb) {
- double current_time = git__timer();
- double elapsed = current_time - payload->last_progress_report_time;
+ uint64_t current_time = git_time_monotonic();
+ uint64_t elapsed = current_time - payload->last_progress_report_time;
payload->last_bytes += size;
- if (elapsed < 0 || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ if (elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
payload->last_progress_report_time = current_time;
error = payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
}
@@ -1020,7 +1191,7 @@ int git_smart__push(git_transport *transport, git_push *push)
#ifdef PUSH_DEBUG
{
git_remote_head *head;
- char hex[GIT_OID_SHA1_HEXSIZE+1]; hex[GIT_OID_SHA1_HEXSIZE] = '\0';
+ char hex[GIT_OID_MAX_HEXSIZE+1], hex[GIT_OID_MAX_HEXSIZE] = '\0';
git_vector_foreach(&push->remote->refs, i, head) {
git_oid_fmt(hex, &head->oid);
diff --git a/src/libgit2/transports/ssh.c b/src/libgit2/transports/ssh.c
index 85e779744d2..3f3a127f256 100644
--- a/src/libgit2/transports/ssh.c
+++ b/src/libgit2/transports/ssh.c
@@ -5,1093 +5,67 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "ssh.h"
+#include "ssh_exec.h"
+#include "ssh_libssh2.h"
-#ifdef GIT_SSH
-#include
-#endif
-
-#include "runtime.h"
-#include "net.h"
-#include "netops.h"
-#include "smart.h"
-#include "streams/socket.h"
-
-#include "git2/credential.h"
-#include "git2/sys/credential.h"
-
-#ifdef GIT_SSH
-
-#define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
-
-static const char cmd_uploadpack[] = "git-upload-pack";
-static const char cmd_receivepack[] = "git-receive-pack";
-
-typedef struct {
- git_smart_subtransport_stream parent;
- git_stream *io;
- LIBSSH2_SESSION *session;
- LIBSSH2_CHANNEL *channel;
- const char *cmd;
- git_net_url url;
- unsigned sent_command : 1;
-} ssh_stream;
-
-typedef struct {
- git_smart_subtransport parent;
- transport_smart *owner;
- ssh_stream *current_stream;
- git_credential *cred;
- char *cmd_uploadpack;
- char *cmd_receivepack;
-} ssh_subtransport;
-
-static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username);
-
-static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
-{
- char *ssherr;
- libssh2_session_last_error(session, &ssherr, NULL, 0);
-
- git_error_set(GIT_ERROR_SSH, "%s: %s", errmsg, ssherr);
-}
-
-/*
- * Create a git protocol request.
- *
- * For example: git-upload-pack '/libgit2/libgit2'
- */
-static int gen_proto(git_str *request, const char *cmd, git_net_url *url)
-{
- const char *repo;
-
- repo = url->path;
-
- if (repo && repo[0] == '/' && repo[1] == '~')
- repo++;
-
- if (!repo || !repo[0]) {
- git_error_set(GIT_ERROR_NET, "malformed git protocol URL");
- return -1;
- }
-
- git_str_puts(request, cmd);
- git_str_puts(request, " '");
- git_str_puts(request, repo);
- git_str_puts(request, "'");
-
- if (git_str_oom(request))
- return -1;
-
- return 0;
-}
-
-static int send_command(ssh_stream *s)
-{
- int error;
- git_str request = GIT_STR_INIT;
-
- error = gen_proto(&request, s->cmd, &s->url);
- if (error < 0)
- goto cleanup;
-
- error = libssh2_channel_exec(s->channel, request.ptr);
- if (error < LIBSSH2_ERROR_NONE) {
- ssh_error(s->session, "SSH could not execute request");
- goto cleanup;
- }
-
- s->sent_command = 1;
-
-cleanup:
- git_str_dispose(&request);
- return error;
-}
-
-static int ssh_stream_read(
- git_smart_subtransport_stream *stream,
- char *buffer,
- size_t buf_size,
- size_t *bytes_read)
-{
- int rc;
- ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent);
-
- *bytes_read = 0;
-
- if (!s->sent_command && send_command(s) < 0)
- return -1;
-
- if ((rc = libssh2_channel_read(s->channel, buffer, buf_size)) < LIBSSH2_ERROR_NONE) {
- ssh_error(s->session, "SSH could not read data");
- return -1;
- }
-
- /*
- * If we can't get anything out of stdout, it's typically a
- * not-found error, so read from stderr and signal EOF on
- * stderr.
- */
- if (rc == 0) {
- if ((rc = libssh2_channel_read_stderr(s->channel, buffer, buf_size)) > 0) {
- git_error_set(GIT_ERROR_SSH, "%*s", rc, buffer);
- return GIT_EEOF;
- } else if (rc < LIBSSH2_ERROR_NONE) {
- ssh_error(s->session, "SSH could not read stderr");
- return -1;
- }
- }
-
-
- *bytes_read = rc;
-
- return 0;
-}
-
-static int ssh_stream_write(
- git_smart_subtransport_stream *stream,
- const char *buffer,
- size_t len)
-{
- ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent);
- size_t off = 0;
- ssize_t ret = 0;
-
- if (!s->sent_command && send_command(s) < 0)
- return -1;
-
- do {
- ret = libssh2_channel_write(s->channel, buffer + off, len - off);
- if (ret < 0)
- break;
-
- off += ret;
-
- } while (off < len);
-
- if (ret < 0) {
- ssh_error(s->session, "SSH could not write data");
- return -1;
- }
-
- return 0;
-}
-
-static void ssh_stream_free(git_smart_subtransport_stream *stream)
-{
- ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent);
- ssh_subtransport *t;
-
- if (!stream)
- return;
-
- t = OWNING_SUBTRANSPORT(s);
- t->current_stream = NULL;
-
- if (s->channel) {
- libssh2_channel_close(s->channel);
- libssh2_channel_free(s->channel);
- s->channel = NULL;
- }
-
- if (s->session) {
- libssh2_session_disconnect(s->session, "closing transport");
- libssh2_session_free(s->session);
- s->session = NULL;
- }
-
- if (s->io) {
- git_stream_close(s->io);
- git_stream_free(s->io);
- s->io = NULL;
- }
-
- git_net_url_dispose(&s->url);
- git__free(s);
-}
-
-static int ssh_stream_alloc(
- ssh_subtransport *t,
- const char *cmd,
- git_smart_subtransport_stream **stream)
-{
- ssh_stream *s;
-
- GIT_ASSERT_ARG(stream);
-
- s = git__calloc(sizeof(ssh_stream), 1);
- GIT_ERROR_CHECK_ALLOC(s);
-
- s->parent.subtransport = &t->parent;
- s->parent.read = ssh_stream_read;
- s->parent.write = ssh_stream_write;
- s->parent.free = ssh_stream_free;
-
- s->cmd = cmd;
-
- *stream = &s->parent;
- return 0;
-}
-
-static int ssh_agent_auth(LIBSSH2_SESSION *session, git_credential_ssh_key *c) {
- int rc = LIBSSH2_ERROR_NONE;
-
- struct libssh2_agent_publickey *curr, *prev = NULL;
-
- LIBSSH2_AGENT *agent = libssh2_agent_init(session);
-
- if (agent == NULL)
- return -1;
-
- rc = libssh2_agent_connect(agent);
-
- if (rc != LIBSSH2_ERROR_NONE)
- goto shutdown;
-
- rc = libssh2_agent_list_identities(agent);
-
- if (rc != LIBSSH2_ERROR_NONE)
- goto shutdown;
-
- while (1) {
- rc = libssh2_agent_get_identity(agent, &curr, prev);
-
- if (rc < 0)
- goto shutdown;
-
- /* rc is set to 1 whenever the ssh agent ran out of keys to check.
- * Set the error code to authentication failure rather than erroring
- * out with an untranslatable error code.
- */
- if (rc == 1) {
- rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
- goto shutdown;
- }
-
- rc = libssh2_agent_userauth(agent, c->username, curr);
-
- if (rc == 0)
- break;
-
- prev = curr;
- }
-
-shutdown:
-
- if (rc != LIBSSH2_ERROR_NONE)
- ssh_error(session, "error authenticating");
-
- libssh2_agent_disconnect(agent);
- libssh2_agent_free(agent);
-
- return rc;
-}
-
-static int _git_ssh_authenticate_session(
- LIBSSH2_SESSION *session,
- git_credential *cred)
-{
- int rc;
-
- do {
- git_error_clear();
- switch (cred->credtype) {
- case GIT_CREDENTIAL_USERPASS_PLAINTEXT: {
- git_credential_userpass_plaintext *c = (git_credential_userpass_plaintext *)cred;
- rc = libssh2_userauth_password(session, c->username, c->password);
- break;
- }
- case GIT_CREDENTIAL_SSH_KEY: {
- git_credential_ssh_key *c = (git_credential_ssh_key *)cred;
-
- if (c->privatekey)
- rc = libssh2_userauth_publickey_fromfile(
- session, c->username, c->publickey,
- c->privatekey, c->passphrase);
- else
- rc = ssh_agent_auth(session, c);
-
- break;
- }
- case GIT_CREDENTIAL_SSH_CUSTOM: {
- git_credential_ssh_custom *c = (git_credential_ssh_custom *)cred;
-
- rc = libssh2_userauth_publickey(
- session, c->username, (const unsigned char *)c->publickey,
- c->publickey_len, c->sign_callback, &c->payload);
- break;
- }
- case GIT_CREDENTIAL_SSH_INTERACTIVE: {
- void **abstract = libssh2_session_abstract(session);
- git_credential_ssh_interactive *c = (git_credential_ssh_interactive *)cred;
-
- /* ideally, we should be able to set this by calling
- * libssh2_session_init_ex() instead of libssh2_session_init().
- * libssh2's API is inconsistent here i.e. libssh2_userauth_publickey()
- * allows you to pass the `abstract` as part of the call, whereas
- * libssh2_userauth_keyboard_interactive() does not!
- *
- * The only way to set the `abstract` pointer is by calling
- * libssh2_session_abstract(), which will replace the existing
- * pointer as is done below. This is safe for now (at time of writing),
- * but may not be valid in future.
- */
- *abstract = c->payload;
-
- rc = libssh2_userauth_keyboard_interactive(
- session, c->username, c->prompt_callback);
- break;
- }
-#ifdef GIT_SSH_MEMORY_CREDENTIALS
- case GIT_CREDENTIAL_SSH_MEMORY: {
- git_credential_ssh_key *c = (git_credential_ssh_key *)cred;
-
- GIT_ASSERT(c->username);
- GIT_ASSERT(c->privatekey);
-
- rc = libssh2_userauth_publickey_frommemory(
- session,
- c->username,
- strlen(c->username),
- c->publickey,
- c->publickey ? strlen(c->publickey) : 0,
- c->privatekey,
- strlen(c->privatekey),
- c->passphrase);
- break;
- }
-#endif
- default:
- rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
- }
- } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
-
- if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED ||
- rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED ||
- rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED)
- return GIT_EAUTH;
-
- if (rc != LIBSSH2_ERROR_NONE) {
- if (!git_error_last())
- ssh_error(session, "Failed to authenticate SSH session");
- return -1;
- }
-
- return 0;
-}
-
-static int request_creds(git_credential **out, ssh_subtransport *t, const char *user, int auth_methods)
-{
- int error, no_callback = 0;
- git_credential *cred = NULL;
-
- if (!t->owner->connect_opts.callbacks.credentials) {
- no_callback = 1;
- } else {
- error = t->owner->connect_opts.callbacks.credentials(
- &cred,
- t->owner->url,
- user,
- auth_methods,
- t->owner->connect_opts.callbacks.payload);
-
- if (error == GIT_PASSTHROUGH) {
- no_callback = 1;
- } else if (error < 0) {
- return error;
- } else if (!cred) {
- git_error_set(GIT_ERROR_SSH, "callback failed to initialize SSH credentials");
- return -1;
- }
- }
-
- if (no_callback) {
- git_error_set(GIT_ERROR_SSH, "authentication required but no callback set");
- return GIT_EAUTH;
- }
-
- if (!(cred->credtype & auth_methods)) {
- cred->free(cred);
- git_error_set(GIT_ERROR_SSH, "authentication callback returned unsupported credentials type");
- return GIT_EAUTH;
- }
-
- *out = cred;
-
- return 0;
-}
-
-#define KNOWN_HOSTS_FILE ".ssh/known_hosts"
-
-/*
- * Load the known_hosts file.
- *
- * Returns success but leaves the output NULL if we couldn't find the file.
- */
-static int load_known_hosts(LIBSSH2_KNOWNHOSTS **hosts, LIBSSH2_SESSION *session)
-{
- git_str path = GIT_STR_INIT, home = GIT_STR_INIT;
- LIBSSH2_KNOWNHOSTS *known_hosts = NULL;
- int error;
-
- GIT_ASSERT_ARG(hosts);
-
- if ((error = git__getenv(&home, "HOME")) < 0)
- return error;
-
- if ((error = git_str_joinpath(&path, git_str_cstr(&home), KNOWN_HOSTS_FILE)) < 0)
- goto out;
-
- if ((known_hosts = libssh2_knownhost_init(session)) == NULL) {
- ssh_error(session, "error initializing known hosts");
- error = -1;
- goto out;
- }
-
- /*
- * Try to read the file and consider not finding it as not trusting the
- * host rather than an error.
- */
- error = libssh2_knownhost_readfile(known_hosts, git_str_cstr(&path), LIBSSH2_KNOWNHOST_FILE_OPENSSH);
- if (error == LIBSSH2_ERROR_FILE)
- error = 0;
- if (error < 0)
- ssh_error(session, "error reading known_hosts");
-
-out:
- *hosts = known_hosts;
-
- git_str_clear(&home);
- git_str_clear(&path);
-
- return error;
-}
-
-static const char *hostkey_type_to_string(int type)
-{
- switch (type) {
- case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
- return "ssh-rsa";
- case LIBSSH2_KNOWNHOST_KEY_SSHDSS:
- return "ssh-dss";
-#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
- case LIBSSH2_KNOWNHOST_KEY_ECDSA_256:
- return "ecdsa-sha2-nistp256";
- case LIBSSH2_KNOWNHOST_KEY_ECDSA_384:
- return "ecdsa-sha2-nistp384";
- case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
- return "ecdsa-sha2-nistp521";
-#endif
-#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
- case LIBSSH2_KNOWNHOST_KEY_ED25519:
- return "ssh-ed25519";
-#endif
- }
-
- return NULL;
-}
-
-/*
- * We figure out what kind of key we want to ask the remote for by trying to
- * look it up with a nonsense key and using that mismatch to figure out what key
- * we do have stored for the host.
- *
- * Returns the string to pass to libssh2_session_method_pref or NULL if we were
- * unable to find anything or an error happened.
- */
-static const char *find_hostkey_preference(LIBSSH2_KNOWNHOSTS *known_hosts, const char *hostname, int port)
-{
- struct libssh2_knownhost *host = NULL;
- /* Specify no key type so we don't filter on that */
- int type = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW;
- const char key = '\0';
- int error;
-
- /*
- * In case of mismatch, we can find the type of key from known_hosts in
- * the returned host's information as it means that an entry was found
- * but our nonsense key obviously didn't match.
- */
- error = libssh2_knownhost_checkp(known_hosts, hostname, port, &key, 1, type, &host);
- if (error == LIBSSH2_KNOWNHOST_CHECK_MISMATCH)
- return hostkey_type_to_string(host->typemask & LIBSSH2_KNOWNHOST_KEY_MASK);
-
- return NULL;
-}
-
-static int _git_ssh_session_create(
- LIBSSH2_SESSION **session,
- LIBSSH2_KNOWNHOSTS **hosts,
- const char *hostname,
- int port,
- git_stream *io)
-{
- int rc = 0;
- LIBSSH2_SESSION *s;
- LIBSSH2_KNOWNHOSTS *known_hosts;
- git_socket_stream *socket = GIT_CONTAINER_OF(io, git_socket_stream, parent);
- const char *keytype = NULL;
-
- GIT_ASSERT_ARG(session);
- GIT_ASSERT_ARG(hosts);
-
- s = libssh2_session_init();
- if (!s) {
- git_error_set(GIT_ERROR_NET, "failed to initialize SSH session");
- return -1;
- }
-
- if ((rc = load_known_hosts(&known_hosts, s)) < 0) {
- ssh_error(s, "error loading known_hosts");
- libssh2_session_free(s);
- return -1;
- }
-
- if ((keytype = find_hostkey_preference(known_hosts, hostname, port)) != NULL) {
- do {
- rc = libssh2_session_method_pref(s, LIBSSH2_METHOD_HOSTKEY, keytype);
- } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
- if (rc != LIBSSH2_ERROR_NONE) {
- ssh_error(s, "failed to set hostkey preference");
- goto on_error;
- }
- }
-
-
- do {
- rc = libssh2_session_handshake(s, socket->s);
- } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
-
- if (rc != LIBSSH2_ERROR_NONE) {
- ssh_error(s, "failed to start SSH session");
- goto on_error;
- }
-
- libssh2_session_set_blocking(s, 1);
-
- *session = s;
- *hosts = known_hosts;
-
- return 0;
-
-on_error:
- libssh2_knownhost_free(known_hosts);
- libssh2_session_free(s);
- return -1;
-}
+#include "transports/smart.h"
+int git_smart_subtransport_ssh(
+ git_smart_subtransport **out,
+ git_transport *owner,
+ void *param)
+{
+#ifdef GIT_SSH_LIBSSH2
+ return git_smart_subtransport_ssh_libssh2(out, owner, param);
+#elif GIT_SSH_EXEC
+ return git_smart_subtransport_ssh_exec(out, owner, param);
+#else
+ GIT_UNUSED(out);
+ GIT_UNUSED(owner);
+ GIT_UNUSED(param);
-/*
- * Returns the typemask argument to pass to libssh2_knownhost_check{,p} based on
- * the type of key that libssh2_session_hostkey returns.
- */
-static int fingerprint_type_mask(int keytype)
-{
- int mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW;
- return mask;
-
- switch (keytype) {
- case LIBSSH2_HOSTKEY_TYPE_RSA:
- mask |= LIBSSH2_KNOWNHOST_KEY_SSHRSA;
- break;
- case LIBSSH2_HOSTKEY_TYPE_DSS:
- mask |= LIBSSH2_KNOWNHOST_KEY_SSHDSS;
- break;
-#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
- case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
- mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
- break;
- case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
- mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
- break;
- case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
- mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
- break;
-#endif
-#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
- case LIBSSH2_HOSTKEY_TYPE_ED25519:
- mask |= LIBSSH2_KNOWNHOST_KEY_ED25519;
- break;
-#endif
- }
-
- return mask;
-}
-
-/*
- * Check the host against the user's known_hosts file.
- *
- * Returns 1/0 for valid/''not-valid or <0 for an error
- */
-static int check_against_known_hosts(
- LIBSSH2_SESSION *session,
- LIBSSH2_KNOWNHOSTS *known_hosts,
- const char *hostname,
- int port,
- const char *key,
- size_t key_len,
- int key_type)
-{
- int check, typemask, ret = 0;
- struct libssh2_knownhost *host = NULL;
-
- if (known_hosts == NULL)
- return 0;
-
- typemask = fingerprint_type_mask(key_type);
- check = libssh2_knownhost_checkp(known_hosts, hostname, port, key, key_len, typemask, &host);
- if (check == LIBSSH2_KNOWNHOST_CHECK_FAILURE) {
- ssh_error(session, "error checking for known host");
- return -1;
- }
-
- ret = check == LIBSSH2_KNOWNHOST_CHECK_MATCH ? 1 : 0;
-
- return ret;
-}
-
-/*
- * Perform the check for the session's certificate against known hosts if
- * possible and then ask the user if they have a callback.
- *
- * Returns 1/0 for valid/not-valid or <0 for an error
- */
-static int check_certificate(
- LIBSSH2_SESSION *session,
- LIBSSH2_KNOWNHOSTS *known_hosts,
- git_transport_certificate_check_cb check_cb,
- void *check_cb_payload,
- const char *host,
- int port)
-{
- git_cert_hostkey cert = {{ 0 }};
- const char *key;
- size_t cert_len;
- int cert_type, cert_valid = 0, error = 0;
-
- if ((key = libssh2_session_hostkey(session, &cert_len, &cert_type)) == NULL) {
- ssh_error(session, "failed to retrieve hostkey");
- return -1;
- }
-
- if ((cert_valid = check_against_known_hosts(session, known_hosts, host, port, key, cert_len, cert_type)) < 0)
- return -1;
-
- cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
- if (key != NULL) {
- cert.type |= GIT_CERT_SSH_RAW;
- cert.hostkey = key;
- cert.hostkey_len = cert_len;
- switch (cert_type) {
- case LIBSSH2_HOSTKEY_TYPE_RSA:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_RSA;
- break;
- case LIBSSH2_HOSTKEY_TYPE_DSS:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_DSS;
- break;
-
-#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
- case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256;
- break;
- case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384;
- break;
- case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521;
- break;
-#endif
-
-#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
- case LIBSSH2_HOSTKEY_TYPE_ED25519:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ED25519;
- break;
-#endif
- default:
- cert.raw_type = GIT_CERT_SSH_RAW_TYPE_UNKNOWN;
- }
- }
-
-#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
- key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256);
- if (key != NULL) {
- cert.type |= GIT_CERT_SSH_SHA256;
- memcpy(&cert.hash_sha256, key, 32);
- }
-#endif
-
- key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
- if (key != NULL) {
- cert.type |= GIT_CERT_SSH_SHA1;
- memcpy(&cert.hash_sha1, key, 20);
- }
-
- key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
- if (key != NULL) {
- cert.type |= GIT_CERT_SSH_MD5;
- memcpy(&cert.hash_md5, key, 16);
- }
-
- if (cert.type == 0) {
- git_error_set(GIT_ERROR_SSH, "unable to get the host key");
- return -1;
- }
-
- git_error_clear();
- error = 0;
- if (!cert_valid) {
- git_error_set(GIT_ERROR_SSH, "invalid or unknown remote ssh hostkey");
- error = GIT_ECERTIFICATE;
- }
-
- if (check_cb != NULL) {
- git_cert_hostkey *cert_ptr = &cert;
- git_error_state previous_error = {0};
-
- git_error_state_capture(&previous_error, error);
- error = check_cb((git_cert *) cert_ptr, cert_valid, host, check_cb_payload);
- if (error == GIT_PASSTHROUGH) {
- error = git_error_state_restore(&previous_error);
- } else if (error < 0 && !git_error_last()) {
- git_error_set(GIT_ERROR_NET, "user canceled hostkey check");
- }
-
- git_error_state_free(&previous_error);
- }
-
- return error;
-}
-
-#define SSH_DEFAULT_PORT "22"
-
-static int _git_ssh_setup_conn(
- ssh_subtransport *t,
- const char *url,
- const char *cmd,
- git_smart_subtransport_stream **stream)
-{
- int auth_methods, error = 0, port;
- ssh_stream *s;
- git_credential *cred = NULL;
- LIBSSH2_SESSION *session=NULL;
- LIBSSH2_CHANNEL *channel=NULL;
- LIBSSH2_KNOWNHOSTS *known_hosts = NULL;
-
- t->current_stream = NULL;
-
- *stream = NULL;
- if (ssh_stream_alloc(t, cmd, stream) < 0)
- return -1;
-
- s = (ssh_stream *)*stream;
- s->session = NULL;
- s->channel = NULL;
-
- if (git_net_str_is_url(url))
- error = git_net_url_parse(&s->url, url);
- else
- error = git_net_url_parse_scp(&s->url, url);
-
- if (error < 0)
- goto done;
-
- if ((error = git_socket_stream_new(&s->io, s->url.host, s->url.port)) < 0 ||
- (error = git_stream_connect(s->io)) < 0)
- goto done;
-
- /*
- * Try to parse the port as a number, if we can't then fall back to
- * default. It would be nice if we could get the port that was resolved
- * as part of the stream connection, but that's not something that's
- * exposed.
- */
- if (git__strntol32(&port, s->url.port, strlen(s->url.port), NULL, 10) < 0)
- port = -1;
-
- if ((error = _git_ssh_session_create(&session, &known_hosts, s->url.host, port, s->io)) < 0)
- goto done;
-
- if ((error = check_certificate(session, known_hosts, t->owner->connect_opts.callbacks.certificate_check, t->owner->connect_opts.callbacks.payload, s->url.host, port)) < 0)
- goto done;
-
- /* we need the username to ask for auth methods */
- if (!s->url.username) {
- if ((error = request_creds(&cred, t, NULL, GIT_CREDENTIAL_USERNAME)) < 0)
- goto done;
-
- s->url.username = git__strdup(((git_credential_username *) cred)->username);
- cred->free(cred);
- cred = NULL;
- if (!s->url.username)
- goto done;
- } else if (s->url.username && s->url.password) {
- if ((error = git_credential_userpass_plaintext_new(&cred, s->url.username, s->url.password)) < 0)
- goto done;
- }
-
- if ((error = list_auth_methods(&auth_methods, session, s->url.username)) < 0)
- goto done;
-
- error = GIT_EAUTH;
- /* if we already have something to try */
- if (cred && auth_methods & cred->credtype)
- error = _git_ssh_authenticate_session(session, cred);
-
- while (error == GIT_EAUTH) {
- if (cred) {
- cred->free(cred);
- cred = NULL;
- }
-
- if ((error = request_creds(&cred, t, s->url.username, auth_methods)) < 0)
- goto done;
-
- if (strcmp(s->url.username, git_credential_get_username(cred))) {
- git_error_set(GIT_ERROR_SSH, "username does not match previous request");
- error = -1;
- goto done;
- }
-
- error = _git_ssh_authenticate_session(session, cred);
-
- if (error == GIT_EAUTH) {
- /* refresh auth methods */
- if ((error = list_auth_methods(&auth_methods, session, s->url.username)) < 0)
- goto done;
- else
- error = GIT_EAUTH;
- }
- }
-
- if (error < 0)
- goto done;
-
- channel = libssh2_channel_open_session(session);
- if (!channel) {
- error = -1;
- ssh_error(session, "Failed to open SSH channel");
- goto done;
- }
-
- libssh2_channel_set_blocking(channel, 1);
-
- s->session = session;
- s->channel = channel;
-
- t->current_stream = s;
-
-done:
- if (error < 0) {
- ssh_stream_free(*stream);
-
- if (known_hosts)
- libssh2_knownhost_free(known_hosts);
- if (session)
- libssh2_session_free(session);
- }
-
- if (cred)
- cred->free(cred);
-
- return error;
-}
-
-static int ssh_uploadpack_ls(
- ssh_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- const char *cmd = t->cmd_uploadpack ? t->cmd_uploadpack : cmd_uploadpack;
-
- return _git_ssh_setup_conn(t, url, cmd, stream);
-}
-
-static int ssh_uploadpack(
- ssh_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- GIT_UNUSED(url);
-
- if (t->current_stream) {
- *stream = &t->current_stream->parent;
- return 0;
- }
-
- git_error_set(GIT_ERROR_NET, "must call UPLOADPACK_LS before UPLOADPACK");
- return -1;
-}
-
-static int ssh_receivepack_ls(
- ssh_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- const char *cmd = t->cmd_receivepack ? t->cmd_receivepack : cmd_receivepack;
-
-
- return _git_ssh_setup_conn(t, url, cmd, stream);
-}
-
-static int ssh_receivepack(
- ssh_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- GIT_UNUSED(url);
-
- if (t->current_stream) {
- *stream = &t->current_stream->parent;
- return 0;
- }
-
- git_error_set(GIT_ERROR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK");
- return -1;
-}
-
-static int _ssh_action(
- git_smart_subtransport_stream **stream,
- git_smart_subtransport *subtransport,
- const char *url,
- git_smart_service_t action)
-{
- ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
-
- switch (action) {
- case GIT_SERVICE_UPLOADPACK_LS:
- return ssh_uploadpack_ls(t, url, stream);
-
- case GIT_SERVICE_UPLOADPACK:
- return ssh_uploadpack(t, url, stream);
-
- case GIT_SERVICE_RECEIVEPACK_LS:
- return ssh_receivepack_ls(t, url, stream);
-
- case GIT_SERVICE_RECEIVEPACK:
- return ssh_receivepack(t, url, stream);
- }
-
- *stream = NULL;
+ git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport; library was built without SSH support");
return -1;
-}
-
-static int _ssh_close(git_smart_subtransport *subtransport)
-{
- ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
-
- GIT_ASSERT(!t->current_stream);
-
- GIT_UNUSED(t);
-
- return 0;
-}
-
-static void _ssh_free(git_smart_subtransport *subtransport)
-{
- ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
-
- git__free(t->cmd_uploadpack);
- git__free(t->cmd_receivepack);
- git__free(t);
-}
-
-#define SSH_AUTH_PUBLICKEY "publickey"
-#define SSH_AUTH_PASSWORD "password"
-#define SSH_AUTH_KEYBOARD_INTERACTIVE "keyboard-interactive"
-
-static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username)
-{
- const char *list, *ptr;
-
- *out = 0;
-
- list = libssh2_userauth_list(session, username, strlen(username));
-
- /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
- if (list == NULL && !libssh2_userauth_authenticated(session)) {
- ssh_error(session, "Failed to retrieve list of SSH authentication methods");
- return GIT_EAUTH;
- }
-
- ptr = list;
- while (ptr) {
- if (*ptr == ',')
- ptr++;
-
- if (!git__prefixcmp(ptr, SSH_AUTH_PUBLICKEY)) {
- *out |= GIT_CREDENTIAL_SSH_KEY;
- *out |= GIT_CREDENTIAL_SSH_CUSTOM;
-#ifdef GIT_SSH_MEMORY_CREDENTIALS
- *out |= GIT_CREDENTIAL_SSH_MEMORY;
#endif
- ptr += strlen(SSH_AUTH_PUBLICKEY);
- continue;
- }
-
- if (!git__prefixcmp(ptr, SSH_AUTH_PASSWORD)) {
- *out |= GIT_CREDENTIAL_USERPASS_PLAINTEXT;
- ptr += strlen(SSH_AUTH_PASSWORD);
- continue;
- }
-
- if (!git__prefixcmp(ptr, SSH_AUTH_KEYBOARD_INTERACTIVE)) {
- *out |= GIT_CREDENTIAL_SSH_INTERACTIVE;
- ptr += strlen(SSH_AUTH_KEYBOARD_INTERACTIVE);
- continue;
- }
-
- /* Skip it if we don't know it */
- ptr = strchr(ptr, ',');
- }
-
- return 0;
}
-#endif
-int git_smart_subtransport_ssh(
- git_smart_subtransport **out, git_transport *owner, void *param)
+static int transport_set_paths(git_transport *t, git_strarray *paths)
{
-#ifdef GIT_SSH
- ssh_subtransport *t;
-
- GIT_ASSERT_ARG(out);
-
- GIT_UNUSED(param);
-
- t = git__calloc(sizeof(ssh_subtransport), 1);
- GIT_ERROR_CHECK_ALLOC(t);
-
- t->owner = (transport_smart *)owner;
- t->parent.action = _ssh_action;
- t->parent.close = _ssh_close;
- t->parent.free = _ssh_free;
+ transport_smart *smart = (transport_smart *)t;
- *out = (git_smart_subtransport *) t;
- return 0;
+#ifdef GIT_SSH_LIBSSH2
+ return git_smart_subtransport_ssh_libssh2_set_paths(
+ (git_smart_subtransport *)smart->wrapped,
+ paths->strings[0],
+ paths->strings[1]);
+#elif GIT_SSH_EXEC
+ return git_smart_subtransport_ssh_exec_set_paths(
+ (git_smart_subtransport *)smart->wrapped,
+ paths->strings[0],
+ paths->strings[1]);
#else
- GIT_UNUSED(owner);
- GIT_UNUSED(param);
-
- GIT_ASSERT_ARG(out);
- *out = NULL;
+ GIT_UNUSED(t);
+ GIT_UNUSED(smart);
+ GIT_UNUSED(paths);
- git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport. Library was built without SSH support");
+ GIT_ASSERT(!"cannot create SSH library; library was built without SSH support");
return -1;
#endif
}
-int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload)
+int git_transport_ssh_with_paths(
+ git_transport **out,
+ git_remote *owner,
+ void *payload)
{
-#ifdef GIT_SSH
git_strarray *paths = (git_strarray *) payload;
git_transport *transport;
- transport_smart *smart;
- ssh_subtransport *t;
int error;
+
git_smart_subtransport_definition ssh_definition = {
git_smart_subtransport_ssh,
0, /* no RPC */
- NULL,
+ NULL
};
if (paths->count != 2) {
@@ -1102,49 +76,10 @@ int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *p
if ((error = git_transport_smart(&transport, owner, &ssh_definition)) < 0)
return error;
- smart = (transport_smart *) transport;
- t = (ssh_subtransport *) smart->wrapped;
-
- t->cmd_uploadpack = git__strdup(paths->strings[0]);
- GIT_ERROR_CHECK_ALLOC(t->cmd_uploadpack);
- t->cmd_receivepack = git__strdup(paths->strings[1]);
- GIT_ERROR_CHECK_ALLOC(t->cmd_receivepack);
+ if ((error = transport_set_paths(transport, paths)) < 0)
+ return error;
*out = transport;
return 0;
-#else
- GIT_UNUSED(owner);
- GIT_UNUSED(payload);
-
- GIT_ASSERT_ARG(out);
- *out = NULL;
-
- git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport. Library was built without SSH support");
- return -1;
-#endif
}
-#ifdef GIT_SSH
-static void shutdown_ssh(void)
-{
- libssh2_exit();
-}
-#endif
-
-int git_transport_ssh_global_init(void)
-{
-#ifdef GIT_SSH
- if (libssh2_init(0) < 0) {
- git_error_set(GIT_ERROR_SSH, "unable to initialize libssh2");
- return -1;
- }
-
- return git_runtime_shutdown_register(shutdown_ssh);
-
-#else
-
- /* Nothing to initialize */
- return 0;
-
-#endif
-}
diff --git a/src/libgit2/transports/ssh_exec.c b/src/libgit2/transports/ssh_exec.c
new file mode 100644
index 00000000000..a09c1db9441
--- /dev/null
+++ b/src/libgit2/transports/ssh_exec.c
@@ -0,0 +1,346 @@
+/*
+ * 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 "ssh_exec.h"
+
+#ifdef GIT_SSH_EXEC
+
+#include "common.h"
+
+#include "config.h"
+#include "net.h"
+#include "path.h"
+#include "futils.h"
+#include "process.h"
+#include "transports/smart.h"
+
+typedef struct {
+ git_smart_subtransport_stream parent;
+} ssh_exec_subtransport_stream;
+
+typedef struct {
+ git_smart_subtransport parent;
+ git_transport *owner;
+
+ ssh_exec_subtransport_stream *current_stream;
+
+ char *cmd_uploadpack;
+ char *cmd_receivepack;
+
+ git_smart_service_t action;
+ git_process *process;
+} ssh_exec_subtransport;
+
+static int ssh_exec_subtransport_stream_read(
+ git_smart_subtransport_stream *s,
+ char *buffer,
+ size_t buf_size,
+ size_t *bytes_read)
+{
+ ssh_exec_subtransport *transport;
+ ssh_exec_subtransport_stream *stream = (ssh_exec_subtransport_stream *)s;
+ ssize_t ret;
+
+ GIT_ASSERT_ARG(stream);
+ GIT_ASSERT(stream->parent.subtransport);
+
+ transport = (ssh_exec_subtransport *)stream->parent.subtransport;
+
+ if ((ret = git_process_read(transport->process, buffer, buf_size)) < 0) {
+ return (int)ret;
+ }
+
+ *bytes_read = (size_t)ret;
+ return 0;
+}
+
+static int ssh_exec_subtransport_stream_write(
+ git_smart_subtransport_stream *s,
+ const char *buffer,
+ size_t len)
+{
+ ssh_exec_subtransport *transport;
+ ssh_exec_subtransport_stream *stream = (ssh_exec_subtransport_stream *)s;
+ ssize_t ret;
+
+ GIT_ASSERT(stream && stream->parent.subtransport);
+
+ transport = (ssh_exec_subtransport *)stream->parent.subtransport;
+
+ while (len > 0) {
+ if ((ret = git_process_write(transport->process, buffer, len)) < 0)
+ return (int)ret;
+
+ len -= ret;
+ }
+
+ return 0;
+}
+
+static void ssh_exec_subtransport_stream_free(git_smart_subtransport_stream *s)
+{
+ ssh_exec_subtransport_stream *stream = (ssh_exec_subtransport_stream *)s;
+
+ git__free(stream);
+}
+
+static int ssh_exec_subtransport_stream_init(
+ ssh_exec_subtransport_stream **out,
+ ssh_exec_subtransport *transport)
+{
+ GIT_ASSERT_ARG(out);
+
+ *out = git__calloc(sizeof(ssh_exec_subtransport_stream), 1);
+ GIT_ERROR_CHECK_ALLOC(*out);
+
+ (*out)->parent.subtransport = &transport->parent;
+ (*out)->parent.read = ssh_exec_subtransport_stream_read;
+ (*out)->parent.write = ssh_exec_subtransport_stream_write;
+ (*out)->parent.free = ssh_exec_subtransport_stream_free;
+
+ return 0;
+}
+
+GIT_INLINE(int) ensure_transport_state(
+ ssh_exec_subtransport *transport,
+ git_smart_service_t expected,
+ git_smart_service_t next)
+{
+ if (transport->action != expected && transport->action != next) {
+ git_error_set(GIT_ERROR_NET, "invalid transport state");
+
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_ssh_cmdline(
+ git_str *out,
+ ssh_exec_subtransport *transport,
+ git_net_url *url,
+ const char *command)
+{
+ git_remote *remote = ((transport_smart *)transport->owner)->owner;
+ git_repository *repo = remote->repo;
+ git_config *cfg;
+ git_str ssh_cmd = GIT_STR_INIT;
+ const char *default_ssh_cmd = "ssh";
+ int error;
+
+ /*
+ * Safety check: like git, we forbid paths that look like an
+ * option as that could lead to injection to ssh that can make
+ * us do unexpected things
+ */
+ if (git_process__is_cmdline_option(url->username)) {
+ git_error_set(GIT_ERROR_NET, "cannot ssh: username '%s' is ambiguous with command-line option", url->username);
+ return -1;
+ } else if (git_process__is_cmdline_option(url->host)) {
+ git_error_set(GIT_ERROR_NET, "cannot ssh: host '%s' is ambiguous with command-line option", url->host);
+ return -1;
+ } else if (git_process__is_cmdline_option(url->path)) {
+ git_error_set(GIT_ERROR_NET, "cannot ssh: path '%s' is ambiguous with command-line option", url->path);
+ return -1;
+ }
+
+ if ((error = git_repository_config_snapshot(&cfg, repo)) < 0)
+ return error;
+
+ if ((error = git__getenv(&ssh_cmd, "GIT_SSH")) == 0)
+ ;
+ else if (error != GIT_ENOTFOUND)
+ goto done;
+ else if ((error = git_config__get_string_buf(&ssh_cmd, cfg, "core.sshcommand")) < 0 && error != GIT_ENOTFOUND)
+ goto done;
+
+ error = git_str_printf(out, "%s -p %s \"%s%s%s\" \"%s\" \"%s\"",
+ ssh_cmd.size > 0 ? ssh_cmd.ptr : default_ssh_cmd,
+ url->port,
+ url->username ? url->username : "",
+ url->username ? "@" : "",
+ url->host,
+ command,
+ url->path);
+
+done:
+ git_str_dispose(&ssh_cmd);
+ git_config_free(cfg);
+ return error;
+}
+
+static int start_ssh(
+ ssh_exec_subtransport *transport,
+ git_smart_service_t action,
+ const char *sshpath)
+{
+ const char *env[] = { "GIT_DIR=" };
+
+ git_process_options process_opts = GIT_PROCESS_OPTIONS_INIT;
+ git_net_url url = GIT_NET_URL_INIT;
+ git_str ssh_cmdline = GIT_STR_INIT;
+ const char *command;
+ int error;
+
+ process_opts.capture_in = 1;
+ process_opts.capture_out = 1;
+ process_opts.capture_err = 0;
+
+ switch (action) {
+ case GIT_SERVICE_UPLOADPACK_LS:
+ command = transport->cmd_uploadpack ?
+ transport->cmd_uploadpack : "git-upload-pack";
+ break;
+ case GIT_SERVICE_RECEIVEPACK_LS:
+ command = transport->cmd_receivepack ?
+ transport->cmd_receivepack : "git-receive-pack";
+ break;
+ default:
+ git_error_set(GIT_ERROR_NET, "invalid action");
+ error = -1;
+ goto done;
+ }
+
+ if (git_net_str_is_url(sshpath))
+ error = git_net_url_parse(&url, sshpath);
+ else
+ error = git_net_url_parse_scp(&url, sshpath);
+
+ if (error < 0)
+ goto done;
+
+ if ((error = get_ssh_cmdline(&ssh_cmdline, transport, &url, command)) < 0)
+ goto done;
+
+ if ((error = git_process_new_from_cmdline(&transport->process,
+ ssh_cmdline.ptr, env, ARRAY_SIZE(env), &process_opts)) < 0 ||
+ (error = git_process_start(transport->process)) < 0) {
+ git_process_free(transport->process);
+ transport->process = NULL;
+ goto done;
+ }
+
+done:
+ git_str_dispose(&ssh_cmdline);
+ git_net_url_dispose(&url);
+ return error;
+}
+
+static int ssh_exec_subtransport_action(
+ git_smart_subtransport_stream **out,
+ git_smart_subtransport *t,
+ const char *sshpath,
+ git_smart_service_t action)
+{
+ ssh_exec_subtransport *transport = (ssh_exec_subtransport *)t;
+ ssh_exec_subtransport_stream *stream = NULL;
+ git_smart_service_t expected;
+ int error;
+
+ switch (action) {
+ case GIT_SERVICE_UPLOADPACK_LS:
+ case GIT_SERVICE_RECEIVEPACK_LS:
+ if ((error = ensure_transport_state(transport, 0, 0)) < 0 ||
+ (error = ssh_exec_subtransport_stream_init(&stream, transport)) < 0 ||
+ (error = start_ssh(transport, action, sshpath)) < 0)
+ goto on_error;
+
+ transport->current_stream = stream;
+ break;
+
+ case GIT_SERVICE_UPLOADPACK:
+ case GIT_SERVICE_RECEIVEPACK:
+ expected = (action == GIT_SERVICE_UPLOADPACK) ?
+ GIT_SERVICE_UPLOADPACK_LS : GIT_SERVICE_RECEIVEPACK_LS;
+
+ if ((error = ensure_transport_state(transport, expected, action)) < 0)
+ goto on_error;
+
+ break;
+
+ default:
+ git_error_set(GIT_ERROR_INVALID, "invalid service request");
+ goto on_error;
+ }
+
+ transport->action = action;
+ *out = &transport->current_stream->parent;
+
+ return 0;
+
+on_error:
+ if (stream != NULL)
+ ssh_exec_subtransport_stream_free(&stream->parent);
+
+ return -1;
+}
+
+static int ssh_exec_subtransport_close(git_smart_subtransport *t)
+{
+ ssh_exec_subtransport *transport = (ssh_exec_subtransport *)t;
+
+ if (transport->process) {
+ git_process_close(transport->process);
+ git_process_free(transport->process);
+ transport->process = NULL;
+ }
+
+ transport->action = 0;
+
+ return 0;
+}
+
+static void ssh_exec_subtransport_free(git_smart_subtransport *t)
+{
+ ssh_exec_subtransport *transport = (ssh_exec_subtransport *)t;
+
+ git__free(transport->cmd_uploadpack);
+ git__free(transport->cmd_receivepack);
+ git__free(transport);
+}
+
+int git_smart_subtransport_ssh_exec(
+ git_smart_subtransport **out,
+ git_transport *owner,
+ void *payload)
+{
+ ssh_exec_subtransport *transport;
+
+ GIT_UNUSED(payload);
+
+ transport = git__calloc(sizeof(ssh_exec_subtransport), 1);
+ GIT_ERROR_CHECK_ALLOC(transport);
+
+ transport->owner = owner;
+ transport->parent.action = ssh_exec_subtransport_action;
+ transport->parent.close = ssh_exec_subtransport_close;
+ transport->parent.free = ssh_exec_subtransport_free;
+
+ *out = (git_smart_subtransport *) transport;
+ return 0;
+}
+
+int git_smart_subtransport_ssh_exec_set_paths(
+ git_smart_subtransport *subtransport,
+ const char *cmd_uploadpack,
+ const char *cmd_receivepack)
+{
+ ssh_exec_subtransport *t = (ssh_exec_subtransport *)subtransport;
+
+ git__free(t->cmd_uploadpack);
+ git__free(t->cmd_receivepack);
+
+ t->cmd_uploadpack = git__strdup(cmd_uploadpack);
+ GIT_ERROR_CHECK_ALLOC(t->cmd_uploadpack);
+
+ t->cmd_receivepack = git__strdup(cmd_receivepack);
+ GIT_ERROR_CHECK_ALLOC(t->cmd_receivepack);
+
+ return 0;
+}
+
+#endif
diff --git a/src/libgit2/transports/ssh_exec.h b/src/libgit2/transports/ssh_exec.h
new file mode 100644
index 00000000000..4bcba06b16b
--- /dev/null
+++ b/src/libgit2/transports/ssh_exec.h
@@ -0,0 +1,26 @@
+/*
+ * 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_transports_ssh_exec_h__
+#define INCLUDE_transports_ssh_exec_h__
+
+#include "common.h"
+
+#include "git2.h"
+#include "git2/transport.h"
+#include "git2/sys/transport.h"
+
+int git_smart_subtransport_ssh_exec(
+ git_smart_subtransport **out,
+ git_transport *owner,
+ void *param);
+
+int git_smart_subtransport_ssh_exec_set_paths(
+ git_smart_subtransport *subtransport,
+ const char *cmd_uploadpack,
+ const char *cmd_receivepack);
+
+#endif
diff --git a/src/libgit2/transports/ssh_libssh2.c b/src/libgit2/transports/ssh_libssh2.c
new file mode 100644
index 00000000000..1993ffe5c3a
--- /dev/null
+++ b/src/libgit2/transports/ssh_libssh2.c
@@ -0,0 +1,1124 @@
+/*
+ * 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 "ssh_libssh2.h"
+
+#ifdef GIT_SSH_LIBSSH2
+
+#include
+
+#include "runtime.h"
+#include "net.h"
+#include "smart.h"
+#include "process.h"
+#include "streams/socket.h"
+#include "sysdir.h"
+
+#include "git2/credential.h"
+#include "git2/sys/credential.h"
+
+#define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
+
+extern int git_socket_stream__timeout;
+
+static const char cmd_uploadpack[] = "git-upload-pack";
+static const char cmd_receivepack[] = "git-receive-pack";
+
+typedef struct {
+ git_smart_subtransport_stream parent;
+ git_stream *io;
+ LIBSSH2_SESSION *session;
+ LIBSSH2_CHANNEL *channel;
+ const char *cmd;
+ git_net_url url;
+ unsigned sent_command : 1;
+} ssh_stream;
+
+typedef struct {
+ git_smart_subtransport parent;
+ transport_smart *owner;
+ ssh_stream *current_stream;
+ git_credential *cred;
+ char *cmd_uploadpack;
+ char *cmd_receivepack;
+} ssh_subtransport;
+
+static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username);
+
+static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
+{
+ char *ssherr;
+ libssh2_session_last_error(session, &ssherr, NULL, 0);
+
+ git_error_set(GIT_ERROR_SSH, "%s: %s", errmsg, ssherr);
+}
+
+/*
+ * Create a git protocol request.
+ *
+ * For example: git-upload-pack '/libgit2/libgit2'
+ */
+static int gen_proto(git_str *request, const char *cmd, git_net_url *url)
+{
+ const char *repo;
+
+ repo = url->path;
+
+ if (repo && repo[0] == '/' && repo[1] == '~')
+ repo++;
+
+ if (!repo || !repo[0]) {
+ git_error_set(GIT_ERROR_NET, "malformed git protocol URL");
+ return -1;
+ }
+
+ git_str_puts(request, cmd);
+ git_str_puts(request, " '");
+ git_str_puts(request, repo);
+ git_str_puts(request, "'");
+
+ if (git_str_oom(request))
+ return -1;
+
+ return 0;
+}
+
+static int send_command(ssh_stream *s)
+{
+ int error;
+ git_str request = GIT_STR_INIT;
+
+ error = gen_proto(&request, s->cmd, &s->url);
+ if (error < 0)
+ goto cleanup;
+
+ error = libssh2_channel_exec(s->channel, request.ptr);
+ if (error < LIBSSH2_ERROR_NONE) {
+ ssh_error(s->session, "SSH could not execute request");
+ goto cleanup;
+ }
+
+ s->sent_command = 1;
+
+cleanup:
+ git_str_dispose(&request);
+ return error;
+}
+
+static int ssh_stream_read(
+ git_smart_subtransport_stream *stream,
+ char *buffer,
+ size_t buf_size,
+ size_t *bytes_read)
+{
+ int rc;
+ ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent);
+
+ *bytes_read = 0;
+
+ if (!s->sent_command && send_command(s) < 0)
+ return -1;
+
+ if ((rc = libssh2_channel_read(s->channel, buffer, buf_size)) < LIBSSH2_ERROR_NONE) {
+ ssh_error(s->session, "SSH could not read data");
+ return -1;
+ }
+
+ /*
+ * If we can't get anything out of stdout, it's typically a
+ * not-found error, so read from stderr and signal EOF on
+ * stderr.
+ */
+ if (rc == 0) {
+ if ((rc = libssh2_channel_read_stderr(s->channel, buffer, buf_size)) > 0) {
+ git_error_set(GIT_ERROR_SSH, "%*s", rc, buffer);
+ return GIT_EEOF;
+ } else if (rc < LIBSSH2_ERROR_NONE) {
+ ssh_error(s->session, "SSH could not read stderr");
+ return -1;
+ }
+ }
+
+
+ *bytes_read = rc;
+
+ return 0;
+}
+
+static int ssh_stream_write(
+ git_smart_subtransport_stream *stream,
+ const char *buffer,
+ size_t len)
+{
+ ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent);
+ size_t off = 0;
+ ssize_t ret = 0;
+
+ if (!s->sent_command && send_command(s) < 0)
+ return -1;
+
+ do {
+ ret = libssh2_channel_write(s->channel, buffer + off, len - off);
+ if (ret < 0)
+ break;
+
+ off += ret;
+
+ } while (off < len);
+
+ if (ret < 0) {
+ ssh_error(s->session, "SSH could not write data");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ssh_stream_free(git_smart_subtransport_stream *stream)
+{
+ ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent);
+ ssh_subtransport *t;
+
+ if (!stream)
+ return;
+
+ t = OWNING_SUBTRANSPORT(s);
+ t->current_stream = NULL;
+
+ if (s->channel) {
+ libssh2_channel_close(s->channel);
+ libssh2_channel_free(s->channel);
+ s->channel = NULL;
+ }
+
+ if (s->session) {
+ libssh2_session_disconnect(s->session, "closing transport");
+ libssh2_session_free(s->session);
+ s->session = NULL;
+ }
+
+ if (s->io) {
+ git_stream_close(s->io);
+ git_stream_free(s->io);
+ s->io = NULL;
+ }
+
+ git_net_url_dispose(&s->url);
+ git__free(s);
+}
+
+static int ssh_stream_alloc(
+ ssh_subtransport *t,
+ const char *cmd,
+ git_smart_subtransport_stream **stream)
+{
+ ssh_stream *s;
+
+ GIT_ASSERT_ARG(stream);
+
+ s = git__calloc(sizeof(ssh_stream), 1);
+ GIT_ERROR_CHECK_ALLOC(s);
+
+ s->parent.subtransport = &t->parent;
+ s->parent.read = ssh_stream_read;
+ s->parent.write = ssh_stream_write;
+ s->parent.free = ssh_stream_free;
+
+ s->cmd = cmd;
+
+ *stream = &s->parent;
+ return 0;
+}
+
+static int ssh_agent_auth(LIBSSH2_SESSION *session, git_credential_ssh_key *c) {
+ int rc = LIBSSH2_ERROR_NONE;
+
+ struct libssh2_agent_publickey *curr, *prev = NULL;
+
+ LIBSSH2_AGENT *agent = libssh2_agent_init(session);
+
+ if (agent == NULL)
+ return -1;
+
+ rc = libssh2_agent_connect(agent);
+
+ if (rc != LIBSSH2_ERROR_NONE) {
+ rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
+ goto shutdown;
+ }
+
+ rc = libssh2_agent_list_identities(agent);
+
+ if (rc != LIBSSH2_ERROR_NONE)
+ goto shutdown;
+
+ while (1) {
+ rc = libssh2_agent_get_identity(agent, &curr, prev);
+
+ if (rc < 0)
+ goto shutdown;
+
+ /* rc is set to 1 whenever the ssh agent ran out of keys to check.
+ * Set the error code to authentication failure rather than erroring
+ * out with an untranslatable error code.
+ */
+ if (rc == 1) {
+ rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
+ goto shutdown;
+ }
+
+ rc = libssh2_agent_userauth(agent, c->username, curr);
+
+ if (rc == 0)
+ break;
+
+ prev = curr;
+ }
+
+shutdown:
+
+ if (rc != LIBSSH2_ERROR_NONE)
+ ssh_error(session, "error authenticating");
+
+ libssh2_agent_disconnect(agent);
+ libssh2_agent_free(agent);
+
+ return rc;
+}
+
+static int _git_ssh_authenticate_session(
+ LIBSSH2_SESSION *session,
+ git_credential *cred)
+{
+ int rc;
+
+ do {
+ git_error_clear();
+ switch (cred->credtype) {
+ case GIT_CREDENTIAL_USERPASS_PLAINTEXT: {
+ git_credential_userpass_plaintext *c = (git_credential_userpass_plaintext *)cred;
+ rc = libssh2_userauth_password(session, c->username, c->password);
+ break;
+ }
+ case GIT_CREDENTIAL_SSH_KEY: {
+ git_credential_ssh_key *c = (git_credential_ssh_key *)cred;
+
+ if (c->privatekey)
+ rc = libssh2_userauth_publickey_fromfile(
+ session, c->username, c->publickey,
+ c->privatekey, c->passphrase);
+ else
+ rc = ssh_agent_auth(session, c);
+
+ break;
+ }
+ case GIT_CREDENTIAL_SSH_CUSTOM: {
+ git_credential_ssh_custom *c = (git_credential_ssh_custom *)cred;
+
+ rc = libssh2_userauth_publickey(
+ session, c->username, (const unsigned char *)c->publickey,
+ c->publickey_len, c->sign_callback, &c->payload);
+ break;
+ }
+ case GIT_CREDENTIAL_SSH_INTERACTIVE: {
+ void **abstract = libssh2_session_abstract(session);
+ git_credential_ssh_interactive *c = (git_credential_ssh_interactive *)cred;
+
+ /* ideally, we should be able to set this by calling
+ * libssh2_session_init_ex() instead of libssh2_session_init().
+ * libssh2's API is inconsistent here i.e. libssh2_userauth_publickey()
+ * allows you to pass the `abstract` as part of the call, whereas
+ * libssh2_userauth_keyboard_interactive() does not!
+ *
+ * The only way to set the `abstract` pointer is by calling
+ * libssh2_session_abstract(), which will replace the existing
+ * pointer as is done below. This is safe for now (at time of writing),
+ * but may not be valid in future.
+ */
+ *abstract = c->payload;
+
+ rc = libssh2_userauth_keyboard_interactive(
+ session, c->username, c->prompt_callback);
+ break;
+ }
+#ifdef GIT_SSH_LIBSSH2_MEMORY_CREDENTIALS
+ case GIT_CREDENTIAL_SSH_MEMORY: {
+ git_credential_ssh_key *c = (git_credential_ssh_key *)cred;
+
+ GIT_ASSERT(c->username);
+ GIT_ASSERT(c->privatekey);
+
+ rc = libssh2_userauth_publickey_frommemory(
+ session,
+ c->username,
+ strlen(c->username),
+ c->publickey,
+ c->publickey ? strlen(c->publickey) : 0,
+ c->privatekey,
+ strlen(c->privatekey),
+ c->passphrase);
+ break;
+ }
+#endif
+ default:
+ rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
+ }
+ } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+
+ if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED ||
+ rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED ||
+ rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED)
+ return GIT_EAUTH;
+
+ if (rc != LIBSSH2_ERROR_NONE) {
+ if (git_error_last()->klass == GIT_ERROR_NONE)
+ ssh_error(session, "failed to authenticate SSH session");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int request_creds(git_credential **out, ssh_subtransport *t, const char *user, int auth_methods)
+{
+ int error, no_callback = 0;
+ git_credential *cred = NULL;
+
+ if (!t->owner->connect_opts.callbacks.credentials) {
+ no_callback = 1;
+ } else {
+ error = t->owner->connect_opts.callbacks.credentials(
+ &cred,
+ t->owner->url,
+ user,
+ auth_methods,
+ t->owner->connect_opts.callbacks.payload);
+
+ if (error == GIT_PASSTHROUGH) {
+ no_callback = 1;
+ } else if (error < 0) {
+ return error;
+ } else if (!cred) {
+ git_error_set(GIT_ERROR_SSH, "callback failed to initialize SSH credentials");
+ return -1;
+ }
+ }
+
+ if (no_callback) {
+ git_error_set(GIT_ERROR_SSH, "authentication required but no callback set");
+ return GIT_EAUTH;
+ }
+
+ if (!(cred->credtype & auth_methods)) {
+ cred->free(cred);
+ git_error_set(GIT_ERROR_SSH, "authentication callback returned unsupported credentials type");
+ return GIT_EAUTH;
+ }
+
+ *out = cred;
+
+ return 0;
+}
+
+#define SSH_DIR ".ssh"
+#define KNOWN_HOSTS_FILE "known_hosts"
+
+/*
+ * Load the known_hosts file.
+ *
+ * Returns success but leaves the output NULL if we couldn't find the file.
+ */
+static int load_known_hosts(LIBSSH2_KNOWNHOSTS **hosts, LIBSSH2_SESSION *session)
+{
+ git_str path = GIT_STR_INIT, sshdir = GIT_STR_INIT;
+ LIBSSH2_KNOWNHOSTS *known_hosts = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(hosts);
+
+ if ((error = git_sysdir_expand_homedir_file(&sshdir, SSH_DIR)) < 0 ||
+ (error = git_str_joinpath(&path, git_str_cstr(&sshdir), KNOWN_HOSTS_FILE)) < 0)
+ goto out;
+
+ if ((known_hosts = libssh2_knownhost_init(session)) == NULL) {
+ ssh_error(session, "error initializing known hosts");
+ error = -1;
+ goto out;
+ }
+
+ /*
+ * Try to read the file and consider not finding it as not trusting the
+ * host rather than an error.
+ */
+ error = libssh2_knownhost_readfile(known_hosts, git_str_cstr(&path), LIBSSH2_KNOWNHOST_FILE_OPENSSH);
+ if (error == LIBSSH2_ERROR_FILE)
+ error = 0;
+ if (error < 0)
+ ssh_error(session, "error reading known_hosts");
+
+out:
+ *hosts = known_hosts;
+
+ git_str_dispose(&sshdir);
+ git_str_dispose(&path);
+
+ return error;
+}
+
+static void add_hostkey_pref_if_avail(
+ LIBSSH2_KNOWNHOSTS *known_hosts,
+ const char *hostname,
+ int port,
+ git_str *prefs,
+ int type,
+ const char *type_name)
+{
+ struct libssh2_knownhost *host = NULL;
+ const char key = '\0';
+ int mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | type;
+ int error;
+
+ error = libssh2_knownhost_checkp(known_hosts, hostname, port, &key, 1, mask, &host);
+ if (error == LIBSSH2_KNOWNHOST_CHECK_MISMATCH) {
+ if (git_str_len(prefs) > 0) {
+ git_str_putc(prefs, ',');
+ }
+ git_str_puts(prefs, type_name);
+ }
+}
+
+/*
+ * We figure out what kind of key we want to ask the remote for by trying to
+ * look it up with a nonsense key and using that mismatch to figure out what key
+ * we do have stored for the host.
+ *
+ * Populates prefs with the string to pass to libssh2_session_method_pref.
+ */
+static void find_hostkey_preference(
+ LIBSSH2_KNOWNHOSTS *known_hosts,
+ const char *hostname,
+ int port,
+ git_str *prefs)
+{
+ /*
+ * The order here is important as it indicates the priority of what will
+ * be preferred.
+ */
+#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ED25519, "ssh-ed25519");
+#endif
+#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_256, "ecdsa-sha2-nistp256");
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_384, "ecdsa-sha2-nistp384");
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_521, "ecdsa-sha2-nistp521");
+#endif
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_SSHRSA, "ssh-rsa");
+}
+
+static int _git_ssh_session_create(
+ LIBSSH2_SESSION **session,
+ LIBSSH2_KNOWNHOSTS **hosts,
+ const char *hostname,
+ int port,
+ git_stream *io)
+{
+ git_socket_stream *socket = GIT_CONTAINER_OF(io, git_socket_stream, parent);
+ LIBSSH2_SESSION *s;
+ LIBSSH2_KNOWNHOSTS *known_hosts;
+ git_str prefs = GIT_STR_INIT;
+ int rc = 0;
+
+ GIT_ASSERT_ARG(session);
+ GIT_ASSERT_ARG(hosts);
+
+ s = libssh2_session_init();
+ if (!s) {
+ git_error_set(GIT_ERROR_NET, "failed to initialize SSH session");
+ return -1;
+ }
+
+ if (git_socket_stream__timeout > 0) {
+ libssh2_session_set_timeout(s, git_socket_stream__timeout);
+ }
+
+ if ((rc = load_known_hosts(&known_hosts, s)) < 0) {
+ ssh_error(s, "error loading known_hosts");
+ libssh2_session_free(s);
+ return -1;
+ }
+
+ find_hostkey_preference(known_hosts, hostname, port, &prefs);
+ if (git_str_len(&prefs) > 0) {
+ do {
+ rc = libssh2_session_method_pref(s, LIBSSH2_METHOD_HOSTKEY, git_str_cstr(&prefs));
+ } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+ if (rc != LIBSSH2_ERROR_NONE) {
+ ssh_error(s, "failed to set hostkey preference");
+ goto on_error;
+ }
+ }
+ git_str_dispose(&prefs);
+
+ do {
+ rc = libssh2_session_handshake(s, socket->s);
+ } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+
+ if (rc != LIBSSH2_ERROR_NONE) {
+ ssh_error(s, "failed to start SSH session");
+ goto on_error;
+ }
+
+ libssh2_session_set_blocking(s, 1);
+
+ *session = s;
+ *hosts = known_hosts;
+
+ return 0;
+
+on_error:
+ libssh2_knownhost_free(known_hosts);
+ libssh2_session_free(s);
+ return -1;
+}
+
+
+/*
+ * Returns the typemask argument to pass to libssh2_knownhost_check{,p} based on
+ * the type of key that libssh2_session_hostkey returns.
+ */
+static int fingerprint_type_mask(int keytype)
+{
+ int mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW;
+ return mask;
+
+ switch (keytype) {
+ case LIBSSH2_HOSTKEY_TYPE_RSA:
+ mask |= LIBSSH2_KNOWNHOST_KEY_SSHRSA;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_DSS:
+ mask |= LIBSSH2_KNOWNHOST_KEY_SSHDSS;
+ break;
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
+ mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
+ mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
+ mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
+ break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
+ case LIBSSH2_HOSTKEY_TYPE_ED25519:
+ mask |= LIBSSH2_KNOWNHOST_KEY_ED25519;
+ break;
+#endif
+ }
+
+ return mask;
+}
+
+/*
+ * Check the host against the user's known_hosts file.
+ *
+ * Returns 1/0 for valid/''not-valid or <0 for an error
+ */
+static int check_against_known_hosts(
+ LIBSSH2_SESSION *session,
+ LIBSSH2_KNOWNHOSTS *known_hosts,
+ const char *hostname,
+ int port,
+ const char *key,
+ size_t key_len,
+ int key_type)
+{
+ int check, typemask, ret = 0;
+ struct libssh2_knownhost *host = NULL;
+
+ if (known_hosts == NULL)
+ return 0;
+
+ typemask = fingerprint_type_mask(key_type);
+ check = libssh2_knownhost_checkp(known_hosts, hostname, port, key, key_len, typemask, &host);
+ if (check == LIBSSH2_KNOWNHOST_CHECK_FAILURE) {
+ ssh_error(session, "error checking for known host");
+ return -1;
+ }
+
+ ret = check == LIBSSH2_KNOWNHOST_CHECK_MATCH ? 1 : 0;
+
+ return ret;
+}
+
+/*
+ * Perform the check for the session's certificate against known hosts if
+ * possible and then ask the user if they have a callback.
+ *
+ * Returns 1/0 for valid/not-valid or <0 for an error
+ */
+static int check_certificate(
+ LIBSSH2_SESSION *session,
+ LIBSSH2_KNOWNHOSTS *known_hosts,
+ git_transport_certificate_check_cb check_cb,
+ void *check_cb_payload,
+ const char *host,
+ int port)
+{
+ git_cert_hostkey cert = {{ 0 }};
+ const char *key;
+ size_t cert_len;
+ int cert_type, cert_valid = 0, error = GIT_ECERTIFICATE;
+
+ if ((key = libssh2_session_hostkey(session, &cert_len, &cert_type)) == NULL) {
+ ssh_error(session, "failed to retrieve hostkey");
+ return -1;
+ }
+
+ if ((cert_valid = check_against_known_hosts(session, known_hosts, host, port, key, cert_len, cert_type)) < 0)
+ return -1;
+
+ cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_RAW;
+ cert.hostkey = key;
+ cert.hostkey_len = cert_len;
+ switch (cert_type) {
+ case LIBSSH2_HOSTKEY_TYPE_RSA:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_RSA;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_DSS:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_DSS;
+ break;
+
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521;
+ break;
+#endif
+
+#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
+ case LIBSSH2_HOSTKEY_TYPE_ED25519:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ED25519;
+ break;
+#endif
+ default:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_UNKNOWN;
+ }
+ }
+
+#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
+ key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256);
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_SHA256;
+ memcpy(&cert.hash_sha256, key, 32);
+ }
+#endif
+
+ key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_SHA1;
+ memcpy(&cert.hash_sha1, key, 20);
+ }
+
+ key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_MD5;
+ memcpy(&cert.hash_md5, key, 16);
+ }
+
+ if (cert.type == 0) {
+ git_error_set(GIT_ERROR_SSH, "unable to get the host key");
+ return -1;
+ }
+
+ if (check_cb != NULL) {
+ git_cert_hostkey *cert_ptr = &cert;
+
+ error = check_cb((git_cert *)cert_ptr, cert_valid, host,
+ check_cb_payload);
+
+ if (error == 0)
+ cert_valid = 1;
+ else if (error != GIT_PASSTHROUGH)
+ cert_valid = 0;
+ }
+
+ if (!cert_valid) {
+ git_error_set(GIT_ERROR_SSH, "invalid or unknown remote ssh hostkey");
+ return (error == GIT_PASSTHROUGH) ? GIT_ECERTIFICATE : error;
+ }
+
+ return 0;
+}
+
+#define SSH_DEFAULT_PORT "22"
+
+static int _git_ssh_setup_conn(
+ ssh_subtransport *t,
+ const char *url,
+ const char *cmd,
+ git_smart_subtransport_stream **stream)
+{
+ int auth_methods, error = 0, port;
+ ssh_stream *s;
+ git_credential *cred = NULL;
+ LIBSSH2_SESSION *session=NULL;
+ LIBSSH2_CHANNEL *channel=NULL;
+ LIBSSH2_KNOWNHOSTS *known_hosts = NULL;
+
+ t->current_stream = NULL;
+
+ *stream = NULL;
+ if (ssh_stream_alloc(t, cmd, stream) < 0)
+ return -1;
+
+ s = (ssh_stream *)*stream;
+ s->session = NULL;
+ s->channel = NULL;
+
+ if (git_net_str_is_url(url))
+ error = git_net_url_parse(&s->url, url);
+ else
+ error = git_net_url_parse_scp(&s->url, url);
+
+ if (error < 0)
+ goto done;
+
+ /* Safety check: like git, we forbid paths that look like an option as
+ * that could lead to injection on the remote side */
+ if (git_process__is_cmdline_option(s->url.path)) {
+ git_error_set(GIT_ERROR_NET, "cannot ssh: path '%s' is ambiguous with command-line option", s->url.path);
+ error = -1;
+ goto done;
+ }
+
+
+ if ((error = git_socket_stream_new(&s->io, s->url.host, s->url.port)) < 0 ||
+ (error = git_stream_connect(s->io)) < 0)
+ goto done;
+
+ /*
+ * Try to parse the port as a number, if we can't then fall back to
+ * default. It would be nice if we could get the port that was resolved
+ * as part of the stream connection, but that's not something that's
+ * exposed.
+ */
+ if (git__strntol32(&port, s->url.port, strlen(s->url.port), NULL, 10) < 0)
+ port = -1;
+
+ if ((error = _git_ssh_session_create(&session, &known_hosts, s->url.host, port, s->io)) < 0)
+ goto done;
+
+ if ((error = check_certificate(session, known_hosts, t->owner->connect_opts.callbacks.certificate_check, t->owner->connect_opts.callbacks.payload, s->url.host, port)) < 0)
+ goto done;
+
+ /* we need the username to ask for auth methods */
+ if (!s->url.username) {
+ if ((error = request_creds(&cred, t, NULL, GIT_CREDENTIAL_USERNAME)) < 0)
+ goto done;
+
+ s->url.username = git__strdup(((git_credential_username *) cred)->username);
+ cred->free(cred);
+ cred = NULL;
+ if (!s->url.username)
+ goto done;
+ } else if (s->url.username && s->url.password) {
+ if ((error = git_credential_userpass_plaintext_new(&cred, s->url.username, s->url.password)) < 0)
+ goto done;
+ }
+
+ if ((error = list_auth_methods(&auth_methods, session, s->url.username)) < 0)
+ goto done;
+
+ error = GIT_EAUTH;
+ /* if we already have something to try */
+ if (cred && auth_methods & cred->credtype)
+ error = _git_ssh_authenticate_session(session, cred);
+
+ while (error == GIT_EAUTH) {
+ if (cred) {
+ cred->free(cred);
+ cred = NULL;
+ }
+
+ if ((error = request_creds(&cred, t, s->url.username, auth_methods)) < 0)
+ goto done;
+
+ if (strcmp(s->url.username, git_credential_get_username(cred))) {
+ git_error_set(GIT_ERROR_SSH, "username does not match previous request");
+ error = -1;
+ goto done;
+ }
+
+ error = _git_ssh_authenticate_session(session, cred);
+
+ if (error == GIT_EAUTH) {
+ /* refresh auth methods */
+ if ((error = list_auth_methods(&auth_methods, session, s->url.username)) < 0)
+ goto done;
+ else
+ error = GIT_EAUTH;
+ }
+ }
+
+ if (error < 0)
+ goto done;
+
+ channel = libssh2_channel_open_session(session);
+ if (!channel) {
+ error = -1;
+ ssh_error(session, "Failed to open SSH channel");
+ goto done;
+ }
+
+ libssh2_channel_set_blocking(channel, 1);
+
+ s->session = session;
+ s->channel = channel;
+
+ t->current_stream = s;
+
+done:
+ if (known_hosts)
+ libssh2_knownhost_free(known_hosts);
+
+ if (error < 0) {
+ ssh_stream_free(*stream);
+
+ if (session)
+ libssh2_session_free(session);
+ }
+
+ if (cred)
+ cred->free(cred);
+
+ return error;
+}
+
+static int ssh_uploadpack_ls(
+ ssh_subtransport *t,
+ const char *url,
+ git_smart_subtransport_stream **stream)
+{
+ const char *cmd = t->cmd_uploadpack ? t->cmd_uploadpack : cmd_uploadpack;
+
+ return _git_ssh_setup_conn(t, url, cmd, stream);
+}
+
+static int ssh_uploadpack(
+ ssh_subtransport *t,
+ const char *url,
+ git_smart_subtransport_stream **stream)
+{
+ GIT_UNUSED(url);
+
+ if (t->current_stream) {
+ *stream = &t->current_stream->parent;
+ return 0;
+ }
+
+ git_error_set(GIT_ERROR_NET, "must call UPLOADPACK_LS before UPLOADPACK");
+ return -1;
+}
+
+static int ssh_receivepack_ls(
+ ssh_subtransport *t,
+ const char *url,
+ git_smart_subtransport_stream **stream)
+{
+ const char *cmd = t->cmd_receivepack ? t->cmd_receivepack : cmd_receivepack;
+
+
+ return _git_ssh_setup_conn(t, url, cmd, stream);
+}
+
+static int ssh_receivepack(
+ ssh_subtransport *t,
+ const char *url,
+ git_smart_subtransport_stream **stream)
+{
+ GIT_UNUSED(url);
+
+ if (t->current_stream) {
+ *stream = &t->current_stream->parent;
+ return 0;
+ }
+
+ git_error_set(GIT_ERROR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK");
+ return -1;
+}
+
+static int _ssh_action(
+ git_smart_subtransport_stream **stream,
+ git_smart_subtransport *subtransport,
+ const char *url,
+ git_smart_service_t action)
+{
+ ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
+
+ switch (action) {
+ case GIT_SERVICE_UPLOADPACK_LS:
+ return ssh_uploadpack_ls(t, url, stream);
+
+ case GIT_SERVICE_UPLOADPACK:
+ return ssh_uploadpack(t, url, stream);
+
+ case GIT_SERVICE_RECEIVEPACK_LS:
+ return ssh_receivepack_ls(t, url, stream);
+
+ case GIT_SERVICE_RECEIVEPACK:
+ return ssh_receivepack(t, url, stream);
+ }
+
+ *stream = NULL;
+ return -1;
+}
+
+static int _ssh_close(git_smart_subtransport *subtransport)
+{
+ ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
+
+ GIT_ASSERT(!t->current_stream);
+
+ GIT_UNUSED(t);
+
+ return 0;
+}
+
+static void _ssh_free(git_smart_subtransport *subtransport)
+{
+ ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
+
+ git__free(t->cmd_uploadpack);
+ git__free(t->cmd_receivepack);
+ git__free(t);
+}
+
+#define SSH_AUTH_PUBLICKEY "publickey"
+#define SSH_AUTH_PASSWORD "password"
+#define SSH_AUTH_KEYBOARD_INTERACTIVE "keyboard-interactive"
+
+static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username)
+{
+ const char *list, *ptr;
+
+ *out = 0;
+
+ list = libssh2_userauth_list(session, username, strlen(username));
+
+ /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
+ if (list == NULL && !libssh2_userauth_authenticated(session)) {
+ ssh_error(session, "remote rejected authentication");
+ return GIT_EAUTH;
+ }
+
+ ptr = list;
+ while (ptr) {
+ if (*ptr == ',')
+ ptr++;
+
+ if (!git__prefixcmp(ptr, SSH_AUTH_PUBLICKEY)) {
+ *out |= GIT_CREDENTIAL_SSH_KEY;
+ *out |= GIT_CREDENTIAL_SSH_CUSTOM;
+#ifdef GIT_SSH_LIBSSH2_MEMORY_CREDENTIALS
+ *out |= GIT_CREDENTIAL_SSH_MEMORY;
+#endif
+ ptr += strlen(SSH_AUTH_PUBLICKEY);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, SSH_AUTH_PASSWORD)) {
+ *out |= GIT_CREDENTIAL_USERPASS_PLAINTEXT;
+ ptr += strlen(SSH_AUTH_PASSWORD);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, SSH_AUTH_KEYBOARD_INTERACTIVE)) {
+ *out |= GIT_CREDENTIAL_SSH_INTERACTIVE;
+ ptr += strlen(SSH_AUTH_KEYBOARD_INTERACTIVE);
+ continue;
+ }
+
+ /* Skip it if we don't know it */
+ ptr = strchr(ptr, ',');
+ }
+
+ return 0;
+}
+
+int git_smart_subtransport_ssh_libssh2(
+ git_smart_subtransport **out,
+ git_transport *owner,
+ void *param)
+{
+ ssh_subtransport *t;
+
+ GIT_ASSERT_ARG(out);
+
+ GIT_UNUSED(param);
+
+ t = git__calloc(sizeof(ssh_subtransport), 1);
+ GIT_ERROR_CHECK_ALLOC(t);
+
+ t->owner = (transport_smart *)owner;
+ t->parent.action = _ssh_action;
+ t->parent.close = _ssh_close;
+ t->parent.free = _ssh_free;
+
+ *out = (git_smart_subtransport *) t;
+ return 0;
+}
+
+int git_smart_subtransport_ssh_libssh2_set_paths(
+ git_smart_subtransport *subtransport,
+ const char *cmd_uploadpack,
+ const char *cmd_receivepack)
+{
+ ssh_subtransport *t = (ssh_subtransport *)subtransport;
+
+ git__free(t->cmd_uploadpack);
+ git__free(t->cmd_receivepack);
+
+ t->cmd_uploadpack = git__strdup(cmd_uploadpack);
+ GIT_ERROR_CHECK_ALLOC(t->cmd_uploadpack);
+
+ t->cmd_receivepack = git__strdup(cmd_receivepack);
+ GIT_ERROR_CHECK_ALLOC(t->cmd_receivepack);
+
+ return 0;
+}
+
+static void shutdown_libssh2(void)
+{
+ libssh2_exit();
+}
+
+int git_transport_ssh_libssh2_global_init(void)
+{
+ if (libssh2_init(0) < 0) {
+ git_error_set(GIT_ERROR_SSH, "unable to initialize libssh2");
+ return -1;
+ }
+
+ return git_runtime_shutdown_register(shutdown_libssh2);
+}
+
+#else /* GIT_SSH */
+
+int git_transport_ssh_libssh2_global_init(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/src/libgit2/transports/ssh_libssh2.h b/src/libgit2/transports/ssh_libssh2.h
new file mode 100644
index 00000000000..3f8cc2a8ad9
--- /dev/null
+++ b/src/libgit2/transports/ssh_libssh2.h
@@ -0,0 +1,28 @@
+/*
+ * 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_transports_libssh2_h__
+#define INCLUDE_transports_libssh2_h__
+
+#include "common.h"
+
+#include "git2.h"
+#include "git2/transport.h"
+#include "git2/sys/transport.h"
+
+int git_transport_ssh_libssh2_global_init(void);
+
+int git_smart_subtransport_ssh_libssh2(
+ git_smart_subtransport **out,
+ git_transport *owner,
+ void *param);
+
+int git_smart_subtransport_ssh_libssh2_set_paths(
+ git_smart_subtransport *subtransport,
+ const char *cmd_uploadpack,
+ const char *cmd_receivepack);
+
+#endif
diff --git a/src/libgit2/transports/winhttp.c b/src/libgit2/transports/winhttp.c
index 09822760799..b83ef990de6 100644
--- a/src/libgit2/transports/winhttp.c
+++ b/src/libgit2/transports/winhttp.c
@@ -13,7 +13,6 @@
#include "git2/transport.h"
#include "posix.h"
#include "str.h"
-#include "netops.h"
#include "smart.h"
#include "remote.h"
#include "repository.h"
@@ -158,10 +157,10 @@ static int apply_userpass_credentials(HINTERNET request, DWORD target, int mecha
goto done;
}
- if ((error = user_len = git__utf8_to_16_alloc(&user, c->username)) < 0)
+ if ((error = user_len = git_utf8_to_16_alloc(&user, c->username)) < 0)
goto done;
- if ((error = pass_len = git__utf8_to_16_alloc(&pass, c->password)) < 0)
+ if ((error = pass_len = git_utf8_to_16_alloc(&pass, c->password)) < 0)
goto done;
if (!WinHttpSetCredentials(request, target, native_scheme, user, pass, NULL)) {
@@ -242,7 +241,7 @@ static int acquire_fallback_cred(
HRESULT hCoInitResult;
/* Convert URL to wide characters */
- if (git__utf8_to_16_alloc(&wide_url, url) < 0) {
+ if (git_utf8_to_16_alloc(&wide_url, url) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert string to wide form");
return -1;
}
@@ -294,7 +293,7 @@ static int certificate_check(winhttp_stream *s, int valid)
/* If there is no override, we should fail if WinHTTP doesn't think it's fine */
if (t->owner->connect_opts.callbacks.certificate_check == NULL && !valid) {
- if (!git_error_last())
+ if (git_error_last()->klass == GIT_ERROR_NONE)
git_error_set(GIT_ERROR_HTTP, "unknown certificate check failure");
return GIT_ECERTIFICATE;
@@ -318,7 +317,7 @@ static int certificate_check(winhttp_stream *s, int valid)
if (error == GIT_PASSTHROUGH)
error = valid ? 0 : GIT_ECERTIFICATE;
- if (error < 0 && !git_error_last())
+ if (error < 0 && git_error_last()->klass == GIT_ERROR_NONE)
git_error_set(GIT_ERROR_HTTP, "user cancelled certificate check");
return error;
@@ -397,7 +396,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
return -1;
/* Convert URL to wide characters */
- if (git__utf8_to_16_alloc(&s->request_uri, git_str_cstr(&buf)) < 0) {
+ if (git_utf8_to_16_alloc(&s->request_uri, git_str_cstr(&buf)) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert string to wide form");
goto on_error;
}
@@ -437,17 +436,17 @@ static int winhttp_stream_connect(winhttp_stream *s)
GIT_ERROR_CHECK_ALLOC(proxy_url);
}
- if (proxy_url) {
+ if (proxy_url && *proxy_url) {
git_str processed_url = GIT_STR_INIT;
WINHTTP_PROXY_INFO proxy_info;
wchar_t *proxy_wide;
git_net_url_dispose(&t->proxy.url);
- if ((error = git_net_url_parse(&t->proxy.url, proxy_url)) < 0)
+ if ((error = git_net_url_parse_http(&t->proxy.url, proxy_url)) < 0)
goto on_error;
- if (strcmp(t->proxy.url.scheme, "http") != 0 && strcmp(t->proxy.url.scheme, "https") != 0) {
+ if (!git_net_url_valid(&t->proxy.url)) {
git_error_set(GIT_ERROR_HTTP, "invalid URL: '%s'", proxy_url);
error = -1;
goto on_error;
@@ -473,7 +472,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
/* Convert URL to wide characters */
- error = git__utf8_to_16_alloc(&proxy_wide, processed_url.ptr);
+ error = git_utf8_to_16_alloc(&proxy_wide, processed_url.ptr);
git_str_dispose(&processed_url);
if (error < 0)
goto on_error;
@@ -531,7 +530,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
s->service) < 0)
goto on_error;
- if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
+ if (git_utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert content-type to wide characters");
goto on_error;
}
@@ -548,7 +547,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
s->service) < 0)
goto on_error;
- if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
+ if (git_utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert accept header to wide characters");
goto on_error;
}
@@ -568,7 +567,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
git_str_puts(&buf, t->owner->connect_opts.custom_headers.strings[i]);
/* Convert header to wide characters */
- if ((error = git__utf8_to_16_alloc(&custom_header_wide, git_str_cstr(&buf))) < 0)
+ if ((error = git_utf8_to_16_alloc(&custom_header_wide, git_str_cstr(&buf))) < 0)
goto on_error;
if (!WinHttpAddRequestHeaders(s->request, custom_header_wide, (ULONG)-1L,
@@ -747,6 +746,33 @@ static void CALLBACK winhttp_status(
}
}
+static int user_agent(bool *exists, git_str *out)
+{
+ const char *product = git_settings__user_agent_product();
+ const char *comment = git_settings__user_agent();
+
+ GIT_ASSERT(product && comment);
+
+ if (!*product) {
+ *exists = false;
+ return 0;
+ }
+
+ git_str_puts(out, product);
+
+ if (*comment) {
+ git_str_puts(out, " (");
+ git_str_puts(out, comment);
+ git_str_puts(out, ")");
+ }
+
+ if (git_str_oom(out))
+ return -1;
+
+ *exists = true;
+ return 0;
+}
+
static int winhttp_connect(
winhttp_subtransport *t)
{
@@ -758,6 +784,7 @@ static int winhttp_connect(
int error = -1;
int default_timeout = TIMEOUT_INFINITE;
int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
+ bool has_ua = true;
DWORD protocols =
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
@@ -783,16 +810,16 @@ static int winhttp_connect(
}
/* Prepare host */
- if (git__utf8_to_16_alloc(&wide_host, host) < 0) {
+ if (git_utf8_to_16_alloc(&wide_host, host) < 0) {
git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters");
goto on_error;
}
-
- if (git_http__user_agent(&ua) < 0)
+ if (user_agent(&has_ua, &ua) < 0)
goto on_error;
- if (git__utf8_to_16_alloc(&wide_ua, git_str_cstr(&ua)) < 0) {
+ if (has_ua &&
+ git_utf8_to_16_alloc(&wide_ua, git_str_cstr(&ua)) < 0) {
git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters");
goto on_error;
}
@@ -934,7 +961,7 @@ static int send_request(winhttp_stream *s, size_t len, bool chunked)
(!request_failed && s->status_sending_request_reached)) {
git_error_clear();
if ((error = certificate_check(s, cert_valid)) < 0) {
- if (!git_error_last())
+ if (git_error_last()->klass == GIT_ERROR_NONE)
git_error_set(GIT_ERROR_OS, "user cancelled certificate check");
return error;
@@ -1182,7 +1209,7 @@ static int winhttp_stream_read(
}
/* Convert the Location header to UTF-8 */
- if (git__utf16_to_8_alloc(&location8, location) < 0) {
+ if (git_utf8_from_16_alloc(&location8, location) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert Location header to UTF-8");
git__free(location);
return -1;
@@ -1254,7 +1281,7 @@ static int winhttp_stream_read(
else
p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service);
- if (git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) {
+ if (git_utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert expected content-type to wide characters");
return -1;
}
diff --git a/src/libgit2/tree-cache.c b/src/libgit2/tree-cache.c
index 19fad85aea3..95d879860f1 100644
--- a/src/libgit2/tree-cache.c
+++ b/src/libgit2/tree-cache.c
@@ -71,12 +71,16 @@ const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char
}
}
-static int read_tree_internal(git_tree_cache **out,
- const char **buffer_in, const char *buffer_end,
- git_pool *pool)
+static int read_tree_internal(
+ git_tree_cache **out,
+ const char **buffer_in,
+ const char *buffer_end,
+ git_oid_t oid_type,
+ git_pool *pool)
{
git_tree_cache *tree = NULL;
const char *name_start, *buffer;
+ size_t oid_size = git_oid_size(oid_type);
int count;
buffer = name_start = *buffer_in;
@@ -87,7 +91,7 @@ static int read_tree_internal(git_tree_cache **out,
if (++buffer >= buffer_end)
goto corrupted;
- if (git_tree_cache_new(&tree, name_start, pool) < 0)
+ if (git_tree_cache_new(&tree, name_start, oid_type, pool) < 0)
return -1;
/* Blank-terminated ASCII decimal number of entries in this tree */
@@ -108,14 +112,14 @@ static int read_tree_internal(git_tree_cache **out,
if (*buffer != '\n' || ++buffer > buffer_end)
goto corrupted;
- /* The SHA1 is only there if it's not invalidated */
+ /* The OID is only there if it's not invalidated */
if (tree->entry_count >= 0) {
/* 160-bit SHA-1 for this tree and it's children */
- if (buffer + GIT_OID_SHA1_SIZE > buffer_end)
+ if (buffer + oid_size > buffer_end)
goto corrupted;
- git_oid__fromraw(&tree->oid, (const unsigned char *)buffer, GIT_OID_SHA1);
- buffer += GIT_OID_SHA1_SIZE;
+ git_oid__fromraw(&tree->oid, (const unsigned char *)buffer, oid_type);
+ buffer += oid_size;
}
/* Parse children: */
@@ -130,7 +134,7 @@ static int read_tree_internal(git_tree_cache **out,
memset(tree->children, 0x0, bufsize);
for (i = 0; i < tree->children_count; ++i) {
- if (read_tree_internal(&tree->children[i], &buffer, buffer_end, pool) < 0)
+ if (read_tree_internal(&tree->children[i], &buffer, buffer_end, oid_type, pool) < 0)
goto corrupted;
}
}
@@ -144,11 +148,16 @@ static int read_tree_internal(git_tree_cache **out,
return -1;
}
-int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_pool *pool)
+int git_tree_cache_read(
+ git_tree_cache **tree,
+ const char *buffer,
+ size_t buffer_size,
+ git_oid_t oid_type,
+ git_pool *pool)
{
const char *buffer_end = buffer + buffer_size;
- if (read_tree_internal(tree, &buffer, buffer_end, pool) < 0)
+ if (read_tree_internal(tree, &buffer, buffer_end, oid_type, pool) < 0)
return -1;
if (buffer < buffer_end) {
@@ -201,7 +210,7 @@ static int read_tree_recursive(git_tree_cache *cache, const git_tree *tree, git_
continue;
}
- if ((error = git_tree_cache_new(&cache->children[j], git_tree_entry_name(entry), pool)) < 0)
+ if ((error = git_tree_cache_new(&cache->children[j], git_tree_entry_name(entry), cache->oid_type, pool)) < 0)
return error;
if ((error = git_tree_lookup(&subtree, repo, git_tree_entry_id(entry))) < 0)
@@ -219,12 +228,12 @@ static int read_tree_recursive(git_tree_cache *cache, const git_tree *tree, git_
return 0;
}
-int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_pool *pool)
+int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_oid_t oid_type, git_pool *pool)
{
int error;
git_tree_cache *cache;
- if ((error = git_tree_cache_new(&cache, "", pool)) < 0)
+ if ((error = git_tree_cache_new(&cache, "", oid_type, pool)) < 0)
return error;
if ((error = read_tree_recursive(cache, tree, pool)) < 0)
@@ -234,7 +243,7 @@ int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_poo
return 0;
}
-int git_tree_cache_new(git_tree_cache **out, const char *name, git_pool *pool)
+int git_tree_cache_new(git_tree_cache **out, const char *name, git_oid_t oid_type, git_pool *pool)
{
size_t name_len, alloc_size;
git_tree_cache *tree;
@@ -248,6 +257,7 @@ int git_tree_cache_new(git_tree_cache **out, const char *name, git_pool *pool)
memset(tree, 0x0, sizeof(git_tree_cache));
/* NUL-terminated tree name */
+ tree->oid_type = oid_type;
tree->namelen = name_len;
memcpy(tree->name, name, name_len);
tree->name[name_len] = '\0';
@@ -263,7 +273,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, (char *)&tree->oid.id, GIT_OID_SHA1_SIZE);
+ git_str_put(out, (char *)&tree->oid.id, git_oid_size(tree->oid_type));
for (i = 0; i < tree->children_count; i++)
write_tree(out, tree->children[i]);
diff --git a/src/libgit2/tree-cache.h b/src/libgit2/tree-cache.h
index a27e3046614..e4a73f277e0 100644
--- a/src/libgit2/tree-cache.h
+++ b/src/libgit2/tree-cache.h
@@ -18,6 +18,8 @@ typedef struct git_tree_cache {
struct git_tree_cache **children;
size_t children_count;
+ git_oid_t oid_type;
+
ssize_t entry_count;
git_oid oid;
size_t namelen;
@@ -25,14 +27,14 @@ typedef struct git_tree_cache {
} git_tree_cache;
int git_tree_cache_write(git_str *out, git_tree_cache *tree);
-int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_pool *pool);
+int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_oid_t oid_type, git_pool *pool);
void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path);
const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path);
-int git_tree_cache_new(git_tree_cache **out, const char *name, git_pool *pool);
+int git_tree_cache_new(git_tree_cache **out, const char *name, git_oid_t oid_type, git_pool *pool);
/**
* Read a tree as the root of the tree cache (like for `git read-tree`)
*/
-int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_pool *pool);
+int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_oid_t oid_type, git_pool *pool);
void git_tree_cache_free(git_tree_cache *tree);
#endif
diff --git a/src/libgit2/tree.c b/src/libgit2/tree.c
index 9a43d585c4f..18278d34e77 100644
--- a/src/libgit2/tree.c
+++ b/src/libgit2/tree.c
@@ -85,11 +85,17 @@ static git_tree_entry *alloc_entry(const char *filename, size_t filename_len, co
char *filename_ptr;
size_t tree_len;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ size_t oid_size = git_oid_size(id->type);
+#else
+ size_t oid_size = GIT_OID_SHA1_SIZE;
+#endif
+
TREE_ENTRY_CHECK_NAMELEN(filename_len);
if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) ||
GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1) ||
- GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, GIT_OID_SHA1_SIZE))
+ GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, oid_size))
return NULL;
entry = git__calloc(1, tree_len);
@@ -375,7 +381,7 @@ static int parse_mode(uint16_t *mode_out, const char *buffer, size_t buffer_len,
if ((error = git__strntol32(&mode, buffer, buffer_len, buffer_out, 8)) < 0)
return error;
- if (mode < 0 || mode > UINT16_MAX)
+ if (mode < 0 || (uint32_t)mode > UINT16_MAX)
return -1;
*mode_out = mode;
@@ -383,11 +389,12 @@ static int parse_mode(uint16_t *mode_out, const char *buffer, size_t buffer_len,
return 0;
}
-int git_tree__parse_raw(void *_tree, const char *data, size_t size)
+int git_tree__parse_raw(void *_tree, const char *data, size_t size, git_oid_t oid_type)
{
git_tree *tree = _tree;
const char *buffer;
const char *buffer_end;
+ const long oid_size = (long)git_oid_size(oid_type);
buffer = data;
buffer_end = buffer + size;
@@ -414,35 +421,33 @@ int git_tree__parse_raw(void *_tree, const char *data, size_t size)
if ((filename_len = nul - buffer) == 0 || filename_len > UINT16_MAX)
return tree_parse_error("failed to parse tree: can't parse filename", NULL);
- if ((buffer_end - (nul + 1)) < GIT_OID_SHA1_SIZE)
+ if ((buffer_end - (nul + 1)) < (long)oid_size)
return tree_parse_error("failed to parse tree: can't parse OID", NULL);
/* Allocate the entry */
- {
- entry = git_array_alloc(tree->entries);
- GIT_ERROR_CHECK_ALLOC(entry);
-
- entry->attr = attr;
- entry->filename_len = (uint16_t)filename_len;
- entry->filename = buffer;
- git_oid__fromraw(&entry->oid, ((unsigned char *) buffer + filename_len + 1), GIT_OID_SHA1);
- }
+ entry = git_array_alloc(tree->entries);
+ GIT_ERROR_CHECK_ALLOC(entry);
+ entry->attr = attr;
+ entry->filename_len = (uint16_t)filename_len;
+ entry->filename = buffer;
buffer += filename_len + 1;
- buffer += GIT_OID_SHA1_SIZE;
+
+ git_oid__fromraw(&entry->oid, (unsigned char *)buffer, oid_type);
+ buffer += oid_size;
}
return 0;
}
-int git_tree__parse(void *_tree, git_odb_object *odb_obj)
+int git_tree__parse(void *_tree, git_odb_object *odb_obj, git_oid_t oid_type)
{
git_tree *tree = _tree;
const char *data = git_odb_object_data(odb_obj);
size_t size = git_odb_object_size(odb_obj);
int error;
- if ((error = git_tree__parse_raw(tree, data, size)) < 0 ||
+ if ((error = git_tree__parse_raw(tree, data, size, oid_type)) < 0 ||
(error = git_odb_object_dup(&tree->odb_obj, odb_obj)) < 0)
return error;
@@ -506,6 +511,7 @@ static int git_treebuilder__write_with_buffer(
git_odb *odb;
git_tree_entry *entry;
git_vector entries = GIT_VECTOR_INIT;
+ size_t oid_size = git_oid_size(bld->repo->oid_type);
git_str_clear(buf);
@@ -529,7 +535,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_SHA1_SIZE);
+ git_str_put(buf, (char *)entry->oid.id, oid_size);
if (git_str_oom(buf)) {
error = -1;
@@ -725,7 +731,7 @@ int git_tree__write_index(
return ret;
/* Read the tree cache into the index */
- ret = git_tree_cache_read_tree(&index->tree, tree, &index->tree_pool);
+ ret = git_tree_cache_read_tree(&index->tree, tree, index->oid_type, &index->tree_pool);
git_tree_free(tree);
return ret;
diff --git a/src/libgit2/tree.h b/src/libgit2/tree.h
index 0dd963ff291..5088450ab9b 100644
--- a/src/libgit2/tree.h
+++ b/src/libgit2/tree.h
@@ -41,8 +41,8 @@ GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
}
void git_tree__free(void *tree);
-int git_tree__parse(void *tree, git_odb_object *obj);
-int git_tree__parse_raw(void *_tree, const char *data, size_t size);
+int git_tree__parse(void *tree, git_odb_object *obj, git_oid_t oid_type);
+int git_tree__parse_raw(void *_tree, const char *data, size_t size, git_oid_t oid_type);
/**
* Write a tree to the given repository
diff --git a/src/libgit2/worktree.c b/src/libgit2/worktree.c
index 82e1d2d7e18..00ff9e7da6c 100644
--- a/src/libgit2/worktree.c
+++ b/src/libgit2/worktree.c
@@ -335,11 +335,21 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
goto out;
}
- if (git_branch_is_checked_out(wtopts.ref)) {
- git_error_set(GIT_ERROR_WORKTREE, "reference is already checked out");
- err = -1;
+ if ((err = git_reference_dup(&ref, wtopts.ref)) < 0)
goto out;
- }
+ } else if (wtopts.checkout_existing && git_branch_lookup(&ref, repo, name, GIT_BRANCH_LOCAL) == 0) {
+ /* Do nothing */
+ } else if ((err = git_repository_head(&head, repo)) < 0 ||
+ (err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0 ||
+ (err = git_branch_create(&ref, repo, name, commit, false)) < 0) {
+ goto out;
+ }
+
+ if (git_branch_is_checked_out(ref)) {
+ git_error_set(GIT_ERROR_WORKTREE, "reference %s is already checked out",
+ git_reference_name(ref));
+ err = -1;
+ goto out;
}
/* Create gitdir directory ".git/worktrees/" */
@@ -392,19 +402,6 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
|| (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0)
goto out;
- /* Set up worktree reference */
- if (wtopts.ref) {
- if ((err = git_reference_dup(&ref, wtopts.ref)) < 0)
- goto out;
- } else {
- if ((err = git_repository_head(&head, repo)) < 0)
- goto out;
- if ((err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0)
- goto out;
- if ((err = git_branch_create(&ref, repo, name, commit, false)) < 0)
- goto out;
- }
-
/* Set worktree's HEAD */
if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0)
goto out;
@@ -565,6 +562,8 @@ int git_worktree_is_prunable(git_worktree *wt,
git_worktree_prune_options *opts)
{
git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+ git_str path = GIT_STR_INIT;
+ int ret = 0;
GIT_ERROR_CHECK_VERSION(
opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION,
@@ -575,27 +574,40 @@ int git_worktree_is_prunable(git_worktree *wt,
if ((popts.flags & GIT_WORKTREE_PRUNE_LOCKED) == 0) {
git_str reason = GIT_STR_INIT;
- int error;
- if ((error = git_worktree__is_locked(&reason, wt)) < 0)
- return error;
+ if ((ret = git_worktree__is_locked(&reason, wt)) < 0)
+ goto out;
+
+ if (ret) {
+ git_error_set(GIT_ERROR_WORKTREE,
+ "not pruning locked working tree: '%s'",
+ reason.size ? reason.ptr : "is locked");
- if (error) {
- if (!reason.size)
- git_str_attach_notowned(&reason, "no reason given", 15);
- git_error_set(GIT_ERROR_WORKTREE, "not pruning locked working tree: '%s'", reason.ptr);
git_str_dispose(&reason);
- return 0;
+ ret = 0;
+ goto out;
}
}
if ((popts.flags & GIT_WORKTREE_PRUNE_VALID) == 0 &&
git_worktree_validate(wt) == 0) {
git_error_set(GIT_ERROR_WORKTREE, "not pruning valid working tree");
- return 0;
+ goto out;
+ }
+
+ if ((ret = git_str_printf(&path, "%s/worktrees/%s", wt->commondir_path, wt->name) < 0))
+ goto out;
+
+ if (!git_fs_path_exists(path.ptr)) {
+ git_error_set(GIT_ERROR_WORKTREE, "worktree gitdir ('%s') does not exist", path.ptr);
+ goto out;
}
- return 1;
+ ret = 1;
+
+out:
+ git_str_dispose(&path);
+ return ret;
}
int git_worktree_prune(git_worktree *wt,
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
index b12ce409b85..ee35eb9610e 100644
--- a/src/util/CMakeLists.txt
+++ b/src/util/CMakeLists.txt
@@ -9,7 +9,6 @@ configure_file(git2_features.h.in git2_features.h)
set(UTIL_INCLUDES
"${PROJECT_BINARY_DIR}/src/util"
"${PROJECT_BINARY_DIR}/include"
- "${PROJECT_BINARY_DIR}/include/git2"
"${PROJECT_SOURCE_DIR}/src/util"
"${PROJECT_SOURCE_DIR}/include")
@@ -38,6 +37,7 @@ if(USE_SHA1 STREQUAL "CollisionDetection")
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")
+ add_definitions(-DOPENSSL_API_COMPAT=0x10100000L)
file(GLOB UTIL_SRC_SHA1 hash/openssl.*)
elseif(USE_SHA1 STREQUAL "CommonCrypto")
file(GLOB UTIL_SRC_SHA1 hash/common_crypto.*)
@@ -54,6 +54,7 @@ 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")
+ add_definitions(-DOPENSSL_API_COMPAT=0x10100000L)
file(GLOB UTIL_SRC_SHA256 hash/openssl.*)
elseif(USE_SHA256 STREQUAL "CommonCrypto")
file(GLOB UTIL_SRC_SHA256 hash/common_crypto.*)
diff --git a/src/util/alloc.c b/src/util/alloc.c
index 2820d84a219..998b0aea1d9 100644
--- a/src/util/alloc.c
+++ b/src/util/alloc.c
@@ -8,27 +8,89 @@
#include "alloc.h"
#include "runtime.h"
-#include "allocators/failalloc.h"
#include "allocators/stdalloc.h"
+#include "allocators/debugalloc.h"
+#include "allocators/failalloc.h"
#include "allocators/win32_leakcheck.h"
/* Fail any allocation until git_libgit2_init is called. */
git_allocator git__allocator = {
git_failalloc_malloc,
- git_failalloc_calloc,
- git_failalloc_strdup,
- git_failalloc_strndup,
- git_failalloc_substrdup,
git_failalloc_realloc,
- git_failalloc_reallocarray,
- git_failalloc_mallocarray,
git_failalloc_free
};
+void *git__calloc(size_t nelem, size_t elsize)
+{
+ size_t newsize;
+ void *ptr;
+
+ if (GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize))
+ return NULL;
+
+ if ((ptr = git__malloc(newsize)))
+ memset(ptr, 0, newsize);
+
+ return ptr;
+}
+
+void *git__reallocarray(void *ptr, size_t nelem, size_t elsize)
+{
+ size_t newsize;
+
+ if (GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize))
+ return NULL;
+
+ return git__realloc(ptr, newsize);
+}
+
+void *git__mallocarray(size_t nelem, size_t elsize)
+{
+ return git__reallocarray(NULL, nelem, elsize);
+}
+
+char *git__strdup(const char *str)
+{
+ size_t len = strlen(str) + 1;
+ void *ptr = git__malloc(len);
+
+ if (ptr)
+ memcpy(ptr, str, len);
+
+ return ptr;
+}
+
+char *git__strndup(const char *str, size_t n)
+{
+ size_t len = p_strnlen(str, n);
+ char *ptr = git__malloc(len + 1);
+
+ if (ptr) {
+ memcpy(ptr, str, len);
+ ptr[len] = '\0';
+ }
+
+ return ptr;
+}
+
+char *git__substrdup(const char *str, size_t n)
+{
+ char *ptr = git__malloc(n + 1);
+
+ if (ptr) {
+ memcpy(ptr, str, n);
+ ptr[n] = '\0';
+ }
+
+ return ptr;
+}
+
static int setup_default_allocator(void)
{
#if defined(GIT_WIN32_LEAKCHECK)
return git_win32_leakcheck_init_allocator(&git__allocator);
+#elif defined(GIT_DEBUG_STRICT_ALLOC)
+ return git_debugalloc_init_allocator(&git__allocator);
#else
return git_stdalloc_init_allocator(&git__allocator);
#endif
diff --git a/src/util/alloc.h b/src/util/alloc.h
index 04fb7e10175..32b614b25cc 100644
--- a/src/util/alloc.h
+++ b/src/util/alloc.h
@@ -10,17 +10,42 @@
#include "git2/sys/alloc.h"
+#include "git2_util.h"
+
extern git_allocator git__allocator;
-#define git__malloc(len) git__allocator.gmalloc(len, __FILE__, __LINE__)
-#define git__calloc(nelem, elsize) git__allocator.gcalloc(nelem, elsize, __FILE__, __LINE__)
-#define git__strdup(str) git__allocator.gstrdup(str, __FILE__, __LINE__)
-#define git__strndup(str, n) git__allocator.gstrndup(str, n, __FILE__, __LINE__)
-#define git__substrdup(str, n) git__allocator.gsubstrdup(str, n, __FILE__, __LINE__)
-#define git__realloc(ptr, size) git__allocator.grealloc(ptr, size, __FILE__, __LINE__)
-#define git__reallocarray(ptr, nelem, elsize) git__allocator.greallocarray(ptr, nelem, elsize, __FILE__, __LINE__)
-#define git__mallocarray(nelem, elsize) git__allocator.gmallocarray(nelem, elsize, __FILE__, __LINE__)
-#define git__free git__allocator.gfree
+GIT_INLINE(void *) git__malloc(size_t len)
+{
+ void *p = git__allocator.gmalloc(len, __FILE__, __LINE__);
+
+ if (!p)
+ git_error_set_oom();
+
+ return p;
+}
+
+GIT_INLINE(void *) git__realloc(void *ptr, size_t size)
+{
+ void *p = git__allocator.grealloc(ptr, size, __FILE__, __LINE__);
+
+ if (!p)
+ git_error_set_oom();
+
+ return p;
+}
+
+GIT_INLINE(void) git__free(void *ptr)
+{
+ git__allocator.gfree(ptr);
+}
+
+extern void *git__calloc(size_t nelem, size_t elsize);
+extern void *git__mallocarray(size_t nelem, size_t elsize);
+extern void *git__reallocarray(void *ptr, size_t nelem, size_t elsize);
+
+extern char *git__strdup(const char *str);
+extern char *git__strndup(const char *str, size_t n);
+extern char *git__substrdup(const char *str, size_t n);
/**
* This function is being called by our global setup routines to
diff --git a/src/util/allocators/debugalloc.c b/src/util/allocators/debugalloc.c
new file mode 100644
index 00000000000..44022cd785a
--- /dev/null
+++ b/src/util/allocators/debugalloc.c
@@ -0,0 +1,73 @@
+/*
+ * 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 "debugalloc.h"
+
+static void *debugalloc__malloc(size_t len, const char *file, int line)
+{
+ unsigned char *ptr;
+ size_t total = len + sizeof(size_t);
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ if (!len || (ptr = malloc(total)) == NULL)
+ return NULL;
+
+ memcpy(ptr, &len, sizeof(size_t));
+ return ptr + sizeof(size_t);
+}
+
+static void *debugalloc__realloc(void *_ptr, size_t len, const char *file, int line)
+{
+ unsigned char *ptr = _ptr, *newptr;
+ size_t original_len;
+ size_t total = len + sizeof(size_t);
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ if (!len && !ptr)
+ return NULL;
+
+ if (!len) {
+ free(ptr - sizeof(size_t));
+ return NULL;
+ }
+
+ if ((newptr = malloc(total)) == NULL)
+ return NULL;
+
+ if (ptr) {
+ memcpy(&original_len, ptr - sizeof(size_t), sizeof(size_t));
+ memcpy(newptr + sizeof(size_t), ptr, min(len, original_len));
+
+ memset(ptr - sizeof(size_t), 0xfd, original_len + sizeof(size_t));
+ free(ptr - sizeof(size_t));
+ }
+
+ memcpy(newptr, &len, sizeof(size_t));
+ return newptr + sizeof(size_t);
+}
+
+static void debugalloc__free(void *_ptr)
+{
+ unsigned char *ptr = _ptr;
+
+ if (!ptr)
+ return;
+
+ free(ptr - sizeof(size_t));
+}
+
+int git_debugalloc_init_allocator(git_allocator *allocator)
+{
+ allocator->gmalloc = debugalloc__malloc;
+ allocator->grealloc = debugalloc__realloc;
+ allocator->gfree = debugalloc__free;
+ return 0;
+}
diff --git a/src/cli/cli.h b/src/util/allocators/debugalloc.h
similarity index 57%
rename from src/cli/cli.h
rename to src/util/allocators/debugalloc.h
index 7dede678519..dea0ca31cc1 100644
--- a/src/cli/cli.h
+++ b/src/util/allocators/debugalloc.h
@@ -5,16 +5,13 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef CLI_cli_h__
-#define CLI_cli_h__
-
-#define PROGRAM_NAME "git2"
+#ifndef INCLUDE_allocators_debugalloc_h__
+#define INCLUDE_allocators_debugalloc_h__
#include "git2_util.h"
-#include "error.h"
-#include "opt.h"
-#include "opt_usage.h"
-#include "sighandler.h"
+#include "alloc.h"
+
+int git_debugalloc_init_allocator(git_allocator *allocator);
-#endif /* CLI_cli_h__ */
+#endif
diff --git a/src/util/allocators/failalloc.c b/src/util/allocators/failalloc.c
index 5257d1dece0..c1025e32fef 100644
--- a/src/util/allocators/failalloc.c
+++ b/src/util/allocators/failalloc.c
@@ -16,45 +16,6 @@ void *git_failalloc_malloc(size_t len, const char *file, int line)
return NULL;
}
-void *git_failalloc_calloc(size_t nelem, size_t elsize, const char *file, int line)
-{
- GIT_UNUSED(nelem);
- GIT_UNUSED(elsize);
- GIT_UNUSED(file);
- GIT_UNUSED(line);
-
- return NULL;
-}
-
-char *git_failalloc_strdup(const char *str, const char *file, int line)
-{
- GIT_UNUSED(str);
- GIT_UNUSED(file);
- GIT_UNUSED(line);
-
- return NULL;
-}
-
-char *git_failalloc_strndup(const char *str, size_t n, const char *file, int line)
-{
- GIT_UNUSED(str);
- GIT_UNUSED(n);
- GIT_UNUSED(file);
- GIT_UNUSED(line);
-
- return NULL;
-}
-
-char *git_failalloc_substrdup(const char *start, size_t n, const char *file, int line)
-{
- GIT_UNUSED(start);
- GIT_UNUSED(n);
- GIT_UNUSED(file);
- GIT_UNUSED(line);
-
- return NULL;
-}
-
void *git_failalloc_realloc(void *ptr, size_t size, const char *file, int line)
{
GIT_UNUSED(ptr);
@@ -65,27 +26,6 @@ void *git_failalloc_realloc(void *ptr, size_t size, const char *file, int line)
return NULL;
}
-void *git_failalloc_reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
-{
- GIT_UNUSED(ptr);
- GIT_UNUSED(nelem);
- GIT_UNUSED(elsize);
- GIT_UNUSED(file);
- GIT_UNUSED(line);
-
- return NULL;
-}
-
-void *git_failalloc_mallocarray(size_t nelem, size_t elsize, const char *file, int line)
-{
- GIT_UNUSED(nelem);
- GIT_UNUSED(elsize);
- GIT_UNUSED(file);
- GIT_UNUSED(line);
-
- return NULL;
-}
-
void git_failalloc_free(void *ptr)
{
GIT_UNUSED(ptr);
diff --git a/src/util/allocators/failalloc.h b/src/util/allocators/failalloc.h
index 91264a0bb91..a3788e634ee 100644
--- a/src/util/allocators/failalloc.h
+++ b/src/util/allocators/failalloc.h
@@ -11,13 +11,7 @@
#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);
-extern char *git_failalloc_strdup(const char *str, const char *file, int line);
-extern char *git_failalloc_strndup(const char *str, size_t n, const char *file, int line);
-extern char *git_failalloc_substrdup(const char *start, size_t n, const char *file, int line);
extern void *git_failalloc_realloc(void *ptr, size_t size, const char *file, int line);
-extern void *git_failalloc_reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line);
-extern void *git_failalloc_mallocarray(size_t nelem, size_t elsize, const char *file, int line);
extern void git_failalloc_free(void *ptr);
#endif
diff --git a/src/util/allocators/stdalloc.c b/src/util/allocators/stdalloc.c
index 2b36d9f3df3..65ec40fbe9d 100644
--- a/src/util/allocators/stdalloc.c
+++ b/src/util/allocators/stdalloc.c
@@ -9,125 +9,18 @@
static void *stdalloc__malloc(size_t len, const char *file, int line)
{
- void *ptr;
-
- GIT_UNUSED(file);
- GIT_UNUSED(line);
-
-#ifdef GIT_DEBUG_STRICT_ALLOC
- if (!len)
- return NULL;
-#endif
-
- ptr = malloc(len);
-
- if (!ptr)
- git_error_set_oom();
-
- return ptr;
-}
-
-static void *stdalloc__calloc(size_t nelem, size_t elsize, const char *file, int line)
-{
- void *ptr;
-
- GIT_UNUSED(file);
- GIT_UNUSED(line);
-
-#ifdef GIT_DEBUG_STRICT_ALLOC
- if (!elsize || !nelem)
- return NULL;
-#endif
-
- ptr = calloc(nelem, elsize);
-
- if (!ptr)
- git_error_set_oom();
-
- return ptr;
-}
-
-static char *stdalloc__strdup(const char *str, const char *file, int line)
-{
- char *ptr;
-
GIT_UNUSED(file);
GIT_UNUSED(line);
- ptr = strdup(str);
-
- if (!ptr)
- git_error_set_oom();
-
- return ptr;
-}
-
-static char *stdalloc__strndup(const char *str, size_t n, const char *file, int line)
-{
- size_t length = 0, alloclength;
- char *ptr;
-
- length = p_strnlen(str, n);
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
- !(ptr = stdalloc__malloc(alloclength, file, line)))
- return NULL;
-
- if (length)
- memcpy(ptr, str, length);
-
- ptr[length] = '\0';
-
- return ptr;
-}
-
-static char *stdalloc__substrdup(const char *start, size_t n, const char *file, int line)
-{
- char *ptr;
- size_t alloclen;
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
- !(ptr = stdalloc__malloc(alloclen, file, line)))
- return NULL;
-
- memcpy(ptr, start, n);
- ptr[n] = '\0';
- return ptr;
+ return malloc(len);
}
static void *stdalloc__realloc(void *ptr, size_t size, const char *file, int line)
{
- void *new_ptr;
-
GIT_UNUSED(file);
GIT_UNUSED(line);
-#ifdef GIT_DEBUG_STRICT_ALLOC
- if (!size)
- return NULL;
-#endif
-
- new_ptr = realloc(ptr, size);
-
- if (!new_ptr)
- git_error_set_oom();
-
- return new_ptr;
-}
-
-static void *stdalloc__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
-{
- size_t newsize;
-
- if (GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize))
- return NULL;
-
- return stdalloc__realloc(ptr, newsize, file, line);
-}
-
-static void *stdalloc__mallocarray(size_t nelem, size_t elsize, const char *file, int line)
-{
- return stdalloc__reallocarray(NULL, nelem, elsize, file, line);
+ return realloc(ptr, size);
}
static void stdalloc__free(void *ptr)
@@ -138,13 +31,7 @@ static void stdalloc__free(void *ptr)
int git_stdalloc_init_allocator(git_allocator *allocator)
{
allocator->gmalloc = stdalloc__malloc;
- allocator->gcalloc = stdalloc__calloc;
- allocator->gstrdup = stdalloc__strdup;
- allocator->gstrndup = stdalloc__strndup;
- allocator->gsubstrdup = stdalloc__substrdup;
allocator->grealloc = stdalloc__realloc;
- allocator->greallocarray = stdalloc__reallocarray;
- allocator->gmallocarray = stdalloc__mallocarray;
allocator->gfree = stdalloc__free;
return 0;
}
diff --git a/src/util/allocators/win32_leakcheck.c b/src/util/allocators/win32_leakcheck.c
index fe06a14af73..cdf16d34880 100644
--- a/src/util/allocators/win32_leakcheck.c
+++ b/src/util/allocators/win32_leakcheck.c
@@ -18,53 +18,6 @@ static void *leakcheck_malloc(size_t len, const char *file, int line)
return ptr;
}
-static void *leakcheck_calloc(size_t nelem, size_t elsize, const char *file, int line)
-{
- void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line);
- if (!ptr) git_error_set_oom();
- return ptr;
-}
-
-static char *leakcheck_strdup(const char *str, const char *file, int line)
-{
- char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line);
- if (!ptr) git_error_set_oom();
- return ptr;
-}
-
-static char *leakcheck_strndup(const char *str, size_t n, const char *file, int line)
-{
- size_t length = 0, alloclength;
- char *ptr;
-
- length = p_strnlen(str, n);
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
- !(ptr = leakcheck_malloc(alloclength, file, line)))
- return NULL;
-
- if (length)
- memcpy(ptr, str, length);
-
- ptr[length] = '\0';
-
- return ptr;
-}
-
-static char *leakcheck_substrdup(const char *start, size_t n, const char *file, int line)
-{
- char *ptr;
- size_t alloclen;
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
- !(ptr = leakcheck_malloc(alloclen, file, line)))
- return NULL;
-
- memcpy(ptr, start, n);
- ptr[n] = '\0';
- return ptr;
-}
-
static void *leakcheck_realloc(void *ptr, size_t size, const char *file, int line)
{
void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line);
@@ -72,21 +25,6 @@ static void *leakcheck_realloc(void *ptr, size_t size, const char *file, int lin
return new_ptr;
}
-static void *leakcheck_reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
-{
- size_t newsize;
-
- if (GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize))
- return NULL;
-
- return leakcheck_realloc(ptr, newsize, file, line);
-}
-
-static void *leakcheck_mallocarray(size_t nelem, size_t elsize, const char *file, int line)
-{
- return leakcheck_reallocarray(NULL, nelem, elsize, file, line);
-}
-
static void leakcheck_free(void *ptr)
{
free(ptr);
@@ -95,13 +33,7 @@ static void leakcheck_free(void *ptr)
int git_win32_leakcheck_init_allocator(git_allocator *allocator)
{
allocator->gmalloc = leakcheck_malloc;
- allocator->gcalloc = leakcheck_calloc;
- allocator->gstrdup = leakcheck_strdup;
- allocator->gstrndup = leakcheck_strndup;
- allocator->gsubstrdup = leakcheck_substrdup;
allocator->grealloc = leakcheck_realloc;
- allocator->greallocarray = leakcheck_reallocarray;
- allocator->gmallocarray = leakcheck_mallocarray;
allocator->gfree = leakcheck_free;
return 0;
}
diff --git a/src/util/array.h b/src/util/array.h
index cbab52ad1f3..515e6e3ab46 100644
--- a/src/util/array.h
+++ b/src/util/array.h
@@ -33,44 +33,48 @@
#define git_array_init_to_size(a, desired) \
do { (a).size = 0; (a).asize = desired; (a).ptr = git__calloc(desired, sizeof(*(a).ptr)); } while (0)
+#define git_array_dispose(a) \
+ do { git__free((a).ptr); } while (0)
+
#define git_array_clear(a) \
do { git__free((a).ptr); git_array_init(a); } while (0)
#define GIT_ERROR_CHECK_ARRAY(a) GIT_ERROR_CHECK_ALLOC((a).ptr)
-
-typedef git_array_t(char) git_array_generic_t;
-
-/* use a generic array for growth, return 0 on success */
-GIT_INLINE(int) git_array_grow(void *_a, size_t item_size)
+GIT_INLINE(void *) git_array__alloc(void *arr, size_t *size, size_t *asize, size_t item_size)
{
- volatile git_array_generic_t *a = _a;
size_t new_size;
- char *new_array;
+ void *new_array;
+
+ if (*size < *asize)
+ return arr;
- if (a->size < 8) {
+ if (*size < 8) {
new_size = 8;
} else {
- if (GIT_MULTIPLY_SIZET_OVERFLOW(&new_size, a->size, 3))
+ if (GIT_MULTIPLY_SIZET_OVERFLOW(&new_size, *asize, 3))
goto on_oom;
+
new_size /= 2;
}
- if ((new_array = git__reallocarray(a->ptr, new_size, item_size)) == NULL)
+ if ((new_array = git__reallocarray(arr, new_size, item_size)) == NULL)
goto on_oom;
- a->ptr = new_array;
- a->asize = new_size;
- return 0;
+ *asize = new_size;
+
+ return new_array;
on_oom:
- git_array_clear(*a);
- return -1;
+ git__free(arr);
+ *size = 0;
+ *asize = 0;
+ return NULL;
}
#define git_array_alloc(a) \
- (((a).size < (a).asize || git_array_grow(&(a), sizeof(*(a).ptr)) == 0) ? \
- &(a).ptr[(a).size++] : (void *)NULL)
+ (((a).size < (a).asize || \
+ ((a).ptr = git_array__alloc((a).ptr, &(a).size, &(a).asize, sizeof(*(a).ptr))) != NULL) ? &(a).ptr[(a).size++] : (void *)NULL)
#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : (void *)NULL)
@@ -85,12 +89,14 @@ GIT_INLINE(int) git_array_grow(void *_a, size_t item_size)
#define git_array_foreach(a, i, element) \
for ((i) = 0; (i) < (a).size && ((element) = &(a).ptr[(i)]); (i)++)
+typedef int (*git_array_compare_cb)(const void *, const void *);
+
GIT_INLINE(int) git_array__search(
size_t *out,
void *array_ptr,
size_t item_size,
size_t array_len,
- int (*compare)(const void *, const void *),
+ git_array_compare_cb compare,
const void *key)
{
size_t lim;
diff --git a/src/util/cc-compat.h b/src/util/cc-compat.h
index a0971e86c98..ede6e9aa9a1 100644
--- a/src/util/cc-compat.h
+++ b/src/util/cc-compat.h
@@ -43,8 +43,10 @@
__typeof__(x) _unused __attribute__((unused)); \
_unused = (x); \
} while (0)
+# define GIT_UNUSED_ARG __attribute__((unused))
#else
# define GIT_UNUSED(x) ((void)(x))
+# define GIT_UNUSED_ARG
#endif
/* Define the printf format specifier to use for size_t output */
diff --git a/src/util/ctype_compat.h b/src/util/ctype_compat.h
new file mode 100644
index 00000000000..462c8a17f17
--- /dev/null
+++ b/src/util/ctype_compat.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_ctype_compat_h__
+#define INCLUDE_ctype_compat_h__
+
+/*
+ * The Microsoft C runtime (MSVCRT) may take a heavy lock on the
+ * locale in order to figure out how the `ctype` functions work.
+ * This is deeply slow. Provide our own to avoid that.
+ */
+
+#ifdef GIT_WIN32
+
+GIT_INLINE(int) git__tolower(int c)
+{
+ return (c >= 'A' && c <= 'Z') ? (c + 32) : c;
+}
+
+GIT_INLINE(int) git__toupper(int c)
+{
+ return (c >= 'a' && c <= 'z') ? (c - 32) : c;
+}
+
+GIT_INLINE(bool) git__isalpha(int c)
+{
+ return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
+}
+
+GIT_INLINE(bool) git__isdigit(int c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+GIT_INLINE(bool) git__isalnum(int c)
+{
+ return git__isalpha(c) || git__isdigit(c);
+}
+
+GIT_INLINE(bool) git__isspace(int c)
+{
+ return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');
+}
+
+GIT_INLINE(bool) git__isxdigit(int c)
+{
+ return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+}
+
+GIT_INLINE(bool) git__isprint(int c)
+{
+ return (c >= ' ' && c <= '~');
+}
+
+#else
+# define git__tolower(a) tolower((unsigned char)(a))
+# define git__toupper(a) toupper((unsigned char)(a))
+
+# define git__isalpha(a) (!!isalpha((unsigned char)(a)))
+# define git__isdigit(a) (!!isdigit((unsigned char)(a)))
+# define git__isalnum(a) (!!isalnum((unsigned char)(a)))
+# define git__isspace(a) (!!isspace((unsigned char)(a)))
+# define git__isxdigit(a) (!!isxdigit((unsigned char)(a)))
+# define git__isprint(a) (!!isprint((unsigned char)(a)))
+#endif
+
+#endif
diff --git a/src/util/date.c b/src/util/date.c
index 4d757e21a00..872cb81f33c 100644
--- a/src/util/date.c
+++ b/src/util/date.c
@@ -129,9 +129,9 @@ static size_t match_string(const char *date, const char *str)
for (i = 0; *date; date++, str++, i++) {
if (*date == *str)
continue;
- if (toupper(*date) == toupper(*str))
+ if (git__toupper(*date) == git__toupper(*str))
continue;
- if (!isalnum(*date))
+ if (!git__isalnum(*date))
break;
return 0;
}
@@ -143,7 +143,7 @@ static int skip_alpha(const char *date)
int i = 0;
do {
i++;
- } while (isalpha(date[i]));
+ } while (git__isalpha(date[i]));
return i;
}
@@ -251,7 +251,7 @@ static size_t match_multi_number(unsigned long num, char c, const char *date, ch
num2 = strtol(end+1, &end, 10);
num3 = -1;
- if (*end == c && isdigit(end[1]))
+ if (*end == c && git__isdigit(end[1]))
num3 = strtol(end+1, &end, 10);
/* Time? Date? */
@@ -349,7 +349,7 @@ static size_t match_digit(const char *date, struct tm *tm, int *offset, int *tm_
case '.':
case '/':
case '-':
- if (isdigit(end[1])) {
+ if (git__isdigit(end[1])) {
size_t match = match_multi_number(num, *end, date, end, tm);
if (match)
return match;
@@ -364,7 +364,7 @@ static size_t match_digit(const char *date, struct tm *tm, int *offset, int *tm_
n = 0;
do {
n++;
- } while (isdigit(date[n]));
+ } while (git__isdigit(date[n]));
/* Four-digit year or a timezone? */
if (n == 4) {
@@ -514,11 +514,11 @@ static int parse_date_basic(const char *date, git_time_t *timestamp, int *offset
if (!c || c == '\n')
break;
- if (isalpha(c))
+ if (git__isalpha(c))
match = match_alpha(date, &tm, offset);
- else if (isdigit(c))
+ else if (git__isdigit(c))
match = match_digit(date, &tm, offset, &tm_gmt);
- else if ((c == '-' || c == '+') && isdigit(date[1]))
+ else if ((c == '-' || c == '+') && git__isdigit(date[1]))
match = match_tz(date, offset);
if (!match) {
@@ -682,7 +682,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm
const char *end = date;
int i;
- while (isalpha(*++end))
+ while (git__isalpha(*++end))
/* scan to non-alpha */;
for (i = 0; i < 12; i++) {
@@ -783,7 +783,7 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
case '.':
case '/':
case '-':
- if (isdigit(end[1])) {
+ if (git__isdigit(end[1])) {
size_t match = match_multi_number(number, *end, date, end, tm);
if (match)
return date + match;
@@ -843,13 +843,13 @@ static git_time_t approxidate_str(const char *date,
if (!c)
break;
date++;
- if (isdigit(c)) {
+ if (git__isdigit(c)) {
pending_number(&tm, &number);
date = approxidate_digit(date-1, &tm, &number);
touched = 1;
continue;
}
- if (isalpha(c))
+ if (git__isalpha(c))
date = approxidate_alpha(date-1, &tm, &now, &number, &touched);
}
pending_number(&tm, &number);
diff --git a/src/util/errors.c b/src/util/errors.c
new file mode 100644
index 00000000000..feed6a835f5
--- /dev/null
+++ b/src/util/errors.c
@@ -0,0 +1,401 @@
+/*
+ * 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 "errors.h"
+#include "posix.h"
+#include "str.h"
+#include "runtime.h"
+
+/*
+ * Some static error data that is used when we're out of memory, TLS
+ * has not been setup, or TLS has failed.
+ */
+
+static git_error oom_error = {
+ "Out of memory",
+ GIT_ERROR_NOMEMORY
+};
+
+static git_error uninitialized_error = {
+ "library has not been initialized",
+ GIT_ERROR_INVALID
+};
+
+static git_error tlsdata_error = {
+ "thread-local data initialization failure",
+ GIT_ERROR_THREAD
+};
+
+static git_error no_error = {
+ "no error",
+ GIT_ERROR_NONE
+};
+
+#define IS_STATIC_ERROR(err) \
+ ((err) == &oom_error || (err) == &uninitialized_error || \
+ (err) == &tlsdata_error || (err) == &no_error)
+
+/* Per-thread error state (TLS) */
+
+static git_tlsdata_key tls_key;
+
+struct error_threadstate {
+ /* The error message buffer. */
+ git_str message;
+
+ /* Error information, set by `git_error_set` and friends. */
+ git_error error;
+
+ /*
+ * The last error to occur; points to the error member of this
+ * struct _or_ a static error.
+ */
+ git_error *last;
+};
+
+static void threadstate_dispose(struct error_threadstate *threadstate)
+{
+ if (!threadstate)
+ return;
+
+ git_str_dispose(&threadstate->message);
+}
+
+static struct error_threadstate *threadstate_get(void)
+{
+ struct error_threadstate *threadstate;
+
+ if ((threadstate = git_tlsdata_get(tls_key)) != NULL)
+ return threadstate;
+
+ /*
+ * Avoid git__malloc here, since if it fails, it sets an error
+ * message, which requires thread state, which would allocate
+ * here, which would fail, which would set an error message...
+ */
+
+ if ((threadstate = git__allocator.gmalloc(
+ sizeof(struct error_threadstate),
+ __FILE__, __LINE__)) == NULL)
+ return NULL;
+
+ memset(threadstate, 0, sizeof(struct error_threadstate));
+
+ if (git_str_init(&threadstate->message, 0) < 0) {
+ git__allocator.gfree(threadstate);
+ return NULL;
+ }
+
+ git_tlsdata_set(tls_key, threadstate);
+ return threadstate;
+}
+
+static void GIT_SYSTEM_CALL threadstate_free(void *threadstate)
+{
+ threadstate_dispose(threadstate);
+ git__free(threadstate);
+}
+
+static void git_error_global_shutdown(void)
+{
+ struct error_threadstate *threadstate;
+
+ threadstate = git_tlsdata_get(tls_key);
+ git_tlsdata_set(tls_key, NULL);
+
+ threadstate_dispose(threadstate);
+ git__free(threadstate);
+
+ git_tlsdata_dispose(tls_key);
+}
+
+int git_error_global_init(void)
+{
+ if (git_tlsdata_init(&tls_key, &threadstate_free) != 0)
+ return -1;
+
+ return git_runtime_shutdown_register(git_error_global_shutdown);
+}
+
+static void set_error_from_buffer(int error_class)
+{
+ struct error_threadstate *threadstate = threadstate_get();
+ git_error *error;
+ git_str *buf;
+
+ if (!threadstate)
+ return;
+
+ error = &threadstate->error;
+ buf = &threadstate->message;
+
+ error->message = buf->ptr;
+ error->klass = error_class;
+
+ threadstate->last = error;
+}
+
+static void set_error(int error_class, char *string)
+{
+ struct error_threadstate *threadstate = threadstate_get();
+ git_str *buf;
+
+ if (!threadstate)
+ return;
+
+ buf = &threadstate->message;
+
+ git_str_clear(buf);
+
+ if (string)
+ git_str_puts(buf, string);
+
+ if (!git_str_oom(buf))
+ set_error_from_buffer(error_class);
+}
+
+void git_error_set_oom(void)
+{
+ struct error_threadstate *threadstate = threadstate_get();
+
+ if (!threadstate)
+ return;
+
+ threadstate->last = &oom_error;
+}
+
+void git_error_set(int error_class, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ git_error_vset(error_class, fmt, ap);
+ va_end(ap);
+}
+
+void git_error_vset(int error_class, const char *fmt, va_list ap)
+{
+#ifdef GIT_WIN32
+ DWORD win32_error_code = (error_class == GIT_ERROR_OS) ? GetLastError() : 0;
+#endif
+
+ struct error_threadstate *threadstate = threadstate_get();
+ int error_code = (error_class == GIT_ERROR_OS) ? errno : 0;
+ git_str *buf;
+
+ if (!threadstate)
+ return;
+
+ buf = &threadstate->message;
+
+ git_str_clear(buf);
+
+ if (fmt) {
+ git_str_vprintf(buf, fmt, ap);
+ if (error_class == GIT_ERROR_OS)
+ git_str_PUTS(buf, ": ");
+ }
+
+ if (error_class == GIT_ERROR_OS) {
+#ifdef GIT_WIN32
+ char *win32_error = git_win32_get_error_message(win32_error_code);
+ if (win32_error) {
+ git_str_puts(buf, win32_error);
+ git__free(win32_error);
+
+ SetLastError(0);
+ }
+ else
+#endif
+ if (error_code)
+ git_str_puts(buf, strerror(error_code));
+
+ if (error_code)
+ errno = 0;
+ }
+
+ if (!git_str_oom(buf))
+ set_error_from_buffer(error_class);
+}
+
+int git_error_set_str(int error_class, const char *string)
+{
+ struct error_threadstate *threadstate = threadstate_get();
+ git_str *buf;
+
+ GIT_ASSERT_ARG(string);
+
+ if (!threadstate)
+ return -1;
+
+ buf = &threadstate->message;
+
+ git_str_clear(buf);
+ git_str_puts(buf, string);
+
+ if (git_str_oom(buf))
+ return -1;
+
+ set_error_from_buffer(error_class);
+ return 0;
+}
+
+void git_error_clear(void)
+{
+ struct error_threadstate *threadstate = threadstate_get();
+
+ if (!threadstate)
+ return;
+
+ if (threadstate->last != NULL) {
+ set_error(0, NULL);
+ threadstate->last = NULL;
+ }
+
+ errno = 0;
+#ifdef GIT_WIN32
+ SetLastError(0);
+#endif
+}
+
+bool git_error_exists(void)
+{
+ struct error_threadstate *threadstate;
+
+ if ((threadstate = threadstate_get()) == NULL)
+ return true;
+
+ return threadstate->last != NULL;
+}
+
+const git_error *git_error_last(void)
+{
+ struct error_threadstate *threadstate;
+
+ /* If the library is not initialized, return a static error. */
+ if (!git_runtime_init_count())
+ return &uninitialized_error;
+
+ if ((threadstate = threadstate_get()) == NULL)
+ return &tlsdata_error;
+
+ if (!threadstate->last)
+ return &no_error;
+
+ return threadstate->last;
+}
+
+int git_error_save(git_error **out)
+{
+ struct error_threadstate *threadstate = threadstate_get();
+ git_error *error, *dup;
+
+ if (!threadstate) {
+ *out = &tlsdata_error;
+ return -1;
+ }
+
+ error = threadstate->last;
+
+ if (!error || error == &no_error) {
+ *out = &no_error;
+ return 0;
+ } else if (IS_STATIC_ERROR(error)) {
+ *out = error;
+ return 0;
+ }
+
+ if ((dup = git__malloc(sizeof(git_error))) == NULL) {
+ *out = &oom_error;
+ return -1;
+ }
+
+ dup->klass = error->klass;
+ dup->message = git__strdup(error->message);
+
+ if (!dup->message) {
+ *out = &oom_error;
+ return -1;
+ }
+
+ *out = dup;
+ return 0;
+}
+
+int git_error_restore(git_error *error)
+{
+ struct error_threadstate *threadstate = threadstate_get();
+
+ GIT_ASSERT_ARG(error);
+
+ if (IS_STATIC_ERROR(error) && threadstate)
+ threadstate->last = error;
+ else
+ set_error(error->klass, error->message);
+
+ git_error_free(error);
+ return 0;
+}
+
+void git_error_free(git_error *error)
+{
+ if (!error)
+ return;
+
+ if (IS_STATIC_ERROR(error))
+ return;
+
+ git__free(error->message);
+ git__free(error);
+}
+
+int git_error_system_last(void)
+{
+#ifdef GIT_WIN32
+ return GetLastError();
+#else
+ return errno;
+#endif
+}
+
+void git_error_system_set(int code)
+{
+#ifdef GIT_WIN32
+ SetLastError(code);
+#else
+ errno = code;
+#endif
+}
+
+/* Deprecated error values and functions */
+
+#ifndef GIT_DEPRECATE_HARD
+
+#include "git2/deprecated.h"
+
+const git_error *giterr_last(void)
+{
+ return git_error_last();
+}
+
+void giterr_clear(void)
+{
+ git_error_clear();
+}
+
+void giterr_set_str(int error_class, const char *string)
+{
+ git_error_set_str(error_class, string);
+}
+
+void giterr_set_oom(void)
+{
+ git_error_set_oom();
+}
+#endif
diff --git a/src/libgit2/errors.h b/src/util/errors.h
similarity index 67%
rename from src/libgit2/errors.h
rename to src/util/errors.h
index 772c7bad18b..8d5877550b1 100644
--- a/src/libgit2/errors.h
+++ b/src/util/errors.h
@@ -8,13 +8,22 @@
#ifndef INCLUDE_errors_h__
#define INCLUDE_errors_h__
-#include "common.h"
+#include "git2_util.h"
+#include "git2/sys/errors.h"
+
+/* Initialize the error thread-state. */
+int git_error_global_init(void);
/*
* `vprintf`-style formatting for the error message for this thread.
*/
void git_error_vset(int error_class, const char *fmt, va_list ap);
+/**
+ * Determines whether an error exists.
+ */
+bool git_error_exists(void);
+
/**
* Set error message for user callback if needed.
*
@@ -27,9 +36,8 @@ GIT_INLINE(int) git_error_set_after_callback_function(
int error_code, const char *action)
{
if (error_code) {
- const git_error *e = git_error_last();
- if (!e || !e->message)
- git_error_set(e ? e->klass : GIT_ERROR_CALLBACK,
+ if (!git_error_exists())
+ git_error_set(GIT_ERROR_CALLBACK,
"%s callback returned %d", action, error_code);
}
return error_code;
@@ -53,28 +61,24 @@ int git_error_system_last(void);
*/
void git_error_system_set(int code);
-/**
- * Structure to preserve libgit2 error state
- */
-typedef struct {
- int error_code;
- unsigned int oom : 1;
- git_error error_msg;
-} git_error_state;
-
/**
* Capture current error state to restore later, returning error code.
* If `error_code` is zero, this does not clear the current error state.
* You must either restore this error state, or free it.
+ *
+ * This function returns 0 on success, or -1 on failure. If the function
+ * fails, the `out` structure is set to the failure error message and
+ * the normal system error message is not updated.
*/
-extern int git_error_state_capture(git_error_state *state, int error_code);
+extern int git_error_save(git_error **out);
/**
- * Restore error state to a previous value, returning saved error code.
+ * Restore thread error state to the given value. The given value is
+ * freed and `git_error_free` need not be called on it.
*/
-extern int git_error_state_restore(git_error_state *state);
+extern int git_error_restore(git_error *error);
/** Free an error state. */
-extern void git_error_state_free(git_error_state *state);
+extern void git_error_free(git_error *error);
#endif
diff --git a/src/util/filebuf.c b/src/util/filebuf.c
index e014d43b25f..7afb76b88ec 100644
--- a/src/util/filebuf.c
+++ b/src/util/filebuf.c
@@ -302,11 +302,16 @@ int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mo
}
/* If we are hashing on-write, allocate a new hash context */
- if (flags & GIT_FILEBUF_HASH_CONTENTS) {
+ if (flags & GIT_FILEBUF_HASH_SHA1) {
file->compute_digest = 1;
if (git_hash_ctx_init(&file->digest, GIT_HASH_ALGORITHM_SHA1) < 0)
goto cleanup;
+ } else if (flags & GIT_FILEBUF_HASH_SHA256) {
+ file->compute_digest = 1;
+
+ if (git_hash_ctx_init(&file->digest, GIT_HASH_ALGORITHM_SHA256) < 0)
+ goto cleanup;
}
compression = flags >> GIT_FILEBUF_DEFLATE_SHIFT;
diff --git a/src/util/filebuf.h b/src/util/filebuf.h
index 4a61ae4e3bf..e23b9ed2adc 100644
--- a/src/util/filebuf.h
+++ b/src/util/filebuf.h
@@ -17,13 +17,14 @@
# define GIT_FILEBUF_THREADS
#endif
-#define GIT_FILEBUF_HASH_CONTENTS (1 << 0)
-#define GIT_FILEBUF_APPEND (1 << 2)
+#define GIT_FILEBUF_HASH_SHA1 (1 << 0)
+#define GIT_FILEBUF_HASH_SHA256 (1 << 1)
+#define GIT_FILEBUF_APPEND (1 << 2)
#define GIT_FILEBUF_CREATE_LEADING_DIRS (1 << 3)
-#define GIT_FILEBUF_TEMPORARY (1 << 4)
-#define GIT_FILEBUF_DO_NOT_BUFFER (1 << 5)
-#define GIT_FILEBUF_FSYNC (1 << 6)
-#define GIT_FILEBUF_DEFLATE_SHIFT (7)
+#define GIT_FILEBUF_TEMPORARY (1 << 4)
+#define GIT_FILEBUF_DO_NOT_BUFFER (1 << 5)
+#define GIT_FILEBUF_FSYNC (1 << 6)
+#define GIT_FILEBUF_DEFLATE_SHIFT (7)
#define GIT_FILELOCK_EXTENSION ".lock\0"
#define GIT_FILELOCK_EXTLENGTH 6
@@ -91,4 +92,16 @@ int git_filebuf_hash(unsigned char *out, git_filebuf *file);
int git_filebuf_flush(git_filebuf *file);
int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file);
+GIT_INLINE(int) git_filebuf_hash_flags(git_hash_algorithm_t algorithm)
+{
+ switch (algorithm) {
+ case GIT_HASH_ALGORITHM_SHA1:
+ return GIT_FILEBUF_HASH_SHA1;
+ case GIT_HASH_ALGORITHM_SHA256:
+ return GIT_FILEBUF_HASH_SHA256;
+ default:
+ return 0;
+ }
+}
+
#endif
diff --git a/src/util/fs_path.c b/src/util/fs_path.c
index 6c87bfd01b2..9d5c99eab81 100644
--- a/src/util/fs_path.c
+++ b/src/util/fs_path.c
@@ -419,6 +419,16 @@ int git_fs_path_to_dir(git_str *path)
return git_str_oom(path) ? -1 : 0;
}
+size_t git_fs_path_dirlen(const char *path)
+{
+ size_t len = strlen(path);
+
+ while (len > 1 && path[len - 1] == '/')
+ len--;
+
+ return len;
+}
+
void git_fs_path_string_to_dir(char *path, size_t size)
{
size_t end = strlen(path);
@@ -1855,7 +1865,7 @@ static int file_owner_sid(PSID *out, const char *path)
PSECURITY_DESCRIPTOR descriptor = NULL;
PSID owner_sid;
DWORD ret;
- int error = -1;
+ int error = GIT_EINVALID;
if (git_win32_path_from_utf8(path_w32, path) < 0)
return -1;
@@ -1938,12 +1948,13 @@ static int sudo_uid_lookup(uid_t *out)
{
git_str uid_str = GIT_STR_INIT;
int64_t uid;
- int error;
+ int error = -1;
- 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)) {
+ if (git__getenv(&uid_str, "SUDO_UID") == 0 &&
+ git__strntol64(&uid, uid_str.ptr, uid_str.size, NULL, 10) == 0 &&
+ uid == (int64_t)((uid_t)uid)) {
*out = (uid_t)uid;
+ error = 0;
}
git_str_dispose(&uid_str);
@@ -2015,7 +2026,7 @@ int git_fs_path_find_executable(git_str *fullpath, const char *executable)
git_win32_path fullpath_w, executable_w;
int error;
- if (git__utf8_to_16(executable_w, GIT_WIN_PATH_MAX, executable) < 0)
+ if (git_utf8_to_16(executable_w, GIT_WIN_PATH_MAX, executable) < 0)
return -1;
error = git_win32_path_find_executable(fullpath_w, executable_w);
diff --git a/src/util/fs_path.h b/src/util/fs_path.h
index e5ca6737818..43f7951adde 100644
--- a/src/util/fs_path.h
+++ b/src/util/fs_path.h
@@ -86,6 +86,29 @@ extern int git_fs_path_to_dir(git_str *path);
*/
extern void git_fs_path_string_to_dir(char *path, size_t size);
+/**
+ * Provides the length of the given path string with no trailing
+ * slashes.
+ */
+size_t git_fs_path_dirlen(const char *path);
+
+/**
+ * Returns nonzero if the given path is a filesystem root; on Windows, this
+ * means a drive letter (eg `A:/`, `C:\`). On POSIX this is `/`.
+ */
+GIT_INLINE(int) git_fs_path_is_root(const char *name)
+{
+#ifdef GIT_WIN32
+ if (((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z')) &&
+ name[1] == ':' &&
+ (name[2] == '/' || name[2] == '\\') &&
+ name[3] == '\0')
+ return 1;
+#endif
+
+ return (name[0] == '/' && name[1] == '\0');
+}
+
/**
* Taken from git.git; returns nonzero if the given path is "." or "..".
*/
diff --git a/src/util/futils.c b/src/util/futils.c
index cb872de09f6..7b5a24b305f 100644
--- a/src/util/futils.c
+++ b/src/util/futils.c
@@ -13,9 +13,6 @@
#include "rand.h"
#include
-#if GIT_WIN32
-#include "win32/findfile.h"
-#endif
#define GIT_FILEMODE_DEFAULT 0100666
@@ -224,14 +221,14 @@ int git_futils_readbuffer_fd_full(git_str *buf, git_file fd)
int git_futils_readbuffer_updated(
git_str *out,
const char *path,
- unsigned char checksum[GIT_HASH_SHA1_SIZE],
+ unsigned char checksum[GIT_HASH_SHA256_SIZE],
int *updated)
{
int error;
git_file fd;
struct stat st;
git_str buf = GIT_STR_INIT;
- unsigned char checksum_new[GIT_HASH_SHA1_SIZE];
+ unsigned char checksum_new[GIT_HASH_SHA256_SIZE];
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(path && *path);
@@ -264,7 +261,10 @@ int git_futils_readbuffer_updated(
p_close(fd);
if (checksum) {
- if ((error = git_hash_buf(checksum_new, buf.ptr, buf.size, GIT_HASH_ALGORITHM_SHA1)) < 0) {
+ error = git_hash_buf(checksum_new, buf.ptr,
+ buf.size, GIT_HASH_ALGORITHM_SHA256);
+
+ if (error < 0) {
git_str_dispose(&buf);
return error;
}
@@ -272,7 +272,7 @@ int git_futils_readbuffer_updated(
/*
* If we were given a checksum, we only want to use it if it's different
*/
- if (!memcmp(checksum, checksum_new, GIT_HASH_SHA1_SIZE)) {
+ if (!memcmp(checksum, checksum_new, GIT_HASH_SHA256_SIZE)) {
git_str_dispose(&buf);
if (updated)
*updated = 0;
@@ -280,7 +280,7 @@ int git_futils_readbuffer_updated(
return 0;
}
- memcpy(checksum, checksum_new, GIT_HASH_SHA1_SIZE);
+ memcpy(checksum, checksum_new, GIT_HASH_SHA256_SIZE);
}
/*
diff --git a/src/util/futils.h b/src/util/futils.h
index 3f207afb2f2..53bcc551890 100644
--- a/src/util/futils.h
+++ b/src/util/futils.h
@@ -25,7 +25,7 @@ extern int git_futils_readbuffer(git_str *obj, const char *path);
extern int git_futils_readbuffer_updated(
git_str *obj,
const char *path,
- unsigned char checksum[GIT_HASH_SHA1_SIZE],
+ unsigned char checksum[GIT_HASH_SHA256_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);
diff --git a/src/util/git2_features.h.in b/src/util/git2_features.h.in
index fbf0cab60ea..52b73284687 100644
--- a/src/util/git2_features.h.in
+++ b/src/util/git2_features.h.in
@@ -24,12 +24,15 @@
#cmakedefine GIT_REGEX_PCRE2
#cmakedefine GIT_REGEX_BUILTIN 1
-#cmakedefine GIT_QSORT_R_BSD
-#cmakedefine GIT_QSORT_R_GNU
-#cmakedefine GIT_QSORT_S
+#cmakedefine GIT_QSORT_BSD
+#cmakedefine GIT_QSORT_GNU
+#cmakedefine GIT_QSORT_C11
+#cmakedefine GIT_QSORT_MSC
#cmakedefine GIT_SSH 1
-#cmakedefine GIT_SSH_MEMORY_CREDENTIALS 1
+#cmakedefine GIT_SSH_EXEC 1
+#cmakedefine GIT_SSH_LIBSSH2 1
+#cmakedefine GIT_SSH_LIBSSH2_MEMORY_CREDENTIALS 1
#cmakedefine GIT_NTLM 1
#cmakedefine GIT_GSSAPI 1
@@ -41,6 +44,11 @@
#cmakedefine GIT_OPENSSL_DYNAMIC 1
#cmakedefine GIT_SECURE_TRANSPORT 1
#cmakedefine GIT_MBEDTLS 1
+#cmakedefine GIT_SCHANNEL 1
+
+#cmakedefine GIT_HTTPPARSER_HTTPPARSER 1
+#cmakedefine GIT_HTTPPARSER_LLHTTP 1
+#cmakedefine GIT_HTTPPARSER_BUILTIN 1
#cmakedefine GIT_SHA1_COLLISIONDETECT 1
#cmakedefine GIT_SHA1_WIN32 1
@@ -59,4 +67,8 @@
#cmakedefine GIT_RAND_GETENTROPY 1
#cmakedefine GIT_RAND_GETLOADAVG 1
+#cmakedefine GIT_IO_POLL 1
+#cmakedefine GIT_IO_WSAPOLL 1
+#cmakedefine GIT_IO_SELECT 1
+
#endif
diff --git a/src/util/git2_util.h b/src/util/git2_util.h
index c62dc24199d..5bf09819956 100644
--- a/src/util/git2_util.h
+++ b/src/util/git2_util.h
@@ -12,6 +12,7 @@
#endif
#include "git2/common.h"
+#include "git2/sys/errors.h"
#include "cc-compat.h"
typedef struct git_str git_str;
@@ -164,5 +165,6 @@ typedef struct git_str git_str;
if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; }
#include "util.h"
+#include "ctype_compat.h"
#endif
diff --git a/src/util/hash.h b/src/util/hash.h
index 387c5a66f96..21fcaf045e7 100644
--- a/src/util/hash.h
+++ b/src/util/hash.h
@@ -23,6 +23,8 @@ typedef enum {
GIT_HASH_ALGORITHM_SHA256
} git_hash_algorithm_t;
+#define GIT_HASH_MAX_SIZE GIT_HASH_SHA256_SIZE
+
typedef struct git_hash_ctx {
union {
git_hash_sha1_ctx sha1;
@@ -45,4 +47,15 @@ int git_hash_vec(unsigned char *out, git_str_vec *vec, size_t n, git_hash_algori
int git_hash_fmt(char *out, unsigned char *hash, size_t hash_len);
+GIT_INLINE(size_t) git_hash_size(git_hash_algorithm_t algorithm) {
+ switch (algorithm) {
+ case GIT_HASH_ALGORITHM_SHA1:
+ return GIT_HASH_SHA1_SIZE;
+ case GIT_HASH_ALGORITHM_SHA256:
+ return GIT_HASH_SHA256_SIZE;
+ default:
+ return 0;
+ }
+}
+
#endif
diff --git a/src/util/hash/openssl.c b/src/util/hash/openssl.c
index 649358ca2a6..eaf91e74c1d 100644
--- a/src/util/hash/openssl.c
+++ b/src/util/hash/openssl.c
@@ -10,8 +10,8 @@
#ifdef GIT_OPENSSL_DYNAMIC
# include
-int handle_count;
-void *openssl_handle;
+static int handle_count;
+static void *openssl_handle;
static int git_hash_openssl_global_shutdown(void)
{
@@ -30,7 +30,8 @@ static int git_hash_openssl_global_init(void)
(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) {
+ (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.3", RTLD_NOW)) == NULL) {
git_error_set(GIT_ERROR_SSL, "could not load ssl libraries");
return -1;
}
diff --git a/src/util/integer.h b/src/util/integer.h
index 63277177bf3..a9e416cc342 100644
--- a/src/util/integer.h
+++ b/src/util/integer.h
@@ -89,7 +89,9 @@ GIT_INLINE(int) git__is_int(int64_t p)
/* Use Microsoft's safe integer handling functions where available */
#elif defined(_MSC_VER)
-# define ENABLE_INTSAFE_SIGNED_FUNCTIONS
+# if !defined(ENABLE_INTSAFE_SIGNED_FUNCTIONS)
+# define ENABLE_INTSAFE_SIGNED_FUNCTIONS
+# endif
# include
# define git__add_sizet_overflow(out, one, two) \
diff --git a/src/util/net.c b/src/util/net.c
index 43c7dc952ac..dede784cc31 100644
--- a/src/util/net.c
+++ b/src/util/net.c
@@ -11,7 +11,6 @@
#include "posix.h"
#include "str.h"
-#include "http_parser.h"
#include "runtime.h"
#define DEFAULT_PORT_HTTP "80"
@@ -19,6 +18,80 @@
#define DEFAULT_PORT_GIT "9418"
#define DEFAULT_PORT_SSH "22"
+#define GIT_NET_URL_PARSER_INIT { 0 }
+
+typedef struct {
+ unsigned int hierarchical : 1;
+
+ const char *scheme;
+ const char *user;
+ const char *password;
+ const char *host;
+ const char *port;
+ const char *path;
+ const char *query;
+ const char *fragment;
+
+ size_t scheme_len;
+ size_t user_len;
+ size_t password_len;
+ size_t host_len;
+ size_t port_len;
+ size_t path_len;
+ size_t query_len;
+ size_t fragment_len;
+} git_net_url_parser;
+
+bool git_net_hostname_matches_cert(
+ const char *hostname,
+ const char *pattern)
+{
+ for (;;) {
+ char c = git__tolower(*pattern++);
+
+ if (c == '\0')
+ return *hostname ? false : true;
+
+ if (c == '*') {
+ c = *pattern;
+
+ /* '*' at the end matches everything left */
+ if (c == '\0')
+ return true;
+
+ /*
+ * We've found a pattern, so move towards the
+ * next matching char. The '.' is handled
+ * specially because wildcards aren't allowed
+ * to cross subdomains.
+ */
+ while(*hostname) {
+ char h = git__tolower(*hostname);
+
+ if (h == c)
+ return git_net_hostname_matches_cert(hostname++, pattern);
+ else if (h == '.')
+ return git_net_hostname_matches_cert(hostname, pattern);
+
+ hostname++;
+ }
+
+ return false;
+ }
+
+ if (c != git__tolower(*hostname++))
+ return false;
+ }
+
+ return false;
+}
+
+#define is_valid_scheme_char(c) \
+ (((c) >= 'a' && (c) <= 'z') || \
+ ((c) >= 'A' && (c) <= 'Z') || \
+ ((c) >= '0' && (c) <= '9') || \
+ (c) == '+' || (c) == '-' || (c) == '.')
+
bool git_net_str_is_url(const char *str)
{
const char *c;
@@ -27,10 +100,7 @@ bool git_net_str_is_url(const char *str)
if (*c == ':' && *(c+1) == '/' && *(c+2) == '/')
return true;
- if ((*c < 'a' || *c > 'z') &&
- (*c < 'A' || *c > 'Z') &&
- (*c < '0' || *c > '9') &&
- (*c != '+' && *c != '-' && *c != '.'))
+ if (!is_valid_scheme_char(*c))
break;
}
@@ -53,6 +123,16 @@ static const char *default_port_for_scheme(const char *scheme)
return NULL;
}
+static bool is_ssh_scheme(const char *scheme, size_t scheme_len)
+{
+ if (!scheme_len)
+ return false;
+
+ return strncasecmp(scheme, "ssh", scheme_len) == 0 ||
+ strncasecmp(scheme, "ssh+git", scheme_len) == 0 ||
+ strncasecmp(scheme, "git+ssh", scheme_len) == 0;
+}
+
int git_net_url_dup(git_net_url *out, git_net_url *in)
{
if (in->scheme) {
@@ -100,12 +180,9 @@ static int url_invalid(const char *message)
}
static int url_parse_authority(
- const char **user_start, size_t *user_len,
- const char **password_start, size_t *password_len,
- const char **host_start, size_t *host_len,
- const char **port_start, size_t *port_len,
- const char *authority_start, size_t len,
- const char *scheme_start, size_t scheme_len)
+ git_net_url_parser *parser,
+ const char *authority,
+ size_t len)
{
const char *c, *hostport_end, *host_end = NULL,
*userpass_end, *user_end = NULL;
@@ -121,14 +198,14 @@ static int url_parse_authority(
* walk the authority backwards so that we can parse google code's
* ssh urls that are not rfc compliant and allow @ in the username
*/
- for (hostport_end = authority_start + len, c = hostport_end - 1;
- c >= authority_start && !user_end;
+ for (hostport_end = authority + len, c = hostport_end - 1;
+ c >= authority && !user_end;
c--) {
switch (state) {
case HOSTPORT:
if (*c == ':') {
- *port_start = c + 1;
- *port_len = hostport_end - *port_start;
+ parser->port = c + 1;
+ parser->port_len = hostport_end - parser->port;
host_end = c;
state = HOST;
break;
@@ -156,9 +233,10 @@ static int url_parse_authority(
}
else if (*c == '@') {
- *host_start = c + 1;
- *host_len = host_end ? host_end - *host_start :
- hostport_end - *host_start;
+ parser->host = c + 1;
+ parser->host_len = host_end ?
+ host_end - parser->host :
+ hostport_end - parser->host;
userpass_end = c;
state = USERPASS;
}
@@ -171,8 +249,8 @@ static int url_parse_authority(
case IPV6:
if (*c == '[') {
- *host_start = c + 1;
- *host_len = host_end - *host_start;
+ parser->host = c + 1;
+ parser->host_len = host_end - parser->host;
state = HOST_END;
}
@@ -196,12 +274,12 @@ static int url_parse_authority(
case USERPASS:
if (*c == '@' &&
- strncasecmp(scheme_start, "ssh", scheme_len))
+ !is_ssh_scheme(parser->scheme, parser->scheme_len))
return url_invalid("malformed hostname");
if (*c == ':') {
- *password_start = c + 1;
- *password_len = userpass_end - *password_start;
+ parser->password = c + 1;
+ parser->password_len = userpass_end - parser->password;
user_end = c;
state = USER;
break;
@@ -216,24 +294,24 @@ static int url_parse_authority(
switch (state) {
case HOSTPORT:
- *host_start = authority_start;
- *host_len = (hostport_end - *host_start);
+ parser->host = authority;
+ parser->host_len = (hostport_end - parser->host);
break;
case HOST:
- *host_start = authority_start;
- *host_len = (host_end - *host_start);
+ parser->host = authority;
+ parser->host_len = (host_end - parser->host);
break;
case IPV6:
return url_invalid("malformed hostname");
case HOST_END:
break;
case USERPASS:
- *user_start = authority_start;
- *user_len = (userpass_end - *user_start);
+ parser->user = authority;
+ parser->user_len = (userpass_end - parser->user);
break;
case USER:
- *user_start = authority_start;
- *user_len = (user_end - *user_start);
+ parser->user = authority;
+ parser->user_len = (user_end - parser->user);
break;
default:
GIT_ASSERT(!"unhandled state");
@@ -242,97 +320,30 @@ static int url_parse_authority(
return 0;
}
-int git_net_url_parse(git_net_url *url, const char *given)
+static int url_parse_path(
+ git_net_url_parser *parser,
+ const char *path,
+ size_t len)
{
- const char *c, *scheme_start, *authority_start, *user_start,
- *password_start, *host_start, *port_start, *path_start,
- *query_start, *fragment_start, *default_port;
- git_str scheme = GIT_STR_INIT, user = GIT_STR_INIT,
- password = GIT_STR_INIT, host = GIT_STR_INIT,
- port = GIT_STR_INIT, path = GIT_STR_INIT,
- query = GIT_STR_INIT, fragment = GIT_STR_INIT;
- size_t scheme_len = 0, user_len = 0, password_len = 0, host_len = 0,
- port_len = 0, path_len = 0, query_len = 0, fragment_len = 0;
- bool hierarchical = false;
- int error = 0;
+ const char *c, *end;
- enum {
- SCHEME,
- AUTHORITY_START, AUTHORITY,
- PATH_START, PATH,
- QUERY,
- FRAGMENT
- } state = SCHEME;
+ enum { PATH, QUERY, FRAGMENT } state = PATH;
- memset(url, 0, sizeof(git_net_url));
+ parser->path = path;
+ end = path + len;
- for (c = scheme_start = given; *c; c++) {
+ for (c = path; c < end; c++) {
switch (state) {
- case SCHEME:
- if (*c == ':') {
- scheme_len = (c - scheme_start);
-
- if (*(c+1) == '/' && *(c+2) == '/') {
- c += 2;
- hierarchical = true;
- state = AUTHORITY_START;
- } else {
- state = PATH_START;
- }
- } else if ((*c < 'A' || *c > 'Z') &&
- (*c < 'a' || *c > 'z') &&
- (*c < '0' || *c > '9') &&
- (*c != '+' && *c != '-' && *c != '.')) {
- /*
- * an illegal scheme character means that we
- * were just given a relative path
- */
- path_start = given;
- state = PATH;
- break;
- }
- break;
-
- case AUTHORITY_START:
- authority_start = c;
- state = AUTHORITY;
-
- /* fall through */
-
- case AUTHORITY:
- if (*c != '/')
- break;
-
- /*
- * authority is sufficiently complex that we parse
- * it separately
- */
- if ((error = url_parse_authority(
- &user_start, &user_len,
- &password_start,&password_len,
- &host_start, &host_len,
- &port_start, &port_len,
- authority_start, (c - authority_start),
- scheme_start, scheme_len)) < 0)
- goto done;
-
- /* fall through */
-
- case PATH_START:
- path_start = c;
- state = PATH;
- /* fall through */
-
case PATH:
switch (*c) {
case '?':
- path_len = (c - path_start);
- query_start = c + 1;
+ parser->path_len = (c - parser->path);
+ parser->query = c + 1;
state = QUERY;
break;
case '#':
- path_len = (c - path_start);
- fragment_start = c + 1;
+ parser->path_len = (c - parser->path);
+ parser->fragment = c + 1;
state = FRAGMENT;
break;
}
@@ -340,8 +351,8 @@ int git_net_url_parse(git_net_url *url, const char *given)
case QUERY:
if (*c == '#') {
- query_len = (c - query_start);
- fragment_start = c + 1;
+ parser->query_len = (c - parser->query);
+ parser->fragment = c + 1;
state = FRAGMENT;
}
break;
@@ -355,82 +366,70 @@ int git_net_url_parse(git_net_url *url, const char *given)
}
switch (state) {
- case SCHEME:
- /*
- * if we never saw a ':' then we were given a relative
- * path, not a bare scheme
- */
- path_start = given;
- path_len = (c - scheme_start);
- break;
- case AUTHORITY_START:
- break;
- case AUTHORITY:
- if ((error = url_parse_authority(
- &user_start, &user_len,
- &password_start,&password_len,
- &host_start, &host_len,
- &port_start, &port_len,
- authority_start, (c - authority_start),
- scheme_start, scheme_len)) < 0)
- goto done;
- break;
- case PATH_START:
- break;
case PATH:
- path_len = (c - path_start);
+ parser->path_len = (c - parser->path);
break;
case QUERY:
- query_len = (c - query_start);
+ parser->query_len = (c - parser->query);
break;
case FRAGMENT:
- fragment_len = (c - fragment_start);
+ parser->fragment_len = (c - parser->fragment);
break;
- default:
- GIT_ASSERT(!"unhandled state");
}
- if (scheme_len) {
- if ((error = git_str_put(&scheme, scheme_start, scheme_len)) < 0)
+ return 0;
+}
+
+static int url_parse_finalize(git_net_url *url, git_net_url_parser *parser)
+{
+ git_str scheme = GIT_STR_INIT, user = GIT_STR_INIT,
+ password = GIT_STR_INIT, host = GIT_STR_INIT,
+ port = GIT_STR_INIT, path = GIT_STR_INIT,
+ query = GIT_STR_INIT, fragment = GIT_STR_INIT;
+ const char *default_port;
+ int error = 0;
+
+ if (parser->scheme_len) {
+ if ((error = git_str_put(&scheme, parser->scheme, parser->scheme_len)) < 0)
goto done;
git__strntolower(scheme.ptr, scheme.size);
}
- if (user_len &&
- (error = git_str_decode_percent(&user, user_start, user_len)) < 0)
+ if (parser->user_len &&
+ (error = git_str_decode_percent(&user, parser->user, parser->user_len)) < 0)
goto done;
- if (password_len &&
- (error = git_str_decode_percent(&password, password_start, password_len)) < 0)
+ if (parser->password_len &&
+ (error = git_str_decode_percent(&password, parser->password, parser->password_len)) < 0)
goto done;
- if (host_len &&
- (error = git_str_decode_percent(&host, host_start, host_len)) < 0)
+ if (parser->host_len &&
+ (error = git_str_decode_percent(&host, parser->host, parser->host_len)) < 0)
goto done;
- if (port_len)
- error = git_str_put(&port, port_start, port_len);
- else if (scheme_len && (default_port = default_port_for_scheme(scheme.ptr)) != NULL)
+ if (parser->port_len)
+ error = git_str_put(&port, parser->port, parser->port_len);
+ else if (parser->scheme_len && (default_port = default_port_for_scheme(scheme.ptr)) != NULL)
error = git_str_puts(&port, default_port);
if (error < 0)
goto done;
- if (path_len)
- error = git_str_put(&path, path_start, path_len);
- else if (hierarchical)
+ if (parser->path_len)
+ error = git_str_put(&path, parser->path, parser->path_len);
+ else if (parser->hierarchical)
error = git_str_puts(&path, "/");
if (error < 0)
goto done;
- if (query_len &&
- (error = git_str_decode_percent(&query, query_start, query_len)) < 0)
+ if (parser->query_len &&
+ (error = git_str_decode_percent(&query, parser->query, parser->query_len)) < 0)
goto done;
- if (fragment_len &&
- (error = git_str_decode_percent(&fragment, fragment_start, fragment_len)) < 0)
+ if (parser->fragment_len &&
+ (error = git_str_decode_percent(&fragment, parser->fragment, parser->fragment_len)) < 0)
goto done;
url->scheme = git_str_detach(&scheme);
@@ -457,6 +456,157 @@ int git_net_url_parse(git_net_url *url, const char *given)
return error;
}
+int git_net_url_parse(git_net_url *url, const char *given)
+{
+ git_net_url_parser parser = GIT_NET_URL_PARSER_INIT;
+ const char *c, *authority, *path;
+ size_t authority_len = 0, path_len = 0;
+ int error = 0;
+
+ enum {
+ SCHEME_START, SCHEME,
+ AUTHORITY_START, AUTHORITY,
+ PATH_START, PATH
+ } state = SCHEME_START;
+
+ memset(url, 0, sizeof(git_net_url));
+
+ for (c = given; *c; c++) {
+ switch (state) {
+ case SCHEME_START:
+ parser.scheme = c;
+ state = SCHEME;
+
+ /* fall through */
+
+ case SCHEME:
+ if (*c == ':') {
+ parser.scheme_len = (c - parser.scheme);
+
+ if (parser.scheme_len &&
+ *(c+1) == '/' && *(c+2) == '/') {
+ c += 2;
+ parser.hierarchical = 1;
+ state = AUTHORITY_START;
+ } else {
+ state = PATH_START;
+ }
+ } else if (!is_valid_scheme_char(*c)) {
+ /*
+ * an illegal scheme character means that we
+ * were just given a relative path
+ */
+ path = given;
+ state = PATH;
+ break;
+ }
+ break;
+
+ case AUTHORITY_START:
+ authority = c;
+ state = AUTHORITY;
+
+ /* fall through */
+ case AUTHORITY:
+ if (*c != '/')
+ break;
+
+ authority_len = (c - authority);
+
+ /* fall through */
+ case PATH_START:
+ path = c;
+ state = PATH;
+ break;
+
+ case PATH:
+ break;
+
+ default:
+ GIT_ASSERT(!"unhandled state");
+ }
+ }
+
+ switch (state) {
+ case SCHEME:
+ /*
+ * if we never saw a ':' then we were given a relative
+ * path, not a bare scheme
+ */
+ path = given;
+ path_len = (c - path);
+ break;
+ case AUTHORITY_START:
+ break;
+ case AUTHORITY:
+ authority_len = (c - authority);
+ break;
+ case PATH_START:
+ break;
+ case PATH:
+ path_len = (c - path);
+ break;
+ default:
+ GIT_ASSERT(!"unhandled state");
+ }
+
+ if (authority_len &&
+ (error = url_parse_authority(&parser, authority, authority_len)) < 0)
+ goto done;
+
+ if (path_len &&
+ (error = url_parse_path(&parser, path, path_len)) < 0)
+ goto done;
+
+ error = url_parse_finalize(url, &parser);
+
+done:
+ return error;
+}
+
+int git_net_url_parse_http(
+ git_net_url *url,
+ const char *given)
+{
+ git_net_url_parser parser = GIT_NET_URL_PARSER_INIT;
+ const char *c, *authority, *path = NULL;
+ size_t authority_len = 0, path_len = 0;
+ int error;
+
+ /* Hopefully this is a proper URL with a scheme. */
+ if (git_net_str_is_url(given))
+ return git_net_url_parse(url, given);
+
+ memset(url, 0, sizeof(git_net_url));
+
+ /* Without a scheme, we are in the host (authority) section. */
+ for (c = authority = given; *c; c++) {
+ if (!path && *c == '/') {
+ authority_len = (c - authority);
+ path = c;
+ }
+ }
+
+ if (path)
+ path_len = (c - path);
+ else
+ authority_len = (c - authority);
+
+ parser.scheme = "http";
+ parser.scheme_len = 4;
+ parser.hierarchical = 1;
+
+ if (authority_len &&
+ (error = url_parse_authority(&parser, authority, authority_len)) < 0)
+ return error;
+
+ if (path_len &&
+ (error = url_parse_path(&parser, path, path_len)) < 0)
+ return error;
+
+ return url_parse_finalize(url, &parser);
+}
+
static int scp_invalid(const char *message)
{
git_error_set(GIT_ERROR_NET, "invalid scp-style path: %s", message);
@@ -506,7 +656,7 @@ static bool has_at(const char *str)
int git_net_url_parse_scp(git_net_url *url, const char *given)
{
const char *default_port = default_port_for_scheme("ssh");
- const char *c, *user, *host, *port, *path = NULL;
+ const char *c, *user, *host, *port = NULL, *path = NULL;
size_t user_len = 0, host_len = 0, port_len = 0;
unsigned short bracket = 0;
@@ -646,6 +796,13 @@ int git_net_url_parse_scp(git_net_url *url, const char *given)
return 0;
}
+int git_net_url_parse_standard_or_scp(git_net_url *url, const char *given)
+{
+ return git_net_str_is_url(given) ?
+ git_net_url_parse(url, given) :
+ git_net_url_parse_scp(url, given);
+}
+
int git_net_url_joinpath(
git_net_url *out,
git_net_url *one,
diff --git a/src/util/net.h b/src/util/net.h
index 383592812bd..8024956ad0c 100644
--- a/src/util/net.h
+++ b/src/util/net.h
@@ -9,6 +9,23 @@
#include "git2_util.h"
+/*
+ * Hostname handling
+ */
+
+/*
+ * See if a given hostname matches a certificate name pattern, according
+ * to RFC2818 rules (which specifies HTTP over TLS). Mainly, an asterisk
+ * matches anything, but is limited to a single url component.
+ */
+extern bool git_net_hostname_matches_cert(
+ const char *hostname,
+ const char *pattern);
+
+/*
+ * URL handling
+ */
+
typedef struct git_net_url {
char *scheme;
char *host;
@@ -34,6 +51,20 @@ extern int git_net_url_parse(git_net_url *url, const char *str);
/** Parses a string containing an SCP style path into a URL structure. */
extern int git_net_url_parse_scp(git_net_url *url, const char *str);
+/**
+ * Parses a string containing a standard URL or an SCP style path into
+ * a URL structure.
+ */
+extern int git_net_url_parse_standard_or_scp(git_net_url *url, const char *str);
+
+/**
+ * Parses a string containing an HTTP endpoint that may not be a
+ * well-formed URL. For example, "localhost" or "localhost:port".
+ */
+extern int git_net_url_parse_http(
+ git_net_url *url,
+ const char *str);
+
/** Appends a path and/or query string to the given URL */
extern int git_net_url_joinpath(
git_net_url *out,
diff --git a/src/util/posix.c b/src/util/posix.c
index b1f85dc9412..cfc0e0751be 100644
--- a/src/util/posix.c
+++ b/src/util/posix.c
@@ -301,3 +301,57 @@ int p_munmap(git_map *map)
}
#endif
+
+#if defined(GIT_IO_POLL) || defined(GIT_IO_WSAPOLL)
+
+/* Handled by posix.h; this test simplifies the final else */
+
+#elif defined(GIT_IO_SELECT)
+
+int p_poll(struct pollfd *fds, unsigned int nfds, int timeout_ms)
+{
+ fd_set read_fds, write_fds, except_fds;
+ struct timeval timeout = { 0, 0 };
+ unsigned int i;
+ int max_fd = -1, ret;
+
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+ FD_ZERO(&except_fds);
+
+ for (i = 0; i < nfds; i++) {
+ if ((fds[i].events & POLLIN))
+ FD_SET(fds[i].fd, &read_fds);
+
+ if ((fds[i].events & POLLOUT))
+ FD_SET(fds[i].fd, &write_fds);
+
+ if ((fds[i].events & POLLPRI))
+ FD_SET(fds[i].fd, &except_fds);
+
+ max_fd = MAX(max_fd, fds[i].fd);
+ }
+
+ if (timeout_ms > 0) {
+ timeout.tv_sec = timeout_ms / 1000;
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
+ }
+
+ if ((ret = select(max_fd + 1, &read_fds, &write_fds, &except_fds,
+ timeout_ms < 0 ? NULL : &timeout)) < 0)
+ goto done;
+
+ for (i = 0; i < nfds; i++) {
+ fds[i].revents = 0 |
+ FD_ISSET(fds[i].fd, &read_fds) ? POLLIN : 0 |
+ FD_ISSET(fds[i].fd, &write_fds) ? POLLOUT : 0 |
+ FD_ISSET(fds[i].fd, &except_fds) ? POLLPRI : 0;
+ }
+
+done:
+ return ret;
+}
+
+#else
+# error no poll compatible implementation
+#endif
diff --git a/src/util/posix.h b/src/util/posix.h
index c8f8cd9d2cc..74707453a6d 100644
--- a/src/util/posix.h
+++ b/src/util/posix.h
@@ -104,6 +104,8 @@ typedef __int64 off64_t;
typedef __haiku_std_int64 off64_t;
#elif defined(__APPLE__)
typedef __int64_t off64_t;
+#elif defined(_AIX)
+typedef long long off64_t;
#else
typedef int64_t off64_t;
#endif
@@ -193,4 +195,26 @@ extern const char *p_gai_strerror(int ret);
# define p_gai_strerror(c) gai_strerror(c)
#endif /* NO_ADDRINFO */
+#ifdef GIT_IO_POLL
+# include
+# define p_poll poll
+#elif GIT_IO_WSAPOLL
+# include
+# define p_poll WSAPoll
+#else
+# define POLLIN 0x01
+# define POLLPRI 0x02
+# define POLLOUT 0x04
+# define POLLERR 0x08
+# define POLLHUP 0x10
+
+struct pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+extern int p_poll(struct pollfd *fds, unsigned int nfds, int timeout);
+#endif
+
#endif
diff --git a/src/util/process.h b/src/util/process.h
new file mode 100644
index 00000000000..3ada6696d22
--- /dev/null
+++ b/src/util/process.h
@@ -0,0 +1,222 @@
+/*
+ * 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_process_h__
+#define INCLUDE_process_h__
+
+typedef struct git_process git_process;
+
+typedef struct {
+ unsigned int capture_in : 1,
+ capture_out : 1,
+ capture_err : 1,
+ exclude_env : 1;
+
+ char *cwd;
+} git_process_options;
+
+typedef enum {
+ GIT_PROCESS_STATUS_NONE,
+ GIT_PROCESS_STATUS_NORMAL,
+ GIT_PROCESS_STATUS_ERROR
+} git_process_result_status;
+
+#define GIT_PROCESS_RESULT_INIT { GIT_PROCESS_STATUS_NONE }
+
+typedef struct {
+ git_process_result_status status;
+ int exitcode;
+ int signal;
+} git_process_result;
+
+#define GIT_PROCESS_OPTIONS_INIT { 0 }
+
+#ifdef GIT_WIN32
+# define p_pid_t DWORD
+#else
+# define p_pid_t pid_t
+#endif
+
+/**
+ * Create a new process. The command to run should be specified as the
+ * element of the `arg` array, execv-style. This should be the full path
+ * to the command to run, the PATH is not obeyed.
+ *
+ * This function will add the given environment variables (in `env`)
+ * to the current environment. Operations on environment variables
+ * are not thread safe, so you may not modify the environment during
+ * this call. You can avoid this by setting `exclude_env` in the
+ * options and providing the entire environment yourself.
+ *
+ * @param out location to store the process
+ * @param args the command (with arguments) to run
+ * @param args_len the length of the args array
+ * @param env environment variables to add (or NULL)
+ * @param env_len the length of the env len
+ * @param opts the options for creating the process
+ * @return 0 or an error code
+ */
+extern int git_process_new(
+ git_process **out,
+ const char **args,
+ size_t args_len,
+ const char **env,
+ size_t env_len,
+ git_process_options *opts);
+
+/**
+ * Create a new process. The command to run should be specified as the
+ * `cmdline` option - which is the full text of the command line as it
+ * would be specified or run by a user. The command to run will be
+ * looked up in the PATH.
+ *
+ * On Unix, this will be executed by the system's shell (`/bin/sh`)
+ * and may contain _Bourne-style_ shell quoting rules. On Windows,
+ * this will be passed to `CreateProcess`, and similarly, may
+ * contain _Windows-style_ shell quoting rules.
+ *
+ * This function will add the given environment variables (in `env`)
+ * to the current environment. Operations on environment variables
+ * are not thread safe, so you may not modify the environment during
+ * this call. You can avoid this by setting `exclude_env` in the
+ * options and providing the entire environment yourself.
+ */
+extern int git_process_new_from_cmdline(
+ git_process **out,
+ const char *cmdline,
+ const char **env,
+ size_t env_len,
+ git_process_options *opts);
+
+#ifdef GIT_WIN32
+
+extern int git_process__appname(
+ git_str *out,
+ const char *cmdline);
+
+/* Windows path parsing is tricky; this helper function is for testing. */
+extern int git_process__cmdline(
+ git_str *out,
+ const char **in,
+ size_t in_len);
+
+#endif
+
+/*
+ * Whether the given string looks like a command line option (starts
+ * with a dash). This is useful for examining strings that will become
+ * cmdline arguments to ensure that they are not erroneously treated
+ * as an option. For example, arguments to `ssh`.
+ */
+GIT_INLINE(bool) git_process__is_cmdline_option(const char *str)
+{
+ return (str && str[0] == '-');
+}
+
+/**
+ * Start the process.
+ *
+ * @param process the process to start
+ * @return 0 or an error code
+ */
+extern int git_process_start(git_process *process);
+
+/**
+ * Returns the process id of the process.
+ *
+ * @param out pointer to a pid_t to store the process id
+ * @param process the process to query
+ * @return 0 or an error code
+ */
+extern int git_process_id(p_pid_t *out, git_process *process);
+
+/**
+ * Read from the process's stdout. The process must have been created with
+ * `capture_out` set to true.
+ *
+ * @param process the process to read from
+ * @param buf the buf to read into
+ * @param count maximum number of bytes to read
+ * @return number of bytes read or an error code
+ */
+extern ssize_t git_process_read(git_process *process, void *buf, size_t count);
+
+/**
+ * Read from the process's stderr. The process must have been created with
+ * `capture_err` set to true.
+ *
+ * @param process the process to read from
+ * @param buf the buf to read into
+ * @param count maximum number of bytes to read
+ * @return number of bytes read or an error code
+ */
+extern ssize_t git_process_read_err(git_process *process, void *buf, size_t count);
+
+/**
+ * Write to the process's stdin. The process must have been created with
+ * `capture_in` set to true.
+ *
+ * @param process the process to write to
+ * @param buf the buf to write
+ * @param count maximum number of bytes to write
+ * @return number of bytes written or an error code
+ */
+extern ssize_t git_process_write(git_process *process, const void *buf, size_t count);
+
+/**
+ * Wait for the process to finish.
+ *
+ * @param result the result of the process or NULL
+ * @param process the process to wait on
+ */
+extern int git_process_wait(git_process_result *result, git_process *process);
+
+/**
+ * Close the input pipe from the child.
+ *
+ * @param process the process to close the pipe on
+ */
+extern int git_process_close_in(git_process *process);
+
+/**
+ * Close the output pipe from the child.
+ *
+ * @param process the process to close the pipe on
+ */
+extern int git_process_close_out(git_process *process);
+
+/**
+ * Close the error pipe from the child.
+ *
+ * @param process the process to close the pipe on
+ */
+extern int git_process_close_err(git_process *process);
+
+/**
+ * Close all resources that are used by the process. This does not
+ * wait for the process to complete.
+ *
+ * @parma process the process to close
+ */
+extern int git_process_close(git_process *process);
+
+/**
+ * Place a human-readable error message in the given git buffer.
+ *
+ * @param msg the buffer to store the message
+ * @param result the process result that produced an error
+ */
+extern int git_process_result_msg(git_str *msg, git_process_result *result);
+
+/**
+ * Free a process structure
+ *
+ * @param process the process to free
+ */
+extern void git_process_free(git_process *process);
+
+#endif
diff --git a/src/util/rand.c b/src/util/rand.c
index 940faf94796..2b137a57b56 100644
--- a/src/util/rand.c
+++ b/src/util/rand.c
@@ -10,10 +10,6 @@ See . */
#include "rand.h"
#include "runtime.h"
-#if defined(GIT_RAND_GETENTROPY)
-# include
-#endif
-
#if defined(GIT_WIN32)
# include
#endif
@@ -32,7 +28,6 @@ GIT_INLINE(int) getseed(uint64_t *seed)
HCRYPTPROV provider;
SYSTEMTIME systemtime;
FILETIME filetime, idletime, kerneltime, usertime;
- bits convert;
if (CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
@@ -67,7 +62,7 @@ GIT_INLINE(int) getseed(uint64_t *seed)
*seed ^= ((uint64_t)GetCurrentProcessId() << 32);
*seed ^= ((uint64_t)GetCurrentThreadId() << 48);
- convert.f = git__timer(); *seed ^= (convert.d);
+ *seed ^= git_time_monotonic();
/* Mix in the addresses of some functions and variables */
*seed ^= (((uint64_t)((uintptr_t)seed) << 32));
@@ -81,9 +76,12 @@ GIT_INLINE(int) getseed(uint64_t *seed)
GIT_INLINE(int) getseed(uint64_t *seed)
{
struct timeval tv;
+ int fd;
+
+# if defined(GIT_RAND_GETLOADAVG)
double loadavg[3];
bits convert;
- int fd;
+# endif
# if defined(GIT_RAND_GETENTROPY)
GIT_UNUSED((fd = 0));
@@ -127,11 +125,9 @@ GIT_INLINE(int) getseed(uint64_t *seed)
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);
+ *seed ^= git_time_monotonic();
/* Mix in the addresses of some variables */
*seed ^= ((uint64_t)((size_t)((void *)seed)) << 32);
diff --git a/src/util/regexp.c b/src/util/regexp.c
index 08700882bc3..eb45822474d 100644
--- a/src/util/regexp.c
+++ b/src/util/regexp.c
@@ -125,7 +125,7 @@ int git_regexp_search(const git_regexp *r, const char *string, size_t nmatches,
if ((data = pcre2_match_data_create(nmatches, NULL)) == NULL) {
git_error_set_oom();
- goto out;
+ return -1;
}
if ((error = pcre2_match(*r, (const unsigned char *) string, strlen(string),
diff --git a/src/util/staticstr.h b/src/util/staticstr.h
new file mode 100644
index 00000000000..b7d0790c4fd
--- /dev/null
+++ b/src/util/staticstr.h
@@ -0,0 +1,66 @@
+/*
+ * 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_stackstr_h__
+#define INCLUDE_stackstr_h__
+
+#include "git2_util.h"
+
+typedef struct {
+ /* Length of / number of bytes used by `data`. */
+ size_t len;
+
+ /* Size of the allocated `data` buffer. */
+ size_t size;
+
+ /* The actual string buffer data. */
+ char data[GIT_FLEX_ARRAY];
+} git_staticstr;
+
+#define git_staticstr_with_size(__size) \
+ struct { \
+ size_t len; \
+ size_t size; \
+ char data[__size]; \
+ }
+
+#define git_staticstr_init(__str, __size) \
+ do { \
+ (__str)->len = 0; \
+ (__str)->size = __size; \
+ (__str)->data[0] = '\0'; \
+ } while(0)
+
+#define git_staticstr_offset(__str) \
+ ((__str)->data + (__str)->len)
+
+#define git_staticstr_remain(__str) \
+ ((__str)->len > (__str)->size ? 0 : ((__str)->size - (__str)->len))
+
+#define git_staticstr_increase(__str, __len) \
+ do { ((__str)->len += __len); } while(0)
+
+#define git_staticstr_consume_bytes(__str, __len) \
+ do { git_staticstr_consume(__str, (__str)->data + __len); } while(0)
+
+#define git_staticstr_consume(__str, __end) \
+ do { \
+ if (__end > (__str)->data && \
+ __end <= (__str)->data + (__str)->len) { \
+ size_t __consumed = __end - (__str)->data; \
+ memmove((__str)->data, __end, (__str)->len - __consumed); \
+ (__str)->len -= __consumed; \
+ (__str)->data[(__str)->len] = '\0'; \
+ } \
+ } while(0)
+
+#define git_staticstr_clear(__str) \
+ do { \
+ (__str)->len = 0; \
+ (__str)->data[0] = 0; \
+ } while(0)
+
+#endif
diff --git a/src/util/str.c b/src/util/str.c
index 0d405bfda50..0b07c814702 100644
--- a/src/util/str.c
+++ b/src/util/str.c
@@ -485,8 +485,8 @@ int git_str_decode_percent(
for (str_pos = 0; str_pos < str_len; buf->size++, str_pos++) {
if (str[str_pos] == '%' &&
str_len > str_pos + 2 &&
- isxdigit(str[str_pos + 1]) &&
- isxdigit(str[str_pos + 2])) {
+ git__isxdigit(str[str_pos + 1]) &&
+ git__isxdigit(str[str_pos + 2])) {
buf->ptr[buf->size] = (HEX_DECODE(str[str_pos + 1]) << 4) +
HEX_DECODE(str[str_pos + 2]);
str_pos += 2;
diff --git a/src/util/strlist.c b/src/util/strlist.c
new file mode 100644
index 00000000000..df5640c2a1f
--- /dev/null
+++ b/src/util/strlist.c
@@ -0,0 +1,108 @@
+/*
+ * 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 "git2_util.h"
+#include "vector.h"
+#include "strlist.h"
+
+int git_strlist_copy(char ***out, const char **in, size_t len)
+{
+ char **dup;
+ size_t i;
+
+ dup = git__calloc(len, sizeof(char *));
+ GIT_ERROR_CHECK_ALLOC(dup);
+
+ for (i = 0; i < len; i++) {
+ dup[i] = git__strdup(in[i]);
+ GIT_ERROR_CHECK_ALLOC(dup[i]);
+ }
+
+ *out = dup;
+ return 0;
+}
+
+int git_strlist_copy_with_null(char ***out, const char **in, size_t len)
+{
+ char **dup;
+ size_t new_len, i;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_len, len, 1);
+
+ dup = git__calloc(new_len, sizeof(char *));
+ GIT_ERROR_CHECK_ALLOC(dup);
+
+ for (i = 0; i < len; i++) {
+ dup[i] = git__strdup(in[i]);
+ GIT_ERROR_CHECK_ALLOC(dup[i]);
+ }
+
+ *out = dup;
+ return 0;
+}
+
+bool git_strlist_contains_prefix(
+ const char **strings,
+ size_t len,
+ const char *str,
+ size_t n)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (strncmp(strings[i], str, n) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+bool git_strlist_contains_key(
+ const char **strings,
+ size_t len,
+ const char *key,
+ char delimiter)
+{
+ const char *c;
+
+ for (c = key; *c; c++) {
+ if (*c == delimiter)
+ break;
+ }
+
+ return *c ?
+ git_strlist_contains_prefix(strings, len, key, (c - key)) :
+ false;
+}
+
+void git_strlist_free(char **strings, size_t len)
+{
+ size_t i;
+
+ if (!strings)
+ return;
+
+ for (i = 0; i < len; i++)
+ git__free(strings[i]);
+
+ git__free(strings);
+}
+
+void git_strlist_free_with_null(char **strings)
+{
+ char **s;
+
+ if (!strings)
+ return;
+
+ for (s = strings; *s; s++)
+ git__free(*s);
+
+ git__free(strings);
+}
diff --git a/src/util/strlist.h b/src/util/strlist.h
new file mode 100644
index 00000000000..68fbf8fb263
--- /dev/null
+++ b/src/util/strlist.h
@@ -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.
+ */
+
+#ifndef INCLUDE_runtime_h__
+#define INCLUDE_runtime_h__
+
+#include "git2_util.h"
+
+extern int git_strlist_copy(char ***out, const char **in, size_t len);
+
+extern int git_strlist_copy_with_null(
+ char ***out,
+ const char **in,
+ size_t len);
+
+extern bool git_strlist_contains_prefix(
+ const char **strings,
+ size_t len,
+ const char *str,
+ size_t n);
+
+extern bool git_strlist_contains_key(
+ const char **strings,
+ size_t len,
+ const char *key,
+ char delimiter);
+
+extern void git_strlist_free(char **strings, size_t len);
+
+extern void git_strlist_free_with_null(char **strings);
+
+#endif
diff --git a/src/util/unix/posix.h b/src/util/unix/posix.h
index 778477e8e2f..60f27d3d333 100644
--- a/src/util/unix/posix.h
+++ b/src/util/unix/posix.h
@@ -54,8 +54,6 @@ GIT_INLINE(int) p_fsync(int fd)
#define p_send(s,b,l,f) send(s,b,l,f)
#define p_inet_pton(a, b, c) inet_pton(a, b, c)
-#define p_strcasecmp(s1, s2) strcasecmp(s1, s2)
-#define p_strncasecmp(s1, s2, c) strncasecmp(s1, s2, c)
#define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a)
#define p_snprintf snprintf
#define p_chdir(p) chdir(p)
diff --git a/src/util/unix/process.c b/src/util/unix/process.c
new file mode 100644
index 00000000000..68c0384a4c4
--- /dev/null
+++ b/src/util/unix/process.c
@@ -0,0 +1,629 @@
+/*
+ * 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
+
+#include "git2_util.h"
+#include "vector.h"
+#include "process.h"
+#include "strlist.h"
+
+#ifdef __APPLE__
+ #include
+ #define environ (*_NSGetEnviron())
+#else
+ extern char **environ;
+#endif
+
+struct git_process {
+ char **args;
+ char **env;
+
+ char *cwd;
+
+ unsigned int capture_in : 1,
+ capture_out : 1,
+ capture_err : 1;
+
+ pid_t pid;
+
+ int child_in;
+ int child_out;
+ int child_err;
+ git_process_result_status status;
+};
+
+GIT_INLINE(bool) is_delete_env(const char *env)
+{
+ char *c = strchr(env, '=');
+
+ if (c == NULL)
+ return false;
+
+ return *(c+1) == '\0';
+}
+
+static int merge_env(
+ char ***out,
+ const char **env,
+ size_t env_len,
+ bool exclude_env)
+{
+ git_vector merged = GIT_VECTOR_INIT;
+ char **kv, *dup;
+ size_t max, cnt;
+ int error = 0;
+
+ for (max = env_len, kv = environ; !exclude_env && *kv; kv++)
+ max++;
+
+ if ((error = git_vector_init(&merged, max, NULL)) < 0)
+ goto on_error;
+
+ for (cnt = 0; env && cnt < env_len; cnt++) {
+ if (is_delete_env(env[cnt]))
+ continue;
+
+ dup = git__strdup(env[cnt]);
+ GIT_ERROR_CHECK_ALLOC(dup);
+
+ if ((error = git_vector_insert(&merged, dup)) < 0)
+ goto on_error;
+ }
+
+ if (!exclude_env) {
+ for (kv = environ; *kv; kv++) {
+ if (env && git_strlist_contains_key(env, env_len, *kv, '='))
+ continue;
+
+ dup = git__strdup(*kv);
+ GIT_ERROR_CHECK_ALLOC(dup);
+
+ if ((error = git_vector_insert(&merged, dup)) < 0)
+ goto on_error;
+ }
+ }
+
+ if (merged.length == 0) {
+ *out = NULL;
+ error = 0;
+ goto on_error;
+ }
+
+ git_vector_insert(&merged, NULL);
+
+ *out = (char **)merged.contents;
+
+ return 0;
+
+on_error:
+ git_vector_free_deep(&merged);
+ return error;
+}
+
+int git_process_new(
+ git_process **out,
+ const char **args,
+ size_t args_len,
+ const char **env,
+ size_t env_len,
+ git_process_options *opts)
+{
+ git_process *process;
+
+ GIT_ASSERT_ARG(out && args && args_len > 0);
+
+ *out = NULL;
+
+ process = git__calloc(sizeof(git_process), 1);
+ GIT_ERROR_CHECK_ALLOC(process);
+
+ if (git_strlist_copy_with_null(&process->args, args, args_len) < 0 ||
+ merge_env(&process->env, env, env_len, opts ? opts->exclude_env : false) < 0) {
+ git_process_free(process);
+ return -1;
+ }
+
+ if (opts) {
+ process->capture_in = opts->capture_in;
+ process->capture_out = opts->capture_out;
+ process->capture_err = opts->capture_err;
+
+ if (opts->cwd) {
+ process->cwd = git__strdup(opts->cwd);
+ GIT_ERROR_CHECK_ALLOC(process->cwd);
+ }
+ }
+
+ process->child_in = -1;
+ process->child_out = -1;
+ process->child_err = -1;
+ process->status = -1;
+
+ *out = process;
+ return 0;
+}
+
+extern int git_process_new_from_cmdline(
+ git_process **out,
+ const char *cmdline,
+ const char **env,
+ size_t env_len,
+ git_process_options *opts)
+{
+ const char *args[] = { "/bin/sh", "-c", cmdline };
+
+ return git_process_new(out,
+ args, ARRAY_SIZE(args), env, env_len, opts);
+}
+
+#define CLOSE_FD(fd) \
+ if (fd >= 0) { \
+ close(fd); \
+ fd = -1; \
+ }
+
+static int try_read_status(size_t *out, int fd, void *buf, size_t len)
+{
+ size_t read_len = 0;
+ int ret = -1;
+
+ while (ret && read_len < len) {
+ ret = read(fd, buf + read_len, len - read_len);
+
+ if (ret < 0 && errno != EAGAIN && errno != EINTR) {
+ git_error_set(GIT_ERROR_OS, "could not read child status");
+ return -1;
+ }
+
+ read_len += ret;
+ }
+
+ *out = read_len;
+ return 0;
+}
+
+
+static int read_status(int fd)
+{
+ size_t status_len = sizeof(int) * 3, read_len = 0;
+ char buffer[status_len], fn[128];
+ int error, fn_error, os_error, fn_len = 0;
+
+ if ((error = try_read_status(&read_len, fd, buffer, status_len)) < 0)
+ return error;
+
+ /* Immediate EOF indicates the exec succeeded. */
+ if (read_len == 0)
+ return 0;
+
+ if (read_len < status_len) {
+ git_error_set(GIT_ERROR_INVALID, "child status truncated");
+ return -1;
+ }
+
+ memcpy(&fn_error, &buffer[0], sizeof(int));
+ memcpy(&os_error, &buffer[sizeof(int)], sizeof(int));
+ memcpy(&fn_len, &buffer[sizeof(int) * 2], sizeof(int));
+
+ if (fn_len > 0) {
+ fn_len = min(fn_len, (int)(ARRAY_SIZE(fn) - 1));
+
+ if ((error = try_read_status(&read_len, fd, fn, fn_len)) < 0)
+ return error;
+
+ fn[fn_len] = '\0';
+ } else {
+ fn[0] = '\0';
+ }
+
+ if (fn_error) {
+ errno = os_error;
+ git_error_set(GIT_ERROR_OS, "could not %s", fn[0] ? fn : "(unknown)");
+ }
+
+ return fn_error;
+}
+
+static bool try_write_status(int fd, const void *buf, size_t len)
+{
+ size_t write_len;
+ int ret;
+
+ for (write_len = 0; write_len < len; ) {
+ ret = write(fd, buf + write_len, len - write_len);
+
+ if (ret <= 0)
+ break;
+
+ write_len += ret;
+ }
+
+ return (len == write_len);
+}
+
+static void write_status(int fd, const char *fn, int error, int os_error)
+{
+ size_t status_len = sizeof(int) * 3, fn_len;
+ char buffer[status_len];
+
+ fn_len = strlen(fn);
+
+ if (fn_len > INT_MAX)
+ fn_len = INT_MAX;
+
+ memcpy(&buffer[0], &error, sizeof(int));
+ memcpy(&buffer[sizeof(int)], &os_error, sizeof(int));
+ memcpy(&buffer[sizeof(int) * 2], &fn_len, sizeof(int));
+
+ /* Do our best effort to write all the status. */
+ if (!try_write_status(fd, buffer, status_len))
+ return;
+
+ if (fn_len)
+ try_write_status(fd, fn, fn_len);
+}
+
+int git_process_start(git_process *process)
+{
+ int in[2] = { -1, -1 }, out[2] = { -1, -1 },
+ err[2] = { -1, -1 }, status[2] = { -1, -1 };
+ int fdflags, state, error;
+ pid_t pid;
+
+ /* Set up the pipes to read from/write to the process */
+ if ((process->capture_in && pipe(in) < 0) ||
+ (process->capture_out && pipe(out) < 0) ||
+ (process->capture_err && pipe(err) < 0)) {
+ git_error_set(GIT_ERROR_OS, "could not create pipe");
+ goto on_error;
+ }
+
+ /* Set up a self-pipe for status from the forked process. */
+ if (pipe(status) < 0 ||
+ (fdflags = fcntl(status[1], F_GETFD)) < 0 ||
+ fcntl(status[1], F_SETFD, fdflags | FD_CLOEXEC) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not create pipe");
+ goto on_error;
+ }
+
+ switch (pid = fork()) {
+ case -1:
+ git_error_set(GIT_ERROR_OS, "could not fork");
+ goto on_error;
+
+ /* Child: start the process. */
+ case 0:
+ /* Close the opposing side of the pipes */
+ CLOSE_FD(status[0]);
+
+ if (process->capture_in) {
+ CLOSE_FD(in[1]);
+ dup2(in[0], STDIN_FILENO);
+ }
+
+ if (process->capture_out) {
+ CLOSE_FD(out[0]);
+ dup2(out[1], STDOUT_FILENO);
+ }
+
+ if (process->capture_err) {
+ CLOSE_FD(err[0]);
+ dup2(err[1], STDERR_FILENO);
+ }
+
+ if (process->cwd && (error = chdir(process->cwd)) < 0) {
+ write_status(status[1], "chdir", error, errno);
+ exit(0);
+ }
+
+ /*
+ * Exec the process and write the results back if the
+ * call fails. If it succeeds, we'll close the status
+ * pipe (via CLOEXEC) and the parent will know.
+ */
+ error = execve(process->args[0],
+ process->args,
+ process->env);
+
+ write_status(status[1], "execve", error, errno);
+ exit(0);
+
+ /* Parent: make sure the child process exec'd correctly. */
+ default:
+ /* Close the opposing side of the pipes */
+ CLOSE_FD(status[1]);
+
+ if (process->capture_in) {
+ CLOSE_FD(in[0]);
+ process->child_in = in[1];
+ }
+
+ if (process->capture_out) {
+ CLOSE_FD(out[1]);
+ process->child_out = out[0];
+ }
+
+ if (process->capture_err) {
+ CLOSE_FD(err[1]);
+ process->child_err = err[0];
+ }
+
+ /* Try to read the status */
+ process->status = status[0];
+ if ((error = read_status(status[0])) < 0) {
+ waitpid(process->pid, &state, 0);
+ goto on_error;
+ }
+
+ process->pid = pid;
+ return 0;
+ }
+
+on_error:
+ CLOSE_FD(in[0]); CLOSE_FD(in[1]);
+ CLOSE_FD(out[0]); CLOSE_FD(out[1]);
+ CLOSE_FD(err[0]); CLOSE_FD(err[1]);
+ CLOSE_FD(status[0]); CLOSE_FD(status[1]);
+ return -1;
+}
+
+int git_process_id(p_pid_t *out, git_process *process)
+{
+ GIT_ASSERT(out && process);
+
+ if (!process->pid) {
+ git_error_set(GIT_ERROR_INVALID, "process not running");
+ return -1;
+ }
+
+ *out = process->pid;
+ return 0;
+}
+
+static ssize_t process_read(int fd, void *buf, size_t count)
+{
+ ssize_t ret;
+
+ if (count > SSIZE_MAX)
+ count = SSIZE_MAX;
+
+ if ((ret = read(fd, buf, count)) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not read from child process");
+ return -1;
+ }
+
+ return ret;
+}
+
+ssize_t git_process_read(git_process *process, void *buf, size_t count)
+{
+ GIT_ASSERT_ARG(process);
+ GIT_ASSERT(process->capture_out);
+
+ return process_read(process->child_out, buf, count);
+}
+
+ssize_t git_process_read_err(git_process *process, void *buf, size_t count)
+{
+ GIT_ASSERT_ARG(process);
+ GIT_ASSERT(process->capture_err);
+
+ return process_read(process->child_err, buf, count);
+}
+
+#ifdef GIT_THREADS
+
+# define signal_state sigset_t
+
+/*
+ * Since signal-handling is process-wide, we cannot simply use
+ * SIG_IGN to avoid SIGPIPE. Instead: http://www.microhowto.info:80/howto/ignore_sigpipe_without_affecting_other_threads_in_a_process.html
+ */
+
+GIT_INLINE(int) disable_signals(sigset_t *saved_mask)
+{
+ sigset_t sigpipe_mask;
+
+ sigemptyset(&sigpipe_mask);
+ sigaddset(&sigpipe_mask, SIGPIPE);
+
+ if (pthread_sigmask(SIG_BLOCK, &sigpipe_mask, saved_mask) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not configure signal mask");
+ return -1;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) restore_signals(sigset_t *saved_mask)
+{
+ sigset_t sigpipe_mask, pending;
+ int signal;
+
+ sigemptyset(&sigpipe_mask);
+ sigaddset(&sigpipe_mask, SIGPIPE);
+
+ if (sigpending(&pending) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not examine pending signals");
+ return -1;
+ }
+
+ if (sigismember(&pending, SIGPIPE) == 1 &&
+ sigwait(&sigpipe_mask, &signal) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not wait for (blocking) signal delivery");
+ return -1;
+ }
+
+ if (pthread_sigmask(SIG_SETMASK, saved_mask, 0) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not configure signal mask");
+ return -1;
+ }
+
+ return 0;
+}
+
+#else
+
+# define signal_state struct sigaction
+
+GIT_INLINE(int) disable_signals(struct sigaction *saved_handler)
+{
+ struct sigaction ign_handler = { 0 };
+
+ ign_handler.sa_handler = SIG_IGN;
+
+ if (sigaction(SIGPIPE, &ign_handler, saved_handler) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not configure signal handler");
+ return -1;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) restore_signals(struct sigaction *saved_handler)
+{
+ if (sigaction(SIGPIPE, saved_handler, NULL) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not configure signal handler");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif
+
+ssize_t git_process_write(git_process *process, const void *buf, size_t count)
+{
+ signal_state saved_signal;
+ ssize_t ret;
+
+ GIT_ASSERT_ARG(process);
+ GIT_ASSERT(process->capture_in);
+
+ if (count > SSIZE_MAX)
+ count = SSIZE_MAX;
+
+ if (disable_signals(&saved_signal) < 0)
+ return -1;
+
+ if ((ret = write(process->child_in, buf, count)) < 0)
+ git_error_set(GIT_ERROR_OS, "could not write to child process");
+
+ if (restore_signals(&saved_signal) < 0)
+ return -1;
+
+ return (ret < 0) ? -1 : ret;
+}
+
+int git_process_close_in(git_process *process)
+{
+ if (!process->capture_in) {
+ git_error_set(GIT_ERROR_INVALID, "input is not open");
+ return -1;
+ }
+
+ CLOSE_FD(process->child_in);
+ return 0;
+}
+
+int git_process_close_out(git_process *process)
+{
+ if (!process->capture_out) {
+ git_error_set(GIT_ERROR_INVALID, "output is not open");
+ return -1;
+ }
+
+ CLOSE_FD(process->child_out);
+ return 0;
+}
+
+int git_process_close_err(git_process *process)
+{
+ if (!process->capture_err) {
+ git_error_set(GIT_ERROR_INVALID, "error is not open");
+ return -1;
+ }
+
+ CLOSE_FD(process->child_err);
+ return 0;
+}
+
+int git_process_close(git_process *process)
+{
+ CLOSE_FD(process->child_in);
+ CLOSE_FD(process->child_out);
+ CLOSE_FD(process->child_err);
+
+ return 0;
+}
+
+int git_process_wait(git_process_result *result, git_process *process)
+{
+ int state;
+
+ if (result)
+ memset(result, 0, sizeof(git_process_result));
+
+ if (!process->pid) {
+ git_error_set(GIT_ERROR_INVALID, "process is stopped");
+ return -1;
+ }
+
+ if (waitpid(process->pid, &state, 0) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not wait for child");
+ return -1;
+ }
+
+ process->pid = 0;
+
+ if (result) {
+ if (WIFEXITED(state)) {
+ result->status = GIT_PROCESS_STATUS_NORMAL;
+ result->exitcode = WEXITSTATUS(state);
+ } else if (WIFSIGNALED(state)) {
+ result->status = GIT_PROCESS_STATUS_ERROR;
+ result->signal = WTERMSIG(state);
+ } else {
+ result->status = GIT_PROCESS_STATUS_ERROR;
+ }
+ }
+
+ return 0;
+}
+
+int git_process_result_msg(git_str *out, git_process_result *result)
+{
+ if (result->status == GIT_PROCESS_STATUS_NONE) {
+ return git_str_puts(out, "process not started");
+ } else if (result->status == GIT_PROCESS_STATUS_NORMAL) {
+ return git_str_printf(out, "process exited with code %d",
+ result->exitcode);
+ } else if (result->signal) {
+ return git_str_printf(out, "process exited on signal %d",
+ result->signal);
+ }
+
+ return git_str_puts(out, "unknown error");
+}
+
+void git_process_free(git_process *process)
+{
+ if (!process)
+ return;
+
+ if (process->pid)
+ git_process_close(process);
+
+ git__free(process->cwd);
+ git_strlist_free_with_null(process->args);
+ git_strlist_free_with_null(process->env);
+ git__free(process);
+}
diff --git a/src/util/unix/realpath.c b/src/util/unix/realpath.c
index 9e31a63b9f4..e1d2adb8dba 100644
--- a/src/util/unix/realpath.c
+++ b/src/util/unix/realpath.c
@@ -16,17 +16,35 @@
char *p_realpath(const char *pathname, char *resolved)
{
- char *ret;
- if ((ret = realpath(pathname, resolved)) == NULL)
+ char *result;
+
+ if ((result = realpath(pathname, resolved)) == NULL)
return NULL;
#ifdef __OpenBSD__
/* The OpenBSD realpath function behaves differently,
* figure out if the file exists */
- if (access(ret, F_OK) < 0)
- ret = NULL;
+ if (access(ret, F_OK) < 0) {
+ if (!resolved)
+ free(result);
+
+ return NULL;
+ }
#endif
- return ret;
+
+ /*
+ * If resolved == NULL, the system has allocated the result
+ * string. We need to strdup this into _our_ allocator pool
+ * so that callers can free it with git__free.
+ */
+ if (!resolved) {
+ char *dup = git__strdup(result);
+ free(result);
+
+ result = dup;
+ }
+
+ return result;
}
#endif
diff --git a/src/util/util.c b/src/util/util.c
index aee95fddfb5..e86bceeb5a8 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -18,7 +18,7 @@
# endif
# include
-# ifdef GIT_QSORT_S
+# ifdef GIT_QSORT_MSC
# include
# endif
#endif
@@ -623,12 +623,12 @@ int git__bsearch_r(
*/
int git__strcmp_cb(const void *a, const void *b)
{
- return strcmp((const char *)a, (const char *)b);
+ return git__strcmp((const char *)a, (const char *)b);
}
int git__strcasecmp_cb(const void *a, const void *b)
{
- return strcasecmp((const char *)a, (const char *)b);
+ return git__strcasecmp((const char *)a, (const char *)b);
}
int git__parse_bool(int *out, const char *value)
@@ -673,7 +673,7 @@ size_t git__unescape(char *str)
return (pos - str);
}
-#if defined(GIT_QSORT_S) || defined(GIT_QSORT_R_BSD)
+#if defined(GIT_QSORT_MSC) || defined(GIT_QSORT_BSD)
typedef struct {
git__sort_r_cmp cmp;
void *payload;
@@ -688,9 +688,11 @@ static int GIT_LIBGIT2_CALL git__qsort_r_glue_cmp(
#endif
-#if !defined(GIT_QSORT_R_BSD) && \
- !defined(GIT_QSORT_R_GNU) && \
- !defined(GIT_QSORT_S)
+#if !defined(GIT_QSORT_BSD) && \
+ !defined(GIT_QSORT_GNU) && \
+ !defined(GIT_QSORT_C11) && \
+ !defined(GIT_QSORT_MSC)
+
static void swap(uint8_t *a, uint8_t *b, size_t elsize)
{
char tmp[256];
@@ -716,17 +718,20 @@ static void insertsort(
for (j = i; j > base && cmp(j, j - elsize, payload) < 0; j -= elsize)
swap(j, j - elsize, elsize);
}
+
#endif
void git__qsort_r(
void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload)
{
-#if defined(GIT_QSORT_R_BSD)
+#if defined(GIT_QSORT_GNU)
+ qsort_r(els, nel, elsize, cmp, payload);
+#elif defined(GIT_QSORT_C11)
+ qsort_s(els, nel, elsize, cmp, payload);
+#elif defined(GIT_QSORT_BSD)
git__qsort_r_glue glue = { cmp, payload };
qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp);
-#elif defined(GIT_QSORT_R_GNU)
- qsort_r(els, nel, elsize, cmp, payload);
-#elif defined(GIT_QSORT_S)
+#elif defined(GIT_QSORT_MSC)
git__qsort_r_glue glue = { cmp, payload };
qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue);
#else
@@ -743,7 +748,7 @@ int git__getenv(git_str *out, const char *name)
git_str_clear(out);
- if (git__utf8_to_16_alloc(&wide_name, name) < 0)
+ if (git_utf8_to_16_alloc(&wide_name, name) < 0)
return -1;
if ((value_len = GetEnvironmentVariableW(wide_name, NULL, 0)) > 0) {
diff --git a/src/util/util.h b/src/util/util.h
index 63d6080f730..2ed005110ef 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -83,15 +83,6 @@ extern char *git__strsep(char **end, const char *sep);
extern void git__strntolower(char *str, size_t len);
extern void git__strtolower(char *str);
-#ifdef GIT_WIN32
-GIT_INLINE(int) git__tolower(int c)
-{
- return (c >= 'A' && c <= 'Z') ? (c + 32) : c;
-}
-#else
-# define git__tolower(a) tolower(a)
-#endif
-
extern size_t git__linenlen(const char *buffer, size_t buffer_len);
GIT_INLINE(const char *) git__next_line(const char *s)
@@ -249,26 +240,6 @@ GIT_INLINE(size_t) git__size_t_powerof2(size_t v)
return git__size_t_bitmask(v) + 1;
}
-GIT_INLINE(bool) git__isupper(int c)
-{
- return (c >= 'A' && c <= 'Z');
-}
-
-GIT_INLINE(bool) git__isalpha(int c)
-{
- return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
-}
-
-GIT_INLINE(bool) git__isdigit(int c)
-{
- return (c >= '0' && c <= '9');
-}
-
-GIT_INLINE(bool) git__isspace(int c)
-{
- return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');
-}
-
GIT_INLINE(bool) git__isspace_nonlf(int c)
{
return (c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\v');
@@ -279,11 +250,6 @@ GIT_INLINE(bool) git__iswildcard(int c)
return (c == '*' || c == '?' || c == '[');
}
-GIT_INLINE(bool) git__isxdigit(int c)
-{
- return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
-}
-
/*
* Parse a string value as a boolean, just like Core Git does.
*
@@ -319,59 +285,67 @@ GIT_INLINE(void) git__memzero(void *data, size_t size)
#ifdef GIT_WIN32
-GIT_INLINE(double) git__timer(void)
+GIT_INLINE(uint64_t) git_time_monotonic(void)
{
/* GetTickCount64 returns the number of milliseconds that have
* elapsed since the system was started. */
- return (double) GetTickCount64() / (double) 1000;
+ return GetTickCount64();
}
#elif __APPLE__
#include
+#include
-GIT_INLINE(double) git__timer(void)
+GIT_INLINE(uint64_t) git_time_monotonic(void)
{
- uint64_t time = mach_absolute_time();
- static double scaling_factor = 0;
+ static double scaling_factor = 0;
- if (scaling_factor == 0) {
- mach_timebase_info_data_t info;
- (void)mach_timebase_info(&info);
- scaling_factor = (double)info.numer / (double)info.denom;
- }
+ if (scaling_factor == 0) {
+ mach_timebase_info_data_t info;
+
+ scaling_factor = mach_timebase_info(&info) == KERN_SUCCESS ?
+ ((double)info.numer / (double)info.denom) / 1.0E6 :
+ -1;
+ } else if (scaling_factor < 0) {
+ struct timeval tv;
+
+ /* mach_timebase_info failed; fall back to gettimeofday */
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+ }
- return (double)time * scaling_factor / 1.0E9;
+ return (uint64_t)(mach_absolute_time() * scaling_factor);
}
#elif defined(__amigaos4__)
#include
-GIT_INLINE(double) git__timer(void)
+GIT_INLINE(uint64_t) git_time_monotonic(void)
{
struct TimeVal tv;
ITimer->GetUpTime(&tv);
- return (double)tv.Seconds + (double)tv.Microseconds / 1.0E6;
+ return (tv.Seconds * 1000) + (tv.Microseconds / 1000);
}
#else
#include
-GIT_INLINE(double) git__timer(void)
+GIT_INLINE(uint64_t) git_time_monotonic(void)
{
struct timeval tv;
#ifdef CLOCK_MONOTONIC
struct timespec tp;
if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
- return (double) tp.tv_sec + (double) tp.tv_nsec / 1.0E9;
+ return (tp.tv_sec * 1000) + (tp.tv_nsec / 1.0E6);
#endif
/* Fall back to using gettimeofday */
gettimeofday(&tv, NULL);
- return (double)tv.tv_sec + (double)tv.tv_usec / 1.0E6;
+ return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}
#endif
diff --git a/src/util/win32/error.c b/src/util/win32/error.c
index 3a52fb5a90a..dfd6fa1e8a1 100644
--- a/src/util/win32/error.c
+++ b/src/util/win32/error.c
@@ -43,7 +43,7 @@ char *git_win32_get_error_message(DWORD error_code)
(LPWSTR)&lpMsgBuf, 0, NULL)) {
/* Convert the message to UTF-8. If this fails, we will
* return NULL, which is a condition expected by the caller */
- if (git__utf16_to_8_alloc(&utf8_msg, lpMsgBuf) < 0)
+ if (git_utf8_from_16_alloc(&utf8_msg, lpMsgBuf) < 0)
utf8_msg = NULL;
LocalFree(lpMsgBuf);
diff --git a/src/util/win32/findfile.c b/src/util/win32/findfile.c
deleted file mode 100644
index 725a90167f2..00000000000
--- a/src/util/win32/findfile.c
+++ /dev/null
@@ -1,286 +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 "findfile.h"
-
-#include "path_w32.h"
-#include "utf-conv.h"
-#include "fs_path.h"
-
-#define REG_GITFORWINDOWS_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
-#define REG_GITFORWINDOWS_KEY_WOW64 L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
-
-static int git_win32__expand_path(git_win32_path dest, const wchar_t *src)
-{
- DWORD len = ExpandEnvironmentStringsW(src, dest, GIT_WIN_PATH_UTF16);
-
- if (!len || len > GIT_WIN_PATH_UTF16)
- return -1;
-
- return 0;
-}
-
-static int win32_path_to_8(git_str *dest, const wchar_t *src)
-{
- git_win32_utf8_path utf8_path;
-
- if (git_win32_path_to_utf8(utf8_path, src) < 0) {
- git_error_set(GIT_ERROR_OS, "unable to convert path to UTF-8");
- return -1;
- }
-
- /* Convert backslashes to forward slashes */
- git_fs_path_mkposix(utf8_path);
-
- return git_str_sets(dest, utf8_path);
-}
-
-static git_win32_path mock_registry;
-static bool mock_registry_set;
-
-extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir)
-{
- if (!mock_sysdir) {
- mock_registry[0] = L'\0';
- mock_registry_set = false;
- } else {
- size_t len = wcslen(mock_sysdir);
-
- if (len > GIT_WIN_PATH_MAX) {
- git_error_set(GIT_ERROR_INVALID, "mock path too long");
- return -1;
- }
-
- wcscpy(mock_registry, mock_sysdir);
- mock_registry_set = true;
- }
-
- return 0;
-}
-
-static int lookup_registry_key(
- git_win32_path out,
- const HKEY hive,
- const wchar_t* key,
- const wchar_t *value)
-{
- HKEY hkey;
- DWORD type, size;
- int error = GIT_ENOTFOUND;
-
- /*
- * Registry data may not be NUL terminated, provide room to do
- * it ourselves.
- */
- size = (DWORD)((sizeof(git_win32_path) - 1) * sizeof(wchar_t));
-
- if (RegOpenKeyExW(hive, key, 0, KEY_READ, &hkey) != 0)
- return GIT_ENOTFOUND;
-
- if (RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)out, &size) == 0 &&
- type == REG_SZ &&
- size > 0 &&
- size < sizeof(git_win32_path)) {
- size_t wsize = size / sizeof(wchar_t);
- size_t len = wsize - 1;
-
- if (out[wsize - 1] != L'\0') {
- len = wsize;
- out[wsize] = L'\0';
- }
-
- if (out[len - 1] == L'\\')
- out[len - 1] = L'\0';
-
- if (_waccess(out, F_OK) == 0)
- error = 0;
- }
-
- RegCloseKey(hkey);
- return error;
-}
-
-static int find_sysdir_in_registry(git_win32_path out)
-{
- if (mock_registry_set) {
- if (mock_registry[0] == L'\0')
- return GIT_ENOTFOUND;
-
- wcscpy(out, mock_registry);
- return 0;
- }
-
- if (lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
- lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0 ||
- lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
- lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0)
- return 0;
-
- return GIT_ENOTFOUND;
-}
-
-static int find_sysdir_in_path(git_win32_path out)
-{
- size_t out_len;
-
- if (git_win32_path_find_executable(out, L"git.exe") < 0 &&
- git_win32_path_find_executable(out, L"git.cmd") < 0)
- return GIT_ENOTFOUND;
-
- out_len = wcslen(out);
-
- /* Trim the file name */
- if (out_len <= CONST_STRLEN(L"git.exe"))
- return GIT_ENOTFOUND;
-
- out_len -= CONST_STRLEN(L"git.exe");
-
- if (out_len && out[out_len - 1] == L'\\')
- out_len--;
-
- /*
- * Git for Windows usually places the command in a 'bin' or
- * 'cmd' directory, trim that.
- */
- if (out_len >= CONST_STRLEN(L"\\bin") &&
- wcsncmp(&out[out_len - CONST_STRLEN(L"\\bin")], L"\\bin", CONST_STRLEN(L"\\bin")) == 0)
- out_len -= CONST_STRLEN(L"\\bin");
- else if (out_len >= CONST_STRLEN(L"\\cmd") &&
- wcsncmp(&out[out_len - CONST_STRLEN(L"\\cmd")], L"\\cmd", CONST_STRLEN(L"\\cmd")) == 0)
- out_len -= CONST_STRLEN(L"\\cmd");
-
- if (!out_len)
- return GIT_ENOTFOUND;
-
- out[out_len] = L'\0';
- return 0;
-}
-
-static int win32_find_existing_dirs(
- git_str* out,
- const wchar_t* tmpl[])
-{
- git_win32_path path16;
- git_str buf = GIT_STR_INIT;
-
- git_str_clear(out);
-
- for (; *tmpl != NULL; tmpl++) {
- if (!git_win32__expand_path(path16, *tmpl) &&
- path16[0] != L'%' &&
- !_waccess(path16, F_OK)) {
- win32_path_to_8(&buf, path16);
-
- if (buf.size)
- git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
- }
- }
-
- git_str_dispose(&buf);
-
- return (git_str_oom(out) ? -1 : 0);
-}
-
-static int append_subdir(git_str *out, git_str *path, const char *subdir)
-{
- static const char* architecture_roots[] = {
- "",
- "mingw64",
- "mingw32",
- NULL
- };
- const char **root;
- size_t orig_path_len = path->size;
-
- for (root = architecture_roots; *root; root++) {
- if ((*root[0] && git_str_joinpath(path, path->ptr, *root) < 0) ||
- git_str_joinpath(path, path->ptr, subdir) < 0)
- return -1;
-
- if (git_fs_path_exists(path->ptr) &&
- git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, path->ptr) < 0)
- return -1;
-
- git_str_truncate(path, orig_path_len);
- }
-
- return 0;
-}
-
-int git_win32__find_system_dirs(git_str *out, const char *subdir)
-{
- git_win32_path pathdir, regdir;
- git_str path8 = GIT_STR_INIT;
- bool has_pathdir, has_regdir;
- int error;
-
- has_pathdir = (find_sysdir_in_path(pathdir) == 0);
- has_regdir = (find_sysdir_in_registry(regdir) == 0);
-
- if (!has_pathdir && !has_regdir)
- return 0;
-
- /*
- * Usually the git in the path is the same git in the registry,
- * in this case there's no need to duplicate the paths.
- */
- if (has_pathdir && has_regdir && wcscmp(pathdir, regdir) == 0)
- has_regdir = false;
-
- if (has_pathdir) {
- if ((error = win32_path_to_8(&path8, pathdir)) < 0 ||
- (error = append_subdir(out, &path8, subdir)) < 0)
- goto done;
- }
-
- if (has_regdir) {
- if ((error = win32_path_to_8(&path8, regdir)) < 0 ||
- (error = append_subdir(out, &path8, subdir)) < 0)
- goto done;
- }
-
-done:
- git_str_dispose(&path8);
- return error;
-}
-
-int git_win32__find_global_dirs(git_str *out)
-{
- static const wchar_t *global_tmpls[4] = {
- L"%HOME%\\",
- L"%HOMEDRIVE%%HOMEPATH%\\",
- L"%USERPROFILE%\\",
- NULL,
- };
-
- return win32_find_existing_dirs(out, global_tmpls);
-}
-
-int git_win32__find_xdg_dirs(git_str *out)
-{
- static const wchar_t *global_tmpls[7] = {
- L"%XDG_CONFIG_HOME%\\git",
- L"%APPDATA%\\git",
- L"%LOCALAPPDATA%\\git",
- L"%HOME%\\.config\\git",
- L"%HOMEDRIVE%%HOMEPATH%\\.config\\git",
- L"%USERPROFILE%\\.config\\git",
- NULL,
- };
-
- return win32_find_existing_dirs(out, global_tmpls);
-}
-
-int git_win32__find_programdata_dirs(git_str *out)
-{
- static const wchar_t *programdata_tmpls[2] = {
- L"%PROGRAMDATA%\\Git",
- NULL,
- };
-
- return win32_find_existing_dirs(out, programdata_tmpls);
-}
diff --git a/src/util/win32/findfile.h b/src/util/win32/findfile.h
deleted file mode 100644
index 7b191d1feff..00000000000
--- a/src/util/win32/findfile.h
+++ /dev/null
@@ -1,22 +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_win32_findfile_h__
-#define INCLUDE_win32_findfile_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);
-
-extern int git_win32__find_system_dirs(git_str *out, const char *subpath);
-extern int git_win32__find_global_dirs(git_str *out);
-extern int git_win32__find_xdg_dirs(git_str *out);
-extern int git_win32__find_programdata_dirs(git_str *out);
-
-#endif
-
diff --git a/src/util/win32/path_w32.c b/src/util/win32/path_w32.c
index d9fc8292b0c..7a559e45c58 100644
--- a/src/util/win32/path_w32.c
+++ b/src/util/win32/path_w32.c
@@ -336,13 +336,13 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
/* See if this is an absolute path (beginning with a drive letter) */
if (git_fs_path_is_absolute(src)) {
- if (git__utf8_to_16(dest, GIT_WIN_PATH_MAX, src) < 0)
+ if (git_utf8_to_16(dest, GIT_WIN_PATH_MAX, src) < 0)
goto on_error;
}
/* File-prefixed NT-style paths beginning with \\?\ */
else if (path__is_nt_namespace(src)) {
/* Skip the NT prefix, the destination already contains it */
- if (git__utf8_to_16(dest, GIT_WIN_PATH_MAX, src + PATH__NT_NAMESPACE_LEN) < 0)
+ if (git_utf8_to_16(dest, GIT_WIN_PATH_MAX, src + PATH__NT_NAMESPACE_LEN) < 0)
goto on_error;
}
/* UNC paths */
@@ -351,7 +351,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
dest += 4;
/* Skip the leading "\\" */
- if (git__utf8_to_16(dest, GIT_WIN_PATH_MAX - 2, src + 2) < 0)
+ if (git_utf8_to_16(dest, GIT_WIN_PATH_MAX - 2, src + 2) < 0)
goto on_error;
}
/* Absolute paths omitting the drive letter */
@@ -365,7 +365,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
}
/* Skip the drive letter specification ("C:") */
- if (git__utf8_to_16(dest + 2, GIT_WIN_PATH_MAX - 2, src) < 0)
+ if (git_utf8_to_16(dest + 2, GIT_WIN_PATH_MAX - 2, src) < 0)
goto on_error;
}
/* Relative paths */
@@ -377,7 +377,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
dest[cwd_len++] = L'\\';
- if (git__utf8_to_16(dest + cwd_len, GIT_WIN_PATH_MAX - cwd_len, src) < 0)
+ if (git_utf8_to_16(dest + cwd_len, GIT_WIN_PATH_MAX - cwd_len, src) < 0)
goto on_error;
}
@@ -404,7 +404,7 @@ int git_win32_path_relative_from_utf8(git_win32_path out, const char *src)
return git_win32_path_from_utf8(out, src);
}
- if ((len = git__utf8_to_16(dest, GIT_WIN_PATH_MAX, src)) < 0)
+ if ((len = git_utf8_to_16(dest, GIT_WIN_PATH_MAX, src)) < 0)
return -1;
for (p = dest; p < (dest + len); p++) {
@@ -433,7 +433,7 @@ int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
}
}
- if ((len = git__utf16_to_8(out, GIT_WIN_PATH_UTF8, src)) < 0)
+ if ((len = git_utf8_from_16(out, GIT_WIN_PATH_UTF8, src)) < 0)
return len;
git_fs_path_mkposix(dest);
@@ -471,7 +471,7 @@ char *git_win32_path_8dot3_name(const char *path)
if (namelen > 12 || (shortname = git__malloc(namelen + 1)) == NULL)
return NULL;
- if ((len = git__utf16_to_8(shortname, namelen + 1, start)) < 0)
+ if ((len = git_utf8_from_16(shortname, namelen + 1, start)) < 0)
return NULL;
return shortname;
diff --git a/src/util/win32/posix_w32.c b/src/util/win32/posix_w32.c
index 5862e5c9ad6..ace23200f59 100644
--- a/src/util/win32/posix_w32.c
+++ b/src/util/win32/posix_w32.c
@@ -649,7 +649,7 @@ int p_getcwd(char *buffer_out, size_t size)
git_win32_path_remove_namespace(cwd, wcslen(cwd));
/* Convert the working directory back to UTF-8 */
- if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
+ if (git_utf8_from_16(buffer_out, size, cwd) < 0) {
DWORD code = GetLastError();
if (code == ERROR_INSUFFICIENT_BUFFER)
@@ -787,13 +787,19 @@ int p_rmdir(const char *path)
char *p_realpath(const char *orig_path, char *buffer)
{
git_win32_path orig_path_w, buffer_w;
+ DWORD long_len;
if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
return NULL;
- /* Note that if the path provided is a relative path, then the current directory
+ /*
+ * POSIX realpath performs two functions: first, it turns relative
+ * paths into absolute paths. For this, we need GetFullPathName.
+ *
+ * Note that if the path provided is a relative path, then the current directory
* is used to resolve the path -- which is a concurrency issue because the current
- * directory is a process-wide variable. */
+ * directory is a process-wide variable.
+ */
if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
errno = ENAMETOOLONG;
@@ -803,9 +809,26 @@ char *p_realpath(const char *orig_path, char *buffer)
return NULL;
}
- /* The path must exist. */
- if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
- errno = ENOENT;
+ /*
+ * Then, the path is canonicalized. eg, on macOS,
+ * "/TMP" -> "/private/tmp". For this, we need GetLongPathName.
+ */
+ if ((long_len = GetLongPathNameW(buffer_w, buffer_w, GIT_WIN_PATH_UTF16)) == 0) {
+ DWORD error = GetLastError();
+
+ if (error == ERROR_FILE_NOT_FOUND ||
+ error == ERROR_PATH_NOT_FOUND)
+ errno = ENOENT;
+ else if (error == ERROR_ACCESS_DENIED)
+ errno = EPERM;
+ else
+ errno = EINVAL;
+
+ return NULL;
+ }
+
+ if (long_len > GIT_WIN_PATH_UTF16) {
+ errno = ENAMETOOLONG;
return NULL;
}
@@ -821,7 +844,6 @@ char *p_realpath(const char *orig_path, char *buffer)
return NULL;
git_fs_path_mkposix(buffer);
-
return buffer;
}
diff --git a/src/util/win32/process.c b/src/util/win32/process.c
new file mode 100644
index 00000000000..bb522459711
--- /dev/null
+++ b/src/util/win32/process.c
@@ -0,0 +1,506 @@
+/*
+ * 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 "process.h"
+#include "strlist.h"
+
+#ifndef DWORD_MAX
+# define DWORD_MAX INT32_MAX
+#endif
+
+#define ENV_MAX 32767
+
+struct git_process {
+ wchar_t *appname;
+ wchar_t *cmdline;
+ wchar_t *env;
+
+ wchar_t *cwd;
+
+ unsigned int capture_in : 1,
+ capture_out : 1,
+ capture_err : 1;
+
+ PROCESS_INFORMATION process_info;
+
+ HANDLE child_in;
+ HANDLE child_out;
+ HANDLE child_err;
+
+ git_process_result_status status;
+};
+
+/*
+ * Windows processes have a single command-line that is split by the
+ * invoked application into arguments (instead of an array of
+ * command-line arguments). This command-line is split by space or
+ * tab delimiters, unless that whitespace is within a double quote.
+ * Literal double-quotes themselves can be escaped by a backslash,
+ * but only when not within double quotes. Literal backslashes can
+ * be escaped by a backslash.
+ *
+ * Effectively, this means that instead of thinking about quoting
+ * individual strings, think about double quotes as an escaping
+ * mechanism for whitespace.
+ *
+ * In other words (using ` as a string boundary):
+ * [ `foo`, `bar` ] => `foo bar`
+ * [ `foo bar` ] => `foo" "bar`
+ * [ `foo bar`, `foo bar` ] => `foo" "bar foo" "bar`
+ * [ `foo "bar" foo` ] => `foo" "\"bar\"" "foo`
+ */
+int git_process__cmdline(
+ git_str *out,
+ const char **in,
+ size_t in_len)
+{
+ bool quoted = false;
+ const char *c;
+ size_t i;
+
+ for (i = 0; i < in_len; i++) {
+ /* Arguments are delimited by an unquoted space */
+ if (i)
+ git_str_putc(out, ' ');
+
+ for (c = in[i]; *c; c++) {
+ /* Start or stop quoting spaces within an argument */
+ if ((*c == ' ' || *c == '\t') && !quoted) {
+ git_str_putc(out, '"');
+ quoted = true;
+ } else if (*c != ' ' && *c != '\t' && quoted) {
+ git_str_putc(out, '"');
+ quoted = false;
+ }
+
+ /* Escape double-quotes and backslashes */
+ if (*c == '"' || *c == '\\')
+ git_str_putc(out, '\\');
+
+ git_str_putc(out, *c);
+ }
+ }
+
+ return git_str_oom(out) ? -1 : 0;
+}
+
+GIT_INLINE(bool) is_delete_env(const char *env)
+{
+ char *c = strchr(env, '=');
+
+ if (c == NULL)
+ return false;
+
+ return *(c+1) == '\0';
+}
+
+static int merge_env(wchar_t **out, const char **in, size_t in_len, bool exclude_env)
+{
+ git_str merged = GIT_STR_INIT;
+ wchar_t *in16 = NULL, *env = NULL, *e;
+ char *e8 = NULL;
+ size_t e_len;
+ int ret = 0;
+ size_t i;
+
+ *out = NULL;
+
+ in16 = git__malloc(ENV_MAX * sizeof(wchar_t));
+ GIT_ERROR_CHECK_ALLOC(in16);
+
+ e8 = git__malloc(ENV_MAX);
+ GIT_ERROR_CHECK_ALLOC(e8);
+
+ for (i = 0; in && i < in_len; i++) {
+ if (is_delete_env(in[i]))
+ continue;
+
+ if ((ret = git_utf8_to_16(in16, ENV_MAX, in[i])) < 0)
+ goto done;
+
+ git_str_put(&merged, (const char *)in16, ret * 2);
+ git_str_put(&merged, "\0\0", 2);
+ }
+
+ if (!exclude_env) {
+ env = GetEnvironmentStringsW();
+
+ for (e = env; *e; e += (e_len + 1)) {
+ e_len = wcslen(e);
+
+ if ((ret = git_utf8_from_16(e8, ENV_MAX, e)) < 0)
+ goto done;
+
+ if (git_strlist_contains_key(in, in_len, e8, '='))
+ continue;
+
+ git_str_put(&merged, (const char *)e, e_len * 2);
+ git_str_put(&merged, "\0\0", 2);
+ }
+ }
+
+ git_str_put(&merged, "\0\0", 2);
+
+ *out = (wchar_t *)git_str_detach(&merged);
+
+done:
+ if (env)
+ FreeEnvironmentStringsW(env);
+
+ git_str_dispose(&merged);
+ git__free(e8);
+ git__free(in16);
+
+ return ret < 0 ? -1 : 0;
+}
+
+static int process_new(
+ git_process **out,
+ const char *appname,
+ const char *cmdline,
+ const char **env,
+ size_t env_len,
+ git_process_options *opts)
+{
+ git_process *process;
+ int error = 0;
+
+ *out = NULL;
+
+ process = git__calloc(1, sizeof(git_process));
+ GIT_ERROR_CHECK_ALLOC(process);
+
+ if (appname &&
+ git_utf8_to_16_alloc(&process->appname, appname) < 0) {
+ error = -1;
+ goto done;
+ }
+
+ if (git_utf8_to_16_alloc(&process->cmdline, cmdline) < 0) {
+ error = -1;
+ goto done;
+ }
+
+ if (opts && opts->cwd &&
+ git_utf8_to_16_alloc(&process->cwd, opts->cwd) < 0) {
+ error = -1;
+ goto done;
+ }
+
+ if (env && (error = merge_env(&process->env, env, env_len, opts && opts->exclude_env) < 0))
+ goto done;
+
+ if (opts) {
+ process->capture_in = opts->capture_in;
+ process->capture_out = opts->capture_out;
+ process->capture_err = opts->capture_err;
+ }
+
+done:
+ if (error)
+ git_process_free(process);
+ else
+ *out = process;
+
+ return error;
+}
+
+int git_process_new_from_cmdline(
+ git_process **out,
+ const char *cmdline,
+ const char **env,
+ size_t env_len,
+ git_process_options *opts)
+{
+ GIT_ASSERT_ARG(out && cmdline);
+
+ return process_new(out, NULL, cmdline, env, env_len, opts);
+}
+
+int git_process_new(
+ git_process **out,
+ const char **args,
+ size_t args_len,
+ const char **env,
+ size_t env_len,
+ git_process_options *opts)
+{
+ git_str cmdline = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(out && args && args_len > 0);
+
+ if ((error = git_process__cmdline(&cmdline, args, args_len)) < 0)
+ goto done;
+
+ error = process_new(out, args[0], cmdline.ptr, env, env_len, opts);
+
+done:
+ git_str_dispose(&cmdline);
+ return error;
+}
+
+#define CLOSE_HANDLE(h) do { if ((h) != NULL) CloseHandle(h); } while(0)
+
+int git_process_start(git_process *process)
+{
+ STARTUPINFOW startup_info;
+ SECURITY_ATTRIBUTES security_attrs;
+ DWORD flags = CREATE_UNICODE_ENVIRONMENT;
+ HANDLE in[2] = { NULL, NULL },
+ out[2] = { NULL, NULL },
+ err[2] = { NULL, NULL };
+
+ memset(&security_attrs, 0, sizeof(SECURITY_ATTRIBUTES));
+ security_attrs.bInheritHandle = TRUE;
+
+ memset(&startup_info, 0, sizeof(STARTUPINFOW));
+ startup_info.cb = sizeof(STARTUPINFOW);
+ startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+ if (process->capture_in) {
+ if (!CreatePipe(&in[0], &in[1], &security_attrs, 0) ||
+ !SetHandleInformation(in[1], HANDLE_FLAG_INHERIT, 0)) {
+ git_error_set(GIT_ERROR_OS, "could not create pipe");
+ goto on_error;
+ }
+
+ startup_info.hStdInput = in[0];
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+ }
+
+ if (process->capture_out) {
+ if (!CreatePipe(&out[0], &out[1], &security_attrs, 0) ||
+ !SetHandleInformation(out[0], HANDLE_FLAG_INHERIT, 0)) {
+ git_error_set(GIT_ERROR_OS, "could not create pipe");
+ goto on_error;
+ }
+
+ startup_info.hStdOutput = out[1];
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+ }
+
+ if (process->capture_err) {
+ if (!CreatePipe(&err[0], &err[1], &security_attrs, 0) ||
+ !SetHandleInformation(err[0], HANDLE_FLAG_INHERIT, 0)) {
+ git_error_set(GIT_ERROR_OS, "could not create pipe");
+ goto on_error;
+ }
+
+ startup_info.hStdError = err[1];
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+ }
+
+ memset(&process->process_info, 0, sizeof(PROCESS_INFORMATION));
+
+ if (!CreateProcessW(process->appname, process->cmdline,
+ NULL, NULL, TRUE, flags, process->env,
+ process->cwd,
+ &startup_info,
+ &process->process_info)) {
+ git_error_set(GIT_ERROR_OS, "could not create process");
+ goto on_error;
+ }
+
+ CLOSE_HANDLE(in[0]); process->child_in = in[1];
+ CLOSE_HANDLE(out[1]); process->child_out = out[0];
+ CLOSE_HANDLE(err[1]); process->child_err = err[0];
+
+ return 0;
+
+on_error:
+ CLOSE_HANDLE(in[0]); CLOSE_HANDLE(in[1]);
+ CLOSE_HANDLE(out[0]); CLOSE_HANDLE(out[1]);
+ CLOSE_HANDLE(err[0]); CLOSE_HANDLE(err[1]);
+ return -1;
+}
+
+int git_process_id(p_pid_t *out, git_process *process)
+{
+ GIT_ASSERT(out && process);
+
+ if (!process->process_info.dwProcessId) {
+ git_error_set(GIT_ERROR_INVALID, "process not running");
+ return -1;
+ }
+
+ *out = process->process_info.dwProcessId;
+ return 0;
+}
+
+ssize_t git_process_read(git_process *process, void *buf, size_t count)
+{
+ DWORD ret;
+
+ if (count > DWORD_MAX)
+ count = DWORD_MAX;
+ if (count > SSIZE_MAX)
+ count = SSIZE_MAX;
+
+ if (!ReadFile(process->child_out, buf, (DWORD)count, &ret, NULL)) {
+ if (GetLastError() == ERROR_BROKEN_PIPE)
+ return 0;
+
+ git_error_set(GIT_ERROR_OS, "could not read");
+ return -1;
+ }
+
+ return ret;
+}
+
+ssize_t git_process_write(git_process *process, const void *buf, size_t count)
+{
+ DWORD ret;
+
+ if (count > DWORD_MAX)
+ count = DWORD_MAX;
+ if (count > SSIZE_MAX)
+ count = SSIZE_MAX;
+
+ if (!WriteFile(process->child_in, buf, (DWORD)count, &ret, NULL)) {
+ git_error_set(GIT_ERROR_OS, "could not write");
+ return -1;
+ }
+
+ return ret;
+}
+
+int git_process_close_in(git_process *process)
+{
+ if (!process->capture_in) {
+ git_error_set(GIT_ERROR_INVALID, "input is not open");
+ return -1;
+ }
+
+ if (process->child_in) {
+ CloseHandle(process->child_in);
+ process->child_in = NULL;
+ }
+
+ return 0;
+}
+
+int git_process_close_out(git_process *process)
+{
+ if (!process->capture_out) {
+ git_error_set(GIT_ERROR_INVALID, "output is not open");
+ return -1;
+ }
+
+ if (process->child_out) {
+ CloseHandle(process->child_out);
+ process->child_out = NULL;
+ }
+
+ return 0;
+}
+
+int git_process_close_err(git_process *process)
+{
+ if (!process->capture_err) {
+ git_error_set(GIT_ERROR_INVALID, "error is not open");
+ return -1;
+ }
+
+ if (process->child_err) {
+ CloseHandle(process->child_err);
+ process->child_err = NULL;
+ }
+
+ return 0;
+}
+
+int git_process_close(git_process *process)
+{
+ if (process->child_in) {
+ CloseHandle(process->child_in);
+ process->child_in = NULL;
+ }
+
+ if (process->child_out) {
+ CloseHandle(process->child_out);
+ process->child_out = NULL;
+ }
+
+ if (process->child_err) {
+ CloseHandle(process->child_err);
+ process->child_err = NULL;
+ }
+
+ CloseHandle(process->process_info.hProcess);
+ process->process_info.hProcess = NULL;
+
+ CloseHandle(process->process_info.hThread);
+ process->process_info.hThread = NULL;
+
+ return 0;
+}
+
+int git_process_wait(git_process_result *result, git_process *process)
+{
+ DWORD exitcode;
+
+ if (result)
+ memset(result, 0, sizeof(git_process_result));
+
+ if (!process->process_info.dwProcessId) {
+ git_error_set(GIT_ERROR_INVALID, "process is stopped");
+ return -1;
+ }
+
+ if (WaitForSingleObject(process->process_info.hProcess, INFINITE) == WAIT_FAILED) {
+ git_error_set(GIT_ERROR_OS, "could not wait for process");
+ return -1;
+ }
+
+ if (!GetExitCodeProcess(process->process_info.hProcess, &exitcode)) {
+ git_error_set(GIT_ERROR_OS, "could not get process exit code");
+ return -1;
+ }
+
+ result->status = GIT_PROCESS_STATUS_NORMAL;
+ result->exitcode = exitcode;
+
+ memset(&process->process_info, 0, sizeof(PROCESS_INFORMATION));
+ return 0;
+}
+
+int git_process_result_msg(git_str *out, git_process_result *result)
+{
+ if (result->status == GIT_PROCESS_STATUS_NONE) {
+ return git_str_puts(out, "process not started");
+ } else if (result->status == GIT_PROCESS_STATUS_NORMAL) {
+ return git_str_printf(out, "process exited with code %d",
+ result->exitcode);
+ } else if (result->signal) {
+ return git_str_printf(out, "process exited on signal %d",
+ result->signal);
+ }
+
+ return git_str_puts(out, "unknown error");
+}
+
+void git_process_free(git_process *process)
+{
+ if (!process)
+ return;
+
+ if (process->process_info.hProcess)
+ git_process_close(process);
+
+ git__free(process->env);
+ git__free(process->cwd);
+ git__free(process->cmdline);
+ git__free(process->appname);
+ git__free(process);
+}
diff --git a/src/util/win32/utf-conv.c b/src/util/win32/utf-conv.c
index 4bde3023ab6..ad35c0c35ff 100644
--- a/src/util/win32/utf-conv.c
+++ b/src/util/win32/utf-conv.c
@@ -15,108 +15,114 @@ GIT_INLINE(void) git__set_errno(void)
errno = EINVAL;
}
-/**
- * Converts a UTF-8 string to wide characters.
- *
- * @param dest The buffer to receive the wide string.
- * @param dest_size The size of the buffer, in characters.
- * @param src The UTF-8 string to convert.
- * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
- */
-int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src)
+int git_utf8_to_16(wchar_t *dest, size_t dest_size, const char *src)
+{
+ /* Length of -1 indicates NULL termination of the input string. */
+ return git_utf8_to_16_with_len(dest, dest_size, src, -1);
+}
+
+int git_utf8_to_16_with_len(
+ wchar_t *dest,
+ size_t _dest_size,
+ const char *src,
+ int src_len)
{
+ int dest_size = (int)min(_dest_size, INT_MAX);
int len;
- /* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to
- * turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's
- * length. MultiByteToWideChar never returns int's minvalue, so underflow is not possible */
- if ((len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size) - 1) < 0)
+ /*
+ * Subtract 1 from the result to turn 0 into -1 (an error code) and
+ * to not count the NULL terminator as part of the string's length.
+ * MultiByteToWideChar never returns int's minvalue, so underflow
+ * is not possible.
+ */
+ len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
+ src, src_len, dest, dest_size) - 1;
+
+ if (len < 0)
git__set_errno();
return len;
}
-/**
- * Converts a wide string to UTF-8.
- *
- * @param dest The buffer to receive the UTF-8 string.
- * @param dest_size The size of the buffer, in bytes.
- * @param src The wide string to convert.
- * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
- */
-int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src)
+int git_utf8_from_16(char *dest, size_t dest_size, const wchar_t *src)
{
+ /* Length of -1 indicates NULL termination of the input string. */
+ return git_utf8_from_16_with_len(dest, dest_size, src, -1);
+}
+
+int git_utf8_from_16_with_len(
+ char *dest,
+ size_t _dest_size,
+ const wchar_t *src,
+ int src_len)
+{
+ int dest_size = (int)min(_dest_size, INT_MAX);
int len;
- /* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to
- * turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's
- * length. WideCharToMultiByte never returns int's minvalue, so underflow is not possible */
- if ((len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size, NULL, NULL) - 1) < 0)
+ /*
+ * Subtract 1 from the result to turn 0 into -1 (an error code) and
+ * to not count the NULL terminator as part of the string's length.
+ * WideCharToMultiByte never returns int's minvalue, so underflow
+ * is not possible.
+ */
+ len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
+ src, src_len, dest, dest_size, NULL, NULL) - 1;
+
+ if (len < 0)
git__set_errno();
return len;
}
-/**
- * Converts a UTF-8 string to wide characters.
- * Memory is allocated to hold the converted string.
- * The caller is responsible for freeing the string with git__free.
- *
- * @param dest Receives a pointer to the wide string.
- * @param src The UTF-8 string to convert.
- * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
- */
-int git__utf8_to_16_alloc(wchar_t **dest, const char *src)
+int git_utf8_to_16_alloc(wchar_t **dest, const char *src)
+{
+ /* Length of -1 indicates NULL termination of the input string. */
+ return git_utf8_to_16_alloc_with_len(dest, src, -1);
+}
+
+int git_utf8_to_16_alloc_with_len(wchar_t **dest, const char *src, int src_len)
{
int utf16_size;
*dest = NULL;
- /* Length of -1 indicates NULL termination of the input string */
- utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, NULL, 0);
+ utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
+ src, src_len, NULL, 0);
if (!utf16_size) {
git__set_errno();
return -1;
}
- if (!(*dest = git__mallocarray(utf16_size, sizeof(wchar_t)))) {
- errno = ENOMEM;
- return -1;
- }
+ *dest = git__mallocarray(utf16_size, sizeof(wchar_t));
+ GIT_ERROR_CHECK_ALLOC(*dest);
- utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, *dest, utf16_size);
-
- if (!utf16_size) {
- git__set_errno();
+ utf16_size = git_utf8_to_16_with_len(*dest, (size_t)utf16_size,
+ src, src_len);
+ if (utf16_size < 0) {
git__free(*dest);
*dest = NULL;
}
- /* Subtract 1 from the result to turn 0 into -1 (an error code) and to not count the NULL
- * terminator as part of the string's length. MultiByteToWideChar never returns int's minvalue,
- * so underflow is not possible */
- return utf16_size - 1;
+ return utf16_size;
}
-/**
- * Converts a wide string to UTF-8.
- * Memory is allocated to hold the converted string.
- * The caller is responsible for freeing the string with git__free.
- *
- * @param dest Receives a pointer to the UTF-8 string.
- * @param src The wide string to convert.
- * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
- */
-int git__utf16_to_8_alloc(char **dest, const wchar_t *src)
+int git_utf8_from_16_alloc(char **dest, const wchar_t *src)
+{
+ /* Length of -1 indicates NULL termination of the input string. */
+ return git_utf8_from_16_alloc_with_len(dest, src, -1);
+}
+
+int git_utf8_from_16_alloc_with_len(char **dest, const wchar_t *src, int src_len)
{
int utf8_size;
*dest = NULL;
- /* Length of -1 indicates NULL termination of the input string */
- utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, NULL, 0, NULL, NULL);
+ utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
+ src, src_len, NULL, 0, NULL, NULL);
if (!utf8_size) {
git__set_errno();
@@ -124,23 +130,15 @@ int git__utf16_to_8_alloc(char **dest, const wchar_t *src)
}
*dest = git__malloc(utf8_size);
+ GIT_ERROR_CHECK_ALLOC(*dest);
- if (!*dest) {
- errno = ENOMEM;
- return -1;
- }
-
- utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, *dest, utf8_size, NULL, NULL);
-
- if (!utf8_size) {
- git__set_errno();
+ utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
+ src, src_len, *dest, utf8_size, NULL, NULL);
+ if (utf8_size < 0) {
git__free(*dest);
*dest = NULL;
}
- /* Subtract 1 from the result to turn 0 into -1 (an error code) and to not count the NULL
- * terminator as part of the string's length. MultiByteToWideChar never returns int's minvalue,
- * so underflow is not possible */
- return utf8_size - 1;
+ return utf8_size;
}
diff --git a/src/util/win32/utf-conv.h b/src/util/win32/utf-conv.h
index 120d647efdf..301f5a6d36c 100644
--- a/src/util/win32/utf-conv.h
+++ b/src/util/win32/utf-conv.h
@@ -15,15 +15,46 @@
# define WC_ERR_INVALID_CHARS 0x80
#endif
+/**
+ * Converts a NUL-terminated UTF-8 string to wide characters. This is a
+ * convenience function for `git_utf8_to_16_with_len`.
+ *
+ * @param dest The buffer to receive the wide string.
+ * @param dest_size The size of the buffer, in characters.
+ * @param src The UTF-8 string to convert.
+ * @return The length of the wide string, in characters
+ * (not counting the NULL terminator), or < 0 for failure
+ */
+int git_utf8_to_16(wchar_t *dest, size_t dest_size, const char *src);
+
/**
* Converts a UTF-8 string to wide characters.
*
* @param dest The buffer to receive the wide string.
* @param dest_size The size of the buffer, in characters.
* @param src The UTF-8 string to convert.
- * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
+ * @param src_len The length of the string to convert.
+ * @return The length of the wide string, in characters
+ * (not counting the NULL terminator), or < 0 for failure
+ */
+int git_utf8_to_16_with_len(
+ wchar_t *dest,
+ size_t dest_size,
+ const char *src,
+ int src_len);
+
+/**
+ * Converts a NUL-terminated wide string to UTF-8. This is a convenience
+ * function for `git_utf8_from_16_with_len`.
+ *
+ * @param dest The buffer to receive the UTF-8 string.
+ * @param dest_size The size of the buffer, in bytes.
+ * @param src The wide string to convert.
+ * @param src_len The length of the string to convert.
+ * @return The length of the UTF-8 string, in bytes
+ * (not counting the NULL terminator), or < 0 for failure
*/
-int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src);
+int git_utf8_from_16(char *dest, size_t dest_size, const wchar_t *src);
/**
* Converts a wide string to UTF-8.
@@ -31,30 +62,66 @@ int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src);
* @param dest The buffer to receive the UTF-8 string.
* @param dest_size The size of the buffer, in bytes.
* @param src The wide string to convert.
- * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
+ * @param src_len The length of the string to convert.
+ * @return The length of the UTF-8 string, in bytes
+ * (not counting the NULL terminator), or < 0 for failure
*/
-int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src);
+int git_utf8_from_16_with_len(char *dest, size_t dest_size, const wchar_t *src, int src_len);
/**
- * Converts a UTF-8 string to wide characters.
- * Memory is allocated to hold the converted string.
- * The caller is responsible for freeing the string with git__free.
+ * Converts a UTF-8 string to wide characters. Memory is allocated to hold
+ * the converted string. The caller is responsible for freeing the string
+ * with git__free.
*
* @param dest Receives a pointer to the wide string.
* @param src The UTF-8 string to convert.
- * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
+ * @return The length of the wide string, in characters
+ * (not counting the NULL terminator), or < 0 for failure
*/
-int git__utf8_to_16_alloc(wchar_t **dest, const char *src);
+int git_utf8_to_16_alloc(wchar_t **dest, const char *src);
/**
- * Converts a wide string to UTF-8.
- * Memory is allocated to hold the converted string.
- * The caller is responsible for freeing the string with git__free.
+ * Converts a UTF-8 string to wide characters. Memory is allocated to hold
+ * the converted string. The caller is responsible for freeing the string
+ * with git__free.
+ *
+ * @param dest Receives a pointer to the wide string.
+ * @param src The UTF-8 string to convert.
+ * @param src_len The length of the string.
+ * @return The length of the wide string, in characters
+ * (not counting the NULL terminator), or < 0 for failure
+ */
+int git_utf8_to_16_alloc_with_len(
+ wchar_t **dest,
+ const char *src,
+ int src_len);
+
+/**
+ * Converts a wide string to UTF-8. Memory is allocated to hold the
+ * converted string. The caller is responsible for freeing the string
+ * with git__free.
+ *
+ * @param dest Receives a pointer to the UTF-8 string.
+ * @param src The wide string to convert.
+ * @return The length of the UTF-8 string, in bytes
+ * (not counting the NULL terminator), or < 0 for failure
+ */
+int git_utf8_from_16_alloc(char **dest, const wchar_t *src);
+
+/**
+ * Converts a wide string to UTF-8. Memory is allocated to hold the
+ * converted string. The caller is responsible for freeing the string
+ * with git__free.
*
* @param dest Receives a pointer to the UTF-8 string.
* @param src The wide string to convert.
- * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
+ * @param src_len The length of the wide string.
+ * @return The length of the UTF-8 string, in bytes
+ * (not counting the NULL terminator), or < 0 for failure
*/
-int git__utf16_to_8_alloc(char **dest, const wchar_t *src);
+int git_utf8_from_16_alloc_with_len(
+ char **dest,
+ const wchar_t *src,
+ int src_len);
#endif
diff --git a/src/util/win32/w32_util.c b/src/util/win32/w32_util.c
index fe4b75baefd..f5b006a1974 100644
--- a/src/util/win32/w32_util.c
+++ b/src/util/win32/w32_util.c
@@ -115,7 +115,7 @@ int git_win32__file_attribute_to_stat(
/* st_size gets the UTF-8 length of the target name, in bytes,
* not counting the NULL terminator */
- if ((st->st_size = git__utf16_to_8(NULL, 0, target)) < 0) {
+ if ((st->st_size = git_utf8_from_16(NULL, 0, target)) < 0) {
git_error_set(GIT_ERROR_OS, "could not convert reparse point name for '%ls'", path);
return -1;
}
diff --git a/tests/benchmarks/benchmark.sh b/tests/benchmarks/benchmark.sh
index 4a89807b789..4cb2b2fbfd3 100755
--- a/tests/benchmarks/benchmark.sh
+++ b/tests/benchmarks/benchmark.sh
@@ -6,9 +6,10 @@ set -eo pipefail
# parse the command line
#
-usage() { echo "usage: $(basename "$0") [--cli ] [--baseline-cli ] [--suite ] [--json ] [--zip ] [--verbose] [--debug]"; }
+usage() { echo "usage: $(basename "$0") [--cli ] [--name ] [--baseline-cli ] [--suite ] [--json ] [--zip ] [--verbose] [--debug]"; }
TEST_CLI="git"
+TEST_CLI_NAME=
BASELINE_CLI=
SUITE=
JSON_RESULT=
@@ -22,6 +23,9 @@ for a in "$@"; do
if [ "${NEXT}" = "cli" ]; then
TEST_CLI="${a}"
NEXT=
+ elif [ "${NEXT}" = "name" ]; then
+ TEST_CLI_NAME="${a}"
+ NEXT=
elif [ "${NEXT}" = "baseline-cli" ]; then
BASELINE_CLI="${a}"
NEXT=
@@ -41,6 +45,10 @@ for a in "$@"; do
NEXT="cli"
elif [[ "${a}" == "-c"* ]]; then
TEST_CLI="${a/-c/}"
+ elif [ "${a}" = "n" ] || [ "${a}" = "--name" ]; then
+ NEXT="name"
+ elif [[ "${a}" == "-n"* ]]; then
+ TEST_CLI_NAME="${a/-n/}"
elif [ "${a}" = "b" ] || [ "${a}" = "--baseline-cli" ]; then
NEXT="baseline-cli"
elif [[ "${a}" == "-b"* ]]; then
diff --git a/tests/clar/clar.c b/tests/clar/clar.c
index c9c3fde3827..9695dc946e4 100644
--- a/tests/clar/clar.c
+++ b/tests/clar/clar.c
@@ -41,9 +41,6 @@
# ifndef strdup
# define strdup(str) _strdup(str)
# endif
-# ifndef strcasecmp
-# define strcasecmp(a,b) _stricmp(a,b)
-# endif
# ifndef __MINGW32__
# pragma comment(lib, "shell32")
@@ -94,8 +91,10 @@
static void fs_rm(const char *_source);
static void fs_copy(const char *_source, const char *dest);
+#ifdef CLAR_FIXTURE_PATH
static const char *
fixture_path(const char *base, const char *fixture_name);
+#endif
struct clar_error {
const char *file;
diff --git a/tests/clar/clar/fixtures.h b/tests/clar/clar/fixtures.h
index 77033d36507..6ec6423484d 100644
--- a/tests/clar/clar/fixtures.h
+++ b/tests/clar/clar/fixtures.h
@@ -1,3 +1,4 @@
+#ifdef CLAR_FIXTURE_PATH
static const char *
fixture_path(const char *base, const char *fixture_name)
{
@@ -20,7 +21,6 @@ fixture_path(const char *base, const char *fixture_name)
return _path;
}
-#ifdef CLAR_FIXTURE_PATH
const char *cl_fixture(const char *fixture_name)
{
return fixture_path(CLAR_FIXTURE_PATH, fixture_name);
diff --git a/tests/clar/clar/fs.h b/tests/clar/clar/fs.h
index 44ede457258..a6eda5e5dc2 100644
--- a/tests/clar/clar/fs.h
+++ b/tests/clar/clar/fs.h
@@ -295,7 +295,9 @@ fs_copy(const char *_source, const char *_dest)
void
cl_fs_cleanup(void)
{
+#ifdef CLAR_FIXTURE_PATH
fs_rm(fixture_path(_clar_path, "*"));
+#endif
}
#else
diff --git a/tests/clar/clar/sandbox.h b/tests/clar/clar/sandbox.h
index 0ba1479620a..0688374f8d6 100644
--- a/tests/clar/clar/sandbox.h
+++ b/tests/clar/clar/sandbox.h
@@ -2,7 +2,8 @@
#include
#endif
-static char _clar_path[4096 + 1];
+#define CLAR_PATH_MAX 4096
+static char _clar_path[CLAR_PATH_MAX];
static int
is_valid_tmp_path(const char *path)
@@ -35,10 +36,9 @@ find_tmp_path(char *buffer, size_t length)
continue;
if (is_valid_tmp_path(env)) {
-#ifdef __APPLE__
- if (length >= PATH_MAX && realpath(env, buffer) != NULL)
- return 0;
-#endif
+ if (strlen(env) + 1 > CLAR_PATH_MAX)
+ return -1;
+
strncpy(buffer, env, length - 1);
buffer[length - 1] = '\0';
return 0;
@@ -47,10 +47,6 @@ find_tmp_path(char *buffer, size_t length)
/* If the environment doesn't say anything, try to use /tmp */
if (is_valid_tmp_path("/tmp")) {
-#ifdef __APPLE__
- if (length >= PATH_MAX && realpath("/tmp", buffer) != NULL)
- return 0;
-#endif
strncpy(buffer, "/tmp", length - 1);
buffer[length - 1] = '\0';
return 0;
@@ -75,6 +71,34 @@ find_tmp_path(char *buffer, size_t length)
return -1;
}
+static int canonicalize_tmp_path(char *buffer)
+{
+#ifdef _WIN32
+ char tmp[CLAR_PATH_MAX];
+ DWORD ret;
+
+ ret = GetFullPathName(buffer, CLAR_PATH_MAX, tmp, NULL);
+
+ if (ret == 0 || ret > CLAR_PATH_MAX)
+ return -1;
+
+ ret = GetLongPathName(tmp, buffer, CLAR_PATH_MAX);
+
+ if (ret == 0 || ret > CLAR_PATH_MAX)
+ return -1;
+
+ return 0;
+#else
+ char tmp[CLAR_PATH_MAX];
+
+ if (realpath(buffer, tmp) == NULL)
+ return -1;
+
+ strcpy(buffer, tmp);
+ return 0;
+#endif
+}
+
static void clar_unsandbox(void)
{
if (_clar_path[0] == '\0')
@@ -95,7 +119,8 @@ static int build_sandbox_path(void)
size_t len;
- if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0)
+ if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0 ||
+ canonicalize_tmp_path(_clar_path) < 0)
return -1;
len = strlen(_clar_path);
diff --git a/tests/clar/clar_libgit2.c b/tests/clar/clar_libgit2.c
index 783b457f958..a1b92fc336e 100644
--- a/tests/clar/clar_libgit2.c
+++ b/tests/clar/clar_libgit2.c
@@ -1,6 +1,7 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "fs_path.h"
+#include "futils.h"
#include "git2/sys/repository.h"
void cl_git_report_failure(
@@ -102,10 +103,10 @@ int cl_setenv(const char *name, const char *value)
{
wchar_t *wide_name, *wide_value = NULL;
- cl_assert(git__utf8_to_16_alloc(&wide_name, name) >= 0);
+ cl_assert(git_utf8_to_16_alloc(&wide_name, name) >= 0);
if (value) {
- cl_assert(git__utf8_to_16_alloc(&wide_value, value) >= 0);
+ cl_assert(git_utf8_to_16_alloc(&wide_value, value) >= 0);
cl_assert(SetEnvironmentVariableW(wide_name, wide_value));
} else {
/* Windows XP returns 0 (failed) when passing NULL for lpValue when
@@ -476,6 +477,25 @@ int cl_repo_get_bool(git_repository *repo, const char *cfg)
return val;
}
+void cl_repo_set_int(git_repository *repo, const char *cfg, int value)
+{
+ git_config *config;
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_int32(config, cfg, value));
+ git_config_free(config);
+}
+
+int cl_repo_get_int(git_repository *repo, const char *cfg)
+{
+ int val = 0;
+ git_config *config;
+ cl_git_pass(git_repository_config(&config, repo));
+ if (git_config_get_int32(&val, config, cfg) < 0)
+ git_error_clear();
+ git_config_free(config);
+ return val;
+}
+
void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value)
{
git_config *config;
@@ -548,33 +568,95 @@ void clar__assert_equal_file(
(size_t)expected_bytes, (size_t)total_bytes);
}
-static git_buf _cl_restore_home = GIT_BUF_INIT;
+#define FAKE_HOMEDIR_NAME "cl_fake_home"
-void cl_fake_home_cleanup(void *payload)
+static git_buf _cl_restore_homedir = GIT_BUF_INIT;
+
+void cl_fake_homedir_cleanup(void *payload)
{
GIT_UNUSED(payload);
- if (_cl_restore_home.ptr) {
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, _cl_restore_home.ptr));
- git_buf_dispose(&_cl_restore_home);
+ if (_cl_restore_homedir.ptr) {
+ cl_git_pass(git_futils_rmdir_r(FAKE_HOMEDIR_NAME, NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_HOMEDIR, _cl_restore_homedir.ptr));
+ git_buf_dispose(&_cl_restore_homedir);
}
}
-void cl_fake_home(void)
+void cl_fake_homedir(git_str *out)
{
git_str path = GIT_STR_INIT;
cl_git_pass(git_libgit2_opts(
- GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &_cl_restore_home));
+ GIT_OPT_GET_HOMEDIR, &_cl_restore_homedir));
- cl_set_cleanup(cl_fake_home_cleanup, NULL);
+ cl_set_cleanup(cl_fake_homedir_cleanup, NULL);
+
+ /* TOC/TOU but merely attempts to prevent accidental cleanup. */
+ cl_assert(!git_fs_path_exists(FAKE_HOMEDIR_NAME));
+ cl_must_pass(p_mkdir(FAKE_HOMEDIR_NAME, 0777));
+ cl_git_pass(git_fs_path_prettify(&path, FAKE_HOMEDIR_NAME, NULL));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_HOMEDIR, path.ptr));
+
+ if (out)
+ git_str_swap(out, &path);
+
+ git_str_dispose(&path);
+}
+
+#define FAKE_GLOBALCONFIG_NAME "cl_fake_global"
+
+static git_buf _cl_restore_globalconfig = GIT_BUF_INIT;
+
+void cl_fake_globalconfig_cleanup(void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (_cl_restore_globalconfig.ptr) {
+ cl_git_pass(git_futils_rmdir_r(FAKE_GLOBALCONFIG_NAME, NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_HOMEDIR, _cl_restore_globalconfig.ptr));
+ git_buf_dispose(&_cl_restore_globalconfig);
+ }
+}
+
+void cl_fake_globalconfig(git_str *out)
+{
+ git_str path = GIT_STR_INIT;
- if (!git_fs_path_exists("home"))
- cl_must_pass(p_mkdir("home", 0777));
- cl_git_pass(git_fs_path_prettify(&path, "home", NULL));
cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+ GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &_cl_restore_globalconfig));
+
+ cl_set_cleanup(cl_fake_globalconfig_cleanup, NULL);
+
+ /* TOC/TOU but merely attempts to prevent accidental cleanup. */
+ cl_assert(!git_fs_path_exists(FAKE_GLOBALCONFIG_NAME));
+ cl_must_pass(p_mkdir(FAKE_GLOBALCONFIG_NAME, 0777));
+ cl_git_pass(git_fs_path_prettify(&path, FAKE_GLOBALCONFIG_NAME, NULL));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+
+ if (out)
+ git_str_swap(out, &path);
+
+ git_str_dispose(&path);
+}
+
+void cl_sandbox_set_homedir(const char *home)
+{
+ git_str path = GIT_STR_INIT;
+
+ if (home) {
+ git_libgit2_opts(GIT_OPT_SET_HOMEDIR, home);
+ } else {
+ git_str_joinpath(&path, clar_sandbox_path(), "__home");
+
+ if (!git_fs_path_exists(path.ptr))
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ git_libgit2_opts(GIT_OPT_SET_HOMEDIR, path.ptr);
+ }
+
git_str_dispose(&path);
}
diff --git a/tests/clar/clar_libgit2.h b/tests/clar/clar_libgit2.h
index d2d9da0aaf1..d8105c841b4 100644
--- a/tests/clar/clar_libgit2.h
+++ b/tests/clar/clar_libgit2.h
@@ -166,10 +166,27 @@ GIT_INLINE(void) clar__assert_equal_oid(
}
}
+GIT_INLINE(void) clar__assert_equal_oidstr(
+ const char *file, const char *func, int line, const char *desc,
+ const char *one_str, const git_oid *two)
+{
+ git_oid one;
+
+ if (git_oid__fromstr(&one, one_str, git_oid_type(two)) < 0) {
+ clar__fail(file, func, line, desc, "could not parse oid string", 1);
+ } else {
+ clar__assert_equal_oid(file, func, line, desc, &one, two);
+ }
+}
+
#define cl_assert_equal_oid(one, two) \
clar__assert_equal_oid(__FILE__, __func__, __LINE__, \
"OID mismatch: " #one " != " #two, (one), (two))
+#define cl_assert_equal_oidstr(one_str, two) \
+ clar__assert_equal_oidstr(__FILE__, __func__, __LINE__, \
+ "OID mismatch: " #one_str " != " #two, (one_str), (two))
+
/*
* Some utility macros for building long strings
*/
@@ -231,16 +248,28 @@ void cl_repo_commit_from_index(
void cl_repo_set_bool(git_repository *repo, const char *cfg, int value);
int cl_repo_get_bool(git_repository *repo, const char *cfg);
+void cl_repo_set_int(git_repository *repo, const char *cfg, int value);
+int cl_repo_get_int(git_repository *repo, const char *cfg);
+
void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value);
-/* set up a fake "home" directory and set libgit2 GLOBAL search path.
- *
- * automatically configures cleanup function to restore the regular search
- * path, although you can call it explicitly if you wish (with NULL).
+/*
+ * set up a fake "home" directory -- automatically configures cleanup
+ * function to restore the home directory, although you can call it
+ * explicitly if you wish (with NULL).
+ */
+void cl_fake_homedir(git_str *);
+void cl_fake_homedir_cleanup(void *);
+
+/*
+ * set up a fake global configuration directory -- automatically
+ * configures cleanup function to restore the global config
+ * although you can call it explicitly if you wish (with NULL).
*/
-void cl_fake_home(void);
-void cl_fake_home_cleanup(void *);
+void cl_fake_globalconfig(git_str *);
+void cl_fake_globalconfig_cleanup(void *);
+void cl_sandbox_set_homedir(const char *);
void cl_sandbox_set_search_path_defaults(void);
void cl_sandbox_disable_ownership_validation(void);
diff --git a/tests/clar/clar_libgit2_alloc.c b/tests/clar/clar_libgit2_alloc.c
new file mode 100644
index 00000000000..54eacd543e2
--- /dev/null
+++ b/tests/clar/clar_libgit2_alloc.c
@@ -0,0 +1,108 @@
+/*
+ * 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 "clar_libgit2_alloc.h"
+
+static size_t bytes_available;
+
+/*
+ * The clar allocator uses a tagging mechanism for pointers that
+ * prepends the actual pointer's number bytes as `size_t`.
+ *
+ * First, this is required in order to be able to implement
+ * proper bookkeeping of allocated bytes in both `free` and
+ * `realloc`.
+ *
+ * Second, it may also be able to spot bugs that are
+ * otherwise hard to grasp, as the returned pointer cannot be
+ * free'd directly via free(3P). Instead, one is forced to use
+ * the tandem of `cl__malloc` and `cl__free`, as otherwise the
+ * code is going to crash hard. This is considered to be a
+ * feature, as it helps e.g. in finding cases where by accident
+ * malloc(3P) and free(3P) were used instead of git__malloc and
+ * git__free, respectively.
+ *
+ * The downside is obviously that each allocation grows by
+ * sizeof(size_t) bytes. As the allocator is for testing purposes
+ * only, this tradeoff is considered to be perfectly fine,
+ * though.
+ */
+
+static void *cl__malloc(size_t len, const char *file, int line)
+{
+ char *ptr = NULL;
+ size_t alloclen;
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ if (len > bytes_available)
+ goto out;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclen, len, sizeof(size_t)) ||
+ (ptr = malloc(alloclen)) == NULL)
+ goto out;
+ memcpy(ptr, &len, sizeof(size_t));
+
+ bytes_available -= len;
+
+out:
+ return ptr ? ptr + sizeof(size_t) : NULL;
+}
+
+static void cl__free(void *ptr)
+{
+ if (ptr) {
+ char *p = ptr;
+ size_t len;
+ memcpy(&len, p - sizeof(size_t), sizeof(size_t));
+ free(p - sizeof(size_t));
+ bytes_available += len;
+ }
+}
+
+static void *cl__realloc(void *ptr, size_t size, const char *file, int line)
+{
+ size_t copybytes = 0;
+ char *p = ptr;
+ void *new;
+
+ if (p)
+ memcpy(©bytes, p - sizeof(size_t), sizeof(size_t));
+
+ if (copybytes > size)
+ copybytes = size;
+
+ if ((new = cl__malloc(size, file, line)) == NULL)
+ goto out;
+
+ if (p) {
+ memcpy(new, p, copybytes);
+ cl__free(p);
+ }
+
+out:
+ return new;
+}
+
+void cl_alloc_limit(size_t bytes)
+{
+ git_allocator alloc;
+
+ alloc.gmalloc = cl__malloc;
+ alloc.grealloc = cl__realloc;
+ alloc.gfree = cl__free;
+
+ git_allocator_setup(&alloc);
+
+ bytes_available = bytes;
+}
+
+void cl_alloc_reset(void)
+{
+ git_allocator_setup(NULL);
+}
diff --git a/tests/clar/clar_libgit2_alloc.h b/tests/clar/clar_libgit2_alloc.h
new file mode 100644
index 00000000000..78a18b67d16
--- /dev/null
+++ b/tests/clar/clar_libgit2_alloc.h
@@ -0,0 +1,11 @@
+#ifndef __CLAR_LIBGIT2_ALLOC__
+#define __CLAR_LIBGIT2_ALLOC__
+
+#include "clar.h"
+#include "common.h"
+#include "git2/sys/alloc.h"
+
+void cl_alloc_limit(size_t bytes);
+void cl_alloc_reset(void);
+
+#endif
diff --git a/tests/clar/clar_libgit2_timer.c b/tests/clar/clar_libgit2_timer.c
index 2330f9351d1..6c75413be7e 100644
--- a/tests/clar/clar_libgit2_timer.c
+++ b/tests/clar/clar_libgit2_timer.c
@@ -8,23 +8,23 @@ void cl_perf_timer__init(cl_perf_timer *t)
void cl_perf_timer__start(cl_perf_timer *t)
{
- t->time_started = git__timer();
+ t->time_started = git_time_monotonic();
}
void cl_perf_timer__stop(cl_perf_timer *t)
{
- double time_now = git__timer();
+ uint64_t time_now = git_time_monotonic();
t->last = time_now - t->time_started;
t->sum += t->last;
}
-double cl_perf_timer__last(const cl_perf_timer *t)
+uint64_t cl_perf_timer__last(const cl_perf_timer *t)
{
return t->last;
}
-double cl_perf_timer__sum(const cl_perf_timer *t)
+uint64_t cl_perf_timer__sum(const cl_perf_timer *t)
{
return t->sum;
}
diff --git a/tests/clar/clar_libgit2_timer.h b/tests/clar/clar_libgit2_timer.h
index 7571a52e997..88706727884 100644
--- a/tests/clar/clar_libgit2_timer.h
+++ b/tests/clar/clar_libgit2_timer.h
@@ -4,13 +4,13 @@
struct cl_perf_timer
{
/* cumulative running time across all start..stop intervals */
- double sum;
+ uint64_t sum;
/* value of last start..stop interval */
- double last;
+ uint64_t last;
/* clock value at start */
- double time_started;
+ uint64_t time_started;
};
#define CL_PERF_TIMER_INIT {0}
@@ -24,12 +24,12 @@ void cl_perf_timer__stop(cl_perf_timer *t);
/**
* return value of last start..stop interval in seconds.
*/
-double cl_perf_timer__last(const cl_perf_timer *t);
+uint64_t cl_perf_timer__last(const cl_perf_timer *t);
/**
* return cumulative running time across all start..stop
* intervals in seconds.
*/
-double cl_perf_timer__sum(const cl_perf_timer *t);
+uint64_t cl_perf_timer__sum(const cl_perf_timer *t);
#endif /* __CLAR_LIBGIT2_TIMER__ */
diff --git a/tests/clar/clar_libgit2_trace.c b/tests/clar/clar_libgit2_trace.c
index ebb0f41dda7..814a5fa9ee7 100644
--- a/tests/clar/clar_libgit2_trace.c
+++ b/tests/clar/clar_libgit2_trace.c
@@ -197,7 +197,7 @@ static void _cl_trace_cb__event_handler(
case CL_TRACE__TEST__END:
cl_perf_timer__stop(&s_timer_test);
- git_trace(GIT_TRACE_TRACE, "%s::%s: End Test (%.3f %.3f)", suite_name, test_name,
+ git_trace(GIT_TRACE_TRACE, "%s::%s: End Test (%" PRIuZ " %" PRIuZ ")", suite_name, test_name,
cl_perf_timer__last(&s_timer_run),
cl_perf_timer__last(&s_timer_test));
break;
diff --git a/tests/clar/main.c b/tests/clar/main.c
index d879073a825..e3f4fe740bd 100644
--- a/tests/clar/main.c
+++ b/tests/clar/main.c
@@ -25,6 +25,7 @@ int main(int argc, char *argv[])
}
cl_global_trace_register();
+ cl_sandbox_set_homedir(getenv("CLAR_HOMEDIR"));
cl_sandbox_set_search_path_defaults();
cl_sandbox_disable_ownership_validation();
diff --git a/tests/libgit2/CMakeLists.txt b/tests/libgit2/CMakeLists.txt
index f581d3075b6..af70f55a78b 100644
--- a/tests/libgit2/CMakeLists.txt
+++ b/tests/libgit2/CMakeLists.txt
@@ -65,11 +65,12 @@ endif()
include(AddClarTest)
add_clar_test(libgit2_tests offline -v -xonline)
-add_clar_test(libgit2_tests invasive -v -sfilter::stream::bigfile -sodb::largefiles -siterator::workdir::filesystem_gunk -srepo::init -srepo::init::at_filesystem_root)
-add_clar_test(libgit2_tests online -v -sonline -xonline::customcert -xonline::clone::ssh_auth_methods)
+add_clar_test(libgit2_tests invasive -v -sfilter::stream::bigfile -sodb::largefiles -siterator::workdir::filesystem_gunk -srepo::init -srepo::init::at_filesystem_root -sonline::clone::connect_timeout_default)
+add_clar_test(libgit2_tests online -v -sonline -xonline::customcert)
add_clar_test(libgit2_tests online_customcert -v -sonline::customcert)
add_clar_test(libgit2_tests gitdaemon -v -sonline::push)
add_clar_test(libgit2_tests gitdaemon_namespace -v -sonline::clone::namespace)
+add_clar_test(libgit2_tests gitdaemon_sha256 -v -sonline::clone::sha256)
add_clar_test(libgit2_tests ssh -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths -sonline::clone::path_whitespace_ssh -sonline::clone::ssh_auth_methods)
add_clar_test(libgit2_tests proxy -v -sonline::clone::proxy)
add_clar_test(libgit2_tests auth_clone -v -sonline::clone::cred)
diff --git a/tests/libgit2/apply/apply_helpers.h b/tests/libgit2/apply/apply_helpers.h
index 82094773e15..b1a1479de45 100644
--- a/tests/libgit2/apply/apply_helpers.h
+++ b/tests/libgit2/apply/apply_helpers.h
@@ -1,4 +1,5 @@
#include "../merge/merge_helpers.h"
+#include "../diff/diff_helpers.h"
#define TEST_REPO_PATH "merge-recursive"
diff --git a/tests/libgit2/apply/both.c b/tests/libgit2/apply/both.c
index 1331e7ea452..44c5b19371f 100644
--- a/tests/libgit2/apply/both.c
+++ b/tests/libgit2/apply/both.c
@@ -78,7 +78,7 @@ void test_apply_both__parsed_diff(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -102,7 +102,7 @@ void test_apply_both__removes_file(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_DELETE_FILE,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_DELETE_FILE,
strlen(DIFF_DELETE_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -128,7 +128,7 @@ void test_apply_both__adds_file(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_ADD_FILE, strlen(DIFF_ADD_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -161,7 +161,7 @@ void test_apply_both__application_failure_leaves_index_unmodified(void)
cl_git_pass(git_index_write(index));
git_index_free(index);
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
@@ -198,7 +198,7 @@ void test_apply_both__index_must_match_workdir(void)
cl_git_pass(git_index_write(index));
git_index_free(index);
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
git_diff_free(diff);
@@ -214,7 +214,7 @@ void test_apply_both__index_mode_must_match_workdir(void)
/* Set a file in the working directory executable. */
cl_must_pass(p_chmod("merge-recursive/asparagus.txt", 0755));
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_MODIFY_TWO_FILES,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_MODIFY_TWO_FILES,
strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -248,7 +248,7 @@ void test_apply_both__application_failure_leaves_workdir_unmodified(void)
cl_git_pass(git_index_write(index));
git_index_free(index);
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
@@ -301,7 +301,7 @@ void test_apply_both__keeps_nonconflicting_changes(void)
cl_git_rmfile("merge-recursive/oyster.txt");
cl_git_rewritefile("merge-recursive/gravy.txt", "Hello, world.\n");
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
@@ -341,7 +341,7 @@ void test_apply_both__can_apply_nonconflicting_file_changes(void)
cl_git_pass(git_index_write(index));
git_index_free(index);
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, both_expected, both_expected_cnt);
@@ -391,7 +391,7 @@ void test_apply_both__honors_crlf_attributes(void)
cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
git_commit_free(commit);
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
@@ -415,7 +415,7 @@ void test_apply_both__rename(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_FILE,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_FILE,
strlen(DIFF_RENAME_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -440,7 +440,7 @@ void test_apply_both__rename_and_modify(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_AND_MODIFY_FILE,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_AND_MODIFY_FILE,
strlen(DIFF_RENAME_AND_MODIFY_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -465,7 +465,7 @@ void test_apply_both__rename_a_to_b_to_c(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_A_TO_B_TO_C,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_A_TO_B_TO_C,
strlen(DIFF_RENAME_A_TO_B_TO_C)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -490,7 +490,7 @@ void test_apply_both__rename_a_to_b_to_c_exact(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_A_TO_B_TO_C_EXACT,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_A_TO_B_TO_C_EXACT,
strlen(DIFF_RENAME_A_TO_B_TO_C_EXACT)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -515,7 +515,7 @@ void test_apply_both__rename_circular(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_CIRCULAR,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_CIRCULAR,
strlen(DIFF_RENAME_CIRCULAR)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -539,7 +539,7 @@ void test_apply_both__rename_2_to_1(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_2_TO_1,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_2_TO_1,
strlen(DIFF_RENAME_2_TO_1)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -565,7 +565,7 @@ void test_apply_both__rename_1_to_2(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_1_TO_2,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_1_TO_2,
strlen(DIFF_RENAME_1_TO_2)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -590,7 +590,7 @@ void test_apply_both__two_deltas_one_file(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_TWO_DELTAS_ONE_FILE,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_TWO_DELTAS_ONE_FILE,
strlen(DIFF_TWO_DELTAS_ONE_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -616,7 +616,7 @@ void test_apply_both__two_deltas_one_new_file(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_TWO_DELTAS_ONE_NEW_FILE,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_TWO_DELTAS_ONE_NEW_FILE,
strlen(DIFF_TWO_DELTAS_ONE_NEW_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -641,7 +641,7 @@ void test_apply_both__rename_and_modify_deltas(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_AND_MODIFY_DELTAS,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_AND_MODIFY_DELTAS,
strlen(DIFF_RENAME_AND_MODIFY_DELTAS)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -667,7 +667,7 @@ void test_apply_both__rename_delta_after_modify_delta(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_AFTER_MODIFY,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_AFTER_MODIFY,
strlen(DIFF_RENAME_AFTER_MODIFY)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -681,7 +681,7 @@ void test_apply_both__cant_rename_after_modify_nonexistent_target_path(void)
{
git_diff *diff;
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_AFTER_MODIFY_TARGET_PATH,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_AFTER_MODIFY_TARGET_PATH,
strlen(DIFF_RENAME_AFTER_MODIFY_TARGET_PATH)));
cl_git_fail(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -692,7 +692,7 @@ void test_apply_both__cant_modify_source_path_after_rename(void)
{
git_diff *diff;
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_RENAME_AND_MODIFY_SOURCE_PATH,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_AND_MODIFY_SOURCE_PATH,
strlen(DIFF_RENAME_AND_MODIFY_SOURCE_PATH)));
cl_git_fail(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -714,7 +714,7 @@ void test_apply_both__readd_deleted_file(void)
size_t both_expected_cnt = sizeof(both_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_DELETE_AND_READD_FILE,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_DELETE_AND_READD_FILE,
strlen(DIFF_DELETE_AND_READD_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -728,7 +728,7 @@ void test_apply_both__cant_remove_file_twice(void)
{
git_diff *diff;
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_REMOVE_FILE_TWICE,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_REMOVE_FILE_TWICE,
strlen(DIFF_REMOVE_FILE_TWICE)));
cl_git_fail(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
@@ -739,7 +739,7 @@ void test_apply_both__cant_add_invalid_filename(void)
{
git_diff *diff;
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_ADD_INVALID_FILENAME,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_ADD_INVALID_FILENAME,
strlen(DIFF_ADD_INVALID_FILENAME)));
cl_git_fail(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
diff --git a/tests/libgit2/apply/callbacks.c b/tests/libgit2/apply/callbacks.c
index 2f9af310161..f076ca48622 100644
--- a/tests/libgit2/apply/callbacks.c
+++ b/tests/libgit2/apply/callbacks.c
@@ -40,7 +40,7 @@ void test_apply_callbacks__delta_aborts(void)
opts.delta_cb = delta_abort_cb;
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_fail_with(-99,
git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, &opts));
@@ -79,7 +79,7 @@ void test_apply_callbacks__delta_can_skip(void)
opts.delta_cb = delta_skip_cb;
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, &opts));
@@ -117,7 +117,7 @@ void test_apply_callbacks__hunk_can_skip(void)
opts.hunk_cb = hunk_skip_odds_cb;
opts.payload = &count;
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_MANY_CHANGES_ONE, strlen(DIFF_MANY_CHANGES_ONE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, &opts));
diff --git a/tests/libgit2/apply/check.c b/tests/libgit2/apply/check.c
index d055d455b8f..0c1f86dc531 100644
--- a/tests/libgit2/apply/check.c
+++ b/tests/libgit2/apply/check.c
@@ -60,7 +60,7 @@ void test_apply_check__parsed_diff(void)
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
opts.flags |= GIT_APPLY_CHECK;
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, &opts));
@@ -76,7 +76,7 @@ void test_apply_check__binary(void)
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
opts.flags |= GIT_APPLY_CHECK;
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES_BINARY,
strlen(DIFF_MODIFY_TWO_FILES_BINARY)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, &opts));
@@ -112,7 +112,7 @@ void test_apply_check__does_not_apply(void)
git_index_free(index);
opts.flags |= GIT_APPLY_CHECK;
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, &opts));
validate_apply_index(repo, index_expected, index_expected_cnt);
diff --git a/tests/libgit2/apply/index.c b/tests/libgit2/apply/index.c
index 2dc0d53cb34..564d55c8c1c 100644
--- a/tests/libgit2/apply/index.c
+++ b/tests/libgit2/apply/index.c
@@ -78,7 +78,7 @@ void test_apply_index__parsed_diff(void)
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
@@ -102,7 +102,7 @@ void test_apply_index__removes_file(void)
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_DELETE_FILE,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_DELETE_FILE,
strlen(DIFF_DELETE_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
@@ -128,7 +128,7 @@ void test_apply_index__adds_file(void)
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_ADD_FILE, strlen(DIFF_ADD_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
@@ -169,7 +169,7 @@ void test_apply_index__modified_workdir_with_unmodified_index_is_ok(void)
cl_git_rmfile("merge-recursive/asparagus.txt");
cl_git_rewritefile("merge-recursive/veal.txt", "Hello, world.\n");
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
@@ -201,7 +201,7 @@ void test_apply_index__application_failure_leaves_index_unmodified(void)
cl_git_pass(git_index_write(index));
git_index_free(index);
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
@@ -240,7 +240,7 @@ void test_apply_index__keeps_nonconflicting_changes(void)
cl_git_pass(git_index_write(index));
git_index_free(index);
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
@@ -285,7 +285,7 @@ void test_apply_index__can_apply_nonconflicting_file_changes(void)
cl_git_pass(git_index_write(index));
git_index_free(index);
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
@@ -311,7 +311,7 @@ void test_apply_index__change_mode(void)
size_t index_expected_cnt = sizeof(index_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
diff --git a/tests/libgit2/apply/tree.c b/tests/libgit2/apply/tree.c
index 667bb9d401a..b97fe8d352b 100644
--- a/tests/libgit2/apply/tree.c
+++ b/tests/libgit2/apply/tree.c
@@ -81,7 +81,7 @@ void test_apply_tree__adds_file(void)
cl_git_pass(git_commit_tree(&a_tree, a_commit));
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_ADD_FILE, strlen(DIFF_ADD_FILE)));
cl_git_pass(git_apply_to_tree(&index, repo, a_tree, diff, NULL));
diff --git a/tests/libgit2/apply/workdir.c b/tests/libgit2/apply/workdir.c
index e1011d114cc..5ae56847a80 100644
--- a/tests/libgit2/apply/workdir.c
+++ b/tests/libgit2/apply/workdir.c
@@ -77,7 +77,7 @@ void test_apply_workdir__parsed_diff(void)
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
@@ -101,7 +101,7 @@ void test_apply_workdir__removes_file(void)
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, DIFF_DELETE_FILE,
+ cl_git_pass(diff_from_buffer(&diff, DIFF_DELETE_FILE,
strlen(DIFF_DELETE_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
@@ -127,7 +127,7 @@ void test_apply_workdir__adds_file(void)
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_ADD_FILE, strlen(DIFF_ADD_FILE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
@@ -177,7 +177,7 @@ void test_apply_workdir__modified_index_with_unmodified_workdir_is_ok(void)
cl_git_pass(git_index_remove(index, "asparagus.txt", 0));
cl_git_pass(git_index_write(index));
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_apply_index(repo, index_expected, index_expected_cnt);
@@ -208,7 +208,7 @@ void test_apply_workdir__application_failure_leaves_workdir_unmodified(void)
cl_git_rewritefile("merge-recursive/veal.txt",
"This is a modification.\n");
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
@@ -233,7 +233,7 @@ void test_apply_workdir__keeps_nonconflicting_changes(void)
cl_git_rmfile("merge-recursive/oyster.txt");
cl_git_rewritefile("merge-recursive/gravy.txt", "Hello, world.\n");
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
@@ -268,7 +268,7 @@ void test_apply_workdir__can_apply_nonconflicting_file_changes(void)
cl_git_append2file("merge-recursive/asparagus.txt",
"This line is added in the workdir.\n");
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_index_unchanged(repo);
@@ -295,7 +295,7 @@ void test_apply_workdir__change_mode(void)
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
validate_index_unchanged(repo);
@@ -321,7 +321,7 @@ void test_apply_workdir__apply_many_changes_one(void)
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_MANY_CHANGES_ONE, strlen(DIFF_MANY_CHANGES_ONE)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, &opts));
@@ -347,7 +347,7 @@ void test_apply_workdir__apply_many_changes_two(void)
size_t workdir_expected_cnt = sizeof(workdir_expected) /
sizeof(struct merge_index_entry);
- cl_git_pass(git_diff_from_buffer(&diff,
+ cl_git_pass(diff_from_buffer(&diff,
DIFF_MANY_CHANGES_TWO, strlen(DIFF_MANY_CHANGES_TWO)));
cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, &opts));
diff --git a/tests/libgit2/attr/repo.c b/tests/libgit2/attr/repo.c
index abd2381541d..747715b51fa 100644
--- a/tests/libgit2/attr/repo.c
+++ b/tests/libgit2/attr/repo.c
@@ -309,6 +309,31 @@ void test_attr_repo__bare_repo_with_index(void)
cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3]));
}
+void test_attr_repo__inmemory_repo_without_index(void)
+{
+ const char *names[1] = { "fake" };
+ const char *values[1];
+ git_repository *inmemory;
+ git_index *index = NULL;
+
+ /* setup bare in-memory repo without index */
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_repository_new(&inmemory, GIT_OID_SHA1));
+#else
+ cl_git_pass(git_repository_new(&inmemory));
+#endif
+ cl_assert(git_repository_is_bare(inmemory));
+
+ /* verify repo isn't given an index upfront in future */
+ git_repository_index(&index, inmemory);
+ cl_assert(!index);
+
+ /* check attributes can be queried without error due to missing index */
+ cl_git_pass(git_attr_get_many(values, inmemory, 0, "fake.txt", 1, names));
+
+ git_repository_free(inmemory);
+}
+
void test_attr_repo__sysdir(void)
{
git_str sysdir = GIT_STR_INIT;
diff --git a/tests/libgit2/blame/buffer.c b/tests/libgit2/blame/buffer.c
index 06d5042dd05..456402c4e8b 100644
--- a/tests/libgit2/blame/buffer.c
+++ b/tests/libgit2/blame/buffer.c
@@ -17,15 +17,204 @@ void test_blame_buffer__cleanup(void)
git_repository_free(g_repo);
}
+
+void test_blame_buffer__4_edits(void)
+{
+
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+x\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 2, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 1, 3, 1, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 4, 1, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 5, 1, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 6, 1, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 5, 7, 1, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 6, 8, 1, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 7, 9, 1, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 8, 10, 1, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 9, 11, 5, 0, "aa06ecca", "b.txt");
+}
+
+void test_blame_buffer__two_added_lines_and_one_modified(void)
+{
+
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+x\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+x\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 3, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 1, 4, 1, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 5, 1, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 6, 1, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 7, 1, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 5, 8, 1, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 6, 9, 3, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 7, 12, 5, 0, "aa06ecca", "b.txt");
+}
+
+void test_blame_buffer__two_added_lines(void)
+{
+
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+abc\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+def\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 3, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 1, 4, 1, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 5, 1, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 6, 1, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 7, 5, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 5, 12, 5, 0, "aa06ecca", "b.txt");
+}
+
+void test_blame_buffer__added_blocks(void)
+{
+
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+abcdefg\n\
+hijlmno\n\
+pqrstuv\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+abcdefg\n\
+hijlmno\n\
+pqrstuv\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+\n\
+abcdefg\n\
+hijlmno\n\
+pqrstuv\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 4, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 3, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 9, 4, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 13, 3, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 5, 16, 5, 0, "aa06ecca", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 6, 21, 3, 0, "000000", "b.txt");
+
+
+}
+
+void test_blame_buffer__overlapping_blocks(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+abcdefg\n\
+hijlmno\n\
+pqrstuv\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+\n\
+";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+
+ check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 3, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 1, 4, 3, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 7, 4, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 11, 5, 0, "aa06ecca", "b.txt");
+
+}
+
+void test_blame_buffer__2_add_splits_hunk(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+abc\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+abc\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 2, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 1, 3, 1, 0, "00000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 4, 2, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 6, 1, 1, "b99f7ac0", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 7, 2, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 5, 9, 1, 0, "00000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 6, 10, 3, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 7, 13, 5, 0, "aa06ecca", "b.txt");
+}
+
void test_blame_buffer__index(void)
{
const git_blame_hunk *hunk;
const char *buffer = "Hello\nWorld!";
- /*
- * We need to open a different file from the ones used in other tests. Close
- * the one opened in test_blame_buffer__initialize() to avoid a leak.
- */
git_blame_free(g_fileblame);
g_fileblame = NULL;
cl_git_pass(git_blame_file(&g_fileblame, g_repo, "file.txt", NULL));
@@ -43,6 +232,8 @@ void test_blame_buffer__index(void)
cl_assert(hunk->final_signature == NULL);
}
+
+
void test_blame_buffer__added_line(void)
{
const git_blame_hunk *hunk;
@@ -73,6 +264,43 @@ CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
cl_assert_equal_s("Ben Straub", hunk->final_signature->name);
}
+void test_blame_buffer__added_lines(void)
+{
+
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+\n\
+\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+\n\
+\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+\n\
+\n\
+\n\
+\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ cl_assert_equal_i(7, git_blame_get_hunk_count(g_bufferblame));
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 3, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 14, 3, 0, "000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 6, 22, 3, 0, "000000", "b.txt");
+
+}
+
void test_blame_buffer__deleted_line(void)
{
const char *buffer = "\
diff --git a/tests/libgit2/checkout/conflict.c b/tests/libgit2/checkout/conflict.c
index b2eb939dcd7..3539c8b2dee 100644
--- a/tests/libgit2/checkout/conflict.c
+++ b/tests/libgit2/checkout/conflict.c
@@ -1095,7 +1095,7 @@ static void collect_progress(
if (path == NULL)
return;
- git_vector_insert(paths, strdup(path));
+ git_vector_insert(paths, git__strdup(path));
}
void test_checkout_conflict__report_progress(void)
diff --git a/tests/libgit2/checkout/icase.c b/tests/libgit2/checkout/icase.c
index d77c7abd514..3769a9f9b7e 100644
--- a/tests/libgit2/checkout/icase.c
+++ b/tests/libgit2/checkout/icase.c
@@ -89,7 +89,7 @@ static void assert_name_is(const char *expected)
if (start)
cl_assert_equal_strn("/", actual + (start - 1), 1);
- free(actual);
+ git__free(actual);
}
static int symlink_or_fake(git_repository *repo, const char *a, const char *b)
diff --git a/tests/libgit2/checkout/index.c b/tests/libgit2/checkout/index.c
index 6432cba841d..3dfdaa630ae 100644
--- a/tests/libgit2/checkout/index.c
+++ b/tests/libgit2/checkout/index.c
@@ -4,6 +4,7 @@
#include "git2/checkout.h"
#include "futils.h"
#include "repository.h"
+#include "index.h"
#include "remote.h"
#include "repo/repo_helpers.h"
@@ -834,7 +835,7 @@ void test_checkout_index__adding_conflict_removes_stage_0(void)
git_index *new_index, *index;
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
- cl_git_pass(git_index_new(&new_index));
+ cl_git_pass(git_index__new(&new_index, GIT_OID_SHA1));
add_conflict(new_index, "new.txt");
cl_git_pass(git_checkout_index(g_repo, new_index, &opts));
diff --git a/tests/libgit2/clone/local.c b/tests/libgit2/clone/local.c
index e0bd74df78a..d35fe86b27e 100644
--- a/tests/libgit2/clone/local.c
+++ b/tests/libgit2/clone/local.c
@@ -210,3 +210,13 @@ void test_clone_local__git_style_unc_paths(void)
cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
#endif
}
+
+void test_clone_local__shallow_fails(void)
+{
+ git_repository *repo;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+
+ opts.fetch_opts.depth = 4;
+
+ cl_git_fail_with(GIT_ENOTSUPPORTED, git_clone(&repo, cl_fixture("testrepo.git"), "./clone.git", &opts));
+}
diff --git a/tests/libgit2/clone/nonetwork.c b/tests/libgit2/clone/nonetwork.c
index eab63363516..5316003f82a 100644
--- a/tests/libgit2/clone/nonetwork.c
+++ b/tests/libgit2/clone/nonetwork.c
@@ -5,6 +5,7 @@
#include "remote.h"
#include "futils.h"
#include "repository.h"
+#include "index.h"
#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
@@ -271,7 +272,7 @@ void test_clone_nonetwork__clone_tag_to_tree(void)
stage = cl_git_sandbox_init("testrepo.git");
cl_git_pass(git_repository_odb(&odb, stage));
- cl_git_pass(git_index_new(&index));
+ cl_git_pass(git_index__new(&index, GIT_OID_SHA1));
memset(&entry, 0, sizeof(git_index_entry));
entry.path = file_path;
diff --git a/tests/libgit2/commit/create.c b/tests/libgit2/commit/create.c
new file mode 100644
index 00000000000..9f627dc87b5
--- /dev/null
+++ b/tests/libgit2/commit/create.c
@@ -0,0 +1,112 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+
+/* Fixture setup */
+static git_repository *g_repo;
+static git_signature *g_author, *g_committer;
+
+void test_commit_create__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo2");
+ cl_git_pass(git_signature_new(&g_author, "Edward Thomson", "ethomson@edwardthomson.com", 123456789, 60));
+ cl_git_pass(git_signature_new(&g_committer, "libgit2 user", "nobody@noreply.libgit2.org", 987654321, 90));
+}
+
+void test_commit_create__cleanup(void)
+{
+ git_signature_free(g_committer);
+ git_signature_free(g_author);
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_commit_create__from_stage_simple(void)
+{
+ git_commit_create_options opts = GIT_COMMIT_CREATE_OPTIONS_INIT;
+ git_index *index;
+ git_oid commit_id;
+ git_tree *tree;
+
+ opts.author = g_author;
+ opts.committer = g_committer;
+
+ cl_git_rewritefile("testrepo2/newfile.txt", "This is a new file.\n");
+ cl_git_rewritefile("testrepo2/newfile2.txt", "This is a new file.\n");
+ cl_git_rewritefile("testrepo2/README", "hello, world.\n");
+ cl_git_rewritefile("testrepo2/new.txt", "hi there.\n");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "newfile2.txt"));
+ cl_git_pass(git_index_add_bypath(index, "README"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_commit_create_from_stage(&commit_id, g_repo, "This is the message.", &opts));
+
+ cl_git_pass(git_repository_head_tree(&tree, g_repo));
+
+ cl_assert_equal_oidstr("241b5b04e847bc38dd7b4b9f49f21e55da40f3a6", &commit_id);
+ cl_assert_equal_oidstr("b27210772d0633870b4f486d04ed3eb5ebbef5e7", git_tree_id(tree));
+
+ git_index_free(index);
+ git_tree_free(tree);
+}
+
+void test_commit_create__from_stage_nochanges(void)
+{
+ git_commit_create_options opts = GIT_COMMIT_CREATE_OPTIONS_INIT;
+ git_oid commit_id;
+ git_tree *tree;
+
+ opts.author = g_author;
+ opts.committer = g_committer;
+
+ cl_git_fail_with(GIT_EUNCHANGED, git_commit_create_from_stage(&commit_id, g_repo, "Message goes here.", &opts));
+
+ opts.allow_empty_commit = 1;
+
+ cl_git_pass(git_commit_create_from_stage(&commit_id, g_repo, "Message goes here.", &opts));
+
+ cl_git_pass(git_repository_head_tree(&tree, g_repo));
+
+ cl_assert_equal_oidstr("f776dc4c7fd8164b7127dc8e4f9b44421cb01b56", &commit_id);
+ cl_assert_equal_oidstr("c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b", git_tree_id(tree));
+
+ git_tree_free(tree);
+}
+
+void test_commit_create__from_stage_newrepo(void)
+{
+ git_commit_create_options opts = GIT_COMMIT_CREATE_OPTIONS_INIT;
+ git_repository *newrepo;
+ git_index *index;
+ git_commit *commit;
+ git_tree *tree;
+ git_oid commit_id;
+
+ opts.author = g_author;
+ opts.committer = g_committer;
+
+ git_repository_init(&newrepo, "newrepo", false);
+ cl_git_pass(git_repository_index(&index, newrepo));
+
+ cl_git_rewritefile("newrepo/hello.txt", "hello, world.\n");
+ cl_git_rewritefile("newrepo/hi.txt", "hi there.\n");
+ cl_git_rewritefile("newrepo/foo.txt", "bar.\n");
+
+ cl_git_pass(git_index_add_bypath(index, "hello.txt"));
+ cl_git_pass(git_index_add_bypath(index, "foo.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_commit_create_from_stage(&commit_id, newrepo, "Initial commit.", &opts));
+ cl_git_pass(git_repository_head_commit(&commit, newrepo));
+ cl_git_pass(git_repository_head_tree(&tree, newrepo));
+
+ cl_assert_equal_oid(&commit_id, git_commit_id(commit));
+ cl_assert_equal_oidstr("b2fa96a4f191c76eb172437281c66aa29609dcaa", git_commit_tree_id(commit));
+
+ git_tree_free(tree);
+ git_commit_free(commit);
+ git_index_free(index);
+ git_repository_free(newrepo);
+ cl_fixture_cleanup("newrepo");
+}
diff --git a/tests/libgit2/commit/parse.c b/tests/libgit2/commit/parse.c
index b313fc3088b..3a1fc3d26bd 100644
--- a/tests/libgit2/commit/parse.c
+++ b/tests/libgit2/commit/parse.c
@@ -287,7 +287,7 @@ static int parse_commit(git_commit **out, const char *buffer)
fake_odb_object.buffer = (char *)buffer;
fake_odb_object.cached.size = strlen(fake_odb_object.buffer);
- error = git_commit__parse(commit, &fake_odb_object);
+ error = git_commit__parse(commit, &fake_odb_object, GIT_OID_SHA1);
*out = commit;
return error;
diff --git a/tests/libgit2/commit/signature.c b/tests/libgit2/commit/signature.c
index a91551415d6..fddd5076eb7 100644
--- a/tests/libgit2/commit/signature.c
+++ b/tests/libgit2/commit/signature.c
@@ -36,10 +36,17 @@ void test_commit_signature__leading_and_trailing_spaces_are_trimmed(void)
assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " \t nulltoken \n", " \n emeric.fermas@gmail.com \n");
}
+void test_commit_signature__leading_and_trailing_dots_are_supported(void)
+{
+ assert_name_and_email(".nulltoken", ".emeric.fermas@gmail.com", ".nulltoken", ".emeric.fermas@gmail.com");
+ assert_name_and_email("nulltoken.", "emeric.fermas@gmail.com.", "nulltoken.", "emeric.fermas@gmail.com.");
+ assert_name_and_email(".nulltoken.", ".emeric.fermas@gmail.com.", ".nulltoken.", ".emeric.fermas@gmail.com.");
+}
+
void test_commit_signature__leading_and_trailing_crud_is_trimmed(void)
{
assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", "\"nulltoken\"", "\"emeric.fermas@gmail.com\"");
- assert_name_and_email("nulltoken w", "emeric.fermas@gmail.com", "nulltoken w.", "emeric.fermas@gmail.com");
+ assert_name_and_email("nulltoken w", "emeric.fermas@gmail.com", "nulltoken w;", "emeric.fermas@gmail.com");
assert_name_and_email("nulltoken \xe2\x98\xba", "emeric.fermas@gmail.com", "nulltoken \xe2\x98\xba", "emeric.fermas@gmail.com");
}
diff --git a/tests/libgit2/config/configlevel.c b/tests/libgit2/config/configlevel.c
index 8422d32c944..0e31268b0c6 100644
--- a/tests/libgit2/config/configlevel.c
+++ b/tests/libgit2/config/configlevel.c
@@ -71,3 +71,44 @@ void test_config_configlevel__fetching_a_level_from_an_empty_compound_config_ret
git_config_free(cfg);
}
+
+void test_config_configlevel__can_fetch_highest_level(void)
+{
+ git_config *cfg;
+ git_config *single_level_cfg;
+ git_buf buf = {0};
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
+
+ cl_git_pass(git_config_open_level(&single_level_cfg, cfg, GIT_CONFIG_HIGHEST_LEVEL));
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_get_string_buf(&buf, single_level_cfg, "core.stringglobal"));
+ cl_assert_equal_s("don't find me!", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_config_free(single_level_cfg);
+}
+
+void test_config_configlevel__can_override_local_with_worktree(void)
+{
+ git_config *cfg;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
+ GIT_CONFIG_LEVEL_WORKTREE, NULL, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.stringglobal"));
+ cl_assert_equal_s("don't find me!", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+}
diff --git a/tests/libgit2/config/find.c b/tests/libgit2/config/find.c
new file mode 100644
index 00000000000..7ca8ec767e1
--- /dev/null
+++ b/tests/libgit2/config/find.c
@@ -0,0 +1,11 @@
+#include "clar_libgit2.h"
+
+void test_config_find__one(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_find_global(&buf));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_find_xdg(&buf));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_find_system(&buf));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_find_programdata(&buf));
+}
diff --git a/tests/libgit2/config/include.c b/tests/libgit2/config/include.c
index 9328f3cf649..ba8bcad4387 100644
--- a/tests/libgit2/config/include.c
+++ b/tests/libgit2/config/include.c
@@ -42,8 +42,13 @@ void test_config_include__absolute(void)
void test_config_include__homedir(void)
{
- cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
+ git_str homefile = GIT_STR_INIT;
+
+ cl_fake_homedir(&homefile);
+ cl_git_pass(git_str_joinpath(&homefile, homefile.ptr, "config-included"));
+
cl_git_mkfile("config-include-homedir", "[include]\npath = ~/config-included");
+ cl_git_mkfile(homefile.ptr, "[foo \"bar\"]\n\tbaz = huzzah\n");
cl_git_pass(git_config_open_ondisk(&cfg, "config-include-homedir"));
@@ -53,6 +58,8 @@ void test_config_include__homedir(void)
cl_sandbox_set_search_path_defaults();
cl_git_pass(p_unlink("config-include-homedir"));
+
+ git_str_dispose(&homefile);
}
/* We need to pretend that the variables were defined where the file was included */
@@ -104,7 +111,7 @@ void test_config_include__missing(void)
git_error_clear();
cl_git_pass(git_config_open_ondisk(&cfg, "including"));
- cl_assert(git_error_last() == NULL);
+ cl_assert_equal_i(GIT_ERROR_NONE, git_error_last()->klass);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_assert_equal_s("baz", buf.ptr);
@@ -113,12 +120,13 @@ void test_config_include__missing(void)
void test_config_include__missing_homedir(void)
{
- cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
+ cl_fake_homedir(NULL);
+
cl_git_mkfile("including", "[include]\npath = ~/.nonexistentfile\n[foo]\nbar = baz");
git_error_clear();
cl_git_pass(git_config_open_ondisk(&cfg, "including"));
- cl_assert(git_error_last() == NULL);
+ cl_assert_equal_i(GIT_ERROR_NONE, git_error_last()->klass);
cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
cl_assert_equal_s("baz", buf.ptr);
diff --git a/tests/libgit2/config/memory.c b/tests/libgit2/config/memory.c
index ae661899da7..9f533e282fd 100644
--- a/tests/libgit2/config/memory.c
+++ b/tests/libgit2/config/memory.c
@@ -34,8 +34,13 @@ static int contains_all_cb(const git_config_entry *entry, void *payload)
int i;
for (i = 0; entries[i].name; i++) {
- if (strcmp(entries[i].name, entry->name) ||
- strcmp(entries[i].value , entry->value))
+ if (strcmp(entries[i].name, entry->name))
+ continue;
+
+ if ((entries[i].value == NULL) ^ (entry->value == NULL))
+ continue;
+
+ if (entry->value && strcmp(entries[i].value , entry->value))
continue;
if (entries[i].seen)
@@ -61,7 +66,23 @@ static void assert_config_contains_all(git_config_backend *backend,
static void setup_backend(const char *cfg)
{
- cl_git_pass(git_config_backend_from_string(&backend, cfg, strlen(cfg)));
+ git_config_backend_memory_options opts =
+ GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT;
+
+ opts.backend_type = "test";
+
+ cl_git_pass(git_config_backend_from_string(&backend, cfg, strlen(cfg), &opts));
+ cl_git_pass(git_config_backend_open(backend, 0, NULL));
+}
+
+static void setup_values_backend(const char **values, size_t len)
+{
+ git_config_backend_memory_options opts =
+ GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT;
+
+ opts.backend_type = "test";
+
+ cl_git_pass(git_config_backend_from_values(&backend, values, len, &opts));
cl_git_pass(git_config_backend_open(backend, 0, NULL));
}
@@ -88,7 +109,13 @@ void test_config_memory__malformed_fails_to_open(void)
const char *cfg =
"[general\n"
"foo=bar\n";
- cl_git_pass(git_config_backend_from_string(&backend, cfg, strlen(cfg)));
+
+ git_config_backend_memory_options opts =
+ GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT;
+
+ opts.backend_type = "test";
+
+ cl_git_pass(git_config_backend_from_string(&backend, cfg, strlen(cfg), &opts));
cl_git_fail(git_config_backend_open(backend, 0, NULL));
}
@@ -137,3 +164,43 @@ void test_config_memory__foreach_sees_multivar(void)
"foo=bar2\n");
assert_config_contains_all(backend, entries);
}
+
+void test_config_memory__values(void)
+{
+ const char *values[] = {
+ "general.foo=bar1",
+ "general.foo=bar2",
+ "other.key=value",
+ "empty.value=",
+ "no.value",
+ };
+
+ struct expected_entry entries[] = {
+ { "general.foo", "bar1", 0 },
+ { "general.foo", "bar2", 0 },
+ { "other.key", "value", 0 },
+ { "empty.value", "", 0 },
+ { "no.value", NULL, 0 },
+ { NULL, NULL, 0 }
+ };
+
+ setup_values_backend(values, 5);
+ assert_config_contains_all(backend, entries);
+}
+
+void test_config_memory__valid_values(void)
+{
+ const char *values[] = {
+ "general.foo=bar1",
+ "=bar2",
+ "other.key=value"
+ };
+
+ git_config_backend_memory_options opts =
+ GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT;
+
+ opts.backend_type = "test";
+
+ cl_git_pass(git_config_backend_from_values(&backend, values, 3, &opts));
+ cl_git_fail(git_config_backend_open(backend, 0, NULL));
+}
diff --git a/tests/libgit2/config/multivar.c b/tests/libgit2/config/multivar.c
index 244e3755965..3ed846012fa 100644
--- a/tests/libgit2/config/multivar.c
+++ b/tests/libgit2/config/multivar.c
@@ -1,4 +1,6 @@
#include "clar_libgit2.h"
+#include "config.h"
+#include "config/config_helpers.h"
static const char *_name = "remote.ab.url";
@@ -286,3 +288,32 @@ void test_config_multivar__delete_notfound(void)
git_config_free(cfg);
}
+
+void test_config_multivar__rename_section(void)
+{
+ git_repository *repo;
+ git_config *cfg;
+ int n;
+
+ repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_repository_config(&cfg, repo));
+
+ cl_git_pass(git_config_set_multivar(cfg, "branch.foo.name", "^$", "bar"));
+ cl_git_pass(git_config_set_multivar(cfg, "branch.foo.name", "^$", "xyzzy"));
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(
+ cfg, "branch.foo.name", NULL, cb, &n));
+ cl_assert(n == 2);
+
+ cl_git_pass(
+ git_config_rename_section(repo, "branch.foo", "branch.foobar"));
+
+ assert_config_entry_existence(repo, "branch.foo.name", false);
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(
+ cfg, "branch.foobar.name", NULL, cb, &n));
+ cl_assert(n == 2);
+
+ git_config_free(cfg);
+ cl_git_sandbox_cleanup();
+}
diff --git a/tests/libgit2/config/read.c b/tests/libgit2/config/read.c
index a2e668c207b..25e7b963c4d 100644
--- a/tests/libgit2/config/read.c
+++ b/tests/libgit2/config/read.c
@@ -495,6 +495,8 @@ void test_config_read__read_git_config_entry(void)
cl_assert_equal_s("core.dummy2", entry->name);
cl_assert_equal_s("42", entry->value);
cl_assert_equal_i(GIT_CONFIG_LEVEL_SYSTEM, entry->level);
+ cl_assert_equal_s("file", entry->backend_type);
+ cl_assert_equal_s(cl_fixture("config/config9"), entry->origin_path);
git_config_entry_free(entry);
git_config_free(cfg);
@@ -728,14 +730,11 @@ void test_config_read__path(void)
{
git_config *cfg;
git_buf path = GIT_BUF_INIT;
- git_buf old_path = GIT_BUF_INIT;
git_str home_path = GIT_STR_INIT;
git_str expected_path = GIT_STR_INIT;
- cl_git_pass(p_mkdir("fakehome", 0777));
- cl_git_pass(git_fs_path_prettify(&home_path, "fakehome", NULL));
- cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &old_path));
- cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, home_path.ptr));
+ cl_fake_homedir(&home_path);
+
cl_git_mkfile("./testconfig", "[some]\n path = ~/somefile");
cl_git_pass(git_fs_path_join_unrooted(&expected_path, "somefile", home_path.ptr, NULL));
@@ -761,8 +760,6 @@ void test_config_read__path(void)
cl_git_mkfile("./testconfig", "[some]\n path = ~user/foo");
cl_git_fail(git_config_get_path(&path, cfg, "some.path"));
- cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, old_path.ptr));
- git_buf_dispose(&old_path);
git_str_dispose(&home_path);
git_str_dispose(&expected_path);
git_config_free(cfg);
diff --git a/tests/libgit2/config/readonly.c b/tests/libgit2/config/readonly.c
index a8901e394c0..483f83a85fd 100644
--- a/tests/libgit2/config/readonly.c
+++ b/tests/libgit2/config/readonly.c
@@ -24,7 +24,7 @@ void test_config_readonly__writing_to_readonly_fails(void)
backend->readonly = 1;
cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
- cl_git_fail_with(GIT_ENOTFOUND, git_config_set_string(cfg, "foo.bar", "baz"));
+ cl_git_fail_with(GIT_EREADONLY, git_config_set_string(cfg, "foo.bar", "baz"));
cl_assert(!git_fs_path_exists("global"));
}
diff --git a/tests/libgit2/config/snapshot.c b/tests/libgit2/config/snapshot.c
index 5cc08a721ac..cc877063c08 100644
--- a/tests/libgit2/config/snapshot.c
+++ b/tests/libgit2/config/snapshot.c
@@ -79,6 +79,7 @@ void test_config_snapshot__multivar(void)
void test_config_snapshot__includes(void)
{
+ git_config_entry *entry;
int i;
cl_git_mkfile("including", "[include]\npath = included");
@@ -99,6 +100,16 @@ void test_config_snapshot__includes(void)
cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
cl_assert_equal_i(i, 1);
+ /* Ensure that the config entry is populated with origin */
+ cl_git_pass(git_config_get_entry(&entry, snapshot, "section.key"));
+
+ cl_assert_equal_s("section.key", entry->name);
+ cl_assert_equal_s("1", entry->value);
+ cl_assert_equal_s("file", entry->backend_type);
+ cl_assert_equal_s("./included", entry->origin_path);
+
+ git_config_entry_free(entry);
+
cl_git_pass(p_unlink("including"));
cl_git_pass(p_unlink("included"));
}
@@ -106,6 +117,7 @@ void test_config_snapshot__includes(void)
void test_config_snapshot__snapshot(void)
{
git_config *snapshot_snapshot;
+ git_config_entry *entry;
int i;
cl_git_mkfile("configfile", "[section]\nkey = 1\n");
@@ -118,22 +130,49 @@ void test_config_snapshot__snapshot(void)
cl_git_pass(git_config_get_int32(&i, snapshot_snapshot, "section.key"));
cl_assert_equal_i(i, 1);
+ /* Ensure that the config entry is populated with origin */
+ cl_git_pass(git_config_get_entry(&entry, snapshot_snapshot, "section.key"));
+
+ cl_assert_equal_s("section.key", entry->name);
+ cl_assert_equal_s("1", entry->value);
+ cl_assert_equal_s("file", entry->backend_type);
+ cl_assert_equal_s("configfile", entry->origin_path);
+
+ git_config_entry_free(entry);
+
git_config_free(snapshot_snapshot);
cl_git_pass(p_unlink("configfile"));
}
-void test_config_snapshot__snapshot_from_in_memony(void)
+void test_config_snapshot__snapshot_from_in_memory(void)
{
const char *configuration = "[section]\nkey = 1\n";
git_config_backend *backend;
+ git_config_entry *entry;
int i;
+ git_config_backend_memory_options opts =
+ GIT_CONFIG_BACKEND_MEMORY_OPTIONS_INIT;
+
+ opts.backend_type = "test";
+ opts.origin_path = "hello";
+
cl_git_pass(git_config_new(&cfg));
- cl_git_pass(git_config_backend_from_string(&backend, configuration, strlen(configuration)));
+ cl_git_pass(git_config_backend_from_string(&backend, configuration, strlen(configuration), &opts));
cl_git_pass(git_config_add_backend(cfg, backend, 0, NULL, 0));
cl_git_pass(git_config_snapshot(&snapshot, cfg));
cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
cl_assert_equal_i(i, 1);
+
+ /* Ensure that the config entry is populated with origin */
+ cl_git_pass(git_config_get_entry(&entry, snapshot, "section.key"));
+
+ cl_assert_equal_s("section.key", entry->name);
+ cl_assert_equal_s("1", entry->value);
+ cl_assert_equal_s("test", entry->backend_type);
+ cl_assert_equal_s("hello", entry->origin_path);
+
+ git_config_entry_free(entry);
}
diff --git a/tests/libgit2/config/write.c b/tests/libgit2/config/write.c
index 9d8c3fe9495..c71d4f6dc86 100644
--- a/tests/libgit2/config/write.c
+++ b/tests/libgit2/config/write.c
@@ -696,6 +696,36 @@ void test_config_write__locking(void)
git_config_free(cfg);
}
+void test_config_write__abort_lock(void)
+{
+ git_config *cfg;
+ git_config_entry *entry;
+ git_transaction *tx;
+ const char *filename = "locked-file";
+
+ /* Open the config and lock it */
+ cl_git_mkfile(filename, "[section]\n\tname = value\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+ cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
+ cl_assert_equal_s("value", entry->value);
+ git_config_entry_free(entry);
+ cl_git_pass(git_config_lock(&tx, cfg));
+
+ /* Change entries in the locked backend */
+ cl_git_pass(git_config_set_string(cfg, "section.name", "other value"));
+ cl_git_pass(git_config_set_string(cfg, "section2.name3", "more value"));
+
+ git_transaction_free(tx);
+
+ /* Now that we've unlocked it, we should see no changes */
+ cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
+ cl_assert_equal_s("value", entry->value);
+ git_config_entry_free(entry);
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg, "section2.name3"));
+
+ git_config_free(cfg);
+}
+
void test_config_write__repeated(void)
{
const char *filename = "config-repeated";
diff --git a/tests/libgit2/core/oid.c b/tests/libgit2/core/oid.c
index 90fb37514ba..a405b3344d7 100644
--- a/tests/libgit2/core/oid.c
+++ b/tests/libgit2/core/oid.c
@@ -192,3 +192,22 @@ void test_core_oid__fmt_substr_sha1(void)
git_oid_fmt_substr(buf, &id_sha1, 5, 6);
cl_assert_equal_s(buf, "12eea6");
}
+
+void test_core_oid__type_lookup(void)
+{
+ cl_assert_equal_i(GIT_OID_SHA1, git_oid_type_fromstr("sha1"));
+ cl_assert_equal_i(GIT_OID_SHA1, git_oid_type_fromstrn("sha1...", 4));
+ cl_assert_equal_s("sha1", git_oid_type_name(GIT_OID_SHA1));
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_assert_equal_i(GIT_OID_SHA256, git_oid_type_fromstr("sha256"));
+ cl_assert_equal_i(GIT_OID_SHA256, git_oid_type_fromstrn("sha256...", 6));
+ cl_assert_equal_s("sha256", git_oid_type_name(GIT_OID_SHA256));
+#endif
+
+ cl_assert_equal_i(0, git_oid_type_fromstr("sha42"));
+ cl_assert_equal_i(0, git_oid_type_fromstrn("sha1", 3));
+ cl_assert_equal_i(0, git_oid_type_fromstrn("sha1...", 5));
+ cl_assert_equal_s("unknown", git_oid_type_name(0));
+ cl_assert_equal_s("unknown", git_oid_type_name(42));
+}
diff --git a/tests/libgit2/core/oidarray.c b/tests/libgit2/core/oidarray.c
new file mode 100644
index 00000000000..4a9e47c701d
--- /dev/null
+++ b/tests/libgit2/core/oidarray.c
@@ -0,0 +1,98 @@
+#include "clar_libgit2.h"
+
+#include "git2/oid.h"
+#include "git2/transport.h"
+
+#include "common.h"
+#include "transports/smart.h"
+#include "oid.h"
+#include "oidarray.h"
+
+#include
+
+#define oid_0 "c070ad8c08840c8116da865b2d65593a6bb9cd2a"
+#define oid_1 "0966a434eb1a025db6b71485ab63a3bfbea520b6"
+#define oid_2 "83834a7afdaa1a1260568567f6ad90020389f664"
+#define oid_3 "746fb4c91a7b6190bc4761adf7410afc4b59812c"
+
+void test_core_oidarray__add_and_remove_oid_from_shallowarray(void)
+{
+ git_oid oid_0_obj, oid_1_obj, oid_2_obj, oid_3_obj;
+ git_array_oid_t array = GIT_ARRAY_INIT;
+
+ git_oid__fromstr(&oid_0_obj, oid_0, GIT_OID_SHA1);
+ git_oid__fromstr(&oid_1_obj, oid_1, GIT_OID_SHA1);
+ git_oid__fromstr(&oid_2_obj, oid_2, GIT_OID_SHA1);
+ git_oid__fromstr(&oid_3_obj, oid_3, GIT_OID_SHA1);
+
+ /* add some initial ids */
+ git_oidarray__add(&array, &oid_0_obj);
+ git_oidarray__add(&array, &oid_1_obj);
+ git_oidarray__add(&array, &oid_2_obj);
+
+ cl_assert_equal_i(3, array.size);
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[1]));
+ cl_assert_equal_s("83834a7afdaa1a1260568567f6ad90020389f664", git_oid_tostr_s(&array.ptr[2]));
+
+ /* don't duplicate existing ids */
+ git_oidarray__add(&array, &oid_1_obj);
+
+ cl_assert_equal_i(3, array.size);
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[1]));
+ cl_assert_equal_s("83834a7afdaa1a1260568567f6ad90020389f664", git_oid_tostr_s(&array.ptr[2]));
+
+ /* remove the last id */
+ cl_assert_equal_i(1, git_oidarray__remove(&array, &oid_2_obj));
+
+ cl_assert_equal_i(2, array.size);
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[1]));
+
+ /* add another id */
+ git_oidarray__add(&array, &oid_3_obj);
+
+ cl_assert_equal_i(3, array.size);
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[1]));
+ cl_assert_equal_s("746fb4c91a7b6190bc4761adf7410afc4b59812c", git_oid_tostr_s(&array.ptr[2]));
+
+ /* remove the first id */
+ cl_assert_equal_i(1, git_oidarray__remove(&array, &oid_0_obj));
+
+ cl_assert_equal_i(2, array.size);
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("746fb4c91a7b6190bc4761adf7410afc4b59812c", git_oid_tostr_s(&array.ptr[1]));
+
+ /* removing a nonexistent oid does nothing */
+ cl_assert_equal_i(0, git_oidarray__remove(&array, &oid_2_obj));
+
+ /* add another id */
+ git_oidarray__add(&array, &oid_0_obj);
+
+ cl_assert_equal_i(3, array.size);
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("746fb4c91a7b6190bc4761adf7410afc4b59812c", git_oid_tostr_s(&array.ptr[1]));
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[2]));
+
+ /* remove another id */
+ cl_assert_equal_i(1, git_oidarray__remove(&array, &oid_3_obj));
+
+ cl_assert_equal_i(2, array.size);
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[1]));
+
+ /* remove another id */
+ cl_assert_equal_i(1, git_oidarray__remove(&array, &oid_1_obj));
+
+ cl_assert_equal_i(1, array.size);
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[0]));
+
+ /* remove the final id */
+ cl_assert_equal_i(1, git_oidarray__remove(&array, &oid_0_obj));
+
+ cl_assert_equal_i(0, array.size);
+
+ git_array_clear(array);
+}
diff --git a/tests/libgit2/core/opts.c b/tests/libgit2/core/opts.c
index e8f65d51017..cbef29f991d 100644
--- a/tests/libgit2/core/opts.c
+++ b/tests/libgit2/core/opts.c
@@ -34,8 +34,10 @@ void test_core_opts__extensions_query(void)
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
- cl_assert_equal_sz(out.count, 1);
+ cl_assert_equal_sz(out.count, 3);
cl_assert_equal_s("noop", out.strings[0]);
+ cl_assert_equal_s("objectformat", out.strings[1]);
+ cl_assert_equal_s("worktreeconfig", out.strings[2]);
git_strarray_dispose(&out);
}
@@ -48,9 +50,11 @@ void test_core_opts__extensions_add(void)
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
- cl_assert_equal_sz(out.count, 2);
- cl_assert_equal_s("noop", out.strings[0]);
- cl_assert_equal_s("foo", out.strings[1]);
+ cl_assert_equal_sz(out.count, 4);
+ cl_assert_equal_s("foo", out.strings[0]);
+ cl_assert_equal_s("noop", out.strings[1]);
+ cl_assert_equal_s("objectformat", out.strings[2]);
+ cl_assert_equal_s("worktreeconfig", out.strings[3]);
git_strarray_dispose(&out);
}
@@ -63,9 +67,29 @@ void test_core_opts__extensions_remove(void)
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
- cl_assert_equal_sz(out.count, 2);
+ cl_assert_equal_sz(out.count, 4);
cl_assert_equal_s("bar", out.strings[0]);
cl_assert_equal_s("baz", out.strings[1]);
+ cl_assert_equal_s("objectformat", out.strings[2]);
+ cl_assert_equal_s("worktreeconfig", out.strings[3]);
+
+ git_strarray_dispose(&out);
+}
+
+void test_core_opts__extensions_uniq(void)
+{
+ const char *in[] = { "foo", "noop", "bar", "bar", "foo", "objectformat" };
+ git_strarray out = { 0 };
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
+
+ cl_assert_equal_sz(out.count, 5);
+ cl_assert_equal_s("bar", out.strings[0]);
+ cl_assert_equal_s("foo", out.strings[1]);
+ cl_assert_equal_s("noop", out.strings[2]);
+ cl_assert_equal_s("objectformat", out.strings[3]);
+ cl_assert_equal_s("worktreeconfig", out.strings[4]);
git_strarray_dispose(&out);
}
diff --git a/tests/libgit2/core/structinit.c b/tests/libgit2/core/structinit.c
index 160e2f61218..8a6e48d2a9c 100644
--- a/tests/libgit2/core/structinit.c
+++ b/tests/libgit2/core/structinit.c
@@ -160,6 +160,11 @@ void test_core_structinit__compare(void)
git_stash_apply_options, GIT_STASH_APPLY_OPTIONS_VERSION, \
GIT_STASH_APPLY_OPTIONS_INIT, git_stash_apply_options_init);
+ /* stash save */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_stash_save_options, GIT_STASH_SAVE_OPTIONS_VERSION, \
+ GIT_STASH_SAVE_OPTIONS_INIT, git_stash_save_options_init);
+
/* status */
CHECK_MACRO_FUNC_INIT_EQUAL( \
git_status_options, GIT_STATUS_OPTIONS_VERSION, \
diff --git a/tests/libgit2/core/useragent.c b/tests/libgit2/core/useragent.c
index a4ece902fd9..2e119de4490 100644
--- a/tests/libgit2/core/useragent.c
+++ b/tests/libgit2/core/useragent.c
@@ -1,17 +1,52 @@
#include "clar_libgit2.h"
#include "settings.h"
-void test_core_useragent__get(void)
+static git_buf default_ua = GIT_BUF_INIT;
+static git_buf default_product = GIT_BUF_INIT;
+
+void test_core_useragent__initialize(void)
+{
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_USER_AGENT, &default_ua));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_USER_AGENT_PRODUCT, &default_product));
+}
+
+void test_core_useragent__cleanup(void)
+{
+ git_libgit2_opts(GIT_OPT_SET_USER_AGENT, NULL);
+ git_libgit2_opts(GIT_OPT_SET_USER_AGENT_PRODUCT, NULL);
+
+ git_buf_dispose(&default_ua);
+ git_buf_dispose(&default_product);
+}
+
+void test_core_useragent__get_default(void)
+{
+ cl_assert(default_ua.size);
+ cl_assert(default_ua.ptr);
+ cl_assert(git__prefixcmp(default_ua.ptr, "libgit2 ") == 0);
+
+ cl_assert(default_product.size);
+ cl_assert(default_product.ptr);
+ cl_assert(git__prefixcmp(default_product.ptr, "git/") == 0);
+}
+
+void test_core_useragent__set(void)
{
- const char *custom_name = "super duper git";
- git_str buf = GIT_STR_INIT;
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, "foo bar 4.24"));
+ cl_assert_equal_s("foo bar 4.24", git_settings__user_agent());
+ cl_assert_equal_s(default_product.ptr, git_settings__user_agent_product());
- cl_assert_equal_p(NULL, git_libgit2__user_agent());
- cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, custom_name));
- cl_assert_equal_s(custom_name, git_libgit2__user_agent());
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT_PRODUCT, "baz/2.2.3"));
+ cl_assert_equal_s("foo bar 4.24", git_settings__user_agent());
+ cl_assert_equal_s("baz/2.2.3", git_settings__user_agent_product());
- cl_git_pass(git_libgit2_opts(GIT_OPT_GET_USER_AGENT, &buf));
- cl_assert_equal_s(custom_name, buf.ptr);
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, ""));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT_PRODUCT, ""));
+ cl_assert_equal_s("", git_settings__user_agent());
+ cl_assert_equal_s("", git_settings__user_agent_product());
- git_str_dispose(&buf);
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, NULL));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT_PRODUCT, NULL));
+ cl_assert_equal_s(default_ua.ptr, git_settings__user_agent());
+ cl_assert_equal_s(default_product.ptr, git_settings__user_agent_product());
}
diff --git a/tests/libgit2/diff/diff_helpers.c b/tests/libgit2/diff/diff_helpers.c
index 341b0a448a3..5daebffeb3c 100644
--- a/tests/libgit2/diff/diff_helpers.c
+++ b/tests/libgit2/diff/diff_helpers.c
@@ -314,3 +314,20 @@ void diff_assert_equal(git_diff *a, git_diff *b)
}
}
+#ifdef GIT_EXPERIMENTAL_SHA256
+int diff_from_buffer(
+ git_diff **out,
+ const char *content,
+ size_t content_len)
+{
+ return git_diff_from_buffer(out, content, content_len, NULL);
+}
+#else
+int diff_from_buffer(
+ git_diff **out,
+ const char *content,
+ size_t content_len)
+{
+ return git_diff_from_buffer(out, content, content_len);
+}
+#endif
diff --git a/tests/libgit2/diff/diff_helpers.h b/tests/libgit2/diff/diff_helpers.h
index af855ce684f..1be4b47801c 100644
--- a/tests/libgit2/diff/diff_helpers.h
+++ b/tests/libgit2/diff/diff_helpers.h
@@ -71,3 +71,7 @@ extern void diff_print_raw(FILE *fp, git_diff *diff);
extern void diff_assert_equal(git_diff *a, git_diff *b);
+extern int diff_from_buffer(
+ git_diff **out,
+ const char *content,
+ size_t content_len);
diff --git a/tests/libgit2/diff/index.c b/tests/libgit2/diff/index.c
index 5773b748e10..b7866750bb3 100644
--- a/tests/libgit2/diff/index.c
+++ b/tests/libgit2/diff/index.c
@@ -1,5 +1,6 @@
#include "clar_libgit2.h"
#include "diff_helpers.h"
+#include "index.h"
static git_repository *g_repo = NULL;
@@ -278,7 +279,7 @@ void test_diff_index__to_index(void)
git_diff *diff;
diff_expects exp;
- cl_git_pass(git_index_new(&old_index));
+ cl_git_pass(git_index__new(&old_index, GIT_OID_SHA1));
old_tree = resolve_commit_oid_to_tree(g_repo, a_commit);
cl_git_pass(git_index_read_tree(old_index, old_tree));
diff --git a/tests/libgit2/diff/parse.c b/tests/libgit2/diff/parse.c
index cae843cc836..59fc0280ed6 100644
--- a/tests/libgit2/diff/parse.c
+++ b/tests/libgit2/diff/parse.c
@@ -19,19 +19,19 @@ void test_diff_parse__nonpatches_fail_with_notfound(void)
const char *not_with_both = "Lead.\n" PATCH_NOT_A_PATCH "Trail.\n";
cl_git_fail_with(GIT_ENOTFOUND,
- git_diff_from_buffer(&diff,
+ diff_from_buffer(&diff,
not,
strlen(not)));
cl_git_fail_with(GIT_ENOTFOUND,
- git_diff_from_buffer(&diff,
+ diff_from_buffer(&diff,
not_with_leading,
strlen(not_with_leading)));
cl_git_fail_with(GIT_ENOTFOUND,
- git_diff_from_buffer(&diff,
+ diff_from_buffer(&diff,
not_with_trailing,
strlen(not_with_trailing)));
cl_git_fail_with(GIT_ENOTFOUND,
- git_diff_from_buffer(&diff,
+ diff_from_buffer(&diff,
not_with_both,
strlen(not_with_both)));
}
@@ -51,7 +51,7 @@ static void test_parse_invalid_diff(const char *invalid_diff)
git_str_puts(&buf, PATCH_BINARY_LITERAL);
cl_git_fail_with(GIT_ERROR,
- git_diff_from_buffer(&diff, buf.ptr, buf.size));
+ diff_from_buffer(&diff, buf.ptr, buf.size));
git_str_dispose(&buf);
}
@@ -72,7 +72,7 @@ void test_diff_parse__exact_rename(void)
"2.9.3\n";
git_diff *diff;
- cl_git_pass(git_diff_from_buffer(
+ cl_git_pass(diff_from_buffer(
&diff, content, strlen(content)));
git_diff_free(diff);
}
@@ -92,7 +92,7 @@ void test_diff_parse__empty_file(void)
"2.20.1\n";
git_diff *diff;
- cl_git_pass(git_diff_from_buffer(
+ cl_git_pass(diff_from_buffer(
&diff, content, strlen(content)));
git_diff_free(diff);
}
@@ -102,7 +102,7 @@ void test_diff_parse__no_extended_headers(void)
const char *content = PATCH_NO_EXTENDED_HEADERS;
git_diff *diff;
- cl_git_pass(git_diff_from_buffer(
+ cl_git_pass(diff_from_buffer(
&diff, content, strlen(content)));
git_diff_free(diff);
}
@@ -125,7 +125,7 @@ void test_diff_parse__add_delete_no_index(void)
"-three\n";
git_diff *diff;
- cl_git_pass(git_diff_from_buffer(
+ cl_git_pass(diff_from_buffer(
&diff, content, strlen(content)));
git_diff_free(diff);
}
@@ -166,7 +166,7 @@ static void test_tree_to_tree_computed_to_parsed(
cl_git_pass(git_diff_to_buf(&computed_buf,
computed, GIT_DIFF_FORMAT_PATCH));
- cl_git_pass(git_diff_from_buffer(&parsed,
+ cl_git_pass(diff_from_buffer(&parsed,
computed_buf.ptr, computed_buf.size));
diff_assert_equal(computed, parsed);
@@ -248,7 +248,7 @@ void test_diff_parse__get_patch_from_diff(void)
computed, GIT_DIFF_FORMAT_PATCH));
cl_git_pass(git_patch_from_diff(&patch_computed, computed, 0));
- cl_git_pass(git_diff_from_buffer(&parsed,
+ cl_git_pass(diff_from_buffer(&parsed,
computed_buf.ptr, computed_buf.size));
cl_git_pass(git_patch_from_diff(&patch_parsed, parsed, 0));
@@ -279,6 +279,31 @@ static int file_cb(const git_diff_delta *delta, float progress, void *payload)
return 0;
}
+void test_diff_parse__eof_nl_missing(void)
+{
+ const char patch[] =
+ "diff --git a/.env b/.env\n"
+ "index f89e4c0..7c56eb7 100644\n"
+ "--- a/.env\n"
+ "+++ b/.env\n"
+ "@@ -1 +1 @@\n"
+ "-hello=12345\n"
+ "+hello=123456\n"
+ "\\ No newline at end of file\n";
+ git_diff *diff;
+ git_patch *ret_patch;
+ git_diff_line *line;
+
+ cl_git_pass(diff_from_buffer(&diff, patch, strlen(patch)));
+ cl_git_pass(git_patch_from_diff(&ret_patch, diff, 0));
+
+ cl_assert((line = git_array_get(ret_patch->lines, 2)) != NULL);
+ cl_assert(line->origin == GIT_DIFF_LINE_DEL_EOFNL);
+
+ git_diff_free(diff);
+ git_patch_free(ret_patch);
+}
+
void test_diff_parse__foreach_works_with_parsed_patch(void)
{
const char patch[] =
@@ -292,7 +317,7 @@ void test_diff_parse__foreach_works_with_parsed_patch(void)
int called = 0;
git_diff *diff;
- cl_git_pass(git_diff_from_buffer(&diff, patch, strlen(patch)));
+ cl_git_pass(diff_from_buffer(&diff, patch, strlen(patch)));
cl_git_pass(git_diff_foreach(diff, file_cb, NULL, NULL, NULL, &called));
cl_assert_equal_i(called, 1);
@@ -312,7 +337,7 @@ void test_diff_parse__parsing_minimal_patch_succeeds(void)
git_buf buf = GIT_BUF_INIT;
git_diff *diff;
- cl_git_pass(git_diff_from_buffer(&diff, patch, strlen(patch)));
+ cl_git_pass(diff_from_buffer(&diff, patch, strlen(patch)));
cl_git_pass(git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH));
cl_assert_equal_s(patch, buf.ptr);
@@ -330,7 +355,7 @@ void test_diff_parse__patch_roundtrip_succeeds(void)
cl_git_pass(git_patch_from_buffers(&patch, buf1, strlen(buf1), "obj1", buf2, strlen(buf2), "obj2", NULL));
cl_git_pass(git_patch_to_buf(&patchbuf, patch));
- cl_git_pass(git_diff_from_buffer(&diff, patchbuf.ptr, patchbuf.size));
+ cl_git_pass(diff_from_buffer(&diff, patchbuf.ptr, patchbuf.size));
cl_git_pass(git_diff_to_buf(&diffbuf, diff, GIT_DIFF_FORMAT_PATCH));
cl_assert_equal_s(patchbuf.ptr, diffbuf.ptr);
@@ -372,7 +397,7 @@ void test_diff_parse__issue4672(void)
const git_diff_hunk *hunk;
size_t n, l = 0;
- cl_git_pass(git_diff_from_buffer(&diff, text, strlen(text)));
+ cl_git_pass(diff_from_buffer(&diff, text, strlen(text)));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_get_hunk(&hunk, &n, patch, 0));
@@ -393,7 +418,7 @@ void test_diff_parse__lineinfo(void)
const git_diff_hunk *hunk;
size_t n, l = 0;
- cl_git_pass(git_diff_from_buffer(&diff, text, strlen(text)));
+ cl_git_pass(diff_from_buffer(&diff, text, strlen(text)));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_get_hunk(&hunk, &n, patch, 0));
@@ -419,7 +444,7 @@ void test_diff_parse__new_file_with_space(void)
git_patch *patch;
git_diff *diff;
- cl_git_pass(git_diff_from_buffer(&diff, content, strlen(content)));
+ cl_git_pass(diff_from_buffer(&diff, content, strlen(content)));
cl_git_pass(git_patch_from_diff((git_patch **) &patch, diff, 0));
cl_assert_equal_p(patch->diff_opts.old_prefix, NULL);
@@ -437,7 +462,7 @@ void test_diff_parse__new_file_with_space_and_regenerate_patch(void)
git_diff *diff = NULL;
git_buf buf = GIT_BUF_INIT;
- cl_git_pass(git_diff_from_buffer(&diff, content, strlen(content)));
+ cl_git_pass(diff_from_buffer(&diff, content, strlen(content)));
cl_git_pass(git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH));
git_buf_dispose(&buf);
@@ -450,7 +475,7 @@ void test_diff_parse__delete_file_with_space_and_regenerate_patch(void)
git_diff *diff = NULL;
git_buf buf = GIT_BUF_INIT;
- cl_git_pass(git_diff_from_buffer(&diff, content, strlen(content)));
+ cl_git_pass(diff_from_buffer(&diff, content, strlen(content)));
cl_git_pass(git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH));
git_buf_dispose(&buf);
@@ -464,7 +489,7 @@ void test_diff_parse__crlf(void)
git_patch *patch;
const git_diff_delta *delta;
- cl_git_pass(git_diff_from_buffer(&diff, text, strlen(text)));
+ cl_git_pass(diff_from_buffer(&diff, text, strlen(text)));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
delta = git_patch_get_delta(patch);
diff --git a/tests/libgit2/diff/patchid.c b/tests/libgit2/diff/patchid.c
index 1cc368e2193..91807e7b747 100644
--- a/tests/libgit2/diff/patchid.c
+++ b/tests/libgit2/diff/patchid.c
@@ -1,5 +1,6 @@
#include "clar_libgit2.h"
#include "patch/patch_common.h"
+#include "diff_helpers.h"
static void verify_patch_id(const char *diff_content, const char *expected_id)
{
@@ -7,7 +8,7 @@ static void verify_patch_id(const char *diff_content, const char *expected_id)
git_diff *diff;
cl_git_pass(git_oid__fromstr(&expected_oid, expected_id, GIT_OID_SHA1));
- cl_git_pass(git_diff_from_buffer(&diff, diff_content, strlen(diff_content)));
+ cl_git_pass(diff_from_buffer(&diff, diff_content, strlen(diff_content)));
cl_git_pass(git_diff_patchid(&actual_oid, diff, NULL));
cl_assert_equal_oid(&expected_oid, &actual_oid);
diff --git a/tests/libgit2/diff/rename.c b/tests/libgit2/diff/rename.c
index 9d44394624d..61a2f839cb3 100644
--- a/tests/libgit2/diff/rename.c
+++ b/tests/libgit2/diff/rename.c
@@ -1441,6 +1441,52 @@ void test_diff_rename__can_delete_unmodified_deltas(void)
git_str_dispose(&c1);
}
+void test_diff_rename__can_delete_unmodified_deltas_including_submodule(void)
+{
+ git_repository *repo; /* "submodules" */
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ cl_git_sandbox_cleanup(); /* Don't use "renames" for this test */
+ repo = cl_git_sandbox_init("submodules");
+
+ cl_repo_set_bool(repo, "core.autocrlf", false);
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+
+ diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_tree_to_index(&diff, repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(5, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_REMOVE_UNMODIFIED;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(0, exp.files);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ cl_git_sandbox_cleanup();
+}
+
void test_diff_rename__matches_config_behavior(void)
{
const char *sha0 = INITIAL_COMMIT;
diff --git a/tests/libgit2/diff/stats.c b/tests/libgit2/diff/stats.c
index b076ad5a93d..7af89155084 100644
--- a/tests/libgit2/diff/stats.c
+++ b/tests/libgit2/diff/stats.c
@@ -4,6 +4,7 @@
#include "commit.h"
#include "diff.h"
#include "diff_generate.h"
+#include "diff_helpers.h"
static git_repository *_repo;
static git_diff_stats *_stats;
@@ -368,7 +369,7 @@ void test_diff_stats__new_file(void)
" 1 file changed, 1 insertion(+)\n"
" create mode 100644 Gurjeet Singh\n";
- cl_git_pass(git_diff_from_buffer(&diff, input, strlen(input)));
+ cl_git_pass(diff_from_buffer(&diff, input, strlen(input)));
cl_git_pass(git_diff_get_stats(&_stats, diff));
cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY, 0));
cl_assert_equal_s(stat, buf.ptr);
diff --git a/tests/libgit2/diff/workdir.c b/tests/libgit2/diff/workdir.c
index 8ccde41ff2a..504ece6fc91 100644
--- a/tests/libgit2/diff/workdir.c
+++ b/tests/libgit2/diff/workdir.c
@@ -1232,6 +1232,42 @@ void test_diff_workdir__checks_options_version(void)
cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
}
+void test_diff_workdir__can_diff_empty_untracked_file(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_patch *patch = NULL;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile("empty_standard_repo/emptyfile.txt", "");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_SHOW_UNTRACKED_CONTENT;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ /* without filters */
+ git_patch_from_diff(&patch, diff, 0);
+ cl_assert(NULL != patch);
+ cl_assert(0 == git_patch_get_delta(patch)->new_file.size);
+ cl_assert(0 == strcmp("emptyfile.txt", git_patch_get_delta(patch)->new_file.path));
+ git_patch_free(patch);
+ patch = NULL;
+
+ /* with a filter */
+ cl_repo_set_bool(g_repo, "core.autocrlf", true); /* install some filter */
+ git_patch_from_diff(&patch, diff, 0);
+ cl_assert(NULL != patch);
+ cl_assert(0 == git_patch_get_delta(patch)->new_file.size);
+ cl_assert(0 == strcmp("emptyfile.txt", git_patch_get_delta(patch)->new_file.path));
+ git_patch_free(patch);
+ patch = NULL;
+
+ git_diff_free(diff);
+}
+
void test_diff_workdir__can_diff_empty_file(void)
{
git_diff *diff;
@@ -2242,3 +2278,89 @@ void test_diff_workdir__ignore_blank_lines(void)
git_patch_free(patch);
git_diff_free(diff);
}
+
+void test_diff_workdir__to_index_reversed_content_loads(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ int use_iterator;
+ char *pathspec = "new_file";
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_SHOW_UNTRACKED_CONTENT | GIT_DIFF_REVERSE;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ cl_assert_equal_i(1, exp.hunks);
+
+ cl_assert_equal_i(1, exp.lines);
+ cl_assert_equal_i(0, exp.line_ctxt);
+ cl_assert_equal_i(0, exp.line_adds);
+ cl_assert_equal_i(1, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__completely_ignored_shows_empty_diff(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff;
+ git_patch *patch;
+ git_buf buf = GIT_BUF_INIT;
+ char *pathspec = "subdir.txt";
+
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ g_repo = cl_git_sandbox_init("status");
+ cl_git_rewritefile("status/subdir.txt", "Is it a bird?\n\nIs it a plane?\n");
+
+ /* Perform the diff normally */
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ cl_assert_equal_s("diff --git a/subdir.txt b/subdir.txt\nindex e8ee89e..53c8db5 100644\n--- a/subdir.txt\n+++ b/subdir.txt\n@@ -1,2 +1,3 @@\n Is it a bird?\n+\n Is it a plane?\n", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* Perform the diff ignoring blank lines */
+ opts.flags |= GIT_DIFF_IGNORE_BLANK_LINES;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ cl_assert_equal_s("", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
diff --git a/tests/libgit2/grafts/basic.c b/tests/libgit2/grafts/basic.c
new file mode 100644
index 00000000000..30c87f908af
--- /dev/null
+++ b/tests/libgit2/grafts/basic.c
@@ -0,0 +1,121 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "grafts.h"
+
+static git_repository *g_repo;
+
+void test_grafts_basic__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("grafted.git");
+}
+
+void test_grafts_basic__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_grafts_basic__graft_add(void)
+{
+ git_array_oid_t parents = GIT_ARRAY_INIT;
+ git_oid oid_src, *oid1;
+ git_commit_graft *graft;
+ git_grafts *grafts;
+
+ cl_git_pass(git_grafts_new(&grafts, GIT_OID_SHA1));
+
+ cl_assert(oid1 = git_array_alloc(parents));
+ cl_git_pass(git_oid__fromstr(&oid_src, "2f3053cbff8a4ca2f0666de364ddb734a28a31a9", GIT_OID_SHA1));
+ git_oid_cpy(oid1, &oid_src);
+
+ git_oid__fromstr(&oid_src, "f503807ffa920e407a600cfaee96b7152259acc7", GIT_OID_SHA1);
+ cl_git_pass(git_grafts_add(grafts, &oid_src, parents));
+ git_array_clear(parents);
+
+ cl_assert_equal_i(1, git_grafts_size(grafts));
+ cl_git_pass(git_grafts_get(&graft, grafts, &oid_src));
+ cl_assert_equal_s("f503807ffa920e407a600cfaee96b7152259acc7", git_oid_tostr_s(&graft->oid));
+ cl_assert_equal_i(1, git_array_size(graft->parents));
+ cl_assert_equal_s("2f3053cbff8a4ca2f0666de364ddb734a28a31a9", git_oid_tostr_s(git_array_get(graft->parents, 0)));
+
+ git_grafts_free(grafts);
+}
+
+void test_grafts_basic__grafted_revwalk(void)
+{
+ git_revwalk *w;
+ git_oid oids[10];
+ size_t i = 0;
+ git_commit *commit;
+
+ cl_git_pass(git_revwalk_new(&w, g_repo));
+ cl_git_pass(git_revwalk_push_ref(w, "refs/heads/branch"));
+
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[0]), "8a00e91619098618be97c0d2ceabb05a2c58edd9");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[1]), "f503807ffa920e407a600cfaee96b7152259acc7");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[2]), "2f3053cbff8a4ca2f0666de364ddb734a28a31a9");
+
+ cl_git_fail_with(GIT_ITEROVER, git_revwalk_next(&oids[i++], w));
+
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oids[0]));
+
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+
+ git_commit_free(commit);
+ git_revwalk_free(w);
+}
+
+void test_grafts_basic__grafted_objects(void)
+{
+ git_oid oid;
+ git_commit *commit;
+
+ cl_git_pass(git_oid__fromstr(&oid, "f503807ffa920e407a600cfaee96b7152259acc7", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+ git_commit_free(commit);
+
+ cl_git_pass(git_oid__fromstr(&oid, "0512adebd3782157f0d5c9b22b043f87b4aaff9e", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+ git_commit_free(commit);
+
+ cl_git_pass(git_oid__fromstr(&oid, "66cc22a015f6ca75b34c82d28f78ba663876bade", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_assert_equal_i(4, git_commit_parentcount(commit));
+ git_commit_free(commit);
+}
+
+void test_grafts_basic__grafted_merge_revwalk(void)
+{
+ git_revwalk *w;
+ git_oid oids[10];
+ size_t i = 0;
+
+ cl_git_pass(git_revwalk_new(&w, g_repo));
+ cl_git_pass(git_revwalk_push_ref(w, "refs/heads/bottom"));
+
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "66cc22a015f6ca75b34c82d28f78ba663876bade");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "e414f42f4e6bc6934563a2349a8600f0ab68618e");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "8a00e91619098618be97c0d2ceabb05a2c58edd9");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "1c18e80a276611bb9b146590616bbc5aebdf2945");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "d7224d49d6d5aff6ade596ed74f4bcd4f77b29e2");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "0512adebd3782157f0d5c9b22b043f87b4aaff9e");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "f503807ffa920e407a600cfaee96b7152259acc7");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "2f3053cbff8a4ca2f0666de364ddb734a28a31a9");
+
+ cl_git_fail_with(GIT_ITEROVER, git_revwalk_next(&oids[i++], w));
+
+ git_revwalk_free(w);
+}
diff --git a/tests/libgit2/grafts/parse.c b/tests/libgit2/grafts/parse.c
new file mode 100644
index 00000000000..3b0618a1d99
--- /dev/null
+++ b/tests/libgit2/grafts/parse.c
@@ -0,0 +1,149 @@
+#include "clar_libgit2.h"
+
+#include "grafts.h"
+
+#define OID0 "c0368f9f9743e950e6cfe1f45a649f8a9dfcd97e"
+#define OID1 "cfc50a0db87ce908fb8a8c5b8f7b4ab96eee8643"
+#define OID2 "6914d97cd08b9edf5e855fca211c750fa82fd80a"
+#define OID3 "516521937d0e9ce9d0d836149a0702671f326b4a"
+#define OID4 "e2c29d67ef2f217650196f94c796f0532b8caad6"
+#define OID5 "79bcb936596cb50353fe7be28b7444e66e4a2842"
+#define OID6 "b9c54107d57c17dbcaf646c4d52f66eb9e69d23d"
+#define OID7 "9f8a746e9ad7b58cc840016bc3944d5ad262acb5"
+#define OID8 "392f4beef7d0d15b2bc5b1abe1a754eba0ec36da"
+
+#define OID_TRUNCATED "392f4beef7d0d15b2bc5b1abe1a754eba0ec36d"
+#define OID_NONHEX "9f8a746e9ax7b58cc840016bc3944d5ad262acb5"
+
+static git_grafts *grafts;
+
+void test_grafts_parse__initialize(void)
+{
+ cl_git_pass(git_grafts_new(&grafts, GIT_OID_SHA1));
+}
+
+void test_grafts_parse__cleanup(void)
+{
+ git_grafts_free(grafts);
+ grafts = NULL;
+}
+
+static void assert_parse_succeeds(git_grafts *grafts, const char *string, size_t n)
+{
+ cl_git_pass(git_grafts_parse(grafts, string, strlen(string)));
+ cl_assert_equal_i(git_grafts_size(grafts), n);
+}
+
+static void assert_parse_fails(git_grafts *grafts, const char *string)
+{
+ cl_git_fail(git_grafts_parse(grafts, string, strlen(string)));
+}
+
+static void assert_graft_contains(git_grafts *grafts, const char *graft, size_t n, ...)
+{
+ git_commit_graft *commit;
+ git_oid oid;
+ va_list ap;
+ size_t i = 0;
+
+ cl_git_pass(git_oid__fromstr(&oid, graft, GIT_OID_SHA1));
+ cl_git_pass(git_grafts_get(&commit, grafts, &oid));
+ cl_assert_equal_oid(&commit->oid, &oid);
+ cl_assert_equal_i(commit->parents.size, n);
+
+ va_start(ap, n);
+ while (i < n) {
+ cl_git_pass(git_oid__fromstr(&oid, va_arg(ap, const char *), GIT_OID_SHA1));
+ cl_assert_equal_oid(&commit->parents.ptr[i], &oid);
+ i++;
+ }
+ va_end(ap);
+}
+
+void test_grafts_parse__single_oid(void)
+{
+ assert_parse_succeeds(grafts, OID1, 1);
+ assert_graft_contains(grafts, OID1, 0);
+}
+
+void test_grafts_parse__single_oid_with_newline(void)
+{
+ assert_parse_succeeds(grafts, OID1 "\n", 1);
+ assert_graft_contains(grafts, OID1, 0);
+}
+
+void test_grafts_parse__multiple_oids(void)
+{
+ assert_parse_succeeds(grafts, OID1 "\n" OID2 "\n" OID3, 3);
+ assert_graft_contains(grafts, OID1, 0);
+ assert_graft_contains(grafts, OID2, 0);
+ assert_graft_contains(grafts, OID3, 0);
+}
+
+void test_grafts_parse__same_oid(void)
+{
+ assert_parse_succeeds(grafts, OID1 "\n" OID1, 1);
+ assert_graft_contains(grafts, OID1, 0);
+}
+
+void test_grafts_parse__oid_with_parent(void)
+{
+ assert_parse_succeeds(grafts, OID1 " " OID2, 1);
+ assert_graft_contains(grafts, OID1, 1, OID2);
+}
+
+void test_grafts_parse__oid_with_parent_and_newline(void)
+{
+ assert_parse_succeeds(grafts, OID1 " " OID2 "\n", 1);
+ assert_graft_contains(grafts, OID1, 1, OID2);
+}
+
+void test_grafts_parse__oid_with_multiple_parents(void)
+{
+ assert_parse_succeeds(grafts, OID1 " " OID2 " " OID3 " " OID4 " " OID5, 1);
+ assert_graft_contains(grafts, OID1, 4, OID2, OID3, OID4, OID5);
+}
+
+void test_grafts_parse__multiple_oids_with_multiple_parents(void)
+{
+ assert_parse_succeeds(grafts,
+ OID1 " " OID2 " " OID3 " " OID4 " " OID5 "\n"
+ OID6 " " OID7 " " OID8 "\n" , 2);
+ assert_graft_contains(grafts, OID1, 4, OID2, OID3, OID4, OID5);
+ assert_graft_contains(grafts, OID6, 2, OID7, OID8);
+}
+
+void test_grafts_parse__multiple_spaces_fails(void)
+{
+ assert_parse_fails(grafts, OID1 " " OID2);
+}
+
+void test_grafts_parse__trailing_space_fails(void)
+{
+ assert_parse_fails(grafts, OID1 " " OID2 " ");
+}
+
+void test_grafts_parse__invalid_character_inbetween_fails(void)
+{
+ assert_parse_fails(grafts, OID1 " x " OID2);
+}
+
+void test_grafts_parse__truncated_oid_fails(void)
+{
+ assert_parse_fails(grafts, OID_TRUNCATED);
+}
+
+void test_grafts_parse__truncated_parent_fails(void)
+{
+ assert_parse_fails(grafts, OID1 " " OID_TRUNCATED);
+}
+
+void test_grafts_parse__invalid_oid_fails(void)
+{
+ assert_parse_fails(grafts, OID_NONHEX);
+}
+
+void test_grafts_parse__invalid_parent_fails(void)
+{
+ assert_parse_fails(grafts, OID1 " " OID_NONHEX);
+}
diff --git a/tests/libgit2/grafts/shallow.c b/tests/libgit2/grafts/shallow.c
new file mode 100644
index 00000000000..289d1b19105
--- /dev/null
+++ b/tests/libgit2/grafts/shallow.c
@@ -0,0 +1,134 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "grafts.h"
+#include "repository.h"
+
+static git_repository *g_repo;
+static git_oid g_shallow_oid;
+
+void test_grafts_shallow__initialize(void)
+{
+ cl_git_pass(git_oid__fromstr(&g_shallow_oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1));
+}
+
+void test_grafts_shallow__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_grafts_shallow__no_shallow_file(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+}
+
+void test_grafts_shallow__empty_shallow_file(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_mkfile("testrepo.git/shallow", "");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+}
+
+void test_grafts_shallow__shallow_repo(void)
+{
+ g_repo = cl_git_sandbox_init("shallow.git");
+ cl_assert_equal_i(1, git_repository_is_shallow(g_repo));
+}
+
+void test_grafts_shallow__clears_errors(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+ cl_assert_equal_i(GIT_ERROR_NONE, git_error_last()->klass);
+}
+
+void test_grafts_shallow__shallow_oids(void)
+{
+ git_commit_graft *graft;
+ git_grafts *grafts;
+
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_pass(git_repository_shallow_grafts__weakptr(&grafts, g_repo));
+ cl_assert_equal_i(1, git_grafts_size(grafts));
+ cl_git_pass(git_grafts_get(&graft, grafts, &g_shallow_oid));
+}
+
+void test_grafts_shallow__cache_clearing(void)
+{
+ git_commit_graft *graft;
+ git_grafts *grafts;
+ git_oid tmp_oid;
+
+ cl_git_pass(git_oid__fromstr(&tmp_oid, "0000000000000000000000000000000000000000", GIT_OID_SHA1));
+ g_repo = cl_git_sandbox_init("shallow.git");
+ cl_git_pass(git_repository_shallow_grafts__weakptr(&grafts, g_repo));
+
+ cl_assert_equal_i(1, git_grafts_size(grafts));
+ cl_git_pass(git_grafts_get(&graft, grafts, &g_shallow_oid));
+
+ cl_git_mkfile("shallow.git/shallow",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644\n"
+ "0000000000000000000000000000000000000000\n"
+ );
+
+ cl_git_pass(git_grafts_refresh(grafts));
+ cl_assert_equal_i(2, git_grafts_size(grafts));
+ cl_git_pass(git_grafts_get(&graft, grafts, &g_shallow_oid));
+ cl_git_pass(git_grafts_get(&graft, grafts, &tmp_oid));
+
+ cl_git_pass(p_unlink("shallow.git/shallow"));
+ cl_git_pass(git_grafts_refresh(grafts));
+ cl_assert_equal_i(0, git_grafts_size(grafts));
+}
+
+void test_grafts_shallow__errors_on_borked(void)
+{
+ git_grafts *grafts;
+
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_mkfile("shallow.git/shallow", "lolno");
+ cl_git_pass(git_repository_shallow_grafts__weakptr(&grafts, g_repo));
+ cl_git_fail(git_grafts_refresh(grafts));
+ cl_assert_equal_i(0, git_grafts_size(grafts));
+
+ cl_git_mkfile("shallow.git/shallow", "lolno\n");
+ cl_git_pass(git_repository_shallow_grafts__weakptr(&grafts, g_repo));
+ cl_git_fail(git_grafts_refresh(grafts));
+ cl_assert_equal_i(0, git_grafts_size(grafts));
+}
+
+void test_grafts_shallow__revwalk_behavior(void)
+{
+ git_revwalk *w;
+ git_oid oid_1, oid_2, oid_3;
+
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_pass(git_revwalk_new(&w, g_repo));
+ cl_git_pass(git_revwalk_push_head(w));
+
+ cl_git_pass(git_revwalk_next(&oid_1, w)); // a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ cl_git_pass(git_revwalk_next(&oid_2, w)); // be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+ cl_git_fail_with(GIT_ITEROVER, git_revwalk_next(&oid_3, w));
+
+ cl_assert_equal_s(git_oid_tostr_s(&oid_1), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_assert_equal_s(git_oid_tostr_s(&oid_2), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+
+ git_revwalk_free(w);
+}
+
+void test_grafts_shallow__grafted_object(void)
+{
+ git_commit *commit;
+
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &g_shallow_oid));
+
+ cl_assert_equal_i(0, git_commit_parentcount(commit));
+
+ git_commit_free(commit);
+}
diff --git a/tests/libgit2/graph/commitgraph.c b/tests/libgit2/graph/commitgraph.c
index 82f7f936f97..53869d61db0 100644
--- a/tests/libgit2/graph/commitgraph.c
+++ b/tests/libgit2/graph/commitgraph.c
@@ -16,7 +16,7 @@ void test_graph_commitgraph__parse(void)
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
cl_git_pass(git_str_joinpath(&commit_graph_path, git_repository_path(repo), "objects/info/commit-graph"));
- cl_git_pass(git_commit_graph_file_open(&file, git_str_cstr(&commit_graph_path)));
+ cl_git_pass(git_commit_graph_file_open(&file, git_str_cstr(&commit_graph_path), GIT_OID_SHA1));
cl_assert_equal_i(git_commit_graph_file_needs_refresh(file, git_str_cstr(&commit_graph_path)), 0);
cl_git_pass(git_oid__fromstr(&id, "5001298e0c09ad9c34e4249bc5801c75e9754fa5", GIT_OID_SHA1));
@@ -60,7 +60,7 @@ void test_graph_commitgraph__parse_octopus_merge(void)
cl_git_pass(git_repository_open(&repo, cl_fixture("merge-recursive/.gitted")));
cl_git_pass(git_str_joinpath(&commit_graph_path, git_repository_path(repo), "objects/info/commit-graph"));
- cl_git_pass(git_commit_graph_file_open(&file, git_str_cstr(&commit_graph_path)));
+ cl_git_pass(git_commit_graph_file_open(&file, git_str_cstr(&commit_graph_path), GIT_OID_SHA1));
cl_git_pass(git_oid__fromstr(&id, "d71c24b3b113fd1d1909998c5bfe33b86a65ee03", GIT_OID_SHA1));
cl_git_pass(git_commit_graph_entry_find(&e, file, &id, GIT_OID_SHA1_HEXSIZE));
@@ -103,7 +103,12 @@ void test_graph_commitgraph__writer(void)
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
cl_git_pass(git_str_joinpath(&path, git_repository_path(repo), "objects/info"));
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_commit_graph_writer_new(&w, git_str_cstr(&path), GIT_OID_SHA1));
+#else
cl_git_pass(git_commit_graph_writer_new(&w, git_str_cstr(&path)));
+#endif
/* This is equivalent to `git commit-graph write --reachable`. */
cl_git_pass(git_revwalk_new(&walk, repo));
@@ -135,7 +140,11 @@ void test_graph_commitgraph__validate(void)
cl_git_pass(git_str_joinpath(&objects_dir, git_repository_path(repo), "objects"));
/* git_commit_graph_open() calls git_commit_graph_validate() */
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_commit_graph_open(&cgraph, git_str_cstr(&objects_dir), GIT_OID_SHA1));
+#else
cl_git_pass(git_commit_graph_open(&cgraph, git_str_cstr(&objects_dir)));
+#endif
git_commit_graph_free(cgraph);
git_str_dispose(&objects_dir);
@@ -158,7 +167,11 @@ void test_graph_commitgraph__validate_corrupt(void)
cl_must_pass(p_close(fd));
/* git_commit_graph_open() calls git_commit_graph_validate() */
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_fail(git_commit_graph_open(&cgraph, cl_git_sandbox_path(1, "testrepo.git", "objects", NULL), GIT_OID_SHA1));
+#else
cl_git_fail(git_commit_graph_open(&cgraph, cl_git_sandbox_path(1, "testrepo.git", "objects", NULL)));
+#endif
git_commit_graph_free(cgraph);
git_repository_free(repo);
diff --git a/tests/libgit2/ignore/path.c b/tests/libgit2/ignore/path.c
index a574d1d799d..17f28bc5d89 100644
--- a/tests/libgit2/ignore/path.c
+++ b/tests/libgit2/ignore/path.c
@@ -286,14 +286,16 @@ void test_ignore_path__subdirectory_gitignore(void)
void test_ignore_path__expand_tilde_to_homedir(void)
{
+ git_str homefile = GIT_STR_INIT;
git_config *cfg;
assert_is_ignored(false, "example.global_with_tilde");
- cl_fake_home();
+ cl_fake_homedir(&homefile);
+ cl_git_pass(git_str_joinpath(&homefile, homefile.ptr, "globalexclude"));
/* construct fake home with fake global excludes */
- cl_git_mkfile("home/globalexclude", "# found me\n*.global_with_tilde\n");
+ cl_git_mkfile(homefile.ptr, "# found me\n*.global_with_tilde\n");
cl_git_pass(git_repository_config(&cfg, g_repo));
cl_git_pass(git_config_set_string(cfg, "core.excludesfile", "~/globalexclude"));
@@ -305,11 +307,13 @@ void test_ignore_path__expand_tilde_to_homedir(void)
cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
- cl_fake_home_cleanup(NULL);
+ cl_fake_homedir_cleanup(NULL);
git_attr_cache_flush(g_repo); /* must reset to pick up change */
assert_is_ignored(false, "example.global_with_tilde");
+
+ git_str_dispose(&homefile);
}
/* Ensure that the .gitignore in the subdirectory only affects
diff --git a/tests/libgit2/ignore/status.c b/tests/libgit2/ignore/status.c
index deb7175900e..a007774d7c0 100644
--- a/tests/libgit2/ignore/status.c
+++ b/tests/libgit2/ignore/status.c
@@ -363,6 +363,7 @@ void test_ignore_status__subdirectories_not_at_root(void)
void test_ignore_status__leading_slash_ignores(void)
{
+ git_str homedir = GIT_STR_INIT;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
static const char *paths_2[] = {
@@ -385,8 +386,9 @@ void test_ignore_status__leading_slash_ignores(void)
make_test_data(test_repo_1, test_files_1);
- cl_fake_home();
- cl_git_mkfile("home/.gitignore", "/ignore_me\n");
+ cl_fake_homedir(&homedir);
+ cl_git_pass(git_str_joinpath(&homedir, homedir.ptr, ".gitignore"));
+ cl_git_mkfile(homedir.ptr, "/ignore_me\n");
{
git_config *cfg;
cl_git_pass(git_repository_config(&cfg, g_repo));
@@ -412,6 +414,8 @@ void test_ignore_status__leading_slash_ignores(void)
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
cl_assert_equal_i(0, counts.wrong_status_flags_count);
cl_assert_equal_i(0, counts.wrong_sorted_path);
+
+ git_str_dispose(&homedir);
}
void test_ignore_status__multiple_leading_slash(void)
diff --git a/tests/libgit2/index/add.c b/tests/libgit2/index/add.c
index b0c3bd2b7ae..588a2ad148c 100644
--- a/tests/libgit2/index/add.c
+++ b/tests/libgit2/index/add.c
@@ -82,3 +82,27 @@ void test_index_add__invalid_entries_succeeds_by_default(void)
test_add_entry(true, valid_commit_id, GIT_FILEMODE_LINK);
}
+void test_index_add__two_slash_prefixed(void)
+{
+ git_index_entry one = {{0}}, two = {{0}};
+ const git_index_entry *result;
+ size_t orig_count;
+
+ orig_count = git_index_entrycount(g_index);
+
+ cl_git_pass(git_oid__fromstr(&one.id, "fa49b077972391ad58037050f2a75f74e3671e92", GIT_OID_SHA1));
+ one.path = "/a";
+ one.mode = GIT_FILEMODE_BLOB;
+
+ cl_git_pass(git_oid__fromstr(&two.id, "3697d64be941a53d4ae8f6a271e4e3fa56b022cc", GIT_OID_SHA1));
+ two.path = "/a";
+ two.mode = GIT_FILEMODE_BLOB;
+
+ cl_git_pass(git_index_add(g_index, &one));
+ cl_git_pass(git_index_add(g_index, &two));
+
+ cl_assert_equal_i(orig_count + 1, git_index_entrycount(g_index));
+
+ cl_assert(result = git_index_get_bypath(g_index, "/a", 0));
+ cl_assert_equal_oid(&two.id, &result->id);
+}
diff --git a/tests/libgit2/index/addall.c b/tests/libgit2/index/addall.c
index 6f95f6386f4..e76b6e81d89 100644
--- a/tests/libgit2/index/addall.c
+++ b/tests/libgit2/index/addall.c
@@ -441,6 +441,52 @@ void test_index_addall__callback_filtering(void)
git_index_free(index);
}
+void test_index_addall__handles_ignored_files_in_directory(void)
+{
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init_new(TEST_DIR);
+
+ cl_git_mkfile(TEST_DIR "/file.foo", "a file");
+ cl_git_mkfile(TEST_DIR "/file.bar", "another file");
+ cl_must_pass(p_mkdir(TEST_DIR "/folder", 0777));
+ cl_git_mkfile(TEST_DIR "/folder/asdf", "yet another file");
+
+ cl_git_mkfile(TEST_DIR "/.gitignore", "folder/\n");
+
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1, 0);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL));
+
+ check_status(g_repo, 3, 0, 0, 0, 0, 0, 1, 0);
+
+ git_index_free(index);
+}
+
+void test_index_addall__force_adds_ignored_directories(void)
+{
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init_new(TEST_DIR);
+
+ cl_git_mkfile(TEST_DIR "/file.foo", "a file");
+ cl_git_mkfile(TEST_DIR "/file.bar", "another file");
+ cl_must_pass(p_mkdir(TEST_DIR "/folder", 0777));
+ cl_git_mkfile(TEST_DIR "/folder/asdf", "yet another file");
+
+ cl_git_mkfile(TEST_DIR "/.gitignore", "folder/\n");
+
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1, 0);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_all(index, NULL, GIT_INDEX_ADD_FORCE, NULL, NULL));
+
+ check_status(g_repo, 4, 0, 0, 0, 0, 0, 0, 0);
+
+ git_index_free(index);
+}
+
void test_index_addall__adds_conflicts(void)
{
git_index *index;
diff --git a/tests/libgit2/index/cache.c b/tests/libgit2/index/cache.c
index 7576331b014..77f19a50b29 100644
--- a/tests/libgit2/index/cache.c
+++ b/tests/libgit2/index/cache.c
@@ -24,7 +24,7 @@ void test_index_cache__write_extension_at_root(void)
const char *tree_id_str = "45dd856fdd4d89b884c340ba0e047752d9b085d6";
const char *index_file = "index-tree";
- cl_git_pass(git_index_open(&index, index_file));
+ cl_git_pass(git_index__open(&index, index_file, GIT_OID_SHA1));
cl_assert(index->tree == NULL);
cl_git_pass(git_oid__fromstr(&id, tree_id_str, GIT_OID_SHA1));
cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
@@ -35,7 +35,7 @@ void test_index_cache__write_extension_at_root(void)
cl_git_pass(git_index_write(index));
git_index_free(index);
- cl_git_pass(git_index_open(&index, index_file));
+ cl_git_pass(git_index__open(&index, index_file, GIT_OID_SHA1));
cl_assert(index->tree);
cl_assert_equal_i(git_index_entrycount(index), index->tree->entry_count);
@@ -56,7 +56,7 @@ void test_index_cache__write_extension_invalidated_root(void)
const char *index_file = "index-tree-invalidated";
git_index_entry entry;
- cl_git_pass(git_index_open(&index, index_file));
+ cl_git_pass(git_index__open(&index, index_file, GIT_OID_SHA1));
cl_assert(index->tree == NULL);
cl_git_pass(git_oid__fromstr(&id, tree_id_str, GIT_OID_SHA1));
cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
@@ -77,7 +77,7 @@ void test_index_cache__write_extension_invalidated_root(void)
cl_git_pass(git_index_write(index));
git_index_free(index);
- cl_git_pass(git_index_open(&index, index_file));
+ cl_git_pass(git_index__open(&index, index_file, GIT_OID_SHA1));
cl_assert(index->tree);
cl_assert_equal_i(-1, index->tree->entry_count);
@@ -96,7 +96,7 @@ void test_index_cache__read_tree_no_children(void)
git_tree *tree;
git_oid id;
- cl_git_pass(git_index_new(&index));
+ cl_git_pass(git_index__new(&index, GIT_OID_SHA1));
cl_assert(index->tree == NULL);
cl_git_pass(git_oid__fromstr(&id, "45dd856fdd4d89b884c340ba0e047752d9b085d6", GIT_OID_SHA1));
cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
diff --git a/tests/libgit2/index/inmemory.c b/tests/libgit2/index/inmemory.c
index 38e91e0fd94..39374af67bc 100644
--- a/tests/libgit2/index/inmemory.c
+++ b/tests/libgit2/index/inmemory.c
@@ -1,10 +1,11 @@
#include "clar_libgit2.h"
+#include "index.h"
void test_index_inmemory__can_create_an_inmemory_index(void)
{
git_index *index;
- cl_git_pass(git_index_new(&index));
+ cl_git_pass(git_index__new(&index, GIT_OID_SHA1));
cl_assert_equal_i(0, (int)git_index_entrycount(index));
git_index_free(index);
@@ -14,7 +15,7 @@ void test_index_inmemory__cannot_add_bypath_to_an_inmemory_index(void)
{
git_index *index;
- cl_git_pass(git_index_new(&index));
+ cl_git_pass(git_index__new(&index, GIT_OID_SHA1));
cl_assert_equal_i(GIT_ERROR, git_index_add_bypath(index, "test.txt"));
diff --git a/tests/libgit2/index/racy.c b/tests/libgit2/index/racy.c
index 07b3b73d429..a1d6f9c6ec0 100644
--- a/tests/libgit2/index/racy.c
+++ b/tests/libgit2/index/racy.c
@@ -287,7 +287,7 @@ void test_index_racy__read_index_smudges(void)
setup_race();
cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(git_index_new(&newindex));
+ cl_git_pass(git_index__new(&newindex, GIT_OID_SHA1));
cl_git_pass(git_index_read_index(newindex, index));
cl_assert(entry = git_index_get_bypath(newindex, "A", 0));
@@ -305,7 +305,7 @@ void test_index_racy__read_index_clears_uptodate_bit(void)
setup_uptodate_files();
cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(git_index_new(&newindex));
+ cl_git_pass(git_index__new(&newindex, GIT_OID_SHA1));
cl_git_pass(git_index_read_index(newindex, index));
/* ensure that files brought in from the other index are not uptodate */
diff --git a/tests/libgit2/index/read_index.c b/tests/libgit2/index/read_index.c
index ac03cf1773e..9c80be299f1 100644
--- a/tests/libgit2/index/read_index.c
+++ b/tests/libgit2/index/read_index.c
@@ -42,7 +42,7 @@ void test_index_read_index__maintains_stat_cache(void)
/* read-tree, then read index */
git_tree_lookup(&tree, _repo, &index_id);
- cl_git_pass(git_index_new(&new_index));
+ cl_git_pass(git_index__new(&new_index, GIT_OID_SHA1));
cl_git_pass(git_index_read_tree(new_index, tree));
git_tree_free(tree);
@@ -81,7 +81,7 @@ static bool roundtrip_with_read_index(const char *tree_idstr)
cl_git_pass(git_oid__fromstr(&tree_id, tree_idstr, GIT_OID_SHA1));
cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id));
- cl_git_pass(git_index_new(&tree_index));
+ cl_git_pass(git_index__new(&tree_index, GIT_OID_SHA1));
cl_git_pass(git_index_read_tree(tree_index, tree));
cl_git_pass(git_index_read_index(_index, tree_index));
cl_git_pass(git_index_write_tree(&new_tree_id, _index));
@@ -113,12 +113,12 @@ void test_index_read_index__read_and_writes(void)
cl_git_pass(git_oid__fromstr(&tree_id, "ae90f12eea699729ed24555e40b9fd669da12a12", GIT_OID_SHA1));
cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id));
- cl_git_pass(git_index_new(&tree_index));
+ cl_git_pass(git_index__new(&tree_index, GIT_OID_SHA1));
cl_git_pass(git_index_read_tree(tree_index, tree));
cl_git_pass(git_index_read_index(_index, tree_index));
cl_git_pass(git_index_write(_index));
- cl_git_pass(git_index_open(&new_index, git_index_path(_index)));
+ cl_git_pass(git_index__open(&new_index, git_index_path(_index), GIT_OID_SHA1));
cl_git_pass(git_index_write_tree_to(&new_tree_id, new_index, _repo));
cl_assert_equal_oid(&tree_id, &new_tree_id);
@@ -174,8 +174,8 @@ void test_index_read_index__handles_conflicts(void)
cl_git_pass(git_oid__fromstr(&tree_id, "ae90f12eea699729ed24555e40b9fd669da12a12", GIT_OID_SHA1));
cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id));
- cl_git_pass(git_index_new(&index));
- cl_git_pass(git_index_new(&new_index));
+ cl_git_pass(git_index__new(&index, GIT_OID_SHA1));
+ cl_git_pass(git_index__new(&new_index, GIT_OID_SHA1));
cl_git_pass(git_index_read_tree(index, tree));
cl_git_pass(git_index_read_tree(new_index, tree));
diff --git a/tests/libgit2/index/tests.c b/tests/libgit2/index/tests.c
index da3ff6dd7b5..b48eb0fc170 100644
--- a/tests/libgit2/index/tests.c
+++ b/tests/libgit2/index/tests.c
@@ -81,7 +81,7 @@ void test_index_tests__empty_index(void)
{
git_index *index;
- cl_git_pass(git_index_open(&index, "in-memory-index"));
+ cl_git_pass(git_index__open(&index, "in-memory-index", GIT_OID_SHA1));
cl_assert(index->on_disk == 0);
cl_assert(git_index_entrycount(index) == 0);
@@ -96,7 +96,7 @@ void test_index_tests__default_test_index(void)
unsigned int i;
git_index_entry **entries;
- cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
cl_assert(index->on_disk);
cl_assert(git_index_entrycount(index) == index_entry_count);
@@ -119,7 +119,7 @@ void test_index_tests__gitgit_index(void)
{
git_index *index;
- cl_git_pass(git_index_open(&index, TEST_INDEX2_PATH));
+ cl_git_pass(git_index__open(&index, TEST_INDEX2_PATH, GIT_OID_SHA1));
cl_assert(index->on_disk);
cl_assert(git_index_entrycount(index) == index_entry_count_2);
@@ -134,7 +134,7 @@ void test_index_tests__find_in_existing(void)
git_index *index;
unsigned int i;
- cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
size_t idx;
@@ -151,7 +151,7 @@ void test_index_tests__find_in_empty(void)
git_index *index;
unsigned int i;
- cl_git_pass(git_index_open(&index, "fake-index"));
+ cl_git_pass(git_index__open(&index, "fake-index", GIT_OID_SHA1));
for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
cl_assert(GIT_ENOTFOUND == git_index_find(NULL, index, test_entries[i].path));
@@ -166,7 +166,7 @@ void test_index_tests__find_prefix(void)
const git_index_entry *entry;
size_t pos;
- cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
cl_git_pass(git_index_find_prefix(&pos, index, "src"));
entry = git_index_get_byindex(index, pos);
@@ -187,7 +187,7 @@ void test_index_tests__write(void)
copy_file(TEST_INDEXBIG_PATH, "index_rewrite");
- cl_git_pass(git_index_open(&index, "index_rewrite"));
+ cl_git_pass(git_index__open(&index, "index_rewrite", GIT_OID_SHA1));
cl_assert(index->on_disk);
cl_git_pass(git_index_write(index));
@@ -218,7 +218,7 @@ void test_index_tests__sort1(void)
/* sort the entries in an empty index */
git_index *index;
- cl_git_pass(git_index_open(&index, "fake-index"));
+ cl_git_pass(git_index__open(&index, "fake-index", GIT_OID_SHA1));
/* FIXME: this test is slightly dumb */
cl_assert(git_vector_is_sorted(&index->entries));
@@ -703,7 +703,7 @@ void test_index_tests__write_tree_invalid_unowned_index(void)
git_index_entry entry = {{0}};
git_oid tree_id;
- cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
cl_git_pass(git_oid__fromstr(&entry.id, "8312e0a89a9cbab77c732b6bc39b51a783e3a318", GIT_OID_SHA1));
entry.path = "foo";
@@ -966,7 +966,7 @@ void test_index_tests__reload_from_disk(void)
cl_git_pass(git_repository_index(&write_index, repo));
cl_assert_equal_i(false, write_index->on_disk);
- cl_git_pass(git_index_open(&read_index, write_index->index_file_path));
+ cl_git_pass(git_index__open(&read_index, write_index->index_file_path, GIT_OID_SHA1));
cl_assert_equal_i(false, read_index->on_disk);
/* Stage two new files against the write_index */
@@ -1004,7 +1004,7 @@ void test_index_tests__corrupted_extension(void)
{
git_index *index;
- cl_git_fail_with(git_index_open(&index, TEST_INDEXBAD_PATH), GIT_ERROR);
+ cl_git_fail_with(git_index__open(&index, TEST_INDEXBAD_PATH, GIT_OID_SHA1), GIT_ERROR);
}
void test_index_tests__reload_while_ignoring_case(void)
@@ -1012,7 +1012,7 @@ void test_index_tests__reload_while_ignoring_case(void)
git_index *index;
unsigned int caps;
- cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
cl_git_pass(git_vector_verify_sorted(&index->entries));
caps = git_index_caps(index);
@@ -1037,7 +1037,7 @@ void test_index_tests__change_icase_on_instance(void)
unsigned int caps;
const git_index_entry *e;
- cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
cl_git_pass(git_vector_verify_sorted(&index->entries));
caps = git_index_caps(index);
@@ -1093,7 +1093,7 @@ void test_index_tests__can_iterate(void)
size_t i, iterator_idx = 0, found = 0;
int ret;
- cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
cl_git_pass(git_index_iterator_new(&iterator, index));
cl_assert(git_vector_is_sorted(&iterator->snap));
@@ -1136,7 +1136,7 @@ void test_index_tests__can_modify_while_iterating(void)
size_t expected = 0, seen = 0;
int ret;
- cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
cl_git_pass(git_index_iterator_new(&iterator, index));
expected = git_index_entrycount(index);
diff --git a/tests/libgit2/index/tests256.c b/tests/libgit2/index/tests256.c
new file mode 100644
index 00000000000..fed8bfb9358
--- /dev/null
+++ b/tests/libgit2/index/tests256.c
@@ -0,0 +1,1169 @@
+#include "clar_libgit2.h"
+#include "index.h"
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+
+static const size_t index_entry_count = 4344;
+#define TEST_INDEX_PATH cl_fixture("git-sha256.index")
+
+static git_repository_init_options repo_init_opts =
+ GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+/* Suite data */
+struct test_entry {
+ size_t index;
+ char path[128];
+ off64_t file_size;
+ git_time_t mtime;
+};
+
+static struct test_entry test_entries[] = {
+ { 892, "Makefile", 120084, 0x642c3a6e },
+ { 1542, "git.c", 27432, 0x642c3a6e },
+ { 1737, "perl/Git.pm", 48084, 0x642c3a6e },
+ { 1961, "t/Makefile", 4711, 0x642c3a6e },
+ { 4343, "zlib.c", 6271, 0x642c3a6f }
+};
+
+/* Helpers */
+static void copy_file(const char *src, const char *dst)
+{
+ git_str source_buf = GIT_STR_INIT;
+ git_file dst_fd;
+
+ cl_git_pass(git_futils_readbuffer(&source_buf, src));
+
+ dst_fd = git_futils_creat_withpath(dst, 0777, 0666); /* -V536 */
+ if (dst_fd < 0)
+ goto cleanup;
+
+ cl_git_pass(p_write(dst_fd, source_buf.ptr, source_buf.size));
+
+cleanup:
+ git_str_dispose(&source_buf);
+ p_close(dst_fd);
+}
+
+static void files_are_equal(const char *a, const char *b)
+{
+ git_str buf_a = GIT_STR_INIT;
+ git_str buf_b = GIT_STR_INIT;
+
+ if (git_futils_readbuffer(&buf_a, a) < 0)
+ cl_assert(0);
+
+ if (git_futils_readbuffer(&buf_b, b) < 0) {
+ git_str_dispose(&buf_a);
+ cl_assert(0);
+ }
+
+ cl_assert_equal_sz(buf_a.size, buf_b.size);
+ cl_assert(!memcmp(buf_a.ptr, buf_b.ptr, buf_a.size));
+
+ git_str_dispose(&buf_a);
+ git_str_dispose(&buf_b);
+}
+
+#endif
+
+/* Fixture setup and teardown */
+void test_index_tests256__initialize(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ repo_init_opts.flags |= GIT_REPOSITORY_INIT_MKPATH;
+ repo_init_opts.oid_type = GIT_OID_SHA256;
+#endif
+}
+
+void test_index_tests256__cleanup(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, 0));
+#endif
+}
+
+void test_index_tests256__empty_index(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+
+ cl_git_pass(git_index__open(&index, "in-memory-index", GIT_OID_SHA256));
+ cl_assert(index->on_disk == 0);
+
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(git_vector_is_sorted(&index->entries));
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__default_test_index(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ unsigned int i;
+ git_index_entry **entries;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+ cl_assert(index->on_disk);
+
+ cl_assert_equal_sz(git_index_entrycount(index), index_entry_count);
+ cl_assert(git_vector_is_sorted(&index->entries));
+
+ entries = (git_index_entry **)index->entries.contents;
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ git_index_entry *e = entries[test_entries[i].index];
+
+ cl_assert_equal_s(e->path, test_entries[i].path);
+ cl_assert_equal_i(e->mtime.seconds, test_entries[i].mtime);
+ cl_assert_equal_i(e->file_size, test_entries[i].file_size);
+ }
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__find_in_existing(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ unsigned int i;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ size_t idx;
+
+ cl_assert(!git_index_find(&idx, index, test_entries[i].path));
+ cl_assert(idx == test_entries[i].index);
+ }
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__find_in_empty(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ unsigned int i;
+
+ cl_git_pass(git_index__open(&index, "fake-index", GIT_OID_SHA256));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ cl_assert(GIT_ENOTFOUND == git_index_find(NULL, index, test_entries[i].path));
+ }
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__find_prefix(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ const git_index_entry *entry;
+ size_t pos;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+
+ cl_git_pass(git_index_find_prefix(&pos, index, "Documentation"));
+ entry = git_index_get_byindex(index, pos);
+ cl_assert(git__strcmp(entry->path, "Documentation/.gitattributes") == 0);
+
+ cl_git_pass(git_index_find_prefix(&pos, index, "contrib/RE"));
+ entry = git_index_get_byindex(index, pos);
+ cl_assert(git__strcmp(entry->path, "contrib/README") == 0);
+
+ cl_assert(GIT_ENOTFOUND == git_index_find_prefix(NULL, index, "blah"));
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__write(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+
+ copy_file(TEST_INDEX_PATH, "index_rewrite");
+
+ cl_git_pass(git_index__open(&index, "index_rewrite", GIT_OID_SHA256));
+ cl_assert(index->on_disk);
+
+ cl_git_pass(git_index_write(index));
+ files_are_equal(TEST_INDEX_PATH, "index_rewrite");
+
+ git_index_free(index);
+
+ p_unlink("index_rewrite");
+#endif
+}
+
+void test_index_tests256__sort1(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ /* sort the entries in an empty index */
+ git_index *index;
+
+ cl_git_pass(git_index__open(&index, "fake-index", GIT_OID_SHA256));
+
+ /* FIXME: this test is slightly dumb */
+ cl_assert(git_vector_is_sorted(&index->entries));
+
+ git_index_free(index);
+#endif
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static void cleanup_myrepo(void *opaque)
+{
+ GIT_UNUSED(opaque);
+ cl_fixture_cleanup("myrepo");
+}
+#endif
+
+void test_index_tests256__add(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_repository *repo;
+ const git_index_entry *entry;
+ git_oid id1;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ /* Initialize a new repository */
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+
+ /* Ensure we're the only guy in the room */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ /* Create a new file in the working directory */
+ cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
+ cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0, 0666));
+ cl_git_pass(git_filebuf_write(&file, "hey there\n", 10));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ echo "hey there" | git hash-object --stdin
+ */
+ cl_git_pass(git_oid__fromstr(&id1, "aea29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5", GIT_OID_SHA256));
+
+ /* Add the new file to the index */
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ /* Wow... it worked! */
+ cl_assert(git_index_entrycount(index) == 1);
+ entry = git_index_get_byindex(index, 0);
+
+ /* And the built-in hashing mechanism worked as expected */
+ cl_assert_equal_oid(&id1, &entry->id);
+
+ /* Test access by path instead of index */
+ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ cl_assert_equal_oid(&id1, &entry->id);
+
+ git_index_free(index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__add_frombuffer(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ git_repository *repo;
+ git_index_entry entry;
+ const git_index_entry *returned_entry;
+
+ git_oid id1;
+ git_blob *blob;
+
+ const char *content = "hey there\n";
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ /* Initialize a new repository */
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+
+ /* Ensure we're the only guy in the room */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ echo "hey there" | git hash-object --stdin
+ */
+ cl_git_pass(git_oid__fromstr(&id1, "aea29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5", GIT_OID_SHA256));
+
+ /* Add the new file to the index */
+ memset(&entry, 0x0, sizeof(git_index_entry));
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry,
+ content, strlen(content)));
+
+ /* Wow... it worked! */
+ cl_assert(git_index_entrycount(index) == 1);
+ returned_entry = git_index_get_byindex(index, 0);
+
+ /* And the built-in hashing mechanism worked as expected */
+ cl_assert_equal_oid(&id1, &returned_entry->id);
+ /* And mode is the one asked */
+ cl_assert_equal_i(GIT_FILEMODE_BLOB, returned_entry->mode);
+
+ /* Test access by path instead of index */
+ cl_assert((returned_entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ cl_assert_equal_oid(&id1, &returned_entry->id);
+
+ /* Test the blob is in the repository */
+ cl_git_pass(git_blob_lookup(&blob, repo, &id1));
+ cl_assert_equal_s(
+ content, git_blob_rawcontent(blob));
+ git_blob_free(blob);
+
+ git_index_free(index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__dirty_and_clean(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+ git_index_entry entry = {{0}};
+
+ /* Index is not dirty after opening */
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(!git_index_is_dirty(index));
+
+ /* Index is dirty after adding an entry */
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
+ cl_assert(git_index_entrycount(index) == 1);
+ cl_assert(git_index_is_dirty(index));
+
+ /* Index is not dirty after write */
+ cl_git_pass(git_index_write(index));
+ cl_assert(!git_index_is_dirty(index));
+
+ /* Index is dirty after removing an entry */
+ cl_git_pass(git_index_remove_bypath(index, "test.txt"));
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(git_index_is_dirty(index));
+
+ /* Index is not dirty after write */
+ cl_git_pass(git_index_write(index));
+ cl_assert(!git_index_is_dirty(index));
+
+ /* Index remains not dirty after read */
+ cl_git_pass(git_index_read(index, 0));
+ cl_assert(!git_index_is_dirty(index));
+
+ /* Index is dirty when we do an unforced read with dirty content */
+ cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
+ cl_assert(git_index_entrycount(index) == 1);
+ cl_assert(git_index_is_dirty(index));
+
+ cl_git_pass(git_index_read(index, 0));
+ cl_assert(git_index_is_dirty(index));
+
+ /* Index is clean when we force a read with dirty content */
+ cl_git_pass(git_index_read(index, 1));
+ cl_assert(!git_index_is_dirty(index));
+
+ git_index_free(index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__dirty_fails_optionally(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+ git_index_entry entry = {{0}};
+
+ /* Index is not dirty after opening */
+ repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /* Index is dirty after adding an entry */
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
+ cl_assert(git_index_is_dirty(index));
+
+ cl_git_pass(git_checkout_head(repo, NULL));
+
+ /* Index is dirty (again) after adding an entry */
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
+ cl_assert(git_index_is_dirty(index));
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, 1));
+ cl_git_fail_with(GIT_EINDEXDIRTY, git_checkout_head(repo, NULL));
+
+ git_index_free(index);
+ cl_git_sandbox_cleanup();
+#endif
+}
+
+void test_index_tests256__add_frombuffer_reset_entry(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ git_repository *repo;
+ git_index_entry entry;
+ const git_index_entry *returned_entry;
+ git_filebuf file = GIT_FILEBUF_INIT;
+
+ git_oid id1;
+ git_blob *blob;
+ const char *old_content = "here\n";
+ const char *content = "hey there\n";
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ /* Initialize a new repository */
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
+ cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0, 0666));
+ cl_git_pass(git_filebuf_write(&file, old_content, strlen(old_content)));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ echo "hey there" | git hash-object --stdin
+ */
+ cl_git_pass(git_oid__fromstr(&id1, "aea29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5", GIT_OID_SHA256));
+
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ /* Add the new file to the index */
+ memset(&entry, 0x0, sizeof(git_index_entry));
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry,
+ content, strlen(content)));
+
+ /* Wow... it worked! */
+ cl_assert(git_index_entrycount(index) == 1);
+ returned_entry = git_index_get_byindex(index, 0);
+
+ /* And the built-in hashing mechanism worked as expected */
+ cl_assert_equal_oid(&id1, &returned_entry->id);
+ /* And mode is the one asked */
+ cl_assert_equal_i(GIT_FILEMODE_BLOB, returned_entry->mode);
+
+ /* Test access by path instead of index */
+ cl_assert((returned_entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ cl_assert_equal_oid(&id1, &returned_entry->id);
+ cl_assert_equal_i(0, returned_entry->dev);
+ cl_assert_equal_i(0, returned_entry->ino);
+ cl_assert_equal_i(0, returned_entry->uid);
+ cl_assert_equal_i(0, returned_entry->uid);
+ cl_assert_equal_i(10, returned_entry->file_size);
+
+ /* Test the blob is in the repository */
+ cl_git_pass(git_blob_lookup(&blob, repo, &id1));
+ cl_assert_equal_s(content, git_blob_rawcontent(blob));
+ git_blob_free(blob);
+
+ git_index_free(index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__add_bypath_to_a_bare_repository_returns_EBAREPO(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *bare_repo;
+ git_index *index;
+
+ cl_git_pass(git_repository_open(&bare_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_index(&index, bare_repo));
+
+ cl_assert_equal_i(GIT_EBAREREPO, git_index_add_bypath(index, "test.txt"));
+
+ git_index_free(index);
+ git_repository_free(bare_repo);
+#endif
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static void assert_add_bypath_fails(git_repository *repo, const char *fn)
+{
+ git_index *index;
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ git_str_joinpath(&path, "./invalid", fn);
+
+ cl_git_mkfile(path.ptr, NULL);
+ cl_git_fail(git_index_add_bypath(index, fn));
+ cl_must_pass(p_unlink(path.ptr));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ git_str_dispose(&path);
+ git_index_free(index);
+}
+#endif
+
+/* Test that writing an invalid filename fails */
+void test_index_tests256__cannot_add_invalid_filename(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+
+ cl_must_pass(p_mkdir("invalid", 0700));
+ cl_git_pass(git_repository_init_ext(&repo, "./invalid", &repo_init_opts));
+ cl_must_pass(p_mkdir("./invalid/subdir", 0777));
+
+ /* cl_git_mkfile() needs the dir to exist */
+ if (!git_fs_path_exists("./invalid/.GIT"))
+ cl_must_pass(p_mkdir("./invalid/.GIT", 0777));
+ if (!git_fs_path_exists("./invalid/.GiT"))
+ cl_must_pass(p_mkdir("./invalid/.GiT", 0777));
+
+ assert_add_bypath_fails(repo, ".git/hello");
+ assert_add_bypath_fails(repo, ".GIT/hello");
+ assert_add_bypath_fails(repo, ".GiT/hello");
+ assert_add_bypath_fails(repo, "./.git/hello");
+ assert_add_bypath_fails(repo, "./foo");
+ assert_add_bypath_fails(repo, "./bar");
+ assert_add_bypath_fails(repo, "subdir/../bar");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+#endif
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static void assert_add_fails(git_repository *repo, const char *fn)
+{
+ git_index *index;
+ git_str path = GIT_STR_INIT;
+ git_index_entry entry = {{0}};
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ entry.path = fn;
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid__fromstr(&entry.id, "aea29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5", GIT_OID_SHA256));
+
+ cl_git_fail(git_index_add(index, &entry));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ git_str_dispose(&path);
+ git_index_free(index);
+}
+#endif
+
+/*
+ * Test that writing an invalid filename fails on filesystem
+ * specific protected names
+ */
+void test_index_tests256__cannot_add_protected_invalid_filename(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+
+ cl_must_pass(p_mkdir("invalid", 0700));
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+
+ /* add a file to the repository so we can reference it later */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_mkfile("invalid/dummy.txt", "");
+ cl_git_pass(git_index_add_bypath(index, "dummy.txt"));
+ cl_must_pass(p_unlink("invalid/dummy.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "dummy.txt"));
+ git_index_free(index);
+
+ cl_repo_set_bool(repo, "core.protectHFS", true);
+ cl_repo_set_bool(repo, "core.protectNTFS", true);
+
+ assert_add_fails(repo, ".git./hello");
+ assert_add_fails(repo, ".git\xe2\x80\xad/hello");
+ assert_add_fails(repo, "git~1/hello");
+ assert_add_fails(repo, ".git\xe2\x81\xaf/hello");
+ assert_add_fails(repo, ".git::$INDEX_ALLOCATION/dummy-file");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+#endif
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static void replace_char(char *str, char in, char out)
+{
+ char *c = str;
+
+ while (*c++)
+ if (*c == in)
+ *c = out;
+}
+
+static void assert_write_fails(git_repository *repo, const char *fn_orig)
+{
+ git_index *index;
+ git_oid expected;
+ const git_index_entry *entry;
+ git_str path = GIT_STR_INIT;
+ char *fn;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ /*
+ * Sneak a valid path into the index, we'll update it
+ * to an invalid path when we try to write the index.
+ */
+ fn = git__strdup(fn_orig);
+ replace_char(fn, '/', '_');
+ replace_char(fn, ':', '!');
+
+ git_str_joinpath(&path, "./invalid", fn);
+
+ cl_git_mkfile(path.ptr, NULL);
+
+ cl_git_pass(git_index_add_bypath(index, fn));
+
+ cl_assert(entry = git_index_get_bypath(index, fn, 0));
+
+ /* kids, don't try this at home */
+ replace_char((char *)entry->path, '_', '/');
+ replace_char((char *)entry->path, '!', ':');
+
+ /* write-tree */
+ cl_git_fail(git_index_write_tree(&expected, index));
+
+ p_unlink(path.ptr);
+
+ cl_git_pass(git_index_remove_all(index, NULL, NULL, NULL));
+ git_str_dispose(&path);
+ git_index_free(index);
+ git__free(fn);
+}
+#endif
+
+void test_index_tests256__write_tree_invalid_unowned_index(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *idx;
+ git_repository *repo;
+ git_index_entry entry = {{0}};
+ git_oid tree_id;
+
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA256));
+
+ // TODO: this one is failing
+ cl_git_pass(git_oid__fromstr(&entry.id, "a8c2e0a89a9cbab77c732b6bc39b51a783e3a318a847f46cba7614cac9814291", GIT_OID_SHA256));
+ entry.path = "foo";
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(idx, &entry));
+
+ cl_git_pass(git_repository_init_ext(&repo, "./invalid-id", &repo_init_opts));
+
+ cl_git_fail(git_index_write_tree_to(&tree_id, idx, repo));
+
+ git_index_free(idx);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid-id");
+#endif
+}
+
+/* Test that writing an invalid filename fails */
+void test_index_tests256__write_invalid_filename(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+
+ p_mkdir("invalid", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+
+ assert_write_fails(repo, ".git/hello");
+ assert_write_fails(repo, ".GIT/hello");
+ assert_write_fails(repo, ".GiT/hello");
+ assert_write_fails(repo, "./.git/hello");
+ assert_write_fails(repo, "./foo");
+ assert_write_fails(repo, "./bar");
+ assert_write_fails(repo, "foo/../bar");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+#endif
+}
+
+void test_index_tests256__honors_protect_filesystems(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+
+ p_mkdir("invalid", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+
+ cl_repo_set_bool(repo, "core.protectHFS", true);
+ cl_repo_set_bool(repo, "core.protectNTFS", true);
+
+ assert_write_fails(repo, ".git./hello");
+ assert_write_fails(repo, ".git\xe2\x80\xad/hello");
+ assert_write_fails(repo, "git~1/hello");
+ assert_write_fails(repo, ".git\xe2\x81\xaf/hello");
+ assert_write_fails(repo, ".git::$INDEX_ALLOCATION/dummy-file");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+#endif
+}
+
+void test_index_tests256__protectntfs_on_by_default(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+
+ p_mkdir("invalid", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+ assert_write_fails(repo, ".git./hello");
+ assert_write_fails(repo, "git~1/hello");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+#endif
+}
+
+void test_index_tests256__can_disable_protectntfs(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+
+ cl_must_pass(p_mkdir("valid", 0700));
+ cl_git_rewritefile("valid/git~1", "steal the shortname");
+
+ cl_git_pass(git_repository_init(&repo, "./valid", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_repo_set_bool(repo, "core.protectNTFS", false);
+
+ cl_git_pass(git_index_add_bypath(index, "git~1"));
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("valid");
+#endif
+}
+
+void test_index_tests256__remove_entry(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+
+ p_mkdir("index_test", 0770);
+
+ cl_git_pass(git_repository_init(&repo, "index_test", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ cl_git_mkfile("index_test/hello", NULL);
+ cl_git_pass(git_index_add_bypath(index, "hello"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert(git_index_entrycount(index) == 1);
+ cl_assert(git_index_get_bypath(index, "hello", 0) != NULL);
+
+ cl_git_pass(git_index_remove(index, "hello", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(git_index_get_bypath(index, "hello", 0) == NULL);
+
+ git_index_free(index);
+ git_repository_free(repo);
+ cl_fixture_cleanup("index_test");
+#endif
+}
+
+void test_index_tests256__remove_directory(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+
+ p_mkdir("index_test", 0770);
+
+ cl_git_pass(git_repository_init(&repo, "index_test", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert_equal_i(0, (int)git_index_entrycount(index));
+
+ p_mkdir("index_test/a", 0770);
+ cl_git_mkfile("index_test/a/1.txt", NULL);
+ cl_git_mkfile("index_test/a/2.txt", NULL);
+ cl_git_mkfile("index_test/a/3.txt", NULL);
+ cl_git_mkfile("index_test/b.txt", NULL);
+
+ cl_git_pass(git_index_add_bypath(index, "a/1.txt"));
+ cl_git_pass(git_index_add_bypath(index, "a/2.txt"));
+ cl_git_pass(git_index_add_bypath(index, "a/3.txt"));
+ cl_git_pass(git_index_add_bypath(index, "b.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(4, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ cl_git_pass(git_index_remove(index, "a/1.txt", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(3, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ cl_git_pass(git_index_remove_directory(index, "a", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(1, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ git_index_free(index);
+ git_repository_free(repo);
+ cl_fixture_cleanup("index_test");
+#endif
+}
+
+void test_index_tests256__preserves_case(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+ const git_index_entry *entry;
+ int index_caps;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ index_caps = git_index_caps(index);
+
+ cl_git_rewritefile("myrepo/test.txt", "hey there\n");
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ cl_git_pass(p_rename("myrepo/test.txt", "myrepo/TEST.txt"));
+ cl_git_rewritefile("myrepo/TEST.txt", "hello again\n");
+ cl_git_pass(git_index_add_bypath(index, "TEST.txt"));
+
+ if (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE)
+ cl_assert_equal_i(1, (int)git_index_entrycount(index));
+ else
+ cl_assert_equal_i(2, (int)git_index_entrycount(index));
+
+ /* Test access by path instead of index */
+ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ /* The path should *not* have changed without an explicit remove */
+ cl_assert(git__strcmp(entry->path, "test.txt") == 0);
+
+ cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL);
+ if (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE)
+ /* The path should *not* have changed without an explicit remove */
+ cl_assert(git__strcmp(entry->path, "test.txt") == 0);
+ else
+ cl_assert(git__strcmp(entry->path, "TEST.txt") == 0);
+
+ git_index_free(index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__elocked(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ const git_error *err;
+ int error;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /* Lock the index file so we fail to lock it */
+ cl_git_pass(git_filebuf_open(&file, index->index_file_path, 0, 0666));
+ error = git_index_write(index);
+ cl_assert_equal_i(GIT_ELOCKED, error);
+
+ err = git_error_last();
+ cl_assert_equal_i(err->klass, GIT_ERROR_INDEX);
+
+ git_filebuf_cleanup(&file);
+ git_index_free(index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__reload_from_disk(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *read_index;
+ git_index *write_index;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_futils_mkdir("./myrepo", 0777, GIT_MKDIR_PATH));
+ cl_git_mkfile("./myrepo/a.txt", "a\n");
+ cl_git_mkfile("./myrepo/b.txt", "b\n");
+
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+ cl_git_pass(git_repository_index(&write_index, repo));
+ cl_assert_equal_i(false, write_index->on_disk);
+
+ cl_git_pass(git_index__open(&read_index, write_index->index_file_path, GIT_OID_SHA256));
+ cl_assert_equal_i(false, read_index->on_disk);
+
+ /* Stage two new files against the write_index */
+ cl_git_pass(git_index_add_bypath(write_index, "a.txt"));
+ cl_git_pass(git_index_add_bypath(write_index, "b.txt"));
+
+ cl_assert_equal_sz(2, git_index_entrycount(write_index));
+
+ /* Persist the index changes to disk */
+ cl_git_pass(git_index_write(write_index));
+ cl_assert_equal_i(true, write_index->on_disk);
+
+ /* Sync the changes back into the read_index */
+ cl_assert_equal_sz(0, git_index_entrycount(read_index));
+
+ cl_git_pass(git_index_read(read_index, true));
+ cl_assert_equal_i(true, read_index->on_disk);
+
+ cl_assert_equal_sz(2, git_index_entrycount(read_index));
+
+ /* Remove the index file from the filesystem */
+ cl_git_pass(p_unlink(write_index->index_file_path));
+
+ /* Sync the changes back into the read_index */
+ cl_git_pass(git_index_read(read_index, true));
+ cl_assert_equal_i(false, read_index->on_disk);
+ cl_assert_equal_sz(0, git_index_entrycount(read_index));
+
+ git_index_free(read_index);
+ git_index_free(write_index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__reload_while_ignoring_case(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ unsigned int caps;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+
+ caps = git_index_caps(index);
+ cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEX_CAPABILITY_IGNORE_CASE));
+ cl_git_pass(git_index_read(index, true));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(git_index_get_bypath(index, "contrib/README", 0));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "CONTRIB/readme", 0));
+
+ cl_git_pass(git_index_set_caps(index, caps | GIT_INDEX_CAPABILITY_IGNORE_CASE));
+ cl_git_pass(git_index_read(index, true));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(git_index_get_bypath(index, "contrib/README", 0));
+ cl_assert(git_index_get_bypath(index, "CONTRIB/readme", 0));
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__change_icase_on_instance(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ unsigned int caps;
+ const git_index_entry *e;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+
+ caps = git_index_caps(index);
+ cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEX_CAPABILITY_IGNORE_CASE));
+ cl_assert_equal_i(false, index->ignore_case);
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(e = git_index_get_bypath(index, "contrib/README", 0));
+ cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "CONTRIB/readme", 0));
+ cl_assert(e = git_index_get_bypath(index, "config.h", 0));
+ cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "CONFIG.H", 0));
+
+ cl_git_pass(git_index_set_caps(index, caps | GIT_INDEX_CAPABILITY_IGNORE_CASE));
+ cl_assert_equal_i(true, index->ignore_case);
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(e = git_index_get_bypath(index, "config.h", 0));
+ cl_assert_equal_s("config.h", e->path);
+ cl_assert(e = git_index_get_bypath(index, "CONFIG.H", 0));
+ cl_assert_equal_s("config.h", e->path);
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__can_lock_index(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+ git_indexwriter one = GIT_INDEXWRITER_INIT,
+ two = GIT_INDEXWRITER_INIT;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_indexwriter_init(&one, index));
+
+ cl_git_fail_with(GIT_ELOCKED, git_indexwriter_init(&two, index));
+ cl_git_fail_with(GIT_ELOCKED, git_index_write(index));
+
+ cl_git_pass(git_indexwriter_commit(&one));
+
+ cl_git_pass(git_index_write(index));
+
+ git_indexwriter_cleanup(&one);
+ git_indexwriter_cleanup(&two);
+ git_index_free(index);
+ cl_git_sandbox_cleanup();
+#endif
+}
+
+void test_index_tests256__can_iterate(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ git_index_iterator *iterator;
+ const git_index_entry *entry;
+ size_t i, iterator_idx = 0, found = 0;
+ int ret;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+ cl_git_pass(git_index_iterator_new(&iterator, index));
+
+ cl_assert(git_vector_is_sorted(&iterator->snap));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); i++) {
+ /* Advance iterator to next test entry index */
+ do {
+ ret = git_index_iterator_next(&entry, iterator);
+
+ if (ret == GIT_ITEROVER)
+ cl_fail("iterator did not contain all test entries");
+
+ cl_git_pass(ret);
+ } while (iterator_idx++ < test_entries[i].index);
+
+ cl_assert_equal_s(entry->path, test_entries[i].path);
+ cl_assert_equal_i(entry->mtime.seconds, test_entries[i].mtime);
+ cl_assert_equal_i(entry->file_size, test_entries[i].file_size);
+ found++;
+ }
+
+ while ((ret = git_index_iterator_next(&entry, iterator)) == 0)
+ ;
+
+ if (ret != GIT_ITEROVER)
+ cl_git_fail(ret);
+
+ cl_assert_equal_i(found, ARRAY_SIZE(test_entries));
+
+ git_index_iterator_free(iterator);
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__can_modify_while_iterating(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ git_index_iterator *iterator;
+ const git_index_entry *entry;
+ git_index_entry new_entry = {{0}};
+ size_t expected = 0, seen = 0;
+ int ret;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+ cl_git_pass(git_index_iterator_new(&iterator, index));
+
+ expected = git_index_entrycount(index);
+ cl_assert(git_vector_is_sorted(&iterator->snap));
+
+ /*
+ * After we've counted the entries, add a new one and change another;
+ * ensure that our iterator is backed by a snapshot and thus returns
+ * the number of entries from when the iterator was created.
+ */
+ cl_git_pass(git_oid__fromstr(&new_entry.id, "8312e0a89a9cbab77c732b6bc39b51a783e3a318a847f46cba7614cac9814291", GIT_OID_SHA256));
+ new_entry.path = "newfile";
+ new_entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(index, &new_entry));
+
+ cl_git_pass(git_oid__fromstr(&new_entry.id, "4141414141414141414141414141414141414141414141414141414141414141", GIT_OID_SHA256));
+ new_entry.path = "Makefile";
+ new_entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(index, &new_entry));
+
+ while (true) {
+ ret = git_index_iterator_next(&entry, iterator);
+
+ if (ret == GIT_ITEROVER)
+ break;
+
+ seen++;
+ }
+
+ cl_assert_equal_i(expected, seen);
+
+ git_index_iterator_free(iterator);
+ git_index_free(index);
+#endif
+}
diff --git a/tests/libgit2/iterator/workdir.c b/tests/libgit2/iterator/workdir.c
index 7634997e1e5..af47d8b3601 100644
--- a/tests/libgit2/iterator/workdir.c
+++ b/tests/libgit2/iterator/workdir.c
@@ -585,9 +585,9 @@ void test_iterator_workdir__filesystem(void)
expect_iterator_items(i, 5, expect_noauto, 18, expect_trees);
git_iterator_free(i);
- git__tsort((void **)expect_base, 8, (git__tsort_cmp)git__strcasecmp);
- git__tsort((void **)expect_trees, 18, (git__tsort_cmp)git__strcasecmp);
- git__tsort((void **)expect_noauto, 5, (git__tsort_cmp)git__strcasecmp);
+ git__tsort((void **)expect_base, 8, git__strcasecmp_cb);
+ git__tsort((void **)expect_trees, 18, git__strcasecmp_cb);
+ git__tsort((void **)expect_noauto, 5, git__strcasecmp_cb);
i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", &i_opts));
diff --git a/tests/libgit2/merge/trees/renames.c b/tests/libgit2/merge/trees/renames.c
index a27945ee071..9507b51bc9e 100644
--- a/tests/libgit2/merge/trees/renames.c
+++ b/tests/libgit2/merge/trees/renames.c
@@ -350,3 +350,22 @@ void test_merge_trees_renames__cache_recomputation(void)
git_tree_free(our_tree);
git__free(data);
}
+
+void test_merge_trees_renames__emptyfile_renames(void)
+{
+ git_index *index;
+ git_merge_options *opts = NULL;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", 1, "bar" },
+ { 0100644, "60b12be2d2f57977ce83d8dfd32e2394ac1ba1a2", 3, "bar" },
+ { 0100644, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", 0, "boo" },
+ { 0100644, "e50a49f9558d09d4d3bfc108363bb24c127ed263", 0, "foo" },
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo,
+ "emptyfile_renames", "emptyfile_renames-branch",
+ opts));
+ cl_assert(merge_test_index(index, merge_index_entries, 4));
+ git_index_free(index);
+}
diff --git a/tests/libgit2/message/trailer.c b/tests/libgit2/message/trailer.c
index 919e10a499c..09e8f6115a7 100644
--- a/tests/libgit2/message/trailer.c
+++ b/tests/libgit2/message/trailer.c
@@ -3,19 +3,22 @@
static void assert_trailers(const char *message, git_message_trailer *trailers)
{
git_message_trailer_array arr;
- size_t i;
+ size_t i, count;
int rc = git_message_trailers(&arr, message);
cl_assert_equal_i(0, rc);
- for(i=0; itree_id);
}
@@ -60,15 +63,18 @@ static void assert_commit_parses(const char *data, size_t datalen,
git_object__free(&commit->object);
}
-static void assert_commit_fails(const char *data, size_t datalen)
+static void assert_commit_fails(
+ const char *data,
+ size_t datalen,
+ git_oid_t oid_type)
{
git_object *object;
if (!datalen)
datalen = strlen(data);
- cl_git_fail(git_object__from_raw(&object, data, datalen, GIT_OBJECT_COMMIT));
+ cl_git_fail(git_object__from_raw(&object, data, datalen, GIT_OBJECT_COMMIT, oid_type));
}
-void test_object_commit_parse__parsing_commit_succeeds(void)
+void test_object_commit_parse__sha1_parsing_commit_succeeds(void)
{
const char *commit =
"tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
@@ -77,7 +83,7 @@ void test_object_commit_parse__parsing_commit_succeeds(void)
"encoding Encoding\n"
"\n"
"Message";
- assert_commit_parses(commit, 0,
+ assert_commit_parses(commit, 0, GIT_OID_SHA1,
"3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
"Author ",
"Committer ",
@@ -85,7 +91,7 @@ void test_object_commit_parse__parsing_commit_succeeds(void)
"Message", 0);
}
-void test_object_commit_parse__parsing_commit_without_encoding_succeeds(void)
+void test_object_commit_parse__sha1_parsing_commit_without_encoding_succeeds(void)
{
const char *commit =
"tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
@@ -93,7 +99,7 @@ void test_object_commit_parse__parsing_commit_without_encoding_succeeds(void)
"committer Committer \n"
"\n"
"Message";
- assert_commit_parses(commit, 0,
+ assert_commit_parses(commit, 0, GIT_OID_SHA1,
"3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
"Author ",
"Committer ",
@@ -101,7 +107,7 @@ void test_object_commit_parse__parsing_commit_without_encoding_succeeds(void)
"Message", 0);
}
-void test_object_commit_parse__parsing_commit_with_multiple_authors_succeeds(void)
+void test_object_commit_parse__sha1_parsing_commit_with_multiple_authors_succeeds(void)
{
const char *commit =
"tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
@@ -112,7 +118,7 @@ void test_object_commit_parse__parsing_commit_with_multiple_authors_succeeds(voi
"committer Committer \n"
"\n"
"Message";
- assert_commit_parses(commit, 0,
+ assert_commit_parses(commit, 0, GIT_OID_SHA1,
"3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
"Author1 ",
"Committer ",
@@ -120,7 +126,7 @@ void test_object_commit_parse__parsing_commit_with_multiple_authors_succeeds(voi
"Message", 0);
}
-void test_object_commit_parse__parsing_commit_with_multiple_committers_succeeds(void)
+void test_object_commit_parse__sha1_parsing_commit_with_multiple_committers_succeeds(void)
{
const char *commit =
"tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
@@ -131,7 +137,7 @@ void test_object_commit_parse__parsing_commit_with_multiple_committers_succeeds(
"committer Committer4 \n"
"\n"
"Message";
- assert_commit_parses(commit, 0,
+ assert_commit_parses(commit, 0, GIT_OID_SHA1,
"3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
"Author ",
"Committer1 ",
@@ -139,13 +145,13 @@ void test_object_commit_parse__parsing_commit_with_multiple_committers_succeeds(
"Message", 0);
}
-void test_object_commit_parse__parsing_commit_without_message_succeeds(void)
+void test_object_commit_parse__sha1_parsing_commit_without_message_succeeds(void)
{
const char *commit =
"tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
"author Author \n"
"committer Committer \n";
- assert_commit_parses(commit, 0,
+ assert_commit_parses(commit, 0, GIT_OID_SHA1,
"3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
"Author ",
"Committer ",
@@ -153,7 +159,7 @@ void test_object_commit_parse__parsing_commit_without_message_succeeds(void)
"", 0);
}
-void test_object_commit_parse__parsing_commit_with_unknown_fields_succeeds(void)
+void test_object_commit_parse__sha1_parsing_commit_with_unknown_fields_succeeds(void)
{
const char *commit =
"tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
@@ -163,7 +169,7 @@ void test_object_commit_parse__parsing_commit_with_unknown_fields_succeeds(void)
"more garbage\n"
"\n"
"Message";
- assert_commit_parses(commit, 0,
+ assert_commit_parses(commit, 0, GIT_OID_SHA1,
"3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
"Author ",
"Committer ",
@@ -171,7 +177,7 @@ void test_object_commit_parse__parsing_commit_with_unknown_fields_succeeds(void)
"Message", 0);
}
-void test_object_commit_parse__parsing_commit_with_invalid_tree_fails(void)
+void test_object_commit_parse__sha1_parsing_commit_with_invalid_tree_fails(void)
{
const char *commit =
"tree 3e7ac388cadacccdf1xxx5f3445895b71d9cb0f8\n"
@@ -179,40 +185,51 @@ void test_object_commit_parse__parsing_commit_with_invalid_tree_fails(void)
"committer Committer \n"
"\n"
"Message";
- assert_commit_fails(commit, 0);
+ assert_commit_fails(commit, 0, GIT_OID_SHA1);
}
-void test_object_commit_parse__parsing_commit_without_tree_fails(void)
+void test_object_commit_parse__sha1_parsing_commit_with_sha256_tree_fails(void)
{
const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
"author Author \n"
"committer Committer \n"
"\n"
"Message";
- assert_commit_fails(commit, 0);
+ assert_commit_fails(commit, 0, GIT_OID_SHA1);
}
-void test_object_commit_parse__parsing_commit_without_author_fails(void)
+void test_object_commit_parse__sha1_parsing_commit_without_tree_fails(void)
+{
+ const char *commit =
+ "author Author \n"
+ "committer Committer \n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA1);
+}
+
+void test_object_commit_parse__sha1_parsing_commit_without_author_fails(void)
{
const char *commit =
"tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
"committer Committer \n"
"\n"
"Message";
- assert_commit_fails(commit, 0);
+ assert_commit_fails(commit, 0, GIT_OID_SHA1);
}
-void test_object_commit_parse__parsing_commit_without_committer_fails(void)
+void test_object_commit_parse__sha1_parsing_commit_without_committer_fails(void)
{
const char *commit =
"tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
"author Author \n"
"\n"
"Message";
- assert_commit_fails(commit, 0);
+ assert_commit_fails(commit, 0, GIT_OID_SHA1);
}
-void test_object_commit_parse__parsing_encoding_will_not_cause_oob_read(void)
+void test_object_commit_parse__sha1_parsing_encoding_will_not_cause_oob_read(void)
{
const char *commit =
"tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
@@ -223,10 +240,237 @@ void test_object_commit_parse__parsing_encoding_will_not_cause_oob_read(void)
* As we ignore unknown fields, the cut-off encoding field will be
* parsed just fine.
*/
- assert_commit_parses(commit, strlen(commit) - strlen("ncoding foo\n"),
+ assert_commit_parses(
+ commit, strlen(commit) - strlen("ncoding foo\n"),
+ GIT_OID_SHA1,
"3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
"<>",
"<>",
NULL,
"", 0);
}
+
+
+void test_object_commit_parse__sha256_parsing_commit_succeeds(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author \n"
+ "committer Committer \n"
+ "encoding Encoding\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "Author ",
+ "Committer ",
+ "Encoding",
+ "Message", 0);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_without_encoding_succeeds(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author \n"
+ "committer Committer \n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "Author ",
+ "Committer ",
+ NULL,
+ "Message", 0);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_with_multiple_authors_succeeds(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author1 \n"
+ "author Author2 \n"
+ "author Author3 \n"
+ "author Author4 \n"
+ "committer Committer \n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "Author1 ",
+ "Committer ",
+ NULL,
+ "Message", 0);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_with_multiple_committers_succeeds(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author \n"
+ "committer Committer1 \n"
+ "committer Committer2 \n"
+ "committer Committer3 \n"
+ "committer Committer4 \n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "Author ",
+ "Committer1 ",
+ NULL,
+ "Message", 0);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_without_message_succeeds(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author \n"
+ "committer Committer \n";
+ assert_commit_parses(commit, 0, GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "Author ",
+ "Committer ",
+ NULL,
+ "", 0);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_with_unknown_fields_succeeds(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author \n"
+ "committer Committer \n"
+ "foo bar\n"
+ "more garbage\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "Author ",
+ "Committer ",
+ NULL,
+ "Message", 0);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_with_invalid_tree_fails(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9adxxxd55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author \n"
+ "committer Committer \n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA256);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_with_sha1_tree_fails(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author \n"
+ "committer Committer \n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA256);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_without_tree_fails(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "author Author \n"
+ "committer Committer \n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA256);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_without_author_fails(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "committer Committer \n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA256);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_without_committer_fails(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author \n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA256);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_encoding_will_not_cause_oob_read(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author <>\n"
+ "committer <>\n"
+ "encoding foo\n";
+ /*
+ * As we ignore unknown fields, the cut-off encoding field will be
+ * parsed just fine.
+ */
+ assert_commit_parses(
+ commit, strlen(commit) - strlen("ncoding foo\n"),
+ GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "<>",
+ "<>",
+ NULL,
+ "", 0);
+#endif
+}
diff --git a/tests/libgit2/object/lookup256.c b/tests/libgit2/object/lookup256.c
new file mode 100644
index 00000000000..3e1dab6610a
--- /dev/null
+++ b/tests/libgit2/object/lookup256.c
@@ -0,0 +1,153 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static git_repository *g_repo;
+#endif
+
+void test_object_lookup256__initialize(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ g_repo = cl_git_sandbox_init("testrepo_256.git");
+#endif
+}
+
+void test_object_lookup256__cleanup(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_sandbox_cleanup();
+#endif
+}
+
+void test_object_lookup256__lookup_wrong_type_returns_enotfound(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit = "4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid__fromstr(&oid, commit, GIT_OID_SHA256));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_TAG));
+#endif
+}
+
+void test_object_lookup256__lookup_nonexisting_returns_enotfound(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *unknown = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid__fromstr(&oid, unknown, GIT_OID_SHA256));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_ANY));
+#endif
+}
+
+void test_object_lookup256__lookup_wrong_type_by_abbreviated_id_returns_enotfound(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit = "4d46d97";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid__fromstrn(&oid, commit, strlen(commit), GIT_OID_SHA256));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup_prefix(&object, g_repo, &oid, strlen(commit), GIT_OBJECT_TAG));
+#endif
+}
+
+void test_object_lookup256__lookup_wrong_type_eventually_returns_enotfound(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit = "4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid__fromstr(&oid, commit, GIT_OID_SHA256));
+
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+ git_object_free(object);
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_TAG));
+#endif
+}
+
+void test_object_lookup256__lookup_corrupt_object_returns_error(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit = "5ca8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848",
+ *file = "objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848";
+ git_str path = GIT_STR_INIT, contents = GIT_STR_INIT;
+ git_oid oid;
+ git_object *object;
+ size_t i;
+
+ cl_git_pass(git_oid__fromstr(&oid, commit, GIT_OID_SHA256));
+ cl_git_pass(git_str_joinpath(&path, git_repository_path(g_repo), file));
+ cl_git_pass(git_futils_readbuffer(&contents, path.ptr));
+
+ /* Corrupt and try to read the object */
+ for (i = 0; i < contents.size; i++) {
+ contents.ptr[i] ^= 0x1;
+ cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644));
+ cl_git_fail(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+ contents.ptr[i] ^= 0x1;
+ }
+
+ /* Restore original content and assert we can read the object */
+ cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644));
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+
+ git_object_free(object);
+ git_str_dispose(&path);
+ git_str_dispose(&contents);
+#endif
+}
+
+void test_object_lookup256__lookup_object_with_wrong_hash_returns_error(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *oldloose = "objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848",
+ *newloose = "objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26840",
+ *commit = "5ca8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26840";
+
+ git_str oldpath = GIT_STR_INIT, newpath = GIT_STR_INIT;
+ git_object *object;
+ git_oid oid;
+
+ cl_git_pass(git_oid__fromstr(&oid, commit, GIT_OID_SHA256));
+
+ /* Copy object to another location with wrong hash */
+ cl_git_pass(git_str_joinpath(&oldpath, git_repository_path(g_repo), oldloose));
+ cl_git_pass(git_str_joinpath(&newpath, git_repository_path(g_repo), newloose));
+ cl_git_pass(git_futils_cp(oldpath.ptr, newpath.ptr, 0644));
+
+ /* Verify that lookup fails due to a hashsum mismatch */
+ cl_git_fail_with(GIT_EMISMATCH, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+
+ /* Disable verification and try again */
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0));
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1));
+
+ git_object_free(object);
+ git_str_dispose(&oldpath);
+ git_str_dispose(&newpath);
+#endif
+}
diff --git a/tests/libgit2/object/tag/parse.c b/tests/libgit2/object/tag/parse.c
index 92298b5ce4a..d7a4d85bfda 100644
--- a/tests/libgit2/object/tag/parse.c
+++ b/tests/libgit2/object/tag/parse.c
@@ -14,7 +14,7 @@ static void assert_tag_parses(const char *data, size_t datalen,
if (!datalen)
datalen = strlen(data);
- cl_git_pass(git_object__from_raw((git_object **) &tag, data, datalen, GIT_OBJECT_TAG));
+ cl_git_pass(git_object__from_raw((git_object **) &tag, data, datalen, GIT_OBJECT_TAG, GIT_OID_SHA1));
cl_assert_equal_i(tag->type, GIT_OBJECT_TAG);
if (expected_oid) {
@@ -54,7 +54,7 @@ static void assert_tag_fails(const char *data, size_t datalen)
git_object *object;
if (!datalen)
datalen = strlen(data);
- cl_git_fail(git_object__from_raw(&object, data, datalen, GIT_OBJECT_TAG));
+ cl_git_fail(git_object__from_raw(&object, data, datalen, GIT_OBJECT_TAG, GIT_OID_SHA1));
}
void test_object_tag_parse__valid_tag_parses(void)
diff --git a/tests/libgit2/object/tree/parse.c b/tests/libgit2/object/tree/parse.c
index d6d6e53026a..fc985d672d8 100644
--- a/tests/libgit2/object/tree/parse.c
+++ b/tests/libgit2/object/tree/parse.c
@@ -26,7 +26,7 @@ static void assert_tree_parses(const char *data, size_t datalen,
if (!datalen)
datalen = strlen(data);
- cl_git_pass(git_object__from_raw((git_object **) &tree, data, datalen, GIT_OBJECT_TREE));
+ cl_git_pass(git_object__from_raw((git_object **) &tree, data, datalen, GIT_OBJECT_TREE, GIT_OID_SHA1));
cl_assert_equal_i(git_tree_entrycount(tree), expected_nentries);
@@ -51,7 +51,7 @@ static void assert_tree_fails(const char *data, size_t datalen)
git_object *object;
if (!datalen)
datalen = strlen(data);
- cl_git_fail(git_object__from_raw(&object, data, datalen, GIT_OBJECT_TREE));
+ cl_git_fail(git_object__from_raw(&object, data, datalen, GIT_OBJECT_TREE, GIT_OID_SHA1));
}
void test_object_tree_parse__single_blob_parses(void)
diff --git a/tests/libgit2/object/tree/update.c b/tests/libgit2/object/tree/update.c
index 1861ac838af..1e82bdcd621 100644
--- a/tests/libgit2/object/tree/update.c
+++ b/tests/libgit2/object/tree/update.c
@@ -1,5 +1,6 @@
#include "clar_libgit2.h"
#include "tree.h"
+#include "index.h"
static git_repository *g_repo;
@@ -28,7 +29,7 @@ void test_object_tree_update__remove_blob(void)
cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
/* Create it with an index */
- cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
cl_git_pass(git_index_read_tree(idx, base_tree));
cl_git_pass(git_index_remove(idx, path, 0));
cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
@@ -57,7 +58,7 @@ void test_object_tree_update__remove_blob_deeper(void)
cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
/* Create it with an index */
- cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
cl_git_pass(git_index_read_tree(idx, base_tree));
cl_git_pass(git_index_remove(idx, path, 0));
cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
@@ -88,7 +89,7 @@ void test_object_tree_update__remove_all_entries(void)
cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
/* Create it with an index */
- cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
cl_git_pass(git_index_read_tree(idx, base_tree));
cl_git_pass(git_index_remove(idx, path1, 0));
cl_git_pass(git_index_remove(idx, path2, 0));
@@ -119,7 +120,7 @@ void test_object_tree_update__replace_blob(void)
cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
/* Create it with an index */
- cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
cl_git_pass(git_index_read_tree(idx, base_tree));
entry.path = path;
@@ -171,7 +172,7 @@ void test_object_tree_update__add_blobs(void)
int j;
/* Create it with an index */
- cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
base_tree = NULL;
if (i == 1) {
@@ -228,7 +229,7 @@ void test_object_tree_update__add_blobs_unsorted(void)
int j;
/* Create it with an index */
- cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
base_tree = NULL;
if (i == 1) {
diff --git a/tests/libgit2/object/validate.c b/tests/libgit2/object/validate.c
index 87193deb64c..e11038115da 100644
--- a/tests/libgit2/object/validate.c
+++ b/tests/libgit2/object/validate.c
@@ -1,50 +1,154 @@
#include "clar_libgit2.h"
-#define VALID_COMMIT "tree bdd24e358576f1baa275df98cdcaf3ac9a3f4233\n" \
- "parent d6d956f1d66210bfcd0484166befab33b5987a39\n" \
- "author Edward Thomson 1638286404 -0500\n" \
- "committer Edward Thomson 1638324642 -0500\n" \
- "\n" \
- "commit go here.\n"
-#define VALID_TREE "100644 HEADER\0\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
-
-#define INVALID_COMMIT "tree bdd24e358576f1baa275df98cdcaf3ac9a3f4233\n" \
- "parent d6d956f1d66210bfcd0484166befab33b5987a39\n" \
- "committer Edward Thomson 1638324642 -0500\n" \
- "\n" \
- "commit go here.\n"
-#define INVALID_TREE "100644 HEADER \x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
-
-void test_object_validate__valid(void)
+#define VALID_COMMIT_SHA1 \
+ "tree bdd24e358576f1baa275df98cdcaf3ac9a3f4233\n" \
+ "parent d6d956f1d66210bfcd0484166befab33b5987a39\n" \
+ "author Edward Thomson 1638286404 -0500\n" \
+ "committer Edward Thomson 1638324642 -0500\n" \
+ "\n" \
+ "commit go here.\n"
+
+#define VALID_TREE_SHA1 \
+ "100644 HEADER\0\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
+
+#define INVALID_COMMIT_SHA1 \
+ "tree bdd24e358576f1baa275df98cdcaf3ac9a3f4233\n" \
+ "parent d6d956f1d66210bfcd0484166befab33b5987a39\n" \
+ "committer Edward Thomson 1638324642 -0500\n" \
+ "\n" \
+ "commit go here.\n"
+
+#define INVALID_TREE_SHA1 \
+ "100644 HEADER \x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
+
+#define VALID_COMMIT_SHA256 \
+ "tree d0fc7f52dc42358506e7f3f3be72f5271994abb104b9397ab3e19bb42361504d\n" \
+ "parent 652412419a24ba62a1d897f40aeb80eecbf873797b04a1bbb8d71918653ef65b\n" \
+ "author Edward Thomson 1638286404 -0500\n" \
+ "committer Edward Thomson 1638324642 -0500\n" \
+ "\n" \
+ "commit go here.\n"
+
+#define VALID_TREE_SHA256 \
+ "100644 HEADER\0\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
+
+#define INVALID_COMMIT_SHA256 \
+ "tree d0fc7f52dc42358506e7f3f3be72f5271994abb104b9397ab3e19bb42361504d\n" \
+ "parent 652412419a24ba62a1d897f40aeb80eecbf873797b04a1bbb8d71918653ef65b\n" \
+ "committer Edward Thomson 1638324642 -0500\n" \
+ "\n" \
+ "commit go here.\n"
+
+#define INVALID_TREE_SHA256 \
+ "100644 HEADER \x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+# define sha1_rawcontent_is_valid(v, c, l, t) \
+ git_object_rawcontent_is_valid(v, c, l, t, GIT_OID_SHA1)
+#else
+# define sha1_rawcontent_is_valid(v, c, l, t) \
+ git_object_rawcontent_is_valid(v, c, l, t)
+#endif
+
+void test_object_validate__valid_sha1(void)
+{
+ int valid;
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_BLOB));
+ cl_assert_equal_i(1, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_BLOB));
+ cl_assert_equal_i(1, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, VALID_COMMIT_SHA1, CONST_STRLEN(VALID_COMMIT_SHA1), GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(1, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, VALID_TREE_SHA1, CONST_STRLEN(VALID_TREE_SHA1), GIT_OBJECT_TREE));
+ cl_assert_equal_i(1, valid);
+}
+
+void test_object_validate__cannot_parse_sha256_as_sha1(void)
+{
+ int valid;
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, VALID_COMMIT_SHA256, CONST_STRLEN(INVALID_COMMIT_SHA256), GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, INVALID_TREE_SHA256, CONST_STRLEN(INVALID_TREE_SHA256), GIT_OBJECT_TREE));
+ cl_assert_equal_i(0, valid);
+}
+
+void test_object_validate__invalid_sha1(void)
{
int valid;
- cl_git_pass(git_object_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_BLOB));
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, INVALID_COMMIT_SHA1, CONST_STRLEN(INVALID_COMMIT_SHA1), GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, INVALID_TREE_SHA1, CONST_STRLEN(INVALID_TREE_SHA1), GIT_OBJECT_TREE));
+ cl_assert_equal_i(0, valid);
+}
+
+
+void test_object_validate__valid_sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ int valid;
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_BLOB, GIT_OID_SHA256));
cl_assert_equal_i(1, valid);
- cl_git_pass(git_object_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_BLOB));
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_BLOB, GIT_OID_SHA256));
cl_assert_equal_i(1, valid);
- cl_git_pass(git_object_rawcontent_is_valid(&valid, VALID_COMMIT, CONST_STRLEN(VALID_COMMIT), GIT_OBJECT_COMMIT));
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, VALID_COMMIT_SHA256, CONST_STRLEN(VALID_COMMIT_SHA256), GIT_OBJECT_COMMIT, GIT_OID_SHA256));
cl_assert_equal_i(1, valid);
- cl_git_pass(git_object_rawcontent_is_valid(&valid, VALID_TREE, CONST_STRLEN(VALID_TREE), GIT_OBJECT_TREE));
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, VALID_TREE_SHA256, CONST_STRLEN(VALID_TREE_SHA256), GIT_OBJECT_TREE, GIT_OID_SHA256));
cl_assert_equal_i(1, valid);
+#endif
}
-void test_object_validate__invalid(void)
+void test_object_validate__invalid_sha256(void)
{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
int valid;
- cl_git_pass(git_object_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_COMMIT, GIT_OID_SHA256));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_COMMIT, GIT_OID_SHA256));
cl_assert_equal_i(0, valid);
- cl_git_pass(git_object_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, INVALID_COMMIT_SHA256, CONST_STRLEN(INVALID_COMMIT_SHA256), GIT_OBJECT_COMMIT, GIT_OID_SHA256));
cl_assert_equal_i(0, valid);
- cl_git_pass(git_object_rawcontent_is_valid(&valid, INVALID_COMMIT, CONST_STRLEN(INVALID_COMMIT), GIT_OBJECT_COMMIT));
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, INVALID_TREE_SHA256, CONST_STRLEN(INVALID_TREE_SHA256), GIT_OBJECT_TREE, GIT_OID_SHA256));
+ cl_assert_equal_i(0, valid);
+#endif
+}
+
+void test_object_validate__cannot_parse_sha1_as_sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ int valid;
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, VALID_COMMIT_SHA1, CONST_STRLEN(INVALID_COMMIT_SHA1), GIT_OBJECT_COMMIT, GIT_OID_SHA256));
cl_assert_equal_i(0, valid);
- cl_git_pass(git_object_rawcontent_is_valid(&valid, INVALID_TREE, CONST_STRLEN(INVALID_TREE), GIT_OBJECT_TREE));
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, INVALID_TREE_SHA1, CONST_STRLEN(INVALID_TREE_SHA1), GIT_OBJECT_TREE, GIT_OID_SHA256));
cl_assert_equal_i(0, valid);
+#endif
}
diff --git a/tests/libgit2/odb/backend/loose.c b/tests/libgit2/odb/backend/loose.c
new file mode 100644
index 00000000000..02227945d71
--- /dev/null
+++ b/tests/libgit2/odb/backend/loose.c
@@ -0,0 +1,62 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "odb.h"
+#include "backend_helpers.h"
+#include "git2/sys/mempack.h"
+
+static git_repository *_repo;
+static git_odb *_odb;
+
+void test_odb_backend_loose__initialize(void)
+{
+ git_odb_backend *backend;
+
+ cl_fixture_sandbox("testrepo.git");
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_odb_backend_loose(&backend, "testrepo.git/objects", NULL));
+#else
+ cl_git_pass(git_odb_backend_loose(&backend, "testrepo.git/objects", 0, 0, 0, 0));
+#endif
+
+ cl_git_pass(git_odb__new(&_odb, NULL));
+ cl_git_pass(git_odb_add_backend(_odb, backend, 10));
+ cl_git_pass(git_repository__wrap_odb(&_repo, _odb, GIT_OID_SHA1));
+}
+
+void test_odb_backend_loose__cleanup(void)
+{
+ git_odb_free(_odb);
+ git_repository_free(_repo);
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_odb_backend_loose__read_from_odb(void)
+{
+ git_oid oid;
+ git_odb_object *obj;
+
+ cl_git_pass(git_oid__fromstr(&oid, "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OID_SHA1));
+ cl_git_pass(git_odb_read(&obj, _odb, &oid));
+ git_odb_object_free(obj);
+
+ cl_git_pass(git_oid__fromstr(&oid, "fd093bff70906175335656e6ce6ae05783708765", GIT_OID_SHA1));
+ cl_git_pass(git_odb_read(&obj, _odb, &oid));
+ git_odb_object_free(obj);
+}
+
+void test_odb_backend_loose__read_from_repo(void)
+{
+ git_oid oid;
+ git_blob *blob;
+ git_tree *tree;
+
+ cl_git_pass(git_oid__fromstr(&oid, "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup(&blob, _repo, &oid));
+ git_blob_free(blob);
+
+ cl_git_pass(git_oid__fromstr(&oid, "fd093bff70906175335656e6ce6ae05783708765", GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, _repo, &oid));
+ git_tree_free(tree);
+}
diff --git a/tests/libgit2/odb/backend/mempack.c b/tests/libgit2/odb/backend/mempack.c
index eb8ab3cb0b2..c8a86a2ae95 100644
--- a/tests/libgit2/odb/backend/mempack.c
+++ b/tests/libgit2/odb/backend/mempack.c
@@ -16,7 +16,7 @@ void test_odb_backend_mempack__initialize(void)
cl_git_pass(git_mempack_new(&backend));
cl_git_pass(git_odb__new(&_odb, NULL));
cl_git_pass(git_odb_add_backend(_odb, backend, 10));
- cl_git_pass(git_repository_wrap_odb(&_repo, _odb));
+ cl_git_pass(git_repository__wrap_odb(&_repo, _odb, GIT_OID_SHA1));
}
void test_odb_backend_mempack__cleanup(void)
diff --git a/tests/libgit2/odb/backend/nobackend.c b/tests/libgit2/odb/backend/nobackend.c
index 7d9394c6f33..a81e5857715 100644
--- a/tests/libgit2/odb/backend/nobackend.c
+++ b/tests/libgit2/odb/backend/nobackend.c
@@ -11,7 +11,11 @@ void test_odb_backend_nobackend__initialize(void)
git_odb *odb;
git_refdb *refdb;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_repository_new(&_repo, GIT_OID_SHA1));
+#else
cl_git_pass(git_repository_new(&_repo));
+#endif
cl_git_pass(git_config_new(&config));
cl_git_pass(git_odb__new(&odb, NULL));
cl_git_pass(git_refdb_new(&refdb, _repo));
diff --git a/tests/libgit2/odb/foreach.c b/tests/libgit2/odb/foreach.c
index 165a511a0ca..56b3e882ce0 100644
--- a/tests/libgit2/odb/foreach.c
+++ b/tests/libgit2/odb/foreach.c
@@ -52,7 +52,16 @@ void test_odb_foreach__one_pack(void)
int nobj = 0;
cl_git_pass(git_odb__new(&_odb, NULL));
- cl_git_pass(git_odb_backend_one_pack(&backend, cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx")));
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_odb_backend_one_pack(&backend,
+ cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx"),
+ NULL));
+#else
+ cl_git_pass(git_odb_backend_one_pack(&backend,
+ cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx")));
+#endif
+
cl_git_pass(git_odb_add_backend(_odb, backend, 1));
_repo = NULL;
diff --git a/tests/libgit2/odb/open.c b/tests/libgit2/odb/open.c
new file mode 100644
index 00000000000..395406d0f3c
--- /dev/null
+++ b/tests/libgit2/odb/open.c
@@ -0,0 +1,34 @@
+#include "clar_libgit2.h"
+
+void test_odb_open__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+}
+
+void test_odb_open__cleanup(void)
+{
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_odb_open__exists(void)
+{
+ git_odb *odb;
+ git_oid one, two;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_odb_options opts = GIT_ODB_OPTIONS_INIT;
+
+ cl_git_pass(git_odb_open(&odb, "testrepo.git/objects", &opts));
+ cl_git_pass(git_oid_fromstr(&one, "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OID_SHA1));
+ cl_git_pass(git_oid_fromstr(&two, "00112233445566778899aabbccddeeff00112233", GIT_OID_SHA1));
+#else
+ cl_git_pass(git_odb_open(&odb, "testrepo.git/objects"));
+ cl_git_pass(git_oid_fromstr(&one, "1385f264afb75a56a5bec74243be9b367ba4ca08"));
+ cl_git_pass(git_oid_fromstr(&two, "00112233445566778899aabbccddeeff00112233"));
+#endif
+
+ cl_assert(git_odb_exists(odb, &one));
+ cl_assert(!git_odb_exists(odb, &two));
+
+ git_odb_free(odb);
+}
diff --git a/tests/libgit2/odb/pack_data_256.h b/tests/libgit2/odb/pack_data_256.h
new file mode 100644
index 00000000000..b631882273a
--- /dev/null
+++ b/tests/libgit2/odb/pack_data_256.h
@@ -0,0 +1,154 @@
+#ifdef GIT_EXPERIMENTAL_SHA256
+
+static const char *packed_objects_256[] = {
+ "99f3b405443221141eb0fd1e0cca5d355f893983749b7fb455769fba434e7945",
+ "d0fc7f52dc42358506e7f3f3be72f5271994abb104b9397ab3e19bb42361504d",
+ "86e228d9904af64586e9a8378005ba654681ff5be3c43ca930bf6b1f28d4395f",
+ "652412419a24ba62a1d897f40aeb80eecbf873797b04a1bbb8d71918653ef65b",
+ "ad90f638cb67720b20b904478471504acebacc7bb36e5dcad3e882acec496fed",
+ "4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744",
+ "80ec1e36b983e68664e8357c538cd35b30638bb0cb99626f906d145e2d2e2558",
+ "e8bbf40ee280bc43b33c04df2250903b75e92f2497e91759cf1cad753c23be6c",
+ "8864b5746d7c5780083bb98449a3f5bf78d8281e8c5e3fd12a8ccd9103eb3a1f",
+ "083c2b8b445640d171e7aa667b0b32007006f786711032ebb82931cca69cc15b",
+ "5c8bea399f78d3d6a037a41cee2e763d00024180b66f2ec738d443b6a3dd7081",
+ "281c36286eab5e534f1c2121a4cf2cd48a32b3773a3e78df500bed3f3c9747f3",
+ "c9dc53358a0d83bee1caae40ee81d752abf4962a9f206702e24a447b766b5bd7",
+ "fafc05a1d0b7614ba32f428eb52f3439ffbfed9a817e5ae069364cfc3fa3e4d4",
+ "25a4efebb38c55b8f2309ce5e3116b2b9287239952cc2fa174074e05c6e5875a",
+ "c223f1b579bad18635efdeffa7a8ff40567d03fa427e08bb90d9878f958d8021",
+ "8a0042d434a2f8a2e8d47caa4eb454f388752fb3fe71150c1cea12e807cfdf1c",
+ "d79e913b4137117b7f8fc2a8d184373f657d6f71bbeaca0fe83b7757ce486108",
+ "375f69d3d41e36d6904bfa86221690ec49de2a030664a362abaf86c426f9f7e0",
+ "22b6705b86e4aa9120eff203af24709d483698d9f78695e86e82de121784b570",
+ "4516b0e63349c81abc6584cb11ce84ab8ba38b105f9de39d0d0a1455dba2478d",
+ "c2b535bfc3501f0b4e179d5d6f0e2cea613940fa3813be5923db7e71e190849f",
+ "aa793f0e9d9d746eba8a7a60cb4981f7e24ce9691910350d7df9b9e94c7567b9",
+ "890bb959ac8c20db603bf083bc82f55f9f42b6dca6581d941d0b361188abae3b",
+ "bfc0ee6fe04854c11011539f38cc6b9b73e0c445bac2008de2fb877123efc2e3",
+ "5ba7253f47d390ee2c7c7afe8fd9a963a7a2674bbdadeb9a927665c9246306c4",
+ "79bc735b91f8dfa9379d1d6c21e2d519ac1bf0d04d48534864c9ff571df5297d",
+ "f362826c827aa3bcbeb3ff8b71bba08d7440b89ab53fe95d61b8922d01f46e28",
+ "4a1b9c078e7bb20759a2d75e3a4b96827c851446c0261750b96aa5f286efe378",
+ "2e2aa456dbbb8889923eb6713672427854020298a764967c50235a9a76d7ce4b",
+ "97b0e3661fa0caa6200c50381233f8320b907540ceb9d17ac94fedc66fd093c2",
+ "ade7d297ede7bb58008da582de1253f0a55cb76e82d1cd376f82ccf97f70bced",
+ "3315093132a8f28bd202c0a9562d04eebea4943dcd1e1c754341b0389722042f",
+ "6fae137ba81d0d81c2a85759b99322e7ea8103bd7c8b85be3163b7e91e18c125",
+ "f8b45f792840019909fd35f9dcb98082b3bc39373268d467e1b00f1da5ac71e5",
+ "d2ab425d6092770366bc3dff5276e3a869221bc7b6d22e99c089d556e7eb8331",
+ "4dd37b9df07bcc7b45ce72a44e3f5fcaf5f0a9a5f3148963714eac4c99a60388",
+ "8c53c0f9f0972a1c77e40549170b9ae51d365c200ffc4cb220628c5bce3dd0b2",
+ "11d0463a82345c2512bd704dd00211aefb7d5b8590ca92809122fd09486a9f06",
+ "1ab4fa663d22416f45cb1a007d767a58d0abf5255bf86f888393dae637b37c3b",
+ "345d2fbf1d306c7ea46a05f497793308357e9e17ab0e866446d2b9894378ddce",
+ "304352ddad641770fcc94ee4d9f957cde7aaf4c107dbf8b5ba14d543640bf7dd",
+ "cfdd565f4cbc315760c287d57714852e1a4894eef9c715332fd556f2e114a9f1",
+ "d6d2e87e6de8690efd26b8c5b58de28dcb3c9bd2b7659eccf34468d71a7a4478",
+ "aa61d4adf622265ec814c1a97198d2bcd3f58fb08989cb9beda32a4d0aab6697",
+ "40799f33b8cd9ca41f36a2f89d8ac8550537ad01dfb21fbc76f01eeb62f512d4",
+ "faa7ca59426e17f6b34fa407d06cd634faaabeb4abe26df12296e05a17c98eef",
+ "1f6ea2cab887a2ff4bb1557a36dd6bac9931ef1f36794ddd22b4b7b7276051fb",
+ "b38a7a2ee69d55f021efa91caf59e23bccdaf6b8a9c3f83acd978aa177587537",
+ "f66691f32eb9b23a029b43251bfa994d16481fe97903057e121b76e4e78f6ff4",
+ "afe1fad6f6b22eea530ff7b373d6b9b787b39792f720e6fcd0692ba6ef99e02f",
+ "b71ee6c8837efa5b3ba3361f88c321d391ac05f41d5b2506cad39319e80716e4",
+ "e207a266e228414023223530eb77c64b10f2f5124f3354deb45aff04c1db98b9",
+ "696503760a18787240ba52ad1abec3be6517bb802238e9469b3a8999cbd6432c",
+ "c30d06fd49797e3135652d654db0de122dd83f8400df1c7a0e95ca3720defb0d",
+ "8a9b600a21987e6ffddbad745f38c115797eacef9117043bd9d2da4835ee3cc4",
+ "d509fa76ff5944e25f48c2476736b6239a53f0463cba6ebc488464d087951951",
+ "3e00d8cae2726dc33879adf876b30c306f50e7a85b15c8add4a27f84d88616d7",
+ "ce55f9f5ab1d799a9ffaa839539af196d13f35677b3d0761b0fe034e764f8d07",
+ "84005e38a65f4115d5c94790010dc57e1a3297f4aa89744f5927f208af758bd0",
+ "75b0ed5f4a2d5c810f34d867dbca51db6a596d4739abfaf24fcc0f05d99097a8",
+ "3eab74a6894d790767f3d92615a6887dcccaffa9e48fdd2ce482b5f17efcf9b9",
+ "8e61988c998c96a131cfe72225fce43e555bec4e590fa8c239373172a9d485ce",
+ "5f45a18f90f2934e7c7985d05b2b5b3584886fd057c9202f26f562d6c3080038",
+ "7609c608c1097270356a6fe336a8756ff124d4a9c2e941bba26a6e8c3becdeb1",
+ "81ba4e67aa59ccb078e3dc9ff3075f50084ec1696bd867e8e4284fcfc34fe3cf",
+ "3b1b991ae70d1f5388ad16b63d2285e99ada7a618c6f5d01e50a6d4b33c4767d",
+ "ac49410f64fad10760838866a40107573e42c86908c83ece433d64b7a8b57f7f",
+ "72c472319cf7d5d59bf9fea9e90b9785d7ab39340003fbc68619c11a9e583c2f",
+ "3a0cd33a47fcd0c7b8f8c1d407aaa53f648e25f2ffe2533a7e9c09c3d1b9da75",
+ "bf409f1aa76256282ebf1a7cc6c9f4220be9ffc47b2cc42248fcc5cbb67bce1f",
+ "13c8c9dae9fa63b1ec48b8abb12312fc8df61b9414678f504fa68164a48eef28",
+ "078baf54914fab56842798c90fec863f15f67a22041e8aa54a88c43f059da050",
+ "41c166c241a4e878f444932a193501e12ab38ba0634747291df70e619dccee1c",
+ "9db520a5a88c7d75b86a4faa1ce9010edf38af922f400c69a4a93aef69e25c4c",
+ "edf6e0dbfaf8a7ad89b05b5768981eeedd7a2bda4b1d0fae07aa2a9d49bb39df",
+ "ec1866b39026366e69ab8e167c15312c27f5eda4c0afdf9367ac3d76f56bf8e7",
+ "695bdb545f636d454b4825effd29db96c46d81772dda6c104f97dffe2ec509f1",
+ "07c31b4be5f3ec8f82c7ad6c91cbdf07ae876bb73041903d64fe8bac64bbcb6e",
+ "06665948a581e547ac1dc883b35bab54682fc311f7a87d5563a76501ecba55fc",
+ "fa6a2b7e588e57115d2772c0d5250d886757404fc510a5956be8de4926e94c01",
+ "28aec9a4450d9de98db98a021f97026bf12f1328f66e53319669eb5adce5ed3e",
+ "290d84a2cf108c074d3764ca8dc56e1215a5a7482837f098dff7a55e23d89d5f",
+ "ed10f9520d5f0d6bbd9c467ed83a239df0cba94ae9031602e02f6ad6256c459e",
+ "ad05c66a177f844f8239a1d186a1f803a4daa6286c959538838e222bc6337dc9",
+ "88fecc715077807bd7ee9e6a1dff65fde000379e0bf2a15cc2404469cef4c82b",
+ "bcc4fa242e55c5e08f64900407dca1cf2451806830905be616f339bf7a5580aa",
+ "4a4ebb837e3c9883f35d0ecdc26d4bc76a0f665568b08f3e967096f2fd3fa537",
+ "9abb7801e72353060a1490ffa3331674d6882558e7d6458de397b4b00d31ff8c",
+ "466cdfec4c74f3ed4fe53165e468a52df5dff9c6533ec433cdf235a73e099d32",
+ "8a1e1a6cc00519c4df8ce404c987751446fa299662308622cb63576fb52996d7",
+ "e5ea38108d603a6ed8dbb0b8455abe6d971f2d60920207e67ba646d267ece305",
+ "97e00ba4c8028ea91e7131c8c7596227cd03d3d2d14ed2d179fe0f305ebbca39",
+ "807faa3586b7f1aff2797a7c39c135b0196647c9ae7ee8843322accc960b2f22",
+ "b9134c331629e9cb29d1bbc03904d911a942b451e087543fc16deb0116391297",
+ "23cb97c200205dc84109c248be5bd719c23bdab52b52c51ec92fef9a48790833",
+ "b6e8427e3afe1cf0095d0f5aeca0642c4fb12d4e529f62e8264b6c4db72f04b4",
+ "92128dfb792caea934f5218807ec993867b0c8487a3de69ebac33e067f64d38a",
+ "a13ecbc514b571721a9a1c92af7f89d473a5fe13228904f8d17368e71f273ea1",
+ "3f5dca26a2f512d6681ce1957b8afba5e031bf63d52fd52d8f57093bf92391a4",
+ "1aa24b7ebd910f39a676f2ccab8e9a79f14842c20b55ed18e3dc297bdbaca279",
+ "1e0ce38d00e8f3e613febc0f8b275e0fd7fbef8af293fd62698be46dfbbc937d",
+ "dd9b1d5ca653752cef167e034324198971ab6f2f38f3db9db571cb2985759f00",
+ "efc4b8fd4b0b2586fce256b107ee2a052d11d26f99d85ce0478c3d49d1b2186b",
+ "da38f65b32fb03ce332e0c4238edae0a733e4cd793d849522bb4e0bdd0af608c",
+ "bf66badfda7b5d2157db65c5310cfdc4e904d7d5da57ac5abe17542de612f856",
+ "2c0f52f9ae1f34f280dfc1c755cbfaf2b9968fd3bab1f1eb16d3ac0fafd71940",
+ "165ec90aa4190bbe12ee415b294fd6d204c64afcf1ca64dd815782872b24ea26",
+ "cc3d272d457c7e26c5d611923841511a1766bcd58e5be66433698627e6fb3f9d",
+ "d970d1a6296d149bfb8283b8b4a9a6f7e9ee320c5d46a5ef216e10400df2d281",
+ "bdc530e0b98dd736cd812408eeed9aa0d393bbd0630b355eb7601e61f0dbc7c4",
+ "94db24aca3f8e07f481744f62633730feb4fc47605280381a08be510ae971ffe",
+ "151c7527acc5b731199a03a932ae374331e16e5ae29256e98cb652f37669889b",
+ "ac88849a26c126b03fc6fcb17cb23ec563e87a5f63b7afe800ad0f436128ce98",
+ "26a2dac21f8f0939566570e48f7f4fcf89239c2746ef8d3dbf31d179c691808f",
+ "619f24a7f37f8ca922c83b3a1b9a384eb6a444ff3a2a52c712f3c60dde6f24fb",
+ "5329dd2fd8557be5ad06b57882cf42e23d767cdc8a4b25e464fdb00890649e07",
+ "fff5cbc10ffea865d69aba64082ae17479c522e8e0305e678469749282bf0a18",
+ "d0c992aae9cde855b17ac826234a73255ecb09534cdcfa633d90640f6a4324d9",
+ "6129f1672465ee7b9e2edef53fbf3846ab5d06e8e6a1d7fb51e31666a8b411f8",
+ "0a8c0add81b065b97451f7b47a9935f0e240251c5f90a89ced885cb7d4efc2ba",
+ "471fa4fc2da467dd94e57babb1912bbdb5e40b96c8129d46fc709c0bfc009bca",
+ "23cea2b49eed4993c5b92a9d5f0b82efe4fae3837ea707d921de645d04479015",
+ "c85e35eec23dea4089aee7a2dc7f6d937ad7e13c66bdbd7eef37bd6336418609",
+ "a0f3dac8fa0e22dcf356aecbbdf79440715687c1053bc59b83354f276d688ceb",
+ "1cfb8ae71e9e576d1b16b7bd1a62156d0641c6e51f5d76877be6de4f26410623"
+};
+
+static const char *loose_objects_256[] = {
+ "96c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045",
+ "aea29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5",
+ "73b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6",
+ "901505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724",
+ "4bc142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5",
+ "7e4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f",
+ "473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813",
+ "7030f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7",
+ "cb282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0",
+ "33e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8",
+ "8155958bbda08eed88c8ac908dc44452ed38911cffa54ccc06076f30a1ffb1bf",
+ "1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24",
+ "f31459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562",
+ "6d5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3",
+ "b83624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022",
+ "61489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e",
+ "abee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640",
+ "a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95",
+ "43e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b"
+};
+
+#endif
diff --git a/tests/libgit2/odb/pack_data_one256.h b/tests/libgit2/odb/pack_data_one256.h
new file mode 100644
index 00000000000..98a874798c6
--- /dev/null
+++ b/tests/libgit2/odb/pack_data_one256.h
@@ -0,0 +1,21 @@
+/* Just a few to make sure it's working, the rest is tested already */
+#ifdef GIT_EXPERIMENTAL_SHA256
+static const char *packed_objects_one256[] = {
+ "ea926306b1bab6d3f25f45609907eb6dff91a1460b25e63bf4a0494c70e7a269",
+ "d048ba2ef4fafa502a44cbc1a50cd58359b9bc243b84a08f541a08ca5f621137",
+ "a66bda0109d2b3c9bc87970da81bd91076b5f871febbc860f09ae997668b6800",
+ "3609a41c0506fe19d01fb8b4729923362675f191fe5f63fab3111ef804c48fdf",
+ "22b6705b86e4aa9120eff203af24709d483698d9f78695e86e82de121784b570",
+ "6f11d93bfb269ee8c7a506178f60c430abfac5d424acfd9c0b0b27b98e6ab49b",
+ "0aefd477d9e5b3f8d708a3cf6d78be6b670dfa2e2ec41244634f3b8f115d8e04",
+ "580474d948cd2ebd2e5ce7a5b81b872d87ba4639c1ac4c0fa7a11a8eddf9827c",
+ "0636b4292bfdd7274a977cb6f8b2ded8f315ea1bcd8dbedfca37964c2ed3d085",
+ "19fb1c78b11f0f8bda658d6fa6cc63c0b573c0f6760ee5a9c2df6ce2cde00c5c",
+ "7f2f7afccb317bb3fdd28555f126846dc4eebe5d9ae7b8d8a1456e8ff85422ce",
+ "4066249c68b0d3c8b39ebe02c9188935900465acad02a49269710f56720fa58e",
+ "a560d1fa1edf114f57b402e16d662c17b1e3b7e8601ff7dcea6615ba7f1e48ef",
+ "58923faa87c7d559d308a114ec2b164e5d6046c889420ed1def6eef2d55106a2",
+ "753ddabab8ae9c1e733cda15e8e3c83dd43f5a2d939ae32cc3b30b0be1e91f96",
+ "46333d32b3801cf11d9f80b557245c9e32b0e05deca61dae968914fde159f0e5"
+};
+#endif
diff --git a/tests/libgit2/odb/packed256.c b/tests/libgit2/odb/packed256.c
new file mode 100644
index 00000000000..65220fd4c8b
--- /dev/null
+++ b/tests/libgit2/odb/packed256.c
@@ -0,0 +1,98 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "pack_data_256.h"
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static git_odb *_odb;
+#endif
+
+void test_odb_packed256__initialize(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_odb_options opts = GIT_ODB_OPTIONS_INIT;
+
+ opts.oid_type = GIT_OID_SHA256;
+
+ cl_git_pass(git_odb__open(
+ &_odb,
+ cl_fixture("testrepo_256.git/objects"),
+ &opts));
+#endif
+}
+
+void test_odb_packed256__cleanup(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_odb_free(_odb);
+ _odb = NULL;
+#endif
+}
+
+void test_odb_packed256__mass_read(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects_256); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+
+ cl_git_pass(git_oid__fromstr(&id, packed_objects_256[i], GIT_OID_SHA256));
+ cl_assert(git_odb_exists(_odb, &id) == 1);
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+
+ git_odb_object_free(obj);
+ }
+#endif
+}
+
+void test_odb_packed256__read_header_0(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects_256); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_oid__fromstr(&id, packed_objects_256[i], GIT_OID_SHA256));
+
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+ cl_git_pass(git_odb_read_header(&len, &type, _odb, &id));
+
+ cl_assert(obj->cached.size == len);
+ cl_assert(obj->cached.type == type);
+
+ git_odb_object_free(obj);
+ }
+#endif
+}
+
+void test_odb_packed256__read_header_1(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(loose_objects_256); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_oid__fromstr(&id, loose_objects_256[i], GIT_OID_SHA256));
+
+ cl_assert(git_odb_exists(_odb, &id) == 1);
+
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+ cl_git_pass(git_odb_read_header(&len, &type, _odb, &id));
+
+ cl_assert(obj->cached.size == len);
+ cl_assert(obj->cached.type == type);
+
+ git_odb_object_free(obj);
+ }
+#endif
+}
+
diff --git a/tests/libgit2/odb/packed_one.c b/tests/libgit2/odb/packedone.c
similarity index 67%
rename from tests/libgit2/odb/packed_one.c
rename to tests/libgit2/odb/packedone.c
index 7a1d3d913d6..8637001ffa7 100644
--- a/tests/libgit2/odb/packed_one.c
+++ b/tests/libgit2/odb/packedone.c
@@ -6,22 +6,29 @@
static git_odb *_odb;
-void test_odb_packed_one__initialize(void)
+void test_odb_packedone__initialize(void)
{
git_odb_backend *backend = NULL;
cl_git_pass(git_odb__new(&_odb, NULL));
- cl_git_pass(git_odb_backend_one_pack(&backend, cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx")));
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_odb_backend_one_pack(&backend,
+ cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx"),
+ NULL));
+#else
+ cl_git_pass(git_odb_backend_one_pack(&backend,
+ cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx")));
+#endif
cl_git_pass(git_odb_add_backend(_odb, backend, 1));
}
-void test_odb_packed_one__cleanup(void)
+void test_odb_packedone__cleanup(void)
{
git_odb_free(_odb);
_odb = NULL;
}
-void test_odb_packed_one__mass_read(void)
+void test_odb_packedone__mass_read(void)
{
unsigned int i;
@@ -37,7 +44,7 @@ void test_odb_packed_one__mass_read(void)
}
}
-void test_odb_packed_one__read_header_0(void)
+void test_odb_packedone__read_header_0(void)
{
unsigned int i;
diff --git a/tests/libgit2/odb/packedone256.c b/tests/libgit2/odb/packedone256.c
new file mode 100644
index 00000000000..fdeac42055f
--- /dev/null
+++ b/tests/libgit2/odb/packedone256.c
@@ -0,0 +1,78 @@
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+
+#include "pack_data_one256.h"
+#include "pack.h"
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static git_odb *_odb;
+#endif
+
+void test_odb_packedone256__initialize(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_odb_backend *backend = NULL;
+ git_odb_options odb_opts = GIT_ODB_OPTIONS_INIT;
+ git_odb_backend_pack_options backend_opts = GIT_ODB_BACKEND_PACK_OPTIONS_INIT;
+
+ odb_opts.oid_type = GIT_OID_SHA256;
+ backend_opts.oid_type = GIT_OID_SHA256;
+
+ cl_git_pass(git_odb__new(&_odb, &odb_opts));
+ cl_git_pass(git_odb_backend_one_pack(
+ &backend,
+ cl_fixture("testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx"),
+ &backend_opts));
+ cl_git_pass(git_odb_add_backend(_odb, backend, 1));
+#endif
+}
+
+void test_odb_packedone256__cleanup(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_odb_free(_odb);
+ _odb = NULL;
+#endif
+}
+
+void test_odb_packedone256__mass_read(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects_one256); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+
+ cl_git_pass(git_oid__fromstr(&id, packed_objects_one256[i], GIT_OID_SHA256));
+ cl_assert(git_odb_exists(_odb, &id) == 1);
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+
+ git_odb_object_free(obj);
+ }
+#endif
+}
+
+void test_odb_packedone256__read_header_0(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects_one256); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_oid__fromstr(&id, packed_objects_one256[i], GIT_OID_SHA256));
+
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+ cl_git_pass(git_odb_read_header(&len, &type, _odb, &id));
+
+ cl_assert(obj->cached.size == len);
+ cl_assert(obj->cached.type == type);
+
+ git_odb_object_free(obj);
+ }
+#endif
+}
diff --git a/tests/libgit2/odb/sorting.c b/tests/libgit2/odb/sorting.c
index 1010872f466..3fe52e852cd 100644
--- a/tests/libgit2/odb/sorting.c
+++ b/tests/libgit2/odb/sorting.c
@@ -7,6 +7,11 @@ typedef struct {
size_t position;
} fake_backend;
+static void odb_backend_free(git_odb_backend *odb)
+{
+ git__free(odb);
+}
+
static git_odb_backend *new_backend(size_t position)
{
fake_backend *b;
@@ -15,7 +20,7 @@ static git_odb_backend *new_backend(size_t position)
if (b == NULL)
return NULL;
- b->base.free = (void (*)(git_odb_backend *)) git__free;
+ b->base.free = odb_backend_free;
b->base.version = GIT_ODB_BACKEND_VERSION;
b->position = position;
return (git_odb_backend *)b;
@@ -82,7 +87,11 @@ void test_odb_sorting__override_default_backend_priority(void)
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_LOOSE_PRIORITY, 5));
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_PACKED_PRIORITY, 3));
- git_odb_backend_pack(&packed, "./testrepo.git/objects");
+ git_odb_backend_pack(&packed, "./testrepo.git/objects"
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , NULL
+#endif
+ );
git_odb__backend_loose(&loose, "./testrepo.git/objects", NULL);
cl_git_pass(git_odb__open(&new_odb, cl_fixture("testrepo.git/objects"), NULL));
diff --git a/tests/libgit2/online/clone.c b/tests/libgit2/online/clone.c
index 96ff66ae06a..207dd839172 100644
--- a/tests/libgit2/online/clone.c
+++ b/tests/libgit2/online/clone.c
@@ -7,6 +7,7 @@
#include "refs.h"
#define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository"
+#define LIVE_REPO_AS_DIR "http:/github.com/libgit2/TestGitRepository"
#define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository"
#define BB_REPO_URL "https://libgit2-test@bitbucket.org/libgit2-test/testgitrepository.git"
#define BB_REPO_URL_WITH_PASS "https://libgit2-test:YT77Ppm2nq8w4TYjGS8U@bitbucket.org/libgit2-test/testgitrepository.git"
@@ -35,12 +36,21 @@ static char *_remote_proxy_selfsigned = NULL;
static char *_remote_expectcontinue = NULL;
static char *_remote_redirect_initial = NULL;
static char *_remote_redirect_subsequent = NULL;
+static char *_remote_speed_timesout = NULL;
+static char *_remote_speed_slow = NULL;
+
+static char *_github_ssh_pubkey = NULL;
+static char *_github_ssh_privkey = NULL;
+static char *_github_ssh_passphrase = NULL;
+static char *_github_ssh_remotehostkey = NULL;
-static int _orig_proxies_need_reset = 0;
static char *_orig_http_proxy = NULL;
static char *_orig_https_proxy = NULL;
static char *_orig_no_proxy = NULL;
+static char *_ssh_cmd = NULL;
+static char *_orig_ssh_cmd = NULL;
+
static int ssl_cert(git_cert *cert, int valid, const char *host, void *payload)
{
GIT_UNUSED(cert);
@@ -84,11 +94,38 @@ void test_online_clone__initialize(void)
_remote_expectcontinue = cl_getenv("GITTEST_REMOTE_EXPECTCONTINUE");
_remote_redirect_initial = cl_getenv("GITTEST_REMOTE_REDIRECT_INITIAL");
_remote_redirect_subsequent = cl_getenv("GITTEST_REMOTE_REDIRECT_SUBSEQUENT");
+ _remote_speed_timesout = cl_getenv("GITTEST_REMOTE_SPEED_TIMESOUT");
+ _remote_speed_slow = cl_getenv("GITTEST_REMOTE_SPEED_SLOW");
+
+ _github_ssh_pubkey = cl_getenv("GITTEST_GITHUB_SSH_PUBKEY");
+ _github_ssh_privkey = cl_getenv("GITTEST_GITHUB_SSH_KEY");
+ _github_ssh_passphrase = cl_getenv("GITTEST_GITHUB_SSH_PASSPHRASE");
+ _github_ssh_remotehostkey = cl_getenv("GITTEST_GITHUB_SSH_REMOTE_HOSTKEY");
+
+ _orig_http_proxy = cl_getenv("HTTP_PROXY");
+ _orig_https_proxy = cl_getenv("HTTPS_PROXY");
+ _orig_no_proxy = cl_getenv("NO_PROXY");
+
+ _orig_ssh_cmd = cl_getenv("GIT_SSH");
+ _ssh_cmd = cl_getenv("GITTEST_SSH_CMD");
+
+ if (_ssh_cmd)
+ cl_setenv("GIT_SSH", _ssh_cmd);
+ else
+ cl_setenv("GIT_SSH", NULL);
if (_remote_expectcontinue)
git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 1);
- _orig_proxies_need_reset = 0;
+#if !defined(GIT_WIN32)
+ /*
+ * On system that allows ':' in filenames "http://path" can be misinterpreted
+ * as the local path "http:/path".
+ * Create a local non-repository path that looks like LIVE_REPO_URL to make
+ * sure we can handle cloning despite this directory being around.
+ */
+ git_futils_mkdir_r(LIVE_REPO_AS_DIR, 0777);
+#endif
}
void test_online_clone__cleanup(void)
@@ -101,6 +138,10 @@ void test_online_clone__cleanup(void)
cl_fixture_cleanup("./initial");
cl_fixture_cleanup("./subsequent");
+#if !defined(GIT_WIN32)
+ cl_fixture_cleanup("http:");
+#endif
+
git__free(_remote_url);
git__free(_remote_user);
git__free(_remote_pass);
@@ -118,18 +159,30 @@ void test_online_clone__cleanup(void)
git__free(_remote_expectcontinue);
git__free(_remote_redirect_initial);
git__free(_remote_redirect_subsequent);
+ git__free(_remote_speed_timesout);
+ git__free(_remote_speed_slow);
- if (_orig_proxies_need_reset) {
- cl_setenv("HTTP_PROXY", _orig_http_proxy);
- cl_setenv("HTTPS_PROXY", _orig_https_proxy);
- cl_setenv("NO_PROXY", _orig_no_proxy);
+ git__free(_github_ssh_pubkey);
+ git__free(_github_ssh_privkey);
+ git__free(_github_ssh_passphrase);
+ git__free(_github_ssh_remotehostkey);
- git__free(_orig_http_proxy);
- git__free(_orig_https_proxy);
- git__free(_orig_no_proxy);
- }
+ cl_setenv("HTTP_PROXY", _orig_http_proxy);
+ cl_setenv("HTTPS_PROXY", _orig_https_proxy);
+ cl_setenv("NO_PROXY", _orig_no_proxy);
+
+ git__free(_orig_http_proxy);
+ git__free(_orig_https_proxy);
+ git__free(_orig_no_proxy);
+
+ cl_setenv("GIT_SSH", _orig_ssh_cmd);
+ git__free(_orig_ssh_cmd);
+
+ git__free(_ssh_cmd);
git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, NULL, NULL);
+ git_libgit2_opts(GIT_OPT_SET_SERVER_TIMEOUT, 0);
+ git_libgit2_opts(GIT_OPT_SET_SERVER_CONNECT_TIMEOUT, 0);
}
void test_online_clone__network_full(void)
@@ -554,16 +607,89 @@ static int check_ssh_auth_methods(git_credential **cred, const char *url, const
return GIT_EUSER;
}
+static int succeed_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
+{
+ GIT_UNUSED(cert);
+ GIT_UNUSED(valid);
+ GIT_UNUSED(payload);
+
+ cl_assert_equal_s("github.com", host);
+
+ return 0;
+}
+
+static int x509_succeed_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
+{
+ GIT_UNUSED(valid);
+ GIT_UNUSED(payload);
+
+ cl_assert_equal_s("github.com", host);
+ cl_assert_equal_i(GIT_CERT_X509, cert->cert_type);
+
+ return 0;
+}
+
+static int fail_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
+{
+ GIT_UNUSED(cert);
+ GIT_UNUSED(valid);
+ GIT_UNUSED(host);
+ GIT_UNUSED(payload);
+
+ return GIT_ECERTIFICATE;
+}
+
+static int github_credentials(
+ git_credential **cred,
+ const char *url,
+ const char *username_from_url,
+ unsigned int allowed_types,
+ void *data)
+{
+ GIT_UNUSED(url);
+ GIT_UNUSED(username_from_url);
+ GIT_UNUSED(data);
+
+ if ((allowed_types & GIT_CREDENTIAL_USERNAME) != 0) {
+ return git_credential_username_new(cred, "git");
+ }
+
+ cl_assert((allowed_types & GIT_CREDENTIAL_SSH_KEY) != 0);
+
+ return git_credential_ssh_key_memory_new(cred,
+ "git",
+ _github_ssh_pubkey,
+ _github_ssh_privkey,
+ _github_ssh_passphrase);
+}
+
+void test_online_clone__ssh_github(void)
+{
+#if !defined(GIT_SSH) || !defined(GIT_SSH_MEMORY_CREDENTIALS)
+ clar__skip();
+#endif
+
+ if (!_github_ssh_pubkey || !_github_ssh_privkey)
+ clar__skip();
+
+ cl_fake_homedir(NULL);
+
+ g_options.fetch_opts.callbacks.credentials = github_credentials;
+ g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
+
+ cl_git_pass(git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
+}
+
void test_online_clone__ssh_auth_methods(void)
{
int with_user;
-#ifndef GIT_SSH
+#ifndef GIT_SSH_LIBSSH2
clar__skip();
#endif
g_options.fetch_opts.callbacks.credentials = check_ssh_auth_methods;
g_options.fetch_opts.callbacks.payload = &with_user;
- g_options.fetch_opts.callbacks.certificate_check = NULL;
+ g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
with_user = 0;
cl_git_fail_with(GIT_EUSER,
@@ -574,6 +700,69 @@ void test_online_clone__ssh_auth_methods(void)
git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options));
}
+/*
+ * Ensure that the certificate check callback is still called, and
+ * can accept a host key that is not in the known hosts file.
+ */
+void test_online_clone__ssh_certcheck_accepts_unknown(void)
+{
+#if !defined(GIT_SSH_LIBSSH2) || !defined(GIT_SSH_MEMORY_CREDENTIALS)
+ clar__skip();
+#endif
+
+ if (!_github_ssh_pubkey || !_github_ssh_privkey)
+ clar__skip();
+
+ cl_fake_homedir(NULL);
+
+ g_options.fetch_opts.callbacks.credentials = github_credentials;
+
+ /* Ensure we fail without the certificate check */
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, SSH_REPO_URL, "./foo", NULL));
+
+ /* Set the callback to accept the certificate */
+ g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
+
+ cl_git_pass(git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
+}
+
+/*
+ * Ensure that the known hosts file is read and the certificate check
+ * callback is still called after that.
+ */
+void test_online_clone__ssh_certcheck_override_knownhosts(void)
+{
+ git_str knownhostsfile = GIT_STR_INIT;
+
+#if !defined(GIT_SSH) || !defined(GIT_SSH_MEMORY_CREDENTIALS)
+ clar__skip();
+#endif
+
+ if (!_github_ssh_pubkey || !_github_ssh_privkey || !_github_ssh_remotehostkey)
+ clar__skip();
+
+ g_options.fetch_opts.callbacks.credentials = github_credentials;
+
+ cl_fake_homedir(&knownhostsfile);
+ cl_git_pass(git_str_joinpath(&knownhostsfile, knownhostsfile.ptr, ".ssh"));
+ cl_git_pass(p_mkdir(knownhostsfile.ptr, 0777));
+
+ cl_git_pass(git_str_joinpath(&knownhostsfile, knownhostsfile.ptr, "known_hosts"));
+ cl_git_rewritefile(knownhostsfile.ptr, _github_ssh_remotehostkey);
+
+ /* Ensure we succeed without the certificate check */
+ cl_git_pass(git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ /* Set the callback to reject the certificate */
+ g_options.fetch_opts.callbacks.certificate_check = fail_certificate_check;
+ cl_git_fail_with(GIT_ECERTIFICATE, git_clone(&g_repo, SSH_REPO_URL, "./bar", &g_options));
+
+ git_str_dispose(&knownhostsfile);
+}
+
static int custom_remote_ssh_with_paths(
git_remote **out,
git_repository *repo,
@@ -635,9 +824,10 @@ static int cred_foo_bar(git_credential **cred, const char *url, const char *user
void test_online_clone__ssh_cannot_change_username(void)
{
-#ifndef GIT_SSH
+#ifndef GIT_SSH_LIBSSH2
clar__skip();
#endif
+
g_options.fetch_opts.callbacks.credentials = cred_foo_bar;
cl_git_fail(git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options));
@@ -679,6 +869,10 @@ static int ssh_certificate_check(git_cert *cert, int valid, const char *host, vo
void test_online_clone__ssh_cert(void)
{
+#ifndef GIT_SSH_LIBSSH2
+ cl_skip();
+#endif
+
g_options.fetch_opts.callbacks.certificate_check = ssh_certificate_check;
if (!_remote_ssh_fingerprint)
@@ -746,43 +940,22 @@ void test_online_clone__ssh_memory_auth(void)
cl_git_pass(git_clone(&g_repo, _remote_url, "./foo", &g_options));
}
-static int fail_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
-{
- GIT_UNUSED(cert);
- GIT_UNUSED(valid);
- GIT_UNUSED(host);
- GIT_UNUSED(payload);
-
- return GIT_ECERTIFICATE;
-}
-
void test_online_clone__certificate_invalid(void)
{
g_options.fetch_opts.callbacks.certificate_check = fail_certificate_check;
- cl_git_fail_with(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options),
- GIT_ECERTIFICATE);
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
-#ifdef GIT_SSH
- cl_git_fail_with(git_clone(&g_repo, "ssh://github.com/libgit2/TestGitRepository", "./foo", &g_options),
- GIT_ECERTIFICATE);
+#ifdef GIT_SSH_LIBSSH2
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, "ssh://github.com/libgit2/TestGitRepository", "./foo", &g_options));
#endif
}
-static int succeed_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
-{
- GIT_UNUSED(cert);
- GIT_UNUSED(valid);
- GIT_UNUSED(payload);
-
- cl_assert_equal_s("github.com", host);
-
- return 0;
-}
-
void test_online_clone__certificate_valid(void)
{
- g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
+ g_options.fetch_opts.callbacks.certificate_check = x509_succeed_certificate_check;
cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
}
@@ -830,6 +1003,92 @@ static int proxy_cert_cb(git_cert *cert, int valid, const char *host, void *payl
return valid ? 0 : GIT_ECERTIFICATE;
}
+void test_online_clone__proxy_http_host_port_in_opts(void)
+{
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ if (_remote_proxy_scheme && strcmp(_remote_proxy_scheme, "http") != 0)
+ cl_skip();
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ g_options.fetch_opts.proxy_opts.url = _remote_proxy_host;
+ g_options.fetch_opts.proxy_opts.credentials = proxy_cred_cb;
+
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds == 1);
+}
+
+void test_online_clone__proxy_http_host_port_in_env(void)
+{
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ if (_remote_proxy_scheme && strcmp(_remote_proxy_scheme, "http") != 0)
+ cl_skip();
+
+ cl_setenv("HTTP_PROXY", _remote_proxy_host);
+ cl_setenv("HTTPS_PROXY", _remote_proxy_host);
+ cl_setenv("NO_PROXY", NULL);
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
+ g_options.fetch_opts.proxy_opts.credentials = proxy_cred_cb;
+
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds == 1);
+}
+
+static int repository_create_with_proxy(
+ git_repository **out,
+ const char *path,
+ int bare,
+ void *payload)
+{
+ git_repository *repo;
+ git_config *config;
+ char *value = (char *)payload;
+
+ cl_git_pass(git_repository_init(&repo, path, bare));
+ cl_git_pass(git_repository_config(&config, repo));
+
+ cl_git_pass(git_config_set_string(config, "http.proxy", value));
+
+ git_config_free(config);
+
+ *out = repo;
+ return 0;
+}
+
+void test_online_clone__proxy_http_host_port_in_config(void)
+{
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
+ g_options.fetch_opts.proxy_opts.credentials = proxy_cred_cb;
+ g_options.repository_cb = repository_create_with_proxy;
+ g_options.repository_cb_payload = _remote_proxy_host;
+
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds == 1);
+}
+
+void test_online_clone__proxy_invalid_url(void)
+{
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ g_options.fetch_opts.proxy_opts.credentials = proxy_cred_cb;
+ g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
+
+ g_options.fetch_opts.proxy_opts.url = "noschemeorport";
+ cl_git_fail(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+
+ g_options.fetch_opts.proxy_opts.url = "noscheme:8080";
+ cl_git_fail(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+}
+
void test_online_clone__proxy_credentials_request(void)
{
git_str url = GIT_STR_INIT;
@@ -852,7 +1111,7 @@ void test_online_clone__proxy_credentials_request(void)
git_str_dispose(&url);
}
-void test_online_clone__proxy_credentials_in_url(void)
+void test_online_clone__proxy_credentials_in_well_formed_url(void)
{
git_str url = GIT_STR_INIT;
@@ -873,17 +1132,35 @@ void test_online_clone__proxy_credentials_in_url(void)
git_str_dispose(&url);
}
-void test_online_clone__proxy_credentials_in_environment(void)
+void test_online_clone__proxy_credentials_in_host_port_format(void)
{
git_str url = GIT_STR_INIT;
if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
cl_skip();
- _orig_http_proxy = cl_getenv("HTTP_PROXY");
- _orig_https_proxy = cl_getenv("HTTPS_PROXY");
- _orig_no_proxy = cl_getenv("NO_PROXY");
- _orig_proxies_need_reset = 1;
+ if (_remote_proxy_scheme && strcmp(_remote_proxy_scheme, "http") != 0)
+ cl_skip();
+
+ cl_git_pass(git_str_printf(&url, "%s:%s@%s",
+ _remote_proxy_user, _remote_proxy_pass, _remote_proxy_host));
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ g_options.fetch_opts.proxy_opts.url = url.ptr;
+ g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds == 0);
+
+ git_str_dispose(&url);
+}
+
+void test_online_clone__proxy_credentials_in_environment(void)
+{
+ git_str url = GIT_STR_INIT;
+
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
@@ -1058,3 +1335,90 @@ void test_online_clone__namespace_with_specified_branch(void)
git_reference_free(head);
}
+
+void test_online_clone__sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
+ git_reference *head;
+
+ if (!_remote_url)
+ cl_skip();
+
+ cl_git_pass(git_clone(&g_repo, _remote_url, "./sha256", &options));
+ cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head));
+
+ git_reference_free(head);
+#endif
+}
+
+void test_online_clone__connect_timeout_configurable(void)
+{
+#ifdef GIT_WINHTTP
+ cl_skip();
+#else
+ uint64_t start, finish;
+
+ start = git_time_monotonic();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SERVER_CONNECT_TIMEOUT, 1));
+ cl_git_fail(git_clone(&g_repo, "http://www.google.com:8000/", "./timedout", NULL));
+ cl_assert(git_error_last() && strstr(git_error_last()->message, "timed out"));
+
+ finish = git_time_monotonic();
+
+ cl_assert(finish - start < 1000);
+#endif
+}
+
+void test_online_clone__connect_timeout_default(void)
+{
+#ifdef GIT_WINHTTP
+ cl_skip();
+#else
+ /* This test takes ~ 75 seconds on Unix. */
+ if (!cl_is_env_set("GITTEST_INVASIVE_SPEED"))
+ cl_skip();
+
+ /*
+ * Use a host/port pair that blackholes packets and does not
+ * send an RST.
+ */
+ cl_git_fail_with(GIT_TIMEOUT, git_clone(&g_repo, "http://www.google.com:8000/", "./refused", NULL));
+ cl_assert(git_error_last() && strstr(git_error_last()->message, "timed out"));
+#endif
+}
+
+void test_online_clone__timeout_configurable_times_out(void)
+{
+#ifdef GIT_WINHTTP
+ cl_skip();
+#else
+ git_repository *failed_repo;
+
+ if (!_remote_speed_timesout)
+ cl_skip();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SERVER_TIMEOUT, 1000));
+
+ cl_git_fail_with(GIT_TIMEOUT, git_clone(&failed_repo, _remote_speed_timesout, "./timedout", NULL));
+ cl_assert(git_error_last() && strstr(git_error_last()->message, "timed out"));
+#endif
+}
+
+void test_online_clone__timeout_configurable_succeeds_slowly(void)
+{
+#ifdef GIT_WINHTTP
+ cl_skip();
+#else
+ if (!_remote_speed_slow)
+ cl_skip();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SERVER_TIMEOUT, 1000));
+
+ cl_git_pass(git_clone(&g_repo, _remote_speed_slow, "./slow-but-successful", NULL));
+#endif
+}
diff --git a/tests/libgit2/online/fetch.c b/tests/libgit2/online/fetch.c
index a557bbf7506..08a14c6317d 100644
--- a/tests/libgit2/online/fetch.c
+++ b/tests/libgit2/online/fetch.c
@@ -110,6 +110,26 @@ void test_online_fetch__fetch_twice(void)
git_remote_free(remote);
}
+void test_online_fetch__fetch_with_empty_http_proxy(void)
+{
+ git_remote *remote;
+ git_config *config;
+ git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
+
+ opts.proxy_opts.type = GIT_PROXY_AUTO;
+
+ cl_git_pass(git_repository_config(&config, _repo));
+ cl_git_pass(git_config_set_string(config, "http.proxy", ""));
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test",
+ "https://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_remote_fetch(remote, NULL, &opts, NULL));
+
+ git_remote_disconnect(remote);
+ git_remote_free(remote);
+ git_config_free(config);
+}
+
static int transferProgressCallback(const git_indexer_progress *stats, void *payload)
{
bool *invoked = (bool *)payload;
@@ -128,6 +148,8 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
opts.bare = true;
+ counter = 0;
+
cl_git_pass(git_clone(&_repository, "https://github.com/libgit2/TestGitRepository.git",
"./fetch/lg2", &opts));
git_repository_free(_repository);
@@ -141,11 +163,52 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date
options.callbacks.transfer_progress = &transferProgressCallback;
options.callbacks.payload = &invoked;
+ options.callbacks.update_tips = update_tips;
cl_git_pass(git_remote_download(remote, NULL, &options));
cl_assert_equal_i(false, invoked);
- cl_git_pass(git_remote_update_tips(remote, &options.callbacks, 1, options.download_tags, NULL));
+ cl_git_pass(git_remote_update_tips(remote, &options.callbacks, GIT_REMOTE_UPDATE_FETCHHEAD, options.download_tags, NULL));
+ cl_assert_equal_i(0, counter);
+
+ git_remote_disconnect(remote);
+
+ git_remote_free(remote);
+ git_repository_free(_repository);
+}
+
+void test_online_fetch__report_unchanged_tips(void)
+{
+ git_repository *_repository;
+ bool invoked = false;
+ git_remote *remote;
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ opts.bare = true;
+
+ counter = 0;
+
+ cl_git_pass(git_clone(&_repository, "https://github.com/libgit2/TestGitRepository.git",
+ "./fetch/lg2", &opts));
+ git_repository_free(_repository);
+
+ cl_git_pass(git_repository_open(&_repository, "./fetch/lg2"));
+
+ cl_git_pass(git_remote_lookup(&remote, _repository, "origin"));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+
+ cl_assert_equal_i(false, invoked);
+
+ options.callbacks.transfer_progress = &transferProgressCallback;
+ options.callbacks.payload = &invoked;
+ options.callbacks.update_tips = update_tips;
+ cl_git_pass(git_remote_download(remote, NULL, &options));
+
+ cl_assert_equal_i(false, invoked);
+
+ cl_git_pass(git_remote_update_tips(remote, &options.callbacks, GIT_REMOTE_UPDATE_REPORT_UNCHANGED, options.download_tags, NULL));
+ cl_assert(counter > 0);
+
git_remote_disconnect(remote);
git_remote_free(remote);
diff --git a/tests/libgit2/online/push.c b/tests/libgit2/online/push.c
index 204572cf57b..e5693bf346c 100644
--- a/tests/libgit2/online/push.c
+++ b/tests/libgit2/online/push.c
@@ -5,6 +5,7 @@
#include "push_util.h"
#include "refspec.h"
#include "remote.h"
+#include "futils.h"
static git_repository *_repo;
@@ -20,7 +21,12 @@ static char *_remote_ssh_passphrase = NULL;
static char *_remote_default = NULL;
static char *_remote_expectcontinue = NULL;
-static int cred_acquire_cb(git_credential **, const char *, const char *, unsigned int, void *);
+static char *_remote_push_options = NULL;
+
+static char *_orig_ssh_cmd = NULL;
+static char *_ssh_cmd = NULL;
+
+static int cred_acquire_cb(git_credential **, const char *, const char *, unsigned int, void *);
static git_remote *_remote;
static record_callbacks_data _record_cbs_data = {{ 0 }};
@@ -367,8 +373,17 @@ void test_online_push__initialize(void)
_remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
_remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT");
_remote_expectcontinue = cl_getenv("GITTEST_REMOTE_EXPECTCONTINUE");
+ _remote_push_options = cl_getenv("GITTEST_PUSH_OPTIONS");
_remote = NULL;
+ _orig_ssh_cmd = cl_getenv("GIT_SSH");
+ _ssh_cmd = cl_getenv("GITTEST_SSH_CMD");
+
+ if (_ssh_cmd)
+ cl_setenv("GIT_SSH", _ssh_cmd);
+ else
+ cl_setenv("GIT_SSH", NULL);
+
/* Skip the test if we're missing the remote URL */
if (!_remote_url)
cl_skip();
@@ -422,6 +437,10 @@ void test_online_push__cleanup(void)
git__free(_remote_ssh_passphrase);
git__free(_remote_default);
git__free(_remote_expectcontinue);
+ git__free(_remote_push_options);
+
+ git__free(_orig_ssh_cmd);
+ git__free(_ssh_cmd);
/* Freed by cl_git_sandbox_cleanup */
_repo = NULL;
@@ -430,6 +449,7 @@ void test_online_push__cleanup(void)
record_callbacks_data_clear(&_record_cbs_data);
+ cl_fixture_cleanup("push-options-result");
cl_fixture_cleanup("testrepo.git");
cl_git_sandbox_cleanup();
}
@@ -472,7 +492,8 @@ static void do_push(
const char *refspecs[], size_t refspecs_len,
push_status expected_statuses[], size_t expected_statuses_len,
expected_ref expected_refs[], size_t expected_refs_len,
- int expected_ret, int check_progress_cb, int check_update_tips_cb)
+ int expected_ret, int check_progress_cb, int check_update_tips_cb,
+ git_strarray *remote_push_options)
{
git_push_options opts = GIT_PUSH_OPTIONS_INIT;
size_t i;
@@ -484,6 +505,9 @@ static void do_push(
/* Auto-detect the number of threads to use */
opts.pb_parallelism = 0;
+ if (remote_push_options)
+ memcpy(&opts.remote_push_options, remote_push_options, sizeof(git_strarray));
+
memcpy(&opts.callbacks, &_record_cbs, sizeof(git_remote_callbacks));
data = opts.callbacks.payload;
@@ -527,13 +551,12 @@ static void do_push(
verify_update_tips_callback(_remote, expected_refs, expected_refs_len);
}
-
}
/* Call push_finish() without ever calling git_push_add_refspec() */
void test_online_push__noop(void)
{
- do_push(NULL, 0, NULL, 0, NULL, 0, 0, 0, 1);
+ do_push(NULL, 0, NULL, 0, NULL, 0, 0, 0, 1, NULL);
}
void test_online_push__b1(void)
@@ -543,7 +566,9 @@ void test_online_push__b1(void)
expected_ref exp_refs[] = { { "refs/heads/b1", &_oid_b1 } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
}
void test_online_push__b2(void)
@@ -553,7 +578,9 @@ void test_online_push__b2(void)
expected_ref exp_refs[] = { { "refs/heads/b2", &_oid_b2 } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
}
void test_online_push__b3(void)
@@ -563,7 +590,9 @@ void test_online_push__b3(void)
expected_ref exp_refs[] = { { "refs/heads/b3", &_oid_b3 } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
}
void test_online_push__b4(void)
@@ -573,7 +602,9 @@ void test_online_push__b4(void)
expected_ref exp_refs[] = { { "refs/heads/b4", &_oid_b4 } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
}
void test_online_push__b5(void)
@@ -583,13 +614,15 @@ void test_online_push__b5(void)
expected_ref exp_refs[] = { { "refs/heads/b5", &_oid_b5 } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
}
void test_online_push__b5_cancel(void)
{
const char *specs[] = { "refs/heads/b5:refs/heads/b5" };
- do_push(specs, ARRAY_SIZE(specs), NULL, 0, NULL, 0, GIT_EUSER, 1, 1);
+ do_push(specs, ARRAY_SIZE(specs), NULL, 0, NULL, 0, GIT_EUSER, 1, 1, NULL);
}
void test_online_push__multi(void)
@@ -620,7 +653,9 @@ void test_online_push__multi(void)
};
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
cl_git_pass(git_reflog_read(&log, _repo, "refs/remotes/test/b1"));
entry = git_reflog_entry_byindex(log, 0);
@@ -641,16 +676,21 @@ void test_online_push__implicit_tgt(void)
const char *specs2[] = { "refs/heads/b2" };
push_status exp_stats2[] = { { "refs/heads/b2", 1 } };
expected_ref exp_refs2[] = {
- { "refs/heads/b1", &_oid_b1 },
- { "refs/heads/b2", &_oid_b2 }
+ { "refs/heads/b1", &_oid_b1 },
+ { "refs/heads/b2", &_oid_b2 }
};
do_push(specs1, ARRAY_SIZE(specs1),
exp_stats1, ARRAY_SIZE(exp_stats1),
- exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1, 1);
+ exp_refs1, ARRAY_SIZE(exp_refs1),
+ 0, 1, 1,
+ NULL);
+
do_push(specs2, ARRAY_SIZE(specs2),
exp_stats2, ARRAY_SIZE(exp_stats2),
- exp_refs2, ARRAY_SIZE(exp_refs2), 0, 0, 0);
+ exp_refs2, ARRAY_SIZE(exp_refs2),
+ 0, 0, 0,
+ NULL);
}
void test_online_push__fast_fwd(void)
@@ -672,19 +712,27 @@ void test_online_push__fast_fwd(void)
do_push(specs_init, ARRAY_SIZE(specs_init),
exp_stats_init, ARRAY_SIZE(exp_stats_init),
- exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 1, 1);
+ exp_refs_init, ARRAY_SIZE(exp_refs_init),
+ 0, 1, 1,
+ NULL);
do_push(specs_ff, ARRAY_SIZE(specs_ff),
exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
- exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0, 0);
+ exp_refs_ff, ARRAY_SIZE(exp_refs_ff),
+ 0, 0, 0,
+ NULL);
do_push(specs_reset, ARRAY_SIZE(specs_reset),
exp_stats_init, ARRAY_SIZE(exp_stats_init),
- exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 0, 0);
+ exp_refs_init, ARRAY_SIZE(exp_refs_init),
+ 0, 0, 0,
+ NULL);
do_push(specs_ff_force, ARRAY_SIZE(specs_ff_force),
exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
- exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0, 0);
+ exp_refs_ff, ARRAY_SIZE(exp_refs_ff),
+ 0, 0, 0,
+ NULL);
}
void test_online_push__tag_commit(void)
@@ -694,7 +742,9 @@ void test_online_push__tag_commit(void)
expected_ref exp_refs[] = { { "refs/tags/tag-commit", &_tag_commit } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
}
void test_online_push__tag_tree(void)
@@ -704,7 +754,9 @@ void test_online_push__tag_tree(void)
expected_ref exp_refs[] = { { "refs/tags/tag-tree", &_tag_tree } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
}
void test_online_push__tag_blob(void)
@@ -714,7 +766,9 @@ void test_online_push__tag_blob(void)
expected_ref exp_refs[] = { { "refs/tags/tag-blob", &_tag_blob } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
}
void test_online_push__tag_lightweight(void)
@@ -724,7 +778,9 @@ void test_online_push__tag_lightweight(void)
expected_ref exp_refs[] = { { "refs/tags/tag-lightweight", &_tag_lightweight } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
}
void test_online_push__tag_to_tag(void)
@@ -734,7 +790,9 @@ void test_online_push__tag_to_tag(void)
expected_ref exp_refs[] = { { "refs/tags/tag-tag", &_tag_tag } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 0, 0);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 0, 0,
+ NULL);
}
void test_online_push__force(void)
@@ -751,17 +809,80 @@ void test_online_push__force(void)
do_push(specs1, ARRAY_SIZE(specs1),
exp_stats1, ARRAY_SIZE(exp_stats1),
- exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1, 1);
+ exp_refs1, ARRAY_SIZE(exp_refs1),
+ 0, 1, 1,
+ NULL);
do_push(specs2, ARRAY_SIZE(specs2),
NULL, 0,
- exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD, 0, 0);
+ exp_refs1, ARRAY_SIZE(exp_refs1),
+ GIT_ENONFASTFORWARD, 0, 0,
+ NULL);
/* Non-fast-forward update with force should pass. */
record_callbacks_data_clear(&_record_cbs_data);
do_push(specs2_force, ARRAY_SIZE(specs2_force),
exp_stats2_force, ARRAY_SIZE(exp_stats2_force),
- exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0, 1, 1);
+ exp_refs2_force, ARRAY_SIZE(exp_refs2_force),
+ 0, 1, 1,
+ NULL);
+}
+
+static void push_option_test(git_strarray given_options, const char *expected_option)
+{
+ const char *specs[] = { "refs/heads/b1:refs/heads/b1" };
+ push_status exp_stats[] = { { "refs/heads/b1", 1 } };
+ expected_ref exp_refs[] = { { "refs/heads/b1", &_oid_b1 } };
+ git_str push_options_path = GIT_STR_INIT;
+ git_str push_options_result = GIT_STR_INIT;
+ char *options[16];
+ git_strarray push_options = { options, given_options.count + 1 };
+ size_t i;
+
+ /* Skip the test if we're missing the push options result file */
+ if (!_remote_push_options)
+ cl_skip();
+
+ cl_assert(given_options.count < 16);
+
+ cl_git_pass(git_str_joinpath(&push_options_path, clar_sandbox_path(), "push-options-result"));
+
+ options[0] = push_options_path.ptr;
+ for (i = 0; i < given_options.count; i++)
+ options[i + 1] = given_options.strings[i];
+
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ &push_options);
+
+ cl_assert(git_fs_path_exists(push_options_path.ptr));
+ cl_git_pass(git_futils_readbuffer(&push_options_result, push_options_path.ptr));
+
+ cl_assert_equal_s(expected_option, git_str_cstr(&push_options_result));
+ git_str_dispose(&push_options_result);
+ git_str_dispose(&push_options_path);
+}
+
+void test_online_push__options(void)
+{
+ char *push_options_string_args_test_1[1] = { "test_string" };
+ git_strarray push_options_test_1 = { push_options_string_args_test_1, 1 };
+
+ char *push_options_string_args_test_2[2] = { "test_string", "another arg?" };
+ git_strarray push_options_test_2 = { push_options_string_args_test_2, 2 };
+
+ char *push_options_string_args_test_3[1] = { "👨ðŸ¿â€ðŸ’» but can it do unicode? 🇺🇦" };
+ git_strarray push_options_test_3 = { push_options_string_args_test_3, 1 };
+
+ char *push_options_string_args_test_4[3] = { "\0", "\0", "\0" };
+ git_strarray push_options_test_4 = { push_options_string_args_test_4, 3 };
+
+ push_option_test(push_options_test_1, "test_string");
+ push_option_test(push_options_test_2, "test_stringanother arg?");
+ push_option_test(push_options_test_3, "👨ðŸ¿â€ðŸ’» but can it do unicode? 🇺🇦");
+ push_option_test(push_options_test_4, "\0\0\0");
}
void test_online_push__delete(void)
@@ -792,7 +913,9 @@ void test_online_push__delete(void)
do_push(specs1, ARRAY_SIZE(specs1),
exp_stats1, ARRAY_SIZE(exp_stats1),
- exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1, 1);
+ exp_refs1, ARRAY_SIZE(exp_refs1),
+ 0, 1, 1,
+ NULL);
/* When deleting a non-existent branch, the git client sends zero for both
* the old and new commit id. This should succeed on the server with the
@@ -802,23 +925,35 @@ void test_online_push__delete(void)
*/
do_push(specs_del_fake, ARRAY_SIZE(specs_del_fake),
exp_stats_fake, 1,
- exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0, 0);
+ exp_refs1, ARRAY_SIZE(exp_refs1),
+ 0, 0, 0,
+ NULL);
+
do_push(specs_del_fake_force, ARRAY_SIZE(specs_del_fake_force),
exp_stats_fake, 1,
- exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0, 0);
+ exp_refs1, ARRAY_SIZE(exp_refs1),
+ 0, 0, 0,
+ NULL);
/* Delete one of the pushed branches. */
do_push(specs_delete, ARRAY_SIZE(specs_delete),
exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
- exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0, 0);
+ exp_refs_delete, ARRAY_SIZE(exp_refs_delete),
+ 0, 0, 0,
+ NULL);
/* Re-push branches and retry delete with force. */
do_push(specs1, ARRAY_SIZE(specs1),
exp_stats1, ARRAY_SIZE(exp_stats1),
- exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0, 0);
+ exp_refs1, ARRAY_SIZE(exp_refs1),
+ 0, 0, 0,
+ NULL);
+
do_push(specs_delete_force, ARRAY_SIZE(specs_delete_force),
exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
- exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0, 0);
+ exp_refs_delete, ARRAY_SIZE(exp_refs_delete),
+ 0, 0, 0,
+ NULL);
}
void test_online_push__bad_refspecs(void)
@@ -862,7 +997,9 @@ void test_online_push__expressions(void)
do_push(specs_left_expr, ARRAY_SIZE(specs_left_expr),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
}
void test_online_push__notes(void)
@@ -884,13 +1021,17 @@ void test_online_push__notes(void)
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
/* And make sure to delete the note */
do_push(specs_del, ARRAY_SIZE(specs_del),
exp_stats, 1,
- NULL, 0, 0, 0, 0);
+ NULL, 0,
+ 0, 0, 0,
+ NULL);
git_signature_free(signature);
}
@@ -920,13 +1061,17 @@ void test_online_push__configured(void)
do_push(NULL, 0,
exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+ exp_refs, ARRAY_SIZE(exp_refs),
+ 0, 1, 1,
+ NULL);
/* And make sure to delete the note */
do_push(specs_del, ARRAY_SIZE(specs_del),
exp_stats, 1,
- NULL, 0, 0, 0, 0);
+ NULL, 0,
+ 0, 0, 0,
+ NULL);
git_signature_free(signature);
}
diff --git a/tests/libgit2/online/shallow.c b/tests/libgit2/online/shallow.c
new file mode 100644
index 00000000000..ee4aaf37ed4
--- /dev/null
+++ b/tests/libgit2/online/shallow.c
@@ -0,0 +1,265 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "repository.h"
+
+static int remote_single_branch(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ cl_git_pass(git_remote_create_with_fetchspec(out, repo, name, url, "+refs/heads/master:refs/remotes/origin/master"));
+
+ return 0;
+}
+
+void test_online_shallow__clone_depth_zero(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_oid *roots;
+ size_t roots_len;
+
+ clone_opts.fetch_opts.depth = 0;
+ clone_opts.remote_cb = remote_single_branch;
+
+ git_str_joinpath(&path, clar_sandbox_path(), "shallowclone_0");
+
+ cl_git_pass(git_clone(&repo, "https://github.com/libgit2/TestGitRepository", git_str_cstr(&path), &clone_opts));
+
+ /* cloning with depth 0 results in a full clone. */
+ cl_assert_equal_b(false, git_repository_is_shallow(repo));
+
+ /* full clones do not have shallow roots. */
+ cl_git_pass(git_repository__shallow_roots(&roots, &roots_len, repo));
+ cl_assert_equal_i(0, roots_len);
+
+ git__free(roots);
+ git_str_dispose(&path);
+ git_repository_free(repo);
+}
+
+void test_online_shallow__clone_depth_one(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_revwalk *walk;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_oid oid;
+ git_oid *roots;
+ size_t roots_len;
+ size_t num_commits = 0;
+ int error = 0;
+
+ clone_opts.fetch_opts.depth = 1;
+ clone_opts.remote_cb = remote_single_branch;
+
+ git_str_joinpath(&path, clar_sandbox_path(), "shallowclone_1");
+
+ cl_git_pass(git_clone(&repo, "https://github.com/libgit2/TestGitRepository", git_str_cstr(&path), &clone_opts));
+
+ cl_assert_equal_b(true, git_repository_is_shallow(repo));
+
+ cl_git_pass(git_repository__shallow_roots(&roots, &roots_len, repo));
+ cl_assert_equal_i(1, roots_len);
+ cl_assert_equal_s("49322bb17d3acc9146f98c97d078513228bbf3c0", git_oid_tostr_s(&roots[0]));
+
+ git_revwalk_new(&walk, repo);
+
+ git_revwalk_push_head(walk);
+
+ while ((error = git_revwalk_next(&oid, walk)) == GIT_OK) {
+ num_commits++;
+ }
+
+ cl_assert_equal_i(num_commits, 1);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git__free(roots);
+ git_str_dispose(&path);
+ git_revwalk_free(walk);
+ git_repository_free(repo);
+}
+
+void test_online_shallow__clone_depth_five(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_revwalk *walk;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_oid oid;
+ git_oid *roots;
+ size_t roots_len;
+ size_t num_commits = 0;
+ int error = 0;
+
+ clone_opts.fetch_opts.depth = 5;
+ clone_opts.remote_cb = remote_single_branch;
+
+ git_str_joinpath(&path, clar_sandbox_path(), "shallowclone_5");
+
+ cl_git_pass(git_clone(&repo, "https://github.com/libgit2/TestGitRepository", git_str_cstr(&path), &clone_opts));
+
+ cl_assert_equal_b(true, git_repository_is_shallow(repo));
+
+ cl_git_pass(git_repository__shallow_roots(&roots, &roots_len, repo));
+ cl_assert_equal_i(3, roots_len);
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&roots[0]));
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&roots[1]));
+ cl_assert_equal_s("83834a7afdaa1a1260568567f6ad90020389f664", git_oid_tostr_s(&roots[2]));
+
+ git_revwalk_new(&walk, repo);
+
+ git_revwalk_push_head(walk);
+
+ while ((error = git_revwalk_next(&oid, walk)) == GIT_OK) {
+ num_commits++;
+ }
+
+ cl_assert_equal_i(num_commits, 13);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git__free(roots);
+ git_str_dispose(&path);
+ git_revwalk_free(walk);
+ git_repository_free(repo);
+}
+
+void test_online_shallow__unshallow(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_revwalk *walk;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
+ git_remote *origin = NULL;
+ git_oid oid;
+ size_t num_commits = 0;
+ int error = 0;
+
+ clone_opts.fetch_opts.depth = 5;
+ clone_opts.remote_cb = remote_single_branch;
+
+ git_str_joinpath(&path, clar_sandbox_path(), "unshallow");
+ cl_git_pass(git_clone(&repo, "https://github.com/libgit2/TestGitRepository", git_str_cstr(&path), &clone_opts));
+ cl_assert_equal_b(true, git_repository_is_shallow(repo));
+
+ fetch_opts.depth = GIT_FETCH_DEPTH_UNSHALLOW;
+ cl_git_pass(git_remote_lookup(&origin, repo, "origin"));
+
+ cl_git_pass(git_remote_fetch(origin, NULL, &fetch_opts, NULL));
+ cl_assert_equal_b(false, git_repository_is_shallow(repo));
+
+ git_revwalk_new(&walk, repo);
+ git_revwalk_push_head(walk);
+
+ while ((error = git_revwalk_next(&oid, walk)) == GIT_OK) {
+ num_commits++;
+ }
+
+ cl_assert_equal_i(num_commits, 21);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git_remote_free(origin);
+ git_str_dispose(&path);
+ git_revwalk_free(walk);
+ git_repository_free(repo);
+}
+
+void test_online_shallow__deepen_six(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_revwalk *walk;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
+ git_remote *origin = NULL;
+ git_oid oid;
+ git_oid *roots;
+ size_t roots_len;
+ size_t num_commits = 0;
+ int error = 0;
+
+ clone_opts.fetch_opts.depth = 5;
+ clone_opts.remote_cb = remote_single_branch;
+
+ git_str_joinpath(&path, clar_sandbox_path(), "deepen_6");
+ cl_git_pass(git_clone(&repo, "https://github.com/libgit2/TestGitRepository", git_str_cstr(&path), &clone_opts));
+ cl_assert_equal_b(true, git_repository_is_shallow(repo));
+
+ fetch_opts.depth = 6;
+ cl_git_pass(git_remote_lookup(&origin, repo, "origin"));
+ cl_git_pass(git_remote_fetch(origin, NULL, &fetch_opts, NULL));
+ cl_assert_equal_b(true, git_repository_is_shallow(repo));
+
+ cl_git_pass(git_repository__shallow_roots(&roots, &roots_len, repo));
+ cl_assert_equal_i(4, roots_len);
+ cl_assert_equal_s("58be4659bb571194ed4562d04b359d26216f526e", git_oid_tostr_s(&roots[0]));
+ cl_assert_equal_s("d31f5a60d406e831d056b8ac2538d515100c2df2", git_oid_tostr_s(&roots[1]));
+ cl_assert_equal_s("6462e7d8024396b14d7651e2ec11e2bbf07a05c4", git_oid_tostr_s(&roots[2]));
+ cl_assert_equal_s("2c349335b7f797072cf729c4f3bb0914ecb6dec9", git_oid_tostr_s(&roots[3]));
+
+ git_revwalk_new(&walk, repo);
+ git_revwalk_push_head(walk);
+
+ while ((error = git_revwalk_next(&oid, walk)) == GIT_OK) {
+ num_commits++;
+ }
+
+ cl_assert_equal_i(num_commits, 17);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git__free(roots);
+ git_remote_free(origin);
+ git_str_dispose(&path);
+ git_revwalk_free(walk);
+ git_repository_free(repo);
+}
+
+void test_online_shallow__shorten_four(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_revwalk *walk;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
+ git_remote *origin = NULL;
+ git_oid oid;
+ git_oid *roots;
+ size_t roots_len;
+ size_t num_commits = 0;
+ int error = 0;
+
+ clone_opts.fetch_opts.depth = 5;
+ clone_opts.remote_cb = remote_single_branch;
+
+ git_str_joinpath(&path, clar_sandbox_path(), "shorten_4");
+ cl_git_pass(git_clone(&repo, "https://github.com/libgit2/TestGitRepository", git_str_cstr(&path), &clone_opts));
+ cl_assert_equal_b(true, git_repository_is_shallow(repo));
+
+ fetch_opts.depth = 4;
+ cl_git_pass(git_remote_lookup(&origin, repo, "origin"));
+ cl_git_pass(git_remote_fetch(origin, NULL, &fetch_opts, NULL));
+ cl_assert_equal_b(true, git_repository_is_shallow(repo));
+
+ cl_git_pass(git_repository__shallow_roots(&roots, &roots_len, repo));
+ cl_assert_equal_i(3, roots_len);
+ cl_assert_equal_s("d86a2aada2f5e7ccf6f11880bfb9ab404e8a8864", git_oid_tostr_s(&roots[0]));
+ cl_assert_equal_s("59706a11bde2b9899a278838ef20a97e8f8795d2", git_oid_tostr_s(&roots[1]));
+ cl_assert_equal_s("bab66b48f836ed950c99134ef666436fb07a09a0", git_oid_tostr_s(&roots[2]));
+
+ git_revwalk_new(&walk, repo);
+ git_revwalk_push_head(walk);
+
+ while ((error = git_revwalk_next(&oid, walk)) == GIT_OK) {
+ num_commits++;
+ }
+
+ cl_assert_equal_i(num_commits, 10);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git__free(roots);
+ git_remote_free(origin);
+ git_str_dispose(&path);
+ git_revwalk_free(walk);
+ git_repository_free(repo);
+}
diff --git a/tests/libgit2/pack/indexer.c b/tests/libgit2/pack/indexer.c
index 2ac2872554f..9722decafc0 100644
--- a/tests/libgit2/pack/indexer.c
+++ b/tests/libgit2/pack/indexer.c
@@ -100,7 +100,12 @@ void test_pack_indexer__out_of_order(void)
git_indexer *idx = 0;
git_indexer_progress stats = { 0 };
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
cl_git_pass(git_indexer_append(
idx, out_of_order_pack, out_of_order_pack_len, &stats));
cl_git_pass(git_indexer_commit(idx, &stats));
@@ -117,7 +122,12 @@ void test_pack_indexer__missing_trailer(void)
git_indexer *idx = 0;
git_indexer_progress stats = { 0 };
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
cl_git_pass(git_indexer_append(
idx, missing_trailer_pack, missing_trailer_pack_len, &stats));
cl_git_fail(git_indexer_commit(idx, &stats));
@@ -133,7 +143,12 @@ void test_pack_indexer__leaky(void)
git_indexer *idx = 0;
git_indexer_progress stats = { 0 };
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
cl_git_pass(git_indexer_append(
idx, leaky_pack, leaky_pack_len, &stats));
cl_git_fail(git_indexer_commit(idx, &stats));
@@ -151,6 +166,7 @@ void test_pack_indexer__fix_thin(void)
git_repository *repo;
git_odb *odb;
git_oid id, should_id;
+ git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
cl_git_pass(git_repository_init(&repo, "thin.git", true));
cl_git_pass(git_repository_odb(&odb, repo));
@@ -160,7 +176,13 @@ void test_pack_indexer__fix_thin(void)
git_oid__fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18", GIT_OID_SHA1);
cl_assert_equal_oid(&should_id, &id);
- cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL));
+#ifdef GIT_EXPERIMENTAL_SHA256
+ opts.odb = odb;
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, &opts));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, odb, &opts));
+#endif
+
cl_git_pass(git_indexer_append(idx, thin_pack, thin_pack_len, &stats));
cl_git_pass(git_indexer_commit(idx, &stats));
@@ -192,7 +214,12 @@ void test_pack_indexer__fix_thin(void)
cl_git_pass(p_stat(name, &st));
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
read = p_read(fd, buffer, sizeof(buffer));
cl_assert(read != -1);
p_close(fd);
@@ -216,6 +243,7 @@ void test_pack_indexer__corrupt_length(void)
git_repository *repo;
git_odb *odb;
git_oid id, should_id;
+ git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
cl_git_pass(git_repository_init(&repo, "thin.git", true));
cl_git_pass(git_repository_odb(&odb, repo));
@@ -225,7 +253,13 @@ void test_pack_indexer__corrupt_length(void)
git_oid__fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18", GIT_OID_SHA1);
cl_assert_equal_oid(&should_id, &id);
- cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL));
+#ifdef GIT_EXPERIMENTAL_SHA256
+ opts.odb = odb;
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, &opts));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, odb, &opts));
+#endif
+
cl_git_pass(git_indexer_append(
idx, corrupt_thin_pack, corrupt_thin_pack_len, &stats));
cl_git_fail(git_indexer_commit(idx, &stats));
@@ -246,7 +280,12 @@ void test_pack_indexer__incomplete_pack_fails_with_strict(void)
opts.verify = 1;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, &opts));
+#else
cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, &opts));
+#endif
+
cl_git_pass(git_indexer_append(
idx, incomplete_pack, incomplete_pack_len, &stats));
cl_git_fail(git_indexer_commit(idx, &stats));
@@ -266,7 +305,12 @@ void test_pack_indexer__out_of_order_with_connectivity_checks(void)
opts.verify = 1;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, &opts));
+#else
cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, &opts));
+#endif
+
cl_git_pass(git_indexer_append(
idx, out_of_order_pack, out_of_order_pack_len, &stats));
cl_git_pass(git_indexer_commit(idx, &stats));
@@ -309,7 +353,12 @@ void test_pack_indexer__no_tmp_files(void)
git_str_dispose(&path);
cl_assert(git_str_len(&first_tmp_file) == 0);
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
git_indexer_free(idx);
cl_git_pass(git_str_sets(&path, clar_sandbox_path()));
diff --git a/tests/libgit2/pack/midx.c b/tests/libgit2/pack/midx.c
index f7d680165bc..4c4dfc51178 100644
--- a/tests/libgit2/pack/midx.c
+++ b/tests/libgit2/pack/midx.c
@@ -16,7 +16,7 @@ void test_pack_midx__parse(void)
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
cl_git_pass(git_str_joinpath(&midx_path, git_repository_path(repo), "objects/pack/multi-pack-index"));
- cl_git_pass(git_midx_open(&idx, git_str_cstr(&midx_path)));
+ cl_git_pass(git_midx_open(&idx, git_str_cstr(&midx_path), GIT_OID_SHA1));
cl_assert_equal_i(git_midx_needs_refresh(idx, git_str_cstr(&midx_path)), 0);
cl_git_pass(git_oid__fromstr(&id, "5001298e0c09ad9c34e4249bc5801c75e9754fa5", GIT_OID_SHA1));
@@ -57,7 +57,12 @@ void test_pack_midx__writer(void)
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
cl_git_pass(git_str_joinpath(&path, git_repository_path(repo), "objects/pack"));
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_midx_writer_new(&w, git_str_cstr(&path), GIT_OID_SHA1));
+#else
cl_git_pass(git_midx_writer_new(&w, git_str_cstr(&path)));
+#endif
cl_git_pass(git_midx_writer_add(w, "pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx"));
cl_git_pass(git_midx_writer_add(w, "pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx"));
diff --git a/tests/libgit2/pack/packbuilder.c b/tests/libgit2/pack/packbuilder.c
index 0889f46eddb..7da3877e451 100644
--- a/tests/libgit2/pack/packbuilder.c
+++ b/tests/libgit2/pack/packbuilder.c
@@ -98,44 +98,26 @@ void test_pack_packbuilder__create_pack(void)
{
git_indexer_progress stats;
git_str buf = GIT_STR_INIT, path = GIT_STR_INIT;
- git_hash_ctx ctx;
- unsigned char hash[GIT_HASH_SHA1_SIZE];
- char hex[(GIT_HASH_SHA1_SIZE * 2) + 1];
seed_packbuilder();
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&_indexer, ".", GIT_OID_SHA1, NULL));
+#else
cl_git_pass(git_indexer_new(&_indexer, ".", 0, NULL, NULL));
+#endif
+
cl_git_pass(git_packbuilder_foreach(_packbuilder, feed_indexer, &stats));
cl_git_pass(git_indexer_commit(_indexer, &stats));
git_str_printf(&path, "pack-%s.pack", git_indexer_name(_indexer));
-
- /*
- * By default, packfiles are created with only one thread.
- * Therefore we can predict the object ordering and make sure
- * we create exactly the same pack as git.git does when *not*
- * reusing existing deltas (as libgit2).
- *
- * $ cd tests/resources/testrepo.git
- * $ git rev-list --objects HEAD | \
- * git pack-objects -q --no-reuse-delta --threads=1 pack
- * $ sha1sum pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.pack
- * 5d410bdf97cf896f9007681b92868471d636954b
- *
- */
+ cl_assert(git_fs_path_exists(path.ptr));
cl_git_pass(git_futils_readbuffer(&buf, git_str_cstr(&path)));
-
- cl_git_pass(git_hash_ctx_init(&ctx, GIT_HASH_ALGORITHM_SHA1));
- cl_git_pass(git_hash_update(&ctx, buf.ptr, buf.size));
- cl_git_pass(git_hash_final(hash, &ctx));
- git_hash_ctx_cleanup(&ctx);
+ cl_assert(buf.size > 256);
git_str_dispose(&path);
git_str_dispose(&buf);
-
- git_hash_fmt(hex, hash, GIT_HASH_SHA1_SIZE);
- cl_assert_equal_s(hex, "5d410bdf97cf896f9007681b92868471d636954b");
}
void test_pack_packbuilder__get_name(void)
@@ -143,22 +125,49 @@ void test_pack_packbuilder__get_name(void)
seed_packbuilder();
cl_git_pass(git_packbuilder_write(_packbuilder, ".", 0, NULL, NULL));
- cl_assert_equal_s("7f5fa362c664d68ba7221259be1cbd187434b2f0", git_packbuilder_name(_packbuilder));
+ cl_assert(git_packbuilder_name(_packbuilder) != NULL);
+}
+
+static void get_packfile_path(git_str *out, git_packbuilder *pb)
+{
+ git_str_puts(out, "pack-");
+ git_str_puts(out, git_packbuilder_name(pb));
+ git_str_puts(out, ".pack");
+}
+
+static void get_index_path(git_str *out, git_packbuilder *pb)
+{
+ git_str_puts(out, "pack-");
+ git_str_puts(out, git_packbuilder_name(pb));
+ git_str_puts(out, ".idx");
}
void test_pack_packbuilder__write_default_path(void)
{
+ git_str idx = GIT_STR_INIT, pack = GIT_STR_INIT;
+
seed_packbuilder();
cl_git_pass(git_packbuilder_write(_packbuilder, NULL, 0, NULL, NULL));
- cl_assert(git_fs_path_exists("objects/pack/pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.idx"));
- cl_assert(git_fs_path_exists("objects/pack/pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.pack"));
+
+ git_str_puts(&idx, "objects/pack/");
+ get_index_path(&idx, _packbuilder);
+
+ git_str_puts(&pack, "objects/pack/");
+ get_packfile_path(&pack, _packbuilder);
+
+ cl_assert(git_fs_path_exists(idx.ptr));
+ cl_assert(git_fs_path_exists(pack.ptr));
+
+ git_str_dispose(&idx);
+ git_str_dispose(&pack);
}
static void test_write_pack_permission(mode_t given, mode_t expected)
{
struct stat statbuf;
mode_t mask, os_mask;
+ git_str idx = GIT_STR_INIT, pack = GIT_STR_INIT;
seed_packbuilder();
@@ -176,11 +185,17 @@ static void test_write_pack_permission(mode_t given, mode_t expected)
mask = p_umask(0);
p_umask(mask);
- cl_git_pass(p_stat("pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.idx", &statbuf));
+ get_index_path(&idx, _packbuilder);
+ get_packfile_path(&pack, _packbuilder);
+
+ cl_git_pass(p_stat(idx.ptr, &statbuf));
cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
- cl_git_pass(p_stat("pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.pack", &statbuf));
+ cl_git_pass(p_stat(pack.ptr, &statbuf));
cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
+
+ git_str_dispose(&idx);
+ git_str_dispose(&pack);
}
void test_pack_packbuilder__permissions_standard(void)
@@ -244,7 +259,13 @@ void test_pack_packbuilder__foreach(void)
git_indexer *idx;
seed_packbuilder();
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
cl_git_pass(git_packbuilder_foreach(_packbuilder, foreach_cb, idx));
cl_git_pass(git_indexer_commit(idx, &_stats));
git_indexer_free(idx);
@@ -262,7 +283,13 @@ void test_pack_packbuilder__foreach_with_cancel(void)
git_indexer *idx;
seed_packbuilder();
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
cl_git_fail_with(
git_packbuilder_foreach(_packbuilder, foreach_cancel_cb, idx), -1111);
git_indexer_free(idx);
diff --git a/tests/libgit2/refs/branches/delete.c b/tests/libgit2/refs/branches/delete.c
index 6b3d507a869..63f8c5d95c7 100644
--- a/tests/libgit2/refs/branches/delete.c
+++ b/tests/libgit2/refs/branches/delete.c
@@ -92,6 +92,21 @@ void test_refs_branches_delete__can_delete_a_local_branch(void)
git_reference_free(branch);
}
+void test_refs_branches_delete__can_delete_a_local_branch_with_multivar(void)
+{
+ git_reference *branch;
+ git_config *cfg;
+
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_multivar(
+ cfg, "branch.br2.gitpublishto", "^$", "example1@example.com"));
+ cl_git_pass(git_config_set_multivar(
+ cfg, "branch.br2.gitpublishto", "^$", "example2@example.com"));
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
void test_refs_branches_delete__can_delete_a_remote_branch(void)
{
git_reference *branch;
diff --git a/tests/libgit2/refs/branches/move.c b/tests/libgit2/refs/branches/move.c
index 46a5082d2fd..4cfb7b83ae6 100644
--- a/tests/libgit2/refs/branches/move.c
+++ b/tests/libgit2/refs/branches/move.c
@@ -210,3 +210,41 @@ void test_refs_branches_move__can_move_with_unicode(void)
git_reference_free(original_ref);
git_reference_free(new_ref);
}
+
+void test_refs_branches_move__moves_reflog_correctly(void)
+{
+ git_reference *original_ref, *new_ref;
+ git_reflog *original_reflog, *new_reflog;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_git_pass(git_reflog_read(&original_reflog, repo, "refs/heads/br2"));
+ cl_assert_equal_i(2, git_reflog_entrycount(original_reflog));
+
+ cl_git_pass(git_branch_move(&new_ref, original_ref, NEW_BRANCH_NAME, 0));
+ cl_assert_equal_s(GIT_REFS_HEADS_DIR NEW_BRANCH_NAME, git_reference_name(new_ref));
+
+ cl_git_pass(git_reflog_read(&new_reflog, repo, GIT_REFS_HEADS_DIR NEW_BRANCH_NAME));
+ cl_assert_equal_i(3, git_reflog_entrycount(new_reflog));
+
+ git_reference_free(original_ref);
+ git_reference_free(new_ref);
+ git_reflog_free(original_reflog);
+ git_reflog_free(new_reflog);
+}
+
+void test_refs_branches_move__failed_move_restores_reflog(void)
+{
+ git_reference *original_ref, *new_ref;
+ git_reflog *recovered_reflog;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_branch_move(&new_ref, original_ref, "Inv@{id", 0));
+
+ cl_git_pass(git_reflog_read(&recovered_reflog, repo, "refs/heads/br2"));
+ cl_assert_equal_i(2, git_reflog_entrycount(recovered_reflog));
+
+ git_reference_free(original_ref);
+ git_reflog_free(recovered_reflog);
+}
diff --git a/tests/libgit2/refs/iterator.c b/tests/libgit2/refs/iterator.c
index f40d35d1171..706fd1ef7e0 100644
--- a/tests/libgit2/refs/iterator.c
+++ b/tests/libgit2/refs/iterator.c
@@ -2,6 +2,7 @@
#include "refs.h"
#include "vector.h"
#include "odb.h"
+#include "repository.h"
static git_repository *repo;
@@ -128,7 +129,7 @@ void test_refs_iterator__empty(void)
git_repository *empty;
cl_git_pass(git_odb__new(&odb, NULL));
- cl_git_pass(git_repository_wrap_odb(&empty, odb));
+ cl_git_pass(git_repository__wrap_odb(&empty, odb, GIT_OID_SHA1));
cl_git_pass(git_reference_iterator_new(&iter, empty));
cl_assert_equal_i(GIT_ITEROVER, git_reference_next(&ref, iter));
diff --git a/tests/libgit2/refs/revparse.c b/tests/libgit2/refs/revparse.c
index 02ffe005df6..9bc3bd10e4c 100644
--- a/tests/libgit2/refs/revparse.c
+++ b/tests/libgit2/refs/revparse.c
@@ -304,6 +304,9 @@ void test_refs_revparse__ordinal(void)
test_object("@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("HEAD@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD@{4}", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+
test_object("master@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
test_object("heads/master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
@@ -744,6 +747,25 @@ void test_refs_revparse__try_to_retrieve_branch_before_abbrev_sha(void)
cl_git_sandbox_cleanup();
}
+void test_refs_revparse__at_at_end_of_refname(void)
+{
+ git_repository *repo;
+ git_reference *branch;
+ git_object *target;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD"));
+ cl_git_pass(git_branch_create(&branch, repo, "master@", (git_commit *)target, 0));
+ git_object_free(target);
+
+ test_id_inrepo("master@", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE, repo);
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_revparse_single(&target, repo, "foo@"));
+
+ git_reference_free(branch);
+ cl_git_sandbox_cleanup();
+}
void test_refs_revparse__range(void)
{
diff --git a/tests/libgit2/remote/fetch.c b/tests/libgit2/remote/fetch.c
index 85e99206fdc..7d2d11e2702 100644
--- a/tests/libgit2/remote/fetch.c
+++ b/tests/libgit2/remote/fetch.c
@@ -40,10 +40,10 @@ void test_remote_fetch__cleanup(void) {
git_repository_free(repo2);
cl_git_pass(git_futils_rmdir_r(repo1_path, NULL, GIT_RMDIR_REMOVE_FILES));
- free(repo1_path);
+ git__free(repo1_path);
cl_git_pass(git_futils_rmdir_r(repo2_path, NULL, GIT_RMDIR_REMOVE_FILES));
- free(repo2_path);
+ git__free(repo2_path);
}
@@ -75,6 +75,7 @@ static void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
/* create two commits in repo 1 and a reference to them */
{
git_oid empty_tree_id;
+ git_commit *commit1;
git_tree *empty_tree;
git_signature *sig;
git_treebuilder *tb;
@@ -84,10 +85,12 @@ static void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
cl_git_pass(git_signature_default(&sig, repo1));
cl_git_pass(git_commit_create(commit1id, repo1, REPO1_REFNAME, sig,
sig, NULL, "one", empty_tree, 0, NULL));
+ cl_git_pass(git_commit_lookup(&commit1, repo1, commit1id));
cl_git_pass(git_commit_create_v(commit2id, repo1, REPO1_REFNAME, sig,
- sig, NULL, "two", empty_tree, 1, commit1id));
+ sig, NULL, "two", empty_tree, 1, commit1));
git_tree_free(empty_tree);
+ git_commit_free(commit1);
git_signature_free(sig);
git_treebuilder_free(tb);
}
diff --git a/tests/libgit2/remote/httpproxy.c b/tests/libgit2/remote/httpproxy.c
index f62a2545bac..60fc67dec31 100644
--- a/tests/libgit2/remote/httpproxy.c
+++ b/tests/libgit2/remote/httpproxy.c
@@ -6,7 +6,6 @@
static git_repository *repo;
static git_net_url url = GIT_NET_URL_INIT;
-static int orig_proxies_need_reset = 0;
static char *orig_http_proxy = NULL;
static char *orig_https_proxy = NULL;
static char *orig_no_proxy = NULL;
@@ -21,20 +20,25 @@ void test_remote_httpproxy__initialize(void)
git_remote_free(remote);
- orig_proxies_need_reset = 0;
+ /* Clear everything for a fresh start */
+ orig_http_proxy = cl_getenv("HTTP_PROXY");
+ orig_https_proxy = cl_getenv("HTTPS_PROXY");
+ orig_no_proxy = cl_getenv("NO_PROXY");
+
+ cl_setenv("HTTP_PROXY", NULL);
+ cl_setenv("HTTPS_PROXY", NULL);
+ cl_setenv("NO_PROXY", NULL);
}
void test_remote_httpproxy__cleanup(void)
{
- if (orig_proxies_need_reset) {
- cl_setenv("HTTP_PROXY", orig_http_proxy);
- cl_setenv("HTTPS_PROXY", orig_https_proxy);
- cl_setenv("NO_PROXY", orig_no_proxy);
-
- git__free(orig_http_proxy);
- git__free(orig_https_proxy);
- git__free(orig_no_proxy);
- }
+ cl_setenv("HTTP_PROXY", orig_http_proxy);
+ cl_setenv("HTTPS_PROXY", orig_https_proxy);
+ cl_setenv("NO_PROXY", orig_no_proxy);
+
+ git__free(orig_http_proxy);
+ git__free(orig_https_proxy);
+ git__free(orig_no_proxy);
git_net_url_dispose(&url);
cl_git_sandbox_cleanup();
@@ -132,7 +136,7 @@ static void assert_global_config_match(const char *config, const char *expected)
void test_remote_httpproxy__config_overrides_detached_remote(void)
{
- cl_fake_home();
+ cl_fake_globalconfig(NULL);
assert_global_config_match(NULL, NULL);
assert_global_config_match("http.proxy", "http://localhost:1/");
@@ -141,22 +145,10 @@ void test_remote_httpproxy__config_overrides_detached_remote(void)
assert_global_config_match("http.https://github.com/libgit2.proxy", "http://localhost:4/");
assert_global_config_match("http.https://github.com/libgit2/.proxy", "http://localhost:5/");
assert_global_config_match("http.https://github.com/libgit2/libgit2.proxy", "http://localhost:6/");
-
- cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
}
void test_remote_httpproxy__env(void)
{
- orig_http_proxy = cl_getenv("HTTP_PROXY");
- orig_https_proxy = cl_getenv("HTTPS_PROXY");
- orig_no_proxy = cl_getenv("NO_PROXY");
- orig_proxies_need_reset = 1;
-
- /* Clear everything for a fresh start */
- cl_setenv("HTTP_PROXY", NULL);
- cl_setenv("HTTPS_PROXY", NULL);
- cl_setenv("NO_PROXY", NULL);
-
/* HTTP proxy is ignored for HTTPS */
cl_setenv("HTTP_PROXY", "http://localhost:9/");
assert_proxy_is(NULL);
diff --git a/tests/libgit2/repo/discover.c b/tests/libgit2/repo/discover.c
index 523fdf8e395..983d75e3ae5 100644
--- a/tests/libgit2/repo/discover.c
+++ b/tests/libgit2/repo/discover.c
@@ -122,7 +122,10 @@ void test_repo_discover__cleanup(void)
void test_repo_discover__discovering_repo_with_exact_path_succeeds(void)
{
cl_git_pass(git_repository_discover(&discovered, DISCOVER_FOLDER, 0, ceiling_dirs.ptr));
+ git_buf_dispose(&discovered);
+
cl_git_pass(git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs.ptr));
+ git_buf_dispose(&discovered);
}
void test_repo_discover__discovering_nonexistent_dir_fails(void)
diff --git a/tests/libgit2/repo/env.c b/tests/libgit2/repo/env.c
index 790ffd40f0f..0e6cc59d5e2 100644
--- a/tests/libgit2/repo/env.c
+++ b/tests/libgit2/repo/env.c
@@ -31,6 +31,10 @@ void test_repo_env__cleanup(void)
if (git_fs_path_isdir("peeled.git"))
git_futils_rmdir_r("peeled.git", NULL, GIT_RMDIR_REMOVE_FILES);
+ cl_fixture_cleanup("test_workdir");
+ cl_fixture_cleanup("test_global_conf");
+ cl_fixture_cleanup("test_system_conf");
+
clear_git_env();
}
@@ -275,3 +279,91 @@ void test_repo_env__open(void)
clear_git_env();
}
+
+void test_repo_env__work_tree(void)
+{
+ git_repository *repo;
+ const char *test_path;
+
+ cl_fixture_sandbox("attr");
+ cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
+
+ cl_must_pass(p_mkdir("test_workdir", 0777));
+ test_path = cl_git_sandbox_path(1, "test_workdir", NULL);
+
+ cl_setenv("GIT_WORK_TREE", test_path);
+ cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
+ cl_assert_equal_s(test_path, git_repository_workdir(repo));
+ git_repository_free(repo);
+ cl_setenv("GIT_WORK_TREE", NULL);
+}
+
+void test_repo_env__commondir(void)
+{
+ git_repository *repo;
+ const char *test_path;
+
+ cl_fixture_sandbox("attr");
+ cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(p_rename("testrepo.git", "test_commondir"));
+
+ test_path = cl_git_sandbox_path(1, "test_commondir", NULL);
+
+ cl_setenv("GIT_COMMON_DIR", test_path);
+ cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
+ cl_assert_equal_s(test_path, git_repository_commondir(repo));
+ git_repository_free(repo);
+ cl_setenv("GIT_COMMON_DIR", NULL);
+}
+
+void test_repo_env__config(void)
+{
+ git_repository *repo;
+ git_config *config;
+ const char *system_path, *global_path;
+ int s, g;
+
+ cl_fixture_sandbox("attr");
+ cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
+
+ cl_git_rewritefile("test_system_conf", "[tttest]\n\tsys = true\n");
+ cl_git_rewritefile("test_global_conf", "[tttest]\n\tglb = true\n");
+
+ system_path = cl_git_sandbox_path(0, "test_system_conf", NULL);
+ cl_setenv("GIT_CONFIG_SYSTEM", system_path);
+
+ global_path = cl_git_sandbox_path(0, "test_global_conf", NULL);
+ cl_setenv("GIT_CONFIG_GLOBAL", global_path);
+
+ /* Ensure we can override the system and global files */
+
+ cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
+ cl_git_pass(git_repository_config(&config, repo));
+
+ cl_git_pass(git_config_get_bool(&s, config, "tttest.sys"));
+ cl_assert_equal_i(1, s);
+ cl_git_pass(git_config_get_bool(&g, config, "tttest.glb"));
+ cl_assert_equal_i(1, g);
+
+ git_config_free(config);
+ git_repository_free(repo);
+
+ /* Further ensure we can ignore the system file. */
+ cl_setenv("GIT_CONFIG_NOSYSTEM", "TrUe");
+
+ cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
+ cl_git_pass(git_repository_config(&config, repo));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_bool(&s, config, "tttest.sys"));
+ cl_git_pass(git_config_get_bool(&g, config, "tttest.glb"));
+ cl_assert_equal_i(1, g);
+
+ git_config_free(config);
+ git_repository_free(repo);
+
+ cl_setenv("GIT_CONFIG_NOSYSTEM", NULL);
+ cl_setenv("GIT_CONFIG_SYSTEM", NULL);
+ cl_setenv("GIT_CONFIG_GLOBAL", NULL);
+}
diff --git a/tests/libgit2/repo/extensions.c b/tests/libgit2/repo/extensions.c
index e7772acd512..105cdae12f4 100644
--- a/tests/libgit2/repo/extensions.c
+++ b/tests/libgit2/repo/extensions.c
@@ -3,7 +3,7 @@
#include "sysdir.h"
#include
-git_repository *repo;
+static git_repository *repo;
void test_repo_extensions__initialize(void)
{
diff --git a/tests/libgit2/repo/getters.c b/tests/libgit2/repo/getters.c
index d401bb8327f..8e21d35b512 100644
--- a/tests/libgit2/repo/getters.c
+++ b/tests/libgit2/repo/getters.c
@@ -51,3 +51,63 @@ void test_repo_getters__retrieving_the_odb_honors_the_refcount(void)
git_odb_free(odb);
}
+
+void test_repo_getters__commit_parents(void)
+{
+ git_repository *repo;
+ git_commitarray parents;
+ git_oid first_parent;
+ git_oid merge_parents[4];
+
+ git_oid__fromstr(&first_parent, "099fabac3a9ea935598528c27f866e34089c2eff", GIT_OID_SHA1);
+
+ /* A commit on a new repository has no parents */
+
+ cl_git_pass(git_repository_init(&repo, "new_repo", false));
+ cl_git_pass(git_repository_commit_parents(&parents, repo));
+
+ cl_assert_equal_sz(0, parents.count);
+ cl_assert_equal_p(NULL, parents.commits);
+
+ git_commitarray_dispose(&parents);
+ git_repository_free(repo);
+
+ /* A standard commit has one parent */
+
+ repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_repository_commit_parents(&parents, repo));
+
+ cl_assert_equal_sz(1, parents.count);
+ cl_assert_equal_oid(&first_parent, git_commit_id(parents.commits[0]));
+
+ git_commitarray_dispose(&parents);
+
+ /* A merge commit has multiple parents */
+
+ cl_git_rewritefile("testrepo/.git/MERGE_HEAD",
+ "8496071c1b46c854b31185ea97743be6a8774479\n"
+ "5b5b025afb0b4c913b4c338a42934a3863bf3644\n"
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045\n"
+ "9fd738e8f7967c078dceed8190330fc8648ee56a\n");
+
+ cl_git_pass(git_repository_commit_parents(&parents, repo));
+
+ cl_assert_equal_sz(5, parents.count);
+
+ cl_assert_equal_oid(&first_parent, git_commit_id(parents.commits[0]));
+
+ git_oid__fromstr(&merge_parents[0], "8496071c1b46c854b31185ea97743be6a8774479", GIT_OID_SHA1);
+ cl_assert_equal_oid(&merge_parents[0], git_commit_id(parents.commits[1]));
+ git_oid__fromstr(&merge_parents[1], "5b5b025afb0b4c913b4c338a42934a3863bf3644", GIT_OID_SHA1);
+ cl_assert_equal_oid(&merge_parents[1], git_commit_id(parents.commits[2]));
+ git_oid__fromstr(&merge_parents[2], "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", GIT_OID_SHA1);
+ cl_assert_equal_oid(&merge_parents[2], git_commit_id(parents.commits[3]));
+ git_oid__fromstr(&merge_parents[3], "9fd738e8f7967c078dceed8190330fc8648ee56a", GIT_OID_SHA1);
+ cl_assert_equal_oid(&merge_parents[3], git_commit_id(parents.commits[4]));
+
+ git_commitarray_dispose(&parents);
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("testrepo");
+}
diff --git a/tests/libgit2/repo/init.c b/tests/libgit2/repo/init.c
index adcd9e02551..446ab735e4e 100644
--- a/tests/libgit2/repo/init.c
+++ b/tests/libgit2/repo/init.c
@@ -142,27 +142,46 @@ void test_repo_init__reinit_bare_repo(void)
cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
}
-void test_repo_init__reinit_too_recent_bare_repo(void)
+void test_repo_init__reinit_nondefault_version(void)
{
git_config *config;
+ cl_set_cleanup(&cleanup_repository, "reinit.git");
+
/* Initialize the repository */
cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
git_repository_config(&config, g_repo);
+ /* Set the config to a supported but not default version */
+ cl_repo_set_string(g_repo, "core.repositoryformatversion", "1");
+ git_config_free(config);
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ /* Try to reinitialize the repository */
+ cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
+ cl_assert_equal_i(1, cl_repo_get_int(g_repo, "core.repositoryformatversion"));
+
+ cl_fixture_cleanup("reinit.git");
+}
+
+void test_repo_init__reinit_unsupported_version(void)
+{
+ cl_set_cleanup(&cleanup_repository, "reinit.git");
+
+ /* Initialize the repository */
+ cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
+
/*
* Hack the config of the repository to make it look like it has
- * been created by a recenter version of git/libgit2
+ * been created by a too new and unsupported version of git/libgit2
*/
- cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 42));
-
- git_config_free(config);
+ cl_repo_set_string(g_repo, "core.repositoryformatversion", "42");
git_repository_free(g_repo);
g_repo = NULL;
- /* Try to reinitialize the repository */
+ /* Try and fail to reinitialize the repository */
cl_git_fail(git_repository_init(&g_repo, "reinit.git", 1));
-
cl_fixture_cleanup("reinit.git");
}
@@ -708,7 +727,7 @@ void test_repo_init__defaultbranch_config_empty(void)
void test_repo_init__longpath(void)
{
#ifdef GIT_WIN32
- size_t padding = CONST_STRLEN("objects/pack/pack-.pack.lock") + GIT_OID_SHA1_HEXSIZE;
+ size_t padding = CONST_STRLEN("objects/pack/pack-.pack.lock") + GIT_OID_MAX_HEXSIZE;
size_t max, i;
git_str path = GIT_STR_INIT;
git_repository *one = NULL, *two = NULL;
@@ -736,3 +755,28 @@ void test_repo_init__longpath(void)
git_str_dispose(&path);
#endif
}
+
+void test_repo_init__absolute_path_with_backslashes(void)
+{
+#ifdef GIT_WIN32
+ git_repository_init_options initopts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_str path = GIT_STR_INIT;
+ char *c;
+
+ cl_set_cleanup(&cleanup_repository, "path");
+
+ cl_git_pass(git_str_joinpath(&path, clar_sandbox_path(), "path/to/newrepo"));
+
+ for (c = path.ptr; *c; c++) {
+ if (*c == '/')
+ *c = '\\';
+ }
+
+ initopts.flags |= GIT_REPOSITORY_INIT_MKDIR | GIT_REPOSITORY_INIT_MKPATH;
+
+ cl_git_pass(git_repository_init_ext(&g_repo, path.ptr, &initopts));
+ git_str_dispose(&path);
+#else
+ clar__skip();
+#endif
+}
diff --git a/tests/libgit2/repo/new.c b/tests/libgit2/repo/new.c
index d77e903f670..aaa917a4aba 100644
--- a/tests/libgit2/repo/new.c
+++ b/tests/libgit2/repo/new.c
@@ -5,7 +5,11 @@ void test_repo_new__has_nothing(void)
{
git_repository *repo;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_repository_new(&repo, GIT_OID_SHA1));
+#else
cl_git_pass(git_repository_new(&repo));
+#endif
cl_assert_equal_b(true, git_repository_is_bare(repo));
cl_assert_equal_p(NULL, git_repository_path(repo));
cl_assert_equal_p(NULL, git_repository_workdir(repo));
@@ -16,7 +20,11 @@ void test_repo_new__is_bare_until_workdir_set(void)
{
git_repository *repo;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_repository_new(&repo, GIT_OID_SHA1));
+#else
cl_git_pass(git_repository_new(&repo));
+#endif
cl_assert_equal_b(true, git_repository_is_bare(repo));
cl_git_pass(git_repository_set_workdir(repo, clar_sandbox_path(), 0));
@@ -25,3 +33,30 @@ void test_repo_new__is_bare_until_workdir_set(void)
git_repository_free(repo);
}
+void test_repo_new__sha1(void)
+{
+ git_repository *repo;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_repository_new(&repo, GIT_OID_SHA1));
+#else
+ cl_git_pass(git_repository_new(&repo));
+#endif
+ cl_assert_equal_i(GIT_OID_SHA1, git_repository_oid_type(repo));
+
+ git_repository_free(repo);
+}
+
+void test_repo_new__sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ git_repository *repo;
+
+ cl_git_pass(git_repository_new(&repo, GIT_OID_SHA256));
+ cl_assert_equal_i(GIT_OID_SHA256, git_repository_oid_type(repo));
+
+ git_repository_free(repo);
+#endif
+}
diff --git a/tests/libgit2/repo/objectformat.c b/tests/libgit2/repo/objectformat.c
new file mode 100644
index 00000000000..d278e10c4b2
--- /dev/null
+++ b/tests/libgit2/repo/objectformat.c
@@ -0,0 +1,69 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "sysdir.h"
+#include "repository.h"
+#include
+
+static git_repository *repo;
+static git_config *config;
+
+void test_repo_objectformat__initialize(void)
+{
+ repo = cl_git_sandbox_init("empty_bare.git");
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 1));
+}
+
+void test_repo_objectformat__cleanup(void)
+{
+ git_config_free(config);
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_objectformat__unspecified(void)
+{
+ git_repository *other;
+
+ cl_git_pass(git_repository_open(&other, "empty_bare.git"));
+ cl_assert_equal_i(GIT_OID_SHA1, git_repository_oid_type(other));
+ git_repository_free(other);
+}
+
+void test_repo_objectformat__sha1(void)
+{
+ git_repository *other;
+
+ cl_git_pass(git_config_set_string(config, "extensions.objectformat", "sha1"));
+
+ cl_git_pass(git_repository_open(&other, "empty_bare.git"));
+ cl_assert_equal_i(GIT_OID_SHA1, git_repository_oid_type(other));
+ git_repository_free(other);
+}
+
+void test_repo_objectformat__sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ git_repository *other;
+
+ cl_git_pass(git_config_set_string(config, "extensions.objectformat", "sha256"));
+
+ cl_git_pass(git_repository_open(&other, "empty_bare.git"));
+ cl_assert_equal_i(GIT_OID_SHA256, git_repository_oid_type(other));
+ git_repository_free(other);
+#endif
+}
+
+void test_repo_objectformat__invalid(void)
+{
+ git_repository *other;
+
+ cl_git_pass(git_config_set_string(config, "extensions.objectformat", "bogus"));
+
+ cl_git_fail_with(GIT_EINVALID, git_repository_open(&other, "empty_bare.git"));
+ cl_assert_equal_s("unknown object format 'bogus'", git_error_last()->message);
+ git_repository_free(other);
+}
+
diff --git a/tests/libgit2/repo/open.c b/tests/libgit2/repo/open.c
index d835240b779..d58551343e1 100644
--- a/tests/libgit2/repo/open.c
+++ b/tests/libgit2/repo/open.c
@@ -316,7 +316,7 @@ static void unposix_path(git_str *path)
src = tgt = path->ptr;
/* convert "/d/..." to "d:\..." */
- if (src[0] == '/' && isalpha(src[1]) && src[2] == '/') {
+ if (src[0] == '/' && git__isalpha(src[1]) && src[2] == '/') {
*tgt++ = src[1];
*tgt++ = ':';
*tgt++ = '\\';
@@ -410,6 +410,7 @@ void test_repo_open__no_config(void)
git_str_dispose(&path);
cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ cl_assert(git_repository_oid_type(repo) == GIT_OID_SHA1);
cl_git_pass(git_repository_config(&config, repo));
cl_git_pass(git_config_set_string(config, "test.set", "42"));
@@ -433,11 +434,13 @@ void test_repo_open__force_bare(void)
cl_git_pass(git_repository_open(&barerepo, "alternate"));
cl_assert(!git_repository_is_bare(barerepo));
+ cl_assert(git_repository_oid_type(barerepo) == GIT_OID_SHA1);
git_repository_free(barerepo);
cl_git_pass(git_repository_open_bare(
&barerepo, "empty_standard_repo/.git"));
cl_assert(git_repository_is_bare(barerepo));
+ cl_assert(git_repository_oid_type(barerepo) == GIT_OID_SHA1);
git_repository_free(barerepo);
cl_git_fail(git_repository_open_bare(&barerepo, "alternate/.git"));
@@ -530,15 +533,21 @@ void test_repo_open__validates_bare_repo_ownership(void)
cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
}
-void test_repo_open__can_allowlist_dirs_with_problematic_ownership(void)
+static int test_safe_path(const char *path)
{
git_repository *repo;
git_str config_path = GIT_STR_INIT,
config_filename = GIT_STR_INIT,
config_data = GIT_STR_INIT;
+ int error;
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
+ /*
+ * Sandbox the fixture, and ensure that when we fake an owner
+ * of "other" that the repository cannot be opened (and fails
+ * with `GIT_EOWNER`).
+ */
cl_fixture_sandbox("empty_standard_repo");
cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
@@ -552,81 +561,179 @@ void test_repo_open__can_allowlist_dirs_with_problematic_ownership(void)
git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
+ git_str_clear(&config_data);
git_str_printf(&config_data,
"[foo]\n" \
"\tbar = Foobar\n" \
"\tbaz = Baz!\n" \
"[safe]\n" \
- "\tdirectory = /non/existent/path\n" \
- "\tdirectory = /\n" \
- "\tdirectory = c:\\\\temp\n" \
- "\tdirectory = %s/%s\n" \
- "\tdirectory = /tmp\n" \
+ "\tdirectory = %s\n" \
"[bar]\n" \
"\tfoo = barfoo\n",
- clar_sandbox_path(), "empty_standard_repo");
+ path);
cl_git_rewritefile(config_filename.ptr, config_data.ptr);
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ error = git_repository_open(&repo, "empty_standard_repo");
git_repository_free(repo);
git_str_dispose(&config_path);
git_str_dispose(&config_filename);
git_str_dispose(&config_data);
+
+ return error;
}
-void test_repo_open__can_wildcard_allowlist_with_problematic_ownership(void)
+static int test_bare_safe_path(const char *path)
{
git_repository *repo;
- git_str config_path = GIT_STR_INIT, config_filename = GIT_STR_INIT;
+ git_str config_path = GIT_STR_INIT,
+ config_filename = GIT_STR_INIT,
+ config_data = GIT_STR_INIT;
+ int error;
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
- cl_fixture_sandbox("empty_standard_repo");
- cl_git_pass(cl_rename(
- "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+ cl_fixture_sandbox("testrepo.git");
git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
- cl_git_fail_with(
- GIT_EOWNER, git_repository_open(&repo, "empty_standard_repo"));
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
/* Add safe.directory options to the global configuration */
git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
cl_must_pass(p_mkdir(config_path.ptr, 0777));
- git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
- config_path.ptr);
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr);
git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
- cl_git_rewritefile(config_filename.ptr, "[foo]\n"
- "\tbar = Foobar\n"
- "\tbaz = Baz!\n"
- "[safe]\n"
- "\tdirectory = *\n"
- "[bar]\n"
- "\tfoo = barfoo\n");
+ git_str_printf(&config_data,
+ "[foo]\n" \
+ "\tbar = Foobar\n" \
+ "\tbaz = Baz!\n" \
+ "[safe]\n" \
+ "\tdirectory = /non/existent/path\n" \
+ "\tdirectory = /\n" \
+ "\tdirectory = c:\\\\temp\n" \
+ "\tdirectory = %s\n" \
+ "\tdirectory = /tmp\n" \
+ "[bar]\n" \
+ "\tfoo = barfoo\n",
+ path);
+ cl_git_rewritefile(config_filename.ptr, config_data.ptr);
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ error = git_repository_open(&repo, "testrepo.git");
git_repository_free(repo);
git_str_dispose(&config_path);
git_str_dispose(&config_filename);
+ git_str_dispose(&config_data);
+
+ return error;
+}
+
+void test_repo_open__can_allowlist_dirs_with_problematic_ownership(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_printf(&path, "%s/%s",
+ clar_sandbox_path(), "empty_standard_repo"));
+ cl_git_pass(test_safe_path(path.ptr));
+ git_str_dispose(&path);
+}
+
+void test_repo_open__safe_directory_fails_with_trailing_slash(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ /*
+ * "/tmp/foo/" is not permitted; safe path must be specified
+ * as "/tmp/foo"
+ */
+ cl_git_pass(git_str_printf(&path, "%s/%s/",
+ clar_sandbox_path(), "empty_standard_repo"));
+ cl_git_fail_with(GIT_EOWNER, test_safe_path(path.ptr));
+ git_str_dispose(&path);
+}
+
+void test_repo_open__can_wildcard_allowlist_with_problematic_ownership(void)
+{
+ cl_git_pass(test_safe_path("*"));
}
void test_repo_open__can_allowlist_bare_gitdir(void)
{
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_printf(&path, "%s/%s",
+ clar_sandbox_path(), "testrepo.git"));
+ cl_git_pass(test_bare_safe_path(path.ptr));
+ git_str_dispose(&path);
+}
+
+void test_repo_open__can_wildcard_allowlist_bare_gitdir(void)
+{
+ cl_git_pass(test_bare_safe_path("*"));
+}
+
+void test_repo_open__can_handle_prefixed_safe_paths(void)
+{
+#ifndef GIT_WIN32
+ git_str path = GIT_STR_INIT;
+
+ /*
+ * Using "%(prefix)/" becomes "%(prefix)//tmp/foo" - so
+ * "%(prefix)/" is stripped and means the literal path
+ * follows.
+ */
+ cl_git_pass(git_str_printf(&path, "%%(prefix)/%s/%s",
+ clar_sandbox_path(), "empty_standard_repo"));
+ cl_git_pass(test_safe_path(path.ptr));
+ git_str_dispose(&path);
+#endif
+}
+
+void test_repo_open__prefixed_safe_paths_must_have_two_slashes(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ /*
+ * Using "%(prefix)" becomes "%(prefix)/tmp/foo" - so it's
+ * actually trying to look in the git prefix, for example,
+ * "/usr/local/tmp/foo", which we don't actually support.
+ */
+ cl_git_pass(git_str_printf(&path, "%%(prefix)%s/%s",
+ clar_sandbox_path(), "empty_standard_repo"));
+ cl_git_fail_with(GIT_EOWNER, test_safe_path(path.ptr));
+ git_str_dispose(&path);
+}
+
+void test_repo_open__can_handle_win32_prefixed_safe_paths(void)
+{
+#ifdef GIT_WIN32
git_repository *repo;
- git_str config_path = GIT_STR_INIT,
+ git_str unc_path = GIT_STR_INIT,
+ config_path = GIT_STR_INIT,
config_filename = GIT_STR_INIT,
config_data = GIT_STR_INIT;
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
- cl_fixture_sandbox("testrepo.git");
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+
+ /*
+ * On Windows, we can generally map a local drive to a UNC path;
+ * for example C:\Foo\Bar becomes //localhost/C$/Foo/bar
+ */
+ cl_git_pass(git_str_printf(&unc_path, "//localhost/%s/%s",
+ clar_sandbox_path(), "empty_standard_repo"));
+
+ if (unc_path.ptr[13] != ':' || unc_path.ptr[14] != '/')
+ cl_skip();
+
+ unc_path.ptr[13] = '$';
git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
- cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, unc_path.ptr));
/* Add safe.directory options to the global configuration */
git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
@@ -635,64 +742,74 @@ void test_repo_open__can_allowlist_bare_gitdir(void)
git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
+ /* The blank resets our sandbox directory and opening fails */
+
git_str_printf(&config_data,
- "[foo]\n" \
- "\tbar = Foobar\n" \
- "\tbaz = Baz!\n" \
- "[safe]\n" \
- "\tdirectory = /non/existent/path\n" \
- "\tdirectory = /\n" \
- "\tdirectory = c:\\\\temp\n" \
- "\tdirectory = %s/%s\n" \
- "\tdirectory = /tmp\n" \
- "[bar]\n" \
- "\tfoo = barfoo\n",
- clar_sandbox_path(), "testrepo.git");
+ "[safe]\n\tdirectory = %%(prefix)/%s\n",
+ unc_path.ptr);
cl_git_rewritefile(config_filename.ptr, config_data.ptr);
- cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+ cl_git_pass(git_repository_open(&repo, unc_path.ptr));
git_repository_free(repo);
git_str_dispose(&config_path);
git_str_dispose(&config_filename);
git_str_dispose(&config_data);
+ git_str_dispose(&unc_path);
+#endif
}
-void test_repo_open__can_wildcard_allowlist_bare_gitdir(void)
+void test_repo_open__can_handle_win32_unc_safe_paths(void)
{
+#ifdef GIT_WIN32
git_repository *repo;
- git_str config_path = GIT_STR_INIT, config_filename = GIT_STR_INIT;
+ git_str unc_path = GIT_STR_INIT,
+ config_path = GIT_STR_INIT,
+ config_filename = GIT_STR_INIT,
+ config_data = GIT_STR_INIT;
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
- cl_fixture_sandbox("testrepo.git");
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+
+ /*
+ * On Windows, we can generally map a local drive to a UNC path;
+ * for example C:\Foo\Bar becomes //localhost/C$/Foo/bar
+ */
+ cl_git_pass(git_str_printf(&unc_path, "//localhost/%s/%s",
+ clar_sandbox_path(), "empty_standard_repo"));
+
+ if (unc_path.ptr[13] != ':' || unc_path.ptr[14] != '/')
+ cl_skip();
+
+ unc_path.ptr[13] = '$';
git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
- cl_git_fail_with(
- GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, unc_path.ptr));
/* Add safe.directory options to the global configuration */
git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
cl_must_pass(p_mkdir(config_path.ptr, 0777));
- git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
- config_path.ptr);
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr);
git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
- cl_git_rewritefile(config_filename.ptr, "[foo]\n"
- "\tbar = Foobar\n"
- "\tbaz = Baz!\n"
- "[safe]\n"
- "\tdirectory = *\n"
- "[bar]\n"
- "\tfoo = barfoo\n");
+ /* The blank resets our sandbox directory and opening fails */
- cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+ git_str_printf(&config_data,
+ "[safe]\n\tdirectory = %s\n",
+ unc_path.ptr);
+ cl_git_rewritefile(config_filename.ptr, config_data.ptr);
+
+ cl_git_pass(git_repository_open(&repo, unc_path.ptr));
git_repository_free(repo);
git_str_dispose(&config_path);
git_str_dispose(&config_filename);
+ git_str_dispose(&config_data);
+ git_str_dispose(&unc_path);
+#endif
}
void test_repo_open__can_reset_safe_directory_list(void)
diff --git a/tests/libgit2/repo/setters.c b/tests/libgit2/repo/setters.c
index 66ec7706c2b..5c91ed39005 100644
--- a/tests/libgit2/repo/setters.c
+++ b/tests/libgit2/repo/setters.c
@@ -1,6 +1,7 @@
#include "clar_libgit2.h"
#include "git2/sys/repository.h"
+#include "index.h"
#include "odb.h"
#include "posix.h"
#include "util.h"
@@ -70,7 +71,7 @@ void test_repo_setters__setting_a_new_index_on_a_repo_which_has_already_loaded_o
{
git_index *new_index;
- cl_git_pass(git_index_open(&new_index, "./my-index"));
+ cl_git_pass(git_index__open(&new_index, "./my-index", GIT_OID_SHA1));
cl_assert(((git_refcount *)new_index)->refcount.val == 1);
git_repository_set_index(repo, new_index);
diff --git a/tests/libgit2/repo/shallow.c b/tests/libgit2/repo/shallow.c
index adb7a9e44b5..a3e3036c533 100644
--- a/tests/libgit2/repo/shallow.c
+++ b/tests/libgit2/repo/shallow.c
@@ -35,5 +35,5 @@ void test_repo_shallow__clears_errors(void)
{
g_repo = cl_git_sandbox_init("testrepo.git");
cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
- cl_assert_equal_p(NULL, git_error_last());
+ cl_assert_equal_i(GIT_ERROR_NONE, git_error_last()->klass);
}
diff --git a/tests/libgit2/reset/hard.c b/tests/libgit2/reset/hard.c
index 0b6342cb2f8..06a8a049afc 100644
--- a/tests/libgit2/reset/hard.c
+++ b/tests/libgit2/reset/hard.c
@@ -3,6 +3,7 @@
#include "reset_helpers.h"
#include "path.h"
#include "futils.h"
+#include "index.h"
static git_repository *repo;
static git_object *target;
@@ -252,7 +253,7 @@ void test_reset_hard__switch_file_to_dir(void)
git_odb_free(odb);
entry.mode = GIT_FILEMODE_BLOB;
- cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
cl_git_pass(git_signature_now(&sig, "foo", "bar"));
/* Create the old tree */
diff --git a/tests/libgit2/revwalk/basic.c b/tests/libgit2/revwalk/basic.c
index 62a57aaa3c0..41090a1dac8 100644
--- a/tests/libgit2/revwalk/basic.c
+++ b/tests/libgit2/revwalk/basic.c
@@ -180,6 +180,23 @@ void test_revwalk_basic__glob_heads_with_invalid(void)
cl_assert_equal_i(20, i);
}
+void test_revwalk_basic__glob_invalid_symbolic_ref(void)
+{
+ int i;
+ git_oid oid;
+
+ revwalk_basic_setup_walk("testrepo");
+
+ cl_git_mkfile("testrepo/.git/refs/heads/broken-sym-ref", "ref: refs/heads/does-not-exist");
+ cl_git_pass(git_revwalk_push_glob(_walk, "heads"));
+
+ for (i = 0; !git_revwalk_next(&oid, _walk); ++i)
+ /* walking */;
+
+ /* git log --branches --oneline | wc -l => 16 */
+ cl_assert_equal_i(20, i);
+}
+
void test_revwalk_basic__push_head(void)
{
int i = 0;
diff --git a/tests/libgit2/stash/save.c b/tests/libgit2/stash/save.c
index f574211d794..23f3c1cbb41 100644
--- a/tests/libgit2/stash/save.c
+++ b/tests/libgit2/stash/save.c
@@ -130,6 +130,19 @@ void test_stash_save__can_keep_index(void)
assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
}
+void test_stash_save__can_keep_all(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_KEEP_ALL));
+
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
+ assert_status(repo, "where", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW);
+ assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
+}
+
static void assert_commit_message_contains(const char *revision, const char *fragment)
{
git_commit *commit;
@@ -488,3 +501,27 @@ void test_stash_save__deleted_in_index_modified_in_workdir(void)
git_index_free(index);
}
+
+void test_stash_save__option_paths(void)
+{
+ git_stash_save_options options = GIT_STASH_SAVE_OPTIONS_INIT;
+ char *paths[2] = { "who", "where" };
+
+ options.paths = (git_strarray){
+ paths,
+ 2
+ };
+ options.stasher = signature;
+
+ cl_git_pass(git_stash_save_with_opts(&stash_tip_oid, repo, &options));
+
+ assert_blob_oid("refs/stash:who", "a0400d4954659306a976567af43125a0b1aa8595");
+ assert_blob_oid("refs/stash:where", "e3d6434ec12eb76af8dfa843a64ba6ab91014a0b");
+
+ assert_blob_oid("refs/stash:what", "ce013625030ba8dba906f756967f9e9ca394464a");
+ assert_blob_oid("refs/stash:how", "ac790413e2d7a26c3767e78c57bb28716686eebc");
+ assert_blob_oid("refs/stash:when", NULL);
+ assert_blob_oid("refs/stash:why", NULL);
+ assert_blob_oid("refs/stash:.gitignore", "ac4d88de61733173d9959e4b77c69b9f17a00980");
+ assert_blob_oid("refs/stash:just.ignore", NULL);
+}
diff --git a/tests/libgit2/status/worktree.c b/tests/libgit2/status/worktree.c
index efbf597a723..8a2ea9cb674 100644
--- a/tests/libgit2/status/worktree.c
+++ b/tests/libgit2/status/worktree.c
@@ -1360,3 +1360,13 @@ void test_status_worktree__at_head_parent(void)
git_tree_free(parent_tree);
git_status_list_free(statuslist);
}
+
+void test_status_worktree__skip_hash(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status_skiphash");
+ git_index *index;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_read(index, true));
+ git_index_free(index);
+}
diff --git a/tests/libgit2/status/worktree_init.c b/tests/libgit2/status/worktree_init.c
index 40f1b2a3140..db6e71f1234 100644
--- a/tests/libgit2/status/worktree_init.c
+++ b/tests/libgit2/status/worktree_init.c
@@ -7,6 +7,7 @@
#include "posix.h"
#include "util.h"
#include "path.h"
+#include "index.h"
static void cleanup_new_repo(void *path)
{
@@ -65,7 +66,7 @@ void test_status_worktree_init__status_file_without_index_or_workdir(void)
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
cl_git_pass(git_repository_set_workdir(repo, "wd", false));
- cl_git_pass(git_index_open(&index, "empty-index"));
+ cl_git_pass(git_index__open(&index, "empty-index", GIT_OID_SHA1));
cl_assert_equal_i(0, (int)git_index_entrycount(index));
git_repository_set_index(repo, index);
@@ -106,7 +107,7 @@ void test_status_worktree_init__status_file_with_clean_index_and_empty_workdir(v
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
cl_git_pass(git_repository_set_workdir(repo, "wd", false));
- cl_git_pass(git_index_open(&index, "my-index"));
+ cl_git_pass(git_index__open(&index, "my-index", GIT_OID_SHA1));
fill_index_wth_head_entries(repo, index);
git_repository_set_index(repo, index);
@@ -283,7 +284,7 @@ void test_status_worktree_init__disable_pathspec_match(void)
{
git_repository *repo;
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- char *file_with_bracket = "LICENSE[1].md",
+ char *file_with_bracket = "LICENSE[1].md",
*imaginary_file_with_bracket = "LICENSE[1-2].md";
cl_set_cleanup(&cleanup_new_repo, "pathspec");
@@ -291,18 +292,18 @@ void test_status_worktree_init__disable_pathspec_match(void)
cl_git_mkfile("pathspec/LICENSE[1].md", "screaming bracket\n");
cl_git_mkfile("pathspec/LICENSE1.md", "no bracket\n");
- opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH;
opts.pathspec.count = 1;
opts.pathspec.strings = &file_with_bracket;
cl_git_pass(
- git_status_foreach_ext(repo, &opts, cb_status__expected_path,
+ git_status_foreach_ext(repo, &opts, cb_status__expected_path,
file_with_bracket)
);
/* Test passing a pathspec matching files in the workdir. */
- /* Must not match because pathspecs are disabled. */
+ /* Must not match because pathspecs are disabled. */
opts.pathspec.strings = &imaginary_file_with_bracket;
cl_git_pass(
git_status_foreach_ext(repo, &opts, cb_status__expected_path, NULL)
diff --git a/tests/libgit2/stream/registration.c b/tests/libgit2/stream/registration.c
index bf3c20502a0..ccaecee8c1e 100644
--- a/tests/libgit2/stream/registration.c
+++ b/tests/libgit2/stream/registration.c
@@ -81,10 +81,10 @@ void test_stream_registration__tls(void)
cl_git_pass(git_stream_register(GIT_STREAM_TLS, NULL));
error = git_tls_stream_new(&stream, "localhost", "443");
- /* We don't have TLS support enabled, or we're on Windows,
- * which has no arbitrary TLS stream support.
+ /* We don't have TLS support enabled, or we're on Windows
+ * with WinHTTP, which is not actually TLS stream support.
*/
-#if defined(GIT_WIN32) || !defined(GIT_HTTPS)
+#if defined(GIT_WINHTTP) || !defined(GIT_HTTPS)
cl_git_fail_with(-1, error);
#else
cl_git_pass(error);
diff --git a/tests/libgit2/submodule/lookup.c b/tests/libgit2/submodule/lookup.c
index acfdc838c26..14a624badef 100644
--- a/tests/libgit2/submodule/lookup.c
+++ b/tests/libgit2/submodule/lookup.c
@@ -3,6 +3,7 @@
#include "git2/sys/repository.h"
#include "repository.h"
#include "futils.h"
+#include "index.h"
static git_repository *g_repo = NULL;
@@ -210,7 +211,7 @@ void test_submodule_lookup__lookup_even_with_missing_index(void)
git_index *idx;
/* give the repo an empty index */
- cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
git_repository_set_index(g_repo, idx);
git_index_free(idx);
@@ -400,6 +401,24 @@ void test_submodule_lookup__prefix_name(void)
git_submodule_free(sm);
}
+/* ".path" in name of submodule */
+void test_submodule_lookup__dotpath_in_name(void)
+{
+ sm_lookup_data data;
+
+ cl_git_rewritefile(
+ "submod2/.gitmodules", "[submodule \"kwb.pathdict\"]\n"
+ " path = kwb.pathdict\n"
+ " url = ../Test_App\n"
+ "[submodule \"fakin.path.app\"]\n"
+ " path = fakin.path.app\n"
+ " url = ../Test_App\n");
+
+ memset(&data, 0, sizeof(data));
+ cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
+ cl_assert_equal_i(9, data.count);
+}
+
void test_submodule_lookup__renamed(void)
{
const char *newpath = "sm_actually_changed";
diff --git a/tests/libgit2/trace/trace.c b/tests/libgit2/trace/trace.c
index 097208bffd6..9fea57668a2 100644
--- a/tests/libgit2/trace/trace.c
+++ b/tests/libgit2/trace/trace.c
@@ -32,16 +32,11 @@ void test_trace_trace__cleanup(void)
void test_trace_trace__sets(void)
{
-#ifdef GIT_TRACE
cl_assert(git_trace_level() == GIT_TRACE_INFO);
-#else
- cl_skip();
-#endif
}
void test_trace_trace__can_reset(void)
{
-#ifdef GIT_TRACE
cl_assert(git_trace_level() == GIT_TRACE_INFO);
cl_git_pass(git_trace_set(GIT_TRACE_ERROR, trace_callback));
@@ -51,14 +46,10 @@ void test_trace_trace__can_reset(void)
git_trace(GIT_TRACE_ERROR, "Hello %s!", "world");
cl_assert(written == 1);
-#else
- cl_skip();
-#endif
}
void test_trace_trace__can_unset(void)
{
-#ifdef GIT_TRACE
cl_assert(git_trace_level() == GIT_TRACE_INFO);
cl_git_pass(git_trace_set(GIT_TRACE_NONE, NULL));
@@ -67,40 +58,25 @@ void test_trace_trace__can_unset(void)
cl_assert(written == 0);
git_trace(GIT_TRACE_FATAL, "Hello %s!", "world");
cl_assert(written == 0);
-#else
- cl_skip();
-#endif
}
void test_trace_trace__skips_higher_level(void)
{
-#ifdef GIT_TRACE
cl_assert(written == 0);
git_trace(GIT_TRACE_DEBUG, "Hello %s!", "world");
cl_assert(written == 0);
-#else
- cl_skip();
-#endif
}
void test_trace_trace__writes(void)
{
-#ifdef GIT_TRACE
cl_assert(written == 0);
git_trace(GIT_TRACE_INFO, "Hello %s!", "world");
cl_assert(written == 1);
-#else
- cl_skip();
-#endif
}
void test_trace_trace__writes_lower_level(void)
{
-#ifdef GIT_TRACE
cl_assert(written == 0);
git_trace(GIT_TRACE_ERROR, "Hello %s!", "world");
cl_assert(written == 1);
-#else
- cl_skip();
-#endif
}
diff --git a/tests/libgit2/transport/ssh_exec.c b/tests/libgit2/transport/ssh_exec.c
new file mode 100644
index 00000000000..886c10ad7c3
--- /dev/null
+++ b/tests/libgit2/transport/ssh_exec.c
@@ -0,0 +1,79 @@
+#include "clar_libgit2.h"
+#include "git2/sys/remote.h"
+#include "git2/sys/transport.h"
+
+
+void test_transport_ssh_exec__reject_injection_username(void)
+{
+#ifndef GIT_SSH_EXEC
+ cl_skip();
+#else
+ git_remote *remote;
+ git_repository *repo;
+ git_transport *transport;
+ const char *url = "-oProxyCommand=git@somehost:somepath";
+ git_remote_connect_options opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+
+
+ cl_git_pass(git_repository_init(&repo, "./transport-username", 0));
+ cl_git_pass(git_remote_create(&remote, repo, "test",
+ cl_fixture("testrepo.git")));
+ cl_git_pass(git_transport_new(&transport, remote, url));
+ cl_git_fail_with(-1, transport->connect(transport, url,
+ GIT_SERVICE_UPLOADPACK_LS, &opts));
+
+ transport->free(transport);
+ git_remote_free(remote);
+ git_repository_free(repo);
+#endif
+}
+
+void test_transport_ssh_exec__reject_injection_hostname(void)
+{
+#ifndef GIT_SSH_EXEC
+ cl_skip();
+#else
+ git_remote *remote;
+ git_repository *repo;
+ git_transport *transport;
+ const char *url = "-oProxyCommand=somehost:somepath-hostname";
+ git_remote_connect_options opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+
+
+ cl_git_pass(git_repository_init(&repo, "./transport-hostname", 0));
+ cl_git_pass(git_remote_create(&remote, repo, "test",
+ cl_fixture("testrepo.git")));
+ cl_git_pass(git_transport_new(&transport, remote, url));
+ cl_git_fail_with(-1, transport->connect(transport, url,
+ GIT_SERVICE_UPLOADPACK_LS, &opts));
+
+ transport->free(transport);
+ git_remote_free(remote);
+ git_repository_free(repo);
+#endif
+}
+
+void test_transport_ssh_exec__reject_injection_path(void)
+{
+#ifndef GIT_SSH_EXEC
+ cl_skip();
+#else
+ git_remote *remote;
+ git_repository *repo;
+ git_transport *transport;
+ const char *url = "git@somehost:-somepath";
+ git_remote_connect_options opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+
+
+ cl_git_pass(git_repository_init(&repo, "./transport-path", 0));
+ cl_git_pass(git_remote_create(&remote, repo, "test",
+ cl_fixture("testrepo.git")));
+ cl_git_pass(git_transport_new(&transport, remote, url));
+ cl_git_fail_with(-1, transport->connect(transport, url,
+ GIT_SERVICE_UPLOADPACK_LS, &opts));
+
+ transport->free(transport);
+ git_remote_free(remote);
+ git_repository_free(repo);
+#endif
+}
diff --git a/tests/libgit2/transports/smart/packet.c b/tests/libgit2/transports/smart/packet.c
index 8801ef833f7..a775a4cfab4 100644
--- a/tests/libgit2/transports/smart/packet.c
+++ b/tests/libgit2/transports/smart/packet.c
@@ -11,8 +11,9 @@ static void assert_flush_parses(const char *line)
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
- cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
cl_assert_equal_i(pkt->type, GIT_PKT_FLUSH);
cl_assert_equal_strn(endptr, line + 4, linelen - 4);
@@ -24,8 +25,9 @@ static void assert_data_pkt_parses(const char *line, const char *expected_data,
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_data *pkt;
+ git_pkt_parse_data pkt_parse_data = { 1, GIT_OID_SHA1 };
- cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
cl_assert_equal_i(pkt->type, GIT_PKT_DATA);
cl_assert_equal_i(pkt->len, expected_len);
cl_assert_equal_strn(pkt->data, expected_data, expected_len);
@@ -38,8 +40,9 @@ static void assert_sideband_progress_parses(const char *line, const char *expect
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_progress *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
- cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
cl_assert_equal_i(pkt->type, GIT_PKT_PROGRESS);
cl_assert_equal_i(pkt->len, expected_len);
cl_assert_equal_strn(pkt->data, expected_data, expected_len);
@@ -52,8 +55,9 @@ static void assert_error_parses(const char *line, const char *expected_error, si
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_err *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
- cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
cl_assert_equal_i(pkt->type, GIT_PKT_ERR);
cl_assert_equal_i(pkt->len, expected_len);
cl_assert_equal_strn(pkt->error, expected_error, expected_len);
@@ -67,10 +71,11 @@ static void assert_ack_parses(const char *line, const char *expected_oid, enum g
const char *endptr;
git_pkt_ack *pkt;
git_oid oid;
+ git_pkt_parse_data pkt_parse_data = { 1, GIT_OID_SHA1 };
cl_git_pass(git_oid__fromstr(&oid, expected_oid, GIT_OID_SHA1));
- cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
cl_assert_equal_i(pkt->type, GIT_PKT_ACK);
cl_assert_equal_oid(&pkt->oid, &oid);
cl_assert_equal_i(pkt->status, expected_status);
@@ -83,8 +88,9 @@ static void assert_nak_parses(const char *line)
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
- cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
cl_assert_equal_i(pkt->type, GIT_PKT_NAK);
cl_assert_equal_strn(endptr, line + 7, linelen - 7);
@@ -96,8 +102,9 @@ static void assert_comment_parses(const char *line, const char *expected_comment
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_comment *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
- cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
cl_assert_equal_i(pkt->type, GIT_PKT_COMMENT);
cl_assert_equal_strn(pkt->comment, expected_comment, strlen(expected_comment));
@@ -109,8 +116,9 @@ static void assert_ok_parses(const char *line, const char *expected_ref)
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_ok *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
- cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
cl_assert_equal_i(pkt->type, GIT_PKT_OK);
cl_assert_equal_strn(pkt->ref, expected_ref, strlen(expected_ref));
@@ -122,8 +130,9 @@ static void assert_unpack_parses(const char *line, bool ok)
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_unpack *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
- cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
cl_assert_equal_i(pkt->type, GIT_PKT_UNPACK);
cl_assert_equal_i(pkt->unpack_ok, ok);
@@ -135,8 +144,9 @@ static void assert_ng_parses(const char *line, const char *expected_ref, const c
size_t linelen = strlen(line) + 1;
const char *endptr;
git_pkt_ng *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
- cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
cl_assert_equal_i(pkt->type, GIT_PKT_NG);
cl_assert_equal_strn(pkt->ref, expected_ref, strlen(expected_ref));
cl_assert_equal_strn(pkt->msg, expected_msg, strlen(expected_msg));
@@ -153,10 +163,11 @@ static void assert_ref_parses_(const char *line, size_t linelen, const char *exp
const char *endptr;
git_pkt_ref *pkt;
git_oid oid;
+ git_pkt_parse_data pkt_parse_data = { 0 };
cl_git_pass(git_oid__fromstr(&oid, expected_oid, GIT_OID_SHA1));
- cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen));
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
cl_assert_equal_i(pkt->type, GIT_PKT_REF);
cl_assert_equal_oid(&pkt->head.oid, &oid);
cl_assert_equal_strn(pkt->head.name, expected_ref, strlen(expected_ref));
@@ -171,8 +182,10 @@ static void assert_ref_parses_(const char *line, size_t linelen, const char *exp
static void assert_pkt_fails(const char *line)
{
const char *endptr;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+
git_pkt *pkt;
- cl_git_fail(git_pkt_parse_line(&pkt, &endptr, line, strlen(line) + 1));
+ cl_git_fail(git_pkt_parse_line(&pkt, &endptr, line, strlen(line) + 1, &pkt_parse_data));
}
void test_transports_smart_packet__parsing_garbage_fails(void)
diff --git a/tests/libgit2/win32/systemdir.c b/tests/libgit2/win32/systemdir.c
index 52c1784a1b7..9039f05b21a 100644
--- a/tests/libgit2/win32/systemdir.c
+++ b/tests/libgit2/win32/systemdir.c
@@ -1,7 +1,6 @@
#include "clar_libgit2.h"
#include "futils.h"
#include "sysdir.h"
-#include "win32/findfile.h"
#ifdef GIT_WIN32
static char *path_save;
diff --git a/tests/libgit2/worktree/config.c b/tests/libgit2/worktree/config.c
index 81dcfe1fa51..1fd1f75b47b 100644
--- a/tests/libgit2/worktree/config.c
+++ b/tests/libgit2/worktree/config.c
@@ -6,15 +6,19 @@
static worktree_fixture fixture =
WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+static worktree_fixture submodule =
+ WORKTREE_FIXTURE_INIT("submodules", "submodules-worktree-parent");
void test_worktree_config__initialize(void)
{
setup_fixture_worktree(&fixture);
+ setup_fixture_worktree(&submodule);
}
void test_worktree_config__cleanup(void)
{
cleanup_fixture_worktree(&fixture);
+ cleanup_fixture_worktree(&submodule);
}
void test_worktree_config__open(void)
@@ -27,7 +31,7 @@ void test_worktree_config__open(void)
git_config_free(cfg);
}
-void test_worktree_config__set(void)
+void test_worktree_config__set_level_local(void)
{
git_config *cfg;
int32_t val;
@@ -45,3 +49,78 @@ void test_worktree_config__set(void)
cl_assert_equal_i(val, 5);
git_config_free(cfg);
}
+
+void test_worktree_config__requires_extension(void)
+{
+ git_config *cfg;
+ git_config *wtcfg;
+ int extension = 0;
+
+ /*
+ * the "submodules" repo does not have extensions.worktreeconfig
+ * set, the worktree configuration should not be available.
+ */
+ cl_git_pass(git_repository_config(&cfg, submodule.repo));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_bool(&extension, cfg, "extensions.worktreeconfig"));
+ cl_assert_equal_i(0, extension);
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_open_level(&wtcfg, cfg, GIT_CONFIG_LEVEL_WORKTREE));
+ git_config_free(cfg);
+
+ /* the "testrepo" repo does have the configuration set. */
+ cl_git_pass(git_repository_config(&cfg, fixture.repo));
+ cl_git_pass(git_config_get_bool(&extension, cfg, "extensions.worktreeconfig"));
+ cl_assert_equal_i(1, extension);
+ cl_git_pass(git_config_open_level(&wtcfg, cfg, GIT_CONFIG_LEVEL_WORKTREE));
+ git_config_free(wtcfg);
+ git_config_free(cfg);
+}
+
+void test_worktree_config__exists(void)
+{
+ git_config *cfg, *wtcfg, *snap;
+ const char *str;
+
+ cl_git_pass(git_repository_config(&cfg, fixture.repo));
+ cl_git_pass(git_repository_config(&wtcfg, fixture.worktree));
+
+ cl_git_pass(git_config_snapshot(&snap, cfg));
+ cl_git_pass(git_config_get_string(&str, snap, "worktreetest.config"));
+ cl_assert_equal_s("mainrepo", str);
+ git_config_free(snap);
+
+ cl_git_pass(git_config_snapshot(&snap, wtcfg));
+ cl_git_pass(git_config_get_string(&str, snap, "worktreetest.config"));
+ cl_assert_equal_s("worktreerepo", str);
+ git_config_free(snap);
+
+ git_config_free(cfg);
+ git_config_free(wtcfg);
+}
+
+void test_worktree_config__set_level_worktree(void)
+{
+ git_config *cfg;
+ git_config *wtcfg;
+ int32_t val;
+
+ cl_git_pass(git_repository_config(&cfg, fixture.repo));
+ cl_git_pass(git_config_open_level(&wtcfg, cfg, GIT_CONFIG_LEVEL_WORKTREE));
+ cl_git_pass(git_config_set_int32(wtcfg, "worktree.specific", 42));
+
+ cl_git_pass(git_config_get_int32(&val, cfg, "worktree.specific"));
+ cl_assert_equal_i(val, 42);
+
+ /* reopen to verify config has been set */
+ git_config_free(cfg);
+ cl_git_pass(git_repository_config(&cfg, fixture.repo));
+ cl_git_pass(git_config_get_int32(&val, cfg, "worktree.specific"));
+ cl_assert_equal_i(val, 42);
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_delete_entry(cfg, "worktree.specific"));
+
+ cl_git_pass(git_config_delete_entry(wtcfg, "worktree.specific"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_int32(&val, cfg, "worktree.specific"));
+
+ git_config_free(cfg);
+ git_config_free(wtcfg);
+}
diff --git a/tests/libgit2/worktree/refs.c b/tests/libgit2/worktree/refs.c
index 557726aafb6..51e7b2b9463 100644
--- a/tests/libgit2/worktree/refs.c
+++ b/tests/libgit2/worktree/refs.c
@@ -20,7 +20,7 @@ void test_worktree_refs__cleanup(void)
cleanup_fixture_worktree(&fixture);
}
-void test_worktree_refs__list(void)
+void test_worktree_refs__list_no_difference_in_worktree(void)
{
git_strarray refs, wtrefs;
unsigned i, j;
@@ -61,6 +61,66 @@ void test_worktree_refs__list(void)
cl_git_pass(error);
}
+void test_worktree_refs__list_worktree_specific(void)
+{
+ git_strarray refs, wtrefs;
+ git_reference *ref, *new_branch;
+ int error = 0;
+ git_oid oid;
+
+ cl_git_pass(git_reference_name_to_id(&oid, fixture.repo, "refs/heads/dir"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, fixture.repo, "refs/bisect/a-bisect-ref"));
+ cl_git_pass(git_reference_create(
+ &new_branch, fixture.worktree, "refs/bisect/a-bisect-ref", &oid,
+ 0, "test"));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, fixture.repo, "refs/bisect/a-bisect-ref"));
+ cl_git_pass(git_reference_lookup(&ref, fixture.worktree, "refs/bisect/a-bisect-ref"));
+
+ cl_git_pass(git_reference_list(&refs, fixture.repo));
+ cl_git_pass(git_reference_list(&wtrefs, fixture.worktree));
+
+ cl_assert_equal_sz(wtrefs.count, refs.count + 1);
+
+ git_reference_free(ref);
+ git_reference_free(new_branch);
+ git_strarray_dispose(&refs);
+ git_strarray_dispose(&wtrefs);
+ cl_git_pass(error);
+}
+
+void test_worktree_refs__list_worktree_specific_hidden_in_main_repo(void)
+{
+ git_strarray refs, wtrefs;
+ git_reference *ref, *new_branch;
+ int error = 0;
+ git_oid oid;
+
+ cl_git_pass(
+ git_reference_name_to_id(&oid, fixture.repo, "refs/heads/dir"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(
+ &ref, fixture.worktree, "refs/bisect/a-bisect-ref"));
+ cl_git_pass(git_reference_create(
+ &new_branch, fixture.repo, "refs/bisect/a-bisect-ref", &oid,
+ 0, "test"));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(
+ &ref, fixture.worktree, "refs/bisect/a-bisect-ref"));
+ cl_git_pass(git_reference_lookup(
+ &ref, fixture.repo, "refs/bisect/a-bisect-ref"));
+
+ cl_git_pass(git_reference_list(&refs, fixture.repo));
+ cl_git_pass(git_reference_list(&wtrefs, fixture.worktree));
+
+ cl_assert_equal_sz(refs.count, wtrefs.count + 1);
+
+ git_reference_free(ref);
+ git_reference_free(new_branch);
+ git_strarray_dispose(&refs);
+ git_strarray_dispose(&wtrefs);
+ cl_git_pass(error);
+}
+
void test_worktree_refs__read_head(void)
{
git_reference *head;
diff --git a/tests/libgit2/worktree/worktree.c b/tests/libgit2/worktree/worktree.c
index 9fd27f49c5f..00e3e3fe791 100644
--- a/tests/libgit2/worktree/worktree.c
+++ b/tests/libgit2/worktree/worktree.c
@@ -217,6 +217,50 @@ void test_worktree_worktree__init(void)
git_repository_free(repo);
}
+void test_worktree_worktree__add_remove_add(void)
+{
+ git_worktree_add_options add_opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
+ git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+ git_str path = GIT_BUF_INIT;
+ git_reference *branch;
+ git_repository *repo;
+ git_worktree *wt;
+
+ /* Add the worktree */
+ cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-add-remove-add"));
+ cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, NULL));
+
+ /* Open and verify created repo */
+ cl_git_pass(git_repository_open(&repo, path.ptr));
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-add-remove-add/") == 0);
+ cl_git_pass(git_branch_lookup(&branch, repo, "worktree-add-remove-add", GIT_BRANCH_LOCAL));
+ git_reference_free(branch);
+ git_repository_free(repo);
+
+ /* Prune the worktree */
+ opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_WORKING_TREE;
+ cl_git_pass(git_worktree_prune(wt, &opts));
+ cl_assert(!git_fs_path_exists(wt->gitdir_path));
+ cl_assert(!git_fs_path_exists(wt->gitlink_path));
+ git_worktree_free(wt);
+
+ /* Add the worktree back with default options should fail. */
+ cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, &add_opts));
+ /* If allowing checkout of existing branches, it should succeed. */
+ add_opts.checkout_existing = 1;
+ cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, &add_opts));
+
+ /* Open and verify created repo */
+ cl_git_pass(git_repository_open(&repo, path.ptr));
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-add-remove-add/") == 0);
+ cl_git_pass(git_branch_lookup(&branch, repo, "worktree-add-remove-add", GIT_BRANCH_LOCAL));
+ git_reference_free(branch);
+ git_repository_free(repo);
+
+ git_str_dispose(&path);
+ git_worktree_free(wt);
+}
+
void test_worktree_worktree__add_locked(void)
{
git_worktree *wt;
@@ -244,6 +288,7 @@ void test_worktree_worktree__add_locked(void)
void test_worktree_worktree__init_existing_branch(void)
{
+ git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
git_reference *head, *branch;
git_commit *commit;
git_worktree *wt;
@@ -251,12 +296,18 @@ void test_worktree_worktree__init_existing_branch(void)
cl_git_pass(git_repository_head(&head, fixture.repo));
cl_git_pass(git_commit_lookup(&commit, fixture.repo, &head->target.oid));
- cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new", commit, false));
+ cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new-exist", commit, false));
- cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
- cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL));
+ cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-new-exist"));
+
+ /* Add the worktree back with default options should fail. */
+ cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new-exist", path.ptr, NULL));
+ /* If allowing checkout of existing branches, it should succeed. */
+ opts.checkout_existing = 1;
+ cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new-exist", path.ptr, &opts));
git_str_dispose(&path);
+ git_worktree_free(wt);
git_commit_free(commit);
git_reference_free(head);
git_reference_free(branch);
@@ -645,3 +696,16 @@ void test_worktree_worktree__validate_invalid_worktreedir(void)
git_worktree_free(wt);
}
+
+void test_worktree_worktree__is_prunable_missing_repo(void)
+{
+ git_worktree *wt;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ p_rename("testrepo", "testrepo-tmp");
+ /* Should not be prunable since the repository moved */
+ cl_assert(!git_worktree_is_prunable(wt, NULL));
+ p_rename("testrepo-tmp", "testrepo");
+
+ git_worktree_free(wt);
+}
diff --git a/tests/resources/git-sha256.index b/tests/resources/git-sha256.index
new file mode 100644
index 00000000000..84b5cabd998
Binary files /dev/null and b/tests/resources/git-sha256.index differ
diff --git a/tests/resources/grafted.git/HEAD b/tests/resources/grafted.git/HEAD
new file mode 100644
index 00000000000..cb089cd89a7
--- /dev/null
+++ b/tests/resources/grafted.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/grafted.git/config b/tests/resources/grafted.git/config
new file mode 100644
index 00000000000..e6da231579b
--- /dev/null
+++ b/tests/resources/grafted.git/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ ignorecase = true
+ precomposeunicode = true
diff --git a/tests/resources/grafted.git/info/grafts b/tests/resources/grafted.git/info/grafts
new file mode 100644
index 00000000000..bb9df8c0a80
--- /dev/null
+++ b/tests/resources/grafted.git/info/grafts
@@ -0,0 +1,3 @@
+f503807ffa920e407a600cfaee96b7152259acc7 2f3053cbff8a4ca2f0666de364ddb734a28a31a9
+0512adebd3782157f0d5c9b22b043f87b4aaff9e 2f3053cbff8a4ca2f0666de364ddb734a28a31a9
+66cc22a015f6ca75b34c82d28f78ba663876bade e414f42f4e6bc6934563a2349a8600f0ab68618e 8a00e91619098618be97c0d2ceabb05a2c58edd9 1c18e80a276611bb9b146590616bbc5aebdf2945 2f3053cbff8a4ca2f0666de364ddb734a28a31a9
diff --git a/tests/resources/grafted.git/objects/05/12adebd3782157f0d5c9b22b043f87b4aaff9e b/tests/resources/grafted.git/objects/05/12adebd3782157f0d5c9b22b043f87b4aaff9e
new file mode 100644
index 00000000000..16880d596e5
Binary files /dev/null and b/tests/resources/grafted.git/objects/05/12adebd3782157f0d5c9b22b043f87b4aaff9e differ
diff --git a/tests/resources/grafted.git/objects/1c/18e80a276611bb9b146590616bbc5aebdf2945 b/tests/resources/grafted.git/objects/1c/18e80a276611bb9b146590616bbc5aebdf2945
new file mode 100644
index 00000000000..2c057b85d87
Binary files /dev/null and b/tests/resources/grafted.git/objects/1c/18e80a276611bb9b146590616bbc5aebdf2945 differ
diff --git a/tests/resources/grafted.git/objects/1c/3f11eca55d76bc1bf7353ca7e4226246d353ed b/tests/resources/grafted.git/objects/1c/3f11eca55d76bc1bf7353ca7e4226246d353ed
new file mode 100644
index 00000000000..b92a3047f18
Binary files /dev/null and b/tests/resources/grafted.git/objects/1c/3f11eca55d76bc1bf7353ca7e4226246d353ed differ
diff --git a/tests/resources/grafted.git/objects/2a/f02ebff1fc0142d2380c98758d81c67b365869 b/tests/resources/grafted.git/objects/2a/f02ebff1fc0142d2380c98758d81c67b365869
new file mode 100644
index 00000000000..ed3f874a7bd
Binary files /dev/null and b/tests/resources/grafted.git/objects/2a/f02ebff1fc0142d2380c98758d81c67b365869 differ
diff --git a/tests/resources/grafted.git/objects/2b/ecadd3f1ecad07a054392421edf9c0e1c375b2 b/tests/resources/grafted.git/objects/2b/ecadd3f1ecad07a054392421edf9c0e1c375b2
new file mode 100644
index 00000000000..724eedbb2ec
Binary files /dev/null and b/tests/resources/grafted.git/objects/2b/ecadd3f1ecad07a054392421edf9c0e1c375b2 differ
diff --git a/tests/resources/grafted.git/objects/2f/3053cbff8a4ca2f0666de364ddb734a28a31a9 b/tests/resources/grafted.git/objects/2f/3053cbff8a4ca2f0666de364ddb734a28a31a9
new file mode 100644
index 00000000000..3d124a67352
Binary files /dev/null and b/tests/resources/grafted.git/objects/2f/3053cbff8a4ca2f0666de364ddb734a28a31a9 differ
diff --git a/tests/resources/grafted.git/objects/45/342912745ba6f8893b1e126df4653a4355df1a b/tests/resources/grafted.git/objects/45/342912745ba6f8893b1e126df4653a4355df1a
new file mode 100644
index 00000000000..4a8c471bd5b
Binary files /dev/null and b/tests/resources/grafted.git/objects/45/342912745ba6f8893b1e126df4653a4355df1a differ
diff --git a/tests/resources/grafted.git/objects/48/b2b333732644eafb385771a992b923fa88f135 b/tests/resources/grafted.git/objects/48/b2b333732644eafb385771a992b923fa88f135
new file mode 100644
index 00000000000..ac640636beb
Binary files /dev/null and b/tests/resources/grafted.git/objects/48/b2b333732644eafb385771a992b923fa88f135 differ
diff --git a/tests/resources/grafted.git/objects/5d/31bf4b437e1191b6c709c665f1bd329d0ed0bf b/tests/resources/grafted.git/objects/5d/31bf4b437e1191b6c709c665f1bd329d0ed0bf
new file mode 100644
index 00000000000..47a05377ef3
Binary files /dev/null and b/tests/resources/grafted.git/objects/5d/31bf4b437e1191b6c709c665f1bd329d0ed0bf differ
diff --git a/tests/resources/grafted.git/objects/66/cc22a015f6ca75b34c82d28f78ba663876bade b/tests/resources/grafted.git/objects/66/cc22a015f6ca75b34c82d28f78ba663876bade
new file mode 100644
index 00000000000..c68b2cd4fb1
--- /dev/null
+++ b/tests/resources/grafted.git/objects/66/cc22a015f6ca75b34c82d28f78ba663876bade
@@ -0,0 +1,2 @@
+xœ¥ŽM
+Â0F]ç³Ê$™üDÜx=Á4N´Ð4ÒFϯÔ#¸úàñx|©–260dvmñap1°Ñaò}ö¹·N‹hL!E&}ã”BTO^dn ¤)“É$~øê–œ·l,õ=bF|ô:ŠâW{ÔÎm”y¸rYë‡uÛN~øt/Ñ«M,
\ No newline at end of file
diff --git a/tests/resources/grafted.git/objects/6c/f192eb71cd3243c9fbbe2551012c4449de3fcf b/tests/resources/grafted.git/objects/6c/f192eb71cd3243c9fbbe2551012c4449de3fcf
new file mode 100644
index 00000000000..a437f243204
Binary files /dev/null and b/tests/resources/grafted.git/objects/6c/f192eb71cd3243c9fbbe2551012c4449de3fcf differ
diff --git a/tests/resources/grafted.git/objects/7c/9da502b2744b70522bb694cd607fb00104a233 b/tests/resources/grafted.git/objects/7c/9da502b2744b70522bb694cd607fb00104a233
new file mode 100644
index 00000000000..b363584fd9a
Binary files /dev/null and b/tests/resources/grafted.git/objects/7c/9da502b2744b70522bb694cd607fb00104a233 differ
diff --git a/tests/resources/grafted.git/objects/8a/00e91619098618be97c0d2ceabb05a2c58edd9 b/tests/resources/grafted.git/objects/8a/00e91619098618be97c0d2ceabb05a2c58edd9
new file mode 100644
index 00000000000..887778a60a0
--- /dev/null
+++ b/tests/resources/grafted.git/objects/8a/00e91619098618be97c0d2ceabb05a2c58edd9
@@ -0,0 +1,2 @@
+xœ¥ŽA
+Â0E]ç³Êt’4Dtá <Á$™Ô@“Jï¯Ô#¸úðïý°”’ч¶Š@B§5ú@b$Ñ'ÒØ[´iƒ÷±g&V/^¥6Hõ ]J<ŠAÇbH,2Þõ–ÈŽ‚SünÏe…{ËR«ÀƒË¶T8oûvòÃשpž»°”|›Ódàˆ„¨Â~µÉ_u‹1× RžÅ¨ßšNC
\ No newline at end of file
diff --git a/tests/resources/grafted.git/objects/a0/4de168dd5c43aa2af594d794d62e922f8b3b34 b/tests/resources/grafted.git/objects/a0/4de168dd5c43aa2af594d794d62e922f8b3b34
new file mode 100644
index 00000000000..1ed3ed906ab
Binary files /dev/null and b/tests/resources/grafted.git/objects/a0/4de168dd5c43aa2af594d794d62e922f8b3b34 differ
diff --git a/tests/resources/grafted.git/objects/b2/b4f9e5fe5dacbb2f98bd71d1dc86c7b571ddd1 b/tests/resources/grafted.git/objects/b2/b4f9e5fe5dacbb2f98bd71d1dc86c7b571ddd1
new file mode 100644
index 00000000000..2adc857219e
Binary files /dev/null and b/tests/resources/grafted.git/objects/b2/b4f9e5fe5dacbb2f98bd71d1dc86c7b571ddd1 differ
diff --git a/tests/resources/grafted.git/objects/ba/54010f8d41532eb130eba420f50248881f7fc2 b/tests/resources/grafted.git/objects/ba/54010f8d41532eb130eba420f50248881f7fc2
new file mode 100644
index 00000000000..52a88727471
Binary files /dev/null and b/tests/resources/grafted.git/objects/ba/54010f8d41532eb130eba420f50248881f7fc2 differ
diff --git a/tests/resources/grafted.git/objects/d7/224d49d6d5aff6ade596ed74f4bcd4f77b29e2 b/tests/resources/grafted.git/objects/d7/224d49d6d5aff6ade596ed74f4bcd4f77b29e2
new file mode 100644
index 00000000000..5b41b6778f8
--- /dev/null
+++ b/tests/resources/grafted.git/objects/d7/224d49d6d5aff6ade596ed74f4bcd4f77b29e2
@@ -0,0 +1,2 @@
+xϴA
+Â0E]ç³ÊdÒDÜx=Á$™h¡I¤ž_Gpõà/ÿ…šóÔ€,íÚ"ä%pŒ&é/Ñ1Úތԓ–˜Æ€¢ƒqÖ“zð"¥ZMÅGãÒÖ%Œ6ŒžÈcoÒà|ÏœÒ(ŠŸí^8·IJ¸r^kú±“ß|ºežæ.Ô|mÑ u„öHˆ*lW›ü%QÉõ%¦Y„ZÚ§bUoaRj
\ No newline at end of file
diff --git a/tests/resources/grafted.git/objects/db/8e43f297a313c439530c977b733aaa8c10d54e b/tests/resources/grafted.git/objects/db/8e43f297a313c439530c977b733aaa8c10d54e
new file mode 100644
index 00000000000..b9cf5947b70
Binary files /dev/null and b/tests/resources/grafted.git/objects/db/8e43f297a313c439530c977b733aaa8c10d54e differ
diff --git a/tests/resources/grafted.git/objects/e4/14f42f4e6bc6934563a2349a8600f0ab68618e b/tests/resources/grafted.git/objects/e4/14f42f4e6bc6934563a2349a8600f0ab68618e
new file mode 100644
index 00000000000..1a14959c49b
Binary files /dev/null and b/tests/resources/grafted.git/objects/e4/14f42f4e6bc6934563a2349a8600f0ab68618e differ
diff --git a/tests/resources/grafted.git/objects/e6/7b587a57850c69f6f9351ee10c7c8a41dacc78 b/tests/resources/grafted.git/objects/e6/7b587a57850c69f6f9351ee10c7c8a41dacc78
new file mode 100644
index 00000000000..213f9ac22ef
Binary files /dev/null and b/tests/resources/grafted.git/objects/e6/7b587a57850c69f6f9351ee10c7c8a41dacc78 differ
diff --git a/tests/resources/grafted.git/objects/f0/7330bc2e4ed4bd0bf2301505f6c6bbad01aa2a b/tests/resources/grafted.git/objects/f0/7330bc2e4ed4bd0bf2301505f6c6bbad01aa2a
new file mode 100644
index 00000000000..f2d64889207
Binary files /dev/null and b/tests/resources/grafted.git/objects/f0/7330bc2e4ed4bd0bf2301505f6c6bbad01aa2a differ
diff --git a/tests/resources/grafted.git/objects/f5/03807ffa920e407a600cfaee96b7152259acc7 b/tests/resources/grafted.git/objects/f5/03807ffa920e407a600cfaee96b7152259acc7
new file mode 100644
index 00000000000..21436c177e9
--- /dev/null
+++ b/tests/resources/grafted.git/objects/f5/03807ffa920e407a600cfaee96b7152259acc7
@@ -0,0 +1,2 @@
+xϴA
+Â0E]ç³J&˜6)ºðž ™™Ô@“@ï¯Ô#¸ú·xŸZ)¹z{ê›D/É‹Mb9PŒ&yyBF&7Òí—™Q…wµ
=KÏPöVáº;ÈOß–ò:P+3 ÕÆƒ³6Z+:®»üQwæ\Hy£>zAƒ
\ No newline at end of file
diff --git a/tests/resources/grafted.git/refs/heads/bottom b/tests/resources/grafted.git/refs/heads/bottom
new file mode 100644
index 00000000000..10513e69877
--- /dev/null
+++ b/tests/resources/grafted.git/refs/heads/bottom
@@ -0,0 +1 @@
+66cc22a015f6ca75b34c82d28f78ba663876bade
diff --git a/tests/resources/grafted.git/refs/heads/branch b/tests/resources/grafted.git/refs/heads/branch
new file mode 100644
index 00000000000..d0fe5c28390
--- /dev/null
+++ b/tests/resources/grafted.git/refs/heads/branch
@@ -0,0 +1 @@
+8a00e91619098618be97c0d2ceabb05a2c58edd9
diff --git a/tests/resources/grafted.git/refs/heads/master b/tests/resources/grafted.git/refs/heads/master
new file mode 100644
index 00000000000..de809b9421a
--- /dev/null
+++ b/tests/resources/grafted.git/refs/heads/master
@@ -0,0 +1 @@
+2f3053cbff8a4ca2f0666de364ddb734a28a31a9
diff --git a/tests/resources/grafted.git/refs/heads/top b/tests/resources/grafted.git/refs/heads/top
new file mode 100644
index 00000000000..ce226110b8e
--- /dev/null
+++ b/tests/resources/grafted.git/refs/heads/top
@@ -0,0 +1 @@
+1c18e80a276611bb9b146590616bbc5aebdf2945
diff --git a/tests/resources/merge-resolve/.gitted/objects/29 b/tests/resources/merge-resolve/.gitted/objects/29
new file mode 100644
index 00000000000..9661507079f
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/29 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/54/c9d15687fb4f56e08252662962d6d1dbc09d9d b/tests/resources/merge-resolve/.gitted/objects/54/c9d15687fb4f56e08252662962d6d1dbc09d9d
new file mode 100644
index 00000000000..7e0555b3ff6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/54/c9d15687fb4f56e08252662962d6d1dbc09d9d
@@ -0,0 +1,3 @@
+x¥NI
+1ôœWô]žì/þÀ´Žãa&#~ß(þÀ:ÕFQ\—åÖAÛ¸éMtrš8K0Y›àK&Ž¥Xk¢w—É™"è$pPwj²vÕ\RðhÑgI=ŘK*SDaMÚ*zö¹68åµç¹.ºÂ^†ûaGù?µãº`
+q6YØâ€î8ÛåÏÅ3W^áBM½ùiQ†
\ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/57/16ca5987cbf97d6bb54920bea6adde242d87e6 b/tests/resources/merge-resolve/.gitted/objects/57/16ca5987cbf97d6bb54920bea6adde242d87e6
new file mode 100644
index 00000000000..cfc3920fb3f
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/57/16ca5987cbf97d6bb54920bea6adde242d87e6 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/60/b12be2d2f57977ce83d8dfd32e2394ac1ba1a2 b/tests/resources/merge-resolve/.gitted/objects/60/b12be2d2f57977ce83d8dfd32e2394ac1ba1a2
new file mode 100644
index 00000000000..f53f75e0a13
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/60/b12be2d2f57977ce83d8dfd32e2394ac1ba1a2 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a6/5140d5ec9f47064f614ecf8e43776baa5c0c11 b/tests/resources/merge-resolve/.gitted/objects/a6/5140d5ec9f47064f614ecf8e43776baa5c0c11
new file mode 100644
index 00000000000..dc6cf6493cb
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/a6/5140d5ec9f47064f614ecf8e43776baa5c0c11 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ab/347abd8cda4a0e3b8bb42bb620c0c72c7df779 b/tests/resources/merge-resolve/.gitted/objects/ab/347abd8cda4a0e3b8bb42bb620c0c72c7df779
new file mode 100644
index 00000000000..d743a385c21
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/ab/347abd8cda4a0e3b8bb42bb620c0c72c7df779 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/bc/114411903fd2afaa4bb9b85ed13f27e37ac375 b/tests/resources/merge-resolve/.gitted/objects/bc/114411903fd2afaa4bb9b85ed13f27e37ac375
new file mode 100644
index 00000000000..08941ff9538
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/bc/114411903fd2afaa4bb9b85ed13f27e37ac375 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/cd/edf9760406dc79e0c6a8899ce9f180ec2a23a0 b/tests/resources/merge-resolve/.gitted/objects/cd/edf9760406dc79e0c6a8899ce9f180ec2a23a0
new file mode 100644
index 00000000000..011b5b3e58c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cd/edf9760406dc79e0c6a8899ce9f180ec2a23a0
@@ -0,0 +1,2 @@
+x¥A
+B1C]÷³¤µÓö"n¼¨Ó)ÀþBÕë[ŘUò ÕR¤Ã^ÛMo̽3¨“c:dÚcö™òÄhCð·i2FÅGŸkƒKzÅ–à:ײ֎<èÇù[üÒŽj9 zBë0XØê!5è8ïü猒EºÄ;4~Ê*uQo—$DÝ
\ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/de/06afe070b65f94d7d791c39a6d389c58dda60d b/tests/resources/merge-resolve/.gitted/objects/de/06afe070b65f94d7d791c39a6d389c58dda60d
new file mode 100644
index 00000000000..28567b624d1
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/de/06afe070b65f94d7d791c39a6d389c58dda60d differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e5/0a49f9558d09d4d3bfc108363bb24c127ed263 b/tests/resources/merge-resolve/.gitted/objects/e5/0a49f9558d09d4d3bfc108363bb24c127ed263
new file mode 100644
index 00000000000..251c5dfb20c
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/e5/0a49f9558d09d4d3bfc108363bb24c127ed263 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ea/789495e0a72efadcd0f86a48f4c9ed435bb8a3 b/tests/resources/merge-resolve/.gitted/objects/ea/789495e0a72efadcd0f86a48f4c9ed435bb8a3
new file mode 100644
index 00000000000..ed98d70564d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ea/789495e0a72efadcd0f86a48f4c9ed435bb8a3
@@ -0,0 +1,3 @@
+x¥OIj1ÌY¯è»!´¶‘ÁøâäV‹ÉA£ Ëøû–M~:ÕE÷Ö~&o>æÌZ;§uB[‹¡JärN9z)ÚVÄb¼ú¥!Ç.Rj
+:Ü
+‡$ÈŘKª:¢°!c ÝçÞ\˃Fï½·[?àK–ûbyê“{;ƒõÞ œpA-wòϵ–S[_iÀì{WOG‰Rï
\ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/emptyfile_renames b/tests/resources/merge-resolve/.gitted/refs/heads/emptyfile_renames
new file mode 100644
index 00000000000..89b4eea8ff5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/emptyfile_renames
@@ -0,0 +1 @@
+ea789495e0a72efadcd0f86a48f4c9ed435bb8a3
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/emptyfile_renames-branch b/tests/resources/merge-resolve/.gitted/refs/heads/emptyfile_renames-branch
new file mode 100644
index 00000000000..1c6a9f4db55
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/emptyfile_renames-branch
@@ -0,0 +1 @@
+ab347abd8cda4a0e3b8bb42bb620c0c72c7df779
diff --git a/tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.idx b/tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.idx
new file mode 100644
index 00000000000..7aa472ceaea
Binary files /dev/null and b/tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.idx differ
diff --git a/tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.pack b/tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.pack
new file mode 100644
index 00000000000..adbe70ad35a
Binary files /dev/null and b/tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.pack differ
diff --git a/tests/resources/process/cat.bat b/tests/resources/process/cat.bat
new file mode 100644
index 00000000000..af9b573c793
--- /dev/null
+++ b/tests/resources/process/cat.bat
@@ -0,0 +1,2 @@
+@ECHO OFF
+FOR /F "tokens=*" %%a IN ('more') DO ECHO %%a
diff --git a/tests/resources/process/env.cmd b/tests/resources/process/env.cmd
new file mode 100644
index 00000000000..62675cf9edd
--- /dev/null
+++ b/tests/resources/process/env.cmd
@@ -0,0 +1,2 @@
+@ECHO OFF
+SET
diff --git a/tests/resources/process/helloworld.sh b/tests/resources/process/helloworld.sh
new file mode 100755
index 00000000000..0c4aefc384d
--- /dev/null
+++ b/tests/resources/process/helloworld.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "Hello, world."
diff --git a/tests/resources/process/pwd.bat b/tests/resources/process/pwd.bat
new file mode 100644
index 00000000000..82e4fb60f17
--- /dev/null
+++ b/tests/resources/process/pwd.bat
@@ -0,0 +1,2 @@
+@ECHO OFF
+ECHO %CD%
diff --git a/tests/resources/push.sh b/tests/resources/push.sh
index 3e77fb5307f..648c2ad26f5 100644
--- a/tests/resources/push.sh
+++ b/tests/resources/push.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/usr/bin/env bash
#creates push_src repo for libgit2 push tests.
set -eu
diff --git a/tests/resources/pushoptions.git/HEAD b/tests/resources/pushoptions.git/HEAD
new file mode 100644
index 00000000000..b870d82622c
--- /dev/null
+++ b/tests/resources/pushoptions.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/main
diff --git a/tests/resources/pushoptions.git/branches/.gitignore b/tests/resources/pushoptions.git/branches/.gitignore
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/resources/pushoptions.git/config b/tests/resources/pushoptions.git/config
new file mode 100644
index 00000000000..23d39788fa7
--- /dev/null
+++ b/tests/resources/pushoptions.git/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ ignorecase = true
+ precomposeunicode = true
+[receive]
+ advertisePushOptions = true
diff --git a/tests/resources/pushoptions.git/description b/tests/resources/pushoptions.git/description
new file mode 100644
index 00000000000..498b267a8c7
--- /dev/null
+++ b/tests/resources/pushoptions.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/pushoptions.git/hooks/pre-receive b/tests/resources/pushoptions.git/hooks/pre-receive
new file mode 100755
index 00000000000..24f48d34c54
--- /dev/null
+++ b/tests/resources/pushoptions.git/hooks/pre-receive
@@ -0,0 +1,3 @@
+#!/bin/sh
+printf "${GIT_PUSH_OPTION_1}${GIT_PUSH_OPTION_2}${GIT_PUSH_OPTION_3}" > "${GIT_PUSH_OPTION_0}"
+exit 0
diff --git a/tests/resources/pushoptions.git/info/exclude b/tests/resources/pushoptions.git/info/exclude
new file mode 100644
index 00000000000..a5196d1be8f
--- /dev/null
+++ b/tests/resources/pushoptions.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/pushoptions.git/objects/info/.gitignore b/tests/resources/pushoptions.git/objects/info/.gitignore
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/resources/pushoptions.git/objects/pack/.gitignore b/tests/resources/pushoptions.git/objects/pack/.gitignore
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/resources/pushoptions.git/refs/heads/.gitignore b/tests/resources/pushoptions.git/refs/heads/.gitignore
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/resources/pushoptions.git/refs/tags/.gitignore b/tests/resources/pushoptions.git/refs/tags/.gitignore
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/resources/status_skiphash/.gitted/COMMIT_EDITMSG b/tests/resources/status_skiphash/.gitted/COMMIT_EDITMSG
new file mode 100644
index 00000000000..ea450f959b9
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+New file
diff --git a/tests/resources/status_skiphash/.gitted/HEAD b/tests/resources/status_skiphash/.gitted/HEAD
new file mode 100644
index 00000000000..b870d82622c
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/main
diff --git a/tests/resources/status_skiphash/.gitted/MERGE_RR b/tests/resources/status_skiphash/.gitted/MERGE_RR
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/resources/status_skiphash/.gitted/config b/tests/resources/status_skiphash/.gitted/config
new file mode 100644
index 00000000000..16aebb686c2
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/config
@@ -0,0 +1,9 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+[index]
+ skipHash = true
diff --git a/tests/resources/status_skiphash/.gitted/description b/tests/resources/status_skiphash/.gitted/description
new file mode 100644
index 00000000000..498b267a8c7
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/status_skiphash/.gitted/index b/tests/resources/status_skiphash/.gitted/index
new file mode 100644
index 00000000000..1963fe0d3d4
Binary files /dev/null and b/tests/resources/status_skiphash/.gitted/index differ
diff --git a/tests/resources/status_skiphash/.gitted/info/exclude b/tests/resources/status_skiphash/.gitted/info/exclude
new file mode 100644
index 00000000000..a5196d1be8f
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/status_skiphash/.gitted/logs/HEAD b/tests/resources/status_skiphash/.gitted/logs/HEAD
new file mode 100644
index 00000000000..35e1a747dbe
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 34f4c90b237fcb4c677772a6093f3cba239c41a5 Parnic 1708097798 -0600 commit (initial): New file
diff --git a/tests/resources/status_skiphash/.gitted/logs/refs/heads/main b/tests/resources/status_skiphash/.gitted/logs/refs/heads/main
new file mode 100644
index 00000000000..35e1a747dbe
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/logs/refs/heads/main
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 34f4c90b237fcb4c677772a6093f3cba239c41a5 Parnic 1708097798 -0600 commit (initial): New file
diff --git a/tests/resources/status_skiphash/.gitted/objects/34/f4c90b237fcb4c677772a6093f3cba239c41a5 b/tests/resources/status_skiphash/.gitted/objects/34/f4c90b237fcb4c677772a6093f3cba239c41a5
new file mode 100644
index 00000000000..0513158cb21
Binary files /dev/null and b/tests/resources/status_skiphash/.gitted/objects/34/f4c90b237fcb4c677772a6093f3cba239c41a5 differ
diff --git a/tests/resources/status_skiphash/.gitted/objects/71/a21e67674e9717aa7380e5782ec5e070a8d7e0 b/tests/resources/status_skiphash/.gitted/objects/71/a21e67674e9717aa7380e5782ec5e070a8d7e0
new file mode 100644
index 00000000000..7c48fa4f43b
Binary files /dev/null and b/tests/resources/status_skiphash/.gitted/objects/71/a21e67674e9717aa7380e5782ec5e070a8d7e0 differ
diff --git a/tests/resources/status_skiphash/.gitted/objects/d7/c1f165e51adbbfd7760162b7a5802d4117740c b/tests/resources/status_skiphash/.gitted/objects/d7/c1f165e51adbbfd7760162b7a5802d4117740c
new file mode 100644
index 00000000000..c685321ce69
Binary files /dev/null and b/tests/resources/status_skiphash/.gitted/objects/d7/c1f165e51adbbfd7760162b7a5802d4117740c differ
diff --git a/tests/resources/status_skiphash/.gitted/refs/heads/main b/tests/resources/status_skiphash/.gitted/refs/heads/main
new file mode 100644
index 00000000000..693a12b664c
--- /dev/null
+++ b/tests/resources/status_skiphash/.gitted/refs/heads/main
@@ -0,0 +1 @@
+34f4c90b237fcb4c677772a6093f3cba239c41a5
diff --git a/tests/resources/status_skiphash/new_file b/tests/resources/status_skiphash/new_file
new file mode 100644
index 00000000000..badcfca348a
--- /dev/null
+++ b/tests/resources/status_skiphash/new_file
@@ -0,0 +1 @@
+new_file
diff --git a/tests/resources/testrepo/.gitted/config b/tests/resources/testrepo/.gitted/config
index d0114012f98..04d750a93bc 100644
--- a/tests/resources/testrepo/.gitted/config
+++ b/tests/resources/testrepo/.gitted/config
@@ -3,6 +3,8 @@
filemode = true
bare = false
logallrefupdates = true
+[extensions]
+ worktreeconfig = true
[remote "test"]
url = git://github.com/libgit2/libgit2
fetch = +refs/heads/*:refs/remotes/test/*
diff --git a/tests/resources/testrepo/.gitted/config.worktree b/tests/resources/testrepo/.gitted/config.worktree
new file mode 100644
index 00000000000..df9f0caf650
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/config.worktree
@@ -0,0 +1,2 @@
+[worktreetest]
+ config = mainrepo
diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/config.worktree b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/config.worktree
new file mode 100644
index 00000000000..7a130a7aed7
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/config.worktree
@@ -0,0 +1,2 @@
+[worktreetest]
+ config = worktreerepo
diff --git a/tests/resources/testrepo_256.git/FETCH_HEAD b/tests/resources/testrepo_256.git/FETCH_HEAD
new file mode 100644
index 00000000000..4562d90261c
--- /dev/null
+++ b/tests/resources/testrepo_256.git/FETCH_HEAD
@@ -0,0 +1,2 @@
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f branch 'master' of git://example.com/git/testrepo.git
+7e9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c not-for-merge branch 'haacked' of git://example.com/git/testrepo.git
diff --git a/tests/resources/testrepo_256.git/HEAD b/tests/resources/testrepo_256.git/HEAD
new file mode 100644
index 00000000000..cb089cd89a7
--- /dev/null
+++ b/tests/resources/testrepo_256.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/testrepo_256.git/HEAD_TRACKER b/tests/resources/testrepo_256.git/HEAD_TRACKER
new file mode 100644
index 00000000000..40d876b4c67
--- /dev/null
+++ b/tests/resources/testrepo_256.git/HEAD_TRACKER
@@ -0,0 +1 @@
+ref: HEAD
diff --git a/tests/resources/testrepo_256.git/config b/tests/resources/testrepo_256.git/config
new file mode 100644
index 00000000000..c1aac4a0392
--- /dev/null
+++ b/tests/resources/testrepo_256.git/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryFormatVersion = 1
+ filemode = true
+ bare = true
+ ignorecase = true
+ precomposeunicode = true
+[extensions]
+ objectFormat = sha256
diff --git a/tests/resources/testrepo_256.git/description b/tests/resources/testrepo_256.git/description
new file mode 100644
index 00000000000..498b267a8c7
--- /dev/null
+++ b/tests/resources/testrepo_256.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/testrepo_256.git/hooks/applypatch-msg.sample b/tests/resources/testrepo_256.git/hooks/applypatch-msg.sample
new file mode 100755
index 00000000000..a5d7b84a673
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/applypatch-msg.sample
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message taken by
+# applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit. The hook is
+# allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "applypatch-msg".
+
+. git-sh-setup
+commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
+test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
+:
diff --git a/tests/resources/testrepo_256.git/hooks/commit-msg.sample b/tests/resources/testrepo_256.git/hooks/commit-msg.sample
new file mode 100755
index 00000000000..b58d1184a9d
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/commit-msg.sample
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message.
+# Called by "git commit" with one argument, the name of the file
+# that has the commit message. The hook should exit with non-zero
+# status after issuing an appropriate message if it wants to stop the
+# commit. The hook is allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "commit-msg".
+
+# Uncomment the below to add a Signed-off-by line to the message.
+# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
+# hook is more suited to it.
+#
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
+
+# This example catches duplicate Signed-off-by lines.
+
+test "" = "$(grep '^Signed-off-by: ' "$1" |
+ sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
+ echo >&2 Duplicate Signed-off-by lines.
+ exit 1
+}
diff --git a/tests/resources/testrepo_256.git/hooks/fsmonitor-watchman.sample b/tests/resources/testrepo_256.git/hooks/fsmonitor-watchman.sample
new file mode 100755
index 00000000000..14ed0aa42de
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/fsmonitor-watchman.sample
@@ -0,0 +1,173 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use IPC::Open2;
+
+# An example hook script to integrate Watchman
+# (https://facebook.github.io/watchman/) with git to speed up detecting
+# new and modified files.
+#
+# The hook is passed a version (currently 2) and last update token
+# formatted as a string and outputs to stdout a new update token and
+# all files that have been modified since the update token. Paths must
+# be relative to the root of the working tree and separated by a single NUL.
+#
+# To enable this hook, rename this file to "query-watchman" and set
+# 'git config core.fsmonitor .git/hooks/query-watchman'
+#
+my ($version, $last_update_token) = @ARGV;
+
+# Uncomment for debugging
+# print STDERR "$0 $version $last_update_token\n";
+
+# Check the hook interface version
+if ($version ne 2) {
+ die "Unsupported query-fsmonitor hook version '$version'.\n" .
+ "Falling back to scanning...\n";
+}
+
+my $git_work_tree = get_working_dir();
+
+my $retry = 1;
+
+my $json_pkg;
+eval {
+ require JSON::XS;
+ $json_pkg = "JSON::XS";
+ 1;
+} or do {
+ require JSON::PP;
+ $json_pkg = "JSON::PP";
+};
+
+launch_watchman();
+
+sub launch_watchman {
+ my $o = watchman_query();
+ if (is_work_tree_watched($o)) {
+ output_result($o->{clock}, @{$o->{files}});
+ }
+}
+
+sub output_result {
+ my ($clockid, @files) = @_;
+
+ # Uncomment for debugging watchman output
+ # open (my $fh, ">", ".git/watchman-output.out");
+ # binmode $fh, ":utf8";
+ # print $fh "$clockid\n@files\n";
+ # close $fh;
+
+ binmode STDOUT, ":utf8";
+ print $clockid;
+ print "\0";
+ local $, = "\0";
+ print @files;
+}
+
+sub watchman_clock {
+ my $response = qx/watchman clock "$git_work_tree"/;
+ die "Failed to get clock id on '$git_work_tree'.\n" .
+ "Falling back to scanning...\n" if $? != 0;
+
+ return $json_pkg->new->utf8->decode($response);
+}
+
+sub watchman_query {
+ my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
+ or die "open2() failed: $!\n" .
+ "Falling back to scanning...\n";
+
+ # In the query expression below we're asking for names of files that
+ # changed since $last_update_token but not from the .git folder.
+ #
+ # To accomplish this, we're using the "since" generator to use the
+ # recency index to select candidate nodes and "fields" to limit the
+ # output to file names only. Then we're using the "expression" term to
+ # further constrain the results.
+ if (substr($last_update_token, 0, 1) eq "c") {
+ $last_update_token = "\"$last_update_token\"";
+ }
+ my $query = <<" END";
+ ["query", "$git_work_tree", {
+ "since": $last_update_token,
+ "fields": ["name"],
+ "expression": ["not", ["dirname", ".git"]]
+ }]
+ END
+
+ # Uncomment for debugging the watchman query
+ # open (my $fh, ">", ".git/watchman-query.json");
+ # print $fh $query;
+ # close $fh;
+
+ print CHLD_IN $query;
+ close CHLD_IN;
+ my $response = do {local $/; };
+
+ # Uncomment for debugging the watch response
+ # open ($fh, ">", ".git/watchman-response.json");
+ # print $fh $response;
+ # close $fh;
+
+ die "Watchman: command returned no output.\n" .
+ "Falling back to scanning...\n" if $response eq "";
+ die "Watchman: command returned invalid output: $response\n" .
+ "Falling back to scanning...\n" unless $response =~ /^\{/;
+
+ return $json_pkg->new->utf8->decode($response);
+}
+
+sub is_work_tree_watched {
+ my ($output) = @_;
+ my $error = $output->{error};
+ if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
+ $retry--;
+ my $response = qx/watchman watch "$git_work_tree"/;
+ die "Failed to make watchman watch '$git_work_tree'.\n" .
+ "Falling back to scanning...\n" if $? != 0;
+ $output = $json_pkg->new->utf8->decode($response);
+ $error = $output->{error};
+ die "Watchman: $error.\n" .
+ "Falling back to scanning...\n" if $error;
+
+ # Uncomment for debugging watchman output
+ # open (my $fh, ">", ".git/watchman-output.out");
+ # close $fh;
+
+ # Watchman will always return all files on the first query so
+ # return the fast "everything is dirty" flag to git and do the
+ # Watchman query just to get it over with now so we won't pay
+ # the cost in git to look up each individual file.
+ my $o = watchman_clock();
+ $error = $output->{error};
+
+ die "Watchman: $error.\n" .
+ "Falling back to scanning...\n" if $error;
+
+ output_result($o->{clock}, ("/"));
+ $last_update_token = $o->{clock};
+
+ eval { launch_watchman() };
+ return 0;
+ }
+
+ die "Watchman: $error.\n" .
+ "Falling back to scanning...\n" if $error;
+
+ return 1;
+}
+
+sub get_working_dir {
+ my $working_dir;
+ if ($^O =~ 'msys' || $^O =~ 'cygwin') {
+ $working_dir = Win32::GetCwd();
+ $working_dir =~ tr/\\/\//;
+ } else {
+ require Cwd;
+ $working_dir = Cwd::cwd();
+ }
+
+ return $working_dir;
+}
diff --git a/tests/resources/testrepo_256.git/hooks/post-update.sample b/tests/resources/testrepo_256.git/hooks/post-update.sample
new file mode 100755
index 00000000000..ec17ec1939b
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/post-update.sample
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# An example hook script to prepare a packed repository for use over
+# dumb transports.
+#
+# To enable this hook, rename this file to "post-update".
+
+exec git update-server-info
diff --git a/tests/resources/testrepo_256.git/hooks/pre-applypatch.sample b/tests/resources/testrepo_256.git/hooks/pre-applypatch.sample
new file mode 100755
index 00000000000..4142082bcb9
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/pre-applypatch.sample
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed
+# by applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-applypatch".
+
+. git-sh-setup
+precommit="$(git rev-parse --git-path hooks/pre-commit)"
+test -x "$precommit" && exec "$precommit" ${1+"$@"}
+:
diff --git a/tests/resources/testrepo_256.git/hooks/pre-commit.sample b/tests/resources/testrepo_256.git/hooks/pre-commit.sample
new file mode 100755
index 00000000000..e144712c85c
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/pre-commit.sample
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by "git commit" with no arguments. The hook should
+# exit with non-zero status after issuing an appropriate message if
+# it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-commit".
+
+if git rev-parse --verify HEAD >/dev/null 2>&1
+then
+ against=HEAD
+else
+ # Initial commit: diff against an empty tree object
+ against=$(git hash-object -t tree /dev/null)
+fi
+
+# If you want to allow non-ASCII filenames set this variable to true.
+allownonascii=$(git config --type=bool hooks.allownonascii)
+
+# Redirect output to stderr.
+exec 1>&2
+
+# Cross platform projects tend to avoid non-ASCII filenames; prevent
+# them from being added to the repository. We exploit the fact that the
+# printable range starts at the space character and ends with tilde.
+if [ "$allownonascii" != "true" ] &&
+ # Note that the use of brackets around a tr range is ok here, (it's
+ # even required, for portability to Solaris 10's /usr/bin/tr), since
+ # the square bracket bytes happen to fall in the designated range.
+ test $(git diff --cached --name-only --diff-filter=A -z $against |
+ LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
+then
+ cat <<\EOF
+Error: Attempt to add a non-ASCII file name.
+
+This can cause problems if you want to work with people on other platforms.
+
+To be portable it is advisable to rename the file.
+
+If you know what you are doing you can disable this check using:
+
+ git config hooks.allownonascii true
+EOF
+ exit 1
+fi
+
+# If there are whitespace errors, print the offending file names and fail.
+exec git diff-index --check --cached $against --
diff --git a/tests/resources/testrepo_256.git/hooks/pre-merge-commit.sample b/tests/resources/testrepo_256.git/hooks/pre-merge-commit.sample
new file mode 100755
index 00000000000..399eab1924e
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/pre-merge-commit.sample
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by "git merge" with no arguments. The hook should
+# exit with non-zero status after issuing an appropriate message to
+# stderr if it wants to stop the merge commit.
+#
+# To enable this hook, rename this file to "pre-merge-commit".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/pre-commit" &&
+ exec "$GIT_DIR/hooks/pre-commit"
+:
diff --git a/tests/resources/testrepo_256.git/hooks/pre-push.sample b/tests/resources/testrepo_256.git/hooks/pre-push.sample
new file mode 100755
index 00000000000..4ce688d32b7
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/pre-push.sample
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+# An example hook script to verify what is about to be pushed. Called by "git
+# push" after it has checked the remote status, but before anything has been
+# pushed. If this script exits with a non-zero status nothing will be pushed.
+#
+# This hook is called with the following parameters:
+#
+# $1 -- Name of the remote to which the push is being done
+# $2 -- URL to which the push is being done
+#
+# If pushing without using a named remote those arguments will be equal.
+#
+# Information about the commits which are being pushed is supplied as lines to
+# the standard input in the form:
+#
+#
+#
+# This sample shows how to prevent push of commits where the log message starts
+# with "WIP" (work in progress).
+
+remote="$1"
+url="$2"
+
+zero=$(git hash-object --stdin &2 "Found WIP commit in $local_ref, not pushing"
+ exit 1
+ fi
+ fi
+done
+
+exit 0
diff --git a/tests/resources/testrepo_256.git/hooks/pre-rebase.sample b/tests/resources/testrepo_256.git/hooks/pre-rebase.sample
new file mode 100755
index 00000000000..6cbef5c370d
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/pre-rebase.sample
@@ -0,0 +1,169 @@
+#!/bin/sh
+#
+# Copyright (c) 2006, 2008 Junio C Hamano
+#
+# The "pre-rebase" hook is run just before "git rebase" starts doing
+# its job, and can prevent the command from running by exiting with
+# non-zero status.
+#
+# The hook is called with the following parameters:
+#
+# $1 -- the upstream the series was forked from.
+# $2 -- the branch being rebased (or empty when rebasing the current branch).
+#
+# This sample shows how to prevent topic branches that are already
+# merged to 'next' branch from getting rebased, because allowing it
+# would result in rebasing already published history.
+
+publish=next
+basebranch="$1"
+if test "$#" = 2
+then
+ topic="refs/heads/$2"
+else
+ topic=`git symbolic-ref HEAD` ||
+ exit 0 ;# we do not interrupt rebasing detached HEAD
+fi
+
+case "$topic" in
+refs/heads/??/*)
+ ;;
+*)
+ exit 0 ;# we do not interrupt others.
+ ;;
+esac
+
+# Now we are dealing with a topic branch being rebased
+# on top of master. Is it OK to rebase it?
+
+# Does the topic really exist?
+git show-ref -q "$topic" || {
+ echo >&2 "No such branch $topic"
+ exit 1
+}
+
+# Is topic fully merged to master?
+not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
+if test -z "$not_in_master"
+then
+ echo >&2 "$topic is fully merged to master; better remove it."
+ exit 1 ;# we could allow it, but there is no point.
+fi
+
+# Is topic ever merged to next? If so you should not be rebasing it.
+only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
+only_next_2=`git rev-list ^master ${publish} | sort`
+if test "$only_next_1" = "$only_next_2"
+then
+ not_in_topic=`git rev-list "^$topic" master`
+ if test -z "$not_in_topic"
+ then
+ echo >&2 "$topic is already up to date with master"
+ exit 1 ;# we could allow it, but there is no point.
+ else
+ exit 0
+ fi
+else
+ not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
+ /usr/bin/perl -e '
+ my $topic = $ARGV[0];
+ my $msg = "* $topic has commits already merged to public branch:\n";
+ my (%not_in_next) = map {
+ /^([0-9a-f]+) /;
+ ($1 => 1);
+ } split(/\n/, $ARGV[1]);
+ for my $elem (map {
+ /^([0-9a-f]+) (.*)$/;
+ [$1 => $2];
+ } split(/\n/, $ARGV[2])) {
+ if (!exists $not_in_next{$elem->[0]}) {
+ if ($msg) {
+ print STDERR $msg;
+ undef $msg;
+ }
+ print STDERR " $elem->[1]\n";
+ }
+ }
+ ' "$topic" "$not_in_next" "$not_in_master"
+ exit 1
+fi
+
+<<\DOC_END
+
+This sample hook safeguards topic branches that have been
+published from being rewound.
+
+The workflow assumed here is:
+
+ * Once a topic branch forks from "master", "master" is never
+ merged into it again (either directly or indirectly).
+
+ * Once a topic branch is fully cooked and merged into "master",
+ it is deleted. If you need to build on top of it to correct
+ earlier mistakes, a new topic branch is created by forking at
+ the tip of the "master". This is not strictly necessary, but
+ it makes it easier to keep your history simple.
+
+ * Whenever you need to test or publish your changes to topic
+ branches, merge them into "next" branch.
+
+The script, being an example, hardcodes the publish branch name
+to be "next", but it is trivial to make it configurable via
+$GIT_DIR/config mechanism.
+
+With this workflow, you would want to know:
+
+(1) ... if a topic branch has ever been merged to "next". Young
+ topic branches can have stupid mistakes you would rather
+ clean up before publishing, and things that have not been
+ merged into other branches can be easily rebased without
+ affecting other people. But once it is published, you would
+ not want to rewind it.
+
+(2) ... if a topic branch has been fully merged to "master".
+ Then you can delete it. More importantly, you should not
+ build on top of it -- other people may already want to
+ change things related to the topic as patches against your
+ "master", so if you need further changes, it is better to
+ fork the topic (perhaps with the same name) afresh from the
+ tip of "master".
+
+Let's look at this example:
+
+ o---o---o---o---o---o---o---o---o---o "next"
+ / / / /
+ / a---a---b A / /
+ / / / /
+ / / c---c---c---c B /
+ / / / \ /
+ / / / b---b C \ /
+ / / / / \ /
+ ---o---o---o---o---o---o---o---o---o---o---o "master"
+
+
+A, B and C are topic branches.
+
+ * A has one fix since it was merged up to "next".
+
+ * B has finished. It has been fully merged up to "master" and "next",
+ and is ready to be deleted.
+
+ * C has not merged to "next" at all.
+
+We would want to allow C to be rebased, refuse A, and encourage
+B to be deleted.
+
+To compute (1):
+
+ git rev-list ^master ^topic next
+ git rev-list ^master next
+
+ if these match, topic has not merged in next at all.
+
+To compute (2):
+
+ git rev-list master..topic
+
+ if this is empty, it is fully merged to "master".
+
+DOC_END
diff --git a/tests/resources/testrepo_256.git/hooks/pre-receive.sample b/tests/resources/testrepo_256.git/hooks/pre-receive.sample
new file mode 100755
index 00000000000..a1fd29ec148
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/pre-receive.sample
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An example hook script to make use of push options.
+# The example simply echoes all push options that start with 'echoback='
+# and rejects all pushes when the "reject" push option is used.
+#
+# To enable this hook, rename this file to "pre-receive".
+
+if test -n "$GIT_PUSH_OPTION_COUNT"
+then
+ i=0
+ while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
+ do
+ eval "value=\$GIT_PUSH_OPTION_$i"
+ case "$value" in
+ echoback=*)
+ echo "echo from the pre-receive-hook: ${value#*=}" >&2
+ ;;
+ reject)
+ exit 1
+ esac
+ i=$((i + 1))
+ done
+fi
diff --git a/tests/resources/testrepo_256.git/hooks/prepare-commit-msg.sample b/tests/resources/testrepo_256.git/hooks/prepare-commit-msg.sample
new file mode 100755
index 00000000000..10fa14c5ab0
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/prepare-commit-msg.sample
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# An example hook script to prepare the commit log message.
+# Called by "git commit" with the name of the file that has the
+# commit message, followed by the description of the commit
+# message's source. The hook's purpose is to edit the commit
+# message file. If the hook fails with a non-zero status,
+# the commit is aborted.
+#
+# To enable this hook, rename this file to "prepare-commit-msg".
+
+# This hook includes three examples. The first one removes the
+# "# Please enter the commit message..." help message.
+#
+# The second includes the output of "git diff --name-status -r"
+# into the message, just before the "git status" output. It is
+# commented because it doesn't cope with --amend or with squashed
+# commits.
+#
+# The third example adds a Signed-off-by line to the message, that can
+# still be edited. This is rarely a good idea.
+
+COMMIT_MSG_FILE=$1
+COMMIT_SOURCE=$2
+SHA1=$3
+
+/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"
+
+# case "$COMMIT_SOURCE,$SHA1" in
+# ,|template,)
+# /usr/bin/perl -i.bak -pe '
+# print "\n" . `git diff --cached --name-status -r`
+# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
+# *) ;;
+# esac
+
+# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
+# if test -z "$COMMIT_SOURCE"
+# then
+# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
+# fi
diff --git a/tests/resources/testrepo_256.git/hooks/push-to-checkout.sample b/tests/resources/testrepo_256.git/hooks/push-to-checkout.sample
new file mode 100755
index 00000000000..af5a0c0018b
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/push-to-checkout.sample
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+# An example hook script to update a checked-out tree on a git push.
+#
+# This hook is invoked by git-receive-pack(1) when it reacts to git
+# push and updates reference(s) in its repository, and when the push
+# tries to update the branch that is currently checked out and the
+# receive.denyCurrentBranch configuration variable is set to
+# updateInstead.
+#
+# By default, such a push is refused if the working tree and the index
+# of the remote repository has any difference from the currently
+# checked out commit; when both the working tree and the index match
+# the current commit, they are updated to match the newly pushed tip
+# of the branch. This hook is to be used to override the default
+# behaviour; however the code below reimplements the default behaviour
+# as a starting point for convenient modification.
+#
+# The hook receives the commit with which the tip of the current
+# branch is going to be updated:
+commit=$1
+
+# It can exit with a non-zero status to refuse the push (when it does
+# so, it must not modify the index or the working tree).
+die () {
+ echo >&2 "$*"
+ exit 1
+}
+
+# Or it can make any necessary changes to the working tree and to the
+# index to bring them to the desired state when the tip of the current
+# branch is updated to the new commit, and exit with a zero status.
+#
+# For example, the hook can simply run git read-tree -u -m HEAD "$1"
+# in order to emulate git fetch that is run in the reverse direction
+# with git push, as the two-tree form of git read-tree -u -m is
+# essentially the same as git switch or git checkout that switches
+# branches while keeping the local changes in the working tree that do
+# not interfere with the difference between the branches.
+
+# The below is a more-or-less exact translation to shell of the C code
+# for the default behaviour for git's push-to-checkout hook defined in
+# the push_to_deploy() function in builtin/receive-pack.c.
+#
+# Note that the hook will be executed from the repository directory,
+# not from the working tree, so if you want to perform operations on
+# the working tree, you will have to adapt your code accordingly, e.g.
+# by adding "cd .." or using relative paths.
+
+if ! git update-index -q --ignore-submodules --refresh
+then
+ die "Up-to-date check failed"
+fi
+
+if ! git diff-files --quiet --ignore-submodules --
+then
+ die "Working directory has unstaged changes"
+fi
+
+# This is a rough translation of:
+#
+# head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX
+if git cat-file -e HEAD 2>/dev/null
+then
+ head=HEAD
+else
+ head=$(git hash-object -t tree --stdin &2
+ echo " (if you want, you could supply GIT_DIR then run" >&2
+ echo " $0 [ )" >&2
+ exit 1
+fi
+
+if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
+ echo "usage: $0 ][ " >&2
+ exit 1
+fi
+
+# --- Config
+allowunannotated=$(git config --type=bool hooks.allowunannotated)
+allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch)
+denycreatebranch=$(git config --type=bool hooks.denycreatebranch)
+allowdeletetag=$(git config --type=bool hooks.allowdeletetag)
+allowmodifytag=$(git config --type=bool hooks.allowmodifytag)
+
+# check for no description
+projectdesc=$(sed -e '1q' "$GIT_DIR/description")
+case "$projectdesc" in
+"Unnamed repository"* | "")
+ echo "*** Project description file hasn't been set" >&2
+ exit 1
+ ;;
+esac
+
+# --- Check types
+# if $newrev is 0000...0000, it's a commit to delete a ref.
+zero=$(git hash-object --stdin &2
+ echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
+ exit 1
+ fi
+ ;;
+ refs/tags/*,delete)
+ # delete tag
+ if [ "$allowdeletetag" != "true" ]; then
+ echo "*** Deleting a tag is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/tags/*,tag)
+ # annotated tag
+ if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
+ then
+ echo "*** Tag '$refname' already exists." >&2
+ echo "*** Modifying a tag is not allowed in this repository." >&2
+ exit 1
+ fi
+ ;;
+ refs/heads/*,commit)
+ # branch
+ if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
+ echo "*** Creating a branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/heads/*,delete)
+ # delete branch
+ if [ "$allowdeletebranch" != "true" ]; then
+ echo "*** Deleting a branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/remotes/*,commit)
+ # tracking branch
+ ;;
+ refs/remotes/*,delete)
+ # delete tracking branch
+ if [ "$allowdeletebranch" != "true" ]; then
+ echo "*** Deleting a tracking branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ *)
+ # Anything else (is there anything else?)
+ echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
+ exit 1
+ ;;
+esac
+
+# --- Finished
+exit 0
diff --git a/tests/resources/testrepo_256.git/index b/tests/resources/testrepo_256.git/index
new file mode 100644
index 00000000000..838e73883ba
Binary files /dev/null and b/tests/resources/testrepo_256.git/index differ
diff --git a/tests/resources/testrepo_256.git/info/exclude b/tests/resources/testrepo_256.git/info/exclude
new file mode 100644
index 00000000000..a5196d1be8f
--- /dev/null
+++ b/tests/resources/testrepo_256.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/testrepo_256.git/logs/HEAD b/tests/resources/testrepo_256.git/logs/HEAD
new file mode 100644
index 00000000000..59a4bb5f049
--- /dev/null
+++ b/tests/resources/testrepo_256.git/logs/HEAD
@@ -0,0 +1,7 @@
+0000000000000000000000000000000000000000000000000000000000000000 1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 Ben Straub 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
+1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Ben Straub 1335806603 -0900 commit:
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f cb282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 Ben Straub 1335806604 -0900 checkout: moving from master to 5b5b025
+cb282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Ben Straub 1335806605 -0900 checkout: moving from 5b5b025 to master
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f 901505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724 Ben Straub 1335806608 -0900 checkout: moving from master to br2
+901505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724 a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 Ben Straub 1335806617 -0900 commit: checking in
+a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Ben Straub ]