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 | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush) | -| **v1.5 branch** CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?branch=maint%2Fv1.5&event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.5) | -| **v1.4 branch** CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?branch=maint%2Fv1.4&event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.4) | +| **v1.8 branch** CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?branch=maint%2Fv1.8&event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.8) | +| **v1.7 branch** CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?branch=maint%2Fv1.7&event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.7) | | **Nightly** builds | [![Nightly Build](https://github.com/libgit2/libgit2/workflows/Nightly%20Build/badge.svg)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22Nightly+Build%22) [![Coverity Scan Status](https://scan.coverity.com/projects/639/badge.svg)](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ÛåÏÅ3­W^á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 1335806621 -0900 checkout: moving from br2 to master diff --git a/tests/resources/testrepo_256.git/logs/refs/heads/br2 b/tests/resources/testrepo_256.git/logs/refs/heads/br2 new file mode 100644 index 00000000000..e8ada81930a --- /dev/null +++ b/tests/resources/testrepo_256.git/logs/refs/heads/br2 @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000000000000000000000000000 901505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724 Ben Straub 1335806608 -0700 branch: Created from refs/remotes/origin/br2 +a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 Ben Straub 1335806617 -0700 commit: checking in diff --git a/tests/resources/testrepo_256.git/logs/refs/heads/master b/tests/resources/testrepo_256.git/logs/refs/heads/master new file mode 100644 index 00000000000..4b4c53f42bc --- /dev/null +++ b/tests/resources/testrepo_256.git/logs/refs/heads/master @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000000000000000000000000000 1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 Ben Straub 1335806563 -0800 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git +1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Ben Straub 1335806603 -0800 commit: checking in diff --git a/tests/resources/testrepo_256.git/logs/refs/heads/not-good b/tests/resources/testrepo_256.git/logs/refs/heads/not-good new file mode 100644 index 00000000000..6bac45bec14 --- /dev/null +++ b/tests/resources/testrepo_256.git/logs/refs/heads/not-good @@ -0,0 +1 @@ +0000000000000000000000000000000000000000000000000000000000000000 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Ben Straub 1336761944 -0700 branch: Created from master diff --git a/tests/resources/testrepo_256.git/logs/refs/heads/with-empty-log b/tests/resources/testrepo_256.git/logs/refs/heads/with-empty-log new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/resources/testrepo_256.git/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo_256.git/logs/refs/remotes/origin/HEAD new file mode 100644 index 00000000000..c1c020cff3e --- /dev/null +++ b/tests/resources/testrepo_256.git/logs/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000000000000000000000000000 1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 Ben Straub 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git diff --git a/tests/resources/testrepo_256.git/logs/refs/remotes/test/master b/tests/resources/testrepo_256.git/logs/refs/remotes/test/master new file mode 100644 index 00000000000..42bdaa27fc1 --- /dev/null +++ b/tests/resources/testrepo_256.git/logs/refs/remotes/test/master @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000000000000000000000000000 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Ben Straub 1335806565 -0800 update by push +decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f 1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 Ben Straub 1335806688 -0800 update by push diff --git a/tests/resources/testrepo_256.git/object-idx b/tests/resources/testrepo_256.git/object-idx new file mode 100644 index 00000000000..c4ce7c4bfde --- /dev/null +++ b/tests/resources/testrepo_256.git/object-idx @@ -0,0 +1,1700 @@ +0007e4a07f4c922a647ac9930790e6640c61b8555d0e0195d164bce8685f2390 05bf6713e26fca9af0b116adf6fa1a0e4a3a957c +002bf814b089dd7941f9cdd449e8c3a52815e8f8f5fdc918c4ff3fe4a2a88119 3e590fb2221ea3c117b7347956843d24132ac22b +00404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045 b91e763008b10db366442469339f90a2b8400d0a +0052fc5cfde91bf84d96ea764b24ccd20ce9d98a354bf69a6bd89c2ad38f977b 00c5bdc46d8f91dda9f64086347d60c18d1a1f8f +0092a5b445971910ad22ef5636caf11f6926695fa8eeaf34a4fb7abecc4a0e44 404e1182dc52d4caf284d0f39567cde79649b651 +00b1f32aab597e7bff93f9790bf580b030f4ed71d620a7178b2d9a2bad5b1f86 27c3c27807e7fd3c053e0bffc6da68051f343ba2 +0113a79463a552823f5865aeaa641137ad8741ac85ef923014a6a7c0c36aa1a3 73630ef121eb0e4a601df784dff973b321813f02 +01177bbe7923049c27d557ed912d7440e2925bda5a37492744a83c1ff2fdda1f 5f10ae02d45a1f030cf0255ff813d62fcf7e5575 +0118010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 763d71aadf09a7951596c9746c024e7eece7c7af +01731a6f97a36dc0299d3bd87b16f0679d675dc8bfc484dfc1173e3dea5914de 5f340851194fa963878fa70e6ef4b19bd2c74ee2 +01a1e78e1078db69cb6197fbc564851bb5861e6938638169c9111bfd08b608b0 11d973c9507b5768f2c312ad9ffa35f0baae8793 +01d396efb693b1b691ada667e623864b802bf4b57bd4fade0fb6d75688a9b99b d5d4b8cd7faeb7749015896a303f1d5331638d00 +0210a4981f476c0d98929dded65b4dc0ff653f0876c45ff08a6e7a1d647a4b2a d39941396f81224f394f1d59c31f64e2d0424fab +0216b94271c2795357116d8f9ae45facd9bb1658c5a42ef38ea54e57833ffa3f bdf2334251d8e65a999b96fc99547ba99e2f0205 +0244a04d70a80f0e5baaefe7ac1cd73dcb00692f982e1e3af824252a5d48fb3d 182f2b39391ff4db4008554e97753fdba4e1b57e +024e2a82f18d1034c0954bc91c96af19c98029aaab3f6aa8fde5ff1f5e70afe6 f57c220bb0949ba4d6da1967bbbbc7544c18c0d0 +027d76e06b173a74af36c5d1a10761c830b59ed8dc03acc58d1ae008b176160e 1b24fa1fb836589fef658723c5a68cda9a2647dc +0285241dd82eda29ba38b91c04f6385fae1b1b88990f8d0c86a88b5e6401a495 7c25137751c32c3971a57cc66c564dfc321328e1 +028e02901e7de25b3a0079c438da76f03df181cba76d986d3445be0675af21c2 f206b37de925edb8e36dce24fddb125e493d2cc9 +02a6da582f284e708f78ab9360628740395d9003bae9cc9cd5dc47e042a95e58 39e01811cd58f4ea0f5f5612d24b3115ad0736f4 +02c3ca38a3cadcb769643ed692998c0f3984429600365a39e143e5984c33c871 a42df0a3df3e2524c532f9f75aea0ac69c3984c3 +02df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b b235959d89084af8d3544fbdf675e47944f86524 +02dfdb9fccff7beaad58de3ae93694e3f957cefa10945d4652b3b1af4ecc1e41 a84d1309159aa125367376b01ec4c76847a4f514 +03063757ec40cebe0042c667ddb85bc4404925390abe9ce38fbf89761b9929e7 820e02ccef8c90470ec7f351464f9212f9dc0651 +033de70fa8afc174d134a3dfda7a8477b550dc12019904d418d9d0bb77d75c11 336ab27f4ead4627cc51ed162c31bc01f13b793e +03653094a4e4d102cb4ea4d29ca5d17dcd99a0d6997d0a6ea590b774333a4afa 03a9cba35dd9060f15a7309b438afaa246e80d5c +037bb440672e051dfb647627dd961209d4d5f65a3134aa561e9a22dea0c2ddff 6e9fb9dad8184cde865b1b062163dc209042f88d +039df023a072d4c4dedc8d69bdfd04955c9661ad0a6032d59417fae3d9a6e66f 93a16f146306fea6df405db1e6e065e52994ee5f +03c29c5b092a27c7d22553dabbf94cdd05ea9892de6a017bb7ebad8cd381a72e 8529bb1ac5647c96f1af4e933f34de2e9d8921fb +041ee569b4648c25a375895bf0e615b1e967164142dde5e3b74f42aebaa8b1db 15bffce9f7ce45f3b17d9357931667485a890455 +041f178780c21ca66c1a644e406bcc631e3374d3d3ff12963f27b7a2e4dcb98f f2ee83a040d7c1c26b85729558e7afc121d35651 +044fbdc5fa0794e3fe81c4cb6735792a7e2224ef108533d9e4d64dcc2f79f727 4738f4f3ea24fd89b4b4b27aa115eb6eaff9b397 +04504468bbc5da740dff1ee1bc21644ceac33a5a39e186624ac9c1700a005802 34a1dba52a7a0f2309c3bcca5ebd76dc8d2627c5 +046ec406f62cf062f45bcaa8881c901563595ad5e4e3eda3915979c8e8ac8786 c7417a02d56346deb435359fdba3de19c0be2f62 +048a56b75d20462e59f0b0c4f8c326013116a117654226fbd086eafc417b227b 894a57d31fdc8326a97814ad2403a45e7c8b6371 +04c49256444a1cca25ef7a6d0f6fb13db62c3a7d887c2b3bd0daf02b36665923 0e115c5be6bf252aa8cba76141eefb67f1541a06 +04cbb5dec4810b725cedad9ed47b8e070ebf7912e7f8a38b1524c7a5d18d6711 62ab0af99d2955901133fff37560bff1962e9b14 +050224b1d07966f578ce168d709148391b41eb6fd4b08e51fec8a549654a6780 c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 +05257051a1b3dc12041d32d8392ef288a18aa225b138ee92058a76f6453ffeed b6d7472f568134d9662dbe0753e692b7d57ed993 +052e483d293dd8832a959201ae52fbe0a3578a53dc964260c1ccd00563109d05 e8b17444a9abd2c9be360b44a8f8e13a4a2fdf27 +053bb083785b3e6287242a1e9b7cdebe356a25574b5f063538bfcf01d646f0d8 f15336fafccff5f3a05b4f4d1527254f25a364ef +0541301a56755c364219db68258da2e70e9146e9cad185e78b188d560dfbfc38 45504b7b9013d55e193001c8336f3db0ec2a81ed +058ec10e754833d209791fc619b8651f9f8738eceb4af6c9c4e096656b4b5d0c 639753339cadc1e57823094276afd3b57bdd6b62 +05a40f056205025b75b15337629dd90b2abe57a2622ca7968db2ffd4fa1198a7 77c0fe987fc4a3c24843c77615c3b6d2f53e27ab +05a580d164ba0fb859466759e0db1d63795eee186a48ad7b6684337933ed16b7 448a7a7cf218a5988f12274945613e86e1c2de11 +05f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750 e7b4ad382349ff96dd8199000580b9b1e2042eb0 +060c18716f5e2d8c74a30960f002096e43a4a8b0344e3df4f81222d0b7d73b89 7322d43c8aecf06b61944b8fd63d60758680a957 +061079b6d522c910cfe03f6549a63ab4ab43e212aaaf7dce90bdecd4c4f616d5 4b8e8b32c1ea1bee8c8c01795e46aa97006bb065 +06240c7992e7306c80b1d058777029034fc2a9028931dd93c5611bd56332b99a d5f668c547ff80ee9c5ec24613a2e552cb531c59 +062835d273525fdde70fef08a703cdff6fba048a2e1ef3e49a4a6f60e7e5c7e4 3e00d3bc4d8a36c974bbe91608da3b6b1af69caa +063517f949bd44147862a7eb7210fc0baccdf6c5c02c18c51459c7f317d803ad 2e2a9ece460a44d8f30f3df9af1e3686a8e609ec +0636b4292bfdd7274a977cb6f8b2ded8f315ea1bcd8dbedfca37964c2ed3d085 236e7579fed7763be77209efb8708960982f3cb3 +0642ed9f6735aa25854c90e266798a4843922fe3736a04252809e824bd4093d2 5db59540265ec48b82f604cd251907ca5d79b6fe +06665948a581e547ac1dc883b35bab54682fc311f7a87d5563a76501ecba55fc 4f0adcd0e61eabe06fe32be66b16559537124b7a +0676bc9e224b7a050b3e88cf7dd8f7fb148f521594a6d4132f0400bba6b1da83 8e9d2f904854c0dab03de84dcef103c1904b2115 +0709e20677dfeeb21242f9f7fa277ec464fbbfb56d303f8006c7e60c8e2be458 b3a2f90e54bcb32a88d13539e5672993c5037638 +0722571dc44f703576daf0dc43370557e6e9d83784b9454c98a7246ea3e0ab91 215da649e1c68079fb03f4f9bc0f196cca9855c8 +07314e5bfe0b98c1e061219664284ea9567e1d739530b3be8e3a6ef9527d59b9 9d3c39dfd8210faa558b0cc6fb427b680db343e7 +074acdefc8d17e7337df33b74c1ec5349ef0a0b18a7ec5fed41a2cf420db4f5e d8310141b69fa36657a620a045acad4e4ce0920c +074e9049747d5816aa03be67451eb42c425d1e328730b4d999dee78c4d2579f5 caa7d9e0c8f8671cf8b8c9c16eb18d31ca4802d8 +07571960f3f64bbb4d31f24f79abe05daae0498f6b4b77cca3d8e8ceccbd75d3 ad7cf03cce61aba463b9a687e388260395c8e727 +075e288bb4b170c21e5a569c3cafec80736aead134829acee0f9d4ba5b966a24 e9f5e87781e88aa3afa7679783c204604479f9e9 +076202d7d8fb1d193628801f76208f3a4967fa031a7f94d9236a0fbbe6f022f5 221c73c758cdfd3e7f7dc45b10576d1ced8232b5 +0763eb50fe80aa21eda853b7da406425b172b1d0ec7bdb41369a15868fddfb6f 02e741c6fca96b6d2bb89d064c3d1e31625d474c +076800113a06dabe75d578f2ce6018fc23027a83dc74cfd66f69f3c50f037ba0 270eb8c6bfbcece0f3abdf20f16f5a2c0b0e65e3 +078baf54914fab56842798c90fec863f15f67a22041e8aa54a88c43f059da050 3184a3abdfea231992254929ff4e275898e5bbf6 +07c31b4be5f3ec8f82c7ad6c91cbdf07ae876bb73041903d64fe8bac64bbcb6e 4ea481c61c59ab55169b7cbaae536ad50b49d6f0 +07dcde657a4d8046d02962b4fd67ee5b6ac27f712de66ae22c11d14406ecf092 b39843f3e42c8e062a29b2a64e3c3ce6a16eb72f +08198b33785f1eaf950aaa9ade1961e1513461f7f799905a6a625946ebf2db8e 1f9a9b4edab6da73b1625fc99e57fd84b88feaf7 +0838ea9dbbfb9167ea97d3b395b06abce5c951386d5549d87b3c4fe1ea8cbfd2 52261e14080afcd9cb0b1b17dc4146f2b6b252bc +083c2b8b445640d171e7aa667b0b32007006f786711032ebb82931cca69cc15b fd8430bc864cfcd5f10e5590f8a447e01b942bfe +083cf84e4ec87e8df6bb8cf4b5e95a5bf0e6b97b892fb99286d652362f0f1a8e 2e5faa78b12048d2818bf37986516cd287219f9b +083dc854acb0f53c854ca89defefdd7c9bc76bd6158a461045057c8febdee51d 2c4b7707f7e1d591ec1765ab7057a03283841c3e +08896c87dca842ffa2e7f9ca1941fc1b231b4f0cf966c992251fffc01be4eb0c ee5f4b6e2c16c9cf2510fd7ea60afb830a3e89b2 +08af1ef3969b835c4058d3c19fd6f30d0dca883b9553061823f016db50b935be f4153cc66a588da14bfb5e0ce59dcd638e6cad2d +092184eacb4296d03cd379da1e30b23163a3174e74f5f072b71dad644565d5bd e7bed373b521d9d5e20c3f7a2372a7eae76ae66a +09ca781675e86449d0910da772dfad9c80525b52452ea837a9820c3bc349296f fb799dfe77c7a1c9e2806c0021d659b5588d9105 +09f22926cfddb415baa1a22193d765facfea02edfb140702fae90714dcd402c2 621dbe6381d0b5239b6ca2f206fee56877326d5c +0a13ff7efebb553b2dc091008c6b27ae8fd8da265a0fc672ee0349533574a1d6 892bb2941b4c993ee188ba1d2da6c305dd5f6ff4 +0a280fa035c75607e5399cebe0e2e4ab44de35bd4d5c46a1accb47c8ea2bbee4 417f0abc9b41f01520df863567b25efd03bd281c +0a45b25fa6ba0d67638d8230be426a7a61baf23ea734ceea38312a380dd42181 a78bbeca7f7efad6ff1652d4ab03d4fbf8bb4c48 +0a58869d818eb06ab4b6601c466755db3a7e0d310e87f9f5f340f4ff8f3d84af 55eee3fca2f24bdaef26ea42ce12b98682b8988e +0a8400e1a7dfc68b38c9e82bb4d64f94a20f75ba0f026eb14d29ec978f66dd3a 41fb16c71644d9acab037f4a43d23627545d6d86 +0a85ae1fd17b2a87e83699ba3dac6e0ebb2efd62948ea70dcc127a250323b066 6dc05568057b37706c8453b3e24645a351fef6a3 +0a8c0add81b065b97451f7b47a9935f0e240251c5f90a89ced885cb7d4efc2ba b0a3077f9ef6e093f8d9869bdb0c07095bd722cb +0abaf90248f239f967d2361480e8111858721d240de6e928a9537af9a495062b b6d031157980136043dfbca0e49514a19c973759 +0ad45dbb04ca242774c42f4d7283094c9d0018db25224ec4e2b1512bd3e21e5a 45fb9738926af78da7280cb84ea022e8f7da98c9 +0aea420f9ece0be17a656910e5a64f71a591f2bb4a8938090680925a262fb511 0bf364b355f1dff3c70116f8629774848dadb9db +0aefd477d9e5b3f8d708a3cf6d78be6b670dfa2e2ec41244634f3b8f115d8e04 775e4b4c1296e9e3104f2a36ca9cf9356a130959 +0af326907c9f9a1c91cdfcba6e241582f97ade4535b16a1863b03938ca983104 29a210a471a1964a68f7f1f15214c626c58c1701 +0b1a932c7db41cb113d42e06acf29eddd00aaae9e39398328d475754930c6cb8 343a4b52f37094d193a27fde5ff20825d6d1e7c3 +0b2613505624d7fb949258282f13add0019ba5521f3075ad0f61bc1306b6d828 faa612641fdc2590a969e9a8600af696be701f07 +0b41ced8fc49173b868c15cba070c0167cc83eaf7c0bc62a19e2081d1e7f2676 2876a4c21cb5848079e2f2700312e54d4988c76f +0b6b4c4b598c420ebc1b9e404f53bbac59c8992100a92fb557d679a65e6c7bd5 ee2eb6fafc239de8d88016a1579314908b643b87 +0b99d2b73b57e4d477d096a82661320656f2e61f1d6811f569abb99e2d321b5d 3864bcd2aa1fe77365e8310662c037309d4db56c +0bb1a5866d49c4f22b173586a2d0f2a3d6a91d8ed4d8f6fc43a624df4afe00d3 1b9191dd8a427addca8309a59decc90267082bae +0bb5c743db0d4ef0b30982921e78fe42863d48ea34a6a917bf740ac2d677f281 9689311c952655dd9e315aaef81ea7bfc60d7c7a +0bd7502a0ec038d7740979ad9c9d2198f61b51bcf75b20259aa56e845c62c884 13edd1b7d5996a6b1ac38ca113f6fd751bf77827 +0bf8e96a28828be201f392a2f8d511d4b0d88296db716d64bd9e8268a5cf912a f019bd17a1769103dd82b885d7b46bb9a30bdc29 +0c0a6e5b0322e1f51e3a53c3e302901e9f6acfd0e3f9cf227bd02e61ddefd401 cd2c3cc64b9c003e5049d8a3efe4fed7154e6e3d +0c4f26749b1dbfbf4487590dea5b6bb49564712cb3f8343434092fd9b2ef0a0f 5ac6648ec382ec2f2c9ef81e6ded0f2fe5819a5c +0c55511a79e278b18657bfc60a0893d002d266c05809f54aa49337a0973a1eb9 6013ffa66ef5d856b2c4c72c44b1a63a7ccc2b57 +0c889bfb5e530a62c89b8c18ba11f4bf3417582a28c70b51f075ae74ab1c9a6c 5152c60f1d88042a52d66344b1d628d2465fef6c +0cffbec3386e267c6d32c541b5eb3e0492851a5b35b05a506dc60b8de0410b46 7828fb1437298bbf3f31da65581775665eece9cd +0d15b51f164a1237b5a6ea2f35f32d3f953c4476c1ca7edc91890cd5fb5d8541 18eb2b8adddc7eb7e2a0165e8d759ec5172a65a8 +0d737e3eab91d43f8080513e45fa2d708b3626020ff4b6900b236a613af8cb6e 34ff3378eac2b37e5220195e235a25a21f9b7e25 +0d8b5fe52e725773014a0487616540efea381d2af971ab5a18ab25d33ead1390 9aa923b26f49d08404fd272e0688f57c899d9116 +0da6d514fdcd1a5248f1ae2343991a7b08ee5da3169da9326ade767fe50bf166 74c7fe4f3a657d606a4a004c87336749079c0edf +0def2b73f560d08d26ce006d956af37c429277d3b492839521d00403060fc189 50d97878f49fd4c950a7a494b8dafb0d4797b57c +0e1ee44bdd5aeffc3a6601c83744e64a421a38d1c9355cd5aa20a1dd7c55d849 73473718290fe068243e8596fe649f200e8a6f34 +0e265ece40fee110c5a6cd29be1534c268f16e4f3c488bcfb9f3c8d1f46a4791 711223894375fe1186ac5bfffdc48fb1fa1e65cc +0e278cfd338b1dcc4f15bebb53ea527e756337a337e46d29806ed28c23f5469c f5124bfd46444f572c5faef3d45b084635c106a0 +0e31ba4e455789640544ac02d4e478fcd75d39de130241e0d2bc80db11c7b7f2 8722a77ec384aa0bc87b8f8f6c1c7ab6aeffae83 +0e3e687c74a6be88d4c09233ebe1d1a75748e349f7013284742e200c56821d9a 58f7931837dc08b3f94dce8ffe743afa40b15f0f +0e4970dd94378fe30b6519edf25dcff7679e395072715c12e6f365b74ef94415 624320d5f878cb1603c31bfd8cf1427fb6474443 +0e49790ea8b918a427928eab3e17f8925efc10e2c1a8c5bdef4c0eb20cac0c77 0e40ca2e7c706ac2f46af7f4f2d4bccb90bfbb5f +0e51009a15c1599ba55888c464d5d806694702f0593bd59895e4a134026bd34b 9e41833ef25a52288a46441ed9ad03dccd0b16c0 +0e6a1aaa2118384e5ceb8af69725827ff4bd942200a9b831522dddfb7aac8287 763dd65d1e7ca14658649d7c4d1cc1e4bfd2c671 +0e85ff7bdd5c6ef0a1a449461d1d01b6a0bcd3393a994bc5113915d173810071 e17a3f5673e0dc4d9ade34a0a276e6c202506fdf +0ea35e446e65bc1894a303aaff5ed807151bdbbdc8ab257510abab4e83fbf13b ab42d1514cd6b1007d794885dbcc0e7918e91b1f +0ec37a6fc3216ad07f16cba63ab57f80dea7e4091484cb25e6d677abca268fb0 9e210cb8bb8d3ad9204d90a584098d43c456a5e8 +0ee0fc56010269854bdcbd4a1a55675b2fc29c4410217004dbf77d3ec948954a b5cadb77fd216aee5dc133012142de6f78d4d47f +0ef8005c1111d4e19e9636d02032c8c3c295ba6f94598e4e782eb28cff273062 408509eef776dff28a36b63e8785ff04f7cf9846 +0ef9ad163c0b4599adf0444b293ad267012e784ca37dca8d8c286fa36914001e 3a6b70664b4c5bc0ea2e6d3fa8fb3b3b54d724dc +0f89c2e306e636e3084aa0bfe57938fbcd22981e809b7fc031ff90250befb83b 226eca421e206b16826b86f9aa9706aa31680240 +0f8caf733b3a1530869d8d6faf6d4069acec05cebdc33f15a8effbf0c7daf370 e92aec82dae6f9fc5d96b0ca7ce39d750905d7db +0fc8f0a12ec6889419fd677c9830b2d3feec2e9abf89415147cf0b3c6d444a35 182859f51a5cdd6ae9a1f75e21eb8d3f95d0a833 +1056c834175ba8749080270f4772ed2aad4ac1b74896a7cd36a75fa8a1aa53af 628c97ce7d9539e1009702b53fc7aab98c7f7759 +10b531c577e3e3fac1dee215ea33ea21202309a23b93352e1a9f09d6e9f45f4b 169d698699b441e9912283936a81aaab8e59c6ec +10d12b721c027f4041d0a52d5c02da1cadaa1ae0d05051421a36b861c4df210b c960d6a3f97ebd360dc3c9ea00fdb3cd5dc56224 +112e60f7f4409f6427a2bc302108becea27da72e31f92ee3476c31347cda4cb0 d22990a266f309c6eca013259c35ce567a0dfe08 +117e89c749a48a0c10b47e9433f26efd38192395676086af83ea2d48306b9f9f b4164f93a830a383b69daec35b6902f0cfe7ac21 +11bd4567822919bcbbf44a1323a1af5a0fcf9e2c6e3d2ad1329990eb1c0e9f5d 83de31463c02d36138f1d76cc22c18613920cd3b +11d0463a82345c2512bd704dd00211aefb7d5b8590ca92809122fd09486a9f06 d0a0d63086fae3b0682af7261df21f7d0f7f066d +11d187fe3c4fde36184f7e171e9b6afd066563b4ed568d9e1e3195b8488be47e 7f8a1d79534d90ed868ba664343b058258ddc64d +11f2111f69ec85dbbb408d68349c750a9ed94722b094e971f595f8aca5028713 0b19f4634685fdf3c2e58564038e614b8a8a563c +11f58a21e063f88f8aca15a05a6bb5148056acdc619be09214bba78819727a92 58840c6f7e9973b3700eae294e6f219c34cc6962 +12240f46f98af843abb3e12053abd997a1a5cf7ca50ec460775b6dfec9594346 2c9eb23012b2c0effdf247a9dfdeda4dcc6ad8b5 +122a2103f404b68ff8cce6c6ae82effa1026b8a254212401bf5473d27d70efba e97179b6de293c8f288957dc556239a44fa52e3e +1258e0cba7f787b1c74e00872bcf43799e5a7c77202685dc0ba60adc20da977b b2dea1ed674386234946b2604df68d71b677d2c1 +127437808b4e14417e5654a8e00dac615d334a14747d78d2ba1bd0df426ddc18 60711175510355240283174b84d16ee85fd599ec +12a834fdb798b91600e94c6851fc231cfd20f3eb65dcd736f39fecf96bff2788 edc438eedf6854c51e1a0d7954a6849046f5a4f6 +12e4e4e3649c0817fd80bf805c8daf890b40fe6788fd479db758575278bb58a3 91e5038d62abc64e8be483f3f764e51b0496de27 +12e8b48d89fb071c73fb0736f784c6ef0fcc2d4178a1fe17ed6d7a8950e6943b 01316e2444ef01a3a2939d6d891b1c7ee49bd05d +1331f4c99429b5934be1c4186c592069ee9167e73e65807cf8004ba54a151fc1 e740db6f334253940722dccd42ee45c96ee4f56f +137343a187f9ed56ddc750ccbdc5f284c1240ca1061854f609b4893cfc0ee035 b24cf42e6753bfe5763fdd7639f9cd8b629907e1 +13c8c9dae9fa63b1ec48b8abb12312fc8df61b9414678f504fa68164a48eef28 31438e245492d85fd6da4d1406eba0fbde8332a4 +13e9b8e6cd38ad58df3b3d9dd3480c06e69ea21f02639404c44c57f376ff2ec9 cb8e90b049ec87e0a999952544839c3c17f88e37 +1402d1979a18fa32944a3e584bc2bf63f08fd50fd16b228690b7c8614923b603 62860787aba2b59b1dd0bb7b9a810fbf3428cf75 +14386136c01f7aa705d82984066e49b9a3380d4f7a7f42dd5cc51511314e9a11 a43ff639453d9a5e374cd655bfb562d2d87b9edd +14747f3fe457467be5310c526d23808fcc18f5bf29feca812a3b3c74dba53a39 77c39760447694c5e9f5c7d09acbc788560a322d +1483e39b34bf45eeceada3f43bd76b8f1d8af079128291ee1f0ff9868fff8539 483b17569d2655bd9945ceac8e95c4279b2f2b2a +14a40b61fbea11fffdac7e51616ec7a7264e88a16a9021b4e252d443794d50d2 a27fb9c96feb433fb28a2713a863a7356d45eed7 +14ab853cff3d477dc79386905229f0854da0bd832125d6e8c3f3186073ec0709 b6d1b7d59f42493a4aa5440ce24070bbc4044f88 +14bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f 4a23e2e65ad4e31c4c9db7dc746650bfad082679 +15011ec9c79d290af2e08c865de53631aab299a27fc2a681903b3e3efab7dd47 b5cc24e623d5257f37da86627b8b314609945cde +1503ed3428050930a44def1fb250fbee95bb021a61f3ebe831502871c7df0035 f78cfc187cfead79cbeafb6e0c30f882a9d7c14f +15041151380e43cd57db1e6b758b00a2ec32782a3004e2b1ededc5e8bbd2e815 09fbd28ab7ae43f75acdf7c8adc6d148b8e78d57 +151c7527acc5b731199a03a932ae374331e16e5ae29256e98cb652f37669889b 91d973263a55708fa8255867b3202d81ef9c2868 +153c7d90e1734b2c9d1b32035aede6299a2a2eaeec425fabcd86706c8afc6194 faf2dae9a5d206471233bfa8698ecbdfb24785d1 +158217c424b47fba461c300c9f99cc27b50f7122c6156d4264c648197e634fd6 c14aa17cb87c64a3cc0f21e343389bcba1992d32 +15a4a79b6ab927befa6798c2028d37fa6cb4f6fe0bf80fdce6444a2fd9e67eca c72d81b5de8042aaefed06ca1abc1a248bfd0a4b +15bef585bbfec8410833ebdcb1760b7013d1dc3e4a7085902f199ed303f26098 89217d8f1a70da2916d611c989e6b046cdb94fca +15f128540ddf555e902e837a9e4bc39d752d70af7de1c91b9b0048c1bdf4ba31 f70dcd0924f1d58e9b68ec47fae83d29696865d7 +15f8faf9eb65aa6ddc66cd5a1519d7fd4ac08cde54d6b25e6fe1698c55661335 0c51be2c8e9c40f947c9527b1c633c78c3d56e0b +160a14259c471b4d0ef1594494f33126d5763d958ae31bb14e95818399f6e3ee 01e0d82509eadc367a67209ac1150e299447cb25 +1632e799dac6927cdf520ba3cba8987a1258382dedacf72dd5b3316af2fd619f ad2fd2eb9ab0a392524bc5d4a90888d550bcbc0d +165ec90aa4190bbe12ee415b294fd6d204c64afcf1ca64dd815782872b24ea26 904c0ac12b23548de524adae712241b423d765a3 +167fd44243abde82c97106ac315e0f026b447dcf1016918ee42c0777533e6c0e e7cd3468b748fba2eab5162991ff781e2ca8507b +169a47e7a7d688f8e6ee34ba57c07f3d1654f0886d7e0be582ec76cb445af1d3 d1aa69929be363f0798f1ff226078c9912415f5f +16e15f2497ec3aca6b94a96dd1ae3e2c8c5a351112345e16c1013ddb23fe9131 89d3150640c0ed5b386bc8113ac8999b20371ece +175515c1c0df4fd74f9f4683321b84cedcac3419563a268562f08560ad7684ad 3b8ab0b93539049b0f7c4a09f5dad48a1d89cd49 +1784913c0c472812c39dd12870f8fb69346327bda8383d7c2ef22d611c808513 ea9fa2b55f597931edb375c234b42833321e6ebf +179496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498 3259a6bd5b57fb9c1281bb7ed3167b50f224cb54 +17c8bb815beb9afed2efb9fad7e2fff9ad127e2bbf742e7688d70ced51875c11 4575d8b63290baafbdc8345a297945514542f2e8 +181a0d8c3fae35ec8f21046d043457cfb07975ef6ee20b3b2a2b75ef47e6b54f 017867366494259fa88e71955be05758b17f2568 +185248d640acd5ef59cd18b373ebeebe0c8f5d31e8d0da8a39b69509b82a8b38 3cc1e1683dfe9fa759c81ee61b2871cf95f0a3b8 +188d0f5e289ebe28d3eb0027212ea0b1af1c84ebb0229c452922d277ad4f6b41 086ab3e1892c5375fccb647a4b1e2d9ed55ab750 +18c2e619f1850cba3b69767e9433d37b9eba4616521994568a09e6620fdc32c5 46c7d0de2393b0064ff2b909a34944f30a80d8c3 +18ca8ae1ea1ab24474fefe881b7ad27805bd8942051fa9421c277b0ea0909361 069a41c3acd6dfeba2d5ebd1ce0e568d5ce3ad51 +18cc02db4956ff4c1ac8b49430ac9aa889482ebdf985e8f218415038466e870f 83fc82fc80ee42dc76405745ac74a80f7b7a9fd0 +18d28fe4d7da8143a66562a085a1082caf7abdc6ee0dc13c48a509f82ef9a6b2 72afab8fbae47da683ed24f4a3b5deb01c73ea9e +18de7aa78fb2acafc30a3bfb0b5620543d2d9c21b8474f0d02ea94ac16b3e414 c1f22c54fb7ae97543f8fe6c6ceff95734f4f9c3 +18de852cc5d52a03ac90027225dd12cad93079ca8e1a2dfb11214ee8dc06deea b447fb5a08337aa08134b854f8d65369f03c37c2 +18f2403ff16f42833737b7746497f5f79c014e1cb33bd639c47d7869cb07873b 480cc70e318d8a80657f36fb55967477050cf3ec +190a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e d71aab4f9b04b45ce09bcaa636a9be6231474759 +191782022d7767a9a15579163f106741358f2503fec2f9c4c093a6718c0c764e e5f6ef80021c1a1ce27300c4de4bc720a08ccfe1 +191cea84239bce688afb3e171e9e4b3aaae9d419515dd5629b3c01902210334b 2a7e671b12478a4246a11e8f3db61ac76f241e5e +19762806906133d4265885d96213490c2b04f17560b8d724f91770913531488e 422ef9df86d2b3647e8d9a0525318b1d33dd9f34 +19ac68c5ed3b0a7a8b9f5a4a7098d002d70e0578f8735105ff622805287dc356 8e8169ce53213d649d52304e3a8f98ea04c35801 +19c0e0233f0ce56698402b2e9bc0c77a02e5089c3e28d03f55c9a3bcd6c1df57 76a8c44727406a76d251102bdee607871887a119 +19cd37eadbdbee580b4e2d650374b1e2517600ef4f45666547f9030a1de25a40 a23bec23d8075a05cd04df2f52fb2fddc8ab4185 +19e5ca97ae44d0f46334d1bbed31bcecc2b753ab4de4413e03c3020a2ca0e68e 8157f9e57bd9de5ab95b89fb9c7192a5668322f4 +19fb1c78b11f0f8bda658d6fa6cc63c0b573c0f6760ee5a9c2df6ce2cde00c5c 09fe9364461cf60dd1c46b0e9545b1e47bb1a297 +1a002e34aa1589da6dec7e55014927ebbfb007d33aa6d93dc1aea53a9711c061 e9c9018f4b898a2be9aa0d053153cb7d8c8af22c +1a08ad814e0d9ee5f719a39c4efe32a55b5fa1bcfc7fde7cb527b30e2fdcf33a 1b30c5d6f15d06aaf5e4edc55d33d440890416fc +1a174438f39a3ef0bb9d11bc1532ba677dfd530d2c71429eff281102f8128793 3517bc697600fa61730369f35cd91da86e12d595 +1a372ad827add2ebf07d3508c8636f891bf9fa15d173b7299614d543fab4208f efa3c49e538c7d0724cbd9c32b24bbcde100b3cb +1a6a20523216002f961c5b6b0e2b9f93fd002adee3fb48c0b038c4219dd46b35 5e78a5bc12d1dcd94751e36dff18badcfd9dc89d +1aa24b7ebd910f39a676f2ccab8e9a79f14842c20b55ed18e3dc297bdbaca279 70957110ce446c4e250f865760fb3da513cdcc92 +1ab4fa663d22416f45cb1a007d767a58d0abf5255bf86f888393dae637b37c3b d0a44bd6ed0be21b725a96c0891bbc79bc1a540c +1afe3af49295268534bff09062da89d993de8bf539031ae5b5deed8c74e2ed76 b371c056bb696430f0dfcef0d5e2bf62944ff323 +1b01b7b52d7ef909782114be3168d4c11d9125138c36dbec219de41bcc729c1b 7f7103b840176225a27d40ff95c541ecaf74bb59 +1b0218569c4ddcbca6d3c5793c5e822ec57ae0a1132ba51edb9131b68b361946 a916c9b256fe6e1c75540972d2c6ec07acd46384 +1b1b914f2ebd1179c06130fee875bbdb44fba22bcd879e460c8cd295854120c8 a0571e5b921af9cc8aeaf14688ee22fe9146371f +1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 +1b8fc08818cc6f44cc340576ad2f114f2b15e4f8bc4d306e86e97f65e2043eaa bd1a70b317032b45e10b9d24515905c86baa071a +1bd6ab383a4cbd2611367ec21c4742326fbeead3ff912f194af0f853e7b63304 004393eb8ee7f51fc57f25ebfee55193d74b3b07 +1c24b0165ded9e7bd0c8848c667f0b08a8212496303aeddf741d5e0e4da3f81a ac04bdf6e0ee6bb4286343bc70d745146eb1e506 +1c34ba81c57a802a66d3026e14cce495dfd8263ba7a98233c4927a74e441aa3f 08d5d00056a7237bf6c60f85a6e72b7549cf9133 +1c4cfeabeb57c496034724e4649ab5acbff04788b6dbef87c50167e16e91b571 5a408036aa992bffa67a0000ddd163e45be5f457 +1c7bbd4852fde2f3dbb2990d329a20e76ddb3b2b570416e4c6ba5633972f67eb bbd29af5b49251be2a6498bd84b488bb4304ae96 +1cfb8ae71e9e576d1b16b7bd1a62156d0641c6e51f5d76877be6de4f26410623 b1bb1d888f0c5e19278536d49fa77db035fac7ae +1d2005951bd5058f1b001eeac119a95f15ae052239a17e43180fe02624d1ee54 a77eac7c388ce4cc9da95c0c66f0be2416eb1197 +1d3847d94a4153fa5b4171642f366908c7613ae239f8135f47a38baf47812292 2cf16038b9c815d6c2be936c90f76049483af23f +1d4b030078160a5498398766e70bf11699359798f01d47bed25d1883eed4c139 c0972c6411e01048c50c98361c50ab1969ef8bcd +1d7bd64ed18b5ce0ef15a15ca1294e92e403b58fdc621382e4dd8125b736a203 ac8abfa28ae1dcb1ff4bcc297a7269ad64e8f486 +1dacd6409a61346885f7998a7b2abc74bd00a588d100800217a3732b55d7e027 1763ca88b7bda3bb7e98f1dca8d49bb9dfa3373b +1dd44fb999c3043ff69a7d846378969eb6b425971eaa49d14628fdb85ce58345 c23841c833ab35d82b32553d0659e815859e6c01 +1de74dd7ee94c061dfcf5906456ad25ee317925d6f6545ed7e5c8deb4dc2ef1e 7c106af8cc957a256d2a9dccecbe6b974a49a9c9 +1df5ac46362cb6d1e92e3b62ab4833c8b9db62bf69faec2456b88eee8d1922e8 14599df1093c4dd512ad3d54892c03102f7aeaac +1e0acf5db1f4a90ada03fc3df61f0196d3ea09f3c592de0a83f2cc6d5fbb15a2 9bffded46b22351f3c12eb5c020ae005e57cbc90 +1e0ce38d00e8f3e613febc0f8b275e0fd7fbef8af293fd62698be46dfbbc937d 8ec696a4734f16479d091bc70574d23dd9fe7443 +1e82caea1fba091c1795671232b6de19e3c9b18015bb66a158ad4728bfc8e0b1 8b2e33413c0e43bda8a98c5ef090561605a366ab +1e8a2396a612e14b5a4a7f23ab5cfc15df6abbcc5ab6ff9c89a3788ab450f0dd 3cc606359d5ac5c869901d89d8f25cac348f4c98 +1e9c076a9357a539b2126c275c8774c961ab8bda2dd6cd37750ca969dac15d6b 2944c0cded94117886bb85e4e41f7d4a3a553245 +1eaafe5b4dff4769bcb95b0adc254d77016aa32c1acf1d765cf44fb85c5c8b4b e2ec61507666a29ef12e60eed443146f8431edad +1eb53bfa2e681bf9a76be9dd3a9c2a5deff28be13d29b2f633f74f9281eab61b ccbb507cc3bb0c961c35d08c7a1d18009ac3bf64 +1ebb7c1f5a6a2f0151c4bbec3788d0d065bae1f0bcd152c9a3ee35b41e1db2b6 efaa4277c709be26d6d475927569d592db39b4dc +1ed3f54cc02a9ca7528af4986af6870319d1f9ea1d2a2ac59c45cc75277c34f8 575cdc563801dcbef0ff667322c8d00176771516 +1ef82afd4f727427bf8d10690824f403713558c930ab46a90c2572e214bf7aff a9d70a87634c0932d8c03976c80467cc5de7522b +1f6ea2cab887a2ff4bb1557a36dd6bac9931ef1f36794ddd22b4b7b7276051fb bff8618112330763327cfa6ce6e914db84f51ddf +1f7bf17705d025c6ec01f2c3e48ac73674b061f4d33704527b12220d34fbb32f 81657de62397e72bf47229c7a0a27f0325ddb8ff +1f8268dfb6d72ca7bb47bef89869a745d98b950af1dd62d716b70edf33850f33 c5e5c7d94109f5b26ebb69ffceb81f0530fde633 +1f9efce84e985329c26351dbdc81516bcdf02d1e5d64a92fe097b62f9fda8228 8601554dda0b46a69fda76189c126fef0aaf1ba2 +1ffe1f5d30a25e6517f439e1a05b59bca7ca16fa26870557901218e2b3cdbd7a 6b0b66190924ec57928b827412a75ca7643ecc97 +200f1e71b8c5655069f997534192616f9a3344ea45caf96b889cace34b547572 2ed634540823fb7131f54c5f1179c8ff1c59dbbf +204b6601aea846e40b93e2859b47760350d0d29d2e100def2f89a4d9aeee4e00 58653f4e36c26ebc8e3622e8f3922773082cb328 +206acf23f3313f369c63f14e9d85276e95b0dbaa3cf271e9ab13f6b03b70979e 9cb1d49a7a94f9cb757f414ae46186f1bb36b839 +20ce0489de3716bd85f6b5787f155d53bfc81ba930bf4329ed57dbe4bd99a367 818d98c0665384ae6ef6f7bb0f8d4b0c34acf9b5 +20d7a7a6fda600c0c703114b07d8469a9d188dbdbf1fb3494ac72b2af899905c 09e1e12190d1de05ea714c98cedc81777eb40545 +211597b7b8ae016a314953731f661ce2fb4061ca2f867668fb6114f1b2693b3e 46b55d1d63bd191a9f364400bc1c9112349c5ee9 +21492c6dbc50134219f63a081f52e3aa674d90070674a43332eccd2f4fde17eb 97603e2d1760651b6749067ce3d980ccab9c715f +214e2d61bdf436636b18c56a7ff55107de6315c85e7d60ab793607b173413a17 ae692bf0e91255fb9ad8393edfb73c6065eb82e8 +21824283eef83846585f59963be963b547ec08faedd27f5a994ef76281d0d2d9 f5a98ad911c9ffe375ce43fe23c1a3e4ef47e999 +21ac834dcd64c8c3759d851ed4ebd3a9121338d2a77c5ee0853046cf13f7bd4a 19c6a202cea3421fb72f7e4c4eb90fa35c391554 +21c3050b5f1069492acf40767ef180b24b7949a9928d7bf52e4278b53b64467e 4430a3812186c60d7a3cc7048f97a4e05f3048be +21c709ef5ac66209b70aef877fd0c8bd77701fde29e7e6343eaae9cfea3ddc7f 7ce8cf840e9a4d86680c0c13788fb744636658f8 +21d1e21f58b5003ebc6c47526f1efc9834c4fd77df0b7c201bb0809c881d5ed9 b290b470aa40a42fc5b24a408ab9250443aed5da +21e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 7b4384978d2493e851f9cca7858815fac9b10980 +2252e803f7d4a9c197725e54f40d4d10272edaa05570832be4ff849ae4431fbc c6736542ff91f0d0c6b6a8ecf08eccd70f202cbb +22a3c8f57f9465c979a96ae07e60850d2b19bda93021c982fb9dcef491bd9aa2 c863948af21d4a58a4daaf359f38881263b3568f +22b6705b86e4aa9120eff203af24709d483698d9f78695e86e82de121784b570 dfae6ed8f6dd8acc3b40a31811ea316239223559 +22ba8f17bfeeb46d141280d030a87674e2be73e2360c6b60f0d925778a894cff 791826cd86946d8aed19ccdf7d7530da2023585d +22c61fc7b3feb0f7cb851cfc5fb6a3d84f11a63a4a32d9f06bb6ac0a332f4888 a4af2dd790a63f6312e6916dd0881bfee2cb3201 +22da3f3c129409fa9d299e05428fec54d0a4e49e69400d6808d9caf21d3e7253 880aa99fe67ce18436b9d253ca15565e22423f15 +22ea84a1b29b0fcc3889c026a418eaf785cd2f87702b023045e60c76021203b5 d74679498086d0fc2293dceb59155c696b11b86c +23380226b60b663254cf2334626a2918975dbe0029ded408c957de839706747e 6110c89446f2281e5db9b798a0fa020fad6e63e1 +233c055cc3c747f868b73686726307a307dcadbb562d3271007e4ad25520330e 1e00a3436acf0452f969a8394ceb4ffaaabae689 +2370e245ad47ad0b2c88ad121138fbe3b4931a7c9a5b2102f27eaffb5f09f892 05664605d827b3b2d88b6de331f0c39a4b9ac09e +23715dc3ee50fd82c9eca67b6bdd7a51c4c65d7508e32096bb42e755489b01fd b3e8a0abbf795114ca87ef5e714d0d2f36b84f04 +238a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a f1425cef211cc08caa31e7b545ffb232acb098c3 +23b010f4991fbff884638d5e894e14280a3f59b07749acc5c40a35d2f955fa92 40a7c27acc737f9efa912a66cbdd29d75bf44c74 +23b3583e5e642fb73f2f025263c7016caa285589bbd8ab905f7b87af6269c3b7 813b24636f71e0ebf9aaf9afbcad21518f4e1072 +23cb97c200205dc84109c248be5bd719c23bdab52b52c51ec92fef9a48790833 6fdccd49f442a7204399ca9b418f017322dbded8 +23cea2b49eed4993c5b92a9d5f0b82efe4fae3837ea707d921de645d04479015 b0fb7372f242233d1d35ce7d8e74d3990cbc5841 +2470dca53501f9587cae6103c471eecfc8089cadc68de24d72eaabb17da90f51 1ff4954e2b4c365a517362e7a9b285532a79e52c +24893e425252bc0e02b5f056663284f32bd3db39f8ee746c54835db081369516 1a039633309bdb88eb5e6c46d1f8c2ade51f09e6 +24aea63796fb2d407b9f1c63d19dcfe927f7f26af79966636c957bdf7dff58a1 ebae795e1fd8502328b176c3f63a4320c91de800 +24b7406da6799c945658578a2cac92132d7ecb27d03655c05ca8a6eb5971cf9f 3e0fb8fb56426d4645a50ce85fead9ad93ce59ba +2545649a60be2cd66432e449bb1e0ebe15a05bcc82b50292ed406464e4e66c92 1937579fc4f2bc5d813f3c8b77688b37829b394e +25579ffadad7d6abcb2a12feb00a49c2160a35b6e2806adf77445a20e5a90b1a 4c64f551d91b63e8b58d08d9d0674e1fd59b2afd +255a9457976612734498fea47aec5875822a07030bfb87462afb021e7caba0da 9db3e7f23c37f8764357828dfd6e9ea4a58365f5 +25648e64000f520ab94d539a63eb0fe4fe2a32306a70a9ae5b32444071f8df65 982cc52d0e7c0a9ece1606c37d6b8d00f682f471 +25a4efebb38c55b8f2309ce5e3116b2b9287239952cc2fa174074e05c6e5875a fe3a6a42c87ff1239370c741a265f3997add87c1 +26149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a b204707bbc546a1a770ef6ced37c7089cc3bfe6b +263939ccfe22fbd0bc6475d50b830395233a3417bca1fe7d1e74c35e9292f723 116caa6ac8ce8e28cfd7d2eb29670554cba3a71b +263d72fa5cd7aa029340ef5e0bc8c409aa33ff8d1f5a4054919799e16c4c6967 46d8b885bd65158e8cb53266ba4b627b5991bce8 +2643ed9ec1615eb7e3504de7e9365473464cc93211b953392894f4e262d9f55c ced645ea9ca4170bd75c17cb9c2c8f19f935491d +265b89b438f5161c93ce2d8346739c1f0462bc9688a4d549e93f4c0b03093ff7 02283e8165fe86c4974c7082aa1c255fc07ae3e1 +269f91868728e97e447d745490b0e7418e8d1bb6cb8ee5d8e766e1ae9a559b62 28c77740506e6099df65d8c7d40f9800c9574068 +26a2dac21f8f0939566570e48f7f4fcf89239c2746ef8d3dbf31d179c691808f af3b99d5be330dbbce0b9250c3a5fb05911908cc +2726a64752aeac8c0bf4921a99e722dde7cebe782428c471fbb8dad1b2784b63 9469a286a2240165c93b1940280a86802daa5f85 +27783ad60355fe63839b8ab4d1f4e74675d2da204757b708dff7038b02bce77f e71445c43ca0d573e2f61300e454ceee357624b4 +27a8b0bdddea1bad3b442452d2cd2178ad738d0a595153f52a61b0759ce6c2bc 1a4bb64d48619e2a1ef0106be89bba17768bff63 +27db440f99ec1ff5418253bd4f354807842fea54c31053e2064de6ed0a45638e 66108a6375b0a3572ea897ddcf4470534c6d4247 +28129011003ebf7100cd50ea6bb41f61d88db419df95564bf4252aa70771b7fd 1ce3f92bf1c7b10aa4c76d1fd51094641ef53cbc +281c36286eab5e534f1c2121a4cf2cd48a32b3773a3e78df500bed3f3c9747f3 fda23b974899e7e1f938619099280bfda13bdca9 +282258be802767efbdf8c3ae693049d0dd652b161ed25c75b85568bc32931ad4 e0ed676cdefdc14fc274668b67ebb2be310fc2d6 +28772383fe514695504d51d0dcbaebb5af526f76b3e262ccf54fb3b6001a7768 209849a44961d4fb86fc51710affec6d8c407cf7 +28aec9a4450d9de98db98a021f97026bf12f1328f66e53319669eb5adce5ed3e 4f6eadeb08b9d0d1e8b1b3eac8a34940adf29a2d +28d4103b9d69469f94bcc9b762b5ff31b05b404eea961bcebc14e320209d9563 540240e7bb85dff664406a35eaeb222890228c0b +28fc5830291e38452b4b43c54c5d5a7613ccb39c4866de51b890b55cc35207cf 98fffeb8462945a9c9f19d6e8bd40470afea58e0 +290d84a2cf108c074d3764ca8dc56e1215a5a7482837f098dff7a55e23d89d5f 4f9339df943c53117a5fc8e86e2f38716ff3a668 +297b1f32141df100021efd1a47d8315833d19d47097fccb14c80c9217d8840d9 ca481fc4f12cec56b71bd50ae72e3f088b69021f +29aee3837e46aab84222310330ff8fd255096d4a22be608ddf847df5053debea 3f61a75703b0430062d1908fc0e3e682cb8b95e7 +29af12bc8b8dfc24018ada712ee5727e1185bf8db62cf5a0b693ec59995ced37 a2e130b3f5a8978bbcb28cf5743d7672c5ea10a6 +29c3d5b0abae694f9694c2f7f63eb20b80de152886642e5d0f5282b4792cf84c 3e89665eb6d5ab75051dc59fc8b63316908b19d1 +29c86d618bdd9b53a1df6ec30dd246a221bc2670aef3319b6d688a1965a78e65 4141dd83e5a7b99c7f2fb295709e2ec5a66aecc8 +2a23921ccd2ac045bb0bea1a077d8834d23f2c5292284511cc2ab2380eb5925d 0395b97d1e2df4bc7cf46a9a82b753a8908157b0 +2a497c0a14d1f5cb4d497b659b5ca43d2abd353a35ee86aa5ebd22b79e5b48e8 abf5681c033e83f92198fdce8d30c799a6e42af4 +2a53e939a3578ca6e258a421658d15fc90a4bfd310f8c6b1dce41a26bb09e2bb 4730b7224276579fcc8fc7fdb9bf796ef158fde4 +2a6348df1ac5a449ad2de13641b2993f5707c5aa37ec0108bc02caca9d93c4c6 dd1a5b5b3b7a1e3abd956a0808675001ee06347e +2a6e55b7502ae9e503aa71712eb38a7db4a400f3476d3c8c91b0de39bf4a050b 279d8e37642a9aa06a4aff44694ab7a6eb68a60a +2a744551456986934ef43201eee6334a89b1ae4e4c919ee2f8f09f893a6a1c51 2cb51104bbd83bed39bc8a178ea172b234a3d55e +2a7492a020b054d3af4f1c6ad11021a153198359f50989c9cb2d3a58d347fa8f 1a57ba73220bc222e2d290820bd9f4157c8a504e +2a7adfdbe35f2b25328c964f8e40855b92eae773accd60a2e3aa1717b6b70341 a6a1a6fa11f7d0c989afae4695d4661514cda8c8 +2aa41eec6c80ed40eeb14896e1716f9439c6a5e99725168bef1f876d0e48ebd3 12b66564dafa01d8a13b8ecf53474b1d305ea771 +2abdec884f1cda652bc5132328df25b97083f3c0d4527dbae2aacdd789ee134c f31fd16adbc7a2165c0091c22e89e50811f95df9 +2af226bda5c9ca85442284f6c50145bc14e632bf62258127f56c7ae77d295771 40721f6b1297f2a48aeb5c9a3ac095767b1153bf +2b3b4a4a848d85bc2993d0a7e3bb36e354b5053e7cb6165dac164bee2dd9712b fa19c3ef71b1ec077434b0e08bf0757ddbaa22ae +2b59f9fbfad86c6d61e684b441eddac921c532c40dd645884be08058db3d7fcb 8cb3e7d4c2c5ae36a7ec398299007af302bab6a3 +2b72c9f2e8b8a2b0e041410a14d94e9374b1d8fbbaeba9dd02dad3995d5700d8 7c982100f3c7b81e576b552484d58470631f2cbe +2b86b328af6dac7919c87c258ed08cc08fddfc033eb0c8c197c45165ef435219 b7058c66c000c55f92fc4ffd7274a16b41b196f8 +2bc0fd59c1b97b08b6018f1243a34a00e0aa32742ae2319c07ed5bdb3d9b4269 e5aedc0644f6be56bba890d932e552cf9e08e717 +2bcdebb6b95252485caa69957ca19c85b9c4f99ee36e4292a74c8505fb8158f1 158e2b76aef44441ab2bfd42ee69873822a4d67b +2bd621e4518cfff39970c47c32f704838519c36d67380758666e19699ed387ea d54b77f23f9cf4f0016dca8ceadeed48b1ad5686 +2c045ed1bfe1436578328ce00e3d7b6cf2e80742ff260b5c2b524f2dccdf97c7 86897ebdca9adf45f7789399e9c4786d819e09f9 +2c0f52f9ae1f34f280dfc1c755cbfaf2b9968fd3bab1f1eb16d3ac0fafd71940 8fac94df3035405c2e60b3799153ce7c428af6b9 +2c2688f8f004501fa4917b31de04513365c6ccb796a9319862e0f6d8ced5dbfc 0e46b1d04f022804f08de3466b5cb6b91fd366c0 +2c97e388a515dd8871f47fee997fda7ddb7626f68fabe2e2dfbed0d9e5410f04 13ce9f1b96279187249d56cc641252ff617fa35a +2c9b933ff2dabc5a043061d2aab80d967e41b01840b9790a87c981afde9bc0a6 a256495f5eb22ac2e5de21f37b22ae3c497b3580 +2cc2ff33694c392abbde2169365b575e662405af09624502d3e151d205d50c25 e6823108533f65740e0e5e75d0fc335e97439b75 +2cdcfa2499b832dbf7b855e5a7d210d538be9674dd2dc3ac301a21783eb7b333 c7f0591ff41fa0633796122d09212d3fc325b4d6 +2d22bd62fa3abdac2de06753deca74c77bd2f4a00df6014e0bba6507c076d6ba 3dd02e2850f34d7bcbfa3ccd4e909867dc89598b +2d851572773ae43b2bb09543fea4f36091522c46c0a9c494bded5cb3d0f302e1 f82a8eb4cb20e88d1030fd10d89286215a715396 +2db6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2 dbc0be625bed24b5d8f5d9a927484f2065d321af +2dc5fcaaf371da4d552fdcbfb79da7a4cec2792c823c9bd09a90defaf73b437e 698d940f85a59f00286c49af24b390be7cd8466d +2dd4d7ceabcce385003eaf8bdf8c53fbc262cc3dbc2041eabc72a7fa4b6ddf1f 0b8979fc62c435ff5b984d0c13ee42d7ac2e03dd +2def5408c21e4c0611f22321783bf0d4c21f8a9006a3b6522cf295f493ec34f2 5de89cbd8c0a0c8bf4fc43938f09e58936b8c2c7 +2df3172408416389faa2bb43248e82a2220d09c9f4b56b1d2093286d9a53843e fb20a5a4b6185d9188d82c874db3d9729ef31f3b +2dff3f4b2fe826157fc7c3fc1b4c9736259160f718bfb7fdb4892bed18378c60 856017fbf64464e3797689463e95d75496c0d554 +2e2aa456dbbb8889923eb6713672427854020298a764967c50235a9a76d7ce4b cf5f7235b9c9689b133f6ea12015720b411329bd +2e51a5916d344edef37d84ade43e3b93524ed39456a7ea3054bbdbe06e2534b6 d4191884cffeb8b2481197c4c4b7dae933493174 +2e5fde11fa87cedb853bea73f5ebcc9ed34830584c5a7ef74f083916cdd4e900 e896d00181e2053cf3cf6441732a13f6ca83f104 +2e900cde6077a860354bc38e8ccade669cadf2cdfa60396a3071215c5d32cbe4 33a790ddf892b1b7922a6bf78e4362965c7fb51c +2ea5535daa4731ac4a94a7551a891f08e3c6f29cc2a864be5b164652f2f53630 11f76e30c38265404b042b0a4347e86b66132713 +2ecc823b86990906263a4369e86108176e5971872da7307b73c46f4ac7ed8a38 9890c3f69ebe264206de411473f6220fe2f9627c +2f18dee4a944014c0e20e1a96046373cd58710db5cc74b584ed7d96a324f998f 4188d28f1c38240392d896fc79561cc461fb12c0 +2f403b3668fc75cd5a4b4dc572dcba3769ddc02d13bdaa3e1c0a144e976aff0a 6586dbd5849fb1369f4719df685763ed9ba48d5b +2f468a1542ddd517af3dd118fead80293c5c77790af57e351189155e96dedc24 7a2b79d3e2f273750e3855d7a0b15a4c6acaccd4 +2faffb9919a0b6717066117ee9f156a40371e8ba0eb585170188a65ec89a8082 ab5685026cc2b70c18e2c43689a57944eacf59ed +30312e4b199c64d45ad2bc917e2ac5364ca8f8901adf7ebd1492b7adc21ba915 e412d5a411cf4e9cad80ff3fb315c42a7e718ea1 +304352ddad641770fcc94ee4d9f957cde7aaf4c107dbf8b5ba14d543640bf7dd d0e7959d4b95ffec6198df6f5a7ae259b23a5f50 +307109fcf8b70083d2fb902f2316b8a0f790c3d9f73f7870f4eb774b00bbf44d d8603ed901d4af4d0d2b493d1164c74eae34f147 +31138d44b7d0bdc213b18a0845bd56025bd976cdbc1fc323901e9c50238b30e6 81723c22eae0570f3774ac588b572636f34a7467 +312f60f6c352b8c7367e976ec359c92196d0b17d491066d47279d4e1e2f44cad efa59b726a6d367badc5c39d83e86a1664616da2 +31ae8919eaf87822e8d11a589c35fee500b0d9ca51d0548c2bdb99d4fb299310 7c6be4f3fc4d952d4eab59ba2e553c6b77725bfa +31b05c73b9305a342be592d83d59408dcd54a5adc94f0da7fb6dba61acc2929f e719ec29cf9da6022610b46b463b80d393d22778 +31cbc27193bede4e2ff534542aa46c1a8ed7c4d2f5e9c9e462bb99d73e5f7691 c27b446edd6db301478ee29880aaabfe2bb66539 +31d5e6deb4c8fb548582ca4d37fd37f2605231277340f5b402fd64904c542ebf 5e8220f46ca2e7002bba745d8c0708622c64150f +31e01e45015260b88c5131254e3a3f72df292442143d2ae03224d61aa5177c24 8ea17a2c5a5bbf0f5aa72f73e6f980136d72e6b5 +31fccc3f5f0b0e514673895b4aba0dc2a54b7bf1441abe8c41b348ac997c5566 79038fb3d450dd7b37328281a717f275cb7eb7e0 +32ec9339b53b2bdc73e027642a1c990d3ada79a168de1cbcea2afd29fedc9709 027a51a064c6a3d91392f31f32b041bb1920ec60 +3302d4fbb5614b19c697d3f3bcae7ff785944cebcb095ceb418ae601dacfd986 75e6d95a87b9c6a5683a6f4b56fbfc9ae6e53a7e +33139bd70082e3948104945187410076178d57d31dd2b50542b9e2b321e85776 e65ddb38019b6e2c268f8ab9c28eb19bb162b032 +3315093132a8f28bd202c0a9562d04eebea4943dcd1e1c754341b0389722042f cfe3a027ab12506d4144ee8a35669ae8fc4b7ab1 +3317ced38dfcc6285ecd4187b6fda9e9027cd26fe11a4a81e537e770a892745b 39c222290d1392e028478037e250bfd215635cb1 +332d62e05adc040df538a64bb4cdaa02cf5bcc83fc8a0c3a19081be7956fe1dc aa0c0f471d53ead366e80077a9fc82649edbb4bc +335633f31770ec3147c2fa9305b5eb1fe95372bb00fc07b8f5a28d0aa40e9769 bb61d8117a8cae026fe4061e15c29a96aea3496e +3366326706d1691a8e07d043d23df3a4a96945bf9377cbe95d4ca9e695ae34bc be876eda8beabd473360e2dbd1a2ae5e80f6455e +3381a5bc62348132331d43b4c62f99c69e9d927072eb35fab9f10b9911d805de 15595ebcc29767ee05eb33adb216fc2e92af85f4 +338aa6b9127d19d369e1973f72e10306d02fae93bbefe85e40160dc40417db80 7aa92d44ad52b314e335fa1aa45096ebf284cc30 +33a9bfeba76f77472d80e57af9f6362744595f3dec1fb9e011446ccc43f0db0b 5d7f65df66f2bc9f3581ecb1a7932b31b8c98aa1 +33cba02acdd63428702757d61751c0dc47d51b011ba0f006774a2347763c5dd1 089c2d931b6b68dd82527430da51881d23bbd9a6 +33e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 1385f264afb75a56a5bec74243be9b367ba4ca08 +3447c9eb6bbfce8c4048e647e17d5c170564bb373efbafa65e3f2c64f56bd7f1 5dddf7c855f9c8e43954a99a6f85c098dfc7ed41 +345d2fbf1d306c7ea46a05f497793308357e9e17ab0e866446d2b9894378ddce d0d7e736e536a41bcb885005f8bf258c61cad682 +34e4e17c4c3b2175b0c8eff812e9c3c6bc689829b435c0f529cd73a69cdd7f2b 20c4a63e8cd8a70bc0f894ada6a54afbdefffadf +34f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b 849a5e34a26815e821f865b8479f5815a47af0fe +35511d2821ee472ed03f36d2e481f6d1d47a85d03ef56996b67f43b401e2732b 7a750b897cf8d5321db7b7f8a06e7d098b13a74b +3576c3885a36e2e498004c5eb560a8b98ddb87778d2680aa24bf12c2a34a8870 e8d0c5322cff755bc73d89db95334482f920ccdd +35f9aa36440bb182dd897a1a2c212fc1dc45e17ffd5cac859c87ec4ded0a36da 698b078c75c911b8361318db385d70cc84543a13 +3609a41c0506fe19d01fb8b4729923362675f191fe5f63fab3111ef804c48fdf ed1ea164cdbe3c4b200fb4fa19861ea90eaee222 +3612a2a9b865800bd6450c6cf21330676dadf843a578eb12d2e6cc6092ba3eb5 088a731f00a39fb3158dc9150b7c8d176df51867 +3612a2cc30e97df2d2bc86ec0aa57d7f332642cdc370d2b0df18eaa6a53bf220 0474870ab94adafa0bfca2fe128d6ea7fa2f3c99 +36693b61b675ecfe1babc324bbcef2e01bee0275b422152cd8c48593555d27ec a57e9a8cc15b61ae1ab908b362fd829b3e36e513 +3669ab580ce27ccc1dea9e89c70fdc98d0853d961dbac4dd0d56130f1784ac12 0bfeb8244a6a16625a0b6df594118e041f16a185 +36809d2768524a8d74cb2513e3f0e4d07e765b54230f7dcdde40298fe58a90e5 e3059f59ca36951d3817675131aad82c626c6d97 +3682fbc67dccfd36dc615ba17e3ade5fb7f1dcd6ebb425104317593be72e529f da5f70ee04085d85be124739bacefe69e829c0e1 +3699821b431131e6a684f5faa9e940a1097dd276bc5fd41e6a9f9424b442fbf2 098db3d369e28b5e5355db956df3fb45241f4feb +36d63bb74fd24718c0d6113c70db5bc718357bf0a9b336dd23e0a08a51fdfe6a 4275617b02912a72f62ca7d2a1b8f22602af4de5 +36eac24505d4c4405864ccf2f30d79af178374166daeceefbf11e2f058d30d60 270b8ea76056d5cad83af921837702d3e3c2924d +36eba302d2fb99d0b1c79bcec1198f1cbba6cc8c804a19d3bf2bd3883e5d9fbd 4e0d6d864d3632a820d8bf3dd89ff333b9bbd34d +37113624744ef096dff48983ffb612b765818092c6b30fc0756f22050e9c9cdd 03c3f39467c727707a5522e6ecdb4fd2ef09f8b1 +3740df04c191176e7dcfb8ffc9eaa32a49474fad69a959fd03a1234ea1e5dae0 5b209c9654c83719b07d9fc1d2287ca361663fe1 +375f69d3d41e36d6904bfa86221690ec49de2a030664a362abaf86c426f9f7e0 df48000ac4f48570054e3a71a81916357997b680 +37930b649e0a232d9d5068e3eca85924117dd83095ee0855e2e9524fe227ab30 864af6d01aff80a76f16241ceb82c74d9df49e34 +37a3d0dc04bf341eba75c9a147cda7995d68a8f9ff1c7a43871b9c7a7e3a9ff8 e24efbdd0d24660b10a736020ecbb98453bcd99c +37c7c3d1205434567f6127eb53c246e5760f03742e2157813c5809152d461491 f15b75039d7d4db8e989b85fc085e6f1412ea318 +37efd2fcbb9d39295e0c1521d7a4c53b9be48cc20028ebb1718aaff90149ab2a 7e78a9c8d5aba74a0c85d77ef590a9a9a6426bbb +37fd90d6abe96a2b664d80443bc426a3d47ab5d60c68e06b29bb32b82c9c702c c0d11745152c0a3bd8bd0e94a8996ac952604ab4 +38245b6deef0fde5656f71d86a9d89a86886d42298e46ed740756281cc07ca18 4638c830017efa737b74da9e55236bd515ac569d +3829e422229d9c5b4bc01df9388483e29b4e52935c4cf4c531904edf80f012b7 375649f879c2d828c5a10ab61992e5d84c016dbe +384d76a2f80b78ad569d3619586c957e6dff985e2fadfcc99f2f58e4f1690be1 9e2cd50ab5fa56644adcaa13bef9e3dfc7bf8cee +389af1fc5e97b1e7d9cb01b1e387b08862e26d67c3650788f51b8714c9f5fd6e 60f4720d8ef4cbbd09f7f9ad8e41bd77b4814cb8 +38ba5ee9660e8ec8b261e2edb1441987798bdaa4f19d478241a76df16b88d0e5 dea66ba25c0b724e0dbfdf494d50f7f3f2ca780b +39bf1ac28cc3f8432ba7cfeeca6bfffd9a0fe641784db85de2eb0f57b7553869 b6361fc6a97178d8fc8639fdeed71c775ab52593 +3a0a4805f179ea4e115d542497fc755a2c1ea5792853609a25b26b299553df6d 684a1d3d44cbbc86381f61d4d9305012799d9f5d +3a0cd33a47fcd0c7b8f8c1d407aaa53f648e25f2ffe2533a7e9c09c3d1b9da75 2f9b6b6e3d9250ba09360734aa47973a993b59d1 +3a103fcd415b1565358ec83f4341844d2b4cb68e1db409778a7604cc47ee8663 92ff6464177928722632f05a3083dfd9c5dcf47a +3a1b33e58ce33cb9658a2ed1cb6215a4120f4d8d066cb7f4c5979db9070fa59a 1fcdf832d88aaf0f7abe480f5636d0cea10aa109 +3a32ac3a0452f13635a41fbef02879bffb3733491b4b791fc1766b6f208acb59 3a612bc5fb913bcd8dd90fc3042f23882d626a91 +3a3735b8a28278539697e5f6af5c45670d526fb7538ff1594e99df6331c382b4 495eefc6e0986083b95f87974942afebde688173 +3a70044491dcb883ea825b2e299c512f1877761862d5568028f7ac185cf38604 17e194d81de579d956587ed7411eeb77f88df277 +3aae8af9b31d378f55fe8390becba6e9d7a4cc07c2df45fad2289083bcc090cc 12dcefefdbc9972cf851906ca5c8e8fea46d1fcc +3ab0a48f387c62d22abef3b52bab7bc7e41a3bce649e573912d0de4c79b68af4 0aaf8708b0f67ed9b99f1c1e2fd28492a671123d +3b1b991ae70d1f5388ad16b63d2285e99ada7a618c6f5d01e50a6d4b33c4767d 2f06098183b0d7be350acbe39cdbaccff2df0c4a +3b58565ee067f13349cd4f89aa396d10f71c69e168d5c48ea23de59734ec3ab1 f0a2a10243ca64f935dbe3dccb89ec8bf16bdace +3b76cee4a13d688c4c7f83c2394d6a0b3908fb4289d917c439431d85e53e08d6 b3e014dd4810983396c9ab09edc7be1e0ed1ff51 +3b80aefa5801cc70308cfbb6300e7e04f9eeaae97703e0ced39d05f6ccb3a552 a74605e0562a64dd538a56a3f29ea85322f4220b +3bb429f2e42b885e937592255fa2da4014dcdc63fadfcf62413f0660f438a85f 79ca2edcd4e7a801400fa33c2c705b8f03a5d7f5 +3bba055be795e5d49a89496f0219ea2f43cd9eb6cb084669eae97dc69bfeb459 a7f70745f85f445596125cae6e357e84b84cb925 +3bcdf0dbf3cf72b38fd65d0dbd8d6c6c4d9e3ddbf3916f03e1e6e4de72785c77 20d191c80cedbcc63ed52096b8b0e180c6bc16b1 +3bf9e14c7f197bfefdff9d52063713de4bff0b857f53b799db0f4050bd62d3c5 52f3dfe9b5593c9dd45980c36e2499f2e9de701d +3c75c9f9f003694aaaff66b2efb4932dfb0216bb85d6ba5500d6f4a05e92b0b5 eeae5bdbc9a3075ff9ee6a08af804b5df11154ec +3c96c1697a81679472afac3dd2723abe86f34f50cb2804eece8b0c942b3a6d9c d4d88ab7e48adcc92099c41b7a5e85d580f9d77f +3cb25d62ced555f8409804796b32e06540c096ef858ad45c864f31503dbd4982 e3409fb59571d2eabd07d4d7c09a0fcf07b6cc5e +3ccee1f77ce4b4db6e6ec7162538efcaf479f3ddd950ada7ac800abe7e9661d3 fe3c74b75812f161cf1838602ad7567143bcb593 +3ce006ae57f23548f85a5527e5cfd9ab29ae42aa1ba90057a807346404f8174f 94f7fb59f74edb08d375562d1814645dbe7ffcf2 +3d14ff2d57c0186871a99a497966bbdcd17e61c41739cbeb860efb0f7be31c18 dc5b2ce5d1c4c9c38b94af511ba60946b8761681 +3d45b4ff158011774b796fe55f7041f932df6dfdaaac2dee934a0b6f2a2e5d4f 8b049129be2405e428a3bae21ea9b99387a96afc +3d47ad957bcd83e6603d706016af1b6158878b32c902fbc86d28918d640feb0c d490452617988fed2ef9cc3ff667396532426903 +3d8632d47f3f70b96b9391e692c79b6237506bf999a012e50c1c3b15998748c5 7fb15ecd2e65e379b6f3af1f25be0024c0be27ad +3d8a6f490eba51281c43d4b67e0336a2947896a4bfaac3c0449a7dfc84411128 1e4ac7e37cb4d50c35b7a52202c646a81e5c3dac +3dbaa90e18232adedec438665cb022c35cb97c999ede79097d3ba17b1810df0b ce4344fa507b0c6330a3559850c9cd186d1b052d +3dbc65bd993e6e700790c5c51ff0e0027b55f55699293e73fcda7c5e53a8a3a8 1b6dfa9817a8f324f234f0f31cdc79c016b40264 +3de437349b8b47ba5cd83d5f70956594de271cda393792922fb2416e3f327f5a b7b0d0ed2d91c83b59914db77c3dbc34e36e2c0a +3e00d8cae2726dc33879adf876b30c306f50e7a85b15c8add4a27f84d88616d7 1088490171d9b984d68b8b9be9ca003f4eafff59 +3e24bd404d914a9195b89466b1801a0bce6e55fec958ca7ca9a778a08fae6fa2 010f258ae4bf72c23d01e8902d7f5dc8e4533ab1 +3e29ebc6f1b56a649256d7c35fc227c530f1477638fcc68981b3626e49746928 ac7d251b493d022de34162880d30392aaf88cd3c +3e5411e6190b46bd834ffe79f2bff3fe5a9f99ff2d4b5520bfe84cb22e295b38 6624a3e24af09a12acd9b5b98c3109fca580410e +3e9c52a7e147ac9dc1a9e23ed7193642e108276f5e74397201c630bc1dcf20d8 c8df0297553b2e8c273ced67b3bd3d1603c9d4c7 +3eab74a6894d790767f3d92615a6887dcccaffa9e48fdd2ce482b5f17efcf9b9 111d5ccf0bb010c4e8d7af3eedfa12ef4c5e265b +3ebff01bceb09f6f4e2a24596d6e281587981dde381be64240283c0d161e753b 64c993e1ed74492bfe41ddfaa8e5f7999b5b3952 +3ec0a36347125293ae217e7d4049dbda22951ff9cd0d5207118f6e963a7428d5 60ea5e87d97ab72853fe358b4de61a68ca7a3908 +3eee10d9f6089f9d03d64c65b9aea882217f833ed9538e7dc49c2ff73ec9e41d 0ccf3c2464ea829a6320156b5ee131337898f2cd +3ef82d40c8f2f25a1f20d1e459d083eba406bad227af5881c6fe97aec74ca7ab a0b61b552c08fb538365870b3ff49d936a46c21a +3f0764d48b89e7d99138b291b09ec88b9132fab0a60de5b6505ff9d9b383e843 8e9e423eddd73420ca4b824f54211208f33628c6 +3f429bf36329d28d40103a6b1813f92b8322bdffb7bd5943cea40569196e6838 eccaf6f8e31a3c87b89243153770455805ed4c1d +3f5dca26a2f512d6681ce1957b8afba5e031bf63d52fd52d8f57093bf92391a4 7033f9ee0e52b08cb5679cd49b7b7999eaf9eaf8 +3f7fdefa5ab3f52b546c3cf59cd50d61c29efbe4a2c40b1d5ab8e50ef34fa398 3aed57726754bd5c09bcd425d9b99add5db50a78 +3f838c3c9c70ea829eb28dcb763341b8c730131251979eb1f60ed6819f6a500f ff02f4077fc8420d328c8b91c1f5b2e71a7c19c8 +3f881332ff93d2176b18dcd38c990146ff160b3eb83a262bc77462edbc246a32 66ce77617726f19d295e56cf7de330c848325646 +3f933cd9565bcc7b8be630d5626c9dede75c7aeca14473b2bc2b88d26417c7f8 29f0e90ff41bd7079d3e620e15370e3a8385884f +3fb315ae00172e2f6cffb933843a407277ea426015fa6224865a7c63ecc37d8f 2dbdb824f0cfbf5f583e6b94aec753f276ed3368 +3fdd086cf7ac8ab728ff01b763ad65b9d1b7122580d88aee652b8ced232647fc 175396917d98ba13e5bc959bc3dc237cdd492036 +3fe6ac134c9e29c44bef2dbd0b11718e203347e4b867cecdedc20e3466bd9896 69dca959508cd04b226b3747c0c07f89f3bd1a87 +3feda025daa440402e3c020cd88a0e9378dd2442e55ed409c466376d5b424730 ea790f337b0e401f5e4acabf9af9c2bc756d5f3b +40154096dfe3b0b1abf64230345d327bfb530fdc2eea093d407bc245a94cd71f d15e94c0d4eb0fe06dd1e25d7e9e3c4940417f56 +402b3758191ee821d652dc759baf30a8949a66e3b7d073e7e79d91c053a7207e caebc63b34886497fa2b6ed9814a327d3c91e545 +404b795df44dc8a0830397f7598ed7383ffc5c227c2c8a2223bcf9cac3e6a4cf cb98fecad0c4379496f32a3f7ce3aea2ca553b3a +4066249c68b0d3c8b39ebe02c9188935900465acad02a49269710f56720fa58e d0fdf2dcff2f548952eec536ccc6d266550041bc +4071ba3d3cd2494d4951a322550a934507c44cc7278c90d5f7458c512407fc8c e58c5633474c35eb16f9dec209d57aaf881b97cc +40799f33b8cd9ca41f36a2f89d8ac8550537ad01dfb21fbc76f01eeb62f512d4 bfaf8c42eb8842abe206179fee864cfba87e3ca9 +40c6785bdf24b49d3b75f3f1f918dcdff55b2a001b66afd0a411b3e8439c9066 979709bdbc7b1c91505138e1aeab9f8b714480ed +410cbca5119f2d6f16c3e42b52d56148d5f72d45679c92ffa3b6d9a2edb756ce 80176fc970e2964677967ca1a2a3689364513593 +4187fa8d107c1c86d8615ef4712f1f7c64829749a52a1e68c715ad9a531a6e2e 33f0f5ce19ce81d7d32752b140dd9f5871d72eca +41aa03e11a4af5047dceb683d7a03cdeeb904c85f8a58464c99709defc5c0301 c45579ca160b99e14290db789f3c690c406b6dc0 +41b613b0200cd70a7f5f0dd85a98cff80cf770d2b69e3a270fda0ed2ea9e3577 4a9c7f5656c7c53f5172e16174b96ab44606de35 +41c166c241a4e878f444932a193501e12ab38ba0634747291df70e619dccee1c 3188ffdbb3a3d52e0f78f30c484533899224436e +41ddb608f05a0551ca421a4c6031f9f0b0d1c165d8268462155614a741db2f50 ec66baf57304fbc94d8b1bf55a6264b6a34c5972 +41f0c76d526a391e6642bf1724a2f32c926d1d10262aad6183d7cacade456c87 cc7301d8a23a16789de6d298a6b1d2ad7c686184 +42582993ace8a3488078298b40b08e2e48bf576ab05b1bd36bb4884a9ce853d4 6add857acd3537b186d2c8d2c0e3027c364872c9 +426317ab5167d24de98d3d49614d713fa8e5ba435e1d9600c3b95d2fe9f2d785 f3489977f6f6d5839be9f17118bb723fbac74a89 +4277a22c721ffe0b89f6fad5e7779f6fe80a708bc471ab49bba329d41d5843ad b1bdce4f69d6893727df16ed00fed96d701fac06 +432fa95d99227269dcc7298e892e53873ae8b6a283041bab4febeb298fd3ae10 eda57c53e981161ad7cb25103fbccb31661181e5 +43630957599e6247ff790ed2a0021c6dd072687ea8952d64b767251f79135d42 53d25eef40016eb27787814df58e0b56e76d9326 +43723656763f3659a5e094a87df27c5b43d270840870dbe1734142e2a9466d7b 1711eef6e71be6d243016b5e138766b3777e08ef +43989c902407cb7bc3753f4338f4847b21f53f49c4e9f750cb866507b64ab669 b438016ecda7648426036e66c4ed17fc93e71cfa +43e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +43f15e413a5b988bfff18d10d4b3bd546b13084560e0a723f4500fc094e13558 783257d3b6a3279dc62d194bea3b7f301a899351 +43fc14cd889029d5ad6e285b60f162e73c9576d49068607e879a2a0e5127fa5a 44b6a517f15303e5ca4cadb090c2ae47cbdf0c68 +441eb4d681a9c013e67255dd2fa2fd722863bccb7fa9d9a9230b7aaf5f107ae0 47b0273228dd08a6ce617f8f3ab2092a2d4dca57 +443f41124cbcc448cff03bf4ed757ad88a5f5b711f761764bf37c64f7e900869 608d33fa676aab1ed350312cc07e02cd7f94847d +4481293fc62420e43723b5402eb6ad4042f5f2951f9c9413c4ac3235edd4553a 08167bdf6e553be1acfce09de11aa69b1c3a807e +44be0bb4fa20d53c8b321492e98794e9054b66ea200d626b9d9f8c990344b32a a41bae0b8aab4991d9d4bf9cddbaac3c6f38ef9e +44c505e4121cbe40d889b39082f5bb700ccae8e93019335df7242e6c84f8e2dd 13b8725687aa88dbac7a9a34865984d6fa68dbbf +44db3dd46bf12039f93ff4752baebc137f2f638089fb0e1a217c99528c3602d4 9fd8412e652899d03576190cbafe0ae194357827 +4516b0e63349c81abc6584cb11ce84ab8ba38b105f9de39d0d0a1455dba2478d dff79e27d3d2cdc09790ded80fe2ea8ff5d61034 +45b27cc016ef3dfa31290c1e564c82266dfd4e626d65c10e4b84717f59acaef6 76f293894f4ca1846f52513b420dd68bcb8da127 +460ad78c6b885a65e795a6ac9a4c85d5a6083b49f2a168cf14701b1aac7ab51e ad9f7df6bcf1b6e69be44c85641380848eea36b3 +461f1d5cecafb15ce791d599ac2114b5cd320cefeaebb90732970655e6f85050 aee8b26e6322bee55403a61634d7e01b77aea4c1 +46333d32b3801cf11d9f80b557245c9e32b0e05deca61dae968914fde159f0e5 0cfd861bd547b6520d1fc2e190e8359e0a9c9b90 +46698c7215f6b45759d9cc387e8d3121ee1ebaac4330cc1e1f38358b5d67b973 9bd5c5f028c505f9777c63ea85c5399bcb9cbfb8 +466cdfec4c74f3ed4fe53165e468a52df5dff9c6533ec433cdf235a73e099d32 5052bf355d9f8c52446561a39733a8767bf31e37 +4686fa9fcfbd200774f6b6e55dd719a3eea3da7933217c7da0efd14fa9fc3242 ed2292c12cde99459c5859362b45712deb1d5136 +46cf6ef7c306ad8d79ae690f643ffbda6d1cc3481e46f4202a166c6dd141cfb3 d3b23e08fe7235ea387b45453424409fb54ab7f4 +46fe85670f28bcacc7894ef9227f4816ff018994a67425ca92ca71f58f57c5a1 f5607b58a0364fa99eb1bab8e1c06fd948cc701b +471b08f85206aaa22b74d87e57de5caf02fca30587512ffc81bbf32d63e9915d 58a948216a0b01a66b21ab7a2116adfd536cf922 +471fa4fc2da467dd94e57babb1912bbdb5e40b96c8129d46fc709c0bfc009bca b0a8568a7614806378a54db5706ee3b06ae58693 +473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 +477a19a87cb58e25978dc3302ae5f089d4e4997257fa09e259f2f03ffc5572e4 c5bf0cd945f7bb251ab2d30ecec98e982bbeb49c +47f06c894892f592fde64369a0850fd5d7b78d0e3ef825f66a064acf6781fa9e b923f2f97dc26e981c18da93b98bd5e7b44c3b79 +4815816fa5a372f9391088eb904b85bcd83b0f5117772b597691dc43fbf5bced 5df58dda56789631c78aeed62708e1b694440195 +48206be797f7ae1a97312fbc167d2f1d68a7a739844105ee0829635ad3db5001 5cb9d1b563afb84d201d9cfc98aa774d7c714266 +489243ccce2df9992af678d0bac3bbe47d19de6cf9577e4e993e036efb0b194a e6b63299b2cbc04ba8720f77425cb58c7774d4aa +4892b87dbc8972e6de31028fdef2e8ef01f4adadb782e61e2995424a4e660c1b 55ea3a1ed6a61ba508ad6f9e8b70169fbd7746a4 +48d25469668795d0d0fcaa9964a1c7555b750a8d700869d5d8cd466b1f3b0881 2434915bce998e8aec539a19419f2c50f26fa6f9 +48fc94b3d4233d194db85973d6028ef5da29aa21aed20f8096539b837fdb9cbb 171aaf21d9f7582270c390962f61d3d2613c4d59 +49235ddeecf412d250153a630dd1372fa0ea2953f64edf66f9cc712c4e003504 41e13b227932a121b8971909081382da4236d674 +494fc3881f4dd0afeb9c67b9d03ee0a7bfbdc344db53d35ca91b23b27b8a25b1 8a208841484daebf2dc98866e6d2e44bd9467cf7 +49818c2e41a63cac45e51f99834d583c2623c48a2add67395238eb16609c7f43 d90233146d40105f042773fce6917fa4687b94a1 +4981f3f393dad6299538f859bfc10bfd1a46e6c1a76991a35f66c4a63563bf74 42fd40db68e5e5797531dc1f5de7aef39a2262c0 +49a226f8527a921c15864067e049ef18fe0e56672cd9be551eb1ac520cc2fbe0 29d1016d5fd281f10d5fd8183fd8bd2f1aa37121 +49d34d8e410e2bc0d4e83f55f013feff99a8412092b44fe18c388e2cb61539d9 01ef4fad010ce3b90426982172b2d93a9409797a +49d6ca3fd96a83a837867c38d32bebffa599cd8f0e1442ec38f9881b33184f78 a1d34bc000cee6d72c3b5e329faa58424641611f +49ea7b53ee934be208c963260e61d8939898040d12b4e1c166222b5a7400eb42 0ef9d2aa934d6e5861206e8c5dd829dc09f4026a +49f1fcadf4ffce148c8dc15c655c325b0c52315f3e093ca5e60184b826f1d575 9b17380391d7e2e4cc26654ee4423ec14d379d28 +49f7c79a7b31b51b469de45d980e77ecb6540a138121dd60bdf399d0000db2da 29e77b4c60769b427a0cebf6573bcbe3b8779bec +4a1b9c078e7bb20759a2d75e3a4b96827c851446c0261750b96aa5f286efe378 cf33ac7a3d8b2b8f6bb266518aadbf59de397608 +4a2c51c855f0dae37a9d35532cca06e82c6c00e1e35cdd5a1294ea79a0c89b8d 8b9579b77d705268991880156c59a6ecf3a00e32 +4a4ebb837e3c9883f35d0ecdc26d4bc76a0f665568b08f3e967096f2fd3fa537 502acd164fb115768d723144da2e7bb5a24891bb +4a7732457b004efb34e8eb1405a9a26f66d28866d8f043ba3d732b4a640174bd 150bce88353a5b322b1600a5989e126cf8288562 +4a98d245f29b382f3bcfe34639530bb5d04f6d5074d9c22359b6c8a1d5b25540 1dd35dc8e28b539e6b2e15ea2a99d43ee4d2f282 +4ab6132f0fb323c66309c552e3d84cbe8a374258ac42bb51b727e02d881b04df 007e075337848055a92e218bdfe137451a4c9635 +4ac0a355c8c8c1d9f943292761fcbaf0f9a342b6dafa1ece1145903e2a07ab44 75d584305598407be4c53d42c85eca3cd58c973c +4ae0fd2cd8725f4c1af5517529f28e608f1dae886966739d3a6889b8722e71a1 bde6fd7aa4ede6f79d41fa5be13567d12388c79c +4ae3f76027b14d13e85f0f5498a4a697583940df57ac8733aa56d82900112a80 ec250c6e18e56d12714f9010e1b15e5feec5f473 +4ba2e6fe88717d069f04f6504a12052b38307bb7d50adf36b19152f50b533082 4a869a273478d93810045203fa44e2404d0e32a2 +4bb30c70803cdc8f5945d4f61437010efb53e992301ed4f406b0706d0edd26c8 3f84b7d30bcee174491ff0015816fe8831b1440a +4bc0f94102287f8e84de5459edd04872cdb10a27652c49499a9689fa073566ed a6a793147da3bbdf15931e03128af729b5d0c4bb +4bc142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5 a71586c1dfe8a71c6cbf6c129f404c5642ff31bd +4bdc2bd639e67ab657405823281a1f4e8185eecbb81a4942278212d1fd47d941 4b09c08aefb8298b3ef03691eb8b4970ecadb1a3 +4bf5c64f79284904a0c0d54bbd380a63b7c7be4ded56619ac7aba0ac763345b1 d25c9bb2bf4c052f41ead5328a068f6828d75ba2 +4bff6d41212764361eba5a8167bc0168cb027f50724f3c53e00e2c435c92fd06 46f4e377719b3df0279e66f4d8d7677c7fa16dd2 +4c288bf404c8e93868ae54b4a9e93124c56c1c61184466629d03310065031e88 388e9c621a9deb2180cd601e30a540cdbd310b53 +4c989a75733680cdbe9068f0896f30aba0cb9c8cd4334e349dd815a2e6ae6b7c ae3a1615a8ab6b9a13ef98fb3f60efe727615310 +4ca78a50e220f784d9f0d792184efb85d2e78ca1bc8aa01713c794cb099c0cee c79dded300f279a6d8660b13f4fca3c684533449 +4cb34795575974e851198c515d39f43c8482c0f82f39518e7ea31b862168a6c5 ff5f93335a52245a405e12e9ad1091f4cb875b5a +4cd983d076e60b69cf7cc6c645f66eba7df5032d271c9ee95d74193b8e3dcc79 795f44a7d7e1728199aa9ecacf35a55d3722e06c +4ceb14f374b242ce6b94a527429bf46799f37071e59d43add03c79cc3f78f7ea 04e8824004970f6889b9df126b5db343013c82c9 +4d068ba9c35443df0a7723e96ec1eb1b0c86dafd4b6892a93491a5d818d944ad 98f3b5407694b1977ed2b4fe548831916e788ab8 +4d400cf49a7d61e1829d382168d34818c10f174e6141909e6f253142c48bc725 62496552534d8c5a4fc1839c20c33b0de4ba2b9b +4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 e90810b8df3e80c413d903f631643c716887138d +4d95fd7a1a738b7b73ff5b0c595d8953497404cb61a91b787ea266450bacb2ce 1d1be8ee22a69ad37b6cab510bf5f480dc6df779 +4dd37b9df07bcc7b45ce72a44e3f5fcaf5f0a9a5f3148963714eac4c99a60388 d03f567593f346a1ca96a57f8191def098d126e3 +4de260deee1620a10ad5f497cd446e3c6bea110a5baf5b82fc510bc6ce5074f1 c620f25d7fb92598b819001c8265d0d324275546 +4df8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd 4b22b35d44b5a4f589edf3dc89196399771796ea +4e04d7c66334581183749a291a12d263492195f3418d12a06ad507705dcb8f5f 290ba2a03a1b3c4d6d76737df0c9a8964c0fc192 +4e731979325f7c7ea26f941615779e107442e05af41f66959ee130f0a6d0864b 37eb3fae578d0c74cd43af472a96e58eeb29c7d6 +4e7d3270a3ec5957db4fb47c36ca53e140252a975ae3019565280787952f28c6 8be856d8f8c8aab89f093583f091979aa2a4b8e3 +4e8061916861af6c1400709d97a34eb696efb43e9d862c53d9920fa0c71a76fe b580f2da89f867e9031f9d5dd8f58a20baf20b33 +4e8deea38a82e0ea12ef279bdb6669f3b31e1b10862ecfdf0f4a2b1c37ae14d0 96e4df3bdb0802fef56077e3f2b24fbe4b72b4a3 +4ed01278b8fd34390518ee993e4454928b40d85760ebc6fdfbc8e66e45ef7fa9 abb8a36f30d20e6f6c1ec2f5639c014e3cc18535 +4edae24c115b595fcb68ac98ea93339b9f2f168ecabacfab22cd6a8bab93114a f2a98a88cf3372bdb57d72c7136be6d42b5be1b4 +4eff51656a824127b4d8c5d9a6f4880830c91fe325fa8d213fd1afd6790affce cac5d927b51bf378cf599a02d38538cf7ab70146 +4f67cba8f2b69fb809b5b8303d82624e443647408af3466e59c10aedb2422e7f 0c7fda8e41f67823610884815c71cf47c6dde496 +4f79bfa3bbbcee0f835fb81706a3ea7264ea4ce29533000a24ee291748193d18 491442f97ec18dfdfa88912d20c2304d6ded378f +4fccb0e68f9445601a507d0ee3d00531a914a409bd2fceed6c07bf3189c25362 1ca0fbb0838e8859d5928cfab9acd749e618b6ec +4fe798a4c86598a3d39cc2e8e7cd4166f110d79e49d14fc0ce656016142b9ea7 018fce57e6ca903bffe9abadd2b8faf1e60c3899 +4fef7f7c937fa759ef00bce38f77e622a67b3bee32d6d98d50c84630e74c40dd 2bf93fa1071c3df7f43926c8e6c3bd2353d78160 +50085f68f619f133f16b57b7e3d18636eaa349151ebdcc0eac3f6de491de0828 e323fc5116fcc4fd39bbe3dbbeab1e2712052e09 +50141d4c9e46aed05c08d59a50a230e5507cf172166784d0f5bb6a1edca97377 c5696427b6d53a3f79baad35ea33c556884a410a +502a0aef29fd792e608b9d0e312dec720cd1b1bbc41a22a5ff05b5679ac9de94 6c31ebf84bd6eff927f1e6cb5fdcb4ebf56e1e4c +503680aa43ed7beea11c79ba04a43cfcbc84741e7e8b9fceeecf7eb3a3e46e62 7f12e62c5139c9c43ed54c3d44cd3a32263d245d +505fee9e489599e4e4d0a3f966a981a7aa7ac60ba8c774b2b942f0dba4b60ca7 16ba02bda9682555bec1bfc9225921d7e949f16e +50a8f69b7ad3d2fc73678d3b5a7d474998c04c83af503e57b2368a78e8e4975b 1cfb0ff4b6193c409e846a5992683559afd4be07 +50f19348faeb431dd64cbc6c14ffd04d1ed09f3cc7582d56d449cbef87a52b72 84a5237f8ce73ff4cdf8b644ddfa9f0bcedb45df +50fae421c835c89e59c0103070a489a071ad663c992308b67cfb346218d1da78 f16c489d96a9a046cddb3de04198e96b65f73330 +51555c7599dab2ffe66391540f293df885d8f5859b17b8de3bb3ff7db3832619 a5ab36d1d5a5b73d0df5425e5b0ff25eda320563 +5216691483e994addc87468ca4d0e29b4ed2d2e85cdded14f5a0ff5604edf733 3bbeff3fc22b75c1a26f4ab9b64449b33002aea5 +52cc121e697beb8a03c666a7cef327534c6093ec79c3a53babcd5e8daf5beb4e a4e23d6af621573f5e01adeae0c34099f66df32f +52dd20196726c4c9586b9fdd025af66adc7eb0373f5c62aba864fefb526cea55 64a47c01426a36cdb7e598d17018d5791e54bb97 +52e11d5ceb65af472f4665ebe312542aad2b6cd032093254539db22126f7f7e9 1906f2369d2906cfd4affeab0852b67c4a91ddca +5327a985a8eae90deba5b4061d53c803cfd87ce7d5b6102b6ba8a440f1708a8d 32d5e64646c0c3a5229e2008e21a45a741e197f4 +5329dd2fd8557be5ad06b57882cf42e23d767cdc8a4b25e464fdb00890649e07 af795e498d411142ddb073e8ca2c5447c3295a4c +532bf3caadaf4a236b802370418cfcf91668947474f540cfa8cfd54223435efd f9660220534f2ff457939a16634bdd5bc55b7984 +534f1da60db89d7c6279315acf64e3ccdd1d2892938f9bcd0ec9850f127bec92 38c513b9d1f6c19f191927003c5befd2fa70da9a +538bde541f9512cf48fe308a110a279cd36ae036d3845db0bb7574832dda72f1 e667e1741dc49767b8563796879f344bc2b08197 +5398744baee56c62882d02aea2750298ac9f54415fbb62894d246d9c0a332676 94eb865e6b5e041f6ca6ad16ad102ece39ca3482 +540354213f70c39f4f6e39fafc65ea211a96e6925403a3857955d42097f52e39 8839d5b02bf316795bf8716c33efe255c3b6c8cd +54139ddc224333e61424ecd3e0d1d3657422696d8215228bd5b26db14f63647c 0d9380246ef66cb3702c70bb9c1733fd200739ee +548f39b3c94981fd63b03b135f58b8790e2acf91bcb754546c7ac87820fed04b 425aa0032cbe69c0ca70f1467e54e862fbc572fc +548fc7632f9b3793ae8ca2f9bbf4c60d640973910cdf5567d252d5074dd79e8b 581a704418ba8f49517cbec2bbe44e281c2f5042 +54ac7856186ec30cf5e419d09fc567128eb24a7e73244f70f55b4860065fe055 74766fd92581310d08c6faff6998dbf307800282 +54e3102db46f8f1304dc4ab62763a71f915e4918ce4c7dba10c015a4f807fe6a f4e43ffc62d7d238e971ecdd8617d45ddac81caf +5522689ad5f3e1754f7cc5daae217735eb70d0f68d55890333e8e7522ada298c 62da145b914438c95116b46e7ddf11ac3a56883b +553b8a974a6f8d2a8cec357147161961adc5a0ad4b9f5fca91eb383d8043a16a f9d0c1adb25cdce71cac5f01e8333f201208a781 +561439ae8748339d28a29eb0497876fc8d47cf78c515a3c989739b674ed7df01 351cfa4a4983e1f9241542a855d53d93c951fb5f +561c1a1f3786c440d90fa9d2fe5549290d95cf10881d334c11fb919d3c81b4bf 5d1e7929bb93b783b52bb144372985b8e5b9ba2c +5629b9b5e602665557c9c7c31f9b2ab72e235ecf54ce1943c344dee027db8564 a1c0728d12b170e1e643f219fab53b03cd4e1b09 +56359702cb503f029d099bd51e6c54d47b0166dd43efa99ad2d05eb548773970 933cb118437ee8a4422e956197a8a4f09fd7e9df +564017a60020eafdc626cf1a2caf4c57f128c489d23845e70e0c3c4dca8a187a 9ae72bd6780d6cd354719a1d84120e0690ffb5a7 +564d38d384f7e08bf03f021d1088837f3f4dd0c1b2c495fc3733c241e7586e19 58abd58a7f232ec3468da806682f39267d5b23bd +567e9815220b48ce3e8b3710364d499f84be5e1beafbf04e4e9d0772839e6213 72ea4e962dcc9bc157391d5679f13276a21f8c37 +56abb178ac61ea5ad9e6d2e0eb7dd731c1ae88bd15af3c65623f8d93212b48c1 630785638fdad6d35d47213ba2867a702777dcd5 +56b44edb90e571e38446121cf36270dc284556d8b5e1150ec03f9fb2f9059a58 83f7e7bb0a88634bfee2c4cf3434a2418c5f2880 +56b5280262679ad90e14567f62e22ebc108c9765f8421beb643e2ce0499671ee 95d21f5564cb13338520ecc7fc50b55866a32b24 +56b6ff774f9a813cb058c31144b98d4254329cee2d00e595fb26336be9fa9b96 b376cb120f0113294b80e7fd540d5dc197797764 +57ab87cb9eaf3afb28933d81fb2e3ca43e699cc1f02da50a56c3afef103b7673 ae31ebb7078c0bb2fe5a3dd697195e2d9068dd92 +57b1d9ea18f9cc3a9c596ecec921938223879a49c375968ea034608e077283b6 76d50675cca89a89b263f23d2014c7de7f59565f +580474d948cd2ebd2e5ce7a5b81b872d87ba4639c1ac4c0fa7a11a8eddf9827c 412ec4e4a6a7419bc1be00561fe474e54cb499fe +5818ece015b48860c7e518f12b06399b60d7868b92c3fdea05f2d25d0077e476 498bccdfec1fc223c27c0d84030ff419058e452d +582841d0c8af7a4d254d30790f1b07b3ac8f3f1c947be0a31791e84e307da00d 9de0a701bebd5d07bbae73bbdb3f8c39e204bd20 +585d35a8f1d6c41b0d48ab5722811c0daa8bb51164f24d77bb4459f95cb05169 44181c23ea6c39d51a4b481dc59ecf2cc3967e76 +587d967295661afcdb5a1a7162f59927c824268d19cb4d9b81fb5962d55b9bf2 225fe21522a98075bdc18dae90ce459f797ac366 +588e7ecc3c2704834f904a41fb90bd1091ac1eac8b5112bd60fb2382087aea82 9a827fad4fddacb757be5c6eb3e8c5f323ff9952 +58923faa87c7d559d308a114ec2b164e5d6046c889420ed1def6eef2d55106a2 785d3fe8e7db5ade2c2242fecd46c32a7f4dc59f +58c150d2dbb9c2ee86c46b5124a9508f632c1fb35325d8a8fd1c59dae3bb4b42 6dafd0566786a417cfe1131ab804b162fcc79d15 +58cbb3fd7fb9820caf0f9c15b066ab41df8de6fdf1c83d72f3bb460a9922b2d3 7b08db51663cabc4794305fe36fb5e09c575d883 +5905b697743f4998b3deeb3da29d5270ea6bb52c90d4dadff6f28f0f72edb968 2ad730b25892080b9bcc990c05079a8d605946d9 +593b262bd7c0c40d61962a36f9e9983402bf8b4b585a6245a47b161a31193807 7c3f1a8504912d590d12048d32cd31d2d75d69ac +59a1967792d9b5338bfe0bfb7658a932bd7a65b3aa8c8401dcd6f149bbe1e4ec 046c8a0d9a405b354f298c7a73e8755081a949b0 +59cc00acc478955e567e5d9eb9b888b8b538165673147538b6132b4113010171 e815adf588053e45a5f5410d44a586f69e66c1db +5a025657f0e06e135eff8536b93877c7725b74f2317b6844c002ea135797c531 425b3cd58cc77a921d6970967aafaa4391cb7f4a +5a0eac41da13655a7cfa4f4586d152049b9ef83b5cbb41c290e3529a856c7fbf 2a0665949d7f96b9776b6f36f60940351b47cdf4 +5a1fa5495a4229c962054c7a09623056643e55db9601d584db08039af54bd01b 33fb00b13d006c5497ac0152361769b650397dd6 +5a2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b 9a03079b8a8ee85a0bee58bf9be3da8b62414ed4 +5a417fc113d54074a22bcbf157923fd439fae36ef7c22cf96e6a99542809495b 200a9469e6f76a538b8539d671161a573c3ac91a +5a48249041aa1dc714d3f4337776768d827320553e118cb4d99673dceb9c975e 0ebbc36594071221b685805590415d74624f41e3 +5a4fa7cf1e417c1032786472b9906074d75df51bd0a5870b34125d3e386d5b8d 4c67e2e95f0bb6f0e214bd7bf052f914d365f129 +5a9353a7576920c090a94ebf978f786d43418c111468ae2a343eb561324dfdf3 3442b9486ece0187998e802a3e98f9c97258133f +5a9391ddc692a70f59eb4640f4f221bbdddcb4dc9ce0e8abcbbfb15ccb9a1513 beaa5abd55600b276ceba3363285fadddb906433 +5afcaa3ebee991ad963e8bc7d3d947da769dea7095973e5e6be1254138855e2b a62239680a8bbdf85cb5602325caa42abd141d66 +5b0f9a00dc1ccc48a7d52f512338cecd3f4f5b9706c227c5f8fd158419862da2 c63cb6a816126549e95c8585a65942981ff4d634 +5b264a6c0568f6e60ca657ca41b3608fca3a6ae87bd77ef9635edc75b5e421ec 6d681ba6396167845281717f4ae90389f884de48 +5b92057969cd5f96af9b9f1504b532bd3fa93b9dcd5d58c4d650c3ff640e54a2 b8d515dc02a663f0d260c69e23c6cc42a363d5d6 +5ba7253f47d390ee2c7c7afe8fd9a963a7a2674bbdadeb9a927665c9246306c4 e04529998989ba8ae3419538dd57969af819b241 +5c05f5112a30ead09ff2ff553056755dbf568c06127dccecdce9e2696fb41398 634a64b10a627e64f1a4370d085eed0be242b6ed +5c14a1005d727dc5ce74effcae1dadc76a77533167c76347e04844c290aa7ad7 4c3bbc923330f9896bf5789864212cec21a8ca0b +5c18ce500d2a3c3085f74b3e68dc4ebe703a6210b3343f50796cc15cb18c7f8e 20e7f426c3d56ba9e4b55f31a61835dd0031b464 +5c3bddffe44de776f2d7715ee97a62b190049e623d3b278ec131336f0a1cdd70 2506dae36e0f9cab6f81fdd825d808a9e4c12dd1 +5c54a41a0ef2f1b7f505d12cc7902120b6ba3ada7e023307792b56537fef2a49 e98f74e52cd1231424378d9d26ea40d1ee4d7fa2 +5c89f33a737d292d062924589b856045d9d24630fd3bd1afe5e65e53166cae89 192678b55c30d0652356a5021b521f8e205f3428 +5c8b348f9e152d98efd79d2990dbe4c702eabb06d4a1427472c1435be0a13cc9 50b20c4bd550428a6f33137bd52db4b69799f1ef +5c8bea399f78d3d6a037a41cee2e763d00024180b66f2ec738d443b6a3dd7081 fd899f45951c15c1c5f7c34b1c864e91bd6556c6 +5ca8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848 8e73b769e97678d684b809b163bebdae2911720f +5cebe9f8fc63534c919bac2f0c8695c707e33647d44792b16e28702d26e0ed4e d25d5d3bce20e400407b010132579541d116a8c2 +5d021306f157c028290dc5eef52bad4a3998b50006fcaf283cad4e6918157c6a 54b9460fee5219cb5a57118bce066697704ef3f8 +5d2460e61d9d5cb4fbdb9a017d4761827f339c067ec209c8c44529a5d9c93239 36f0f61fc8578478a9dc9b33a1a0d07cdb98a5e5 +5d2e103059f5a5629d12e4bbec2e6abd1d89f225dee891dc630f67ac331209cd 96f34447e508c58b5444992d4522d93687c9ddc9 +5d33684397a6875ee09f048a93202b5ddcc468807ff5faa382f9508833af38d8 7ca4ceed50400af7e36b25ff200a7be95f0bc61f +5d4e078cdc4960b27c7a0f8470c9d797ae21983e08efc65d0cf36583737b183d 0129895fa52dfb06cfe4f1f456d57d8e16453686 +5d7a52475763e50597142ac2dc2d1b33d771ab83f3a719fdc0e7b880bce4513a e589dde541c522ea4096a32308ee1db9bad53079 +5d809610df93ede304c638ccebbfb2aea1010020d5a04e25836320a890e40381 b20f1d8a6eba1c138a6a41206ed45ba0dfb7323e +5dbb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f 43da5ec3274dd061df152ff5e69853d562b01842 +5e0bdcfea7ab39bfe79b8e00a459a68f5b60b2b0e7600bd01c09a526734cc769 399b7d5ee364ac49da9ed64fcbcc4ce2e0badf56 +5e4e3a2f07a2d26f7ac4ae12b4157a7c6201141b08a04a296d5c3d32bfe9d4cf 5720bbffa1377642eccac7fbc34b35a4b44fae4e +5e53b0f3e603756d985e82d66a1186c2467f2c5b027a1f4baae651a5a68d1aa0 c2ef0cadc189a076b8ed0d855da1e58502210507 +5e60eccf824d865dcee2ee9ad6c947207c3649dfc0bef1cb940c6d05168ff55b 627513e78ae0c8dbcdb62e371ea674495c841aca +5e7c2a3395be2546d41331f9e310217396252fbff62f139e41493df6f47a548d f9fce2b42184f2181a5ceff90dab05f7cff12697 +5e92f55d41b34d254a0fc5e5270c7f78ba48d70743484489a2e81e0c9b420527 52f2390b4308fe51ecceecbc35a87bf6e74e9aa8 +5ecf05b5a0c0b45065f977149190b13ac8bcb18342c35cca02ad93a3e7236ee4 5213c69284b521c5a60ff677baadd36c97a5b333 +5f191eb81f39438560c516e2f091de40fe3b7e090b89b854d013caf446b0aa8e 51fcd3cac86ed8f6ec215146b79b0989d407ec48 +5f45642bfa66a6eabdfed22c7dc5fc170dbd9a4d93fad22e402b38cbbcc28444 bd0d32c74ae66ddafc9c875b391717e1bdfd6f8c +5f45a18f90f2934e7c7985d05b2b5b3584886fd057c9202f26f562d6c3080038 112998d425717bb922ce74e8f6f0f831d8dc4510 +5f53944c9390e883fcd33819062a750cbbc9b483c15b554eedd351696583d215 f85c76fe4882c7a4befdea9224fa8c01574fdfce +5f5f4cc82137a85080f6995d5fa9ed50980e07b0d68bf96c0d8c9834f8d4ae64 66a4bfac99c07b79d75c015aed8f5972f0a4a336 +5f877d011f066c0131dd3a53b96dd2f776651a96c610e7194fbc07de06d9e851 56846420552d85e7d5e8a19109594ce4e9792b7d +5f884e140db9164254a551fec3b2da3596f9404b80a9e322ea01ae3a46f45247 6c1a2d8c39d1becfee3cbc738c9a126a29f98c92 +5fd43a5fd154026f2952c2974e251c98989a7350228cf84f27c58f0925eccc50 fa33b5202315671c762ee1612d5d153c55f728ed +5fde3c5b024dee434df0642b1f2068bd65894a9fbc346e15d7bb75608f948ebf 32e41390c34dbd3f59c8cd78aa18adcc45bf57f9 +6004573477242428d7b6ee1f642da73ddc68de2218e36b8b037b068bc3de006b 0d65e1969a5c01a5ec64b6950f17f132bac18a2e +60087c8cdabb0c7a8fc1cb6134747d82f02a2a920d4c2eb7dd783e7ab1f37850 b3715dc1acad54598fea69d118c992fff7ea4ee6 +60b2cd28c2b0b58ab103d838cf2ef4522a1c42490cb93e425df77f39d7105e03 458c271d8dc57f36e3c6824d54a7a4462bd3866d +60c64d140395cf067197db3df59b33830a8ca7410c0732a1c1643dfbab2ed069 2ce6f8018fbaf125ed50e66adc46ff4f9d0a9288 +61068c6c7b2f2254e55c83317eadfd4ae210a6dd362dfdf4925e1ca20c345d17 b231ef3acdcf396dba5f83f89488be7519da551a +610894ea28aa99982aba7156a1a6ee0d18652d43f7ed7b2dfd89fe1252b9e645 93d96a8a0e09ccd1526ae4e769b5d41c239db3fb +6111c38997cbd2dffd851ad4d840c9dc45bf992d342b827bab5114ff519ab57b 1a2e9c0f19f0143c69fa2c4d5d95da3d5ccf1357 +6129f1672465ee7b9e2edef53fbf3846ab5d06e8e6a1d7fb51e31666a8b411f8 b0941f9c70ffe67f0387a827b338e64ecf3190f0 +613faf6e366095154ba6b9cefa47d7c3a664401174497daeba14a13c3e444fc7 77e9dca7958f1ef5a01dd17de6e5fd1175a13ec6 +61489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e 1810dff58d8a660512d4832e740f692884338ccd +6152d01aacb9fd63bdb908a5153ec9319bc837b50a22322aed77ac01764364c2 32d6b9111a767ee0a7500f132f797280dd042367 +617e5bac096ddc00f0c17e6dcd6a084cd21ae6d4ef7369ba888060f3c7e0a988 2917c22c87060a43fedc431c0a125cb15c1e3bc2 +61894490bc16340ae5eabaddab9816b93d2231629f5c5709f0c09d4c9dccee4c c985f0cf48b852798f4c5752da322a34f6d386c0 +619f24a7f37f8ca922c83b3a1b9a384eb6a444ff3a2a52c712f3c60dde6f24fb af55d0cdeb280af2db8697e5afa506e081012719 +61d09b305f785a367a8e1cb85347dc38d4ae9de484dd3f2a5bffcb1af3b2417a 803a6b4d418c4b2d7463bf23a86a09cb2359ec98 +61decb10e8bcf036b7b0668b5f1e158381a26ad14f4420bff686801f9824aee4 1f8435d47ad5fcd9e92a80d1cb2b8836fb711366 +6257fc48e458a95396b6781a738dbae7866999cddfccef63eb16f0919bc2cb82 4d25aed8f9ae7653206031efdb0b682d62ece767 +627d6943fa03029b8c4d730ab2acd266509261d14042b1445c477505b29fee21 3ae3c85110b04ee92aa528c3da0956391e038cf7 +629e9c4fa5465689d98c29fe048a1aa7f52da0235c5d67842322cf5d183e1175 b72ca26740c71371258bd178188a3d981e39701e +62b1222084376703ecc7737181def068375c47640206721dc4674491b8980a5f cd874895197adf33e41cb14cc4b5fa2fe7ff758f +62b4aed734e93df5fa848ee90fcc38fa472e13581d05fa42f11f6aa8a388da89 4a143abf80a82b6e51a3a32e0be40b4e559f6a17 +62cc32311fb4f113536d2004edf25611cacaf657bc1f59552032c7cae29cfd6c 65912dec2c33ed1cbae320dfe24f868e10f30ee1 +62f462260b2bf90079c917b3ad2cbe8eb2c8bc136512961c839ac53845f7bdce 784b10b4b075a60fa18c703b88a744a30fa7eb3b +63071afc7376250fef5787562b2463404ff4002698f92a61a774bda0a21a6ff8 22b3e83e3cdca53a20808202a795c73fb47880d9 +631b70c3c1335f8aec14d98eea4a36890c3a00bb2367761e8f7d1e209baf74d4 1b858839e1f08792f2e5e1165bb4fcffcc95fa86 +63fee1d7ec8c84e8854fe394dcd134a633d8f3de7c6a5749b7c12cb26b8912cc 99228417f71549fc6392a359d59d33050c055733 +6408a234856eb88bf60ecf977da417e65d38cca482547247a143aff624e66872 7ce66fdc1f7778999b3b3b87608771eb2f5c124e +643cb3995f617257573a639e4b8639b46bd96335f61bf6d98ee3706397c75544 47ee6e20700957518dc21886f769ef415a592568 +646b7e4bee30efd073aa27ddca866ae7ff72bb284b035acf721306ac5dab00c0 56f77b95f87e01b56f732417c1d557f0b6dc0be9 +64887aae9bb21a35ea8a484354297a8a079b3bc20d11d253b71afc1ec36b1f74 0269c3f82d4a93b859ff58582b7c6f1843dbbbcc +64dab4ad3eb854bcf575c7d9c9e9673021ba74203147d130a209ec2f04a98715 850cf44e8347194a57fd1fb637f0a12f1838ad38 +64f0605de2dcad18e95b1cbf6cbb85064f916406f63e8f76c9c223a4e59d1e55 eee3d3f684fda02fc8614dc4ea5c399ee71e7e4f +650f6cd874aaa3fcfbfdaf49d6683dd6a3e9e3e45d21987dcd6d67d11bd12632 7cd60e59edbd298d5e2790e2e62aa5c4dd55e8a7 +652412419a24ba62a1d897f40aeb80eecbf873797b04a1bbb8d71918653ef65b 6dcf9bf7541ee10456529833502442f385010c3d +653aedbf79f7c6e9377832bb81f3d408988d1ef51e130b08c29217a9a53daeec 4a59d93f4f618c2b2ede1b21c807cab24453056f +656dac701c40a400c97a4d145190117d6f226b41ac39801032b5ce2f8a67881d db31e1df61d8586c1992036ce4585a69fa82076c +659c01981f8ecb286bf26d7759cdf0e59f3250dbad69c5d74e38c5ca5c8ebd36 c1052be270f379ac8e245838c8362f89528c8e34 +65caa3413382890c53fa78aec55069027f1918985bf6e62c97716cb846b74f0f 0b59a10d29ac9446320f6b9424e427ca7548af77 +65e0543438a38463238bf25be5064d5e6a9a9b80f0d67c3babe8bac7a3f9b269 edf57b4b98fe7845c0f37b6d261f15eaa6a27556 +662c71991cb89a89f8b0d3479839daa1c3837455808983dcaa3c89131757d4dd 408de023c9bae4e7cbfdbf7ea65f0e41012f5a4c +663848827efd772f21ab7953f2c3edfcca12400bccf92de5616e9ef57aa09897 7ab2d5413945768710d357e69ebe6ea89d0e0f75 +663ba0d9c53f18f52af699fcc1f7e1600098961324a48f3e264b98718e189498 c9c7b0e147c44682c9d4a100dce290c14762ff85 +6675306226a1869a0e696a57ae9f8172511d961199a2ec17ae6827e39e4304c3 52e47ee702754a385a930079d5e127486e52bf68 +66a993bb9ea76040f448de0e3bd65d1f9d88dd5192921cbf12f234c813466452 75e1e84d121120833cdaefc7e764878b7cd7d602 +66c5b943797fa9a7bfe9194d4f1550a9c4ad05d69c6d1a1be4e97932cf06a813 4c9a39732942f763191d47be51b414bbc81bbbfd +66f3606b5eb16bb5a34fcea47edad9a68a54c2804c09da37afbed6bf512d5221 ac8237e18da19ff375f051a973bac91d6dbb2f56 +66f736d33cf07582c5d6366a9a1bc39d39937714e729dba38a7b61faf2b156b8 67b430937482d4d1d877ed3bc4a6ee743028c3ff +66fe8385c6378bfa5ca5573bd0fdd773e4eadb0e86416b483f2c50c839859ecb 41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 +670332674a169ca0bb0f1d14bda945c03bd6687b756b4a54bd8da9208403fd07 5e9e1f22cc64d3a31b37e3d0c865228a71c4415f +6705ab009e12496e243d5fae60f7ce6c77fe68325a8818ec75ff317ae80c5e78 eba91472284330a048142950c1de90afbbbe2c0a +6727e691fc664f1f28bb23cc30d6b84c34382029e56edb264b2498117398f8fa eaa63e1496702ae9c36436969edddcf7e9817545 +673e079162dc9e99a8bc43698e05492357041a3a661b104d1d40fb37e89cf965 dab2ff4ba3ebf8cb58f5b52975a58c308b2ed283 +678d331b936a897f3ada21a29b403968b075611588c9b176abf5a05d415e275e c8d42b9bd1b6e45f48feadb5b02e152c5e5f1f62 +683b60d7962844415b532765aa212699c5414b0833974f292c233c6c8c380dda f2b93d4124d2a46ec7070cf6a40a3863e2c15556 +684b9226d707a7b783f0705986600b2c9c7decfbc9cc72938cedd113a8f0dc83 7e44c57fbdc0eefa79348d25870369b2f4b43ed0 +68a467e533bb9e4b2c5ad712d0f8d1b484bab740dd667814f1c9cac3a4a30fc8 19af3949ca8ce2831049a4755bce3091d7f4bceb +69136323b1d7ba81e8cad8626dd83cba0c41f780caa43421f0e5f2462d78acf6 cb3ab00e68da089aecd496ba4e39b1c706ed7c68 +695bdb545f636d454b4825effd29db96c46d81772dda6c104f97dffe2ec509f1 4e8cda27ddc8be7db875ceb0f360c37734724c6d +696503760a18787240ba52ad1abec3be6517bb802238e9469b3a8999cbd6432c c07204a1897aeeaa3c248d29dbfa9b033baf9755 +69c1a2bb3151164052ca0435160424d460eb6268858cbd027cc551a174a52785 5fdc7166eac68b0b0923439b8685a29ff4c2be27 +6a23c1c1d0570cfa36151e678751e16ae587b0a8c262efd011a4716555b345e2 4d503f884a241c507ec4e474e3e3463c2e394d52 +6a28b5afbed89366932ea57b68cdc739f347d242f0d6e6a0f8e9a794205a5213 11fd982b055012bc932fe17b6187629555000b76 +6a3ddbe9560f1509d2a15cf4f580ece49e83b83f9baecbc0f5ea512ee93e63aa e35ef00595a0767f16bbae5182f7dd1c07138834 +6a44891d48bbbe85ec77c332a4d809ed1d7bdd39be781785d7e8b4b31a97f586 963f3cc863ffc5d238de5ac3f425df6ef1e3e3ea +6a4f8e777996a56c3d69559f2006bc0f0a990130aee3d2b29aa29fb3cf7fc437 1b1e2deb5f90be7f267af8d3d335bb47d648944e +6a5f397f63599bff44400a30cf482d44d5b76808fd9b24c043e8da3909b7971d 85a843cc1363932d8cd5d1e88024b9357f0d5572 +6a837864fb44516a249c5df72b8674fb3f16107af84e2f38c9c4f2ae7bf8676a 357bf823711de56d93f51d9f4618ec2068d2244f +6aaf452708cf86455c8ebef269c0cbcbf873993e9cf003e3eaae8569eb997da8 a1a73455eb3d2a9571208c4d4af74f4ec1b1d161 +6aee1b075ec6d9f63ec6ff0689e80f4df81dbd2c0c2bb17a8627d7f0602345fd ec6a1926d4465e61364a7a8450e8f4c86ed841f0 +6c04b20ecceba56f5ef3dff0d5b93a551d0c6b56311599a3633ef16c3f2604aa 0051f09759ad432aefbacba58fd306dba4a13f79 +6c14ae62c1369df64a8feb0dc01acc99445e13708726f5687343ca15185a3e9c 86dcfa927930bc90239e647cfaad5c7706b9f609 +6c18ed11d243aac62a14a6863430458296354e445f0ac7927d5231161ad90033 c945829d434378a0bd235640ce08144ec35a8cb3 +6c3b3ad04c2ed7bb11f59cc76d0a980cc087bc6b0b27d96d395af4ead3950200 e272b10388ebc4b7ed9fe2b42dd46e367193c218 +6ca4b09e9c92f8cfcddf218cfb39cd76062c88e0ddb2fa387bce031b902247cc 0a902f96c80acaf50a534832e1892cdc99ac6d31 +6cc829640a9596f33442bdd65ce68aa387caa63b8b0ca914a3af4b8143ead15e 8a6be5524f20162044c8e48a530abdabefefc083 +6d0df366aa3aa46ebc26fd4099838660b562e9d140ced9631fc091bdd5f72b32 3ba86b2be1383df8e6ac574538dba7d1d5d0120f +6d159afbb9fc4660e17efcb7a3a0e1944cfec34d8f8b7d1794faa8507e5d42d0 19190397b94cd8b56e6cecbcbb4ed24c3d454d77 +6d16425f59d59f573f029a4128206020fb89e849280d0ef894b8cbfe38803af6 c994bb473428299c9d568c99805ff1b49949aef2 +6d429bf11e88cdcde78b43bd5227e273911cfbe30b8f56aa170ea00227811ba9 82b1db3b35e34c87862172bf3f4705c4aca8cc98 +6d47966640b12cf618c9bac61c60c2cc245ec18723ed4056fcb5e771e313b8d5 09cc79a662b98af20006f2601826d8fd6085a37b +6d5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3 fa49b077972391ad58037050f2a75f74e3671e92 +6daeb00c6942fb4696f08232d22ca0dfa56d416450633b39cf491a18c6e2ef76 e8beee4c04ff7775b7180a7675acbe95d5af64f1 +6e22fb1278de7b49fe6b298192ffcbc3a2b1e8abcde77b9cb2ff67f3faacf3ed d9eea640862b43efa179924882eec554fcbce684 +6e490b63a52e4f2327be88b4b91812a05e62cb2500f8dcbaee56563803b47fa3 241514f3af6dd20d6d647db9ed37a6dc21d9532a +6e657f9f175e434bad449932ac6211bbb129f4df1c9502f4ecce7a5cc1fb5fd6 2525ae8a3e074a4c65659ffaad29315fdeffb698 +6e6f43fbb89dcd1526fc897e2a42008f8af30ed890af079de9961befe36ed36b 1699efc4217d925c50abee60d196d9f6e738a72e +6eab2abd4d5c1d1ffe7e473b3c8b2889bc8801457737f43255166f3ade399992 f68f0b1b936c397976127b08400c6e30c07cdfc8 +6ef8ae778c5f998b9beae6b2da18a6ea674b268a249c3b55fec38d1b8cadfaa4 9475785cffaf1ab6216a9a5f1e1b4daeee718657 +6f0055a8c0d2d3b66d21dbc546e605d7097533d949fdd85f08c5874c01ef1723 ce022069960b6347b20ee2f90681ffe7802d953f +6f11d93bfb269ee8c7a506178f60c430abfac5d424acfd9c0b0b27b98e6ab49b aefe66d192771201e369fde830530f4475beec30 +6f26102d332afdc855dd129b9486952f8c6b7c071fd96a6f1f762c95a79a3d67 064301cc3528ec3bccd07dafe9af824d87a50dba +6f3876830557f53900630d24eb67e39e64f116501ee781f18d5d3c1c823f255c eb815f96b04732b893bc17c209665b211e3cab0f +6f6ea36739eba39758b45f4a0b0e7ac3d5c3facb390751101e2a8998c3a6f190 0f39781c25a06c1d38877adef4ff5a6a5f67f1ca +6f7d74e7c76bad67c52697562414ac40cce152a6197b3621342f4009c141ca4e 556c36fd15024aa7e888c3739d74dadedc6ed424 +6fae137ba81d0d81c2a85759b99322e7ea8103bd7c8b85be3163b7e91e18c125 cfe96f31dfad7bab49977aa1df7302f7fafcb025 +702a6340ce839f0cd2601cf6fee0e4bb5b308fc8c0a731aee40d1bd1b0ed5f6d 4498654c7989a59db451179ed264ac43d3b7131f +7030f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7 814889a078c031f61ed08ab5fa863aea9314344d +7039e3408aab6a7cd4c16bc8ae9fa81407ac415c427466a602bee592d3ebb243 0238a56890c6185070331056033f4dfe61a275f7 +70bc69623357648bb7ce7526e92d8abea0299a9273b9fd28fe5f9ad55048445c 73dcf2876fecab9adba9ec9bc148ccbab431af12 +70c467f2c60c4c2b12e0a8343c454edcb457bb7daee059b6f7f625db44827ded 12a30e261b490439ded615bddd7d6e3a135fcd13 +70db96a9031877b2d11791f4a75ee2e2db4c6c657c9f1d5e30975e69108ee95d 4078e70ce62371860584b6f42312097b1fae94f8 +70e04bc26c957ee1d57dbd0ff0dc8d8086ebad49e3dd4fbc7b158b544eba4b21 c20cadb993d1ade2db86def0b3e107765d2ab3ee +712b2b96ef712f696523967fc471a5c5d446dbefd9bd46853e6f3030fc4e42df d1ea30c399629de48ac3fe68869a21171ad131eb +7151491618209e86b24c84d0bf4962828c4b1ece1924a44ee1d3cec82d0c7ef1 8b6f008edad28fec6e3732fce75c20b0608e4c46 +715588dc06122ab123e11c9590ef6fadb817b103da857e89d619f98381e194fb 33458aee86009abe87f4f182120e0a01dd828ed1 +715ac0c5f0e08622ce2dba0358e6180103dc26aaad65f463fe319ea28227c991 7ce510638eb4f25e6d74a788e941e27557831291 +716e598151cf540036a73177dbf04dff847cc5f0123027bad6111a2b747d6403 4b44ce23b4357ded8bdbdac545ef49a39d5bd6ea +71fb49e1a6feda729eacebd50518cd5e0dac988101fff035bd27e66a2d1c020b 54b5223aaa8e0c973ad508f4741e2a96961c0f3f +727a5310c154efc27b636efe2503aa36928392b543a121fd4c67e991e53be3e7 efdf2bd1d646b85961828b897633374b09249fcd +72b2b1341efde56221a00b934ea70e0e0bac428cf3853f7f0432371cfb2694be 744e1c78fbfc9438ed8c947aa87beb000fe4bab9 +72c472319cf7d5d59bf9fea9e90b9785d7ab39340003fbc68619c11a9e583c2f 2f205b20fc16423c42b3ba51b2ea78d7b9ff3578 +72e5af4874e5dfd926179a7df9b22f5c784d0953c08f52f57b74ea4c429bcdad 3e337843bf57f0fd5b29d56e8678d23bedb78b49 +7348a064c19fb5f536cf9df91b0241da4dc493b2e4eac9b886b7de3f96b0c414 ca4bb51f66396b06853c444a1f3d259267a7bafa +7350dbb12c8f696a5c1f758da310e02cbca318c96c7854e13ed9383a313c4a09 3c6a4c09f57517d0670e5e1d78587c643afb7d80 +737b6922276ec4b8ff150f102403c48c4ec60e6b3d4ff6904261bbe2ba2d61d0 552e23ba566d975b4dd48163b91021892504f021 +738ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed fd4959ce7510db09d4d8217fa2d1780413e05a09 +73a5d42076f213bc0ec6816f8a51e3786d5fb0eedb6d672b9793d582eec8477f a7c60cfc6d3af29669ef5cba9df325719b9f30cf +73b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6 fd093bff70906175335656e6ce6ae05783708765 +73de6bd126ba92253a3968a1081da3bb585f9010339fa8b9c02ab1c37f253bcb 6522d02a70408e526425ca1579215bcb6bc505a7 +73e67e592fa176648ad7fe7a65c8e3e782902ed1e6a2a03b2fdda1ce0648d705 0db888dbd4366d6fb2e5871a6bba39530e3bca70 +740441a445c2ab20d2ba91f9237116e39c25aacb7527bff9252a15a376ccf1cc ebd94df2050b09575346ce82a94079dba504f057 +7436189d4ef74b65e205d88fbe33f8b130e907b7951f03e12366ed6124342aab 88c3c7230f3d49807323df8f01546b8a7fdce9fc +7453e1303e8cc0bc8fa0debfa7545e7f6b8b0036ffaf950c1d86f86710ca3bcc 562c86cad456bc709af6982e24256844661fa5c1 +74a75a34793adfc5dc91b0997b113e820f1a08ce3f2582580b21a63a22db2dce 977c1793305b307275e2a12335b22346efb342af +74b69ca7e493351440a95d92487789420cadf3ced93cc3c4f9451e4483d7a1b4 1c2df27a59b90ce934bee51a95ba617efce1c77a +74e28415489b8056cb9537fc207015aea0e828fafaacc5bccdc93b2f1609e479 be01c47996d638f76c9149a90a25939f624b6cb9 +7509f5f98d13f7a093342e712ea0876cf17e747f8e2d04738d8592401da74586 32a550a0213bc2254d0e46285d7df4e154573009 +753ddabab8ae9c1e733cda15e8e3c83dd43f5a2d939ae32cc3b30b0be1e91f96 4d8d0fd9cb6045075385701c3f933ec13345e9c4 +755b0be6d86fcd39eb376f2324bc61103ad7268463b6eef46314e079a4d2c34b db31c0661cd0bb4fc5104aa0f27b1986ffbf9214 +757f587c2ab935eefe8ac8e05e3ca862e9bb838cd525a6c4402839f532713b5e 360b107978af0651ca59f07430fd71ed52150e4a +75b0ed5f4a2d5c810f34d867dbca51db6a596d4739abfaf24fcc0f05d99097a8 10b759e734e8299aa0dca08be935d95d886127b6 +75d6f2e4eba19cf2adaab6f58e16c9e3eecce4909efc53d6690f865a4f89b53a ce524c6b3a984455f474968d01ef86156e92db15 +7609c608c1097270356a6fe336a8756ff124d4a9c2e941bba26a6e8c3becdeb1 2ef4e5d838b6507bd61d457cf6466662b791c5c0 +762099bfceec4cf75e4f0cf913c7ac6a0ba6754249fb45579f7d820466045d26 933888647a5d500b90c472b5c99fadd6b8691c86 +7655c1e83698d9a5d2fa841438e1a0ec815d7daddbc7a21a7e930587c880a2e2 0817229bc1a1fd88bfdf4f35e2ca11172054756e +7671193d6cd77b8b81681438be0bf15db49ef9b60f5389ee74b907d7254f403a 5069c192113c59f791d5f566c0a57f6714bb856a +76c1bc10fdf66c17d7f9c02a12c80cda9d1f14fcf9a467e85160993f807abfa5 7cc26c12adbe5ab35986e2e57a572145c86bbc05 +76d5d21efad0c83f17a63c2a68ebd39136b56fc25f97e0697ccc6c73973c9753 81aba2b4fc907644c29735ebcceffa4ce01dd23a +774fc4ddd5efeac3ba9d525a968612df8097a9c903ba0b5b33625ca86eece2b0 9a4973ba6bf406aafc48b98677422fe6e54cf764 +775a1415621a751174ecc0bd86be251ae64c305f39c0b6423f8bb5a2c4cace70 60811b0baebdec5b8663134d8b754b912039b6c4 +7771d48bc8502e2b23c5f87c5f82b446f90060d54a8094d4dd829da9cb5d716e b3178627077e4c6b240ed61c4e9a3f80e42d6c21 +778c14d7c53286273b58bbaf5f42aa7ce8bff5c63737cb1b5d3dd99fe43fa32a 46a29b40df56c1885fcc16a59a95bf319048fcdf +77b15f8f906ba92609d8e70d038973964661a30ae5a1b9ab52f96dee694459b1 0ba17ded678e92f9150df14c2e9a310ff5a79baa +78247330cfbfe12044d4a85428ab774b9a88413b618ed91d812cbdb1694be9eb 7e7c39379436c6c048670386fd4d470444232e30 +784989941709ad0a0f4a6e7c9065f081561dd146e7c84a535a38b8c2b89389e0 84b9cec72cc98dea3353325e42ef78b3f7e7c79e +78568215f27ed3fe1e34ab5c944349511304e3f2b9721610599b3fe24ef90f60 42c07750c431b6bd77c6a9e1ff4997285932e352 +786ea2ae3bc3abb0f17a985bd588c7e95934a332b0ff095ded0e2192e29d42c1 938317c9319e3280b38d705ad1cf74830cb39eff +78ec10765ac8bd54fb141ff1601aa5fce9abe86141d2b5457c0a3dcad48dbc61 166bd0fc25eac4f184a3185eb5994988cfd6c201 +790bf70318c215c3fd3d8bbf2c6aa799f7a9407409aaa79a6f286940dff143fb c96d985df9957e7c042290d868d44feed8a4fc08 +790f39b54161d26f82f98c7ebf3620b0fb7763fce49a5d2d1f6213671c2b1bae 8be50a412d448035b974312389e9a6703e71e8cb +79239b015d62fe6c9c543e6f394372f63ac181b2219bf7eb451d397836377890 d48d51dea1dd8e90bb8c960e68afe72cabc5f6aa +7941b4226958aa66c7f44e76550ed49408f8665dd32d823fc6c59eaa16ab8f02 6e2b3105c1c5fa82ae4e33a8c24d50d69c50782c +795db8a78b6836d82b72af2904b6df4587356b800c06e74159d39b83f2ca76db ebe2b33d7c2366205d44620e57da3b0afde2524f +7989c0a71ef11028135720da34e14d29e795c8e78e450cd85a49d0bc7913a8b1 1a7bae4d0f9d9314b905109b5b321219b7e6be3b +79bc735b91f8dfa9379d1d6c21e2d519ac1bf0d04d48534864c9ff571df5297d e0637ddfbea67c8d7f557c709e095af8906e9176 +7a07cb0fae32a92c8faaa8f6aebee30b9e10b25e1d4f49726d28f223f4bd5255 c34eacedfaf132a6f506d2125588a9094eb82812 +7a135043f1708123027e529d2fc8102581d11dcc2dc51e32913014ead7132ddb 065d464e42f271efe08daf3f5b69dbe266266880 +7a1ae05e508530aed4f9bed33082bd4191feb167f2d351fa27180970fa375b09 b3be0fc7562d51df415c14abf671deb37d8f89ef +7a1d4d9971f84fc5afe7f66d5925e78e67a8ea58e5569a519c40f671f44d0b8f fc3ab91bb4df267ae9de1f7b4ab596406485cffe +7a5d59089d4a3dc1b9df7f4259be2ec0cf9f9819397b775d53a6592d62c471ff 4c65453197fd21460f0ddf2b722ec2fed6af221f +7a7d3b7a1c17103608948e990a24f4a67f6728b7c8a5dbb3f369e55972a55d4c 73c4dd9253f8d43c436fc88e64096910f4f1b8ee +7a907433347ef815623322907d4cc216939ba2c065306fa81b2fbbd49ac42789 4852017de90aab7f3f2af89ce8ce29fe8226af34 +7ac8c1830b6a44dfa1a41203f9368e79de094e5b62134bc3783069ca70080cd8 0acd9f8ba026c0e6845ca4cdb920cd27503752dc +7b0d0dfa23c212f76f2317ba39d30710ab0014c9ac4bad0f5ae54ece22cba249 04dfbd14950661cefcaae35a7ee273c8775d1bf2 +7b5110bda6ac70764dc1801b16e91b662364cbeb80884b3f6a0ab3e4c4028ba7 f7c4cf4a40255c3b0ca8d8981121e329478a92dd +7b5359b4e59ca2f60b3c528c5effabb858b81b1a9b5cbdbf70977379230f8f5e 2da4231d050f9761ba615b77fae3ad00a7d479a3 +7b761468247f90725be8d05d9744bea962f660617685c76bf7b2c58fb125dfd2 b55dd77ea78ddd49d9ee082e2785b3c1ad9de7d5 +7b9d5e67db60bd61f55eeed5ba200583330d36ff4ecb53a6c341c2a6eb3f681a 4063e6c4aa51155f18adbc2bcda3d0ee63880c2e +7b9d61429850c13d9e3f280d37d51b2e0e3873495d610f371ab45807cf0b3bd3 e8a952561c4e59ca308d1973de46fc32ad91a022 +7bcb94b54e168e6e26a3cb840139c11c0ae7634203e8a614c3caf1eafcef5fcd 792a1db7cf2e1cdddaee0446a8cab4bf0bf2f6fb +7bddd447d756b99d30b72a45986b2324e161a728cd3af6d44a089656395b60d0 d23f6863f89e23c82bf14b91de931cb446294af6 +7c0d59b28a150c519159ac4562b1a4dbc1348bc3caedf70af29584fe226988fc 431007a7fb7a938d3061cfbbb438ddc42ae95c8d +7c2ef1ea335fde84cc76d1a10e4d4012c1f5238e170499606509ad3c4530ab39 ae23486285f4e16896afd53b20dd11180d26381a +7c41edf4209d1b0b0e793a714e78107e7322d7f473fcef602d52aa5664bb5735 367ab010d69ec4fff533c770eba5b1882f79cb41 +7c50daf12d9002ad995f31e02d6dbf7851a586bba49118d87f69fa8ee71ff890 9e90ed0d151be3fe089cbabe216980e2f00ab242 +7c73285498380b4af00105c8d1f887472d50fe26cfd3acc2dab9725a716f3efa 6d091399f96a97ba9bf5f3d82b43179c82a93312 +7c79b5d0b74a35d095bd72c42c8855851df2a6f2b07e926259a819306ed7a657 a1a5bf7f5e8c069ac59fab62d2f7a9aedf198413 +7ca86b4b5f7fa4d66a6dd5715334af377f3627977caa9d68a3472a6e7c3433bc f971ce419affdcf8545527ed3e55e6310ab8731f +7d31d02993384dd0237937ba6ca89cc504fff2d366aba10bb81110a82f4de656 2cb057eeac11a10cd4864a66d8cf4b5210bbe308 +7d3909567be3abdfc8a087e14457d7238887602385f1700ef81ad5d65f47d130 47fd30c3508b95c97bc9177a59fd0e2d0a034277 +7d3b8d28c951267a7fa4c1c75dc171d8f0bb826b9b919bfbad2aa6d181a53a52 ac3e84651504dfc63c144d35e6802a111fc7d75e +7d41257ee4425e8a204c6d6a4ab54f46f3a370a01dcb5d5abcf54060f411c944 ab9e1881cb3b612e775f1ba27f22dad911fd0cc5 +7d991a5ee9468d8278d9d790567db72ca7c907b7397136758e18c267e39ead17 caa749d19261a3d284833bad6d8344bf33ba6e7a +7da15ff8e29e2eac38e6f4fac5611f4c4a396ac483e0be7cae06c9186d2808b7 f5e3dd78875f51a20d22e4bbe3adbbaa20974387 +7da21d9aac71e4797714dcc6ca3350b265f7e144e43715e9f39a401c4bc96816 767f20ca88fcb1617bedd49d413884875befc79b +7e4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f 8496071c1b46c854b31185ea97743be6a8774479 +7e6e4b147775fef8d337da248ecaedb10ed1f00cd308c85cd142055b5ed414e6 78abdb650f92cb34a78cd9ff17a013f383dac0f9 +7e9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c 258f0e2a959a364e40ed6603d5d44fbb24765b10 +7e9a1ff18073a5033098583da250301e2f813fb87a718e66b6c21e6b9d78b8c1 960678d19dd854cb3d5f1d234959a680bb8656ed +7f14ad6df05c4b6444d964a5b963c3c753e8ea8a6d4b2f51819a90b841f788cc 7dd8a9f71052cc12f8dd9c1eec2e99afbf08df76 +7f1c021c028dbba42ebf0688415150e8d9eebb1470b5b23ad41f9b4bc5149406 6267cbc52698d2158581d2d63a4667ada2d1df22 +7f1ff49eb6a35b7a50e8e1054e0690127b6fcb866cc2c32283a0b595c39a3a42 0e6e7fdcbf9cf558f09a1e2b1d9ae76b4209ccf0 +7f2d965918db64ad856f1b565281d55dc401d04493a57079b85724ebf834b6a7 b51eb250ed0cbda59d3108d04569fab9413909fd +7f2f7afccb317bb3fdd28555f126846dc4eebe5d9ae7b8d8a1456e8ff85422ce d76d8a6390d1cf32138d98a91b1eb7e0275a12f5 +7fa1afc6ba5045aa7a04b5c88edb03a13ad98702fe1a98f7effaf0a128a83ec9 8669fbeeb6d19f564281bd064a21a31206bd18d2 +7fc47fb594ba964d7397406b5a2ea930a40e870652c0ce3a30c16797d537bb79 eb9264e3ac14439f2036d9e47d31f6f94f073187 +807dc79b9af571de3b9c82133b77e38f81eb8ebc766bcaf2b2b2c7fd58feee7e d9903730e055e85e16e62356b11ba755093e72b0 +807faa3586b7f1aff2797a7c39c135b0196647c9ae7ee8843322accc960b2f22 6faad60901e36538634f0d8b8ff3f21f83503c71 +8091b686de8bf697ef632dda9b3179f2419717275e3bfd2055b303489dbbfa47 1b8cbad43e867676df601306689fe7c3def5e689 +80c5983f528a83fb288b30f073e52614f1530e85904c71974a5343fc4b16a40a 8030223178f0bc0ba83a453e0b7d1585de4adc9d +80ec1e36b983e68664e8357c538cd35b30638bb0cb99626f906d145e2d2e2558 fc3c3a2083e9f6f89e6bd53e9420e70d1e357c9b +80f961fefc9978d410b73aecb091bc67e255e94f7e7c601f8a6addad6866b290 13de54b9267a98310d25bc6cb06caf569b2bc26d +80fe2c1b41caeccb5aef5b42580afcb065b928c4f95a1094d46b9ca1e3702428 f3d6802e942425bc120b6971e3bd8545059eb063 +81274306bb28af9ffe7c212be4e48209b9e13f2c82ab3821bd402b175a10851a 72b9511ad87afa7591ea80101da12f8c17bef161 +812be9eef0a2edf14229e1d392ca8d4957eaa2e45c8d3949b90dc3aea7950648 0fcfda73c48cc5cbdc6b0a328b9fdd7f2ed43837 +814681285f754a19aa21096d18507b24910a4eccca24cfc30918e830f6de83f6 83131fe4c37bbc26706764ee63732315906beb89 +8155958bbda08eed88c8ac908dc44452ed38911cffa54ccc06076f30a1ffb1bf f60079018b664e4e79329a7ef9559c8d9e0378d1 +81a810b2374dd6ddede7ca2b3daf3cf470b54b7e9290d280c6d7e4e92f076247 3e9360aff20efa836e3592d1c017edaf881b936d +81b4939f4b31e1107a389c4964f1a987098da179f9c574cf7e5449a4e50f537c d3231f4399eb8a1887fb901798d5ba4abc02df4d +81ba4e67aa59ccb078e3dc9ff3075f50084ec1696bd867e8e4284fcfc34fe3cf 2ef4faa0f82efa00eeac6cae9e8b2abccc8566ee +81c13819eb19176803f7052925538cd08f8d0e3036dcb6caba2e27f862dabe9d 0c68adb5d31b9605bd13b92ac56a3ca6c76e8ee7 +822761cc7e699abcf0acadbf1c7717045de822f5373bb9bcec7c56cfe35e2c60 b9de2100b3991e6ed3252d4b240eb84b87e0aa0a +82462a4bf3fd4d0de14197547370d4708ffeb5c3bd75236ef485d67fcf04ef6a f34fd002b826e94e60ed67347ffbedd93793fb0c +8259bf4baab8beb19e2e194f36fb573863dab4cf1898f9c3be52efb0620137fd b433410c8d0c3f8345621e9b7f1603656972c5cc +825ca0a496df92f4a70387d4e605a509d083b4f990830ababe6062e70592373d 68b3dbceec6248d67e7bd8b3065fd8d5ff417c22 +826057e0780d68f304f3a202e8444b7283d92c1394c75866249e67f312fbe4ef 0a6dcaf4b0c72cb913e860eb634535704233ba66 +8265075eadf051a5d5ba5eb3d620ad0a859693239c82128869450a057638ae9e 49306beee0b66c81741bee1e004706b7e5c9921b +827595cea6baed7b3a76e99c658c2efe68f41efd0b9c71e76c39849e0f11f4c4 06d6e03af9a9bcfd3c97687f1afde4516bdeec57 +827fdde8c1c0714a38efeaa0325f252745eedaaa32837a45bf68e0d58354571c 195d1f67da1749b0a8d39adc087c5cc196238b59 +829deacf4c5316eb399ee7ed590a2d51192977c8ff901b8143384dc649be1e22 56931d1ab4de660b27c56ce488245e78f8c713c3 +833ecfb032c26b1a04cd46875e99367a6162e1720c81ee410ce504d7f6aa7eed 255a0dabb28beb56096af18dc20187deca4a545c +835a70ea81f05845bae544217f94ccb867c80850d363f7a237df4e3242bc96c9 4a65039859952e2ffe776d2e1afbe0c7ea02d14c +836c31befd72ff44f39e142cad83023dfcd5d58816cee2aca9740b4566150c46 ca52813342a2d2cc406c3173f6b6d65a752fb783 +838057f426dadbb890b29a79f70d68f566b8c1d19d1d954036e0d95a681d2a28 8c57e3ed53bfd1cb5f5d907a171a86140be37e06 +83b17ae4b422cbf5002107b436fb2e5ca0fa6c3d0ec1838ddeca4362bec37624 25263fda46ebc849617f65306ce5c626f5575340 +83b6edb74bd99f1b5042e0ecc2403959f19e61674663c245e76084ace8db9a9b 640fbbfd48119f1c4217e81c644b28e31362e269 +83b9e2440ea84e5e467bcf921ebd6fda90c09a1d781e8976b1550e3b0302969a 0dac883d02d2cd142a85f44f642afad79853e982 +84005e38a65f4115d5c94790010dc57e1a3297f4aa89744f5927f208af758bd0 10aa3fa72afab7ee31e116ae06442fe0f7b79df2 +84503271d3c088446000ba59060bdd4ac4119f6a7cc021cbaae918e86f220c17 9979e66b2e87b085ea23db0b4839b52e09639d55 +848b4725950462479038f34889436ddad0d3842332417707821571cb0627a31b 760529d873e72200ecd6cf05179fa0573ce77fe1 +848e2e89858bf76ddc3208ce52d8a6633d0b9c86e74b9d447fa0aff3911666d0 5a6fa6e57189ab9577c62bcb4fefa2b80cc16730 +84cfd6056673cb703ce9fc11316be0f4e9bd58b0c69af563ec8fd8b36a105847 37e46f4918c08ff7af7145dc364481a316b34c44 +850a21529a706f522be5a979f4b2dc685755658d9bb42c9c929673a99cbb09ca 6baba0d067c8ce4f23a148cb1b7a256e6fbd76b3 +85513265e2670ddd1ee23214af654254e62ac330883340cc73f1329014e53c48 b7c891c629d298f2d82310d8ced2ee2e48084213 +8553f7236da96bd44bc84c4281b4b3c059510a908ea6a4bdb06ee01bfe39a4c4 d7f80d5fb81b7f1ca8443a2298bdc07eb3a144d6 +8559f031d21ea80c51c33842e3df2b69a4eadd01786fbb119b321d457b40fb6d 4e7596567f9f86fead9cd398d3e3b056a2867fdb +855d77c6095e55b8d217076bf2f6bdaaf5852cc4b7c189660454cb311c6e3be3 f101cec32d82654159d264d177ed9ae0de42b6d7 +85b09a51ca2cbbbb68cffe492c905d4b20e8c3a4f988f5baf7f5032e2683e84a b3039beea6d0637e131b24e8b6d61005cc1f6515 +85c1508d2b524c0ca3001873e6f0f8d2f291be2dad43d12375faf37ce19f72e8 de2220a48fcf7901432f8093d3223e65f4072ff7 +85c2f81898a85957ef3bf6783a34d161a568cffde615c3248f7ccd0021b36060 18a7f61c29ea8c5c9a48e3b30bead7f058d06293 +85cb06c94423ebb8ec44805dcfb22cd56419a0d502cd6a7eb3d1e157fa0b0c27 cbf7144d8b79387bb97f115273515b9453e94b2f +85e0897e275601bcfa3d653d02bf6b49530d66d5ba2fa2977a6638059cff46d4 4195117c1ca5211c7c74366ba45b4dd2c8f4ec0f +8618d77ee61738f3e6679b5892a7ce404bd0177775fa65d647892fd42dd4736d 56933fffb8f2329d098fb9ad6085045a14c9ec2c +865c71423b25f5c4d2565a0189352e4162207a3e3d711488eb05f6e6553dc664 7f09466ca8150a7bb9eb9728841b0099b340dccc +867eab070ee07beadb0193608a62052fde41f94ee70c3574395679fc30c6f021 186a08f4b6d4a08b1692365cfef59755bd8c0485 +8699dbbfab2d020fa8fee7abebf6ce00fada2ca40eb0d09de73224daab3838ee e8cc449fe9c99f9ca35088e3247ab9c0088081fe +86e228d9904af64586e9a8378005ba654681ff5be3c43ca930bf6b1f28d4395f 6336846bd5c88d32f93ae57d846683e61ab5c530 +86f7012dddeb074d3313851cb47e7e6d2a89fed1d23b030131b7b9796c63beb7 71d0a2d522bfbc72bd8f518ba69939b2123e3f68 +86fb56a271a202b67ce596425048d4a2d0b5d0f320a196d3a4c9b6ceddedf1ce 5001298e0c09ad9c34e4249bc5801c75e9754fa5 +8727c60dc37a204127adefe3fd92fb6e1740225f3a1d8985ce29458c322ff169 ce4be0c1c06f8e311b68f70c89165cff698d5c85 +87ca3d9823e24cb99700ca833ba7aaec3cb8d34eca32240779930de95fe7caff 27aa76e5ab35a01a87512c5f6b215d1c9c736bb0 +8810957e46cdfefbea090991ec0a237fe844f0b4cad1eefa99f05f3c421f3e3b d76261e048bdda2e6d2211aa308e675507bb434a +882f8c78660ab5efc60c2259e8fdc601ccf75c16c5f787e689314197cd345793 796cf9d0a06b922a730ec98984876982c2d5bfd5 +884130c0e07babd81f0679e1ab15f50bcde3fd076fd540f42c5f7a450647c811 f5be02b49b5b3ae73bcebdc94a84893a012bf46c +8864b5746d7c5780083bb98449a3f5bf78d8281e8c5e3fd12a8ccd9103eb3a1f fd0ec0333948dfe23265ac46be0205a436a8c3a5 +886637d6a4c8dc11525f740a2e3723692bc67f42753e7b7a75d4e9207027989f b2899380601ba014c011ae9cf00a675689764856 +8873ad21d0ff871dcd53e0b2c4446a7da3439519da9c7ee509bf9a09cbe20e16 e2fd2428625c296a3dcca706dab7873368ae2818 +88a6c8138e7dcc2f7dc252dedf17830b3bdcd67021df4538c05dae27293ee343 ca65c747f8fd5110284f21d624fddbf87f41f836 +88ea074339e8277b932e8ccd92bb661e1b3dba88d86d31e5029577b41da793d5 5e15176dac8b0baa6f7950f5f763608c83b29093 +88eee581883a2bf8cc278592faec71b3964b36033590ea99e211b298101d2357 c47baa4adba9bc69f3e4a78e4b6e295b1f20634a +88fecc715077807bd7ee9e6a1dff65fde000379e0bf2a15cc2404469cef4c82b 5018a35e0b7e2eec7ce5050baf9c7343f3f74164 +890bb959ac8c20db603bf083bc82f55f9f42b6dca6581d941d0b361188abae3b e032d863f512c47b479bd984f8b6c8061f66b7d4 +89117557e4b6f0e5fde4d058c32582418fda69ed651551c0d682b0bd9a4f5343 ff87c872c7846c5725ec711b6d2005febab7d6be +894fb499b85985208f105302d3cf3d9c5ef13b6e4c51ba4233656ca6aea65cf5 8add01539268300564482f854412cfe9839e980c +897f5f26ba1178254ba5170ce0cb7a23a83dcfb1e528c87ad3e26b89c62cf9bd 44debd80c4908e439d4a505ad7f6de367635b0eb +89cdf765f35bfe191fc18ece04228795566f412e0cd6fadb80102a8268c99085 605324ba5ba92d44cff024069be576817e41c7de +89dc8ca30d409ad37a872d80b88661ef50d6c226a37989cd089e60c183609b35 6ca549d9a75628c1703ed6d2d06877351a268a88 +89f819abdee0700e804a202787da29c6c14aae8dd6d3b8cc04c50cef70c44756 802589d07105d24bc88c908c9e62edb3fd679895 +8a0042d434a2f8a2e8d47caa4eb454f388752fb3fe71150c1cea12e807cfdf1c dec93efc79e60f2680de3e666755d335967eec30 +8a1e1a6cc00519c4df8ce404c987751446fa299662308622cb63576fb52996d7 6f2cd729ae42988c1dd43588d3a6661ba48ad7a0 +8a5dc4432a7a135282a588c0249f63895f8cdd2d68906a2e94924ca31c89189d eb9ac8b7622f3c144605733fb4321b7cad8c41f9 +8a5df168a787cdbb8fc1560d31fa917061c53071da52148a8b63ebb16335091d 16d9bd5394fcbe6cc3dd5cae5124bd8400343e10 +8a9b600a21987e6ffddbad745f38c115797eacef9117043bd9d2da4835ee3cc4 0fd7e4bfba5b3a82be88d1057757ca8b2c5e6d26 +8ab64652ae74f7d9866fcecf118eb9994a0083fa7e52a4bc49a29e9887a2dd7f 9bdf5bf0bb8dfa08e11ea902931100c4605d1d09 +8acc0ab2fd3b22535ef3f534e51a9843fc8aa927903e1157434c9d72b1ff1503 257bd746cf4b3f1e048a640202493c3e3bcdc1ae +8b399358a71ffc0adbbe2d3160310e19b5a08da84565b0b74f4ed24d572bd476 ba0bfb30cd31b4d6bcebfe9ff614794e4f5881b9 +8b592ef1a82b329ecbc463843ddb64c4378787bb632999461b743c8c41ec0d82 2683a6b43d73cae20a876eaf50b541b8efc18e82 +8b9acba2b7d2bbc3947820166fd0b7fe6e274a1387957cd6488730d243d762da ce02d0ef9ea8dc621df41d3f244858bd60f41d48 +8be5e8938f38464bf1cc68f917fc9538d598bd4e35462ce33777a65bfb98b504 e6e4e04556df092bce179810d25ce5330b34d8d5 +8bfa45d9dceeee0ca326757a5c7feb70c3bbec6084ffba27c44acf73270484c1 80829e69e76fcaf3eecf606794fb5beaf3561f17 +8c0f41bacf4388237e1e53c564f3a0fbd1dad819856035fd41370942389cfac0 01b905d074ca43b1b84f507264d3175e5659acb7 +8c1b59c9c4abecdd5107384206e51fff8ebb6eda359c6acc8ae9d608f777920b 3b104436374b9785fcca167c9d3bf1e751c1e56f +8c241c359f8adedf6fd0497cc05f5821570e05b6d92537d7ad8d5d12fdccebc2 bca03c4e40b1f29603ff131d702c46591b8ff960 +8c53c0f9f0972a1c77e40549170b9ae51d365c200ffc4cb220628c5bce3dd0b2 d047b47aadf88501238f36f5c17dd0a50dc62087 +8c834cad51361fc89cee23325b17dfe11294cf9241f2b7579671e6b2cd45d79f 8844825b8244e0cc54c96d73469a956b507bbf8c +8cb46741dbbee92758abcded6eca24fc2b74f43720c8525633a80410ff0e2b7e 331578fb1d3ff7013b312914fa9c78ee61d8ce52 +8ce4527e0214a617e08c7aca94006d77ae31755ddb5f757f635c0bafb35f2afd e8919be6068c14b5c99b828af9fb84769ce1d79e +8cf83523411bffb0b74c7f8b5f9576b23ca2b2578f93ab8ba59bc6948efc74fb 974a17ff165327d457cb3d50324e122542255524 +8d4fd5f56519059e2797709107cd15d1e62715e26d83a27eec8160fcaa57a7e4 06a1c29eb70dd0b5f43cb58052641dfc6e8dd2fa +8d79784fd1848028802597c0ca8342ae4b7c7eecdcec8a9f806f537798f74110 b5e02a3b76c602bcd6230826850672f828e67d23 +8d9d39e6fd6ad7cdda32b877633dc72a1fb7f4da9ef96aba6c284ee9f32ed71c d44cfd460e2c52d67dea14838b03219967cf9cb9 +8da4f409d9349020273e9fd00dc8f7a0aaa655b5351af47c9a220ee542a31ae9 7b2a0b6f0d56e59dacf05a30087b58c1ff93cad6 +8db27a1dfe3454a225917713da4b0ae56c617de64876e549541e8ab991f2d1a4 5ad63728f19e4c606be7455a8a7101c9255b2b21 +8de876e616d65723cf46a842086e338036f8d9ad6c6969ba07ce2abdb27271a6 0f6d2f4cd1a1391a98c6a89e2ee6a69c0afc0c9d +8dfd647590d3913a31bb0d11ccea6df7655b8c1c2898c49e85550fd5491cc3b1 07b63040096107db747c6c80b02b0619e6983ec5 +8e3ce5aaff68c05137728f5845d627de532b2294b665c3d05eddf79972439909 b27d0a8066fd0fbddfcf8a30b4e77760147b0817 +8e50c5148de4a8ba9fc799c67dc669d6f82cf0a10ee9f03bfa2b5a3641762466 d1621f2e35b6db0d27cee8c6b1b03c2199d7dbd1 +8e579056a6928f364735405ea0694fd779e51a2a873fa32f3c1eee7db4ac186d 45bb348411937ed17e912aad83a14c151f922215 +8e61988c998c96a131cfe72225fce43e555bec4e590fa8c239373172a9d485ce 11261fbff21758444d426356ff6327ee01e90752 +8e67ab781e8d1b4f8c35972ebc695bd8645f51718ebc866be72ca3863956c3f8 b864df623e3b89ad678a888dce4b0d4c997f1ac3 +8e786640865ee4f226cc81422201c22dc4229852bcc61018a2ab7c73cc9c8e7c c3b53a3518b99b4ce31aa3f6e51dfecc5bc772e2 +8e7caefe45c938cd9dbba149c377c1cb4392457f11b1d8a258f464a24752a40c cc57d8aaae44f013e3caeefbf401ae96d858ec9b +8ee7bff2cdb8e47b525d8d472fef394ba46ead8db298dd5bc09c89313bc4b1a5 e313f6d036812969a06ace47a56c539fbdf8e82f +8eff6924525965875cdc054d19a4b3a00c5e7c57538ef7ede78461bb6829c818 5ddbd5edf83e034a62499a1a9c94645d7b9030dc +8f74f6adef88cea42ab0854fae5f0e3f195edf161acfb12d5f65a5e9717f0cac e235f89f548d732c4e05baa54b8b3dbbaf5537e0 +8f9cede149494538f4de2ae7c79d5abf3e8309762ed0846a9d2839031cb7b456 7dd46af6b7c177bb38c3875ee6d79a78dc1076a9 +8ff22fe4eca09e4f6bec8e71cdda911f37d836532f568bffa0da55d90822320b 6bb7aa1318f7f31a346c71ef81d2b33c6fd41600 +90115fca32a9cd85c4c9c0ad4c23e1d2772e2e91a105d3ac3c92b76f895f8b92 5c0b005b617d01c512a8dbf9e9dc1aded652f244 +901505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724 c47800c7266a2be04c571c04d5a6614691ea99bd +902a50088f5721565b264e26665043ef0de4e9d2841414ede099b24b3793b1b2 e2401d52544ebf052730a63e8acfe4ccfb8fa0d0 +90445c1dd3c04315c85766841ef3f61e3bd6cf0685e03382dba4376426d365fc b6c9e4203b69a576ac5c462241a7e2f676cf551e +906ddb4156860b47274ea8ecdac5f374ee64306d9b9f03c34bfc9df5afdeb7b4 74eff33f8be680e821d9674ab12da3c0b76dad23 +908288eddd0888db0d536666139e193ae7e2867d214033fae2557701a5d8a9f9 80133dad8aa9b6d8bec7050a77d603c53ecd8512 +90884a4cca02648ee29efaeb429c0af915e0d2eff2d9aa281aa8dfb2d86f415b 470704779e16fdd3872fd111089ded59aa12a576 +909cfa7665c9be2db4a58346155ac887ba00f6079b76f71b4f7d4ed9939cf0b9 eb942285420814778642962e7b4cc95636bce88f +90d744132ccf86487aaf3f51ebc92ca3315c59c48e12b4d576e253be91a0c460 50acd0c53572be81b5c54379b2442b19ee240953 +9107412362d36b5e2350e5b3e3b6950ed4519963bc036b84509f452bfb73e580 a24f1bb1a8d16429bb17455a5f332eec88e777f8 +9112f74e9c8db2d4574bb1d568099294486e293e7625b3d6baa257970ac1f81a 1765d783910805c69509416785b2b780d9b36866 +912aafce2240cef7a68ef8c0e53e10b0d7450818c471f5b09f8f718458ef9c4e 1e564f1ef0e3a4a5fe861b46024daf93bde4834d +9197064ab68802fb19dde16fbffd2a40053b37850ea393274d10b3cf5fbfbaf4 d337abf625b6912fb8501993b7fb2b472d5ebcd3 +91c9d8df151e1161258091beb0822444b9af63bdebb8aec39417153503c0b669 f528160ee7dcb57d2c56341befcc00aa80393b5d +92128dfb792caea934f5218807ec993867b0c8487a3de69ebac33e067f64d38a 700f55d91d7b55665594676a4bada1f1457a0598 +921be768179935cee45bce4a0b2d27ebdcf05ed06c7d10fff2d9cf900298024c 78f9923320d8c6ff69015ffa8c0410f6599a3499 +924786a7e975fc305e917d97cda11a71eafae57d7b3c916cd84536a0c2487a76 685145309c964db9a49d6b7ef96140db6830c131 +929ca73a254eb7b0dee0a7476e09ba2a53c0b6975473c9d7574afc0228c34e73 c22bd2f5514799cef5657d67dec855c6b32a7eb7 +92a2ce8258f27dd45ea652d5336dc3685dd3d60627011f6215e923318f7325af 15c883bae698321aba3b162df438254512b316eb +92e6474ccbb1fde2326731bf69e57142cb428ea3e916b71b2980e4348ede2a15 0e3ce9a193137134d355caabb98126cab35ac236 +92eb919acbba3d8094a46d43a4fcfaeb6243859c3c15b2897e0f7f50be66af37 4e67695cb9de10a3dd32f78cfa01eed91ebf155d +92ee0adc53ff899c85d7f42cece06277f063d89e0b496e735fb68b25d02e2b93 c1044cde11b3aaad524cb3ea49d75227ed7e5f48 +92f0bd5f6ea173aba9d25ffe352a2a7246dc32e30f0aa2905cf566464db2182b c117a5386c76f01b4c444f608f4cddae9d1d2233 +93010571894137ed6a1d91c6caaa46adf9437aa37e672ec4ea55ae0fc434d601 f6baaae117d058baa0865206b834cd8e32fa4e6e +930d5c174308a2d7109171b86756093b656cf7f129524b1fd121f66a840f63b5 2351b9dd3b52f5421a6dd9e2effb503497b1c447 +931093620e5f050e2127fb0b96786ebaa9ee6535fb698ec01b5f7a800fa27cbe 1a443023183e3f2bfbef8ac923cd81c1018a18fd +9315ebbeb52ce6c79a2998a3bd9486831c6d2b2b3925326a3308aff6e5d879e7 524570f2c3b95b5a9f1c8df74893451e04f1017a +9391424f1c846bd6e8aa6a2ab9d1c9896f415068f516217c20484705763082d2 04c9c16e55c53fc12c2eed43e3d7e42f78fe7005 +939db02ed91a33c32932ca7c00f3c5f18bca0c2d348c06efe566f70bd3b2d18d 96f702218fe6bccb09568c2ec24cc186ace8c575 +93b0ac3a6caa8be7b891ec756913b8f3f3071a20ac1de863b16c8037e2f4c21a 84a13dc9b196c2b0db6b0f664e4ab2bf9d6ce9ac +93c3547a4e0bdce7c7dc444b4cc7204be32bea44e740daf0a41cd32a069fbabf 948171a894ff8a25635232606c5afa7bc3224e38 +9454571eb895fa350d59a9fec2b5b2ca135e1938dace531702ca77bc342424d5 4633dd81e3462424635f7d231a2373971ce736d4 +94a67a2dfa7ceb5a3416e25db885e3bfceba064499fc4b817d7945cd271b1b75 1801cf406d4b17f019a789ffeb9346c1cdb29491 +94db24aca3f8e07f481744f62633730feb4fc47605280381a08be510ae971ffe 91822c50ebe4f9bf5bbb8308ecf9f6557062775c +94ed253efa9e86fc636805c294c441d08b89b455903c0c14e9b16587fec081f5 43e968a905a821532069bb413801d35b200631cf +94f81211f6452f3b46656d5ffe4ced3377ddab58a2e9063a6a4b15cba49cc08f cf16919dcf33635a64999185522263bee1428e26 +9558e844b96edf250889c0660f121678993205d280a3e488cec0605b979f370a 7b6c856c6e276e9a3689914797d2598a37f4e1f9 +9565fe314fe3bf77a01611ffc14a48246459ad6a19ea01d4082522622fbd001c 1cd20d3af067093c571be764bef038be1a39b502 +957a8f955cfabb1507ba2fb3bd3a859a9f74b4707424dac86ac543db0d69c667 09845e76453ec9527128f73fd5ac7114e9158432 +959c738f7d2c764ebe052a66001643400118c7851aba3b836377623501a14222 1b9e92c73bd223661fe63dffa0c9138da4e1ea80 +95a2dc8f7a4fd47c27fc95dc9d90923d11032f4f436c410c60beb6f4d5b6984b e2337f39c3a4ab046b7a572fce89df27451fd2bb +95bb14319c7449829409166e9e1d1a750938bc52ba7523d752150636556803cb 5440906feb5e41241a699ee413475b7911284c41 +95c49d1abdc7b172424e8d7db1f142590e40c04a882441728b1e9b2268848cf8 8953b6cefc86a339924d5d0ceb7fd523a149fddc +963954464526b415ff976b8a59efb85e521ba5239faac654bed92979fa976f92 14dc301e7f99cd5f88098e32366951f803e9832b +9670e7ceb635f35942559001b8c628211efb5bc4f57380909523fd828c706f93 73bb602a74eda507838372d66093c8e85246159d +969287ad751eaab33d0109bb39436e2babafaecd5ef8405734713431c64250cb 468b12adb15f5546e7c5536f72204605fbd83f6d +9699ea5560b7b35db0fdcd4c7d50269402d5624d72cb1a8646b33b65dd416c07 42608eb49e4be32002462b51e86065368b342c05 +96c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045 45b983be36b73c0788dc9cbcb76cbb80fc7bb057 +975c9b644598f22c51616e9c8afdadff852639aad6c08b6c7bae1a34e2d812ee e34dee0c7f0ac8abf228369e1016eb6016c40758 +97a021b36da9610ff840213c9277124fe653804e85f2081f6dedba953b5706df 8a7d625f53726fc7703dc0a413686d1ec15488e0 +97b0e3661fa0caa6200c50381233f8320b907540ceb9d17ac94fedc66fd093c2 cf6cccf1297284833a9a03138a1f5738fa1c6c94 +97ba6db30e1bcc6f9e5916915fea9dc0e205599c97586f7846b9a456e5e9b6b5 b47ba8387c1d7209d4976af9c3928f6ce93ed545 +97e00ba4c8028ea91e7131c8c7596227cd03d3d2d14ed2d179fe0f305ebbca39 6f6a17db05a83620cef4572761831c20a70ba9b9 +97f425b009376be705660ee81095773e10fc99bee7ad513046fa0108bdec5d22 1594f6c292f52e077142ffc0f35b544e2c5dc108 +9832b7f86876eb62f53f0fc649fbcae18dd43f2ea05ac7302239f7f91848d554 db01475e9527b251dc429b03a96ecfdb2928aa1d +98488e25f5011c5d690f33397fd78528c38921473a0a4bb4b3a4d70b0f5a8d84 8a8cd3e5f3aebdbf53e677b176e3a434231a26f5 +984ef110535289c1e7afa6a33058e1ced09d7e0da058e419d8ddf8e8ca060b24 07f4f5f82f7825893865e8404eb2bfcf4a2e6932 +9862a961bf9c8b544304d2dc638c340b9c9aa6168ab4eb93e309326adb2f4499 657cec1480bf4d4bc58119da119f12a5b0545b8a +987d206013d0289d45845304b00c8f9adfa2255ccbb3545db0157f79c7cb8ce5 655d381a1948783d7d26ff9ec5ef54ed6bbefb29 +987db14ff31dd4b8eb26b09aa1abcfaab773ba9c27b6040d47033678e7cfd0e7 121c862c3a2bc66c12b64903e508d8bb8d2904c3 +988a04ad188e91c521853685421f1b7d8b0a00767a6fee4c58a98bd1d449c3fb 710a14e927dc232d94caf0fe87a42f0ffffe4b79 +98ab296fe0c2cb1cf8c37553f659614482e37c84f0b50fbb63e9eb33cdf1f85a 94b6d09da1161aceeaa537f55802d794cf021627 +98bdca06d862a2b96154af5b34e7567f60421b307d1b067a0218d6f3532eea0e 6cdf0445fc0de286fdb7a343a5dfcd1937666132 +98ea93628189727f2a49d3fc8800e24062ee013433b4914e2f8a6406be1b733c 944a1233c2873c5e21f941460c3940f8a563bdf6 +991cd4eb50d0bd943326a09398c691e3715278549d2e62720c1a90495996fb85 51eb2f90a1469542799823e0f3249520bf8f9cf3 +9932236d190601ab8f93e8ce8ce0fa59dba7f393d917390c1d53b34ef05b41dd 5614dc18f626902796fee9c87f1f703694e59295 +993f6b6de11fdbd469b7f1fab0d06ae9c97e47b4b15fea92903cc8a5f1dd981a 5b9c679bf878b9eec21b022f662da96601343fdf +99556a8adf928dd8b307733e4ecb5a8ef495b490011158d71960ee1b0a0e0718 e3fe32b6a4ecb340fa7c9802285711cc52b8a101 +995d546eb0fbe7f94b918920c68b83f8f92a2c433e48fb458374d347982cdc33 35df53402ea1bd8079cab9bdbd887c79319baff6 +995f00a0542d56fea94d5ec6bd4912016c7bfcd9f0609103766bb8c8a0d1fd50 daee7af942c5f7d25a00e3ec4d25eb587c61ac94 +99676bb02a367fe46c82e0d17e7c0f3e85a6c39c3a7f51233c85bfc73a20401d 8c870fcebb8f625a8e172a49a44153af8f37c8b7 +9969afffd2839b9abfae52451171231fa3d701bd6b3d0be45c2381cc993278cf c7cc7cc6e3a1480a18fafcd08574a337648f68f8 +99836286369db283d666122f999d6bae55d099525ecfa19678919c75de3ef920 558d6aece77743bc4be56a0a62ca9838131d217f +998a830952adcca170929a187cdf235190873515a9d8577dc7610373720be0dc 05504cd34d6ed35cbc7c99708d226758a1cf8b57 +998da46ec222491d7911e92bdd6711408dae8e47f65007634cf0286f78d042f4 0daa6cdcad92746551f78edfa93be0bc188d4f4a +9998dfc144a21e3e124bef5dc0f9a60e0feb92d600ac1e0fa77a329b69f0d256 4319860c60d84ec2513be34ee1a65dec5eceb6c4 +99add46dddd278865eff998db5c7e17e806ab0696d7cc8514ca638873fd4c6be 3fdbbc28dc8eef8c0bc32fa6940a9468307f2dc9 +99e8fd0d2d4d41d946a1bd932c4dc4c600ec25ec1ca10d3dbcbac0542c85f01d 84675cbcb6c1213c1e662afac915c861999fad7c +99f3b405443221141eb0fd1e0cca5d355f893983749b7fb455769fba434e7945 0266163a49e280c4f5ed1e08facd36a2bd716bcf +99f81d6e58403d464cb05e5425c6db0219ac6a03dd4b12d8db19f8ad7894eadb d435e706e0daa21e06d16f00d0605d598fc21b38 +9a2db41cb4dfbd68346f5aae4ef7a75a2756853da451e62b52999bd40712fa08 f57d4aab1617ffb4b923ba461db47073df131478 +9a3436818efa95ceed279b481957c3881d4387824b3ac7d6a4e5c4cf57d1339f 9a2edb897c93a7d1bfc28aaaab1b9634dd684ff9 +9abb7801e72353060a1490ffa3331674d6882558e7d6458de397b4b00d31ff8c 50330c02bd4fd95c9db1fcf2f97f4218e42b7226 +9ac1548112cb48d9fbe867db82c9a73eb6557f77d941f8fce68ff14d276d3e87 2e6776cae6c71d745beb9e102a4a18429f3c4cb6 +9ac284bc88212994d14f96edf294d17a7a4796193e3138f84f9e60eb1294cca2 dbafe4e0c5def65ebcbf13789a84e996cdf8aa25 +9ac9dca313f55249c085fcaff33f161594a35636e465e10368e4ab9e7b977b87 91f3b517f65492e5225d853826e3837d404c6ca4 +9b66436f2b9350f6d20d44171b51b0dc1b8859499ceb556249635125a86578d0 f2e2e7e17751b629cc3c13b0a7d0cab059cd1df4 +9b91942519f6ea0ba7841d14598c082615cbf66066995e85859813d3eedb43d1 62ee0b89958479ae19eb68f18bb23de238a6a2f0 +9bd609366724dee19ab10d3f795965cbedf785ce8572f56dcddd42f48454ac91 16b9dc5202084b857d167c8af606422df771b8ab +9c0f06e8344fe1af55ae50f4be06d090a443c24455252539371c9aab581699cc 1213764b28dcd72fce1be359db724fef38265ad5 +9c486e5fab95b06f1208424d889811dfaa8d498ba8a64af6faf2eab78e005baf d4594747a02e72be9399b3a54018676e0799be22 +9c4b7bf896a50fcad21d07e1a9c21786876c78350dffa6bf7309ceee396942a3 3d3552e8fd709214634435ee188e6fb4930e7b39 +9c627d7d80e595ff49c610c35fdf73d3e04757b12c75b724270cc51b1778f781 8c08cf624be754f3ba55777638f6cf54007dc516 +9c67024053a5a19e5c5f27bbd02419c09194a13d4cdce62d5873b9d6289a5e55 e5ff267406ab4a456be0b46e38fadf8fe822e13b +9c72eecdf03014bab3444907a5c9b1abacf3068537c5c7651f9492326205cb5c ca88eb6224eb55fefdf6ed43963b5d34c3558d03 +9c7b63b5572f9c6e49382f5ccb71e77747a7fb3723d13dc91f62fe6e3bbaa5ba b3099082e5108151c0cc93ec6bbf8b7d6a28c4fb +9c84470c221d8722ddc005be2e4e9f662bd4ddedbdbcdf0999328b84b7aa14ed 20bb1bec4aac38a69c0a6fcc62919a472e33a136 +9c9d32c9c37ea95e0e7a22420e60f28c4ec7d16076fbdbeb9e16485fd7323975 3aeb4c1456431b730322d7a7d88f5a2ff60299ad +9ca320473e41a10134f39c3d2361a6bc82c68bf5328ece73146233bddb2bbecf 485eaa8bb045019e615302bafa10302f86a01553 +9ccfa556cd7f73b426a7bedb7fc3a850e94f8c5ac1d71b9afa365a89005aff54 db793a00a5615eca1aac97e42b3a68b1acfa8bfd +9cfa4ebe14380d4922f098d03f17be12019fa52ed3e602b9925ff90f55c5a314 c94eb4aa7665d04c8412a72f3554401d2e89e03c +9d59e5d73d2bf09ae93c4a9d7afd5d4b7e983a20fbffbdf97ad78eb4d949f3c5 1554ef7957dd89f4d94f00dfbcf7989d3a409306 +9da6f7f0cc86c1260d40470e395f1e86aad5ab8e27d29bea60cec1d35c017b64 b946f1a6f5fd8b14500fe986d18d1d008cd00f18 +9daab17c25f647d652c72c8cc3cf4602c270a369beebc7d0b67238897bbc426b ae90f12eea699729ed24555e40b9fd669da12a12 +9db520a5a88c7d75b86a4faa1ce9010edf38af922f400c69a4a93aef69e25c4c 32581d0093429770d044a60eb0e9cc0462bedb13 +9dd0778372a1290bbaef59f6c04ad41776480beb00e529f6534550ccdec849bd aeb13951b1f97e44e4274201683e090cd7644046 +9e0c79efc2ddaf22864c1fde0db347832bc1a4e132fae802e95b6b0c5853c2cb 8399c6e8a4fce8eb0448429ad557eae77d2d78ab +9e2bad8166fbe654963d974e55a2f08da38a9e3d1db676a5a2c14ac3714b01d5 ee0be6f12e1e53703f2320e67389b85643305422 +9e77f6b7446eb082bcc2cccff17f7b83c69fbcb03117ea110012cb8bbfc37515 93407057f0107cacd2ec2fd97f940ed842caba94 +9e9f5a22329db59e32b25d8bded97e479d9fd505c6b61a52e6fd3113af5e88cd ed6e85be8971b49a86ef11745b5a27bfa4f4cb2f +9eb5ebb282122e48e20e161b3715061c4381c5e6a24512b0109c195d2f2ba73d 154eeb7642b8abbce7954016ba549e399ffeea24 +9ebb9db12bca36755036e35e51abff05eecdd42a23c620a2a372d7cfa55fee52 213e720ca80207fe1bb48ad95e2c422e17281a9a +9ec93e31545ade8f8122f1ba534269ea72fd84f1ce0b71c51f40f74b23cae7b3 a399208309046656ecc01f7653c5d5b8905fc16e +9eee45dbee480fe07ecda366c972abae9c1a4f322f4e2a00143638b6b877fe10 8929a79acf82b9414cc37bb9f464c9d0bcb384d8 +9f063b7c95baf5482f968577719e51ae9feafc11121b1d1c2b1460ed47ac713f a870abdd5f33c3cdd75c9c6229207346d1a3e5a7 +9f61cea7f0b3e6e51d51b3c63cdf7a875704cf5d8bdc20d1b4a05daff2275286 f863bba4dd0964e82908ab6ddec36bfc26f8bc48 +9f61f57710e2f3a52ba694faac763f14fa68329d758e713152956f67156aad3e eb160e949ef7a76c384dbbfa0d61d580a11d095b +9f6353cd9e03b3325fd0ea23ff06cca2e7871b39d0dd546d023743ed4d0e9c81 47d75e0475f54c1f51fa4f3a508e3a71bb3cf27a +9f68e485be09cb4f0406832c3043ff73bbaba9057dd32ca5120bf58fb968dcd8 c341bd71e5bc97d012fe7d788f5d95ad61f421c9 +9f7d9c3ff7c7873a474c6e71d117de23c0ca92afd7dd838e6425de0259d807a7 d17bf1cee2b79f0c371cffa58246faec7fa70292 +a05508af8a460097b5be87d0d1adebd9965d001c00c0342d5823d62f5d504147 d8b9e83ad587491de5e0ce5e105d6732da7efd02 +a0806f2837164079433fb98454b3beb353b38625d7f1f2e5b2737909a2fd7f3d 9b52f4120bf54829149cb3ef756dba5018fb812f +a0a1c80c937e6f8c7f4b9deacd53dd76b6f9bda0710ef14762f26b5c7722d85c 488de7f1158680124370c16d9b5787f2cffb290f +a0b19bf38b63a7127318ca02600424c2cd491023af9dce6d707c0e5891751207 bd65f47097a6ff27de3e1d908ec1d53cc6b0fe48 +a0e4131177d5b53a0a0008fd5ffa88bc29c07ee6c3020adb8f45751e28eb16c7 5c7507cc2e5b04fb9acba04042d900f15271da8e +a0f3dac8fa0e22dcf356aecbbdf79440715687c1053bc59b83354f276d688ceb b196a807b323f2748ffc6b1d42cd0812d04c9a40 +a0f8444fdfdc6276b3b2ab225d14358833ac50141d73bc10cd016a063437e6e9 d7fbfe155fbddb41936407612fbce9c34276b671 +a1034d13a76b0d29b83f6dadc5a44826ada4accc7889f4b2f6ba880763f2ec36 634e22b2323599edd0d177e2eaf69e7ddafe49b2 +a1053628fac1667c26c16ef757cfb87f4bc6553b90a659cd39bb1d46c39bbcf5 ee1765e529ab6b44adbcc6a0ff6e9ac1359c6cd6 +a13eb24e3a3948f74a9d83f553c937e82cc6e2fbdb7948bd12f2794d822fbb6f cb5245e80f90f14c1b8daadf95bfc08d29c2467b +a13ecbc514b571721a9a1c92af7f89d473a5fe13228904f8d17368e71f273ea1 702bd70595a7b19afc48a1f784a6505be68469d4 +a14fd01f6c75813480bc15c6f3ec6f8a2b18c541212c93fd96f6287fe82bc104 6829e2c4923538bd75c6ad6a2590c1aca8d4a212 +a15a130a98c1cae5e6dd70c309b31bc21fcb9ea731653d21054e0df82d4f252f c18626eef6cbd1b420b85fc9f8a44d5031ad2fcd +a1b4063379bedd4c5c0940aa2dc8c07b509d3a5164a853f1b5a20d156a7c5c33 b4440c0becb8f009bab65b3b3c901a409c49add2 +a1d1162067d6018f0b1d90daa0ea14dc570052758b6a3171d7895073fc15d992 7350e6337a2d94c2d2a105845fc78ba0ddc89ea5 +a204c52c18c7bcbf5cfd33f2739b6a67af9938ef27d904cbf102f5dda766044c 0c3aa34742a8470a4ab4acc60e061bbd9a85650b +a2127a5161de12892e23011d647ef0e291101a6120b75de2a8cad765e56311f7 880dd9e150364fa22ada592cb5daf913e775cfbd +a215658dd4f0927c758ffcd6487b359161f0ddacd2543cf0fe86bf085c174bda 870880896dd1c399bb940510903a3639ee61bf68 +a27bb9f32d0647d311aca00a8f7664d5b30182a0eda31b1352f003bbf22eb58b 36cf0f3c2a9b192dde9ab57992dd730a54a2e302 +a31067ab4135316257f12ca5852ee49031e0cc3c62fc7e36857356a1311f864e bc7ab9aaf82cd5778fe998117b465b5feeb58c41 +a33b7c23c1f42033a24b58f5ea0d33aba4b6fe18d4bff86f0f70a4d266b66b69 64b5d4fbe7bb8ac0a5dbde1fef81dd84b593324e +a344b247217e51783935ecdcea5837be2f47a56172d3dcc2069936ef3d25dea5 7c866536731236d38f1d75711a2af253d479dd8f +a37c93ab010ecc9c7f017623c38324ae2b7d06385eca57fc6da35102a2c54a54 e5a2858907d153905dbc09d1445779caac0adc6d +a389c085c2a136c9a2c2b4a818b851a87af9904f933d7fd6aa99846208efe800 f501265f976a745e5c0d21104635d695227171cd +a3a074de072e21878238d9d7b7b1081fd4dbf3bdc50c145e85e6e4d7a09d7909 0967f654549002d2b6e8495c10cd0bb20ff3afff +a3b7619b6d836ece7d4dcd5d35eb3cd636b6ad1fb22bfea768ea5d4102cc9ece efd690f67a58c2ea1c3984397c2aefebd716d7ce +a3c8832d145147c568d4954618aca12117705d399fc02f00272fa32f3ff95ae0 dca7499de88fd02b75a47bee8def272c14b5f7bf +a3d3035f44784137774392f722a38f0d5a43b9df0a631531d0e47c48779cd940 141d3dd299cb117c277cfe0907b0ddaccdaa75ac +a3e39ed0693d43349755e98d7e125ac17b5d3d2a5dc232b8d5b2ba01302ace04 c15c5619a51d0a0dc6bec0d9961f9ef50c5c2178 +a43230765f7a5a3d422ba2e7cfeed85ad722551db2ac6784b04f6f8b0e1001b5 ea2adb2520150efa688d3c48f03a66de3312bd4c +a46ca0d0e30ed9fed1eb8939c497f79aba95b9e8e4e712079d73a6e6ebc70d72 f0374c882b77ba4a05ea2d50f506239da26a60fd +a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 a4a7dce85cf63874e984719f4fdd239f5145052f +a4c29fb8becd3a01d36d8cae4b12b4d3709e6b45eea54d27f2ae2e43c9ba9ae7 e8b638f771ea8e8ed9049b56148fcb2a86625f7a +a4f0c2fd0cdb1ab3dacb8758af443e9536c5987f7c66c5fa89e2cc79a1b61c92 da7791a0235d172e7be4f5c299387ed1c95c0921 +a4f8a0eab46e58b3afa198ed68ef78d6fec944abd7ffa6fab63a8af923aa973a 521b4e69c6e0a257724d59f3c803f4eb11e0521f +a50ed806c69fd2c6864e9658b0294feb4f7c24970cd4a785b3788beb190266d1 a7fcf7cf6ab5763af5a332041327182130235ab4 +a5191d9e8f879091e1ba926dd387711ad308a6a0b110dc58a4c9897209b14605 523412bef70b1d4cb92c574c89bf7225e205d489 +a52d36b5dd7c95a9e8596f01aded392be81ae538591f5d990459592bed96be7b cc5aaa5fa825a103a547d381c7a5c0524c4853cd +a560d1fa1edf114f57b402e16d662c17b1e3b7e8601ff7dcea6615ba7f1e48ef a20d733a9fa79fa5b4cbb9639864f93325ec27a6 +a564b979c786a7a32c6e8477e940a2a43238c57e8fa6be1f8ef7954944c41b89 571a65a81061ea62a11fa210ea6819322be4cba6 +a5ab6e423fa4649571d39b9a29484a0bb17cc70534ee6a410882d72ab1e31597 45a5f836b0bbdb163a13ce0e1ba1e8f7acffb5e6 +a5ad182a309ce0243d95b53ca5eecd2a139795321ff24f6fbc548ebe1f425729 f52cd06b7d0641754c6f7124382b9fb397e4070e +a630a33919b24191dc33f2e09b46196fbc7cb4b866b6416b8980431735146a5e 0392558815cbd6674d2187260948cf5a1fb22e6e +a657e241eb57d29d515eeb128f31ad3a8012e71be44872b0778f278df6ab9e4b 0735f982dd5a183a7fa901b0103ea4b0719af5e1 +a66bda0109d2b3c9bc87970da81bd91076b5f871febbc860f09ae997668b6800 12bf5f3e3470d90db177ccf1b5e8126409377fc6 +a679511b71b78a1b0781d6cf5e3c4524c1f213d671a5acc777ef36a2ec9b0f37 b98b09da1d6c4ec25911b2fd859282a41f4225e1 +a6ce423d816299e53b9b87164b3153dc3a4bcd733c33be1c2a13f41f140b69a4 f494e47725e1cb76e98b5aeb9bdb7f8d8271537f +a710c2c837a5a6f27f2b7f5a198c7797f148ae6a37a2209748845adc9d0bef8e 0087a575a0655d2e53e8ba4feffcc321dcb8cbc3 +a766d34d210e448439135e553915e30b62f09b926c669b7e2d5cb073e96bccfb 3bc2493e6ac633c6119b7899505a5a0513ae4e44 +a787901c8e7f31a8450a152bd8aa7033203693d8ce807348e53125a47afd7c24 eadc9ce6cb6cb722723e228d9e44f001d4236b4c +a7b3a389f8d3a36403a8b2bdc6b9b2579c81d79f91347655f50ce4356b4f2831 7f07667d4c520a81c4fd6b5631c119bf2de791e9 +a7b9c8cf9c7469bf18c8fd079039fb8d1de1867de46294726c110899d94b5ae1 3d2577feb4985c61a7e393777d78cd9a8e8a516c +a7bf36aba7ea2fc2eca014f1ff42ae012ef82b450220cdd5e77d7c5bee306c71 c10a39190029484263750293825632cbdaa9757a +a7dc9999e70a00c97b97f71fa0fa877e92e29a09ef6eae72de2ee112d3854ff0 42150e0aa48a3ee95ffe74b7f937111a4afe17cf +a7f778c6ab0c219aa051ebca04abf1397864be8c9c69873792c6bc8953426874 ae5ceb4b269403c6d1653c7bb32e71ede7398892 +a7fb6dbb2154115eab700f4e4a6a6eeddc30d3f8a14f0ccacfa98d9347ccff8d 4893b75d346a20df101789352b198beee55901a0 +a86aa44f58a3f3c347dbd0f99a84c1ff6ddd41c58b19697c49d3193f65873a08 c8f5cfa836b403abafe7256ce964e65b24b3fc0b +a8a227647205ac1f01b263c1a81b8776337124a6e519b67ac69ffd4d2274f682 ce53c27f666673c2af8d406447078ea03bd95f6b +a8c3ec591a4e8c04a28e1251e391aa6218df72cf9b5f0ae0eba6eca6877902b6 94996dfd9511a12bfa9190d97d33d2bf6599f1fd +a8c79d136e0c69d1c0d2281bdf7acfeac0f55d6239b0141e17a6de422628c7d4 696bbac1204a1d56b0f482568b1a7d504747138a +a8d8a08205cfdf47585c32111e5327b328c0fe68098eeb58c939cab2e83785e7 3f88988aa04971faf2b1170fb8a5f7a0e2cf5f8a +a8eea8c78cf7e55ec2cfa539d860b5e5bcc4760277d1403f2c3d06c583e3905a a0cfd1297d1eed63d0690c167d67e50ce06b6791 +a91887e522da2b66ed048d77858041c9cdb2fbfb0b4d463b971911217aa3a4a6 c2550609e37e0778814d328c47eb84a7ede6931f +a91e8591cdbcab2353f3d0f75a016d31c14157fa7ba9657bb72bea1772be10de 82bca8633fcfbafe94997fac9fb855bd93e36bcc +aa11e156cf0329ca3cf624ca8a185356315552962035a549ddf6f09268402084 18c321ca3c1f1d360c8f6c793cd48afeddb4b54e +aa4089cbc630982cab278c49d506c357fc9a8dc27c002ae1a6f8af32d5bb3e89 966f3c578c405f92510a54de59ef2263a8b217a9 +aa61d4adf622265ec814c1a97198d2bcd3f58fb08989cb9beda32a4d0aab6697 bf92206f8b633b88a66dca4a911777630b06fbac +aa65747207487983a77cd65713928d62e1b326b93a330ab74e9c9517b42f9ec0 75d979718393e2d0e6d1399e6c1e760cd739ce35 +aa77e77d6dbd35635d786aa503c8a85b08274c73ddc420ce75b66b8dfac4860e 6e9df7119d93ba93371c317285688b49a40f56c9 +aa793f0e9d9d746eba8a7a60cb4981f7e24ce9691910350d7df9b9e94c7567b9 e01b107b4f77f8f98645adac0206a504f2d29d7c +aa83de8b0f4b0c6d26c97e1048eebff482698b79131cf68b0671eafc40444d08 8654e891d437bfac94db90e4c1f6878e0efd052e +aa8c472ac90d32654157b4a43e31135a9a864a3f02c2358c3b78316cf175b5fd 1cdb9a4f4f307b8d0a3439d6d496e7ffa0a19917 +aaa1b07f32e2a452b7c24d7068bd63f27349668ad1c834cca9d9a2abc2ce49b1 4260699b37a7a0cc6be231d232d424a858a7d111 +aaa6f15815044f9acc8541d8d28612affafc1cc820a084798ef762ac6978784d 6bdfcbc41317d86eda9bb191e87a57651e8c6245 +aad6240d16f18620120c221b902c9777d220fff8c5a0cbc8071ef6e826f6c630 4801a2389fa38676d2a42061f5e3841324667b10 +aafa750212c82433e5b7a0ade7048fa1951800536992c59f6d66c46b2337ba6b 3cd8b687d5dc8006a5eae9e9c4dac212e8df67d1 +ab2a5eb5541f5accca9d9491bf53002b131b547493d6c248c1f51628468b91ee 157999b26778bbe322028a30869648267bee7995 +ab4ca17ec21ddc7e0bb1bff660ed18d90053425adf7d85aa320454e873a18b75 c29a831c884ea240437bd2459e60ffb383dcd149 +abee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640 181037049a54a1eb5fab404658a3a250b44335d7 +ac2a850fed51b1da02abb706f3da5768337aa2d49b414add21a668e2497ae125 2cd78883e494f23858067087893c38219aa4fe76 +ac2b785aef8cb5a9b563764be378ea45a50cf163d8bc37daea68fe2cabe064e8 6eef8dddafa405da3230fc1dbfbfc7ee8565fa26 +ac49410f64fad10760838866a40107573e42c86908c83ece433d64b7a8b57f7f 2f1c5d509ac5bffb3c62f710a1c2c542e126dfd1 +ac7908fb8ac8b6f3aec395f7b5a82f320ac634496b34c4e5534bb291bed1a8e2 c6039d8f24940f4bebcb7391e705b6b375f8fb93 +ac88849a26c126b03fc6fcb17cb23ec563e87a5f63b7afe800ad0f436128ce98 af292c99c6148d772af3315a1c74e83330e7ead7 +acc7061eeb4b06b989b9b95db3a726463793a54752bdaf978f2b643064e61650 37870a4c5009c19f3da47997b40b786f6f2f8b58 +ad00202dae3bd195b18e458e7f527e04135398a1d5f9aa3c25291803c26482de b6fca797357066296e2cd9bc9bdbbcd2489a67a4 +ad05c66a177f844f8239a1d186a1f803a4daa6286c959538838e222bc6337dc9 4ff1dd0992dd6baafdb5e166be6f9f23b59bdf87 +ad150ac016bb6e3d11d5d640927038619573205f34285a0270426b5d0d9171ca c867e4ff48cb00173ab50011a7cd593ab9d3905e +ad69d65326774c8e0f1a8fe7b218d2f4786db7657a5d6b03d8f43d8265fd3c44 3d35c193ff6508d92b3986a52c103b2c1fddea0f +ad90f638cb67720b20b904478471504acebacc7bb36e5dcad3e882acec496fed bed08a0b30b72a9d4aed7f1af8c8ca124e8d64b9 +adbab8789a2829bab3ae26bae696810557ec48f125e57e26e25b22a7f375a551 c405f734c2050e2e4ad3469d087368a29a98d27e +adced03f19d05aee4179e749bf666dbadbf031a12cd11796e4e463b0e2eba5ed 5ec2b329adcb74a8bdf99871f0f6c048fc90de70 +add26f5a369d9230fda1aee868bff357cfb63e39bbc21abcbe5e87a77dd70393 8e0a161f97b84d42c44757798dfc0449b1ad3c9a +adde54361f01131673d95e3f74e73b5b4645913941e061efc66bd5c85c40bb02 a0c0512a8bb09a7fc87c41528ba63083b44056fd +ade7d297ede7bb58008da582de1253f0a55cb76e82d1cd376f82ccf97f70bced cf7992bde17ce7a79cab5f0c1fcbe8a0108721ed +adf2fe0b01c0f20184cc5f4cafbaa7dd71f292a8f06ed0ae3690b21be95f661d 615cef56af79a5f5b39e4f337cc5d6e5e5c1759a +adfb7008e983eca01cd37bbe9717a22ab7f62bc731f2d5bae26fb99589628853 e33a4bd5f9681cae1b8956247dc73812a7406615 +ae235704494a6aa176dc2c4dad7705e40ca8932b44796287278bb70878206bb7 b92ae7738d06508c5fbfd1f2d08fb5190343ffc8 +ae29942a0032a27d0dd5dd802d5feebf1500cec3f37b9b0997070e9ade4df219 f6b73d281810e3ecb7e984ab7c951ba52b72c10c +ae52d4711e8115fa72fbb0397f995fd9163849ddd522582c1efee23a4371b846 bc95d2dc839d6993e4ed0a13966361fff93c270f +ae558ea7cc33669b88e4cba1d9448d5638f5ad5c6effc852948555f57f8321af 8baf75bedd0f29bcb61efc7c7c95761dabce5e63 +ae8542d347eead448e089ffb0c35067c2df35b7abfd9eec4f62eeb701c59b05b b7af25b7c9b2f436fd6a5cbabf9d974d6111f6d1 +aea29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5 a8233120f6ad708f843d861ce2b7228ec4e3dec6 +af17bf05f3b3dbd780997fd54887b276c0c972aa0615ec4cf8da9bd960d560ec 1181cfdd77061b39dfcd72660eafcc1c578fef0b +af2ed32dc25c587ab2afe464a73b49a0c5454fa2bf12ae538a2165c373ac2de5 bab41e61c338ed174de4cad036da7a5edb40d28c +af34d66ddd3c768aae865ba6958dd9158ea5f5c47fbb5348a8e359f85f89cbef 9e4700a6033d42474c800cac6dc0095bebb14c76 +af4b69c54262883b42d42c72e8383a20843ca1521021808f73dad9216c5af6cf 6607b1c62646fb35b6f55f22c11a1aa14f0a1145 +af8cc9a93a75ef2e04ebd8aac5c62beb755ce4c1162d8c045ef578dcfe25ceb1 d9f106b9f6fe553c649cf682ed66b72567dcc367 +af90f5cacbfe4caeb5726627f9ac36e5bb49e8497b1d5428858cbe64a332cec0 f161ce12cbe824defc0e445bcfdbbb6f3d5050c6 +afcd6780d3255be3d03402a43209f1c5c32cdb3bcb23068b94e6e9247ad13e7c dcc1b764890f85180542055150c2eb8a7f3cb9f5 +afe1fad6f6b22eea530ff7b373d6b9b787b39792f720e6fcd0692ba6ef99e02f c0220fca67f48b8a5d4163d53b1486224be3a198 +b01b505b9db0c6fe12355a5dd4384184eb067779e23028ac2e1ab9a504bb1bf0 14be9ebaa30a1d6085e889eecaf2a24968eea24e +b01e2a8d4c07527e6492e7fad1f749dff75216d4f129e0d11ca5caf9ab6d8273 b60488e1d7564716dc2ae7ce078a415280bd7df6 +b0516a64349a18f3d4a48850359ab66ec681ee6575103523acf4fc9b5e201e08 739bfc1798bb97563e72c0491ccf8fdfecaf6a47 +b0675d24f637e6c2c05b09b88dd2169fa169c37dbfade733a5e0af0bb1e8080e 4386ee2acbcbbf1fdf06c75b91da3a9653075c6f +b06e3a0bfe758a68c5c6b9720f281b2526c19564179a19a0ba1844a45b089e9a 8ec389978cc56e25a6a5bdf6be993c7664e762b5 +b0a25f084f327104a3da545a926c6b89526d5399fdfec65d1accf43c8f197bf0 6e9ac9c85e1329debc18a30c520e59130d5b190e +b0a6aa2b450048e43155fd6d655cada03ba242a2c1b31438f93638a69e891727 94093ddb4164980ef98d78b1de318eb0ed74d933 +b0cb0a55f09a8192d882c14405fa0e8a366db3d07c17214f41614fa571566ff2 3e1d42b7d980e29595572e550d5ce58c0a34f15f +b0e890ff4d407faf502bc1eda5a18bb9f08f5761c2da4f3d6a6d563dd2f0b1e5 ffb55c532ccc1cb3c47eeeaac6c64240ab88fe29 +b1328b04eba6d6c573e7c7aa9eff201a654a1cd2c077ec9fdfad0499d259e7bf 2ca59d57a4451602a6db44da4b2d37a505a84698 +b167ee336fa10383d04670681af258d040347e6e1edc71d61af722cd19f1192e f2ddf1eecca39a307ab8544c2076a59441eaf5e0 +b185929191c7ff2f9df80b08b2698ec2811f3466cfd520aa4df2a8b777aea135 89b765b0e9f0b54f9dc85fa0a982774d07a935fa +b186cc0087e119842e1b7c70dc70f705d7da0013de7eb434a6f48dc299acec75 57b5df4e635d8324ca75b0dd33cb96271b426e23 +b195873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd 08b041783f40edfe12bb406c9c9a8a040177c125 +b1a61b0e46d4d5ff7268e381a6f7d32eee81e5c1304481e62ae5fba97870c960 9eebf8791489850089738a95773d2f3c1d56d89e +b1c6b80f177e1869b39215b2ee4089474d685459fbda1bc4936457567036812c 7e822f212370d60dc701504a5512544891f3ae05 +b1d6e5441c09075f9ee5aa32d8501acaa0c57f5812b316a74bd7911786dbb1b2 4915947ace431f2d1b4fe920d4cc446ca2388169 +b1d8511cbdce9fa2ff79665dc3ada50e6ea82b506cf11decd40539e6478b4458 c1b62b2eded035d22584c427487503335f519d4c +b21c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827 db4df74a2fc340a0d0cb0cafc0db471fdfff1048 +b22a9ce429c6518de3a48dc441da4387fcec90fc88631499e61702ee5a8f8b30 345881abc471d0c65c8bb7e4cb3be10571cd32ee +b236af68cc7637d84a5cfa565e8b666c303570844c3d3dda6534a30b4824c352 eb2da5c7c3cbd270a1f945007ad098eadb4a68cd +b2732eaa1b3cb8942b40bf8da2ba51b735540dcb5605c29b694d7bf1cd500e9e cc15381ea2855139cb87d3a5d9aa64841f98c1fd +b2bc5b027b583a336347be589add47e0a89fecc19dd8a154b826c363af9a4345 da7ad841bb28fffb5546b529a00463206e47c8e1 +b2d355727ea29d217971a84e2a88527e0f6996015bcd01e4a2adb18910dc3aad fe6f9f29340241491470ad8296ce178ebcb428f4 +b30bc6949534a21693aeb43b8379df1b6dc621136a2c6c356e3f6bb86b9858f2 d570b36d8ea2d61450a40ca03c3cff9b144bd147 +b3364fd9c7680d53ca88929ade181defe8a00009e26e073ad7204fb7571aeca8 da8182721657446127a006281e0f1bb4219be952 +b34f4bbdec1daaa7d6e1e99aaa1e6cded1bec27170881f5c6527fffaf55adce7 1bb11859357114684a1a740e471edf1a7541b82a +b370899e23e1a0b5e50780586fe822e2e6e17dc03a2e56020cc1d11e072f5160 a9984a4e60291958a7cf0045758f47db3d214040 +b3836efc8cd5eb1c0ecc0d99df3644902cbccf9b6c7c38c3fc65900492cacb21 41c1bdce587d5c8b8ca03a5a8691ea3f09f16316 +b38a7a2ee69d55f021efa91caf59e23bccdaf6b8a9c3f83acd978aa177587537 bff873e9853ed99fed52c25f7ad29f78b27dcec2 +b38c89863825241eef7e7090bb11233910a0117436211183502d501ecc5b2879 e4e56117de8b3bd0bd899701da4712caee27c7d6 +b3ac32466d95d82432e7aa5a2080be9e3cf9b69f8620177053557d982a5f6eea 6909925086c86d40934c73e6d7a4b3886d5037c1 +b3fbad0e2e846628da5954b92c1c9b9bce24236f166547c5352503d7abe6984d d132e975594a1f22a5f3a480f5695e6001367b53 +b487428ddffd22b992094ae29527df9741cc33075a2ec0f4533bdf26459724ba 3316387b546b1d495ce646422e95dc209cd23705 +b4b7236050c279fcd2b6b05b6539c178614db05615eca1895ee1e9de98d73b44 a57d1866c26829f12095fd4f20f359e7552c52f9 +b4c3e22b042d111d7546f8d3bb4d3468236d28caf926732b8ef6c93eefbad39d 0cd5264e932f5dfc68959d11b5a3bb3a8714aa87 +b4ff5a6d6ac893413f20f16c9b3c999a5703e5e1238c50585ed32a64368a54d9 334fff1495c554746ca66f2359281b7de69f35be +b51d286a9549d2d2d6ee18aa839b2fedb8f3b4235bd763553a2a415548e7c81f 6e0fa05b58c140a42ef25dc74041e30f25772b2c +b5454cc2a66d713d5be85d5297def275ddd102817d96014c46f438814446e29e 5e98d798bcc9613cc34a083719c378d5c66bb9a2 +b61b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890 5f34cd6e3285089647165983482cf90873d50940 +b6cff922b0be268a213f763aad8e357bf2f30e905ce7ee47d586eff02c5ad26a 1aaa4daefcdf508ae661e81131d810472fba488b +b6e8427e3afe1cf0095d0f5aeca0642c4fb12d4e529f62e8264b6c4db72f04b4 6fe7568fc3861c334cb008fd85d57d9647249ef5 +b71ee6c8837efa5b3ba3361f88c321d391ac05f41d5b2506cad39319e80716e4 c02d0b160b82ee72469c269f13de4c26a7ea09cb +b7238ffe9e20cb2e62107858809248620802f8f15c603594daace4681810bfca acf362a92101202f5f09c9b51db352be27b5bf7e +b73c6046e934b9a76e5b9c587c18a20b7a69852801d6b3517bafe09b78d1cec4 964c192fbc2997e871c6101cb63f69b3ec377b66 +b75cad007d473a24d06563276d7b254378f2a0935d231cd82b5332944f22ad1c 7b6e8067ec96acef9a4184b43210d583b6d2f99a +b790ebf3f307102c84da36abefd196078e569ec4279f9cba04bec0ed68ae93fa ffc359bfbb59bdfc5ca1fc95c9bdc618f89dd8d7 +b7bbec7e6e8cc5a640d966739b0c7ff09f5710f534b278ddda5a5edd554dbac1 d42e47121844049cea15900ad772f51164397573 +b7e7455bb13e40eb23c5521f75bd912da55c7cc4882f62cb1a5ab0780f04faf8 a732c71d3e34cd444e9e0386b797207bc116ca42 +b7f1e115ee187338ff5720b118395dafd6087ee4ebbd63883bcb4c239cc1ec2d a59b93fe881d5983360984cc2f81b4d12d306d8a +b83624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 9fd738e8f7967c078dceed8190330fc8648ee56a +b88071883c4741b0f044dd83f33c8b7fd514a6534f212aff1c00efbd96cb0220 7f11132869231c5461b911c054577d101cff4b1b +b884efcbb7ccfbdb587e2049b9b28a7d025d3a1a5f9c1d7708f39a2a2431fe89 04133b0608f824a5d662b85c866b2e84244ec0c9 +b89ca9872ffc92ed33fbf3e18f6acf5f9ac58c70a7408137a6f042b7ced845f7 40e1af83bd6b13b718c52673d2ad68edf54b6d6f +b9134c331629e9cb29d1bbc03904d911a942b451e087543fc16deb0116391297 6fc72e46de3df0c3842dab302bbacf697a63abab +b96f8b46286c8c2a0e73d2e320abf35334e7821e561a888f85db612b2871a1d1 f4113b8f6c796edfd95d2d178dd27140e8d5eb40 +b98e3de756c625603d536f742b69bf11955d439631f286712927f5975d560e1f d2ef83fcdc0c9cad633e7b654ab789d17f583f30 +b9fa227a38b8d66e3c2acc8a463ea37768496b746d370b95c4803505fb79b84e ff4fd6b1cde132c6cc6278ad7873d7047c897d2f +ba038509caf46d121547a01557da21517aace95267ee6ae54bb7db1776fb5825 ccd7ccdd6a61f941642d75c3870d82cdea9e6fa6 +ba0a8d2454aefb8f2723fb6fa5a1424439b63bf2f6f47d575aee73a76a542a73 b4b1f3118c4afc6034b5f99eddd18f7dbf15e5d5 +ba0dc4a1787f9159a174b1e010018af3ad2f5f84c8e8eab98f67540663ae82b9 fe4db4d4c93b5fc5d85aab2198393dcfdfd390a3 +ba312594582a5a746b8fe46d639f8db0314522ead4339ee6b7299802443ba5c0 d9a9eb65ed8db7f1200aa017e5ea2204266e7ae4 +ba3e0a5d754cb8fb417f0b8826d943fce3007ea5fa332abca0eaf31ceb99caab 41cb5ab070140451804663d84b8190d1c5d546cc +bb04bb109db80e12067bb064a5e3eb34c5090e41b4ea8f289c2542efd21fd8a2 662a69f7b8260cdd2461918f55353740451b0ae3 +bb5497a9ce810703e014a2ac6fa1eaf22625ab103c7d141ef40343fbd9db2a71 89442107982a1e968f643808a9e4c9227c1cba2e +bb5d090bb1914554341daf6c1dd6fe4e5c25e466818e5012ebae812392b58934 bc2c876e099e4a12b7b28779abba186216355c53 +bb680fba7ce88de98d4881df75bbc7921b97102a8c67ea9d9b45baf765a4c354 0a420998aa494c4b9152041bf5d8ebb80a2c4cb9 +bb6a768c5f4888cb5f37b641ae0475c1e8806f53e1e55acf6befc7ca727a170e d1487fd5790bffdaa06c181d53cb966a4521e899 +bba3e6952c7a791c03cb65d4919df3ad767346cde441854153327e24a829c1d0 860bc8dfd43318cc0513e98d4696df7378b6077c +bbe6aae28af3c8f82c5f1d6cf882f4eda9165750e08de45e9796134a294bf83d e074873b8c02e2a3d3beef03dd3c8ee02cf3ce9e +bc1b9a106a7ea9414b5149bc9a25a49df09cbbd91a99e5ffd53f52462792e28d 5abe9edb69b20ea386597b062e94f2679cb81fc6 +bc3c9e7c3339eec5a01dbac0a679b06e04124c4f51c79fbc16ea8a68323c0654 fbbfdf9f13e92da2dc31b56081302f09fdb62e49 +bca7f0c01fb672c05f86199908ae2f853f58f0adffb9b97b897b37c2b5633cf4 0cc8c5d79ad42f4673374b0e9cb00c3777e55a8b +bcbb54aab5ec52d387c70baaceb1ae313b312d354319b95c58b59d5140bef0fe bc584bb33674be3b3925c7bbc129f1f53af21869 +bcbc6e0586bfa74313dcd57a61c4e958abc0ac7abb3be0f2d82d0d08a3d5b357 755a4f75d2184b2ad4ef107932c86c7eecd0806d +bcc4fa242e55c5e08f64900407dca1cf2451806830905be616f339bf7a5580aa 50298f44a45eda3a29dae82dbe911b5aa176ac07 +bce08cb2060cdbba7216cdee466762f3cc8e3dd655387e5f5092a748ead67f6d c3ffa781d527d08ddd3cf626ade4685fadce498c +bce2be789b7c5f0d236cb597083143d261c705c2a66a88f4ed547ed9e2065793 87f7f37296ff41d6f2c8603d68f159cdabd4510d +bd15edba8822cf76e60aec16a0edca6b464f22bedb2f9d55078f51cfc7134405 9ddea2008d33878aded26b0c64ae82ef6def49b4 +bd32ba8cf33cd3049b5dbb3c18c0b0478a52389cfcddc7e7b80f66577dd3ad0e 6524d538430cad9215962d6d01b078afd7720bc6 +bd531ed2f7d5971c642a86f8fbd5347e3b85e2619c891ecd4bcb4e23976f35b4 0bc152bdf2707576e6ef9331189607c89f9ef8a1 +bd69bb8de277fcd70b2accb62a064eacd6ad2e01817188f5577eae25cee3a35b ba6f870d2cccbb6439c43979a9fdfa72c05b1a73 +bda24eb65236b353a3c5ce73585fca46ac4339a386a463afd3e28f69fcae213d 8c7af5e16d87c12dee61f9aaa6cfd06ed93a08ea +bdc530e0b98dd736cd812408eeed9aa0d393bbd0630b355eb7601e61f0dbc7c4 90f9ff6755330b685feff6c3d81782ee3592ab04 +bdc6e8229c3beab1f6fb0439687b325467e2a8fcd6267373881c429f2f9a3f78 4711d372e66dfac8218c4d5eafcf03dbdd4706ef +bdddc903f4cb49c257ce31b6e651349f9808d82c320223c339a66a03a9f7829a ab415d59cdf8cd11da6d5281c75423691c0a34e4 +bdf2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23 3697d64be941a53d4ae8f6a271e4e3fa56b022cc +be173dd892e4502149db328c9372962a08ccfe705a22fbfc89190dd52ac9f939 2a6d97c7907f685ae4b18d31d2ef6ec7099298f6 +be218dbc2fc82105b177a9a07b4e0f615ead9b9be02b13b2a239265812c1e739 8c1460102ab097ef014291e939693cc38daa7e98 +be3a95fe895bbfaa243ae46c89573d9836914465791d41ef6404c14453ad9e2f c51355c1972fbaf3cbb5c7be21939d2f4445e9bd +be7648b598b8c830353f3d8a9da3b434ab4b636edd7c5755171912682061e943 e5d1faefab1bb51f3d0fff2d5e2cfe4fe96a9c22 +be954c7dd99bb22f2730f3c65b8a6ae9d1940cb909cc89e6d83de1aa5ded6946 690624bdfc9dde7d14609ca798b61bbf5fc9e9c1 +bee70212dae887b4181f4c256a008a0509792568c82ce6f78ea5170dc5141b82 58b932645c948010c10a9edb2e7f1f51fc165230 +bf148c977af56b2c4f132adeb987da6eb63b309d7e1755889bd8c17cc9e28520 ecb100a9c8feb1d3bcdd6e13ebd4097eae84be9a +bf278d619d1bd8cb9e47d70df1929ff019a1f6c4d8bbe070628213778238551c 1f798df229a70aaa4ee221ecc0e24025ad92ed9a +bf37e4ecb48fa5813c8c587c13b1dbf65c9de2278ca1093b02d241bd148aa36b bb2bad2a2f6cf5dca3d3157b68112ef9cd0c5817 +bf409f1aa76256282ebf1a7cc6c9f4220be9ffc47b2cc42248fcc5cbb67bce1f 30c62a2d5a8d644f1311d4f7fe3f6a788e4c8188 +bf543f7bbd00b73f6ab1cd8203c5279ee3c319705580525916a86f2916a5cbe2 a0d9ac96c359e218d35b6faff16e63ff6271f1bc +bf575bb55d3a86838a50d8c74f46cb4f00515f36e639a1c169ade92eb7ed5822 82324ac1a5ec79069877ed5d807c87762a16ed9d +bf66badfda7b5d2157db65c5310cfdc4e904d7d5da57ac5abe17542de612f856 8f0c4b543f4bb6eb1518ecfc3d4699e43108d393 +bfa3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19 2d59075e0681f540482d4f6223a68e0fef790bc7 +bfb8433faf78ecf75ed9fc3bd8a717827bc186db50e525ec46ad318d6182f91a 0a7ee40d31baff4e6d9b302446547e53bdd08b64 +bfbe326cc9d9fd4dc3904de4c9fb919194f3215baff612b6d3f403e2522fe163 8382cd353101c0963d5f401bbbeab5f6e0248f07 +bfc0ee6fe04854c11011539f38cc6b9b73e0c445bac2008de2fb877123efc2e3 e044baa468a1c74f9f9da36805445f6888358b49 +bfcc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5 5d0f8f7891e872d284beef38254882dc879b2602 +bfde5e9939f87db0b49bb0ad5303c14d555a32b2db03dc63218af87fc9ae7cbd d7a09674532ffc0bda73e7c6e028bb78120c85e6 +c0011465109f2622a12479cb68d0117547513d48254a74c1a905ee88943263c2 e8dc745f5603c43ab9d69e790ba6d600f24d108d +c06ee60e4559ac134deab08997476c059b4baf28973f75ac00310f9c0c0df532 e8abd2f25c5cfb7e6f296699f1bc120a78bcb7ba +c07142deecd175549344fbf31f3987ff54e6fdd013b15d502e83f67edee4be4c 67df18d487d28edf3fbd965836f212f7edd2fa6c +c09c5282575731a965aa96bc17770d001552a22577cd80110737c2889a2b4833 4b09231a1e78cfc8e19477c2302ab80b689cb8cf +c0abe8f37fbbea90e1ff3d571c86ea4c6fadfa7f7f12a4a712c821b065f2b0a5 a0a6eea81880dcc9841f68766351df223be95f1c +c0b3f002ab7ed5b861ba27d5566f8d54ce86ae8e3f0d7039eebf52941a164e0a 0867c7b57704e5c98cbf67d46df03dfd549cb0b0 +c0c38ad9820923073ff986beaedb4daf3217b3028e48622bca80411442ef6047 629bac23f626ff2f0aff71580b3f46742643b14c +c0f1323124d0f0a45ad1511ff43041f766fe110e56d0b063d41deaa9f443b61f 5fac2a68ae5bcead128415169a68fc21d9e0f038 +c0f85283375a304cb98d2b47d3b0456a10b934bf1f89bc3f80d6c4df2087460d bcc85e9ef60749eb8a4cd9db1c9f7b3b974ff407 +c11677e589c7d737baef01be425cd0c2912b78f6e426f1750a3b7d5cfdb123ee e45535849cb7187e22c9fd11d14e0eb37f5d7dc8 +c123679643c715765e9fe6859d5a5eece56404b03f29266eea7c0e83ae14263a 6d646a4ddbd3c0fa824b193aa83b48b38b48848f +c13a073132718d2674e3aa4bc56a039582e61cedc9e178ef9862588a7f8c7675 5cae6c25279bdd525124b7686d67072059bb0fd5 +c15299caeb002894ed0743a79dfd1cc520d3aa71a2a84e377ed2fd7827096969 1b61137f9a2d6f90f46ea3a7fa7b89b8f839b202 +c159593092a4a8e5c4dfd9ca42fbc98c67ef75060f87b92c4bc677b809be0a80 ca3939e682a3198a3d18f42b7fbf84823312dc61 +c1599569b3f7b5ab531371b6c762c9d578a794e6289a1943f6e6e45ca02ea05d 00f43bfc81fc4ee3a1f92d56808aacfc8089766a +c194ab25a95d8b7d4897515a6fb62a723e809afd7c1039b7a50fcda3c2edb2db 475fe13a56409b64b9ebbcfa1a0e77b27d21d189 +c1f503e2869f51ae5487b9c89dc93892aa92e58dff2cdbf19348056997a17a8d 94c3c71da52ca3d4761c4e9041b384d9bc75ad9b +c223ddc0bf50e860fd0e85b9b4d47b74409106098826fe330a9d98f2e3a8ba7d 1ae06d0bcc8ac256710c81ba92a16a644edbf456 +c223f1b579bad18635efdeffa7a8ff40567d03fa427e08bb90d9878f958d8021 deb106bfd2d36ecf9f0079224c12022201a39ad1 +c23068515ad0d427ea509d3b0a3b2a6c5d6ab433d6531a10da6b7d63ca0d3594 8a3653350f78e3e8e91f2e14747d6dca4b435d8a +c258f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 b25fa35b38051e4ae45d4222e795f9df2e43f1d1 +c259153848cb0a2d858dbcbf7b4f40546f2fb60c31421f9726da11953cfea9eb 8997caa24765077a3c3b70deb995819488bceb90 +c268fbe2c4169776699a64bf021f1767ca74c21970090388777465437910c3cd 5ee2fe777c41d9ad9e7eb9fece56fe21f830c5cd +c284394baf0418c32e379cb3bcd5caf190e83e7a8b5056789d800f27be6462aa c5c58a0dad7a0cf18859f29326a0b7a44f23e4ca +c28e313f349b558f23f9b4c7d209d5f0faa3ad79edb8260fb3fb154e5f578905 22bef3bc97a19b18125656bcd77b517680168970 +c2b1f6e286be2959e3725a1d890e178cda1c31116467d5cc0c799825b17c8d74 6a5c2bc91cc42f38753da59a3291a6d9bad0c852 +c2b535bfc3501f0b4e179d5d6f0e2cea613940fa3813be5923db7e71e190849f e00e46abe4c542e17c8bc83d72cf5be8018d7b0e +c2c43938f901a6206679cf488e48f38742c36dd7bee0e027fc298093b88d9cf2 b3106cd34a7b69d2d3b3913415423bb2c45701f2 +c3055d62611a3247b1863e5b5955ebd263d8b8e3367d8efe283bfe0462b4db1d 19d13c65e11f45c3be6666c6c4c9732c6f4ab40a +c30d06fd49797e3135652d654db0de122dd83f8400df1c7a0e95ca3720defb0d c073337a4dd7276931b4b3fdbc3f0040e9441793 +c31ec989f2f75e339b9b7b106d5c1f5386197bd4bb16140fa8e0305e03ced1d7 5b7487bee63e7f5cb6ea1d9090d52ae631296cd8 +c339c838d5d21d9c6c6e4b7efede56c1580e7e957b5da395cee2f689f108709c d3cf2a0e30b4d3ab11340efb3d9af75e61b7de8b +c3f4fedca384b7225c7bbecd31c52d6158b1ee4872d0a85fce2d44ad357a6b3c 581dc350d5e699a9428caaf6a59fca78709f4bbe +c436565f09dca3e9556afa41fcc79ecb57c448a462d6c310df86b8b35d5fb604 43bb43402ea3fdb9ed6b4aa82876c38f46d1eb72 +c494dee1d03eddaa9eb923ea640f07f660eecb9661499495899c1601b885c2cc 81d3ab652c6af3483f72ed93b089edb999e7d140 +c4b306fe1f9564616cf048af4e566ea7b404ad069c972ef433d21469dd93056e 4caa8962a6af9fac72ab81afb03779bc3acff910 +c4fb0a710b3cafe7f8f43319d72d308a678c12cebf0682860b1035352cb5d4cc 2ba363e5885997c504921f8c8f7278a97fbe753a +c51ca339b4668e4ff289c3e9ed3f6c11de42c85e2c5491fcecdad93432a2b13d b8fce12b6878b56b0d7be4ce7123b4feef44c2c3 +c51eb1e41d61d89ddebdd78357baaec018226bff72a4c9296934b1c48fd785f7 f1b16987ec81874cb9cc3d6a2e7d533c950fa68f +c52bf4d013448d7c53a6598f85de0cfdbd2eb110a058974534ffc27c174a9ff0 92142d22d55a2d449d5c563f6e72d827512cdd49 +c5446a46d4c6637c2f45ce4fa113c64ced344a4eaccd3ecbf2107a85fbe18fd8 47015c6bbd6b6736985a562bf227a989c6340df8 +c564b89940661e3223cd7ded73de0206cfc21827bc0b9bc44e95d411d1df1b0e 01768b00b6b8cf1f5e34f7f416e0d506497cfec8 +c57f2bab01f9798f75e61c27f55a91c743b8d23075728699e84b9280dcaaf900 2812156aaff1a1d7785d56809e8cc6643083ac4c +c5afc2ee84f7b25ef38420d60f97b8906c178cfa90497d9f5ac5d1825b0e0cb3 f76c2592b57baa77423bb382d2e86898992d883e +c5dd7dbd64522fc4b9b92c6a56065eba728d026a8d9274c30ae26cca30525d46 b38ab8c4a1c9e346c9520ff9377be3ca45b62f23 +c5fa0af1a59da387fa8082d801573bdb342bdd59346f7599b5f8e9697dc1f076 89039682651474c6e2bf9abcc02cb71897f8b4e1 +c64531faf49ed50c4fb19066a734fd236e6bfe093efd59596dd7d802fd311d71 02e4e5bb290f13ce81d239c1d590621e2d146671 +c67a633f4eb848ff63a1941b3ca756dd08b5226684821768d918e0d839ccc4ee 833ae912e8d5bd6309a2ebab027b9ce85253587a +c67e84e246ad6aefab06f12ef980425062b46b04a66efe482927dd937b5b76f5 eeeb57b574aa97501bd7f477330a2bca8d1bdfe3 +c6bb20cd7be8c9ff8b9cd6246f5dd2928d2428b6a0b2e38e7012e565bf7d9bae c6ebb4a9404e6cb7ed09f0d775c093f33a82f9b8 +c6e7cde92dc4e7be645bf8ec2b6d2ae9953f7c9ae934cea12fb087a9673629be 64883249d3cbf1db4523d95beceaf0f9f8e6485c +c72158b0050e8c9fb81af7d4265c0cdc389cae3cf235232051eadd50f4c1f931 c4c199245f930c01729a0d7c60dc05d9dfc5f53b +c733169129f390cf971099f88fec9eea3e23de7bedb2c72a0599010d6f9030df 6018dbfa702b818590ae682bf9bbede716ef290e +c76564a8a44e38ab10e01f08b5064779775d050848ca85b41ef2b58cd07fb661 a1c96890e1b1fa314c4b4a3746c86a31e4a9d78b +c76e3695d0e5e27713dc9b34d2e93cbe8a3c780f0c829ebe2fbbc9561fe2b0af 621c87ce34d888782dc907b62eece21f94b951dd +c7aa36af51e9e39744a3d71a524f3228eb0ed8c7816cd863c81daa83bc33394b ecd016dbd912f8176bf9d8ade8b896e0e200ddab +c7b0a7eae7a77dd7a50c57b9221b7955c083c8aa2401b20ab7db4738d93faf88 7b5d9df4c3cff229a132e5684f5e5e7bf6219463 +c7c775e8d426131b0ccf13245977f948f9e6f92f11bcdc748f2e005e8662eb84 84f7ad61f33939cd7bf04d8db04cc9ae7cecd8ed +c7ff2786a6ce3a0531eb2f4ed36456392a5bfe79b2b53e8f55ff987b31058eb1 1e5dd57214dbec1b166c881c1c44c8930923da17 +c85e35eec23dea4089aee7a2dc7f6d937ad7e13c66bdbd7eef37bd6336418609 b10489944b9ead17427551759d180d10203e06ba +c87c2cf84057545f38b55aba1b610af68236dbe0dcd196a81f69a050489572c1 088171c4dcffec240af3df4cfb03580d5bbc2a6e +c9158067646a4f730de9e833b1c57412e5fdbac9391f1d374eacbef4d5906f48 f269af47f67816813da4b4a06935bec131ad7155 +c95f52be90f0078415cd38052e756f4937c8d98dd5404b2833e7d1505744d6dd 89525c4620a1065dd47fd7d3960cc560a289d3ae +c9a1e208a73a83131879e9a986b05691ff7b082a45f79bd840f1b87e3bd34948 7335ffc31f110ce77ba291719841231921dcb17b +c9ae9e3895579effbbee5aa744a268beacd3be882e7306a8361eaffd7de9d89c c16281e3250d7103ad6149960c6918b2ffa459ff +c9dc53358a0d83bee1caae40ee81d752abf4962a9f206702e24a447b766b5bd7 fdbec189efb657c8325962b494875987881a356b +c9ef872fd3e8065a122b989550e08b24e38c91b6598be9d4e8b13cd17f55067f b74eefdaa96e4a781d649d043524fe7620aa7e86 +ca1bf49cb84d97e4e068bb833a4f2f151401bffcd3aad55fdaf496aca3cf7001 1ddf7a3bc63dd2ffbd9438c3b25881de9ac0bff3 +ca2b746be39b1d1ae42925727ddfdc75ba08bc650657647da081931ed1a905fd ce9eddc98909836d71b0cbacbc05951b90880964 +ca31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269 9f13f7d0a9402c681f91dc590cf7b5470e6a77d2 +ca3660522468fd1d803bbf34a9c1685a0cbdd1b923528d69e6103955b03d749b 784536a2a4cb6805d0f64821337ea0b4b20daf0a +ca5237cc6b66c598930504f24af69c3011786d0d9aa5d5da2219f2f10bdde3fe 22237f6c06ff14efd3c233debc41b91c3df29514 +ca72a5c3e0c926b9e8abb95d8cd4110646dfd0dc5f2b283d48b781cbe7ac80e3 7b592ecfb272916e95ec2180a02361f23ed1bb37 +ca9468483c7625c1ec1fc8e19a3a2cc3697d9caa3c4d9df9e51d80791658d4ce 70aab459aa2df8f13b828a06976969ba83612f36 +cacc70435c5fd40c4e75ac083c898a5f35105b072d3863bab571cc63b424cdf0 6b7d1f7996feac885365b05637efc5a4e40a0e5f +cb1bb869adbd4a5bf2861b40afacd0f89906ed7f2d2ff4ceefec444f0166373c 0985c00bcf2bc8403f5d0c4957f3c2c8bc2808b4 +cb252a39ce30cd7b6859222db23553977f7805004501de9bf5ce2b5cb05bee7f e25bc23dbed506f090c7b7794568171d992785d0 +cb282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 5b5b025afb0b4c913b4c338a42934a3863bf3644 +cbdba7ee4f46c2afb421f33275c78d6bef60194f81571e5f9b027416872d1efa 7f5dd37769557fb8ce6eb0f82dbca7394eb94fcc +cc360fbcb49b10d8e9b6c5b95564133fd0ca52489f158e37422fc9d6c09a10fb 61650ac5909362f425cb2c1056cbae080c594b24 +cc3d272d457c7e26c5d611923841511a1766bcd58e5be66433698627e6fb3f9d 90bbaa9a809c3a768d873a9cc7d52b4f3bf3d1b9 +cc8a9debdfda8ea2c6c7ff5e0c9b5d63cc23455e53a1653d42f58c1ad34c8db2 cedb2a22e6914c3bbbed90bbedf8fd2095bf5a7d +cc9e079a90406c7642c6177117674d5e542b08b2db88464291eab80ceefd84ea 19970ce5d4f01cf1e0c540623e7ca9cd6e0d9c7e +cc9f9094162658c03f18809a18b701a42499695da0c257c9e85c33abbf55b43f 1137d90e655031d2c56e925d51af666d0eb0b949 +cca7b385bd43a402abd3527290d1acd5f68bcee3f331bafdc389a2b4d1cee92b 48bef82ec4dd52cd440661294e511e4ec183988e +ccafd013fd2ea60aab89c7548df3ef5ec8ec2e7bfb8c1bd50236790fce491c3c 8d330c18a62adc01b16cdd3cf19fde92ef0dafba +ccb5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f d6c93164c249c8000205dd4ec5cbca1b516d487f +cce687695f024952191b9a882f65d6659e2bf75ff6d70311c35d1a17a7a72be3 028ef0de72da1ad4d1b2ee4a125ede81e3f2ebed +cd0eac51035ebff474d4995e000cbf329bb0793da89b81dabab277ee3977140a 6acffa8eb2a20e3391b5f64c961d7176315f1c1f +cd179a62d181b98a8b7e7dbbeae518694d9cb6314a7ed06c67c5c44c64a6bf17 1764376034ce4f4c899f141c121e1be9167d1e83 +cd9398b37060a3710a0ea0125ea12e0fbfff1ad1f5a835ea880ebab058545bee 0428026e2169038ce215dc7a616cf2f85d9833ba +ce00531bf1b7ed6c57f8dd8a02fd86e7f9e961019962d5ad5093fafb7d851888 d74fb65670afa5bd44b5d9b996a2dc8e409d1d92 +ce354c4274883074fbfc8f1db21fd783e76450619fe4c09ccb8234f308c9ca14 095a3d15445daaad79df8ea9edb4c7b78ba2cfa6 +ce3ac8622b4d6f466e837b0c7cff550d040be27d80b941950b3f5515ad66cfcf 523f154de39d4da383a9e790d513d111a68b7c51 +ce55f9f5ab1d799a9ffaa839539af196d13f35677b3d0761b0fe034e764f8d07 1093c8ff4cb78fcf5f79dbbeedcb6e824bd4e253 +ce601c82543fb1fd705d7c45deceabca8dfcd7b1f156aa4cce6a8ad1efe7d61a efaa56ae9705f9dbc24ff0b27daff60c382efb9a +ce82899d44732510ed59ffdf65793e8b87aba085bbe791592648c2e54149e31c 66a0d3e080b950bd021b41e88344b320ff6cd788 +cea0392913fe99400ea093a60cc9bfdf9bbf5595c3b431c19e55e2fc6817edf2 59b80d2729ddb3883b6033165586e4a8ba206e1b +cea8d32b6932211a24dda86e4f78a7ae9ab96f7473e401a644fd050f3cf98722 238e54bcfff45d477a17cc5a0f51f8ee4bc68bad +cf03eedf2689e76a91a1a230b386e6c9d74167205fe089f44062a96bf7470e89 770de6abbf0bb31fb200f0ea0f86ba2cdafda441 +cf096f21a02aa07d39587714d828e04cd237457a026545866f3fe520ec55646b 3e9e69098af2014a0c3fe9e46cddcb5365dd538b +cf1d995805102eaa9b331c1f6175a3bc900d02712c0136d5eb4994acd628955d 0856dd76baf3d609acb5ffe3b13753cd5a046482 +cf21a1ff89546dc52c6a7d5fcb6d95bdde4210fca4b6fe3e7e61945c09c37ab8 840fb8b7cb13069c2c5f691beb30c8039b384526 +cf329ab6c0737e02eeb28014efc5968aeb023de5ac3bc00f18b254deb46deb0f d6694db21c2f0a7e111ea1b0d9686cadfbd2c7b8 +cf8449d51d5dd28842df865aa457d144e93f12aa1491e553137780ee78f12cc6 1fec05920387b516347793c23c32667b08208fd9 +cf84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0 d07b0f9a8c89f1d9e74dc4fce6421dec5ef8a659 +cf8e9045fd1d957f8010c75d60039bcab5510d34cd0d74ee63d3e5d35c135b6d 28f9832928c52e08c7db5a0d31fde054f51fc475 +cf9d92824b6450da8a2dc21ffe27b92d49b9f4fe7625aef81eb2ffc9023d2d25 60b2b64f87b2e7956a2a1b5cbc5b08ace8140703 +cfaeef6c51a5ff131946932d81b5d0adf3ea351c12c69d11a6ea24c579063914 93bf51fc98691be66471275974cf020974864199 +cfd1c700cc165ffae0f209da9e257bf5a81073b72f99c65b9229ffb7863ce861 54bdf67864a5d9dc16fc548f2748462b3fb63697 +cfdd565f4cbc315760c287d57714852e1a4894eef9c715332fd556f2e114a9f1 bf2fe2acca17d13356ce802ba9dc8343f710dfb7 +d045e845cde8405d26b094b960aea32e0ac01d5822b9e9bed9a62e036e453a1c 6e19d89f34fc5516f5c01542ab0050bf127fabd1 +d04809ed41a6c0d0b713efd6d7e586a7a05287929a296a4b963938c7c01cc317 c545d2d17706399afcf4482163359b03b485fa7c +d048ba2ef4fafa502a44cbc1a50cd58359b9bc243b84a08f541a08ca5f621137 a93f42a5b5e9de40fa645a9ff1e276a021c9542b +d06600e96b3e787b0487b24a3a1ebc255fce0bac6f59d24fcd84814628032faf 56d0888fe6c6144ff623eecb768013569e9458dc +d0665f4f91544ffe89e66d84e7c24b6d87186fbbe3866bdefc5027d8068f1fa3 61950787a1b26ddd5e6f28ccfb381b91c7cf5236 +d088ed0704b46349ee0f8758cb05f1ca5fae4ef74383034d29ecd87b010f6c10 dd95fb53d92f45a9901b5b5e983760a850d98850 +d09b74a21594a6ea4388732a10b7d1d73eae0f94fe3d5f4855fc80444d885372 0c01d80a09b0908856d5494d3c7ac7e6d22f1126 +d0c992aae9cde855b17ac826234a73255ecb09534cdcfa633d90640f6a4324d9 affd84ed8ec7ce67612fe3c12a80f8164b101f6a +d0fc7f52dc42358506e7f3f3be72f5271994abb104b9397ab3e19bb42361504d 53fc32d17276939fc79ed05badaef2db09990016 +d19c2efd2154cd79d3b430619d99438464b038da8540cf407f76119204ab0782 5a646197ed0c131e5326b5fcd95ad88a1fa3f2de +d1a49a45a5f109805fbf478a95715b528644d0e0f10b03dd2dacb4261dbe0094 28cdde6f9faf5a78c048dbc17259f89dd4b26e29 +d1d60af9799c7be57ab4d80713420c9038e360c0164a540247614916996a4af5 3bf9a46136794395a845084011283d07ebc3e958 +d1ddefb4e0093d5a1626d9a1b6757ba2b63a062fbdf2df846f8ed602f2af6166 89b6f61f7b4d3ac809de9427927a8cd515b4bc1e +d1e8c4b8fbda2e1696c9b9ff4e0aa9ed3b7caee2017c01c06525d08e982e2c3b bc23042895607cd8127e5de36f3d330cf20edb57 +d1f527040a42190f2005017fd900e895a77f9f8849deb117f24450a437c27527 749a91d742b7535e1a746ae6708350705e270748 +d1fae456ffe8eac24049a507d590380f8b9e44593bc33e465fcf94be9cc88cee 89cadd27f3c2f56f5c943e55416dab3ee2d4fb40 +d217cd3b30193bf232ef480c7338f40d2abe46d5c104b485edb6e5997e4606cf 383d52d47751e52c11b097d54e14b436bd41ddd8 +d21854ac4b6ae500514b5e6d7874258bb86400934d411ed47345853edc50bdf4 ab3f234e5f33426172ecead8d964b927bb281850 +d252ef75129500a3f78f16cabc6e0a824d4c1bc3e86b9e0a18bad7d389353cc6 7d685c7221b01af31c9808145ccd5a356dc67f27 +d279a299cec8dc4fcfcf8f6bd4c3ec5769628693a3d459f1dde581754c3b2745 d65577e1580cc68402df5827fd9ab4154edc0fe6 +d297d0f4a84ed8d5cbeac8e262906504d1238d59e9bb0adc45699eaf1da67372 199447c2a54e662c9db370c9a6c89219a32f9fc3 +d29c10f22acb667743faf888201a16d5a52dfdd3168c434bc7b6418d24f0bc87 2d38f94e73d3a6df5d650ddc6835d75498437d22 +d2ab425d6092770366bc3dff5276e3a869221bc7b6d22e99c089d556e7eb8331 d01f7573ac34c2f502bd1cf18cde73480c741151 +d36979c8da3118ed133cc999f0d4bb1aac029f59b2a6975893f41ed2d5c97342 a73891d7c5aab19750b0f1999120759e6ab0eb32 +d37ab9a95d602c7979d3dfae35fca327b313836f11e8511e1f29dd3eebe0e5aa 3d1f0e5811ec9f56650e70f8b27acaba9f34e602 +d3834b1c2da88ed1e98a742f7e8f326cd53ab464cbc726887d9cb019eed6ebc2 1a895dd787a5699c21d7925e9cdffa66f23605c4 +d3c2cc39665ee7d8e3f16069e0e27f35813e27c802d5a98be106dda765effd1d 3f541ffa6803066aa2db492a8229e0b35b5862c3 +d3cc02106ea9b78cc5d1ebf609a4931036ae3ff283ced382252ee713ddd5417b 5690f02e87e4fd31dfa9fd7c9c01aba03603cde8 +d3e3ade60c7ad970e4ee6eeab5fded32ee932e29d6dcb3656d7ed3bc9cfbe3ff bc403249eeeb37ce4cf73dc66711c13c5d19639e +d3fa679ce39641a538cc30576fd8980999a6edbebfb8a4ad187dd406e2dc4156 bc874e116fb4e785081344c2ae39aed3bd072288 +d3faee8d75868598e59a88279f0509327844800b54e51fa96283d3c8e3e7578f 6533aadc21270052d8d05835b1e30d8b13a2f164 +d451619d5bee5af825ded81f99b3862ae60fe8b182ff10b72db8d005c173dd6d a7335c5170f4a963c8d67782fa35222d03d8a16b +d455043371b8730cc74f280b64e25183233fdd330acf54ff76526861d513bda5 f92446126451b27205551041c92f5f7f84bf6d99 +d4698b848903dbdef9f5ffcc0496b1c82bcff777107819638591bc5c93fa2942 dba81bc591c332550be284f9414350861b6b066a +d46a8a40c5bba0b053951a958ab021f19eedf8fa4e5a75fe1927d33ca498c973 a920633902d32a6fcba87c729b14e08341bfa097 +d47674eaf38c4547b4a490e5fcfa7d68ca91e8aeed2386942daf07bd1f96ca97 f29249340c11b069e46b70fc145c1d7fbe213b94 +d4923d7b828b897cc6628e6e026fc956a347002f62e1f6f52c022c2c9c664954 418382dff1ffb8bdfba833f4d8bbcde58b1e7f47 +d4fab9558c0aae0a9010ac44434c891492e63358a30960b40e48b0d0e5f70694 9a9e5abd7e7d2ce46fd6dff590360da875b81247 +d4fb1e12bb875235a16a5037ebaa06cca35204c1fa89c9abff02282beb74820b 9655ff12d31716c9049912eb6378a305346652c4 +d4ff08d0ea45b6d8eb47fafd430e2496b132b2a0617b6cd77809eab2612f9823 eab36c05f0ca151dc5732210223d0abc9e9ab321 +d509bbc6e4420c5d0b833045f466b02c02922bc4bee55f91d4b6751eb1a87074 061605020c8f212d696a2a3d28e20c7091c09443 +d509fa76ff5944e25f48c2476736b6239a53f0463cba6ebc488464d087951951 100746511cc45c9f1ad6721c4ef5be49222fee4d +d5473cb18ca962ea72ddc29df047c3fc1aba13e5cef68e600f5563135730b357 7e02841efbb3d464e61bd4dee8d6031afccc6a70 +d58aa1f4d8c35426d90279e0a0b81669fbe25bd200f969fe75440f8022f98ec7 b81dd80e8cb77046b6196a1dec3f8f356307fcfb +d5a9b3e16e2756a30bc62fa92552f73d9d3e02a344f047e3b24ae1392ae90c69 59c513fca0ea668f5c815cfff903279a4e859731 +d5abb2bf61fa4675ce068d8842ac6bcbe7c097d035ed5e79ab83694b92e3b8ae d5ef6a1381ec4cbb9b78979d2ea7c6459a48efa4 +d5c255a2cc319613a97ec8b66dd44e5a8c4d8328a3559840627511c05625b4bb 9514445e534ffb33b50c8752a7c9267e3db3a4bb +d5d0a313ddd0d6d451ce2178da7d7ba19e1f968f3603b80193699ad5a8b51744 068817d917a1c86a5fc33e7925fbee9e2128d648 +d5dee571df1d0b72a7e820d9a08d770561776d044894b626c3ea556cb804b64b ed473e2431c5c013f214e0ee975e3535b6196679 +d6569dea7a2dbf053eb60492f35432622090918137696fd26f01fc69b4dcd67d b32af570d6088f5ec58b08d0f7ad3a74265667f4 +d6a3a68c9c9ac80171333351683d26ca70792679a830155047ca4f85207be32a d4b9769056bc3f7d22860c87fffbe9d4a9065c03 +d6d2e87e6de8690efd26b8c5b58de28dcb3c9bd2b7659eccf34468d71a7a4478 bf55f407d6d9418e51f42ea7a3a6aadf17388349 +d70b0c834e8a1efde1158c15681472b3c7d12da4d1167ed62581e0ca64d184e7 a9e9df3b695e91894be66c30366f0e2bcc73ff96 +d714f59c54cb4bc5a57b9c68b44bf21e2ce63a45f1030baf115ab3c6e7af9709 ef810961d54761c4954df5566da929cba3459561 +d73158b62753ea4eed99124b3dcdc4c873a4ac449204a932b46f46a4da4c4778 8e5a43b2781f75268ca9cbb71993b377cd1d6106 +d79e913b4137117b7f8fc2a8d184373f657d6f71bbeaca0fe83b7757ce486108 def425bf8568b9c1e20879bf5be6f9c52b7361c4 +d7a0fa6d2cc7b83596b51ba9029ee05c553c79eaf3f22298a407c7cbfde61d07 f8588696bcb81e3cf38fc47f2536cf8e25331c7a +d81b2839dc735c3ea774b024636a3f0cb13fe75373e9d1e81365fb1013090e81 89ec4c021467168e5507cf91b712b6873ebe8ade +d88b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca 521d87c1ec3aef9824daf6d96cc0ae3710766d91 +d8bbcaceb334b4a84eae3db74b9201390a13f9cdc2705f5fd68d1c124baf745b 126a72cbe290b3a6a8caf8732e886d3d21c6bed1 +d91051fa024310949c3ea677750d8dded9dd9091ff410826644521c852b15ef6 259ad1a27fc30dc0167d8dd506cb800bd128abc5 +d91df71dc184de6b93ac0d3fced0cdccbdb3f8bedfa433f57aeec533ec657861 65ee31bd49393a45dac5fd1593aba0d332c829f6 +d963a010dcf03c6b21fca8bb99725da1d2443a9dc0a610c5ecbd25f46858a7a2 2bf7e29956df690eb8462a49aa1f8ad9a85047bc +d970d1a6296d149bfb8283b8b4a9a6f7e9ee320c5d46a5ef216e10400df2d281 90d4d2f0fc362beabbbf76b4ffda0828229c198d +d9750ce4daa558910affe057a0a21ff130abafffa5cd3b9bd8857ccff7875543 b370dc5521c2dea03a3d2d1d1209b2691de80323 +d990f65cc071cb0668259dd2f7c1acff8710fe2196fc56e4f43496208ec0edc2 eb7100b307f2c35e7a2d1b05125cb58702da2897 +d9c20b21c4037980a27b4cf716771f1877ccebce89f08b9d07f5b35f4bfff8e4 42281e007e15713713b2ff4324d60360332e194e +da38f65b32fb03ce332e0c4238edae0a733e4cd793d849522bb4e0bdd0af608c 8ef9060a954118a698fc10e20acdc430566a100f +db5ffb15114676bd1246296817d68013ead717c9efd093ffcbbccc805e2b50dd ccb8c44ae6da1b71f65c26324aa39ca35206271b +dbc10ea8c211fef2c138839705ebcf10b6d1261f9af7fb1da206fb8b19050222 bbbf64b0d5c1b962db9d58cadeff78c11bd5c515 +dbd89b614245ebb6f859f64e47f5caccdc93ca05609e83ca5ca2ba478f2d6f31 436cda88b3b747c66549eb0fc0c89d1102f967c1 +dbf3df54acf12a89a09b6f1e7fa9aa29685f419e9a595ec6389181ecd9880bf0 c470e0e0a392d57a681df5d337fd64fbbd9928f2 +dc1d3d6e12bd685f89221ca5051ed749c4ccd59083e2c26d815e6326b8bf8cd7 98822557cf753bf7219fb362c32c9330d35e5f60 +dc8a2c27640a60ce6b6c5424325abfd5d6ca1ee7fcdbf49b7772c107fc0a6cc5 0cf02ff92d5c08516ff2253f59daad1c4c582d8f +dd2e1251481bdf69c7e256ee65ce366ecd4ea43c6aaa61b30bc2d4be17b3fe57 c9280f9f1b0482a337c3250f951431030f9fc91b +dd60b4f51b88aef523ee1cbd6e71b8166b9427c3656cd2d1524cacd9e768e4fb c15648cbd059b92c177586ab1701a167222c7681 +dd6281df774f9e6af8b68a79fbd7c3bb5eab20c85c3385980b8c98141d080c72 36fd65cf283151b18e93dcb5e625fefec79762d9 +dd81a2885ff56d3d275ea52e79809d2c2665fc64949bf3add600d0b19d169f59 2733c6ad2584625f9849e9824927829190fadad1 +dd9b1d5ca653752cef167e034324198971ab6f2f38f3db9db571cb2985759f00 8ed341c55ed4d6f4cdc8bf4f0ca18a08c93f6962 +ddc8a477387d01547a809399b37acb2456faf0b527b5795b3caa5c2d1cd3ecfe 9b3577eda0e6bb13d18a304763b91665575adfd3 +ddcf2e7ae93da5f152a605d82c821b530f88d8f09e5600a09e44e7c89463b3cb 2061cfee59ecd5074bfcc0eac7dc5b3637d290f5 +de11650c5186daa434fa253c05372fc92c34c69af7afdc4137aa62adffe216d5 f9395075e3c7bef6cee2ca5140610896f4855c72 +de82cc04db42cf7ec85c7d6847517ab322ffd81674669fff30b2092c4d10d319 71628d165d4273c15f1ad2320ec382c8f29f566a +deae56ba25b1e1fd5c971fc027ba413e4efebc393c1edab6aa887881d111ce46 21648b45356a49c76d58acf3590bd98ad12b3d58 +deb5d5c69e8ec5f11412724decfcb6913ae501fcecd381350521e3dd30a84cb3 098ac57a13165290bad223e4949fc6f13960b604 +decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f a65fedf39aefe402d3bb6e24df4d4f5fe4547750 +def8ae3f46a6251e846b25383d6989ff7931be0906da0e6049d25669df260c7e 53495ca95b5d1fb2db527d35e6a8825bea12b51f +df21e6bbf78ab81e603c9a0eb8a3f2c2edc2659101553405ca57f48d13d49773 5189d8ea8bb07be6dbcc0aaae2294f2b1840d335 +df70ff5ce6431e87f4ecb612cf69aee57b2d8c514e51da1728af6e140ad2ef9d 4870103f0d85d833fafcac9f3eea8b17f9151125 +df7592900e9c0c8191e659cdb9b7a02f0eafc08333ca122bb65f90c466993bbf bbb0d9beb03f3f2d2d81c2199525e7b459f1216a +df773ded7bc7739ffa988358979e1ecf8cba729f6eb8234fe48bd3804d909194 5673434fff718d93ac738a03403442f16abeecba +df7c2829b803b4d008d50676565314253dc23a2f8b80a9d2abec2f8fc027e1f1 6a133ac4d1120d3bbf563d61338bbcdfad4b6ebe +df7ca8caf338f34197d9bc1e346c66690321f27dedcef793f2eb8f99976cfd57 9eb7976448bf684dfb41993ef6c76098978ff933 +e01ed58736f8d22949babb858e5b7c9e9e8c6da45f125c92ff74e4ebc30f678f 6c48d15ce6a8b8cc415eacb9192c5a7b93c3dc94 +e0243b96d2d6668ddccbf4e1744c365b8bb828f0edccc3adeed9cee2fcc0718b 11bb049bdddf1d203dfe8fd6031c4dba328a7525 +e08ee0ca30e32a912f051a443280fd3bbcf593f5e23b5c5a60cd40dfb8b31c38 e3805dff27023ffc98b1e8bb5f8e71e613f134f8 +e08ff41efc23feee5e8dadf3d3901ddbd0e80d4f39c1a893a9b1a4ca6e457c2b 24f3b8d8e8c8f3a1b495bac67d0c493d425ab6e3 +e0920162225ac182b02b72a4999007ce63491fcc0d5fb5b130c8ab6ace46d5e9 36b7cdb6a1a2e685c7141406808366d4c4b9f98e +e0c95bbc34c21872d8a284055d1861b542037744cfcec048c261b5dcaf0d8c4b 15f71b9429442ed616b66b986814e94f983d4050 +e0d289871eae81a3c89d3760d65ec61601a007c010a129ee02f3762961389ac5 e96aa1e25e4ad7dab5d397227ba8f14d302bb393 +e0e636769168575a7c6a7f262ab2e3f24d15aef148224a75949b19bec3204555 09b79f8c2ea7aac83efa086f2ee2377b11c4986e +e13a3a7900774366f6927fbd5006c86b97e0c500873120a4ee73fea158b0a0a7 1ae36609fa336f5d3373eedc18f00c4f05194931 +e153a6b70868bf54a33528407abb749a5ec4a278e36ad587585a807439e75792 001d938dbe69b6251f4a03cf374235c72fd0a0d2 +e15e12b19203d0869a45ad94af16bbad57b91dcfd8d70a23291da3eb39d1db70 87a63e99a903a009e04760bc4fdc195ea25922ee +e1853ed6756da2e3c8c3902b9d373d06158e0e42daf8cd1b364059bcf705825c b42f3f731f6709055f7baf5bce3f39881d4c62ac +e1e91e53ba1b4c377a3a86f493517ed6af162dfcd79191cf0c6ed6cd1846eb64 9c3d59f42c90513a69cb72d3680656ecd6fcc309 +e1f458e5f0d9f51709fecc937aa77830d32d5a06c35fb5a71f37beeaeb5e14e5 9e300586fb4dbcd7a7e7592e8bd36bae946bcf61 +e2040ad7e0b914aab16f77a98f629a8df48c3d5c6346c591a5bf0b6bb19cc6b1 d4627ae2e5d2510b641c806f023335206bb4b575 +e207a266e228414023223530eb77c64b10f2f5124f3354deb45aff04c1db98b9 c059510ad1b45ab58390e042d7dee1ac46703854 +e207de850c812ada0f0196143822e6f60237a7de1f189bad75ad55337706ba73 eb1228f46b3c2adf90a4a4f4093dc12434c71bcf +e2199e776f22c6c9a6f60a74711cf168914dddabef1082bb18027baa9d224c0d c6dbfe02ff6359247b49b0bfbd55dbe659a90def +e2b8d0a4730f8678f5c0bf72165f12a58aea0b5bba85405da8e17aa3e4c672ab d7ae061cab626313265730ce7c33203ea371b497 +e305332f5c4735afd3f441fb9fe7c270df8e8cd77ee73e6f9c00c5cb2cd74c5f 66ae897b9fa0efb9ca0097531be2be366c82d125 +e32cf5f9c0e63f632a2e85c2f57b4d74e352d382fd9e449c5660d5f42696b5b9 88db4b005a4f3883f127d8ff0fa39ea7344d5b35 +e3c7bbc5d5fc396e20897c2124898b3601c494e6051e1a5162938d664e872477 5e95e5342bb02f75240eb1b5978246e2f10c2810 +e43f7ff09259618dd58412fa34610db1c46f6b1f11d7e61c74d8ee91f15afee1 c62b80f26a9b4253aab953c3d860d18ee042bd5c +e486f397c337cb2ab4ed93f7e4bdfe0e96eb2f19b4666b4189d60cc961db4d56 40aa5f7bd409d7467369818208e9eeacae39523f +e488ea88853da32feb570d822cd2458bb7b525f7c946d5956967ea6c2ebde4d5 6a10662131e014c3753a6490644428760fbf1c9a +e4891afff0042d2e3ec44f05b0c973d9bac7bff295b053b614f62377aebb4014 ab9c9b9ee553fd11f9f6d5534e09337c769904d8 +e4cb9e225a817b0f5695fc52c0ce05860a23b345e8338987c7038fd68b46747f 8dfd652805e877abaca7383ad28d8eaa5b9a7e04 +e50daa85ddf947a33ee311516011a786e12a1e5cd1b9b60fe34fcff79af97c6e 0703e6ccf92d6f62c552c7be1af34bfc45fafe34 +e51625cf3375b401cb07f27c258fbe0deed231836136c72670c9beb02f022b52 a45b803b8bc607c1d8fe2ad0f559359574231548 +e51d1f186aaa3e81f6545e45e30138f520f9cddac17f121e3a6dfff5f4286185 d9bb4c3ba46cf7de51761352c7b04a2d49633bcd +e5714ecc9e216d2d23e0c9265aeb7f94917f5445a06e988f40677020a557c1b4 ff7c7576aad8b6d87100eb1a62c0bfcdd6e24e01 +e5a8af67e6a6ef9eab9989c16c19bfeb1503a4d9b3c2100e6a98d07155b0c16f 789527fb457255cc90b2b79dab55b67023c7b8d2 +e5dd60bc585584073807044262f8c611f3c23cbba01562c81ceeb7a046226b28 2e3e1ba4b6f21a028ff2d8f17febfe22fe93afe3 +e5ea38108d603a6ed8dbb0b8455abe6d971f2d60920207e67ba646d267ece305 6f4e2c42d9138bfbf3e0f908f1308828cc6f2178 +e60d23fb634bb7ba008578d69619ecb715555c539e5e1a09995c9fc1a2fc8f89 4324de04e80f5cee8a6d8d72ae7eff4a7d7e632e +e625f75b91fa95253eeb059ff3a4329509939b2af57d406ca6eadbd385441136 6d8e93b8384392ca44be9693c850dfa04b9469af +e64b4ef0c042862ed0071ccc2b2dc8766392d69a1c69ddf9f6e04f61b79bf424 f8ed08a88d6f982e040feeeb2240fec0b5e4b151 +e64ee640eb592c7f2d9592b700c416b96e395d114c13752555ac220d850bc739 2b1ad31b77c1c1e9de3af7863e3916f2fcbdd1ee +e6ffc310d72a72943da3796f9716d5bf6ec72c477eba5d55cd44b542ed50bb8c 2cdc4544233b503a5aff7dd1baa4ba8743fef7ab +e702c3dc4fc84a70aa440a441e905982e17e8c5bf0bdfb58ccf6862ddfe02d2f 963dd0fe0fdb9581df0159efeff615de0771709f +e71ffd37f2119fcce2a02248cfc164238040138822152ce6c8959b35486036ef 06d2a40e85256ae9426dcf3847b0384c547c2252 +e7481fdba48ca2efe4928cbd4a631e313450ec901be85092bfc2428544ea7dfb 1fd98a61779c499739cbc3f34f351eb40d3cb4ee +e7675558e5b9b4a8f0eb006d18979a6019cc8493836ad5b7f06ca75acce29892 02f498e4bb85187f9fbc0b0e2eee06d5c9c27a0d +e77505da06f70c52541f5c07441cf6205da770f7a4fee730757d12962172b93b ed10f7124a75cec05081b751801b6a0a0e42e56c +e77be3b1ba3bd6211b30953bbcb31b52e8c0ac0c68f89ed62bc708caa489cd2d f89099c671ed8df50e040b7a62989b451366d084 +e84639624a2e2f2f89bd215bf640e61374cc0f3385b646d0d59a85bd7baafd62 c770eeb29c9c95d27b9df2d30b6f47d62e78d07b +e85c03d559c7b05c4b6ed5b796dd42a5d018e9f3cfcfdd4645a110e3695f32d6 5071d53225a4dad9e07bd0c9f04a45d0948bad06 +e86d64b9d11cfd3d2a06196fcb3785d0b716500d06b1daba9f582a0368c78b27 0617d6a892682a5d4b4081a759f84ad57b28c577 +e8965fae4238a25fba359135d62087dd0e7fccbae626d5004601b6e9c50461f8 0e7fa1fe6837a46fad03e7fa9a3df0815364982d +e8a865ff7fc36bb0f8cc2460fd7b2977189b5b6144c69e14500d5478cbc9ad6d a79612435aa5b1996ff8ab70e789c8864e55ad00 +e8bbf40ee280bc43b33c04df2250903b75e92f2497e91759cf1cad753c23be6c fc58168adf502d0c0ef614c3111a7038fc8c09c8 +e8dcc2ca3e4e882fb1a824c72543408d014ceaaa04f0657432baab8ee69e1195 8d762aafd2108c0f4fee8133dd2213e3658c66af +e8ef4c26d3c9ea277e490ff6e7d270093e14dbd1c01a3f9d78dc972bac88d87c 46fccbd742b48fecb83be48e16885b86bb299027 +e8fd9ef5c4543a16e74c80cd9bbee3e2b387072a14c7bc653e8f96864b7a4bfe d1adf1be64991ef9c94d836d8075f232b73b640f +e91c29c398d4ba343251bf45e859ee4820c2e5cabef1148565809bfeaf000f5f 2e997a35970424b1d6a6de26f5182898c6bcb1f2 +e92d166df3d560dde2087eb6bb14e2951618a37fced2fba2cce748ea2e159382 a651bba3601c34d3ea21c2cd5b898fad29854433 +e9570c71d360e851fd1945be15a2c4875ebeeba9d9dca7848b72c0e35a562bb0 6457fa0a84176ab4efb565b64170dff4175d68b9 +e986254345a65c0b48ecb229c1f47d9c7b8c03b1dd8cf6cf5bb6a877efac7086 97ba57f428e6d74fdbbe4144b8c9bb46b9ba5315 +e9b7c2531cbe327ebf55552276f330835034014be5e5d603b9f1e1ee5dc28465 9285c652cadce66ec115e1b079648e64a9bf0c67 +e9b83772d89c5948ee301059939d6c37f803e5f6055894a2d169715421d3f0ab 5fc06ac83625f97d0b03000c4c224aa86c3a350f +e9ba6f1e38d419a6e3e66a8859ab291f70cc042192866fe0f0c76229f7acf18d 498bc0906810bd43c6fbc73385fecb7f2d04be3a +ea2ec997ce482c0d1d2b83f7e5bb6c3ec634c2018b8dd4e4a67cb3a108bf1f57 3a2aabdce127a367aa27a4c27025199230e26c15 +ea462d69e697a2d9c70e2445739290c6a4a2d649561c0544f5bb027f9a90248b 58b0cbea74c160c61ec70768568e150c4d31f633 +ea926306b1bab6d3f25f45609907eb6dff91a1460b25e63bf4a0494c70e7a269 9fcf811e00fa469688943a9152c16d4ee90fb9a9 +eacf702e5d2a14313d65279dfe65a9d1be0188107a3cbe5573b268cebe483876 1eea99f3a439dfa65fc9d86f7fb4bc2338fd7dc0 +eb128916f9450004d9468de8c8458ded8db58ba01fbf36201aa16f7255269e30 bc4153d7a1038ee96648f97f75aff45c22501412 +eb38ab81898b34d0060be1e38fffc4e04545dc24ab8aa517550d801e1d98865b 798d19706dbf082766df35d66087d40b1a685a7d +eb61f971c9c8dceec3dfe54fbedbaa9a07e2ebbb0cd44fc38617431fa213ea44 aeef9c0679084b1a3048b81831d4ef3d9d7eb10f +ebb24827556f1cfccf502c909626674796e7e1e1cfd08ca430f3490e9f1622f2 e6fe30dc427d63dde4107715ec219c92a017c864 +ebd4e93650254bfaeae56dbe5ba868427274f867d5d859e406f5981f3ce454f9 4df1521318774135d66161a37fd1a06423d0dc7d +ebead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1 1f67fc4386b2d171e0d21be1c447e12660561f9b +ec05ea37ad15fbda3b72d3de3cddfd82b411da97a4dd5dea9bd4c5be71f2fdbf 89528ef257ac5bd32f4980146df41872732ba2ac +ec0afa8e0f8d3f924ae29e9ce6e314f06a3327a596763e7c0afea13c6deff8d3 2066aa3e804f6344b131387e17e64dd20df24cf8 +ec0e609764571fd852ef82c0f2272fd4940fc593dfa28a1a2aa4118f764babd0 f5121b1e6ac7f7e40c08e5c072c05a936d6237c6 +ec12fa9a4f7c288ea3ca253b0cde3250f01f2420de8da0e882e15f7c1e875d3d 2c09181f38234634e478130191323c7f74958c33 +ec1866b39026366e69ab8e167c15312c27f5eda4c0afdf9367ac3d76f56bf8e7 4e7e9f60b7e2049b7f5697daf133161a18ef688f +ec21b1d718a46155b66932c5ea66255e651ff00131904f5c15b13e278ed394ab ee3f1ebf8ca0d0bb7040a888fcefc14873cc1484 +ec9d0c0503395aba37c14bad6f94e30773413e51f4c4192f637be5079922e9b8 37334566347064df74299d0479ba44ee7cd844e4 +ecbf01d97e2c2d6c04e428632edbfe9b67dbbc2f22967c35610d634f8534ece2 838bceb4b87e6a65042f955c90fdc809b34ba3ba +ecc250a19bce5bb071ba73708879b69c8d93cc655ba47964873087260da82532 872ad88d663eb6eac5365bd64f0d2ed2820b2017 +ed1002021629a4a96d2d53168633fd4bec45fc52a4dc3514d09becfb1f4846d1 52121c9b19c8a7625126766102b2a8145c1c0632 +ed10f9520d5f0d6bbd9c467ed83a239df0cba94ae9031602e02f6ad6256c459e 4fc3874b118752e40de556b1c3e7b4a9f1737d00 +ed41f28b6dec2c88eb9b4825a1ec79f770ddd524ff8e8c8dc117697cd8f3f039 1f3ae5e22cbbd99f678fddf3eb1a86726cbca831 +ed994d1acd346cb201da40004ad4f59cd42270537a086298c89f81873ed078d5 0fa9bc03e0832bd0e4f9b9a4735a3301bb37a8d7 +edf6e0dbfaf8a7ad89b05b5768981eeedd7a2bda4b1d0fae07aa2a9d49bb39df 32679a9544d83e5403202c4d5efb61ad02492847 +ee16964dd77360202417cb8e958673a3be107fcb1551a7b71f6985d2102c472e 7c297e8eb9ca1fedca286fb5877353c7356c380d +ee4981c3f6d4d973a1f7d99998848fe4b778ed2762a31cae46f9090bf47aa0e1 a7c182c59414cece10c819989bce3f1247f4eacc +eed74bc11a8f6e68a63c093bcb7b793c3a77b18239d9340a75dd690b4e790dc1 ddc1e625381d4fcb00107f5640030d0dbedcfd0e +eee9bb8a199012d24a2f94bb633930f5bbdd4b59d7aeddd430d7d5b0c0abde4a 960ca1d7799e02b72ca828373c3fff04e2cf0334 +ef08c989d6195b9dbc09f3053b94f642d84925d5d5f3a2dfd28be482d77f1d97 3aa9ebb63e642246946ca397c8498df46fe6db0a +ef461668dec669ddde121af3bfcc11f173be86c4fee529001cc95e22ead79a2b 8e9e1e4f95e44ecfc1230a91036257ccbefe605b +ef5dd9207c9041337f20818723f4baa003a8e0e55646ca726a62b4e7f831583c e3c672819ce10dd2207fd8df6a52eade74f00996 +ef9f17d60e921db4ae02169736de6f4da6b6ea76cf3419ca30494fbe2a40f0ac 77bad856f6306f1c1243ce0f4e363477205d3cbb +efc4b8fd4b0b2586fce256b107ee2a052d11d26f99d85ce0478c3d49d1b2186b 8edc2805f1f11b63e44bf81f4557f8b473612b69 +f00a3d864ac7cc74bfc0d238ba10b171fe7b6f5f2dbe7aa08ea07f99865bafd2 5bcc94ea49a378e97626436c5b28be17b54eeac6 +f02b8fcaa64271d82d9021dfea900a09fd74997a61c21642da82fee6e6b20f21 f3687dc858ea475e3691834bf4ec00a2fe8c3b27 +f039acbef00c0dffe624ad124760b27a085358a81f9ddfc071a2eb2e44035097 5841342a0a9453dca640dc53d2ea18ed70409c86 +f043d8f788777ec076d2e6072c36ecebc8772847e7effbd7fb04256181ea1e65 f09aecf46ef68bfb4da47e593082805ebc644d5a +f08cad6328b43e133362254e1468ead13b1a5f39a22d45fd956a90a0a860b401 32db4c9153f750f183f5f6a11e4a1e5f093a739a +f09899f54bb2a1feee2a205c24b5c2e9ffad5ead4fe15b2f21285ae0c973ec60 18f4b6b56c084ab586170cc47e5f8d55ffe1add5 +f0a7a226654089b999b5c92b34d304a9a7c7c874a6484b2437909a6dd225e382 a4f863af4d0055126435eb01278940aaf727d2b3 +f0b7aba704d8816667f0465022228bbd93ec18502f1ba75d0e4105c49ddf1a99 0b30644f7cc496eaceba0130713c7be51021bd72 +f1982fbf726534f88311eb2eac67ebb828c10bbedbe596ddb956413b5e9e1f17 4033d6b8cb15107abc2288b64f405d595f4d68f1 +f1b2af9976ce14a31b39e96b27b97a72606393c5f87c500b933795959fbc5122 9bdb75947178f72df30314d66bc9bcc90df643d7 +f1d8da2c2039b56e385ff2f76f1f2585b5d708ef1b8b5527f253a335ada68d83 88ccfe32b8a8455a7d6e709666494c33c7387050 +f28ac33bebcfce2579b1e8e91bd6749c7b06a7e5825bd6fc2375e3e724f20612 5235041760e8186f96bd757bfa378ce5adc90b4b +f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736 944c0f6e4dfa41595e6eb3ceecdb14f50fe18162 +f2b78ab9d3df69f23d3e9a9e03b63a466a06c760c4eead34f90e10187f86acc2 487addc854add2a93bfb25f29cd7a5c2ce3bc40f +f2c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978 bd758010071961f28336333bc41e9c64c9a64866 +f2ccc71ac9c89dd36f44b911594484abb969c62f640f9cbf60c853896b6d1d13 93a527ab2bf2555e28fbdfdf31960326c4055d2f +f2d5807c5279cfdfcf302fe93d1ac90025a03a57132ea1d852f80f0ba0a1158e 898dabc73fa99e31f117c366498046cb33c8a24b +f2f432405c8298ed098d704a9590d7f41451b16c834520580fc1270fae0f8b17 bce499af70d434e89c9a110374f1b7c6dd3621df +f31459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562 75057dd4114e74cca1d750d0aee1647c903cb60a +f32ab793f10b04cea82cb3bda32a0b9595b5f69ca89608f88d3572914de6f720 feb1bd8ab25180e447be9862c852a45143e98375 +f34224f82528a95407c2202f0340d08ce68435c02c440932d4a098ace60907cb 022d8b4e47f1ee40270ad3beafaeb5c025ea1b76 +f355aa3c4819d7e2e235e2638c11eefff5ea1872c2304889bee6e843da7a382b 3d40a87326d25342300fb50d78744671440c5408 +f362826c827aa3bcbeb3ff8b71bba08d7440b89ab53fe95d61b8922d01f46e28 e0743ad4031231e71700abdc6fdbe94f189d20e5 +f3c7b2b4491f7edb2150c74d063ef6f49c81ed818d8b447215f3b6c3f6091cbd ea59a210dbab63d18bbb616fc27087d68dc59777 +f4076ec94766226a8ee1d52c84ffe070d8d58cfa111e845bfc7e3e99e314f19f 43288a0733c1dc0a506a1e45087bb7f9bd87d047 +f430e24c70cbfe819b5f201e3ebc34a3dad712ceb08ee4f13fd8bd07a6fa6d44 ea28d017cc7a2b9a329dec6bd91236f5424d72b6 +f47bbbbe82207974769ce35769b74de8abc56d4770efca4a2cb755d55c150390 6a840ebc2b332a23243a5590965eefaeeb460bcb +f47e3b2db30186f550ec8c5c0182409514e10cdd3e66a936e0ce57349df8eddf bf05bcbabc31dc7627bccd33535c5bcc6be2498e +f47ffce6447e860dd48eefbbd7aacc7aca083eefbf2df94f9b96113c58c08206 ed8ab62f1be3062011dbb3c6033952e4657abd94 +f54e47e3fb25c0fdedf1a58eae2aafa499f95c821b702623778455cbe8cba53b 2e85427e16f2823de8324bb47d07f6f78a38746c +f5656eab7d016da5fbe176ba06fd46f809e8f3163fc143887055f4dadfd829a2 86726c5a8c1200f732412fd3ec7601f3d2100801 +f5a821e9f70620ad45e86113ee0ee42211caedeb0db3ff3088500dda553c046f 00a27258320f37a893d4c8e52a991997706c730c +f5be968ddbfc5aba6d676304dd513eaf8b5fb877235c1d5b8c76e8a93f24bd0c 5fcb851f4bbff2968e56b9792ce8310a2eb50002 +f5bf0317f8e2f3280613bd0f1574b5d9f2f0760b0335091ed3fabcab327af221 1fd9c75f95b22df3f52a0891dd36d3c18e9fc0d2 +f5c6feab3b2b51d205f6bc4b3d8fa2a8307ea5f61031b711204778104bfdcb4a 61bfa90a8f4300c31966e3309cd2461507b02264 +f5ef3ebcfc8f054e80fd190746057638615b9479d3917b594e00838ae2b69890 7b7d17f9b6bcb21de339efa2e1ffdef5f15cc5d3 +f5f86c720ab7d820ae97ab3ee2efb1009109a3a54dafb4e986b4f810b7016d21 8c7833b361ef6a4c68d72865bbf312fc3d806ce1 +f63c392c2e03143c9c3b37fdeea26691076a424f336b45bbafe3dcb4537ad29f 129156d84004e59c894c7e273ba9b5824bb9ea0f +f63c4d6eb84809e54f87f531118170c28758305df7888104365e74742cdac390 2ee716e9a389bfbb76261371c1e65348ed1b9a38 +f64b9eec0c8df9ad7a5a2a50b9bbdb9f8da4b55c9f3176cde323aff07de313c8 e2b5e2857b75278155944594c2beee36079b6b1d +f64f370eb4726c47b6c90efe54938119ebc024e49fe07e7eec0207b42827b98b e7ed799defef0499d47c8d8330955ab387ad8852 +f65ec16ed7554295cfdd98a0a5b67c1995c0b7b2a6778c8b36ea2e5b94cc5902 75f541f109d783d298a774cb9f6e1e7891965949 +f6633a3ba290c6e9f4b37ee0d3532e47cdf16cb2a43a7049ff3a3210044e3121 29c52c3a0cda152f11dd39d6217b46f6f246bb40 +f66691f32eb9b23a029b43251bfa994d16481fe97903057e121b76e4e78f6ff4 c01c3fae7251098d7af1b459bcd0786e81d4616d +f66c29d989b01cf3feb6fb2bb7329f8acab284376c3b860f3d7e0fff904c4298 532a2f46e4a8ffc15bb5a13d9a02364197115d63 +f69bc64f14b90aa437c84a4b4d0e4ace3bb8c911a16ea8d77a6ed781c1956a02 9396ec28d49129250ff79463cf588e1a8512984f +f69f3d4f7b8713dbb3e3adf1f64d6b8f88eadb77bf4f358fc28c32c756b5df9f d42c7e287c39463759ee11e4fe7bc9d722e487f6 +f705853cd543d8f69b6e57c3bd580ae4cd8b6ebcac63460fb322e0133cd0dfa9 406ee23fcb2900af9eaaff243f20ec9e07494920 +f718026e4badd0b9b38989a5538a627858e3319cd8b155925f00daff5768c138 7f73f247cd1be8def4d07063ddb72bbb2e6ade45 +f76f10b126720bb7d7577603fb3b54f0cf0ca722fdbc29c924379f4ccff97c14 09197a342b635784bfbaddcf6b8afc01d142f8f1 +f7c7c0e0d32eefee600c90a65bfca1564c643d30451276d3a4085930586db81d 8a086f872a3bb2cf8e437f0e374ea18e5cbaa899 +f7cbb8c92f729f08203f027870bcf2a1a71c94199408c3bc6bb7f06859212989 187613a431e241f9aff9884bfee28c3ca82ae1eb +f7e772589bf8ab335ea3a7bb2c2d8158755e0b2bc08b34d80eaf50d070fe6607 083f644dce33ceb8318ab7f1fcae008d6bae2b7d +f7ee8274a6a3963b96ba1b041f25ff1136cbb2ff07ffe04566f321f02f4099dc 2b8988ea7a90b45116bc4d4101fbdc678c48b410 +f7f06b26124f95366b331eea19be3182ff9dc0ea208ea1de2235e22249683a1e fafedcb78f9bf54e54f0a819ad1461c2ae3b4463 +f82729f2cc042ac51302f20404d9e218148448a3ed6d6c7936b063aa4532eb80 4d5b2a922151ea86d986e012971896622f5d3f77 +f88e32bdeb55ef0690249212cf64c42a4255101a95b74e3dbf78e6ba34f67def ac7c4c450b9a90d66957511688dcddcd011281fd +f8b2eda314c8c39ac692033eade0bc766ca39c8e8c0a40fc0a25c67fcda825da 3a33c7b37f55d5bd30ff944625499e64919b9a9d +f8b45f792840019909fd35f9dcb98082b3bc39373268d467e1b00f1da5ac71e5 cff54d138945ef4de384e9d2759291d0c13ea90a +f8e91f003a1b5b5c5c842f5a27be8f5054c71bbd97ec516d0d8293f4eff40372 160148d67a3f12f0f393e23bc4b7e4e58c0eb921 +f92a00b48199229aedc6c30dc4157eb40b364efc1d843f91b67fe7113681ecb3 5130f2343aa863e7f17ddc0c65f71a993b061a07 +f979948512aa2fcdcad17b3fb76c3cbb196f28f88af5aecb1c29b2f2bbfa51a1 ca9840588d1718b996d4e0987b2caee0931e92f2 +f99100a033a521b0bb725db49ff10e6f27bf21ee6a18c3bfdfce294a3c70eb93 d4043ee9d97031e94f205110996d2381dd26c540 +f9bc81246e5d5f703bc5cfcf7dc637f1ac952dfbb4ece2f84d68d84e1bef99ff 713417dfe4608035e6615918b57a6edd78a3955f +f9e46cd8f3f9eba5b9530820893f31ec9b135df075cde7e1331ec1834616a405 29a61a7f86f3703914b82d1d06b1c6e2775c684d +fa2a467607e74eeb7d2865994f8a105837ad47c7f34d930a219544a187899dae 4428dfb78da0d5cb229763c8ff3278f6f4ee5bfc +fa2ded3190944dfaf1a56eee66b9e298edc098aa834ed57952f04771f7ad5f45 de141d4bb983dad861835e181e7ecd5692a58aba +fa50bed23c21de9e38ae6492dda179c028b9b618cfffec62311cd8e52bd90a12 d0f0c327029d770d1b10efc7a6c93080ed7d6844 +fa6a2b7e588e57115d2772c0d5250d886757404fc510a5956be8de4926e94c01 4f1355c91100d12f9e7202f91b245df0c110867c +fa9b280330449aaf7fbd9709144d458cd54593a29045c6562a840a8580a80554 8cc293419bf3b5eaf51c8a90f3a1a86c2b415121 +faa7ca59426e17f6b34fa407d06cd634faaabeb4abe26df12296e05a17c98eef bfe05675d4e8f6b59d50932add8790f1a06b10ee +fafc05a1d0b7614ba32f428eb52f3439ffbfed9a817e5ae069364cfc3fa3e4d4 fe1ca6bd22b5d8353ce6c2f3aba80805c438a7a5 +fb21dc7e0744c92621f4d94df822be5c275d509431d647c5e1cdd0cee85a57ad 2e7e8953cd4d7bd21729b29e1f7eae3ccf941b20 +fb56a4825bdf67a9ccfaf2d3962c16c49294fb565743f8ef3b3adf6ff721e729 47c31f584e09d79f3674987582a60c7bb6b673c0 +fb74b8e06683d2154d9ca50317e5e3581e1d0dea24229d4aad83ff0296e384bb 44bc212b00ca8cbdcc2751bd48534ee274318445 +fb93630decf348bd577c39423a6905f7f119e371eb098d0199e888fbee4154fe 0be51ee21b376c8848207ef5608aa7b3798d5020 +fbb6481c1ce45e504fe1233d0aaf228384b9739908d69d753da9fa7baffdb64a bc5aba43f785c144c9b5c6406ffbce6a7fec5304 +fbc60a067b3286e1547e78c5d195099580125b2b80e8e44716a313a4ebb66e6f 7a3b12fc4fb66960d4660e3e24af873bc07ac932 +fbc712543e696976405b7a070164abf6ac7f379b66f74dc2538563d4380a2004 7a6cf8153799563464d6f1761e55352c327b4122 +fc2157d0df676f018ad7a001a8150b8c5cae4313e50a69021f90cc82d8c0366d f9e43c1b9b200572edf0dbf333184151db14b711 +fc36ce993654a9c2f2ba5944b90d9fbb8b52bf3e45da4efcff1b137812573e63 8ba79944d51268d80b65372d508cf4d0ba239a4d +fc7d032290d1d7af6b021e29f190d7a534aa3a31ecd9fd72e4a246ba7dc13e96 edc21d065b345f6af9ab4c3f07acc3f137436b27 +fc899c4e3fd5e61b944f9a9d4bc143888a80f4a11836024d441d0fef00cd355d 75636f4771c5ccf864814b83431ab7cebb091506 +fcc409c956764fbd4904916ec1a1dfb00ad9492ad11aa85fc272b94ff2412517 427c5e85811a8c7062c2c4621342e4e9c445817d +fcfa61e2c38f220b1cf11a62ae7f6120b699c6f4fcdc0b0e02d31cfc2f1550f4 661900993cf98d9854e4d9c8584ae215e1eba148 +fd21b90bdf88489115886ad6a66f842e4b3b99f2c47c74f107a3012206a0975d dd583895e15b6ddbcca3ee7e8a1f7d8b39c25d1e +fd6ab50ef7201a166b711edd386b9649ea052562bbab8a09ae5714f5f9c5a24d c215be4120385fc5f358e8a0609c94f6998184e7 +fd77de19e02087045739e074b6368724fd41d66a3e196b96c4092efc76b9a325 f83b69a6538188e9dad0b6dc09e5476bf36c9a1e +fdf50c9b7119e7018b4a2f5c3235802104674e7d467f86608c7fee73c22321df b2bc567f25e3e07d0d6d55fab16f33423d863107 +fdf78aeb5b52940bbb069a7e2ae5495b1e77fcf4f9bb699bbe2b8372526bd3b3 b31b32f52231610f4e8c2c98e1ba5834b323555d +fe191677f9b0be2d6232dffe0d8cf68edec0106e1c57efb1f084205ab56389b4 88bb8fe275f64c587e9d2f960cffa059d2b76fb7 +fe20b68ee605e6b7aeed310d188b6b597f7c184dc10166f645a683d77bdcfd7a 03770969aa16f40d4192c733408f152fc17b1f19 +fe9a766dd36f1a877fcec8eedede85556ff031d42678699c5350ee9117498a28 339a0ed36a822ace408b5084f8e3bc3e63b868aa +fea0d6a630e0c517db1c57ab26a4229f48ac0cb61563777509633198bb60eadf a5274d8192aca15b24b1c7ddcba42726f5418ea4 +feae789c354caab8c9f38b38e18c04c41d7381d402eab1ca87befdeb5fa62539 65654b679591001c402b3925a31ea80f9e083969 +fec50a36447cf7f413ee48757b0f3d80322f3939d984556bda46600139cb5a89 0234c186bacb5144ca13f69f0dc9bfcc8ec7a075 +ff04d2f2105e9fc8f103e4b5840b46b3914a317e9c53cf523be81ca59cb5f023 6d34f01ce0ab2a3a198bd43ec250a5062d8e1217 +ff14a5e654a9963042a8183b2c7c356bb52440bc05f62ec193ba77a94c1f5290 bed3229b555ecd18bc7824c3ead7d2372b5f4f5a +ff1854a73752a8779bf1d78e840d85b7e406fcc16a4cb7d1bb6a35bc60da49bb a52aa75b56d579790985af4ade1e73437d50b414 +ff3f1e98e17179dc0503307f2a01732096ea309288c2ceddcf71dbcce290691a fe9d965dc040c687edc1acefeb5a7919b14c6340 +ff503ebb130051b71bc883c83861389f4f4a66ebcfd5c05d5b2a7b805e45e773 143b052f17fbb3ac1310ecdb2bce4a10b9d2821e +ff77a7e762c77408890442669da2a3a6ae2d0f130e8b953ce85edd0b81cb6f5e f7cce7c2d30260da7e0cc04e14ec618b832ce327 +ff77d10d1335090c32371bfb5179f915266de4425afaf4387dfca0bf2f6f98a2 86ff216d8fe665daecde956b38de7ec33b074477 +ffbbfce866283c7571b728f367b998d05b3767cb26600953db1b237b442fc0bd 16a67770b7d8d72317c4b775213c23a8bd74f5e0 +fff5cbc10ffea865d69aba64082ae17479c522e8e0305e678469749282bf0a18 afadc73a392f8cc8e2cc77dd62a7433dd3bafa8c diff --git a/tests/resources/testrepo_256.git/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045 b/tests/resources/testrepo_256.git/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045 new file mode 100644 index 00000000000..8d8d1d8e828 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045 differ diff --git a/tests/resources/testrepo_256.git/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 b/tests/resources/testrepo_256.git/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 new file mode 100644 index 00000000000..c7fbd7e9ec8 --- /dev/null +++ b/tests/resources/testrepo_256.git/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 @@ -0,0 +1 @@ +x[NÅ0 ùî*üt•8§B°”Øq ¢iPš»º~GgŽ42zo œ±/kªÂ^rfK‚¡FO%BI"NªÉdwVe¡b8º”vb‘·ß<õ\°LçB6Ýk< ÅUtœÅ–¢%¦ÄÈÅYÿ#SµÔCè·ü\ßcÂù<Ž5~ô„7í:›<ªÎž¯¯žÛñÑßÁ:›n3&‚WƒÆl7½£–þSß>Kkt…Ú½ kÀõäҦʳéµý÷_‘ \ No newline at end of file diff --git a/tests/resources/testrepo_256.git/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b b/tests/resources/testrepo_256.git/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b new file mode 100644 index 00000000000..cdfafaca76c Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b differ diff --git a/tests/resources/testrepo_256.git/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750 b/tests/resources/testrepo_256.git/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750 new file mode 100644 index 00000000000..b135eccdafa Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750 differ diff --git a/tests/resources/testrepo_256.git/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f b/tests/resources/testrepo_256.git/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f new file mode 100644 index 00000000000..58b2d0932a9 --- /dev/null +++ b/tests/resources/testrepo_256.git/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f @@ -0,0 +1 @@ +x ̱ƒ0 ÐÔžÂ#ÈB k„¾¹ä‘ j²}(^ûrÝ«Ùãô¶¬²„i3ÏÃg¾’õ _ÜœÝ8H§Ö¡N] “Š”ü}P·ó8žYò®o;¾o\Wùw×  \ No newline at end of file diff --git a/tests/resources/testrepo_256.git/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498 b/tests/resources/testrepo_256.git/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498 new file mode 100644 index 00000000000..97157644b1b Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498 differ diff --git a/tests/resources/testrepo_256.git/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e b/tests/resources/testrepo_256.git/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e new file mode 100644 index 00000000000..554d191b3ac Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e differ diff --git a/tests/resources/testrepo_256.git/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 b/tests/resources/testrepo_256.git/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 new file mode 100644 index 00000000000..d5c518ecc8c Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 differ diff --git a/tests/resources/testrepo_256.git/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 b/tests/resources/testrepo_256.git/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 new file mode 100644 index 00000000000..31aa9e5f53d Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 differ diff --git a/tests/resources/testrepo_256.git/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a b/tests/resources/testrepo_256.git/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a new file mode 100644 index 00000000000..66dc15db486 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a differ diff --git a/tests/resources/testrepo_256.git/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a b/tests/resources/testrepo_256.git/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a new file mode 100644 index 00000000000..bee6a42d79b Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a differ diff --git a/tests/resources/testrepo_256.git/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2 b/tests/resources/testrepo_256.git/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2 new file mode 100644 index 00000000000..3dfd5463ba9 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2 differ diff --git a/tests/resources/testrepo_256.git/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 b/tests/resources/testrepo_256.git/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 new file mode 100644 index 00000000000..cedb2a22e69 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 differ diff --git a/tests/resources/testrepo_256.git/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b b/tests/resources/testrepo_256.git/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b new file mode 100644 index 00000000000..3abc52f4164 --- /dev/null +++ b/tests/resources/testrepo_256.git/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b @@ -0,0 +1 @@ +xÌM‚0†a×=Å\@3ÃôcŒWнi;-`, ooq÷~‹ï)¾²æ°„WŠ$EŸ3£¡Î¶B™}#) ‹ö-Æ$È„dŒI”¥íˆ­±±9ÑY•ï;A\¦i,ªT{ð«ÍÇ×!û÷›Žãr`t¼Z¶Ö¬t=ší?¬˜Û~Vø1ÅfT \ No newline at end of file diff --git a/tests/resources/testrepo_256.git/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 b/tests/resources/testrepo_256.git/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 new file mode 100644 index 00000000000..71122389437 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 differ diff --git a/tests/resources/testrepo_256.git/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5 b/tests/resources/testrepo_256.git/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5 new file mode 100644 index 00000000000..d0d7e736e53 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5 differ diff --git a/tests/resources/testrepo_256.git/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd b/tests/resources/testrepo_256.git/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd new file mode 100644 index 00000000000..8dc1932822c Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd differ diff --git a/tests/resources/testrepo_256.git/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b b/tests/resources/testrepo_256.git/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b new file mode 100644 index 00000000000..dd993131694 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b differ diff --git a/tests/resources/testrepo_256.git/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848 b/tests/resources/testrepo_256.git/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848 new file mode 100644 index 00000000000..39e27c06a34 --- /dev/null +++ b/tests/resources/testrepo_256.git/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848 @@ -0,0 +1 @@ +x1N1E©÷îRÆöØKÑPÒqÏxœ”]£#®Spº¯'ý÷¥/}Û.ÃøäŸÆ¡j\å1‹KRP€8RDImªPšºÌ!´ˆD>“pÈÍú¦ìš[¾Ë¡û0µºàµ•¬›D ‚¸Œ‚hëSf !ƒ‹šÙÆ@©éµ-üyÀZ S>©¢å\çn¬ÖSŒ ]É€Cl¸äÂT[#GM|Û-å>Ö~˜úSŽj¾Ö¾Ýún^uÒGz?_ÆzçéÛ›±.y—,b0ÏS ˤó¡ÿí/ŸzœÕðQvYÍévçÇÉ·“¹ì£›~­Ë/(jrS \ No newline at end of file diff --git a/tests/resources/testrepo_256.git/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f b/tests/resources/testrepo_256.git/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f new file mode 100644 index 00000000000..17fae64f464 --- /dev/null +++ b/tests/resources/testrepo_256.git/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f @@ -0,0 +1,3 @@ +xuKj1D³žSô¤–Üê† !'H. OgÀ3 +²Œ¯9É6»¢¨÷ rݶµ2>õ¦ +d=‹Š²³³-bŒ5ì‹“dò०Pî(DÁeŽÉ{Òé+6Ý;Ì>±‹4KÆÄ†­¦rLfˆ Á,ö¿Ýo}© >ríÞ–˜ë/×üNç-®—ç\·W°<[Çâá`‚1ÓhÇ£® ÞË=¶ŸKÝ®Z‡ñ‘Nçµ/·ôÇ{"‚ï?¥÷¼À¯ê®Ó7bÆW \ No newline at end of file diff --git a/tests/resources/testrepo_256.git/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e b/tests/resources/testrepo_256.git/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e new file mode 100644 index 00000000000..0bece845b96 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e differ diff --git a/tests/resources/testrepo_256.git/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3 b/tests/resources/testrepo_256.git/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3 new file mode 100644 index 00000000000..112998d4257 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3 differ diff --git a/tests/resources/testrepo_256.git/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7 b/tests/resources/testrepo_256.git/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7 new file mode 100644 index 00000000000..860cad13cf8 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7 differ diff --git a/tests/resources/testrepo_256.git/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed b/tests/resources/testrepo_256.git/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed new file mode 100644 index 00000000000..4c973ea83af Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed differ diff --git a/tests/resources/testrepo_256.git/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6 b/tests/resources/testrepo_256.git/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6 new file mode 100644 index 00000000000..67b84c462af Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6 differ diff --git a/tests/resources/testrepo_256.git/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f b/tests/resources/testrepo_256.git/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f new file mode 100644 index 00000000000..993a62b1637 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f differ diff --git a/tests/resources/testrepo_256.git/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c b/tests/resources/testrepo_256.git/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c new file mode 100644 index 00000000000..70bf64e16bf --- /dev/null +++ b/tests/resources/testrepo_256.git/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c @@ -0,0 +1,2 @@ +xÏAj1 …á®çÚ‚-i,B(]µ(tiÙ2’™ ƒ=~]zƒnÿÅ÷xe_×¥Æù©f]òb¨µ…$Öa­9)yI Ù'ñ‚2i«èæYÉÇTU[f™îù°­ƒWVaÔ©5sùu,QãÜ +¢¤€¤:„P’OœCp*£ä)?ú×~Àùó'x]6x˹\­^àÜöýžóz¿Ù©ìë‘õIÞêÏ]³äœ¾àWÖÞµS¹ Æ&%æ%għeæ¤ê•T”0L;ØÏ4ýq/ã—M·ÎÞ[¢:ëU¶fâk¥½:<ö šÌ ®Pmy©å`å¹ñ—&îæOwñ›¹ÚS_úãŸû±§8omòùlø PpËùƒ‹¨TO \ No newline at end of file diff --git a/tests/resources/testrepo_256.git/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 b/tests/resources/testrepo_256.git/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 new file mode 100644 index 00000000000..2419974cbc2 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 differ diff --git a/tests/resources/testrepo_256.git/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640 b/tests/resources/testrepo_256.git/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640 new file mode 100644 index 00000000000..b390250e308 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640 differ diff --git a/tests/resources/testrepo_256.git/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5 b/tests/resources/testrepo_256.git/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5 new file mode 100644 index 00000000000..18a7f61c29e Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5 differ diff --git a/tests/resources/testrepo_256.git/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd b/tests/resources/testrepo_256.git/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd new file mode 100644 index 00000000000..d1c032fce34 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd differ diff --git a/tests/resources/testrepo_256.git/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827 b/tests/resources/testrepo_256.git/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827 new file mode 100644 index 00000000000..c6da2ff7ace --- /dev/null +++ b/tests/resources/testrepo_256.git/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827 @@ -0,0 +1 @@ +xuQj1 DûíSè)²WëÕB)д°e%^¨×Åqèõë´ýÍß0Ì{0RKÙ:8vO½©‚å³µleåà–e1ršEO³LŽ0aò8?Ú™¯ÐtBŠäBÒàÉG'Q**MYÉŸ'áhY…íL¸õ\¼KíN9HÝáå*¿áx)aû|–Z^Áº…ØN¼pA4£º6xKß¡%øÈµ\ï´ã=/[Ï·øÏ“÷¼zD7x¼‰-ì’áÏuWó¯W \ No newline at end of file diff --git a/tests/resources/testrepo_256.git/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890 b/tests/resources/testrepo_256.git/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890 new file mode 100644 index 00000000000..b1df3bdd5cf Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890 differ diff --git a/tests/resources/testrepo_256.git/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 b/tests/resources/testrepo_256.git/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 new file mode 100644 index 00000000000..3e36331ea66 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 differ diff --git a/tests/resources/testrepo_256.git/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23 b/tests/resources/testrepo_256.git/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23 new file mode 100644 index 00000000000..9bb5b623bdb Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23 differ diff --git a/tests/resources/testrepo_256.git/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19 b/tests/resources/testrepo_256.git/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19 new file mode 100644 index 00000000000..3cbf7e6b714 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19 differ diff --git a/tests/resources/testrepo_256.git/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5 b/tests/resources/testrepo_256.git/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5 new file mode 100644 index 00000000000..be8b99bba80 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5 differ diff --git a/tests/resources/testrepo_256.git/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 b/tests/resources/testrepo_256.git/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 new file mode 100644 index 00000000000..9d2ceb1fff9 --- /dev/null +++ b/tests/resources/testrepo_256.git/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 @@ -0,0 +1,2 @@ +xŒÁ‚0D=÷+önbvK[JbŒ?àÍxßÖ¥¢Öƒ/™I^æð” P»1=%+X’5IœO6“´uà =V•“*pDòì]h±v˜„G/„÷HÜ,o £¿€r1Ë@eÖŠLpë² +ž´ƒ£ò0¾¾çÒs÷>ä±?ÙH¾ŽÎ!ìÑ"s}t3,åͳIÿmø3‘ \ No newline at end of file diff --git a/tests/resources/testrepo_256.git/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269 b/tests/resources/testrepo_256.git/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269 new file mode 100644 index 00000000000..cfcdac30697 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269 differ diff --git a/tests/resources/testrepo_256.git/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 b/tests/resources/testrepo_256.git/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 new file mode 100644 index 00000000000..77d9ec27d54 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 differ diff --git a/tests/resources/testrepo_256.git/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f b/tests/resources/testrepo_256.git/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f new file mode 100644 index 00000000000..a67d6e647cc Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f differ diff --git a/tests/resources/testrepo_256.git/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0 b/tests/resources/testrepo_256.git/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0 new file mode 100644 index 00000000000..ec37060e379 --- /dev/null +++ b/tests/resources/testrepo_256.git/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0 @@ -0,0 +1,3 @@ +x•ŽA +Â0E]ç³ëBÌ$© ˆx¯ ™dª…ÆHŒ ·7xWÞãÃ%祑޴*<Æ}À“qVˆœ ÖZFЉP‹'q/yާ(–ÙšQÏèUxµ[©Ö% +~8É;äÇ*»XòИ‰¼vä`«QkÕm¿Ð䯑:—&O)IþÀp]ÚåÞÝ%V M˜kɰ.Ü©/××Eÿ \ No newline at end of file diff --git a/tests/resources/testrepo_256.git/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca b/tests/resources/testrepo_256.git/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca new file mode 100644 index 00000000000..6845087e53a --- /dev/null +++ b/tests/resources/testrepo_256.git/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca @@ -0,0 +1 @@ +x5ÌÁjÃ0 €áýêyP”ÈŽ(£ÐëÖW²#‡´q4Rõ°·_/=þ|ðÏÐôǦ›d"ñMH=î"¦2娡FOÄsšÚÉû.¥ëK;â%bãsi¹wö÷+VMÎ^gÞ656™ÆW¦ã[fÙa{®«é]68I•}ÉÇ"{åÇy®¼¬Ç¬õ "Š}ð‰-¢sߪwøá\.Zëb÷Ø=B \ No newline at end of file diff --git a/tests/resources/testrepo_256.git/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f b/tests/resources/testrepo_256.git/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f new file mode 100644 index 00000000000..a53ab84cf2c Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f differ diff --git a/tests/resources/testrepo_256.git/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1 b/tests/resources/testrepo_256.git/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1 new file mode 100644 index 00000000000..225c45734e0 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1 differ diff --git a/tests/resources/testrepo_256.git/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736 b/tests/resources/testrepo_256.git/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736 new file mode 100644 index 00000000000..58d51e50232 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736 differ diff --git a/tests/resources/testrepo_256.git/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978 b/tests/resources/testrepo_256.git/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978 new file mode 100644 index 00000000000..04bf5eb0621 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978 differ diff --git a/tests/resources/testrepo_256.git/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562 b/tests/resources/testrepo_256.git/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562 new file mode 100644 index 00000000000..37a289ecc18 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562 differ diff --git a/tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idx b/tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idx new file mode 100644 index 00000000000..897e8a478fc Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idx differ diff --git a/tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.pack b/tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.pack new file mode 100644 index 00000000000..9c8557886c8 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.pack differ diff --git a/tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx b/tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx new file mode 100644 index 00000000000..9e2ec99c546 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx differ diff --git a/tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.pack b/tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.pack new file mode 100644 index 00000000000..66cd292a257 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.pack differ diff --git a/tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idx b/tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idx new file mode 100644 index 00000000000..1d197e87085 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idx differ diff --git a/tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.pack b/tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.pack new file mode 100644 index 00000000000..5b615e15c27 Binary files /dev/null and b/tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.pack differ diff --git a/tests/resources/testrepo_256.git/packed-refs b/tests/resources/testrepo_256.git/packed-refs new file mode 100644 index 00000000000..995685f0805 --- /dev/null +++ b/tests/resources/testrepo_256.git/packed-refs @@ -0,0 +1,3 @@ +# pack-refs with: peeled sorted +66fe8385c6378bfa5ca5573bd0fdd773e4eadb0e86416b483f2c50c839859ecb refs/heads/packed +cb282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 refs/heads/packed-test diff --git a/tests/resources/testrepo_256.git/refs/blobs/annotated_tag_to_blob b/tests/resources/testrepo_256.git/refs/blobs/annotated_tag_to_blob new file mode 100644 index 00000000000..2723f8909fe --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/blobs/annotated_tag_to_blob @@ -0,0 +1 @@ +d88b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca diff --git a/tests/resources/testrepo_256.git/refs/heads/br2 b/tests/resources/testrepo_256.git/refs/heads/br2 new file mode 100644 index 00000000000..2abd96e0992 --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/heads/br2 @@ -0,0 +1 @@ +a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 diff --git a/tests/resources/testrepo_256.git/refs/heads/cannot-fetch b/tests/resources/testrepo_256.git/refs/heads/cannot-fetch new file mode 100644 index 00000000000..2abd96e0992 --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/heads/cannot-fetch @@ -0,0 +1 @@ +a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 diff --git a/tests/resources/testrepo_256.git/refs/heads/chomped b/tests/resources/testrepo_256.git/refs/heads/chomped new file mode 100644 index 00000000000..de0d95305df --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/heads/chomped @@ -0,0 +1 @@ +4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 diff --git a/tests/resources/testrepo_256.git/refs/heads/haacked b/tests/resources/testrepo_256.git/refs/heads/haacked new file mode 100644 index 00000000000..5feda4d4178 --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/heads/haacked @@ -0,0 +1 @@ +7e9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c diff --git a/tests/resources/testrepo_256.git/refs/heads/master b/tests/resources/testrepo_256.git/refs/heads/master new file mode 100644 index 00000000000..106231c4cfa --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/heads/master @@ -0,0 +1 @@ +decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f diff --git a/tests/resources/testrepo_256.git/refs/heads/not-good b/tests/resources/testrepo_256.git/refs/heads/not-good new file mode 100644 index 00000000000..106231c4cfa --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/heads/not-good @@ -0,0 +1 @@ +decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f diff --git a/tests/resources/testrepo_256.git/refs/heads/packed-test b/tests/resources/testrepo_256.git/refs/heads/packed-test new file mode 100644 index 00000000000..7c2c5e4383f --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/heads/packed-test @@ -0,0 +1 @@ +43e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b diff --git a/tests/resources/testrepo_256.git/refs/heads/subtrees b/tests/resources/testrepo_256.git/refs/heads/subtrees new file mode 100644 index 00000000000..3b352e1adff --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/heads/subtrees @@ -0,0 +1 @@ +0118010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 diff --git a/tests/resources/testrepo_256.git/refs/heads/test b/tests/resources/testrepo_256.git/refs/heads/test new file mode 100644 index 00000000000..de0d95305df --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/heads/test @@ -0,0 +1 @@ +4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 diff --git a/tests/resources/testrepo_256.git/refs/heads/track-local b/tests/resources/testrepo_256.git/refs/heads/track-local new file mode 100644 index 00000000000..8f6ecbe2b82 --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/heads/track-local @@ -0,0 +1 @@ +b83624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 diff --git a/tests/resources/testrepo_256.git/refs/heads/trailing b/tests/resources/testrepo_256.git/refs/heads/trailing new file mode 100644 index 00000000000..de0d95305df --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/heads/trailing @@ -0,0 +1 @@ +4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 diff --git a/tests/resources/testrepo_256.git/refs/heads/with-empty-log b/tests/resources/testrepo_256.git/refs/heads/with-empty-log new file mode 100644 index 00000000000..bd392a7d2ae --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/heads/with-empty-log @@ -0,0 +1 @@ +7e4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f diff --git a/tests/resources/testrepo_256.git/refs/notes/fanout b/tests/resources/testrepo_256.git/refs/notes/fanout new file mode 100644 index 00000000000..307abb49c2a --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/notes/fanout @@ -0,0 +1 @@ +cf84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0 diff --git a/tests/resources/testrepo_256.git/refs/remotes/test/master b/tests/resources/testrepo_256.git/refs/remotes/test/master new file mode 100644 index 00000000000..ee4aa140b33 --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/remotes/test/master @@ -0,0 +1 @@ +1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 diff --git a/tests/resources/testrepo_256.git/refs/tags/annotated_tag_to_blob b/tests/resources/testrepo_256.git/refs/tags/annotated_tag_to_blob new file mode 100644 index 00000000000..2723f8909fe --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/tags/annotated_tag_to_blob @@ -0,0 +1 @@ +d88b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca diff --git a/tests/resources/testrepo_256.git/refs/tags/e90810b b/tests/resources/testrepo_256.git/refs/tags/e90810b new file mode 100644 index 00000000000..eb8846a05a5 --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/tags/e90810b @@ -0,0 +1 @@ +21e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 diff --git a/tests/resources/testrepo_256.git/refs/tags/hard_tag b/tests/resources/testrepo_256.git/refs/tags/hard_tag new file mode 100644 index 00000000000..05dfe06158e --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/tags/hard_tag @@ -0,0 +1 @@ +34f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b diff --git a/tests/resources/testrepo_256.git/refs/tags/point_to_blob b/tests/resources/testrepo_256.git/refs/tags/point_to_blob new file mode 100644 index 00000000000..5efdc777d2c --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/tags/point_to_blob @@ -0,0 +1 @@ +33e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 diff --git a/tests/resources/testrepo_256.git/refs/tags/taggerless b/tests/resources/testrepo_256.git/refs/tags/taggerless new file mode 100644 index 00000000000..45547b7fa0c --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/tags/taggerless @@ -0,0 +1 @@ +14bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f diff --git a/tests/resources/testrepo_256.git/refs/tags/test b/tests/resources/testrepo_256.git/refs/tags/test new file mode 100644 index 00000000000..a7d2fef64c4 --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/tags/test @@ -0,0 +1 @@ +c258f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 diff --git a/tests/resources/testrepo_256.git/refs/tags/wrapped_tag b/tests/resources/testrepo_256.git/refs/tags/wrapped_tag new file mode 100644 index 00000000000..05dfe06158e --- /dev/null +++ b/tests/resources/testrepo_256.git/refs/tags/wrapped_tag @@ -0,0 +1 @@ +34f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b diff --git a/tests/resources/testrepo_256/.gitted/HEAD b/tests/resources/testrepo_256/.gitted/HEAD new file mode 100644 index 00000000000..cb089cd89a7 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/tests/resources/testrepo_256/.gitted/config b/tests/resources/testrepo_256/.gitted/config new file mode 100644 index 00000000000..ba975e1be69 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/config @@ -0,0 +1,15 @@ +[core] + repositoryformatversion = 1 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true +[remote "origin"] + url = /Users/ethomson/Personal/Projects/libgit2/libgit2/tests/resources/testrepo_256.git + fetch = +refs/heads/*:refs/remotes/origin/* +[extensions] + objectformat = sha256 +[branch "master"] + remote = origin + merge = refs/heads/master diff --git a/tests/resources/testrepo_256/.gitted/description b/tests/resources/testrepo_256/.gitted/description new file mode 100644 index 00000000000..498b267a8c7 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/tests/resources/testrepo_256/.gitted/index b/tests/resources/testrepo_256/.gitted/index new file mode 100644 index 00000000000..6b18426ca7e Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/index differ diff --git a/tests/resources/testrepo_256/.gitted/info/exclude b/tests/resources/testrepo_256/.gitted/info/exclude new file mode 100644 index 00000000000..a5196d1be8f --- /dev/null +++ b/tests/resources/testrepo_256/.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/testrepo_256/.gitted/logs/HEAD b/tests/resources/testrepo_256/.gitted/logs/HEAD new file mode 100644 index 00000000000..35923030200 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/logs/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000000000000000000000000000 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Edward Thomson 1680595792 +0100 clone: from /Users/ethomson/Personal/Projects/libgit2/libgit2/tests/resources/testrepo_256.git diff --git a/tests/resources/testrepo_256/.gitted/logs/refs/heads/master b/tests/resources/testrepo_256/.gitted/logs/refs/heads/master new file mode 100644 index 00000000000..35923030200 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000000000000000000000000000 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Edward Thomson 1680595792 +0100 clone: from /Users/ethomson/Personal/Projects/libgit2/libgit2/tests/resources/testrepo_256.git diff --git a/tests/resources/testrepo_256/.gitted/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo_256/.gitted/logs/refs/remotes/origin/HEAD new file mode 100644 index 00000000000..35923030200 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/logs/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000000000000000000000000000 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Edward Thomson 1680595792 +0100 clone: from /Users/ethomson/Personal/Projects/libgit2/libgit2/tests/resources/testrepo_256.git diff --git a/tests/resources/testrepo_256/.gitted/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045 b/tests/resources/testrepo_256/.gitted/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045 new file mode 100644 index 00000000000..8d8d1d8e828 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 b/tests/resources/testrepo_256/.gitted/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 new file mode 100644 index 00000000000..c7fbd7e9ec8 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 @@ -0,0 +1 @@ +x[NÅ0 ùî*üt•8§B°”Øq ¢iPš»º~GgŽ42zo œ±/kªÂ^rfK‚¡FO%BI"NªÉdwVe¡b8º”vb‘·ß<õ\°LçB6Ýk< ÅUtœÅ–¢%¦ÄÈÅYÿ#SµÔCè·ü\ßcÂù<Ž5~ô„7í:›<ªÎž¯¯žÛñÑßÁ:›n3&‚WƒÆl7½£–þSß>Kkt…Ú½ kÀõäҦʳéµý÷_‘ \ No newline at end of file diff --git a/tests/resources/testrepo_256/.gitted/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b b/tests/resources/testrepo_256/.gitted/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b new file mode 100644 index 00000000000..cdfafaca76c Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b differ diff --git a/tests/resources/testrepo_256/.gitted/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750 b/tests/resources/testrepo_256/.gitted/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750 new file mode 100644 index 00000000000..b135eccdafa Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f b/tests/resources/testrepo_256/.gitted/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f new file mode 100644 index 00000000000..58b2d0932a9 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f @@ -0,0 +1 @@ +x ̱ƒ0 ÐÔžÂ#ÈB k„¾¹ä‘ j²}(^ûrÝ«Ùãô¶¬²„i3ÏÃg¾’õ _ÜœÝ8H§Ö¡N] “Š”ü}P·ó8žYò®o;¾o\Wùw×  \ No newline at end of file diff --git a/tests/resources/testrepo_256/.gitted/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498 b/tests/resources/testrepo_256/.gitted/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498 new file mode 100644 index 00000000000..97157644b1b Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e b/tests/resources/testrepo_256/.gitted/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e new file mode 100644 index 00000000000..554d191b3ac Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e differ diff --git a/tests/resources/testrepo_256/.gitted/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 b/tests/resources/testrepo_256/.gitted/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 new file mode 100644 index 00000000000..d5c518ecc8c Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 b/tests/resources/testrepo_256/.gitted/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 new file mode 100644 index 00000000000..31aa9e5f53d Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a b/tests/resources/testrepo_256/.gitted/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a new file mode 100644 index 00000000000..66dc15db486 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a differ diff --git a/tests/resources/testrepo_256/.gitted/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a b/tests/resources/testrepo_256/.gitted/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a new file mode 100644 index 00000000000..bee6a42d79b Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a differ diff --git a/tests/resources/testrepo_256/.gitted/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2 b/tests/resources/testrepo_256/.gitted/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2 new file mode 100644 index 00000000000..3dfd5463ba9 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 b/tests/resources/testrepo_256/.gitted/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 new file mode 100644 index 00000000000..cedb2a22e69 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b b/tests/resources/testrepo_256/.gitted/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b new file mode 100644 index 00000000000..3abc52f4164 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b @@ -0,0 +1 @@ +xÌM‚0†a×=Å\@3ÃôcŒWнi;-`, ooq÷~‹ï)¾²æ°„WŠ$EŸ3£¡Î¶B™}#) ‹ö-Æ$È„dŒI”¥íˆ­±±9ÑY•ï;A\¦i,ªT{ð«ÍÇ×!û÷›Žãr`t¼Z¶Ö¬t=ší?¬˜Û~Vø1ÅfT \ No newline at end of file diff --git a/tests/resources/testrepo_256/.gitted/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 b/tests/resources/testrepo_256/.gitted/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 new file mode 100644 index 00000000000..71122389437 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5 b/tests/resources/testrepo_256/.gitted/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5 new file mode 100644 index 00000000000..d0d7e736e53 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd b/tests/resources/testrepo_256/.gitted/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd new file mode 100644 index 00000000000..8dc1932822c Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd differ diff --git a/tests/resources/testrepo_256/.gitted/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b b/tests/resources/testrepo_256/.gitted/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b new file mode 100644 index 00000000000..dd993131694 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b differ diff --git a/tests/resources/testrepo_256/.gitted/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848 b/tests/resources/testrepo_256/.gitted/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848 new file mode 100644 index 00000000000..39e27c06a34 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848 @@ -0,0 +1 @@ +x1N1E©÷îRÆöØKÑPÒqÏxœ”]£#®Spº¯'ý÷¥/}Û.ÃøäŸÆ¡j\å1‹KRP€8RDImªPšºÌ!´ˆD>“pÈÍú¦ìš[¾Ë¡û0µºàµ•¬›D ‚¸Œ‚hëSf !ƒ‹šÙÆ@©éµ-üyÀZ S>©¢å\çn¬ÖSŒ ]É€Cl¸äÂT[#GM|Û-å>Ö~˜úSŽj¾Ö¾Ýún^uÒGz?_ÆzçéÛ›±.y—,b0ÏS ˤó¡ÿí/ŸzœÕðQvYÍévçÇÉ·“¹ì£›~­Ë/(jrS \ No newline at end of file diff --git a/tests/resources/testrepo_256/.gitted/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f b/tests/resources/testrepo_256/.gitted/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f new file mode 100644 index 00000000000..17fae64f464 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f @@ -0,0 +1,3 @@ +xuKj1D³žSô¤–Üê† !'H. OgÀ3 +²Œ¯9É6»¢¨÷ rݶµ2>õ¦ +d=‹Š²³³-bŒ5ì‹“dò०Pî(DÁeŽÉ{Òé+6Ý;Ì>±‹4KÆÄ†­¦rLfˆ Á,ö¿Ýo}© >ríÞ–˜ë/×üNç-®—ç\·W°<[Çâá`‚1ÓhÇ£® ÞË=¶ŸKÝ®Z‡ñ‘Nçµ/·ôÇ{"‚ï?¥÷¼À¯ê®Ó7bÆW \ No newline at end of file diff --git a/tests/resources/testrepo_256/.gitted/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e b/tests/resources/testrepo_256/.gitted/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e new file mode 100644 index 00000000000..0bece845b96 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e differ diff --git a/tests/resources/testrepo_256/.gitted/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3 b/tests/resources/testrepo_256/.gitted/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3 new file mode 100644 index 00000000000..112998d4257 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7 b/tests/resources/testrepo_256/.gitted/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7 new file mode 100644 index 00000000000..860cad13cf8 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed b/tests/resources/testrepo_256/.gitted/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed new file mode 100644 index 00000000000..4c973ea83af Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed differ diff --git a/tests/resources/testrepo_256/.gitted/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6 b/tests/resources/testrepo_256/.gitted/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6 new file mode 100644 index 00000000000..67b84c462af Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f b/tests/resources/testrepo_256/.gitted/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f new file mode 100644 index 00000000000..993a62b1637 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f differ diff --git a/tests/resources/testrepo_256/.gitted/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c b/tests/resources/testrepo_256/.gitted/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c new file mode 100644 index 00000000000..70bf64e16bf --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c @@ -0,0 +1,2 @@ +xÏAj1 …á®çÚ‚-i,B(]µ(tiÙ2’™ ƒ=~]zƒnÿÅ÷xe_×¥Æù©f]òb¨µ…$Öa­9)yI Ù'ñ‚2i«èæYÉÇTU[f™îù°­ƒWVaÔ©5sùu,QãÜ +¢¤€¤:„P’OœCp*£ä)?ú×~Àùó'x]6x˹\­^àÜöýžóz¿Ù©ìë‘õIÞêÏ]³äœ¾àWÖÞµS¹ Æ&%æ%għeæ¤ê•T”0L;ØÏ4ýq/ã—M·ÎÞ[¢:ëU¶fâk¥½:<ö šÌ ®Pmy©å`å¹ñ—&îæOwñ›¹ÚS_úãŸû±§8omòùlø PpËùƒ‹¨TO \ No newline at end of file diff --git a/tests/resources/testrepo_256/.gitted/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 b/tests/resources/testrepo_256/.gitted/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 new file mode 100644 index 00000000000..2419974cbc2 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640 b/tests/resources/testrepo_256/.gitted/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640 new file mode 100644 index 00000000000..b390250e308 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5 b/tests/resources/testrepo_256/.gitted/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5 new file mode 100644 index 00000000000..18a7f61c29e Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd b/tests/resources/testrepo_256/.gitted/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd new file mode 100644 index 00000000000..d1c032fce34 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd differ diff --git a/tests/resources/testrepo_256/.gitted/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827 b/tests/resources/testrepo_256/.gitted/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827 new file mode 100644 index 00000000000..c6da2ff7ace --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827 @@ -0,0 +1 @@ +xuQj1 DûíSè)²WëÕB)д°e%^¨×Åqèõë´ýÍß0Ì{0RKÙ:8vO½©‚å³µleåà–e1ršEO³LŽ0aò8?Ú™¯ÐtBŠäBÒàÉG'Q**MYÉŸ'áhY…íL¸õ\¼KíN9HÝáå*¿áx)aû|–Z^Áº…ØN¼pA4£º6xKß¡%øÈµ\ï´ã=/[Ï·øÏ“÷¼zD7x¼‰-ì’áÏuWó¯W \ No newline at end of file diff --git a/tests/resources/testrepo_256/.gitted/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890 b/tests/resources/testrepo_256/.gitted/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890 new file mode 100644 index 00000000000..b1df3bdd5cf Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 b/tests/resources/testrepo_256/.gitted/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 new file mode 100644 index 00000000000..3e36331ea66 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23 b/tests/resources/testrepo_256/.gitted/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23 new file mode 100644 index 00000000000..9bb5b623bdb Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19 b/tests/resources/testrepo_256/.gitted/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19 new file mode 100644 index 00000000000..3cbf7e6b714 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5 b/tests/resources/testrepo_256/.gitted/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5 new file mode 100644 index 00000000000..be8b99bba80 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 b/tests/resources/testrepo_256/.gitted/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 new file mode 100644 index 00000000000..9d2ceb1fff9 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 @@ -0,0 +1,2 @@ +xŒÁ‚0D=÷+önbvK[JbŒ?àÍxßÖ¥¢Öƒ/™I^æð” P»1=%+X’5IœO6“´uà =V•“*pDòì]h±v˜„G/„÷HÜ,o £¿€r1Ë@eÖŠLpë² +ž´ƒ£ò0¾¾çÒs÷>ä±?ÙH¾ŽÎ!ìÑ"s}t3,åͳIÿmø3‘ \ No newline at end of file diff --git a/tests/resources/testrepo_256/.gitted/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269 b/tests/resources/testrepo_256/.gitted/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269 new file mode 100644 index 00000000000..cfcdac30697 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 b/tests/resources/testrepo_256/.gitted/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 new file mode 100644 index 00000000000..77d9ec27d54 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f b/tests/resources/testrepo_256/.gitted/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f new file mode 100644 index 00000000000..a67d6e647cc Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f differ diff --git a/tests/resources/testrepo_256/.gitted/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0 b/tests/resources/testrepo_256/.gitted/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0 new file mode 100644 index 00000000000..ec37060e379 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0 @@ -0,0 +1,3 @@ +x•ŽA +Â0E]ç³ëBÌ$© ˆx¯ ™dª…ÆHŒ ·7xWÞãÃ%祑޴*<Æ}À“qVˆœ ÖZFЉP‹'q/yާ(–ÙšQÏèUxµ[©Ö% +~8É;äÇ*»XòИ‰¼vä`«QkÕm¿Ð䯑:—&O)IþÀp]ÚåÞÝ%V M˜kɰ.Ü©/××Eÿ \ No newline at end of file diff --git a/tests/resources/testrepo_256/.gitted/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca b/tests/resources/testrepo_256/.gitted/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca new file mode 100644 index 00000000000..6845087e53a --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca @@ -0,0 +1 @@ +x5ÌÁjÃ0 €áýêyP”ÈŽ(£ÐëÖW²#‡´q4Rõ°·_/=þ|ðÏÐôǦ›d"ñMH=î"¦2娡FOÄsšÚÉû.¥ëK;â%bãsi¹wö÷+VMÎ^gÞ656™ÆW¦ã[fÙa{®«é]68I•}ÉÇ"{åÇy®¼¬Ç¬õ "Š}ð‰-¢sߪwøá\.Zëb÷Ø=B \ No newline at end of file diff --git a/tests/resources/testrepo_256/.gitted/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f b/tests/resources/testrepo_256/.gitted/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f new file mode 100644 index 00000000000..a53ab84cf2c Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f differ diff --git a/tests/resources/testrepo_256/.gitted/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1 b/tests/resources/testrepo_256/.gitted/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1 new file mode 100644 index 00000000000..225c45734e0 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736 b/tests/resources/testrepo_256/.gitted/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736 new file mode 100644 index 00000000000..58d51e50232 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978 b/tests/resources/testrepo_256/.gitted/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978 new file mode 100644 index 00000000000..04bf5eb0621 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562 b/tests/resources/testrepo_256/.gitted/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562 new file mode 100644 index 00000000000..37a289ecc18 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562 differ diff --git a/tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idx b/tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idx new file mode 100644 index 00000000000..897e8a478fc Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idx differ diff --git a/tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.pack b/tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.pack new file mode 100644 index 00000000000..9c8557886c8 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.pack differ diff --git a/tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx b/tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx new file mode 100644 index 00000000000..9e2ec99c546 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx differ diff --git a/tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.pack b/tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.pack new file mode 100644 index 00000000000..66cd292a257 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.pack differ diff --git a/tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idx b/tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idx new file mode 100644 index 00000000000..1d197e87085 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idx differ diff --git a/tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.pack b/tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.pack new file mode 100644 index 00000000000..5b615e15c27 Binary files /dev/null and b/tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.pack differ diff --git a/tests/resources/testrepo_256/.gitted/packed-refs b/tests/resources/testrepo_256/.gitted/packed-refs new file mode 100644 index 00000000000..75626adfed4 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/packed-refs @@ -0,0 +1,27 @@ +# pack-refs with: peeled fully-peeled sorted +a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 refs/remotes/origin/br2 +a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 refs/remotes/origin/cannot-fetch +4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 refs/remotes/origin/chomped +7e9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c refs/remotes/origin/haacked +decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f refs/remotes/origin/master +decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f refs/remotes/origin/not-good +66fe8385c6378bfa5ca5573bd0fdd773e4eadb0e86416b483f2c50c839859ecb refs/remotes/origin/packed +43e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b refs/remotes/origin/packed-test +0118010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 refs/remotes/origin/subtrees +4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 refs/remotes/origin/test +b83624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 refs/remotes/origin/track-local +4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 refs/remotes/origin/trailing +7e4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f refs/remotes/origin/with-empty-log +d88b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca refs/tags/annotated_tag_to_blob +^33e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 +21e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 refs/tags/e90810b +^4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 +34f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b refs/tags/hard_tag +^decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f +33e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 refs/tags/point_to_blob +14bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f refs/tags/taggerless +^4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 +c258f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 refs/tags/test +^4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 +34f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b refs/tags/wrapped_tag +^decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f diff --git a/tests/resources/testrepo_256/.gitted/refs/heads/master b/tests/resources/testrepo_256/.gitted/refs/heads/master new file mode 100644 index 00000000000..106231c4cfa --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/refs/heads/master @@ -0,0 +1 @@ +decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f diff --git a/tests/resources/testrepo_256/.gitted/refs/remotes/origin/HEAD b/tests/resources/testrepo_256/.gitted/refs/remotes/origin/HEAD new file mode 100644 index 00000000000..6efe28fff83 --- /dev/null +++ b/tests/resources/testrepo_256/.gitted/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +ref: refs/remotes/origin/master diff --git a/tests/resources/testrepo_256/README b/tests/resources/testrepo_256/README new file mode 100644 index 00000000000..a8233120f6a --- /dev/null +++ b/tests/resources/testrepo_256/README @@ -0,0 +1 @@ +hey there diff --git a/tests/resources/testrepo_256/branch_file.txt b/tests/resources/testrepo_256/branch_file.txt new file mode 100644 index 00000000000..3697d64be94 --- /dev/null +++ b/tests/resources/testrepo_256/branch_file.txt @@ -0,0 +1,2 @@ +hi +bye! diff --git a/tests/resources/testrepo_256/new.txt b/tests/resources/testrepo_256/new.txt new file mode 100644 index 00000000000..a71586c1dfe --- /dev/null +++ b/tests/resources/testrepo_256/new.txt @@ -0,0 +1 @@ +my new file diff --git a/tests/util/alloc.c b/tests/util/alloc.c new file mode 100644 index 00000000000..492394a52c0 --- /dev/null +++ b/tests/util/alloc.c @@ -0,0 +1,68 @@ +#include "clar_libgit2.h" +#include "clar_libgit2_alloc.h" +#include "alloc.h" + +void test_alloc__cleanup(void) +{ + cl_alloc_reset(); +} + +void test_alloc__oom(void) +{ + void *ptr = NULL; + + cl_alloc_limit(0); + + cl_assert(git__malloc(1) == NULL); + cl_assert(git__calloc(1, 1) == NULL); + cl_assert(git__realloc(ptr, 1) == NULL); + cl_assert(git__strdup("test") == NULL); + cl_assert(git__strndup("test", 4) == NULL); +} + +void test_alloc__single_byte_is_exhausted(void) +{ + void *ptr; + + cl_alloc_limit(1); + + cl_assert(ptr = git__malloc(1)); + cl_assert(git__malloc(1) == NULL); + git__free(ptr); +} + +void test_alloc__free_replenishes_byte(void) +{ + void *ptr; + + cl_alloc_limit(1); + + cl_assert(ptr = git__malloc(1)); + cl_assert(git__malloc(1) == NULL); + git__free(ptr); + cl_assert(ptr = git__malloc(1)); + git__free(ptr); +} + +void test_alloc__realloc(void) +{ + char *ptr = NULL; + + cl_alloc_limit(3); + + cl_assert(ptr = git__realloc(ptr, 1)); + *ptr = 'x'; + + cl_assert(ptr = git__realloc(ptr, 1)); + cl_assert_equal_i(*ptr, 'x'); + + cl_assert(ptr = git__realloc(ptr, 2)); + cl_assert_equal_i(*ptr, 'x'); + + cl_assert(git__realloc(ptr, 2) == NULL); + + cl_assert(ptr = git__realloc(ptr, 1)); + cl_assert_equal_i(*ptr, 'x'); + + git__free(ptr); +} diff --git a/tests/util/assert.c b/tests/util/assert.c index 3babc475ae3..4644f3533e9 100644 --- a/tests/util/assert.c +++ b/tests/util/assert.c @@ -92,7 +92,8 @@ void test_assert__argument_with_void_return_type(void) git_error_clear(); cl_assert_equal_p(foo, fn_returns_string(foo)); - cl_assert_equal_p(NULL, git_error_last()); + cl_assert_equal_i(GIT_ERROR_NONE, git_error_last()->klass); + cl_assert_equal_s("no error", git_error_last()->message); } void test_assert__internal(void) diff --git a/tests/util/errors.c b/tests/util/errors.c index 78654a753ae..f9b85a65fde 100644 --- a/tests/util/errors.c +++ b/tests/util/errors.c @@ -5,7 +5,10 @@ void test_errors__public_api(void) char *str_in_error; git_error_clear(); - cl_assert(git_error_last() == NULL); + + cl_assert(git_error_last() != NULL); + cl_assert(git_error_last()->klass == GIT_ERROR_NONE); + cl_assert(strcmp(git_error_last()->message, "no error") == 0); git_error_set_oom(); @@ -23,7 +26,9 @@ void test_errors__public_api(void) cl_assert(str_in_error != NULL); git_error_clear(); - cl_assert(git_error_last() == NULL); + cl_assert(git_error_last() != NULL); + cl_assert(git_error_last()->klass == GIT_ERROR_NONE); + cl_assert(strcmp(git_error_last()->message, "no error") == 0); } #include "common.h" @@ -35,7 +40,9 @@ void test_errors__new_school(void) char *str_in_error; git_error_clear(); - cl_assert(git_error_last() == NULL); + cl_assert(git_error_last() != NULL); + cl_assert(git_error_last()->klass == GIT_ERROR_NONE); + cl_assert(strcmp(git_error_last()->message, "no error") == 0); git_error_set_oom(); /* internal fn */ @@ -53,7 +60,9 @@ void test_errors__new_school(void) cl_assert(str_in_error != NULL); git_error_clear(); - cl_assert(git_error_last() == NULL); + cl_assert(git_error_last() != NULL); + cl_assert(git_error_last()->klass == GIT_ERROR_NONE); + cl_assert(strcmp(git_error_last()->message, "no error") == 0); do { struct stat st; @@ -88,52 +97,32 @@ void test_errors__new_school(void) void test_errors__restore(void) { - git_error_state err_state = {0}; + git_error *last_error; git_error_clear(); - cl_assert(git_error_last() == NULL); - - cl_assert_equal_i(0, git_error_state_capture(&err_state, 0)); - - memset(&err_state, 0x0, sizeof(git_error_state)); + cl_assert(git_error_last() != NULL); + cl_assert(git_error_last()->klass == GIT_ERROR_NONE); + cl_assert(strcmp("no error", git_error_last()->message) == 0); git_error_set(42, "Foo: %s", "bar"); - cl_assert_equal_i(-1, git_error_state_capture(&err_state, -1)); - - cl_assert(git_error_last() == NULL); - - git_error_set(99, "Bar: %s", "foo"); - - git_error_state_restore(&err_state); - - cl_assert_equal_i(42, git_error_last()->klass); - cl_assert_equal_s("Foo: bar", git_error_last()->message); -} - -void test_errors__free_state(void) -{ - git_error_state err_state = {0}; + cl_assert(git_error_save(&last_error) == 0); git_error_clear(); - - git_error_set(42, "Foo: %s", "bar"); - cl_assert_equal_i(-1, git_error_state_capture(&err_state, -1)); + cl_assert(git_error_last() != NULL); + cl_assert(git_error_last()->klass == GIT_ERROR_NONE); + cl_assert(strcmp("no error", git_error_last()->message) == 0); git_error_set(99, "Bar: %s", "foo"); - git_error_state_free(&err_state); - - cl_assert_equal_i(99, git_error_last()->klass); - cl_assert_equal_s("Bar: foo", git_error_last()->message); + git_error_restore(last_error); - git_error_state_restore(&err_state); - - cl_assert(git_error_last() == NULL); + cl_assert(git_error_last()->klass == 42); + cl_assert(strcmp("Foo: bar", git_error_last()->message) == 0); } void test_errors__restore_oom(void) { - git_error_state err_state = {0}; + git_error *last_error; const git_error *oom_error = NULL; git_error_clear(); @@ -141,15 +130,18 @@ void test_errors__restore_oom(void) git_error_set_oom(); /* internal fn */ oom_error = git_error_last(); cl_assert(oom_error); + cl_assert(oom_error->klass == GIT_ERROR_NOMEMORY); - cl_assert_equal_i(-1, git_error_state_capture(&err_state, -1)); - - cl_assert(git_error_last() == NULL); - cl_assert_equal_i(GIT_ERROR_NOMEMORY, err_state.error_msg.klass); - cl_assert_equal_s("Out of memory", err_state.error_msg.message); + cl_assert(git_error_save(&last_error) == 0); + cl_assert(last_error->klass == GIT_ERROR_NOMEMORY); + cl_assert(strcmp("Out of memory", last_error->message) == 0); - git_error_state_restore(&err_state); + git_error_clear(); + cl_assert(git_error_last() != NULL); + cl_assert(git_error_last()->klass == GIT_ERROR_NONE); + cl_assert(strcmp("no error", git_error_last()->message) == 0); + git_error_restore(last_error); cl_assert(git_error_last()->klass == GIT_ERROR_NOMEMORY); cl_assert_(git_error_last() == oom_error, "static oom error not restored"); @@ -204,11 +196,15 @@ void test_errors__integer_overflow_sets_oom(void) git_error_clear(); cl_assert(!GIT_ADD_SIZET_OVERFLOW(&out, SIZE_MAX-1, 1)); - cl_assert_equal_p(NULL, git_error_last()); + cl_assert(git_error_last() != NULL); + cl_assert(git_error_last()->klass == GIT_ERROR_NONE); + cl_assert(strcmp(git_error_last()->message, "no error") == 0); git_error_clear(); cl_assert(!GIT_ADD_SIZET_OVERFLOW(&out, 42, 69)); - cl_assert_equal_p(NULL, git_error_last()); + cl_assert(git_error_last() != NULL); + cl_assert(git_error_last()->klass == GIT_ERROR_NONE); + cl_assert(strcmp(git_error_last()->message, "no error") == 0); git_error_clear(); cl_assert(GIT_ADD_SIZET_OVERFLOW(&out, SIZE_MAX, SIZE_MAX)); diff --git a/tests/util/hostname.c b/tests/util/hostname.c new file mode 100644 index 00000000000..5d8bfe2ac99 --- /dev/null +++ b/tests/util/hostname.c @@ -0,0 +1,13 @@ +#include "clar_libgit2.h" +#include "net.h" + +void test_hostname__matches_cert(void) +{ + cl_assert_equal_b(true, git_net_hostname_matches_cert("www.example.org", "*.example.org")); + cl_assert_equal_b(true, git_net_hostname_matches_cert("www.foo.example.org", "*.foo.example.org")); + cl_assert_equal_b(false, git_net_hostname_matches_cert("foo.example.org", "*.foo.example.org")); + cl_assert_equal_b(false, git_net_hostname_matches_cert("www.example.org", "*.foo.example.org")); + cl_assert_equal_b(false, git_net_hostname_matches_cert("example.org", "*.example.org")); + cl_assert_equal_b(false, git_net_hostname_matches_cert("www.foo.example.org", "*.example.org")); + cl_assert_equal_b(false, git_net_hostname_matches_cert("blah.www.www.example.org", "*.example.org")); +} diff --git a/tests/util/link.c b/tests/util/link.c index 46cafada7e9..5909e26e391 100644 --- a/tests/util/link.c +++ b/tests/util/link.c @@ -98,7 +98,7 @@ static void do_junction(const char *old, const char *new) git_str_putc(&unparsed_buf, '\\'); - subst_utf16_len = git__utf8_to_16(NULL, 0, git_str_cstr(&unparsed_buf)); + subst_utf16_len = git_utf8_to_16(NULL, 0, git_str_cstr(&unparsed_buf)); subst_byte_len = subst_utf16_len * sizeof(WCHAR); print_utf16_len = subst_utf16_len - 4; @@ -124,11 +124,11 @@ static void do_junction(const char *old, const char *new) subst_utf16 = reparse_buf->ReparseBuffer.MountPoint.PathBuffer; print_utf16 = subst_utf16 + subst_utf16_len + 1; - ret = git__utf8_to_16(subst_utf16, subst_utf16_len + 1, + ret = git_utf8_to_16(subst_utf16, subst_utf16_len + 1, git_str_cstr(&unparsed_buf)); cl_assert_equal_i(subst_utf16_len, ret); - ret = git__utf8_to_16(print_utf16, + ret = git_utf8_to_16(print_utf16, print_utf16_len + 1, git_str_cstr(&unparsed_buf) + 4); cl_assert_equal_i(print_utf16_len, ret); diff --git a/tests/util/path.c b/tests/util/path.c deleted file mode 100644 index 2c39e088700..00000000000 --- a/tests/util/path.c +++ /dev/null @@ -1,764 +0,0 @@ -#include "clar_libgit2.h" -#include "futils.h" -#include "fs_path.h" - -static char *path_save; - -void test_path__initialize(void) -{ - path_save = cl_getenv("PATH"); -} - -void test_path__cleanup(void) -{ - cl_setenv("PATH", path_save); - git__free(path_save); - path_save = NULL; -} - -static void -check_dirname(const char *A, const char *B) -{ - git_str dir = GIT_STR_INIT; - char *dir2; - - cl_assert(git_fs_path_dirname_r(&dir, A) >= 0); - cl_assert_equal_s(B, dir.ptr); - git_str_dispose(&dir); - - cl_assert((dir2 = git_fs_path_dirname(A)) != NULL); - cl_assert_equal_s(B, dir2); - git__free(dir2); -} - -static void -check_basename(const char *A, const char *B) -{ - git_str base = GIT_STR_INIT; - char *base2; - - cl_assert(git_fs_path_basename_r(&base, A) >= 0); - cl_assert_equal_s(B, base.ptr); - git_str_dispose(&base); - - cl_assert((base2 = git_fs_path_basename(A)) != NULL); - cl_assert_equal_s(B, base2); - git__free(base2); -} - -static void -check_joinpath(const char *path_a, const char *path_b, const char *expected_path) -{ - git_str joined_path = GIT_STR_INIT; - - cl_git_pass(git_str_joinpath(&joined_path, path_a, path_b)); - cl_assert_equal_s(expected_path, joined_path.ptr); - - git_str_dispose(&joined_path); -} - -static void -check_joinpath_n( - const char *path_a, - const char *path_b, - const char *path_c, - const char *path_d, - const char *expected_path) -{ - git_str joined_path = GIT_STR_INIT; - - cl_git_pass(git_str_join_n(&joined_path, '/', 4, - path_a, path_b, path_c, path_d)); - cl_assert_equal_s(expected_path, joined_path.ptr); - - git_str_dispose(&joined_path); -} - -static void check_setenv(const char* name, const char* value) -{ - char* check; - - cl_git_pass(cl_setenv(name, value)); - check = cl_getenv(name); - - if (value) - cl_assert_equal_s(value, check); - else - cl_assert(check == NULL); - - git__free(check); -} - -/* get the dirname of a path */ -void test_path__00_dirname(void) -{ - check_dirname(NULL, "."); - check_dirname("", "."); - check_dirname("a", "."); - check_dirname("/", "/"); - check_dirname("/usr", "/"); - check_dirname("/usr/", "/"); - check_dirname("/usr/lib", "/usr"); - check_dirname("/usr/lib/", "/usr"); - check_dirname("/usr/lib//", "/usr"); - check_dirname("usr/lib", "usr"); - check_dirname("usr/lib/", "usr"); - check_dirname("usr/lib//", "usr"); - check_dirname(".git/", "."); - - check_dirname(REP16("/abc"), REP15("/abc")); - -#ifdef GIT_WIN32 - check_dirname("C:/", "C:/"); - check_dirname("C:", "C:/"); - check_dirname("C:/path/", "C:/"); - check_dirname("C:/path", "C:/"); - check_dirname("//computername/", "//computername/"); - check_dirname("//computername", "//computername/"); - check_dirname("//computername/path/", "//computername/"); - check_dirname("//computername/path", "//computername/"); - check_dirname("//computername/sub/path/", "//computername/sub"); - check_dirname("//computername/sub/path", "//computername/sub"); -#endif -} - -/* get the base name of a path */ -void test_path__01_basename(void) -{ - check_basename(NULL, "."); - check_basename("", "."); - check_basename("a", "a"); - check_basename("/", "/"); - check_basename("/usr", "usr"); - check_basename("/usr/", "usr"); - check_basename("/usr/lib", "lib"); - check_basename("/usr/lib//", "lib"); - check_basename("usr/lib", "lib"); - - check_basename(REP16("/abc"), "abc"); - check_basename(REP1024("/abc"), "abc"); -} - -/* properly join path components */ -void test_path__05_joins(void) -{ - check_joinpath("", "", ""); - check_joinpath("", "a", "a"); - check_joinpath("", "/a", "/a"); - check_joinpath("a", "", "a/"); - check_joinpath("a", "/", "a/"); - check_joinpath("a", "b", "a/b"); - check_joinpath("/", "a", "/a"); - check_joinpath("/", "", "/"); - check_joinpath("/a", "/b", "/a/b"); - check_joinpath("/a", "/b/", "/a/b/"); - check_joinpath("/a/", "b/", "/a/b/"); - check_joinpath("/a/", "/b/", "/a/b/"); - - check_joinpath("/abcd", "/defg", "/abcd/defg"); - check_joinpath("/abcd", "/defg/", "/abcd/defg/"); - check_joinpath("/abcd/", "defg/", "/abcd/defg/"); - check_joinpath("/abcd/", "/defg/", "/abcd/defg/"); - - check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678"); - check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/"); - check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/"); - - check_joinpath(REP1024("aaaa"), "", REP1024("aaaa") "/"); - check_joinpath(REP1024("aaaa/"), "", REP1024("aaaa/")); - check_joinpath(REP1024("/aaaa"), "", REP1024("/aaaa") "/"); - - check_joinpath(REP1024("aaaa"), REP1024("bbbb"), - REP1024("aaaa") "/" REP1024("bbbb")); - check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"), - REP1024("/aaaa") REP1024("/bbbb")); -} - -/* properly join path components for more than one path */ -void test_path__06_long_joins(void) -{ - check_joinpath_n("", "", "", "", ""); - check_joinpath_n("", "a", "", "", "a/"); - check_joinpath_n("a", "", "", "", "a/"); - check_joinpath_n("", "", "", "a", "a"); - check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/"); - check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d"); - check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop"); - check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/"); - check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/"); - - check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"), - REP1024("a") "/" REP1024("b") "/" - REP1024("c") "/" REP1024("d")); - check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"), - REP1024("/a") REP1024("/b") - REP1024("/c") REP1024("/d")); -} - - -static void -check_path_to_dir( - const char* path, - const char* expected) -{ - git_str tgt = GIT_STR_INIT; - - git_str_sets(&tgt, path); - cl_git_pass(git_fs_path_to_dir(&tgt)); - cl_assert_equal_s(expected, tgt.ptr); - - git_str_dispose(&tgt); -} - -static void -check_string_to_dir( - const char* path, - size_t maxlen, - const char* expected) -{ - size_t len = strlen(path); - char *buf = git__malloc(len + 2); - cl_assert(buf); - - strncpy(buf, path, len + 2); - - git_fs_path_string_to_dir(buf, maxlen); - - cl_assert_equal_s(expected, buf); - - git__free(buf); -} - -/* convert paths to dirs */ -void test_path__07_path_to_dir(void) -{ - check_path_to_dir("", ""); - check_path_to_dir(".", "./"); - check_path_to_dir("./", "./"); - check_path_to_dir("a/", "a/"); - check_path_to_dir("ab", "ab/"); - /* make sure we try just under and just over an expansion that will - * require a realloc - */ - check_path_to_dir("abcdef", "abcdef/"); - check_path_to_dir("abcdefg", "abcdefg/"); - check_path_to_dir("abcdefgh", "abcdefgh/"); - check_path_to_dir("abcdefghi", "abcdefghi/"); - check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/"); - check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/"); - - check_string_to_dir("", 1, ""); - check_string_to_dir(".", 1, "."); - check_string_to_dir(".", 2, "./"); - check_string_to_dir(".", 3, "./"); - check_string_to_dir("abcd", 3, "abcd"); - check_string_to_dir("abcd", 4, "abcd"); - check_string_to_dir("abcd", 5, "abcd/"); - check_string_to_dir("abcd", 6, "abcd/"); -} - -/* join path to itself */ -void test_path__08_self_join(void) -{ - git_str path = GIT_STR_INIT; - size_t asize = 0; - - asize = path.asize; - cl_git_pass(git_str_sets(&path, "/foo")); - cl_assert_equal_s(path.ptr, "/foo"); - cl_assert(asize < path.asize); - - asize = path.asize; - cl_git_pass(git_str_joinpath(&path, path.ptr, "this is a new string")); - cl_assert_equal_s(path.ptr, "/foo/this is a new string"); - cl_assert(asize < path.asize); - - asize = path.asize; - cl_git_pass(git_str_joinpath(&path, path.ptr, "/grow the buffer, grow the buffer, grow the buffer")); - cl_assert_equal_s(path.ptr, "/foo/this is a new string/grow the buffer, grow the buffer, grow the buffer"); - cl_assert(asize < path.asize); - - git_str_dispose(&path); - cl_git_pass(git_str_sets(&path, "/foo/bar")); - - cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "baz")); - cl_assert_equal_s(path.ptr, "/bar/baz"); - - asize = path.asize; - cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc")); - cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc"); - cl_assert(asize < path.asize); - - git_str_dispose(&path); -} - -static void check_percent_decoding(const char *expected_result, const char *input) -{ - git_str buf = GIT_STR_INIT; - - cl_git_pass(git__percent_decode(&buf, input)); - cl_assert_equal_s(expected_result, git_str_cstr(&buf)); - - git_str_dispose(&buf); -} - -void test_path__09_percent_decode(void) -{ - check_percent_decoding("abcd", "abcd"); - check_percent_decoding("a2%", "a2%"); - check_percent_decoding("a2%3", "a2%3"); - check_percent_decoding("a2%%3", "a2%%3"); - check_percent_decoding("a2%3z", "a2%3z"); - check_percent_decoding("a,", "a%2c"); - check_percent_decoding("a21", "a2%31"); - check_percent_decoding("a2%1", "a2%%31"); - check_percent_decoding("a bc ", "a%20bc%20"); - check_percent_decoding("Vicent Mart" "\355", "Vicent%20Mart%ED"); -} - -static void check_fromurl(const char *expected_result, const char *input, int should_fail) -{ - git_str buf = GIT_STR_INIT; - - assert(should_fail || expected_result); - - if (!should_fail) { - cl_git_pass(git_fs_path_fromurl(&buf, input)); - cl_assert_equal_s(expected_result, git_str_cstr(&buf)); - } else - cl_git_fail(git_fs_path_fromurl(&buf, input)); - - git_str_dispose(&buf); -} - -#ifdef GIT_WIN32 -#define ABS_PATH_MARKER "" -#else -#define ABS_PATH_MARKER "/" -#endif - -void test_path__10_fromurl(void) -{ - /* Failing cases */ - check_fromurl(NULL, "a", 1); - check_fromurl(NULL, "http:///c:/Temp%20folder/note.txt", 1); - check_fromurl(NULL, "file://c:/Temp%20folder/note.txt", 1); - check_fromurl(NULL, "file:////c:/Temp%20folder/note.txt", 1); - check_fromurl(NULL, "file:///", 1); - check_fromurl(NULL, "file:////", 1); - check_fromurl(NULL, "file://servername/c:/Temp%20folder/note.txt", 1); - - /* Passing cases */ - check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file:///c:/Temp%20folder/note.txt", 0); - check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file://localhost/c:/Temp%20folder/note.txt", 0); - check_fromurl(ABS_PATH_MARKER "c:/Temp+folder/note.txt", "file:///c:/Temp+folder/note.txt", 0); - check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0); -} - -typedef struct { - int expect_idx; - int cancel_after; - char **expect; -} check_walkup_info; - -#define CANCEL_VALUE 1234 - -static int check_one_walkup_step(void *ref, const char *path) -{ - check_walkup_info *info = (check_walkup_info *)ref; - - if (!info->cancel_after) { - cl_assert_equal_s(info->expect[info->expect_idx], "[CANCEL]"); - return CANCEL_VALUE; - } - info->cancel_after--; - - cl_assert(info->expect[info->expect_idx] != NULL); - cl_assert_equal_s(info->expect[info->expect_idx], path); - info->expect_idx++; - - return 0; -} - -void test_path__11_walkup(void) -{ - git_str p = GIT_STR_INIT; - - char *expect[] = { - /* 1 */ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, - /* 2 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, - /* 3 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, - /* 4 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, - /* 5 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL, - /* 6 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL, - /* 7 */ "this_is_a_path", "", NULL, - /* 8 */ "this_is_a_path/", "", NULL, - /* 9 */ "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL, - /* 10 */ "a/b/c/", "a/b/", "a/", "", NULL, - /* 11 */ "a/b/c", "a/b/", "a/", "", NULL, - /* 12 */ "a/b/c/", "a/b/", "a/", NULL, - /* 13 */ "", NULL, - /* 14 */ "/", NULL, - /* 15 */ NULL - }; - - char *root[] = { - /* 1 */ NULL, - /* 2 */ NULL, - /* 3 */ "/", - /* 4 */ "", - /* 5 */ "/a/b", - /* 6 */ "/a/b/", - /* 7 */ NULL, - /* 8 */ NULL, - /* 9 */ NULL, - /* 10 */ NULL, - /* 11 */ NULL, - /* 12 */ "a/", - /* 13 */ NULL, - /* 14 */ NULL, - }; - - int i, j; - check_walkup_info info; - - info.expect = expect; - info.cancel_after = -1; - - for (i = 0, j = 0; expect[i] != NULL; i++, j++) { - - git_str_sets(&p, expect[i]); - - info.expect_idx = i; - cl_git_pass( - git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info) - ); - - cl_assert_equal_s(p.ptr, expect[i]); - cl_assert(expect[info.expect_idx] == NULL); - i = info.expect_idx; - } - - git_str_dispose(&p); -} - -void test_path__11a_walkup_cancel(void) -{ - git_str p = GIT_STR_INIT; - int cancel[] = { 3, 2, 1, 0 }; - char *expect[] = { - "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "[CANCEL]", NULL, - "/a/b/c/d/e", "/a/b/c/d/", "[CANCEL]", NULL, - "/a/b/c/d/e", "[CANCEL]", NULL, - "[CANCEL]", NULL, - NULL - }; - char *root[] = { NULL, NULL, "/", "", NULL }; - int i, j; - check_walkup_info info; - - info.expect = expect; - - for (i = 0, j = 0; expect[i] != NULL; i++, j++) { - - git_str_sets(&p, expect[i]); - - info.cancel_after = cancel[j]; - info.expect_idx = i; - - cl_assert_equal_i( - CANCEL_VALUE, - git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info) - ); - - /* skip to next run of expectations */ - while (expect[i] != NULL) i++; - } - - git_str_dispose(&p); -} - -void test_path__12_offset_to_path_root(void) -{ - cl_assert(git_fs_path_root("non/rooted/path") == -1); - cl_assert(git_fs_path_root("/rooted/path") == 0); - -#ifdef GIT_WIN32 - /* Windows specific tests */ - cl_assert(git_fs_path_root("C:non/rooted/path") == -1); - cl_assert(git_fs_path_root("C:/rooted/path") == 2); - cl_assert(git_fs_path_root("//computername/sharefolder/resource") == 14); - cl_assert(git_fs_path_root("//computername/sharefolder") == 14); - cl_assert(git_fs_path_root("//computername") == -1); -#endif -} - -#define NON_EXISTING_FILEPATH "i_hope_i_do_not_exist" - -void test_path__13_cannot_prettify_a_non_existing_file(void) -{ - git_str p = GIT_STR_INIT; - - cl_assert_equal_b(git_fs_path_exists(NON_EXISTING_FILEPATH), false); - cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH, NULL)); - cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH "/so-do-i", NULL)); - - git_str_dispose(&p); -} - -void test_path__14_apply_relative(void) -{ - git_str p = GIT_STR_INIT; - - cl_git_pass(git_str_sets(&p, "/this/is/a/base")); - - cl_git_pass(git_fs_path_apply_relative(&p, "../test")); - cl_assert_equal_s("/this/is/a/test", p.ptr); - - cl_git_pass(git_fs_path_apply_relative(&p, "../../the/./end")); - cl_assert_equal_s("/this/is/the/end", p.ptr); - - cl_git_pass(git_fs_path_apply_relative(&p, "./of/this/../the/string")); - cl_assert_equal_s("/this/is/the/end/of/the/string", p.ptr); - - cl_git_pass(git_fs_path_apply_relative(&p, "../../../../../..")); - cl_assert_equal_s("/this/", p.ptr); - - cl_git_pass(git_fs_path_apply_relative(&p, "../")); - cl_assert_equal_s("/", p.ptr); - - cl_git_fail(git_fs_path_apply_relative(&p, "../../..")); - - - cl_git_pass(git_str_sets(&p, "d:/another/test")); - - cl_git_pass(git_fs_path_apply_relative(&p, "../..")); - cl_assert_equal_s("d:/", p.ptr); - - cl_git_pass(git_fs_path_apply_relative(&p, "from/here/to/../and/./back/.")); - cl_assert_equal_s("d:/from/here/and/back/", p.ptr); - - - cl_git_pass(git_str_sets(&p, "https://my.url.com/test.git")); - - cl_git_pass(git_fs_path_apply_relative(&p, "../another.git")); - cl_assert_equal_s("https://my.url.com/another.git", p.ptr); - - cl_git_pass(git_fs_path_apply_relative(&p, "../full/path/url.patch")); - cl_assert_equal_s("https://my.url.com/full/path/url.patch", p.ptr); - - cl_git_pass(git_fs_path_apply_relative(&p, "..")); - cl_assert_equal_s("https://my.url.com/full/path/", p.ptr); - - cl_git_pass(git_fs_path_apply_relative(&p, "../../../")); - cl_assert_equal_s("https://", p.ptr); - - - cl_git_pass(git_str_sets(&p, "../../this/is/relative")); - - cl_git_pass(git_fs_path_apply_relative(&p, "../../preserves/the/prefix")); - cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr); - - cl_git_pass(git_fs_path_apply_relative(&p, "../../../../that")); - cl_assert_equal_s("../../that", p.ptr); - - cl_git_pass(git_fs_path_apply_relative(&p, "../there")); - cl_assert_equal_s("../../there", p.ptr); - git_str_dispose(&p); -} - -static void assert_resolve_relative( - git_str *buf, const char *expected, const char *path) -{ - cl_git_pass(git_str_sets(buf, path)); - cl_git_pass(git_fs_path_resolve_relative(buf, 0)); - cl_assert_equal_s(expected, buf->ptr); -} - -void test_path__15_resolve_relative(void) -{ - git_str buf = GIT_STR_INIT; - - assert_resolve_relative(&buf, "", ""); - assert_resolve_relative(&buf, "", "."); - assert_resolve_relative(&buf, "", "./"); - assert_resolve_relative(&buf, "..", ".."); - assert_resolve_relative(&buf, "../", "../"); - assert_resolve_relative(&buf, "..", "./.."); - assert_resolve_relative(&buf, "../", "./../"); - assert_resolve_relative(&buf, "../", "../."); - assert_resolve_relative(&buf, "../", ".././"); - assert_resolve_relative(&buf, "../..", "../.."); - assert_resolve_relative(&buf, "../../", "../../"); - - assert_resolve_relative(&buf, "/", "/"); - assert_resolve_relative(&buf, "/", "/."); - - assert_resolve_relative(&buf, "", "a/.."); - assert_resolve_relative(&buf, "", "a/../"); - assert_resolve_relative(&buf, "", "a/../."); - - assert_resolve_relative(&buf, "/a", "/a"); - assert_resolve_relative(&buf, "/a/", "/a/."); - assert_resolve_relative(&buf, "/", "/a/../"); - assert_resolve_relative(&buf, "/", "/a/../."); - assert_resolve_relative(&buf, "/", "/a/.././"); - - assert_resolve_relative(&buf, "a", "a"); - assert_resolve_relative(&buf, "a/", "a/"); - assert_resolve_relative(&buf, "a/", "a/."); - assert_resolve_relative(&buf, "a/", "a/./"); - - assert_resolve_relative(&buf, "a/b", "a//b"); - assert_resolve_relative(&buf, "a/b/c", "a/b/c"); - assert_resolve_relative(&buf, "b/c", "./b/c"); - assert_resolve_relative(&buf, "a/c", "a/./c"); - assert_resolve_relative(&buf, "a/b/", "a/b/."); - - assert_resolve_relative(&buf, "/a/b/c", "///a/b/c"); - assert_resolve_relative(&buf, "/", "////"); - assert_resolve_relative(&buf, "/a", "///a"); - assert_resolve_relative(&buf, "/", "///."); - assert_resolve_relative(&buf, "/", "///a/.."); - - assert_resolve_relative(&buf, "../../path", "../../test//../././path"); - assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d"); - - cl_git_pass(git_str_sets(&buf, "/..")); - cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); - - cl_git_pass(git_str_sets(&buf, "/./..")); - cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); - - cl_git_pass(git_str_sets(&buf, "/.//..")); - cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); - - cl_git_pass(git_str_sets(&buf, "/../.")); - cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); - - cl_git_pass(git_str_sets(&buf, "/../.././../a")); - cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); - - cl_git_pass(git_str_sets(&buf, "////..")); - cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); - - /* things that start with Windows network paths */ -#ifdef GIT_WIN32 - assert_resolve_relative(&buf, "//a/b/c", "//a/b/c"); - assert_resolve_relative(&buf, "//a/", "//a/b/.."); - assert_resolve_relative(&buf, "//a/b/c", "//a/Q/../b/x/y/../../c"); - - cl_git_pass(git_str_sets(&buf, "//a/b/../..")); - cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); -#else - assert_resolve_relative(&buf, "/a/b/c", "//a/b/c"); - assert_resolve_relative(&buf, "/a/", "//a/b/.."); - assert_resolve_relative(&buf, "/a/b/c", "//a/Q/../b/x/y/../../c"); - assert_resolve_relative(&buf, "/", "//a/b/../.."); -#endif - - git_str_dispose(&buf); -} - -#define assert_common_dirlen(i, p, q) \ - cl_assert_equal_i((i), git_fs_path_common_dirlen((p), (q))); - -void test_path__16_resolve_relative(void) -{ - assert_common_dirlen(0, "", ""); - assert_common_dirlen(0, "", "bar.txt"); - assert_common_dirlen(0, "foo.txt", "bar.txt"); - assert_common_dirlen(0, "foo.txt", ""); - assert_common_dirlen(0, "foo/bar.txt", "bar/foo.txt"); - assert_common_dirlen(0, "foo/bar.txt", "../foo.txt"); - - assert_common_dirlen(1, "/one.txt", "/two.txt"); - assert_common_dirlen(4, "foo/one.txt", "foo/two.txt"); - assert_common_dirlen(5, "/foo/one.txt", "/foo/two.txt"); - - assert_common_dirlen(6, "a/b/c/foo.txt", "a/b/c/d/e/bar.txt"); - assert_common_dirlen(7, "/a/b/c/foo.txt", "/a/b/c/d/e/bar.txt"); -} - -static void fix_path(git_str *s) -{ -#ifndef GIT_WIN32 - GIT_UNUSED(s); -#else - char* c; - - for (c = s->ptr; *c; c++) { - if (*c == '/') - *c = '\\'; - } -#endif -} - -void test_path__find_exe_in_path(void) -{ - char *orig_path; - git_str sandbox_path = GIT_STR_INIT; - git_str new_path = GIT_STR_INIT, full_path = GIT_STR_INIT, - dummy_path = GIT_STR_INIT; - -#ifdef GIT_WIN32 - static const char *bogus_path_1 = "c:\\does\\not\\exist\\"; - static const char *bogus_path_2 = "e:\\non\\existent"; -#else - static const char *bogus_path_1 = "/this/path/does/not/exist/"; - static const char *bogus_path_2 = "/non/existent"; -#endif - - orig_path = cl_getenv("PATH"); - - git_str_puts(&sandbox_path, clar_sandbox_path()); - git_str_joinpath(&dummy_path, sandbox_path.ptr, "dummmmmmmy_libgit2_file"); - cl_git_rewritefile(dummy_path.ptr, "this is a dummy file"); - - fix_path(&sandbox_path); - fix_path(&dummy_path); - - cl_git_pass(git_str_printf(&new_path, "%s%c%s%c%s%c%s", - bogus_path_1, GIT_PATH_LIST_SEPARATOR, - orig_path, GIT_PATH_LIST_SEPARATOR, - sandbox_path.ptr, GIT_PATH_LIST_SEPARATOR, - bogus_path_2)); - - check_setenv("PATH", new_path.ptr); - - cl_git_fail_with(GIT_ENOTFOUND, git_fs_path_find_executable(&full_path, "this_file_does_not_exist")); - cl_git_pass(git_fs_path_find_executable(&full_path, "dummmmmmmy_libgit2_file")); - - cl_assert_equal_s(full_path.ptr, dummy_path.ptr); - - git_str_dispose(&full_path); - git_str_dispose(&new_path); - git_str_dispose(&dummy_path); - git_str_dispose(&sandbox_path); - git__free(orig_path); -} - -void test_path__validate_current_user_ownership(void) -{ - bool is_cur; - - cl_must_pass(p_mkdir("testdir", 0777)); - cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testdir")); - cl_assert_equal_i(is_cur, 1); - - cl_git_rewritefile("testfile", "This is a test file."); - cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testfile")); - cl_assert_equal_i(is_cur, 1); - -#ifdef GIT_WIN32 - cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "C:\\")); - cl_assert_equal_i(is_cur, 0); - - cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "c:\\path\\does\\not\\exist")); -#else - cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "/")); - cl_assert_equal_i(is_cur, 0); - - cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "/path/does/not/exist")); -#endif -} diff --git a/tests/util/path/core.c b/tests/util/path/core.c index f30f6c01b0d..d1935a816a9 100644 --- a/tests/util/path/core.c +++ b/tests/util/path/core.c @@ -1,6 +1,784 @@ #include "clar_libgit2.h" +#include "futils.h" #include "fs_path.h" +#ifndef GIT_WIN32 +# include +#endif + +static char *path_save; + +void test_path_core__initialize(void) +{ + path_save = cl_getenv("PATH"); +} + +void test_path_core__cleanup(void) +{ + cl_setenv("PATH", path_save); + git__free(path_save); + path_save = NULL; +} + +static void +check_dirname(const char *A, const char *B) +{ + git_str dir = GIT_STR_INIT; + char *dir2; + + cl_assert(git_fs_path_dirname_r(&dir, A) >= 0); + cl_assert_equal_s(B, dir.ptr); + git_str_dispose(&dir); + + cl_assert((dir2 = git_fs_path_dirname(A)) != NULL); + cl_assert_equal_s(B, dir2); + git__free(dir2); +} + +static void +check_basename(const char *A, const char *B) +{ + git_str base = GIT_STR_INIT; + char *base2; + + cl_assert(git_fs_path_basename_r(&base, A) >= 0); + cl_assert_equal_s(B, base.ptr); + git_str_dispose(&base); + + cl_assert((base2 = git_fs_path_basename(A)) != NULL); + cl_assert_equal_s(B, base2); + git__free(base2); +} + +static void +check_joinpath(const char *path_a, const char *path_b, const char *expected_path) +{ + git_str joined_path = GIT_STR_INIT; + + cl_git_pass(git_str_joinpath(&joined_path, path_a, path_b)); + cl_assert_equal_s(expected_path, joined_path.ptr); + + git_str_dispose(&joined_path); +} + +static void +check_joinpath_n( + const char *path_a, + const char *path_b, + const char *path_c, + const char *path_d, + const char *expected_path) +{ + git_str joined_path = GIT_STR_INIT; + + cl_git_pass(git_str_join_n(&joined_path, '/', 4, + path_a, path_b, path_c, path_d)); + cl_assert_equal_s(expected_path, joined_path.ptr); + + git_str_dispose(&joined_path); +} + +static void check_setenv(const char* name, const char* value) +{ + char* check; + + cl_git_pass(cl_setenv(name, value)); + check = cl_getenv(name); + + if (value) + cl_assert_equal_s(value, check); + else + cl_assert(check == NULL); + + git__free(check); +} + +/* get the dirname of a path */ +void test_path_core__00_dirname(void) +{ + check_dirname(NULL, "."); + check_dirname("", "."); + check_dirname("a", "."); + check_dirname("/", "/"); + check_dirname("/usr", "/"); + check_dirname("/usr/", "/"); + check_dirname("/usr/lib", "/usr"); + check_dirname("/usr/lib/", "/usr"); + check_dirname("/usr/lib//", "/usr"); + check_dirname("usr/lib", "usr"); + check_dirname("usr/lib/", "usr"); + check_dirname("usr/lib//", "usr"); + check_dirname(".git/", "."); + + check_dirname(REP16("/abc"), REP15("/abc")); + +#ifdef GIT_WIN32 + check_dirname("C:/", "C:/"); + check_dirname("C:", "C:/"); + check_dirname("C:/path/", "C:/"); + check_dirname("C:/path", "C:/"); + check_dirname("//computername/", "//computername/"); + check_dirname("//computername", "//computername/"); + check_dirname("//computername/path/", "//computername/"); + check_dirname("//computername/path", "//computername/"); + check_dirname("//computername/sub/path/", "//computername/sub"); + check_dirname("//computername/sub/path", "//computername/sub"); +#endif +} + +/* get the base name of a path */ +void test_path_core__01_basename(void) +{ + check_basename(NULL, "."); + check_basename("", "."); + check_basename("a", "a"); + check_basename("/", "/"); + check_basename("/usr", "usr"); + check_basename("/usr/", "usr"); + check_basename("/usr/lib", "lib"); + check_basename("/usr/lib//", "lib"); + check_basename("usr/lib", "lib"); + + check_basename(REP16("/abc"), "abc"); + check_basename(REP1024("/abc"), "abc"); +} + +/* properly join path components */ +void test_path_core__05_joins(void) +{ + check_joinpath("", "", ""); + check_joinpath("", "a", "a"); + check_joinpath("", "/a", "/a"); + check_joinpath("a", "", "a/"); + check_joinpath("a", "/", "a/"); + check_joinpath("a", "b", "a/b"); + check_joinpath("/", "a", "/a"); + check_joinpath("/", "", "/"); + check_joinpath("/a", "/b", "/a/b"); + check_joinpath("/a", "/b/", "/a/b/"); + check_joinpath("/a/", "b/", "/a/b/"); + check_joinpath("/a/", "/b/", "/a/b/"); + + check_joinpath("/abcd", "/defg", "/abcd/defg"); + check_joinpath("/abcd", "/defg/", "/abcd/defg/"); + check_joinpath("/abcd/", "defg/", "/abcd/defg/"); + check_joinpath("/abcd/", "/defg/", "/abcd/defg/"); + + check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678"); + check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/"); + check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/"); + + check_joinpath(REP1024("aaaa"), "", REP1024("aaaa") "/"); + check_joinpath(REP1024("aaaa/"), "", REP1024("aaaa/")); + check_joinpath(REP1024("/aaaa"), "", REP1024("/aaaa") "/"); + + check_joinpath(REP1024("aaaa"), REP1024("bbbb"), + REP1024("aaaa") "/" REP1024("bbbb")); + check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"), + REP1024("/aaaa") REP1024("/bbbb")); +} + +/* properly join path components for more than one path */ +void test_path_core__06_long_joins(void) +{ + check_joinpath_n("", "", "", "", ""); + check_joinpath_n("", "a", "", "", "a/"); + check_joinpath_n("a", "", "", "", "a/"); + check_joinpath_n("", "", "", "a", "a"); + check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/"); + check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d"); + check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop"); + check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/"); + check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/"); + + check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"), + REP1024("a") "/" REP1024("b") "/" + REP1024("c") "/" REP1024("d")); + check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"), + REP1024("/a") REP1024("/b") + REP1024("/c") REP1024("/d")); +} + + +static void +check_path_to_dir( + const char* path, + const char* expected) +{ + git_str tgt = GIT_STR_INIT; + + git_str_sets(&tgt, path); + cl_git_pass(git_fs_path_to_dir(&tgt)); + cl_assert_equal_s(expected, tgt.ptr); + + git_str_dispose(&tgt); +} + +static void +check_string_to_dir( + const char* path, + size_t maxlen, + const char* expected) +{ + size_t len = strlen(path); + char *buf = git__malloc(len + 2); + cl_assert(buf); + + strncpy(buf, path, len + 2); + + git_fs_path_string_to_dir(buf, maxlen); + + cl_assert_equal_s(expected, buf); + + git__free(buf); +} + +/* convert paths to dirs */ +void test_path_core__07_path_to_dir(void) +{ + check_path_to_dir("", ""); + check_path_to_dir(".", "./"); + check_path_to_dir("./", "./"); + check_path_to_dir("a/", "a/"); + check_path_to_dir("ab", "ab/"); + /* make sure we try just under and just over an expansion that will + * require a realloc + */ + check_path_to_dir("abcdef", "abcdef/"); + check_path_to_dir("abcdefg", "abcdefg/"); + check_path_to_dir("abcdefgh", "abcdefgh/"); + check_path_to_dir("abcdefghi", "abcdefghi/"); + check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/"); + check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/"); + + check_string_to_dir("", 1, ""); + check_string_to_dir(".", 1, "."); + check_string_to_dir(".", 2, "./"); + check_string_to_dir(".", 3, "./"); + check_string_to_dir("abcd", 3, "abcd"); + check_string_to_dir("abcd", 4, "abcd"); + check_string_to_dir("abcd", 5, "abcd/"); + check_string_to_dir("abcd", 6, "abcd/"); +} + +/* join path to itself */ +void test_path_core__08_self_join(void) +{ + git_str path = GIT_STR_INIT; + size_t asize = 0; + + asize = path.asize; + cl_git_pass(git_str_sets(&path, "/foo")); + cl_assert_equal_s(path.ptr, "/foo"); + cl_assert(asize < path.asize); + + asize = path.asize; + cl_git_pass(git_str_joinpath(&path, path.ptr, "this is a new string")); + cl_assert_equal_s(path.ptr, "/foo/this is a new string"); + cl_assert(asize < path.asize); + + asize = path.asize; + cl_git_pass(git_str_joinpath(&path, path.ptr, "/grow the buffer, grow the buffer, grow the buffer")); + cl_assert_equal_s(path.ptr, "/foo/this is a new string/grow the buffer, grow the buffer, grow the buffer"); + cl_assert(asize < path.asize); + + git_str_dispose(&path); + cl_git_pass(git_str_sets(&path, "/foo/bar")); + + cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "baz")); + cl_assert_equal_s(path.ptr, "/bar/baz"); + + asize = path.asize; + cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc")); + cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc"); + cl_assert(asize < path.asize); + + git_str_dispose(&path); +} + +static void check_percent_decoding(const char *expected_result, const char *input) +{ + git_str buf = GIT_STR_INIT; + + cl_git_pass(git__percent_decode(&buf, input)); + cl_assert_equal_s(expected_result, git_str_cstr(&buf)); + + git_str_dispose(&buf); +} + +void test_path_core__09_percent_decode(void) +{ + check_percent_decoding("abcd", "abcd"); + check_percent_decoding("a2%", "a2%"); + check_percent_decoding("a2%3", "a2%3"); + check_percent_decoding("a2%%3", "a2%%3"); + check_percent_decoding("a2%3z", "a2%3z"); + check_percent_decoding("a,", "a%2c"); + check_percent_decoding("a21", "a2%31"); + check_percent_decoding("a2%1", "a2%%31"); + check_percent_decoding("a bc ", "a%20bc%20"); + check_percent_decoding("Vicent Mart" "\355", "Vicent%20Mart%ED"); +} + +static void check_fromurl(const char *expected_result, const char *input, int should_fail) +{ + git_str buf = GIT_STR_INIT; + + assert(should_fail || expected_result); + + if (!should_fail) { + cl_git_pass(git_fs_path_fromurl(&buf, input)); + cl_assert_equal_s(expected_result, git_str_cstr(&buf)); + } else + cl_git_fail(git_fs_path_fromurl(&buf, input)); + + git_str_dispose(&buf); +} + +#ifdef GIT_WIN32 +#define ABS_PATH_MARKER "" +#else +#define ABS_PATH_MARKER "/" +#endif + +void test_path_core__10_fromurl(void) +{ + /* Failing cases */ + check_fromurl(NULL, "a", 1); + check_fromurl(NULL, "http:///c:/Temp%20folder/note.txt", 1); + check_fromurl(NULL, "file://c:/Temp%20folder/note.txt", 1); + check_fromurl(NULL, "file:////c:/Temp%20folder/note.txt", 1); + check_fromurl(NULL, "file:///", 1); + check_fromurl(NULL, "file:////", 1); + check_fromurl(NULL, "file://servername/c:/Temp%20folder/note.txt", 1); + + /* Passing cases */ + check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file:///c:/Temp%20folder/note.txt", 0); + check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file://localhost/c:/Temp%20folder/note.txt", 0); + check_fromurl(ABS_PATH_MARKER "c:/Temp+folder/note.txt", "file:///c:/Temp+folder/note.txt", 0); + check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0); +} + +typedef struct { + int expect_idx; + int cancel_after; + char **expect; +} check_walkup_info; + +#define CANCEL_VALUE 1234 + +static int check_one_walkup_step(void *ref, const char *path) +{ + check_walkup_info *info = (check_walkup_info *)ref; + + if (!info->cancel_after) { + cl_assert_equal_s(info->expect[info->expect_idx], "[CANCEL]"); + return CANCEL_VALUE; + } + info->cancel_after--; + + cl_assert(info->expect[info->expect_idx] != NULL); + cl_assert_equal_s(info->expect[info->expect_idx], path); + info->expect_idx++; + + return 0; +} + +void test_path_core__11_walkup(void) +{ + git_str p = GIT_STR_INIT; + + char *expect[] = { + /* 1 */ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, + /* 2 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, + /* 3 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, + /* 4 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, + /* 5 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL, + /* 6 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL, + /* 7 */ "this_is_a_path", "", NULL, + /* 8 */ "this_is_a_path/", "", NULL, + /* 9 */ "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL, + /* 10 */ "a/b/c/", "a/b/", "a/", "", NULL, + /* 11 */ "a/b/c", "a/b/", "a/", "", NULL, + /* 12 */ "a/b/c/", "a/b/", "a/", NULL, + /* 13 */ "", NULL, + /* 14 */ "/", NULL, + /* 15 */ NULL + }; + + char *root[] = { + /* 1 */ NULL, + /* 2 */ NULL, + /* 3 */ "/", + /* 4 */ "", + /* 5 */ "/a/b", + /* 6 */ "/a/b/", + /* 7 */ NULL, + /* 8 */ NULL, + /* 9 */ NULL, + /* 10 */ NULL, + /* 11 */ NULL, + /* 12 */ "a/", + /* 13 */ NULL, + /* 14 */ NULL, + }; + + int i, j; + check_walkup_info info; + + info.expect = expect; + info.cancel_after = -1; + + for (i = 0, j = 0; expect[i] != NULL; i++, j++) { + + git_str_sets(&p, expect[i]); + + info.expect_idx = i; + cl_git_pass( + git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info) + ); + + cl_assert_equal_s(p.ptr, expect[i]); + cl_assert(expect[info.expect_idx] == NULL); + i = info.expect_idx; + } + + git_str_dispose(&p); +} + +void test_path_core__11a_walkup_cancel(void) +{ + git_str p = GIT_STR_INIT; + int cancel[] = { 3, 2, 1, 0 }; + char *expect[] = { + "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "[CANCEL]", NULL, + "/a/b/c/d/e", "/a/b/c/d/", "[CANCEL]", NULL, + "/a/b/c/d/e", "[CANCEL]", NULL, + "[CANCEL]", NULL, + NULL + }; + char *root[] = { NULL, NULL, "/", "", NULL }; + int i, j; + check_walkup_info info; + + info.expect = expect; + + for (i = 0, j = 0; expect[i] != NULL; i++, j++) { + + git_str_sets(&p, expect[i]); + + info.cancel_after = cancel[j]; + info.expect_idx = i; + + cl_assert_equal_i( + CANCEL_VALUE, + git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info) + ); + + /* skip to next run of expectations */ + while (expect[i] != NULL) i++; + } + + git_str_dispose(&p); +} + +void test_path_core__12_offset_to_path_root(void) +{ + cl_assert(git_fs_path_root("non/rooted/path") == -1); + cl_assert(git_fs_path_root("/rooted/path") == 0); + +#ifdef GIT_WIN32 + /* Windows specific tests */ + cl_assert(git_fs_path_root("C:non/rooted/path") == -1); + cl_assert(git_fs_path_root("C:/rooted/path") == 2); + cl_assert(git_fs_path_root("//computername/sharefolder/resource") == 14); + cl_assert(git_fs_path_root("//computername/sharefolder") == 14); + cl_assert(git_fs_path_root("//computername") == -1); +#endif +} + +#define NON_EXISTING_FILEPATH "i_hope_i_do_not_exist" + +void test_path_core__13_cannot_prettify_a_non_existing_file(void) +{ + git_str p = GIT_STR_INIT; + + cl_assert_equal_b(git_fs_path_exists(NON_EXISTING_FILEPATH), false); + cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH, NULL)); + cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH "/so-do-i", NULL)); + + git_str_dispose(&p); +} + +void test_path_core__14_apply_relative(void) +{ + git_str p = GIT_STR_INIT; + + cl_git_pass(git_str_sets(&p, "/this/is/a/base")); + + cl_git_pass(git_fs_path_apply_relative(&p, "../test")); + cl_assert_equal_s("/this/is/a/test", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../../the/./end")); + cl_assert_equal_s("/this/is/the/end", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "./of/this/../the/string")); + cl_assert_equal_s("/this/is/the/end/of/the/string", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../../../../../..")); + cl_assert_equal_s("/this/", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../")); + cl_assert_equal_s("/", p.ptr); + + cl_git_fail(git_fs_path_apply_relative(&p, "../../..")); + + + cl_git_pass(git_str_sets(&p, "d:/another/test")); + + cl_git_pass(git_fs_path_apply_relative(&p, "../..")); + cl_assert_equal_s("d:/", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "from/here/to/../and/./back/.")); + cl_assert_equal_s("d:/from/here/and/back/", p.ptr); + + + cl_git_pass(git_str_sets(&p, "https://my.url.com/test.git")); + + cl_git_pass(git_fs_path_apply_relative(&p, "../another.git")); + cl_assert_equal_s("https://my.url.com/another.git", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../full/path/url.patch")); + cl_assert_equal_s("https://my.url.com/full/path/url.patch", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "..")); + cl_assert_equal_s("https://my.url.com/full/path/", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../../../")); + cl_assert_equal_s("https://", p.ptr); + + + cl_git_pass(git_str_sets(&p, "../../this/is/relative")); + + cl_git_pass(git_fs_path_apply_relative(&p, "../../preserves/the/prefix")); + cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../../../../that")); + cl_assert_equal_s("../../that", p.ptr); + + cl_git_pass(git_fs_path_apply_relative(&p, "../there")); + cl_assert_equal_s("../../there", p.ptr); + git_str_dispose(&p); +} + +static void assert_resolve_relative( + git_str *buf, const char *expected, const char *path) +{ + cl_git_pass(git_str_sets(buf, path)); + cl_git_pass(git_fs_path_resolve_relative(buf, 0)); + cl_assert_equal_s(expected, buf->ptr); +} + +void test_path_core__15_resolve_relative(void) +{ + git_str buf = GIT_STR_INIT; + + assert_resolve_relative(&buf, "", ""); + assert_resolve_relative(&buf, "", "."); + assert_resolve_relative(&buf, "", "./"); + assert_resolve_relative(&buf, "..", ".."); + assert_resolve_relative(&buf, "../", "../"); + assert_resolve_relative(&buf, "..", "./.."); + assert_resolve_relative(&buf, "../", "./../"); + assert_resolve_relative(&buf, "../", "../."); + assert_resolve_relative(&buf, "../", ".././"); + assert_resolve_relative(&buf, "../..", "../.."); + assert_resolve_relative(&buf, "../../", "../../"); + + assert_resolve_relative(&buf, "/", "/"); + assert_resolve_relative(&buf, "/", "/."); + + assert_resolve_relative(&buf, "", "a/.."); + assert_resolve_relative(&buf, "", "a/../"); + assert_resolve_relative(&buf, "", "a/../."); + + assert_resolve_relative(&buf, "/a", "/a"); + assert_resolve_relative(&buf, "/a/", "/a/."); + assert_resolve_relative(&buf, "/", "/a/../"); + assert_resolve_relative(&buf, "/", "/a/../."); + assert_resolve_relative(&buf, "/", "/a/.././"); + + assert_resolve_relative(&buf, "a", "a"); + assert_resolve_relative(&buf, "a/", "a/"); + assert_resolve_relative(&buf, "a/", "a/."); + assert_resolve_relative(&buf, "a/", "a/./"); + + assert_resolve_relative(&buf, "a/b", "a//b"); + assert_resolve_relative(&buf, "a/b/c", "a/b/c"); + assert_resolve_relative(&buf, "b/c", "./b/c"); + assert_resolve_relative(&buf, "a/c", "a/./c"); + assert_resolve_relative(&buf, "a/b/", "a/b/."); + + assert_resolve_relative(&buf, "/a/b/c", "///a/b/c"); + assert_resolve_relative(&buf, "/", "////"); + assert_resolve_relative(&buf, "/a", "///a"); + assert_resolve_relative(&buf, "/", "///."); + assert_resolve_relative(&buf, "/", "///a/.."); + + assert_resolve_relative(&buf, "../../path", "../../test//../././path"); + assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d"); + + cl_git_pass(git_str_sets(&buf, "/..")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_str_sets(&buf, "/./..")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_str_sets(&buf, "/.//..")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_str_sets(&buf, "/../.")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_str_sets(&buf, "/../.././../a")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); + + cl_git_pass(git_str_sets(&buf, "////..")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); + + /* things that start with Windows network paths */ +#ifdef GIT_WIN32 + assert_resolve_relative(&buf, "//a/b/c", "//a/b/c"); + assert_resolve_relative(&buf, "//a/", "//a/b/.."); + assert_resolve_relative(&buf, "//a/b/c", "//a/Q/../b/x/y/../../c"); + + cl_git_pass(git_str_sets(&buf, "//a/b/../..")); + cl_git_fail(git_fs_path_resolve_relative(&buf, 0)); +#else + assert_resolve_relative(&buf, "/a/b/c", "//a/b/c"); + assert_resolve_relative(&buf, "/a/", "//a/b/.."); + assert_resolve_relative(&buf, "/a/b/c", "//a/Q/../b/x/y/../../c"); + assert_resolve_relative(&buf, "/", "//a/b/../.."); +#endif + + git_str_dispose(&buf); +} + +#define assert_common_dirlen(i, p, q) \ + cl_assert_equal_i((i), git_fs_path_common_dirlen((p), (q))); + +void test_path_core__16_resolve_relative(void) +{ + assert_common_dirlen(0, "", ""); + assert_common_dirlen(0, "", "bar.txt"); + assert_common_dirlen(0, "foo.txt", "bar.txt"); + assert_common_dirlen(0, "foo.txt", ""); + assert_common_dirlen(0, "foo/bar.txt", "bar/foo.txt"); + assert_common_dirlen(0, "foo/bar.txt", "../foo.txt"); + + assert_common_dirlen(1, "/one.txt", "/two.txt"); + assert_common_dirlen(4, "foo/one.txt", "foo/two.txt"); + assert_common_dirlen(5, "/foo/one.txt", "/foo/two.txt"); + + assert_common_dirlen(6, "a/b/c/foo.txt", "a/b/c/d/e/bar.txt"); + assert_common_dirlen(7, "/a/b/c/foo.txt", "/a/b/c/d/e/bar.txt"); +} + +static void fix_path(git_str *s) +{ +#ifndef GIT_WIN32 + GIT_UNUSED(s); +#else + char* c; + + for (c = s->ptr; *c; c++) { + if (*c == '/') + *c = '\\'; + } +#endif +} + +void test_path_core__find_exe_in_path(void) +{ + char *orig_path; + git_str sandbox_path = GIT_STR_INIT; + git_str new_path = GIT_STR_INIT, full_path = GIT_STR_INIT, + dummy_path = GIT_STR_INIT; + +#ifdef GIT_WIN32 + static const char *bogus_path_1 = "c:\\does\\not\\exist\\"; + static const char *bogus_path_2 = "e:\\non\\existent"; +#else + static const char *bogus_path_1 = "/this/path/does/not/exist/"; + static const char *bogus_path_2 = "/non/existent"; +#endif + + orig_path = cl_getenv("PATH"); + + git_str_puts(&sandbox_path, clar_sandbox_path()); + git_str_joinpath(&dummy_path, sandbox_path.ptr, "dummmmmmmy_libgit2_file"); + cl_git_rewritefile(dummy_path.ptr, "this is a dummy file"); + + fix_path(&sandbox_path); + fix_path(&dummy_path); + + cl_git_pass(git_str_printf(&new_path, "%s%c%s%c%s%c%s", + bogus_path_1, GIT_PATH_LIST_SEPARATOR, + orig_path, GIT_PATH_LIST_SEPARATOR, + sandbox_path.ptr, GIT_PATH_LIST_SEPARATOR, + bogus_path_2)); + + check_setenv("PATH", new_path.ptr); + + cl_git_fail_with(GIT_ENOTFOUND, git_fs_path_find_executable(&full_path, "this_file_does_not_exist")); + cl_git_pass(git_fs_path_find_executable(&full_path, "dummmmmmmy_libgit2_file")); + + cl_assert_equal_s(full_path.ptr, dummy_path.ptr); + + git_str_dispose(&full_path); + git_str_dispose(&new_path); + git_str_dispose(&dummy_path); + git_str_dispose(&sandbox_path); + git__free(orig_path); +} + +void test_path_core__validate_current_user_ownership(void) +{ + bool is_cur; + + cl_must_pass(p_mkdir("testdir", 0777)); + cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testdir")); + cl_assert_equal_i(is_cur, 1); + + cl_git_rewritefile("testfile", "This is a test file."); + cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testfile")); + cl_assert_equal_i(is_cur, 1); + +#ifdef GIT_WIN32 + cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "C:\\")); + cl_assert_equal_i(is_cur, 0); + + cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "c:\\path\\does\\not\\exist")); +#else + cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "/")); + cl_assert_equal_i(is_cur, (geteuid() == 0)); + + cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "/path/does/not/exist")); +#endif +} + +void test_path_core__dirlen(void) +{ + cl_assert_equal_sz(13, git_fs_path_dirlen("/foo/bar/asdf")); + cl_assert_equal_sz(13, git_fs_path_dirlen("/foo/bar/asdf/")); + cl_assert_equal_sz(13, git_fs_path_dirlen("/foo/bar/asdf//")); + cl_assert_equal_sz(3, git_fs_path_dirlen("foo////")); + cl_assert_equal_sz(3, git_fs_path_dirlen("foo")); + cl_assert_equal_sz(1, git_fs_path_dirlen("/")); + cl_assert_equal_sz(1, git_fs_path_dirlen("////")); + cl_assert_equal_sz(0, git_fs_path_dirlen("")); +} + static void test_make_relative( const char *expected_path, const char *path, @@ -341,3 +1119,29 @@ void test_path_core__join_unrooted_respects_funny_windows_roots(void) test_join_unrooted("💩:/foo/bar/foobar", 13, "💩:/foo/bar/foobar", "💩:/foo/bar"); test_join_unrooted("💩:/foo/bar/foobar", 9, "💩:/foo/bar/foobar", "💩:/foo/"); } + +void test_path_core__is_root(void) +{ + cl_assert_equal_b(true, git_fs_path_is_root("/")); + cl_assert_equal_b(false, git_fs_path_is_root("//")); + cl_assert_equal_b(false, git_fs_path_is_root("foo/")); + cl_assert_equal_b(false, git_fs_path_is_root("/foo/")); + cl_assert_equal_b(false, git_fs_path_is_root("/foo")); + cl_assert_equal_b(false, git_fs_path_is_root("\\")); + +#ifdef GIT_WIN32 + cl_assert_equal_b(true, git_fs_path_is_root("A:\\")); + cl_assert_equal_b(false, git_fs_path_is_root("B:\\foo")); + cl_assert_equal_b(false, git_fs_path_is_root("B:\\foo\\")); + cl_assert_equal_b(true, git_fs_path_is_root("C:\\")); + cl_assert_equal_b(true, git_fs_path_is_root("c:\\")); + cl_assert_equal_b(true, git_fs_path_is_root("z:\\")); + cl_assert_equal_b(false, git_fs_path_is_root("z:\\\\")); + cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost")); + cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost\\")); + cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost\\c$\\")); + cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost\\c$\\Foo")); + cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost\\c$\\Foo\\")); + cl_assert_equal_b(false, git_fs_path_is_root("\\\\Volume\\12345\\Foo\\Bar.txt")); +#endif +} diff --git a/tests/util/path/win32.c b/tests/util/path/win32.c index 1aaf6867a26..6f90b447dda 100644 --- a/tests/util/path/win32.c +++ b/tests/util/path/win32.c @@ -278,5 +278,38 @@ void test_path_win32__8dot3_name(void) cl_must_pass(p_mkdir(".bar", 0777)); cl_assert_equal_s("BAR~2", (shortname = git_win32_path_8dot3_name(".bar"))); git__free(shortname); + + p_rmdir(".foo"); + p_rmdir(".bar"); + p_unlink("bar~1"); +#endif +} + +void test_path_win32__realpath(void) +{ +#ifdef GIT_WIN32 + git_str expected = GIT_STR_INIT; + char result[GIT_PATH_MAX]; + + /* Ensure relative paths become absolute */ + cl_must_pass(git_str_joinpath(&expected, clar_sandbox_path(), "abcdef")); + cl_must_pass(p_mkdir("abcdef", 0777)); + cl_assert(p_realpath("./abcdef", result) != NULL); + cl_assert_equal_s(expected.ptr, result); + + /* Ensure case is canonicalized */ + git_str_clear(&expected); + cl_must_pass(git_str_joinpath(&expected, clar_sandbox_path(), "FOO")); + cl_must_pass(p_mkdir("FOO", 0777)); + cl_assert(p_realpath("foo", result) != NULL); + cl_assert_equal_s(expected.ptr, result); + + cl_assert(p_realpath("nonexistent", result) == NULL); + cl_assert_equal_i(ENOENT, errno); + + git_str_dispose(&expected); + + p_rmdir("abcdef"); + p_rmdir("FOO"); #endif } diff --git a/tests/util/process/env.c b/tests/util/process/env.c new file mode 100644 index 00000000000..bb7dbcdcdfd --- /dev/null +++ b/tests/util/process/env.c @@ -0,0 +1,111 @@ +#include "clar_libgit2.h" +#include "process.h" +#include "vector.h" + +static git_str env_cmd = GIT_STR_INIT; +static git_str accumulator = GIT_STR_INIT; +static git_vector env_result = GIT_VECTOR_INIT; + +void test_process_env__initialize(void) +{ +#ifdef GIT_WIN32 + git_str_printf(&env_cmd, "%s/env.cmd", cl_fixture("process")); +#else + git_str_puts(&env_cmd, "/usr/bin/env"); +#endif + + cl_git_pass(git_vector_init(&env_result, 32, git__strcmp_cb)); +} + +void test_process_env__cleanup(void) +{ + git_vector_free(&env_result); + git_str_dispose(&accumulator); + git_str_dispose(&env_cmd); +} + +static void run_env(const char **env_array, size_t env_len, bool exclude_env) +{ + const char *args_array[] = { env_cmd.ptr }; + + git_process *process; + git_process_options opts = GIT_PROCESS_OPTIONS_INIT; + git_process_result result = GIT_PROCESS_RESULT_INIT; + + char buf[1024], *tok; + ssize_t ret; + + opts.capture_out = 1; + opts.exclude_env = exclude_env; + + cl_git_pass(git_process_new(&process, args_array, ARRAY_SIZE(args_array), env_array, env_len, &opts)); + cl_git_pass(git_process_start(process)); + + while ((ret = git_process_read(process, buf, 1024)) > 0) + cl_git_pass(git_str_put(&accumulator, buf, (size_t)ret)); + + cl_assert_equal_i(0, ret); + + cl_git_pass(git_process_wait(&result, process)); + + cl_assert_equal_i(GIT_PROCESS_STATUS_NORMAL, result.status); + cl_assert_equal_i(0, result.exitcode); + cl_assert_equal_i(0, result.signal); + + for (tok = strtok(accumulator.ptr, "\n"); tok; tok = strtok(NULL, "\n")) { +#ifdef GIT_WIN32 + if (strlen(tok) && tok[strlen(tok) - 1] == '\r') + tok[strlen(tok) - 1] = '\0'; +#endif + + cl_git_pass(git_vector_insert(&env_result, tok)); + } + + git_process_close(process); + git_process_free(process); +} + +void test_process_env__can_add_env(void) +{ + const char *env_array[] = { "TEST_NEW_ENV=added", "TEST_OTHER_ENV=also_added" }; + run_env(env_array, 2, false); + + cl_git_pass(git_vector_search(NULL, &env_result, "TEST_NEW_ENV=added")); + cl_git_pass(git_vector_search(NULL, &env_result, "TEST_OTHER_ENV=also_added")); +} + +void test_process_env__can_propagate_env(void) +{ + cl_setenv("TEST_NEW_ENV", "propagated"); + run_env(NULL, 0, false); + + cl_git_pass(git_vector_search(NULL, &env_result, "TEST_NEW_ENV=propagated")); +} + +void test_process_env__can_remove_env(void) +{ + const char *env_array[] = { "TEST_NEW_ENV=" }; + char *str; + size_t i; + + cl_setenv("TEST_NEW_ENV", "propagated"); + run_env(env_array, 1, false); + + git_vector_foreach(&env_result, i, str) + cl_assert(git__prefixcmp(str, "TEST_NEW_ENV=") != 0); +} + +void test_process_env__can_clear_env(void) +{ + const char *env_array[] = { "TEST_NEW_ENV=added", "TEST_OTHER_ENV=also_added" }; + + cl_setenv("SOME_EXISTING_ENV", "propagated"); + run_env(env_array, 2, true); + + /* + * We can't simply test that the environment is precisely what we + * provided. Some systems (eg win32) will add environment variables + * to all processes. + */ + cl_assert_equal_i(GIT_ENOTFOUND, git_vector_search(NULL, &env_result, "SOME_EXISTING_ENV=propagated")); +} diff --git a/tests/util/process/start.c b/tests/util/process/start.c new file mode 100644 index 00000000000..19ae5e312a4 --- /dev/null +++ b/tests/util/process/start.c @@ -0,0 +1,247 @@ +#include "clar_libgit2.h" +#include "process.h" +#include "vector.h" + +#ifndef GIT_WIN32 +# include +#endif + +#ifndef SIGTERM +# define SIGTERM 42 +#endif + +#ifndef SIGPIPE +# define SIGPIPE 42 +#endif + +static git_str helloworld_cmd = GIT_STR_INIT; +static git_str cat_cmd = GIT_STR_INIT; +static git_str pwd_cmd = GIT_STR_INIT; + +void test_process_start__initialize(void) +{ +#ifdef GIT_WIN32 + git_str_printf(&helloworld_cmd, "%s/helloworld.bat", cl_fixture("process")); + git_str_printf(&cat_cmd, "%s/cat.bat", cl_fixture("process")); + git_str_printf(&pwd_cmd, "%s/pwd.bat", cl_fixture("process")); +#else + git_str_printf(&helloworld_cmd, "%s/helloworld.sh", cl_fixture("process")); +#endif +} + +void test_process_start__cleanup(void) +{ + git_str_dispose(&pwd_cmd); + git_str_dispose(&cat_cmd); + git_str_dispose(&helloworld_cmd); +} + +void test_process_start__returncode(void) +{ +#if defined(GIT_WIN32) + const char *args_array[] = { "C:\\Windows\\System32\\cmd.exe", "/c", "exit", "1" }; +#elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) || \ + defined(__MidnightBSD__) || defined(__DragonFly__) + const char *args_array[] = { "/usr/bin/false" }; +#else + const char *args_array[] = { "/bin/false" }; +#endif + + git_process *process; + git_process_options opts = GIT_PROCESS_OPTIONS_INIT; + git_process_result result = GIT_PROCESS_RESULT_INIT; + + cl_git_pass(git_process_new(&process, args_array, ARRAY_SIZE(args_array), NULL, 0, &opts)); + cl_git_pass(git_process_start(process)); + cl_git_pass(git_process_wait(&result, process)); + + cl_assert_equal_i(GIT_PROCESS_STATUS_NORMAL, result.status); + cl_assert_equal_i(1, result.exitcode); + cl_assert_equal_i(0, result.signal); + + git_process_free(process); +} + +void test_process_start__not_found(void) +{ +#ifdef GIT_WIN32 + const char *args_array[] = { "C:\\a\\b\\z\\y\\not_found" }; +#else + const char *args_array[] = { "/a/b/z/y/not_found" }; +#endif + + git_process *process; + git_process_options opts = GIT_PROCESS_OPTIONS_INIT; + + cl_git_pass(git_process_new(&process, args_array, ARRAY_SIZE(args_array), NULL, 0, &opts)); + cl_git_fail(git_process_start(process)); + git_process_free(process); +} + +static void write_all(git_process *process, char *buf) +{ + size_t buf_len = strlen(buf); + ssize_t ret; + + while (buf_len) { + ret = git_process_write(process, buf, buf_len); + cl_git_pass(ret < 0 ? (int)ret : 0); + + buf += ret; + buf_len -= ret; + } +} + +static void read_all(git_str *out, git_process *process) +{ + char buf[32]; + size_t buf_len = 32; + ssize_t ret; + + while ((ret = git_process_read(process, buf, buf_len)) > 0) + cl_git_pass(git_str_put(out, buf, ret)); + + cl_git_pass(ret < 0 ? (int)ret : 0); +} + +void test_process_start__redirect_stdio(void) +{ +#ifdef GIT_WIN32 + const char *args_array[] = { "C:\\Windows\\System32\\cmd.exe", "/c", cat_cmd.ptr }; +#else + const char *args_array[] = { "/bin/cat" }; +#endif + + git_process *process; + git_process_options opts = GIT_PROCESS_OPTIONS_INIT; + git_process_result result = GIT_PROCESS_RESULT_INIT; + git_str buf = GIT_STR_INIT; + + opts.capture_in = 1; + opts.capture_out = 1; + + cl_git_pass(git_process_new(&process, args_array, ARRAY_SIZE(args_array), NULL, 0, &opts)); + cl_git_pass(git_process_start(process)); + + write_all(process, "Hello, world.\r\nHello!\r\n"); + cl_git_pass(git_process_close_in(process)); + + read_all(&buf, process); + cl_assert_equal_s("Hello, world.\r\nHello!\r\n", buf.ptr); + + cl_git_pass(git_process_wait(&result, process)); + + cl_assert_equal_i(GIT_PROCESS_STATUS_NORMAL, result.status); + cl_assert_equal_i(0, result.exitcode); + cl_assert_equal_i(0, result.signal); + + git_str_dispose(&buf); + git_process_free(process); +} + +/* +void test_process_start__catch_sigterm(void) +{ + const char *args_array[] = { "/bin/cat" }; + + git_process *process; + git_process_options opts = GIT_PROCESS_OPTIONS_INIT; + git_process_result result = GIT_PROCESS_RESULT_INIT; + p_pid_t pid; + + opts.capture_out = 1; + + cl_git_pass(git_process_new(&process, args_array, ARRAY_SIZE(args_array), NULL, 0, &opts)); + cl_git_pass(git_process_start(process)); + cl_git_pass(git_process_id(&pid, process)); + + cl_must_pass(kill(pid, SIGTERM)); + + cl_git_pass(git_process_wait(&result, process)); + + cl_assert_equal_i(GIT_PROCESS_STATUS_ERROR, result.status); + cl_assert_equal_i(0, result.exitcode); + cl_assert_equal_i(SIGTERM, result.signal); + + git_process_free(process); +} + +void test_process_start__catch_sigpipe(void) +{ + const char *args_array[] = { helloworld_cmd.ptr }; + + git_process *process; + git_process_options opts = GIT_PROCESS_OPTIONS_INIT; + git_process_result result = GIT_PROCESS_RESULT_INIT; + + opts.capture_out = 1; + + cl_git_pass(git_process_new(&process, args_array, ARRAY_SIZE(args_array), NULL, 0, &opts)); + cl_git_pass(git_process_start(process)); + cl_git_pass(git_process_close(process)); + cl_git_pass(git_process_wait(&result, process)); + + cl_assert_equal_i(GIT_PROCESS_STATUS_ERROR, result.status); + cl_assert_equal_i(0, result.exitcode); + cl_assert_equal_i(SIGPIPE, result.signal); + + git_process_free(process); +} +*/ + +void test_process_start__can_chdir(void) +{ +#ifdef GIT_WIN32 + const char *args_array[] = { "C:\\Windows\\System32\\cmd.exe", "/c", pwd_cmd.ptr }; + char *startwd = "C:\\"; +#else + const char *args_array[] = { "/bin/pwd" }; + char *startwd = "/"; +#endif + + git_process *process; + git_process_options opts = GIT_PROCESS_OPTIONS_INIT; + git_process_result result = GIT_PROCESS_RESULT_INIT; + git_str buf = GIT_STR_INIT; + + opts.cwd = startwd; + opts.capture_out = 1; + + cl_git_pass(git_process_new(&process, args_array, ARRAY_SIZE(args_array), NULL, 0, &opts)); + cl_git_pass(git_process_start(process)); + + read_all(&buf, process); + git_str_rtrim(&buf); + + cl_assert_equal_s(startwd, buf.ptr); + + cl_git_pass(git_process_wait(&result, process)); + + cl_assert_equal_i(GIT_PROCESS_STATUS_NORMAL, result.status); + cl_assert_equal_i(0, result.exitcode); + cl_assert_equal_i(0, result.signal); + + git_str_dispose(&buf); + git_process_free(process); +} + +void test_process_start__cannot_chdir_to_nonexistent_dir(void) +{ +#ifdef GIT_WIN32 + const char *args_array[] = { "C:\\Windows\\System32\\cmd.exe", "/c", pwd_cmd.ptr }; + char *startwd = "C:\\a\\b\\z\\y\\not_found"; +#else + const char *args_array[] = { "/bin/pwd" }; + char *startwd = "/a/b/z/y/not_found"; +#endif + + git_process *process; + git_process_options opts = GIT_PROCESS_OPTIONS_INIT; + + opts.cwd = startwd; + opts.capture_out = 1; + + cl_git_pass(git_process_new(&process, args_array, ARRAY_SIZE(args_array), NULL, 0, &opts)); + cl_git_fail(git_process_start(process)); + git_process_free(process); +} diff --git a/tests/util/process/win32.c b/tests/util/process/win32.c new file mode 100644 index 00000000000..c04a56ec54b --- /dev/null +++ b/tests/util/process/win32.c @@ -0,0 +1,63 @@ +#include "clar_libgit2.h" +#include "process.h" +#include "vector.h" + +#ifdef GIT_WIN32 +static git_str result; + +# define assert_cmdline(expected, given) do { \ + cl_git_pass(git_process__cmdline(&result, given, ARRAY_SIZE(given))); \ + cl_assert_equal_s(expected, result.ptr); \ + git_str_dispose(&result); \ + } while(0) + +#endif + + +void test_process_win32__cmdline_is_whitespace_delimited(void) +{ +#ifdef GIT_WIN32 + const char *one[] = { "one" }; + const char *two[] = { "one", "two" }; + const char *three[] = { "one", "two", "three" }; + const char *four[] = { "one", "two", "three", "four" }; + + assert_cmdline("one", one); + assert_cmdline("one two", two); + assert_cmdline("one two three", three); + assert_cmdline("one two three four", four); +#endif +} + +void test_process_win32__cmdline_escapes_whitespace(void) +{ +#ifdef GIT_WIN32 + const char *spaces[] = { "one with spaces" }; + const char *tabs[] = { "one\twith\ttabs" }; + const char *multiple[] = { "one with many spaces" }; + + assert_cmdline("one\" \"with\" \"spaces", spaces); + assert_cmdline("one\"\t\"with\"\t\"tabs", tabs); + assert_cmdline("one\" \"with\" \"many\" \"spaces", multiple); +#endif +} + +void test_process_win32__cmdline_escapes_quotes(void) +{ +#ifdef GIT_WIN32 + const char *one[] = { "echo", "\"hello world\"" }; + + assert_cmdline("echo \\\"hello\" \"world\\\"", one); +#endif +} + +void test_process_win32__cmdline_escapes_backslash(void) +{ +#ifdef GIT_WIN32 + const char *one[] = { "foo\\bar", "foo\\baz" }; + const char *two[] = { "c:\\program files\\foo bar\\foo bar.exe", "c:\\path\\to\\other\\", "/a", "/b" }; + + assert_cmdline("foo\\\\bar foo\\\\baz", one); + assert_cmdline("c:\\\\program\" \"files\\\\foo\" \"bar\\\\foo\" \"bar.exe c:\\\\path\\\\to\\\\other\\\\ /a /b", two); +#endif +} diff --git a/tests/util/str/oom.c b/tests/util/str/oom.c index dd3796674f6..810c1f25a7a 100644 --- a/tests/util/str/oom.c +++ b/tests/util/str/oom.c @@ -1,4 +1,5 @@ #include "clar_libgit2.h" +#include "clar_libgit2_alloc.h" /* Override default allocators with ones that will fail predictably. */ @@ -56,3 +57,15 @@ void test_str_oom__grow_by(void) cl_assert(git_str_grow_by(&buf, 101) == -1); cl_assert(git_str_oom(&buf)); } + +void test_str_oom__allocation_failure(void) +{ + git_str buf = GIT_STR_INIT; + + cl_alloc_limit(10); + + cl_git_pass(git_str_puts(&buf, "foobar")); + cl_git_fail(git_str_puts(&buf, "foobar")); + + cl_alloc_reset(); +} diff --git a/tests/util/string.c b/tests/util/string.c index de04dea6983..051f8c3298c 100644 --- a/tests/util/string.c +++ b/tests/util/string.c @@ -111,12 +111,6 @@ void test_string__strcasecmp(void) cl_assert(git__strcasecmp("foo", "FOO") == 0); cl_assert(git__strcasecmp("foo", "fOO") == 0); - cl_assert(strcasecmp("rt\303\202of", "rt dev\302\266h") > 0); - cl_assert(strcasecmp("e\342\202\254ghi=", "et") > 0); - cl_assert(strcasecmp("rt dev\302\266h", "rt\303\202of") < 0); - cl_assert(strcasecmp("et", "e\342\202\254ghi=") < 0); - cl_assert(strcasecmp("\303\215", "\303\255") < 0); - cl_assert(git__strcasecmp("rt\303\202of", "rt dev\302\266h") > 0); cl_assert(git__strcasecmp("e\342\202\254ghi=", "et") > 0); cl_assert(git__strcasecmp("rt dev\302\266h", "rt\303\202of") < 0); diff --git a/tests/util/url/http.c b/tests/util/url/http.c new file mode 100644 index 00000000000..88238896257 --- /dev/null +++ b/tests/util/url/http.c @@ -0,0 +1,752 @@ +#include "clar_libgit2.h" +#include "net.h" + +static git_net_url conndata; + +void test_url_http__initialize(void) +{ + memset(&conndata, 0, sizeof(conndata)); +} + +void test_url_http__cleanup(void) +{ + git_net_url_dispose(&conndata); +} + +/* Hostname */ + +void test_url_http__has_scheme(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "http://example.com/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__no_scheme(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "example.com/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__hostname_root(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "example.com/")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__hostname_implied_root(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "example.com")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__hostname_numeric(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "8888888/")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "8888888"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__hostname_implied_root_custom_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "example.com:42")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "42"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__hostname_implied_root_empty_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "example.com:")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__hostname_encoded_password(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass%2fis%40bad@hostname.com:1234/")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "hostname.com"); + cl_assert_equal_s(conndata.port, "1234"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass/is@bad"); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__hostname_user(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "user@example.com/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__hostname_user_pass(void) +{ + /* user:pass@hostname.tld/resource */ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass@example.com/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass"); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__hostname_port(void) +{ + /* hostname.tld:port/resource */ + cl_git_pass(git_net_url_parse_http(&conndata, + "example.com:9191/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__hostname_empty_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "example.com:/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__hostname_user_port(void) +{ + /* user@hostname.tld:port/resource */ + cl_git_pass(git_net_url_parse_http(&conndata, + "user@example.com:9191/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__hostname_user_pass_port(void) +{ + /* user:pass@hostname.tld:port/resource */ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass@example.com:9191/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass"); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__hostname_user_pass_port_query(void) +{ + /* user:pass@hostname.tld:port/resource */ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass@example.com:9191/resource?query=q&foo=bar&z=asdf")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass"); + cl_assert_equal_s(conndata.query, "query=q&foo=bar&z=asdf"); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__hostname_user_pass_port_fragment(void) +{ + /* user:pass@hostname.tld:port/resource */ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass@example.com:9191/resource#fragment")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass"); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_s(conndata.fragment, "fragment"); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__hostname_user_pass_port_query_fragment(void) +{ + /* user:pass@hostname.tld:port/resource */ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass@example.com:9191/resource?query=q&foo=bar&z=asdf#fragment")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass"); + cl_assert_equal_s(conndata.query, "query=q&foo=bar&z=asdf"); + cl_assert_equal_s(conndata.fragment, "fragment"); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__fragment_with_question_mark(void) +{ + /* user:pass@hostname.tld:port/resource */ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass@example.com:9191/resource#fragment_with?question_mark")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass"); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_s(conndata.fragment, "fragment_with?question_mark"); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +/* IPv4 addresses */ + +void test_url_http__ipv4_trivial(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "192.168.1.1/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "192.168.1.1"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv4_root(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "192.168.1.1/")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "192.168.1.1"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv4_implied_root(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "192.168.1.1")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "192.168.1.1"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv4_implied_root_custom_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "192.168.1.1:42")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "192.168.1.1"); + cl_assert_equal_s(conndata.port, "42"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__ipv4_implied_root_empty_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "192.168.1.1:")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "192.168.1.1"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv4_encoded_password(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass%2fis%40bad@192.168.1.1:1234/")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "192.168.1.1"); + cl_assert_equal_s(conndata.port, "1234"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass/is@bad"); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__ipv4_user(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "user@192.168.1.1/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "192.168.1.1"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv4_user_pass(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass@192.168.1.1/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "192.168.1.1"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass"); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv4_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "192.168.1.1:9191/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "192.168.1.1"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__ipv4_empty_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "192.168.1.1:/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "192.168.1.1"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv4_user_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "user@192.168.1.1:9191/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "192.168.1.1"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__ipv4_user_pass_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass@192.168.1.1:9191/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "192.168.1.1"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass"); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +/* IPv6 addresses */ + +void test_url_http__ipv6_trivial(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "[fe80::dcad:beff:fe00:0001]/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv6_root(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "[fe80::dcad:beff:fe00:0001]/")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv6_implied_root(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "[fe80::dcad:beff:fe00:0001]")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv6_implied_root_custom_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "[fe80::dcad:beff:fe00:0001]:42")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001"); + cl_assert_equal_s(conndata.port, "42"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__ipv6_implied_root_empty_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "[fe80::dcad:beff:fe00:0001]:")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv6_encoded_password(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass%2fis%40bad@[fe80::dcad:beff:fe00:0001]:1234/")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001"); + cl_assert_equal_s(conndata.port, "1234"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass/is@bad"); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__ipv6_user(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "user@[fe80::dcad:beff:fe00:0001]/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv6_user_pass(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass@[fe80::dcad:beff:fe00:0001]/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass"); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv6_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "[fe80::dcad:beff:fe00:0001]:9191/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__ipv6_empty_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "[fe80::dcad:beff:fe00:0001]:/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__ipv6_user_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "user@[fe80::dcad:beff:fe00:0001]:9191/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__ipv6_user_pass_port(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, + "user:pass@[fe80::dcad:beff:fe00:0001]:9191/resource")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001"); + cl_assert_equal_s(conndata.port, "9191"); + cl_assert_equal_s(conndata.path, "/resource"); + cl_assert_equal_s(conndata.username, "user"); + cl_assert_equal_s(conndata.password, "pass"); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + +void test_url_http__ipv6_invalid_addresses(void) +{ + /* Opening bracket missing */ + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001]/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001]/")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001]")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001]:42")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001]:")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user:pass%2fis%40bad@fe80::dcad:beff:fe00:0001]:1234/")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user@fe80::dcad:beff:fe00:0001]/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user:pass@fe80::dcad:beff:fe00:0001]/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001]:9191/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001]:/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user@fe80::dcad:beff:fe00:0001]:9191/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user:pass@fe80::dcad:beff:fe00:0001]:9191/resource")); + + /* Closing bracket missing */ + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "[fe80::dcad:beff:fe00:0001/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "[fe80::dcad:beff:fe00:0001/")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "[fe80::dcad:beff:fe00:0001")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "[fe80::dcad:beff:fe00:0001:42")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "[fe80::dcad:beff:fe00:0001:")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user:pass%2fis%40bad@[fe80::dcad:beff:fe00:0001:1234/")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user@[fe80::dcad:beff:fe00:0001/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user:pass@[fe80::dcad:beff:fe00:0001/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "[fe80::dcad:beff:fe00:0001:9191/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "[fe80::dcad:beff:fe00:0001:/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user@[fe80::dcad:beff:fe00:0001:9191/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user:pass@[fe80::dcad:beff:fe00:0001:9191/resource")); + + /* Both brackets missing */ + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001/")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001:42")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001:")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user:pass%2fis%40bad@fe80::dcad:beff:fe00:0001:1234/")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user@fe80::dcad:beff:fe00:0001/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user:pass@fe80::dcad:beff:fe00:0001/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001:9191/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::dcad:beff:fe00:0001:/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user@fe80::dcad:beff:fe00:0001:9191/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "user:pass@fe80::dcad:beff:fe00:0001:9191/resource")); + + /* Invalid character inside address */ + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, "[fe8o::dcad:beff:fe00:0001]/resource")); + + /* Characters before/after braces */ + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "fe80::[dcad:beff:fe00:0001]/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "cafe[fe80::dcad:beff:fe00:0001]/resource")); + cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, + "[fe80::dcad:beff:fe00:0001]cafe/resource")); +} + +/* Oddities */ + +void test_url_http__invalid_scheme_is_relative(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "foo!bar://host:42/path/to/project?query_string=yes")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "foo!bar"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "//host:42/path/to/project"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_s(conndata.query, "query_string=yes"); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__scheme_case_is_normalized(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "GIT+SSH://host:42/path/to/project")); + cl_assert_equal_s(conndata.scheme, "git+ssh"); +} + +void test_url_http__no_scheme_relative_path(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "path")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "path"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__no_scheme_absolute_path(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "/path")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_p(conndata.host, NULL); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/path"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__empty_path_with_empty_authority(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_p(conndata.host, NULL); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} + +void test_url_http__spaces_in_the_name(void) +{ + cl_git_pass(git_net_url_parse_http(&conndata, "libgit2@dev.azure.com/libgit2/test/_git/spaces%20in%20the%20name")); + cl_assert_equal_s(conndata.scheme, "http"); + cl_assert_equal_s(conndata.host, "dev.azure.com"); + cl_assert_equal_s(conndata.port, "80"); + cl_assert_equal_s(conndata.path, "/libgit2/test/_git/spaces%20in%20the%20name"); + cl_assert_equal_s(conndata.username, "libgit2"); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1); +} diff --git a/tests/util/url/joinpath.c b/tests/util/url/joinpath.c index 9fc02cde4a9..6027093e492 100644 --- a/tests/util/url/joinpath.c +++ b/tests/util/url/joinpath.c @@ -1,6 +1,5 @@ #include "clar_libgit2.h" #include "net.h" -#include "netops.h" static git_net_url source, target; diff --git a/tests/util/url/parse.c b/tests/util/url/parse.c index 631d9b456d9..35486f7b7a7 100644 --- a/tests/util/url/parse.c +++ b/tests/util/url/parse.c @@ -669,6 +669,20 @@ void test_url_parse__ipv6_invalid_addresses(void) /* Oddities */ +void test_url_parse__empty_scheme(void) +{ + cl_git_pass(git_net_url_parse(&conndata, "://example.com/resource")); + cl_assert_equal_s(conndata.scheme, NULL); + cl_assert_equal_s(conndata.host, NULL); + cl_assert_equal_s(conndata.port, NULL); + cl_assert_equal_s(conndata.path, "//example.com/resource"); + cl_assert_equal_p(conndata.username, NULL); + cl_assert_equal_p(conndata.password, NULL); + cl_assert_equal_p(conndata.query, NULL); + cl_assert_equal_p(conndata.fragment, NULL); + cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0); +} + void test_url_parse__invalid_scheme_is_relative(void) { cl_git_pass(git_net_url_parse(&conndata, "foo!bar://host:42/path/to/project?query_string=yes")); diff --git a/tests/util/url/redirect.c b/tests/util/url/redirect.c index 5401778618f..a6f99dcdf09 100644 --- a/tests/util/url/redirect.c +++ b/tests/util/url/redirect.c @@ -1,6 +1,5 @@ #include "clar_libgit2.h" #include "net.h" -#include "netops.h" static git_net_url conndata;