From ad5611d85b27cf20f342ff382f93db9312eed074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20R=C3=B6hling?= Date: Sun, 28 Aug 2022 14:13:25 +0200 Subject: [PATCH] New upstream version 1.5.0+ds --- .clang-format | 92 +++ .github/release.yml | 5 +- .github/workflows/benchmark.yml | 82 +++ .github/workflows/codeql.yml | 36 - .github/workflows/main.yml | 54 +- .github/workflows/nightly.yml | 43 +- CMakeLists.txt | 19 +- COPYING | 40 ++ README.md | 4 + ci/build.sh | 7 +- ci/docker/bionic | 15 +- ci/docker/centos7 | 7 +- ci/docker/centos8 | 13 +- ci/docker/focal | 14 +- ci/docker/xenial | 16 +- ci/getcontainer.sh | 4 + ci/{setup-mingw.sh => setup-mingw-build.sh} | 4 +- ci/setup-osx-benchmark.sh | 6 + ci/{setup-osx.sh => setup-osx-build.sh} | 2 +- ci/setup-ubuntu-benchmark.sh | 20 + ci/setup-win32-benchmark.sh | 9 + ci/test.sh | 18 +- cmake/AddClarTest.cmake | 7 + cmake/FindPCRE.cmake | 7 +- cmake/FindPCRE2.cmake | 2 +- cmake/SelectHTTPSBackend.cmake | 2 +- cmake/SelectHashes.cmake | 76 +- docs/changelog.md | 100 ++- examples/CMakeLists.txt | 6 +- fuzzers/CMakeLists.txt | 2 + fuzzers/packfile_fuzzer.c | 2 +- include/git2/branch.h | 4 +- include/git2/common.h | 11 + include/git2/config.h | 12 +- include/git2/deprecated.h | 2 + include/git2/errors.h | 20 +- include/git2/merge.h | 2 +- include/git2/status.h | 7 +- include/git2/sys/remote.h | 15 + include/git2/sys/transport.h | 14 +- include/git2/version.h | 33 +- package.json | 2 +- src/CMakeLists.txt | 330 +++------ src/README.md | 12 + src/cli/CMakeLists.txt | 53 ++ src/cli/README.md | 26 + src/{hash/sha1/generic.h => cli/cli.h} | 19 +- src/cli/cmd.c | 21 + src/cli/cmd.h | 33 + src/cli/cmd_cat_file.c | 204 ++++++ src/cli/cmd_clone.c | 176 +++++ src/cli/cmd_hash_object.c | 135 ++++ src/cli/cmd_help.c | 86 +++ src/cli/error.h | 51 ++ src/cli/main.c | 106 +++ src/cli/opt.c | 669 ++++++++++++++++++ src/cli/opt.h | 349 +++++++++ src/cli/opt_usage.c | 194 +++++ src/cli/opt_usage.h | 35 + src/cli/progress.c | 345 +++++++++ src/cli/progress.h | 117 +++ src/cli/sighandler.h | 20 + src/cli/unix/sighandler.c | 36 + src/{ => cli}/win32/precompiled.c | 0 src/cli/win32/precompiled.h | 3 + src/cli/win32/sighandler.c | 37 + src/features.h.in | 9 + src/hash/sha1.h | 40 -- src/hash/sha1/generic.c | 300 -------- src/hash/sha1/openssl.c | 59 -- src/hash/sha1/win32.c | 333 --------- src/hash/sha1/win32.h | 128 ---- src/libgit2/CMakeLists.txt | 131 ++++ src/{ => libgit2}/annotated_commit.c | 0 src/{ => libgit2}/annotated_commit.h | 0 src/{ => libgit2}/apply.c | 0 src/{ => libgit2}/apply.h | 0 src/{ => libgit2}/attr.c | 0 src/{ => libgit2}/attr.h | 0 src/{ => libgit2}/attr_file.c | 0 src/{ => libgit2}/attr_file.h | 0 src/{ => libgit2}/attrcache.c | 0 src/{ => libgit2}/attrcache.h | 0 src/{ => libgit2}/blame.c | 0 src/{ => libgit2}/blame.h | 0 src/{ => libgit2}/blame_git.c | 0 src/{ => libgit2}/blame_git.h | 0 src/{ => libgit2}/blob.c | 2 +- src/{ => libgit2}/blob.h | 0 src/{ => libgit2}/branch.c | 23 +- src/{ => libgit2}/branch.h | 0 src/{ => libgit2}/buf.c | 0 src/{ => libgit2}/buf.h | 0 src/{ => libgit2}/cache.c | 0 src/{ => libgit2}/cache.h | 0 src/{ => libgit2}/checkout.c | 0 src/{ => libgit2}/checkout.h | 0 src/{ => libgit2}/cherrypick.c | 0 src/{ => libgit2}/clone.c | 0 src/{ => libgit2}/clone.h | 0 src/{ => libgit2}/commit.c | 0 src/{ => libgit2}/commit.h | 0 src/{ => libgit2}/commit_graph.c | 37 +- src/{ => libgit2}/commit_graph.h | 2 +- src/{ => libgit2}/commit_list.c | 0 src/{ => libgit2}/commit_list.h | 0 src/libgit2/common.h | 55 ++ src/{ => libgit2}/config.c | 9 +- src/{ => libgit2}/config.h | 0 src/{ => libgit2}/config_backend.h | 0 src/{ => libgit2}/config_cache.c | 0 src/{ => libgit2}/config_entries.c | 0 src/{ => libgit2}/config_entries.h | 0 src/{ => libgit2}/config_file.c | 0 src/{ => libgit2}/config_mem.c | 0 src/{ => libgit2}/config_parse.c | 0 src/{ => libgit2}/config_parse.h | 0 src/{ => libgit2}/config_snapshot.c | 0 src/{ => libgit2}/crlf.c | 0 src/{ => libgit2}/delta.c | 0 src/{ => libgit2}/delta.h | 0 src/{ => libgit2}/describe.c | 0 src/{ => libgit2}/diff.c | 0 src/{ => libgit2}/diff.h | 0 src/{ => libgit2}/diff_driver.c | 0 src/{ => libgit2}/diff_driver.h | 0 src/{ => libgit2}/diff_file.c | 0 src/{ => libgit2}/diff_file.h | 0 src/{ => libgit2}/diff_generate.c | 0 src/{ => libgit2}/diff_generate.h | 0 src/{ => libgit2}/diff_parse.c | 0 src/{ => libgit2}/diff_parse.h | 0 src/{ => libgit2}/diff_print.c | 0 src/{ => libgit2}/diff_stats.c | 0 src/{ => libgit2}/diff_stats.h | 0 src/{ => libgit2}/diff_tform.c | 0 src/{ => libgit2}/diff_tform.h | 0 src/{ => libgit2}/diff_xdiff.c | 1 + src/{ => libgit2}/diff_xdiff.h | 0 src/{ => libgit2}/email.c | 0 src/{ => libgit2}/email.h | 0 src/{ => libgit2}/errors.c | 0 src/{ => libgit2}/errors.h | 3 +- src/{ => libgit2}/fetch.c | 0 src/{ => libgit2}/fetch.h | 0 src/{ => libgit2}/fetchhead.c | 0 src/{ => libgit2}/fetchhead.h | 0 src/{ => libgit2}/filter.c | 32 +- src/{ => libgit2}/filter.h | 0 src/{win32 => libgit2}/git2.rc | 0 src/{ => libgit2}/graph.c | 0 src/{ => libgit2}/hashsig.c | 0 src/{ => libgit2}/ident.c | 0 src/{ => libgit2}/idxmap.c | 0 src/{ => libgit2}/idxmap.h | 0 src/{ => libgit2}/ignore.c | 0 src/{ => libgit2}/ignore.h | 0 src/{ => libgit2}/index.c | 12 +- src/{ => libgit2}/index.h | 0 src/{ => libgit2}/indexer.c | 4 +- src/{ => libgit2}/indexer.h | 0 src/{ => libgit2}/iterator.c | 6 +- src/{ => libgit2}/iterator.h | 0 src/{ => libgit2}/libgit2.c | 5 + src/{ => libgit2}/libgit2.h | 0 src/{ => libgit2}/mailmap.c | 0 src/{ => libgit2}/mailmap.h | 0 src/{ => libgit2}/merge.c | 0 src/{ => libgit2}/merge.h | 0 src/{ => libgit2}/merge_driver.c | 0 src/{ => libgit2}/merge_driver.h | 0 src/{ => libgit2}/merge_file.c | 0 src/{ => libgit2}/message.c | 0 src/{ => libgit2}/midx.c | 43 +- src/{ => libgit2}/midx.h | 3 +- src/{ => libgit2}/mwindow.c | 18 +- src/{ => libgit2}/mwindow.h | 2 +- src/{ => libgit2}/netops.c | 1 - src/{ => libgit2}/netops.h | 0 src/{ => libgit2}/notes.c | 0 src/{ => libgit2}/notes.h | 0 src/{ => libgit2}/object.c | 1 + src/{ => libgit2}/object.h | 0 src/{ => libgit2}/object_api.c | 0 src/{ => libgit2}/odb.c | 4 +- src/{ => libgit2}/odb.h | 0 src/{ => libgit2}/odb_loose.c | 0 src/{ => libgit2}/odb_mempack.c | 0 src/{ => libgit2}/odb_pack.c | 0 src/{ => libgit2}/offmap.c | 0 src/{ => libgit2}/offmap.h | 0 src/{ => libgit2}/oid.c | 23 +- src/{ => libgit2}/oid.h | 37 +- src/{ => libgit2}/oidarray.c | 0 src/{ => libgit2}/oidarray.h | 0 src/{ => libgit2}/oidmap.c | 2 +- src/{ => libgit2}/oidmap.h | 0 src/{ => libgit2}/pack-objects.c | 0 src/{ => libgit2}/pack-objects.h | 0 src/{ => libgit2}/pack.c | 42 +- src/{ => libgit2}/pack.h | 5 +- src/{ => libgit2}/parse.c | 0 src/{ => libgit2}/parse.h | 0 src/{ => libgit2}/patch.c | 0 src/{ => libgit2}/patch.h | 0 src/{ => libgit2}/patch_generate.c | 0 src/{ => libgit2}/patch_generate.h | 0 src/{ => libgit2}/patch_parse.c | 0 src/{ => libgit2}/patch_parse.h | 0 src/{ => libgit2}/path.c | 1 + src/{ => libgit2}/path.h | 0 src/{ => libgit2}/pathspec.c | 0 src/{ => libgit2}/pathspec.h | 0 src/{ => libgit2}/proxy.c | 0 src/{ => libgit2}/proxy.h | 0 src/{ => libgit2}/push.c | 0 src/{ => libgit2}/push.h | 0 src/{ => libgit2}/reader.c | 0 src/{ => libgit2}/reader.h | 0 src/{ => libgit2}/rebase.c | 12 +- src/{ => libgit2}/refdb.c | 0 src/{ => libgit2}/refdb.h | 0 src/{ => libgit2}/refdb_fs.c | 8 +- src/{ => libgit2}/reflog.c | 0 src/{ => libgit2}/reflog.h | 0 src/{ => libgit2}/refs.c | 0 src/{ => libgit2}/refs.h | 0 src/{ => libgit2}/refspec.c | 0 src/{ => libgit2}/refspec.h | 0 src/{ => libgit2}/remote.c | 2 +- src/{ => libgit2}/remote.h | 2 +- src/{ => libgit2}/repo_template.h | 0 src/{ => libgit2}/repository.c | 108 ++- src/{ => libgit2}/repository.h | 0 src/{ => libgit2}/reset.c | 0 src/{ => libgit2}/revert.c | 0 src/{ => libgit2}/revparse.c | 0 src/{ => libgit2}/revwalk.c | 0 src/{ => libgit2}/revwalk.h | 0 src/{ => libgit2}/settings.h | 0 src/{ => libgit2}/signature.c | 0 src/{ => libgit2}/signature.h | 0 src/{ => libgit2}/stash.c | 0 src/{ => libgit2}/status.c | 0 src/{ => libgit2}/status.h | 0 src/{ => libgit2}/strarray.c | 0 src/{ => libgit2}/stream.h | 0 src/{ => libgit2}/streams/mbedtls.c | 0 src/{ => libgit2}/streams/mbedtls.h | 0 src/{ => libgit2}/streams/openssl.c | 0 src/{ => libgit2}/streams/openssl.h | 0 src/{ => libgit2}/streams/openssl_dynamic.c | 0 src/{ => libgit2}/streams/openssl_dynamic.h | 0 src/{ => libgit2}/streams/openssl_legacy.c | 0 src/{ => libgit2}/streams/openssl_legacy.h | 0 src/{ => libgit2}/streams/registry.c | 0 src/{ => libgit2}/streams/registry.h | 0 src/{ => libgit2}/streams/socket.c | 0 src/{ => libgit2}/streams/socket.h | 0 src/{ => libgit2}/streams/stransport.c | 0 src/{ => libgit2}/streams/stransport.h | 0 src/{ => libgit2}/streams/tls.c | 0 src/{ => libgit2}/streams/tls.h | 0 src/{ => libgit2}/submodule.c | 0 src/{ => libgit2}/submodule.h | 0 src/{ => libgit2}/sysdir.c | 0 src/{ => libgit2}/sysdir.h | 0 src/{ => libgit2}/tag.c | 20 +- src/{ => libgit2}/tag.h | 0 src/{ => libgit2}/threadstate.c | 0 src/{ => libgit2}/threadstate.h | 0 src/{ => libgit2}/trace.c | 0 src/{ => libgit2}/trace.h | 0 src/{ => libgit2}/trailer.c | 0 src/{ => libgit2}/transaction.c | 0 src/{ => libgit2}/transaction.h | 0 src/{ => libgit2}/transport.c | 0 src/{ => libgit2}/transports/auth.c | 0 src/{ => libgit2}/transports/auth.h | 0 src/{ => libgit2}/transports/auth_negotiate.c | 0 src/{ => libgit2}/transports/auth_negotiate.h | 0 src/{ => libgit2}/transports/auth_ntlm.c | 0 src/{ => libgit2}/transports/auth_ntlm.h | 0 src/{ => libgit2}/transports/credential.c | 0 .../transports/credential_helpers.c | 0 src/{ => libgit2}/transports/git.c | 0 src/{ => libgit2}/transports/http.c | 0 src/{ => libgit2}/transports/http.h | 0 src/{ => libgit2}/transports/httpclient.c | 2 +- src/{ => libgit2}/transports/httpclient.h | 0 src/{ => libgit2}/transports/local.c | 0 src/{ => libgit2}/transports/smart.c | 12 + src/{ => libgit2}/transports/smart.h | 0 src/{ => libgit2}/transports/smart_pkt.c | 0 src/{ => libgit2}/transports/smart_protocol.c | 0 src/{ => libgit2}/transports/ssh.c | 0 src/{ => libgit2}/transports/ssh.h | 0 src/{ => libgit2}/transports/winhttp.c | 0 src/{ => libgit2}/tree-cache.c | 2 +- src/{ => libgit2}/tree-cache.h | 0 src/{ => libgit2}/tree.c | 39 +- src/{ => libgit2}/tree.h | 2 +- src/{ => libgit2}/userdiff.h | 0 src/{ => libgit2}/worktree.c | 0 src/{ => libgit2}/worktree.h | 0 src/{ => libgit2}/xdiff/git-xdiff.h | 0 src/{ => libgit2}/xdiff/xdiff.h | 0 src/{ => libgit2}/xdiff/xdiffi.c | 0 src/{ => libgit2}/xdiff/xdiffi.h | 0 src/{ => libgit2}/xdiff/xemit.c | 0 src/{ => libgit2}/xdiff/xemit.h | 0 src/{ => libgit2}/xdiff/xhistogram.c | 0 src/{ => libgit2}/xdiff/xinclude.h | 0 src/{ => libgit2}/xdiff/xmacros.h | 0 src/{ => libgit2}/xdiff/xmerge.c | 0 src/{ => libgit2}/xdiff/xpatience.c | 0 src/{ => libgit2}/xdiff/xprepare.c | 0 src/{ => libgit2}/xdiff/xprepare.h | 0 src/{ => libgit2}/xdiff/xtypes.h | 0 src/{ => libgit2}/xdiff/xutils.c | 0 src/{ => libgit2}/xdiff/xutils.h | 0 src/util/CMakeLists.txt | 74 ++ src/{ => util}/alloc.c | 0 src/{ => util}/alloc.h | 0 src/{ => util}/allocators/failalloc.c | 0 src/{ => util}/allocators/failalloc.h | 2 +- src/{ => util}/allocators/stdalloc.c | 0 src/{ => util}/allocators/stdalloc.h | 2 +- src/{ => util}/allocators/win32_leakcheck.c | 0 src/{ => util}/allocators/win32_leakcheck.h | 2 +- src/{ => util}/array.h | 2 +- src/{ => util}/assert_safe.h | 16 + src/{ => util}/bitvec.h | 0 src/{ => util}/cc-compat.h | 0 src/{ => util}/date.c | 7 +- src/{ => util}/date.h | 0 src/{ => util}/filebuf.c | 2 +- src/{ => util}/filebuf.h | 2 +- src/{ => util}/fs_path.c | 207 +++--- src/{ => util}/fs_path.h | 48 +- src/{ => util}/futils.c | 46 +- src/{ => util}/futils.h | 3 +- src/{common.h => util/git2_util.h} | 72 +- src/{ => util}/hash.c | 18 +- src/{ => util}/hash.h | 8 +- src/util/hash/builtin.c | 53 ++ .../sha1/openssl.h => util/hash/builtin.h} | 12 +- .../sha1 => util/hash}/collisiondetect.c | 2 +- .../sha1 => util/hash}/collisiondetect.h | 6 +- src/{hash/sha1 => util/hash}/common_crypto.c | 55 ++ src/{hash/sha1 => util/hash}/common_crypto.h | 14 +- src/{hash/sha1 => util/hash}/mbedtls.c | 46 ++ src/{hash/sha1 => util/hash}/mbedtls.h | 18 +- src/util/hash/openssl.c | 194 +++++ src/util/hash/openssl.h | 45 ++ src/util/hash/rfc6234/sha.h | 355 ++++++++++ src/util/hash/rfc6234/sha224-256.c | 601 ++++++++++++++++ src/util/hash/sha.h | 70 ++ src/{hash/sha1 => util/hash}/sha1dc/sha1.c | 0 src/{hash/sha1 => util/hash}/sha1dc/sha1.h | 0 .../sha1 => util/hash}/sha1dc/ubc_check.c | 0 .../sha1 => util/hash}/sha1dc/ubc_check.h | 0 src/util/hash/win32.c | 549 ++++++++++++++ src/util/hash/win32.h | 60 ++ src/{ => util}/integer.h | 0 src/{ => util}/khash.h | 0 src/{ => util}/map.h | 2 +- src/{ => util}/net.c | 1 - src/{ => util}/net.h | 2 +- src/{ => util}/pool.c | 0 src/{ => util}/pool.h | 2 +- src/{ => util}/posix.c | 0 src/{ => util}/posix.h | 2 +- src/{ => util}/pqueue.c | 0 src/{ => util}/pqueue.h | 2 +- src/{ => util}/rand.c | 10 +- src/{ => util}/rand.h | 2 +- src/{ => util}/regexp.c | 0 src/{ => util}/regexp.h | 2 +- src/{ => util}/runtime.c | 2 +- src/{ => util}/runtime.h | 2 +- src/{ => util}/sortedcache.c | 0 src/{ => util}/sortedcache.h | 2 +- src/{ => util}/str.c | 0 src/{ => util}/str.h | 2 +- src/{ => util}/strmap.c | 0 src/{ => util}/strmap.h | 2 +- src/{ => util}/strnlen.h | 0 src/{ => util}/thread.c | 2 +- src/{ => util}/thread.h | 0 src/{ => util}/tsort.c | 2 +- src/{ => util}/unix/map.c | 2 +- src/{ => util}/unix/posix.h | 2 +- src/{ => util}/unix/pthread.h | 0 src/{ => util}/unix/realpath.c | 2 +- src/{ => util}/utf8.c | 2 +- src/{ => util}/utf8.h | 2 +- src/{ => util}/util.c | 2 +- src/{ => util}/util.h | 2 +- src/{ => util}/varint.c | 0 src/{ => util}/varint.h | 2 +- src/{ => util}/vector.c | 0 src/{ => util}/vector.h | 2 +- src/{ => util}/wildmatch.c | 0 src/{ => util}/wildmatch.h | 2 +- src/{ => util}/win32/dir.c | 0 src/{ => util}/win32/dir.h | 2 +- src/{ => util}/win32/error.c | 0 src/{ => util}/win32/error.h | 2 +- src/{ => util}/win32/findfile.c | 0 src/{ => util}/win32/findfile.h | 2 +- src/{ => util}/win32/map.c | 2 +- src/{ => util}/win32/mingw-compat.h | 0 src/{ => util}/win32/msvc-compat.h | 0 src/{ => util}/win32/path_w32.c | 0 src/{ => util}/win32/path_w32.h | 2 +- src/{ => util}/win32/posix.h | 2 +- src/{ => util}/win32/posix_w32.c | 2 +- {tests => src/util/win32}/precompiled.c | 0 src/{ => util}/win32/precompiled.h | 2 +- src/{ => util}/win32/reparse.h | 0 src/{ => util}/win32/thread.c | 0 src/{ => util}/win32/thread.h | 2 +- src/{ => util}/win32/utf-conv.c | 0 src/{ => util}/win32/utf-conv.h | 2 +- src/{ => util}/win32/version.h | 0 src/{ => util}/win32/w32_buffer.c | 0 src/{ => util}/win32/w32_buffer.h | 2 +- src/{ => util}/win32/w32_common.h | 0 src/{ => util}/win32/w32_leakcheck.c | 0 src/{ => util}/win32/w32_leakcheck.h | 2 +- src/{ => util}/win32/w32_util.c | 0 src/{ => util}/win32/w32_util.h | 2 +- src/{ => util}/win32/win32-compat.h | 0 src/{ => util}/zstream.c | 0 src/{ => util}/zstream.h | 2 +- tests/CMakeLists.txt | 100 +-- tests/README.md | 71 +- tests/benchmarks/README.md | 121 ++++ tests/benchmarks/benchmark.sh | 298 ++++++++ tests/benchmarks/benchmark_helpers.sh | 363 ++++++++++ tests/benchmarks/hash-object__text_100kb | 7 + tests/benchmarks/hash-object__text_10mb | 7 + tests/benchmarks/hash-object__text_1kb | 7 + .../benchmarks/hash-object__text_cached_100kb | 7 + .../benchmarks/hash-object__text_cached_10mb | 7 + tests/benchmarks/hash-object__text_cached_1kb | 7 + .../benchmarks/hash-object__write_text_100kb | 9 + tests/benchmarks/hash-object__write_text_10mb | 9 + tests/benchmarks/hash-object__write_text_1kb | 9 + .../hash-object__write_text_cached_100kb | 9 + .../hash-object__write_text_cached_10mb | 9 + .../hash-object__write_text_cached_1kb | 9 + tests/{ => clar}/clar.c | 0 tests/{ => clar}/clar.h | 0 tests/clar/{ => clar}/fixtures.h | 0 tests/clar/{ => clar}/fs.h | 0 tests/clar/{ => clar}/print.h | 0 tests/clar/{ => clar}/sandbox.h | 0 tests/clar/{ => clar}/summary.h | 0 tests/{ => clar}/clar_libgit2.c | 0 tests/{ => clar}/clar_libgit2.h | 0 tests/{ => clar}/clar_libgit2_timer.c | 0 tests/{ => clar}/clar_libgit2_timer.h | 0 tests/{ => clar}/clar_libgit2_trace.c | 0 tests/{ => clar}/clar_libgit2_trace.h | 0 tests/{ => clar}/generate.py | 0 tests/{ => clar}/main.c | 0 tests/headertest/CMakeLists.txt | 14 + tests/{ => headertest}/headertest.c | 0 tests/libgit2/CMakeLists.txt | 75 ++ tests/{ => libgit2}/apply/apply_helpers.c | 0 tests/{ => libgit2}/apply/apply_helpers.h | 0 tests/{ => libgit2}/apply/both.c | 0 tests/{ => libgit2}/apply/callbacks.c | 0 tests/{ => libgit2}/apply/check.c | 0 tests/{ => libgit2}/apply/fromdiff.c | 0 tests/{ => libgit2}/apply/fromfile.c | 0 tests/{ => libgit2}/apply/index.c | 0 tests/{ => libgit2}/apply/partial.c | 0 tests/{ => libgit2}/apply/tree.c | 0 tests/{ => libgit2}/apply/workdir.c | 0 tests/{ => libgit2}/attr/attr_expect.h | 0 tests/{ => libgit2}/attr/file.c | 0 tests/{ => libgit2}/attr/flags.c | 0 tests/{ => libgit2}/attr/lookup.c | 0 tests/{ => libgit2}/attr/macro.c | 0 tests/{ => libgit2}/attr/repo.c | 0 tests/{ => libgit2}/blame/blame_helpers.c | 0 tests/{ => libgit2}/blame/blame_helpers.h | 0 tests/{ => libgit2}/blame/buffer.c | 0 tests/{ => libgit2}/blame/getters.c | 0 tests/{ => libgit2}/blame/harder.c | 0 tests/{ => libgit2}/blame/simple.c | 0 tests/{ => libgit2}/checkout/binaryunicode.c | 0 .../{ => libgit2}/checkout/checkout_helpers.c | 0 .../{ => libgit2}/checkout/checkout_helpers.h | 0 tests/{ => libgit2}/checkout/conflict.c | 0 tests/{ => libgit2}/checkout/crlf.c | 0 tests/{ => libgit2}/checkout/head.c | 0 tests/{ => libgit2}/checkout/icase.c | 0 tests/{ => libgit2}/checkout/index.c | 0 tests/{ => libgit2}/checkout/nasty.c | 0 tests/{ => libgit2}/checkout/tree.c | 0 tests/{ => libgit2}/checkout/typechange.c | 0 tests/{ => libgit2}/cherrypick/bare.c | 0 tests/{ => libgit2}/cherrypick/workdir.c | 0 tests/{ => libgit2}/clone/empty.c | 0 tests/{ => libgit2}/clone/local.c | 0 tests/{ => libgit2}/clone/nonetwork.c | 0 tests/{ => libgit2}/clone/transport.c | 0 tests/{ => libgit2}/commit/commit.c | 0 tests/{ => libgit2}/commit/parent.c | 0 tests/{ => libgit2}/commit/parse.c | 0 tests/{ => libgit2}/commit/signature.c | 0 tests/{ => libgit2}/commit/write.c | 0 tests/{ => libgit2}/config/add.c | 0 tests/{ => libgit2}/config/backend.c | 0 tests/{ => libgit2}/config/conditionals.c | 0 tests/{ => libgit2}/config/config_helpers.c | 0 tests/{ => libgit2}/config/config_helpers.h | 0 tests/{ => libgit2}/config/configlevel.c | 0 tests/{ => libgit2}/config/global.c | 0 tests/{ => libgit2}/config/include.c | 0 tests/{ => libgit2}/config/memory.c | 0 tests/{ => libgit2}/config/multivar.c | 0 tests/{ => libgit2}/config/new.c | 0 tests/{ => libgit2}/config/read.c | 0 tests/{ => libgit2}/config/readonly.c | 0 tests/{ => libgit2}/config/rename.c | 0 tests/{ => libgit2}/config/snapshot.c | 0 tests/{ => libgit2}/config/stress.c | 0 tests/{ => libgit2}/config/validkeyname.c | 0 tests/{ => libgit2}/config/write.c | 0 tests/{ => libgit2}/core/buf.c | 0 tests/{ => libgit2}/core/env.c | 0 tests/{ => libgit2}/core/features.c | 0 tests/libgit2/core/hashsig.c | 182 +++++ tests/{ => libgit2}/core/oid.c | 0 tests/{ => libgit2}/core/oidmap.c | 0 tests/{ => libgit2}/core/opts.c | 0 tests/libgit2/core/pool.c | 33 + tests/{ => libgit2}/core/structinit.c | 0 tests/{ => libgit2}/core/useragent.c | 0 tests/{ => libgit2}/date/date.c | 0 tests/{ => libgit2}/date/rfc2822.c | 0 tests/{ => libgit2}/delta/apply.c | 0 tests/{ => libgit2}/describe/describe.c | 0 .../{ => libgit2}/describe/describe_helpers.c | 0 .../{ => libgit2}/describe/describe_helpers.h | 0 tests/{ => libgit2}/describe/t6120.c | 0 tests/{ => libgit2}/diff/binary.c | 0 tests/{ => libgit2}/diff/blob.c | 0 tests/{ => libgit2}/diff/diff_helpers.c | 0 tests/{ => libgit2}/diff/diff_helpers.h | 0 tests/{ => libgit2}/diff/diffiter.c | 0 tests/{ => libgit2}/diff/drivers.c | 0 .../diff/externalmodifications.c | 0 tests/{ => libgit2}/diff/format_email.c | 0 tests/{ => libgit2}/diff/index.c | 0 tests/{ => libgit2}/diff/notify.c | 0 tests/{ => libgit2}/diff/parse.c | 0 tests/{ => libgit2}/diff/patch.c | 0 tests/{ => libgit2}/diff/patchid.c | 0 tests/{ => libgit2}/diff/pathspec.c | 0 tests/{ => libgit2}/diff/racediffiter.c | 0 tests/{ => libgit2}/diff/rename.c | 0 tests/{ => libgit2}/diff/stats.c | 0 tests/{ => libgit2}/diff/submodules.c | 0 tests/{ => libgit2}/diff/tree.c | 0 tests/libgit2/diff/userdiff.c | 25 + tests/{ => libgit2}/diff/workdir.c | 8 +- tests/{ => libgit2}/email/create.c | 0 tests/{ => libgit2}/email/create.c.bak | 0 tests/{ => libgit2}/fetch/local.c | 0 .../{ => libgit2}/fetchhead/fetchhead_data.h | 0 tests/{ => libgit2}/fetchhead/nonetwork.c | 0 tests/{ => libgit2}/filter/bare.c | 0 tests/{ => libgit2}/filter/blob.c | 0 tests/{ => libgit2}/filter/crlf.c | 0 tests/{ => libgit2}/filter/crlf.h | 0 tests/{ => libgit2}/filter/custom.c | 0 tests/{ => libgit2}/filter/custom_helpers.c | 0 tests/{ => libgit2}/filter/custom_helpers.h | 0 tests/{ => libgit2}/filter/file.c | 0 tests/{ => libgit2}/filter/ident.c | 0 tests/{ => libgit2}/filter/query.c | 0 tests/{ => libgit2}/filter/stream.c | 0 tests/{ => libgit2}/filter/systemattrs.c | 0 tests/{ => libgit2}/filter/wildcard.c | 0 tests/{ => libgit2}/graph/ahead_behind.c | 0 tests/{ => libgit2}/graph/commitgraph.c | 0 tests/{ => libgit2}/graph/descendant_of.c | 0 .../{ => libgit2}/graph/reachable_from_any.c | 0 tests/{ => libgit2}/ignore/path.c | 0 tests/{ => libgit2}/ignore/status.c | 0 tests/{ => libgit2}/index/add.c | 0 tests/{ => libgit2}/index/addall.c | 0 tests/{ => libgit2}/index/bypath.c | 0 tests/{ => libgit2}/index/cache.c | 0 tests/{ => libgit2}/index/collision.c | 0 tests/{ => libgit2}/index/conflicts.c | 0 tests/{ => libgit2}/index/conflicts.h | 0 tests/{ => libgit2}/index/crlf.c | 0 tests/{ => libgit2}/index/filemodes.c | 0 tests/{ => libgit2}/index/inmemory.c | 0 tests/{ => libgit2}/index/names.c | 0 tests/{ => libgit2}/index/nsec.c | 0 tests/{ => libgit2}/index/racy.c | 0 tests/{ => libgit2}/index/read_index.c | 0 tests/{ => libgit2}/index/read_tree.c | 0 tests/{ => libgit2}/index/rename.c | 0 tests/{ => libgit2}/index/reuc.c | 0 tests/{ => libgit2}/index/splitindex.c | 0 tests/{ => libgit2}/index/stage.c | 0 tests/{ => libgit2}/index/tests.c | 0 tests/{ => libgit2}/index/version.c | 0 tests/{ => libgit2}/iterator/index.c | 0 .../{ => libgit2}/iterator/iterator_helpers.c | 0 .../{ => libgit2}/iterator/iterator_helpers.h | 0 tests/{ => libgit2}/iterator/tree.c | 2 +- tests/{ => libgit2}/iterator/workdir.c | 0 tests/{ => libgit2}/mailmap/basic.c | 0 tests/{ => libgit2}/mailmap/blame.c | 0 .../{ => libgit2}/mailmap/mailmap_testdata.h | 0 tests/{ => libgit2}/mailmap/parsing.c | 0 tests/{ => libgit2}/merge/analysis.c | 0 tests/{ => libgit2}/merge/annotated_commit.c | 0 tests/{ => libgit2}/merge/conflict_data.h | 0 tests/{ => libgit2}/merge/driver.c | 0 tests/{ => libgit2}/merge/files.c | 0 tests/{ => libgit2}/merge/merge_helpers.c | 0 tests/{ => libgit2}/merge/merge_helpers.h | 0 tests/{ => libgit2}/merge/trees/automerge.c | 0 tests/{ => libgit2}/merge/trees/commits.c | 0 .../{ => libgit2}/merge/trees/modeconflict.c | 0 tests/{ => libgit2}/merge/trees/recursive.c | 0 tests/{ => libgit2}/merge/trees/renames.c | 0 tests/{ => libgit2}/merge/trees/treediff.c | 0 tests/{ => libgit2}/merge/trees/trivial.c | 0 tests/{ => libgit2}/merge/trees/whitespace.c | 0 tests/{ => libgit2}/merge/workdir/dirty.c | 0 tests/{ => libgit2}/merge/workdir/recursive.c | 0 tests/{ => libgit2}/merge/workdir/renames.c | 0 tests/{ => libgit2}/merge/workdir/setup.c | 0 tests/{ => libgit2}/merge/workdir/simple.c | 0 .../{ => libgit2}/merge/workdir/submodules.c | 0 tests/{ => libgit2}/merge/workdir/trivial.c | 0 tests/{ => libgit2}/message/trailer.c | 0 tests/{ => libgit2}/network/cred.c | 0 tests/{ => libgit2}/network/fetchlocal.c | 0 tests/{ => libgit2}/network/matchhost.c | 0 tests/{ => libgit2}/network/refspecs.c | 0 .../network/remote/defaultbranch.c | 0 tests/{ => libgit2}/network/remote/delete.c | 0 .../network/remote/isvalidname.c | 0 tests/{ => libgit2}/network/remote/local.c | 0 tests/{ => libgit2}/network/remote/push.c | 0 tests/{ => libgit2}/network/remote/remotes.c | 0 tests/{ => libgit2}/network/remote/rename.c | 0 tests/{ => libgit2}/network/url/joinpath.c | 0 tests/{ => libgit2}/network/url/parse.c | 0 tests/{ => libgit2}/network/url/pattern.c | 0 tests/{ => libgit2}/network/url/redirect.c | 0 tests/{ => libgit2}/network/url/scp.c | 0 tests/{ => libgit2}/network/url/valid.c | 0 tests/{ => libgit2}/notes/notes.c | 0 tests/{ => libgit2}/notes/notesref.c | 0 tests/{ => libgit2}/object/blob/filter.c | 0 tests/{ => libgit2}/object/blob/fromstream.c | 0 tests/{ => libgit2}/object/blob/write.c | 0 tests/{ => libgit2}/object/cache.c | 0 .../object/commit/commitstagedfile.c | 0 tests/{ => libgit2}/object/commit/parse.c | 0 tests/{ => libgit2}/object/lookup.c | 0 tests/{ => libgit2}/object/lookupbypath.c | 0 tests/{ => libgit2}/object/message.c | 0 tests/{ => libgit2}/object/peel.c | 0 tests/{ => libgit2}/object/raw/chars.c | 0 tests/{ => libgit2}/object/raw/compare.c | 0 tests/{ => libgit2}/object/raw/convert.c | 0 tests/{ => libgit2}/object/raw/data.h | 0 tests/{ => libgit2}/object/raw/fromstr.c | 0 tests/{ => libgit2}/object/raw/hash.c | 11 +- tests/{ => libgit2}/object/raw/short.c | 5 +- tests/{ => libgit2}/object/raw/size.c | 0 tests/{ => libgit2}/object/raw/type2string.c | 0 tests/{ => libgit2}/object/raw/write.c | 0 tests/{ => libgit2}/object/shortid.c | 0 tests/{ => libgit2}/object/tag/list.c | 0 tests/{ => libgit2}/object/tag/parse.c | 0 tests/{ => libgit2}/object/tag/peel.c | 0 tests/{ => libgit2}/object/tag/read.c | 0 tests/{ => libgit2}/object/tag/write.c | 19 + tests/{ => libgit2}/object/tree/attributes.c | 0 .../object/tree/duplicateentries.c | 0 tests/{ => libgit2}/object/tree/frompath.c | 0 tests/{ => libgit2}/object/tree/parse.c | 2 +- tests/{ => libgit2}/object/tree/read.c | 0 tests/{ => libgit2}/object/tree/update.c | 0 tests/{ => libgit2}/object/tree/walk.c | 0 tests/{ => libgit2}/object/tree/write.c | 0 tests/{ => libgit2}/object/validate.c | 0 tests/{ => libgit2}/odb/alternates.c | 0 .../odb/backend/backend_helpers.c | 0 .../odb/backend/backend_helpers.h | 0 tests/{ => libgit2}/odb/backend/mempack.c | 0 tests/{ => libgit2}/odb/backend/multiple.c | 0 tests/{ => libgit2}/odb/backend/nobackend.c | 0 .../{ => libgit2}/odb/backend/nonrefreshing.c | 0 tests/{ => libgit2}/odb/backend/refreshing.c | 0 tests/{ => libgit2}/odb/backend/simple.c | 0 tests/{ => libgit2}/odb/emptyobjects.c | 0 tests/{ => libgit2}/odb/foreach.c | 0 tests/{ => libgit2}/odb/freshen.c | 0 tests/{ => libgit2}/odb/largefiles.c | 0 tests/{ => libgit2}/odb/loose.c | 0 tests/{ => libgit2}/odb/loose_data.h | 0 tests/{ => libgit2}/odb/mixed.c | 0 tests/{ => libgit2}/odb/pack_data.h | 0 tests/{ => libgit2}/odb/pack_data_one.h | 0 tests/{ => libgit2}/odb/packed.c | 0 tests/{ => libgit2}/odb/packed_one.c | 0 tests/{ => libgit2}/odb/sorting.c | 0 tests/{ => libgit2}/odb/streamwrite.c | 0 tests/{ => libgit2}/online/badssl.c | 0 tests/{ => libgit2}/online/clone.c | 0 tests/{ => libgit2}/online/customcert.c | 0 tests/{ => libgit2}/online/fetch.c | 0 tests/{ => libgit2}/online/fetchhead.c | 0 tests/{ => libgit2}/online/push.c | 0 tests/{ => libgit2}/online/push_util.c | 0 tests/{ => libgit2}/online/push_util.h | 0 tests/{ => libgit2}/online/remotes.c | 0 tests/{ => libgit2}/pack/filelimit.c | 0 tests/{ => libgit2}/pack/indexer.c | 4 +- tests/{ => libgit2}/pack/midx.c | 0 tests/{ => libgit2}/pack/packbuilder.c | 2 +- tests/{ => libgit2}/pack/sharing.c | 0 tests/{ => libgit2}/pack/threadsafety.c | 0 tests/{ => libgit2}/patch/parse.c | 0 tests/{ => libgit2}/patch/patch_common.h | 0 tests/{ => libgit2}/patch/print.c | 0 tests/libgit2/path/validate.c | 63 ++ .../perf/helper__perf__do_merge.c | 0 .../perf/helper__perf__do_merge.h | 0 .../{ => libgit2}/perf/helper__perf__timer.c | 0 .../{ => libgit2}/perf/helper__perf__timer.h | 0 tests/{ => libgit2}/perf/merge.c | 0 tests/libgit2/precompiled.c | 1 + tests/{ => libgit2}/precompiled.h | 0 tests/{ => libgit2}/rebase/abort.c | 0 tests/{ => libgit2}/rebase/inmemory.c | 0 tests/{ => libgit2}/rebase/iterator.c | 0 tests/{ => libgit2}/rebase/merge.c | 0 tests/{ => libgit2}/rebase/setup.c | 0 tests/{ => libgit2}/rebase/sign.c | 0 tests/{ => libgit2}/rebase/submodule.c | 0 tests/{ => libgit2}/refs/basic.c | 0 .../{ => libgit2}/refs/branches/checkedout.c | 0 tests/{ => libgit2}/refs/branches/create.c | 8 + tests/{ => libgit2}/refs/branches/delete.c | 0 tests/{ => libgit2}/refs/branches/ishead.c | 0 tests/{ => libgit2}/refs/branches/iterator.c | 0 tests/{ => libgit2}/refs/branches/lookup.c | 0 tests/{ => libgit2}/refs/branches/move.c | 0 tests/{ => libgit2}/refs/branches/name.c | 0 tests/{ => libgit2}/refs/branches/remote.c | 0 tests/{ => libgit2}/refs/branches/upstream.c | 0 .../refs/branches/upstreamname.c | 0 tests/{ => libgit2}/refs/crashes.c | 0 tests/{ => libgit2}/refs/create.c | 0 tests/{ => libgit2}/refs/delete.c | 0 tests/{ => libgit2}/refs/dup.c | 0 tests/{ => libgit2}/refs/foreachglob.c | 0 tests/{ => libgit2}/refs/isvalidname.c | 0 tests/{ => libgit2}/refs/iterator.c | 0 tests/{ => libgit2}/refs/list.c | 0 tests/{ => libgit2}/refs/listall.c | 0 tests/{ => libgit2}/refs/lookup.c | 0 tests/{ => libgit2}/refs/namespaces.c | 0 tests/{ => libgit2}/refs/normalize.c | 0 tests/{ => libgit2}/refs/overwrite.c | 0 tests/{ => libgit2}/refs/pack.c | 0 tests/{ => libgit2}/refs/peel.c | 0 tests/{ => libgit2}/refs/races.c | 0 tests/{ => libgit2}/refs/read.c | 0 tests/{ => libgit2}/refs/ref_helpers.c | 0 tests/{ => libgit2}/refs/ref_helpers.h | 0 tests/{ => libgit2}/refs/reflog/drop.c | 0 tests/{ => libgit2}/refs/reflog/messages.c | 0 tests/{ => libgit2}/refs/reflog/reflog.c | 0 .../refs/reflog/reflog_helpers.c | 0 .../refs/reflog/reflog_helpers.h | 0 tests/{ => libgit2}/refs/rename.c | 0 tests/{ => libgit2}/refs/revparse.c | 2 - tests/{ => libgit2}/refs/setter.c | 0 tests/{ => libgit2}/refs/shorthand.c | 0 tests/{ => libgit2}/refs/tags/name.c | 0 tests/{ => libgit2}/refs/transactions.c | 0 tests/{ => libgit2}/refs/unicode.c | 0 tests/{ => libgit2}/refs/update.c | 0 tests/{ => libgit2}/remote/create.c | 0 tests/{ => libgit2}/remote/fetch.c | 8 +- tests/{ => libgit2}/remote/httpproxy.c | 0 tests/{ => libgit2}/remote/insteadof.c | 0 tests/{ => libgit2}/remote/list.c | 0 tests/{ => libgit2}/repo/config.c | 0 tests/{ => libgit2}/repo/discover.c | 0 tests/{ => libgit2}/repo/env.c | 0 tests/{ => libgit2}/repo/extensions.c | 0 tests/{ => libgit2}/repo/getters.c | 0 tests/{ => libgit2}/repo/hashfile.c | 0 tests/{ => libgit2}/repo/head.c | 0 tests/{ => libgit2}/repo/headtree.c | 0 tests/{ => libgit2}/repo/init.c | 0 tests/{ => libgit2}/repo/message.c | 0 tests/{ => libgit2}/repo/new.c | 0 tests/{ => libgit2}/repo/open.c | 93 ++- tests/{ => libgit2}/repo/pathspec.c | 0 tests/{ => libgit2}/repo/repo_helpers.c | 0 tests/{ => libgit2}/repo/repo_helpers.h | 0 tests/{ => libgit2}/repo/reservedname.c | 0 tests/{ => libgit2}/repo/setters.c | 0 tests/{ => libgit2}/repo/shallow.c | 0 tests/{ => libgit2}/repo/state.c | 0 tests/{ => libgit2}/repo/template.c | 0 tests/{ => libgit2}/reset/default.c | 0 tests/{ => libgit2}/reset/hard.c | 0 tests/{ => libgit2}/reset/mixed.c | 0 tests/{ => libgit2}/reset/reset_helpers.c | 0 tests/{ => libgit2}/reset/reset_helpers.h | 0 tests/{ => libgit2}/reset/soft.c | 0 tests/{ => libgit2}/revert/bare.c | 0 tests/{ => libgit2}/revert/rename.c | 0 tests/{ => libgit2}/revert/workdir.c | 0 tests/{ => libgit2}/revwalk/basic.c | 0 tests/{ => libgit2}/revwalk/hidecb.c | 0 tests/{ => libgit2}/revwalk/mergebase.c | 0 .../{ => libgit2}/revwalk/signatureparsing.c | 0 tests/{ => libgit2}/revwalk/simplify.c | 0 tests/{ => libgit2}/stash/apply.c | 0 tests/{ => libgit2}/stash/drop.c | 0 tests/{ => libgit2}/stash/foreach.c | 0 tests/{ => libgit2}/stash/save.c | 0 tests/{ => libgit2}/stash/stash_helpers.c | 0 tests/{ => libgit2}/stash/stash_helpers.h | 0 tests/{ => libgit2}/stash/submodules.c | 0 tests/{ => libgit2}/status/renames.c | 0 tests/{ => libgit2}/status/single.c | 0 tests/{ => libgit2}/status/status_data.h | 0 tests/{ => libgit2}/status/status_helpers.c | 0 tests/{ => libgit2}/status/status_helpers.h | 0 tests/{ => libgit2}/status/submodules.c | 0 tests/{ => libgit2}/status/worktree.c | 0 tests/{ => libgit2}/status/worktree_init.c | 0 tests/{ => libgit2}/stream/deprecated.c | 0 tests/{ => libgit2}/stream/registration.c | 0 tests/{ => libgit2}/stress/diff.c | 0 tests/{ => libgit2}/submodule/add.c | 0 tests/{ => libgit2}/submodule/escape.c | 0 tests/{ => libgit2}/submodule/init.c | 0 tests/{ => libgit2}/submodule/inject_option.c | 0 tests/{ => libgit2}/submodule/lookup.c | 0 tests/{ => libgit2}/submodule/modify.c | 0 tests/{ => libgit2}/submodule/nosubs.c | 0 tests/{ => libgit2}/submodule/open.c | 0 .../{ => libgit2}/submodule/repository_init.c | 0 tests/{ => libgit2}/submodule/status.c | 0 .../submodule/submodule_helpers.c | 0 .../submodule/submodule_helpers.h | 0 tests/{ => libgit2}/submodule/update.c | 0 tests/{ => libgit2}/threads/atomic.c | 0 tests/{ => libgit2}/threads/basic.c | 0 tests/{ => libgit2}/threads/diff.c | 0 tests/{ => libgit2}/threads/iterator.c | 0 tests/{ => libgit2}/threads/refdb.c | 0 tests/{ => libgit2}/threads/thread_helpers.c | 0 tests/{ => libgit2}/threads/thread_helpers.h | 0 tests/{ => libgit2}/threads/tlsdata.c | 0 tests/{ => libgit2}/trace/trace.c | 0 .../{ => libgit2}/trace/windows/stacktrace.c | 0 tests/libgit2/transport/register.c | 224 ++++++ tests/{ => libgit2}/transports/smart/packet.c | 0 tests/{ => libgit2}/valgrind-supp-mac.txt | 0 tests/{ => libgit2}/win32/forbidden.c | 0 tests/{ => libgit2}/win32/longpath.c | 0 tests/{ => libgit2}/win32/systemdir.c | 0 tests/{ => libgit2}/worktree/bare.c | 0 tests/{ => libgit2}/worktree/config.c | 0 tests/{ => libgit2}/worktree/merge.c | 0 tests/{ => libgit2}/worktree/open.c | 0 tests/{ => libgit2}/worktree/reflog.c | 0 tests/{ => libgit2}/worktree/refs.c | 0 tests/{ => libgit2}/worktree/repository.c | 0 tests/{ => libgit2}/worktree/submodule.c | 0 tests/{ => libgit2}/worktree/worktree.c | 0 .../{ => libgit2}/worktree/worktree_helpers.c | 0 .../{ => libgit2}/worktree/worktree_helpers.h | 0 tests/path/dotgit.c | 206 ------ tests/resources/sha1/empty | Bin tests/transport/register.c | 79 --- tests/util/CMakeLists.txt | 68 ++ tests/{core => util}/array.c | 2 +- tests/{core => util}/assert.c | 31 +- tests/{core => util}/bitvec.c | 2 +- tests/{core => util}/copy.c | 25 +- tests/util/crlf.h | 30 + tests/{core => util}/dirent.c | 18 +- tests/{core => util}/encoding.c | 4 +- tests/{core => util}/errors.c | 16 +- tests/{core => util}/filebuf.c | 24 +- tests/{core => util}/ftruncate.c | 8 +- tests/{core => util}/futils.c | 12 +- tests/{core => util}/gitstr.c | 235 +----- tests/{core => util}/hex.c | 2 +- tests/{core => util}/iconv.c | 10 +- tests/{core => util}/init.c | 6 +- tests/{core => util}/integer.c | 6 +- tests/{core => util}/link.c | 50 +- tests/{core => util}/memmem.c | 6 +- tests/{core => util}/mkdir.c | 12 +- tests/{core => util}/path.c | 38 +- tests/{ => util}/path/core.c | 62 -- tests/{ => util}/path/win32.c | 0 tests/{core => util}/pool.c | 36 +- tests/{core => util}/posix.c | 14 +- tests/{core => util}/pqueue.c | 10 +- tests/util/precompiled.c | 1 + tests/util/precompiled.h | 3 + tests/{core => util}/qsort.c | 12 +- tests/{core => util}/regexp.c | 46 +- tests/{core => util}/rmdir.c | 14 +- tests/{core => util}/sha1.c | 38 +- tests/util/sha256.c | 113 +++ tests/{core => util}/sortedcache.c | 6 +- tests/{core => util}/stat.c | 8 +- tests/{ => util}/str/basic.c | 0 tests/{ => util}/str/oom.c | 4 +- tests/{ => util}/str/percent.c | 0 tests/{ => util}/str/quote.c | 0 tests/{ => util}/str/splice.c | 0 tests/{core => util}/string.c | 16 +- tests/{core => util}/strmap.c | 26 +- tests/{core => util}/strtol.c | 16 +- tests/{core => util}/utf8.c | 2 +- tests/{core => util}/vector.c | 20 +- tests/{core => util}/wildmatch.c | 16 +- tests/{core => util}/zstream.c | 8 +- 949 files changed, 8689 insertions(+), 2655 deletions(-) create mode 100644 .clang-format create mode 100644 .github/workflows/benchmark.yml delete mode 100644 .github/workflows/codeql.yml rename ci/{setup-mingw.sh => setup-mingw-build.sh} (97%) create mode 100755 ci/setup-osx-benchmark.sh rename ci/{setup-osx.sh => setup-osx-build.sh} (95%) create mode 100755 ci/setup-ubuntu-benchmark.sh create mode 100755 ci/setup-win32-benchmark.sh create mode 100644 cmake/AddClarTest.cmake create mode 100644 src/README.md create mode 100644 src/cli/CMakeLists.txt create mode 100644 src/cli/README.md rename src/{hash/sha1/generic.h => cli/cli.h} (52%) create mode 100644 src/cli/cmd.c create mode 100644 src/cli/cmd.h create mode 100644 src/cli/cmd_cat_file.c create mode 100644 src/cli/cmd_clone.c create mode 100644 src/cli/cmd_hash_object.c create mode 100644 src/cli/cmd_help.c create mode 100644 src/cli/error.h create mode 100644 src/cli/main.c create mode 100644 src/cli/opt.c create mode 100644 src/cli/opt.h create mode 100644 src/cli/opt_usage.c create mode 100644 src/cli/opt_usage.h create mode 100644 src/cli/progress.c create mode 100644 src/cli/progress.h create mode 100644 src/cli/sighandler.h create mode 100644 src/cli/unix/sighandler.c rename src/{ => cli}/win32/precompiled.c (100%) create mode 100644 src/cli/win32/precompiled.h create mode 100644 src/cli/win32/sighandler.c delete mode 100644 src/hash/sha1.h delete mode 100644 src/hash/sha1/generic.c delete mode 100644 src/hash/sha1/openssl.c delete mode 100644 src/hash/sha1/win32.c delete mode 100644 src/hash/sha1/win32.h create mode 100644 src/libgit2/CMakeLists.txt rename src/{ => libgit2}/annotated_commit.c (100%) rename src/{ => libgit2}/annotated_commit.h (100%) rename src/{ => libgit2}/apply.c (100%) rename src/{ => libgit2}/apply.h (100%) rename src/{ => libgit2}/attr.c (100%) rename src/{ => libgit2}/attr.h (100%) rename src/{ => libgit2}/attr_file.c (100%) rename src/{ => libgit2}/attr_file.h (100%) rename src/{ => libgit2}/attrcache.c (100%) rename src/{ => libgit2}/attrcache.h (100%) rename src/{ => libgit2}/blame.c (100%) rename src/{ => libgit2}/blame.h (100%) rename src/{ => libgit2}/blame_git.c (100%) rename src/{ => libgit2}/blame_git.h (100%) rename src/{ => libgit2}/blob.c (99%) rename src/{ => libgit2}/blob.h (100%) rename src/{ => libgit2}/branch.c (98%) rename src/{ => libgit2}/branch.h (100%) rename src/{ => libgit2}/buf.c (100%) rename src/{ => libgit2}/buf.h (100%) rename src/{ => libgit2}/cache.c (100%) rename src/{ => libgit2}/cache.h (100%) rename src/{ => libgit2}/checkout.c (100%) rename src/{ => libgit2}/checkout.h (100%) rename src/{ => libgit2}/cherrypick.c (100%) rename src/{ => libgit2}/clone.c (100%) rename src/{ => libgit2}/clone.h (100%) rename src/{ => libgit2}/commit.c (100%) rename src/{ => libgit2}/commit.h (100%) rename src/{ => libgit2}/commit_graph.c (97%) rename src/{ => libgit2}/commit_graph.h (99%) rename src/{ => libgit2}/commit_list.c (100%) rename src/{ => libgit2}/commit_list.h (100%) create mode 100644 src/libgit2/common.h rename src/{ => libgit2}/config.c (99%) rename src/{ => libgit2}/config.h (100%) rename src/{ => libgit2}/config_backend.h (100%) rename src/{ => libgit2}/config_cache.c (100%) rename src/{ => libgit2}/config_entries.c (100%) rename src/{ => libgit2}/config_entries.h (100%) rename src/{ => libgit2}/config_file.c (100%) rename src/{ => libgit2}/config_mem.c (100%) rename src/{ => libgit2}/config_parse.c (100%) rename src/{ => libgit2}/config_parse.h (100%) rename src/{ => libgit2}/config_snapshot.c (100%) rename src/{ => libgit2}/crlf.c (100%) rename src/{ => libgit2}/delta.c (100%) rename src/{ => libgit2}/delta.h (100%) rename src/{ => libgit2}/describe.c (100%) rename src/{ => libgit2}/diff.c (100%) rename src/{ => libgit2}/diff.h (100%) rename src/{ => libgit2}/diff_driver.c (100%) rename src/{ => libgit2}/diff_driver.h (100%) rename src/{ => libgit2}/diff_file.c (100%) rename src/{ => libgit2}/diff_file.h (100%) rename src/{ => libgit2}/diff_generate.c (100%) rename src/{ => libgit2}/diff_generate.h (100%) rename src/{ => libgit2}/diff_parse.c (100%) rename src/{ => libgit2}/diff_parse.h (100%) rename src/{ => libgit2}/diff_print.c (100%) rename src/{ => libgit2}/diff_stats.c (100%) rename src/{ => libgit2}/diff_stats.h (100%) rename src/{ => libgit2}/diff_tform.c (100%) rename src/{ => libgit2}/diff_tform.h (100%) rename src/{ => libgit2}/diff_xdiff.c (99%) rename src/{ => libgit2}/diff_xdiff.h (100%) rename src/{ => libgit2}/email.c (100%) rename src/{ => libgit2}/email.h (100%) rename src/{ => libgit2}/errors.c (100%) rename src/{ => libgit2}/errors.h (93%) rename src/{ => libgit2}/fetch.c (100%) rename src/{ => libgit2}/fetch.h (100%) rename src/{ => libgit2}/fetchhead.c (100%) rename src/{ => libgit2}/fetchhead.h (100%) rename src/{ => libgit2}/filter.c (96%) rename src/{ => libgit2}/filter.h (100%) rename src/{win32 => libgit2}/git2.rc (100%) rename src/{ => libgit2}/graph.c (100%) rename src/{ => libgit2}/hashsig.c (100%) rename src/{ => libgit2}/ident.c (100%) rename src/{ => libgit2}/idxmap.c (100%) rename src/{ => libgit2}/idxmap.h (100%) rename src/{ => libgit2}/ignore.c (100%) rename src/{ => libgit2}/ignore.h (100%) rename src/{ => libgit2}/index.c (99%) rename src/{ => libgit2}/index.h (100%) rename src/{ => libgit2}/indexer.c (99%) rename src/{ => libgit2}/indexer.h (100%) rename src/{ => libgit2}/iterator.c (99%) rename src/{ => libgit2}/iterator.h (100%) rename src/{ => libgit2}/libgit2.c (99%) rename src/{ => libgit2}/libgit2.h (100%) rename src/{ => libgit2}/mailmap.c (100%) rename src/{ => libgit2}/mailmap.h (100%) rename src/{ => libgit2}/merge.c (100%) rename src/{ => libgit2}/merge.h (100%) rename src/{ => libgit2}/merge_driver.c (100%) rename src/{ => libgit2}/merge_driver.h (100%) rename src/{ => libgit2}/merge_file.c (100%) rename src/{ => libgit2}/message.c (100%) rename src/{ => libgit2}/midx.c (95%) rename src/{ => libgit2}/midx.h (98%) rename src/{ => libgit2}/mwindow.c (95%) rename src/{ => libgit2}/mwindow.h (95%) rename src/{ => libgit2}/netops.c (99%) rename src/{ => libgit2}/netops.h (100%) rename src/{ => libgit2}/notes.c (100%) rename src/{ => libgit2}/notes.h (100%) rename src/{ => libgit2}/object.c (99%) rename src/{ => libgit2}/object.h (100%) rename src/{ => libgit2}/object_api.c (100%) rename src/{ => libgit2}/odb.c (99%) rename src/{ => libgit2}/odb.h (100%) rename src/{ => libgit2}/odb_loose.c (100%) rename src/{ => libgit2}/odb_mempack.c (100%) rename src/{ => libgit2}/odb_pack.c (100%) rename src/{ => libgit2}/offmap.c (100%) rename src/{ => libgit2}/offmap.h (100%) rename src/{ => libgit2}/oid.c (96%) rename src/{ => libgit2}/oid.h (70%) rename src/{ => libgit2}/oidarray.c (100%) rename src/{ => libgit2}/oidarray.h (100%) rename src/{ => libgit2}/oidmap.c (98%) rename src/{ => libgit2}/oidmap.h (100%) rename src/{ => libgit2}/pack-objects.c (100%) rename src/{ => libgit2}/pack-objects.h (100%) rename src/{ => libgit2}/pack.c (97%) rename src/{ => libgit2}/pack.h (98%) rename src/{ => libgit2}/parse.c (100%) rename src/{ => libgit2}/parse.h (100%) rename src/{ => libgit2}/patch.c (100%) rename src/{ => libgit2}/patch.h (100%) rename src/{ => libgit2}/patch_generate.c (100%) rename src/{ => libgit2}/patch_generate.h (100%) rename src/{ => libgit2}/patch_parse.c (100%) rename src/{ => libgit2}/patch_parse.h (100%) rename src/{ => libgit2}/path.c (99%) rename src/{ => libgit2}/path.h (100%) rename src/{ => libgit2}/pathspec.c (100%) rename src/{ => libgit2}/pathspec.h (100%) rename src/{ => libgit2}/proxy.c (100%) rename src/{ => libgit2}/proxy.h (100%) rename src/{ => libgit2}/push.c (100%) rename src/{ => libgit2}/push.h (100%) rename src/{ => libgit2}/reader.c (100%) rename src/{ => libgit2}/reader.h (100%) rename src/{ => libgit2}/rebase.c (99%) rename src/{ => libgit2}/refdb.c (100%) rename src/{ => libgit2}/refdb.h (100%) rename src/{ => libgit2}/refdb_fs.c (99%) rename src/{ => libgit2}/reflog.c (100%) rename src/{ => libgit2}/reflog.h (100%) rename src/{ => libgit2}/refs.c (100%) rename src/{ => libgit2}/refs.h (100%) rename src/{ => libgit2}/refspec.c (100%) rename src/{ => libgit2}/refspec.h (100%) rename src/{ => libgit2}/remote.c (99%) rename src/{ => libgit2}/remote.h (95%) rename src/{ => libgit2}/repo_template.h (100%) rename src/{ => libgit2}/repository.c (97%) rename src/{ => libgit2}/repository.h (100%) rename src/{ => libgit2}/reset.c (100%) rename src/{ => libgit2}/revert.c (100%) rename src/{ => libgit2}/revparse.c (100%) rename src/{ => libgit2}/revwalk.c (100%) rename src/{ => libgit2}/revwalk.h (100%) rename src/{ => libgit2}/settings.h (100%) rename src/{ => libgit2}/signature.c (100%) rename src/{ => libgit2}/signature.h (100%) rename src/{ => libgit2}/stash.c (100%) rename src/{ => libgit2}/status.c (100%) rename src/{ => libgit2}/status.h (100%) rename src/{ => libgit2}/strarray.c (100%) rename src/{ => libgit2}/stream.h (100%) rename src/{ => libgit2}/streams/mbedtls.c (100%) rename src/{ => libgit2}/streams/mbedtls.h (100%) rename src/{ => libgit2}/streams/openssl.c (100%) rename src/{ => libgit2}/streams/openssl.h (100%) rename src/{ => libgit2}/streams/openssl_dynamic.c (100%) rename src/{ => libgit2}/streams/openssl_dynamic.h (100%) rename src/{ => libgit2}/streams/openssl_legacy.c (100%) rename src/{ => libgit2}/streams/openssl_legacy.h (100%) rename src/{ => libgit2}/streams/registry.c (100%) rename src/{ => libgit2}/streams/registry.h (100%) rename src/{ => libgit2}/streams/socket.c (100%) rename src/{ => libgit2}/streams/socket.h (100%) rename src/{ => libgit2}/streams/stransport.c (100%) rename src/{ => libgit2}/streams/stransport.h (100%) rename src/{ => libgit2}/streams/tls.c (100%) rename src/{ => libgit2}/streams/tls.h (100%) rename src/{ => libgit2}/submodule.c (100%) rename src/{ => libgit2}/submodule.h (100%) rename src/{ => libgit2}/sysdir.c (100%) rename src/{ => libgit2}/sysdir.h (100%) rename src/{ => libgit2}/tag.c (98%) rename src/{ => libgit2}/tag.h (100%) rename src/{ => libgit2}/threadstate.c (100%) rename src/{ => libgit2}/threadstate.h (100%) rename src/{ => libgit2}/trace.c (100%) rename src/{ => libgit2}/trace.h (100%) rename src/{ => libgit2}/trailer.c (100%) rename src/{ => libgit2}/transaction.c (100%) rename src/{ => libgit2}/transaction.h (100%) rename src/{ => libgit2}/transport.c (100%) rename src/{ => libgit2}/transports/auth.c (100%) rename src/{ => libgit2}/transports/auth.h (100%) rename src/{ => libgit2}/transports/auth_negotiate.c (100%) rename src/{ => libgit2}/transports/auth_negotiate.h (100%) rename src/{ => libgit2}/transports/auth_ntlm.c (100%) rename src/{ => libgit2}/transports/auth_ntlm.h (100%) rename src/{ => libgit2}/transports/credential.c (100%) rename src/{ => libgit2}/transports/credential_helpers.c (100%) rename src/{ => libgit2}/transports/git.c (100%) rename src/{ => libgit2}/transports/http.c (100%) rename src/{ => libgit2}/transports/http.h (100%) rename src/{ => libgit2}/transports/httpclient.c (99%) rename src/{ => libgit2}/transports/httpclient.h (100%) rename src/{ => libgit2}/transports/local.c (100%) rename src/{ => libgit2}/transports/smart.c (97%) rename src/{ => libgit2}/transports/smart.h (100%) rename src/{ => libgit2}/transports/smart_pkt.c (100%) rename src/{ => libgit2}/transports/smart_protocol.c (100%) rename src/{ => libgit2}/transports/ssh.c (100%) rename src/{ => libgit2}/transports/ssh.h (100%) rename src/{ => libgit2}/transports/winhttp.c (100%) rename src/{ => libgit2}/tree-cache.c (99%) rename src/{ => libgit2}/tree-cache.h (100%) rename src/{ => libgit2}/tree.c (97%) rename src/{ => libgit2}/tree.h (98%) rename src/{ => libgit2}/userdiff.h (100%) rename src/{ => libgit2}/worktree.c (100%) rename src/{ => libgit2}/worktree.h (100%) rename src/{ => libgit2}/xdiff/git-xdiff.h (100%) rename src/{ => libgit2}/xdiff/xdiff.h (100%) rename src/{ => libgit2}/xdiff/xdiffi.c (100%) rename src/{ => libgit2}/xdiff/xdiffi.h (100%) rename src/{ => libgit2}/xdiff/xemit.c (100%) rename src/{ => libgit2}/xdiff/xemit.h (100%) rename src/{ => libgit2}/xdiff/xhistogram.c (100%) rename src/{ => libgit2}/xdiff/xinclude.h (100%) rename src/{ => libgit2}/xdiff/xmacros.h (100%) rename src/{ => libgit2}/xdiff/xmerge.c (100%) rename src/{ => libgit2}/xdiff/xpatience.c (100%) rename src/{ => libgit2}/xdiff/xprepare.c (100%) rename src/{ => libgit2}/xdiff/xprepare.h (100%) rename src/{ => libgit2}/xdiff/xtypes.h (100%) rename src/{ => libgit2}/xdiff/xutils.c (100%) rename src/{ => libgit2}/xdiff/xutils.h (100%) create mode 100644 src/util/CMakeLists.txt rename src/{ => util}/alloc.c (100%) rename src/{ => util}/alloc.h (100%) rename src/{ => util}/allocators/failalloc.c (100%) rename src/{ => util}/allocators/failalloc.h (97%) rename src/{ => util}/allocators/stdalloc.c (100%) rename src/{ => util}/allocators/stdalloc.h (94%) rename src/{ => util}/allocators/win32_leakcheck.c (100%) rename src/{ => util}/allocators/win32_leakcheck.h (94%) rename src/{ => util}/array.h (99%) rename src/{ => util}/assert_safe.h (79%) rename src/{ => util}/bitvec.h (100%) rename src/{ => util}/cc-compat.h (100%) rename src/{ => util}/date.c (98%) rename src/{ => util}/date.h (100%) rename src/{ => util}/filebuf.c (99%) rename src/{ => util}/filebuf.h (99%) rename src/{ => util}/fs_path.c (94%) rename src/{ => util}/fs_path.h (95%) rename src/{ => util}/futils.c (96%) rename src/{ => util}/futils.h (99%) rename src/{common.h => util/git2_util.h} (71%) rename src/{ => util}/hash.c (82%) rename src/{ => util}/hash.h (89%) create mode 100644 src/util/hash/builtin.c rename src/{hash/sha1/openssl.h => util/hash/builtin.h} (58%) rename src/{hash/sha1 => util/hash}/collisiondetect.c (92%) rename src/{hash/sha1 => util/hash}/collisiondetect.h (71%) rename src/{hash/sha1 => util/hash}/common_crypto.c (54%) rename src/{hash/sha1 => util/hash}/common_crypto.h (57%) rename src/{hash/sha1 => util/hash}/mbedtls.c (53%) rename src/{hash/sha1 => util/hash}/mbedtls.h (54%) create mode 100644 src/util/hash/openssl.c create mode 100644 src/util/hash/openssl.h create mode 100644 src/util/hash/rfc6234/sha.h create mode 100644 src/util/hash/rfc6234/sha224-256.c create mode 100644 src/util/hash/sha.h rename src/{hash/sha1 => util/hash}/sha1dc/sha1.c (100%) rename src/{hash/sha1 => util/hash}/sha1dc/sha1.h (100%) rename src/{hash/sha1 => util/hash}/sha1dc/ubc_check.c (100%) rename src/{hash/sha1 => util/hash}/sha1dc/ubc_check.h (100%) create mode 100644 src/util/hash/win32.c create mode 100644 src/util/hash/win32.h rename src/{ => util}/integer.h (100%) rename src/{ => util}/khash.h (100%) rename src/{ => util}/map.h (98%) rename src/{ => util}/net.c (99%) rename src/{ => util}/net.h (99%) rename src/{ => util}/pool.c (100%) rename src/{ => util}/pool.h (99%) rename src/{ => util}/posix.c (100%) rename src/{ => util}/posix.h (99%) rename src/{ => util}/pqueue.c (100%) rename src/{ => util}/pqueue.h (98%) rename src/{ => util}/rand.c (98%) rename src/{ => util}/rand.h (97%) rename src/{ => util}/regexp.c (100%) rename src/{ => util}/regexp.h (99%) rename src/{ => util}/runtime.c (99%) rename src/{ => util}/runtime.h (98%) rename src/{ => util}/sortedcache.c (100%) rename src/{ => util}/sortedcache.h (99%) rename src/{ => util}/str.c (100%) rename src/{ => util}/str.h (99%) rename src/{ => util}/strmap.c (100%) rename src/{ => util}/strmap.h (99%) rename src/{ => util}/strnlen.h (100%) rename src/{ => util}/thread.c (99%) rename src/{ => util}/thread.h (100%) rename src/{ => util}/tsort.c (99%) rename src/{ => util}/unix/map.c (98%) rename src/{ => util}/unix/posix.h (99%) rename src/{ => util}/unix/pthread.h (100%) rename src/{ => util}/unix/realpath.c (96%) rename src/{ => util}/utf8.c (99%) rename src/{ => util}/utf8.h (98%) rename src/{ => util}/util.c (99%) rename src/{ => util}/util.h (99%) rename src/{ => util}/varint.c (100%) rename src/{ => util}/varint.h (94%) rename src/{ => util}/vector.c (100%) rename src/{ => util}/vector.h (99%) rename src/{ => util}/wildmatch.c (100%) rename src/{ => util}/wildmatch.h (95%) rename src/{ => util}/win32/dir.c (100%) rename src/{ => util}/win32/dir.h (97%) rename src/{ => util}/win32/error.c (100%) rename src/{ => util}/win32/error.h (93%) rename src/{ => util}/win32/findfile.c (100%) rename src/{ => util}/win32/findfile.h (96%) rename src/{ => util}/win32/map.c (99%) rename src/{ => util}/win32/mingw-compat.h (100%) rename src/{ => util}/win32/msvc-compat.h (100%) rename src/{ => util}/win32/path_w32.c (100%) rename src/{ => util}/win32/path_w32.h (99%) rename src/{ => util}/win32/posix.h (99%) rename src/{ => util}/win32/posix_w32.c (99%) rename {tests => src/util/win32}/precompiled.c (100%) rename src/{ => util}/win32/precompiled.h (93%) rename src/{ => util}/win32/reparse.h (100%) rename src/{ => util}/win32/thread.c (100%) rename src/{ => util}/win32/thread.h (98%) rename src/{ => util}/win32/utf-conv.c (100%) rename src/{ => util}/win32/utf-conv.h (98%) rename src/{ => util}/win32/version.h (100%) rename src/{ => util}/win32/w32_buffer.c (100%) rename src/{ => util}/win32/w32_buffer.h (95%) rename src/{ => util}/win32/w32_common.h (100%) rename src/{ => util}/win32/w32_leakcheck.c (100%) rename src/{ => util}/win32/w32_leakcheck.h (99%) rename src/{ => util}/win32/w32_util.c (100%) rename src/{ => util}/win32/w32_util.h (99%) rename src/{ => util}/win32/win32-compat.h (100%) rename src/{ => util}/zstream.c (100%) rename src/{ => util}/zstream.h (98%) create mode 100644 tests/benchmarks/README.md create mode 100755 tests/benchmarks/benchmark.sh create mode 100644 tests/benchmarks/benchmark_helpers.sh create mode 100755 tests/benchmarks/hash-object__text_100kb create mode 100755 tests/benchmarks/hash-object__text_10mb create mode 100755 tests/benchmarks/hash-object__text_1kb create mode 100755 tests/benchmarks/hash-object__text_cached_100kb create mode 100755 tests/benchmarks/hash-object__text_cached_10mb create mode 100755 tests/benchmarks/hash-object__text_cached_1kb create mode 100755 tests/benchmarks/hash-object__write_text_100kb create mode 100755 tests/benchmarks/hash-object__write_text_10mb create mode 100755 tests/benchmarks/hash-object__write_text_1kb create mode 100755 tests/benchmarks/hash-object__write_text_cached_100kb create mode 100755 tests/benchmarks/hash-object__write_text_cached_10mb create mode 100755 tests/benchmarks/hash-object__write_text_cached_1kb rename tests/{ => clar}/clar.c (100%) rename tests/{ => clar}/clar.h (100%) rename tests/clar/{ => clar}/fixtures.h (100%) rename tests/clar/{ => clar}/fs.h (100%) rename tests/clar/{ => clar}/print.h (100%) rename tests/clar/{ => clar}/sandbox.h (100%) rename tests/clar/{ => clar}/summary.h (100%) rename tests/{ => clar}/clar_libgit2.c (100%) rename tests/{ => clar}/clar_libgit2.h (100%) rename tests/{ => clar}/clar_libgit2_timer.c (100%) rename tests/{ => clar}/clar_libgit2_timer.h (100%) rename tests/{ => clar}/clar_libgit2_trace.c (100%) rename tests/{ => clar}/clar_libgit2_trace.h (100%) rename tests/{ => clar}/generate.py (100%) rename tests/{ => clar}/main.c (100%) create mode 100644 tests/headertest/CMakeLists.txt rename tests/{ => headertest}/headertest.c (100%) create mode 100644 tests/libgit2/CMakeLists.txt rename tests/{ => libgit2}/apply/apply_helpers.c (100%) rename tests/{ => libgit2}/apply/apply_helpers.h (100%) rename tests/{ => libgit2}/apply/both.c (100%) rename tests/{ => libgit2}/apply/callbacks.c (100%) rename tests/{ => libgit2}/apply/check.c (100%) rename tests/{ => libgit2}/apply/fromdiff.c (100%) rename tests/{ => libgit2}/apply/fromfile.c (100%) rename tests/{ => libgit2}/apply/index.c (100%) rename tests/{ => libgit2}/apply/partial.c (100%) rename tests/{ => libgit2}/apply/tree.c (100%) rename tests/{ => libgit2}/apply/workdir.c (100%) rename tests/{ => libgit2}/attr/attr_expect.h (100%) rename tests/{ => libgit2}/attr/file.c (100%) rename tests/{ => libgit2}/attr/flags.c (100%) rename tests/{ => libgit2}/attr/lookup.c (100%) rename tests/{ => libgit2}/attr/macro.c (100%) rename tests/{ => libgit2}/attr/repo.c (100%) rename tests/{ => libgit2}/blame/blame_helpers.c (100%) rename tests/{ => libgit2}/blame/blame_helpers.h (100%) rename tests/{ => libgit2}/blame/buffer.c (100%) rename tests/{ => libgit2}/blame/getters.c (100%) rename tests/{ => libgit2}/blame/harder.c (100%) rename tests/{ => libgit2}/blame/simple.c (100%) rename tests/{ => libgit2}/checkout/binaryunicode.c (100%) rename tests/{ => libgit2}/checkout/checkout_helpers.c (100%) rename tests/{ => libgit2}/checkout/checkout_helpers.h (100%) rename tests/{ => libgit2}/checkout/conflict.c (100%) rename tests/{ => libgit2}/checkout/crlf.c (100%) rename tests/{ => libgit2}/checkout/head.c (100%) rename tests/{ => libgit2}/checkout/icase.c (100%) rename tests/{ => libgit2}/checkout/index.c (100%) rename tests/{ => libgit2}/checkout/nasty.c (100%) rename tests/{ => libgit2}/checkout/tree.c (100%) rename tests/{ => libgit2}/checkout/typechange.c (100%) rename tests/{ => libgit2}/cherrypick/bare.c (100%) rename tests/{ => libgit2}/cherrypick/workdir.c (100%) rename tests/{ => libgit2}/clone/empty.c (100%) rename tests/{ => libgit2}/clone/local.c (100%) rename tests/{ => libgit2}/clone/nonetwork.c (100%) rename tests/{ => libgit2}/clone/transport.c (100%) rename tests/{ => libgit2}/commit/commit.c (100%) rename tests/{ => libgit2}/commit/parent.c (100%) rename tests/{ => libgit2}/commit/parse.c (100%) rename tests/{ => libgit2}/commit/signature.c (100%) rename tests/{ => libgit2}/commit/write.c (100%) rename tests/{ => libgit2}/config/add.c (100%) rename tests/{ => libgit2}/config/backend.c (100%) rename tests/{ => libgit2}/config/conditionals.c (100%) rename tests/{ => libgit2}/config/config_helpers.c (100%) rename tests/{ => libgit2}/config/config_helpers.h (100%) rename tests/{ => libgit2}/config/configlevel.c (100%) rename tests/{ => libgit2}/config/global.c (100%) rename tests/{ => libgit2}/config/include.c (100%) rename tests/{ => libgit2}/config/memory.c (100%) rename tests/{ => libgit2}/config/multivar.c (100%) rename tests/{ => libgit2}/config/new.c (100%) rename tests/{ => libgit2}/config/read.c (100%) rename tests/{ => libgit2}/config/readonly.c (100%) rename tests/{ => libgit2}/config/rename.c (100%) rename tests/{ => libgit2}/config/snapshot.c (100%) rename tests/{ => libgit2}/config/stress.c (100%) rename tests/{ => libgit2}/config/validkeyname.c (100%) rename tests/{ => libgit2}/config/write.c (100%) rename tests/{ => libgit2}/core/buf.c (100%) rename tests/{ => libgit2}/core/env.c (100%) rename tests/{ => libgit2}/core/features.c (100%) create mode 100644 tests/libgit2/core/hashsig.c rename tests/{ => libgit2}/core/oid.c (100%) rename tests/{ => libgit2}/core/oidmap.c (100%) rename tests/{ => libgit2}/core/opts.c (100%) create mode 100644 tests/libgit2/core/pool.c rename tests/{ => libgit2}/core/structinit.c (100%) rename tests/{ => libgit2}/core/useragent.c (100%) rename tests/{ => libgit2}/date/date.c (100%) rename tests/{ => libgit2}/date/rfc2822.c (100%) rename tests/{ => libgit2}/delta/apply.c (100%) rename tests/{ => libgit2}/describe/describe.c (100%) rename tests/{ => libgit2}/describe/describe_helpers.c (100%) rename tests/{ => libgit2}/describe/describe_helpers.h (100%) rename tests/{ => libgit2}/describe/t6120.c (100%) rename tests/{ => libgit2}/diff/binary.c (100%) rename tests/{ => libgit2}/diff/blob.c (100%) rename tests/{ => libgit2}/diff/diff_helpers.c (100%) rename tests/{ => libgit2}/diff/diff_helpers.h (100%) rename tests/{ => libgit2}/diff/diffiter.c (100%) rename tests/{ => libgit2}/diff/drivers.c (100%) rename tests/{ => libgit2}/diff/externalmodifications.c (100%) rename tests/{ => libgit2}/diff/format_email.c (100%) rename tests/{ => libgit2}/diff/index.c (100%) rename tests/{ => libgit2}/diff/notify.c (100%) rename tests/{ => libgit2}/diff/parse.c (100%) rename tests/{ => libgit2}/diff/patch.c (100%) rename tests/{ => libgit2}/diff/patchid.c (100%) rename tests/{ => libgit2}/diff/pathspec.c (100%) rename tests/{ => libgit2}/diff/racediffiter.c (100%) rename tests/{ => libgit2}/diff/rename.c (100%) rename tests/{ => libgit2}/diff/stats.c (100%) rename tests/{ => libgit2}/diff/submodules.c (100%) rename tests/{ => libgit2}/diff/tree.c (100%) create mode 100644 tests/libgit2/diff/userdiff.c rename tests/{ => libgit2}/diff/workdir.c (99%) rename tests/{ => libgit2}/email/create.c (100%) rename tests/{ => libgit2}/email/create.c.bak (100%) rename tests/{ => libgit2}/fetch/local.c (100%) rename tests/{ => libgit2}/fetchhead/fetchhead_data.h (100%) rename tests/{ => libgit2}/fetchhead/nonetwork.c (100%) rename tests/{ => libgit2}/filter/bare.c (100%) rename tests/{ => libgit2}/filter/blob.c (100%) rename tests/{ => libgit2}/filter/crlf.c (100%) rename tests/{ => libgit2}/filter/crlf.h (100%) rename tests/{ => libgit2}/filter/custom.c (100%) rename tests/{ => libgit2}/filter/custom_helpers.c (100%) rename tests/{ => libgit2}/filter/custom_helpers.h (100%) rename tests/{ => libgit2}/filter/file.c (100%) rename tests/{ => libgit2}/filter/ident.c (100%) rename tests/{ => libgit2}/filter/query.c (100%) rename tests/{ => libgit2}/filter/stream.c (100%) rename tests/{ => libgit2}/filter/systemattrs.c (100%) rename tests/{ => libgit2}/filter/wildcard.c (100%) rename tests/{ => libgit2}/graph/ahead_behind.c (100%) rename tests/{ => libgit2}/graph/commitgraph.c (100%) rename tests/{ => libgit2}/graph/descendant_of.c (100%) rename tests/{ => libgit2}/graph/reachable_from_any.c (100%) rename tests/{ => libgit2}/ignore/path.c (100%) rename tests/{ => libgit2}/ignore/status.c (100%) rename tests/{ => libgit2}/index/add.c (100%) rename tests/{ => libgit2}/index/addall.c (100%) rename tests/{ => libgit2}/index/bypath.c (100%) rename tests/{ => libgit2}/index/cache.c (100%) rename tests/{ => libgit2}/index/collision.c (100%) rename tests/{ => libgit2}/index/conflicts.c (100%) rename tests/{ => libgit2}/index/conflicts.h (100%) rename tests/{ => libgit2}/index/crlf.c (100%) rename tests/{ => libgit2}/index/filemodes.c (100%) rename tests/{ => libgit2}/index/inmemory.c (100%) rename tests/{ => libgit2}/index/names.c (100%) rename tests/{ => libgit2}/index/nsec.c (100%) rename tests/{ => libgit2}/index/racy.c (100%) rename tests/{ => libgit2}/index/read_index.c (100%) rename tests/{ => libgit2}/index/read_tree.c (100%) rename tests/{ => libgit2}/index/rename.c (100%) rename tests/{ => libgit2}/index/reuc.c (100%) rename tests/{ => libgit2}/index/splitindex.c (100%) rename tests/{ => libgit2}/index/stage.c (100%) rename tests/{ => libgit2}/index/tests.c (100%) rename tests/{ => libgit2}/index/version.c (100%) rename tests/{ => libgit2}/iterator/index.c (100%) rename tests/{ => libgit2}/iterator/iterator_helpers.c (100%) rename tests/{ => libgit2}/iterator/iterator_helpers.h (100%) rename tests/{ => libgit2}/iterator/tree.c (99%) rename tests/{ => libgit2}/iterator/workdir.c (100%) rename tests/{ => libgit2}/mailmap/basic.c (100%) rename tests/{ => libgit2}/mailmap/blame.c (100%) rename tests/{ => libgit2}/mailmap/mailmap_testdata.h (100%) rename tests/{ => libgit2}/mailmap/parsing.c (100%) rename tests/{ => libgit2}/merge/analysis.c (100%) rename tests/{ => libgit2}/merge/annotated_commit.c (100%) rename tests/{ => libgit2}/merge/conflict_data.h (100%) rename tests/{ => libgit2}/merge/driver.c (100%) rename tests/{ => libgit2}/merge/files.c (100%) rename tests/{ => libgit2}/merge/merge_helpers.c (100%) rename tests/{ => libgit2}/merge/merge_helpers.h (100%) rename tests/{ => libgit2}/merge/trees/automerge.c (100%) rename tests/{ => libgit2}/merge/trees/commits.c (100%) rename tests/{ => libgit2}/merge/trees/modeconflict.c (100%) rename tests/{ => libgit2}/merge/trees/recursive.c (100%) rename tests/{ => libgit2}/merge/trees/renames.c (100%) rename tests/{ => libgit2}/merge/trees/treediff.c (100%) rename tests/{ => libgit2}/merge/trees/trivial.c (100%) rename tests/{ => libgit2}/merge/trees/whitespace.c (100%) rename tests/{ => libgit2}/merge/workdir/dirty.c (100%) rename tests/{ => libgit2}/merge/workdir/recursive.c (100%) rename tests/{ => libgit2}/merge/workdir/renames.c (100%) rename tests/{ => libgit2}/merge/workdir/setup.c (100%) rename tests/{ => libgit2}/merge/workdir/simple.c (100%) rename tests/{ => libgit2}/merge/workdir/submodules.c (100%) rename tests/{ => libgit2}/merge/workdir/trivial.c (100%) rename tests/{ => libgit2}/message/trailer.c (100%) rename tests/{ => libgit2}/network/cred.c (100%) rename tests/{ => libgit2}/network/fetchlocal.c (100%) rename tests/{ => libgit2}/network/matchhost.c (100%) rename tests/{ => libgit2}/network/refspecs.c (100%) rename tests/{ => libgit2}/network/remote/defaultbranch.c (100%) rename tests/{ => libgit2}/network/remote/delete.c (100%) rename tests/{ => libgit2}/network/remote/isvalidname.c (100%) rename tests/{ => libgit2}/network/remote/local.c (100%) rename tests/{ => libgit2}/network/remote/push.c (100%) rename tests/{ => libgit2}/network/remote/remotes.c (100%) rename tests/{ => libgit2}/network/remote/rename.c (100%) rename tests/{ => libgit2}/network/url/joinpath.c (100%) rename tests/{ => libgit2}/network/url/parse.c (100%) rename tests/{ => libgit2}/network/url/pattern.c (100%) rename tests/{ => libgit2}/network/url/redirect.c (100%) rename tests/{ => libgit2}/network/url/scp.c (100%) rename tests/{ => libgit2}/network/url/valid.c (100%) rename tests/{ => libgit2}/notes/notes.c (100%) rename tests/{ => libgit2}/notes/notesref.c (100%) rename tests/{ => libgit2}/object/blob/filter.c (100%) rename tests/{ => libgit2}/object/blob/fromstream.c (100%) rename tests/{ => libgit2}/object/blob/write.c (100%) rename tests/{ => libgit2}/object/cache.c (100%) rename tests/{ => libgit2}/object/commit/commitstagedfile.c (100%) rename tests/{ => libgit2}/object/commit/parse.c (100%) rename tests/{ => libgit2}/object/lookup.c (100%) rename tests/{ => libgit2}/object/lookupbypath.c (100%) rename tests/{ => libgit2}/object/message.c (100%) rename tests/{ => libgit2}/object/peel.c (100%) rename tests/{ => libgit2}/object/raw/chars.c (100%) rename tests/{ => libgit2}/object/raw/compare.c (100%) rename tests/{ => libgit2}/object/raw/convert.c (100%) rename tests/{ => libgit2}/object/raw/data.h (100%) rename tests/{ => libgit2}/object/raw/fromstr.c (100%) rename tests/{ => libgit2}/object/raw/hash.c (90%) rename tests/{ => libgit2}/object/raw/short.c (95%) rename tests/{ => libgit2}/object/raw/size.c (100%) rename tests/{ => libgit2}/object/raw/type2string.c (100%) rename tests/{ => libgit2}/object/raw/write.c (100%) rename tests/{ => libgit2}/object/shortid.c (100%) rename tests/{ => libgit2}/object/tag/list.c (100%) rename tests/{ => libgit2}/object/tag/parse.c (100%) rename tests/{ => libgit2}/object/tag/peel.c (100%) rename tests/{ => libgit2}/object/tag/read.c (100%) rename tests/{ => libgit2}/object/tag/write.c (93%) rename tests/{ => libgit2}/object/tree/attributes.c (100%) rename tests/{ => libgit2}/object/tree/duplicateentries.c (100%) rename tests/{ => libgit2}/object/tree/frompath.c (100%) rename tests/{ => libgit2}/object/tree/parse.c (99%) rename tests/{ => libgit2}/object/tree/read.c (100%) rename tests/{ => libgit2}/object/tree/update.c (100%) rename tests/{ => libgit2}/object/tree/walk.c (100%) rename tests/{ => libgit2}/object/tree/write.c (100%) rename tests/{ => libgit2}/object/validate.c (100%) rename tests/{ => libgit2}/odb/alternates.c (100%) rename tests/{ => libgit2}/odb/backend/backend_helpers.c (100%) rename tests/{ => libgit2}/odb/backend/backend_helpers.h (100%) rename tests/{ => libgit2}/odb/backend/mempack.c (100%) rename tests/{ => libgit2}/odb/backend/multiple.c (100%) rename tests/{ => libgit2}/odb/backend/nobackend.c (100%) rename tests/{ => libgit2}/odb/backend/nonrefreshing.c (100%) rename tests/{ => libgit2}/odb/backend/refreshing.c (100%) rename tests/{ => libgit2}/odb/backend/simple.c (100%) rename tests/{ => libgit2}/odb/emptyobjects.c (100%) rename tests/{ => libgit2}/odb/foreach.c (100%) rename tests/{ => libgit2}/odb/freshen.c (100%) rename tests/{ => libgit2}/odb/largefiles.c (100%) rename tests/{ => libgit2}/odb/loose.c (100%) rename tests/{ => libgit2}/odb/loose_data.h (100%) rename tests/{ => libgit2}/odb/mixed.c (100%) rename tests/{ => libgit2}/odb/pack_data.h (100%) rename tests/{ => libgit2}/odb/pack_data_one.h (100%) rename tests/{ => libgit2}/odb/packed.c (100%) rename tests/{ => libgit2}/odb/packed_one.c (100%) rename tests/{ => libgit2}/odb/sorting.c (100%) rename tests/{ => libgit2}/odb/streamwrite.c (100%) rename tests/{ => libgit2}/online/badssl.c (100%) rename tests/{ => libgit2}/online/clone.c (100%) rename tests/{ => libgit2}/online/customcert.c (100%) rename tests/{ => libgit2}/online/fetch.c (100%) rename tests/{ => libgit2}/online/fetchhead.c (100%) rename tests/{ => libgit2}/online/push.c (100%) rename tests/{ => libgit2}/online/push_util.c (100%) rename tests/{ => libgit2}/online/push_util.h (100%) rename tests/{ => libgit2}/online/remotes.c (100%) rename tests/{ => libgit2}/pack/filelimit.c (100%) rename tests/{ => libgit2}/pack/indexer.c (98%) rename tests/{ => libgit2}/pack/midx.c (100%) rename tests/{ => libgit2}/pack/packbuilder.c (99%) rename tests/{ => libgit2}/pack/sharing.c (100%) rename tests/{ => libgit2}/pack/threadsafety.c (100%) rename tests/{ => libgit2}/patch/parse.c (100%) rename tests/{ => libgit2}/patch/patch_common.h (100%) rename tests/{ => libgit2}/patch/print.c (100%) create mode 100644 tests/libgit2/path/validate.c rename tests/{ => libgit2}/perf/helper__perf__do_merge.c (100%) rename tests/{ => libgit2}/perf/helper__perf__do_merge.h (100%) rename tests/{ => libgit2}/perf/helper__perf__timer.c (100%) rename tests/{ => libgit2}/perf/helper__perf__timer.h (100%) rename tests/{ => libgit2}/perf/merge.c (100%) create mode 100644 tests/libgit2/precompiled.c rename tests/{ => libgit2}/precompiled.h (100%) rename tests/{ => libgit2}/rebase/abort.c (100%) rename tests/{ => libgit2}/rebase/inmemory.c (100%) rename tests/{ => libgit2}/rebase/iterator.c (100%) rename tests/{ => libgit2}/rebase/merge.c (100%) rename tests/{ => libgit2}/rebase/setup.c (100%) rename tests/{ => libgit2}/rebase/sign.c (100%) rename tests/{ => libgit2}/rebase/submodule.c (100%) rename tests/{ => libgit2}/refs/basic.c (100%) rename tests/{ => libgit2}/refs/branches/checkedout.c (100%) rename tests/{ => libgit2}/refs/branches/create.c (97%) rename tests/{ => libgit2}/refs/branches/delete.c (100%) rename tests/{ => libgit2}/refs/branches/ishead.c (100%) rename tests/{ => libgit2}/refs/branches/iterator.c (100%) rename tests/{ => libgit2}/refs/branches/lookup.c (100%) rename tests/{ => libgit2}/refs/branches/move.c (100%) rename tests/{ => libgit2}/refs/branches/name.c (100%) rename tests/{ => libgit2}/refs/branches/remote.c (100%) rename tests/{ => libgit2}/refs/branches/upstream.c (100%) rename tests/{ => libgit2}/refs/branches/upstreamname.c (100%) rename tests/{ => libgit2}/refs/crashes.c (100%) rename tests/{ => libgit2}/refs/create.c (100%) rename tests/{ => libgit2}/refs/delete.c (100%) rename tests/{ => libgit2}/refs/dup.c (100%) rename tests/{ => libgit2}/refs/foreachglob.c (100%) rename tests/{ => libgit2}/refs/isvalidname.c (100%) rename tests/{ => libgit2}/refs/iterator.c (100%) rename tests/{ => libgit2}/refs/list.c (100%) rename tests/{ => libgit2}/refs/listall.c (100%) rename tests/{ => libgit2}/refs/lookup.c (100%) rename tests/{ => libgit2}/refs/namespaces.c (100%) rename tests/{ => libgit2}/refs/normalize.c (100%) rename tests/{ => libgit2}/refs/overwrite.c (100%) rename tests/{ => libgit2}/refs/pack.c (100%) rename tests/{ => libgit2}/refs/peel.c (100%) rename tests/{ => libgit2}/refs/races.c (100%) rename tests/{ => libgit2}/refs/read.c (100%) rename tests/{ => libgit2}/refs/ref_helpers.c (100%) rename tests/{ => libgit2}/refs/ref_helpers.h (100%) rename tests/{ => libgit2}/refs/reflog/drop.c (100%) rename tests/{ => libgit2}/refs/reflog/messages.c (100%) rename tests/{ => libgit2}/refs/reflog/reflog.c (100%) rename tests/{ => libgit2}/refs/reflog/reflog_helpers.c (100%) rename tests/{ => libgit2}/refs/reflog/reflog_helpers.h (100%) rename tests/{ => libgit2}/refs/rename.c (100%) rename tests/{ => libgit2}/refs/revparse.c (99%) rename tests/{ => libgit2}/refs/setter.c (100%) rename tests/{ => libgit2}/refs/shorthand.c (100%) rename tests/{ => libgit2}/refs/tags/name.c (100%) rename tests/{ => libgit2}/refs/transactions.c (100%) rename tests/{ => libgit2}/refs/unicode.c (100%) rename tests/{ => libgit2}/refs/update.c (100%) rename tests/{ => libgit2}/remote/create.c (100%) rename tests/{ => libgit2}/remote/fetch.c (98%) rename tests/{ => libgit2}/remote/httpproxy.c (100%) rename tests/{ => libgit2}/remote/insteadof.c (100%) rename tests/{ => libgit2}/remote/list.c (100%) rename tests/{ => libgit2}/repo/config.c (100%) rename tests/{ => libgit2}/repo/discover.c (100%) rename tests/{ => libgit2}/repo/env.c (100%) rename tests/{ => libgit2}/repo/extensions.c (100%) rename tests/{ => libgit2}/repo/getters.c (100%) rename tests/{ => libgit2}/repo/hashfile.c (100%) rename tests/{ => libgit2}/repo/head.c (100%) rename tests/{ => libgit2}/repo/headtree.c (100%) rename tests/{ => libgit2}/repo/init.c (100%) rename tests/{ => libgit2}/repo/message.c (100%) rename tests/{ => libgit2}/repo/new.c (100%) rename tests/{ => libgit2}/repo/open.c (86%) rename tests/{ => libgit2}/repo/pathspec.c (100%) rename tests/{ => libgit2}/repo/repo_helpers.c (100%) rename tests/{ => libgit2}/repo/repo_helpers.h (100%) rename tests/{ => libgit2}/repo/reservedname.c (100%) rename tests/{ => libgit2}/repo/setters.c (100%) rename tests/{ => libgit2}/repo/shallow.c (100%) rename tests/{ => libgit2}/repo/state.c (100%) rename tests/{ => libgit2}/repo/template.c (100%) rename tests/{ => libgit2}/reset/default.c (100%) rename tests/{ => libgit2}/reset/hard.c (100%) rename tests/{ => libgit2}/reset/mixed.c (100%) rename tests/{ => libgit2}/reset/reset_helpers.c (100%) rename tests/{ => libgit2}/reset/reset_helpers.h (100%) rename tests/{ => libgit2}/reset/soft.c (100%) rename tests/{ => libgit2}/revert/bare.c (100%) rename tests/{ => libgit2}/revert/rename.c (100%) rename tests/{ => libgit2}/revert/workdir.c (100%) rename tests/{ => libgit2}/revwalk/basic.c (100%) rename tests/{ => libgit2}/revwalk/hidecb.c (100%) rename tests/{ => libgit2}/revwalk/mergebase.c (100%) rename tests/{ => libgit2}/revwalk/signatureparsing.c (100%) rename tests/{ => libgit2}/revwalk/simplify.c (100%) rename tests/{ => libgit2}/stash/apply.c (100%) rename tests/{ => libgit2}/stash/drop.c (100%) rename tests/{ => libgit2}/stash/foreach.c (100%) rename tests/{ => libgit2}/stash/save.c (100%) rename tests/{ => libgit2}/stash/stash_helpers.c (100%) rename tests/{ => libgit2}/stash/stash_helpers.h (100%) rename tests/{ => libgit2}/stash/submodules.c (100%) rename tests/{ => libgit2}/status/renames.c (100%) rename tests/{ => libgit2}/status/single.c (100%) rename tests/{ => libgit2}/status/status_data.h (100%) rename tests/{ => libgit2}/status/status_helpers.c (100%) rename tests/{ => libgit2}/status/status_helpers.h (100%) rename tests/{ => libgit2}/status/submodules.c (100%) rename tests/{ => libgit2}/status/worktree.c (100%) rename tests/{ => libgit2}/status/worktree_init.c (100%) rename tests/{ => libgit2}/stream/deprecated.c (100%) rename tests/{ => libgit2}/stream/registration.c (100%) rename tests/{ => libgit2}/stress/diff.c (100%) rename tests/{ => libgit2}/submodule/add.c (100%) rename tests/{ => libgit2}/submodule/escape.c (100%) rename tests/{ => libgit2}/submodule/init.c (100%) rename tests/{ => libgit2}/submodule/inject_option.c (100%) rename tests/{ => libgit2}/submodule/lookup.c (100%) rename tests/{ => libgit2}/submodule/modify.c (100%) rename tests/{ => libgit2}/submodule/nosubs.c (100%) rename tests/{ => libgit2}/submodule/open.c (100%) rename tests/{ => libgit2}/submodule/repository_init.c (100%) rename tests/{ => libgit2}/submodule/status.c (100%) rename tests/{ => libgit2}/submodule/submodule_helpers.c (100%) rename tests/{ => libgit2}/submodule/submodule_helpers.h (100%) rename tests/{ => libgit2}/submodule/update.c (100%) rename tests/{ => libgit2}/threads/atomic.c (100%) rename tests/{ => libgit2}/threads/basic.c (100%) rename tests/{ => libgit2}/threads/diff.c (100%) rename tests/{ => libgit2}/threads/iterator.c (100%) rename tests/{ => libgit2}/threads/refdb.c (100%) rename tests/{ => libgit2}/threads/thread_helpers.c (100%) rename tests/{ => libgit2}/threads/thread_helpers.h (100%) rename tests/{ => libgit2}/threads/tlsdata.c (100%) rename tests/{ => libgit2}/trace/trace.c (100%) rename tests/{ => libgit2}/trace/windows/stacktrace.c (100%) create mode 100644 tests/libgit2/transport/register.c rename tests/{ => libgit2}/transports/smart/packet.c (100%) rename tests/{ => libgit2}/valgrind-supp-mac.txt (100%) rename tests/{ => libgit2}/win32/forbidden.c (100%) rename tests/{ => libgit2}/win32/longpath.c (100%) rename tests/{ => libgit2}/win32/systemdir.c (100%) rename tests/{ => libgit2}/worktree/bare.c (100%) rename tests/{ => libgit2}/worktree/config.c (100%) rename tests/{ => libgit2}/worktree/merge.c (100%) rename tests/{ => libgit2}/worktree/open.c (100%) rename tests/{ => libgit2}/worktree/reflog.c (100%) rename tests/{ => libgit2}/worktree/refs.c (100%) rename tests/{ => libgit2}/worktree/repository.c (100%) rename tests/{ => libgit2}/worktree/submodule.c (100%) rename tests/{ => libgit2}/worktree/worktree.c (100%) rename tests/{ => libgit2}/worktree/worktree_helpers.c (100%) rename tests/{ => libgit2}/worktree/worktree_helpers.h (100%) delete mode 100644 tests/path/dotgit.c create mode 100644 tests/resources/sha1/empty delete mode 100644 tests/transport/register.c create mode 100644 tests/util/CMakeLists.txt rename tests/{core => util}/array.c (97%) rename tests/{core => util}/assert.c (79%) rename tests/{core => util}/bitvec.c (97%) rename tests/{core => util}/copy.c (88%) create mode 100644 tests/util/crlf.h rename tests/{core => util}/dirent.c (92%) rename tests/{core => util}/encoding.c (93%) rename tests/{core => util}/errors.c (93%) rename tests/{core => util}/filebuf.c (93%) rename tests/{core => util}/ftruncate.c (80%) rename tests/{core => util}/futils.c (90%) rename tests/{core => util}/gitstr.c (80%) rename tests/{core => util}/hex.c (94%) rename tests/{core => util}/iconv.c (88%) rename tests/{core => util}/init.c (88%) rename tests/{core => util}/integer.c (99%) rename tests/{core => util}/link.c (92%) rename tests/{core => util}/memmem.c (90%) rename tests/{core => util}/mkdir.c (97%) rename tests/{core => util}/path.c (96%) rename tests/{ => util}/path/core.c (73%) rename tests/{ => util}/path/win32.c (100%) rename tests/{core => util}/pool.c (59%) rename tests/{core => util}/posix.c (95%) rename tests/{core => util}/pqueue.c (91%) create mode 100644 tests/util/precompiled.c create mode 100644 tests/util/precompiled.h rename tests/{core => util}/qsort.c (87%) rename tests/{core => util}/regexp.c (75%) rename tests/{core => util}/rmdir.c (91%) rename tests/{core => util}/sha1.c (61%) create mode 100644 tests/util/sha256.c rename tests/{core => util}/sortedcache.c (98%) rename tests/{core => util}/stat.c (95%) rename tests/{ => util}/str/basic.c (100%) rename tests/{ => util}/str/oom.c (90%) rename tests/{ => util}/str/percent.c (100%) rename tests/{ => util}/str/quote.c (100%) rename tests/{ => util}/str/splice.c (100%) rename tests/{core => util}/string.c (94%) rename tests/{core => util}/strmap.c (85%) rename tests/{core => util}/strtol.c (88%) rename tests/{core => util}/utf8.c (95%) rename tests/{core => util}/vector.c (96%) rename tests/{core => util}/wildmatch.c (96%) rename tests/{core => util}/zstream.c (96%) diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..d6e9cfceb --- /dev/null +++ b/.clang-format @@ -0,0 +1,92 @@ +# This file is an example configuration for clang-format 5.0. +# +# Note that this style definition should only be understood as a hint +# for writing new code. The rules are still work-in-progress and does +# not yet exactly match the style we have in the existing code. + +# C Language specifics +Language: Cpp + +# Use tabs whenever we need to fill whitespace that spans at least from one tab +# stop to the next one. +# +# These settings are mirrored in .editorconfig. Keep them in sync. +UseTab: ForIndentation +TabWidth: 8 +IndentWidth: 8 +ContinuationIndentWidth: 8 +ColumnLimit: 80 + +AlignAfterOpenBracket: AlwaysBreak +AlignEscapedNewlines: Left +AlignTrailingComments: false + +# Allow putting parameters onto the next line +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false + +# Don't allow short braced statements to be on a single line +# if (a) not if (a) return; +# return; +AllowShortBlocksOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLoopsOnASingleLine: false +AllowShortLambdasOnASingleLine: None + +# Pack as many parameters or arguments onto the same line as possible +# int myFunction(int aaaaaaaaaaaa, int bbbbbbbb, +# int cccc); +BinPackArguments: true +BinPackParameters: false + +BreakBeforeBraces: Linux +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: false +BreakStringLiterals: false + +# The number of spaces before trailing line comments (// - comments). +# This does not affect trailing block comments (/* - comments). +SpacesBeforeTrailingComments: 1 + +# Don't insert spaces in casts +# x = (int32) y; not x = ( int32 ) y; +SpacesInCStyleCastParentheses: false + +# Don't insert spaces inside container literals +# var arr = [1, 2, 3]; not var arr = [ 1, 2, 3 ]; +SpacesInContainerLiterals: false + +# Don't insert spaces after '(' or before ')' +# f(arg); not f( arg ); +SpacesInParentheses: false + +# Don't insert spaces after '[' or before ']' +# int a[5]; not int a[ 5 ]; +SpacesInSquareBrackets: false + +# Insert a space after '{' and before '}' in struct initializers +Cpp11BracedListStyle: false + +# A list of macros that should be interpreted as foreach loops instead of as +# function calls. +ForEachMacros: + - 'git_array_foreach' + - 'git_vector_foreach' + +# The maximum number of consecutive empty lines to keep. +MaxEmptyLinesToKeep: 1 + +# No empty line at the start of a block. +KeepEmptyLinesAtTheStartOfBlocks: false + +# Penalties +# This decides what order things should be done if a line is too long +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +SortIncludes: false diff --git a/.github/release.yml b/.github/release.yml index c3c8da290..79158f492 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -6,10 +6,13 @@ changelog: - title: Bug fixes labels: - bug + - title: Security fixes + labels: + - security - title: Code cleanups labels: - cleanup - - title: CI improvements + - title: Build and CI improvements labels: - build - title: Documentation improvements diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 000000000..285c273b1 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,82 @@ +# Benchmark libgit2 against the git reference implementation. +name: Benchmark + +on: + workflow_dispatch: + schedule: + - cron: '15 4 * * *' + +jobs: + # Run our nightly builds. We build a matrix with the various build + # targets and their details. Then we build either in a docker container + # (Linux) or on the actual hosts (macOS, Windows). + build: + # Only run scheduled workflows on the main repository; prevents people + # from using build minutes on their forks. + if: github.repository == 'libgit2/libgit2' + + strategy: + matrix: + platform: + - name: "Linux (clang, OpenSSL)" + env: + CC: clang + CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_CLI=ON -DCMAKE_BUILD_TYPE=Release + CMAKE_BUILD_OPTIONS: --config Release + id: linux + os: ubuntu-latest + setup-script: ubuntu + - name: "macOS" + os: macos-10.15 + env: + CC: clang + CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_CLI=ON -DCMAKE_BUILD_TYPE=Release + CMAKE_BUILD_OPTIONS: --config Release + PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig + id: macos + setup-script: osx + - name: "Windows (amd64, Visual Studio)" + os: windows-2019 + env: + ARCH: amd64 + CMAKE_GENERATOR: Visual Studio 16 2019 + CMAKE_OPTIONS: -A x64 -DDEPRECATE_HARD=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_CLI=ON -DCMAKE_BUILD_TYPE=Release + CMAKE_BUILD_OPTIONS: --config Release + id: windows + setup-script: win32 + fail-fast: false + name: "Build ${{ matrix.platform.name }}" + env: ${{ matrix.platform.env }} + runs-on: ${{ matrix.platform.os }} + steps: + - name: Check out repository + uses: actions/checkout@v2 + with: + path: source + fetch-depth: 0 + - name: Set up benchmark environment + run: source/ci/setup-${{ matrix.platform.setup-script }}-benchmark.sh + shell: bash + if: matrix.platform.setup-script != '' + - name: Build + run: | + mkdir build && cd build + ../source/ci/build.sh + shell: bash + - name: Benchmark + run: | + if [[ "$(uname -s)" == MINGW* ]]; then + GIT2_CLI="$(cygpath -w $(pwd))\\build\\Release\\git2_cli" + else + GIT2_CLI="$(pwd)/build/git2_cli" + fi + + mkdir benchmark && cd benchmark + ../source/tests/benchmarks/benchmark.sh --baseline-cli "git" --cli "${GIT2_CLI}" --json benchmarks.json --zip benchmarks.zip + shell: bash + - name: Upload results + uses: actions/upload-artifact@v2 + with: + name: benchmark-${{ matrix.platform.id }} + path: benchmark + if: always() diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 38b4a044a..000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: "CodeQL" - -on: - workflow_dispatch: - schedule: - - cron: '21 3 * * 1' - -env: - docker-registry: docker.pkg.github.com - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - - steps: - - name: Check out repository - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: 'cpp' - - - name: Build - run: | - mkdir build - cd build - cmake .. -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON - cmake --build . - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 74bab53f3..bcad84b8b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,7 @@ on: workflow_dispatch: env: - docker-registry: docker.pkg.github.com + docker-registry: ghcr.io docker-config-path: source/ci/docker jobs: @@ -69,7 +69,7 @@ jobs: if [ "${{ matrix.container.base }}" != "" ]; then BASE_ARG="--build-arg BASE=${{ matrix.container.base }}" fi - docker build -t ${{ env.docker-registry-container-sha }} ${BASE_ARG} -f ${{ env.dockerfile }} . + docker build -t ${{ env.docker-registry-container-sha }} --build-arg UID=$(id -u) --build-arg GID=$(id -g) ${BASE_ARG} -f ${{ env.dockerfile }} . docker tag ${{ env.docker-registry-container-sha }} ${{ env.docker-registry-container-latest }} docker push ${{ env.docker-registry-container-sha }} docker push ${{ env.docker-registry-container-latest }} @@ -85,6 +85,7 @@ jobs: matrix: platform: - name: "Linux (Xenial, GCC, OpenSSL)" + id: xenial-gcc-openssl container: name: xenial env: @@ -93,6 +94,7 @@ jobs: CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON os: ubuntu-latest - name: Linux (Xenial, GCC, mbedTLS) + id: xenial-gcc-mbedtls container: name: xenial env: @@ -101,6 +103,7 @@ jobs: CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON os: ubuntu-latest - name: "Linux (Xenial, Clang, OpenSSL)" + id: xenial-clang-openssl container: name: xenial env: @@ -109,6 +112,7 @@ jobs: CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON os: ubuntu-latest - name: "Linux (Xenial, Clang, mbedTLS)" + id: xenial-clang-mbedtls container: name: xenial env: @@ -117,6 +121,7 @@ jobs: CMAKE_GENERATOR: Ninja os: ubuntu-latest - name: "Linux (MemorySanitizer)" + id: memorysanitizer container: name: focal env: @@ -130,6 +135,7 @@ jobs: UBSAN_OPTIONS: print_stacktrace=1 os: ubuntu-latest - name: "Linux (UndefinedBehaviorSanitizer)" + id: ubsanitizer container: name: focal env: @@ -143,6 +149,7 @@ jobs: UBSAN_OPTIONS: print_stacktrace=1 os: ubuntu-latest - name: "Linux (ThreadSanitizer)" + id: threadsanitizer container: name: focal env: @@ -157,6 +164,7 @@ jobs: TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1 os: ubuntu-latest - name: "macOS" + id: macos os: macos-10.15 env: CC: clang @@ -166,6 +174,7 @@ jobs: SKIP_NEGOTIATE_TESTS: true setup-script: osx - name: "Windows (amd64, Visual Studio)" + id: windows-amd64-vs os: windows-2019 env: ARCH: amd64 @@ -174,6 +183,7 @@ jobs: SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - name: "Windows (x86, Visual Studio)" + id: windows-x86-vs os: windows-2019 env: ARCH: x86 @@ -182,6 +192,7 @@ jobs: SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - name: "Windows (amd64, mingw)" + id: windows-amd64-mingw os: windows-2019 setup-script: mingw env: @@ -193,6 +204,7 @@ jobs: SKIP_SSH_TESTS: true SKIP_NEGOTIATE_TESTS: true - name: "Windows (x86, mingw)" + id: windows-x86-mingw os: windows-2019 setup-script: mingw env: @@ -214,7 +226,7 @@ jobs: path: source fetch-depth: 0 - name: Set up build environment - run: source/ci/setup-${{ matrix.platform.setup-script }}.sh + run: source/ci/setup-${{ matrix.platform.setup-script }}-build.sh shell: bash if: matrix.platform.setup-script != '' - name: Setup QEMU @@ -229,7 +241,11 @@ jobs: working-directory: ${{ env.docker-config-path }} if: matrix.platform.container.name != '' - name: Create container - run: docker build -t ${{ env.docker-registry-container-sha }} -f ${{ env.dockerfile }} . + run: | + if [ "${{ matrix.container.base }}" != "" ]; then + BASE_ARG="--build-arg BASE=${{ matrix.container.base }}" + fi + docker build -t ${{ env.docker-registry-container-sha }} --build-arg UID=$(id -u) --build-arg GID=$(id -g) ${BASE_ARG} -f ${{ env.dockerfile }} . working-directory: ${{ env.docker-config-path }} if: matrix.platform.container.name != '' && env.docker-container-exists != 'true' - name: Build and test @@ -237,10 +253,12 @@ jobs: export GITTEST_NEGOTIATE_PASSWORD="${{ secrets.GITTEST_NEGOTIATE_PASSWORD }}" if [ -n "${{ matrix.platform.container.name }}" ]; then + mkdir build docker run \ --rm \ - --user libgit2:libgit2 \ + --user "$(id -u):$(id -g)" \ -v "$(pwd)/source:/home/libgit2/source" \ + -v "$(pwd)/build:/home/libgit2/build" \ -w /home/libgit2 \ -e ASAN_SYMBOLIZER_PATH \ -e CC \ @@ -248,19 +266,40 @@ jobs: -e CMAKE_GENERATOR \ -e CMAKE_OPTIONS \ -e GITTEST_NEGOTIATE_PASSWORD \ + -e GITTEST_FLAKY_STAT \ -e PKG_CONFIG_PATH \ -e SKIP_NEGOTIATE_TESTS \ -e SKIP_SSH_TESTS \ -e TSAN_OPTIONS \ -e UBSAN_OPTIONS \ ${{ env.docker-registry-container-sha }} \ - /bin/bash -c "mkdir build && cd build && ../source/ci/build.sh && ../source/ci/test.sh" + /bin/bash -c "cd build && ../source/ci/build.sh && ../source/ci/test.sh" else - mkdir build && cd build + mkdir build + cd build ../source/ci/build.sh ../source/ci/test.sh fi shell: bash + - name: Upload test results + uses: actions/upload-artifact@v3 + if: success() || failure() + with: + name: test-results-${{ matrix.platform.id }} + path: build/results_*.xml + + test_results: + name: Test results + needs: [ build ] + runs-on: ubuntu-latest + steps: + - name: Download test results + uses: actions/download-artifact@v3 + - name: Generate test summary + uses: test-summary/action@v1 + with: + paths: 'test-results-*/*.xml' + # Generate documentation using docurium. We'll upload the documentation # as a build artifact so that it can be reviewed as part of a pull @@ -270,6 +309,7 @@ jobs: documentation: name: Generate documentation needs: [ containers ] + if: success() || failure() runs-on: ubuntu-latest steps: - name: Check out repository diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 5513d5b43..856da28a3 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -7,7 +7,7 @@ on: - cron: '15 1 * * *' env: - docker-registry: docker.pkg.github.com + docker-registry: ghcr.io docker-config-path: source/ci/docker jobs: @@ -15,6 +15,10 @@ jobs: # targets and their details. Then we build either in a docker container # (Linux) or on the actual hosts (macOS, Windows). build: + # Only run scheduled workflows on the main repository; prevents people + # from using build minutes on their forks. + if: github.repository == 'libgit2/libgit2' + strategy: matrix: platform: @@ -247,6 +251,7 @@ jobs: CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON RUN_INVASIVE_TESTS: true SKIP_PROXY_TESTS: true + GITTEST_FLAKY_STAT: true os: ubuntu-latest - name: "Linux (arm64, Bionic, GCC, OpenSSL)" container: @@ -271,7 +276,7 @@ jobs: path: source fetch-depth: 0 - name: Set up build environment - run: source/ci/setup-${{ matrix.platform.setup-script }}.sh + run: source/ci/setup-${{ matrix.platform.setup-script }}-build.sh shell: bash if: matrix.platform.setup-script != '' - name: Setup QEMU @@ -305,6 +310,7 @@ jobs: -e CMAKE_GENERATOR \ -e CMAKE_OPTIONS \ -e GITTEST_NEGOTIATE_PASSWORD \ + -e GITTEST_FLAKY_STAT \ -e PKG_CONFIG_PATH \ -e SKIP_NEGOTIATE_TESTS \ -e SKIP_SSH_TESTS \ @@ -319,6 +325,10 @@ jobs: shell: bash coverity: + # Only run scheduled workflows on the main repository; prevents people + # from using build minutes on their forks. + if: github.repository == 'libgit2/libgit2' + name: Coverity runs-on: ubuntu-latest steps: @@ -338,3 +348,32 @@ jobs: run: source/ci/coverity.sh env: COVERITY_TOKEN: ${{ secrets.coverity_token }} + + codeql: + # Only run scheduled workflows on the main repository; prevents people + # from using build minutes on their forks. + if: github.repository == 'libgit2/libgit2' + + name: CodeQL + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: 'cpp' + + - name: Build + run: | + mkdir build + cd build + cmake .. -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON + cmake --build . + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ff6748e0..2b5a2842c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,12 @@ -# CMake build script for the libgit2 project +# libgit2: the cross-platform, linkable library implementation of git. # See `README.md` for build instructions. +# +# This top-level CMakeLists.txt sets up configuration options and +# determines which subprojects to build. cmake_minimum_required(VERSION 3.5.1) -project(libgit2 VERSION "1.4.3" LANGUAGES C) +project(libgit2 VERSION "1.5.0" LANGUAGES C) # Add find modules to the path set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") @@ -15,6 +18,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") # Optional subsystems option(BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON) option(BUILD_TESTS "Build Tests using the Clar suite" ON) +option(BUILD_CLI "Build the command-line interface" ON) option(BUILD_EXAMPLES "Build library usage example apps" OFF) option(BUILD_FUZZERS "Build the fuzz targets" OFF) @@ -25,7 +29,8 @@ option(USE_NSEC "Support nanosecond precision file mtimes and cti # Backend selection option(USE_SSH "Link with libssh2 to enable SSH support" OFF) option(USE_HTTPS "Enable HTTPS support. Can be set to a specific backend" ON) -option(USE_SHA1 "Enable SHA1. Can be set to CollisionDetection(ON)/HTTPS/Generic" ON) +option(USE_SHA1 "Enable SHA1. Can be set to CollisionDetection(ON)/HTTPS" ON) +option(USE_SHA256 "Enable SHA256. Can be set to HTTPS/Builtin" ON) option(USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF) set(USE_HTTP_PARSER "" CACHE STRING "Specifies the HTTP Parser implementation; either system or builtin.") set(REGEX_BACKEND "" CACHE STRING "Regular expression implementation. One of regcomp_l, pcre2, pcre, regcomp, or builtin.") @@ -127,6 +132,14 @@ if(BUILD_FUZZERS) endif() +# Export for people who use us as a dependency + +if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") + set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE) + set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE) +endif() + + # Summary feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:") diff --git a/COPYING b/COPYING index ccfb7dbf8..28226696d 100644 --- a/COPYING +++ b/COPYING @@ -1144,3 +1144,43 @@ worldwide. This software is distributed without any warranty. See . +---------------------------------------------------------------------- + +The built-in SHA256 support (src/hash/rfc6234) is taken from RFC 6234 +under the following license: + +Copyright (c) 2011 IETF Trust and the persons identified as +authors of the code. All rights reserved. + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following +conditions are met: + +- Redistributions of source code must retain the above + copyright notice, this list of conditions and + the following disclaimer. + +- Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +- Neither the name of Internet Society, IETF or IETF Trust, nor + the names of specific contributors, may be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/README.md b/README.md index c73b50701..a9deaa01d 100644 --- a/README.md +++ b/README.md @@ -392,6 +392,8 @@ Here are the bindings to libgit2 that are currently available: * parrot-libgit2 * Perl * Git-Raw +* Pharo Smalltalk + * libgit2-pharo-bindings * PHP * php-git * Python @@ -405,6 +407,8 @@ Here are the bindings to libgit2 that are currently available: * git2-rs * Swift * SwiftGit2 +* Tcl + * lg2 * Vala * libgit2.vapi diff --git a/ci/build.sh b/ci/build.sh index 5a51f925a..21a45af5f 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -59,7 +59,7 @@ echo "########################################################################## echo "## Configuring build environment" echo "##############################################################################" -echo cmake -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G \"${CMAKE_GENERATOR}\" ${CMAKE_OPTIONS} -S \"${SOURCE_DIR}\" +echo "${CMAKE}" -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G \"${CMAKE_GENERATOR}\" ${CMAKE_OPTIONS} -S \"${SOURCE_DIR}\" env PATH="${BUILD_PATH}" "${CMAKE}" -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G "${CMAKE_GENERATOR}" ${CMAKE_OPTIONS} -S "${SOURCE_DIR}" echo "" @@ -69,10 +69,11 @@ echo "########################################################################## # Determine parallelism; newer cmake supports `--build --parallel` but # we cannot yet rely on that. -if [ "${CMAKE_GENERATOR}" = "Unix Makefiles" -a "${CORES}" != "" ]; then +if [ "${CMAKE_GENERATOR}" = "Unix Makefiles" -a "${CORES}" != "" -a "${CMAKE_BUILD_OPTIONS}" = "" ]; then BUILDER=(make -j ${CORES}) else - BUILDER=("${CMAKE}" --build .) + BUILDER=("${CMAKE}" --build . ${CMAKE_BUILD_OPTIONS}) fi +echo "${BUILDER[@]}" env PATH="${BUILD_PATH}" "${BUILDER[@]}" diff --git a/ci/docker/bionic b/ci/docker/bionic index 51af5c01c..f1b69edef 100644 --- a/ci/docker/bionic +++ b/ci/docker/bionic @@ -28,17 +28,22 @@ RUN apt-get update && \ FROM apt AS mbedtls RUN cd /tmp && \ - curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \ - tar -xz && \ - cd mbedtls-2.16.2 && \ + curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \ + tar -xz && \ + cd mbedtls-mbedtls-2.16.2 && \ scripts/config.pl set MBEDTLS_MD4_C 1 && \ CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \ ninja install && \ cd .. && \ - rm -rf mbedtls-2.16.2 + rm -rf mbedtls-mbedtls-2.16.2 FROM mbedtls AS adduser -RUN useradd --shell /bin/bash libgit2 --create-home +ARG UID="" +ARG GID="" +RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \ + if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \ + groupadd ${GROUP_ARG} libgit2 && \ + useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2 FROM adduser AS configure RUN mkdir /var/run/sshd diff --git a/ci/docker/centos7 b/ci/docker/centos7 index 8105f1442..28ed65081 100644 --- a/ci/docker/centos7 +++ b/ci/docker/centos7 @@ -48,7 +48,12 @@ RUN cd /tmp && \ rm -rf cmake-3.21.1 FROM cmake AS adduser -RUN useradd --shell /bin/bash libgit2 --create-home +ARG UID="" +ARG GID="" +RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \ + if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \ + groupadd ${GROUP_ARG} libgit2 && \ + useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2 FROM adduser AS configure ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig diff --git a/ci/docker/centos8 b/ci/docker/centos8 index cca088103..81f0c3c76 100644 --- a/ci/docker/centos8 +++ b/ci/docker/centos8 @@ -1,6 +1,10 @@ ARG BASE=centos:8 -FROM ${BASE} AS yum +FROM ${BASE} AS stream +RUN dnf -y --disablerepo '*' --enablerepo=extras swap centos-linux-repos centos-stream-repos && \ + dnf -y distro-sync + +FROM stream AS yum RUN yum install -y \ which \ bzip2 \ @@ -40,7 +44,12 @@ RUN cd /tmp && \ rm -rf valgrind-3.15.0 FROM valgrind AS adduser -RUN useradd --shell /bin/bash libgit2 --create-home +ARG UID="" +ARG GID="" +RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \ + if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \ + groupadd ${GROUP_ARG} libgit2 && \ + useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2 FROM adduser AS configure ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig diff --git a/ci/docker/focal b/ci/docker/focal index 37d7d6356..b3a402cb0 100644 --- a/ci/docker/focal +++ b/ci/docker/focal @@ -32,9 +32,9 @@ RUN apt-get update && \ FROM apt AS mbedtls RUN cd /tmp && \ - curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \ + curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \ tar -xz && \ - cd mbedtls-2.16.2 && \ + cd mbedtls-mbedtls-2.16.2 && \ scripts/config.pl unset MBEDTLS_AESNI_C && \ scripts/config.pl set MBEDTLS_MD4_C 1 && \ mkdir build build-msan && \ @@ -45,7 +45,7 @@ RUN cd /tmp && \ CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF -DCMAKE_BUILD_TYPE=MemSanDbg -DCMAKE_INSTALL_PREFIX=/usr/local/msan .. && \ ninja install && \ cd .. && \ - rm -rf mbedtls-2.16.2 + rm -rf mbedtls-mbedtls-2.16.2 FROM mbedtls AS libssh2 RUN cd /tmp && \ @@ -73,7 +73,13 @@ RUN cd /tmp && \ rm -rf valgrind-3.15.0 FROM valgrind AS adduser -RUN useradd --shell /bin/bash libgit2 --create-home +ARG UID="" +ARG GID="" +RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \ + if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \ + groupadd ${GROUP_ARG} libgit2 && \ + useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2 + FROM adduser AS configure RUN mkdir /var/run/sshd diff --git a/ci/docker/xenial b/ci/docker/xenial index c19fe421d..f5fa5a315 100644 --- a/ci/docker/xenial +++ b/ci/docker/xenial @@ -30,14 +30,14 @@ RUN apt-get update && \ FROM apt AS mbedtls RUN cd /tmp && \ - curl --location --silent --show-error https://tls.mbed.org/download/mbedtls-2.16.2-apache.tgz | \ - tar -xz && \ - cd mbedtls-2.16.2 && \ + curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \ + tar -xz && \ + cd mbedtls-mbedtls-2.16.2 && \ scripts/config.pl set MBEDTLS_MD4_C 1 && \ CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \ ninja install && \ cd .. && \ - rm -rf mbedtls-2.16.2 + rm -rf mbedtls-mbedtls-2.16.2 FROM mbedtls AS libssh2 RUN cd /tmp && \ @@ -60,7 +60,13 @@ RUN cd /tmp && \ rm -rf valgrind-3.15.0 FROM valgrind AS adduser -RUN useradd --shell /bin/bash libgit2 --create-home +ARG UID="" +ARG GID="" +RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \ + if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \ + groupadd ${GROUP_ARG} libgit2 && \ + useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2 + FROM adduser AS configure RUN mkdir /var/run/sshd diff --git a/ci/getcontainer.sh b/ci/getcontainer.sh index 07ef7b8ea..81d0c1d92 100755 --- a/ci/getcontainer.sh +++ b/ci/getcontainer.sh @@ -37,9 +37,13 @@ DOCKER_REGISTRY_CONTAINER_SHA="${DOCKER_REGISTRY_CONTAINER}:${DOCKER_SHA}" echo "docker-registry-container-sha=${DOCKER_REGISTRY_CONTAINER_SHA}" >> $GITHUB_ENV echo "docker-registry-container-latest=${DOCKER_REGISTRY_CONTAINER}:latest" >> $GITHUB_ENV +echo "::: logging in to ${DOCKER_REGISTRY} as ${GITHUB_ACTOR}" + exists="true" docker login https://${DOCKER_REGISTRY} -u ${GITHUB_ACTOR} -p ${GITHUB_TOKEN} || exists="false" +echo "::: pulling ${DOCKER_REGISTRY_CONTAINER_SHA}" + if [ "${exists}" != "false" ]; then docker pull ${DOCKER_REGISTRY_CONTAINER_SHA} || exists="false" fi diff --git a/ci/setup-mingw.sh b/ci/setup-mingw-build.sh similarity index 97% rename from ci/setup-mingw.sh rename to ci/setup-mingw-build.sh index f5a882d2b..3d72b24eb 100755 --- a/ci/setup-mingw.sh +++ b/ci/setup-mingw-build.sh @@ -1,4 +1,6 @@ -#!/bin/sh -e +#!/bin/sh + +set -ex echo "##############################################################################" echo "## Downloading mingw" diff --git a/ci/setup-osx-benchmark.sh b/ci/setup-osx-benchmark.sh new file mode 100755 index 000000000..80d87682b --- /dev/null +++ b/ci/setup-osx-benchmark.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +set -ex + +brew update +brew install hyperfine diff --git a/ci/setup-osx.sh b/ci/setup-osx-build.sh similarity index 95% rename from ci/setup-osx.sh rename to ci/setup-osx-build.sh index 2e630eedb..0b95e7629 100755 --- a/ci/setup-osx.sh +++ b/ci/setup-osx-build.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -x +set -ex brew update brew install pkgconfig zlib curl openssl libssh2 ninja diff --git a/ci/setup-ubuntu-benchmark.sh b/ci/setup-ubuntu-benchmark.sh new file mode 100755 index 000000000..561a18fd9 --- /dev/null +++ b/ci/setup-ubuntu-benchmark.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +set -ex + +sudo apt-get update +sudo apt-get install -y --no-install-recommends \ + cargo \ + cmake \ + gcc \ + git \ + krb5-user \ + libkrb5-dev \ + libssl-dev \ + libz-dev \ + make \ + ninja-build \ + pkgconf + +wget https://github.com/sharkdp/hyperfine/releases/download/v1.12.0/hyperfine_1.12.0_amd64.deb +sudo dpkg -i hyperfine_1.12.0_amd64.deb diff --git a/ci/setup-win32-benchmark.sh b/ci/setup-win32-benchmark.sh new file mode 100755 index 000000000..0eac2f666 --- /dev/null +++ b/ci/setup-win32-benchmark.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -ex + +choco install hyperfine zip + +CHOCO_PATH=$(mktemp -d) +curl -L https://github.com/ethomson/PurgeStandbyList/releases/download/v1.0/purgestandbylist.1.0.0.nupkg -o "${CHOCO_PATH}/purgestandbylist.1.0.0.nupkg" +choco install purgestandbylist -s $(cygpath -w "${CHOCO_PATH}") diff --git a/ci/test.sh b/ci/test.sh index a94839778..0815522a9 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -159,10 +159,18 @@ fi if [ -z "$SKIP_OFFLINE_TESTS" ]; then echo "" echo "##############################################################################" - echo "## Running (offline) tests" + echo "## Running core tests" echo "##############################################################################" + echo "" + echo "Running libgit2 integration (offline) tests" + echo "" run_test offline + + echo "" + echo "Running utility tests" + echo "" + run_test util fi if [ -n "$RUN_INVASIVE_TESTS" ]; then @@ -186,7 +194,7 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then echo "" echo "##############################################################################" - echo "## Running (online) tests" + echo "## Running networking (online) tests" echo "##############################################################################" export GITTEST_REMOTE_REDIRECT_INITIAL="http://localhost:9000/initial-redirect/libgit2/TestGitRepository" @@ -198,9 +206,9 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then # Run the online tests that immutably change global state separately # to avoid polluting the test environment. echo "" - echo "##############################################################################" - echo "## Running (online_customcert) tests" - echo "##############################################################################" + echo "Running custom certificate (online_customcert) tests" + echo "" + run_test online_customcert fi diff --git a/cmake/AddClarTest.cmake b/cmake/AddClarTest.cmake new file mode 100644 index 000000000..743941638 --- /dev/null +++ b/cmake/AddClarTest.cmake @@ -0,0 +1,7 @@ +function(ADD_CLAR_TEST project name) + if(NOT USE_LEAK_CHECKER STREQUAL "OFF") + add_test(${name} "${PROJECT_SOURCE_DIR}/script/${USE_LEAK_CHECKER}.sh" "${PROJECT_BINARY_DIR}/${project}" ${ARGN}) + else() + add_test(${name} "${PROJECT_BINARY_DIR}/${project}" ${ARGN}) + endif() +endfunction(ADD_CLAR_TEST) diff --git a/cmake/FindPCRE.cmake b/cmake/FindPCRE.cmake index 3a7cfad91..02e7edce1 100644 --- a/cmake/FindPCRE.cmake +++ b/cmake/FindPCRE.cmake @@ -16,19 +16,18 @@ # PCRE_FOUND - True if pcre found. # Look for the header file. -find_path(PCRE_INCLUDE_DIR NAMES pcreposix.h) +find_path(PCRE_INCLUDE_DIR NAMES pcre.h) # Look for the library. find_library(PCRE_LIBRARY NAMES pcre) -find_library(PCRE_POSIX_LIBRARY NAMES pcreposix) # Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE. include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_POSIX_LIBRARY PCRE_INCLUDE_DIR) +find_package_handle_standard_args(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR) # Copy the results to the output variables. if(PCRE_FOUND) - set(PCRE_LIBRARIES ${PCRE_LIBRARY} ${PCRE_POSIX_LIBRARY}) + set(PCRE_LIBRARIES ${PCRE_LIBRARY}) set(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR}) else(PCRE_FOUND) set(PCRE_LIBRARIES) diff --git a/cmake/FindPCRE2.cmake b/cmake/FindPCRE2.cmake index d4b8e6761..41c165b65 100644 --- a/cmake/FindPCRE2.cmake +++ b/cmake/FindPCRE2.cmake @@ -16,7 +16,7 @@ # PCRE2_FOUND - True if pcre found. # Look for the header file. -find_path(PCRE2_INCLUDE_DIR NAMES pcre2posix.h) +find_path(PCRE2_INCLUDE_DIR NAMES pcre2.h) # Look for the library. find_library(PCRE2_LIBRARY NAMES pcre2-8) diff --git a/cmake/SelectHTTPSBackend.cmake b/cmake/SelectHTTPSBackend.cmake index 79319502e..20221bf9f 100644 --- a/cmake/SelectHTTPSBackend.cmake +++ b/cmake/SelectHTTPSBackend.cmake @@ -64,7 +64,7 @@ if(USE_HTTPS) if(NOT CERT_LOCATION) message(STATUS "Auto-detecting default certificates location") - if(CMAKE_SYSTEM_NAME MATCHES Darwin) + if(EXISTS "/usr/local/opt/openssl/bin/openssl") # Check for an Homebrew installation set(OPENSSL_CMD "/usr/local/opt/openssl/bin/openssl") else() diff --git a/cmake/SelectHashes.cmake b/cmake/SelectHashes.cmake index bedd8c4e3..faf9e2ea3 100644 --- a/cmake/SelectHashes.cmake +++ b/cmake/SelectHashes.cmake @@ -4,6 +4,9 @@ include(SanitizeBool) # USE_SHA1=CollisionDetection(ON)/HTTPS/Generic/OFF sanitizebool(USE_SHA1) +sanitizebool(USE_SHA256) + +# sha1 if(USE_SHA1 STREQUAL ON) SET(USE_SHA1 "CollisionDetection") @@ -21,32 +24,77 @@ endif() if(USE_SHA1 STREQUAL "CollisionDetection") set(GIT_SHA1_COLLISIONDETECT 1) - add_definitions(-DSHA1DC_NO_STANDARD_INCLUDES=1) - add_definitions(-DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\"common.h\") - add_definitions(-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"common.h\") elseif(USE_SHA1 STREQUAL "OpenSSL") - # OPENSSL_FOUND should already be set, we're checking USE_HTTPS - set(GIT_SHA1_OPENSSL 1) +elseif(USE_SHA1 STREQUAL "OpenSSL-Dynamic") + set(GIT_SHA1_OPENSSL 1) + set(GIT_SHA1_OPENSSL_DYNAMIC 1) + list(APPEND LIBGIT2_SYSTEM_LIBS dl) +elseif(USE_SHA1 STREQUAL "CommonCrypto") + set(GIT_SHA1_COMMON_CRYPTO 1) +elseif(USE_SHA1 STREQUAL "mbedTLS") + set(GIT_SHA1_MBEDTLS 1) +elseif(USE_SHA1 STREQUAL "Win32") + set(GIT_SHA1_WIN32 1) +else() + message(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}") +endif() + +# sha256 + +if(USE_SHA256 STREQUAL ON AND USE_HTTPS) + SET(USE_SHA256 "HTTPS") +elseif(USE_SHA256 STREQUAL ON) + SET(USE_SHA256 "Builtin") +endif() + +if(USE_SHA256 STREQUAL "HTTPS") + if(USE_HTTPS STREQUAL "SecureTransport") + set(USE_SHA256 "CommonCrypto") + elseif(USE_HTTPS STREQUAL "WinHTTP") + set(USE_SHA256 "Win32") + elseif(USE_HTTPS) + set(USE_SHA256 ${USE_HTTPS}) + endif() +endif() + +if(USE_SHA256 STREQUAL "Builtin") + set(GIT_SHA256_BUILTIN 1) +elseif(USE_SHA256 STREQUAL "OpenSSL") + set(GIT_SHA256_OPENSSL 1) +elseif(USE_SHA256 STREQUAL "OpenSSL-Dynamic") + set(GIT_SHA256_OPENSSL 1) + set(GIT_SHA256_OPENSSL_DYNAMIC 1) + list(APPEND LIBGIT2_SYSTEM_LIBS dl) +elseif(USE_SHA256 STREQUAL "CommonCrypto") + set(GIT_SHA256_COMMON_CRYPTO 1) +elseif(USE_SHA256 STREQUAL "mbedTLS") + set(GIT_SHA256_MBEDTLS 1) +elseif(USE_SHA256 STREQUAL "Win32") + set(GIT_SHA256_WIN32 1) +else() + message(FATAL_ERROR "Asked for unknown SHA256 backend: ${USE_SHA256}") +endif() + +# add library requirements +if(USE_SHA1 STREQUAL "OpenSSL" OR USE_SHA256 STREQUAL "OpenSSL") if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") list(APPEND LIBGIT2_PC_LIBS "-lssl") else() list(APPEND LIBGIT2_PC_REQUIRES "openssl") endif() -elseif(USE_SHA1 STREQUAL "CommonCrypto") - set(GIT_SHA1_COMMON_CRYPTO 1) -elseif(USE_SHA1 STREQUAL "mbedTLS") - set(GIT_SHA1_MBEDTLS 1) +endif() + +if(USE_SHA1 STREQUAL "mbedTLS" OR USE_SHA256 STREQUAL "mbedTLS") list(APPEND LIBGIT2_SYSTEM_INCLUDES ${MBEDTLS_INCLUDE_DIR}) list(APPEND LIBGIT2_SYSTEM_LIBS ${MBEDTLS_LIBRARIES}) # mbedTLS has no pkgconfig file, hence we can't require it # https://github.com/ARMmbed/mbedtls/issues/228 # For now, pass its link flags as our own list(APPEND LIBGIT2_PC_LIBS ${MBEDTLS_LIBRARIES}) -elseif(USE_SHA1 STREQUAL "Win32") - set(GIT_SHA1_WIN32 1) -elseif(NOT (USE_SHA1 STREQUAL "Generic")) - message(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}") endif() -add_feature_info(SHA ON "using ${USE_SHA1}") +# notify feature enablement + +add_feature_info(SHA1 ON "using ${USE_SHA1}") +add_feature_info(SHA256 ON "using ${USE_SHA256}") diff --git a/docs/changelog.md b/docs/changelog.md index 32a67d2c7..989800a11 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,39 +1,79 @@ -v1.4.3 ------- +v1.5 +---- -🔒 This is a security release to provide compatibility with git's changes to address [CVE 2022-24765](https://github.blog/2022-04-12-git-security-vulnerability-announced/). - -**libgit2 is not directly affected** by this vulnerability, because libgit2 does not directly invoke any executable. But we are providing these changes as a security release for any users that use libgit2 for repository discovery and then _also_ use git on that repository. In this release, we will now validate that the user opening the repository is the same user that owns the on-disk repository. This is to match git's behavior. - -In addition, we are providing several correctness fixes where invalid input can lead to a crash. These may prevent possible denial of service attacks. At this time there are not known exploits to these issues. - -Full list of changes: - -* Validate repository directory ownership (v1.4) by @ethomson in https://github.com/libgit2/libgit2/pull/6267 -* midx: Fix an undefined behavior (left-shift signed overflow) by @lhchavez in https://github.com/libgit2/libgit2/pull/6260 -* fetch: support OID refspec without dst by @ethomson in https://github.com/libgit2/libgit2/pull/6251 -* Fix crash when regenerating a patch with unquoted spaces in filename by @jorio in https://github.com/libgit2/libgit2/pull/6244 - -All users of the v1.4 release line are recommended to upgrade. - -**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.4.2...v1.4.3 - -v1.4.2 ------- - -This is a bugfix release with the following changes: +This is release v1.5.0, "Stubentiger". This release adds the basis for an experimental CLI, continues preparing for SHA256 support, adds a benchmarking utility, and has numerous new features and bugfixes. +## What's Changed +### New features +* The beginnings of a git-compatible CLI for testing and benchmarking by @ethomson in https://github.com/libgit2/libgit2/pull/6133 +* Add `clone` support to the CLI @ethomson in https://github.com/libgit2/libgit2/pull/6274 +* A benchmarking suite to compare libgit2 functionality against git by @ethomson in https://github.com/libgit2/libgit2/pull/6235 +* SHA256: add a SHA256 implementation backend by @ethomson in https://github.com/libgit2/libgit2/pull/6144 +* SHA256: support dynamically loaded openssl by @ethomson in https://github.com/libgit2/libgit2/pull/6258 +* Transport: introduce `git_transport_smart_remote_connect_options` by @lhchavez in https://github.com/libgit2/libgit2/pull/6278 +### Bug fixes +* Free parent and ref in lg2_commit before returning. by @apnadkarni in https://github.com/libgit2/libgit2/pull/6219 +* xdiff: use xdl_free not free by @ethomson in https://github.com/libgit2/libgit2/pull/6223 * remote: do store the update_tips callback error value by @carlosmn in https://github.com/libgit2/libgit2/pull/6226 * win32: `find_system_dirs` does not return `GIT_ENOTFOUND` by @ethomson in https://github.com/libgit2/libgit2/pull/6228 +* Some minor fixes for issues discovered by coverity by @ethomson in https://github.com/libgit2/libgit2/pull/6238 +* Fix a string concatenation bug when validating extensions by @bierbaum in https://github.com/libgit2/libgit2/pull/6246 +* fetch: support OID refspec without dst by @ethomson in https://github.com/libgit2/libgit2/pull/6251 +* Fix crash when regenerating a patch with unquoted spaces in filename by @jorio in https://github.com/libgit2/libgit2/pull/6244 +* midx: Fix an undefined behavior (left-shift signed overflow) by @lhchavez in https://github.com/libgit2/libgit2/pull/6260 +* Validate repository directory ownership by @ethomson in https://github.com/libgit2/libgit2/pull/6266 +* midx: fix large offset table check. by @ccstolley in https://github.com/libgit2/libgit2/pull/6309 +* midx: do not verify the checksum on load by @carlosmn in https://github.com/libgit2/libgit2/pull/6291 +* revparse: Remove error-prone, redundant test by @dongcarl in https://github.com/libgit2/libgit2/pull/6299 +* refs: fix missing error message by @zawata in https://github.com/libgit2/libgit2/pull/6305 +* CLI: progress updates by @ethomson in https://github.com/libgit2/libgit2/pull/6319 +* A couple of simplications around mwindow by @carlosmn in https://github.com/libgit2/libgit2/pull/6288 +* config: update config entry iteration lifecycle by @ethomson in https://github.com/libgit2/libgit2/pull/6320 +* repo: allow administrator to own the configuration by @ethomson in https://github.com/libgit2/libgit2/pull/6321 +* filter: Fix Segfault by @zawata in https://github.com/libgit2/libgit2/pull/6303 +* ntlmclient: LibreSSL 3.5 removed HMAC_CTX_cleanup by @vishwin in https://github.com/libgit2/libgit2/pull/6340 +* Fix internal git_sysdir_find* function usage within public git_config_find* functions by @kcsaul in https://github.com/libgit2/libgit2/pull/6335 +* fix interactive rebase detect. by @i-tengfei in https://github.com/libgit2/libgit2/pull/6334 +* cmake: drop posix dependency from pcre* detection by @jpalus in https://github.com/libgit2/libgit2/pull/6333 +* Fix erroneously lax configuration ownership checks by @ethomson in https://github.com/libgit2/libgit2/pull/6341 +* pack: don't pretend we support pack files v3 by @ethomson in https://github.com/libgit2/libgit2/pull/6347 +* Fix creation of branches and tags with invalid names by @lya001 in https://github.com/libgit2/libgit2/pull/6348 +### Security fixes +* Fixes for CVE 2022-29187 by @ethomson in https://github.com/libgit2/libgit2/pull/6349 +* zlib: update bundled zlib to v1.2.12 by @ethomson in https://github.com/libgit2/libgit2/pull/6350 +### Code cleanups +* sha256: refactoring in preparation for sha256 by @ethomson in https://github.com/libgit2/libgit2/pull/6265 +* remote: Delete a now-inexistent API declaration by @lhchavez in https://github.com/libgit2/libgit2/pull/6276 +* Fix missing include by @cschlack in https://github.com/libgit2/libgit2/pull/6277 +### Build and CI improvements +* meta: show build status for v1.3 and v1.4 branches by @ethomson in https://github.com/libgit2/libgit2/pull/6216 +* cmake: Fix package name for system http-parser by @mgorny in https://github.com/libgit2/libgit2/pull/6217 +* meta: update version number to v1.5.0-alpha by @ethomson in https://github.com/libgit2/libgit2/pull/6220 +* cmake: export libraries needed to compile against libgit2 by @ethomson in https://github.com/libgit2/libgit2/pull/6239 +* clone: update bitbucket tests by @ethomson in https://github.com/libgit2/libgit2/pull/6252 +* diff: don't stat empty file on arm32 (flaky test) by @ethomson in https://github.com/libgit2/libgit2/pull/6259 +* tests: support flaky stat by @ethomson in https://github.com/libgit2/libgit2/pull/6262 +* Include test results data in CI by @ethomson in https://github.com/libgit2/libgit2/pull/6306 +* Add a .clang-format with our style by @ethomson in https://github.com/libgit2/libgit2/pull/6023 +* CI: limits actions scheduled workflows to the main repo by @ethomson in https://github.com/libgit2/libgit2/pull/6342 +* ci: update dockerfiles for mbedTLS new url by @ethomson in https://github.com/libgit2/libgit2/pull/6343 +### Documentation improvements +* Add Pharo to language bindings by @theseion in https://github.com/libgit2/libgit2/pull/6310 +* Add link to Tcl bindings for libgit2 by @apnadkarni in https://github.com/libgit2/libgit2/pull/6318 +* fix couple of typos by @SkinnyMind in https://github.com/libgit2/libgit2/pull/6287 +* update documentation for default status options by @ethomson in https://github.com/libgit2/libgit2/pull/6322 -v1.4.1 ------- +## New Contributors +* @bierbaum made their first contribution in https://github.com/libgit2/libgit2/pull/6246 +* @dongcarl made their first contribution in https://github.com/libgit2/libgit2/pull/6299 +* @SkinnyMind made their first contribution in https://github.com/libgit2/libgit2/pull/6287 +* @zawata made their first contribution in https://github.com/libgit2/libgit2/pull/6305 +* @vishwin made their first contribution in https://github.com/libgit2/libgit2/pull/6340 +* @i-tengfei made their first contribution in https://github.com/libgit2/libgit2/pull/6334 +* @jpalus made their first contribution in https://github.com/libgit2/libgit2/pull/6333 +* @lya001 made their first contribution in https://github.com/libgit2/libgit2/pull/6348 -This is a bugfix release with the following changes: - -* xdiff: use xdl_free not free by @ethomson -* cmake: Fix package name for system http-parser by @mgorny -* Free parent and ref in lg2_commit before returning by @apnadkarni +**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.4.0...v1.5.0 v1.4 ---- diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 235e72ada..8e38c7d4e 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,3 +1,5 @@ +# examples: code usage examples of libgit2 + file(GLOB SRC_EXAMPLES *.c *.h) add_executable(lg2 ${SRC_EXAMPLES}) @@ -10,7 +12,7 @@ target_include_directories(lg2 PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_ target_include_directories(lg2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES}) if(WIN32 OR ANDROID) - target_link_libraries(lg2 git2) + target_link_libraries(lg2 libgit2package) else() - target_link_libraries(lg2 git2 pthread) + target_link_libraries(lg2 libgit2package pthread) endif() diff --git a/fuzzers/CMakeLists.txt b/fuzzers/CMakeLists.txt index eaa490fd9..a2c19ed40 100644 --- a/fuzzers/CMakeLists.txt +++ b/fuzzers/CMakeLists.txt @@ -1,3 +1,5 @@ +# fuzzers: libFuzzer and standalone fuzzing utilities + if(BUILD_FUZZERS AND NOT USE_STANDALONE_FUZZERS) set(CMAKE_REQUIRED_FLAGS "-fsanitize=fuzzer-no-link") add_c_flag(-fsanitize=fuzzer) diff --git a/fuzzers/packfile_fuzzer.c b/fuzzers/packfile_fuzzer.c index 6002fa1de..23ecaf80f 100644 --- a/fuzzers/packfile_fuzzer.c +++ b/fuzzers/packfile_fuzzer.c @@ -94,7 +94,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) fprintf(stderr, "Failed to compute the SHA1 hash\n"); abort(); } - if (git_indexer_append(indexer, &oid, sizeof(oid), &stats) < 0) { + if (git_indexer_append(indexer, &oid.id, GIT_OID_RAWSZ, &stats) < 0) { goto cleanup; } } diff --git a/include/git2/branch.h b/include/git2/branch.h index 36a393d10..27c6fa157 100644 --- a/include/git2/branch.h +++ b/include/git2/branch.h @@ -129,8 +129,8 @@ GIT_EXTERN(void) git_branch_iterator_free(git_branch_iterator *iter); * See `git_tag_create()` for rules about valid names. * * Note that if the move succeeds, the old reference object will not - + be valid anymore, and should be freed immediately by the user using - + `git_reference_free()`. + * be valid anymore, and should be freed immediately by the user using + * `git_reference_free()`. * * @param out New reference object for the updated name. * diff --git a/include/git2/common.h b/include/git2/common.h index b8c4ad531..c3e3e7b4e 100644 --- a/include/git2/common.h +++ b/include/git2/common.h @@ -121,6 +121,17 @@ GIT_BEGIN_DECL */ GIT_EXTERN(int) git_libgit2_version(int *major, int *minor, int *rev); +/** + * Return the prerelease state of the libgit2 library currently being + * used. For nightly builds during active development, this will be + * "alpha". Releases may have a "beta" or release candidate ("rc1", + * "rc2", etc) prerelease. For a final release, this function returns + * NULL. + * + * @return the name of the prerelease state or NULL + */ +GIT_EXTERN(const char *) git_libgit2_prerelease(void); + /** * Combinations of these values describe the features with which libgit2 * was compiled diff --git a/include/git2/config.h b/include/git2/config.h index a5486a4c9..cfab0c757 100644 --- a/include/git2/config.h +++ b/include/git2/config.h @@ -122,7 +122,7 @@ typedef struct { * global configuration file. * * This method will not guess the path to the xdg compatible - * config file (.config/git/config). + * config file (`.config/git/config`). * * @param out Pointer to a user-allocated git_buf in which to store the path * @return 0 if a global configuration file has been found. Its path will be stored in `out`. @@ -149,8 +149,8 @@ GIT_EXTERN(int) git_config_find_xdg(git_buf *out); /** * Locate the path to the system configuration file * - * If /etc/gitconfig doesn't exist, it will look for - * %PROGRAMFILES%\Git\etc\gitconfig. + * If `/etc/gitconfig` doesn't exist, it will look for + * `%PROGRAMFILES%\Git\etc\gitconfig`. * * @param out Pointer to a user-allocated git_buf in which to store the path * @return 0 if a system configuration file has been @@ -161,7 +161,7 @@ GIT_EXTERN(int) git_config_find_system(git_buf *out); /** * Locate the path to the configuration file in ProgramData * - * Look for the file in %PROGRAMDATA%\Git\config used by portable git. + * Look for the file in `%PROGRAMDATA%\Git\config` used by portable git. * * @param out Pointer to a user-allocated git_buf in which to store the path * @return 0 if a ProgramData configuration file has been @@ -449,8 +449,8 @@ GIT_EXTERN(int) git_config_multivar_iterator_new(git_config_iterator **out, cons /** * Return the current entry and advance the iterator * - * The pointers returned by this function are valid until the iterator - * is freed. + * The pointers returned by this function are valid until the next call + * to `git_config_next` or until the iterator is freed. * * @param entry pointer to store the entry * @param iter the iterator diff --git a/include/git2/deprecated.h b/include/git2/deprecated.h index f73d7da61..c32abeeb7 100644 --- a/include/git2/deprecated.h +++ b/include/git2/deprecated.h @@ -436,6 +436,8 @@ GIT_EXTERN(int) git_diff_format_email_options_init( #define GITERR_WORKTREE GIT_ERROR_WORKTREE #define GITERR_SHA1 GIT_ERROR_SHA1 +#define GIT_ERROR_SHA1 GIT_ERROR_SHA + /** * Return the last `git_error` object that was generated for the * current thread. This is an alias of `git_error_last` and is diff --git a/include/git2/errors.h b/include/git2/errors.h index 16712f988..a61964bbb 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -107,7 +107,7 @@ typedef enum { GIT_ERROR_FILESYSTEM, GIT_ERROR_PATCH, GIT_ERROR_WORKTREE, - GIT_ERROR_SHA1, + GIT_ERROR_SHA, GIT_ERROR_HTTP, GIT_ERROR_INTERNAL } git_error_t; @@ -131,7 +131,8 @@ GIT_EXTERN(const git_error *) git_error_last(void); GIT_EXTERN(void) git_error_clear(void); /** - * Set the error message string for this thread. + * Set the error message string for this thread, using `printf`-style + * formatting. * * This function is public so that custom ODB backends and the like can * relay an error message through libgit2. Most regular users of libgit2 @@ -144,7 +145,20 @@ GIT_EXTERN(void) git_error_clear(void); * * @param error_class One of the `git_error_t` enum above describing the * general subsystem that is responsible for the error. - * @param string The formatted error message to keep + * @param fmt The `printf`-style format string; subsequent arguments must + * be the arguments for the format string. + */ +GIT_EXTERN(void) git_error_set(int error_class, const char *fmt, ...) + GIT_FORMAT_PRINTF(2, 3); + +/** + * Set the error message string for this thread. This function is like + * `git_error_set` but takes a static string instead of a `printf`-style + * format. + * + * @param error_class One of the `git_error_t` enum above describing the + * general subsystem that is responsible for the error. + * @param string The error message to keep * @return 0 on success or -1 on failure */ GIT_EXTERN(int) git_error_set_str(int error_class, const char *string); diff --git a/include/git2/merge.h b/include/git2/merge.h index e90941a49..fcce5594d 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -603,7 +603,7 @@ GIT_EXTERN(int) git_merge_commits( * completes, resolve any conflicts and prepare a commit. * * For compatibility with git, the repository is put into a merging - * state. Once the commit is done (or if the uses wishes to abort), + * state. Once the commit is done (or if the user wishes to abort), * you should clear this state by calling * `git_repository_state_cleanup()`. * diff --git a/include/git2/status.h b/include/git2/status.h index d8f9663b4..bb28e875b 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -227,13 +227,16 @@ typedef struct { /** * The `show` value is one of the `git_status_show_t` constants that - * control which files to scan and in what order. + * control which files to scan and in what order. The default is + * `GIT_STATUS_SHOW_INDEX_AND_WORKDIR`. */ git_status_show_t show; /** * The `flags` value is an OR'ed combination of the - * `git_status_opt_t` values above. + * `git_status_opt_t` values above. The default is + * `GIT_STATUS_OPT_DEFAULTS`, which matches git's default + * behavior. */ unsigned int flags; diff --git a/include/git2/sys/remote.h b/include/git2/sys/remote.h index dd243ca55..0eae9234d 100644 --- a/include/git2/sys/remote.h +++ b/include/git2/sys/remote.h @@ -8,6 +8,8 @@ #ifndef INCLUDE_sys_git_remote_h #define INCLUDE_sys_git_remote_h +#include "git2/remote.h" + /** * @file git2/sys/remote.h * @brief Low-level remote functionality for custom transports @@ -26,6 +28,19 @@ typedef enum { GIT_REMOTE_CAPABILITY_REACHABLE_OID = (1 << 1), } git_remote_capability_t; +/** + * Disposes libgit2-initialized fields from a git_remote_connect_options. + * This should only be used for git_remote_connect_options returned by + * git_transport_remote_connect_options. + * + * Note that this does not free the `git_remote_connect_options` itself, just + * the memory pointed to by it. + * + * @param opts The `git_remote_connect_options` struct to dispose. + */ +GIT_EXTERN(void) git_remote_connect_options_dispose( + git_remote_connect_options *opts); + /** @} */ GIT_END_DECL #endif diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h index f0c2a3eab..06ae7079f 100644 --- a/include/git2/sys/transport.h +++ b/include/git2/sys/transport.h @@ -9,10 +9,11 @@ #define INCLUDE_sys_git_transport_h #include "git2/net.h" +#include "git2/proxy.h" +#include "git2/remote.h" +#include "git2/strarray.h" #include "git2/transport.h" #include "git2/types.h" -#include "git2/strarray.h" -#include "git2/proxy.h" /** * @file git2/sys/transport.h @@ -261,14 +262,17 @@ GIT_EXTERN(int) git_transport_smart_certificate_check(git_transport *transport, GIT_EXTERN(int) git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods); /** - * Get a copy of the proxy options + * Get a copy of the remote connect options * - * The url is copied and must be freed by the caller. + * All data is copied and must be freed by the caller by calling + * `git_remote_connect_options_dispose`. * * @param out options struct to fill * @param transport the transport to extract the data from. */ -GIT_EXTERN(int) git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport); +GIT_EXTERN(int) git_transport_remote_connect_options( + git_remote_connect_options *out, + git_transport *transport); /* *** End of base transport interface *** diff --git a/include/git2/version.h b/include/git2/version.h index 980f70b69..d591904fd 100644 --- a/include/git2/version.h +++ b/include/git2/version.h @@ -7,12 +7,33 @@ #ifndef INCLUDE_git_version_h__ #define INCLUDE_git_version_h__ -#define LIBGIT2_VERSION "1.4.3" -#define LIBGIT2_VER_MAJOR 1 -#define LIBGIT2_VER_MINOR 4 -#define LIBGIT2_VER_REVISION 3 -#define LIBGIT2_VER_PATCH 0 +/** + * The version string for libgit2. This string follows semantic + * versioning (v2) guidelines. + */ +#define LIBGIT2_VERSION "1.5.0" -#define LIBGIT2_SOVERSION "1.4" +/** The major version number for this version of libgit2. */ +#define LIBGIT2_VER_MAJOR 1 + +/** The minor version number for this version of libgit2. */ +#define LIBGIT2_VER_MINOR 5 + +/** The revision ("teeny") version number for this version of libgit2. */ +#define LIBGIT2_VER_REVISION 0 + +/** The Windows DLL patch number for this version of libgit2. */ +#define LIBGIT2_VER_PATCH 0 + +/** + * The prerelease string for this version of libgit2. For development + * (nightly) builds, this will be "alpha". For prereleases, this will be + * a prerelease name like "beta" or "rc1". For final releases, this will + * be `NULL`. + */ +#define LIBGIT2_VER_PRERELEASE NULL + +/** The library ABI soversion for this version of libgit2. */ +#define LIBGIT2_SOVERSION "1.5" #endif diff --git a/package.json b/package.json index 5caa47aea..8676fe513 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libgit2", - "version": "1.4.3", + "version": "1.5.0", "repo": "https://github.com/libgit2/libgit2", "description": " A cross-platform, linkable library implementation of Git that you can use in your application.", "install": "mkdir build && cd build && cmake .. && cmake --build ." diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e7b54d036..d16cfe538 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,12 +1,22 @@ -add_library(git2internal OBJECT) -set_target_properties(git2internal PROPERTIES C_STANDARD 90) -set_target_properties(git2internal PROPERTIES C_EXTENSIONS OFF) +# The main libgit2 source tree: this CMakeLists.txt identifies platform +# support and includes the subprojects that make up core libgit2 support. +# +# Optional build configuration settings +# if(DEPRECATE_HARD) add_definitions(-DGIT_DEPRECATE_HARD) endif() +if(USE_LEAK_CHECKER STREQUAL "valgrind") + add_definitions(-DVALGRIND) +endif() + +# +# Optional debugging functionality +# + if(DEBUG_POOL) set(GIT_DEBUG_POOL 1) endif() @@ -22,29 +32,32 @@ if(DEBUG_STRICT_OPEN) endif() add_feature_info(debugopen GIT_DEBUG_STRICT_OPEN "path validation in open") +# +# Optional feature enablement +# -include(PkgBuildConfig) -include(SanitizeBool) +include(SelectGSSAPI) +include(SelectHTTPSBackend) +include(SelectHashes) +include(SelectHTTPParser) +include(SelectRegex) +include(SelectSSH) +include(SelectWinHTTP) +include(SelectZlib) -# This variable will contain the libraries we need to put into -# libgit2.pc's Requires.private. That is, what we're linking to or -# what someone who's statically linking us needs to link to. -set(LIBGIT2_PC_REQUIRES "") -# This will be set later if we use the system's http-parser library or -# use iconv (OSX) and will be written to the Libs.private field in the -# pc file. -set(LIBGIT2_PC_LIBS "") +# +# Platform support +# -set(LIBGIT2_INCLUDES - "${CMAKE_CURRENT_BINARY_DIR}" - "${PROJECT_SOURCE_DIR}/src" - "${PROJECT_SOURCE_DIR}/include") +# futimes/futimens if(HAVE_FUTIMENS) set(GIT_USE_FUTIMENS 1) endif () add_feature_info(futimens GIT_USE_FUTIMENS "futimens support") +# qsort + check_prototype_definition(qsort_r "void qsort_r(void *base, size_t nmemb, size_t size, void *thunk, int (*compar)(void *, const void *, const void *))" "" "stdlib.h" GIT_QSORT_R_BSD) @@ -55,69 +68,85 @@ check_prototype_definition(qsort_r check_function_exists(qsort_s GIT_QSORT_S) +# random / entropy data + check_function_exists(getentropy GIT_RAND_GETENTROPY) +check_function_exists(getloadavg GIT_RAND_GETLOADAVG) -# Find required dependencies +# determine architecture of the machine -if(WIN32) - list(APPEND LIBGIT2_SYSTEM_LIBS ws2_32) -elseif(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") - list(APPEND LIBGIT2_SYSTEM_LIBS socket nsl) - list(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl") -elseif(CMAKE_SYSTEM_NAME MATCHES "Haiku") - list(APPEND LIBGIT2_SYSTEM_LIBS network) - list(APPEND LIBGIT2_PC_LIBS "-lnetwork") +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(GIT_ARCH_64 1) +elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(GIT_ARCH_32 1) +elseif(CMAKE_SIZEOF_VOID_P) + message(FATAL_ERROR "Unsupported architecture (pointer size is ${CMAKE_SIZEOF_VOID_P} bytes)") +else() + message(FATAL_ERROR "Unsupported architecture (CMAKE_SIZEOF_VOID_P is unset)") endif() +# nanosecond mtime/ctime support + +if(USE_NSEC) + set(GIT_USE_NSEC 1) +endif() + +# high-resolution stat support + +if(HAVE_STRUCT_STAT_ST_MTIM) + set(GIT_USE_STAT_MTIM 1) +elseif(HAVE_STRUCT_STAT_ST_MTIMESPEC) + set(GIT_USE_STAT_MTIMESPEC 1) +elseif(HAVE_STRUCT_STAT_ST_MTIME_NSEC) + set(GIT_USE_STAT_MTIME_NSEC 1) +endif() + +# realtime support + check_library_exists(rt clock_gettime "time.h" NEED_LIBRT) if(NEED_LIBRT) list(APPEND LIBGIT2_SYSTEM_LIBS rt) list(APPEND LIBGIT2_PC_LIBS "-lrt") endif() +# platform libraries + +if(WIN32) + list(APPEND LIBGIT2_SYSTEM_LIBS ws2_32) +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + list(APPEND LIBGIT2_SYSTEM_LIBS socket nsl) + list(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl") +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "Haiku") + list(APPEND LIBGIT2_SYSTEM_LIBS gnu network) + list(APPEND LIBGIT2_PC_LIBS "-lgnu -lnetwork") +endif() + +if(AMIGA) + add_definitions(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP) +endif() + +# threads + if(USE_THREADS) - list(APPEND LIBGIT2_SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT}) - list(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT}) + if(NOT WIN32) + find_package(Threads REQUIRED) + list(APPEND LIBGIT2_SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT}) + list(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT}) + endif() + + set(GIT_THREADS 1) endif() add_feature_info(threadsafe USE_THREADS "threadsafe support") +# +# Optional bundled features +# -if(WIN32 AND EMBED_SSH_PATH) - file(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c") - list(SORT SRC_SSH) - target_sources(git2internal PRIVATE ${SRC_SSH}) - - list(APPEND LIBGIT2_SYSTEM_INCLUDES "${EMBED_SSH_PATH}/include") - file(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"") - set(GIT_SSH 1) -endif() - -include(SelectHTTPSBackend) -include(SelectHashes) -include(SelectHTTPParser) -include(SelectRegex) -include(SelectSSH) -include(SelectWinHTTP) -include(SelectZlib) - - -if(USE_SHA1 STREQUAL "CollisionDetection") - file(GLOB SRC_SHA1 hash/sha1/collisiondetect.* hash/sha1/sha1dc/*) -elseif(USE_SHA1 STREQUAL "OpenSSL") - file(GLOB SRC_SHA1 hash/sha1/openssl.*) -elseif(USE_SHA1 STREQUAL "CommonCrypto") - file(GLOB SRC_SHA1 hash/sha1/common_crypto.*) -elseif(USE_SHA1 STREQUAL "mbedTLS") - file(GLOB SRC_SHA1 hash/sha1/mbedtls.*) -elseif(USE_SHA1 STREQUAL "Win32") - file(GLOB SRC_SHA1 hash/sha1/win32.*) -elseif(USE_SHA1 STREQUAL "Generic") - file(GLOB SRC_SHA1 hash/sha1/generic.*) -endif() -list(APPEND SRC_SHA1 "hash/sha1.h") -target_sources(git2internal PRIVATE ${SRC_SHA1}) - -# Optional external dependency: ntlmclient +# ntlmclient if(USE_NTLMCLIENT) set(GIT_NTLM 1) add_subdirectory("${PROJECT_SOURCE_DIR}/deps/ntlmclient" "${PROJECT_BINARY_DIR}/deps/ntlmclient") @@ -126,11 +155,10 @@ if(USE_NTLMCLIENT) endif() add_feature_info(ntlmclient GIT_NTLM "NTLM authentication support for Unix") -# Optional external dependency: GSSAPI +# +# Optional external dependencies -include(SelectGSSAPI) - -# Optional external dependency: iconv +# iconv if(USE_ICONV) find_package(Iconv) endif() @@ -142,166 +170,28 @@ if(ICONV_FOUND) endif() add_feature_info(iconv GIT_USE_ICONV "iconv encoding conversion support") - -if(USE_THREADS) - if(NOT WIN32) - find_package(Threads REQUIRED) - endif() - - set(GIT_THREADS 1) -endif() - -if(USE_NSEC) - set(GIT_USE_NSEC 1) -endif() - -if(HAVE_STRUCT_STAT_ST_MTIM) - set(GIT_USE_STAT_MTIM 1) -elseif(HAVE_STRUCT_STAT_ST_MTIMESPEC) - set(GIT_USE_STAT_MTIMESPEC 1) -elseif(HAVE_STRUCT_STAT_ST_MTIME_NSEC) - set(GIT_USE_STAT_MTIME_NSEC 1) -endif() - -target_compile_definitions(git2internal PRIVATE _FILE_OFFSET_BITS=64) - -# Collect sourcefiles -file(GLOB SRC_H - "${PROJECT_SOURCE_DIR}/include/git2.h" - "${PROJECT_SOURCE_DIR}/include/git2/*.h" - "${PROJECT_SOURCE_DIR}/include/git2/sys/*.h") -list(SORT SRC_H) -target_sources(git2internal PRIVATE ${SRC_H}) - -# On Windows use specific platform sources -if(WIN32 AND NOT CYGWIN) - set(WIN_RC "win32/git2.rc") - - file(GLOB SRC_OS win32/*.c win32/*.h) - list(SORT SRC_OS) - target_sources(git2internal PRIVATE ${SRC_OS}) -elseif(AMIGA) - target_compile_definitions(git2internal PRIVATE NO_ADDRINFO NO_READDIR_R NO_MMAP) -else() - file(GLOB SRC_OS unix/*.c unix/*.h) - list(SORT SRC_OS) - target_sources(git2internal PRIVATE ${SRC_OS}) -endif() - -if(USE_LEAK_CHECKER STREQUAL "valgrind") - target_compile_definitions(git2internal PRIVATE VALGRIND) -endif() - -file(GLOB SRC_GIT2 *.c *.h - allocators/*.c allocators/*.h - streams/*.c streams/*.h - transports/*.c transports/*.h - xdiff/*.c xdiff/*.h) -list(SORT SRC_GIT2) -target_sources(git2internal PRIVATE ${SRC_GIT2}) - -if(APPLE) - # The old Secure Transport API has been deprecated in macOS 10.15. - set_source_files_properties(streams/stransport.c PROPERTIES COMPILE_FLAGS -Wno-deprecated) -endif() - -# the xdiff dependency is not (yet) warning-free, disable warnings as -# errors for the xdiff sources until we've sorted them out -if(MSVC) - set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS -WX-) - set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS -WX-) - set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS -WX-) - set_source_files_properties(xdiff/xmerge.c PROPERTIES COMPILE_FLAGS -WX-) - set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS -WX-) - set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS -WX-) -else() - set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter") - set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter") - set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare") - set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare") - set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare") -endif() - -# Determine architecture of the machine -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(GIT_ARCH_64 1) -elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) - set(GIT_ARCH_32 1) -elseif(CMAKE_SIZEOF_VOID_P) - message(FATAL_ERROR "Unsupported architecture (pointer size is ${CMAKE_SIZEOF_VOID_P} bytes)") -else() - message(FATAL_ERROR "Unsupported architecture (CMAKE_SIZEOF_VOID_P is unset)") -endif() +# +# Configure support +# configure_file(features.h.in git2/sys/features.h) -ide_split_sources(git2internal) -list(APPEND LIBGIT2_OBJECTS $ ${LIBGIT2_DEPENDENCY_OBJECTS}) +# +# Include child projects +# -target_include_directories(git2internal PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${PROJECT_SOURCE_DIR}/include) -target_include_directories(git2internal SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES}) +add_subdirectory(libgit2) +add_subdirectory(util) +if(BUILD_CLI) + add_subdirectory(cli) +endif() + +# re-export these to the root so that peer projects (tests, fuzzers, +# examples) can use them set(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE) set(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE) set(LIBGIT2_DEPENDENCY_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES} PARENT_SCOPE) set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE) set(LIBGIT2_SYSTEM_INCLUDES ${LIBGIT2_SYSTEM_INCLUDES} PARENT_SCOPE) set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE) - -if(XCODE_VERSION) - # This is required for Xcode to actually link the libgit2 library - # when using only object libraries. - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/dummy.c "") - list(APPEND LIBGIT2_OBJECTS ${CMAKE_CURRENT_BINARY_DIR}/dummy.c) -endif() - -# Compile and link libgit2 -add_library(git2 ${WIN_RC} ${LIBGIT2_OBJECTS}) -target_link_libraries(git2 ${LIBGIT2_SYSTEM_LIBS}) - -set_target_properties(git2 PROPERTIES C_STANDARD 90) -set_target_properties(git2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) -set_target_properties(git2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) -set_target_properties(git2 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) - -# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240) -# Win64+MSVC+static libs = linker error -if(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS) - set_target_properties(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64") -endif() - -ide_split_sources(git2) - -if(SONAME) - set_target_properties(git2 PROPERTIES VERSION ${libgit2_VERSION}) - set_target_properties(git2 PROPERTIES SOVERSION "${libgit2_VERSION_MAJOR}.${libgit2_VERSION_MINOR}") - if(LIBGIT2_FILENAME) - target_compile_definitions(git2 PRIVATE LIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\") - set_target_properties(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME}) - elseif(DEFINED LIBGIT2_PREFIX) - set_target_properties(git2 PROPERTIES PREFIX "${LIBGIT2_PREFIX}") - endif() -endif() - -pkg_build_config(NAME libgit2 - VERSION ${libgit2_VERSION} - DESCRIPTION "The git library, take 2" - LIBS_SELF git2 - PRIVATE_LIBS ${LIBGIT2_PC_LIBS} - REQUIRES ${LIBGIT2_PC_REQUIRES} -) - -if(MSVC_IDE) - # Precompiled headers - set_target_properties(git2 PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h") - set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h") -endif() - -# Install -install(TARGETS git2 - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) -install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/git2 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(FILES ${PROJECT_SOURCE_DIR}/include/git2.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/src/README.md b/src/README.md new file mode 100644 index 000000000..10b86c1dc --- /dev/null +++ b/src/README.md @@ -0,0 +1,12 @@ +# libgit2 sources + +This is the source that makes up the core of libgit2 and its related +projects. + +* `cli` + A git-compatible command-line interface that uses libgit2. +* `libgit2` + This is the libgit2 project, a cross-platform, linkable library + implementation of Git that you can use in your application. +* `util` + A shared utility library for these projects. diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt new file mode 100644 index 000000000..4f347e93f --- /dev/null +++ b/src/cli/CMakeLists.txt @@ -0,0 +1,53 @@ +set(CLI_INCLUDES + "${libgit2_BINARY_DIR}/src" + "${libgit2_SOURCE_DIR}/src/util" + "${libgit2_SOURCE_DIR}/src/cli" + "${libgit2_SOURCE_DIR}/include") + +if(WIN32 AND NOT CYGWIN) + file(GLOB CLI_SRC_OS win32/*.c) + list(SORT CLI_SRC_OS) +else() + file(GLOB CLI_SRC_OS unix/*.c) + list(SORT CLI_SRC_OS) +endif() + +file(GLOB CLI_SRC_C *.c *.h) +list(SORT CLI_SRC_C) + +# +# The CLI currently needs to be statically linked against libgit2 because +# the utility library uses libgit2's thread-local error buffers. TODO: +# remove this dependency and allow us to dynamically link against libgit2. +# + +if(BUILD_CLI STREQUAL "dynamic") + set(CLI_LIBGIT2_LIBRARY libgit2package) +else() + set(CLI_LIBGIT2_OBJECTS $) +endif() + +# +# Compile and link the CLI +# + +add_executable(git2_cli ${CLI_SRC_C} ${CLI_SRC_OS} ${CLI_OBJECTS} + $ + ${CLI_LIBGIT2_OBJECTS} + ${LIBGIT2_DEPENDENCY_OBJECTS}) +target_link_libraries(git2_cli ${CLI_LIBGIT2_LIBRARY} ${LIBGIT2_SYSTEM_LIBS}) + +set_target_properties(git2_cli PROPERTIES C_STANDARD 90) +set_target_properties(git2_cli PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR}) + +ide_split_sources(git2_cli) + +target_include_directories(git2_cli PRIVATE ${CLI_INCLUDES}) + +if(MSVC_IDE) + # Precompiled headers + set_target_properties(git2_cli PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h") + set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h") +endif() + +install(TARGETS git2_cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/src/cli/README.md b/src/cli/README.md new file mode 100644 index 000000000..3087c39c4 --- /dev/null +++ b/src/cli/README.md @@ -0,0 +1,26 @@ +# cli + +A git-compatible command-line interface that uses libgit2. + +## Adding commands + +1. Individual commands have a `main`-like top-level entrypoint. For example: + + ```c + int cmd_help(int argc, char **argv) + ``` + + Although this is the same signature as `main`, commands are not built as + individual standalone executables, they'll be linked into the main cli. + (Though there may be an option for command executables to be built as + standalone executables in the future.) + +2. Commands are prototyped in `cmd.h` and added to `main.c`'s list of + commands (`cli_cmds[]`). Commands should be specified with their name, + entrypoint and a brief description that can be printed in `git help`. + This is done because commands are linked into the main cli. + +3. Commands should accept a `--help` option that displays their help + information. This will be shown when a user runs ` --help` and + when a user runs `help `. + diff --git a/src/hash/sha1/generic.h b/src/cli/cli.h similarity index 52% rename from src/hash/sha1/generic.h rename to src/cli/cli.h index 53fc0823e..7dede6785 100644 --- a/src/hash/sha1/generic.h +++ b/src/cli/cli.h @@ -5,15 +5,16 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#ifndef INCLUDE_hash_sha1_generic_h__ -#define INCLUDE_hash_sha1_generic_h__ +#ifndef CLI_cli_h__ +#define CLI_cli_h__ -#include "hash/sha1.h" +#define PROGRAM_NAME "git2" -struct git_hash_sha1_ctx { - uint64_t size; - unsigned int H[5]; - unsigned int W[16]; -}; +#include "git2_util.h" -#endif +#include "error.h" +#include "opt.h" +#include "opt_usage.h" +#include "sighandler.h" + +#endif /* CLI_cli_h__ */ diff --git a/src/cli/cmd.c b/src/cli/cmd.c new file mode 100644 index 000000000..2a7e71cdb --- /dev/null +++ b/src/cli/cmd.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "cli.h" +#include "cmd.h" + +const cli_cmd_spec *cli_cmd_spec_byname(const char *name) +{ + const cli_cmd_spec *cmd; + + for (cmd = cli_cmds; cmd->name; cmd++) { + if (!strcmp(cmd->name, name)) + return cmd; + } + + return NULL; +} diff --git a/src/cli/cmd.h b/src/cli/cmd.h new file mode 100644 index 000000000..8b1a1b38f --- /dev/null +++ b/src/cli/cmd.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef CLI_cmd_h__ +#define CLI_cmd_h__ + +/* Command definitions */ +typedef struct { + const char *name; + int (*fn)(int argc, char **argv); + const char *desc; +} cli_cmd_spec; + +/* Options that are common to all commands (eg --help, --git-dir) */ +extern const cli_opt_spec cli_common_opts[]; + +/* All the commands supported by the CLI */ +extern const cli_cmd_spec cli_cmds[]; + +/* Find a command by name */ +extern const cli_cmd_spec *cli_cmd_spec_byname(const char *name); + +/* Commands */ +extern int cmd_cat_file(int argc, char **argv); +extern int cmd_clone(int argc, char **argv); +extern int cmd_hash_object(int argc, char **argv); +extern int cmd_help(int argc, char **argv); + +#endif /* CLI_cmd_h__ */ diff --git a/src/cli/cmd_cat_file.c b/src/cli/cmd_cat_file.c new file mode 100644 index 000000000..fb53a722b --- /dev/null +++ b/src/cli/cmd_cat_file.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include +#include "cli.h" +#include "cmd.h" + +#define COMMAND_NAME "cat-file" + +typedef enum { + DISPLAY_CONTENT = 0, + DISPLAY_EXISTS, + DISPLAY_PRETTY, + DISPLAY_SIZE, + DISPLAY_TYPE +} display_t; + +static int show_help; +static int display = DISPLAY_CONTENT; +static char *type_name, *object_spec; + +static const cli_opt_spec opts[] = { + { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1, + CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL, + "display help about the " COMMAND_NAME " command" }, + + { CLI_OPT_TYPE_SWITCH, NULL, 't', &display, DISPLAY_TYPE, + CLI_OPT_USAGE_REQUIRED, NULL, "display the type of the object" }, + { CLI_OPT_TYPE_SWITCH, NULL, 's', &display, DISPLAY_SIZE, + CLI_OPT_USAGE_CHOICE, NULL, "display the size of the object" }, + { CLI_OPT_TYPE_SWITCH, NULL, 'e', &display, DISPLAY_EXISTS, + CLI_OPT_USAGE_CHOICE, NULL, "displays nothing unless the object is corrupt" }, + { CLI_OPT_TYPE_SWITCH, NULL, 'p', &display, DISPLAY_PRETTY, + CLI_OPT_USAGE_CHOICE, NULL, "pretty-print the object" }, + { CLI_OPT_TYPE_ARG, "type", 0, &type_name, 0, + CLI_OPT_USAGE_CHOICE, "type", "the type of object to display" }, + { CLI_OPT_TYPE_ARG, "object", 0, &object_spec, 0, + CLI_OPT_USAGE_REQUIRED, "object", "the object to display" }, + { 0 }, +}; + +static void print_help(void) +{ + cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts); + printf("\n"); + + printf("Display the content for the given object in the repository.\n"); + printf("\n"); + + printf("Options:\n"); + + cli_opt_help_fprint(stdout, opts); +} + +static int print_odb(git_object *object, display_t display) +{ + git_odb *odb = NULL; + git_odb_object *odb_object = NULL; + const unsigned char *content; + git_object_size_t size; + int ret = 0; + + /* + * Our parsed blobs retain the raw content; all other objects are + * parsed into a working representation. To get the raw content, + * we need to do an ODB lookup. (Thankfully, this should be cached + * in-memory from our last call.) + */ + if (git_object_type(object) == GIT_OBJECT_BLOB) { + content = git_blob_rawcontent((git_blob *)object); + size = git_blob_rawsize((git_blob *)object); + } else { + if (git_repository_odb(&odb, git_object_owner(object)) < 0 || + git_odb_read(&odb_object, odb, git_object_id(object)) < 0) { + ret = cli_error_git(); + goto done; + } + + content = git_odb_object_data(odb_object); + size = git_odb_object_size(odb_object); + } + + switch (display) { + case DISPLAY_SIZE: + if (printf("%" PRIu64 "\n", size) < 0) + ret = cli_error_os(); + break; + case DISPLAY_CONTENT: + if (p_write(fileno(stdout), content, (size_t)size) < 0) + ret = cli_error_os(); + break; + default: + GIT_ASSERT(0); + } + +done: + git_odb_object_free(odb_object); + git_odb_free(odb); + return ret; +} + +static int print_type(git_object *object) +{ + if (printf("%s\n", git_object_type2string(git_object_type(object))) < 0) + return cli_error_os(); + + return 0; +} + +static int print_pretty(git_object *object) +{ + const git_tree_entry *entry; + size_t i, count; + + /* + * Only trees are stored in an unreadable format and benefit from + * pretty-printing. + */ + if (git_object_type(object) != GIT_OBJECT_TREE) + return print_odb(object, DISPLAY_CONTENT); + + for (i = 0, count = git_tree_entrycount((git_tree *)object); i < count; i++) { + entry = git_tree_entry_byindex((git_tree *)object, i); + + if (printf("%06o %s %s\t%s\n", + git_tree_entry_filemode_raw(entry), + git_object_type2string(git_tree_entry_type(entry)), + git_oid_tostr_s(git_tree_entry_id(entry)), + git_tree_entry_name(entry)) < 0) + return cli_error_os(); + } + + return 0; +} + +int cmd_cat_file(int argc, char **argv) +{ + git_repository *repo = NULL; + git_object *object = NULL; + git_object_t type; + cli_opt invalid_opt; + int giterr, ret = 0; + + if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU)) + return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt); + + if (show_help) { + print_help(); + return 0; + } + + if (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0) + return cli_error_git(); + + if ((giterr = git_revparse_single(&object, repo, object_spec)) < 0) { + if (display == DISPLAY_EXISTS && giterr == GIT_ENOTFOUND) + ret = 1; + else + ret = cli_error_git(); + + goto done; + } + + if (type_name) { + git_object *peeled; + + if ((type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID) { + ret = cli_error_usage("invalid object type '%s'", type_name); + goto done; + } + + if (git_object_peel(&peeled, object, type) < 0) { + ret = cli_error_git(); + goto done; + } + + git_object_free(object); + object = peeled; + } + + switch (display) { + case DISPLAY_EXISTS: + ret = 0; + break; + case DISPLAY_TYPE: + ret = print_type(object); + break; + case DISPLAY_PRETTY: + ret = print_pretty(object); + break; + default: + ret = print_odb(object, display); + break; + } + +done: + git_object_free(object); + git_repository_free(repo); + return ret; +} diff --git a/src/cli/cmd_clone.c b/src/cli/cmd_clone.c new file mode 100644 index 000000000..a382b5875 --- /dev/null +++ b/src/cli/cmd_clone.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include +#include +#include "cli.h" +#include "cmd.h" +#include "error.h" +#include "sighandler.h" +#include "progress.h" + +#include "fs_path.h" +#include "futils.h" + +#define COMMAND_NAME "clone" + +static char *branch, *remote_path, *local_path; +static int show_help, quiet, checkout = 1, bare; +static bool local_path_exists; +static cli_progress progress = CLI_PROGRESS_INIT; + +static const cli_opt_spec opts[] = { + { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1, + CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL, + "display help about the " COMMAND_NAME " command" }, + + { CLI_OPT_TYPE_SWITCH, "quiet", 'q', &quiet, 1, + CLI_OPT_USAGE_DEFAULT, NULL, "display the type of the object" }, + { CLI_OPT_TYPE_SWITCH, "no-checkout", 'n', &checkout, 0, + CLI_OPT_USAGE_DEFAULT, NULL, "don't checkout HEAD" }, + { CLI_OPT_TYPE_SWITCH, "bare", 0, &bare, 1, + CLI_OPT_USAGE_DEFAULT, NULL, "don't create a working directory" }, + { CLI_OPT_TYPE_VALUE, "branch", 'b', &branch, 0, + CLI_OPT_USAGE_DEFAULT, "name", "branch to check out" }, + { CLI_OPT_TYPE_LITERAL }, + { CLI_OPT_TYPE_ARG, "repository", 0, &remote_path, 0, + CLI_OPT_USAGE_REQUIRED, "repository", "repository path" }, + { CLI_OPT_TYPE_ARG, "directory", 0, &local_path, 0, + CLI_OPT_USAGE_DEFAULT, "directory", "directory to clone into" }, + { 0 } +}; + +static void print_help(void) +{ + cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts); + printf("\n"); + + printf("Clone a repository into a new directory.\n"); + printf("\n"); + + printf("Options:\n"); + + cli_opt_help_fprint(stdout, opts); +} + +static char *compute_local_path(const char *orig_path) +{ + const char *slash; + char *local_path; + + if ((slash = strrchr(orig_path, '/')) == NULL && + (slash = strrchr(orig_path, '\\')) == NULL) + local_path = git__strdup(orig_path); + else + local_path = git__strdup(slash + 1); + + return local_path; +} + +static bool validate_local_path(const char *path) +{ + if (!git_fs_path_exists(path)) + return false; + + if (!git_fs_path_isdir(path) || !git_fs_path_is_empty_dir(path)) { + fprintf(stderr, "fatal: destination path '%s' already exists and is not an empty directory.\n", + path); + exit(128); + } + + return true; +} + +static void cleanup(void) +{ + int rmdir_flags = GIT_RMDIR_REMOVE_FILES; + + cli_progress_abort(&progress); + + if (local_path_exists) + rmdir_flags |= GIT_RMDIR_SKIP_ROOT; + + if (!git_fs_path_isdir(local_path)) + return; + + git_futils_rmdir_r(local_path, NULL, rmdir_flags); +} + +static void interrupt_cleanup(void) +{ + cleanup(); + exit(130); +} + +int cmd_clone(int argc, char **argv) +{ + git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT; + git_repository *repo = NULL; + cli_opt invalid_opt; + char *computed_path = NULL; + int ret = 0; + + if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU)) + return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt); + + if (show_help) { + print_help(); + return 0; + } + + if (!remote_path) { + ret = cli_error_usage("you must specify a repository to clone"); + goto done; + } + + if (bare) + clone_opts.bare = 1; + + if (branch) + clone_opts.checkout_branch = branch; + + if (!checkout) + clone_opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE; + + if (!local_path) + local_path = computed_path = compute_local_path(remote_path); + + local_path_exists = validate_local_path(local_path); + + cli_sighandler_set_interrupt(interrupt_cleanup); + + if (!local_path_exists && + git_futils_mkdir(local_path, 0777, 0) < 0) { + ret = cli_error_git(); + goto done; + } + + if (!quiet) { + clone_opts.fetch_opts.callbacks.sideband_progress = cli_progress_fetch_sideband; + clone_opts.fetch_opts.callbacks.transfer_progress = cli_progress_fetch_transfer; + clone_opts.fetch_opts.callbacks.payload = &progress; + + clone_opts.checkout_opts.progress_cb = cli_progress_checkout; + clone_opts.checkout_opts.progress_payload = &progress; + + printf("Cloning into '%s'...\n", local_path); + } + + if (git_clone(&repo, remote_path, local_path, &clone_opts) < 0) { + cleanup(); + ret = cli_error_git(); + goto done; + } + + cli_progress_finish(&progress); + +done: + cli_progress_dispose(&progress); + git__free(computed_path); + git_repository_free(repo); + return ret; +} diff --git a/src/cli/cmd_hash_object.c b/src/cli/cmd_hash_object.c new file mode 100644 index 000000000..5cfe9146a --- /dev/null +++ b/src/cli/cmd_hash_object.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include +#include "cli.h" +#include "cmd.h" + +#include "futils.h" + +#define COMMAND_NAME "hash-object" + +static int show_help; +static char *type_name; +static int write_object, read_stdin, literally; +static char **filenames; + +static const cli_opt_spec opts[] = { + { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1, + CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL, + "display help about the " COMMAND_NAME " command" }, + + { CLI_OPT_TYPE_VALUE, NULL, 't', &type_name, 0, + CLI_OPT_USAGE_DEFAULT, "type", "the type of object to hash (default: \"blob\")" }, + { CLI_OPT_TYPE_SWITCH, NULL, 'w', &write_object, 1, + CLI_OPT_USAGE_DEFAULT, NULL, "write the object to the object database" }, + { CLI_OPT_TYPE_SWITCH, "literally", 0, &literally, 1, + CLI_OPT_USAGE_DEFAULT, NULL, "do not validate the object contents" }, + { CLI_OPT_TYPE_SWITCH, "stdin", 0, &read_stdin, 1, + CLI_OPT_USAGE_REQUIRED, NULL, "read content from stdin" }, + { CLI_OPT_TYPE_ARGS, "file", 0, &filenames, 0, + CLI_OPT_USAGE_CHOICE, "file", "the file (or files) to read and hash" }, + { 0 }, +}; + +static void print_help(void) +{ + cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts); + printf("\n"); + + printf("Compute the object ID for a given file and optionally write that file\nto the object database.\n"); + printf("\n"); + + printf("Options:\n"); + + cli_opt_help_fprint(stdout, opts); +} + +static int hash_buf(git_odb *odb, git_str *buf, git_object_t type) +{ + git_oid oid; + + if (!literally) { + int valid = 0; + + if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, type) < 0 || !valid) + return cli_error_git(); + } + + if (write_object) { + if (git_odb_write(&oid, odb, buf->ptr, buf->size, type) < 0) + return cli_error_git(); + } else { + if (git_odb_hash(&oid, buf->ptr, buf->size, type) < 0) + return cli_error_git(); + } + + if (printf("%s\n", git_oid_tostr_s(&oid)) < 0) + return cli_error_os(); + + return 0; +} + +int cmd_hash_object(int argc, char **argv) +{ + git_repository *repo = NULL; + git_odb *odb = NULL; + git_str buf = GIT_STR_INIT; + cli_opt invalid_opt; + git_object_t type = GIT_OBJECT_BLOB; + char **filename; + int ret = 0; + + if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU)) + return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt); + + if (show_help) { + print_help(); + return 0; + } + + if (type_name && (type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID) + return cli_error_usage("invalid object type '%s'", type_name); + + if (write_object && + (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0 || + git_repository_odb(&odb, repo) < 0)) { + ret = cli_error_git(); + goto done; + } + + /* + * TODO: we're reading blobs, we shouldn't pull them all into main + * memory, we should just stream them into the odb instead. + * (Or create a `git_odb_writefile` API.) + */ + if (read_stdin) { + if (git_futils_readbuffer_fd_full(&buf, fileno(stdin)) < 0) { + ret = cli_error_git(); + goto done; + } + + if ((ret = hash_buf(odb, &buf, type)) != 0) + goto done; + } else { + for (filename = filenames; *filename; filename++) { + if (git_futils_readbuffer(&buf, *filename) < 0) { + ret = cli_error_git(); + goto done; + } + + if ((ret = hash_buf(odb, &buf, type)) != 0) + goto done; + } + } + +done: + git_str_dispose(&buf); + git_odb_free(odb); + git_repository_free(repo); + return ret; +} diff --git a/src/cli/cmd_help.c b/src/cli/cmd_help.c new file mode 100644 index 000000000..7ee982242 --- /dev/null +++ b/src/cli/cmd_help.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include +#include +#include "cli.h" +#include "cmd.h" + +#define COMMAND_NAME "help" + +static char *command; +static int show_help; + +static const cli_opt_spec opts[] = { + { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1, + CLI_OPT_USAGE_HIDDEN, NULL, "display help about the help command" }, + { CLI_OPT_TYPE_ARG, "command", 0, &command, 0, + CLI_OPT_USAGE_DEFAULT, "command", "the command to show help for" }, + { 0 }, +}; + +static int print_help(void) +{ + cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts); + printf("\n"); + + printf("Display help information about %s. If a command is specified, help\n", PROGRAM_NAME); + printf("about that command will be shown. Otherwise, general information about\n"); + printf("%s will be shown, including the commands available.\n", PROGRAM_NAME); + + return 0; +} + +static int print_commands(void) +{ + const cli_cmd_spec *cmd; + + cli_opt_usage_fprint(stdout, PROGRAM_NAME, NULL, cli_common_opts); + printf("\n"); + + printf("These are the %s commands available:\n\n", PROGRAM_NAME); + + for (cmd = cli_cmds; cmd->name; cmd++) + printf(" %-11s %s\n", cmd->name, cmd->desc); + + printf("\nSee '%s help ' for more information on a specific command.\n", PROGRAM_NAME); + + return 0; +} + +int cmd_help(int argc, char **argv) +{ + char *fake_args[2]; + const cli_cmd_spec *cmd; + cli_opt invalid_opt; + + if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU)) + return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt); + + /* Show the meta-help */ + if (show_help) + return print_help(); + + /* We were not asked to show help for a specific command. */ + if (!command) + return print_commands(); + + /* + * If we were asked for help for a command (eg, `help `), + * delegate back to that command's `--help` option. This lets + * commands own their help. Emulate the command-line arguments + * that would invoke ` --help` and invoke that command. + */ + fake_args[0] = command; + fake_args[1] = "--help"; + + if ((cmd = cli_cmd_spec_byname(command)) == NULL) + return cli_error("'%s' is not a %s command. See '%s help'.", + command, PROGRAM_NAME, PROGRAM_NAME); + + return cmd->fn(2, fake_args); +} diff --git a/src/cli/error.h b/src/cli/error.h new file mode 100644 index 000000000..cce7a54c0 --- /dev/null +++ b/src/cli/error.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef CLI_error_h__ +#define CLI_error_h__ + +#include "cli.h" +#include + +#define CLI_EXIT_OK 0 +#define CLI_EXIT_ERROR 1 +#define CLI_EXIT_OS 128 +#define CLI_EXIT_GIT 128 +#define CLI_EXIT_USAGE 129 + +#define cli_error__print(fmt) do { \ + va_list ap; \ + va_start(ap, fmt); \ + fprintf(stderr, "%s: ", PROGRAM_NAME); \ + vfprintf(stderr, fmt, ap); \ + fprintf(stderr, "\n"); \ + va_end(ap); \ + } while(0) + +GIT_INLINE(int) cli_error(const char *fmt, ...) +{ + cli_error__print(fmt); + return CLI_EXIT_ERROR; +} + +GIT_INLINE(int) cli_error_usage(const char *fmt, ...) +{ + cli_error__print(fmt); + return CLI_EXIT_USAGE; +} + +GIT_INLINE(int) cli_error_git(void) +{ + const git_error *err = git_error_last(); + fprintf(stderr, "%s: %s\n", PROGRAM_NAME, + err ? err->message : "unknown error"); + return CLI_EXIT_GIT; +} + +#define cli_error_os() (perror(PROGRAM_NAME), CLI_EXIT_OS) + +#endif /* CLI_error_h__ */ diff --git a/src/cli/main.c b/src/cli/main.c new file mode 100644 index 000000000..cbfc50eec --- /dev/null +++ b/src/cli/main.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include +#include +#include "cli.h" +#include "cmd.h" + +static int show_help = 0; +static int show_version = 0; +static char *command = NULL; +static char **args = NULL; + +const cli_opt_spec cli_common_opts[] = { + { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1, + CLI_OPT_USAGE_DEFAULT, NULL, "display help information" }, + { CLI_OPT_TYPE_SWITCH, "version", 0, &show_version, 1, + CLI_OPT_USAGE_DEFAULT, NULL, "display the version" }, + { CLI_OPT_TYPE_ARG, "command", 0, &command, 0, + CLI_OPT_USAGE_REQUIRED, "command", "the command to run" }, + { CLI_OPT_TYPE_ARGS, "args", 0, &args, 0, + CLI_OPT_USAGE_DEFAULT, "args", "arguments for the command" }, + { 0 } +}; + +const cli_cmd_spec cli_cmds[] = { + { "cat-file", cmd_cat_file, "Display an object in the repository" }, + { "clone", cmd_clone, "Clone a repository into a new directory" }, + { "hash-object", cmd_hash_object, "Hash a raw object and product its object ID" }, + { "help", cmd_help, "Display help information" }, + { NULL } +}; + +int main(int argc, char **argv) +{ + const cli_cmd_spec *cmd; + cli_opt_parser optparser; + cli_opt opt; + char *help_args[3] = { NULL }; + int help_args_len; + int args_len = 0; + int ret = 0; + + if (git_libgit2_init() < 0) { + cli_error("failed to initialize libgit2"); + exit(CLI_EXIT_GIT); + } + + cli_opt_parser_init(&optparser, cli_common_opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU); + + /* Parse the top-level (common) options and command information */ + while (cli_opt_parser_next(&opt, &optparser)) { + if (!opt.spec) { + cli_opt_status_fprint(stderr, PROGRAM_NAME, &opt); + cli_opt_usage_fprint(stderr, PROGRAM_NAME, NULL, cli_common_opts); + ret = CLI_EXIT_USAGE; + goto done; + } + + /* + * When we see a command, stop parsing and capture the + * remaining arguments as args for the command itself. + */ + if (command) { + args = &argv[optparser.idx]; + args_len = (int)(argc - optparser.idx); + break; + } + } + + if (show_version) { + printf("%s version %s\n", PROGRAM_NAME, LIBGIT2_VERSION); + goto done; + } + + /* + * If `--help ` is specified, delegate to that command's + * `--help` option. If no command is specified, run the `help` + * command. Do this by updating the args to emulate that behavior. + */ + if (!command || show_help) { + help_args[0] = command ? (char *)command : "help"; + help_args[1] = command ? "--help" : NULL; + help_args_len = command ? 2 : 1; + + command = help_args[0]; + args = help_args; + args_len = help_args_len; + } + + if ((cmd = cli_cmd_spec_byname(command)) == NULL) { + ret = cli_error("'%s' is not a %s command. See '%s help'.", + command, PROGRAM_NAME, PROGRAM_NAME); + goto done; + } + + ret = cmd->fn(args_len, args); + +done: + git_libgit2_shutdown(); + return ret; +} diff --git a/src/cli/opt.c b/src/cli/opt.c new file mode 100644 index 000000000..72df5877f --- /dev/null +++ b/src/cli/opt.c @@ -0,0 +1,669 @@ +/* + * Copyright (c), Edward Thomson + * All rights reserved. + * + * This file is part of adopt, distributed under the MIT license. + * For full terms and conditions, see the included LICENSE file. + * + * THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT. + * + * This file was produced by using the `rename.pl` script included with + * adopt. The command-line specified was: + * + * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage + */ + +#include +#include +#include +#include +#include + +#include "cli.h" +#include "opt.h" + +#ifdef _WIN32 +# include +#else +# include +# include +#endif + +#ifdef _MSC_VER +# define alloca _alloca +#endif + +#define spec_is_option_type(x) \ + ((x)->type == CLI_OPT_TYPE_BOOL || \ + (x)->type == CLI_OPT_TYPE_SWITCH || \ + (x)->type == CLI_OPT_TYPE_VALUE) + +GIT_INLINE(const cli_opt_spec *) spec_for_long( + int *is_negated, + int *has_value, + const char **value, + const cli_opt_parser *parser, + const char *arg) +{ + const cli_opt_spec *spec; + char *eql; + size_t eql_pos; + + eql = strchr(arg, '='); + eql_pos = (eql = strchr(arg, '=')) ? (size_t)(eql - arg) : strlen(arg); + + for (spec = parser->specs; spec->type; ++spec) { + /* Handle -- (everything after this is literal) */ + if (spec->type == CLI_OPT_TYPE_LITERAL && arg[0] == '\0') + return spec; + + /* Handle --no-option arguments for bool types */ + if (spec->type == CLI_OPT_TYPE_BOOL && + strncmp(arg, "no-", 3) == 0 && + strcmp(arg + 3, spec->name) == 0) { + *is_negated = 1; + return spec; + } + + /* Handle the typical --option arguments */ + if (spec_is_option_type(spec) && + spec->name && + strcmp(arg, spec->name) == 0) + return spec; + + /* Handle --option=value arguments */ + if (spec->type == CLI_OPT_TYPE_VALUE && + eql && + strncmp(arg, spec->name, eql_pos) == 0 && + spec->name[eql_pos] == '\0') { + *has_value = 1; + *value = arg[eql_pos + 1] ? &arg[eql_pos + 1] : NULL; + return spec; + } + } + + return NULL; +} + +GIT_INLINE(const cli_opt_spec *) spec_for_short( + const char **value, + const cli_opt_parser *parser, + const char *arg) +{ + const cli_opt_spec *spec; + + for (spec = parser->specs; spec->type; ++spec) { + /* Handle -svalue short options with a value */ + if (spec->type == CLI_OPT_TYPE_VALUE && + arg[0] == spec->alias && + arg[1] != '\0') { + *value = &arg[1]; + return spec; + } + + /* Handle typical -s short options */ + if (arg[0] == spec->alias) { + *value = NULL; + return spec; + } + } + + return NULL; +} + +GIT_INLINE(const cli_opt_spec *) spec_for_arg(cli_opt_parser *parser) +{ + const cli_opt_spec *spec; + size_t args = 0; + + for (spec = parser->specs; spec->type; ++spec) { + if (spec->type == CLI_OPT_TYPE_ARG) { + if (args == parser->arg_idx) { + parser->arg_idx++; + return spec; + } + + args++; + } + + if (spec->type == CLI_OPT_TYPE_ARGS && args == parser->arg_idx) + return spec; + } + + return NULL; +} + +GIT_INLINE(int) spec_is_choice(const cli_opt_spec *spec) +{ + return ((spec + 1)->type && + ((spec + 1)->usage & CLI_OPT_USAGE_CHOICE)); +} + +/* + * If we have a choice with switches and bare arguments, and we see + * the switch, then we no longer expect the bare argument. + */ +GIT_INLINE(void) consume_choices(const cli_opt_spec *spec, cli_opt_parser *parser) +{ + /* back up to the beginning of the choices */ + while (spec->type && (spec->usage & CLI_OPT_USAGE_CHOICE)) + --spec; + + if (!spec_is_choice(spec)) + return; + + do { + if (spec->type == CLI_OPT_TYPE_ARG) + parser->arg_idx++; + ++spec; + } while(spec->type && (spec->usage & CLI_OPT_USAGE_CHOICE)); +} + +static cli_opt_status_t parse_long(cli_opt *opt, cli_opt_parser *parser) +{ + const cli_opt_spec *spec; + char *arg = parser->args[parser->idx++]; + const char *value = NULL; + int is_negated = 0, has_value = 0; + + opt->arg = arg; + + if ((spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2])) == NULL) { + opt->spec = NULL; + opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION; + goto done; + } + + opt->spec = spec; + + /* Future options parsed as literal */ + if (spec->type == CLI_OPT_TYPE_LITERAL) + parser->in_literal = 1; + + /* --bool or --no-bool */ + else if (spec->type == CLI_OPT_TYPE_BOOL && spec->value) + *((int *)spec->value) = !is_negated; + + /* --accumulate */ + else if (spec->type == CLI_OPT_TYPE_ACCUMULATOR && spec->value) + *((int *)spec->value) += spec->switch_value ? spec->switch_value : 1; + + /* --switch */ + else if (spec->type == CLI_OPT_TYPE_SWITCH && spec->value) + *((int *)spec->value) = spec->switch_value; + + /* Parse values as "--foo=bar" or "--foo bar" */ + else if (spec->type == CLI_OPT_TYPE_VALUE) { + if (has_value) + opt->value = (char *)value; + else if ((parser->idx + 1) <= parser->args_len) + opt->value = parser->args[parser->idx++]; + + if (spec->value) + *((char **)spec->value) = opt->value; + } + + /* Required argument was not provided */ + if (spec->type == CLI_OPT_TYPE_VALUE && + !opt->value && + !(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL)) + opt->status = CLI_OPT_STATUS_MISSING_VALUE; + else + opt->status = CLI_OPT_STATUS_OK; + + consume_choices(opt->spec, parser); + +done: + return opt->status; +} + +static cli_opt_status_t parse_short(cli_opt *opt, cli_opt_parser *parser) +{ + const cli_opt_spec *spec; + char *arg = parser->args[parser->idx++]; + const char *value; + + opt->arg = arg; + + if ((spec = spec_for_short(&value, parser, &arg[1 + parser->in_short])) == NULL) { + opt->spec = NULL; + opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION; + goto done; + } + + opt->spec = spec; + + if (spec->type == CLI_OPT_TYPE_BOOL && spec->value) + *((int *)spec->value) = 1; + + else if (spec->type == CLI_OPT_TYPE_ACCUMULATOR && spec->value) + *((int *)spec->value) += spec->switch_value ? spec->switch_value : 1; + + else if (spec->type == CLI_OPT_TYPE_SWITCH && spec->value) + *((int *)spec->value) = spec->switch_value; + + /* Parse values as "-ifoo" or "-i foo" */ + else if (spec->type == CLI_OPT_TYPE_VALUE) { + if (value) + opt->value = (char *)value; + else if ((parser->idx + 1) <= parser->args_len) + opt->value = parser->args[parser->idx++]; + + if (spec->value) + *((char **)spec->value) = opt->value; + } + + /* + * Handle compressed short arguments, like "-fbcd"; see if there's + * another character after the one we processed. If not, advance + * the parser index. + */ + if (spec->type != CLI_OPT_TYPE_VALUE && arg[2 + parser->in_short] != '\0') { + parser->in_short++; + parser->idx--; + } else { + parser->in_short = 0; + } + + /* Required argument was not provided */ + if (spec->type == CLI_OPT_TYPE_VALUE && !opt->value) + opt->status = CLI_OPT_STATUS_MISSING_VALUE; + else + opt->status = CLI_OPT_STATUS_OK; + + consume_choices(opt->spec, parser); + +done: + return opt->status; +} + +static cli_opt_status_t parse_arg(cli_opt *opt, cli_opt_parser *parser) +{ + const cli_opt_spec *spec = spec_for_arg(parser); + + opt->spec = spec; + opt->arg = parser->args[parser->idx]; + + if (!spec) { + parser->idx++; + opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION; + } else if (spec->type == CLI_OPT_TYPE_ARGS) { + if (spec->value) + *((char ***)spec->value) = &parser->args[parser->idx]; + + /* + * We have started a list of arguments; the remainder of + * given arguments need not be examined. + */ + parser->in_args = (parser->args_len - parser->idx); + parser->idx = parser->args_len; + opt->args_len = parser->in_args; + opt->status = CLI_OPT_STATUS_OK; + } else { + if (spec->value) + *((char **)spec->value) = parser->args[parser->idx]; + + parser->idx++; + opt->status = CLI_OPT_STATUS_OK; + } + + return opt->status; +} + +static int support_gnu_style(unsigned int flags) +{ + if ((flags & CLI_OPT_PARSE_FORCE_GNU) != 0) + return 1; + + if ((flags & CLI_OPT_PARSE_GNU) == 0) + return 0; + + /* TODO: Windows */ +#if defined(_WIN32) && defined(UNICODE) + if (_wgetenv(L"POSIXLY_CORRECT") != NULL) + return 0; +#else + if (getenv("POSIXLY_CORRECT") != NULL) + return 0; +#endif + + return 1; +} + +void cli_opt_parser_init( + cli_opt_parser *parser, + const cli_opt_spec specs[], + char **args, + size_t args_len, + unsigned int flags) +{ + assert(parser); + + memset(parser, 0x0, sizeof(cli_opt_parser)); + + parser->specs = specs; + parser->args = args; + parser->args_len = args_len; + parser->flags = flags; + + parser->needs_sort = support_gnu_style(flags); +} + +GIT_INLINE(const cli_opt_spec *) spec_for_sort( + int *needs_value, + const cli_opt_parser *parser, + const char *arg) +{ + int is_negated, has_value = 0; + const char *value; + const cli_opt_spec *spec = NULL; + size_t idx = 0; + + *needs_value = 0; + + if (strncmp(arg, "--", 2) == 0) { + spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2]); + *needs_value = !has_value; + } + + else if (strncmp(arg, "-", 1) == 0) { + spec = spec_for_short(&value, parser, &arg[1]); + + /* + * Advance through compressed short arguments to see if + * the last one has a value, eg "-xvffilename". + */ + while (spec && !value && arg[1 + ++idx] != '\0') + spec = spec_for_short(&value, parser, &arg[1 + idx]); + + *needs_value = (value == NULL); + } + + return spec; +} + +/* + * Some parsers allow for handling arguments like "file1 --help file2"; + * this is done by re-sorting the arguments in-place; emulate that. + */ +static int sort_gnu_style(cli_opt_parser *parser) +{ + size_t i, j, insert_idx = parser->idx, offset; + const cli_opt_spec *spec; + char *option, *value; + int needs_value, changed = 0; + + parser->needs_sort = 0; + + for (i = parser->idx; i < parser->args_len; i++) { + spec = spec_for_sort(&needs_value, parser, parser->args[i]); + + /* Not a "-" or "--" prefixed option. No change. */ + if (!spec) + continue; + + /* A "--" alone means remaining args are literal. */ + if (spec->type == CLI_OPT_TYPE_LITERAL) + break; + + option = parser->args[i]; + + /* + * If the argument is a value type and doesn't already + * have a value (eg "--foo=bar" or "-fbar") then we need + * to copy the next argument as its value. + */ + if (spec->type == CLI_OPT_TYPE_VALUE && needs_value) { + /* + * A required value is not provided; set parser + * index to this value so that we fail on it. + */ + if (i + 1 >= parser->args_len) { + parser->idx = i; + return 1; + } + + value = parser->args[i + 1]; + offset = 1; + } else { + value = NULL; + offset = 0; + } + + /* Caller error if args[0] is an option. */ + if (i == 0) + return 0; + + /* Shift args up one (or two) and insert the option */ + for (j = i; j > insert_idx; j--) + parser->args[j + offset] = parser->args[j - 1]; + + parser->args[insert_idx] = option; + + if (value) + parser->args[insert_idx + 1] = value; + + insert_idx += (1 + offset); + i += offset; + + changed = 1; + } + + return changed; +} + +cli_opt_status_t cli_opt_parser_next(cli_opt *opt, cli_opt_parser *parser) +{ + assert(opt && parser); + + memset(opt, 0x0, sizeof(cli_opt)); + + if (parser->idx >= parser->args_len) { + opt->args_len = parser->in_args; + return CLI_OPT_STATUS_DONE; + } + + /* Handle options in long form, those beginning with "--" */ + if (strncmp(parser->args[parser->idx], "--", 2) == 0 && + !parser->in_short && + !parser->in_literal) + return parse_long(opt, parser); + + /* Handle options in short form, those beginning with "-" */ + else if (parser->in_short || + (strncmp(parser->args[parser->idx], "-", 1) == 0 && + !parser->in_literal)) + return parse_short(opt, parser); + + /* + * We've reached the first "bare" argument. In POSIX mode, all + * remaining items on the command line are arguments. In GNU + * mode, there may be long or short options after this. Sort any + * options up to this position then re-parse the current position. + */ + if (parser->needs_sort && sort_gnu_style(parser)) + return cli_opt_parser_next(opt, parser); + + return parse_arg(opt, parser); +} + +GIT_INLINE(int) spec_included(const cli_opt_spec **specs, const cli_opt_spec *spec) +{ + const cli_opt_spec **i; + + for (i = specs; *i; ++i) { + if (spec == *i) + return 1; + } + + return 0; +} + +static cli_opt_status_t validate_required( + cli_opt *opt, + const cli_opt_spec specs[], + const cli_opt_spec **given_specs) +{ + const cli_opt_spec *spec, *required; + int given; + + /* + * Iterate over the possible specs to identify requirements and + * ensure that those have been given on the command-line. + * Note that we can have required *choices*, where one in a + * list of choices must be specified. + */ + for (spec = specs, required = NULL, given = 0; spec->type; ++spec) { + if (!required && (spec->usage & CLI_OPT_USAGE_REQUIRED)) { + required = spec; + given = 0; + } else if (!required) { + continue; + } + + if (!given) + given = spec_included(given_specs, spec); + + /* + * Validate the requirement unless we're in a required + * choice. In that case, keep the required state and + * validate at the end of the choice list. + */ + if (!spec_is_choice(spec)) { + if (!given) { + opt->spec = required; + opt->status = CLI_OPT_STATUS_MISSING_ARGUMENT; + break; + } + + required = NULL; + given = 0; + } + } + + return opt->status; +} + +cli_opt_status_t cli_opt_parse( + cli_opt *opt, + const cli_opt_spec specs[], + char **args, + size_t args_len, + unsigned int flags) +{ + cli_opt_parser parser; + const cli_opt_spec **given_specs; + size_t given_idx = 0; + + cli_opt_parser_init(&parser, specs, args, args_len, flags); + + given_specs = alloca(sizeof(const cli_opt_spec *) * (args_len + 1)); + + while (cli_opt_parser_next(opt, &parser)) { + if (opt->status != CLI_OPT_STATUS_OK && + opt->status != CLI_OPT_STATUS_DONE) + return opt->status; + + if ((opt->spec->usage & CLI_OPT_USAGE_STOP_PARSING)) + return (opt->status = CLI_OPT_STATUS_DONE); + + given_specs[given_idx++] = opt->spec; + } + + given_specs[given_idx] = NULL; + + return validate_required(opt, specs, given_specs); +} + +static int spec_name_fprint(FILE *file, const cli_opt_spec *spec) +{ + int error; + + if (spec->type == CLI_OPT_TYPE_ARG) + error = fprintf(file, "%s", spec->value_name); + else if (spec->type == CLI_OPT_TYPE_ARGS) + error = fprintf(file, "%s", spec->value_name); + else if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG)) + error = fprintf(file, "-%c", spec->alias); + else + error = fprintf(file, "--%s", spec->name); + + return error; +} + +int cli_opt_status_fprint( + FILE *file, + const char *command, + const cli_opt *opt) +{ + const cli_opt_spec *choice; + int error; + + if (command && (error = fprintf(file, "%s: ", command)) < 0) + return error; + + switch (opt->status) { + case CLI_OPT_STATUS_DONE: + error = fprintf(file, "finished processing arguments (no error)\n"); + break; + case CLI_OPT_STATUS_OK: + error = fprintf(file, "no error\n"); + break; + case CLI_OPT_STATUS_UNKNOWN_OPTION: + error = fprintf(file, "unknown option: %s\n", opt->arg); + break; + case CLI_OPT_STATUS_MISSING_VALUE: + if ((error = fprintf(file, "argument '")) < 0 || + (error = spec_name_fprint(file, opt->spec)) < 0 || + (error = fprintf(file, "' requires a value.\n")) < 0) + break; + break; + case CLI_OPT_STATUS_MISSING_ARGUMENT: + if (spec_is_choice(opt->spec)) { + int is_choice = 1; + + if (spec_is_choice((opt->spec)+1)) + error = fprintf(file, "one of"); + else + error = fprintf(file, "either"); + + if (error < 0) + break; + + for (choice = opt->spec; is_choice; ++choice) { + is_choice = spec_is_choice(choice); + + if (!is_choice) + error = fprintf(file, " or"); + else if (choice != opt->spec) + error = fprintf(file, ","); + + if ((error < 0) || + (error = fprintf(file, " '")) < 0 || + (error = spec_name_fprint(file, choice)) < 0 || + (error = fprintf(file, "'")) < 0) + break; + + if (!spec_is_choice(choice)) + break; + } + + if ((error < 0) || + (error = fprintf(file, " is required.\n")) < 0) + break; + } else { + if ((error = fprintf(file, "argument '")) < 0 || + (error = spec_name_fprint(file, opt->spec)) < 0 || + (error = fprintf(file, "' is required.\n")) < 0) + break; + } + + break; + default: + error = fprintf(file, "unknown status: %d\n", opt->status); + break; + } + + return error; +} + diff --git a/src/cli/opt.h b/src/cli/opt.h new file mode 100644 index 000000000..6c1d4603e --- /dev/null +++ b/src/cli/opt.h @@ -0,0 +1,349 @@ +/* + * Copyright (c), Edward Thomson + * All rights reserved. + * + * This file is part of adopt, distributed under the MIT license. + * For full terms and conditions, see the included LICENSE file. + * + * THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT. + * + * This file was produced by using the `rename.pl` script included with + * adopt. The command-line specified was: + * + * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage + */ + +#ifndef CLI_opt_h__ +#define CLI_opt_h__ + +#include +#include + +/** + * The type of argument to be parsed. + */ +typedef enum { + CLI_OPT_TYPE_NONE = 0, + + /** + * An option that, when specified, sets a given value to true. + * This is useful for options like "--debug". A negation + * option (beginning with "no-") is implicitly specified; for + * example "--no-debug". The `value` pointer in the returned + * option will be set to `1` when this is specified, and set to + * `0` when the negation "no-" option is specified. + */ + CLI_OPT_TYPE_BOOL, + + /** + * An option that, when specified, sets the given `value` pointer + * to the specified `switch_value`. This is useful for booleans + * where you do not want the implicit negation that comes with an + * `CLI_OPT_TYPE_BOOL`, or for switches that multiplex a value, like + * setting a mode. For example, `--read` may set the `value` to + * `MODE_READ` and `--write` may set the `value` to `MODE_WRITE`. + */ + CLI_OPT_TYPE_SWITCH, + + /** + * An option that, when specified, increments the given + * `value` by the given `switch_value`. This can be specified + * multiple times to continue to increment the `value`. + * (For example, "-vvv" to set verbosity to 3.) + */ + CLI_OPT_TYPE_ACCUMULATOR, + + /** + * An option that takes a value, for example `-n value`, + * `-nvalue`, `--name value` or `--name=value`. + */ + CLI_OPT_TYPE_VALUE, + + /** + * A bare "--" that indicates that arguments following this are + * literal. This allows callers to specify things that might + * otherwise look like options, for example to operate on a file + * named "-rf" then you can invoke "program -- -rf" to treat + * "-rf" as an argument not an option. + */ + CLI_OPT_TYPE_LITERAL, + + /** + * A single argument, not an option. When options are exhausted, + * arguments will be matches in the order that they're specified + * in the spec list. For example, if two `CLI_OPT_TYPE_ARGS` are + * specified, `input_file` and `output_file`, then the first bare + * argument on the command line will be `input_file` and the + * second will be `output_file`. + */ + CLI_OPT_TYPE_ARG, + + /** + * A collection of arguments. This is useful when you want to take + * a list of arguments, for example, multiple paths. When specified, + * the value will be set to the first argument in the list. + */ + CLI_OPT_TYPE_ARGS, +} cli_opt_type_t; + +/** + * Additional information about an option, including parsing + * restrictions and usage information to be displayed to the end-user. + */ +typedef enum { + /** Defaults for the argument. */ + CLI_OPT_USAGE_DEFAULT = 0, + + /** This argument is required. */ + CLI_OPT_USAGE_REQUIRED = (1u << 0), + + /** + * This is a multiple choice argument, combined with the previous + * argument. For example, when the previous argument is `-f` and + * this optional is applied to an argument of type `-b` then one + * of `-f` or `-b` may be specified. + */ + CLI_OPT_USAGE_CHOICE = (1u << 1), + + /** + * This argument short-circuits the remainder of parsing. + * Useful for arguments like `--help`. + */ + CLI_OPT_USAGE_STOP_PARSING = (1u << 2), + + /** The argument's value is optional ("-n" or "-n foo") */ + CLI_OPT_USAGE_VALUE_OPTIONAL = (1u << 3), + + /** This argument should not be displayed in usage. */ + CLI_OPT_USAGE_HIDDEN = (1u << 4), + + /** In usage, show the long format instead of the abbreviated format. */ + CLI_OPT_USAGE_SHOW_LONG = (1u << 5), +} cli_opt_usage_t; + +typedef enum { + /** Default parsing behavior. */ + CLI_OPT_PARSE_DEFAULT = 0, + + /** + * Parse with GNU `getopt_long` style behavior, where options can + * be intermixed with arguments at any position (for example, + * "file1 --help file2".) Like `getopt_long`, this can mutate the + * arguments given. + */ + CLI_OPT_PARSE_GNU = (1u << 0), + + /** + * Force GNU `getopt_long` style behavior; the `POSIXLY_CORRECT` + * environment variable is ignored. + */ + CLI_OPT_PARSE_FORCE_GNU = (1u << 1), +} cli_opt_flag_t; + +/** Specification for an available option. */ +typedef struct cli_opt_spec { + /** Type of option expected. */ + cli_opt_type_t type; + + /** Name of the long option. */ + const char *name; + + /** The alias is the short (one-character) option alias. */ + const char alias; + + /** + * If this spec is of type `CLI_OPT_TYPE_BOOL`, this is a pointer + * to an `int` that will be set to `1` if the option is specified. + * + * If this spec is of type `CLI_OPT_TYPE_SWITCH`, this is a pointer + * to an `int` that will be set to the opt's `switch_value` (below) + * when this option is specified. + * + * If this spec is of type `CLI_OPT_TYPE_ACCUMULATOR`, this is a + * pointer to an `int` that will be incremented by the opt's + * `switch_value` (below). If no `switch_value` is provided then + * the value will be incremented by 1. + * + * If this spec is of type `CLI_OPT_TYPE_VALUE`, + * `CLI_OPT_TYPE_VALUE_OPTIONAL`, or `CLI_OPT_TYPE_ARG`, this is + * a pointer to a `char *` that will be set to the value + * specified on the command line. + * + * If this spec is of type `CLI_OPT_TYPE_ARGS`, this is a pointer + * to a `char **` that will be set to the remaining values + * specified on the command line. + */ + void *value; + + /** + * If this spec is of type `CLI_OPT_TYPE_SWITCH`, this is the value + * to set in the option's `value` pointer when it is specified. If + * this spec is of type `CLI_OPT_TYPE_ACCUMULATOR`, this is the value + * to increment in the option's `value` pointer when it is + * specified. This is ignored for other opt types. + */ + int switch_value; + + /** + * Optional usage flags that change parsing behavior and how + * usage information is shown to the end-user. + */ + uint32_t usage; + + /** + * The name of the value, provided when creating usage information. + * This is required only for the functions that display usage + * information and only when a spec is of type `CLI_OPT_TYPE_VALUE, + * `CLI_OPT_TYPE_ARG` or `CLI_OPT_TYPE_ARGS``. + */ + const char *value_name; + + /** + * Optional short description of the option to display to the + * end-user. This is only used when creating usage information. + */ + const char *help; +} cli_opt_spec; + +/** Return value for `cli_opt_parser_next`. */ +typedef enum { + /** Parsing is complete; there are no more arguments. */ + CLI_OPT_STATUS_DONE = 0, + + /** + * This argument was parsed correctly; the `opt` structure is + * populated and the value pointer has been set. + */ + CLI_OPT_STATUS_OK = 1, + + /** + * The argument could not be parsed correctly, it does not match + * any of the specifications provided. + */ + CLI_OPT_STATUS_UNKNOWN_OPTION = 2, + + /** + * The argument matched a spec of type `CLI_OPT_VALUE`, but no value + * was provided. + */ + CLI_OPT_STATUS_MISSING_VALUE = 3, + + /** A required argument was not provided. */ + CLI_OPT_STATUS_MISSING_ARGUMENT = 4, +} cli_opt_status_t; + +/** An option provided on the command-line. */ +typedef struct cli_opt { + /** The status of parsing the most recent argument. */ + cli_opt_status_t status; + + /** + * The specification that was provided on the command-line, or + * `NULL` if the argument did not match an `cli_opt_spec`. + */ + const cli_opt_spec *spec; + + /** + * The argument as it was specified on the command-line, including + * dashes, eg, `-f` or `--foo`. + */ + char *arg; + + /** + * If the spec is of type `CLI_OPT_VALUE` or `CLI_OPT_VALUE_OPTIONAL`, + * this is the value provided to the argument. + */ + char *value; + + /** + * If the argument is of type `CLI_OPT_ARGS`, this is the number of + * arguments remaining. This value is persisted even when parsing + * is complete and `status` == `CLI_OPT_STATUS_DONE`. + */ + size_t args_len; +} cli_opt; + +/* The internal parser state. Callers should not modify this structure. */ +typedef struct cli_opt_parser { + const cli_opt_spec *specs; + char **args; + size_t args_len; + unsigned int flags; + + /* Parser state */ + size_t idx; + size_t arg_idx; + size_t in_args; + size_t in_short; + int needs_sort : 1, + in_literal : 1; +} cli_opt_parser; + +/** + * Parses all the command-line arguments and updates all the options using + * the pointers provided. Parsing stops on any invalid argument and + * information about the failure will be provided in the opt argument. + * + * This is the simplest way to parse options; it handles the initialization + * (`parser_init`) and looping (`parser_next`). + * + * @param opt The The `cli_opt` information that failed parsing + * @param specs A NULL-terminated array of `cli_opt_spec`s that can be parsed + * @param args The arguments that will be parsed + * @param args_len The length of arguments to be parsed + * @param flags The `cli_opt_flag_t flags for parsing + */ +cli_opt_status_t cli_opt_parse( + cli_opt *opt, + const cli_opt_spec specs[], + char **args, + size_t args_len, + unsigned int flags); + +/** + * Initializes a parser that parses the given arguments according to the + * given specifications. + * + * @param parser The `cli_opt_parser` that will be initialized + * @param specs A NULL-terminated array of `cli_opt_spec`s that can be parsed + * @param args The arguments that will be parsed + * @param args_len The length of arguments to be parsed + * @param flags The `cli_opt_flag_t flags for parsing + */ +void cli_opt_parser_init( + cli_opt_parser *parser, + const cli_opt_spec specs[], + char **args, + size_t args_len, + unsigned int flags); + +/** + * Parses the next command-line argument and places the information about + * the argument into the given `opt` data. + * + * @param opt The `cli_opt` information parsed from the argument + * @param parser An `cli_opt_parser` that has been initialized with + * `cli_opt_parser_init` + * @return true if the caller should continue iterating, or 0 if there are + * no arguments left to process. + */ +cli_opt_status_t cli_opt_parser_next( + cli_opt *opt, + cli_opt_parser *parser); + +/** + * Prints the status after parsing the most recent argument. This is + * useful for printing an error message when an unknown argument was + * specified, or when an argument was specified without a value. + * + * @param file The file to print information to + * @param command The name of the command to use when printing (optional) + * @param opt The option that failed to parse + * @return 0 on success, -1 on failure + */ +int cli_opt_status_fprint( + FILE *file, + const char *command, + const cli_opt *opt); + +#endif /* CLI_opt_h__ */ diff --git a/src/cli/opt_usage.c b/src/cli/opt_usage.c new file mode 100644 index 000000000..478b41631 --- /dev/null +++ b/src/cli/opt_usage.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "cli.h" +#include "str.h" + +static int print_spec_name(git_str *out, const cli_opt_spec *spec) +{ + if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias && + !(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL) && + !(spec->usage & CLI_OPT_USAGE_SHOW_LONG)) + return git_str_printf(out, "-%c <%s>", spec->alias, spec->value_name); + if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias && + !(spec->usage & CLI_OPT_USAGE_SHOW_LONG)) + return git_str_printf(out, "-%c [<%s>]", spec->alias, spec->value_name); + if (spec->type == CLI_OPT_TYPE_VALUE && + !(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL)) + return git_str_printf(out, "--%s[=<%s>]", spec->name, spec->value_name); + if (spec->type == CLI_OPT_TYPE_VALUE) + return git_str_printf(out, "--%s=<%s>", spec->name, spec->value_name); + if (spec->type == CLI_OPT_TYPE_ARG) + return git_str_printf(out, "<%s>", spec->value_name); + if (spec->type == CLI_OPT_TYPE_ARGS) + return git_str_printf(out, "<%s>...", spec->value_name); + if (spec->type == CLI_OPT_TYPE_LITERAL) + return git_str_printf(out, "--"); + if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG)) + return git_str_printf(out, "-%c", spec->alias); + if (spec->name) + return git_str_printf(out, "--%s", spec->name); + + GIT_ASSERT(0); +} + +/* + * This is similar to adopt's function, but modified to understand + * that we have a command ("git") and a "subcommand" ("checkout"). + * It also understands a terminal's line length and wrap appropriately, + * using a `git_str` for storage. + */ +int cli_opt_usage_fprint( + FILE *file, + const char *command, + const char *subcommand, + const cli_opt_spec specs[]) +{ + git_str usage = GIT_BUF_INIT, opt = GIT_BUF_INIT; + const cli_opt_spec *spec; + size_t i, prefixlen, linelen; + bool choice = false, next_choice = false, optional = false; + int error; + + /* TODO: query actual console width. */ + int console_width = 80; + + if ((error = git_str_printf(&usage, "usage: %s", command)) < 0) + goto done; + + if (subcommand && + (error = git_str_printf(&usage, " %s", subcommand)) < 0) + goto done; + + linelen = git_str_len(&usage); + prefixlen = linelen + 1; + + for (spec = specs; spec->type; ++spec) { + if (!choice) + optional = !(spec->usage & CLI_OPT_USAGE_REQUIRED); + + next_choice = !!((spec + 1)->usage & CLI_OPT_USAGE_CHOICE); + + if (spec->usage & CLI_OPT_USAGE_HIDDEN) + continue; + + if (choice) + git_str_putc(&opt, '|'); + else + git_str_clear(&opt); + + if (optional && !choice) + git_str_putc(&opt, '['); + if (!optional && !choice && next_choice) + git_str_putc(&opt, '('); + + if ((error = print_spec_name(&opt, spec)) < 0) + goto done; + + if (!optional && choice && !next_choice) + git_str_putc(&opt, ')'); + else if (optional && !next_choice) + git_str_putc(&opt, ']'); + + if ((choice = next_choice)) + continue; + + if (git_str_oom(&opt)) { + error = -1; + goto done; + } + + if (linelen > prefixlen && + console_width > 0 && + linelen + git_str_len(&opt) + 1 > (size_t)console_width) { + git_str_putc(&usage, '\n'); + + for (i = 0; i < prefixlen; i++) + git_str_putc(&usage, ' '); + + linelen = prefixlen; + } else { + git_str_putc(&usage, ' '); + linelen += git_str_len(&opt) + 1; + } + + git_str_puts(&usage, git_str_cstr(&opt)); + + if (git_str_oom(&usage)) { + error = -1; + goto done; + } + } + + error = fprintf(file, "%s\n", git_str_cstr(&usage)); + +done: + error = (error < 0) ? -1 : 0; + + git_str_dispose(&usage); + git_str_dispose(&opt); + return error; +} + +int cli_opt_usage_error( + const char *subcommand, + const cli_opt_spec specs[], + const cli_opt *invalid_opt) +{ + cli_opt_status_fprint(stderr, PROGRAM_NAME, invalid_opt); + cli_opt_usage_fprint(stderr, PROGRAM_NAME, subcommand, specs); + return CLI_EXIT_USAGE; +} + +int cli_opt_help_fprint( + FILE *file, + const cli_opt_spec specs[]) +{ + git_str help = GIT_BUF_INIT; + const cli_opt_spec *spec; + int error = 0; + + /* Display required arguments first */ + for (spec = specs; spec->type; ++spec) { + if (! (spec->usage & CLI_OPT_USAGE_REQUIRED) || + (spec->usage & CLI_OPT_USAGE_HIDDEN)) + continue; + + git_str_printf(&help, " "); + + if ((error = print_spec_name(&help, spec)) < 0) + goto done; + + git_str_printf(&help, ": %s\n", spec->help); + } + + /* Display the remaining arguments */ + for (spec = specs; spec->type; ++spec) { + if ((spec->usage & CLI_OPT_USAGE_REQUIRED) || + (spec->usage & CLI_OPT_USAGE_HIDDEN)) + continue; + + git_str_printf(&help, " "); + + if ((error = print_spec_name(&help, spec)) < 0) + goto done; + + git_str_printf(&help, ": %s\n", spec->help); + + } + + if (git_str_oom(&help) || + p_write(fileno(file), help.ptr, help.size) < 0) + error = -1; + +done: + error = (error < 0) ? -1 : 0; + + git_str_dispose(&help); + return error; +} + diff --git a/src/cli/opt_usage.h b/src/cli/opt_usage.h new file mode 100644 index 000000000..c752494e1 --- /dev/null +++ b/src/cli/opt_usage.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef CLI_opt_usage_h__ +#define CLI_opt_usage_h__ + +/** + * Prints usage information to the given file handle. + * + * @param file The file to print information to + * @param command The name of the command to use when printing + * @param subcommand The name of the subcommand (eg "checkout") to use when printing, or NULL to skip + * @param specs The specifications allowed by the command + * @return 0 on success, -1 on failure + */ +int cli_opt_usage_fprint( + FILE *file, + const char *command, + const char *subcommand, + const cli_opt_spec specs[]); + +int cli_opt_usage_error( + const char *subcommand, + const cli_opt_spec specs[], + const cli_opt *invalid_opt); + +int cli_opt_help_fprint( + FILE *file, + const cli_opt_spec specs[]); + +#endif /* CLI_opt_usage_h__ */ diff --git a/src/cli/progress.c b/src/cli/progress.c new file mode 100644 index 000000000..ba52655e7 --- /dev/null +++ b/src/cli/progress.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include +#include +#include + +#include "progress.h" +#include "error.h" + +/* + * Show updates to the percentage and number of objects received + * separately from the throughput to give an accurate progress while + * avoiding too much noise on the screen. + */ +#define PROGRESS_UPDATE_TIME 0.10 +#define THROUGHPUT_UPDATE_TIME 1.00 + +#define is_nl(c) ((c) == '\r' || (c) == '\n') + +#define return_os_error(msg) do { \ + git_error_set(GIT_ERROR_OS, "%s", msg); return -1; } while(0) + +GIT_INLINE(size_t) no_nl_len(const char *str, size_t len) +{ + size_t i = 0; + + while (i < len && !is_nl(str[i])) + i++; + + return i; +} + +GIT_INLINE(size_t) nl_len(bool *has_nl, const char *str, size_t len) +{ + size_t i = no_nl_len(str, len); + + *has_nl = false; + + while (i < len && is_nl(str[i])) { + *has_nl = true; + i++; + } + + return i; +} + +static int progress_write(cli_progress *progress, bool force, git_str *line) +{ + bool has_nl; + size_t no_nl = no_nl_len(line->ptr, line->size); + size_t nl = nl_len(&has_nl, line->ptr + no_nl, line->size - no_nl); + double now = git__timer(); + size_t i; + + /* Avoid spamming the console with progress updates */ + if (!force && line->ptr[line->size - 1] != '\n' && progress->last_update) { + if (now - progress->last_update < PROGRESS_UPDATE_TIME) { + git_str_clear(&progress->deferred); + git_str_put(&progress->deferred, line->ptr, line->size); + return git_str_oom(&progress->deferred) ? -1 : 0; + } + } + + /* + * If there's something on this line already (eg, a progress line + * with only a trailing `\r` that we'll print over) then we need + * to really print over it in case we're writing a shorter line. + */ + if (printf("%.*s", (int)no_nl, line->ptr) < 0) + return_os_error("could not print status"); + + if (progress->onscreen.size) { + for (i = no_nl; i < progress->onscreen.size; i++) { + if (printf(" ") < 0) + return_os_error("could not print status"); + } + } + + if (printf("%.*s", (int)nl, line->ptr + no_nl) < 0 || + fflush(stdout) != 0) + return_os_error("could not print status"); + + git_str_clear(&progress->onscreen); + + if (line->ptr[line->size - 1] == '\n') { + progress->last_update = 0; + } else { + git_str_put(&progress->onscreen, line->ptr, line->size); + progress->last_update = now; + } + + git_str_clear(&progress->deferred); + return git_str_oom(&progress->onscreen) ? -1 : 0; +} + +static int progress_printf(cli_progress *progress, bool force, const char *fmt, ...) + GIT_FORMAT_PRINTF(3, 4); + +int progress_printf(cli_progress *progress, bool force, const char *fmt, ...) +{ + git_str buf = GIT_BUF_INIT; + va_list ap; + int error; + + va_start(ap, fmt); + error = git_str_vprintf(&buf, fmt, ap); + va_end(ap); + + if (error < 0) + return error; + + error = progress_write(progress, force, &buf); + + git_str_dispose(&buf); + return error; +} + +static int progress_complete(cli_progress *progress) +{ + if (progress->deferred.size) + progress_write(progress, true, &progress->deferred); + + if (progress->onscreen.size) + if (printf("\n") < 0) + return_os_error("could not print status"); + + git_str_clear(&progress->deferred); + git_str_clear(&progress->onscreen); + progress->last_update = 0; + progress->action_start = 0; + progress->action_finish = 0; + + return 0; +} + +GIT_INLINE(int) percent(size_t completed, size_t total) +{ + if (total == 0) + return (completed == 0) ? 100 : 0; + + return (int)(((double)completed / (double)total) * 100); +} + +int cli_progress_fetch_sideband(const char *str, int len, void *payload) +{ + cli_progress *progress = (cli_progress *)payload; + size_t remain; + + if (len <= 0) + return 0; + + /* Accumulate the sideband data, then print it line-at-a-time. */ + if (git_str_put(&progress->sideband, str, len) < 0) + return -1; + + str = progress->sideband.ptr; + remain = progress->sideband.size; + + while (remain) { + bool has_nl; + size_t line_len = nl_len(&has_nl, str, remain); + + if (!has_nl) + break; + + if (line_len < INT_MAX) { + int error = progress_printf(progress, true, + "remote: %.*s", (int)line_len, str); + + if (error < 0) + return error; + } + + str += line_len; + remain -= line_len; + } + + git_str_consume_bytes(&progress->sideband, (progress->sideband.size - remain)); + + return 0; +} + +static int fetch_receiving( + cli_progress *progress, + const git_indexer_progress *stats) +{ + char *recv_units[] = { "B", "KiB", "MiB", "GiB", "TiB", NULL }; + char *rate_units[] = { "B/s", "KiB/s", "MiB/s", "GiB/s", "TiB/s", NULL }; + + double now, recv_len, rate, elapsed; + size_t recv_unit_idx = 0, rate_unit_idx = 0; + bool done = (stats->received_objects == stats->total_objects); + + if (!progress->action_start) + progress->action_start = git__timer(); + + if (done && progress->action_finish) + now = progress->action_finish; + else if (done) + progress->action_finish = now = git__timer(); + else + now = git__timer(); + + if (progress->throughput_update && + now - progress->throughput_update < THROUGHPUT_UPDATE_TIME) { + elapsed = progress->throughput_update - + progress->action_start; + recv_len = progress->throughput_bytes; + } else { + elapsed = now - progress->action_start; + recv_len = (double)stats->received_bytes; + + progress->throughput_update = now; + progress->throughput_bytes = recv_len; + } + + rate = elapsed ? recv_len / elapsed : 0; + + while (recv_len > 1024 && recv_units[recv_unit_idx+1]) { + recv_len /= 1024; + recv_unit_idx++; + } + + while (rate > 1024 && rate_units[rate_unit_idx+1]) { + rate /= 1024; + rate_unit_idx++; + } + + return progress_printf(progress, false, + "Receiving objects: %3d%% (%d/%d), %.2f %s | %.2f %s%s\r", + percent(stats->received_objects, stats->total_objects), + stats->received_objects, + stats->total_objects, + recv_len, recv_units[recv_unit_idx], + rate, rate_units[rate_unit_idx], + done ? ", done." : ""); +} + +static int fetch_resolving( + cli_progress *progress, + const git_indexer_progress *stats) +{ + bool done = (stats->indexed_deltas == stats->total_deltas); + + return progress_printf(progress, false, + "Resolving deltas: %3d%% (%d/%d)%s\r", + percent(stats->indexed_deltas, stats->total_deltas), + stats->indexed_deltas, stats->total_deltas, + done ? ", done." : ""); +} + +int cli_progress_fetch_transfer(const git_indexer_progress *stats, void *payload) +{ + cli_progress *progress = (cli_progress *)payload; + int error = 0; + + switch (progress->action) { + case CLI_PROGRESS_NONE: + progress->action = CLI_PROGRESS_RECEIVING; + /* fall through */ + + case CLI_PROGRESS_RECEIVING: + if ((error = fetch_receiving(progress, stats)) < 0) + break; + + /* + * Upgrade from receiving to resolving; do this after the + * final call to cli_progress_fetch_receiving (above) to + * ensure that we've printed a final "done" string after + * any sideband data. + */ + if (!stats->indexed_deltas) + break; + + progress_complete(progress); + progress->action = CLI_PROGRESS_RESOLVING; + /* fall through */ + + case CLI_PROGRESS_RESOLVING: + error = fetch_resolving(progress, stats); + break; + + default: + /* should not be reached */ + GIT_ASSERT(!"unexpected progress state"); + } + + return error; +} + +void cli_progress_checkout( + const char *path, + size_t completed_steps, + size_t total_steps, + void *payload) +{ + cli_progress *progress = (cli_progress *)payload; + bool done = (completed_steps == total_steps); + + GIT_UNUSED(path); + + if (progress->action != CLI_PROGRESS_CHECKING_OUT) { + progress_complete(progress); + progress->action = CLI_PROGRESS_CHECKING_OUT; + } + + progress_printf(progress, false, + "Checking out files: %3d%% (%" PRIuZ "/%" PRIuZ ")%s\r", + percent(completed_steps, total_steps), + completed_steps, total_steps, + done ? ", done." : ""); +} + +int cli_progress_abort(cli_progress *progress) +{ + if (progress->onscreen.size > 0 && printf("\n") < 0) + return_os_error("could not print status"); + + return 0; +} + +int cli_progress_finish(cli_progress *progress) +{ + int error = progress->action ? progress_complete(progress) : 0; + + progress->action = 0; + return error; +} + +void cli_progress_dispose(cli_progress *progress) +{ + if (progress == NULL) + return; + + git_str_dispose(&progress->sideband); + git_str_dispose(&progress->onscreen); + git_str_dispose(&progress->deferred); + + memset(progress, 0, sizeof(cli_progress)); +} diff --git a/src/cli/progress.h b/src/cli/progress.h new file mode 100644 index 000000000..7a445ec29 --- /dev/null +++ b/src/cli/progress.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef CLI_progress_h__ +#define CLI_progress_h__ + +#include +#include "str.h" + +/* + * A general purpose set of progress printing functions. An individual + * `cli_progress` object is capable of displaying progress for a single + * function, even if that function displays multiple pieces of progress + * (like `git_clone`). `cli_progress_finish` should be called after + * any function invocation to re-set state. + */ + +typedef enum { + CLI_PROGRESS_NONE, + CLI_PROGRESS_RECEIVING, + CLI_PROGRESS_RESOLVING, + CLI_PROGRESS_CHECKING_OUT +} cli_progress_t; + +typedef struct { + cli_progress_t action; + + /* Actions may time themselves (eg fetch) but are not required to */ + double action_start; + double action_finish; + + /* Last console update, avoid too frequent updates. */ + double last_update; + + /* Accumulators for partial output and deferred updates. */ + git_str sideband; + git_str onscreen; + git_str deferred; + + /* Last update about throughput */ + double throughput_update; + double throughput_bytes; +} cli_progress; + +#define CLI_PROGRESS_INIT { 0 } + +/** + * Prints sideband data from fetch to the console. Suitable for a + * `sideband_progress` callback for `git_fetch_options`. + * + * @param str The sideband string + * @param len The length of the sideband string + * @param payload A pointer to the cli_progress + * @return 0 on success, -1 on failure + */ +extern int cli_progress_fetch_sideband( + const char *str, + int len, + void *payload); + +/** + * Prints fetch transfer statistics to the console. Suitable for a + * `transfer_progress` callback for `git_fetch_options`. + * + * @param stats The indexer stats + * @param payload A pointer to the cli_progress + * @return 0 on success, -1 on failure + */ +extern int cli_progress_fetch_transfer( + const git_indexer_progress *stats, + void *payload); + +/** + * Prints checkout progress to the console. Suitable for a + * `progress_cb` callback for `git_checkout_options`. + * + * @param path The path being written + * @param completed_steps The completed checkout steps + * @param total_steps The total number of checkout steps + * @param payload A pointer to the cli_progress + */ +extern void cli_progress_checkout( + const char *path, + size_t completed_steps, + size_t total_steps, + void *payload); + +/** + * Stop displaying progress quickly; suitable for stopping an application + * quickly. Does not display any lines that were buffered, just gets the + * console back to a sensible place. + * + * @param progress The progress information + * @return 0 on success, -1 on failure + */ +extern int cli_progress_abort(cli_progress *progress); + +/** + * Finishes displaying progress; flushes any buffered output. + * + * @param progress The progress information + * @return 0 on success, -1 on failure + */ +extern int cli_progress_finish(cli_progress *progress); + +/** + * Disposes the progress information. + * + * @param progress The progress information + */ +extern void cli_progress_dispose(cli_progress *progress); + +#endif /* CLI_progress_h__ */ diff --git a/src/cli/sighandler.h b/src/cli/sighandler.h new file mode 100644 index 000000000..877223e02 --- /dev/null +++ b/src/cli/sighandler.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef CLI_sighandler_h__ +#define CLI_sighandler_h__ + +/** + * Sets up a signal handler that will run when the process is interrupted + * (via SIGINT on POSIX or Control-C or Control-Break on Windows). + * + * @param handler The function to run on interrupt + * @return 0 on success, -1 on failure + */ +int cli_sighandler_set_interrupt(void (*handler)(void)); + +#endif /* CLI_sighandler_h__ */ diff --git a/src/cli/unix/sighandler.c b/src/cli/unix/sighandler.c new file mode 100644 index 000000000..6b4982d48 --- /dev/null +++ b/src/cli/unix/sighandler.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include +#include +#include "git2_util.h" +#include "cli.h" + +static void (*interrupt_handler)(void) = NULL; + +static void interrupt_proxy(int signal) +{ + GIT_UNUSED(signal); + interrupt_handler(); +} + +int cli_sighandler_set_interrupt(void (*handler)(void)) +{ + void (*result)(int); + + if ((interrupt_handler = handler) != NULL) + result = signal(SIGINT, interrupt_proxy); + else + result = signal(SIGINT, SIG_DFL); + + if (result == SIG_ERR) { + git_error_set(GIT_ERROR_OS, "could not set signal handler"); + return -1; + } + + return 0; +} diff --git a/src/win32/precompiled.c b/src/cli/win32/precompiled.c similarity index 100% rename from src/win32/precompiled.c rename to src/cli/win32/precompiled.c diff --git a/src/cli/win32/precompiled.h b/src/cli/win32/precompiled.h new file mode 100644 index 000000000..b0309b864 --- /dev/null +++ b/src/cli/win32/precompiled.h @@ -0,0 +1,3 @@ +#include + +#include "cli.h" diff --git a/src/cli/win32/sighandler.c b/src/cli/win32/sighandler.c new file mode 100644 index 000000000..cc0b64640 --- /dev/null +++ b/src/cli/win32/sighandler.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "git2_util.h" +#include + +#include "cli.h" + +static void (*interrupt_handler)(void) = NULL; + +static BOOL WINAPI interrupt_proxy(DWORD signal) +{ + GIT_UNUSED(signal); + interrupt_handler(); + return TRUE; +} + +int cli_sighandler_set_interrupt(void (*handler)(void)) +{ + BOOL result; + + if ((interrupt_handler = handler) != NULL) + result = SetConsoleCtrlHandler(interrupt_proxy, FALSE); + else + result = SetConsoleCtrlHandler(NULL, FALSE); + + if (!result) { + git_error_set(GIT_ERROR_OS, "could not set control control handler"); + return -1; + } + + return 0; +} diff --git a/src/features.h.in b/src/features.h.in index f920135da..fbf0cab60 100644 --- a/src/features.h.in +++ b/src/features.h.in @@ -46,8 +46,17 @@ #cmakedefine GIT_SHA1_WIN32 1 #cmakedefine GIT_SHA1_COMMON_CRYPTO 1 #cmakedefine GIT_SHA1_OPENSSL 1 +#cmakedefine GIT_SHA1_OPENSSL_DYNAMIC 1 #cmakedefine GIT_SHA1_MBEDTLS 1 +#cmakedefine GIT_SHA256_BUILTIN 1 +#cmakedefine GIT_SHA256_WIN32 1 +#cmakedefine GIT_SHA256_COMMON_CRYPTO 1 +#cmakedefine GIT_SHA256_OPENSSL 1 +#cmakedefine GIT_SHA256_OPENSSL_DYNAMIC 1 +#cmakedefine GIT_SHA256_MBEDTLS 1 + #cmakedefine GIT_RAND_GETENTROPY 1 +#cmakedefine GIT_RAND_GETLOADAVG 1 #endif diff --git a/src/hash/sha1.h b/src/hash/sha1.h deleted file mode 100644 index 4b4dae3f8..000000000 --- a/src/hash/sha1.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ - -#ifndef INCLUDE_hash_sha1_h__ -#define INCLUDE_hash_sha1_h__ - -#include "common.h" - -typedef struct git_hash_sha1_ctx git_hash_sha1_ctx; - -#if defined(GIT_SHA1_COLLISIONDETECT) -# include "sha1/collisiondetect.h" -#elif defined(GIT_SHA1_COMMON_CRYPTO) -# include "sha1/common_crypto.h" -#elif defined(GIT_SHA1_OPENSSL) -# include "sha1/openssl.h" -#elif defined(GIT_SHA1_WIN32) -# include "sha1/win32.h" -#elif defined(GIT_SHA1_MBEDTLS) -# include "sha1/mbedtls.h" -#else -# include "sha1/generic.h" -#endif - -#define GIT_HASH_SHA1_SIZE 20 - -int git_hash_sha1_global_init(void); - -int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx); -void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx); - -int git_hash_sha1_init(git_hash_sha1_ctx *c); -int git_hash_sha1_update(git_hash_sha1_ctx *c, const void *data, size_t len); -int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *c); - -#endif diff --git a/src/hash/sha1/generic.c b/src/hash/sha1/generic.c deleted file mode 100644 index 85b34c578..000000000 --- a/src/hash/sha1/generic.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ - -#include "generic.h" - -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) - -/* - * Force usage of rol or ror by selecting the one with the smaller constant. - * It _can_ generate slightly smaller code (a constant of 1 is special), but - * perhaps more importantly it's possibly faster on any uarch that does a - * rotate with a loop. - */ - -#define SHA_ASM(op, x, n) (__extension__ ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; })) -#define SHA_ROL(x,n) SHA_ASM("rol", x, n) -#define SHA_ROR(x,n) SHA_ASM("ror", x, n) - -#else - -#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r))) -#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n)) -#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n) - -#endif - -/* - * If you have 32 registers or more, the compiler can (and should) - * try to change the array[] accesses into registers. However, on - * machines with less than ~25 registers, that won't really work, - * and at least gcc will make an unholy mess of it. - * - * So to avoid that mess which just slows things down, we force - * the stores to memory to actually happen (we might be better off - * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as - * suggested by Artur Skawina - that will also make gcc unable to - * try to do the silly "optimize away loads" part because it won't - * see what the value will be). - * - * Ben Herrenschmidt reports that on PPC, the C version comes close - * to the optimized asm with this (ie on PPC you don't want that - * 'volatile', since there are lots of registers). - * - * On ARM we get the best code generation by forcing a full memory barrier - * between each SHA_ROUND, otherwise gcc happily get wild with spilling and - * the stack frame size simply explode and performance goes down the drain. - */ - -#if defined(__i386__) || defined(__x86_64__) - #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val)) -#elif defined(__GNUC__) && defined(__arm__) - #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0) -#else - #define setW(x, val) (W(x) = (val)) -#endif - -/* - * Performance might be improved if the CPU architecture is OK with - * unaligned 32-bit loads and a fast ntohl() is available. - * Otherwise fall back to byte loads and shifts which is portable, - * and is faster on architectures with memory alignment issues. - */ - -#if defined(__i386__) || defined(__x86_64__) || \ - defined(_M_IX86) || defined(_M_X64) || \ - defined(__ppc__) || defined(__ppc64__) || \ - defined(__powerpc__) || defined(__powerpc64__) || \ - defined(__s390__) || defined(__s390x__) - -#define get_be32(p) ntohl(*(const unsigned int *)(p)) -#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0) - -#else - -#define get_be32(p) ( \ - (*((const unsigned char *)(p) + 0) << 24) | \ - (*((const unsigned char *)(p) + 1) << 16) | \ - (*((const unsigned char *)(p) + 2) << 8) | \ - (*((const unsigned char *)(p) + 3) << 0) ) -#define put_be32(p, v) do { \ - unsigned int __v = (v); \ - *((unsigned char *)(p) + 0) = __v >> 24; \ - *((unsigned char *)(p) + 1) = __v >> 16; \ - *((unsigned char *)(p) + 2) = __v >> 8; \ - *((unsigned char *)(p) + 3) = __v >> 0; } while (0) - -#endif - -/* This "rolls" over the 512-bit array */ -#define W(x) (array[(x)&15]) - -/* - * Where do we get the source from? The first 16 iterations get it from - * the input data, the next mix it from the 512-bit array. - */ -#define SHA_SRC(t) get_be32(data + t) -#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1) - -#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ - unsigned int TEMP = input(t); setW(t, TEMP); \ - E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \ - B = SHA_ROR(B, 2); } while (0) - -#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) -#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) -#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E ) -#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E ) -#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E ) - -static void hash__block(git_hash_sha1_ctx *ctx, const unsigned int *data) -{ - unsigned int A,B,C,D,E; - unsigned int array[16]; - - A = ctx->H[0]; - B = ctx->H[1]; - C = ctx->H[2]; - D = ctx->H[3]; - E = ctx->H[4]; - - /* Round 1 - iterations 0-16 take their input from 'data' */ - T_0_15( 0, A, B, C, D, E); - T_0_15( 1, E, A, B, C, D); - T_0_15( 2, D, E, A, B, C); - T_0_15( 3, C, D, E, A, B); - T_0_15( 4, B, C, D, E, A); - T_0_15( 5, A, B, C, D, E); - T_0_15( 6, E, A, B, C, D); - T_0_15( 7, D, E, A, B, C); - T_0_15( 8, C, D, E, A, B); - T_0_15( 9, B, C, D, E, A); - T_0_15(10, A, B, C, D, E); - T_0_15(11, E, A, B, C, D); - T_0_15(12, D, E, A, B, C); - T_0_15(13, C, D, E, A, B); - T_0_15(14, B, C, D, E, A); - T_0_15(15, A, B, C, D, E); - - /* Round 1 - tail. Input from 512-bit mixing array */ - T_16_19(16, E, A, B, C, D); - T_16_19(17, D, E, A, B, C); - T_16_19(18, C, D, E, A, B); - T_16_19(19, B, C, D, E, A); - - /* Round 2 */ - T_20_39(20, A, B, C, D, E); - T_20_39(21, E, A, B, C, D); - T_20_39(22, D, E, A, B, C); - T_20_39(23, C, D, E, A, B); - T_20_39(24, B, C, D, E, A); - T_20_39(25, A, B, C, D, E); - T_20_39(26, E, A, B, C, D); - T_20_39(27, D, E, A, B, C); - T_20_39(28, C, D, E, A, B); - T_20_39(29, B, C, D, E, A); - T_20_39(30, A, B, C, D, E); - T_20_39(31, E, A, B, C, D); - T_20_39(32, D, E, A, B, C); - T_20_39(33, C, D, E, A, B); - T_20_39(34, B, C, D, E, A); - T_20_39(35, A, B, C, D, E); - T_20_39(36, E, A, B, C, D); - T_20_39(37, D, E, A, B, C); - T_20_39(38, C, D, E, A, B); - T_20_39(39, B, C, D, E, A); - - /* Round 3 */ - T_40_59(40, A, B, C, D, E); - T_40_59(41, E, A, B, C, D); - T_40_59(42, D, E, A, B, C); - T_40_59(43, C, D, E, A, B); - T_40_59(44, B, C, D, E, A); - T_40_59(45, A, B, C, D, E); - T_40_59(46, E, A, B, C, D); - T_40_59(47, D, E, A, B, C); - T_40_59(48, C, D, E, A, B); - T_40_59(49, B, C, D, E, A); - T_40_59(50, A, B, C, D, E); - T_40_59(51, E, A, B, C, D); - T_40_59(52, D, E, A, B, C); - T_40_59(53, C, D, E, A, B); - T_40_59(54, B, C, D, E, A); - T_40_59(55, A, B, C, D, E); - T_40_59(56, E, A, B, C, D); - T_40_59(57, D, E, A, B, C); - T_40_59(58, C, D, E, A, B); - T_40_59(59, B, C, D, E, A); - - /* Round 4 */ - T_60_79(60, A, B, C, D, E); - T_60_79(61, E, A, B, C, D); - T_60_79(62, D, E, A, B, C); - T_60_79(63, C, D, E, A, B); - T_60_79(64, B, C, D, E, A); - T_60_79(65, A, B, C, D, E); - T_60_79(66, E, A, B, C, D); - T_60_79(67, D, E, A, B, C); - T_60_79(68, C, D, E, A, B); - T_60_79(69, B, C, D, E, A); - T_60_79(70, A, B, C, D, E); - T_60_79(71, E, A, B, C, D); - T_60_79(72, D, E, A, B, C); - T_60_79(73, C, D, E, A, B); - T_60_79(74, B, C, D, E, A); - T_60_79(75, A, B, C, D, E); - T_60_79(76, E, A, B, C, D); - T_60_79(77, D, E, A, B, C); - T_60_79(78, C, D, E, A, B); - T_60_79(79, B, C, D, E, A); - - ctx->H[0] += A; - ctx->H[1] += B; - ctx->H[2] += C; - ctx->H[3] += D; - ctx->H[4] += E; -} - -int git_hash_sha1_global_init(void) -{ - return 0; -} - -int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx) -{ - return git_hash_sha1_init(ctx); -} - -void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx) -{ - GIT_UNUSED(ctx); -} - -int git_hash_sha1_init(git_hash_sha1_ctx *ctx) -{ - ctx->size = 0; - - /* Initialize H with the magic constants (see FIPS180 for constants) */ - ctx->H[0] = 0x67452301; - ctx->H[1] = 0xefcdab89; - ctx->H[2] = 0x98badcfe; - ctx->H[3] = 0x10325476; - ctx->H[4] = 0xc3d2e1f0; - - return 0; -} - -int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len) -{ - unsigned int lenW = ctx->size & 63; - - ctx->size += len; - - /* Read the data into W and process blocks as they get full */ - if (lenW) { - unsigned int left = 64 - lenW; - if (len < left) - left = (unsigned int)len; - memcpy(lenW + (char *)ctx->W, data, left); - lenW = (lenW + left) & 63; - len -= left; - data = ((const char *)data + left); - if (lenW) - return 0; - hash__block(ctx, ctx->W); - } - while (len >= 64) { - hash__block(ctx, data); - data = ((const char *)data + 64); - len -= 64; - } - if (len) - memcpy(ctx->W, data, len); - - return 0; -} - -int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx) -{ - static const unsigned char pad[64] = { 0x80 }; - unsigned int padlen[2]; - int i; - - /* Pad with a binary 1 (ie 0x80), then zeroes, then length */ - padlen[0] = htonl((uint32_t)(ctx->size >> 29)); - padlen[1] = htonl((uint32_t)(ctx->size << 3)); - - i = ctx->size & 63; - git_hash_sha1_update(ctx, pad, 1+ (63 & (55 - i))); - git_hash_sha1_update(ctx, padlen, 8); - - /* Output hash */ - for (i = 0; i < 5; i++) - put_be32(out + i*4, ctx->H[i]); - - return 0; -} diff --git a/src/hash/sha1/openssl.c b/src/hash/sha1/openssl.c deleted file mode 100644 index 64bf99b3c..000000000 --- a/src/hash/sha1/openssl.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ - -#include "openssl.h" - -int git_hash_sha1_global_init(void) -{ - return 0; -} - -int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx) -{ - return git_hash_sha1_init(ctx); -} - -void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx) -{ - GIT_UNUSED(ctx); -} - -int git_hash_sha1_init(git_hash_sha1_ctx *ctx) -{ - GIT_ASSERT_ARG(ctx); - - if (SHA1_Init(&ctx->c) != 1) { - git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to initialize hash context"); - return -1; - } - - return 0; -} - -int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len) -{ - GIT_ASSERT_ARG(ctx); - - if (SHA1_Update(&ctx->c, data, len) != 1) { - git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to update hash"); - return -1; - } - - return 0; -} - -int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx) -{ - GIT_ASSERT_ARG(ctx); - - if (SHA1_Final(out, &ctx->c) != 1) { - git_error_set(GIT_ERROR_SHA1, "hash_openssl: failed to finalize hash"); - return -1; - } - - return 0; -} diff --git a/src/hash/sha1/win32.c b/src/hash/sha1/win32.c deleted file mode 100644 index b89dfbad8..000000000 --- a/src/hash/sha1/win32.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ - -#include "win32.h" - -#include "runtime.h" - -#include -#include - -#define GIT_HASH_CNG_DLL_NAME "bcrypt.dll" - -/* BCRYPT_SHA1_ALGORITHM */ -#define GIT_HASH_CNG_HASH_TYPE L"SHA1" - -/* BCRYPT_OBJECT_LENGTH */ -#define GIT_HASH_CNG_HASH_OBJECT_LEN L"ObjectLength" - -/* BCRYPT_HASH_REUSEABLE_FLAGS */ -#define GIT_HASH_CNG_HASH_REUSABLE 0x00000020 - -static git_hash_prov hash_prov = {0}; - -/* Hash initialization */ - -/* Initialize CNG, if available */ -GIT_INLINE(int) hash_cng_prov_init(void) -{ - char dll_path[MAX_PATH]; - DWORD dll_path_len, size_len; - - /* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */ - if (!git_has_win32_version(6, 0, 1)) { - git_error_set(GIT_ERROR_SHA1, "CryptoNG is not supported on this platform"); - return -1; - } - - /* Load bcrypt.dll explicitly from the system directory */ - if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 || - dll_path_len > MAX_PATH || - StringCchCat(dll_path, MAX_PATH, "\\") < 0 || - StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 || - (hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL) { - git_error_set(GIT_ERROR_SHA1, "CryptoNG library could not be loaded"); - return -1; - } - - /* Load the function addresses */ - if ((hash_prov.prov.cng.open_algorithm_provider = (hash_win32_cng_open_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptOpenAlgorithmProvider")) == NULL || - (hash_prov.prov.cng.get_property = (hash_win32_cng_get_property_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptGetProperty")) == NULL || - (hash_prov.prov.cng.create_hash = (hash_win32_cng_create_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCreateHash")) == NULL || - (hash_prov.prov.cng.finish_hash = (hash_win32_cng_finish_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptFinishHash")) == NULL || - (hash_prov.prov.cng.hash_data = (hash_win32_cng_hash_data_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptHashData")) == NULL || - (hash_prov.prov.cng.destroy_hash = (hash_win32_cng_destroy_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptDestroyHash")) == NULL || - (hash_prov.prov.cng.close_algorithm_provider = (hash_win32_cng_close_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCloseAlgorithmProvider")) == NULL) { - FreeLibrary(hash_prov.prov.cng.dll); - - git_error_set(GIT_ERROR_OS, "CryptoNG functions could not be loaded"); - return -1; - } - - /* Load the SHA1 algorithm */ - if (hash_prov.prov.cng.open_algorithm_provider(&hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0) { - FreeLibrary(hash_prov.prov.cng.dll); - - git_error_set(GIT_ERROR_OS, "algorithm provider could not be initialized"); - return -1; - } - - /* Get storage space for the hash object */ - if (hash_prov.prov.cng.get_property(hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_prov.prov.cng.hash_object_size, sizeof(DWORD), &size_len, 0) < 0) { - hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0); - FreeLibrary(hash_prov.prov.cng.dll); - - git_error_set(GIT_ERROR_OS, "algorithm handle could not be found"); - return -1; - } - - hash_prov.type = CNG; - return 0; -} - -GIT_INLINE(void) hash_cng_prov_shutdown(void) -{ - hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0); - FreeLibrary(hash_prov.prov.cng.dll); - - hash_prov.type = INVALID; -} - -/* Initialize CryptoAPI */ -GIT_INLINE(int) hash_cryptoapi_prov_init() -{ - if (!CryptAcquireContext(&hash_prov.prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { - git_error_set(GIT_ERROR_OS, "legacy hash context could not be started"); - return -1; - } - - hash_prov.type = CRYPTOAPI; - return 0; -} - -GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void) -{ - CryptReleaseContext(hash_prov.prov.cryptoapi.handle, 0); - - hash_prov.type = INVALID; -} - -static void sha1_shutdown(void) -{ - if (hash_prov.type == CNG) - hash_cng_prov_shutdown(); - else if(hash_prov.type == CRYPTOAPI) - hash_cryptoapi_prov_shutdown(); -} - -int git_hash_sha1_global_init(void) -{ - int error = 0; - - if (hash_prov.type != INVALID) - return 0; - - if ((error = hash_cng_prov_init()) < 0) - error = hash_cryptoapi_prov_init(); - - if (!error) - error = git_runtime_shutdown_register(sha1_shutdown); - - return error; -} - -/* CryptoAPI: available in Windows XP and newer */ - -GIT_INLINE(int) hash_ctx_cryptoapi_init(git_hash_sha1_ctx *ctx) -{ - ctx->type = CRYPTOAPI; - ctx->prov = &hash_prov; - - return git_hash_sha1_init(ctx); -} - -GIT_INLINE(int) hash_cryptoapi_init(git_hash_sha1_ctx *ctx) -{ - if (ctx->ctx.cryptoapi.valid) - CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle); - - if (!CryptCreateHash(ctx->prov->prov.cryptoapi.handle, CALG_SHA1, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) { - ctx->ctx.cryptoapi.valid = 0; - git_error_set(GIT_ERROR_OS, "legacy hash implementation could not be created"); - return -1; - } - - ctx->ctx.cryptoapi.valid = 1; - return 0; -} - -GIT_INLINE(int) hash_cryptoapi_update(git_hash_sha1_ctx *ctx, const void *_data, size_t len) -{ - const BYTE *data = (BYTE *)_data; - - GIT_ASSERT(ctx->ctx.cryptoapi.valid); - - while (len > 0) { - DWORD chunk = (len > MAXDWORD) ? MAXDWORD : (DWORD)len; - - if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, data, chunk, 0)) { - git_error_set(GIT_ERROR_OS, "legacy hash data could not be updated"); - return -1; - } - - data += chunk; - len -= chunk; - } - - return 0; -} - -GIT_INLINE(int) hash_cryptoapi_final(unsigned char *out, git_hash_sha1_ctx *ctx) -{ - DWORD len = GIT_HASH_SHA1_SIZE; - int error = 0; - - GIT_ASSERT(ctx->ctx.cryptoapi.valid); - - if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out, &len, 0)) { - git_error_set(GIT_ERROR_OS, "legacy hash data could not be finished"); - error = -1; - } - - CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle); - ctx->ctx.cryptoapi.valid = 0; - - return error; -} - -GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_sha1_ctx *ctx) -{ - if (ctx->ctx.cryptoapi.valid) - CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle); -} - -/* CNG: Available in Windows Server 2008 and newer */ - -GIT_INLINE(int) hash_ctx_cng_init(git_hash_sha1_ctx *ctx) -{ - if ((ctx->ctx.cng.hash_object = git__malloc(hash_prov.prov.cng.hash_object_size)) == NULL) - return -1; - - if (hash_prov.prov.cng.create_hash(hash_prov.prov.cng.handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_prov.prov.cng.hash_object_size, NULL, 0, 0) < 0) { - git__free(ctx->ctx.cng.hash_object); - - git_error_set(GIT_ERROR_OS, "hash implementation could not be created"); - return -1; - } - - ctx->type = CNG; - ctx->prov = &hash_prov; - - return 0; -} - -GIT_INLINE(int) hash_cng_init(git_hash_sha1_ctx *ctx) -{ - BYTE hash[GIT_OID_RAWSZ]; - - if (!ctx->ctx.cng.updated) - return 0; - - /* CNG needs to be finished to restart */ - if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0) { - git_error_set(GIT_ERROR_OS, "hash implementation could not be finished"); - return -1; - } - - ctx->ctx.cng.updated = 0; - - return 0; -} - -GIT_INLINE(int) hash_cng_update(git_hash_sha1_ctx *ctx, const void *_data, size_t len) -{ - PBYTE data = (PBYTE)_data; - - while (len > 0) { - ULONG chunk = (len > ULONG_MAX) ? ULONG_MAX : (ULONG)len; - - if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, data, chunk, 0) < 0) { - git_error_set(GIT_ERROR_OS, "hash could not be updated"); - return -1; - } - - data += chunk; - len -= chunk; - } - - return 0; -} - -GIT_INLINE(int) hash_cng_final(unsigned char *out, git_hash_sha1_ctx *ctx) -{ - if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out, GIT_HASH_SHA1_SIZE, 0) < 0) { - git_error_set(GIT_ERROR_OS, "hash could not be finished"); - return -1; - } - - ctx->ctx.cng.updated = 0; - - return 0; -} - -GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_sha1_ctx *ctx) -{ - ctx->prov->prov.cng.destroy_hash(ctx->ctx.cng.hash_handle); - git__free(ctx->ctx.cng.hash_object); -} - -/* Indirection between CryptoAPI and CNG */ - -int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx) -{ - int error = 0; - - GIT_ASSERT_ARG(ctx); - - /* - * When compiled with GIT_THREADS, the global hash_prov data is - * initialized with git_libgit2_init. Otherwise, it must be initialized - * at first use. - */ - if (hash_prov.type == INVALID && (error = git_hash_sha1_global_init()) < 0) - return error; - - memset(ctx, 0x0, sizeof(git_hash_sha1_ctx)); - - return (hash_prov.type == CNG) ? hash_ctx_cng_init(ctx) : hash_ctx_cryptoapi_init(ctx); -} - -int git_hash_sha1_init(git_hash_sha1_ctx *ctx) -{ - GIT_ASSERT_ARG(ctx); - GIT_ASSERT_ARG(ctx->type); - return (ctx->type == CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx); -} - -int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len) -{ - GIT_ASSERT_ARG(ctx); - GIT_ASSERT_ARG(ctx->type); - return (ctx->type == CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len); -} - -int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx) -{ - GIT_ASSERT_ARG(ctx); - GIT_ASSERT_ARG(ctx->type); - return (ctx->type == CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx); -} - -void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx) -{ - if (!ctx) - return; - else if (ctx->type == CNG) - hash_ctx_cng_cleanup(ctx); - else if(ctx->type == CRYPTOAPI) - hash_ctx_cryptoapi_cleanup(ctx); -} diff --git a/src/hash/sha1/win32.h b/src/hash/sha1/win32.h deleted file mode 100644 index 791d20a42..000000000 --- a/src/hash/sha1/win32.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ - -#ifndef INCLUDE_hash_sha1_win32_h__ -#define INCLUDE_hash_sha1_win32_h__ - -#include "hash/sha1.h" - -#include -#include - -enum hash_win32_prov_type { - INVALID = 0, - CRYPTOAPI, - CNG -}; - -/* - * CryptoAPI is available for hashing on Windows XP and newer. - */ - -struct hash_cryptoapi_prov { - HCRYPTPROV handle; -}; - -/* - * CNG (bcrypt.dll) is significantly more performant than CryptoAPI and is - * preferred, however it is only available on Windows 2008 and newer and - * must therefore be dynamically loaded, and we must inline constants that - * would not exist when building in pre-Windows 2008 environments. - */ - -/* Function declarations for CNG */ -typedef NTSTATUS (WINAPI *hash_win32_cng_open_algorithm_provider_fn)( - HANDLE /* BCRYPT_ALG_HANDLE */ *phAlgorithm, - LPCWSTR pszAlgId, - LPCWSTR pszImplementation, - DWORD dwFlags); - -typedef NTSTATUS (WINAPI *hash_win32_cng_get_property_fn)( - HANDLE /* BCRYPT_HANDLE */ hObject, - LPCWSTR pszProperty, - PUCHAR pbOutput, - ULONG cbOutput, - ULONG *pcbResult, - ULONG dwFlags); - -typedef NTSTATUS (WINAPI *hash_win32_cng_create_hash_fn)( - HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm, - HANDLE /* BCRYPT_HASH_HANDLE */ *phHash, - PUCHAR pbHashObject, ULONG cbHashObject, - PUCHAR pbSecret, - ULONG cbSecret, - ULONG dwFlags); - -typedef NTSTATUS (WINAPI *hash_win32_cng_finish_hash_fn)( - HANDLE /* BCRYPT_HASH_HANDLE */ hHash, - PUCHAR pbOutput, - ULONG cbOutput, - ULONG dwFlags); - -typedef NTSTATUS (WINAPI *hash_win32_cng_hash_data_fn)( - HANDLE /* BCRYPT_HASH_HANDLE */ hHash, - PUCHAR pbInput, - ULONG cbInput, - ULONG dwFlags); - -typedef NTSTATUS (WINAPI *hash_win32_cng_destroy_hash_fn)( - HANDLE /* BCRYPT_HASH_HANDLE */ hHash); - -typedef NTSTATUS (WINAPI *hash_win32_cng_close_algorithm_provider_fn)( - HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm, - ULONG dwFlags); - -struct hash_cng_prov { - /* DLL for CNG */ - HINSTANCE dll; - - /* Function pointers for CNG */ - hash_win32_cng_open_algorithm_provider_fn open_algorithm_provider; - hash_win32_cng_get_property_fn get_property; - hash_win32_cng_create_hash_fn create_hash; - hash_win32_cng_finish_hash_fn finish_hash; - hash_win32_cng_hash_data_fn hash_data; - hash_win32_cng_destroy_hash_fn destroy_hash; - hash_win32_cng_close_algorithm_provider_fn close_algorithm_provider; - - HANDLE /* BCRYPT_ALG_HANDLE */ handle; - DWORD hash_object_size; -}; - -typedef struct { - enum hash_win32_prov_type type; - - union { - struct hash_cryptoapi_prov cryptoapi; - struct hash_cng_prov cng; - } prov; -} git_hash_prov; - -/* Hash contexts */ - -struct hash_cryptoapi_ctx { - bool valid; - HCRYPTHASH hash_handle; -}; - -struct hash_cng_ctx { - bool updated; - HANDLE /* BCRYPT_HASH_HANDLE */ hash_handle; - PBYTE hash_object; -}; - -struct git_hash_sha1_ctx { - enum hash_win32_prov_type type; - git_hash_prov *prov; - - union { - struct hash_cryptoapi_ctx cryptoapi; - struct hash_cng_ctx cng; - } ctx; -}; - -#endif diff --git a/src/libgit2/CMakeLists.txt b/src/libgit2/CMakeLists.txt new file mode 100644 index 000000000..0c7ddddba --- /dev/null +++ b/src/libgit2/CMakeLists.txt @@ -0,0 +1,131 @@ +# libgit2: the shared library: this CMakeLists.txt compiles the core +# git library functionality. + +add_library(libgit2 OBJECT) +set_target_properties(libgit2 PROPERTIES C_STANDARD 90) +set_target_properties(libgit2 PROPERTIES C_EXTENSIONS OFF) + +include(PkgBuildConfig) + +set(LIBGIT2_INCLUDES + "${PROJECT_BINARY_DIR}/src" + "${PROJECT_SOURCE_DIR}/src/libgit2" + "${PROJECT_SOURCE_DIR}/src/util" + "${PROJECT_SOURCE_DIR}/include") + +if(WIN32 AND EMBED_SSH_PATH) + file(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c") + list(SORT SRC_SSH) + target_sources(libgit2 PRIVATE ${SRC_SSH}) + + list(APPEND LIBGIT2_SYSTEM_INCLUDES "${EMBED_SSH_PATH}/include") + file(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"") + set(GIT_SSH 1) +endif() + +# Collect sourcefiles +file(GLOB SRC_H + "${PROJECT_SOURCE_DIR}/include/git2.h" + "${PROJECT_SOURCE_DIR}/include/git2/*.h" + "${PROJECT_SOURCE_DIR}/include/git2/sys/*.h") +list(SORT SRC_H) +target_sources(libgit2 PRIVATE ${SRC_H}) + +file(GLOB SRC_GIT2 *.c *.h + streams/*.c streams/*.h + transports/*.c transports/*.h + xdiff/*.c xdiff/*.h) +list(SORT SRC_GIT2) +target_sources(libgit2 PRIVATE ${SRC_GIT2}) + +if(WIN32 AND NOT CYGWIN) + # Add resource information on Windows + set(SRC_RC "git2.rc") +endif() + +if(APPLE) + # The old Secure Transport API has been deprecated in macOS 10.15. + set_source_files_properties(streams/stransport.c PROPERTIES COMPILE_FLAGS -Wno-deprecated) +endif() + +# the xdiff dependency is not (yet) warning-free, disable warnings +# as errors for the xdiff sources until we've sorted them out +if(MSVC) + set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS -WX-) + set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS -WX-) + set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS -WX-) + set_source_files_properties(xdiff/xmerge.c PROPERTIES COMPILE_FLAGS -WX-) + set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS -WX-) + set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS -WX-) +else() + set_source_files_properties(xdiff/xdiffi.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter") + set_source_files_properties(xdiff/xemit.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter") + set_source_files_properties(xdiff/xhistogram.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare") + set_source_files_properties(xdiff/xutils.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare") + set_source_files_properties(xdiff/xpatience.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare") +endif() + +ide_split_sources(libgit2) +list(APPEND LIBGIT2_OBJECTS $ $ ${LIBGIT2_DEPENDENCY_OBJECTS}) + +target_include_directories(libgit2 PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${PROJECT_SOURCE_DIR}/include) +target_include_directories(libgit2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES}) + +set(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE) +set(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE) +set(LIBGIT2_DEPENDENCY_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES} PARENT_SCOPE) +set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE) +set(LIBGIT2_SYSTEM_INCLUDES ${LIBGIT2_SYSTEM_INCLUDES} PARENT_SCOPE) +set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE) + +# +# Compile and link libgit2 +# + +add_library(libgit2package ${SRC_RC} ${LIBGIT2_OBJECTS}) +target_link_libraries(libgit2package ${LIBGIT2_SYSTEM_LIBS}) + +set_target_properties(libgit2package PROPERTIES C_STANDARD 90) +set_target_properties(libgit2package PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +set_target_properties(libgit2package PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +set_target_properties(libgit2package PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) + +# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240) +# Win64+MSVC+static libs = linker error +if(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS) + set_target_properties(libgit2package PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64") +endif() + +ide_split_sources(libgit2package) + +if(SONAME) + set_target_properties(libgit2package PROPERTIES VERSION ${libgit2_VERSION}) + set_target_properties(libgit2package PROPERTIES SOVERSION "${libgit2_VERSION_MAJOR}.${libgit2_VERSION_MINOR}") + if(LIBGIT2_FILENAME) + target_compile_definitions(libgit2package PRIVATE LIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\") + set_target_properties(libgit2package PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME}) + elseif(DEFINED LIBGIT2_PREFIX) + set_target_properties(libgit2package PROPERTIES PREFIX "${LIBGIT2_PREFIX}") + endif() +endif() + +pkg_build_config(NAME libgit2 + VERSION ${libgit2_VERSION} + DESCRIPTION "The git library, take 2" + LIBS_SELF git2 + PRIVATE_LIBS ${LIBGIT2_PC_LIBS} + REQUIRES ${LIBGIT2_PC_REQUIRES}) + +if(MSVC_IDE) + # Precompiled headers + set_target_properties(libgit2package PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h") + set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h") +endif() + +# Install +install(TARGETS libgit2package + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/git2 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(FILES ${PROJECT_SOURCE_DIR}/include/git2.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/src/annotated_commit.c b/src/libgit2/annotated_commit.c similarity index 100% rename from src/annotated_commit.c rename to src/libgit2/annotated_commit.c diff --git a/src/annotated_commit.h b/src/libgit2/annotated_commit.h similarity index 100% rename from src/annotated_commit.h rename to src/libgit2/annotated_commit.h diff --git a/src/apply.c b/src/libgit2/apply.c similarity index 100% rename from src/apply.c rename to src/libgit2/apply.c diff --git a/src/apply.h b/src/libgit2/apply.h similarity index 100% rename from src/apply.h rename to src/libgit2/apply.h diff --git a/src/attr.c b/src/libgit2/attr.c similarity index 100% rename from src/attr.c rename to src/libgit2/attr.c diff --git a/src/attr.h b/src/libgit2/attr.h similarity index 100% rename from src/attr.h rename to src/libgit2/attr.h diff --git a/src/attr_file.c b/src/libgit2/attr_file.c similarity index 100% rename from src/attr_file.c rename to src/libgit2/attr_file.c diff --git a/src/attr_file.h b/src/libgit2/attr_file.h similarity index 100% rename from src/attr_file.h rename to src/libgit2/attr_file.h diff --git a/src/attrcache.c b/src/libgit2/attrcache.c similarity index 100% rename from src/attrcache.c rename to src/libgit2/attrcache.c diff --git a/src/attrcache.h b/src/libgit2/attrcache.h similarity index 100% rename from src/attrcache.h rename to src/libgit2/attrcache.h diff --git a/src/blame.c b/src/libgit2/blame.c similarity index 100% rename from src/blame.c rename to src/libgit2/blame.c diff --git a/src/blame.h b/src/libgit2/blame.h similarity index 100% rename from src/blame.h rename to src/libgit2/blame.h diff --git a/src/blame_git.c b/src/libgit2/blame_git.c similarity index 100% rename from src/blame_git.c rename to src/libgit2/blame_git.c diff --git a/src/blame_git.h b/src/libgit2/blame_git.h similarity index 100% rename from src/blame_git.h rename to src/libgit2/blame_git.h diff --git a/src/blob.c b/src/libgit2/blob.c similarity index 99% rename from src/blob.c rename to src/libgit2/blob.c index 19ce8b3b5..b1680d3a8 100644 --- a/src/blob.c +++ b/src/libgit2/blob.c @@ -101,7 +101,7 @@ static int write_file_stream( git_oid *id, git_odb *odb, const char *path, git_object_size_t file_size) { int fd, error; - char buffer[FILEIO_BUFSIZE]; + char buffer[GIT_BUFSIZE_FILEIO]; git_odb_stream *stream = NULL; ssize_t read_len = -1; git_object_size_t written = 0; diff --git a/src/blob.h b/src/libgit2/blob.h similarity index 100% rename from src/blob.h rename to src/libgit2/blob.h diff --git a/src/branch.c b/src/libgit2/branch.c similarity index 98% rename from src/branch.c rename to src/libgit2/branch.c index 2e29af99d..2dd7d2bb4 100644 --- a/src/branch.c +++ b/src/libgit2/branch.c @@ -53,6 +53,17 @@ static int not_a_local_branch(const char *reference_name) return -1; } +static bool branch_name_is_valid(const char *branch_name) +{ + /* + * Discourage branch name starting with dash, + * https://github.com/git/git/commit/6348624010888b + * and discourage HEAD as branch name, + * https://github.com/git/git/commit/a625b092cc5994 + */ + return branch_name[0] != '-' && git__strcmp(branch_name, "HEAD"); +} + static int create_branch( git_reference **ref_out, git_repository *repository, @@ -73,8 +84,8 @@ static int create_branch( GIT_ASSERT_ARG(ref_out); GIT_ASSERT_ARG(git_commit_owner(commit) == repository); - if (!git__strcmp(branch_name, "HEAD")) { - git_error_set(GIT_ERROR_REFERENCE, "'HEAD' is not a valid branch name"); + if (!branch_name_is_valid(branch_name)) { + git_error_set(GIT_ERROR_REFERENCE, "'%s' is not a valid branch name", branch_name); error = -1; goto cleanup; } @@ -797,13 +808,7 @@ int git_branch_name_is_valid(int *valid, const char *name) *valid = 0; - /* - * Discourage branch name starting with dash, - * https://github.com/git/git/commit/6348624010888b - * and discourage HEAD as branch name, - * https://github.com/git/git/commit/a625b092cc5994 - */ - if (!name || name[0] == '-' || !git__strcmp(name, "HEAD")) + if (!name || !branch_name_is_valid(name)) goto done; if ((error = git_str_puts(&ref_name, GIT_REFS_HEADS_DIR)) < 0 || diff --git a/src/branch.h b/src/libgit2/branch.h similarity index 100% rename from src/branch.h rename to src/libgit2/branch.h diff --git a/src/buf.c b/src/libgit2/buf.c similarity index 100% rename from src/buf.c rename to src/libgit2/buf.c diff --git a/src/buf.h b/src/libgit2/buf.h similarity index 100% rename from src/buf.h rename to src/libgit2/buf.h diff --git a/src/cache.c b/src/libgit2/cache.c similarity index 100% rename from src/cache.c rename to src/libgit2/cache.c diff --git a/src/cache.h b/src/libgit2/cache.h similarity index 100% rename from src/cache.h rename to src/libgit2/cache.h diff --git a/src/checkout.c b/src/libgit2/checkout.c similarity index 100% rename from src/checkout.c rename to src/libgit2/checkout.c diff --git a/src/checkout.h b/src/libgit2/checkout.h similarity index 100% rename from src/checkout.h rename to src/libgit2/checkout.h diff --git a/src/cherrypick.c b/src/libgit2/cherrypick.c similarity index 100% rename from src/cherrypick.c rename to src/libgit2/cherrypick.c diff --git a/src/clone.c b/src/libgit2/clone.c similarity index 100% rename from src/clone.c rename to src/libgit2/clone.c diff --git a/src/clone.h b/src/libgit2/clone.h similarity index 100% rename from src/clone.h rename to src/libgit2/clone.h diff --git a/src/commit.c b/src/libgit2/commit.c similarity index 100% rename from src/commit.c rename to src/libgit2/commit.c diff --git a/src/commit.h b/src/libgit2/commit.h similarity index 100% rename from src/commit.h rename to src/libgit2/commit.h diff --git a/src/commit_graph.c b/src/libgit2/commit_graph.c similarity index 97% rename from src/commit_graph.c rename to src/libgit2/commit_graph.c index 70e866b92..10947acec 100644 --- a/src/commit_graph.c +++ b/src/libgit2/commit_graph.c @@ -138,7 +138,7 @@ static int commit_graph_parse_oid_lookup( struct git_commit_graph_chunk *chunk_oid_lookup) { uint32_t i; - git_oid *oid, *prev_oid, zero_oid = {{0}}; + unsigned char *oid, *prev_oid, zero_oid[GIT_OID_RAWSZ] = {0}; if (chunk_oid_lookup->offset == 0) return commit_graph_error("missing OID Lookup chunk"); @@ -147,10 +147,10 @@ static int commit_graph_parse_oid_lookup( if (chunk_oid_lookup->length != file->num_commits * GIT_OID_RAWSZ) return commit_graph_error("OID Lookup chunk has wrong length"); - file->oid_lookup = oid = (git_oid *)(data + chunk_oid_lookup->offset); - prev_oid = &zero_oid; - for (i = 0; i < file->num_commits; ++i, ++oid) { - if (git_oid_cmp(prev_oid, oid) >= 0) + file->oid_lookup = oid = (unsigned char *)(data + chunk_oid_lookup->offset); + prev_oid = zero_oid; + for (i = 0; i < file->num_commits; ++i, oid += GIT_OID_RAWSZ) { + if (git_oid_raw_cmp(prev_oid, oid) >= 0) return commit_graph_error("OID Lookup index is non-monotonic"); prev_oid = oid; } @@ -437,7 +437,7 @@ static int git_commit_graph_entry_get_byindex( } commit_data = file->commit_data + pos * (GIT_OID_RAWSZ + 4 * sizeof(uint32_t)); - git_oid_cpy(&e->tree_oid, (const git_oid *)commit_data); + git_oid_fromraw(&e->tree_oid, commit_data); e->parent_indices[0] = ntohl(*((uint32_t *)(commit_data + GIT_OID_RAWSZ))); e->parent_indices[1] = ntohl( *((uint32_t *)(commit_data + GIT_OID_RAWSZ + sizeof(uint32_t)))); @@ -470,7 +470,8 @@ static int git_commit_graph_entry_get_byindex( e->parent_count++; } } - git_oid_cpy(&e->sha1, &file->oid_lookup[pos]); + + git_oid_fromraw(&e->sha1, &file->oid_lookup[pos * GIT_OID_RAWSZ]); return 0; } @@ -514,7 +515,7 @@ int git_commit_graph_entry_find( { int pos, found = 0; uint32_t hi, lo; - const git_oid *current = NULL; + const unsigned char *current = NULL; GIT_ASSERT_ARG(e); GIT_ASSERT_ARG(file); @@ -528,26 +529,25 @@ int git_commit_graph_entry_find( if (pos >= 0) { /* An object matching exactly the oid was found */ found = 1; - current = file->oid_lookup + pos; + current = file->oid_lookup + (pos * GIT_OID_RAWSZ); } else { /* No object was found */ /* pos refers to the object with the "closest" oid to short_oid */ pos = -1 - pos; if (pos < (int)file->num_commits) { - current = file->oid_lookup + pos; + current = file->oid_lookup + (pos * GIT_OID_RAWSZ); - if (!git_oid_ncmp(short_oid, current, len)) + if (!git_oid_raw_ncmp(short_oid->id, current, len)) found = 1; } } if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)file->num_commits) { /* Check for ambiguousity */ - const git_oid *next = current + 1; + const unsigned char *next = current + GIT_OID_RAWSZ; - if (!git_oid_ncmp(short_oid, next, len)) { + if (!git_oid_raw_ncmp(short_oid->id, next, len)) found = 2; - } } if (!found) @@ -1019,7 +1019,9 @@ static int commit_graph_write( /* Fill the OID Lookup table. */ git_vector_foreach (&w->commits, i, packed_commit) { error = git_str_put(&oid_lookup, - (const char *)&packed_commit->sha1, sizeof(git_oid)); + (const char *)&packed_commit->sha1.id, + GIT_OID_RAWSZ); + if (error < 0) goto cleanup; } @@ -1034,8 +1036,9 @@ static int commit_graph_write( unsigned int parentcount = (unsigned int)git_array_size(packed_commit->parents); error = git_str_put(&commit_data, - (const char *)&packed_commit->tree_oid, - sizeof(git_oid)); + (const char *)&packed_commit->tree_oid.id, + GIT_OID_RAWSZ); + if (error < 0) goto cleanup; diff --git a/src/commit_graph.h b/src/libgit2/commit_graph.h similarity index 99% rename from src/commit_graph.h rename to src/libgit2/commit_graph.h index 45e125b9e..b78ab8177 100644 --- a/src/commit_graph.h +++ b/src/libgit2/commit_graph.h @@ -36,7 +36,7 @@ typedef struct git_commit_graph_file { uint32_t num_commits; /* The OID Lookup table. */ - git_oid *oid_lookup; + unsigned char *oid_lookup; /* * The Commit Data table. Each entry contains the OID of the commit followed diff --git a/src/commit_list.c b/src/libgit2/commit_list.c similarity index 100% rename from src/commit_list.c rename to src/libgit2/commit_list.c diff --git a/src/commit_list.h b/src/libgit2/commit_list.h similarity index 100% rename from src/commit_list.h rename to src/libgit2/commit_list.h diff --git a/src/libgit2/common.h b/src/libgit2/common.h new file mode 100644 index 000000000..bb9ec5ac1 --- /dev/null +++ b/src/libgit2/common.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_common_h__ +#define INCLUDE_common_h__ + +#include "git2_util.h" +#include "errors.h" + +/* +* Include the declarations for deprecated functions; this ensures +* that they're decorated with the proper extern/visibility attributes. +*/ +#include "git2/deprecated.h" + +#include "posix.h" + +/** + * Initialize a structure with a version. + */ +GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int version) +{ + memset(structure, 0, len); + *((int*)structure) = version; +} +#define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V) + +#define GIT_INIT_STRUCTURE_FROM_TEMPLATE(PTR,VERSION,TYPE,TPL) do { \ + TYPE _tmpl = TPL; \ + GIT_ERROR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \ + memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0) + +/** + * Check a versioned structure for validity + */ +GIT_INLINE(int) git_error__check_version(const void *structure, unsigned int expected_max, const char *name) +{ + unsigned int actual; + + if (!structure) + return 0; + + actual = *(const unsigned int*)structure; + if (actual > 0 && actual <= expected_max) + return 0; + + git_error_set(GIT_ERROR_INVALID, "invalid version %d on %s", actual, name); + return -1; +} +#define GIT_ERROR_CHECK_VERSION(S,V,N) if (git_error__check_version(S,V,N) < 0) return -1 + +#endif diff --git a/src/config.c b/src/libgit2/config.c similarity index 99% rename from src/config.c rename to src/libgit2/config.c index 6bd59f2a5..5c366e221 100644 --- a/src/config.c +++ b/src/libgit2/config.c @@ -1137,7 +1137,7 @@ int git_config__find_global(git_str *path) int git_config_find_xdg(git_buf *path) { - GIT_BUF_WRAP_PRIVATE(path, git_sysdir_find_global_file, GIT_CONFIG_FILENAME_XDG); + GIT_BUF_WRAP_PRIVATE(path, git_sysdir_find_xdg_file, GIT_CONFIG_FILENAME_XDG); } int git_config__find_xdg(git_str *path) @@ -1147,7 +1147,7 @@ int git_config__find_xdg(git_str *path) int git_config_find_system(git_buf *path) { - GIT_BUF_WRAP_PRIVATE(path, git_sysdir_find_global_file, GIT_CONFIG_FILENAME_SYSTEM); + GIT_BUF_WRAP_PRIVATE(path, git_sysdir_find_system_file, GIT_CONFIG_FILENAME_SYSTEM); } int git_config__find_system(git_str *path) @@ -1170,10 +1170,13 @@ int git_config_find_programdata(git_buf *path) int git_config__find_programdata(git_str *path) { + git_fs_path_owner_t owner_level = + GIT_FS_PATH_OWNER_CURRENT_USER | + GIT_FS_PATH_OWNER_ADMINISTRATOR; bool is_safe; if (git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA) < 0 || - git_fs_path_owner_is_system_or_current_user(&is_safe, path->ptr) < 0) + git_fs_path_owner_is(&is_safe, path->ptr, owner_level) < 0) return -1; if (!is_safe) { diff --git a/src/config.h b/src/libgit2/config.h similarity index 100% rename from src/config.h rename to src/libgit2/config.h diff --git a/src/config_backend.h b/src/libgit2/config_backend.h similarity index 100% rename from src/config_backend.h rename to src/libgit2/config_backend.h diff --git a/src/config_cache.c b/src/libgit2/config_cache.c similarity index 100% rename from src/config_cache.c rename to src/libgit2/config_cache.c diff --git a/src/config_entries.c b/src/libgit2/config_entries.c similarity index 100% rename from src/config_entries.c rename to src/libgit2/config_entries.c diff --git a/src/config_entries.h b/src/libgit2/config_entries.h similarity index 100% rename from src/config_entries.h rename to src/libgit2/config_entries.h diff --git a/src/config_file.c b/src/libgit2/config_file.c similarity index 100% rename from src/config_file.c rename to src/libgit2/config_file.c diff --git a/src/config_mem.c b/src/libgit2/config_mem.c similarity index 100% rename from src/config_mem.c rename to src/libgit2/config_mem.c diff --git a/src/config_parse.c b/src/libgit2/config_parse.c similarity index 100% rename from src/config_parse.c rename to src/libgit2/config_parse.c diff --git a/src/config_parse.h b/src/libgit2/config_parse.h similarity index 100% rename from src/config_parse.h rename to src/libgit2/config_parse.h diff --git a/src/config_snapshot.c b/src/libgit2/config_snapshot.c similarity index 100% rename from src/config_snapshot.c rename to src/libgit2/config_snapshot.c diff --git a/src/crlf.c b/src/libgit2/crlf.c similarity index 100% rename from src/crlf.c rename to src/libgit2/crlf.c diff --git a/src/delta.c b/src/libgit2/delta.c similarity index 100% rename from src/delta.c rename to src/libgit2/delta.c diff --git a/src/delta.h b/src/libgit2/delta.h similarity index 100% rename from src/delta.h rename to src/libgit2/delta.h diff --git a/src/describe.c b/src/libgit2/describe.c similarity index 100% rename from src/describe.c rename to src/libgit2/describe.c diff --git a/src/diff.c b/src/libgit2/diff.c similarity index 100% rename from src/diff.c rename to src/libgit2/diff.c diff --git a/src/diff.h b/src/libgit2/diff.h similarity index 100% rename from src/diff.h rename to src/libgit2/diff.h diff --git a/src/diff_driver.c b/src/libgit2/diff_driver.c similarity index 100% rename from src/diff_driver.c rename to src/libgit2/diff_driver.c diff --git a/src/diff_driver.h b/src/libgit2/diff_driver.h similarity index 100% rename from src/diff_driver.h rename to src/libgit2/diff_driver.h diff --git a/src/diff_file.c b/src/libgit2/diff_file.c similarity index 100% rename from src/diff_file.c rename to src/libgit2/diff_file.c diff --git a/src/diff_file.h b/src/libgit2/diff_file.h similarity index 100% rename from src/diff_file.h rename to src/libgit2/diff_file.h diff --git a/src/diff_generate.c b/src/libgit2/diff_generate.c similarity index 100% rename from src/diff_generate.c rename to src/libgit2/diff_generate.c diff --git a/src/diff_generate.h b/src/libgit2/diff_generate.h similarity index 100% rename from src/diff_generate.h rename to src/libgit2/diff_generate.h diff --git a/src/diff_parse.c b/src/libgit2/diff_parse.c similarity index 100% rename from src/diff_parse.c rename to src/libgit2/diff_parse.c diff --git a/src/diff_parse.h b/src/libgit2/diff_parse.h similarity index 100% rename from src/diff_parse.h rename to src/libgit2/diff_parse.h diff --git a/src/diff_print.c b/src/libgit2/diff_print.c similarity index 100% rename from src/diff_print.c rename to src/libgit2/diff_print.c diff --git a/src/diff_stats.c b/src/libgit2/diff_stats.c similarity index 100% rename from src/diff_stats.c rename to src/libgit2/diff_stats.c diff --git a/src/diff_stats.h b/src/libgit2/diff_stats.h similarity index 100% rename from src/diff_stats.h rename to src/libgit2/diff_stats.h diff --git a/src/diff_tform.c b/src/libgit2/diff_tform.c similarity index 100% rename from src/diff_tform.c rename to src/libgit2/diff_tform.c diff --git a/src/diff_tform.h b/src/libgit2/diff_tform.h similarity index 100% rename from src/diff_tform.h rename to src/libgit2/diff_tform.h diff --git a/src/diff_xdiff.c b/src/libgit2/diff_xdiff.c similarity index 99% rename from src/diff_xdiff.c rename to src/libgit2/diff_xdiff.c index 3f6eccac1..5f56c5209 100644 --- a/src/diff_xdiff.c +++ b/src/libgit2/diff_xdiff.c @@ -11,6 +11,7 @@ #include "diff.h" #include "diff_driver.h" #include "patch_generate.h" +#include "utf8.h" static int git_xdiff_scan_int(const char **str, int *value) { diff --git a/src/diff_xdiff.h b/src/libgit2/diff_xdiff.h similarity index 100% rename from src/diff_xdiff.h rename to src/libgit2/diff_xdiff.h diff --git a/src/email.c b/src/libgit2/email.c similarity index 100% rename from src/email.c rename to src/libgit2/email.c diff --git a/src/email.h b/src/libgit2/email.h similarity index 100% rename from src/email.h rename to src/libgit2/email.h diff --git a/src/errors.c b/src/libgit2/errors.c similarity index 100% rename from src/errors.c rename to src/libgit2/errors.c diff --git a/src/errors.h b/src/libgit2/errors.h similarity index 93% rename from src/errors.h rename to src/libgit2/errors.h index a2f60f752..772c7bad1 100644 --- a/src/errors.h +++ b/src/libgit2/errors.h @@ -11,9 +11,8 @@ #include "common.h" /* - * Set the error message for this thread, formatting as needed. + * `vprintf`-style formatting for the error message for this thread. */ -void git_error_set(int error_class, const char *fmt, ...) GIT_FORMAT_PRINTF(2, 3); void git_error_vset(int error_class, const char *fmt, va_list ap); /** diff --git a/src/fetch.c b/src/libgit2/fetch.c similarity index 100% rename from src/fetch.c rename to src/libgit2/fetch.c diff --git a/src/fetch.h b/src/libgit2/fetch.h similarity index 100% rename from src/fetch.h rename to src/libgit2/fetch.h diff --git a/src/fetchhead.c b/src/libgit2/fetchhead.c similarity index 100% rename from src/fetchhead.c rename to src/libgit2/fetchhead.c diff --git a/src/fetchhead.h b/src/libgit2/fetchhead.h similarity index 100% rename from src/fetchhead.h rename to src/libgit2/fetchhead.h diff --git a/src/filter.c b/src/libgit2/filter.c similarity index 96% rename from src/filter.c rename to src/libgit2/filter.c index 2712e8c60..80a3cae67 100644 --- a/src/filter.c +++ b/src/libgit2/filter.c @@ -893,6 +893,17 @@ static int buffered_stream_write( return git_str_put(&buffered_stream->input, buffer, len); } +#ifndef GIT_DEPRECATE_HARD +# define BUF_TO_STRUCT(b, s) \ + (b)->ptr = (s)->ptr; \ + (b)->size = (s)->size; \ + (b)->reserved = (s)->asize; +# define STRUCT_TO_BUF(s, b) \ + (s)->ptr = (b)->ptr; \ + (s)->size = (b)->size; \ + (s)->asize = (b)->reserved; +#endif + static int buffered_stream_close(git_writestream *s) { struct buffered_stream *buffered_stream = (struct buffered_stream *)s; @@ -902,6 +913,25 @@ static int buffered_stream_close(git_writestream *s) GIT_ASSERT_ARG(buffered_stream); +#ifndef GIT_DEPRECATE_HARD + if (buffered_stream->write_fn == NULL) { + git_buf legacy_output = GIT_BUF_INIT, + legacy_input = GIT_BUF_INIT; + + BUF_TO_STRUCT(&legacy_output, buffered_stream->output); + BUF_TO_STRUCT(&legacy_input, &buffered_stream->input); + + error = buffered_stream->legacy_write_fn( + buffered_stream->filter, + buffered_stream->payload, + &legacy_output, + &legacy_input, + buffered_stream->source); + + STRUCT_TO_BUF(buffered_stream->output, &legacy_output); + STRUCT_TO_BUF(&buffered_stream->input, &legacy_input); + } else +#endif error = buffered_stream->write_fn( buffered_stream->filter, buffered_stream->payload, @@ -1085,7 +1115,7 @@ int git_filter_list_stream_file( const char *path, git_writestream *target) { - char buf[FILTERIO_BUFSIZE]; + char buf[GIT_BUFSIZE_FILTERIO]; git_str abspath = GIT_STR_INIT; const char *base = repo ? git_repository_workdir(repo) : NULL; git_vector filter_streams = GIT_VECTOR_INIT; diff --git a/src/filter.h b/src/libgit2/filter.h similarity index 100% rename from src/filter.h rename to src/libgit2/filter.h diff --git a/src/win32/git2.rc b/src/libgit2/git2.rc similarity index 100% rename from src/win32/git2.rc rename to src/libgit2/git2.rc diff --git a/src/graph.c b/src/libgit2/graph.c similarity index 100% rename from src/graph.c rename to src/libgit2/graph.c diff --git a/src/hashsig.c b/src/libgit2/hashsig.c similarity index 100% rename from src/hashsig.c rename to src/libgit2/hashsig.c diff --git a/src/ident.c b/src/libgit2/ident.c similarity index 100% rename from src/ident.c rename to src/libgit2/ident.c diff --git a/src/idxmap.c b/src/libgit2/idxmap.c similarity index 100% rename from src/idxmap.c rename to src/libgit2/idxmap.c diff --git a/src/idxmap.h b/src/libgit2/idxmap.h similarity index 100% rename from src/idxmap.h rename to src/libgit2/idxmap.h diff --git a/src/ignore.c b/src/libgit2/ignore.c similarity index 100% rename from src/ignore.c rename to src/libgit2/ignore.c diff --git a/src/ignore.h b/src/libgit2/ignore.h similarity index 100% rename from src/ignore.h rename to src/libgit2/ignore.h diff --git a/src/index.c b/src/libgit2/index.c similarity index 99% rename from src/index.c rename to src/libgit2/index.c index aa97c6421..f44c507d3 100644 --- a/src/index.c +++ b/src/libgit2/index.c @@ -74,7 +74,7 @@ struct entry_short { uint32_t uid; uint32_t gid; uint32_t file_size; - git_oid oid; + unsigned char oid[GIT_OID_RAWSZ]; uint16_t flags; char path[1]; /* arbitrary length */ }; @@ -88,7 +88,7 @@ struct entry_long { uint32_t uid; uint32_t gid; uint32_t file_size; - git_oid oid; + unsigned char oid[GIT_OID_RAWSZ]; uint16_t flags; uint16_t flags_extended; char path[1]; /* arbitrary length */ @@ -2480,9 +2480,11 @@ static int read_entry( entry.uid = ntohl(source.uid); entry.gid = ntohl(source.gid); entry.file_size = ntohl(source.file_size); - git_oid_cpy(&entry.id, &source.oid); entry.flags = ntohs(source.flags); + if (git_oid_fromraw(&entry.id, source.oid) < 0) + return -1; + if (entry.flags & GIT_INDEX_ENTRY_EXTENDED) { uint16_t flags_raw; size_t flags_offset; @@ -2803,9 +2805,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const cha ondisk.uid = htonl(entry->uid); ondisk.gid = htonl(entry->gid); ondisk.file_size = htonl((uint32_t)entry->file_size); - - git_oid_cpy(&ondisk.oid, &entry->id); - + git_oid_raw_cpy(ondisk.oid, entry->id.id); ondisk.flags = htons(entry->flags); if (entry->flags & GIT_INDEX_ENTRY_EXTENDED) { diff --git a/src/index.h b/src/libgit2/index.h similarity index 100% rename from src/index.h rename to src/libgit2/index.h diff --git a/src/indexer.c b/src/libgit2/indexer.c similarity index 99% rename from src/indexer.c rename to src/libgit2/indexer.c index f9a32e7ac..afb9ce81a 100644 --- a/src/indexer.c +++ b/src/libgit2/indexer.c @@ -385,7 +385,7 @@ static int check_object_connectivity(git_indexer *idx, const git_rawobj *obj) size_t i; git_array_foreach(tree->entries, i, entry) - if (add_expected_oid(idx, entry->oid) < 0) + if (add_expected_oid(idx, &entry->oid) < 0) goto out; break; @@ -1269,7 +1269,7 @@ int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats) /* Write out the object names (SHA-1 hashes) */ git_vector_foreach(&idx->objects, i, entry) { - git_filebuf_write(&index_file, &entry->oid, sizeof(git_oid)); + git_filebuf_write(&index_file, &entry->oid.id, GIT_OID_RAWSZ); } /* Write out the CRC32 values */ diff --git a/src/indexer.h b/src/libgit2/indexer.h similarity index 100% rename from src/indexer.h rename to src/libgit2/indexer.h diff --git a/src/iterator.c b/src/libgit2/iterator.c similarity index 99% rename from src/iterator.c rename to src/libgit2/iterator.c index 15bb63dc8..bc6f766ce 100644 --- a/src/iterator.c +++ b/src/libgit2/iterator.c @@ -613,7 +613,7 @@ GIT_INLINE(int) tree_iterator_frame_push_neighbors( break; if ((error = git_tree_lookup(&tree, - iter->base.repo, entry->tree_entry->oid)) < 0) + iter->base.repo, &entry->tree_entry->oid)) < 0) break; if (git_vector_insert(&parent_frame->similar_trees, tree) < 0) @@ -659,7 +659,7 @@ GIT_INLINE(int) tree_iterator_frame_push( int error; if ((error = git_tree_lookup(&tree, - iter->base.repo, entry->tree_entry->oid)) < 0 || + iter->base.repo, &entry->tree_entry->oid)) < 0 || (error = tree_iterator_frame_init(iter, tree, entry)) < 0) goto done; @@ -740,7 +740,7 @@ static void tree_iterator_set_current( iter->entry.mode = tree_entry->attr; iter->entry.path = iter->entry_path.ptr; - git_oid_cpy(&iter->entry.id, tree_entry->oid); + git_oid_cpy(&iter->entry.id, &tree_entry->oid); } static int tree_iterator_advance(const git_index_entry **out, git_iterator *i) diff --git a/src/iterator.h b/src/libgit2/iterator.h similarity index 100% rename from src/iterator.h rename to src/libgit2/iterator.h diff --git a/src/libgit2.c b/src/libgit2/libgit2.c similarity index 99% rename from src/libgit2.c rename to src/libgit2/libgit2.c index c99d5b942..2fda0722e 100644 --- a/src/libgit2.c +++ b/src/libgit2/libgit2.c @@ -107,6 +107,11 @@ int git_libgit2_version(int *major, int *minor, int *rev) return 0; } +const char *git_libgit2_prerelease(void) +{ + return LIBGIT2_VER_PRERELEASE; +} + int git_libgit2_features(void) { return 0 diff --git a/src/libgit2.h b/src/libgit2/libgit2.h similarity index 100% rename from src/libgit2.h rename to src/libgit2/libgit2.h diff --git a/src/mailmap.c b/src/libgit2/mailmap.c similarity index 100% rename from src/mailmap.c rename to src/libgit2/mailmap.c diff --git a/src/mailmap.h b/src/libgit2/mailmap.h similarity index 100% rename from src/mailmap.h rename to src/libgit2/mailmap.h diff --git a/src/merge.c b/src/libgit2/merge.c similarity index 100% rename from src/merge.c rename to src/libgit2/merge.c diff --git a/src/merge.h b/src/libgit2/merge.h similarity index 100% rename from src/merge.h rename to src/libgit2/merge.h diff --git a/src/merge_driver.c b/src/libgit2/merge_driver.c similarity index 100% rename from src/merge_driver.c rename to src/libgit2/merge_driver.c diff --git a/src/merge_driver.h b/src/libgit2/merge_driver.h similarity index 100% rename from src/merge_driver.h rename to src/libgit2/merge_driver.h diff --git a/src/merge_file.c b/src/libgit2/merge_file.c similarity index 100% rename from src/merge_file.c rename to src/libgit2/merge_file.c diff --git a/src/message.c b/src/libgit2/message.c similarity index 100% rename from src/message.c rename to src/libgit2/message.c diff --git a/src/midx.c b/src/libgit2/midx.c similarity index 95% rename from src/midx.c rename to src/libgit2/midx.c index 0092601f6..67eab9acb 100644 --- a/src/midx.c +++ b/src/libgit2/midx.c @@ -115,7 +115,7 @@ static int midx_parse_oid_lookup( struct git_midx_chunk *chunk_oid_lookup) { uint32_t i; - git_oid *oid, *prev_oid, zero_oid = {{0}}; + unsigned char *oid, *prev_oid, zero_oid[GIT_OID_RAWSZ] = {0}; if (chunk_oid_lookup->offset == 0) return midx_error("missing OID Lookup chunk"); @@ -124,10 +124,10 @@ static int midx_parse_oid_lookup( if (chunk_oid_lookup->length != idx->num_objects * GIT_OID_RAWSZ) return midx_error("OID Lookup chunk has wrong length"); - idx->oid_lookup = oid = (git_oid *)(data + chunk_oid_lookup->offset); - prev_oid = &zero_oid; - for (i = 0; i < idx->num_objects; ++i, ++oid) { - if (git_oid_cmp(prev_oid, oid) >= 0) + idx->oid_lookup = oid = (unsigned char *)(data + chunk_oid_lookup->offset); + prev_oid = zero_oid; + for (i = 0; i < idx->num_objects; ++i, oid += GIT_OID_RAWSZ) { + if (git_oid_raw_cmp(prev_oid, oid) >= 0) return midx_error("OID Lookup index is non-monotonic"); prev_oid = oid; } @@ -179,7 +179,6 @@ int git_midx_parse( uint32_t i; off64_t last_chunk_offset, chunk_offset, trailer_offset; size_t checksum_size; - unsigned char checksum[GIT_HASH_SHA1_SIZE]; int error; struct git_midx_chunk chunk_packfile_names = {0}, chunk_oid_fanout = {0}, @@ -217,11 +216,6 @@ int git_midx_parse( return midx_error("wrong index size"); memcpy(idx->checksum, data + trailer_offset, checksum_size); - if (git_hash_buf(checksum, data, (size_t)trailer_offset, GIT_HASH_ALGORITHM_SHA1) < 0) - return midx_error("could not calculate signature"); - if (memcmp(checksum, idx->checksum, checksum_size) != 0) - return midx_error("index signature mismatch"); - chunk_hdr = data + sizeof(struct git_midx_header); last_chunk = NULL; for (i = 0; i < hdr->chunks; ++i, chunk_hdr += 12) { @@ -389,7 +383,7 @@ int git_midx_entry_find( int pos, found = 0; size_t pack_index; uint32_t hi, lo; - const git_oid *current = NULL; + unsigned char *current = NULL; const unsigned char *object_offset; off64_t offset; @@ -403,26 +397,25 @@ int git_midx_entry_find( if (pos >= 0) { /* An object matching exactly the oid was found */ found = 1; - current = idx->oid_lookup + pos; + current = idx->oid_lookup + (pos * GIT_OID_RAWSZ); } else { /* No object was found */ /* pos refers to the object with the "closest" oid to short_oid */ pos = -1 - pos; if (pos < (int)idx->num_objects) { - current = idx->oid_lookup + pos; + current = idx->oid_lookup + (pos * GIT_OID_RAWSZ); - if (!git_oid_ncmp(short_oid, current, len)) + if (!git_oid_raw_ncmp(short_oid->id, current, len)) found = 1; } } if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)idx->num_objects) { /* Check for ambiguousity */ - const git_oid *next = current + 1; + const unsigned char *next = current + GIT_OID_RAWSZ; - if (!git_oid_ncmp(short_oid, next, len)) { + if (!git_oid_raw_ncmp(short_oid->id, next, len)) found = 2; - } } if (!found) @@ -432,8 +425,8 @@ int git_midx_entry_find( object_offset = idx->object_offsets + pos * 8; offset = ntohl(*((uint32_t *)(object_offset + 4))); - if (offset & 0x80000000) { - uint32_t object_large_offsets_pos = offset & 0x7fffffff; + if (idx->object_large_offsets && offset & 0x80000000) { + uint32_t object_large_offsets_pos = (uint32_t) (offset ^ 0x80000000); const unsigned char *object_large_offsets_index = idx->object_large_offsets; /* Make sure we're not being sent out of bounds */ @@ -450,7 +443,7 @@ int git_midx_entry_find( return midx_error("invalid index into the packfile names table"); e->pack_index = pack_index; e->offset = offset; - git_oid_cpy(&e->sha1, current); + git_oid_fromraw(&e->sha1, current); return 0; } @@ -459,13 +452,17 @@ int git_midx_foreach_entry( git_odb_foreach_cb cb, void *data) { + git_oid oid; size_t i; int error; GIT_ASSERT_ARG(idx); for (i = 0; i < idx->num_objects; ++i) { - if ((error = cb(&idx->oid_lookup[i], data)) != 0) + if ((error = git_oid_fromraw(&oid, &idx->oid_lookup[i * GIT_OID_RAWSZ])) < 0) + return error; + + if ((error = cb(&oid, data)) != 0) return git_error_set_after_callback(error); } @@ -751,7 +748,7 @@ static int midx_write( /* Fill the OID Lookup table. */ git_vector_foreach (&object_entries, i, entry) { - error = git_str_put(&oid_lookup, (const char *)&entry->sha1, sizeof(entry->sha1)); + error = git_str_put(&oid_lookup, (char *)&entry->sha1.id, GIT_OID_RAWSZ); if (error < 0) goto cleanup; } diff --git a/src/midx.h b/src/libgit2/midx.h similarity index 98% rename from src/midx.h rename to src/libgit2/midx.h index 7dd851bd3..bcb0d9a0a 100644 --- a/src/midx.h +++ b/src/libgit2/midx.h @@ -17,6 +17,7 @@ #include "map.h" #include "mwindow.h" #include "odb.h" +#include "oid.h" /* * A multi-pack-index file. @@ -40,7 +41,7 @@ typedef struct git_midx_file { uint32_t num_objects; /* The OID Lookup table. */ - git_oid *oid_lookup; + unsigned char *oid_lookup; /* The Object Offsets table. Each entry has two 4-byte fields with the pack index and the offset. */ const unsigned char *object_offsets; diff --git a/src/mwindow.c b/src/libgit2/mwindow.c similarity index 95% rename from src/mwindow.c rename to src/libgit2/mwindow.c index d06b7a80e..ad649490a 100644 --- a/src/mwindow.c +++ b/src/libgit2/mwindow.c @@ -186,13 +186,16 @@ int git_mwindow_free_all(git_mwindow_file *mwf) } /* - * Check if a window 'win' contains the address 'offset' + * Check if a window 'win' contains both the address 'offset' and 'extra'. + * + * 'extra' is the size of the hash we're using as we always want to make sure + * that it's contained. */ -int git_mwindow_contains(git_mwindow *win, off64_t offset) +int git_mwindow_contains(git_mwindow *win, off64_t offset, off64_t extra) { off64_t win_off = win->offset; return win_off <= offset - && offset <= (off64_t)(win_off + win->window_map.len); + && (offset + extra) <= (off64_t)(win_off + win->window_map.len); } #define GIT_MWINDOW__LRU -1 @@ -237,9 +240,7 @@ static bool git_mwindow_scan_recently_used( * store it in the output parameter. If lru_window is NULL, * it's the first loop, so store it as well. */ - if (!lru_window || - (comparison_sign == GIT_MWINDOW__LRU && lru_window->last_used > w->last_used) || - (comparison_sign == GIT_MWINDOW__MRU && lru_window->last_used < w->last_used)) { + if (!lru_window || (comparison_sign * w->last_used) > lru_window->last_used) { lru_window = w; lru_last = w_last; found = true; @@ -406,14 +407,13 @@ unsigned char *git_mwindow_open( return NULL; } - if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) { + if (!w || !(git_mwindow_contains(w, offset, extra))) { if (w) { w->inuse_cnt--; } for (w = mwf->windows; w; w = w->next) { - if (git_mwindow_contains(w, offset) && - git_mwindow_contains(w, offset + extra)) + if (git_mwindow_contains(w, offset, extra)) break; } diff --git a/src/mwindow.h b/src/libgit2/mwindow.h similarity index 95% rename from src/mwindow.h rename to src/libgit2/mwindow.h index e3a03f019..e32ab99d4 100644 --- a/src/mwindow.h +++ b/src/libgit2/mwindow.h @@ -38,7 +38,7 @@ typedef struct git_mwindow_ctl { git_vector windowfiles; } git_mwindow_ctl; -int git_mwindow_contains(git_mwindow *win, off64_t offset); +int git_mwindow_contains(git_mwindow *win, off64_t offset, off64_t extra); int git_mwindow_free_all(git_mwindow_file *mwf); /* locks */ unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, off64_t offset, size_t extra, unsigned int *left); int git_mwindow_file_register(git_mwindow_file *mwf); diff --git a/src/netops.c b/src/libgit2/netops.c similarity index 99% rename from src/netops.c rename to src/libgit2/netops.c index 0a27365b8..00640c600 100644 --- a/src/netops.c +++ b/src/libgit2/netops.c @@ -12,7 +12,6 @@ #include "posix.h" #include "str.h" -#include "http_parser.h" #include "runtime.h" int gitno_recv(gitno_buffer *buf) diff --git a/src/netops.h b/src/libgit2/netops.h similarity index 100% rename from src/netops.h rename to src/libgit2/netops.h diff --git a/src/notes.c b/src/libgit2/notes.c similarity index 100% rename from src/notes.c rename to src/libgit2/notes.c diff --git a/src/notes.h b/src/libgit2/notes.h similarity index 100% rename from src/notes.h rename to src/libgit2/notes.h diff --git a/src/object.c b/src/libgit2/object.c similarity index 99% rename from src/object.c rename to src/libgit2/object.c index 7bc256fce..b828f88f6 100644 --- a/src/object.c +++ b/src/libgit2/object.c @@ -250,6 +250,7 @@ int git_object_lookup_prefix( if (error < 0) return error; + GIT_ASSERT(odb_obj); error = git_object__from_odb_object(object_out, repo, odb_obj, type); git_odb_object_free(odb_obj); diff --git a/src/object.h b/src/libgit2/object.h similarity index 100% rename from src/object.h rename to src/libgit2/object.h diff --git a/src/object_api.c b/src/libgit2/object_api.c similarity index 100% rename from src/object_api.c rename to src/libgit2/object_api.c diff --git a/src/odb.c b/src/libgit2/odb.c similarity index 99% rename from src/odb.c rename to src/libgit2/odb.c index 14eff53c7..7b98c72ee 100644 --- a/src/odb.c +++ b/src/libgit2/odb.c @@ -198,7 +198,7 @@ void git_odb_object_free(git_odb_object *object) int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_object_t type) { size_t hdr_len; - char hdr[64], buffer[FILEIO_BUFSIZE]; + char hdr[64], buffer[GIT_BUFSIZE_FILEIO]; git_hash_ctx ctx; ssize_t read_len = 0; int error = 0; @@ -1067,7 +1067,7 @@ int git_odb_expand_ids( int git_odb_read_header(size_t *len_p, git_object_t *type_p, git_odb *db, const git_oid *id) { int error; - git_odb_object *object; + git_odb_object *object = NULL; error = git_odb__read_header_or_object(&object, len_p, type_p, db, id); diff --git a/src/odb.h b/src/libgit2/odb.h similarity index 100% rename from src/odb.h rename to src/libgit2/odb.h diff --git a/src/odb_loose.c b/src/libgit2/odb_loose.c similarity index 100% rename from src/odb_loose.c rename to src/libgit2/odb_loose.c diff --git a/src/odb_mempack.c b/src/libgit2/odb_mempack.c similarity index 100% rename from src/odb_mempack.c rename to src/libgit2/odb_mempack.c diff --git a/src/odb_pack.c b/src/libgit2/odb_pack.c similarity index 100% rename from src/odb_pack.c rename to src/libgit2/odb_pack.c diff --git a/src/offmap.c b/src/libgit2/offmap.c similarity index 100% rename from src/offmap.c rename to src/libgit2/offmap.c diff --git a/src/offmap.h b/src/libgit2/offmap.h similarity index 100% rename from src/offmap.h rename to src/libgit2/offmap.h diff --git a/src/oid.c b/src/libgit2/oid.c similarity index 96% rename from src/oid.c rename to src/libgit2/oid.c index 19061e899..fb92174ab 100644 --- a/src/oid.c +++ b/src/libgit2/oid.c @@ -187,8 +187,7 @@ int git_oid_fromraw(git_oid *out, const unsigned char *raw) int git_oid_cpy(git_oid *out, const git_oid *src) { - memcpy(out->id, src->id, sizeof(out->id)); - return 0; + return git_oid_raw_cpy(out->id, src->id); } int git_oid_cmp(const git_oid *a, const git_oid *b) @@ -203,25 +202,7 @@ int git_oid_equal(const git_oid *a, const git_oid *b) int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len) { - const unsigned char *a = oid_a->id; - const unsigned char *b = oid_b->id; - - if (len > GIT_OID_HEXSZ) - len = GIT_OID_HEXSZ; - - while (len > 1) { - if (*a != *b) - return 1; - a++; - b++; - len -= 2; - }; - - if (len) - if ((*a ^ *b) & 0xf0) - return 1; - - return 0; + return git_oid_raw_ncmp(oid_a->id, oid_b->id, len); } int git_oid_strcmp(const git_oid *oid_a, const char *str) diff --git a/src/oid.h b/src/libgit2/oid.h similarity index 70% rename from src/oid.h rename to src/libgit2/oid.h index 5baec33e5..abae9a4b2 100644 --- a/src/oid.h +++ b/src/libgit2/oid.h @@ -25,11 +25,44 @@ extern const git_oid git_oid__empty_tree_sha1; */ char *git_oid_allocfmt(const git_oid *id); -GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2) +GIT_INLINE(int) git_oid_raw_ncmp( + const unsigned char *sha1, + const unsigned char *sha2, + size_t len) +{ + if (len > GIT_OID_HEXSZ) + len = GIT_OID_HEXSZ; + + while (len > 1) { + if (*sha1 != *sha2) + return 1; + sha1++; + sha2++; + len -= 2; + }; + + if (len) + if ((*sha1 ^ *sha2) & 0xf0) + return 1; + + return 0; +} + +GIT_INLINE(int) git_oid_raw_cmp( + const unsigned char *sha1, + const unsigned char *sha2) { return memcmp(sha1, sha2, GIT_OID_RAWSZ); } +GIT_INLINE(int) git_oid_raw_cpy( + unsigned char *dst, + const unsigned char *src) +{ + memcpy(dst, src, GIT_OID_RAWSZ); + return 0; +} + /* * Compare two oid structures. * @@ -39,7 +72,7 @@ GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char */ GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b) { - return git_oid__hashcmp(a->id, b->id); + return git_oid_raw_cmp(a->id, b->id); } GIT_INLINE(void) git_oid__cpy_prefix( diff --git a/src/oidarray.c b/src/libgit2/oidarray.c similarity index 100% rename from src/oidarray.c rename to src/libgit2/oidarray.c diff --git a/src/oidarray.h b/src/libgit2/oidarray.h similarity index 100% rename from src/oidarray.h rename to src/libgit2/oidarray.h diff --git a/src/oidmap.c b/src/libgit2/oidmap.c similarity index 98% rename from src/oidmap.c rename to src/libgit2/oidmap.c index 0ae8bf33e..eaf9fa051 100644 --- a/src/oidmap.c +++ b/src/libgit2/oidmap.c @@ -19,7 +19,7 @@ __KHASH_TYPE(oid, const git_oid *, void *) GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid) { khint_t h; - memcpy(&h, oid, sizeof(khint_t)); + memcpy(&h, oid->id, sizeof(khint_t)); return h; } diff --git a/src/oidmap.h b/src/libgit2/oidmap.h similarity index 100% rename from src/oidmap.h rename to src/libgit2/oidmap.h diff --git a/src/pack-objects.c b/src/libgit2/pack-objects.c similarity index 100% rename from src/pack-objects.c rename to src/libgit2/pack-objects.c diff --git a/src/pack-objects.h b/src/libgit2/pack-objects.h similarity index 100% rename from src/pack-objects.h rename to src/libgit2/pack-objects.h diff --git a/src/pack.c b/src/libgit2/pack.c similarity index 97% rename from src/pack.c rename to src/libgit2/pack.c index 5c0cba7e8..16fe378bd 100644 --- a/src/pack.c +++ b/src/libgit2/pack.c @@ -1001,13 +1001,14 @@ int get_delta_base( base_offset = delta_obj_offset - unsigned_base_offset; *curpos += used; } else if (type == GIT_OBJECT_REF_DELTA) { + git_oid base_oid; + git_oid_fromraw(&base_oid, base_info); + /* If we have the cooperative cache, search in it first */ if (p->has_cache) { struct git_pack_entry *entry; - git_oid oid; - git_oid_fromraw(&oid, base_info); - if ((entry = git_oidmap_get(p->idx_cache, &oid)) != NULL) { + if ((entry = git_oidmap_get(p->idx_cache, &base_oid)) != NULL) { if (entry->offset == 0) return packfile_error("delta offset is zero"); @@ -1024,7 +1025,7 @@ int get_delta_base( } /* The base entry _must_ be in the same pack */ - if (pack_entry_find_offset(&base_offset, &unused, p, (git_oid *)base_info, GIT_OID_HEXSZ) < 0) + if (pack_entry_find_offset(&base_offset, &unused, p, &base_oid, GIT_OID_HEXSZ) < 0) return packfile_error("base entry delta is not in the same pack"); *curpos += 20; } else @@ -1082,7 +1083,7 @@ static int packfile_open_locked(struct git_pack_file *p) { struct stat st; struct git_pack_header hdr; - git_oid sha1; + unsigned char sha1[GIT_OID_RAWSZ]; unsigned char *idx_sha1; if (pack_index_open_locked(p) < 0) @@ -1130,12 +1131,12 @@ static int packfile_open_locked(struct git_pack_file *p) /* Verify the pack matches its index. */ if (p->num_objects != ntohl(hdr.hdr_entries) || - p_pread(p->mwf.fd, sha1.id, GIT_OID_RAWSZ, p->mwf.size - GIT_OID_RAWSZ) < 0) + p_pread(p->mwf.fd, sha1, GIT_OID_RAWSZ, p->mwf.size - GIT_OID_RAWSZ) < 0) goto cleanup; idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40; - if (git_oid__cmp(&sha1, (git_oid *)idx_sha1) != 0) + if (git_oid_raw_cmp(sha1, idx_sha1) != 0) goto cleanup; if (git_mwindow_file_register(&p->mwf) < 0) @@ -1340,10 +1341,14 @@ int git_pack_foreach_entry( } git_vector_free(&offsets); - p->oids = (git_oid **)git_vector_detach(NULL, NULL, &oids); + p->oids = (unsigned char **)git_vector_detach(NULL, NULL, &oids); } - /* We need to copy the OIDs to another array before we relinquish the lock to avoid races. */ + /* + * We need to copy the OIDs to another array before we + * relinquish the lock to avoid races. We can also take + * this opportunity to put them into normal form. + */ git_array_init_to_size(oids, p->num_objects); if (!oids.ptr) { git_mutex_unlock(&p->lock); @@ -1357,7 +1362,7 @@ int git_pack_foreach_entry( git_array_clear(oids); GIT_ERROR_CHECK_ALLOC(oid); } - git_oid_cpy(oid, p->oids[i]); + git_oid_fromraw(oid, p->oids[i]); } git_mutex_unlock(&p->lock); @@ -1380,7 +1385,7 @@ int git_pack_foreach_entry_offset( { const unsigned char *index; off64_t current_offset; - const git_oid *current_oid; + git_oid current_oid; uint32_t i; int error = 0; @@ -1422,8 +1427,9 @@ int git_pack_foreach_entry_offset( current_offset = (((off64_t)ntohl(*((uint32_t *)(large_offset_ptr + 0)))) << 32) | ntohl(*((uint32_t *)(large_offset_ptr + 4))); } - current_oid = (const git_oid *)(index + 20 * i); - if ((error = cb(current_oid, current_offset, data)) != 0) { + + git_oid_fromraw(¤t_oid, (index + 20 * i)); + if ((error = cb(¤t_oid, current_offset, data)) != 0) { error = git_error_set_after_callback(error); goto cleanup; } @@ -1431,8 +1437,8 @@ int git_pack_foreach_entry_offset( } else { for (i = 0; i < p->num_objects; i++) { current_offset = ntohl(*(const uint32_t *)(index + 24 * i)); - current_oid = (const git_oid *)(index + 24 * i + 4); - if ((error = cb(current_oid, current_offset, data)) != 0) { + git_oid_fromraw(¤t_oid, (index + 24 * i + 4)); + if ((error = cb(¤t_oid, current_offset, data)) != 0) { error = git_error_set_after_callback(error); goto cleanup; } @@ -1451,7 +1457,7 @@ int git_pack__lookup_sha1(const void *oid_lookup_table, size_t stride, unsigned while (lo < hi) { unsigned mi = (lo + hi) / 2; - int cmp = git_oid__hashcmp(base + mi * stride, oid_prefix); + int cmp = git_oid_raw_cmp(base + mi * stride, oid_prefix); if (!cmp) return mi; @@ -1530,7 +1536,7 @@ static int pack_entry_find_offset( if (pos < (int)p->num_objects) { current = index + pos * stride; - if (!git_oid_ncmp(short_oid, (const git_oid *)current, len)) + if (!git_oid_raw_ncmp(short_oid->id, current, len)) found = 1; } } @@ -1539,7 +1545,7 @@ static int pack_entry_find_offset( /* Check for ambiguousity */ const unsigned char *next = current + stride; - if (!git_oid_ncmp(short_oid, (const git_oid *)next, len)) { + if (!git_oid_raw_ncmp(short_oid->id, next, len)) { found = 2; } } diff --git a/src/pack.h b/src/libgit2/pack.h similarity index 98% rename from src/pack.h rename to src/libgit2/pack.h index bf279c6b6..d90588f79 100644 --- a/src/pack.h +++ b/src/libgit2/pack.h @@ -19,6 +19,7 @@ #include "offmap.h" #include "oidmap.h" #include "zstream.h" +#include "oid.h" /** * Function type for callbacks from git_pack_foreach_entry_offset. @@ -32,7 +33,7 @@ typedef int git_pack_foreach_entry_offset_cb( #define PACK_SIGNATURE 0x5041434b /* "PACK" */ #define PACK_VERSION 2 -#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3)) +#define pack_version_ok(v) ((v) == htonl(2)) struct git_pack_header { uint32_t hdr_signature; uint32_t hdr_version; @@ -104,7 +105,7 @@ struct git_pack_file { git_time_t mtime; unsigned pack_local:1, pack_keep:1, has_cache:1; git_oidmap *idx_cache; - git_oid **oids; + unsigned char **oids; git_pack_cache bases; /* delta base cache */ diff --git a/src/parse.c b/src/libgit2/parse.c similarity index 100% rename from src/parse.c rename to src/libgit2/parse.c diff --git a/src/parse.h b/src/libgit2/parse.h similarity index 100% rename from src/parse.h rename to src/libgit2/parse.h diff --git a/src/patch.c b/src/libgit2/patch.c similarity index 100% rename from src/patch.c rename to src/libgit2/patch.c diff --git a/src/patch.h b/src/libgit2/patch.h similarity index 100% rename from src/patch.h rename to src/libgit2/patch.h diff --git a/src/patch_generate.c b/src/libgit2/patch_generate.c similarity index 100% rename from src/patch_generate.c rename to src/libgit2/patch_generate.c diff --git a/src/patch_generate.h b/src/libgit2/patch_generate.h similarity index 100% rename from src/patch_generate.h rename to src/libgit2/patch_generate.h diff --git a/src/patch_parse.c b/src/libgit2/patch_parse.c similarity index 100% rename from src/patch_parse.c rename to src/libgit2/patch_parse.c diff --git a/src/patch_parse.h b/src/libgit2/patch_parse.h similarity index 100% rename from src/patch_parse.h rename to src/libgit2/patch_parse.h diff --git a/src/path.c b/src/libgit2/path.c similarity index 99% rename from src/path.c rename to src/libgit2/path.c index 05a3dc2cf..a19340efe 100644 --- a/src/path.c +++ b/src/libgit2/path.c @@ -9,6 +9,7 @@ #include "repository.h" #include "fs_path.h" +#include "utf8.h" typedef struct { git_repository *repo; diff --git a/src/path.h b/src/libgit2/path.h similarity index 100% rename from src/path.h rename to src/libgit2/path.h diff --git a/src/pathspec.c b/src/libgit2/pathspec.c similarity index 100% rename from src/pathspec.c rename to src/libgit2/pathspec.c diff --git a/src/pathspec.h b/src/libgit2/pathspec.h similarity index 100% rename from src/pathspec.h rename to src/libgit2/pathspec.h diff --git a/src/proxy.c b/src/libgit2/proxy.c similarity index 100% rename from src/proxy.c rename to src/libgit2/proxy.c diff --git a/src/proxy.h b/src/libgit2/proxy.h similarity index 100% rename from src/proxy.h rename to src/libgit2/proxy.h diff --git a/src/push.c b/src/libgit2/push.c similarity index 100% rename from src/push.c rename to src/libgit2/push.c diff --git a/src/push.h b/src/libgit2/push.h similarity index 100% rename from src/push.h rename to src/libgit2/push.h diff --git a/src/reader.c b/src/libgit2/reader.c similarity index 100% rename from src/reader.c rename to src/libgit2/reader.c diff --git a/src/reader.h b/src/libgit2/reader.h similarity index 100% rename from src/reader.h rename to src/libgit2/reader.h diff --git a/src/rebase.c b/src/libgit2/rebase.c similarity index 99% rename from src/rebase.c rename to src/libgit2/rebase.c index 6f01d3990..5e48f0dfb 100644 --- a/src/rebase.c +++ b/src/libgit2/rebase.c @@ -35,6 +35,7 @@ #define ONTO_FILE "onto" #define ONTO_NAME_FILE "onto_name" #define QUIET_FILE "quiet" +#define INTERACTIVE_FILE "interactive" #define MSGNUM_FILE "msgnum" #define END_FILE "end" @@ -92,6 +93,7 @@ static int rebase_state_type( git_repository *repo) { git_str path = GIT_STR_INIT; + git_str interactive_path = GIT_STR_INIT; git_rebase_t type = GIT_REBASE_NONE; if (git_str_joinpath(&path, repo->gitdir, REBASE_APPLY_DIR) < 0) @@ -107,7 +109,14 @@ static int rebase_state_type( return -1; if (git_fs_path_isdir(git_str_cstr(&path))) { - type = GIT_REBASE_MERGE; + if (git_str_joinpath(&interactive_path, path.ptr, INTERACTIVE_FILE) < 0) + return -1; + + if (git_fs_path_isfile(interactive_path.ptr)) + type = GIT_REBASE_INTERACTIVE; + else + type = GIT_REBASE_MERGE; + goto done; } @@ -118,6 +127,7 @@ done: *path_out = git_str_detach(&path); git_str_dispose(&path); + git_str_dispose(&interactive_path); return 0; } diff --git a/src/refdb.c b/src/libgit2/refdb.c similarity index 100% rename from src/refdb.c rename to src/libgit2/refdb.c diff --git a/src/refdb.h b/src/libgit2/refdb.h similarity index 100% rename from src/refdb.h rename to src/libgit2/refdb.h diff --git a/src/refdb_fs.c b/src/libgit2/refdb_fs.c similarity index 99% rename from src/refdb_fs.c rename to src/libgit2/refdb_fs.c index 95bda9404..0f49b16bb 100644 --- a/src/refdb_fs.c +++ b/src/libgit2/refdb_fs.c @@ -740,7 +740,7 @@ static int packed_lookup( return 0; } } - return GIT_ENOTFOUND; + return ref_error_notfound(ref_name); parse_failed: git_error_set(GIT_ERROR_REFERENCE, "corrupted packed references file"); @@ -1361,7 +1361,11 @@ static int packed_write(refdb_fs_backend *backend) for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) { struct packref *ref = git_sortedcache_entry(refcache, i); - GIT_ASSERT(ref); + + GIT_ASSERT_WITH_CLEANUP(ref, { + error = -1; + goto fail; + }); if ((error = packed_find_peel(backend, ref)) < 0) goto fail; diff --git a/src/reflog.c b/src/libgit2/reflog.c similarity index 100% rename from src/reflog.c rename to src/libgit2/reflog.c diff --git a/src/reflog.h b/src/libgit2/reflog.h similarity index 100% rename from src/reflog.h rename to src/libgit2/reflog.h diff --git a/src/refs.c b/src/libgit2/refs.c similarity index 100% rename from src/refs.c rename to src/libgit2/refs.c diff --git a/src/refs.h b/src/libgit2/refs.h similarity index 100% rename from src/refs.h rename to src/libgit2/refs.h diff --git a/src/refspec.c b/src/libgit2/refspec.c similarity index 100% rename from src/refspec.c rename to src/libgit2/refspec.c diff --git a/src/refspec.h b/src/libgit2/refspec.h similarity index 100% rename from src/refspec.h rename to src/libgit2/refspec.h diff --git a/src/remote.c b/src/libgit2/remote.c similarity index 99% rename from src/remote.c rename to src/libgit2/remote.c index 1a79faaab..3d0593712 100644 --- a/src/remote.c +++ b/src/libgit2/remote.c @@ -1830,7 +1830,7 @@ static int update_one_tip( } if (error == GIT_ENOTFOUND) { - memset(&old, 0, GIT_OID_RAWSZ); + memset(&old, 0, sizeof(git_oid)); error = 0; if (autotag && (error = git_vector_insert(update_heads, head)) < 0) diff --git a/src/remote.h b/src/libgit2/remote.h similarity index 95% rename from src/remote.h rename to src/libgit2/remote.h index ea9c7d17f..41ee58e0f 100644 --- a/src/remote.h +++ b/src/libgit2/remote.h @@ -11,6 +11,7 @@ #include "git2/remote.h" #include "git2/transport.h" +#include "git2/sys/remote.h" #include "git2/sys/transport.h" #include "refspec.h" @@ -53,7 +54,6 @@ int git_remote_connect_options_normalize( git_remote_connect_options *dst, git_repository *repo, const git_remote_connect_options *src); -void git_remote_connect_options_dispose(git_remote_connect_options *opts); int git_remote_capabilities(unsigned int *out, git_remote *remote); diff --git a/src/repo_template.h b/src/libgit2/repo_template.h similarity index 100% rename from src/repo_template.h rename to src/libgit2/repo_template.h diff --git a/src/repository.c b/src/libgit2/repository.c similarity index 97% rename from src/repository.c rename to src/libgit2/repository.c index 6119c1c70..f761b5f32 100644 --- a/src/repository.c +++ b/src/libgit2/repository.c @@ -488,7 +488,7 @@ static int read_gitfile(git_str *path_out, const char *file_path) typedef struct { const char *repo_path; git_str tmp; - bool is_safe; + bool *is_safe; } validate_ownership_data; static int validate_ownership_cb(const git_config_entry *entry, void *payload) @@ -496,49 +496,102 @@ static int validate_ownership_cb(const git_config_entry *entry, void *payload) validate_ownership_data *data = payload; if (strcmp(entry->value, "") == 0) - data->is_safe = false; + *data->is_safe = false; if (git_fs_path_prettify_dir(&data->tmp, entry->value, NULL) == 0 && strcmp(data->tmp.ptr, data->repo_path) == 0) - data->is_safe = true; + *data->is_safe = true; return 0; } -static int validate_ownership(const char *repo_path) +static int validate_ownership_config(bool *is_safe, const char *path) { - git_config *config = NULL; - validate_ownership_data data = { repo_path, GIT_STR_INIT, false }; - bool is_safe; + validate_ownership_data ownership_data = { + path, GIT_STR_INIT, is_safe + }; + git_config *config; int error; - if ((error = git_fs_path_owner_is_current_user(&is_safe, repo_path)) < 0) { - if (error == GIT_ENOTFOUND) - error = 0; + if (load_global_config(&config) != 0) + return 0; - goto done; - } + error = git_config_get_multivar_foreach(config, + "safe.directory", NULL, + validate_ownership_cb, + &ownership_data); - if (is_safe) { + git_config_free(config); + git_str_dispose(&ownership_data.tmp); + + return error; +} + +static int validate_ownership_path(bool *is_safe, const char *path) +{ + git_fs_path_owner_t owner_level = + GIT_FS_PATH_OWNER_CURRENT_USER | + GIT_FS_PATH_USER_IS_ADMINISTRATOR | + GIT_FS_PATH_OWNER_RUNNING_SUDO; + int error = 0; + + if (path) + error = git_fs_path_owner_is(is_safe, path, owner_level); + + if (error == GIT_ENOTFOUND) { + *is_safe = true; error = 0; - goto done; } - if (load_global_config(&config) == 0) { - error = git_config_get_multivar_foreach(config, "safe.directory", NULL, validate_ownership_cb, &data); + return error; +} - if (!error && data.is_safe) +static int validate_ownership(git_repository *repo) +{ + const char *validation_paths[3] = { NULL }, *path; + size_t validation_len = 0, i; + bool is_safe = false; + int error = 0; + + /* + * If there's a worktree, validate the permissions to it *and* + * the git directory, and use the worktree as the configuration + * key for allowlisting the directory. In a bare setup, only + * look at the gitdir and use that as the allowlist. So we + * examine all `validation_paths` but use only the first as + * the configuration lookup. + */ + + if (repo->workdir) + validation_paths[validation_len++] = repo->workdir; + + if (repo->gitlink) + validation_paths[validation_len++] = repo->gitlink; + + validation_paths[validation_len++] = repo->gitdir; + + for (i = 0; i < validation_len; i++) { + path = validation_paths[i]; + + if ((error = validate_ownership_path(&is_safe, path)) < 0) goto done; + + if (!is_safe) + break; } - git_error_set(GIT_ERROR_CONFIG, - "repository path '%s' is not owned by current user", - repo_path); - error = GIT_EOWNER; + if (is_safe || + (error = validate_ownership_config(&is_safe, validation_paths[0])) < 0) + goto done; + + if (!is_safe) { + git_error_set(GIT_ERROR_CONFIG, + "repository path '%s' is not owned by current user", + path); + error = GIT_EOWNER; + } done: - git_config_free(config); - git_str_dispose(&data.tmp); return error; } @@ -915,7 +968,6 @@ int git_repository_open_ext( gitlink = GIT_STR_INIT, commondir = GIT_STR_INIT; git_repository *repo = NULL; git_config *config = NULL; - const char *validation_path; int version = 0; if (flags & GIT_REPOSITORY_OPEN_FROM_ENV) @@ -974,12 +1026,11 @@ int git_repository_open_ext( } /* - * Ensure that the git directory is owned by the current user. + * Ensure that the git directory and worktree are + * owned by the current user. */ - validation_path = repo->is_bare ? repo->gitdir : repo->workdir; - if (git_repository__validate_ownership && - (error = validate_ownership(validation_path)) < 0) + (error = validate_ownership(repo)) < 0) goto cleanup; cleanup: @@ -1530,6 +1581,7 @@ static int check_valid_extension(const git_config_entry *entry, void *payload) } for (i = 0; i < ARRAY_SIZE(builtin_extensions); i++) { + git_str_clear(&cfg); extension = builtin_extensions[i]; if ((error = git_str_printf(&cfg, "extensions.%s", extension)) < 0) diff --git a/src/repository.h b/src/libgit2/repository.h similarity index 100% rename from src/repository.h rename to src/libgit2/repository.h diff --git a/src/reset.c b/src/libgit2/reset.c similarity index 100% rename from src/reset.c rename to src/libgit2/reset.c diff --git a/src/revert.c b/src/libgit2/revert.c similarity index 100% rename from src/revert.c rename to src/libgit2/revert.c diff --git a/src/revparse.c b/src/libgit2/revparse.c similarity index 100% rename from src/revparse.c rename to src/libgit2/revparse.c diff --git a/src/revwalk.c b/src/libgit2/revwalk.c similarity index 100% rename from src/revwalk.c rename to src/libgit2/revwalk.c diff --git a/src/revwalk.h b/src/libgit2/revwalk.h similarity index 100% rename from src/revwalk.h rename to src/libgit2/revwalk.h diff --git a/src/settings.h b/src/libgit2/settings.h similarity index 100% rename from src/settings.h rename to src/libgit2/settings.h diff --git a/src/signature.c b/src/libgit2/signature.c similarity index 100% rename from src/signature.c rename to src/libgit2/signature.c diff --git a/src/signature.h b/src/libgit2/signature.h similarity index 100% rename from src/signature.h rename to src/libgit2/signature.h diff --git a/src/stash.c b/src/libgit2/stash.c similarity index 100% rename from src/stash.c rename to src/libgit2/stash.c diff --git a/src/status.c b/src/libgit2/status.c similarity index 100% rename from src/status.c rename to src/libgit2/status.c diff --git a/src/status.h b/src/libgit2/status.h similarity index 100% rename from src/status.h rename to src/libgit2/status.h diff --git a/src/strarray.c b/src/libgit2/strarray.c similarity index 100% rename from src/strarray.c rename to src/libgit2/strarray.c diff --git a/src/stream.h b/src/libgit2/stream.h similarity index 100% rename from src/stream.h rename to src/libgit2/stream.h diff --git a/src/streams/mbedtls.c b/src/libgit2/streams/mbedtls.c similarity index 100% rename from src/streams/mbedtls.c rename to src/libgit2/streams/mbedtls.c diff --git a/src/streams/mbedtls.h b/src/libgit2/streams/mbedtls.h similarity index 100% rename from src/streams/mbedtls.h rename to src/libgit2/streams/mbedtls.h diff --git a/src/streams/openssl.c b/src/libgit2/streams/openssl.c similarity index 100% rename from src/streams/openssl.c rename to src/libgit2/streams/openssl.c diff --git a/src/streams/openssl.h b/src/libgit2/streams/openssl.h similarity index 100% rename from src/streams/openssl.h rename to src/libgit2/streams/openssl.h diff --git a/src/streams/openssl_dynamic.c b/src/libgit2/streams/openssl_dynamic.c similarity index 100% rename from src/streams/openssl_dynamic.c rename to src/libgit2/streams/openssl_dynamic.c diff --git a/src/streams/openssl_dynamic.h b/src/libgit2/streams/openssl_dynamic.h similarity index 100% rename from src/streams/openssl_dynamic.h rename to src/libgit2/streams/openssl_dynamic.h diff --git a/src/streams/openssl_legacy.c b/src/libgit2/streams/openssl_legacy.c similarity index 100% rename from src/streams/openssl_legacy.c rename to src/libgit2/streams/openssl_legacy.c diff --git a/src/streams/openssl_legacy.h b/src/libgit2/streams/openssl_legacy.h similarity index 100% rename from src/streams/openssl_legacy.h rename to src/libgit2/streams/openssl_legacy.h diff --git a/src/streams/registry.c b/src/libgit2/streams/registry.c similarity index 100% rename from src/streams/registry.c rename to src/libgit2/streams/registry.c diff --git a/src/streams/registry.h b/src/libgit2/streams/registry.h similarity index 100% rename from src/streams/registry.h rename to src/libgit2/streams/registry.h diff --git a/src/streams/socket.c b/src/libgit2/streams/socket.c similarity index 100% rename from src/streams/socket.c rename to src/libgit2/streams/socket.c diff --git a/src/streams/socket.h b/src/libgit2/streams/socket.h similarity index 100% rename from src/streams/socket.h rename to src/libgit2/streams/socket.h diff --git a/src/streams/stransport.c b/src/libgit2/streams/stransport.c similarity index 100% rename from src/streams/stransport.c rename to src/libgit2/streams/stransport.c diff --git a/src/streams/stransport.h b/src/libgit2/streams/stransport.h similarity index 100% rename from src/streams/stransport.h rename to src/libgit2/streams/stransport.h diff --git a/src/streams/tls.c b/src/libgit2/streams/tls.c similarity index 100% rename from src/streams/tls.c rename to src/libgit2/streams/tls.c diff --git a/src/streams/tls.h b/src/libgit2/streams/tls.h similarity index 100% rename from src/streams/tls.h rename to src/libgit2/streams/tls.h diff --git a/src/submodule.c b/src/libgit2/submodule.c similarity index 100% rename from src/submodule.c rename to src/libgit2/submodule.c diff --git a/src/submodule.h b/src/libgit2/submodule.h similarity index 100% rename from src/submodule.h rename to src/libgit2/submodule.h diff --git a/src/sysdir.c b/src/libgit2/sysdir.c similarity index 100% rename from src/sysdir.c rename to src/libgit2/sysdir.c diff --git a/src/sysdir.h b/src/libgit2/sysdir.h similarity index 100% rename from src/sysdir.h rename to src/libgit2/sysdir.h diff --git a/src/tag.c b/src/libgit2/tag.c similarity index 98% rename from src/tag.c rename to src/libgit2/tag.c index 5734106fa..792155a4b 100644 --- a/src/tag.c +++ b/src/libgit2/tag.c @@ -244,6 +244,15 @@ on_error: return -1; } +static bool tag_name_is_valid(const char *tag_name) +{ + /* + * Discourage tag name starting with dash, + * https://github.com/git/git/commit/4f0accd638b8d2 + */ + return tag_name[0] != '-'; +} + static int git_tag_create__internal( git_oid *oid, git_repository *repo, @@ -269,6 +278,11 @@ static int git_tag_create__internal( return -1; } + if (!tag_name_is_valid(tag_name)) { + git_error_set(GIT_ERROR_TAG, "'%s' is not a valid tag name", tag_name); + return -1; + } + error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name); if (error < 0 && error != GIT_ENOTFOUND) goto cleanup; @@ -542,11 +556,7 @@ int git_tag_name_is_valid(int *valid, const char *name) *valid = 0; - /* - * Discourage tag name starting with dash, - * https://github.com/git/git/commit/4f0accd638b8d2 - */ - if (!name || name[0] == '-') + if (!name || !tag_name_is_valid(name)) goto done; if ((error = git_str_puts(&ref_name, GIT_REFS_TAGS_DIR)) < 0 || diff --git a/src/tag.h b/src/libgit2/tag.h similarity index 100% rename from src/tag.h rename to src/libgit2/tag.h diff --git a/src/threadstate.c b/src/libgit2/threadstate.c similarity index 100% rename from src/threadstate.c rename to src/libgit2/threadstate.c diff --git a/src/threadstate.h b/src/libgit2/threadstate.h similarity index 100% rename from src/threadstate.h rename to src/libgit2/threadstate.h diff --git a/src/trace.c b/src/libgit2/trace.c similarity index 100% rename from src/trace.c rename to src/libgit2/trace.c diff --git a/src/trace.h b/src/libgit2/trace.h similarity index 100% rename from src/trace.h rename to src/libgit2/trace.h diff --git a/src/trailer.c b/src/libgit2/trailer.c similarity index 100% rename from src/trailer.c rename to src/libgit2/trailer.c diff --git a/src/transaction.c b/src/libgit2/transaction.c similarity index 100% rename from src/transaction.c rename to src/libgit2/transaction.c diff --git a/src/transaction.h b/src/libgit2/transaction.h similarity index 100% rename from src/transaction.h rename to src/libgit2/transaction.h diff --git a/src/transport.c b/src/libgit2/transport.c similarity index 100% rename from src/transport.c rename to src/libgit2/transport.c diff --git a/src/transports/auth.c b/src/libgit2/transports/auth.c similarity index 100% rename from src/transports/auth.c rename to src/libgit2/transports/auth.c diff --git a/src/transports/auth.h b/src/libgit2/transports/auth.h similarity index 100% rename from src/transports/auth.h rename to src/libgit2/transports/auth.h diff --git a/src/transports/auth_negotiate.c b/src/libgit2/transports/auth_negotiate.c similarity index 100% rename from src/transports/auth_negotiate.c rename to src/libgit2/transports/auth_negotiate.c diff --git a/src/transports/auth_negotiate.h b/src/libgit2/transports/auth_negotiate.h similarity index 100% rename from src/transports/auth_negotiate.h rename to src/libgit2/transports/auth_negotiate.h diff --git a/src/transports/auth_ntlm.c b/src/libgit2/transports/auth_ntlm.c similarity index 100% rename from src/transports/auth_ntlm.c rename to src/libgit2/transports/auth_ntlm.c diff --git a/src/transports/auth_ntlm.h b/src/libgit2/transports/auth_ntlm.h similarity index 100% rename from src/transports/auth_ntlm.h rename to src/libgit2/transports/auth_ntlm.h diff --git a/src/transports/credential.c b/src/libgit2/transports/credential.c similarity index 100% rename from src/transports/credential.c rename to src/libgit2/transports/credential.c diff --git a/src/transports/credential_helpers.c b/src/libgit2/transports/credential_helpers.c similarity index 100% rename from src/transports/credential_helpers.c rename to src/libgit2/transports/credential_helpers.c diff --git a/src/transports/git.c b/src/libgit2/transports/git.c similarity index 100% rename from src/transports/git.c rename to src/libgit2/transports/git.c diff --git a/src/transports/http.c b/src/libgit2/transports/http.c similarity index 100% rename from src/transports/http.c rename to src/libgit2/transports/http.c diff --git a/src/transports/http.h b/src/libgit2/transports/http.h similarity index 100% rename from src/transports/http.h rename to src/libgit2/transports/http.h diff --git a/src/transports/httpclient.c b/src/libgit2/transports/httpclient.c similarity index 99% rename from src/transports/httpclient.c rename to src/libgit2/transports/httpclient.c index 75782da82..f07923ef2 100644 --- a/src/transports/httpclient.c +++ b/src/libgit2/transports/httpclient.c @@ -395,7 +395,7 @@ static int on_body(http_parser *parser, const char *buf, size_t len) size_t max_len; /* Saw data when we expected not to (eg, in consume_response_body) */ - if (ctx->output_buf == NULL && ctx->output_size == 0) { + if (ctx->output_buf == NULL || ctx->output_size == 0) { ctx->parse_status = PARSE_STATUS_NO_OUTPUT; return 0; } diff --git a/src/transports/httpclient.h b/src/libgit2/transports/httpclient.h similarity index 100% rename from src/transports/httpclient.h rename to src/libgit2/transports/httpclient.h diff --git a/src/transports/local.c b/src/libgit2/transports/local.c similarity index 100% rename from src/transports/local.c rename to src/libgit2/transports/local.c diff --git a/src/transports/smart.c b/src/libgit2/transports/smart.c similarity index 97% rename from src/transports/smart.c rename to src/libgit2/transports/smart.c index 801fcbe53..7f57dba2a 100644 --- a/src/transports/smart.c +++ b/src/libgit2/transports/smart.c @@ -425,6 +425,18 @@ int git_transport_smart_credentials(git_credential **out, git_transport *transpo return connect_opts->callbacks.credentials(out, t->url, user, methods, connect_opts->callbacks.payload); } +int git_transport_remote_connect_options( + git_remote_connect_options *out, + git_transport *transport) +{ + transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); + + GIT_ASSERT_ARG(out); + GIT_ASSERT_ARG(transport); + + return git_remote_connect_options_dup(out, &t->connect_opts); +} + int git_transport_smart(git_transport **out, git_remote *owner, void *param) { transport_smart *t; diff --git a/src/transports/smart.h b/src/libgit2/transports/smart.h similarity index 100% rename from src/transports/smart.h rename to src/libgit2/transports/smart.h diff --git a/src/transports/smart_pkt.c b/src/libgit2/transports/smart_pkt.c similarity index 100% rename from src/transports/smart_pkt.c rename to src/libgit2/transports/smart_pkt.c diff --git a/src/transports/smart_protocol.c b/src/libgit2/transports/smart_protocol.c similarity index 100% rename from src/transports/smart_protocol.c rename to src/libgit2/transports/smart_protocol.c diff --git a/src/transports/ssh.c b/src/libgit2/transports/ssh.c similarity index 100% rename from src/transports/ssh.c rename to src/libgit2/transports/ssh.c diff --git a/src/transports/ssh.h b/src/libgit2/transports/ssh.h similarity index 100% rename from src/transports/ssh.h rename to src/libgit2/transports/ssh.h diff --git a/src/transports/winhttp.c b/src/libgit2/transports/winhttp.c similarity index 100% rename from src/transports/winhttp.c rename to src/libgit2/transports/winhttp.c diff --git a/src/tree-cache.c b/src/libgit2/tree-cache.c similarity index 99% rename from src/tree-cache.c rename to src/libgit2/tree-cache.c index 0977c92f3..cd69e7bf8 100644 --- a/src/tree-cache.c +++ b/src/libgit2/tree-cache.c @@ -263,7 +263,7 @@ static void write_tree(git_str *out, git_tree_cache *tree) git_str_printf(out, "%s%c%"PRIdZ" %"PRIuZ"\n", tree->name, 0, tree->entry_count, tree->children_count); if (tree->entry_count != -1) - git_str_put(out, (const char *) &tree->oid, GIT_OID_RAWSZ); + git_str_put(out, (char *)&tree->oid.id, GIT_OID_RAWSZ); for (i = 0; i < tree->children_count; i++) write_tree(out, tree->children[i]); diff --git a/src/tree-cache.h b/src/libgit2/tree-cache.h similarity index 100% rename from src/tree-cache.h rename to src/libgit2/tree-cache.h diff --git a/src/tree.c b/src/libgit2/tree.c similarity index 97% rename from src/tree.c rename to src/libgit2/tree.c index a1545dc2d..a5371fd87 100644 --- a/src/tree.c +++ b/src/libgit2/tree.c @@ -82,6 +82,7 @@ int git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2) static git_tree_entry *alloc_entry(const char *filename, size_t filename_len, const git_oid *id) { git_tree_entry *entry = NULL; + char *filename_ptr; size_t tree_len; TREE_ENTRY_CHECK_NAMELEN(filename_len); @@ -95,21 +96,13 @@ static git_tree_entry *alloc_entry(const char *filename, size_t filename_len, co if (!entry) return NULL; - { - char *filename_ptr; - void *id_ptr; - - filename_ptr = ((char *) entry) + sizeof(git_tree_entry); - memcpy(filename_ptr, filename, filename_len); - entry->filename = filename_ptr; - - id_ptr = filename_ptr + filename_len + 1; - git_oid_cpy(id_ptr, id); - entry->oid = id_ptr; - } - + filename_ptr = ((char *) entry) + sizeof(git_tree_entry); + memcpy(filename_ptr, filename, filename_len); + entry->filename = filename_ptr; entry->filename_len = (uint16_t)filename_len; + git_oid_cpy(&entry->oid, id); + return entry; } @@ -231,7 +224,7 @@ int git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source) GIT_ASSERT_ARG(source); - cpy = alloc_entry(source->filename, source->filename_len, source->oid); + cpy = alloc_entry(source->filename, source->filename_len, &source->oid); if (cpy == NULL) return -1; @@ -269,7 +262,7 @@ const char *git_tree_entry_name(const git_tree_entry *entry) const git_oid *git_tree_entry_id(const git_tree_entry *entry) { GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL); - return entry->oid; + return &entry->oid; } git_object_t git_tree_entry_type(const git_tree_entry *entry) @@ -292,7 +285,7 @@ int git_tree_entry_to_object( GIT_ASSERT_ARG(entry); GIT_ASSERT_ARG(object_out); - return git_object_lookup(object_out, repo, entry->oid, GIT_OBJECT_ANY); + return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJECT_ANY); } static const git_tree_entry *entry_fromname( @@ -331,7 +324,7 @@ const git_tree_entry *git_tree_entry_byid( GIT_ASSERT_ARG_WITH_RETVAL(tree, NULL); git_array_foreach(tree->entries, i, e) { - if (memcmp(&e->oid->id, &id->id, sizeof(id->id)) == 0) + if (git_oid_equal(&e->oid, id)) return e; } @@ -432,7 +425,7 @@ int git_tree__parse_raw(void *_tree, const char *data, size_t size) entry->attr = attr; entry->filename_len = (uint16_t)filename_len; entry->filename = buffer; - entry->oid = (git_oid *) ((char *) buffer + filename_len + 1); + git_oid_fromraw(&entry->oid, ((unsigned char *) buffer + filename_len + 1)); } buffer += filename_len + 1; @@ -536,7 +529,7 @@ static int git_treebuilder__write_with_buffer( git_str_printf(buf, "%o ", entry->attr); git_str_put(buf, entry->filename, entry->filename_len + 1); - git_str_put(buf, (char *)entry->oid->id, GIT_OID_RAWSZ); + git_str_put(buf, (char *)entry->oid.id, GIT_OID_RAWSZ); if (git_str_oom(buf)) { error = -1; @@ -765,7 +758,7 @@ int git_treebuilder_new( git_array_foreach(source->entries, i, entry_src) { if (append_entry( bld, entry_src->filename, - entry_src->oid, + &entry_src->oid, entry_src->attr, false) < 0) goto on_error; @@ -798,7 +791,7 @@ int git_treebuilder_insert( return error; if ((entry = git_strmap_get(bld->map, filename)) != NULL) { - git_oid_cpy((git_oid *) entry->oid, id); + git_oid_cpy(&entry->oid, id); } else { entry = alloc_entry(filename, strlen(filename), id); GIT_ERROR_CHECK_ALLOC(entry); @@ -954,7 +947,7 @@ int git_tree_entry_bypath( return git_tree_entry_dup(entry_out, entry); } - if (git_tree_lookup(&subtree, root->object.repo, entry->oid) < 0) + if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0) return -1; error = git_tree_entry_bypath( @@ -995,7 +988,7 @@ static int tree_walk( git_tree *subtree; size_t path_len = git_str_len(path); - error = git_tree_lookup(&subtree, tree->object.repo, entry->oid); + error = git_tree_lookup(&subtree, tree->object.repo, &entry->oid); if (error < 0) break; diff --git a/src/tree.h b/src/libgit2/tree.h similarity index 98% rename from src/tree.h rename to src/libgit2/tree.h index 6bd9ed652..0dd963ff2 100644 --- a/src/tree.h +++ b/src/libgit2/tree.h @@ -19,7 +19,7 @@ struct git_tree_entry { uint16_t attr; uint16_t filename_len; - const git_oid *oid; + git_oid oid; const char *filename; }; diff --git a/src/userdiff.h b/src/libgit2/userdiff.h similarity index 100% rename from src/userdiff.h rename to src/libgit2/userdiff.h diff --git a/src/worktree.c b/src/libgit2/worktree.c similarity index 100% rename from src/worktree.c rename to src/libgit2/worktree.c diff --git a/src/worktree.h b/src/libgit2/worktree.h similarity index 100% rename from src/worktree.h rename to src/libgit2/worktree.h diff --git a/src/xdiff/git-xdiff.h b/src/libgit2/xdiff/git-xdiff.h similarity index 100% rename from src/xdiff/git-xdiff.h rename to src/libgit2/xdiff/git-xdiff.h diff --git a/src/xdiff/xdiff.h b/src/libgit2/xdiff/xdiff.h similarity index 100% rename from src/xdiff/xdiff.h rename to src/libgit2/xdiff/xdiff.h diff --git a/src/xdiff/xdiffi.c b/src/libgit2/xdiff/xdiffi.c similarity index 100% rename from src/xdiff/xdiffi.c rename to src/libgit2/xdiff/xdiffi.c diff --git a/src/xdiff/xdiffi.h b/src/libgit2/xdiff/xdiffi.h similarity index 100% rename from src/xdiff/xdiffi.h rename to src/libgit2/xdiff/xdiffi.h diff --git a/src/xdiff/xemit.c b/src/libgit2/xdiff/xemit.c similarity index 100% rename from src/xdiff/xemit.c rename to src/libgit2/xdiff/xemit.c diff --git a/src/xdiff/xemit.h b/src/libgit2/xdiff/xemit.h similarity index 100% rename from src/xdiff/xemit.h rename to src/libgit2/xdiff/xemit.h diff --git a/src/xdiff/xhistogram.c b/src/libgit2/xdiff/xhistogram.c similarity index 100% rename from src/xdiff/xhistogram.c rename to src/libgit2/xdiff/xhistogram.c diff --git a/src/xdiff/xinclude.h b/src/libgit2/xdiff/xinclude.h similarity index 100% rename from src/xdiff/xinclude.h rename to src/libgit2/xdiff/xinclude.h diff --git a/src/xdiff/xmacros.h b/src/libgit2/xdiff/xmacros.h similarity index 100% rename from src/xdiff/xmacros.h rename to src/libgit2/xdiff/xmacros.h diff --git a/src/xdiff/xmerge.c b/src/libgit2/xdiff/xmerge.c similarity index 100% rename from src/xdiff/xmerge.c rename to src/libgit2/xdiff/xmerge.c diff --git a/src/xdiff/xpatience.c b/src/libgit2/xdiff/xpatience.c similarity index 100% rename from src/xdiff/xpatience.c rename to src/libgit2/xdiff/xpatience.c diff --git a/src/xdiff/xprepare.c b/src/libgit2/xdiff/xprepare.c similarity index 100% rename from src/xdiff/xprepare.c rename to src/libgit2/xdiff/xprepare.c diff --git a/src/xdiff/xprepare.h b/src/libgit2/xdiff/xprepare.h similarity index 100% rename from src/xdiff/xprepare.h rename to src/libgit2/xdiff/xprepare.h diff --git a/src/xdiff/xtypes.h b/src/libgit2/xdiff/xtypes.h similarity index 100% rename from src/xdiff/xtypes.h rename to src/libgit2/xdiff/xtypes.h diff --git a/src/xdiff/xutils.c b/src/libgit2/xdiff/xutils.c similarity index 100% rename from src/xdiff/xutils.c rename to src/libgit2/xdiff/xutils.c diff --git a/src/xdiff/xutils.h b/src/libgit2/xdiff/xutils.h similarity index 100% rename from src/xdiff/xutils.h rename to src/libgit2/xdiff/xutils.h diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt new file mode 100644 index 000000000..b2833954d --- /dev/null +++ b/src/util/CMakeLists.txt @@ -0,0 +1,74 @@ +# util: a shared library for common utility functions for libgit2 projects + +add_library(util OBJECT) +set_target_properties(util PROPERTIES C_STANDARD 90) +set_target_properties(util PROPERTIES C_EXTENSIONS OFF) + +set(UTIL_INCLUDES + "${PROJECT_BINARY_DIR}/src" + "${PROJECT_SOURCE_DIR}/src/util" + "${PROJECT_SOURCE_DIR}/include") + +file(GLOB UTIL_SRC *.c *.h allocators/*.c allocators/*.h hash.h) +list(SORT UTIL_SRC) + +# +# Platform specific sources +# + +if(WIN32 AND NOT CYGWIN) + file(GLOB UTIL_SRC_OS win32/*.c win32/*.h) + list(SORT UTIL_SRC_OS) +elseif(NOT AMIGA) + file(GLOB UTIL_SRC_OS unix/*.c unix/*.h) + list(SORT UTIL_SRC_OS) +endif() + +# +# Hash backend selection +# + +if(USE_SHA1 STREQUAL "CollisionDetection") + file(GLOB UTIL_SRC_SHA1 hash/collisiondetect.* hash/sha1dc/*) + target_compile_definitions(util PRIVATE SHA1DC_NO_STANDARD_INCLUDES=1) + target_compile_definitions(util PRIVATE SHA1DC_CUSTOM_INCLUDE_SHA1_C=\"git2_util.h\") + target_compile_definitions(util PRIVATE SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"git2_util.h\") +elseif(USE_SHA1 STREQUAL "OpenSSL" OR USE_SHA1 STREQUAL "OpenSSL-Dynamic") + file(GLOB UTIL_SRC_SHA1 hash/openssl.*) +elseif(USE_SHA1 STREQUAL "CommonCrypto") + file(GLOB UTIL_SRC_SHA1 hash/common_crypto.*) +elseif(USE_SHA1 STREQUAL "mbedTLS") + file(GLOB UTIL_SRC_SHA1 hash/mbedtls.*) +elseif(USE_SHA1 STREQUAL "Win32") + file(GLOB UTIL_SRC_SHA1 hash/win32.*) +else() + message(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}") +endif() + +list(SORT UTIL_SRC_SHA1) + +if(USE_SHA256 STREQUAL "Builtin") + file(GLOB UTIL_SRC_SHA256 hash/builtin.* hash/rfc6234/*) +elseif(USE_SHA256 STREQUAL "OpenSSL" OR USE_SHA256 STREQUAL "OpenSSL-Dynamic") + file(GLOB UTIL_SRC_SHA256 hash/openssl.*) +elseif(USE_SHA256 STREQUAL "CommonCrypto") + file(GLOB UTIL_SRC_SHA256 hash/common_crypto.*) +elseif(USE_SHA256 STREQUAL "mbedTLS") + file(GLOB UTIL_SRC_SHA256 hash/mbedtls.*) +elseif(USE_SHA256 STREQUAL "Win32") + file(GLOB UTIL_SRC_SHA256 hash/win32.*) +else() + message(FATAL_ERROR "Asked for unknown SHA256 backend: ${USE_SHA256}") +endif() + +list(SORT UTIL_SRC_SHA256) + +# +# Build the library +# + +target_sources(util PRIVATE ${UTIL_SRC} ${UTIL_SRC_OS} ${UTIL_SRC_SHA1} ${UTIL_SRC_SHA256}) +ide_split_sources(util) + +target_include_directories(util PRIVATE ${UTIL_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${libgit2_SOURCE_DIR}/include) +target_include_directories(util SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES}) diff --git a/src/alloc.c b/src/util/alloc.c similarity index 100% rename from src/alloc.c rename to src/util/alloc.c diff --git a/src/alloc.h b/src/util/alloc.h similarity index 100% rename from src/alloc.h rename to src/util/alloc.h diff --git a/src/allocators/failalloc.c b/src/util/allocators/failalloc.c similarity index 100% rename from src/allocators/failalloc.c rename to src/util/allocators/failalloc.c diff --git a/src/allocators/failalloc.h b/src/util/allocators/failalloc.h similarity index 97% rename from src/allocators/failalloc.h rename to src/util/allocators/failalloc.h index 6115e51e7..91264a0bb 100644 --- a/src/allocators/failalloc.h +++ b/src/util/allocators/failalloc.h @@ -8,7 +8,7 @@ #ifndef INCLUDE_allocators_failalloc_h__ #define INCLUDE_allocators_failalloc_h__ -#include "common.h" +#include "git2_util.h" extern void *git_failalloc_malloc(size_t len, const char *file, int line); extern void *git_failalloc_calloc(size_t nelem, size_t elsize, const char *file, int line); diff --git a/src/allocators/stdalloc.c b/src/util/allocators/stdalloc.c similarity index 100% rename from src/allocators/stdalloc.c rename to src/util/allocators/stdalloc.c diff --git a/src/allocators/stdalloc.h b/src/util/allocators/stdalloc.h similarity index 94% rename from src/allocators/stdalloc.h rename to src/util/allocators/stdalloc.h index fa23fe6e3..955038cb0 100644 --- a/src/allocators/stdalloc.h +++ b/src/util/allocators/stdalloc.h @@ -8,7 +8,7 @@ #ifndef INCLUDE_allocators_stdalloc_h__ #define INCLUDE_allocators_stdalloc_h__ -#include "common.h" +#include "git2_util.h" #include "alloc.h" diff --git a/src/allocators/win32_leakcheck.c b/src/util/allocators/win32_leakcheck.c similarity index 100% rename from src/allocators/win32_leakcheck.c rename to src/util/allocators/win32_leakcheck.c diff --git a/src/allocators/win32_leakcheck.h b/src/util/allocators/win32_leakcheck.h similarity index 94% rename from src/allocators/win32_leakcheck.h rename to src/util/allocators/win32_leakcheck.h index 089690f90..edcd9307f 100644 --- a/src/allocators/win32_leakcheck.h +++ b/src/util/allocators/win32_leakcheck.h @@ -8,7 +8,7 @@ #ifndef INCLUDE_allocators_win32_leakcheck_h #define INCLUDE_allocators_win32_leakcheck_h -#include "common.h" +#include "git2_util.h" #include "alloc.h" diff --git a/src/array.h b/src/util/array.h similarity index 99% rename from src/array.h rename to src/util/array.h index e97688b36..cbab52ad1 100644 --- a/src/array.h +++ b/src/util/array.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_array_h__ #define INCLUDE_array_h__ -#include "common.h" +#include "git2_util.h" /* * Use this to declare a typesafe resizable array of items, a la: diff --git a/src/assert_safe.h b/src/util/assert_safe.h similarity index 79% rename from src/assert_safe.h rename to src/util/assert_safe.h index 8c261100f..cc0bac551 100644 --- a/src/assert_safe.h +++ b/src/util/assert_safe.h @@ -24,6 +24,8 @@ # define GIT_ASSERT_WITH_RETVAL(expr, fail) assert(expr) # define GIT_ASSERT_ARG_WITH_RETVAL(expr, fail) assert(expr) + +# define GIT_ASSERT_WITH_CLEANUP(expr, cleanup) assert(expr) #else /** Internal consistency check to stop the function. */ @@ -53,6 +55,20 @@ } \ } while(0) +/** + * Go to to the given label on assertion failures; useful when you have + * taken a lock or otherwise need to release a resource. + */ +# define GIT_ASSERT_WITH_CLEANUP(expr, cleanup) \ + GIT_ASSERT__WITH_CLEANUP(expr, GIT_ERROR_INTERNAL, "unrecoverable internal error", cleanup) + +# define GIT_ASSERT__WITH_CLEANUP(expr, code, msg, cleanup) do { \ + if (!(expr)) { \ + git_error_set(code, "%s: '%s'", msg, #expr); \ + cleanup; \ + } \ + } while(0) + #endif /* GIT_ASSERT_HARD */ #endif diff --git a/src/bitvec.h b/src/util/bitvec.h similarity index 100% rename from src/bitvec.h rename to src/util/bitvec.h diff --git a/src/cc-compat.h b/src/util/cc-compat.h similarity index 100% rename from src/cc-compat.h rename to src/util/cc-compat.h diff --git a/src/date.c b/src/util/date.c similarity index 98% rename from src/date.c rename to src/util/date.c index 0e5ffc96b..4d757e21a 100644 --- a/src/date.c +++ b/src/util/date.c @@ -1,10 +1,11 @@ /* - * GIT - The information manager from hell + * Copyright (C) the libgit2 contributors. All rights reserved. * - * Copyright (C) Linus Torvalds, 2005 + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "git2_util.h" #ifndef GIT_WIN32 #include diff --git a/src/date.h b/src/util/date.h similarity index 100% rename from src/date.h rename to src/util/date.h diff --git a/src/filebuf.c b/src/util/filebuf.c similarity index 99% rename from src/filebuf.c rename to src/util/filebuf.c index eafcba3bd..e014d43b2 100644 --- a/src/filebuf.c +++ b/src/util/filebuf.c @@ -65,7 +65,7 @@ static int lock_file(git_filebuf *file, int flags, mode_t mode) if ((flags & GIT_FILEBUF_APPEND) && git_fs_path_exists(file->path_original) == true) { git_file source; - char buffer[FILEIO_BUFSIZE]; + char buffer[GIT_BUFSIZE_FILEIO]; ssize_t read_bytes; int error = 0; diff --git a/src/filebuf.h b/src/util/filebuf.h similarity index 99% rename from src/filebuf.h rename to src/util/filebuf.h index adbb19936..4a61ae4e3 100644 --- a/src/filebuf.h +++ b/src/util/filebuf.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_filebuf_h__ #define INCLUDE_filebuf_h__ -#include "common.h" +#include "git2_util.h" #include "futils.h" #include "hash.h" diff --git a/src/fs_path.c b/src/util/fs_path.c similarity index 94% rename from src/fs_path.c rename to src/util/fs_path.c index a67617eca..6c87bfd01 100644 --- a/src/fs_path.c +++ b/src/util/fs_path.c @@ -7,8 +7,9 @@ #include "fs_path.h" +#include "git2_util.h" +#include "futils.h" #include "posix.h" -#include "repository.h" #ifdef GIT_WIN32 #include "win32/posix.h" #include "win32/w32_buffer.h" @@ -21,6 +22,13 @@ #include #include +#define ensure_error_set(code) do { \ + const git_error *e = git_error_last(); \ + if (!e || !e->message) \ + git_error_set(e ? e->klass : GIT_ERROR_CALLBACK, \ + "filesystem callback returned %d", code); \ + } while(0) + static int dos_drive_prefix_length(const char *path) { int i; @@ -101,7 +109,7 @@ int git_fs_path_basename_r(git_str *buffer, const char *path) /* Empty or NULL string gets treated as "." */ if (path == NULL || *path == '\0') { startp = "."; - len = 1; + len = 1; goto Exit; } @@ -113,7 +121,7 @@ int git_fs_path_basename_r(git_str *buffer, const char *path) /* All slashes becomes "/" */ if (endp == path && *endp == '/') { startp = "/"; - len = 1; + len = 1; goto Exit; } @@ -185,8 +193,7 @@ int git_fs_path_dirname_r(git_str *buffer, const char *path) if (endp - path + 1 > INT_MAX) { git_error_set(GIT_ERROR_INVALID, "path too long"); - len = -1; - goto Exit; + return -1; } if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) { @@ -211,8 +218,7 @@ int git_fs_path_dirname_r(git_str *buffer, const char *path) if (endp - path + 1 > INT_MAX) { git_error_set(GIT_ERROR_INVALID, "path too long"); - len = -1; - goto Exit; + return -1; } if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) { @@ -530,7 +536,7 @@ int git_fs_path_walk_up( if (!scan) { error = cb(data, ""); if (error) - git_error_set_after_callback(error); + ensure_error_set(error); return error; } @@ -543,7 +549,7 @@ int git_fs_path_walk_up( iter.ptr[scan] = oldc; if (error) { - git_error_set_after_callback(error); + ensure_error_set(error); break; } @@ -563,7 +569,7 @@ int git_fs_path_walk_up( if (!error && stop == 0 && iter.ptr[0] != '/') { error = cb(data, ""); if (error) - git_error_set_after_callback(error); + ensure_error_set(error); } return error; @@ -1167,7 +1173,7 @@ int git_fs_path_direach( /* Only set our own error if the callback did not set one already */ if (error != 0) { if (!git_error_last()) - git_error_set_after_callback(error); + ensure_error_set(error); break; } @@ -1779,9 +1785,9 @@ done: return supported; } -static git_fs_path__mock_owner_t mock_owner = GIT_FS_PATH_MOCK_OWNER_NONE; +static git_fs_path_owner_t mock_owner = GIT_FS_PATH_OWNER_NONE; -void git_fs_path__set_owner(git_fs_path__mock_owner_t owner) +void git_fs_path__set_owner(git_fs_path_owner_t owner) { mock_owner = owner; } @@ -1873,74 +1879,52 @@ static int file_owner_sid(PSID *out, const char *path) return error; } -int git_fs_path_owner_is_current_user(bool *out, const char *path) +int git_fs_path_owner_is( + bool *out, + const char *path, + git_fs_path_owner_t owner_type) { PSID owner_sid = NULL, user_sid = NULL; - int error = -1; + BOOL is_admin, admin_owned; + int error; if (mock_owner) { - *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_CURRENT_USER); + *out = ((mock_owner & owner_type) != 0); return 0; } - if ((error = file_owner_sid(&owner_sid, path)) < 0 || - (error = current_user_sid(&user_sid)) < 0) + if ((error = file_owner_sid(&owner_sid, path)) < 0) goto done; - *out = EqualSid(owner_sid, user_sid); - error = 0; + if ((owner_type & GIT_FS_PATH_OWNER_CURRENT_USER) != 0) { + if ((error = current_user_sid(&user_sid)) < 0) + goto done; -done: - git__free(owner_sid); - git__free(user_sid); - return error; -} - -int git_fs_path_owner_is_system(bool *out, const char *path) -{ - PSID owner_sid; - - if (mock_owner) { - *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_SYSTEM); - return 0; + if (EqualSid(owner_sid, user_sid)) { + *out = true; + goto done; + } } - if (file_owner_sid(&owner_sid, path) < 0) - return -1; + admin_owned = + IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) || + IsWellKnownSid(owner_sid, WinLocalSystemSid); - *out = IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) || - IsWellKnownSid(owner_sid, WinLocalSystemSid); - - git__free(owner_sid); - return 0; -} - -int git_fs_path_owner_is_system_or_current_user(bool *out, const char *path) -{ - PSID owner_sid = NULL, user_sid = NULL; - int error = -1; - - if (mock_owner) { - *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_SYSTEM || - mock_owner == GIT_FS_PATH_MOCK_OWNER_CURRENT_USER); - return 0; - } - - if (file_owner_sid(&owner_sid, path) < 0) - goto done; - - if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) || - IsWellKnownSid(owner_sid, WinLocalSystemSid)) { - *out = 1; - error = 0; + if (admin_owned && + (owner_type & GIT_FS_PATH_OWNER_ADMINISTRATOR) != 0) { + *out = true; goto done; } - if (current_user_sid(&user_sid) < 0) + if (admin_owned && + (owner_type & GIT_FS_PATH_USER_IS_ADMINISTRATOR) != 0 && + CheckTokenMembership(NULL, owner_sid, &is_admin) && + is_admin) { + *out = true; goto done; + } - *out = EqualSid(owner_sid, user_sid); - error = 0; + *out = false; done: git__free(owner_sid); @@ -1950,12 +1934,36 @@ done: #else -static int fs_path_owner_is(bool *out, const char *path, uid_t *uids, size_t uids_len) +static int sudo_uid_lookup(uid_t *out) +{ + git_str uid_str = GIT_STR_INIT; + int64_t uid; + int error; + + if ((error = git__getenv(&uid_str, "SUDO_UID")) == 0 && + (error = git__strntol64(&uid, uid_str.ptr, uid_str.size, NULL, 10)) == 0 && + uid == (int64_t)((uid_t)uid)) { + *out = (uid_t)uid; + } + + git_str_dispose(&uid_str); + return error; +} + +int git_fs_path_owner_is( + bool *out, + const char *path, + git_fs_path_owner_t owner_type) { struct stat st; - size_t i; + uid_t euid, sudo_uid; - *out = false; + if (mock_owner) { + *out = ((mock_owner & owner_type) != 0); + return 0; + } + + euid = geteuid(); if (p_lstat(path, &st) != 0) { if (errno == ENOENT) @@ -1965,55 +1973,42 @@ static int fs_path_owner_is(bool *out, const char *path, uid_t *uids, size_t uid return -1; } - for (i = 0; i < uids_len; i++) { - if (uids[i] == st.st_uid) { - *out = true; - break; - } - } - - return 0; -} - -int git_fs_path_owner_is_current_user(bool *out, const char *path) -{ - uid_t userid = geteuid(); - - if (mock_owner) { - *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_CURRENT_USER); + if ((owner_type & GIT_FS_PATH_OWNER_CURRENT_USER) != 0 && + st.st_uid == euid) { + *out = true; return 0; } - return fs_path_owner_is(out, path, &userid, 1); + if ((owner_type & GIT_FS_PATH_OWNER_ADMINISTRATOR) != 0 && + st.st_uid == 0) { + *out = true; + return 0; + } + + if ((owner_type & GIT_FS_PATH_OWNER_RUNNING_SUDO) != 0 && + euid == 0 && + sudo_uid_lookup(&sudo_uid) == 0 && + st.st_uid == sudo_uid) { + *out = true; + return 0; + } + + *out = false; + return 0; +} + +#endif + +int git_fs_path_owner_is_current_user(bool *out, const char *path) +{ + return git_fs_path_owner_is(out, path, GIT_FS_PATH_OWNER_CURRENT_USER); } int git_fs_path_owner_is_system(bool *out, const char *path) { - uid_t userid = 0; - - if (mock_owner) { - *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_SYSTEM); - return 0; - } - - return fs_path_owner_is(out, path, &userid, 1); + return git_fs_path_owner_is(out, path, GIT_FS_PATH_OWNER_ADMINISTRATOR); } -int git_fs_path_owner_is_system_or_current_user(bool *out, const char *path) -{ - uid_t userids[2] = { geteuid(), 0 }; - - if (mock_owner) { - *out = (mock_owner == GIT_FS_PATH_MOCK_OWNER_SYSTEM || - mock_owner == GIT_FS_PATH_MOCK_OWNER_CURRENT_USER); - return 0; - } - - return fs_path_owner_is(out, path, userids, 2); -} - -#endif - int git_fs_path_find_executable(git_str *fullpath, const char *executable) { #ifdef GIT_WIN32 diff --git a/src/fs_path.h b/src/util/fs_path.h similarity index 95% rename from src/fs_path.h rename to src/util/fs_path.h index 7e6a22d4f..e5ca67378 100644 --- a/src/fs_path.h +++ b/src/util/fs_path.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_fs_path_h__ #define INCLUDE_fs_path_h__ -#include "common.h" +#include "git2_util.h" #include "posix.h" #include "str.h" @@ -732,18 +732,42 @@ int git_fs_path_normalize_slashes(git_str *out, const char *path); bool git_fs_path_supports_symlinks(const char *dir); typedef enum { - GIT_FS_PATH_MOCK_OWNER_NONE = 0, /* do filesystem lookups as normal */ - GIT_FS_PATH_MOCK_OWNER_SYSTEM = 1, - GIT_FS_PATH_MOCK_OWNER_CURRENT_USER = 2, - GIT_FS_PATH_MOCK_OWNER_OTHER = 3 -} git_fs_path__mock_owner_t; + GIT_FS_PATH_OWNER_NONE = 0, + + /** The file must be owned by the current user. */ + GIT_FS_PATH_OWNER_CURRENT_USER = (1 << 0), + + /** The file must be owned by the system account. */ + GIT_FS_PATH_OWNER_ADMINISTRATOR = (1 << 1), + + /** + * The file may be owned by a system account if the current + * user is in an administrator group. Windows only; this is + * a noop on non-Windows systems. + */ + GIT_FS_PATH_USER_IS_ADMINISTRATOR = (1 << 2), + + /** + * The file is owned by the current user, who is running `sudo`. + */ + GIT_FS_PATH_OWNER_RUNNING_SUDO = (1 << 3), + + /** The file may be owned by another user. */ + GIT_FS_PATH_OWNER_OTHER = (1 << 4) +} git_fs_path_owner_t; /** * Sets the mock ownership for files; subsequent calls to - * `git_fs_path_owner_is_*` functions will return this data until cleared - * with `GIT_FS_PATH_MOCK_OWNER_NONE`. + * `git_fs_path_owner_is_*` functions will return this data until + * cleared with `GIT_FS_PATH_OWNER_NONE`. */ -void git_fs_path__set_owner(git_fs_path__mock_owner_t owner); +void git_fs_path__set_owner(git_fs_path_owner_t owner); + +/** Verify that the file in question is owned by the given owner. */ +int git_fs_path_owner_is( + bool *out, + const char *path, + git_fs_path_owner_t owner_type); /** * Verify that the file in question is owned by an administrator or system @@ -757,12 +781,6 @@ int git_fs_path_owner_is_system(bool *out, const char *path); int git_fs_path_owner_is_current_user(bool *out, const char *path); -/** - * Verify that the file in question is owned by an administrator or system - * account _or_ the current user; - */ -int git_fs_path_owner_is_system_or_current_user(bool *out, const char *path); - /** * Search the current PATH for the given executable, returning the full * path if it is found. diff --git a/src/futils.c b/src/util/futils.c similarity index 96% rename from src/futils.c rename to src/util/futils.c index 42c35955e..cb872de09 100644 --- a/src/futils.c +++ b/src/util/futils.c @@ -167,18 +167,60 @@ int git_futils_readbuffer_fd(git_str *buf, git_file fd, size_t len) /* p_read loops internally to read len bytes */ read_size = p_read(fd, buf->ptr, len); - if (read_size != (ssize_t)len) { + if (read_size < 0) { git_error_set(GIT_ERROR_OS, "failed to read descriptor"); git_str_dispose(buf); return -1; } + if ((size_t)read_size != len) { + git_error_set(GIT_ERROR_FILESYSTEM, "could not read (expected %" PRIuZ " bytes, read %" PRIuZ ")", len, (size_t)read_size); + git_str_dispose(buf); + return -1; + } + buf->ptr[read_size] = '\0'; buf->size = read_size; return 0; } +int git_futils_readbuffer_fd_full(git_str *buf, git_file fd) +{ + static size_t blocksize = 10240; + size_t alloc_len = 0, total_size = 0; + ssize_t read_size = 0; + + git_str_clear(buf); + + while (true) { + GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, blocksize); + + if (git_str_grow(buf, alloc_len) < 0) + return -1; + + /* p_read loops internally to read blocksize bytes */ + read_size = p_read(fd, buf->ptr, blocksize); + + if (read_size < 0) { + git_error_set(GIT_ERROR_OS, "failed to read descriptor"); + git_str_dispose(buf); + return -1; + } + + total_size += read_size; + + if ((size_t)read_size < blocksize) { + break; + } + } + + buf->ptr[total_size] = '\0'; + buf->size = total_size; + + return 0; +} + int git_futils_readbuffer_updated( git_str *out, const char *path, @@ -856,7 +898,7 @@ int git_futils_fake_symlink(const char *target, const char *path) static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done) { int error = 0; - char buffer[FILEIO_BUFSIZE]; + char buffer[GIT_BUFSIZE_FILEIO]; ssize_t len = 0; while (!error && (len = p_read(ifd, buffer, sizeof(buffer))) > 0) diff --git a/src/futils.h b/src/util/futils.h similarity index 99% rename from src/futils.h rename to src/util/futils.h index a82ec41cc..3f207afb2 100644 --- a/src/futils.h +++ b/src/util/futils.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_futils_h__ #define INCLUDE_futils_h__ -#include "common.h" +#include "git2_util.h" #include "map.h" #include "posix.h" @@ -27,6 +27,7 @@ extern int git_futils_readbuffer_updated( const char *path, unsigned char checksum[GIT_HASH_SHA1_SIZE], int *updated); +extern int git_futils_readbuffer_fd_full(git_str *obj, git_file fd); extern int git_futils_readbuffer_fd(git_str *obj, git_file fd, size_t len); /* Additional constants for `git_futils_writebuffer`'s `open_flags`. We diff --git a/src/common.h b/src/util/git2_util.h similarity index 71% rename from src/common.h rename to src/util/git2_util.h index 549bddb59..ad3f1c71f 100644 --- a/src/common.h +++ b/src/util/git2_util.h @@ -4,8 +4,8 @@ * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ -#ifndef INCLUDE_common_h__ -#define INCLUDE_common_h__ +#ifndef INCLUDE_git2_util_h__ +#define INCLUDE_git2_util_h__ #ifndef LIBGIT2_NO_FEATURES_H # include "git2/sys/features.h" @@ -14,13 +14,13 @@ #include "git2/common.h" #include "cc-compat.h" +typedef struct git_str git_str; + /** Declare a function as always inlined. */ #if defined(_MSC_VER) # define GIT_INLINE(type) static __inline type #elif defined(__GNUC__) # define GIT_INLINE(type) static __inline__ type -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -# define GIT_INLINE(type) static inline type #else # define GIT_INLINE(type) static type #endif @@ -67,12 +67,12 @@ # include # include "win32/msvc-compat.h" # include "win32/mingw-compat.h" -# include "win32/w32_common.h" # include "win32/win32-compat.h" -# include "win32/error.h" +# include "win32/w32_common.h" # include "win32/version.h" +# include "win32/error.h" # ifdef GIT_THREADS -# include "win32/thread.h" +# include "win32/thread.h" # endif #else @@ -99,24 +99,17 @@ #include "git2/types.h" #include "git2/errors.h" -#include "errors.h" #include "thread.h" #include "integer.h" #include "assert_safe.h" -#include "utf8.h" - -/* - * Include the declarations for deprecated functions; this ensures - * that they're decorated with the proper extern/visibility attributes. - */ -#include "git2/deprecated.h" #include "posix.h" -#define DEFAULT_BUFSIZE 65536 -#define FILEIO_BUFSIZE DEFAULT_BUFSIZE -#define FILTERIO_BUFSIZE DEFAULT_BUFSIZE -#define NETIO_BUFSIZE DEFAULT_BUFSIZE +#define GIT_BUFSIZE_DEFAULT 65536 +#define GIT_BUFSIZE_FILEIO GIT_BUFSIZE_DEFAULT +#define GIT_BUFSIZE_FILTERIO GIT_BUFSIZE_DEFAULT +#define GIT_BUFSIZE_NETIO GIT_BUFSIZE_DEFAULT + /** * Check a pointer allocation result, returning -1 if it failed. @@ -126,7 +119,7 @@ } while(0) /** - * Check a string buffer allocation result, returning -1 if it failed. + * Check a buffer allocation result, returning -1 if it failed. */ #define GIT_ERROR_CHECK_ALLOC_STR(buf) do { \ if ((void *)(buf) == NULL || git_str_oom(buf)) { return -1; } \ @@ -138,40 +131,6 @@ #define GIT_ERROR_CHECK_ERROR(code) \ do { int _err = (code); if (_err) return _err; } while (0) -/** - * Check a versioned structure for validity - */ -GIT_INLINE(int) git_error__check_version(const void *structure, unsigned int expected_max, const char *name) -{ - unsigned int actual; - - if (!structure) - return 0; - - actual = *(const unsigned int*)structure; - if (actual > 0 && actual <= expected_max) - return 0; - - git_error_set(GIT_ERROR_INVALID, "invalid version %d on %s", actual, name); - return -1; -} -#define GIT_ERROR_CHECK_VERSION(S,V,N) if (git_error__check_version(S,V,N) < 0) return -1 - -/** - * Initialize a structure with a version. - */ -GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int version) -{ - memset(structure, 0, len); - *((int*)structure) = version; -} -#define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V) - -#define GIT_INIT_STRUCTURE_FROM_TEMPLATE(PTR,VERSION,TYPE,TPL) do { \ - TYPE _tmpl = TPL; \ - GIT_ERROR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \ - memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0) - /** Check for additive overflow, setting an error if would occur. */ #define GIT_ADD_SIZET_OVERFLOW(out, one, two) \ @@ -204,11 +163,6 @@ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int v #define GIT_ERROR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \ if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; } -/* NOTE: other git_error functions are in the public errors.h header file */ - -/* Forward declare git_str */ -typedef struct git_str git_str; - #include "util.h" #endif diff --git a/src/hash.c b/src/util/hash.c similarity index 82% rename from src/hash.c rename to src/util/hash.c index 98ceb05d2..ff900cea7 100644 --- a/src/hash.c +++ b/src/util/hash.c @@ -9,7 +9,11 @@ int git_hash_global_init(void) { - return git_hash_sha1_global_init(); + if (git_hash_sha1_global_init() < 0 || + git_hash_sha256_global_init() < 0) + return -1; + + return 0; } int git_hash_ctx_init(git_hash_ctx *ctx, git_hash_algorithm_t algorithm) @@ -20,6 +24,9 @@ int git_hash_ctx_init(git_hash_ctx *ctx, git_hash_algorithm_t algorithm) case GIT_HASH_ALGORITHM_SHA1: error = git_hash_sha1_ctx_init(&ctx->ctx.sha1); break; + case GIT_HASH_ALGORITHM_SHA256: + error = git_hash_sha256_ctx_init(&ctx->ctx.sha256); + break; default: git_error_set(GIT_ERROR_INTERNAL, "unknown hash algorithm"); error = -1; @@ -35,6 +42,9 @@ void git_hash_ctx_cleanup(git_hash_ctx *ctx) case GIT_HASH_ALGORITHM_SHA1: git_hash_sha1_ctx_cleanup(&ctx->ctx.sha1); return; + case GIT_HASH_ALGORITHM_SHA256: + git_hash_sha256_ctx_cleanup(&ctx->ctx.sha256); + return; default: /* unreachable */ ; } @@ -45,6 +55,8 @@ int git_hash_init(git_hash_ctx *ctx) switch (ctx->algorithm) { case GIT_HASH_ALGORITHM_SHA1: return git_hash_sha1_init(&ctx->ctx.sha1); + case GIT_HASH_ALGORITHM_SHA256: + return git_hash_sha256_init(&ctx->ctx.sha256); default: /* unreachable */ ; } @@ -58,6 +70,8 @@ int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len) switch (ctx->algorithm) { case GIT_HASH_ALGORITHM_SHA1: return git_hash_sha1_update(&ctx->ctx.sha1, data, len); + case GIT_HASH_ALGORITHM_SHA256: + return git_hash_sha256_update(&ctx->ctx.sha256, data, len); default: /* unreachable */ ; } @@ -71,6 +85,8 @@ int git_hash_final(unsigned char *out, git_hash_ctx *ctx) switch (ctx->algorithm) { case GIT_HASH_ALGORITHM_SHA1: return git_hash_sha1_final(out, &ctx->ctx.sha1); + case GIT_HASH_ALGORITHM_SHA256: + return git_hash_sha256_final(out, &ctx->ctx.sha256); default: /* unreachable */ ; } diff --git a/src/hash.h b/src/util/hash.h similarity index 89% rename from src/hash.h rename to src/util/hash.h index 507c1cb25..387c5a66f 100644 --- a/src/hash.h +++ b/src/util/hash.h @@ -8,9 +8,9 @@ #ifndef INCLUDE_hash_h__ #define INCLUDE_hash_h__ -#include "common.h" +#include "git2_util.h" -#include "hash/sha1.h" +#include "hash/sha.h" typedef struct { void *data; @@ -19,12 +19,14 @@ typedef struct { typedef enum { GIT_HASH_ALGORITHM_NONE = 0, - GIT_HASH_ALGORITHM_SHA1 + GIT_HASH_ALGORITHM_SHA1, + GIT_HASH_ALGORITHM_SHA256 } git_hash_algorithm_t; typedef struct git_hash_ctx { union { git_hash_sha1_ctx sha1; + git_hash_sha256_ctx sha256; } ctx; git_hash_algorithm_t algorithm; } git_hash_ctx; diff --git a/src/util/hash/builtin.c b/src/util/hash/builtin.c new file mode 100644 index 000000000..cc4aa58fe --- /dev/null +++ b/src/util/hash/builtin.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "builtin.h" + +int git_hash_sha256_global_init(void) +{ + return 0; +} + +int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx) +{ + return git_hash_sha256_init(ctx); +} + +void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx) +{ + GIT_UNUSED(ctx); +} + +int git_hash_sha256_init(git_hash_sha256_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + if (SHA256Reset(&ctx->c)) { + git_error_set(GIT_ERROR_SHA, "SHA256 error"); + return -1; + } + return 0; +} + +int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len) +{ + GIT_ASSERT_ARG(ctx); + if (SHA256Input(&ctx->c, data, len)) { + git_error_set(GIT_ERROR_SHA, "SHA256 error"); + return -1; + } + return 0; +} + +int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + if (SHA256Result(&ctx->c, out)) { + git_error_set(GIT_ERROR_SHA, "SHA256 error"); + return -1; + } + return 0; +} diff --git a/src/hash/sha1/openssl.h b/src/util/hash/builtin.h similarity index 58% rename from src/hash/sha1/openssl.h rename to src/util/hash/builtin.h index a223ca03e..769df1ac2 100644 --- a/src/hash/sha1/openssl.h +++ b/src/util/hash/builtin.h @@ -5,15 +5,15 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#ifndef INCLUDE_hash_sha1_openssl_h__ -#define INCLUDE_hash_sha1_openssl_h__ +#ifndef INCLUDE_hash_builtin_h__ +#define INCLUDE_hash_builtin_h__ -#include "hash/sha1.h" +#include "hash/sha.h" -#include +#include "rfc6234/sha.h" -struct git_hash_sha1_ctx { - SHA_CTX c; +struct git_hash_sha256_ctx { + SHA256Context c; }; #endif diff --git a/src/hash/sha1/collisiondetect.c b/src/util/hash/collisiondetect.c similarity index 92% rename from src/hash/sha1/collisiondetect.c rename to src/util/hash/collisiondetect.c index ec7059c4c..c51a402d7 100644 --- a/src/hash/sha1/collisiondetect.c +++ b/src/util/hash/collisiondetect.c @@ -40,7 +40,7 @@ int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx) { GIT_ASSERT_ARG(ctx); if (SHA1DCFinal(out, &ctx->c)) { - git_error_set(GIT_ERROR_SHA1, "SHA1 collision attack detected"); + git_error_set(GIT_ERROR_SHA, "SHA1 collision attack detected"); return -1; } diff --git a/src/hash/sha1/collisiondetect.h b/src/util/hash/collisiondetect.h similarity index 71% rename from src/hash/sha1/collisiondetect.h rename to src/util/hash/collisiondetect.h index eb88e86c1..8de550230 100644 --- a/src/hash/sha1/collisiondetect.h +++ b/src/util/hash/collisiondetect.h @@ -5,10 +5,10 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#ifndef INCLUDE_hash_sha1_collisiondetect_h__ -#define INCLUDE_hash_sha1_collisiondetect_h__ +#ifndef INCLUDE_hash_collisiondetect_h__ +#define INCLUDE_hash_collisiondetect_h__ -#include "hash/sha1.h" +#include "hash/sha.h" #include "sha1dc/sha1.h" diff --git a/src/hash/sha1/common_crypto.c b/src/util/hash/common_crypto.c similarity index 54% rename from src/hash/sha1/common_crypto.c rename to src/util/hash/common_crypto.c index 9d608f449..b327ba9e3 100644 --- a/src/hash/sha1/common_crypto.c +++ b/src/util/hash/common_crypto.c @@ -9,6 +9,8 @@ #define CC_LONG_MAX ((CC_LONG)-1) +#ifdef GIT_SHA1_COMMON_CRYPTO + int git_hash_sha1_global_init(void) { return 0; @@ -55,3 +57,56 @@ int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx) CC_SHA1_Final(out, &ctx->c); return 0; } + +#endif + +#ifdef GIT_SHA256_COMMON_CRYPTO + +int git_hash_sha256_global_init(void) +{ + return 0; +} + +int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx) +{ + return git_hash_sha256_init(ctx); +} + +void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx) +{ + GIT_UNUSED(ctx); +} + +int git_hash_sha256_init(git_hash_sha256_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + CC_SHA256_Init(&ctx->c); + return 0; +} + +int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *_data, size_t len) +{ + const unsigned char *data = _data; + + GIT_ASSERT_ARG(ctx); + + while (len > 0) { + CC_LONG chunk = (len > CC_LONG_MAX) ? CC_LONG_MAX : (CC_LONG)len; + + CC_SHA256_Update(&ctx->c, data, chunk); + + data += chunk; + len -= chunk; + } + + return 0; +} + +int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + CC_SHA256_Final(out, &ctx->c); + return 0; +} + +#endif diff --git a/src/hash/sha1/common_crypto.h b/src/util/hash/common_crypto.h similarity index 57% rename from src/hash/sha1/common_crypto.h rename to src/util/hash/common_crypto.h index a5fcfb33e..157712b5f 100644 --- a/src/hash/sha1/common_crypto.h +++ b/src/util/hash/common_crypto.h @@ -5,15 +5,23 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#ifndef INCLUDE_hash_sha1_common_crypto_h__ -#define INCLUDE_hash_sha1_common_crypto_h__ +#ifndef INCLUDE_hash_common_crypto_h__ +#define INCLUDE_hash_common_crypto_h__ -#include "hash/sha1.h" +#include "hash/sha.h" #include +#ifdef GIT_SHA1_COMMON_CRYPTO struct git_hash_sha1_ctx { CC_SHA1_CTX c; }; +#endif + +#ifdef GIT_SHA256_COMMON_CRYPTO +struct git_hash_sha256_ctx { + CC_SHA256_CTX c; +}; +#endif #endif diff --git a/src/hash/sha1/mbedtls.c b/src/util/hash/mbedtls.c similarity index 53% rename from src/hash/sha1/mbedtls.c rename to src/util/hash/mbedtls.c index 56016bec8..ecdfb7879 100644 --- a/src/hash/sha1/mbedtls.c +++ b/src/util/hash/mbedtls.c @@ -7,6 +7,8 @@ #include "mbedtls.h" +#ifdef GIT_SHA1_MBEDTLS + int git_hash_sha1_global_init(void) { return 0; @@ -44,3 +46,47 @@ int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx) mbedtls_sha1_finish(&ctx->c, out); return 0; } + +#endif + +#ifdef GIT_SHA256_MBEDTLS + +int git_hash_sha256_global_init(void) +{ + return 0; +} + +int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx) +{ + return git_hash_sha256_init(ctx); +} + +void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx) +{ + if (ctx) + mbedtls_sha256_free(&ctx->c); +} + +int git_hash_sha256_init(git_hash_sha256_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + mbedtls_sha256_init(&ctx->c); + mbedtls_sha256_starts(&ctx->c, 0); + return 0; +} + +int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len) +{ + GIT_ASSERT_ARG(ctx); + mbedtls_sha256_update(&ctx->c, data, len); + return 0; +} + +int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + mbedtls_sha256_finish(&ctx->c, out); + return 0; +} + +#endif diff --git a/src/hash/sha1/mbedtls.h b/src/util/hash/mbedtls.h similarity index 54% rename from src/hash/sha1/mbedtls.h rename to src/util/hash/mbedtls.h index 15f7462a4..05fb38b0e 100644 --- a/src/hash/sha1/mbedtls.h +++ b/src/util/hash/mbedtls.h @@ -5,15 +5,25 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#ifndef INCLUDE_hash_sha1_mbedtls_h__ -#define INCLUDE_hash_sha1_mbedtls_h__ +#ifndef INCLUDE_hash_mbedtls_h__ +#define INCLUDE_hash_mbedtls_h__ -#include "hash/sha1.h" +#include "hash/sha.h" -#include +#ifdef GIT_SHA1_MBEDTLS +# include struct git_hash_sha1_ctx { mbedtls_sha1_context c; }; +#endif + +#ifdef GIT_SHA256_MBEDTLS +# include + +struct git_hash_sha256_ctx { + mbedtls_sha256_context c; +}; +#endif #endif /* INCLUDE_hash_sha1_mbedtls_h__ */ diff --git a/src/util/hash/openssl.c b/src/util/hash/openssl.c new file mode 100644 index 000000000..649358ca2 --- /dev/null +++ b/src/util/hash/openssl.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "openssl.h" + +#ifdef GIT_OPENSSL_DYNAMIC +# include + +int handle_count; +void *openssl_handle; + +static int git_hash_openssl_global_shutdown(void) +{ + if (--handle_count == 0) { + dlclose(openssl_handle); + openssl_handle = NULL; + } + + return 0; +} + +static int git_hash_openssl_global_init(void) +{ + if (!handle_count) { + if ((openssl_handle = dlopen("libssl.so.1.1", RTLD_NOW)) == NULL && + (openssl_handle = dlopen("libssl.1.1.dylib", RTLD_NOW)) == NULL && + (openssl_handle = dlopen("libssl.so.1.0.0", RTLD_NOW)) == NULL && + (openssl_handle = dlopen("libssl.1.0.0.dylib", RTLD_NOW)) == NULL && + (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL) { + git_error_set(GIT_ERROR_SSL, "could not load ssl libraries"); + return -1; + } + } + + if (git_hash_openssl_global_shutdown() < 0) + return -1; + + handle_count++; + return 0; +} + +#endif + +#ifdef GIT_SHA1_OPENSSL + +# ifdef GIT_OPENSSL_DYNAMIC +static int (*SHA1_Init)(SHA_CTX *c); +static int (*SHA1_Update)(SHA_CTX *c, const void *data, size_t len); +static int (*SHA1_Final)(unsigned char *md, SHA_CTX *c); +# endif + +int git_hash_sha1_global_init(void) +{ +#ifdef GIT_OPENSSL_DYNAMIC + if (git_hash_openssl_global_init() < 0) + return -1; + + if ((SHA1_Init = dlsym(openssl_handle, "SHA1_Init")) == NULL || + (SHA1_Update = dlsym(openssl_handle, "SHA1_Update")) == NULL || + (SHA1_Final = dlsym(openssl_handle, "SHA1_Final")) == NULL) { + const char *msg = dlerror(); + git_error_set(GIT_ERROR_SSL, "could not load hash function: %s", msg ? msg : "unknown error"); + return -1; + } +#endif + + return 0; +} + +int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx) +{ + return git_hash_sha1_init(ctx); +} + +void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx) +{ + GIT_UNUSED(ctx); +} + +int git_hash_sha1_init(git_hash_sha1_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + + if (SHA1_Init(&ctx->c) != 1) { + git_error_set(GIT_ERROR_SHA, "failed to initialize sha1 context"); + return -1; + } + + return 0; +} + +int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len) +{ + GIT_ASSERT_ARG(ctx); + + if (SHA1_Update(&ctx->c, data, len) != 1) { + git_error_set(GIT_ERROR_SHA, "failed to update sha1"); + return -1; + } + + return 0; +} + +int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + + if (SHA1_Final(out, &ctx->c) != 1) { + git_error_set(GIT_ERROR_SHA, "failed to finalize sha1"); + return -1; + } + + return 0; +} + +#endif + +#ifdef GIT_SHA256_OPENSSL + +# ifdef GIT_OPENSSL_DYNAMIC +static int (*SHA256_Init)(SHA256_CTX *c); +static int (*SHA256_Update)(SHA256_CTX *c, const void *data, size_t len); +static int (*SHA256_Final)(unsigned char *md, SHA256_CTX *c); +#endif + +int git_hash_sha256_global_init(void) +{ +#ifdef GIT_OPENSSL_DYNAMIC + if (git_hash_openssl_global_init() < 0) + return -1; + + if ((SHA256_Init = dlsym(openssl_handle, "SHA256_Init")) == NULL || + (SHA256_Update = dlsym(openssl_handle, "SHA256_Update")) == NULL || + (SHA256_Final = dlsym(openssl_handle, "SHA256_Final")) == NULL) { + const char *msg = dlerror(); + git_error_set(GIT_ERROR_SSL, "could not load hash function: %s", msg ? msg : "unknown error"); + return -1; + } +#endif + + return 0; +} + +int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx) +{ + return git_hash_sha256_init(ctx); +} + +void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx) +{ + GIT_UNUSED(ctx); +} + +int git_hash_sha256_init(git_hash_sha256_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + + if (SHA256_Init(&ctx->c) != 1) { + git_error_set(GIT_ERROR_SHA, "failed to initialize sha256 context"); + return -1; + } + + return 0; +} + +int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len) +{ + GIT_ASSERT_ARG(ctx); + + if (SHA256_Update(&ctx->c, data, len) != 1) { + git_error_set(GIT_ERROR_SHA, "failed to update sha256"); + return -1; + } + + return 0; +} + +int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + + if (SHA256_Final(out, &ctx->c) != 1) { + git_error_set(GIT_ERROR_SHA, "failed to finalize sha256"); + return -1; + } + + return 0; +} + +#endif diff --git a/src/util/hash/openssl.h b/src/util/hash/openssl.h new file mode 100644 index 000000000..7cb089abc --- /dev/null +++ b/src/util/hash/openssl.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef INCLUDE_hash_openssl_h__ +#define INCLUDE_hash_openssl_h__ + +#include "hash/sha.h" + +#ifndef GIT_OPENSSL_DYNAMIC +# include +#else + +typedef struct { + unsigned int h0, h1, h2, h3, h4; + unsigned int Nl, Nh; + unsigned int data[16]; + unsigned int num; +} SHA_CTX; + +typedef struct { + unsigned int h[8]; + unsigned int Nl, Nh; + unsigned int data[16]; + unsigned int num, md_len; +} SHA256_CTX; + +#endif + +#ifdef GIT_SHA1_OPENSSL +struct git_hash_sha1_ctx { + SHA_CTX c; +}; +#endif + +#ifdef GIT_SHA256_OPENSSL +struct git_hash_sha256_ctx { + SHA256_CTX c; +}; +#endif + +#endif diff --git a/src/util/hash/rfc6234/sha.h b/src/util/hash/rfc6234/sha.h new file mode 100644 index 000000000..e0c400ca1 --- /dev/null +++ b/src/util/hash/rfc6234/sha.h @@ -0,0 +1,355 @@ +/**************************** sha.h ****************************/ +/***************** See RFC 6234 for details. *******************/ +/* + Copyright (c) 2011 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + - Redistributions of source code must retain the above + copyright notice, this list of conditions and + the following disclaimer. + + - Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + - Neither the name of Internet Society, IETF or IETF Trust, nor + the names of specific contributors, may be used to endorse or + promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef _SHA_H_ +#define _SHA_H_ + +/* + * Description: + * This file implements the Secure Hash Algorithms + * as defined in the U.S. National Institute of Standards + * and Technology Federal Information Processing Standards + * Publication (FIPS PUB) 180-3 published in October 2008 + * and formerly defined in its predecessors, FIPS PUB 180-1 + * and FIP PUB 180-2. + * + * A combined document showing all algorithms is available at + * http://csrc.nist.gov/publications/fips/ + * fips180-3/fips180-3_final.pdf + * + * The five hashes are defined in these sizes: + * SHA-1 20 byte / 160 bit + * SHA-224 28 byte / 224 bit + * SHA-256 32 byte / 256 bit + * SHA-384 48 byte / 384 bit + * SHA-512 64 byte / 512 bit + * + * Compilation Note: + * These files may be compiled with two options: + * USE_32BIT_ONLY - use 32-bit arithmetic only, for systems + * without 64-bit integers + * + * USE_MODIFIED_MACROS - use alternate form of the SHA_Ch() + * and SHA_Maj() macros that are equivalent + * and potentially faster on many systems + * + */ + +#include +/* + * If you do not have the ISO standard stdint.h header file, then you + * must typedef the following: + * name meaning + * uint64_t unsigned 64-bit integer + * uint32_t unsigned 32-bit integer + * uint8_t unsigned 8-bit integer (i.e., unsigned char) + * int_least16_t integer of >= 16 bits + * + * See stdint-example.h + */ + +#ifndef _SHA_enum_ +#define _SHA_enum_ +/* + * All SHA functions return one of these values. + */ +enum { + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError, /* called Input after FinalBits or Result */ + shaBadParam /* passed a bad parameter */ +}; +#endif /* _SHA_enum_ */ + +/* + * These constants hold size information for each of the SHA + * hashing operations + */ +enum { + SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64, + SHA256_Message_Block_Size = 64, SHA384_Message_Block_Size = 128, + SHA512_Message_Block_Size = 128, + USHA_Max_Message_Block_Size = SHA512_Message_Block_Size, + SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32, + SHA384HashSize = 48, SHA512HashSize = 64, + USHAMaxHashSize = SHA512HashSize, + + SHA1HashSizeBits = 160, SHA224HashSizeBits = 224, + SHA256HashSizeBits = 256, SHA384HashSizeBits = 384, + SHA512HashSizeBits = 512, USHAMaxHashSizeBits = SHA512HashSizeBits +}; + +/* + * These constants are used in the USHA (Unified SHA) functions. + */ +typedef enum SHAversion { + SHA1, SHA224, SHA256, SHA384, SHA512 +} SHAversion; + +/* + * This structure will hold context information for the SHA-1 + * hashing operation. + */ +typedef struct SHA1Context { + uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ + + uint32_t Length_High; /* Message length in bits */ + uint32_t Length_Low; /* Message length in bits */ + + int_least16_t Message_Block_Index; /* Message_Block array index */ + /* 512-bit message blocks */ + uint8_t Message_Block[SHA1_Message_Block_Size]; + + int Computed; /* Is the hash computed? */ + int Corrupted; /* Cumulative corruption code */ +} SHA1Context; + +/* + * This structure will hold context information for the SHA-256 + * hashing operation. + */ +typedef struct SHA256Context { + uint32_t Intermediate_Hash[SHA256HashSize/4]; /* Message Digest */ + + uint32_t Length_High; /* Message length in bits */ + uint32_t Length_Low; /* Message length in bits */ + + int_least16_t Message_Block_Index; /* Message_Block array index */ + /* 512-bit message blocks */ + uint8_t Message_Block[SHA256_Message_Block_Size]; + + int Computed; /* Is the hash computed? */ + int Corrupted; /* Cumulative corruption code */ +} SHA256Context; + +/* + * This structure will hold context information for the SHA-512 + * hashing operation. + */ +typedef struct SHA512Context { +#ifdef USE_32BIT_ONLY + uint32_t Intermediate_Hash[SHA512HashSize/4]; /* Message Digest */ + uint32_t Length[4]; /* Message length in bits */ +#else /* !USE_32BIT_ONLY */ + uint64_t Intermediate_Hash[SHA512HashSize/8]; /* Message Digest */ + uint64_t Length_High, Length_Low; /* Message length in bits */ +#endif /* USE_32BIT_ONLY */ + + int_least16_t Message_Block_Index; /* Message_Block array index */ + /* 1024-bit message blocks */ + uint8_t Message_Block[SHA512_Message_Block_Size]; + + int Computed; /* Is the hash computed?*/ + int Corrupted; /* Cumulative corruption code */ +} SHA512Context; + +/* + * This structure will hold context information for the SHA-224 + * hashing operation. It uses the SHA-256 structure for computation. + */ +typedef struct SHA256Context SHA224Context; + +/* + * This structure will hold context information for the SHA-384 + * hashing operation. It uses the SHA-512 structure for computation. + */ +typedef struct SHA512Context SHA384Context; + +/* + * This structure holds context information for all SHA + * hashing operations. + */ +typedef struct USHAContext { + int whichSha; /* which SHA is being used */ + union { + SHA1Context sha1Context; + SHA224Context sha224Context; SHA256Context sha256Context; + SHA384Context sha384Context; SHA512Context sha512Context; + } ctx; +} USHAContext; + +/* + * This structure will hold context information for the HMAC + * keyed-hashing operation. + */ +typedef struct HMACContext { + int whichSha; /* which SHA is being used */ + int hashSize; /* hash size of SHA being used */ + int blockSize; /* block size of SHA being used */ + USHAContext shaContext; /* SHA context */ + unsigned char k_opad[USHA_Max_Message_Block_Size]; + /* outer padding - key XORd with opad */ + int Computed; /* Is the MAC computed? */ + int Corrupted; /* Cumulative corruption code */ + +} HMACContext; + +/* + * This structure will hold context information for the HKDF + * extract-and-expand Key Derivation Functions. + */ +typedef struct HKDFContext { + int whichSha; /* which SHA is being used */ + HMACContext hmacContext; + int hashSize; /* hash size of SHA being used */ + unsigned char prk[USHAMaxHashSize]; + /* pseudo-random key - output of hkdfInput */ + int Computed; /* Is the key material computed? */ + int Corrupted; /* Cumulative corruption code */ +} HKDFContext; + +/* + * Function Prototypes + */ + +/* SHA-1 */ +extern int SHA1Reset(SHA1Context *); +extern int SHA1Input(SHA1Context *, const uint8_t *bytes, + unsigned int bytecount); +extern int SHA1FinalBits(SHA1Context *, uint8_t bits, + unsigned int bit_count); +extern int SHA1Result(SHA1Context *, + uint8_t Message_Digest[SHA1HashSize]); + +/* SHA-224 */ +extern int SHA224Reset(SHA224Context *); +extern int SHA224Input(SHA224Context *, const uint8_t *bytes, + unsigned int bytecount); +extern int SHA224FinalBits(SHA224Context *, uint8_t bits, + unsigned int bit_count); +extern int SHA224Result(SHA224Context *, + uint8_t Message_Digest[SHA224HashSize]); + +/* SHA-256 */ +extern int SHA256Reset(SHA256Context *); +extern int SHA256Input(SHA256Context *, const uint8_t *bytes, + unsigned int bytecount); +extern int SHA256FinalBits(SHA256Context *, uint8_t bits, + unsigned int bit_count); +extern int SHA256Result(SHA256Context *, + uint8_t Message_Digest[SHA256HashSize]); + +/* SHA-384 */ +extern int SHA384Reset(SHA384Context *); +extern int SHA384Input(SHA384Context *, const uint8_t *bytes, + unsigned int bytecount); +extern int SHA384FinalBits(SHA384Context *, uint8_t bits, + unsigned int bit_count); +extern int SHA384Result(SHA384Context *, + uint8_t Message_Digest[SHA384HashSize]); + +/* SHA-512 */ +extern int SHA512Reset(SHA512Context *); +extern int SHA512Input(SHA512Context *, const uint8_t *bytes, + unsigned int bytecount); +extern int SHA512FinalBits(SHA512Context *, uint8_t bits, + unsigned int bit_count); +extern int SHA512Result(SHA512Context *, + uint8_t Message_Digest[SHA512HashSize]); + +/* Unified SHA functions, chosen by whichSha */ +extern int USHAReset(USHAContext *context, SHAversion whichSha); +extern int USHAInput(USHAContext *context, + const uint8_t *bytes, unsigned int bytecount); +extern int USHAFinalBits(USHAContext *context, + uint8_t bits, unsigned int bit_count); +extern int USHAResult(USHAContext *context, + uint8_t Message_Digest[USHAMaxHashSize]); +extern int USHABlockSize(enum SHAversion whichSha); +extern int USHAHashSize(enum SHAversion whichSha); +extern int USHAHashSizeBits(enum SHAversion whichSha); +extern const char *USHAHashName(enum SHAversion whichSha); + +/* + * HMAC Keyed-Hashing for Message Authentication, RFC 2104, + * for all SHAs. + * This interface allows a fixed-length text input to be used. + */ +extern int hmac(SHAversion whichSha, /* which SHA algorithm to use */ + const unsigned char *text, /* pointer to data stream */ + int text_len, /* length of data stream */ + const unsigned char *key, /* pointer to authentication key */ + int key_len, /* length of authentication key */ + uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */ + +/* + * HMAC Keyed-Hashing for Message Authentication, RFC 2104, + * for all SHAs. + * This interface allows any length of text input to be used. + */ +extern int hmacReset(HMACContext *context, enum SHAversion whichSha, + const unsigned char *key, int key_len); +extern int hmacInput(HMACContext *context, const unsigned char *text, + int text_len); +extern int hmacFinalBits(HMACContext *context, uint8_t bits, + unsigned int bit_count); +extern int hmacResult(HMACContext *context, + uint8_t digest[USHAMaxHashSize]); + +/* + * HKDF HMAC-based Extract-and-Expand Key Derivation Function, + * RFC 5869, for all SHAs. + */ +extern int hkdf(SHAversion whichSha, const unsigned char *salt, + int salt_len, const unsigned char *ikm, int ikm_len, + const unsigned char *info, int info_len, + uint8_t okm[ ], int okm_len); +extern int hkdfExtract(SHAversion whichSha, const unsigned char *salt, + int salt_len, const unsigned char *ikm, + int ikm_len, uint8_t prk[USHAMaxHashSize]); +extern int hkdfExpand(SHAversion whichSha, const uint8_t prk[ ], + int prk_len, const unsigned char *info, + int info_len, uint8_t okm[ ], int okm_len); + +/* + * HKDF HMAC-based Extract-and-Expand Key Derivation Function, + * RFC 5869, for all SHAs. + * This interface allows any length of text input to be used. + */ +extern int hkdfReset(HKDFContext *context, enum SHAversion whichSha, + const unsigned char *salt, int salt_len); +extern int hkdfInput(HKDFContext *context, const unsigned char *ikm, + int ikm_len); +extern int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits, + unsigned int ikm_bit_count); +extern int hkdfResult(HKDFContext *context, + uint8_t prk[USHAMaxHashSize], + const unsigned char *info, int info_len, + uint8_t okm[USHAMaxHashSize], int okm_len); +#endif /* _SHA_H_ */ diff --git a/src/util/hash/rfc6234/sha224-256.c b/src/util/hash/rfc6234/sha224-256.c new file mode 100644 index 000000000..c8e0cf854 --- /dev/null +++ b/src/util/hash/rfc6234/sha224-256.c @@ -0,0 +1,601 @@ +/************************* sha224-256.c ************************/ +/***************** See RFC 6234 for details. *******************/ +/* Copyright (c) 2011 IETF Trust and the persons identified as */ +/* authors of the code. All rights reserved. */ +/* See sha.h for terms of use and redistribution. */ + +/* + * Description: + * This file implements the Secure Hash Algorithms SHA-224 and + * SHA-256 as defined in the U.S. National Institute of Standards + * and Technology Federal Information Processing Standards + * Publication (FIPS PUB) 180-3 published in October 2008 + * and formerly defined in its predecessors, FIPS PUB 180-1 + * and FIP PUB 180-2. + * + * A combined document showing all algorithms is available at + * http://csrc.nist.gov/publications/fips/ + * fips180-3/fips180-3_final.pdf + * + * The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit + * message digests for a given data stream. It should take about + * 2**n steps to find a message with the same digest as a given + * message and 2**(n/2) to find any two messages with the same + * digest, when n is the digest size in bits. Therefore, this + * algorithm can serve as a means of providing a + * "fingerprint" for a message. + * + * Portability Issues: + * SHA-224 and SHA-256 are defined in terms of 32-bit "words". + * This code uses (included via "sha.h") to define 32- + * and 8-bit unsigned integer types. If your C compiler does not + * support 32-bit unsigned integers, this code is not + * appropriate. + * + * Caveats: + * SHA-224 and SHA-256 are designed to work with messages less + * than 2^64 bits long. This implementation uses SHA224/256Input() + * to hash the bits that are a multiple of the size of an 8-bit + * octet, and then optionally uses SHA224/256FinalBits() + * to hash the final few bits of the input. + */ + +#include "sha.h" + +/* + * These definitions are defined in FIPS 180-3, section 4.1. + * Ch() and Maj() are defined identically in sections 4.1.1, + * 4.1.2, and 4.1.3. + * + * The definitions used in FIPS 180-3 are as follows: + */ +#ifndef USE_MODIFIED_MACROS +#define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#else /* USE_MODIFIED_MACROS */ +/* + * The following definitions are equivalent and potentially faster. + */ +#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z)) +#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) +#endif /* USE_MODIFIED_MACROS */ + +#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z)) + +/* Define the SHA shift, rotate left, and rotate right macros */ +#define SHA256_SHR(bits,word) ((word) >> (bits)) +#define SHA256_ROTL(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) +#define SHA256_ROTR(bits,word) \ + (((word) >> (bits)) | ((word) << (32-(bits)))) + +/* Define the SHA SIGMA and sigma macros */ +#define SHA256_SIGMA0(word) \ + (SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word)) +#define SHA256_SIGMA1(word) \ + (SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word)) +#define SHA256_sigma0(word) \ + (SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word)) +#define SHA256_sigma1(word) \ + (SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word)) + +/* + * Add "length" to the length. + * Set Corrupted when overflow has occurred. + */ +static uint32_t addTemp; +#define SHA224_256AddLength(context, length) \ + (addTemp = (context)->Length_Low, (context)->Corrupted = \ + (((context)->Length_Low += (length)) < addTemp) && \ + (++(context)->Length_High == 0) ? shaInputTooLong : \ + (context)->Corrupted ) + +/* Local Function Prototypes */ +static int SHA224_256Reset(SHA256Context *context, uint32_t *H0); +static void SHA224_256ProcessMessageBlock(SHA256Context *context); +static void SHA224_256Finalize(SHA256Context *context, + uint8_t Pad_Byte); +static void SHA224_256PadMessage(SHA256Context *context, + uint8_t Pad_Byte); +static int SHA224_256ResultN(SHA256Context *context, + uint8_t Message_Digest[ ], int HashSize); + +/* Initial Hash Values: FIPS 180-3 section 5.3.2 */ +static uint32_t SHA224_H0[SHA256HashSize/4] = { + 0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939, + 0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4 +}; + +/* Initial Hash Values: FIPS 180-3 section 5.3.3 */ +static uint32_t SHA256_H0[SHA256HashSize/4] = { + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 +}; + +/* + * SHA224Reset + * + * Description: + * This function will initialize the SHA224Context in preparation + * for computing a new SHA224 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * sha Error Code. + */ +int SHA224Reset(SHA224Context *context) +{ + return SHA224_256Reset(context, SHA224_H0); +} + +/* + * SHA224Input + * + * Description: + * This function accepts an array of octets as the next portion + * of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update. + * message_array[ ]: [in] + * An array of octets representing the next portion of + * the message. + * length: [in] + * The length of the message in message_array. + * + * Returns: + * sha Error Code. + * + */ +int SHA224Input(SHA224Context *context, const uint8_t *message_array, + unsigned int length) +{ + return SHA256Input(context, message_array, length); +} + +/* + * SHA224FinalBits + * + * Description: + * This function will add in any final bits of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update. + * message_bits: [in] + * The final bits of the message, in the upper portion of the + * byte. (Use 0b###00000 instead of 0b00000### to input the + * three bits ###.) + * length: [in] + * The number of bits in message_bits, between 1 and 7. + * + * Returns: + * sha Error Code. + */ +int SHA224FinalBits(SHA224Context *context, + uint8_t message_bits, unsigned int length) +{ + return SHA256FinalBits(context, message_bits, length); +} + +/* + * SHA224Result + * + * Description: + * This function will return the 224-bit message digest + * into the Message_Digest array provided by the caller. + * NOTE: + * The first octet of hash is stored in the element with index 0, + * the last octet of hash in the element with index 27. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA hash. + * Message_Digest[ ]: [out] + * Where the digest is returned. + * + * Returns: + * sha Error Code. + */ +int SHA224Result(SHA224Context *context, + uint8_t Message_Digest[SHA224HashSize]) +{ + return SHA224_256ResultN(context, Message_Digest, SHA224HashSize); +} + +/* + * SHA256Reset + * + * Description: + * This function will initialize the SHA256Context in preparation + * for computing a new SHA256 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * sha Error Code. + */ +int SHA256Reset(SHA256Context *context) +{ + return SHA224_256Reset(context, SHA256_H0); +} + +/* + * SHA256Input + * + * Description: + * This function accepts an array of octets as the next portion + * of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update. + * message_array[ ]: [in] + * An array of octets representing the next portion of + * the message. + * length: [in] + * The length of the message in message_array. + * + * Returns: + * sha Error Code. + */ +int SHA256Input(SHA256Context *context, const uint8_t *message_array, + unsigned int length) +{ + if (!context) return shaNull; + if (!length) return shaSuccess; + if (!message_array) return shaNull; + if (context->Computed) return context->Corrupted = shaStateError; + if (context->Corrupted) return context->Corrupted; + + while (length--) { + context->Message_Block[context->Message_Block_Index++] = + *message_array; + + if ((SHA224_256AddLength(context, 8) == shaSuccess) && + (context->Message_Block_Index == SHA256_Message_Block_Size)) + SHA224_256ProcessMessageBlock(context); + + message_array++; + } + + return context->Corrupted; + +} + +/* + * SHA256FinalBits + * + * Description: + * This function will add in any final bits of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update. + * message_bits: [in] + * The final bits of the message, in the upper portion of the + * byte. (Use 0b###00000 instead of 0b00000### to input the + * three bits ###.) + * length: [in] + * The number of bits in message_bits, between 1 and 7. + * + * Returns: + * sha Error Code. + */ +int SHA256FinalBits(SHA256Context *context, + uint8_t message_bits, unsigned int length) +{ + static uint8_t masks[8] = { + /* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80, + /* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0, + /* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8, + /* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE + }; + static uint8_t markbit[8] = { + /* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40, + /* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10, + /* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04, + /* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01 + }; + + if (!context) return shaNull; + if (!length) return shaSuccess; + if (context->Corrupted) return context->Corrupted; + if (context->Computed) return context->Corrupted = shaStateError; + if (length >= 8) return context->Corrupted = shaBadParam; + + SHA224_256AddLength(context, length); + SHA224_256Finalize(context, (uint8_t) + ((message_bits & masks[length]) | markbit[length])); + + return context->Corrupted; +} + +/* + * SHA256Result + * + * Description: + * This function will return the 256-bit message digest + * into the Message_Digest array provided by the caller. + * NOTE: + * The first octet of hash is stored in the element with index 0, + * the last octet of hash in the element with index 31. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA hash. + * Message_Digest[ ]: [out] + * Where the digest is returned. + * + * Returns: + * sha Error Code. + */ +int SHA256Result(SHA256Context *context, + uint8_t Message_Digest[SHA256HashSize]) +{ + return SHA224_256ResultN(context, Message_Digest, SHA256HashSize); +} + +/* + * SHA224_256Reset + * + * Description: + * This helper function will initialize the SHA256Context in + * preparation for computing a new SHA-224 or SHA-256 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * H0[ ]: [in] + * The initial hash value array to use. + * + * Returns: + * sha Error Code. + */ +static int SHA224_256Reset(SHA256Context *context, uint32_t *H0) +{ + if (!context) return shaNull; + + context->Length_High = context->Length_Low = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = H0[0]; + context->Intermediate_Hash[1] = H0[1]; + context->Intermediate_Hash[2] = H0[2]; + context->Intermediate_Hash[3] = H0[3]; + context->Intermediate_Hash[4] = H0[4]; + context->Intermediate_Hash[5] = H0[5]; + context->Intermediate_Hash[6] = H0[6]; + context->Intermediate_Hash[7] = H0[7]; + + context->Computed = 0; + context->Corrupted = shaSuccess; + + return shaSuccess; +} + +/* + * SHA224_256ProcessMessageBlock + * + * Description: + * This helper function will process the next 512 bits of the + * message stored in the Message_Block array. + * + * Parameters: + * context: [in/out] + * The SHA context to update. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in this code, especially the + * single character names, were used because those were the + * names used in the Secure Hash Standard. + */ +static void SHA224_256ProcessMessageBlock(SHA256Context *context) +{ + /* Constants defined in FIPS 180-3, section 4.2.2 */ + static const uint32_t K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, + 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, + 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, + 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, + 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, + 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, + 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, + 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, + 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + int t, t4; /* Loop counter */ + uint32_t temp1, temp2; /* Temporary word value */ + uint32_t W[64]; /* Word sequence */ + uint32_t A, B, C, D, E, F, G, H; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = t4 = 0; t < 16; t++, t4 += 4) + W[t] = (((uint32_t)context->Message_Block[t4]) << 24) | + (((uint32_t)context->Message_Block[t4 + 1]) << 16) | + (((uint32_t)context->Message_Block[t4 + 2]) << 8) | + (((uint32_t)context->Message_Block[t4 + 3])); + + for (t = 16; t < 64; t++) + W[t] = SHA256_sigma1(W[t-2]) + W[t-7] + + SHA256_sigma0(W[t-15]) + W[t-16]; + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + F = context->Intermediate_Hash[5]; + G = context->Intermediate_Hash[6]; + H = context->Intermediate_Hash[7]; + + for (t = 0; t < 64; t++) { + temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E,F,G) + K[t] + W[t]; + temp2 = SHA256_SIGMA0(A) + SHA_Maj(A,B,C); + H = G; + G = F; + F = E; + E = D + temp1; + D = C; + C = B; + B = A; + A = temp1 + temp2; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + context->Intermediate_Hash[5] += F; + context->Intermediate_Hash[6] += G; + context->Intermediate_Hash[7] += H; + + context->Message_Block_Index = 0; +} + +/* + * SHA224_256Finalize + * + * Description: + * This helper function finishes off the digest calculations. + * + * Parameters: + * context: [in/out] + * The SHA context to update. + * Pad_Byte: [in] + * The last byte to add to the message block before the 0-padding + * and length. This will contain the last bits of the message + * followed by another single bit. If the message was an + * exact multiple of 8-bits long, Pad_Byte will be 0x80. + * + * Returns: + * sha Error Code. + */ +static void SHA224_256Finalize(SHA256Context *context, + uint8_t Pad_Byte) +{ + int i; + SHA224_256PadMessage(context, Pad_Byte); + /* message may be sensitive, so clear it out */ + for (i = 0; i < SHA256_Message_Block_Size; ++i) + context->Message_Block[i] = 0; + context->Length_High = 0; /* and clear length */ + context->Length_Low = 0; + context->Computed = 1; +} + +/* + * SHA224_256PadMessage + * + * Description: + * According to the standard, the message must be padded to the next + * even multiple of 512 bits. The first padding bit must be a '1'. + * The last 64 bits represent the length of the original message. + * All bits in between should be 0. This helper function will pad + * the message according to those rules by filling the + * Message_Block array accordingly. When it returns, it can be + * assumed that the message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad. + * Pad_Byte: [in] + * The last byte to add to the message block before the 0-padding + * and length. This will contain the last bits of the message + * followed by another single bit. If the message was an + * exact multiple of 8-bits long, Pad_Byte will be 0x80. + * + * Returns: + * Nothing. + */ +static void SHA224_256PadMessage(SHA256Context *context, + uint8_t Pad_Byte) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index >= (SHA256_Message_Block_Size-8)) { + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + while (context->Message_Block_Index < SHA256_Message_Block_Size) + context->Message_Block[context->Message_Block_Index++] = 0; + SHA224_256ProcessMessageBlock(context); + } else + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + + while (context->Message_Block_Index < (SHA256_Message_Block_Size-8)) + context->Message_Block[context->Message_Block_Index++] = 0; + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (uint8_t)(context->Length_High >> 24); + context->Message_Block[57] = (uint8_t)(context->Length_High >> 16); + context->Message_Block[58] = (uint8_t)(context->Length_High >> 8); + context->Message_Block[59] = (uint8_t)(context->Length_High); + context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24); + context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16); + context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8); + context->Message_Block[63] = (uint8_t)(context->Length_Low); + + SHA224_256ProcessMessageBlock(context); +} + +/* + * SHA224_256ResultN + * + * Description: + * This helper function will return the 224-bit or 256-bit message + * digest into the Message_Digest array provided by the caller. + * NOTE: + * The first octet of hash is stored in the element with index 0, + * the last octet of hash in the element with index 27/31. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA hash. + * Message_Digest[ ]: [out] + * Where the digest is returned. + * HashSize: [in] + * The size of the hash, either 28 or 32. + * + * Returns: + * sha Error Code. + */ +static int SHA224_256ResultN(SHA256Context *context, + uint8_t Message_Digest[ ], int HashSize) +{ + int i; + + if (!context) return shaNull; + if (!Message_Digest) return shaNull; + if (context->Corrupted) return context->Corrupted; + + if (!context->Computed) + SHA224_256Finalize(context, 0x80); + + for (i = 0; i < HashSize; ++i) + Message_Digest[i] = (uint8_t) + (context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) )); + + return shaSuccess; +} + diff --git a/src/util/hash/sha.h b/src/util/hash/sha.h new file mode 100644 index 000000000..4f596234c --- /dev/null +++ b/src/util/hash/sha.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef INCLUDE_hash_sha_h__ +#define INCLUDE_hash_sha_h__ + +#include "git2_util.h" + +typedef struct git_hash_sha1_ctx git_hash_sha1_ctx; +typedef struct git_hash_sha256_ctx git_hash_sha256_ctx; + +#if defined(GIT_SHA1_COMMON_CRYPTO) || defined(GIT_SHA256_COMMON_CRYPTO) +# include "common_crypto.h" +#endif + +#if defined(GIT_SHA1_OPENSSL) || defined(GIT_SHA256_OPENSSL) +# include "openssl.h" +#endif + +#if defined(GIT_SHA1_WIN32) || defined(GIT_SHA256_WIN32) +# include "win32.h" +#endif + +#if defined(GIT_SHA1_MBEDTLS) || defined(GIT_SHA256_MBEDTLS) +# include "mbedtls.h" +#endif + +#if defined(GIT_SHA1_COLLISIONDETECT) +# include "collisiondetect.h" +#endif + +#if defined(GIT_SHA256_BUILTIN) +# include "builtin.h" +#endif + +/* + * SHA1 + */ + +#define GIT_HASH_SHA1_SIZE 20 + +int git_hash_sha1_global_init(void); + +int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx); +void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx); + +int git_hash_sha1_init(git_hash_sha1_ctx *c); +int git_hash_sha1_update(git_hash_sha1_ctx *c, const void *data, size_t len); +int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *c); + +/* + * SHA256 + */ + +#define GIT_HASH_SHA256_SIZE 32 + +int git_hash_sha256_global_init(void); + +int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx); +void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx); + +int git_hash_sha256_init(git_hash_sha256_ctx *c); +int git_hash_sha256_update(git_hash_sha256_ctx *c, const void *data, size_t len); +int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *c); + +#endif diff --git a/src/hash/sha1/sha1dc/sha1.c b/src/util/hash/sha1dc/sha1.c similarity index 100% rename from src/hash/sha1/sha1dc/sha1.c rename to src/util/hash/sha1dc/sha1.c diff --git a/src/hash/sha1/sha1dc/sha1.h b/src/util/hash/sha1dc/sha1.h similarity index 100% rename from src/hash/sha1/sha1dc/sha1.h rename to src/util/hash/sha1dc/sha1.h diff --git a/src/hash/sha1/sha1dc/ubc_check.c b/src/util/hash/sha1dc/ubc_check.c similarity index 100% rename from src/hash/sha1/sha1dc/ubc_check.c rename to src/util/hash/sha1dc/ubc_check.c diff --git a/src/hash/sha1/sha1dc/ubc_check.h b/src/util/hash/sha1dc/ubc_check.h similarity index 100% rename from src/hash/sha1/sha1dc/ubc_check.h rename to src/util/hash/sha1dc/ubc_check.h diff --git a/src/util/hash/win32.c b/src/util/hash/win32.c new file mode 100644 index 000000000..f80c0d5ca --- /dev/null +++ b/src/util/hash/win32.c @@ -0,0 +1,549 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "win32.h" + +#include "runtime.h" + +#include +#include + +#define GIT_HASH_CNG_DLL_NAME "bcrypt.dll" + +/* BCRYPT_SHA1_ALGORITHM */ +#define GIT_HASH_CNG_SHA1_TYPE L"SHA1" +#define GIT_HASH_CNG_SHA256_TYPE L"SHA256" + +/* BCRYPT_OBJECT_LENGTH */ +#define GIT_HASH_CNG_HASH_OBJECT_LEN L"ObjectLength" + +/* BCRYPT_HASH_REUSEABLE_FLAGS */ +#define GIT_HASH_CNG_HASH_REUSABLE 0x00000020 + +/* Definitions */ + +/* CryptoAPI is available for hashing on Windows XP and newer. */ +struct cryptoapi_provider { + HCRYPTPROV handle; +}; + +/* + * CNG (bcrypt.dll) is significantly more performant than CryptoAPI and is + * preferred, however it is only available on Windows 2008 and newer and + * must therefore be dynamically loaded, and we must inline constants that + * would not exist when building in pre-Windows 2008 environments. + */ + +/* Function declarations for CNG */ +typedef NTSTATUS (WINAPI *cng_open_algorithm_provider_fn)( + HANDLE /* BCRYPT_ALG_HANDLE */ *phAlgorithm, + LPCWSTR pszAlgId, + LPCWSTR pszImplementation, + DWORD dwFlags); + +typedef NTSTATUS (WINAPI *cng_get_property_fn)( + HANDLE /* BCRYPT_HANDLE */ hObject, + LPCWSTR pszProperty, + PUCHAR pbOutput, + ULONG cbOutput, + ULONG *pcbResult, + ULONG dwFlags); + +typedef NTSTATUS (WINAPI *cng_create_hash_fn)( + HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm, + HANDLE /* BCRYPT_HASH_HANDLE */ *phHash, + PUCHAR pbHashObject, ULONG cbHashObject, + PUCHAR pbSecret, + ULONG cbSecret, + ULONG dwFlags); + +typedef NTSTATUS (WINAPI *cng_finish_hash_fn)( + HANDLE /* BCRYPT_HASH_HANDLE */ hHash, + PUCHAR pbOutput, + ULONG cbOutput, + ULONG dwFlags); + +typedef NTSTATUS (WINAPI *cng_hash_data_fn)( + HANDLE /* BCRYPT_HASH_HANDLE */ hHash, + PUCHAR pbInput, + ULONG cbInput, + ULONG dwFlags); + +typedef NTSTATUS (WINAPI *cng_destroy_hash_fn)( + HANDLE /* BCRYPT_HASH_HANDLE */ hHash); + +typedef NTSTATUS (WINAPI *cng_close_algorithm_provider_fn)( + HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm, + ULONG dwFlags); + +struct cng_provider { + /* DLL for CNG */ + HINSTANCE dll; + + /* Function pointers for CNG */ + cng_open_algorithm_provider_fn open_algorithm_provider; + cng_get_property_fn get_property; + cng_create_hash_fn create_hash; + cng_finish_hash_fn finish_hash; + cng_hash_data_fn hash_data; + cng_destroy_hash_fn destroy_hash; + cng_close_algorithm_provider_fn close_algorithm_provider; + + HANDLE /* BCRYPT_ALG_HANDLE */ sha1_handle; + DWORD sha1_object_size; + + HANDLE /* BCRYPT_ALG_HANDLE */ sha256_handle; + DWORD sha256_object_size; +}; + +typedef struct { + git_hash_win32_provider_t type; + + union { + struct cryptoapi_provider cryptoapi; + struct cng_provider cng; + } provider; +} hash_win32_provider; + +/* Hash provider definition */ + +static hash_win32_provider hash_provider = {0}; + +/* Hash initialization */ + +/* Initialize CNG, if available */ +GIT_INLINE(int) cng_provider_init(void) +{ + char dll_path[MAX_PATH]; + DWORD dll_path_len, size_len; + + /* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */ + if (!git_has_win32_version(6, 0, 1)) { + git_error_set(GIT_ERROR_SHA, "CryptoNG is not supported on this platform"); + return -1; + } + + /* Load bcrypt.dll explicitly from the system directory */ + if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 || + dll_path_len > MAX_PATH || + StringCchCat(dll_path, MAX_PATH, "\\") < 0 || + StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 || + (hash_provider.provider.cng.dll = LoadLibrary(dll_path)) == NULL) { + git_error_set(GIT_ERROR_SHA, "CryptoNG library could not be loaded"); + return -1; + } + + /* Load the function addresses */ + if ((hash_provider.provider.cng.open_algorithm_provider = (cng_open_algorithm_provider_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptOpenAlgorithmProvider"))) == NULL || + (hash_provider.provider.cng.get_property = (cng_get_property_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptGetProperty"))) == NULL || + (hash_provider.provider.cng.create_hash = (cng_create_hash_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptCreateHash"))) == NULL || + (hash_provider.provider.cng.finish_hash = (cng_finish_hash_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptFinishHash"))) == NULL || + (hash_provider.provider.cng.hash_data = (cng_hash_data_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptHashData"))) == NULL || + (hash_provider.provider.cng.destroy_hash = (cng_destroy_hash_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptDestroyHash"))) == NULL || + (hash_provider.provider.cng.close_algorithm_provider = (cng_close_algorithm_provider_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptCloseAlgorithmProvider"))) == NULL) { + FreeLibrary(hash_provider.provider.cng.dll); + + git_error_set(GIT_ERROR_OS, "CryptoNG functions could not be loaded"); + return -1; + } + + /* Load the SHA1 algorithm */ + if (hash_provider.provider.cng.open_algorithm_provider(&hash_provider.provider.cng.sha1_handle, GIT_HASH_CNG_SHA1_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0 || + hash_provider.provider.cng.get_property(hash_provider.provider.cng.sha1_handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_provider.provider.cng.sha1_object_size, sizeof(DWORD), &size_len, 0) < 0) { + git_error_set(GIT_ERROR_OS, "algorithm provider could not be initialized"); + goto on_error; + } + + /* Load the SHA256 algorithm */ + if (hash_provider.provider.cng.open_algorithm_provider(&hash_provider.provider.cng.sha256_handle, GIT_HASH_CNG_SHA256_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0 || + hash_provider.provider.cng.get_property(hash_provider.provider.cng.sha256_handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_provider.provider.cng.sha256_object_size, sizeof(DWORD), &size_len, 0) < 0) { + git_error_set(GIT_ERROR_OS, "algorithm provider could not be initialized"); + goto on_error; + } + + hash_provider.type = GIT_HASH_WIN32_CNG; + return 0; + +on_error: + if (hash_provider.provider.cng.sha1_handle) + hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha1_handle, 0); + + if (hash_provider.provider.cng.sha256_handle) + hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha256_handle, 0); + + if (hash_provider.provider.cng.dll) + FreeLibrary(hash_provider.provider.cng.dll); + + return -1; +} + +GIT_INLINE(void) cng_provider_shutdown(void) +{ + if (hash_provider.type == GIT_HASH_WIN32_INVALID) + return; + + hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha1_handle, 0); + hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha256_handle, 0); + FreeLibrary(hash_provider.provider.cng.dll); + + hash_provider.type = GIT_HASH_WIN32_INVALID; +} + +/* Initialize CryptoAPI */ +GIT_INLINE(int) cryptoapi_provider_init(void) +{ + if (!CryptAcquireContext(&hash_provider.provider.cryptoapi.handle, NULL, 0, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { + git_error_set(GIT_ERROR_OS, "legacy hash context could not be started"); + return -1; + } + + hash_provider.type = GIT_HASH_WIN32_CRYPTOAPI; + return 0; +} + +GIT_INLINE(void) cryptoapi_provider_shutdown(void) +{ + if (hash_provider.type == GIT_HASH_WIN32_INVALID) + return; + + CryptReleaseContext(hash_provider.provider.cryptoapi.handle, 0); + + hash_provider.type = GIT_HASH_WIN32_INVALID; +} + +static void hash_provider_shutdown(void) +{ + if (hash_provider.type == GIT_HASH_WIN32_CNG) + cng_provider_shutdown(); + else if (hash_provider.type == GIT_HASH_WIN32_CRYPTOAPI) + cryptoapi_provider_shutdown(); +} + +static int hash_provider_init(void) +{ + int error = 0; + + if (hash_provider.type != GIT_HASH_WIN32_INVALID) + return 0; + + if ((error = cng_provider_init()) < 0) + error = cryptoapi_provider_init(); + + if (!error) + error = git_runtime_shutdown_register(hash_provider_shutdown); + + return error; +} + +git_hash_win32_provider_t git_hash_win32_provider(void) +{ + return hash_provider.type; +} + +int git_hash_win32_set_provider(git_hash_win32_provider_t provider) +{ + if (provider == hash_provider.type) + return 0; + + hash_provider_shutdown(); + + if (provider == GIT_HASH_WIN32_CNG) + return cng_provider_init(); + else if (provider == GIT_HASH_WIN32_CRYPTOAPI) + return cryptoapi_provider_init(); + + git_error_set(GIT_ERROR_SHA, "unsupported win32 provider"); + return -1; +} + +/* CryptoAPI: available in Windows XP and newer */ + +GIT_INLINE(int) hash_cryptoapi_init(git_hash_win32_ctx *ctx) +{ + if (ctx->ctx.cryptoapi.valid) + CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle); + + if (!CryptCreateHash(hash_provider.provider.cryptoapi.handle, ctx->algorithm, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) { + ctx->ctx.cryptoapi.valid = 0; + git_error_set(GIT_ERROR_OS, "legacy hash implementation could not be created"); + return -1; + } + + ctx->ctx.cryptoapi.valid = 1; + return 0; +} + +GIT_INLINE(int) hash_cryptoapi_update(git_hash_win32_ctx *ctx, const void *_data, size_t len) +{ + const BYTE *data = (BYTE *)_data; + + GIT_ASSERT(ctx->ctx.cryptoapi.valid); + + while (len > 0) { + DWORD chunk = (len > MAXDWORD) ? MAXDWORD : (DWORD)len; + + if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, data, chunk, 0)) { + git_error_set(GIT_ERROR_OS, "legacy hash data could not be updated"); + return -1; + } + + data += chunk; + len -= chunk; + } + + return 0; +} + +GIT_INLINE(int) hash_cryptoapi_final(unsigned char *out, git_hash_win32_ctx *ctx) +{ + DWORD len = ctx->algorithm == CALG_SHA_256 ? GIT_HASH_SHA256_SIZE : GIT_HASH_SHA1_SIZE; + int error = 0; + + GIT_ASSERT(ctx->ctx.cryptoapi.valid); + + if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out, &len, 0)) { + git_error_set(GIT_ERROR_OS, "legacy hash data could not be finished"); + error = -1; + } + + CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle); + ctx->ctx.cryptoapi.valid = 0; + + return error; +} + +GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_win32_ctx *ctx) +{ + if (ctx->ctx.cryptoapi.valid) + CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle); +} + +GIT_INLINE(int) hash_sha1_cryptoapi_ctx_init_init(git_hash_win32_ctx *ctx) +{ + ctx->algorithm = CALG_SHA1; + return hash_cryptoapi_init(ctx); +} + +GIT_INLINE(int) hash_sha256_cryptoapi_ctx_init(git_hash_win32_ctx *ctx) +{ + ctx->algorithm = CALG_SHA_256; + return hash_cryptoapi_init(ctx); +} + +/* CNG: Available in Windows Server 2008 and newer */ + +GIT_INLINE(int) hash_sha1_cng_ctx_init(git_hash_win32_ctx *ctx) +{ + if ((ctx->ctx.cng.hash_object = git__malloc(hash_provider.provider.cng.sha1_object_size)) == NULL) + return -1; + + if (hash_provider.provider.cng.create_hash(hash_provider.provider.cng.sha1_handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_provider.provider.cng.sha1_object_size, NULL, 0, 0) < 0) { + git__free(ctx->ctx.cng.hash_object); + + git_error_set(GIT_ERROR_OS, "sha1 implementation could not be created"); + return -1; + } + + ctx->algorithm = CALG_SHA1; + return 0; +} + +GIT_INLINE(int) hash_sha256_cng_ctx_init(git_hash_win32_ctx *ctx) +{ + if ((ctx->ctx.cng.hash_object = git__malloc(hash_provider.provider.cng.sha256_object_size)) == NULL) + return -1; + + if (hash_provider.provider.cng.create_hash(hash_provider.provider.cng.sha256_handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_provider.provider.cng.sha256_object_size, NULL, 0, 0) < 0) { + git__free(ctx->ctx.cng.hash_object); + + git_error_set(GIT_ERROR_OS, "sha256 implementation could not be created"); + return -1; + } + + ctx->algorithm = CALG_SHA_256; + return 0; +} + +GIT_INLINE(int) hash_cng_init(git_hash_win32_ctx *ctx) +{ + BYTE hash[GIT_HASH_SHA256_SIZE]; + ULONG size = ctx->algorithm == CALG_SHA_256 ? GIT_HASH_SHA256_SIZE : GIT_HASH_SHA1_SIZE; + + if (!ctx->ctx.cng.updated) + return 0; + + /* CNG needs to be finished to restart */ + if (hash_provider.provider.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, size, 0) < 0) { + git_error_set(GIT_ERROR_OS, "hash implementation could not be finished"); + return -1; + } + + ctx->ctx.cng.updated = 0; + + return 0; +} + +GIT_INLINE(int) hash_cng_update(git_hash_win32_ctx *ctx, const void *_data, size_t len) +{ + PBYTE data = (PBYTE)_data; + + while (len > 0) { + ULONG chunk = (len > ULONG_MAX) ? ULONG_MAX : (ULONG)len; + + if (hash_provider.provider.cng.hash_data(ctx->ctx.cng.hash_handle, data, chunk, 0) < 0) { + git_error_set(GIT_ERROR_OS, "hash could not be updated"); + return -1; + } + + data += chunk; + len -= chunk; + } + + return 0; +} + +GIT_INLINE(int) hash_cng_final(unsigned char *out, git_hash_win32_ctx *ctx) +{ + ULONG size = ctx->algorithm == CALG_SHA_256 ? GIT_HASH_SHA256_SIZE : GIT_HASH_SHA1_SIZE; + + if (hash_provider.provider.cng.finish_hash(ctx->ctx.cng.hash_handle, out, size, 0) < 0) { + git_error_set(GIT_ERROR_OS, "hash could not be finished"); + return -1; + } + + ctx->ctx.cng.updated = 0; + + return 0; +} + +GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_win32_ctx *ctx) +{ + hash_provider.provider.cng.destroy_hash(ctx->ctx.cng.hash_handle); + git__free(ctx->ctx.cng.hash_object); +} + +/* Indirection between CryptoAPI and CNG */ + +GIT_INLINE(int) hash_sha1_win32_ctx_init(git_hash_win32_ctx *ctx) +{ + GIT_ASSERT_ARG(hash_provider.type); + + memset(ctx, 0x0, sizeof(git_hash_win32_ctx)); + return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_sha1_cng_ctx_init(ctx) : hash_sha1_cryptoapi_ctx_init_init(ctx); +} + +GIT_INLINE(int) hash_sha256_win32_ctx_init(git_hash_win32_ctx *ctx) +{ + GIT_ASSERT_ARG(hash_provider.type); + + memset(ctx, 0x0, sizeof(git_hash_win32_ctx)); + return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_sha256_cng_ctx_init(ctx) : hash_sha256_cryptoapi_ctx_init(ctx); +} + +GIT_INLINE(int) hash_win32_init(git_hash_win32_ctx *ctx) +{ + return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx); +} + +GIT_INLINE(int) hash_win32_update(git_hash_win32_ctx *ctx, const void *data, size_t len) +{ + return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len); +} + +GIT_INLINE(int) hash_win32_final(unsigned char *out, git_hash_win32_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx); +} + +GIT_INLINE(void) hash_win32_cleanup(git_hash_win32_ctx *ctx) +{ + if (hash_provider.type == GIT_HASH_WIN32_CNG) + hash_ctx_cng_cleanup(ctx); + else if(hash_provider.type == GIT_HASH_WIN32_CRYPTOAPI) + hash_ctx_cryptoapi_cleanup(ctx); +} + +#ifdef GIT_SHA1_WIN32 + +int git_hash_sha1_global_init(void) +{ + return hash_provider_init(); +} + +int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + return hash_sha1_win32_ctx_init(&ctx->win32); +} + +int git_hash_sha1_init(git_hash_sha1_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + return hash_win32_init(&ctx->win32); +} + +int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len) +{ + GIT_ASSERT_ARG(ctx); + return hash_win32_update(&ctx->win32, data, len); +} + +int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + return hash_win32_final(out, &ctx->win32); +} + +void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx) +{ + if (!ctx) + return; + hash_win32_cleanup(&ctx->win32); +} + +#endif + +#ifdef GIT_SHA256_WIN32 + +int git_hash_sha256_global_init(void) +{ + return hash_provider_init(); +} + +int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + return hash_sha256_win32_ctx_init(&ctx->win32); +} + +int git_hash_sha256_init(git_hash_sha256_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + return hash_win32_init(&ctx->win32); +} + +int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len) +{ + GIT_ASSERT_ARG(ctx); + return hash_win32_update(&ctx->win32, data, len); +} + +int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx) +{ + GIT_ASSERT_ARG(ctx); + return hash_win32_final(out, &ctx->win32); +} + +void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx) +{ + if (!ctx) + return; + hash_win32_cleanup(&ctx->win32); +} + +#endif diff --git a/src/util/hash/win32.h b/src/util/hash/win32.h new file mode 100644 index 000000000..a9fb87aee --- /dev/null +++ b/src/util/hash/win32.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef INCLUDE_hash_win32_h__ +#define INCLUDE_hash_win32_h__ + +#include "hash/sha.h" + +#include + +typedef enum { + GIT_HASH_WIN32_INVALID = 0, + GIT_HASH_WIN32_CRYPTOAPI, + GIT_HASH_WIN32_CNG +} git_hash_win32_provider_t; + +struct git_hash_win32_cryptoapi_ctx { + bool valid; + HCRYPTHASH hash_handle; +}; + +struct git_hash_win32_cng_ctx { + bool updated; + HANDLE /* BCRYPT_HASH_HANDLE */ hash_handle; + PBYTE hash_object; +}; + +typedef struct { + ALG_ID algorithm; + + union { + struct git_hash_win32_cryptoapi_ctx cryptoapi; + struct git_hash_win32_cng_ctx cng; + } ctx; +} git_hash_win32_ctx; + +/* + * Gets/sets the current hash provider (cng or cryptoapi). This is only + * for testing purposes. + */ +git_hash_win32_provider_t git_hash_win32_provider(void); +int git_hash_win32_set_provider(git_hash_win32_provider_t provider); + +#ifdef GIT_SHA1_WIN32 +struct git_hash_sha1_ctx { + git_hash_win32_ctx win32; +}; +#endif + +#ifdef GIT_SHA256_WIN32 +struct git_hash_sha256_ctx { + git_hash_win32_ctx win32; +}; +#endif + +#endif diff --git a/src/integer.h b/src/util/integer.h similarity index 100% rename from src/integer.h rename to src/util/integer.h diff --git a/src/khash.h b/src/util/khash.h similarity index 100% rename from src/khash.h rename to src/util/khash.h diff --git a/src/map.h b/src/util/map.h similarity index 98% rename from src/map.h rename to src/util/map.h index 01931d199..c101e46f6 100644 --- a/src/map.h +++ b/src/util/map.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_map_h__ #define INCLUDE_map_h__ -#include "common.h" +#include "git2_util.h" /* p_mmap() prot values */ diff --git a/src/net.c b/src/util/net.c similarity index 99% rename from src/net.c rename to src/util/net.c index a76fd1d7c..b2236daf8 100644 --- a/src/net.c +++ b/src/util/net.c @@ -6,7 +6,6 @@ */ #include "net.h" -#include "netops.h" #include diff --git a/src/net.h b/src/util/net.h similarity index 99% rename from src/net.h rename to src/util/net.h index 499315e6c..88030a952 100644 --- a/src/net.h +++ b/src/util/net.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_net_h__ #define INCLUDE_net_h__ -#include "common.h" +#include "git2_util.h" typedef struct git_net_url { char *scheme; diff --git a/src/pool.c b/src/util/pool.c similarity index 100% rename from src/pool.c rename to src/util/pool.c diff --git a/src/pool.h b/src/util/pool.h similarity index 99% rename from src/pool.h rename to src/util/pool.h index cecb84665..0238431b0 100644 --- a/src/pool.h +++ b/src/util/pool.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_pool_h__ #define INCLUDE_pool_h__ -#include "common.h" +#include "git2_util.h" #include "vector.h" diff --git a/src/posix.c b/src/util/posix.c similarity index 100% rename from src/posix.c rename to src/util/posix.c diff --git a/src/posix.h b/src/util/posix.h similarity index 99% rename from src/posix.h rename to src/util/posix.h index e6f603078..c8f8cd9d2 100644 --- a/src/posix.h +++ b/src/util/posix.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_posix_h__ #define INCLUDE_posix_h__ -#include "common.h" +#include "git2_util.h" #include #include diff --git a/src/pqueue.c b/src/util/pqueue.c similarity index 100% rename from src/pqueue.c rename to src/util/pqueue.c diff --git a/src/pqueue.h b/src/util/pqueue.h similarity index 98% rename from src/pqueue.h rename to src/util/pqueue.h index 4db74ea03..97232b4a9 100644 --- a/src/pqueue.h +++ b/src/util/pqueue.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_pqueue_h__ #define INCLUDE_pqueue_h__ -#include "common.h" +#include "git2_util.h" #include "vector.h" diff --git a/src/rand.c b/src/util/rand.c similarity index 98% rename from src/rand.c rename to src/util/rand.c index 0a208134e..d28e4aa97 100644 --- a/src/rand.c +++ b/src/util/rand.c @@ -6,7 +6,7 @@ worldwide. This software is distributed without any warranty. See . */ -#include "common.h" +#include "git2_util.h" #include "rand.h" #include "runtime.h" @@ -106,8 +106,6 @@ GIT_INLINE(int) getseed(uint64_t *seed) return -1; } - getloadavg(loadavg, 3); - *seed = 0; *seed |= ((uint64_t)tv.tv_usec << 40); *seed |= ((uint64_t)tv.tv_sec); @@ -119,9 +117,15 @@ GIT_INLINE(int) getseed(uint64_t *seed) *seed ^= ((uint64_t)getuid() << 8); *seed ^= ((uint64_t)getgid()); +# if defined(GIT_RAND_GETLOADAVG) + getloadavg(loadavg, 3); + convert.f = loadavg[0]; *seed ^= (convert.d >> 36); convert.f = loadavg[1]; *seed ^= (convert.d); convert.f = loadavg[2]; *seed ^= (convert.d >> 16); +# else + GIT_UNUSED(loadavg[0]); +# endif convert.f = git__timer(); *seed ^= (convert.d); diff --git a/src/rand.h b/src/util/rand.h similarity index 97% rename from src/rand.h rename to src/util/rand.h index 2e60561e5..fa0619aa2 100644 --- a/src/rand.h +++ b/src/util/rand.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_rand_h__ #define INCLUDE_rand_h__ -#include "common.h" +#include "git2_util.h" /** * Initialize the random number generation subsystem. This will diff --git a/src/regexp.c b/src/util/regexp.c similarity index 100% rename from src/regexp.c rename to src/util/regexp.c diff --git a/src/regexp.h b/src/util/regexp.h similarity index 99% rename from src/regexp.h rename to src/util/regexp.h index 2592ef383..d0862b107 100644 --- a/src/regexp.h +++ b/src/util/regexp.h @@ -8,7 +8,7 @@ #ifndef INCLUDE_regexp_h__ #define INCLUDE_regexp_h__ -#include "common.h" +#include "git2_util.h" #if defined(GIT_REGEX_BUILTIN) || defined(GIT_REGEX_PCRE) # include "pcre.h" diff --git a/src/runtime.c b/src/util/runtime.c similarity index 99% rename from src/runtime.c rename to src/util/runtime.c index c05dee8b9..a7711ffc4 100644 --- a/src/runtime.c +++ b/src/util/runtime.c @@ -5,7 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "git2_util.h" #include "runtime.h" static git_runtime_shutdown_fn shutdown_callback[32]; diff --git a/src/runtime.h b/src/util/runtime.h similarity index 98% rename from src/runtime.h rename to src/util/runtime.h index 24ac58ee9..6cbfd6043 100644 --- a/src/runtime.h +++ b/src/util/runtime.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_runtime_h__ #define INCLUDE_runtime_h__ -#include "common.h" +#include "git2_util.h" typedef int (*git_runtime_init_fn)(void); typedef void (*git_runtime_shutdown_fn)(void); diff --git a/src/sortedcache.c b/src/util/sortedcache.c similarity index 100% rename from src/sortedcache.c rename to src/util/sortedcache.c diff --git a/src/sortedcache.h b/src/util/sortedcache.h similarity index 99% rename from src/sortedcache.h rename to src/util/sortedcache.h index ef260a093..3eee4659f 100644 --- a/src/sortedcache.h +++ b/src/util/sortedcache.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_sorted_cache_h__ #define INCLUDE_sorted_cache_h__ -#include "common.h" +#include "git2_util.h" #include "util.h" #include "futils.h" diff --git a/src/str.c b/src/util/str.c similarity index 100% rename from src/str.c rename to src/util/str.c diff --git a/src/str.h b/src/util/str.h similarity index 99% rename from src/str.h rename to src/util/str.h index ef769ce2f..588e6fc22 100644 --- a/src/str.h +++ b/src/util/str.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_str_h__ #define INCLUDE_str_h__ -#include "common.h" +#include "git2_util.h" struct git_str { char *ptr; diff --git a/src/strmap.c b/src/util/strmap.c similarity index 100% rename from src/strmap.c rename to src/util/strmap.c diff --git a/src/strmap.h b/src/util/strmap.h similarity index 99% rename from src/strmap.h rename to src/util/strmap.h index 9f5e4cc8b..b64d3dcb5 100644 --- a/src/strmap.h +++ b/src/util/strmap.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_strmap_h__ #define INCLUDE_strmap_h__ -#include "common.h" +#include "git2_util.h" /** A map with C strings as key. */ typedef struct kh_str_s git_strmap; diff --git a/src/strnlen.h b/src/util/strnlen.h similarity index 100% rename from src/strnlen.h rename to src/util/strnlen.h diff --git a/src/thread.c b/src/util/thread.c similarity index 99% rename from src/thread.c rename to src/util/thread.c index 3171771d7..bc7364f8c 100644 --- a/src/thread.c +++ b/src/util/thread.c @@ -5,7 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "git2_util.h" #if !defined(GIT_THREADS) diff --git a/src/thread.h b/src/util/thread.h similarity index 100% rename from src/thread.h rename to src/util/thread.h diff --git a/src/tsort.c b/src/util/tsort.c similarity index 99% rename from src/tsort.c rename to src/util/tsort.c index 045efad23..2ef03d03a 100644 --- a/src/tsort.c +++ b/src/util/tsort.c @@ -5,7 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "git2_util.h" /** * An array-of-pointers implementation of Python's Timsort diff --git a/src/unix/map.c b/src/util/unix/map.c similarity index 98% rename from src/unix/map.c rename to src/util/unix/map.c index 23fcb786e..933077689 100644 --- a/src/unix/map.c +++ b/src/util/unix/map.c @@ -5,7 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "git2_util.h" #if !defined(GIT_WIN32) && !defined(NO_MMAP) diff --git a/src/unix/posix.h b/src/util/unix/posix.h similarity index 99% rename from src/unix/posix.h rename to src/util/unix/posix.h index 49065e533..778477e8e 100644 --- a/src/unix/posix.h +++ b/src/util/unix/posix.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_unix_posix_h__ #define INCLUDE_unix_posix_h__ -#include "common.h" +#include "git2_util.h" #include #include diff --git a/src/unix/pthread.h b/src/util/unix/pthread.h similarity index 100% rename from src/unix/pthread.h rename to src/util/unix/pthread.h diff --git a/src/unix/realpath.c b/src/util/unix/realpath.c similarity index 96% rename from src/unix/realpath.c rename to src/util/unix/realpath.c index f1ca669f7..9e31a63b9 100644 --- a/src/unix/realpath.c +++ b/src/util/unix/realpath.c @@ -5,7 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "git2_util.h" #ifndef GIT_WIN32 diff --git a/src/utf8.c b/src/util/utf8.c similarity index 99% rename from src/utf8.c rename to src/util/utf8.c index 77065cb71..c566fdf20 100644 --- a/src/utf8.c +++ b/src/util/utf8.c @@ -7,7 +7,7 @@ #include "utf8.h" -#include "common.h" +#include "git2_util.h" /* * git_utf8_iterate is taken from the utf8proc project, diff --git a/src/utf8.h b/src/util/utf8.h similarity index 98% rename from src/utf8.h rename to src/util/utf8.h index dff91b294..753ab07e2 100644 --- a/src/utf8.h +++ b/src/util/utf8.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_utf8_h__ #define INCLUDE_utf8_h__ -#include "common.h" +#include "git2_util.h" /* * Iterate through an UTF-8 string, yielding one codepoint at a time. diff --git a/src/util.c b/src/util/util.c similarity index 99% rename from src/util.c rename to src/util/util.c index e06d4ca09..aee95fddf 100644 --- a/src/util.c +++ b/src/util/util.c @@ -7,7 +7,7 @@ #include "util.h" -#include "common.h" +#include "git2_util.h" #ifdef GIT_WIN32 # include "win32/utf-conv.h" diff --git a/src/util.h b/src/util/util.h similarity index 99% rename from src/util.h rename to src/util/util.h index 141779ade..8d6d1d6b6 100644 --- a/src/util.h +++ b/src/util/util.h @@ -12,7 +12,7 @@ #endif #include "str.h" -#include "common.h" +#include "git2_util.h" #include "strnlen.h" #include "thread.h" diff --git a/src/varint.c b/src/util/varint.c similarity index 100% rename from src/varint.c rename to src/util/varint.c diff --git a/src/varint.h b/src/util/varint.h similarity index 94% rename from src/varint.h rename to src/util/varint.h index 652e22486..79b8f5548 100644 --- a/src/varint.h +++ b/src/util/varint.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_varint_h__ #define INCLUDE_varint_h__ -#include "common.h" +#include "git2_util.h" #include diff --git a/src/vector.c b/src/util/vector.c similarity index 100% rename from src/vector.c rename to src/util/vector.c diff --git a/src/vector.h b/src/util/vector.h similarity index 99% rename from src/vector.h rename to src/util/vector.h index ae3c79a4c..e50cdfefc 100644 --- a/src/vector.h +++ b/src/util/vector.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_vector_h__ #define INCLUDE_vector_h__ -#include "common.h" +#include "git2_util.h" typedef int (*git_vector_cmp)(const void *, const void *); diff --git a/src/wildmatch.c b/src/util/wildmatch.c similarity index 100% rename from src/wildmatch.c rename to src/util/wildmatch.c diff --git a/src/wildmatch.h b/src/util/wildmatch.h similarity index 95% rename from src/wildmatch.h rename to src/util/wildmatch.h index 44bb575a6..f20640500 100644 --- a/src/wildmatch.h +++ b/src/util/wildmatch.h @@ -8,7 +8,7 @@ #ifndef INCLUDE_wildmatch_h__ #define INCLUDE_wildmatch_h__ -#include "common.h" +#include "git2_util.h" #define WM_CASEFOLD 1 #define WM_PATHNAME 2 diff --git a/src/win32/dir.c b/src/util/win32/dir.c similarity index 100% rename from src/win32/dir.c rename to src/util/win32/dir.c diff --git a/src/win32/dir.h b/src/util/win32/dir.h similarity index 97% rename from src/win32/dir.h rename to src/util/win32/dir.h index acd64729e..810111534 100644 --- a/src/win32/dir.h +++ b/src/util/win32/dir.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_win32_dir_h__ #define INCLUDE_win32_dir_h__ -#include "common.h" +#include "git2_util.h" #include "w32_util.h" diff --git a/src/win32/error.c b/src/util/win32/error.c similarity index 100% rename from src/win32/error.c rename to src/util/win32/error.c diff --git a/src/win32/error.h b/src/util/win32/error.h similarity index 93% rename from src/win32/error.h rename to src/util/win32/error.h index 9e81141ce..fd53b7f99 100644 --- a/src/win32/error.h +++ b/src/util/win32/error.h @@ -8,7 +8,7 @@ #ifndef INCLUDE_win32_error_h__ #define INCLUDE_win32_error_h__ -#include "common.h" +#include "git2_util.h" extern char *git_win32_get_error_message(DWORD error_code); diff --git a/src/win32/findfile.c b/src/util/win32/findfile.c similarity index 100% rename from src/win32/findfile.c rename to src/util/win32/findfile.c diff --git a/src/win32/findfile.h b/src/util/win32/findfile.h similarity index 96% rename from src/win32/findfile.h rename to src/util/win32/findfile.h index 61fb7dbad..7b191d1fe 100644 --- a/src/win32/findfile.h +++ b/src/util/win32/findfile.h @@ -8,7 +8,7 @@ #ifndef INCLUDE_win32_findfile_h__ #define INCLUDE_win32_findfile_h__ -#include "common.h" +#include "git2_util.h" /** Sets the mock registry root for Git for Windows for testing. */ extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir); diff --git a/src/win32/map.c b/src/util/win32/map.c similarity index 99% rename from src/win32/map.c rename to src/util/win32/map.c index 2aabc9b15..52e1363ea 100644 --- a/src/win32/map.c +++ b/src/util/win32/map.c @@ -5,7 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "git2_util.h" #include "map.h" #include diff --git a/src/win32/mingw-compat.h b/src/util/win32/mingw-compat.h similarity index 100% rename from src/win32/mingw-compat.h rename to src/util/win32/mingw-compat.h diff --git a/src/win32/msvc-compat.h b/src/util/win32/msvc-compat.h similarity index 100% rename from src/win32/msvc-compat.h rename to src/util/win32/msvc-compat.h diff --git a/src/win32/path_w32.c b/src/util/win32/path_w32.c similarity index 100% rename from src/win32/path_w32.c rename to src/util/win32/path_w32.c diff --git a/src/win32/path_w32.h b/src/util/win32/path_w32.h similarity index 99% rename from src/win32/path_w32.h rename to src/util/win32/path_w32.h index 837b11ebd..b241d5c8a 100644 --- a/src/win32/path_w32.h +++ b/src/util/win32/path_w32.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_win32_path_w32_h__ #define INCLUDE_win32_path_w32_h__ -#include "common.h" +#include "git2_util.h" /** * Create a Win32 path (in UCS-2 format) from a UTF-8 string. If the given diff --git a/src/win32/posix.h b/src/util/win32/posix.h similarity index 99% rename from src/win32/posix.h rename to src/util/win32/posix.h index 578347f15..03fa2ac52 100644 --- a/src/win32/posix.h +++ b/src/util/win32/posix.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_win32_posix_h__ #define INCLUDE_win32_posix_h__ -#include "common.h" +#include "git2_util.h" #include "../posix.h" #include "win32-compat.h" #include "path_w32.h" diff --git a/src/win32/posix_w32.c b/src/util/win32/posix_w32.c similarity index 99% rename from src/win32/posix_w32.c rename to src/util/win32/posix_w32.c index 5f7cd0c26..5862e5c9a 100644 --- a/src/win32/posix_w32.c +++ b/src/util/win32/posix_w32.c @@ -5,7 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include "common.h" +#include "git2_util.h" #include "../posix.h" #include "../futils.h" diff --git a/tests/precompiled.c b/src/util/win32/precompiled.c similarity index 100% rename from tests/precompiled.c rename to src/util/win32/precompiled.c diff --git a/src/win32/precompiled.h b/src/util/win32/precompiled.h similarity index 93% rename from src/win32/precompiled.h rename to src/util/win32/precompiled.h index 806b1698a..1163c3d63 100644 --- a/src/win32/precompiled.h +++ b/src/util/win32/precompiled.h @@ -1,4 +1,4 @@ -#include "common.h" +#include "git2_util.h" #include #include diff --git a/src/win32/reparse.h b/src/util/win32/reparse.h similarity index 100% rename from src/win32/reparse.h rename to src/util/win32/reparse.h diff --git a/src/win32/thread.c b/src/util/win32/thread.c similarity index 100% rename from src/win32/thread.c rename to src/util/win32/thread.c diff --git a/src/win32/thread.h b/src/util/win32/thread.h similarity index 98% rename from src/win32/thread.h rename to src/util/win32/thread.h index 8305036b4..184762e2a 100644 --- a/src/win32/thread.h +++ b/src/util/win32/thread.h @@ -8,7 +8,7 @@ #ifndef INCLUDE_win32_thread_h__ #define INCLUDE_win32_thread_h__ -#include "common.h" +#include "git2_util.h" #if defined (_MSC_VER) # define GIT_RESTRICT __restrict diff --git a/src/win32/utf-conv.c b/src/util/win32/utf-conv.c similarity index 100% rename from src/win32/utf-conv.c rename to src/util/win32/utf-conv.c diff --git a/src/win32/utf-conv.h b/src/util/win32/utf-conv.h similarity index 98% rename from src/win32/utf-conv.h rename to src/util/win32/utf-conv.h index 6090a4b35..120d647ef 100644 --- a/src/win32/utf-conv.h +++ b/src/util/win32/utf-conv.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_win32_utf_conv_h__ #define INCLUDE_win32_utf_conv_h__ -#include "common.h" +#include "git2_util.h" #include diff --git a/src/win32/version.h b/src/util/win32/version.h similarity index 100% rename from src/win32/version.h rename to src/util/win32/version.h diff --git a/src/win32/w32_buffer.c b/src/util/win32/w32_buffer.c similarity index 100% rename from src/win32/w32_buffer.c rename to src/util/win32/w32_buffer.c diff --git a/src/win32/w32_buffer.h b/src/util/win32/w32_buffer.h similarity index 95% rename from src/win32/w32_buffer.h rename to src/util/win32/w32_buffer.h index 4227296d8..68ea96035 100644 --- a/src/win32/w32_buffer.h +++ b/src/util/win32/w32_buffer.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_win32_w32_buffer_h__ #define INCLUDE_win32_w32_buffer_h__ -#include "common.h" +#include "git2_util.h" #include "str.h" /** diff --git a/src/win32/w32_common.h b/src/util/win32/w32_common.h similarity index 100% rename from src/win32/w32_common.h rename to src/util/win32/w32_common.h diff --git a/src/win32/w32_leakcheck.c b/src/util/win32/w32_leakcheck.c similarity index 100% rename from src/win32/w32_leakcheck.c rename to src/util/win32/w32_leakcheck.c diff --git a/src/win32/w32_leakcheck.h b/src/util/win32/w32_leakcheck.h similarity index 99% rename from src/win32/w32_leakcheck.h rename to src/util/win32/w32_leakcheck.h index cb45e3675..82d863851 100644 --- a/src/win32/w32_leakcheck.h +++ b/src/util/win32/w32_leakcheck.h @@ -8,7 +8,7 @@ #ifndef INCLUDE_win32_leakcheck_h__ #define INCLUDE_win32_leakcheck_h__ -#include "common.h" +#include "git2_util.h" /* Initialize the win32 leak checking system. */ int git_win32_leakcheck_global_init(void); diff --git a/src/win32/w32_util.c b/src/util/win32/w32_util.c similarity index 100% rename from src/win32/w32_util.c rename to src/util/win32/w32_util.c diff --git a/src/win32/w32_util.h b/src/util/win32/w32_util.h similarity index 99% rename from src/win32/w32_util.h rename to src/util/win32/w32_util.h index 1321d30e6..519663720 100644 --- a/src/win32/w32_util.h +++ b/src/util/win32/w32_util.h @@ -8,7 +8,7 @@ #ifndef INCLUDE_win32_w32_util_h__ #define INCLUDE_win32_w32_util_h__ -#include "common.h" +#include "git2_util.h" #include "utf-conv.h" #include "posix.h" diff --git a/src/win32/win32-compat.h b/src/util/win32/win32-compat.h similarity index 100% rename from src/win32/win32-compat.h rename to src/util/win32/win32-compat.h diff --git a/src/zstream.c b/src/util/zstream.c similarity index 100% rename from src/zstream.c rename to src/util/zstream.c diff --git a/src/zstream.h b/src/util/zstream.h similarity index 98% rename from src/zstream.h rename to src/util/zstream.h index 3f8b1c72f..d78b11291 100644 --- a/src/zstream.h +++ b/src/util/zstream.h @@ -7,7 +7,7 @@ #ifndef INCLUDE_zstream_h__ #define INCLUDE_zstream_h__ -#include "common.h" +#include "git2_util.h" #include diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f293c158d..df100e980 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,96 +1,6 @@ -set(Python_ADDITIONAL_VERSIONS 3 2.7) -find_package(PythonInterp) +# The main libgit2 tests tree: this CMakeLists.txt includes the +# subprojects that make up core libgit2 support. -if(NOT PYTHONINTERP_FOUND) - message(FATAL_ERROR "Could not find a python interpreter, which is needed to build the tests. " - "Make sure python is available, or pass -DBUILD_TESTS=OFF to skip building the tests") -ENDIF() - -set(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/resources/") -set(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}") -add_definitions(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\") -add_definitions(-DCLAR_TMPDIR=\"libgit2_tests\") -add_definitions(-DCLAR_WIN32_LONGPATHS) -add_definitions(-D_FILE_OFFSET_BITS=64) - -# Ensure that we do not use deprecated functions internally -add_definitions(-DGIT_DEPRECATE_HARD) - -set(TEST_INCLUDES "${CLAR_PATH}" "${CMAKE_CURRENT_BINARY_DIR}") -file(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/*/*.h) -set(SRC_CLAR "main.c" "clar_libgit2.c" "clar_libgit2_trace.c" "clar_libgit2_timer.c" "clar.c") - -if(MSVC_IDE) - list(APPEND SRC_CLAR "precompiled.c") -endif() - -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/clar.suite ${CMAKE_CURRENT_BINARY_DIR}/clar_suite.h - COMMAND ${PYTHON_EXECUTABLE} generate.py -o "${CMAKE_CURRENT_BINARY_DIR}" -f -xonline -xstress -xperf . - DEPENDS ${SRC_TEST} - WORKING_DIRECTORY ${CLAR_PATH} -) - -set_source_files_properties( - ${CLAR_PATH}/clar.c - PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clar.suite) - -add_executable(libgit2_tests ${SRC_CLAR} ${SRC_TEST} ${LIBGIT2_OBJECTS}) - -set_target_properties(libgit2_tests PROPERTIES C_STANDARD 90) -set_target_properties(libgit2_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) - -target_include_directories(libgit2_tests PRIVATE ${TEST_INCLUDES} ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES}) -target_include_directories(libgit2_tests SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES}) -target_link_libraries(libgit2_tests ${LIBGIT2_SYSTEM_LIBS}) - -ide_split_sources(libgit2_tests) - -# -# Old versions of gcc require us to declare our test functions; don't do -# this on newer compilers to avoid unnecessary recompilation. -# -if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) - target_compile_options(libgit2_tests PRIVATE -include "clar_suite.h") -endif() - -if(MSVC_IDE) - # Precompiled headers - set_target_properties(libgit2_tests PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h") - set_source_files_properties("precompiled.c" COMPILE_FLAGS "/Ycprecompiled.h") -endif() - -function(ADD_CLAR_TEST name) - if(NOT USE_LEAK_CHECKER STREQUAL "OFF") - add_test(${name} "${PROJECT_SOURCE_DIR}/script/${USE_LEAK_CHECKER}.sh" "${PROJECT_BINARY_DIR}/libgit2_tests" ${ARGN}) - else() - add_test(${name} "${PROJECT_BINARY_DIR}/libgit2_tests" ${ARGN}) - endif() -endfunction(ADD_CLAR_TEST) - -add_clar_test(offline -v -xonline) -add_clar_test(invasive -v -score::ftruncate -sfilter::stream::bigfile -sodb::largefiles -siterator::workdir::filesystem_gunk -srepo::init -srepo::init::at_filesystem_root) -add_clar_test(online -v -sonline -xonline::customcert) -add_clar_test(online_customcert -v -sonline::customcert) -add_clar_test(gitdaemon -v -sonline::push) -add_clar_test(ssh -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths -sonline::clone::path_whitespace_ssh) -add_clar_test(proxy -v -sonline::clone::proxy) -add_clar_test(auth_clone -v -sonline::clone::cred) -add_clar_test(auth_clone_and_push -v -sonline::clone::push -sonline::push) - -# -# Header file validation project: ensure that we do not publish any sloppy -# definitions in our headers and that a consumer can include -# even when they have aggressive C90 warnings enabled. -# - -add_executable(headertest headertest.c) -set_target_properties(headertest PROPERTIES C_STANDARD 90) -set_target_properties(headertest PROPERTIES C_EXTENSIONS OFF) -target_include_directories(headertest PRIVATE ${LIBGIT2_INCLUDES}) - -if (MSVC) - target_compile_options(headertest PUBLIC /W4 /WX) -else() - target_compile_options(headertest PUBLIC -Wall -Wextra -pedantic -Werror) -endif() +add_subdirectory(headertest) +add_subdirectory(libgit2) +add_subdirectory(util) diff --git a/tests/README.md b/tests/README.md index 4369a8f33..460e045e3 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,33 +1,58 @@ -Writing Clar tests for libgit2 -============================== +# libgit2 tests -For information on the Clar testing framework and a detailed introduction -please visit: +These are the unit and integration tests for the libgit2 projects. -https://github.com/vmg/clar +* `benchmarks` + These are benchmark tests that excercise the CLI. +* `clar` + This is [clar](https://github.com/clar-test/clar) the common test framework. +* `headertest` + This is a simple project that ensures that our public headers are + compatible with extremely strict compilation options. +* `libgit2` + These tests exercise the core git functionality in libgit2 itself. +* `resources` + These are the resources for the tests, including files and git + repositories. +* `util` + These are tests of the common utility library. +## Writing tests for libgit2 -* Write your modules and tests. Use good, meaningful names. +libgit2 uses the [clar test framework](http://github.com/clar-test/clar), a +C testing framework. -* Make sure you actually build the tests by setting: +The best resources for learning clar are [clar itself](https://github.com/clar-test/clar) +and the existing tests within libgit2. In general: - cmake -DBUILD_TESTS=ON build/ +* If you place a `.c` file into a test directory, it is eligible to contain +test cases. +* The function name for your test is important; test function names begin + with `test_`, followed by the folder path (underscore separated), two + underscores as a delimiter, then the test name. For example, a file + `merge/analysis.c` may contain a test `uptodate`: -* Test: + ``` + void test_merge_analysis__uptodate(void) + { + ... + } + ``` - ./build/libgit2_tests +* You can run an individual test by passing `-s` to the test runner. Tests + are referred to by their function names; for example, the function + `test_merge_analysis__uptodate` is referred to as `merge::analysis::uptodate`. + To run only that function you can use the `-s` option on the test runner: -* Make sure everything is fine. + ``` + libgit2_tests -smerge::analysis::uptodate + ``` -* Send your pull request. That's it. - - -Memory leak checks ------------------- +## Memory leak checking These are automatically run as part of CI, but if you want to check locally: -#### Linux +### Linux Uses [`valgrind`](http://www.valgrind.org/): @@ -38,7 +63,7 @@ $ valgrind --leak-check=full --show-reachable=yes --num-callers=50 --suppression ./libgit2_tests ``` -#### macOS +### macOS Uses [`leaks`](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/FindingLeaks.html), which requires XCode installed: @@ -46,3 +71,13 @@ Uses [`leaks`](https://developer.apple.com/library/archive/documentation/Perform $ MallocStackLogging=1 MallocScribble=1 MallocLogFile=/dev/null CLAR_AT_EXIT="leaks -quiet \$PPID" \ ./libgit2_tests ``` + +### Windows + +Build with the `WIN32_LEAKCHECK` option: + +```console +$ cmake -DBUILD_TESTS=ON -DWIN32_LEAKCHECK=ON .. +$ cmake --build . +$ ./libgit2_tests +``` diff --git a/tests/benchmarks/README.md b/tests/benchmarks/README.md new file mode 100644 index 000000000..f66b27aea --- /dev/null +++ b/tests/benchmarks/README.md @@ -0,0 +1,121 @@ +# libgit2 benchmarks + +This folder contains the individual benchmark tests for libgit2, +meant for understanding the performance characteristics of libgit2, +comparing your development code to the existing libgit2 code, or +comparing libgit2 to the git reference implementation. + +## Running benchmark tests + +Benchmark tests can be run in several different ways: running all +benchmarks, running one (or more) suite of benchmarks, or running a +single individual benchmark. You can target either an individual +version of a CLI, or you can A/B test a baseline CLI against a test +CLI. + +### Specifying the command-line interface to test + +By default, the `git` in your path is benchmarked. Use the +`-c` (or `--cli`) option to specify the command-line interface +to test. + +Example: `libgit2_bench --cli git2_cli` will run the tests against +`git2_cli`. + +### Running tests to compare two different implementations + +You can compare a baseline command-line interface against a test +command-line interface using the `-b (or `--baseline-cli`) option. + +Example: `libgit2_bench --baseline-cli git --cli git2_cli` will +run the tests against both `git` and `git2_cli`. + +### Running individual benchmark tests + +Similar to how a test suite or individual test is specified in +[clar](https://github.com/clar-test/clar), the `-s` (or `--suite`) +option may be used to specify the suite or individual test to run. +Like clar, the suite and test name are separated by `::`, and like +clar, this is a prefix match. + +Examples: +* `libgit2_bench -shash_object` will run the tests in the + `hash_object` suite. +* `libgit2_bench -shash_object::random_1kb` will run the + `hash_object::random_1kb` test. +* `libgit2_bench -shash_object::random` will run all the tests that + begin with `hash_object::random`. + +## Writing benchmark tests + +Benchmark tests are meant to be easy to write. Each individual +benchmark is a shell script that allows it to do set up (eg, creating +or cloning a repository, creating temporary files, etc), then running +benchmarks, then teardown. + +The `benchmark_helpers.sh` script provides many helpful utility +functions to allow for cross-platform benchmarking, as well as a +wrapper for `hyperfine` that is suited to testing libgit2. +Note that the helper script must be included first, at the beginning +of the benchmark test. + +### Benchmark example + +This simplistic example compares the speed of running the `git help` +command in the baseline CLI to the test CLI. + +```bash +#!/bin/bash -e + +# include the benchmark library +. "$(dirname "$0")/benchmark_helpers.sh" + +# run the "help" command; this will benchmark `git2_cli help` +gitbench help +``` + +### Naming + +The filename of the benchmark itself is important. A benchmark's +filename should be the name of the benchmark suite, followed by two +underscores, followed by the name of the benchmark. For example, +`hash-object__random_1kb` is the `random_1kb` test in the `hash-object` +suite. + +### Options + +The `gitbench` function accepts several options. + +* `--sandbox ` + The name of a test resource (in the `tests/resources` directory). + This will be copied as-is to the sandbox location before test + execution. This is copied _before_ the `prepare` script is run. + This option may be specified multiple times. +* `--repository ` + The name of a test resource repository (in the `tests/resources` + directory). This repository will be copied into a sandbox location + before test execution, and your test will run in this directory. + This is copied _before_ the `prepare` script is run. +* `--prepare