Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
664dcfaa89 |
33 changed files with 950 additions and 7082 deletions
|
@ -1,251 +0,0 @@
|
||||||
name: Build for Releases
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- '*'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ensure-release-exists:
|
|
||||||
runs-on: any_archLinux
|
|
||||||
steps:
|
|
||||||
- name: Check release and create if it doesn't exist
|
|
||||||
run: |
|
|
||||||
THE_ACTION_WORKING_DIRECTORY="$(pwd)"
|
|
||||||
curl -X GET "https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/tags/$GITHUB_REF_NAME" \
|
|
||||||
-H "Authorization: token ${GITHUB_TOKEN}" \
|
|
||||||
-H 'accept: application/json' -o "${THE_ACTION_WORKING_DIRECTORY}/release_${GITHUB_REF_NAME}_check.json" \
|
|
||||||
-w '%{http_code}' 2>/dev/null > release_${GITHUB_REF_NAME}_check_code
|
|
||||||
if [[ "404" == "$(cat release_${GITHUB_REF_NAME}_check_code)" ]]; then
|
|
||||||
curl --fail-with-body -X POST \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases" \
|
|
||||||
-H 'accept: application/json' \
|
|
||||||
-H "Authorization: token ${GITHUB_TOKEN}" \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d "{
|
|
||||||
\"body\": \" SHA256SUMS\",
|
|
||||||
\"name\": \"SimpleArchiver version ${GITHUB_REF_NAME}\",
|
|
||||||
\"tag_name\": \"${GITHUB_REF_NAME}\" }" 2>/dev/null > "${THE_ACTION_WORKING_DIRECTORY}/release_${GITHUB_REF_NAME}_create.json"
|
|
||||||
fi
|
|
||||||
|
|
||||||
push-build-x86_64:
|
|
||||||
needs: ensure-release-exists
|
|
||||||
concurrency:
|
|
||||||
group: push-build-group
|
|
||||||
runs-on: x86_64_archLinux
|
|
||||||
steps:
|
|
||||||
- name: Check release assets and build for x86_64
|
|
||||||
run: |
|
|
||||||
THE_ACTION_WORKING_DIRECTORY="$(pwd)"
|
|
||||||
BUILD_ASSET_NAME="simplearchiver_x86_64_${GITHUB_REF_NAME}.zst"
|
|
||||||
curl -X GET \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/tags/${GITHUB_REF_NAME}" \
|
|
||||||
-H 'accept: application/json' -o "${THE_ACTION_WORKING_DIRECTORY}/release_info.json" 2>/dev/null
|
|
||||||
BUILD_ASSET_EXISTS=0
|
|
||||||
for asset in $(jq '.assets.[].name' < "${THE_ACTION_WORKING_DIRECTORY}/release_info.json" | tr -d '"'); do
|
|
||||||
if [[ "$asset" == "$BUILD_ASSET_NAME" ]]; then
|
|
||||||
BUILD_ASSET_EXISTS=1
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if ! (( BUILD_ASSET_EXISTS )); then
|
|
||||||
if ! [[ -d "SimpleArchiver_clone" ]]; then
|
|
||||||
git clone --depth=1 --no-single-branch https://git.seodisparate.com/stephenseo/SimpleArchiver.git SimpleArchiver_clone
|
|
||||||
fi
|
|
||||||
pushd SimpleArchiver_clone >&/dev/null
|
|
||||||
git clean -xfd && git restore . && git checkout "${GITHUB_REF_NAME}"
|
|
||||||
cmake -S . -B buildRelease -DCMAKE_BUILD_TYPE=Release
|
|
||||||
make -C buildRelease simplearchiver
|
|
||||||
strip --strip-unneeded buildRelease/simplearchiver
|
|
||||||
zstd --ultra -20 buildRelease/simplearchiver -o "${THE_ACTION_WORKING_DIRECTORY}/${BUILD_ASSET_NAME}"
|
|
||||||
curl --fail-with-body -X GET \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/$(jq .id < "${THE_ACTION_WORKING_DIRECTORY}/release_info.json")" \
|
|
||||||
-H 'accept: application/json' -o "${THE_ACTION_WORKING_DIRECTORY}/current_release_info.json" 2>/dev/null
|
|
||||||
curl --fail-with-body -X PATCH \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/$(jq .id < "${THE_ACTION_WORKING_DIRECTORY}/release_info.json")" \
|
|
||||||
-H 'accept: application/json' \
|
|
||||||
-H "Authorization: token ${GITHUB_TOKEN}" \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d "{
|
|
||||||
\"body\": \"$(jq .body < "${THE_ACTION_WORKING_DIRECTORY}/current_release_info.json" | sed -e 's/^"//' -e 's/"$//')
|
|
||||||
$(find "${THE_ACTION_WORKING_DIRECTORY}" -maxdepth 1 -name "*${BUILD_ASSET_NAME}" -execdir sha256sum '{}' ';' | sed -e 's|\./simple|simple|')\"
|
|
||||||
}" >&/dev/null
|
|
||||||
curl --fail-with-body -X POST \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/$(jq .id < "${THE_ACTION_WORKING_DIRECTORY}/release_info.json")/assets" \
|
|
||||||
-H 'accept: application/json' \
|
|
||||||
-H "Authorization: token ${GITHUB_TOKEN}" \
|
|
||||||
-H 'Content-Type: multipart/form-data' \
|
|
||||||
-F "attachment=@${THE_ACTION_WORKING_DIRECTORY}/${BUILD_ASSET_NAME};type=application/zstd" > "${THE_ACTION_WORKING_DIRECTORY}/attach_${GITHUB_REF_NAME}.json" 2>/dev/null
|
|
||||||
popd >&/dev/null
|
|
||||||
fi
|
|
||||||
|
|
||||||
push-build-aarch64:
|
|
||||||
if: ${{ always() }}
|
|
||||||
needs: push-build-x86_64
|
|
||||||
concurrency:
|
|
||||||
group: push-build-group
|
|
||||||
runs-on: aarch64_archLinux
|
|
||||||
steps:
|
|
||||||
- name: Check release assets and build for aarch64
|
|
||||||
run: |
|
|
||||||
THE_ACTION_WORKING_DIRECTORY="$(pwd)"
|
|
||||||
BUILD_ASSET_NAME="simplearchiver_aarch64_${GITHUB_REF_NAME}.zst"
|
|
||||||
curl -X GET \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/tags/${GITHUB_REF_NAME}" \
|
|
||||||
-H 'accept: application/json' -o "${THE_ACTION_WORKING_DIRECTORY}/release_info.json" 2>/dev/null
|
|
||||||
BUILD_ASSET_EXISTS=0
|
|
||||||
for asset in $(jq '.assets.[].name' < "${THE_ACTION_WORKING_DIRECTORY}/release_info.json" | tr -d '"'); do
|
|
||||||
if [[ "$asset" == "$BUILD_ASSET_NAME" ]]; then
|
|
||||||
BUILD_ASSET_EXISTS=1
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if ! (( BUILD_ASSET_EXISTS )); then
|
|
||||||
if ! [[ -d "SimpleArchiver_clone" ]]; then
|
|
||||||
git clone --depth=1 --no-single-branch https://git.seodisparate.com/stephenseo/SimpleArchiver.git SimpleArchiver_clone
|
|
||||||
fi
|
|
||||||
pushd SimpleArchiver_clone >&/dev/null
|
|
||||||
git clean -xfd && git restore . && git checkout "${GITHUB_REF_NAME}"
|
|
||||||
cmake -S . -B buildRelease -DCMAKE_BUILD_TYPE=Release
|
|
||||||
make -C buildRelease simplearchiver
|
|
||||||
strip --strip-unneeded buildRelease/simplearchiver
|
|
||||||
zstd --ultra -20 buildRelease/simplearchiver -o "${THE_ACTION_WORKING_DIRECTORY}/${BUILD_ASSET_NAME}"
|
|
||||||
curl --fail-with-body -X GET \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/$(jq .id < "${THE_ACTION_WORKING_DIRECTORY}/release_info.json")" \
|
|
||||||
-H 'accept: application/json' -o "${THE_ACTION_WORKING_DIRECTORY}/current_release_info.json" 2>/dev/null
|
|
||||||
curl --fail-with-body -X PATCH \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/$(jq .id < "${THE_ACTION_WORKING_DIRECTORY}/release_info.json")" \
|
|
||||||
-H 'accept: application/json' \
|
|
||||||
-H "Authorization: token ${GITHUB_TOKEN}" \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d "{
|
|
||||||
\"body\": \"$(jq .body < "${THE_ACTION_WORKING_DIRECTORY}/current_release_info.json" | sed -e 's/^"//' -e 's/"$//')
|
|
||||||
$(find "${THE_ACTION_WORKING_DIRECTORY}" -maxdepth 1 -name "*${BUILD_ASSET_NAME}" -execdir sha256sum '{}' ';' | sed -e 's|\./simple|simple|')\"
|
|
||||||
}" >&/dev/null
|
|
||||||
curl --fail-with-body -X POST \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/$(jq .id < "${THE_ACTION_WORKING_DIRECTORY}/release_info.json")/assets" \
|
|
||||||
-H 'accept: application/json' \
|
|
||||||
-H "Authorization: token ${GITHUB_TOKEN}" \
|
|
||||||
-H 'Content-Type: multipart/form-data' \
|
|
||||||
-F "attachment=@${THE_ACTION_WORKING_DIRECTORY}/${BUILD_ASSET_NAME};type=application/zstd" > "${THE_ACTION_WORKING_DIRECTORY}/attach_${GITHUB_REF_NAME}.json" 2>/dev/null
|
|
||||||
popd >&/dev/null
|
|
||||||
fi
|
|
||||||
push-build-x86_64_debian:
|
|
||||||
if: ${{ always() }}
|
|
||||||
needs: push-build-aarch64
|
|
||||||
concurrency:
|
|
||||||
group: push-build-group
|
|
||||||
runs-on: docker_debian_bookworm
|
|
||||||
env:
|
|
||||||
BUILD_ASSET_NAME: "simplearchiver_x86_64_debian_${{ github.ref_name }}.zst"
|
|
||||||
steps:
|
|
||||||
- name: Update and upgrade with apt
|
|
||||||
run: apt-get --yes update && apt-get --yes upgrade
|
|
||||||
- name: Get necessary packages
|
|
||||||
run: apt-get --yes install gcc g++ jq curl cmake make zstd sed git
|
|
||||||
- name: Get release info
|
|
||||||
run: |
|
|
||||||
curl -X GET \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/tags/${GITHUB_REF_NAME}" \
|
|
||||||
-H 'accept: application/json' -o release_info.json
|
|
||||||
- name: Check if asset exists
|
|
||||||
run: |
|
|
||||||
for asset in $(jq '.assets[].name' < release_info.json | tr -d '"'); do
|
|
||||||
if [[ "$asset" == "$BUILD_ASSET_NAME" ]]; then
|
|
||||||
touch asset_exists
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
- name: Build and publish if asset does not exist
|
|
||||||
run: |
|
|
||||||
if ! [[ -e ./asset_exists ]]; then
|
|
||||||
git clone --depth=1 --no-single-branch https://git.seodisparate.com/stephenseo/SimpleArchiver.git SimpleArchiver_clone
|
|
||||||
pushd SimpleArchiver_clone >&/dev/null
|
|
||||||
|
|
||||||
git checkout "${GITHUB_REF_NAME}"
|
|
||||||
|
|
||||||
# Patch CMakeLists.txt as some flags aren't yet supported by Debian's GCC.
|
|
||||||
sed -i -e 's/-fstrict-flex-arrays=3//g' CMakeLists.txt
|
|
||||||
|
|
||||||
cmake -S . -B buildRelease -DCMAKE_BUILD_TYPE=Release
|
|
||||||
make -C buildRelease simplearchiver
|
|
||||||
|
|
||||||
popd >&/dev/null
|
|
||||||
zstd --ultra -20 SimpleArchiver_clone/buildRelease/simplearchiver -o "${BUILD_ASSET_NAME}"
|
|
||||||
|
|
||||||
curl --fail-with-body -X PATCH \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/$(jq .id < release_info.json)" \
|
|
||||||
-H 'accept: application/json' \
|
|
||||||
-H "Authorization: token ${GITHUB_TOKEN}" \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d "{
|
|
||||||
\"body\": \"$(jq .body < release_info.json | sed -e 's/^"//' -e 's/"$//')
|
|
||||||
$(sha256sum "${BUILD_ASSET_NAME}")\"
|
|
||||||
}" >&/dev/null
|
|
||||||
|
|
||||||
curl --fail-with-body -X POST \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/$(jq .id < release_info.json)/assets" \
|
|
||||||
-H 'accept: application/json' \
|
|
||||||
-H "Authorization: token ${GITHUB_TOKEN}" \
|
|
||||||
-H 'Content-Type: multipart/form-data' \
|
|
||||||
-F "attachment=@${BUILD_ASSET_NAME};type=application/zstd" > attach.json 2>/dev/null
|
|
||||||
fi
|
|
||||||
push-build-aarch64_debian:
|
|
||||||
if: ${{ always() }}
|
|
||||||
needs: push-build-x86_64_debian
|
|
||||||
concurrency:
|
|
||||||
group: push-build-group
|
|
||||||
runs-on: aarch64_docker_debian_bookworm
|
|
||||||
env:
|
|
||||||
BUILD_ASSET_NAME: "simplearchiver_aarch64_debian_${{ github.ref_name }}.zst"
|
|
||||||
steps:
|
|
||||||
- name: Update and upgrade with apt
|
|
||||||
run: apt-get --yes update && apt-get --yes upgrade
|
|
||||||
- name: Get necessary packages
|
|
||||||
run: apt-get --yes install gcc g++ jq curl cmake make zstd sed git
|
|
||||||
- name: Get release info
|
|
||||||
run: |
|
|
||||||
curl -X GET \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/tags/${GITHUB_REF_NAME}" \
|
|
||||||
-H 'accept: application/json' -o release_info.json
|
|
||||||
- name: Check if asset exists
|
|
||||||
run: |
|
|
||||||
for asset in $(jq '.assets[].name' < release_info.json | tr -d '"'); do
|
|
||||||
if [[ "$asset" == "$BUILD_ASSET_NAME" ]]; then
|
|
||||||
touch asset_exists
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
- name: Build and publish if asset does not exist
|
|
||||||
run: |
|
|
||||||
if ! [[ -e ./asset_exists ]]; then
|
|
||||||
git clone --depth=1 --no-single-branch https://git.seodisparate.com/stephenseo/SimpleArchiver.git SimpleArchiver_clone
|
|
||||||
pushd SimpleArchiver_clone >&/dev/null
|
|
||||||
|
|
||||||
git checkout "${GITHUB_REF_NAME}"
|
|
||||||
|
|
||||||
# Patch CMakeLists.txt as some flags aren't yet supported by Debian's GCC.
|
|
||||||
sed -i -e 's/-fstrict-flex-arrays=3//g' CMakeLists.txt
|
|
||||||
|
|
||||||
cmake -S . -B buildRelease -DCMAKE_BUILD_TYPE=Release
|
|
||||||
make -C buildRelease simplearchiver
|
|
||||||
|
|
||||||
popd >&/dev/null
|
|
||||||
zstd --ultra -20 SimpleArchiver_clone/buildRelease/simplearchiver -o "${BUILD_ASSET_NAME}"
|
|
||||||
|
|
||||||
curl --fail-with-body -X PATCH \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/$(jq .id < release_info.json)" \
|
|
||||||
-H 'accept: application/json' \
|
|
||||||
-H "Authorization: token ${GITHUB_TOKEN}" \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d "{
|
|
||||||
\"body\": \"$(jq .body < release_info.json | sed -e 's/^"//' -e 's/"$//')
|
|
||||||
$(sha256sum "${BUILD_ASSET_NAME}")\"
|
|
||||||
}" >&/dev/null
|
|
||||||
|
|
||||||
curl --fail-with-body -X POST \
|
|
||||||
"https://git.seodisparate.com/api/v1/repos/stephenseo/SimpleArchiver/releases/$(jq .id < release_info.json)/assets" \
|
|
||||||
-H 'accept: application/json' \
|
|
||||||
-H "Authorization: token ${GITHUB_TOKEN}" \
|
|
||||||
-H 'Content-Type: multipart/form-data' \
|
|
||||||
-F "attachment=@${BUILD_ASSET_NAME};type=application/zstd" > attach.json 2>/dev/null
|
|
||||||
fi
|
|
|
@ -1,13 +1,12 @@
|
||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.7)
|
||||||
project(SimpleArchiver C)
|
project(SimpleArchiver C)
|
||||||
|
|
||||||
set(SimpleArchiver_VERSION 1.7)
|
set(SimpleArchiver_VERSION 1.0)
|
||||||
|
|
||||||
set(SimpleArchiver_SOURCES
|
set(SimpleArchiver_SOURCES
|
||||||
src/main.c
|
src/main.c
|
||||||
src/parser.c
|
src/parser.c
|
||||||
src/helpers.c
|
src/helpers.c
|
||||||
src/archiver.c
|
|
||||||
src/data_structures/linked_list.c
|
src/data_structures/linked_list.c
|
||||||
src/data_structures/hash_map.c
|
src/data_structures/hash_map.c
|
||||||
src/data_structures/priority_heap.c
|
src/data_structures/priority_heap.c
|
||||||
|
@ -28,49 +27,6 @@ endif()
|
||||||
|
|
||||||
add_executable(simplearchiver ${SimpleArchiver_SOURCES})
|
add_executable(simplearchiver ${SimpleArchiver_SOURCES})
|
||||||
|
|
||||||
target_compile_options(simplearchiver PUBLIC
|
|
||||||
-Wall -Wformat -Wformat=2 -Wconversion -Wimplicit-fallthrough
|
|
||||||
-Werror=format-security
|
|
||||||
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3
|
|
||||||
-D_GLIBCXX_ASSERTIONS
|
|
||||||
-fstrict-flex-arrays=3
|
|
||||||
-fstack-clash-protection -fstack-protector-strong
|
|
||||||
-Wl,-z,nodlopen -Wl,-z,noexecstack
|
|
||||||
-Wl,-z,relro -Wl,-z,now
|
|
||||||
-Wl,--as-needed -Wl,--no-copy-dt-needed-entries
|
|
||||||
-fPIE -pie
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_options(simplearchiver PUBLIC
|
|
||||||
-Wall -Wformat -Wformat=2 -Wconversion -Wimplicit-fallthrough
|
|
||||||
-Werror=format-security
|
|
||||||
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3
|
|
||||||
-D_GLIBCXX_ASSERTIONS
|
|
||||||
-fstrict-flex-arrays=3
|
|
||||||
-fstack-clash-protection -fstack-protector-strong
|
|
||||||
-Wl,-z,nodlopen -Wl,-z,noexecstack
|
|
||||||
-Wl,-z,relro -Wl,-z,now
|
|
||||||
-Wl,--as-needed -Wl,--no-copy-dt-needed-entries
|
|
||||||
-fPIE -pie
|
|
||||||
)
|
|
||||||
|
|
||||||
# Inhibit format-string-related warning in src/archiver.c .
|
|
||||||
set_source_files_properties(src/archiver.c
|
|
||||||
PROPERTIES
|
|
||||||
COMPILE_FLAGS -Wno-format-nonliteral
|
|
||||||
)
|
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
|
||||||
target_compile_options(simplearchiver PUBLIC
|
|
||||||
-fno-delete-null-pointer-checks -fno-strict-overflow
|
|
||||||
-fno-strict-aliasing -ftrivial-auto-var-init=zero
|
|
||||||
)
|
|
||||||
target_link_options(simplearchiver PUBLIC
|
|
||||||
-fno-delete-null-pointer-checks -fno-strict-overflow
|
|
||||||
-fno-strict-aliasing -ftrivial-auto-var-init=zero
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable(test_datastructures
|
add_executable(test_datastructures
|
||||||
src/data_structures/test.c
|
src/data_structures/test.c
|
||||||
src/data_structures/linked_list.c
|
src/data_structures/linked_list.c
|
||||||
|
@ -83,9 +39,7 @@ add_executable(test_simplearchiver
|
||||||
src/test.c
|
src/test.c
|
||||||
src/parser.c
|
src/parser.c
|
||||||
src/helpers.c
|
src/helpers.c
|
||||||
src/archiver.c
|
|
||||||
src/algorithms/linear_congruential_gen.c
|
src/algorithms/linear_congruential_gen.c
|
||||||
src/data_structures/linked_list.c
|
src/data_structures/linked_list.c
|
||||||
src/data_structures/hash_map.c
|
src/data_structures/hash_map.c
|
||||||
src/data_structures/priority_heap.c
|
|
||||||
)
|
)
|
||||||
|
|
74
Changelog.md
74
Changelog.md
|
@ -1,74 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
## Upcoming Changes
|
|
||||||
|
|
||||||
## Version 1.7
|
|
||||||
|
|
||||||
Refactor the internal hash-map data structure.
|
|
||||||
|
|
||||||
Minor update to CMakeLists.txt.
|
|
||||||
|
|
||||||
## Version 1.6
|
|
||||||
|
|
||||||
Enforce "safe-links" on extraction by ensuring every extracted symlink actually
|
|
||||||
points to a file in the archive. Additionally any extracted symlinks that don't
|
|
||||||
point to a valid destination is removed. This "enforce safe-links on extract"
|
|
||||||
can be disabled with the "--no-safe-links" option.
|
|
||||||
|
|
||||||
Add "--preserve-symlinks" option that will verbatim store the symlinks' target.
|
|
||||||
Not recommended if symlinks are pointing to absolute paths, which will be
|
|
||||||
clobbered on extraction to a different directory unless if "--no-safe-links" is
|
|
||||||
specified on extraction.
|
|
||||||
|
|
||||||
## Version 1.5
|
|
||||||
|
|
||||||
Previous file-format-v1 implementation of "safe links" still created a symlink
|
|
||||||
if a relative or absolute link existed in the file. This version fixes this, and
|
|
||||||
prevents invalid symlinks from being created. (This check is only done if the
|
|
||||||
bit-flag is set in the file as mentioned in the file-format spec for v1 files.)
|
|
||||||
|
|
||||||
## Version 1.4
|
|
||||||
|
|
||||||
Do "safe links" behavior by default: symlinks pointing to outside of archived
|
|
||||||
files (or invalid symlinks) should not be included in the archive, unless if the
|
|
||||||
option "--no-safe-links" is specified. This is supported in both v0 and v1 file
|
|
||||||
formats.
|
|
||||||
|
|
||||||
## Version 1.3
|
|
||||||
|
|
||||||
Prevent `simplearchiver` from busy-waiting during non-blocking IO by sleeping
|
|
||||||
in "EWOULDBLOCK" conditions. This results in less consumed cpu time by the
|
|
||||||
process, especially during compression.
|
|
||||||
|
|
||||||
## Version 1.2
|
|
||||||
|
|
||||||
Proper handling of Ctrl+C (SIGINT). This prevents temporary files from
|
|
||||||
persisting by doing a proper cleanup before stopping the program.
|
|
||||||
|
|
||||||
## Version 1.1
|
|
||||||
|
|
||||||
More robust handling of de/compression process (handling SIGPIPE).
|
|
||||||
|
|
||||||
By default files are now pre-sorted by size before placed into chunks.
|
|
||||||
Add option to NOT pre-sort files by size.
|
|
||||||
|
|
||||||
## Version 1.0
|
|
||||||
|
|
||||||
First release.
|
|
||||||
|
|
||||||
Features:
|
|
||||||
|
|
||||||
- Can specify any command as de/compressor when archiving.
|
|
||||||
- The commands must accept file data in stdin and output processed data to
|
|
||||||
stdout.
|
|
||||||
- Can specify any command as decompressor when extracting to override the
|
|
||||||
simple-archive's stored decompressor.
|
|
||||||
- Archives/compresses into chunks to reduce overhead by compressing per chunk
|
|
||||||
instead of per file.
|
|
||||||
- Chunk size can be tweaked by a parameter setting.
|
|
||||||
- Can archive without de/compressor to be compressed separately.
|
|
||||||
- Supports pre-version-1 simple archiver file format (version 0).
|
|
||||||
- Archives regular files and symlinks.
|
|
||||||
- Keeps track of files and symlink permissions.
|
|
||||||
- Keeps track of file UID and GID (only set if extracting as root).
|
|
||||||
- Can be set to ignore absolute paths for symlinks by parameter setting.
|
|
210
LICENSE
210
LICENSE
|
@ -1,15 +1,201 @@
|
||||||
ISC License
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
Copyright (c) 2024 Stephen Seo
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
1. Definitions.
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
the copyright owner that is granting the License.
|
||||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
PERFORMANCE OF THIS SOFTWARE.
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
44
README.md
44
README.md
|
@ -1,8 +1,6 @@
|
||||||
# Simple Archiver
|
# Simple Archiver
|
||||||
|
|
||||||
This program ~~is not yet~~ ~~almost~~ basically finished! ~~Basic~~ Necessary
|
This program is not yet finished! You can track progress
|
||||||
functionality is implemented and only ~~some advanced features are missing~~
|
|
||||||
some extra features are not yet implemented. You can track progress
|
|
||||||
[here](https://git.seodisparate.com/stephenseo/SimpleArchiver/projects/3).
|
[here](https://git.seodisparate.com/stephenseo/SimpleArchiver/projects/3).
|
||||||
|
|
||||||
This program exists because I could not get `tar` or `ar` to compile with
|
This program exists because I could not get `tar` or `ar` to compile with
|
||||||
|
@ -11,44 +9,6 @@ archiver will be written with support for Cosmopolitan in mind. This means
|
||||||
sticking to the C programming language and possibly using Cosmopolitan-specfic
|
sticking to the C programming language and possibly using Cosmopolitan-specfic
|
||||||
API calls.
|
API calls.
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Usage flags:
|
|
||||||
-c : create archive file
|
|
||||||
-x : extract archive file
|
|
||||||
-t : examine archive file
|
|
||||||
-f <filename> : filename to work on
|
|
||||||
Use "-f -" to work on stdout when creating archive or stdin when reading archive
|
|
||||||
NOTICE: "-f" is not affected by "-C"!
|
|
||||||
-C <dir> : Change current working directory before archiving/extracting
|
|
||||||
--compressor <full_compress_cmd> : requires --decompressor and cmd must use stdin/stdout
|
|
||||||
--decompressor <full_decompress_cmd> : requires --compressor and cmd must use stdin/stdout
|
|
||||||
Specifying "--decompressor" when extracting overrides archive file's stored decompressor cmd
|
|
||||||
--overwrite-create : allows overwriting an archive file
|
|
||||||
--overwrite-extract : allows overwriting when extracting
|
|
||||||
--no-abs-symlink : do not store absolute paths for symlinks
|
|
||||||
--preserve-symlinks : preserve the symlink's path on archive creation instead of deriving abs/relative paths, ignores "--no-abs-symlink" (It is not recommended to use this option, as absolute-path-symlinks may be clobbered on extraction)
|
|
||||||
--no-safe-links : keep symlinks that link to outside archive contents
|
|
||||||
--temp-files-dir <dir> : where to store temporary files created when compressing (defaults to current working directory)
|
|
||||||
--write-version <version> : Force write version file format (default 1)
|
|
||||||
--chunk-min-size <bytes> : v1 file format minimum chunk size (default 4194304 or 4MiB)
|
|
||||||
--no-pre-sort-files : do NOT pre-sort files by size (by default enabled so that the first file is the largest)
|
|
||||||
-- : specifies remaining arguments are files to archive/extract
|
|
||||||
If creating archive file, remaining args specify files to archive.
|
|
||||||
If extracting archive file, remaining args specify files to extract.
|
|
||||||
|
|
||||||
Note that `--compressor` and `--decompressor` cmds must accept data from stdin
|
|
||||||
and return processed data to stdout.
|
|
||||||
|
|
||||||
## Using the Cosmopolitan-Compiled Version
|
|
||||||
|
|
||||||
Note that on Linux, the `actually_portable_simplearchiver` binaries may attempt
|
|
||||||
to open via Wine (if Wine is installed). [A workaround is mentioned here.](https://github.com/jart/cosmopolitan/blob/master/README.md#linux)
|
|
||||||
|
|
||||||
## Changes
|
|
||||||
|
|
||||||
See the [Changelog](https://git.seodisparate.com/stephenseo/SimpleArchiver/src/branch/main/Changelog.md).
|
|
||||||
|
|
||||||
## LICENSE Information
|
## LICENSE Information
|
||||||
|
|
||||||
Uses the [ISC License](https://choosealicense.com/licenses/isc/).
|
Uses the [Apache License 2.0](https://choosealicense.com/licenses/apache-2.0).
|
||||||
|
|
|
@ -7,7 +7,6 @@ SOURCES = \
|
||||||
../src/main.c \
|
../src/main.c \
|
||||||
../src/parser.c \
|
../src/parser.c \
|
||||||
../src/helpers.c \
|
../src/helpers.c \
|
||||||
../src/archiver.c \
|
|
||||||
../src/algorithms/linear_congruential_gen.c \
|
../src/algorithms/linear_congruential_gen.c \
|
||||||
../src/data_structures/linked_list.c \
|
../src/data_structures/linked_list.c \
|
||||||
../src/data_structures/hash_map.c \
|
../src/data_structures/hash_map.c \
|
||||||
|
@ -17,7 +16,6 @@ HEADERS = \
|
||||||
../src/parser.h \
|
../src/parser.h \
|
||||||
../src/parser_internal.h \
|
../src/parser_internal.h \
|
||||||
../src/helpers.h \
|
../src/helpers.h \
|
||||||
../src/archiver.h \
|
|
||||||
../src/algorithms/linear_congruential_gen.h \
|
../src/algorithms/linear_congruential_gen.h \
|
||||||
../src/data_structures/linked_list.h \
|
../src/data_structures/linked_list.h \
|
||||||
../src/data_structures/hash_map.h \
|
../src/data_structures/hash_map.h \
|
||||||
|
|
139
file_format.md
139
file_format.md
|
@ -1,7 +1,5 @@
|
||||||
# File Format
|
# File Format
|
||||||
|
|
||||||
Note that any unused bytes/bits should be zeroed-out before being written.
|
|
||||||
|
|
||||||
## Format Version 0
|
## Format Version 0
|
||||||
|
|
||||||
File extension is "*.simplearchive"
|
File extension is "*.simplearchive"
|
||||||
|
@ -45,23 +43,8 @@ Following the file-count bytes, the following bytes are added for each file:
|
||||||
3. 4 bytes bit-flags
|
3. 4 bytes bit-flags
|
||||||
1. The first byte
|
1. The first byte
|
||||||
1. The first bit is set if the file is a symbolic link.
|
1. The first bit is set if the file is a symbolic link.
|
||||||
2. The second bit is "user read permission".
|
|
||||||
3. The third bit is "user write permission".
|
|
||||||
4. The fourth bit is "user execute permission".
|
|
||||||
5. The fifth bit is "group read permission".
|
|
||||||
6. The sixth bit is "group write permission".
|
|
||||||
7. The seventh bit is "group execute permission".
|
|
||||||
8. The eighth bit is "other read permission".
|
|
||||||
2. The second byte.
|
2. The second byte.
|
||||||
1. The first bit is "other write permission".
|
1. Currently unused.
|
||||||
2. The second bit is "other execute permission".
|
|
||||||
3. The third bit is UNSET if relative links are preferred, and is SET
|
|
||||||
if absolute links are preferred.
|
|
||||||
4. The fourth bit is set if this file/symlink-entry is invalid and must
|
|
||||||
be skipped. Ignore following bytes after these 4 bytes bit-flags in
|
|
||||||
this specification and skip to the next entry; if marked invalid,
|
|
||||||
the following specification bytes for this file/symlink entry must
|
|
||||||
not exist.
|
|
||||||
3. The third byte.
|
3. The third byte.
|
||||||
1. Currently unused.
|
1. Currently unused.
|
||||||
4. The fourth byte.
|
4. The fourth byte.
|
||||||
|
@ -78,123 +61,5 @@ Following the file-count bytes, the following bytes are added for each file:
|
||||||
Is a NULL-terminated string. If the previous "size" value is 0, then
|
Is a NULL-terminated string. If the previous "size" value is 0, then
|
||||||
this entry does not exist and should be skipped.
|
this entry does not exist and should be skipped.
|
||||||
5. If this file is NOT a symbolic link:
|
5. If this file is NOT a symbolic link:
|
||||||
1. 8 bytes 64-bit unsigned integer "size of filename in this archive file"
|
1. 8 bytes 64-bit unsigned integer "size of filename in this archive file".
|
||||||
in big-endian.
|
|
||||||
2. X bytes file data (length defined by previous value).
|
2. X bytes file data (length defined by previous value).
|
||||||
|
|
||||||
## Format Version 1
|
|
||||||
|
|
||||||
File extension is "*.simplearchive" but this isn't really checked.
|
|
||||||
|
|
||||||
First 18 bytes of file will be (in ascii):
|
|
||||||
|
|
||||||
SIMPLE_ARCHIVE_VER
|
|
||||||
|
|
||||||
Next 2 bytes is a 16-bit unsigned integer "version" in big-endian. It will be:
|
|
||||||
|
|
||||||
0x00 0x01
|
|
||||||
|
|
||||||
Next 4 bytes are bit-flags.
|
|
||||||
|
|
||||||
1. The first byte
|
|
||||||
1. The first bit is set if de/compressor is set for this archive.
|
|
||||||
|
|
||||||
The remaining unused flags in the previous bit-flags bytes are reserved for
|
|
||||||
future revisions and are currently ignored.
|
|
||||||
|
|
||||||
If the previous "de/compressor is set" flag is enabled, then the next section is
|
|
||||||
added:
|
|
||||||
|
|
||||||
1. 2 bytes is 16-bit unsigned integer "compressor cmd+args" in big-endian. This
|
|
||||||
does not include the NULL at the end of the string.
|
|
||||||
2. X bytes of "compressor cmd+args" (length defined by previous value). Is a
|
|
||||||
NULL-terminated string.
|
|
||||||
3. 2 bytes is 16-bit unsigned integer "decompressor cmd+args" in big-endian.
|
|
||||||
This does not include the NULL at the end of the string.
|
|
||||||
4. X bytes of "decompressor cmd+args" (length defined by previous value). Is a
|
|
||||||
NULL-terminated string.
|
|
||||||
|
|
||||||
The next 4 bytes is a 32-bit unsigned integer "link count" in big-endian which
|
|
||||||
will indicate the number of symbolic links in this archive.
|
|
||||||
|
|
||||||
Following the link-count bytes, the following bytes are added for each symlink:
|
|
||||||
|
|
||||||
1. 2 bytes bit-flags:
|
|
||||||
1. The first byte.
|
|
||||||
1. The first bit is UNSET if relative links are preferred, and is SET if
|
|
||||||
absolute links are preferred.
|
|
||||||
2. The second bit is "user read permission".
|
|
||||||
3. The third bit is "user write permission".
|
|
||||||
4. The fourth bit is "user execute permission".
|
|
||||||
5. The fifth bit is "group read permission".
|
|
||||||
6. The sixth bit is "group write permission".
|
|
||||||
7. The seventh bit is "group execute permission".
|
|
||||||
8. The eighth bit is "other read permission".
|
|
||||||
2. The second byte.
|
|
||||||
1. The first bit is "other write permission".
|
|
||||||
2. The second bit is "other execute permission".
|
|
||||||
3. If this bit is set, then this entry is marked invalid. The link name
|
|
||||||
will be preserved in this entry, but the following link target paths
|
|
||||||
will be set to zero-length and will not be stored.
|
|
||||||
2. 2 bytes 16-bit unsigned integer "link name" in big-endian. This does not
|
|
||||||
include the NULL at the end of the string. Must not be zero.
|
|
||||||
3. X bytes of link-name (length defined by previous value). Is a NULL-terminated
|
|
||||||
string.
|
|
||||||
4. 2 bytes is 16-bit unsigned integer "link target absolute path" in
|
|
||||||
big-endian. This does not include the NULL at the end of the string.
|
|
||||||
5. X bytes of link-target-absolute-path (length defined by previous value).
|
|
||||||
Is a NULL-terminated string. If the previous "size" value is 0, then
|
|
||||||
this entry does not exist and should be skipped.
|
|
||||||
6. 2 bytes is 16-bit unsigned integer "link target relative path" in
|
|
||||||
big-endian. This does not include the NULL at the end of the string.
|
|
||||||
7. X bytes of link-target-relative-path (length defined by previous value).
|
|
||||||
Is a NULL-terminated string. If the previous "size" value is 0, then
|
|
||||||
this entry does not exist and should be skipped.
|
|
||||||
|
|
||||||
After the symlink related data, the next 4 bytes is a 32-bit unsigned integer
|
|
||||||
"chunk count" in big-endian which will indicate the number of chunks in this
|
|
||||||
archive.
|
|
||||||
|
|
||||||
Following the chunk-count bytes, the following bytes are added for each chunk:
|
|
||||||
|
|
||||||
1. 4 bytes that are a 32-bit unsigned integer "file count" in big-endian.
|
|
||||||
|
|
||||||
The following bytes are added for each file within the current chunk:
|
|
||||||
|
|
||||||
1. 2 bytes that are a 16-bit unsigned integer "filename length" in big-endian.
|
|
||||||
This does not include the NULL at the end of the string.
|
|
||||||
2. X bytes of filename (length defined by previous value). Is a NULL-terminated
|
|
||||||
string.
|
|
||||||
3. 4 bytes bit-flags.
|
|
||||||
1. The first byte.
|
|
||||||
1. The first bit is "user read permission".
|
|
||||||
2. The second bit is "user write permission".
|
|
||||||
3. The third bit is "user execute permission".
|
|
||||||
4. The fourth bit is "group read permission".
|
|
||||||
5. The fifth bit is "group write permission".
|
|
||||||
6. The sixth bit is "group execute permission".
|
|
||||||
7. The seventh bit is "other read permission".
|
|
||||||
8. The eighth bit is "other write permission".
|
|
||||||
2. The second byte.
|
|
||||||
1. The first bit is "other execute permission".
|
|
||||||
3. The third byte.
|
|
||||||
1. Currently unused.
|
|
||||||
4. The fourth byte.
|
|
||||||
1. Currently unused.
|
|
||||||
4. Two 4-byte unsigned integers in big-endian for UID and GID.
|
|
||||||
1. A 32-bit unsigned integer in big endian that specifies the UID of the
|
|
||||||
file. Note that during extraction, if the user is not root, then this
|
|
||||||
value will be ignored.
|
|
||||||
2. A 32-bit unsigned integer in big endian that specifies the GID of the
|
|
||||||
file. Note that during extraction, if the user is not root, then this
|
|
||||||
value will be ignored.
|
|
||||||
5. A 64-bit unsigned integer in big endian for the "size of file".
|
|
||||||
|
|
||||||
After the files' metadata are the current chunk's data:
|
|
||||||
|
|
||||||
1. A 64-bit unsigned integer in big endian for the "size of chunk".
|
|
||||||
2. X bytes of data for the current chunk of the previously specified size. If
|
|
||||||
not using de/compressor, this section is the previously mentioned files
|
|
||||||
concatenated with each other. If using de/compressor, this section is the
|
|
||||||
previously mentioned files concatenated and compressed into a single blob of
|
|
||||||
data.
|
|
||||||
|
|
|
@ -1,30 +1,32 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `linear_congruential_gen.c` is the source for the linear congruential
|
||||||
// `linear_congruential_gen.c` is the source for the linear congruential
|
* generator algorithm.
|
||||||
// generator algorithm.
|
*/
|
||||||
|
|
||||||
#include "linear_congruential_gen.h"
|
#include "linear_congruential_gen.h"
|
||||||
|
|
||||||
uint64_t simple_archiver_algo_lcg(uint64_t seed, uint64_t a, uint64_t c) {
|
unsigned long long simple_archiver_algo_lcg(unsigned long long seed,
|
||||||
|
unsigned long long a,
|
||||||
|
unsigned long long c) {
|
||||||
// "m" is implicity 2^64.
|
// "m" is implicity 2^64.
|
||||||
return seed * a + c;
|
return seed * a + c;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t simple_archiver_algo_lcg_defaults(uint64_t seed) {
|
unsigned long long simple_archiver_algo_lcg_defaults(unsigned long long seed) {
|
||||||
// "m" is implicity 2^64.
|
// "m" is implicity 2^64.
|
||||||
return seed * SC_ALGO_LCG_DEFAULT_A + SC_ALGO_LCG_DEFAULT_C;
|
return seed * SC_ALGO_LCG_DEFAULT_A + SC_ALGO_LCG_DEFAULT_C;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,32 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `linear_congruential_gen.h` is the header for the linear congruential
|
||||||
// `linear_congruential_gen.h` is the header for the linear congruential
|
* generator algorithm.
|
||||||
// generator algorithm.
|
*/
|
||||||
|
|
||||||
#ifndef SEODISPARATE_COM_ALGORITHMS_LINEAR_CONGRUENTIAL_GEN_H_
|
#ifndef SEODISPARATE_COM_ALGORITHMS_LINEAR_CONGRUENTIAL_GEN_H_
|
||||||
#define SEODISPARATE_COM_ALGORITHMS_LINEAR_CONGRUENTIAL_GEN_H_
|
#define SEODISPARATE_COM_ALGORITHMS_LINEAR_CONGRUENTIAL_GEN_H_
|
||||||
|
|
||||||
// Standard library includes.
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define SC_ALGO_LCG_DEFAULT_A 0x9ABD
|
#define SC_ALGO_LCG_DEFAULT_A 0x9ABD
|
||||||
#define SC_ALGO_LCG_DEFAULT_C 0x2A9A9A9
|
#define SC_ALGO_LCG_DEFAULT_C 0x2A9A9A9
|
||||||
|
|
||||||
uint64_t simple_archiver_algo_lcg(uint64_t seed, uint64_t a, uint64_t c);
|
unsigned long long simple_archiver_algo_lcg(unsigned long long seed,
|
||||||
|
unsigned long long a,
|
||||||
|
unsigned long long c);
|
||||||
|
|
||||||
uint64_t simple_archiver_algo_lcg_defaults(uint64_t seed);
|
unsigned long long simple_archiver_algo_lcg_defaults(unsigned long long seed);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
4797
src/archiver.c
4797
src/archiver.c
File diff suppressed because it is too large
Load diff
117
src/archiver.h
117
src/archiver.h
|
@ -1,117 +0,0 @@
|
||||||
// ISC License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2024 Stephen Seo
|
|
||||||
//
|
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
|
||||||
// copyright notice and this permission notice appear in all copies.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
//
|
|
||||||
// `archiver.h` is the header for an interface to creating an archive file.
|
|
||||||
|
|
||||||
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_ARCHIVER_H_
|
|
||||||
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_ARCHIVER_H_
|
|
||||||
|
|
||||||
// Standard library includes.
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
// Local includes.
|
|
||||||
#include "data_structures/hash_map.h"
|
|
||||||
#include "data_structures/linked_list.h"
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
typedef struct SDArchiverState {
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
uint32_t flags;
|
|
||||||
const SDArchiverParsed *parsed;
|
|
||||||
FILE *out_f;
|
|
||||||
SDArchiverHashMap *map;
|
|
||||||
size_t count;
|
|
||||||
size_t max;
|
|
||||||
size_t digits;
|
|
||||||
} SDArchiverState;
|
|
||||||
|
|
||||||
typedef enum SDArchiverStateReturns {
|
|
||||||
SDAS_SUCCESS = 0,
|
|
||||||
SDAS_HEADER_ALREADY_WRITTEN = 1,
|
|
||||||
SDAS_FAILED_TO_WRITE,
|
|
||||||
SDAS_NO_COMPRESSOR,
|
|
||||||
SDAS_NO_DECOMPRESSOR,
|
|
||||||
SDAS_INVALID_PARSED_STATE,
|
|
||||||
SDAS_INVALID_FILE,
|
|
||||||
SDAS_INTERNAL_ERROR,
|
|
||||||
SDAS_FAILED_TO_CREATE_MAP,
|
|
||||||
SDAS_FAILED_TO_EXTRACT_SYMLINK,
|
|
||||||
SDAS_FAILED_TO_CHANGE_CWD,
|
|
||||||
SDAS_INVALID_WRITE_VERSION,
|
|
||||||
SDAS_SIGINT
|
|
||||||
} SDArchiverStateReturns;
|
|
||||||
|
|
||||||
/// Returned pointer must not be freed.
|
|
||||||
char *simple_archiver_error_to_string(enum SDArchiverStateReturns error);
|
|
||||||
|
|
||||||
SDArchiverState *simple_archiver_init_state(const SDArchiverParsed *parsed);
|
|
||||||
void simple_archiver_free_state(SDArchiverState **state);
|
|
||||||
|
|
||||||
/// Returns zero on success. Otherwise one value from SDArchiverStateReturns
|
|
||||||
/// enum.
|
|
||||||
int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
|
|
||||||
const SDArchiverLinkedList *filenames);
|
|
||||||
|
|
||||||
int simple_archiver_write_v0(FILE *out_f, SDArchiverState *state,
|
|
||||||
const SDArchiverLinkedList *filenames);
|
|
||||||
|
|
||||||
int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
|
|
||||||
const SDArchiverLinkedList *filenames);
|
|
||||||
|
|
||||||
/// Returns zero on success.
|
|
||||||
int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
|
|
||||||
const SDArchiverState *state);
|
|
||||||
|
|
||||||
/// Returns zero on success.
|
|
||||||
int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
|
|
||||||
const SDArchiverState *state);
|
|
||||||
|
|
||||||
/// Returns zero on success.
|
|
||||||
int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
|
|
||||||
const SDArchiverState *state);
|
|
||||||
|
|
||||||
/// Returns zero on success.
|
|
||||||
int simple_archiver_de_compress(int pipe_fd_in[2], int pipe_fd_out[2],
|
|
||||||
const char *cmd, void *pid_out);
|
|
||||||
|
|
||||||
/// If returns non-NULL, must be free'd.
|
|
||||||
char *simple_archiver_filenames_to_relative_path(const char *from_abs,
|
|
||||||
const char *to_abs);
|
|
||||||
|
|
||||||
/// Gets the absolute path to a file given a path to a file.
|
|
||||||
/// Should also work on symlinks such that the returned string is the path to
|
|
||||||
/// the link itself, not what it points to.
|
|
||||||
/// Non-NULL on success, and must be free'd if non-NULL.
|
|
||||||
char *simple_archiver_file_abs_path(const char *filename);
|
|
||||||
|
|
||||||
/// Used to validate a file in a ".simplearchive" file to avoid writing outside
|
|
||||||
/// of current working directory.
|
|
||||||
/// Returns zero if file is OK.
|
|
||||||
/// Returns 1 if file starts with '/'.
|
|
||||||
/// Returns 2 if file contains '../' at the start.
|
|
||||||
/// Returns 3 if file contains '/../' in the middle.
|
|
||||||
/// Returns 4 if file contains '/..' at the end.
|
|
||||||
/// Returns 5 if "filepath" is NULL.
|
|
||||||
int simple_archiver_validate_file_path(const char *filepath);
|
|
||||||
|
|
||||||
/// Removes links from "links_list" in cwd if it is not valid or does not point
|
|
||||||
/// to a file in "files_map".
|
|
||||||
void simple_archiver_safe_links_enforce(SDArchiverLinkedList *links_list,
|
|
||||||
SDArchiverHashMap *files_map);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,20 +1,20 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `hash_map.c` is the source for a hash map implementation.
|
||||||
// `hash_map.c` is the source for a hash map implementation.
|
*/
|
||||||
|
|
||||||
#include "hash_map.h"
|
#include "hash_map.h"
|
||||||
|
|
||||||
|
@ -26,22 +26,16 @@
|
||||||
typedef struct SDArchiverHashMapData {
|
typedef struct SDArchiverHashMapData {
|
||||||
void *value;
|
void *value;
|
||||||
void *key;
|
void *key;
|
||||||
size_t key_size;
|
unsigned int key_size;
|
||||||
void (*value_cleanup_fn)(void *);
|
void (*value_cleanup_fn)(void *);
|
||||||
void (*key_cleanup_fn)(void *);
|
void (*key_cleanup_fn)(void *);
|
||||||
} SDArchiverHashMapData;
|
} SDArchiverHashMapData;
|
||||||
|
|
||||||
typedef struct SDArchiverHashMapKeyData {
|
typedef struct SDArchiverHashMapKeyData {
|
||||||
void *key;
|
void *key;
|
||||||
size_t key_size;
|
unsigned int key_size;
|
||||||
} SDArchiverHashMapKeyData;
|
} SDArchiverHashMapKeyData;
|
||||||
|
|
||||||
typedef struct SDArchiverInternalIterContext {
|
|
||||||
int (*iter_check_fn)(const void *, size_t, const void *, void *);
|
|
||||||
int ret;
|
|
||||||
void *user_data;
|
|
||||||
} SDArchiverInternalIterContext;
|
|
||||||
|
|
||||||
void simple_archiver_hash_map_internal_cleanup_data(void *data) {
|
void simple_archiver_hash_map_internal_cleanup_data(void *data) {
|
||||||
SDArchiverHashMapData *hash_map_data = data;
|
SDArchiverHashMapData *hash_map_data = data;
|
||||||
if (hash_map_data->value) {
|
if (hash_map_data->value) {
|
||||||
|
@ -74,19 +68,22 @@ int simple_archiver_hash_map_internal_pick_in_list(void *data, void *ud) {
|
||||||
: 0;
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t simple_archiver_hash_default_fn(const void *key, size_t key_size) {
|
unsigned long long simple_archiver_hash_map_internal_key_to_hash(
|
||||||
uint64_t seed = 0;
|
void *key, unsigned int key_size) {
|
||||||
uint64_t temp;
|
unsigned long long seed = 0;
|
||||||
for (size_t idx = 0; idx < key_size; ++idx) {
|
unsigned long long temp = 0;
|
||||||
temp = (uint64_t)(((uint8_t*)key)[idx]) + seed;
|
unsigned int count = 0;
|
||||||
if (idx % 3 == 0) {
|
for (unsigned int idx = 0; idx < key_size; ++idx) {
|
||||||
temp ^= 0xA5A538A5A9B5A5A5;
|
temp |= ((unsigned long long)*((unsigned char *)key + idx)) << (8 * count);
|
||||||
} else if (idx % 3 == 1) {
|
++count;
|
||||||
temp ^= 0xD7A58BD7A58BD7AA;
|
if (count >= 8) {
|
||||||
} else {
|
count = 0;
|
||||||
temp ^= 0x8B7A8B8B87CB8B84;
|
seed += temp;
|
||||||
|
temp = 0;
|
||||||
}
|
}
|
||||||
seed += simple_archiver_algo_lcg_defaults(temp);
|
}
|
||||||
|
if (temp != 0) {
|
||||||
|
seed += temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
return simple_archiver_algo_lcg_defaults(seed);
|
return simple_archiver_algo_lcg_defaults(seed);
|
||||||
|
@ -98,28 +95,28 @@ void simple_archiver_hash_map_internal_no_free_fn(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns 0 on success.
|
/// Returns 0 on success.
|
||||||
int simple_archiver_hash_map_internal_rehash(SDArchiverHashMap *hash_map) {
|
int simple_archiver_hash_map_internal_rehash(SDArchiverHashMap **hash_map) {
|
||||||
if (!hash_map) {
|
if (!hash_map || !*hash_map) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
SDArchiverHashMap new_hash_map;
|
SDArchiverHashMap *new_hash_map = malloc(sizeof(SDArchiverHashMap));
|
||||||
new_hash_map.hash_fn = hash_map->hash_fn;
|
new_hash_map->buckets_size = (*hash_map)->buckets_size * 2;
|
||||||
new_hash_map.buckets_size = (hash_map->buckets_size - 1) * 2 + 1;
|
|
||||||
// Pointers have the same size (at least on the same machine), so
|
// Pointers have the same size (at least on the same machine), so
|
||||||
// sizeof(void*) should be ok.
|
// sizeof(void*) should be ok.
|
||||||
new_hash_map.buckets = malloc(sizeof(void *) * new_hash_map.buckets_size);
|
new_hash_map->buckets = malloc(sizeof(void *) * new_hash_map->buckets_size);
|
||||||
for (size_t idx = 0; idx < new_hash_map.buckets_size; ++idx) {
|
for (unsigned int idx = 0; idx < new_hash_map->buckets_size; ++idx) {
|
||||||
new_hash_map.buckets[idx] = simple_archiver_list_init();
|
new_hash_map->buckets[idx] = simple_archiver_list_init();
|
||||||
}
|
}
|
||||||
new_hash_map.count = 0;
|
new_hash_map->count = 0;
|
||||||
|
|
||||||
// Iterate through the old hash map to populate the new hash map.
|
// Iterate through the old hash map to populate the new hash map.
|
||||||
for (size_t bucket_idx = 0; bucket_idx < hash_map->buckets_size;
|
for (unsigned int bucket_idx = 0; bucket_idx < (*hash_map)->buckets_size;
|
||||||
++bucket_idx) {
|
++bucket_idx) {
|
||||||
SDArchiverLLNode *node = hash_map->buckets[bucket_idx]->head;
|
SDArchiverLLNode *node = (*hash_map)->buckets[bucket_idx]->head;
|
||||||
while (node) {
|
while (node) {
|
||||||
node = node->next;
|
node = node->next;
|
||||||
if (node && node != hash_map->buckets[bucket_idx]->tail && node->data) {
|
if (node && node != (*hash_map)->buckets[bucket_idx]->tail &&
|
||||||
|
node->data) {
|
||||||
SDArchiverHashMapData *data = node->data;
|
SDArchiverHashMapData *data = node->data;
|
||||||
simple_archiver_hash_map_insert(&new_hash_map, data->value, data->key,
|
simple_archiver_hash_map_insert(&new_hash_map, data->value, data->key,
|
||||||
data->key_size, data->value_cleanup_fn,
|
data->key_size, data->value_cleanup_fn,
|
||||||
|
@ -130,33 +127,19 @@ int simple_archiver_hash_map_internal_rehash(SDArchiverHashMap *hash_map) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the buckets in the old hash_map.
|
simple_archiver_hash_map_free(hash_map);
|
||||||
for (size_t idx = 0; idx < hash_map->buckets_size; ++idx) {
|
|
||||||
SDArchiverLinkedList **linked_list = hash_map->buckets + idx;
|
|
||||||
simple_archiver_list_free(linked_list);
|
|
||||||
}
|
|
||||||
free(hash_map->buckets);
|
|
||||||
|
|
||||||
// Move the new buckets and related data into the old hash_map.
|
|
||||||
*hash_map = new_hash_map;
|
*hash_map = new_hash_map;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDArchiverHashMap *simple_archiver_hash_map_init(void) {
|
SDArchiverHashMap *simple_archiver_hash_map_init(void) {
|
||||||
return simple_archiver_hash_map_init_custom_hasher(
|
|
||||||
simple_archiver_hash_default_fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDArchiverHashMap *simple_archiver_hash_map_init_custom_hasher(
|
|
||||||
uint64_t (*hash_fn)(const void *, size_t)) {
|
|
||||||
SDArchiverHashMap *hash_map = malloc(sizeof(SDArchiverHashMap));
|
SDArchiverHashMap *hash_map = malloc(sizeof(SDArchiverHashMap));
|
||||||
hash_map->hash_fn = hash_fn;
|
hash_map->buckets_size = SC_SA_DS_HASH_MAP_START_BUCKET_SIZE;
|
||||||
hash_map->buckets_size = SC_SA_DS_HASH_MAP_START_BUCKET_SIZE + 1;
|
|
||||||
// Pointers have the same size (at least on the same machine), so
|
// Pointers have the same size (at least on the same machine), so
|
||||||
// sizeof(void*) should be ok.
|
// sizeof(void*) should be ok.
|
||||||
hash_map->buckets = malloc(sizeof(void *) * hash_map->buckets_size);
|
hash_map->buckets = malloc(sizeof(void *) * hash_map->buckets_size);
|
||||||
for (size_t idx = 0; idx < hash_map->buckets_size; ++idx) {
|
for (unsigned int idx = 0; idx < hash_map->buckets_size; ++idx) {
|
||||||
hash_map->buckets[idx] = simple_archiver_list_init();
|
hash_map->buckets[idx] = simple_archiver_list_init();
|
||||||
}
|
}
|
||||||
hash_map->count = 0;
|
hash_map->count = 0;
|
||||||
|
@ -164,30 +147,25 @@ SDArchiverHashMap *simple_archiver_hash_map_init_custom_hasher(
|
||||||
return hash_map;
|
return hash_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
void simple_archiver_hash_map_free_single_ptr(SDArchiverHashMap *hash_map) {
|
void simple_archiver_hash_map_free(SDArchiverHashMap **hash_map) {
|
||||||
if (hash_map) {
|
if (hash_map && *hash_map) {
|
||||||
for (size_t idx = 0; idx < hash_map->buckets_size; ++idx) {
|
for (unsigned int idx = 0; idx < (*hash_map)->buckets_size; ++idx) {
|
||||||
SDArchiverLinkedList **linked_list = hash_map->buckets + idx;
|
SDArchiverLinkedList **linked_list = (*hash_map)->buckets + idx;
|
||||||
simple_archiver_list_free(linked_list);
|
simple_archiver_list_free(linked_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(hash_map->buckets);
|
free((*hash_map)->buckets);
|
||||||
free(hash_map);
|
free(*hash_map);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simple_archiver_hash_map_free(SDArchiverHashMap **hash_map) {
|
|
||||||
if (hash_map && *hash_map) {
|
|
||||||
simple_archiver_hash_map_free_single_ptr(*hash_map);
|
|
||||||
*hash_map = NULL;
|
*hash_map = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int simple_archiver_hash_map_insert(SDArchiverHashMap *hash_map, void *value,
|
int simple_archiver_hash_map_insert(SDArchiverHashMap **hash_map, void *value,
|
||||||
void *key, size_t key_size,
|
void *key, unsigned int key_size,
|
||||||
void (*value_cleanup_fn)(void *),
|
void (*value_cleanup_fn)(void *),
|
||||||
void (*key_cleanup_fn)(void *)) {
|
void (*key_cleanup_fn)(void *)) {
|
||||||
if (hash_map->buckets_size <= hash_map->count) {
|
if ((*hash_map)->buckets_size <= (*hash_map)->count) {
|
||||||
simple_archiver_hash_map_internal_rehash(hash_map);
|
simple_archiver_hash_map_internal_rehash(hash_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,13 +176,15 @@ int simple_archiver_hash_map_insert(SDArchiverHashMap *hash_map, void *value,
|
||||||
data->value_cleanup_fn = value_cleanup_fn;
|
data->value_cleanup_fn = value_cleanup_fn;
|
||||||
data->key_cleanup_fn = key_cleanup_fn;
|
data->key_cleanup_fn = key_cleanup_fn;
|
||||||
|
|
||||||
uint64_t hash = hash_map->hash_fn(key, key_size) % hash_map->buckets_size;
|
unsigned long long hash =
|
||||||
|
simple_archiver_hash_map_internal_key_to_hash(key, key_size) %
|
||||||
|
(*hash_map)->buckets_size;
|
||||||
int result = simple_archiver_list_add_front(
|
int result = simple_archiver_list_add_front(
|
||||||
hash_map->buckets[hash], data,
|
(*hash_map)->buckets[hash], data,
|
||||||
simple_archiver_hash_map_internal_cleanup_data);
|
simple_archiver_hash_map_internal_cleanup_data);
|
||||||
|
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
++hash_map->count;
|
++(*hash_map)->count;
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
@ -227,9 +207,11 @@ int simple_archiver_hash_map_insert(SDArchiverHashMap *hash_map, void *value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *simple_archiver_hash_map_get(const SDArchiverHashMap *hash_map,
|
void *simple_archiver_hash_map_get(SDArchiverHashMap *hash_map, void *key,
|
||||||
const void *key, size_t key_size) {
|
unsigned int key_size) {
|
||||||
uint64_t hash = hash_map->hash_fn(key, key_size) % hash_map->buckets_size;
|
unsigned long long hash =
|
||||||
|
simple_archiver_hash_map_internal_key_to_hash(key, key_size) %
|
||||||
|
hash_map->buckets_size;
|
||||||
|
|
||||||
SDArchiverLLNode *node = hash_map->buckets[hash]->head;
|
SDArchiverLLNode *node = hash_map->buckets[hash]->head;
|
||||||
while (node) {
|
while (node) {
|
||||||
|
@ -246,8 +228,10 @@ void *simple_archiver_hash_map_get(const SDArchiverHashMap *hash_map,
|
||||||
}
|
}
|
||||||
|
|
||||||
int simple_archiver_hash_map_remove(SDArchiverHashMap *hash_map, void *key,
|
int simple_archiver_hash_map_remove(SDArchiverHashMap *hash_map, void *key,
|
||||||
size_t key_size) {
|
unsigned int key_size) {
|
||||||
uint64_t hash = hash_map->hash_fn(key, key_size) % hash_map->buckets_size;
|
unsigned long long hash =
|
||||||
|
simple_archiver_hash_map_internal_key_to_hash(key, key_size) %
|
||||||
|
hash_map->buckets_size;
|
||||||
|
|
||||||
SDArchiverHashMapKeyData key_data;
|
SDArchiverHashMapKeyData key_data;
|
||||||
key_data.key = key;
|
key_data.key = key;
|
||||||
|
@ -264,34 +248,3 @@ int simple_archiver_hash_map_remove(SDArchiverHashMap *hash_map, void *key,
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int simple_archiver_internal_hash_map_bucket_iter_fn(void *data, void *ud) {
|
|
||||||
SDArchiverHashMapData *hash_map_data = data;
|
|
||||||
SDArchiverInternalIterContext *ctx = ud;
|
|
||||||
|
|
||||||
ctx->ret = ctx->iter_check_fn(hash_map_data->key, hash_map_data->key_size,
|
|
||||||
hash_map_data->value, ctx->user_data);
|
|
||||||
if (ctx->ret != 0) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int simple_archiver_hash_map_iter(const SDArchiverHashMap *hash_map,
|
|
||||||
int (*iter_check_fn)(const void *, size_t,
|
|
||||||
const void *, void *),
|
|
||||||
void *user_data) {
|
|
||||||
SDArchiverInternalIterContext ctx;
|
|
||||||
ctx.iter_check_fn = iter_check_fn;
|
|
||||||
ctx.ret = 0;
|
|
||||||
ctx.user_data = user_data;
|
|
||||||
for (size_t idx = 0; idx < hash_map->buckets_size; ++idx) {
|
|
||||||
if (simple_archiver_list_get(
|
|
||||||
hash_map->buckets[idx],
|
|
||||||
simple_archiver_internal_hash_map_bucket_iter_fn, &ctx) != 0) {
|
|
||||||
return ctx.ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ctx.ret;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,56 +1,35 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `hash_map.h` is the header for a hash map implementation.
|
||||||
// `hash_map.h` is the header for a hash map implementation.
|
*/
|
||||||
|
|
||||||
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_HASH_MAP_H_
|
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_HASH_MAP_H_
|
||||||
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_HASH_MAP_H_
|
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_HASH_MAP_H_
|
||||||
|
|
||||||
#define SC_SA_DS_HASH_MAP_START_BUCKET_SIZE 32
|
#define SC_SA_DS_HASH_MAP_START_BUCKET_SIZE 32
|
||||||
|
|
||||||
// Standard library includes.
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
// Local includes.
|
|
||||||
#include "linked_list.h"
|
#include "linked_list.h"
|
||||||
|
|
||||||
typedef struct SDArchiverHashMap {
|
typedef struct SDArchiverHashMap {
|
||||||
SDArchiverLinkedList **buckets;
|
SDArchiverLinkedList **buckets;
|
||||||
size_t buckets_size;
|
unsigned int buckets_size;
|
||||||
size_t count;
|
unsigned int count;
|
||||||
uint64_t (*hash_fn)(const void *, size_t);
|
|
||||||
} SDArchiverHashMap;
|
} SDArchiverHashMap;
|
||||||
|
|
||||||
uint64_t simple_archiver_hash_default_fn(const void *key, size_t key_size);
|
|
||||||
|
|
||||||
SDArchiverHashMap *simple_archiver_hash_map_init(void);
|
SDArchiverHashMap *simple_archiver_hash_map_init(void);
|
||||||
|
|
||||||
/// Creates a hash map that will use the custom hash function instead of the
|
|
||||||
/// default. Note that the hash function must return a 64-bit unsigned integer
|
|
||||||
/// as specified by the function's api. The first parameter of hash_fn is a
|
|
||||||
/// pointer to the key to be hashed, and the second parameter is the size of
|
|
||||||
/// the key in bytes.
|
|
||||||
SDArchiverHashMap *simple_archiver_hash_map_init_custom_hasher(
|
|
||||||
uint64_t (*hash_fn)(const void *, size_t));
|
|
||||||
|
|
||||||
/// It is recommended to use the double-pointer version of hash-map free as
|
|
||||||
/// that will ensure the variable holding the pointer will end up pointing to
|
|
||||||
/// NULL after free.
|
|
||||||
void simple_archiver_hash_map_free_single_ptr(SDArchiverHashMap *hash_map);
|
|
||||||
void simple_archiver_hash_map_free(SDArchiverHashMap **hash_map);
|
void simple_archiver_hash_map_free(SDArchiverHashMap **hash_map);
|
||||||
|
|
||||||
/// Returns zero on success.
|
/// Returns zero on success.
|
||||||
|
@ -58,31 +37,18 @@ void simple_archiver_hash_map_free(SDArchiverHashMap **hash_map);
|
||||||
/// key must remain valid for the lifetime of its entry in the hash map.
|
/// key must remain valid for the lifetime of its entry in the hash map.
|
||||||
/// If value_cleanup_fn is NULL, then "free" is used instead.
|
/// If value_cleanup_fn is NULL, then "free" is used instead.
|
||||||
/// If key_cleanup_fn is NULL, then "free" is used instead.
|
/// If key_cleanup_fn is NULL, then "free" is used instead.
|
||||||
/// NOTICE: You must not pass NULL to value, otherwise all "get" checks will
|
int simple_archiver_hash_map_insert(SDArchiverHashMap **hash_map, void *value,
|
||||||
/// fail for the inserted key.
|
void *key, unsigned int key_size,
|
||||||
int simple_archiver_hash_map_insert(SDArchiverHashMap *hash_map, void *value,
|
|
||||||
void *key, size_t key_size,
|
|
||||||
void (*value_cleanup_fn)(void *),
|
void (*value_cleanup_fn)(void *),
|
||||||
void (*key_cleanup_fn)(void *));
|
void (*key_cleanup_fn)(void *));
|
||||||
|
|
||||||
/// Returns NULL if not found.
|
/// Returns NULL if not found.
|
||||||
void *simple_archiver_hash_map_get(const SDArchiverHashMap *hash_map,
|
void *simple_archiver_hash_map_get(SDArchiverHashMap *hash_map, void *key,
|
||||||
const void *key, size_t key_size);
|
unsigned int key_size);
|
||||||
|
|
||||||
/// Returns zero on success. Returns one if more than one entry was removed.
|
/// Returns zero on success. Returns one if more than one entry was removed.
|
||||||
/// Otherwise returns non-zero and non-one value on error.
|
/// Otherwise returns non-zero and non-one value on error.
|
||||||
int simple_archiver_hash_map_remove(SDArchiverHashMap *hash_map, void *key,
|
int simple_archiver_hash_map_remove(SDArchiverHashMap *hash_map, void *key,
|
||||||
size_t key_size);
|
unsigned int key_size);
|
||||||
|
|
||||||
/// Iterates through the hash map with the "iter_check_fn", which is passed the
|
|
||||||
/// key, key-size, value, and user_data. This function will call "iter_check_fn"
|
|
||||||
/// on every entry in the given hash_map. If "iter_check_fn" returns non-zero,
|
|
||||||
/// iteration will halt and this function will return the same value. If
|
|
||||||
/// "iter_check_fn" returns zero for every call, then this function will return
|
|
||||||
/// zero after having iterated through every key-value pair.
|
|
||||||
int simple_archiver_hash_map_iter(const SDArchiverHashMap *hash_map,
|
|
||||||
int (*iter_check_fn)(const void *, size_t,
|
|
||||||
const void *, void *),
|
|
||||||
void *user_data);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,25 +1,23 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `linked_list.c` is the source for a linked list data structure.
|
||||||
// `linked_list.c` is the source for a linked list data structure.
|
*/
|
||||||
|
|
||||||
#include "linked_list.h"
|
#include "linked_list.h"
|
||||||
|
|
||||||
// Standard library includes.
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
SDArchiverLinkedList *simple_archiver_list_init(void) {
|
SDArchiverLinkedList *simple_archiver_list_init(void) {
|
||||||
|
@ -43,9 +41,9 @@ SDArchiverLinkedList *simple_archiver_list_init(void) {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
void simple_archiver_list_free_single_ptr(SDArchiverLinkedList *list) {
|
void simple_archiver_list_free(SDArchiverLinkedList **list) {
|
||||||
if (list) {
|
if (list && *list) {
|
||||||
SDArchiverLLNode *node = list->head;
|
SDArchiverLLNode *node = (*list)->head;
|
||||||
SDArchiverLLNode *prev;
|
SDArchiverLLNode *prev;
|
||||||
while (node) {
|
while (node) {
|
||||||
prev = node;
|
prev = node;
|
||||||
|
@ -60,13 +58,7 @@ void simple_archiver_list_free_single_ptr(SDArchiverLinkedList *list) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(list);
|
free(*list);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simple_archiver_list_free(SDArchiverLinkedList **list) {
|
|
||||||
if (list && *list) {
|
|
||||||
simple_archiver_list_free_single_ptr(*list);
|
|
||||||
*list = NULL;
|
*list = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,10 +112,10 @@ int simple_archiver_list_remove(SDArchiverLinkedList *list,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t removed_count = 0;
|
int removed_count = 0;
|
||||||
|
|
||||||
SDArchiverLLNode *node = list->head;
|
SDArchiverLLNode *node = list->head;
|
||||||
int32_t iter_removed = 0;
|
int iter_removed = 0;
|
||||||
while (node) {
|
while (node) {
|
||||||
if (iter_removed == 0) {
|
if (iter_removed == 0) {
|
||||||
node = node->next;
|
node = node->next;
|
||||||
|
@ -186,7 +178,7 @@ int simple_archiver_list_remove_once(SDArchiverLinkedList *list,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *simple_archiver_list_get(const SDArchiverLinkedList *list,
|
void *simple_archiver_list_get(SDArchiverLinkedList *list,
|
||||||
int (*data_check_fn)(void *, void *),
|
int (*data_check_fn)(void *, void *),
|
||||||
void *user_data) {
|
void *user_data) {
|
||||||
if (!list) {
|
if (!list) {
|
||||||
|
|
|
@ -1,26 +1,24 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `linked_list.h` is the header for a linked list data structure.
|
||||||
// `linked_list.h` is the header for a linked list data structure.
|
*/
|
||||||
|
|
||||||
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_LINKED_LIST_H_
|
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_LINKED_LIST_H_
|
||||||
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_LINKED_LIST_H_
|
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_LINKED_LIST_H_
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
typedef struct SDArchiverLLNode {
|
typedef struct SDArchiverLLNode {
|
||||||
struct SDArchiverLLNode *next;
|
struct SDArchiverLLNode *next;
|
||||||
struct SDArchiverLLNode *prev;
|
struct SDArchiverLLNode *prev;
|
||||||
|
@ -31,15 +29,10 @@ typedef struct SDArchiverLLNode {
|
||||||
typedef struct SDArchiverLinkedList {
|
typedef struct SDArchiverLinkedList {
|
||||||
SDArchiverLLNode *head;
|
SDArchiverLLNode *head;
|
||||||
SDArchiverLLNode *tail;
|
SDArchiverLLNode *tail;
|
||||||
size_t count;
|
unsigned int count;
|
||||||
} SDArchiverLinkedList;
|
} SDArchiverLinkedList;
|
||||||
|
|
||||||
SDArchiverLinkedList *simple_archiver_list_init(void);
|
SDArchiverLinkedList *simple_archiver_list_init(void);
|
||||||
|
|
||||||
/// It is recommended to use the double-pointer version of list free as that
|
|
||||||
/// will ensure the variable holding the pointer will end up pointing to NULL
|
|
||||||
/// after free.
|
|
||||||
void simple_archiver_list_free_single_ptr(SDArchiverLinkedList *list);
|
|
||||||
void simple_archiver_list_free(SDArchiverLinkedList **list);
|
void simple_archiver_list_free(SDArchiverLinkedList **list);
|
||||||
|
|
||||||
/// Returns 0 on success. Puts data at the end of the list
|
/// Returns 0 on success. Puts data at the end of the list
|
||||||
|
@ -69,7 +62,7 @@ int simple_archiver_list_remove_once(SDArchiverLinkedList *list,
|
||||||
/// Returns non-null on success.
|
/// Returns non-null on success.
|
||||||
/// data_check_fn must return non-zero if the data passed to it is to be
|
/// data_check_fn must return non-zero if the data passed to it is to be
|
||||||
/// returned.
|
/// returned.
|
||||||
void *simple_archiver_list_get(const SDArchiverLinkedList *list,
|
void *simple_archiver_list_get(SDArchiverLinkedList *list,
|
||||||
int (*data_check_fn)(void *, void *),
|
int (*data_check_fn)(void *, void *),
|
||||||
void *user_data);
|
void *user_data);
|
||||||
|
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `priority_heap.c` is the source for a priority heap implementation.
|
||||||
// `priority_heap.c` is the source for a priority heap implementation.
|
*/
|
||||||
|
|
||||||
#include "priority_heap.h"
|
#include "priority_heap.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
void simple_archiver_priority_heap_internal_realloc(
|
void simple_archiver_priority_heap_internal_realloc(
|
||||||
SDArchiverPHeap *priority_heap) {
|
SDArchiverPHeap **priority_heap) {
|
||||||
SDArchiverPHeap new_priority_heap;
|
SDArchiverPHeap *new_priority_heap = malloc(sizeof(SDArchiverPHeap));
|
||||||
|
|
||||||
new_priority_heap.capacity = priority_heap->capacity * 2;
|
new_priority_heap->capacity = (*priority_heap)->capacity * 2;
|
||||||
new_priority_heap.size = 0;
|
new_priority_heap->size = 0;
|
||||||
new_priority_heap.less_fn = priority_heap->less_fn;
|
new_priority_heap->less_fn = (*priority_heap)->less_fn;
|
||||||
|
|
||||||
new_priority_heap.nodes =
|
new_priority_heap->nodes =
|
||||||
calloc(new_priority_heap.capacity, sizeof(SDArchiverPHNode));
|
calloc(new_priority_heap->capacity, sizeof(SDArchiverPHNode));
|
||||||
|
|
||||||
for (size_t idx = 1; idx < priority_heap->size + 1; ++idx) {
|
for (unsigned int idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
|
||||||
if (priority_heap->nodes[idx].is_valid != 0) {
|
if ((*priority_heap)->nodes[idx].is_valid != 0) {
|
||||||
simple_archiver_priority_heap_insert(
|
simple_archiver_priority_heap_insert(
|
||||||
&new_priority_heap, priority_heap->nodes[idx].priority,
|
&new_priority_heap, (*priority_heap)->nodes[idx].priority,
|
||||||
priority_heap->nodes[idx].data,
|
(*priority_heap)->nodes[idx].data,
|
||||||
priority_heap->nodes[idx].data_cleanup_fn);
|
(*priority_heap)->nodes[idx].data_cleanup_fn);
|
||||||
priority_heap->nodes[idx].is_valid = 0;
|
(*priority_heap)->nodes[idx].is_valid = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(priority_heap->nodes);
|
simple_archiver_priority_heap_free(priority_heap);
|
||||||
|
|
||||||
*priority_heap = new_priority_heap;
|
*priority_heap = new_priority_heap;
|
||||||
}
|
}
|
||||||
|
|
||||||
int simple_archiver_priority_heap_default_less(int64_t a, int64_t b) {
|
int simple_archiver_priority_heap_default_less(long long a, long long b) {
|
||||||
return a < b ? 1 : 0;
|
return a < b ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ SDArchiverPHeap *simple_archiver_priority_heap_init(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SDArchiverPHeap *simple_archiver_priority_heap_init_less_fn(
|
SDArchiverPHeap *simple_archiver_priority_heap_init_less_fn(
|
||||||
int (*less_fn)(int64_t, int64_t)) {
|
int (*less_fn)(long long, long long)) {
|
||||||
SDArchiverPHeap *priority_heap = malloc(sizeof(SDArchiverPHeap));
|
SDArchiverPHeap *priority_heap = malloc(sizeof(SDArchiverPHeap));
|
||||||
|
|
||||||
priority_heap->capacity = SC_SA_DS_PRIORITY_HEAP_START_SIZE;
|
priority_heap->capacity = SC_SA_DS_PRIORITY_HEAP_START_SIZE;
|
||||||
|
@ -77,59 +77,54 @@ SDArchiverPHeap *simple_archiver_priority_heap_init_less_fn(
|
||||||
return priority_heap;
|
return priority_heap;
|
||||||
}
|
}
|
||||||
|
|
||||||
void simple_archiver_priority_heap_free_single_ptr(
|
void simple_archiver_priority_heap_free(SDArchiverPHeap **priority_heap) {
|
||||||
SDArchiverPHeap *priority_heap) {
|
if (priority_heap && *priority_heap) {
|
||||||
if (priority_heap) {
|
for (unsigned int idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
|
||||||
for (size_t idx = 1; idx < priority_heap->size + 1; ++idx) {
|
if ((*priority_heap)->nodes[idx].is_valid != 0) {
|
||||||
if (priority_heap->nodes[idx].is_valid != 0) {
|
if ((*priority_heap)->nodes[idx].data_cleanup_fn) {
|
||||||
if (priority_heap->nodes[idx].data_cleanup_fn) {
|
(*priority_heap)
|
||||||
priority_heap->nodes[idx].data_cleanup_fn(
|
->nodes[idx]
|
||||||
priority_heap->nodes[idx].data);
|
.data_cleanup_fn((*priority_heap)->nodes[idx].data);
|
||||||
} else {
|
} else {
|
||||||
free(priority_heap->nodes[idx].data);
|
free((*priority_heap)->nodes[idx].data);
|
||||||
}
|
}
|
||||||
priority_heap->nodes[idx].is_valid = 0;
|
(*priority_heap)->nodes[idx].is_valid = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(priority_heap->nodes);
|
free((*priority_heap)->nodes);
|
||||||
free(priority_heap);
|
free(*priority_heap);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simple_archiver_priority_heap_free(SDArchiverPHeap **priority_heap) {
|
|
||||||
if (priority_heap && *priority_heap) {
|
|
||||||
simple_archiver_priority_heap_free_single_ptr(*priority_heap);
|
|
||||||
*priority_heap = NULL;
|
*priority_heap = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void simple_archiver_priority_heap_insert(SDArchiverPHeap *priority_heap,
|
void simple_archiver_priority_heap_insert(SDArchiverPHeap **priority_heap,
|
||||||
int64_t priority, void *data,
|
long long priority, void *data,
|
||||||
void (*data_cleanup_fn)(void *)) {
|
void (*data_cleanup_fn)(void *)) {
|
||||||
if (!priority_heap) {
|
if (!priority_heap || !*priority_heap) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priority_heap->size + 1 >= priority_heap->capacity) {
|
if ((*priority_heap)->size + 1 >= (*priority_heap)->capacity) {
|
||||||
simple_archiver_priority_heap_internal_realloc(priority_heap);
|
simple_archiver_priority_heap_internal_realloc(priority_heap);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t hole = priority_heap->size + 1;
|
unsigned int hole = (*priority_heap)->size + 1;
|
||||||
|
|
||||||
while (hole > 1 &&
|
while (hole > 1 &&
|
||||||
priority_heap->less_fn(priority,
|
(*priority_heap)
|
||||||
priority_heap->nodes[hole / 2].priority) != 0) {
|
->less_fn(priority,
|
||||||
priority_heap->nodes[hole] = priority_heap->nodes[hole / 2];
|
(*priority_heap)->nodes[hole / 2].priority) != 0) {
|
||||||
|
(*priority_heap)->nodes[hole] = (*priority_heap)->nodes[hole / 2];
|
||||||
hole /= 2;
|
hole /= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
priority_heap->nodes[hole].priority = priority;
|
(*priority_heap)->nodes[hole].priority = priority;
|
||||||
priority_heap->nodes[hole].data = data;
|
(*priority_heap)->nodes[hole].data = data;
|
||||||
priority_heap->nodes[hole].data_cleanup_fn = data_cleanup_fn;
|
(*priority_heap)->nodes[hole].data_cleanup_fn = data_cleanup_fn;
|
||||||
priority_heap->nodes[hole].is_valid = 1;
|
(*priority_heap)->nodes[hole].is_valid = 1;
|
||||||
|
|
||||||
++priority_heap->size;
|
++(*priority_heap)->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *simple_archiver_priority_heap_top(SDArchiverPHeap *priority_heap) {
|
void *simple_archiver_priority_heap_top(SDArchiverPHeap *priority_heap) {
|
||||||
|
@ -151,7 +146,7 @@ void *simple_archiver_priority_heap_pop(SDArchiverPHeap *priority_heap) {
|
||||||
SDArchiverPHNode end = priority_heap->nodes[priority_heap->size];
|
SDArchiverPHNode end = priority_heap->nodes[priority_heap->size];
|
||||||
priority_heap->nodes[priority_heap->size].is_valid = 0;
|
priority_heap->nodes[priority_heap->size].is_valid = 0;
|
||||||
|
|
||||||
size_t hole = 1;
|
unsigned int hole = 1;
|
||||||
while (hole * 2 + 1 <= priority_heap->size) {
|
while (hole * 2 + 1 <= priority_heap->size) {
|
||||||
if (priority_heap->nodes[hole * 2].is_valid != 0 &&
|
if (priority_heap->nodes[hole * 2].is_valid != 0 &&
|
||||||
priority_heap->nodes[hole * 2 + 1].is_valid != 0) {
|
priority_heap->nodes[hole * 2 + 1].is_valid != 0) {
|
||||||
|
|
|
@ -1,31 +1,28 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `priority_heap.h` is the header for a priority heap implementation.
|
||||||
// `priority_heap.h` is the header for a priority heap implementation.
|
*/
|
||||||
|
|
||||||
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_PRIORITY_HEAP_H_
|
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_PRIORITY_HEAP_H_
|
||||||
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_PRIORITY_HEAP_H_
|
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_PRIORITY_HEAP_H_
|
||||||
|
|
||||||
// Standard library includes.
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define SC_SA_DS_PRIORITY_HEAP_START_SIZE 32
|
#define SC_SA_DS_PRIORITY_HEAP_START_SIZE 32
|
||||||
|
|
||||||
typedef struct SDArchiverPHNode {
|
typedef struct SDArchiverPHNode {
|
||||||
int64_t priority;
|
long long priority;
|
||||||
void *data;
|
void *data;
|
||||||
void (*data_cleanup_fn)(void *);
|
void (*data_cleanup_fn)(void *);
|
||||||
/// Is non-zero if valid.
|
/// Is non-zero if valid.
|
||||||
|
@ -34,29 +31,23 @@ typedef struct SDArchiverPHNode {
|
||||||
|
|
||||||
typedef struct SDArchiverPHeap {
|
typedef struct SDArchiverPHeap {
|
||||||
SDArchiverPHNode *nodes;
|
SDArchiverPHNode *nodes;
|
||||||
uint64_t capacity;
|
unsigned long long capacity;
|
||||||
uint64_t size;
|
unsigned long long size;
|
||||||
int (*less_fn)(int64_t, int64_t);
|
int (*less_fn)(long long, long long);
|
||||||
} SDArchiverPHeap;
|
} SDArchiverPHeap;
|
||||||
|
|
||||||
/// Default "less" function to determine if a has higher priority than b.
|
/// Default "less" function to determine if a has higher priority than b.
|
||||||
/// Returns non-zero if "less".
|
/// Returns non-zero if "less".
|
||||||
int simple_archiver_priority_heap_default_less(int64_t a, int64_t b);
|
int simple_archiver_priority_heap_default_less(long long a, long long b);
|
||||||
|
|
||||||
SDArchiverPHeap *simple_archiver_priority_heap_init(void);
|
SDArchiverPHeap *simple_archiver_priority_heap_init(void);
|
||||||
SDArchiverPHeap *simple_archiver_priority_heap_init_less_fn(
|
SDArchiverPHeap *simple_archiver_priority_heap_init_less_fn(
|
||||||
int (*less_fn)(int64_t, int64_t));
|
int (*less_fn)(long long, long long));
|
||||||
|
|
||||||
/// It is recommended to use the double-pointer version of priority-heap free
|
|
||||||
/// as that will ensure the variable holding the pointer will end up pointing
|
|
||||||
/// to NULL after free.
|
|
||||||
void simple_archiver_priority_heap_free_single_ptr(
|
|
||||||
SDArchiverPHeap *priority_heap);
|
|
||||||
void simple_archiver_priority_heap_free(SDArchiverPHeap **priority_heap);
|
void simple_archiver_priority_heap_free(SDArchiverPHeap **priority_heap);
|
||||||
|
|
||||||
/// If data_cleanup_fn is NULL, then "free()" is used on data when freed.
|
/// If data_cleanup_fn is NULL, then "free()" is used on data when freed.
|
||||||
void simple_archiver_priority_heap_insert(SDArchiverPHeap *priority_heap,
|
void simple_archiver_priority_heap_insert(SDArchiverPHeap **priority_heap,
|
||||||
int64_t priority, void *data,
|
long long priority, void *data,
|
||||||
void (*data_cleanup_fn)(void *));
|
void (*data_cleanup_fn)(void *));
|
||||||
|
|
||||||
/// Returns NULL if empty or if priority_heap is NULL.
|
/// Returns NULL if empty or if priority_heap is NULL.
|
||||||
|
|
|
@ -1,37 +1,32 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `data_structures/test.c` is the source for testing data structure code.
|
||||||
// `data_structures/test.c` is the source for testing data structure code.
|
*/
|
||||||
|
|
||||||
// Standard library includes.
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// Local includes.
|
|
||||||
#include "../algorithms/linear_congruential_gen.h"
|
#include "../algorithms/linear_congruential_gen.h"
|
||||||
#include "hash_map.h"
|
#include "hash_map.h"
|
||||||
#include "linked_list.h"
|
#include "linked_list.h"
|
||||||
#include "priority_heap.h"
|
#include "priority_heap.h"
|
||||||
|
|
||||||
#define SDARCHIVER_DS_TEST_HASH_MAP_ITER_SIZE 100
|
static int checks_checked = 0;
|
||||||
|
static int checks_passed = 0;
|
||||||
static int32_t checks_checked = 0;
|
|
||||||
static int32_t checks_passed = 0;
|
|
||||||
|
|
||||||
#define CHECK_TRUE(x) \
|
#define CHECK_TRUE(x) \
|
||||||
do { \
|
do { \
|
||||||
|
@ -66,27 +61,7 @@ int get_three_fn(void *data, __attribute__((unused)) void *ud) {
|
||||||
return strcmp(data, "three") == 0 ? 1 : 0;
|
return strcmp(data, "three") == 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int more_fn(int64_t a, int64_t b) { return a > b ? 1 : 0; }
|
int more_fn(long long a, long long b) { return a > b ? 1 : 0; }
|
||||||
|
|
||||||
int hash_map_iter_check_fn(__attribute__((unused)) const void *key,
|
|
||||||
__attribute__((unused)) size_t key_size,
|
|
||||||
const void *value, void *ud) {
|
|
||||||
char *found_buf = ud;
|
|
||||||
const size_t real_value = (const size_t)value;
|
|
||||||
if (real_value < SDARCHIVER_DS_TEST_HASH_MAP_ITER_SIZE) {
|
|
||||||
found_buf[real_value] += 1;
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int hash_map_iter_check_fn2(__attribute__((unused)) const void *key,
|
|
||||||
__attribute__((unused)) size_t key_size,
|
|
||||||
__attribute__((unused)) const void *value,
|
|
||||||
__attribute__((unused)) void *ud) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
// Test LinkedList.
|
// Test LinkedList.
|
||||||
|
@ -141,37 +116,37 @@ int main(void) {
|
||||||
hash_map = simple_archiver_hash_map_init();
|
hash_map = simple_archiver_hash_map_init();
|
||||||
|
|
||||||
{
|
{
|
||||||
int32_t *value, *key;
|
int *value, *key;
|
||||||
|
|
||||||
for (uint32_t idx = 0; idx < 20; ++idx) {
|
for (unsigned int idx = 0; idx < 20; ++idx) {
|
||||||
value = malloc(sizeof(int32_t));
|
value = malloc(sizeof(int));
|
||||||
key = malloc(sizeof(int32_t));
|
key = malloc(sizeof(int));
|
||||||
*value = idx;
|
*value = idx;
|
||||||
*key = idx;
|
*key = idx;
|
||||||
simple_archiver_hash_map_insert(hash_map, value, key, sizeof(int32_t),
|
simple_archiver_hash_map_insert(&hash_map, value, key, sizeof(int),
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t value, key;
|
int value, key;
|
||||||
void *value_ptr;
|
void *value_ptr;
|
||||||
|
|
||||||
for (value = 0, key = 0; value < 20 && key < 20; ++value, ++key) {
|
for (value = 0, key = 0; value < 20 && key < 20; ++value, ++key) {
|
||||||
value_ptr = simple_archiver_hash_map_get(hash_map, &key, sizeof(int32_t));
|
value_ptr = simple_archiver_hash_map_get(hash_map, &key, sizeof(int));
|
||||||
CHECK_TRUE(value_ptr != NULL);
|
CHECK_TRUE(value_ptr != NULL);
|
||||||
CHECK_TRUE(memcmp(value_ptr, &value, sizeof(int32_t)) == 0);
|
CHECK_TRUE(memcmp(value_ptr, &value, sizeof(int)) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
key = 5;
|
key = 5;
|
||||||
simple_archiver_hash_map_remove(hash_map, &key, sizeof(int32_t));
|
simple_archiver_hash_map_remove(hash_map, &key, sizeof(int));
|
||||||
key = 15;
|
key = 15;
|
||||||
simple_archiver_hash_map_remove(hash_map, &key, sizeof(int32_t));
|
simple_archiver_hash_map_remove(hash_map, &key, sizeof(int));
|
||||||
|
|
||||||
for (value = 0, key = 0; value < 20 && key < 20; ++value, ++key) {
|
for (value = 0, key = 0; value < 20 && key < 20; ++value, ++key) {
|
||||||
value_ptr = simple_archiver_hash_map_get(hash_map, &key, sizeof(int32_t));
|
value_ptr = simple_archiver_hash_map_get(hash_map, &key, sizeof(int));
|
||||||
if (key != 5 && key != 15) {
|
if (key != 5 && key != 15) {
|
||||||
CHECK_TRUE(value_ptr != NULL);
|
CHECK_TRUE(value_ptr != NULL);
|
||||||
CHECK_TRUE(memcmp(value_ptr, &value, sizeof(int32_t)) == 0);
|
CHECK_TRUE(memcmp(value_ptr, &value, sizeof(int)) == 0);
|
||||||
} else {
|
} else {
|
||||||
CHECK_TRUE(value_ptr == NULL);
|
CHECK_TRUE(value_ptr == NULL);
|
||||||
}
|
}
|
||||||
|
@ -181,97 +156,18 @@ int main(void) {
|
||||||
|
|
||||||
// Rehash test for Memcheck.
|
// Rehash test for Memcheck.
|
||||||
hash_map = simple_archiver_hash_map_init();
|
hash_map = simple_archiver_hash_map_init();
|
||||||
for (uint32_t idx = 0; idx < SC_SA_DS_HASH_MAP_START_BUCKET_SIZE + 1;
|
for (unsigned int idx = 0; idx < SC_SA_DS_HASH_MAP_START_BUCKET_SIZE + 1;
|
||||||
++idx) {
|
++idx) {
|
||||||
uint32_t *copy_value = malloc(sizeof(uint32_t));
|
unsigned int *copy_value = malloc(sizeof(unsigned int));
|
||||||
*copy_value = idx;
|
*copy_value = idx;
|
||||||
uint32_t *copy_key = malloc(sizeof(uint32_t));
|
unsigned int *copy_key = malloc(sizeof(unsigned int));
|
||||||
*copy_key = idx;
|
*copy_key = idx;
|
||||||
simple_archiver_hash_map_insert(hash_map, copy_value, copy_key,
|
simple_archiver_hash_map_insert(&hash_map, copy_value, copy_key,
|
||||||
sizeof(uint32_t), NULL, NULL);
|
sizeof(unsigned int), NULL, NULL);
|
||||||
}
|
}
|
||||||
simple_archiver_hash_map_free(&hash_map);
|
simple_archiver_hash_map_free(&hash_map);
|
||||||
|
|
||||||
// Hash map iter test.
|
|
||||||
hash_map = simple_archiver_hash_map_init();
|
|
||||||
|
|
||||||
for (size_t idx = 0; idx < SDARCHIVER_DS_TEST_HASH_MAP_ITER_SIZE; ++idx) {
|
|
||||||
simple_archiver_hash_map_insert(hash_map, (void *)idx, &idx,
|
|
||||||
sizeof(size_t), no_free_fn, no_free_fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
char found[SDARCHIVER_DS_TEST_HASH_MAP_ITER_SIZE] = {0};
|
|
||||||
|
|
||||||
CHECK_TRUE(simple_archiver_hash_map_iter(hash_map, hash_map_iter_check_fn,
|
|
||||||
found) == 0);
|
|
||||||
|
|
||||||
for (uint32_t idx = 0; idx < SDARCHIVER_DS_TEST_HASH_MAP_ITER_SIZE; ++idx) {
|
|
||||||
CHECK_TRUE(found[idx] == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECK_TRUE(simple_archiver_hash_map_iter(hash_map, hash_map_iter_check_fn2,
|
|
||||||
found) == 2);
|
|
||||||
|
|
||||||
for (uint32_t idx = 0; idx < SDARCHIVER_DS_TEST_HASH_MAP_ITER_SIZE; ++idx) {
|
|
||||||
CHECK_TRUE(found[idx] == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
simple_archiver_hash_map_free(&hash_map);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test hashing.
|
|
||||||
//{
|
|
||||||
// printf("Distribution of 13 over 33...\n");
|
|
||||||
// unsigned int counts[33];
|
|
||||||
// memset(counts, 0, sizeof(unsigned int) * 33);
|
|
||||||
|
|
||||||
// uint64_t hash;
|
|
||||||
|
|
||||||
// hash = simple_archiver_hash_default_fn("/", 2);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
// hash = simple_archiver_hash_default_fn("/faq", 5);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/faq", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
// hash = simple_archiver_hash_default_fn("/FAQ", 5);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/FAQ", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
// hash = simple_archiver_hash_default_fn("/url", 5);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/url", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
// hash = simple_archiver_hash_default_fn("/home", 6);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/home", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
// hash = simple_archiver_hash_default_fn("/blog", 6);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/blog", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
// hash = simple_archiver_hash_default_fn("/test", 6);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/test", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
// hash = simple_archiver_hash_default_fn("/menu", 6);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/menu", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
// hash = simple_archiver_hash_default_fn("/posts", 7);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/posts", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
// hash = simple_archiver_hash_default_fn("/about", 7);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/about", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
// hash = simple_archiver_hash_default_fn("/media", 7);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/media", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
// hash = simple_archiver_hash_default_fn("/social", 8);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/social", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
// hash = simple_archiver_hash_default_fn("/projects", 10);
|
|
||||||
// printf("%s in bucket %lu (%lu)\n", "/projects", hash % 33, hash);
|
|
||||||
// counts[hash % 33] += 1;
|
|
||||||
|
|
||||||
// for (unsigned int idx = 0; idx < 33; ++idx) {
|
|
||||||
// printf("Bucket %u: %u\n", idx, counts[idx]);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Test PriorityHeap.
|
// Test PriorityHeap.
|
||||||
{
|
{
|
||||||
SDArchiverPHeap *priority_heap = simple_archiver_priority_heap_init();
|
SDArchiverPHeap *priority_heap = simple_archiver_priority_heap_init();
|
||||||
|
@ -280,13 +176,13 @@ int main(void) {
|
||||||
priority_heap = simple_archiver_priority_heap_init();
|
priority_heap = simple_archiver_priority_heap_init();
|
||||||
|
|
||||||
// Just 3 elements.
|
// Just 3 elements.
|
||||||
for (uint32_t idx = 0; idx < 3; ++idx) {
|
for (unsigned int idx = 0; idx < 3; ++idx) {
|
||||||
uint32_t *data = malloc(sizeof(uint32_t));
|
unsigned int *data = malloc(sizeof(unsigned int));
|
||||||
*data = idx;
|
*data = idx;
|
||||||
simple_archiver_priority_heap_insert(priority_heap, idx, data, NULL);
|
simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
|
||||||
}
|
}
|
||||||
for (uint32_t idx = 0; idx < 3; ++idx) {
|
for (unsigned int idx = 0; idx < 3; ++idx) {
|
||||||
uint32_t *data = simple_archiver_priority_heap_top(priority_heap);
|
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
|
||||||
CHECK_TRUE(*data == idx);
|
CHECK_TRUE(*data == idx);
|
||||||
if (*data != idx) {
|
if (*data != idx) {
|
||||||
printf("idx is %u, data is %u\n", idx, *data);
|
printf("idx is %u, data is %u\n", idx, *data);
|
||||||
|
@ -300,16 +196,16 @@ int main(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 100 elements.
|
// 100 elements.
|
||||||
uint32_t max = 100;
|
unsigned int max = 100;
|
||||||
|
|
||||||
for (uint32_t idx = 0; idx < max; ++idx) {
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
uint32_t *data = malloc(sizeof(uint32_t));
|
unsigned int *data = malloc(sizeof(unsigned int));
|
||||||
*data = idx;
|
*data = idx;
|
||||||
simple_archiver_priority_heap_insert(priority_heap, idx, data, NULL);
|
simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t idx = 0; idx < max; ++idx) {
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
uint32_t *data = simple_archiver_priority_heap_top(priority_heap);
|
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
|
||||||
CHECK_TRUE(*data == idx);
|
CHECK_TRUE(*data == idx);
|
||||||
data = simple_archiver_priority_heap_pop(priority_heap);
|
data = simple_archiver_priority_heap_pop(priority_heap);
|
||||||
CHECK_TRUE(*data == idx);
|
CHECK_TRUE(*data == idx);
|
||||||
|
@ -317,14 +213,14 @@ int main(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert in reverse order.
|
// Insert in reverse order.
|
||||||
for (uint32_t idx = max; idx-- > 0;) {
|
for (unsigned int idx = max; idx-- > 0;) {
|
||||||
uint32_t *data = malloc(sizeof(uint32_t));
|
unsigned int *data = malloc(sizeof(unsigned int));
|
||||||
*data = idx;
|
*data = idx;
|
||||||
simple_archiver_priority_heap_insert(priority_heap, idx, data, NULL);
|
simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t idx = 0; idx < max; ++idx) {
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
uint32_t *data = simple_archiver_priority_heap_top(priority_heap);
|
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
|
||||||
CHECK_TRUE(*data == idx);
|
CHECK_TRUE(*data == idx);
|
||||||
data = simple_archiver_priority_heap_pop(priority_heap);
|
data = simple_archiver_priority_heap_pop(priority_heap);
|
||||||
CHECK_TRUE(*data == idx);
|
CHECK_TRUE(*data == idx);
|
||||||
|
@ -332,30 +228,30 @@ int main(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert in random order.
|
// Insert in random order.
|
||||||
uint32_t *array = malloc(sizeof(uint32_t) * max);
|
unsigned int *array = malloc(sizeof(unsigned int) * max);
|
||||||
for (uint32_t idx = 0; idx < max; ++idx) {
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
array[idx] = idx;
|
array[idx] = idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deterministic randomization.
|
// Deterministic randomization.
|
||||||
for (uint32_t idx = max - 1; idx-- > 0;) {
|
for (unsigned int idx = max - 1; idx-- > 0;) {
|
||||||
uint32_t other_idx =
|
unsigned int other_idx = simple_archiver_algo_lcg_defaults(idx) %
|
||||||
simple_archiver_algo_lcg_defaults(idx) % (uint64_t)(idx + 1);
|
(unsigned long long)(idx + 1);
|
||||||
if (max - 1 != other_idx) {
|
if (max - 1 != other_idx) {
|
||||||
uint32_t temp = array[max - 1];
|
unsigned int temp = array[max - 1];
|
||||||
array[max - 1] = array[other_idx];
|
array[max - 1] = array[other_idx];
|
||||||
array[other_idx] = temp;
|
array[other_idx] = temp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the deterministically randomized array.
|
// Insert the deterministically randomized array.
|
||||||
for (uint32_t idx = 0; idx < max; ++idx) {
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
simple_archiver_priority_heap_insert(priority_heap, array[idx],
|
simple_archiver_priority_heap_insert(&priority_heap, array[idx],
|
||||||
array + idx, no_free_fn);
|
array + idx, no_free_fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t idx = 0; idx < max; ++idx) {
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
uint32_t *data = simple_archiver_priority_heap_top(priority_heap);
|
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
|
||||||
CHECK_TRUE(*data == idx);
|
CHECK_TRUE(*data == idx);
|
||||||
if (*data != idx) {
|
if (*data != idx) {
|
||||||
printf("idx is %u, data is %u\n", idx, *data);
|
printf("idx is %u, data is %u\n", idx, *data);
|
||||||
|
@ -372,24 +268,24 @@ int main(void) {
|
||||||
|
|
||||||
// Insert, don't pop, do free, for memcheck.
|
// Insert, don't pop, do free, for memcheck.
|
||||||
priority_heap = simple_archiver_priority_heap_init();
|
priority_heap = simple_archiver_priority_heap_init();
|
||||||
for (uint32_t idx = 0; idx < max; ++idx) {
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
uint32_t *data = malloc(sizeof(uint32_t));
|
unsigned int *data = malloc(sizeof(unsigned int));
|
||||||
*data = idx;
|
*data = idx;
|
||||||
simple_archiver_priority_heap_insert(priority_heap, idx, data, NULL);
|
simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
|
||||||
}
|
}
|
||||||
simple_archiver_priority_heap_free(&priority_heap);
|
simple_archiver_priority_heap_free(&priority_heap);
|
||||||
|
|
||||||
// Reverse priority.
|
// Reverse priority.
|
||||||
priority_heap = simple_archiver_priority_heap_init_less_fn(more_fn);
|
priority_heap = simple_archiver_priority_heap_init_less_fn(more_fn);
|
||||||
|
|
||||||
for (uint32_t idx = 0; idx < max; ++idx) {
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
uint32_t *data = malloc(sizeof(uint32_t));
|
unsigned int *data = malloc(sizeof(unsigned int));
|
||||||
*data = idx;
|
*data = idx;
|
||||||
simple_archiver_priority_heap_insert(priority_heap, idx, data, NULL);
|
simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t idx = max; idx-- > 0;) {
|
for (unsigned int idx = max; idx-- > 0;) {
|
||||||
uint32_t *data = simple_archiver_priority_heap_top(priority_heap);
|
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
|
||||||
CHECK_TRUE(*data == idx);
|
CHECK_TRUE(*data == idx);
|
||||||
data = simple_archiver_priority_heap_pop(priority_heap);
|
data = simple_archiver_priority_heap_pop(priority_heap);
|
||||||
CHECK_TRUE(*data == idx);
|
CHECK_TRUE(*data == idx);
|
||||||
|
@ -400,30 +296,30 @@ int main(void) {
|
||||||
|
|
||||||
// Insert in random order with reverse-priority-heap.
|
// Insert in random order with reverse-priority-heap.
|
||||||
priority_heap = simple_archiver_priority_heap_init_less_fn(more_fn);
|
priority_heap = simple_archiver_priority_heap_init_less_fn(more_fn);
|
||||||
array = malloc(sizeof(uint32_t) * max);
|
array = malloc(sizeof(unsigned int) * max);
|
||||||
for (uint32_t idx = 0; idx < max; ++idx) {
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
array[idx] = idx;
|
array[idx] = idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deterministic randomization.
|
// Deterministic randomization.
|
||||||
for (uint32_t idx = max - 1; idx-- > 0;) {
|
for (unsigned int idx = max - 1; idx-- > 0;) {
|
||||||
uint32_t other_idx =
|
unsigned int other_idx = simple_archiver_algo_lcg_defaults(idx) %
|
||||||
simple_archiver_algo_lcg_defaults(idx) % (uint64_t)(idx + 1);
|
(unsigned long long)(idx + 1);
|
||||||
if (max - 1 != other_idx) {
|
if (max - 1 != other_idx) {
|
||||||
uint32_t temp = array[max - 1];
|
unsigned int temp = array[max - 1];
|
||||||
array[max - 1] = array[other_idx];
|
array[max - 1] = array[other_idx];
|
||||||
array[other_idx] = temp;
|
array[other_idx] = temp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the deterministically randomized array.
|
// Insert the deterministically randomized array.
|
||||||
for (uint32_t idx = 0; idx < max; ++idx) {
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
simple_archiver_priority_heap_insert(priority_heap, array[idx],
|
simple_archiver_priority_heap_insert(&priority_heap, array[idx],
|
||||||
array + idx, no_free_fn);
|
array + idx, no_free_fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t idx = max; idx-- > 0;) {
|
for (unsigned int idx = max; idx-- > 0;) {
|
||||||
uint32_t *data = simple_archiver_priority_heap_top(priority_heap);
|
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
|
||||||
CHECK_TRUE(*data == idx);
|
CHECK_TRUE(*data == idx);
|
||||||
if (*data != idx) {
|
if (*data != idx) {
|
||||||
printf("idx is %u, data is %u\n", idx, *data);
|
printf("idx is %u, data is %u\n", idx, *data);
|
||||||
|
|
265
src/helpers.c
265
src/helpers.c
|
@ -1,257 +1,56 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `helpers.c` is the source for helpful/utility functions.
|
||||||
// `helpers.c` is the source for helpful/utility functions.
|
*/
|
||||||
|
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "platforms.h"
|
|
||||||
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
|
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
|
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <libgen.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void simple_archiver_helper_cleanup_FILE(FILE **fd) {
|
|
||||||
if (fd && *fd) {
|
|
||||||
fclose(*fd);
|
|
||||||
*fd = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simple_archiver_helper_cleanup_malloced(void **data) {
|
|
||||||
if (data && *data) {
|
|
||||||
free(*data);
|
|
||||||
*data = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simple_archiver_helper_cleanup_c_string(char **str) {
|
|
||||||
if (str && *str) {
|
|
||||||
free(*str);
|
|
||||||
*str = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simple_archiver_helper_cleanup_chdir_back(char **original) {
|
|
||||||
if (original && *original) {
|
|
||||||
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX || \
|
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
|
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN
|
|
||||||
__attribute__((unused)) int unused_ret = chdir(*original);
|
|
||||||
#endif
|
|
||||||
free(*original);
|
|
||||||
*original = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simple_archiver_helper_datastructure_cleanup_nop(
|
|
||||||
__attribute__((unused)) void *unused) {}
|
|
||||||
|
|
||||||
int simple_archiver_helper_is_big_endian(void) {
|
int simple_archiver_helper_is_big_endian(void) {
|
||||||
union {
|
union {
|
||||||
uint32_t i;
|
unsigned int i;
|
||||||
char c[4];
|
char c[4];
|
||||||
} bint = {0x01020304};
|
} bint = {0x01020304};
|
||||||
|
|
||||||
return bint.c[0] == 1 ? 1 : 0;
|
return bint.c[0] == 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void simple_archiver_helper_16_bit_be(uint16_t *value) {
|
void simple_archiver_helper_16_bit_be(unsigned short *value) {
|
||||||
if (simple_archiver_helper_is_big_endian() == 0) {
|
if (simple_archiver_helper_is_big_endian() == 0) {
|
||||||
uint8_t c = ((uint8_t *)value)[0];
|
unsigned char c = ((unsigned char *)value)[0];
|
||||||
((uint8_t *)value)[0] = ((uint8_t *)value)[1];
|
((unsigned char *)value)[0] = ((unsigned char *)value)[1];
|
||||||
((uint8_t *)value)[1] = c;
|
((unsigned char *)value)[1] = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void simple_archiver_helper_32_bit_be(uint32_t *value) {
|
void simple_archiver_helper_32_bit_be(unsigned int *value) {
|
||||||
if (simple_archiver_helper_is_big_endian() == 0) {
|
if (simple_archiver_helper_is_big_endian() == 0) {
|
||||||
for (uint32_t i = 0; i < 2; ++i) {
|
for (unsigned int i = 0; i < 2; ++i) {
|
||||||
uint8_t c = ((uint8_t *)value)[i];
|
unsigned char c = ((unsigned char *)value)[i];
|
||||||
((uint8_t *)value)[i] = ((uint8_t *)value)[3 - i];
|
((unsigned char *)value)[i] = ((unsigned char *)value)[3 - i];
|
||||||
((uint8_t *)value)[3 - i] = c;
|
((unsigned char *)value)[3 - i] = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void simple_archiver_helper_64_bit_be(uint64_t *value) {
|
void simple_archiver_helper_64_bit_be(unsigned long long *value) {
|
||||||
if (simple_archiver_helper_is_big_endian() == 0) {
|
if (simple_archiver_helper_is_big_endian() == 0) {
|
||||||
for (uint32_t i = 0; i < 4; ++i) {
|
for (unsigned int i = 0; i < 4; ++i) {
|
||||||
uint8_t c = ((uint8_t *)value)[i];
|
unsigned char c = ((unsigned char *)value)[i];
|
||||||
((uint8_t *)value)[i] = ((uint8_t *)value)[7 - i];
|
((unsigned char *)value)[i] = ((unsigned char *)value)[7 - i];
|
||||||
((uint8_t *)value)[7 - i] = c;
|
((unsigned char *)value)[7 - i] = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char **simple_archiver_helper_cmd_string_to_argv(const char *cmd) {
|
|
||||||
uint32_t capacity = 16;
|
|
||||||
uint32_t idx = 0;
|
|
||||||
// Size of every pointer is the same, so using size of (void*) should be ok.
|
|
||||||
char **args = malloc(sizeof(void *) * capacity);
|
|
||||||
memset(args, 0, sizeof(void *) * capacity);
|
|
||||||
|
|
||||||
uint32_t word_capacity = 16;
|
|
||||||
uint32_t word_idx = 0;
|
|
||||||
char *word = malloc(word_capacity);
|
|
||||||
memset(word, 0, word_capacity);
|
|
||||||
for (const char *c = cmd; *c != 0; ++c) {
|
|
||||||
if (isspace(*c)) {
|
|
||||||
if (word_idx > 0) {
|
|
||||||
if (idx >= capacity) {
|
|
||||||
capacity *= 2;
|
|
||||||
args = realloc(args, sizeof(void *) * capacity);
|
|
||||||
}
|
|
||||||
args[idx] = malloc(word_idx + 1);
|
|
||||||
memcpy(args[idx], word, word_idx);
|
|
||||||
args[idx][word_idx] = 0;
|
|
||||||
++idx;
|
|
||||||
word_idx = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (word_idx >= word_capacity) {
|
|
||||||
word_capacity *= 2;
|
|
||||||
word = realloc(word, word_capacity);
|
|
||||||
}
|
|
||||||
word[word_idx++] = *c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (word_idx > 0) {
|
|
||||||
if (idx >= capacity) {
|
|
||||||
capacity *= 2;
|
|
||||||
args = realloc(args, sizeof(void *) * capacity);
|
|
||||||
}
|
|
||||||
args[idx] = malloc(word_idx + 1);
|
|
||||||
memcpy(args[idx], word, word_idx);
|
|
||||||
args[idx][word_idx] = 0;
|
|
||||||
++idx;
|
|
||||||
word_idx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(word);
|
|
||||||
|
|
||||||
if (idx >= capacity) {
|
|
||||||
args = realloc(args, sizeof(void *) * (capacity + 1));
|
|
||||||
args[capacity] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
void simple_archiver_helper_cmd_string_argv_free(char **argv_strs) {
|
|
||||||
if (argv_strs) {
|
|
||||||
for (char **iter = argv_strs; *iter != 0; ++iter) {
|
|
||||||
free(*iter);
|
|
||||||
}
|
|
||||||
free(argv_strs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simple_archiver_helper_cmd_string_argv_free_ptr(char ***argv_strs) {
|
|
||||||
if (argv_strs) {
|
|
||||||
simple_archiver_helper_cmd_string_argv_free(*argv_strs);
|
|
||||||
*argv_strs = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int simple_archiver_helper_make_dirs(const char *file_path) {
|
|
||||||
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
|
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
|
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
|
|
||||||
__attribute__((
|
|
||||||
cleanup(simple_archiver_helper_cleanup_c_string))) char *path_dup =
|
|
||||||
strdup(file_path);
|
|
||||||
if (!path_dup) {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
const char *dir = dirname(path_dup);
|
|
||||||
if (strcmp(dir, "/") == 0 || strcmp(dir, ".") == 0) {
|
|
||||||
// At root.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dir_fd = open(dir, O_RDONLY | O_DIRECTORY);
|
|
||||||
if (dir_fd == -1) {
|
|
||||||
if (errno == ENOTDIR) {
|
|
||||||
// Error, somehow got non-dir in path.
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
// Directory does not exist. Check parent dir first.
|
|
||||||
int ret = simple_archiver_helper_make_dirs(dir);
|
|
||||||
if (ret != 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
// Now make dir.
|
|
||||||
ret = mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
|
||||||
if (ret != 0) {
|
|
||||||
// Error.
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Exists.
|
|
||||||
close(dir_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
return 1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
char *simple_archiver_helper_cut_substr(const char *s, size_t start_idx,
|
|
||||||
size_t end_idx) {
|
|
||||||
size_t s_len = strlen(s);
|
|
||||||
if (start_idx > end_idx || start_idx >= s_len || end_idx > s_len) {
|
|
||||||
return NULL;
|
|
||||||
} else if (end_idx == s_len) {
|
|
||||||
if (start_idx == 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
char *new_s = malloc(start_idx + 1);
|
|
||||||
strncpy(new_s, s, start_idx + 1);
|
|
||||||
new_s[start_idx] = 0;
|
|
||||||
return new_s;
|
|
||||||
} else if (start_idx == 0) {
|
|
||||||
char *new_s = malloc(s_len - end_idx + 1);
|
|
||||||
strncpy(new_s, s + end_idx, s_len - end_idx + 1);
|
|
||||||
return new_s;
|
|
||||||
} else {
|
|
||||||
char *new_s = malloc(start_idx + s_len - end_idx + 1);
|
|
||||||
strncpy(new_s, s, start_idx);
|
|
||||||
strncpy(new_s + start_idx, s + end_idx, s_len - end_idx + 1);
|
|
||||||
return new_s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t simple_archiver_helper_num_digits(size_t value) {
|
|
||||||
size_t digits = 0;
|
|
||||||
do {
|
|
||||||
++digits;
|
|
||||||
value /= 10;
|
|
||||||
} while (value != 0);
|
|
||||||
|
|
||||||
return digits;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,66 +1,36 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `helpers.h` is the header for helpful/utility functions.
|
||||||
// `helpers.h` is the header for helpful/utility functions.
|
*/
|
||||||
|
|
||||||
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_HELPERS_H_
|
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_HELPERS_H_
|
||||||
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_HELPERS_H_
|
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_HELPERS_H_
|
||||||
|
|
||||||
// Standard library includes.
|
static const unsigned int MAX_SYMBOLIC_LINK_SIZE = 512;
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
static const uint32_t MAX_SYMBOLIC_LINK_SIZE = 512;
|
|
||||||
|
|
||||||
/// Returns non-zero if this system is big-endian.
|
/// Returns non-zero if this system is big-endian.
|
||||||
int simple_archiver_helper_is_big_endian(void);
|
int simple_archiver_helper_is_big_endian(void);
|
||||||
|
|
||||||
/// Swaps value from/to big-endian. Nop on big-endian systems.
|
/// Swaps value from/to big-endian. Nop on big-endian systems.
|
||||||
void simple_archiver_helper_16_bit_be(uint16_t *value);
|
void simple_archiver_helper_16_bit_be(unsigned short *value);
|
||||||
|
|
||||||
/// Swaps value from/to big-endian. Nop on big-endian systems.
|
/// Swaps value from/to big-endian. Nop on big-endian systems.
|
||||||
void simple_archiver_helper_32_bit_be(uint32_t *value);
|
void simple_archiver_helper_32_bit_be(unsigned int *value);
|
||||||
|
|
||||||
/// Swaps value from/to big-endian. Nop on big-endian systems.
|
/// Swaps value from/to big-endian. Nop on big-endian systems.
|
||||||
void simple_archiver_helper_64_bit_be(uint64_t *value);
|
void simple_archiver_helper_64_bit_be(unsigned long long *value);
|
||||||
|
|
||||||
/// Returns a array of c-strings on success, NULL on error.
|
|
||||||
/// The returned array must be free'd with
|
|
||||||
/// simple_archiver_helper_cmd_string_argv_free(...).
|
|
||||||
char **simple_archiver_helper_cmd_string_to_argv(const char *cmd);
|
|
||||||
|
|
||||||
void simple_archiver_helper_cmd_string_argv_free(char **argv_strs);
|
|
||||||
void simple_archiver_helper_cmd_string_argv_free_ptr(char ***argv_strs);
|
|
||||||
|
|
||||||
/// Returns zero on success.
|
|
||||||
int simple_archiver_helper_make_dirs(const char *file_path);
|
|
||||||
|
|
||||||
/// Returns non-NULL on success.
|
|
||||||
/// Must be free'd with "free()" if non-NULL.
|
|
||||||
/// start_idx is inclusive and end_idx is exclusive.
|
|
||||||
char *simple_archiver_helper_cut_substr(const char *s, size_t start_idx,
|
|
||||||
size_t end_idx);
|
|
||||||
|
|
||||||
size_t simple_archiver_helper_num_digits(size_t value);
|
|
||||||
|
|
||||||
void simple_archiver_helper_cleanup_FILE(FILE **fd);
|
|
||||||
void simple_archiver_helper_cleanup_malloced(void **data);
|
|
||||||
void simple_archiver_helper_cleanup_c_string(char **str);
|
|
||||||
void simple_archiver_helper_cleanup_chdir_back(char **original);
|
|
||||||
|
|
||||||
void simple_archiver_helper_datastructure_cleanup_nop(void *unused);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
185
src/main.c
185
src/main.c
|
@ -1,181 +1,50 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `main.c` is the entry-point of this software/program.
|
||||||
// `main.c` is the entry-point of this software/program.
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "platforms.h"
|
|
||||||
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
|
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
|
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "archiver.h"
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
int print_list_fn(void *data, __attribute__((unused)) void *ud) {
|
int print_list_fn(void *data, __attribute__((unused)) void *ud) {
|
||||||
const SDArchiverFileInfo *file_info = data;
|
const SDArchiverFileInfo *file_info = data;
|
||||||
if (file_info->link_dest == NULL) {
|
if (file_info->link_dest == NULL) {
|
||||||
fprintf(stderr, " REGULAR FILE: %s\n", file_info->filename);
|
printf(" REGULAR FILE: %s\n", file_info->filename);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, " SYMBOLIC LINK: %s -> %s\n", file_info->filename,
|
printf(" SYMBOLIC LINK: %s -> %s\n", file_info->filename,
|
||||||
file_info->link_dest);
|
file_info->link_dest);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char **argv) {
|
int main(int argc, const char **argv) {
|
||||||
__attribute__((
|
simple_archiver_print_usage();
|
||||||
cleanup(simple_archiver_free_parsed))) SDArchiverParsed parsed =
|
|
||||||
simple_archiver_create_parsed();
|
|
||||||
|
|
||||||
if (simple_archiver_parse_args(argc, argv, &parsed)) {
|
__attribute__((cleanup(simple_archiver_free_parsed)))
|
||||||
fprintf(stderr, "Failed to parse args.\n");
|
SDArchiverParsed parsed = simple_archiver_create_parsed();
|
||||||
return 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parsed.filename && (parsed.flags & 0x10) == 0) {
|
simple_archiver_parse_args(argc, argv, &parsed);
|
||||||
fprintf(stderr, "ERROR: Filename not specified!\n");
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((parsed.flags & 0x3) == 0 && (parsed.flags & 0x4) == 0) {
|
|
||||||
FILE *file = fopen(parsed.filename, "r");
|
|
||||||
if (file != NULL) {
|
|
||||||
fclose(file);
|
|
||||||
fprintf(
|
|
||||||
stderr,
|
|
||||||
"ERROR: Archive file exists but --overwrite-create not specified!\n");
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SDArchiverParsedStatus parsed_status;
|
|
||||||
__attribute__((cleanup(simple_archiver_list_free)))
|
__attribute__((cleanup(simple_archiver_list_free)))
|
||||||
SDArchiverLinkedList *filenames =
|
SDArchiverLinkedList *filenames =
|
||||||
simple_archiver_parsed_to_filenames(&parsed, &parsed_status);
|
simple_archiver_parsed_to_filenames(&parsed);
|
||||||
if (!filenames || parsed_status != SDAPS_SUCCESS) {
|
|
||||||
fprintf(stderr, "ERROR: %s!\n",
|
|
||||||
simple_archiver_parsed_status_to_str(parsed_status));
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filenames->count > 0) {
|
puts("Filenames:");
|
||||||
fprintf(stderr, "Filenames:\n");
|
simple_archiver_list_get(filenames, print_list_fn, NULL);
|
||||||
simple_archiver_list_get(filenames, print_list_fn, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((parsed.flags & 3) == 0) {
|
|
||||||
// Is creating archive.
|
|
||||||
__attribute__((cleanup(simple_archiver_free_state)))
|
|
||||||
SDArchiverState *state = simple_archiver_init_state(&parsed);
|
|
||||||
if ((parsed.flags & 0x10) == 0) {
|
|
||||||
FILE *file = fopen(parsed.filename, "wb");
|
|
||||||
if (!file) {
|
|
||||||
fprintf(stderr, "ERROR: Failed to open \"%s\" for writing!\n",
|
|
||||||
parsed.filename);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = simple_archiver_write_all(file, state, filenames);
|
|
||||||
if (ret != SDAS_SUCCESS) {
|
|
||||||
fprintf(stderr, "Error during writing.\n");
|
|
||||||
char *error_str =
|
|
||||||
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
|
|
||||||
fprintf(stderr, " %s\n", error_str);
|
|
||||||
}
|
|
||||||
fclose(file);
|
|
||||||
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
|
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
|
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
|
|
||||||
if (ret != SDAS_SUCCESS) {
|
|
||||||
unlink(parsed.filename);
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
int ret = simple_archiver_write_all(stdout, state, filenames);
|
|
||||||
if (ret != SDAS_SUCCESS) {
|
|
||||||
fprintf(stderr, "Error during writing.\n");
|
|
||||||
char *error_str =
|
|
||||||
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
|
|
||||||
fprintf(stderr, " %s\n", error_str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ((parsed.flags & 3) == 2) {
|
|
||||||
// Is checking archive.
|
|
||||||
if ((parsed.flags & 0x10) == 0) {
|
|
||||||
FILE *file = fopen(parsed.filename, "rb");
|
|
||||||
if (!file) {
|
|
||||||
fprintf(stderr, "ERROR: Failed to open \"%s\" for reading!\n",
|
|
||||||
parsed.filename);
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = simple_archiver_parse_archive_info(file, 0, NULL);
|
|
||||||
if (ret != 0) {
|
|
||||||
fprintf(stderr, "Error during archive checking/examining.\n");
|
|
||||||
char *error_str =
|
|
||||||
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
|
|
||||||
fprintf(stderr, " %s\n", error_str);
|
|
||||||
}
|
|
||||||
fclose(file);
|
|
||||||
} else {
|
|
||||||
int ret = simple_archiver_parse_archive_info(stdin, 0, NULL);
|
|
||||||
if (ret != 0) {
|
|
||||||
fprintf(stderr, "Error during archive checking/examining.\n");
|
|
||||||
char *error_str =
|
|
||||||
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
|
|
||||||
fprintf(stderr, " %s\n", error_str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ((parsed.flags & 3) == 1) {
|
|
||||||
// Is extracting archive.
|
|
||||||
__attribute__((cleanup(simple_archiver_free_state)))
|
|
||||||
SDArchiverState *state = simple_archiver_init_state(&parsed);
|
|
||||||
if ((parsed.flags & 0x10) == 0) {
|
|
||||||
FILE *file = fopen(parsed.filename, "rb");
|
|
||||||
if (!file) {
|
|
||||||
fprintf(stderr, "ERROR: Failed to open \"%s\" for reading!\n",
|
|
||||||
parsed.filename);
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = simple_archiver_parse_archive_info(file, 1, state);
|
|
||||||
if (ret != SDAS_SUCCESS) {
|
|
||||||
fprintf(stderr, "Error during archive extracting.\n");
|
|
||||||
char *error_str =
|
|
||||||
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
|
|
||||||
fprintf(stderr, " %s\n", error_str);
|
|
||||||
}
|
|
||||||
fclose(file);
|
|
||||||
} else {
|
|
||||||
int ret = simple_archiver_parse_archive_info(stdin, 1, state);
|
|
||||||
if (ret != SDAS_SUCCESS) {
|
|
||||||
fprintf(stderr, "Error during archive extracting.\n");
|
|
||||||
char *error_str =
|
|
||||||
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
|
|
||||||
fprintf(stderr, " %s\n", error_str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
517
src/parser.c
517
src/parser.c
|
@ -1,20 +1,20 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `parser.c` is the source file for parsing args.
|
||||||
// `parser.c` is the source file for parsing args.
|
*/
|
||||||
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
|
@ -31,6 +31,12 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#elif SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_WINDOWS
|
||||||
|
#include <fileapi.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winioctl.h>
|
||||||
|
#include <winnt.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "data_structures/hash_map.h"
|
#include "data_structures/hash_map.h"
|
||||||
|
@ -39,15 +45,15 @@
|
||||||
#include "parser_internal.h"
|
#include "parser_internal.h"
|
||||||
|
|
||||||
/// Gets the first non "./"-like character in the filename.
|
/// Gets the first non "./"-like character in the filename.
|
||||||
size_t simple_archiver_parser_internal_get_first_non_current_idx(
|
unsigned int simple_archiver_parser_internal_filename_idx(
|
||||||
const char *filename) {
|
const char *filename) {
|
||||||
size_t idx = 0;
|
unsigned int idx = 0;
|
||||||
size_t known_good_idx = 0;
|
unsigned int known_good_idx = 0;
|
||||||
const size_t length = strlen(filename);
|
const unsigned int length = strlen(filename);
|
||||||
|
|
||||||
// 0b0001 - checked that idx char is '.'
|
// 0b0001 - checked that idx char is '.'
|
||||||
// 0b0010 - checked that idx char is '/'
|
// 0b0010 - checked that idx char is '/'
|
||||||
size_t flags = 0;
|
unsigned int flags = 0;
|
||||||
|
|
||||||
for (; idx < length; ++idx) {
|
for (; idx < length; ++idx) {
|
||||||
if ((flags & 3) == 0) {
|
if ((flags & 3) == 0) {
|
||||||
|
@ -61,7 +67,11 @@ size_t simple_archiver_parser_internal_get_first_non_current_idx(
|
||||||
} else if ((flags & 3) == 1) {
|
} else if ((flags & 3) == 1) {
|
||||||
if (filename[idx] == 0) {
|
if (filename[idx] == 0) {
|
||||||
return known_good_idx;
|
return known_good_idx;
|
||||||
|
#if SIMPLE_ARCHIVER_PLATFORM != SIMPLE_ARCHIVER_PLATFORM_WINDOWS
|
||||||
} else if (filename[idx] == '/') {
|
} else if (filename[idx] == '/') {
|
||||||
|
#else
|
||||||
|
} else if (filename[idx] == '\\') {
|
||||||
|
#endif
|
||||||
flags |= 2;
|
flags |= 2;
|
||||||
} else {
|
} else {
|
||||||
return idx - 1;
|
return idx - 1;
|
||||||
|
@ -69,7 +79,11 @@ size_t simple_archiver_parser_internal_get_first_non_current_idx(
|
||||||
} else if ((flags & 3) == 3) {
|
} else if ((flags & 3) == 3) {
|
||||||
if (filename[idx] == 0) {
|
if (filename[idx] == 0) {
|
||||||
return known_good_idx;
|
return known_good_idx;
|
||||||
|
#if SIMPLE_ARCHIVER_PLATFORM != SIMPLE_ARCHIVER_PLATFORM_WINDOWS
|
||||||
} else if (filename[idx] == '/') {
|
} else if (filename[idx] == '/') {
|
||||||
|
#else
|
||||||
|
} else if (filename[idx] == '\\') {
|
||||||
|
#endif
|
||||||
continue;
|
continue;
|
||||||
} else if (filename[idx] == '.') {
|
} else if (filename[idx] == '.') {
|
||||||
flags &= 0xFFFFFFFC;
|
flags &= 0xFFFFFFFC;
|
||||||
|
@ -90,8 +104,8 @@ size_t simple_archiver_parser_internal_get_first_non_current_idx(
|
||||||
}
|
}
|
||||||
|
|
||||||
void simple_archiver_parser_internal_remove_end_slash(char *filename) {
|
void simple_archiver_parser_internal_remove_end_slash(char *filename) {
|
||||||
size_t len = strlen(filename);
|
int len = strlen(filename);
|
||||||
size_t idx;
|
int idx;
|
||||||
for (idx = len; idx-- > 0;) {
|
for (idx = len; idx-- > 0;) {
|
||||||
if (filename[idx] != '/') {
|
if (filename[idx] != '/') {
|
||||||
++idx;
|
++idx;
|
||||||
|
@ -103,6 +117,34 @@ void simple_archiver_parser_internal_remove_end_slash(char *filename) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *simple_archiver_internal_forward_to_backward_slash(const char *string) {
|
||||||
|
unsigned int len = strlen(string) + 1;
|
||||||
|
char *backward_slash_string = malloc(len);
|
||||||
|
strncpy(backward_slash_string, string, len);
|
||||||
|
|
||||||
|
for (unsigned int idx = 0; idx < len; ++idx) {
|
||||||
|
if (backward_slash_string[idx] == '/') {
|
||||||
|
backward_slash_string[idx] = '\\';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return backward_slash_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *simple_archiver_internal_backward_to_forward_slash(const char *string) {
|
||||||
|
unsigned int len = strlen(string) + 1;
|
||||||
|
char *forward_slash_string = malloc(len);
|
||||||
|
strncpy(forward_slash_string, string, len);
|
||||||
|
|
||||||
|
for (unsigned int idx = 0; idx < len; ++idx) {
|
||||||
|
if (forward_slash_string[idx] == '\\') {
|
||||||
|
forward_slash_string[idx] = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return forward_slash_string;
|
||||||
|
}
|
||||||
|
|
||||||
void simple_archiver_internal_free_file_info_fn(void *data) {
|
void simple_archiver_internal_free_file_info_fn(void *data) {
|
||||||
SDArchiverFileInfo *file_info = data;
|
SDArchiverFileInfo *file_info = data;
|
||||||
if (file_info) {
|
if (file_info) {
|
||||||
|
@ -123,6 +165,8 @@ int list_get_last_fn(void *data, void *ud) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void container_no_free_fn(__attribute__((unused)) void *data) { return; }
|
||||||
|
|
||||||
int list_remove_same_str_fn(void *data, void *ud) {
|
int list_remove_same_str_fn(void *data, void *ud) {
|
||||||
if (strcmp((char *)data, (char *)ud) == 0) {
|
if (strcmp((char *)data, (char *)ud) == 0) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -131,86 +175,26 @@ int list_remove_same_str_fn(void *data, void *ud) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *simple_archiver_parsed_status_to_str(SDArchiverParsedStatus status) {
|
|
||||||
switch (status) {
|
|
||||||
case SDAPS_SUCCESS:
|
|
||||||
return "Success";
|
|
||||||
case SDAPS_NO_USER_CWD:
|
|
||||||
return "No user current working directory (-C <dir>)";
|
|
||||||
default:
|
|
||||||
return "Unknown error";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simple_archiver_print_usage(void) {
|
void simple_archiver_print_usage(void) {
|
||||||
fprintf(stderr, "Usage flags:\n");
|
puts("Usage flags:");
|
||||||
fprintf(stderr, "-c : create archive file\n");
|
puts("-c : create archive file");
|
||||||
fprintf(stderr, "-x : extract archive file\n");
|
puts("-x : extract archive file");
|
||||||
fprintf(stderr, "-t : examine archive file\n");
|
puts("-f <filename> : filename to work on");
|
||||||
fprintf(stderr, "-f <filename> : filename to work on\n");
|
puts("--compressor <full_compress_cmd> : requires --decompressor");
|
||||||
fprintf(stderr,
|
puts("--decompressor <full_decompress_cmd> : requires --compressor");
|
||||||
" Use \"-f -\" to work on stdout when creating archive or stdin "
|
puts("-- : specifies remaining arguments are files to archive/extract");
|
||||||
"when reading archive\n");
|
puts("If creating archive file, remaining args specify files to archive.");
|
||||||
fprintf(stderr, " NOTICE: \"-f\" is not affected by \"-C\"!\n");
|
puts("If extracting archive file, remaining args specify files to extract.");
|
||||||
fprintf(stderr,
|
|
||||||
"-C <dir> : Change current working directory before "
|
|
||||||
"archiving/extracting\n");
|
|
||||||
fprintf(stderr,
|
|
||||||
"--compressor <full_compress_cmd> : requires --decompressor and cmd "
|
|
||||||
"must use stdin/stdout\n");
|
|
||||||
fprintf(stderr,
|
|
||||||
"--decompressor <full_decompress_cmd> : requires --compressor and "
|
|
||||||
"cmd must use stdin/stdout\n");
|
|
||||||
fprintf(stderr,
|
|
||||||
" Specifying \"--decompressor\" when extracting overrides archive "
|
|
||||||
"file's stored decompressor cmd\n");
|
|
||||||
fprintf(stderr, "--overwrite-create : allows overwriting an archive file\n");
|
|
||||||
fprintf(stderr, "--overwrite-extract : allows overwriting when extracting\n");
|
|
||||||
fprintf(stderr,
|
|
||||||
"--no-abs-symlink : do not store absolute paths for symlinks\n");
|
|
||||||
fprintf(
|
|
||||||
stderr,
|
|
||||||
"--preserve-symlinks : preserve the symlink's path on archive creation "
|
|
||||||
"instead of deriving abs/relative paths, ignores \"--no-abs-symlink\" "
|
|
||||||
"(It is not recommended to use this option, as absolute-path-symlinks "
|
|
||||||
"may be clobbered on extraction)\n");
|
|
||||||
fprintf(stderr,
|
|
||||||
"--no-safe-links : keep symlinks that link to outside archive "
|
|
||||||
"contents\n");
|
|
||||||
fprintf(stderr,
|
|
||||||
"--temp-files-dir <dir> : where to store temporary files created "
|
|
||||||
"when compressing (defaults to current working directory)\n");
|
|
||||||
fprintf(stderr,
|
|
||||||
"--write-version <version> : Force write version file format "
|
|
||||||
"(default 1)\n");
|
|
||||||
fprintf(stderr,
|
|
||||||
"--chunk-min-size <bytes> : v1 file format minimum chunk size "
|
|
||||||
"(default 4194304 or 4MiB)\n");
|
|
||||||
fprintf(stderr,
|
|
||||||
"--no-pre-sort-files : do NOT pre-sort files by size (by default "
|
|
||||||
"enabled so that the first file is the largest)\n");
|
|
||||||
fprintf(stderr,
|
|
||||||
"-- : specifies remaining arguments are files to archive/extract\n");
|
|
||||||
fprintf(
|
|
||||||
stderr,
|
|
||||||
"If creating archive file, remaining args specify files to archive.\n");
|
|
||||||
fprintf(
|
|
||||||
stderr,
|
|
||||||
"If extracting archive file, remaining args specify files to extract.\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SDArchiverParsed simple_archiver_create_parsed(void) {
|
SDArchiverParsed simple_archiver_create_parsed(void) {
|
||||||
SDArchiverParsed parsed;
|
SDArchiverParsed parsed;
|
||||||
|
|
||||||
parsed.flags = 0x40;
|
parsed.flags = 0;
|
||||||
parsed.filename = NULL;
|
parsed.filename = NULL;
|
||||||
parsed.compressor = NULL;
|
parsed.compressor = NULL;
|
||||||
parsed.decompressor = NULL;
|
parsed.decompressor = NULL;
|
||||||
parsed.working_files = NULL;
|
parsed.working_files = NULL;
|
||||||
parsed.temp_dir = NULL;
|
|
||||||
parsed.user_cwd = NULL;
|
|
||||||
parsed.write_version = 1;
|
|
||||||
parsed.minimum_chunk_size = 4194304;
|
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
@ -234,159 +218,52 @@ int simple_archiver_parse_args(int argc, const char **argv,
|
||||||
--argc;
|
--argc;
|
||||||
++argv;
|
++argv;
|
||||||
|
|
||||||
int_fast8_t is_remaining_args = 0;
|
int is_remaining_args = 0;
|
||||||
|
|
||||||
while (argc > 0) {
|
while (argc > 0) {
|
||||||
if (!is_remaining_args) {
|
if (!is_remaining_args) {
|
||||||
if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0) {
|
if (strcmp(argv[0], "-c") == 0) {
|
||||||
simple_archiver_free_parsed(out);
|
// unset first bit.
|
||||||
simple_archiver_print_usage();
|
out->flags &= 0xFFFFFFFE;
|
||||||
exit(0);
|
|
||||||
} else if (strcmp(argv[0], "-c") == 0) {
|
|
||||||
// unset first two bits.
|
|
||||||
out->flags &= 0xFFFFFFFC;
|
|
||||||
} else if (strcmp(argv[0], "-x") == 0) {
|
} else if (strcmp(argv[0], "-x") == 0) {
|
||||||
// unset first two bits.
|
|
||||||
out->flags &= 0xFFFFFFFC;
|
|
||||||
// set first bit.
|
// set first bit.
|
||||||
out->flags |= 0x1;
|
out->flags |= 0x1;
|
||||||
} else if (strcmp(argv[0], "-t") == 0) {
|
} else if (strcmp(argv[0], "-f") == 0 && argc > 1) {
|
||||||
// unset first two bits.
|
int size = strlen(argv[1]) + 1;
|
||||||
out->flags &= 0xFFFFFFFC;
|
out->filename = malloc(size);
|
||||||
// set second bit.
|
strncpy(out->filename, argv[1], size);
|
||||||
out->flags |= 0x2;
|
|
||||||
} else if (strcmp(argv[0], "-f") == 0) {
|
|
||||||
if (argc < 2) {
|
|
||||||
fprintf(stderr, "ERROR: -f specified but missing argument!\n");
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (strcmp(argv[1], "-") == 0) {
|
|
||||||
out->flags |= 0x10;
|
|
||||||
if (out->filename) {
|
|
||||||
free(out->filename);
|
|
||||||
}
|
|
||||||
out->filename = NULL;
|
|
||||||
} else {
|
|
||||||
out->flags &= 0xFFFFFFEF;
|
|
||||||
size_t size = strlen(argv[1]) + 1;
|
|
||||||
out->filename = malloc(size);
|
|
||||||
strncpy(out->filename, argv[1], size);
|
|
||||||
}
|
|
||||||
--argc;
|
--argc;
|
||||||
++argv;
|
++argv;
|
||||||
} else if (strcmp(argv[0], "-C") == 0) {
|
} else if (strcmp(argv[0], "--compressor") == 0 && argc > 1) {
|
||||||
if (argc < 2) {
|
int size = strlen(argv[1]) + 1;
|
||||||
fprintf(stderr, "ERROR: -C specified but missing argument!\n");
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
out->user_cwd = argv[1];
|
|
||||||
--argc;
|
|
||||||
++argv;
|
|
||||||
} else if (strcmp(argv[0], "--compressor") == 0) {
|
|
||||||
if (argc < 2) {
|
|
||||||
fprintf(stderr, "--compressor specfied but missing argument!\n");
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
size_t size = strlen(argv[1]) + 1;
|
|
||||||
out->compressor = malloc(size);
|
out->compressor = malloc(size);
|
||||||
strncpy(out->compressor, argv[1], size);
|
strncpy(out->compressor, argv[1], size);
|
||||||
--argc;
|
--argc;
|
||||||
++argv;
|
++argv;
|
||||||
} else if (strcmp(argv[0], "--decompressor") == 0) {
|
} else if (strcmp(argv[0], "--decompressor") == 0 && argc > 1) {
|
||||||
if (argc < 2) {
|
int size = strlen(argv[1]) + 1;
|
||||||
fprintf(stderr, "--decompressor specfied but missing argument!\n");
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
size_t size = strlen(argv[1]) + 1;
|
|
||||||
out->decompressor = malloc(size);
|
out->decompressor = malloc(size);
|
||||||
strncpy(out->decompressor, argv[1], size);
|
strncpy(out->decompressor, argv[1], size);
|
||||||
--argc;
|
--argc;
|
||||||
++argv;
|
++argv;
|
||||||
} else if (strcmp(argv[0], "--overwrite-create") == 0) {
|
|
||||||
out->flags |= 0x4;
|
|
||||||
} else if (strcmp(argv[0], "--overwrite-extract") == 0) {
|
|
||||||
out->flags |= 0x8;
|
|
||||||
} else if (strcmp(argv[0], "--no-abs-symlink") == 0) {
|
|
||||||
out->flags |= 0x20;
|
|
||||||
} else if (strcmp(argv[0], "--preserve-symlinks") == 0) {
|
|
||||||
out->flags |= 0x100;
|
|
||||||
} else if (strcmp(argv[0], "--no-safe-links") == 0) {
|
|
||||||
out->flags |= 0x80;
|
|
||||||
fprintf(stderr,
|
|
||||||
"NOTICE: Disabling safe-links, symlinks that point to outside "
|
|
||||||
"archived files will be preserved!\n");
|
|
||||||
} else if (strcmp(argv[0], "--temp-files-dir") == 0) {
|
|
||||||
if (argc < 2) {
|
|
||||||
fprintf(stderr, "ERROR: --temp-files-dir is missing an argument!\n");
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
out->temp_dir = argv[1];
|
|
||||||
--argc;
|
|
||||||
++argv;
|
|
||||||
} else if (strcmp(argv[0], "--write-version") == 0) {
|
|
||||||
if (argc < 2) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: --write-version expects an integer argument!\n");
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int version = atoi(argv[1]);
|
|
||||||
if (version < 0) {
|
|
||||||
fprintf(stderr, "ERROR: --write-version cannot be negative!\n");
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 1;
|
|
||||||
} else if (version > 1) {
|
|
||||||
fprintf(stderr, "ERROR: --write-version must be 0 or 1!\n");
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
out->write_version = (uint32_t)version;
|
|
||||||
--argc;
|
|
||||||
++argv;
|
|
||||||
} else if (strcmp(argv[0], "--chunk-min-size") == 0) {
|
|
||||||
if (argc < 2) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"ERROR: --chunk-min-size expects an integer argument!\n");
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
out->minimum_chunk_size = strtoull(argv[1], NULL, 10);
|
|
||||||
if (out->minimum_chunk_size == 0) {
|
|
||||||
fprintf(stderr, "ERROR: --chunk-min-size cannot be zero!\n");
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
--argc;
|
|
||||||
++argv;
|
|
||||||
} else if (strcmp(argv[0], "--no-pre-sort-files") == 0) {
|
|
||||||
out->flags &= 0xFFFFFFBF;
|
|
||||||
} else if (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) {
|
} else if (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) {
|
||||||
is_remaining_args = 1;
|
is_remaining_args = 1;
|
||||||
} else if (argv[0][0] != '-') {
|
} else if (argv[0][0] != '-') {
|
||||||
is_remaining_args = 1;
|
is_remaining_args = 1;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
|
||||||
fprintf(stderr, "ERROR: Got invalid arg \"%s\"!\n", argv[0]);
|
|
||||||
simple_archiver_print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (out->working_files == NULL) {
|
if (out->working_files == NULL) {
|
||||||
out->working_files = malloc(sizeof(char *) * 2);
|
out->working_files = malloc(sizeof(char *) * 2);
|
||||||
size_t arg_idx =
|
unsigned int arg_idx =
|
||||||
simple_archiver_parser_internal_get_first_non_current_idx(argv[0]);
|
simple_archiver_parser_internal_filename_idx(argv[0]);
|
||||||
size_t arg_length = strlen(argv[0] + arg_idx) + 1;
|
int arg_length = strlen(argv[0] + arg_idx) + 1;
|
||||||
out->working_files[0] = malloc(arg_length);
|
out->working_files[0] = malloc(arg_length);
|
||||||
strncpy(out->working_files[0], argv[0] + arg_idx, arg_length);
|
strncpy(out->working_files[0], argv[0] + arg_idx, arg_length);
|
||||||
simple_archiver_parser_internal_remove_end_slash(out->working_files[0]);
|
simple_archiver_parser_internal_remove_end_slash(out->working_files[0]);
|
||||||
out->working_files[1] = NULL;
|
out->working_files[1] = NULL;
|
||||||
} else {
|
} else {
|
||||||
size_t working_size = 1;
|
int working_size = 1;
|
||||||
char **ptr = out->working_files;
|
char **ptr = out->working_files;
|
||||||
while (ptr && *ptr) {
|
while (ptr && *ptr) {
|
||||||
++working_size;
|
++working_size;
|
||||||
|
@ -399,9 +276,9 @@ int simple_archiver_parse_args(int argc, const char **argv,
|
||||||
|
|
||||||
// Set new actual last element to NULL.
|
// Set new actual last element to NULL.
|
||||||
out->working_files[working_size] = NULL;
|
out->working_files[working_size] = NULL;
|
||||||
size_t arg_idx =
|
unsigned int arg_idx =
|
||||||
simple_archiver_parser_internal_get_first_non_current_idx(argv[0]);
|
simple_archiver_parser_internal_filename_idx(argv[0]);
|
||||||
size_t size = strlen(argv[0] + arg_idx) + 1;
|
int size = strlen(argv[0] + arg_idx) + 1;
|
||||||
// Set last element to the arg.
|
// Set last element to the arg.
|
||||||
out->working_files[working_size - 1] = malloc(size);
|
out->working_files[working_size - 1] = malloc(size);
|
||||||
strncpy(out->working_files[working_size - 1], argv[0] + arg_idx, size);
|
strncpy(out->working_files[working_size - 1], argv[0] + arg_idx, size);
|
||||||
|
@ -414,10 +291,6 @@ int simple_archiver_parse_args(int argc, const char **argv,
|
||||||
++argv;
|
++argv;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!out->temp_dir) {
|
|
||||||
out->temp_dir = "./";
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,7 +310,7 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed) {
|
||||||
}
|
}
|
||||||
if (parsed->working_files) {
|
if (parsed->working_files) {
|
||||||
char **ptr = parsed->working_files;
|
char **ptr = parsed->working_files;
|
||||||
uint32_t idx = 0;
|
unsigned int idx = 0;
|
||||||
while (ptr[idx]) {
|
while (ptr[idx]) {
|
||||||
free(ptr[idx]);
|
free(ptr[idx]);
|
||||||
++idx;
|
++idx;
|
||||||
|
@ -448,7 +321,7 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
const SDArchiverParsed *parsed, SDArchiverParsedStatus *status_out) {
|
const SDArchiverParsed *parsed) {
|
||||||
SDArchiverLinkedList *files_list = simple_archiver_list_init();
|
SDArchiverLinkedList *files_list = simple_archiver_list_init();
|
||||||
__attribute__((cleanup(simple_archiver_hash_map_free)))
|
__attribute__((cleanup(simple_archiver_hash_map_free)))
|
||||||
SDArchiverHashMap *hash_map = simple_archiver_hash_map_init();
|
SDArchiverHashMap *hash_map = simple_archiver_hash_map_init();
|
||||||
|
@ -456,34 +329,19 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
|
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
|
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
|
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
|
||||||
__attribute__((cleanup(
|
|
||||||
simple_archiver_helper_cleanup_chdir_back))) char *original_cwd = NULL;
|
|
||||||
if (parsed->user_cwd) {
|
|
||||||
original_cwd = realpath(".", NULL);
|
|
||||||
if (chdir(parsed->user_cwd)) {
|
|
||||||
simple_archiver_list_free(&files_list);
|
|
||||||
if (status_out) {
|
|
||||||
*status_out = SDAPS_NO_USER_CWD;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (char **iter = parsed->working_files; iter && *iter; ++iter) {
|
for (char **iter = parsed->working_files; iter && *iter; ++iter) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
memset(&st, 0, sizeof(struct stat));
|
memset(&st, 0, sizeof(struct stat));
|
||||||
char *file_path = *iter;
|
fstatat(AT_FDCWD, *iter, &st, AT_SYMLINK_NOFOLLOW);
|
||||||
fstatat(AT_FDCWD, file_path, &st, AT_SYMLINK_NOFOLLOW);
|
|
||||||
if ((st.st_mode & S_IFMT) == S_IFREG || (st.st_mode & S_IFMT) == S_IFLNK) {
|
if ((st.st_mode & S_IFMT) == S_IFREG || (st.st_mode & S_IFMT) == S_IFLNK) {
|
||||||
// Is a regular file or a symbolic link.
|
// Is a regular file or a symbolic link.
|
||||||
size_t len = strlen(file_path) + 1;
|
int len = strlen(*iter) + 1;
|
||||||
char *filename = malloc(len);
|
char *filename = malloc(len);
|
||||||
strncpy(filename, file_path, len);
|
strncpy(filename, *iter, len);
|
||||||
if (simple_archiver_hash_map_get(hash_map, filename, len - 1) == NULL) {
|
if (simple_archiver_hash_map_get(hash_map, filename, len - 1) == NULL) {
|
||||||
SDArchiverFileInfo *file_info = malloc(sizeof(SDArchiverFileInfo));
|
SDArchiverFileInfo *file_info = malloc(sizeof(SDArchiverFileInfo));
|
||||||
file_info->filename = filename;
|
file_info->filename = filename;
|
||||||
if ((st.st_mode & S_IFMT) == S_IFLNK) {
|
if ((st.st_mode & S_IFMT) == S_IFLNK) {
|
||||||
// Is a symlink.
|
|
||||||
file_info->link_dest = malloc(MAX_SYMBOLIC_LINK_SIZE);
|
file_info->link_dest = malloc(MAX_SYMBOLIC_LINK_SIZE);
|
||||||
ssize_t count = readlinkat(AT_FDCWD, filename, file_info->link_dest,
|
ssize_t count = readlinkat(AT_FDCWD, filename, file_info->link_dest,
|
||||||
MAX_SYMBOLIC_LINK_SIZE - 1);
|
MAX_SYMBOLIC_LINK_SIZE - 1);
|
||||||
|
@ -493,42 +351,19 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
file_info->link_dest[count] = 0;
|
file_info->link_dest[count] = 0;
|
||||||
} else {
|
} else {
|
||||||
// Failure.
|
// Failure.
|
||||||
fprintf(stderr,
|
|
||||||
"WARNING: Could not get link info for file \"%s\"!\n",
|
|
||||||
file_info->filename);
|
|
||||||
free(file_info->link_dest);
|
free(file_info->link_dest);
|
||||||
free(file_info);
|
free(file_info);
|
||||||
free(filename);
|
free(filename);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Is a regular file.
|
|
||||||
file_info->link_dest = NULL;
|
file_info->link_dest = NULL;
|
||||||
// Check that the file is readable by opening it. Easier than to
|
|
||||||
// check permissions because that would also require checking if the
|
|
||||||
// current USER can open the file.
|
|
||||||
FILE *readable_file = fopen(file_info->filename, "rb");
|
|
||||||
if (!readable_file) {
|
|
||||||
// Cannot open file, so it must be unreadable (at least by the
|
|
||||||
// current USER).
|
|
||||||
fprintf(stderr, "WARNING: \"%s\" is not readable, skipping!\n",
|
|
||||||
file_info->filename);
|
|
||||||
free(file_info->link_dest);
|
|
||||||
free(file_info);
|
|
||||||
free(filename);
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
fclose(readable_file);
|
|
||||||
// fprintf(stderr, "DEBUG: \"%s\" is readable.\n",
|
|
||||||
// file_info->filename);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
simple_archiver_list_add(files_list, file_info,
|
simple_archiver_list_add(files_list, file_info,
|
||||||
simple_archiver_internal_free_file_info_fn);
|
simple_archiver_internal_free_file_info_fn);
|
||||||
simple_archiver_hash_map_insert(
|
simple_archiver_hash_map_insert(&hash_map, &hash_map_sentinel, filename,
|
||||||
hash_map, &hash_map_sentinel, filename, len - 1,
|
len - 1, container_no_free_fn,
|
||||||
simple_archiver_helper_datastructure_cleanup_nop,
|
container_no_free_fn);
|
||||||
simple_archiver_helper_datastructure_cleanup_nop);
|
|
||||||
} else {
|
} else {
|
||||||
free(filename);
|
free(filename);
|
||||||
}
|
}
|
||||||
|
@ -536,9 +371,7 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
// Is a directory.
|
// Is a directory.
|
||||||
__attribute__((cleanup(simple_archiver_list_free)))
|
__attribute__((cleanup(simple_archiver_list_free)))
|
||||||
SDArchiverLinkedList *dir_list = simple_archiver_list_init();
|
SDArchiverLinkedList *dir_list = simple_archiver_list_init();
|
||||||
simple_archiver_list_add(
|
simple_archiver_list_add(dir_list, *iter, container_no_free_fn);
|
||||||
dir_list, file_path,
|
|
||||||
simple_archiver_helper_datastructure_cleanup_nop);
|
|
||||||
char *next;
|
char *next;
|
||||||
while (dir_list->count != 0) {
|
while (dir_list->count != 0) {
|
||||||
simple_archiver_list_get(dir_list, list_get_last_fn, &next);
|
simple_archiver_list_get(dir_list, list_get_last_fn, &next);
|
||||||
|
@ -554,15 +387,13 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
strcmp(dir_entry->d_name, "..") == 0) {
|
strcmp(dir_entry->d_name, "..") == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// fprintf(stderr, "dir entry in %s is %s\n", next,
|
printf("dir entry in %s is %s\n", next, dir_entry->d_name);
|
||||||
// dir_entry->d_name);
|
int combined_size = strlen(next) + strlen(dir_entry->d_name) + 2;
|
||||||
size_t combined_size = strlen(next) + strlen(dir_entry->d_name) + 2;
|
|
||||||
char *combined_path = malloc(combined_size);
|
char *combined_path = malloc(combined_size);
|
||||||
snprintf(combined_path, combined_size, "%s/%s", next,
|
snprintf(combined_path, combined_size, "%s/%s", next,
|
||||||
dir_entry->d_name);
|
dir_entry->d_name);
|
||||||
size_t valid_idx =
|
unsigned int valid_idx =
|
||||||
simple_archiver_parser_internal_get_first_non_current_idx(
|
simple_archiver_parser_internal_filename_idx(combined_path);
|
||||||
combined_path);
|
|
||||||
if (valid_idx > 0) {
|
if (valid_idx > 0) {
|
||||||
char *new_path = malloc(combined_size - valid_idx);
|
char *new_path = malloc(combined_size - valid_idx);
|
||||||
strncpy(new_path, combined_path + valid_idx,
|
strncpy(new_path, combined_path + valid_idx,
|
||||||
|
@ -582,7 +413,6 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
malloc(sizeof(SDArchiverFileInfo));
|
malloc(sizeof(SDArchiverFileInfo));
|
||||||
file_info->filename = combined_path;
|
file_info->filename = combined_path;
|
||||||
if ((st.st_mode & S_IFMT) == S_IFLNK) {
|
if ((st.st_mode & S_IFMT) == S_IFLNK) {
|
||||||
// Is a symlink.
|
|
||||||
file_info->link_dest = malloc(MAX_SYMBOLIC_LINK_SIZE);
|
file_info->link_dest = malloc(MAX_SYMBOLIC_LINK_SIZE);
|
||||||
ssize_t count =
|
ssize_t count =
|
||||||
readlinkat(AT_FDCWD, combined_path, file_info->link_dest,
|
readlinkat(AT_FDCWD, combined_path, file_info->link_dest,
|
||||||
|
@ -599,36 +429,15 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Is a regular file.
|
|
||||||
file_info->link_dest = NULL;
|
file_info->link_dest = NULL;
|
||||||
// Check that the file is readable by opening it. Easier than
|
|
||||||
// to check permissions because that would also require
|
|
||||||
// checking if the current USER can open the file.
|
|
||||||
FILE *readable_file = fopen(file_info->filename, "rb");
|
|
||||||
if (!readable_file) {
|
|
||||||
// Cannot open file, so it must be unreadable (at least by
|
|
||||||
// the current USER).
|
|
||||||
fprintf(stderr,
|
|
||||||
"WARNING: \"%s\" is not readable, skipping!\n",
|
|
||||||
file_info->filename);
|
|
||||||
free(file_info->link_dest);
|
|
||||||
free(file_info);
|
|
||||||
free(combined_path);
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
fclose(readable_file);
|
|
||||||
// fprintf(stderr, "DEBUG: \"%s\" is readable.\n",
|
|
||||||
// file_info->filename);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
simple_archiver_list_add(
|
simple_archiver_list_add(
|
||||||
files_list, file_info,
|
files_list, file_info,
|
||||||
simple_archiver_internal_free_file_info_fn);
|
simple_archiver_internal_free_file_info_fn);
|
||||||
simple_archiver_hash_map_insert(
|
simple_archiver_hash_map_insert(
|
||||||
hash_map, &hash_map_sentinel, combined_path,
|
&hash_map, &hash_map_sentinel, combined_path,
|
||||||
combined_size - 1,
|
combined_size - 1, container_no_free_fn,
|
||||||
simple_archiver_helper_datastructure_cleanup_nop,
|
container_no_free_fn);
|
||||||
simple_archiver_helper_datastructure_cleanup_nop);
|
|
||||||
} else {
|
} else {
|
||||||
free(combined_path);
|
free(combined_path);
|
||||||
}
|
}
|
||||||
|
@ -651,48 +460,88 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
// Unhandled type. TODO handle this.
|
// Unhandled type. TODO handle this.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#elif SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_WINDOWS
|
||||||
|
for (char **iter = parsed->working_files; iter && *iter; ++iter) {
|
||||||
|
DWORD file_stats = GetFileAttributesA(*iter);
|
||||||
|
if (file_stats == INVALID_FILE_ATTRIBUTES) {
|
||||||
|
printf("Failed to get attributes for %s\n", *iter);
|
||||||
|
continue;
|
||||||
|
} else if ((file_stats & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||||
|
// Is not a directory.
|
||||||
|
if ((file_stats & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
|
||||||
|
// Is not a symbolic link. Is probably a regular file.
|
||||||
|
printf("%s is probably a regular file.\n", *iter);
|
||||||
|
} else {
|
||||||
|
// Check if "reparse point" is a symbolic link.
|
||||||
|
WIN32_FIND_DATAA find_data;
|
||||||
|
HANDLE find_handle = FindFirstFileA(*iter, &find_data);
|
||||||
|
if (find_handle == INVALID_HANDLE_VALUE) {
|
||||||
|
printf("Unable to verify if %s is a symbolic link!\n", *iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
printf("Filename in find data structure is \"%s\".\n",
|
||||||
|
find_data.cFileName);
|
||||||
|
if (find_data.dwReserved0 != IO_REPARSE_TAG_SYMLINK) {
|
||||||
|
puts("NOTICE: result of FindFirstFileA does not have symlink tag!");
|
||||||
|
// printf("%s is not a symbolic link but is a reparse point (tag is
|
||||||
|
// %#lx).\n", *iter, find_data.dwReserved0);
|
||||||
|
// FindClose(find_handle);
|
||||||
|
// continue;
|
||||||
|
}
|
||||||
|
FindClose(find_handle);
|
||||||
|
// Is a symbolic link.
|
||||||
|
printf("%s is a symbolic link.\n", *iter);
|
||||||
|
HANDLE symlink_handle =
|
||||||
|
CreateFileA(*iter, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||||
|
OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL);
|
||||||
|
if (symlink_handle == INVALID_HANDLE_VALUE) {
|
||||||
|
puts("Failed to get handle on symbolic link!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
REPARSE_GUID_DATA_BUFFER output;
|
||||||
|
printf("Size of REPARSE_GUID_DATA_BUFFER is %llu\n",
|
||||||
|
sizeof(REPARSE_GUID_DATA_BUFFER));
|
||||||
|
DWORD count;
|
||||||
|
OVERLAPPED overlapped;
|
||||||
|
if (DeviceIoControl(symlink_handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
|
||||||
|
&output, sizeof(REPARSE_GUID_DATA_BUFFER), &count,
|
||||||
|
&overlapped) == 0) {
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
printf("Failed to get info on symlink handle (%lu %#lx)!\n", error,
|
||||||
|
error);
|
||||||
|
CloseHandle(symlink_handle);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
printf("link destination count is %lu.\n", count);
|
||||||
|
// printf("First bytes:\n %#hhx %#hhx %#hhx %#hhx %#hhx %#hhx %#hhx
|
||||||
|
// %#hhx\n", output[0], output[1], output[2], output[3], output[4],
|
||||||
|
// output[5], output[6], output[7]);
|
||||||
|
|
||||||
|
// printf("Link to: %s\n", output.SymbolicLinkReparseBuffer.);
|
||||||
|
CloseHandle(symlink_handle);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Is a directory.
|
||||||
|
printf("%s is a directory.\n", *iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Remove leading "./" entries from files_list.
|
||||||
for (SDArchiverLLNode *iter = files_list->head->next;
|
for (SDArchiverLLNode *iter = files_list->head->next;
|
||||||
iter != files_list->tail; iter = iter->next) {
|
iter != files_list->tail; iter = iter->next) {
|
||||||
SDArchiverFileInfo *file_info = iter->data;
|
SDArchiverFileInfo *file_info = iter->data;
|
||||||
|
unsigned int idx =
|
||||||
// Remove leading "./" entries from files_list.
|
simple_archiver_parser_internal_filename_idx(file_info->filename);
|
||||||
size_t idx = simple_archiver_parser_internal_get_first_non_current_idx(
|
|
||||||
file_info->filename);
|
|
||||||
if (idx > 0) {
|
if (idx > 0) {
|
||||||
size_t len = strlen(file_info->filename) + 1 - idx;
|
int len = strlen(file_info->filename) + 1 - idx;
|
||||||
char *substr = malloc(len);
|
char *substr = malloc(len);
|
||||||
strncpy(substr, file_info->filename + idx, len);
|
strncpy(substr, file_info->filename + idx, len);
|
||||||
free(file_info->filename);
|
free(file_info->filename);
|
||||||
file_info->filename = substr;
|
file_info->filename = substr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove "./" entries inside the file path.
|
|
||||||
int_fast8_t slash_found = 0;
|
|
||||||
int_fast8_t dot_found = 0;
|
|
||||||
for (idx = strlen(file_info->filename); idx-- > 0;) {
|
|
||||||
if (file_info->filename[idx] == '/') {
|
|
||||||
if (dot_found) {
|
|
||||||
char *temp = simple_archiver_helper_cut_substr(file_info->filename,
|
|
||||||
idx + 1, idx + 3);
|
|
||||||
free(file_info->filename);
|
|
||||||
file_info->filename = temp;
|
|
||||||
} else {
|
|
||||||
slash_found = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if (file_info->filename[idx] == '.' && slash_found) {
|
|
||||||
dot_found = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
slash_found = 0;
|
|
||||||
dot_found = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status_out) {
|
|
||||||
*status_out = SDAPS_SUCCESS;
|
|
||||||
}
|
|
||||||
return files_list;
|
return files_list;
|
||||||
}
|
}
|
||||||
|
|
75
src/parser.h
75
src/parser.h
|
@ -1,44 +1,31 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `parser.h` is the header for parsing args.
|
||||||
// `parser.h` is the header for parsing args.
|
*/
|
||||||
|
|
||||||
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_H_
|
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_H_
|
||||||
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_H_
|
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_H_
|
||||||
|
|
||||||
// Standard library includes.
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
// Local includes.
|
|
||||||
#include "data_structures/linked_list.h"
|
#include "data_structures/linked_list.h"
|
||||||
|
|
||||||
typedef struct SDArchiverParsed {
|
typedef struct SDArchiverParsed {
|
||||||
/// Each bit is a flag.
|
/// Each bit is a flag.
|
||||||
/// 0b xxxx xx00 - is creating.
|
/// 0b0 - is creating.
|
||||||
/// 0b xxxx xx01 - is extracting.
|
/// 0b1 - is extracting.
|
||||||
/// 0b xxxx xx10 - is checking/examining.
|
unsigned int flags;
|
||||||
/// 0b xxxx x0xx - Do NOT allow create archive overwrite.
|
|
||||||
/// 0b xxxx x1xx - Allow create archive overwrite.
|
|
||||||
/// 0b xxxx 1xxx - Allow extract overwrite.
|
|
||||||
/// 0b xxx1 xxxx - Create archive to stdout or read archive from stdin.
|
|
||||||
/// 0b xx1x xxxx - Do not save absolute paths for symlinks.
|
|
||||||
/// 0b x1xx xxxx - Sort files by size before archiving.
|
|
||||||
/// 0b 1xxx xxxx - No safe links.
|
|
||||||
/// 0b xxxx xxx1 xxxx xxxx - Preserve symlink target.
|
|
||||||
uint32_t flags;
|
|
||||||
/// Null-terminated string.
|
/// Null-terminated string.
|
||||||
char *filename;
|
char *filename;
|
||||||
/// Null-terminated string.
|
/// Null-terminated string.
|
||||||
|
@ -47,17 +34,8 @@ typedef struct SDArchiverParsed {
|
||||||
char *decompressor;
|
char *decompressor;
|
||||||
/// Null-terminated strings in array of strings.
|
/// Null-terminated strings in array of strings.
|
||||||
/// Last entry should be NULL.
|
/// Last entry should be NULL.
|
||||||
/// Determines a "white-list" of files to extract when extracting.
|
/// Not used when extracting.
|
||||||
char **working_files;
|
char **working_files;
|
||||||
/// Determines where to place temporary files. If NULL, temporary files are
|
|
||||||
/// created in the current working directory.
|
|
||||||
const char *temp_dir;
|
|
||||||
/// Dir specified by "-C".
|
|
||||||
const char *user_cwd;
|
|
||||||
/// Currently only 0 and 1 is supported.
|
|
||||||
uint32_t write_version;
|
|
||||||
/// The minimum size of a chunk in bytes (the last chunk may be less).
|
|
||||||
uint64_t minimum_chunk_size;
|
|
||||||
} SDArchiverParsed;
|
} SDArchiverParsed;
|
||||||
|
|
||||||
typedef struct SDArchiverFileInfo {
|
typedef struct SDArchiverFileInfo {
|
||||||
|
@ -66,14 +44,6 @@ typedef struct SDArchiverFileInfo {
|
||||||
char *link_dest;
|
char *link_dest;
|
||||||
} SDArchiverFileInfo;
|
} SDArchiverFileInfo;
|
||||||
|
|
||||||
typedef enum SDArchiverParsedStatus {
|
|
||||||
SDAPS_SUCCESS,
|
|
||||||
SDAPS_NO_USER_CWD,
|
|
||||||
} SDArchiverParsedStatus;
|
|
||||||
|
|
||||||
/// Returned c-string does not need to be free'd.
|
|
||||||
char *simple_archiver_parsed_status_to_str(SDArchiverParsedStatus status);
|
|
||||||
|
|
||||||
void simple_archiver_print_usage(void);
|
void simple_archiver_print_usage(void);
|
||||||
|
|
||||||
SDArchiverParsed simple_archiver_create_parsed(void);
|
SDArchiverParsed simple_archiver_create_parsed(void);
|
||||||
|
@ -81,7 +51,6 @@ SDArchiverParsed simple_archiver_create_parsed(void);
|
||||||
/// Expects the user to pass a pointer to an SDArchiverParsed.
|
/// Expects the user to pass a pointer to an SDArchiverParsed.
|
||||||
/// This means the user should have a SDArchiverParsed variable
|
/// This means the user should have a SDArchiverParsed variable
|
||||||
/// and it should be passed with e.g. "&var".
|
/// and it should be passed with e.g. "&var".
|
||||||
/// Returns 0 on success.
|
|
||||||
int simple_archiver_parse_args(int argc, const char **argv,
|
int simple_archiver_parse_args(int argc, const char **argv,
|
||||||
SDArchiverParsed *out);
|
SDArchiverParsed *out);
|
||||||
|
|
||||||
|
@ -89,6 +58,6 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed);
|
||||||
|
|
||||||
/// Each entry in the linked list is an SDArchiverFileInfo object.
|
/// Each entry in the linked list is an SDArchiverFileInfo object.
|
||||||
SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
const SDArchiverParsed *parsed, SDArchiverParsedStatus *status_out);
|
const SDArchiverParsed *parsed);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,27 +1,26 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `parser_internal.h` is the header for parsing args with internal functions.
|
||||||
// `parser_internal.h` is the header for parsing args with internal functions.
|
*/
|
||||||
|
|
||||||
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_INTERNAL_H_
|
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_INTERNAL_H_
|
||||||
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_INTERNAL_H_
|
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_INTERNAL_H_
|
||||||
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
size_t simple_archiver_parser_internal_get_first_non_current_idx(
|
unsigned int simple_archiver_parser_internal_filename_idx(const char *filename);
|
||||||
const char *filename);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `platforms.h` is the header that determines what platform this program is
|
||||||
// `platforms.h` is the header that determines what platform this program is
|
* compiled for.
|
||||||
// compiled for.
|
*/
|
||||||
|
|
||||||
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_PLATFORMS_H_
|
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_PLATFORMS_H_
|
||||||
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_PLATFORMS_H_
|
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_PLATFORMS_H_
|
||||||
|
|
250
src/test.c
250
src/test.c
|
@ -1,34 +1,30 @@
|
||||||
// ISC License
|
/*
|
||||||
//
|
* Copyright 2024 Stephen Seo
|
||||||
// Copyright (c) 2024 Stephen Seo
|
*
|
||||||
//
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
* you may not use this file except in compliance with the License.
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
* You may obtain a copy of the License at
|
||||||
// copyright notice and this permission notice appear in all copies.
|
*
|
||||||
//
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
*
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* See the License for the specific language governing permissions and
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* limitations under the License.
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
*
|
||||||
//
|
* `test.c` is the source for testing code.
|
||||||
// `test.c` is the source for testing code.
|
*/
|
||||||
|
|
||||||
// Standard library includes.
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// Local includes.
|
|
||||||
#include "archiver.h"
|
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "parser_internal.h"
|
#include "parser_internal.h"
|
||||||
|
|
||||||
static int32_t checks_checked = 0;
|
static int checks_checked = 0;
|
||||||
static int32_t checks_passed = 0;
|
static int checks_passed = 0;
|
||||||
|
|
||||||
#define CHECK_TRUE(x) \
|
#define CHECK_TRUE(x) \
|
||||||
do { \
|
do { \
|
||||||
|
@ -48,55 +44,38 @@ static int32_t checks_passed = 0;
|
||||||
++checks_passed; \
|
++checks_passed; \
|
||||||
} \
|
} \
|
||||||
} while (0);
|
} while (0);
|
||||||
#define CHECK_STREQ(a, b) \
|
|
||||||
do { \
|
|
||||||
++checks_checked; \
|
|
||||||
if (strcmp((a), (b)) == 0) { \
|
|
||||||
++checks_passed; \
|
|
||||||
} else { \
|
|
||||||
printf("CHECK_STREQ at line %u failed: %s != %s\n", __LINE__, #a, #b); \
|
|
||||||
} \
|
|
||||||
} while (0);
|
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
// Test parser.
|
// Test parser.
|
||||||
{
|
{
|
||||||
size_t idx =
|
unsigned int idx = simple_archiver_parser_internal_filename_idx("test");
|
||||||
simple_archiver_parser_internal_get_first_non_current_idx("test");
|
|
||||||
CHECK_TRUE(idx == 0);
|
CHECK_TRUE(idx == 0);
|
||||||
|
|
||||||
idx = simple_archiver_parser_internal_get_first_non_current_idx("./test");
|
idx = simple_archiver_parser_internal_filename_idx("./test");
|
||||||
CHECK_TRUE(idx == 2);
|
CHECK_TRUE(idx == 2);
|
||||||
|
|
||||||
idx = simple_archiver_parser_internal_get_first_non_current_idx("././test");
|
idx = simple_archiver_parser_internal_filename_idx("././test");
|
||||||
CHECK_TRUE(idx == 4);
|
CHECK_TRUE(idx == 4);
|
||||||
|
|
||||||
idx = simple_archiver_parser_internal_get_first_non_current_idx(
|
idx = simple_archiver_parser_internal_filename_idx("././//././//./test");
|
||||||
"././//././//./test");
|
|
||||||
CHECK_TRUE(idx == 14);
|
CHECK_TRUE(idx == 14);
|
||||||
|
|
||||||
idx = simple_archiver_parser_internal_get_first_non_current_idx(
|
idx = simple_archiver_parser_internal_filename_idx("/././//././//./test");
|
||||||
"/././//././//./test");
|
|
||||||
CHECK_TRUE(idx == 0);
|
CHECK_TRUE(idx == 0);
|
||||||
|
|
||||||
idx = simple_archiver_parser_internal_get_first_non_current_idx(
|
idx = simple_archiver_parser_internal_filename_idx(".derp/.//././//./test");
|
||||||
".derp/.//././//./test");
|
|
||||||
CHECK_TRUE(idx == 0);
|
CHECK_TRUE(idx == 0);
|
||||||
|
|
||||||
idx = simple_archiver_parser_internal_get_first_non_current_idx(
|
idx = simple_archiver_parser_internal_filename_idx("././/.derp/.///./test");
|
||||||
"././/.derp/.///./test");
|
|
||||||
CHECK_TRUE(idx == 5);
|
CHECK_TRUE(idx == 5);
|
||||||
|
|
||||||
idx = simple_archiver_parser_internal_get_first_non_current_idx(
|
idx = simple_archiver_parser_internal_filename_idx("././/.//.//./");
|
||||||
"././/.//.//./");
|
|
||||||
CHECK_TRUE(idx == 11);
|
CHECK_TRUE(idx == 11);
|
||||||
|
|
||||||
idx = simple_archiver_parser_internal_get_first_non_current_idx(
|
idx = simple_archiver_parser_internal_filename_idx("././/.//.//.");
|
||||||
"././/.//.//.");
|
|
||||||
CHECK_TRUE(idx == 11);
|
CHECK_TRUE(idx == 11);
|
||||||
|
|
||||||
idx = simple_archiver_parser_internal_get_first_non_current_idx(
|
idx = simple_archiver_parser_internal_filename_idx("././/.//.//");
|
||||||
"././/.//.//");
|
|
||||||
CHECK_TRUE(idx == 8);
|
CHECK_TRUE(idx == 8);
|
||||||
|
|
||||||
SDArchiverParsed parsed = simple_archiver_create_parsed();
|
SDArchiverParsed parsed = simple_archiver_create_parsed();
|
||||||
|
@ -109,7 +88,7 @@ int main(void) {
|
||||||
CHECK_TRUE(strcmp("doop", parsed.working_files[1]) == 0);
|
CHECK_TRUE(strcmp("doop", parsed.working_files[1]) == 0);
|
||||||
CHECK_TRUE(parsed.working_files[2] == NULL);
|
CHECK_TRUE(parsed.working_files[2] == NULL);
|
||||||
CHECK_TRUE(parsed.filename == NULL);
|
CHECK_TRUE(parsed.filename == NULL);
|
||||||
CHECK_TRUE(parsed.flags == 0x40);
|
CHECK_TRUE(parsed.flags == 0);
|
||||||
|
|
||||||
simple_archiver_free_parsed(&parsed);
|
simple_archiver_free_parsed(&parsed);
|
||||||
|
|
||||||
|
@ -126,7 +105,7 @@ int main(void) {
|
||||||
CHECK_TRUE(strcmp("../../.prev_dir_file", parsed.working_files[2]) == 0);
|
CHECK_TRUE(strcmp("../../.prev_dir_file", parsed.working_files[2]) == 0);
|
||||||
CHECK_TRUE(parsed.working_files[3] == NULL);
|
CHECK_TRUE(parsed.working_files[3] == NULL);
|
||||||
CHECK_TRUE(strcmp("the_filename", parsed.filename) == 0);
|
CHECK_TRUE(strcmp("the_filename", parsed.filename) == 0);
|
||||||
CHECK_TRUE(parsed.flags == 0x41);
|
CHECK_TRUE(parsed.flags == 1);
|
||||||
|
|
||||||
simple_archiver_free_parsed(&parsed);
|
simple_archiver_free_parsed(&parsed);
|
||||||
}
|
}
|
||||||
|
@ -135,139 +114,62 @@ int main(void) {
|
||||||
{
|
{
|
||||||
// Only if system is little-endian.
|
// Only if system is little-endian.
|
||||||
if (simple_archiver_helper_is_big_endian() == 0) {
|
if (simple_archiver_helper_is_big_endian() == 0) {
|
||||||
uint16_t u16 = 0x0102;
|
unsigned short u16 = 0x0102;
|
||||||
CHECK_TRUE(((uint8_t *)&u16)[0] == 2);
|
CHECK_TRUE(((unsigned char *)&u16)[0] == 2);
|
||||||
CHECK_TRUE(((uint8_t *)&u16)[1] == 1);
|
CHECK_TRUE(((unsigned char *)&u16)[1] == 1);
|
||||||
simple_archiver_helper_16_bit_be(&u16);
|
simple_archiver_helper_16_bit_be(&u16);
|
||||||
CHECK_TRUE(((uint8_t *)&u16)[0] == 1);
|
CHECK_TRUE(((unsigned char *)&u16)[0] == 1);
|
||||||
CHECK_TRUE(((uint8_t *)&u16)[1] == 2);
|
CHECK_TRUE(((unsigned char *)&u16)[1] == 2);
|
||||||
simple_archiver_helper_16_bit_be(&u16);
|
simple_archiver_helper_16_bit_be(&u16);
|
||||||
CHECK_TRUE(((uint8_t *)&u16)[0] == 2);
|
CHECK_TRUE(((unsigned char *)&u16)[0] == 2);
|
||||||
CHECK_TRUE(((uint8_t *)&u16)[1] == 1);
|
CHECK_TRUE(((unsigned char *)&u16)[1] == 1);
|
||||||
|
|
||||||
uint32_t u32 = 0x01020304;
|
unsigned int u32 = 0x01020304;
|
||||||
CHECK_TRUE(((uint8_t *)&u32)[0] == 4);
|
CHECK_TRUE(((unsigned char *)&u32)[0] == 4);
|
||||||
CHECK_TRUE(((uint8_t *)&u32)[1] == 3);
|
CHECK_TRUE(((unsigned char *)&u32)[1] == 3);
|
||||||
CHECK_TRUE(((uint8_t *)&u32)[2] == 2);
|
CHECK_TRUE(((unsigned char *)&u32)[2] == 2);
|
||||||
CHECK_TRUE(((uint8_t *)&u32)[3] == 1);
|
CHECK_TRUE(((unsigned char *)&u32)[3] == 1);
|
||||||
simple_archiver_helper_32_bit_be(&u32);
|
simple_archiver_helper_32_bit_be(&u32);
|
||||||
CHECK_TRUE(((uint8_t *)&u32)[0] == 1);
|
CHECK_TRUE(((unsigned char *)&u32)[0] == 1);
|
||||||
CHECK_TRUE(((uint8_t *)&u32)[1] == 2);
|
CHECK_TRUE(((unsigned char *)&u32)[1] == 2);
|
||||||
CHECK_TRUE(((uint8_t *)&u32)[2] == 3);
|
CHECK_TRUE(((unsigned char *)&u32)[2] == 3);
|
||||||
CHECK_TRUE(((uint8_t *)&u32)[3] == 4);
|
CHECK_TRUE(((unsigned char *)&u32)[3] == 4);
|
||||||
simple_archiver_helper_32_bit_be(&u32);
|
simple_archiver_helper_32_bit_be(&u32);
|
||||||
CHECK_TRUE(((uint8_t *)&u32)[0] == 4);
|
CHECK_TRUE(((unsigned char *)&u32)[0] == 4);
|
||||||
CHECK_TRUE(((uint8_t *)&u32)[1] == 3);
|
CHECK_TRUE(((unsigned char *)&u32)[1] == 3);
|
||||||
CHECK_TRUE(((uint8_t *)&u32)[2] == 2);
|
CHECK_TRUE(((unsigned char *)&u32)[2] == 2);
|
||||||
CHECK_TRUE(((uint8_t *)&u32)[3] == 1);
|
CHECK_TRUE(((unsigned char *)&u32)[3] == 1);
|
||||||
|
|
||||||
uint64_t u64 = 0x010203040a0b0c0d;
|
unsigned long long u64 = 0x010203040a0b0c0d;
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[0] == 0xd);
|
CHECK_TRUE(((unsigned char *)&u64)[0] == 0xd);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[1] == 0xc);
|
CHECK_TRUE(((unsigned char *)&u64)[1] == 0xc);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[2] == 0xb);
|
CHECK_TRUE(((unsigned char *)&u64)[2] == 0xb);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[3] == 0xa);
|
CHECK_TRUE(((unsigned char *)&u64)[3] == 0xa);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[4] == 0x4);
|
CHECK_TRUE(((unsigned char *)&u64)[4] == 0x4);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[5] == 0x3);
|
CHECK_TRUE(((unsigned char *)&u64)[5] == 0x3);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[6] == 0x2);
|
CHECK_TRUE(((unsigned char *)&u64)[6] == 0x2);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[7] == 0x1);
|
CHECK_TRUE(((unsigned char *)&u64)[7] == 0x1);
|
||||||
simple_archiver_helper_64_bit_be(&u64);
|
simple_archiver_helper_64_bit_be(&u64);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[0] == 0x1);
|
CHECK_TRUE(((unsigned char *)&u64)[0] == 0x1);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[1] == 0x2);
|
CHECK_TRUE(((unsigned char *)&u64)[1] == 0x2);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[2] == 0x3);
|
CHECK_TRUE(((unsigned char *)&u64)[2] == 0x3);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[3] == 0x4);
|
CHECK_TRUE(((unsigned char *)&u64)[3] == 0x4);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[4] == 0xa);
|
CHECK_TRUE(((unsigned char *)&u64)[4] == 0xa);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[5] == 0xb);
|
CHECK_TRUE(((unsigned char *)&u64)[5] == 0xb);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[6] == 0xc);
|
CHECK_TRUE(((unsigned char *)&u64)[6] == 0xc);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[7] == 0xd);
|
CHECK_TRUE(((unsigned char *)&u64)[7] == 0xd);
|
||||||
simple_archiver_helper_64_bit_be(&u64);
|
simple_archiver_helper_64_bit_be(&u64);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[0] == 0xd);
|
CHECK_TRUE(((unsigned char *)&u64)[0] == 0xd);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[1] == 0xc);
|
CHECK_TRUE(((unsigned char *)&u64)[1] == 0xc);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[2] == 0xb);
|
CHECK_TRUE(((unsigned char *)&u64)[2] == 0xb);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[3] == 0xa);
|
CHECK_TRUE(((unsigned char *)&u64)[3] == 0xa);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[4] == 0x4);
|
CHECK_TRUE(((unsigned char *)&u64)[4] == 0x4);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[5] == 0x3);
|
CHECK_TRUE(((unsigned char *)&u64)[5] == 0x3);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[6] == 0x2);
|
CHECK_TRUE(((unsigned char *)&u64)[6] == 0x2);
|
||||||
CHECK_TRUE(((uint8_t *)&u64)[7] == 0x1);
|
CHECK_TRUE(((unsigned char *)&u64)[7] == 0x1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test helpers cmd string to argv.
|
|
||||||
do {
|
|
||||||
const char *cmd = "zstd --compress --ultra\n -20 derp_file";
|
|
||||||
char **result_argv = simple_archiver_helper_cmd_string_to_argv(cmd);
|
|
||||||
CHECK_TRUE(result_argv);
|
|
||||||
if (!result_argv) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
CHECK_STREQ("zstd", result_argv[0]);
|
|
||||||
CHECK_STREQ("--compress", result_argv[1]);
|
|
||||||
CHECK_STREQ("--ultra", result_argv[2]);
|
|
||||||
CHECK_STREQ("-20", result_argv[3]);
|
|
||||||
CHECK_STREQ("derp_file", result_argv[4]);
|
|
||||||
CHECK_TRUE(result_argv[5] == NULL);
|
|
||||||
|
|
||||||
simple_archiver_helper_cmd_string_argv_free(result_argv);
|
|
||||||
} while (0);
|
|
||||||
|
|
||||||
// Test helpers cut substr.
|
|
||||||
{
|
|
||||||
const char *s = "one two three.";
|
|
||||||
uint32_t s_len = strlen(s);
|
|
||||||
// Invalid range.
|
|
||||||
char *out = simple_archiver_helper_cut_substr(s, 1, 0);
|
|
||||||
CHECK_FALSE(out);
|
|
||||||
// First idx out of range.
|
|
||||||
out = simple_archiver_helper_cut_substr(s, s_len, s_len + 1);
|
|
||||||
CHECK_FALSE(out);
|
|
||||||
// Second idx out of range.
|
|
||||||
out = simple_archiver_helper_cut_substr(s, 1, s_len + 1);
|
|
||||||
CHECK_FALSE(out);
|
|
||||||
// Invalid cut of full string.
|
|
||||||
out = simple_archiver_helper_cut_substr(s, 0, s_len);
|
|
||||||
CHECK_FALSE(out);
|
|
||||||
// Cut end of string.
|
|
||||||
out = simple_archiver_helper_cut_substr(s, 2, s_len);
|
|
||||||
CHECK_TRUE(out);
|
|
||||||
CHECK_STREQ(out, "on");
|
|
||||||
free(out);
|
|
||||||
// Cut start of string.
|
|
||||||
out = simple_archiver_helper_cut_substr(s, 0, s_len - 3);
|
|
||||||
CHECK_TRUE(out);
|
|
||||||
CHECK_STREQ(out, "ee.");
|
|
||||||
free(out);
|
|
||||||
// Cut inside string.
|
|
||||||
out = simple_archiver_helper_cut_substr(s, 4, 8);
|
|
||||||
CHECK_TRUE(out);
|
|
||||||
CHECK_STREQ(out, "one three.");
|
|
||||||
free(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test archiver.
|
|
||||||
{
|
|
||||||
__attribute__((
|
|
||||||
cleanup(simple_archiver_helper_cleanup_c_string))) char *rel_path =
|
|
||||||
simple_archiver_filenames_to_relative_path(
|
|
||||||
"/one/two/three/four/five", "/one/two/branch/other/path");
|
|
||||||
CHECK_STREQ(rel_path, "../../branch/other/path");
|
|
||||||
simple_archiver_helper_cleanup_c_string(&rel_path);
|
|
||||||
|
|
||||||
rel_path = simple_archiver_filenames_to_relative_path(
|
|
||||||
"/one/two/three/four/five", "/one/two/three/other/dir/");
|
|
||||||
CHECK_STREQ(rel_path, "../other/dir/");
|
|
||||||
simple_archiver_helper_cleanup_c_string(&rel_path);
|
|
||||||
|
|
||||||
rel_path = simple_archiver_filenames_to_relative_path(
|
|
||||||
"/one/two/three/", "/one/two/three/four");
|
|
||||||
CHECK_STREQ(rel_path, "four");
|
|
||||||
simple_archiver_helper_cleanup_c_string(&rel_path);
|
|
||||||
|
|
||||||
CHECK_FALSE(simple_archiver_validate_file_path("Local/Path"));
|
|
||||||
CHECK_TRUE(simple_archiver_validate_file_path("/Abs/Path"));
|
|
||||||
CHECK_TRUE(simple_archiver_validate_file_path("Local/../../not/really"));
|
|
||||||
CHECK_TRUE(simple_archiver_validate_file_path("./../almost"));
|
|
||||||
CHECK_TRUE(simple_archiver_validate_file_path("strange/.."));
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Checks checked: %u\n", checks_checked);
|
printf("Checks checked: %u\n", checks_checked);
|
||||||
printf("Checks passed: %u\n", checks_passed);
|
printf("Checks passed: %u\n", checks_passed);
|
||||||
return checks_passed == checks_checked ? 0 : 1;
|
return checks_passed == checks_checked ? 0 : 1;
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue