Compare commits

..

1 commit

Author SHA1 Message Date
664dcfaa89 WIP work on windows support
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
Windows support is still WIP.
2024-07-10 19:01:22 +09:00
33 changed files with 950 additions and 7082 deletions

View file

@ -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

View file

@ -1,13 +1,12 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.7)
project(SimpleArchiver C)
set(SimpleArchiver_VERSION 1.7)
set(SimpleArchiver_VERSION 1.0)
set(SimpleArchiver_SOURCES
src/main.c
src/parser.c
src/helpers.c
src/archiver.c
src/data_structures/linked_list.c
src/data_structures/hash_map.c
src/data_structures/priority_heap.c
@ -28,49 +27,6 @@ endif()
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
src/data_structures/test.c
src/data_structures/linked_list.c
@ -83,9 +39,7 @@ add_executable(test_simplearchiver
src/test.c
src/parser.c
src/helpers.c
src/archiver.c
src/algorithms/linear_congruential_gen.c
src/data_structures/linked_list.c
src/data_structures/hash_map.c
src/data_structures/priority_heap.c
)

View file

@ -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
View file

@ -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
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
1. Definitions.
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.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"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.

View file

@ -1,8 +1,6 @@
# Simple Archiver
This program ~~is not yet~~ ~~almost~~ basically finished! ~~Basic~~ Necessary
functionality is implemented and only ~~some advanced features are missing~~
some extra features are not yet implemented. You can track progress
This program is not yet finished! You can track progress
[here](https://git.seodisparate.com/stephenseo/SimpleArchiver/projects/3).
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
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
Uses the [ISC License](https://choosealicense.com/licenses/isc/).
Uses the [Apache License 2.0](https://choosealicense.com/licenses/apache-2.0).

View file

@ -7,7 +7,6 @@ SOURCES = \
../src/main.c \
../src/parser.c \
../src/helpers.c \
../src/archiver.c \
../src/algorithms/linear_congruential_gen.c \
../src/data_structures/linked_list.c \
../src/data_structures/hash_map.c \
@ -17,7 +16,6 @@ HEADERS = \
../src/parser.h \
../src/parser_internal.h \
../src/helpers.h \
../src/archiver.h \
../src/algorithms/linear_congruential_gen.h \
../src/data_structures/linked_list.h \
../src/data_structures/hash_map.h \

View file

@ -1,7 +1,5 @@
# File Format
Note that any unused bytes/bits should be zeroed-out before being written.
## Format Version 0
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
1. The first byte
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.
1. The first bit is "other write permission".
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.
1. Currently unused.
3. The third byte.
1. Currently unused.
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
this entry does not exist and should be skipped.
5. If this file is NOT a symbolic link:
1. 8 bytes 64-bit unsigned integer "size of filename in this archive file"
in big-endian.
1. 8 bytes 64-bit unsigned integer "size of filename in this archive file".
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.

View file

@ -1,30 +1,32 @@
// 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.
//
// `linear_congruential_gen.c` is the source for the linear congruential
// generator algorithm.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `linear_congruential_gen.c` is the source for the linear congruential
* generator algorithm.
*/
#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.
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.
return seed * SC_ALGO_LCG_DEFAULT_A + SC_ALGO_LCG_DEFAULT_C;
}

View file

@ -1,33 +1,32 @@
// 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.
//
// `linear_congruential_gen.h` is the header for the linear congruential
// generator algorithm.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `linear_congruential_gen.h` is the header for the linear congruential
* generator algorithm.
*/
#ifndef 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_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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -1,20 +1,20 @@
// 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.
//
// `hash_map.c` is the source for a hash map implementation.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `hash_map.c` is the source for a hash map implementation.
*/
#include "hash_map.h"
@ -26,22 +26,16 @@
typedef struct SDArchiverHashMapData {
void *value;
void *key;
size_t key_size;
unsigned int key_size;
void (*value_cleanup_fn)(void *);
void (*key_cleanup_fn)(void *);
} SDArchiverHashMapData;
typedef struct SDArchiverHashMapKeyData {
void *key;
size_t key_size;
unsigned int key_size;
} 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) {
SDArchiverHashMapData *hash_map_data = data;
if (hash_map_data->value) {
@ -74,19 +68,22 @@ int simple_archiver_hash_map_internal_pick_in_list(void *data, void *ud) {
: 0;
}
uint64_t simple_archiver_hash_default_fn(const void *key, size_t key_size) {
uint64_t seed = 0;
uint64_t temp;
for (size_t idx = 0; idx < key_size; ++idx) {
temp = (uint64_t)(((uint8_t*)key)[idx]) + seed;
if (idx % 3 == 0) {
temp ^= 0xA5A538A5A9B5A5A5;
} else if (idx % 3 == 1) {
temp ^= 0xD7A58BD7A58BD7AA;
} else {
temp ^= 0x8B7A8B8B87CB8B84;
unsigned long long simple_archiver_hash_map_internal_key_to_hash(
void *key, unsigned int key_size) {
unsigned long long seed = 0;
unsigned long long temp = 0;
unsigned int count = 0;
for (unsigned int idx = 0; idx < key_size; ++idx) {
temp |= ((unsigned long long)*((unsigned char *)key + idx)) << (8 * count);
++count;
if (count >= 8) {
count = 0;
seed += temp;
temp = 0;
}
seed += simple_archiver_algo_lcg_defaults(temp);
}
if (temp != 0) {
seed += temp;
}
return simple_archiver_algo_lcg_defaults(seed);
@ -98,28 +95,28 @@ void simple_archiver_hash_map_internal_no_free_fn(
}
/// Returns 0 on success.
int simple_archiver_hash_map_internal_rehash(SDArchiverHashMap *hash_map) {
if (!hash_map) {
int simple_archiver_hash_map_internal_rehash(SDArchiverHashMap **hash_map) {
if (!hash_map || !*hash_map) {
return 1;
}
SDArchiverHashMap new_hash_map;
new_hash_map.hash_fn = hash_map->hash_fn;
new_hash_map.buckets_size = (hash_map->buckets_size - 1) * 2 + 1;
SDArchiverHashMap *new_hash_map = malloc(sizeof(SDArchiverHashMap));
new_hash_map->buckets_size = (*hash_map)->buckets_size * 2;
// Pointers have the same size (at least on the same machine), so
// sizeof(void*) should be ok.
new_hash_map.buckets = malloc(sizeof(void *) * new_hash_map.buckets_size);
for (size_t idx = 0; idx < new_hash_map.buckets_size; ++idx) {
new_hash_map.buckets[idx] = simple_archiver_list_init();
new_hash_map->buckets = malloc(sizeof(void *) * new_hash_map->buckets_size);
for (unsigned int idx = 0; idx < new_hash_map->buckets_size; ++idx) {
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.
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) {
SDArchiverLLNode *node = hash_map->buckets[bucket_idx]->head;
SDArchiverLLNode *node = (*hash_map)->buckets[bucket_idx]->head;
while (node) {
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;
simple_archiver_hash_map_insert(&new_hash_map, data->value, data->key,
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.
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.
simple_archiver_hash_map_free(hash_map);
*hash_map = new_hash_map;
return 0;
}
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));
hash_map->hash_fn = hash_fn;
hash_map->buckets_size = SC_SA_DS_HASH_MAP_START_BUCKET_SIZE + 1;
hash_map->buckets_size = SC_SA_DS_HASH_MAP_START_BUCKET_SIZE;
// Pointers have the same size (at least on the same machine), so
// sizeof(void*) should be ok.
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->count = 0;
@ -164,30 +147,25 @@ SDArchiverHashMap *simple_archiver_hash_map_init_custom_hasher(
return hash_map;
}
void simple_archiver_hash_map_free_single_ptr(SDArchiverHashMap *hash_map) {
if (hash_map) {
for (size_t idx = 0; idx < hash_map->buckets_size; ++idx) {
SDArchiverLinkedList **linked_list = hash_map->buckets + idx;
void simple_archiver_hash_map_free(SDArchiverHashMap **hash_map) {
if (hash_map && *hash_map) {
for (unsigned int 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);
free(hash_map);
}
}
free((*hash_map)->buckets);
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;
}
}
int simple_archiver_hash_map_insert(SDArchiverHashMap *hash_map, void *value,
void *key, size_t key_size,
int simple_archiver_hash_map_insert(SDArchiverHashMap **hash_map, void *value,
void *key, unsigned int key_size,
void (*value_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);
}
@ -198,13 +176,15 @@ int simple_archiver_hash_map_insert(SDArchiverHashMap *hash_map, void *value,
data->value_cleanup_fn = value_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(
hash_map->buckets[hash], data,
(*hash_map)->buckets[hash], data,
simple_archiver_hash_map_internal_cleanup_data);
if (result == 0) {
++hash_map->count;
++(*hash_map)->count;
return 0;
} else {
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,
const void *key, size_t key_size) {
uint64_t hash = hash_map->hash_fn(key, key_size) % hash_map->buckets_size;
void *simple_archiver_hash_map_get(SDArchiverHashMap *hash_map, void *key,
unsigned int key_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;
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,
size_t key_size) {
uint64_t hash = hash_map->hash_fn(key, key_size) % hash_map->buckets_size;
unsigned int key_size) {
unsigned long long hash =
simple_archiver_hash_map_internal_key_to_hash(key, key_size) %
hash_map->buckets_size;
SDArchiverHashMapKeyData key_data;
key_data.key = key;
@ -264,34 +248,3 @@ int simple_archiver_hash_map_remove(SDArchiverHashMap *hash_map, void *key,
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;
}

View file

@ -1,56 +1,35 @@
// 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.
//
// `hash_map.h` is the header for a hash map implementation.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `hash_map.h` is the header for a hash map implementation.
*/
#ifndef 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
// Standard library includes.
#include <stddef.h>
#include <stdint.h>
// Local includes.
#include "linked_list.h"
typedef struct SDArchiverHashMap {
SDArchiverLinkedList **buckets;
size_t buckets_size;
size_t count;
uint64_t (*hash_fn)(const void *, size_t);
unsigned int buckets_size;
unsigned int count;
} SDArchiverHashMap;
uint64_t simple_archiver_hash_default_fn(const void *key, size_t key_size);
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);
/// 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.
/// If value_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
/// fail for the inserted key.
int simple_archiver_hash_map_insert(SDArchiverHashMap *hash_map, void *value,
void *key, size_t key_size,
int simple_archiver_hash_map_insert(SDArchiverHashMap **hash_map, void *value,
void *key, unsigned int key_size,
void (*value_cleanup_fn)(void *),
void (*key_cleanup_fn)(void *));
/// Returns NULL if not found.
void *simple_archiver_hash_map_get(const SDArchiverHashMap *hash_map,
const void *key, size_t key_size);
void *simple_archiver_hash_map_get(SDArchiverHashMap *hash_map, void *key,
unsigned int key_size);
/// Returns zero on success. Returns one if more than one entry was removed.
/// Otherwise returns non-zero and non-one value on error.
int simple_archiver_hash_map_remove(SDArchiverHashMap *hash_map, void *key,
size_t 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);
unsigned int key_size);
#endif

View file

@ -1,25 +1,23 @@
// 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.
//
// `linked_list.c` is the source for a linked list data structure.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `linked_list.c` is the source for a linked list data structure.
*/
#include "linked_list.h"
// Standard library includes.
#include <stdint.h>
#include <stdlib.h>
SDArchiverLinkedList *simple_archiver_list_init(void) {
@ -43,9 +41,9 @@ SDArchiverLinkedList *simple_archiver_list_init(void) {
return list;
}
void simple_archiver_list_free_single_ptr(SDArchiverLinkedList *list) {
if (list) {
SDArchiverLLNode *node = list->head;
void simple_archiver_list_free(SDArchiverLinkedList **list) {
if (list && *list) {
SDArchiverLLNode *node = (*list)->head;
SDArchiverLLNode *prev;
while (node) {
prev = node;
@ -60,13 +58,7 @@ void simple_archiver_list_free_single_ptr(SDArchiverLinkedList *list) {
}
}
free(list);
}
}
void simple_archiver_list_free(SDArchiverLinkedList **list) {
if (list && *list) {
simple_archiver_list_free_single_ptr(*list);
free(*list);
*list = NULL;
}
}
@ -120,10 +112,10 @@ int simple_archiver_list_remove(SDArchiverLinkedList *list,
return 0;
}
int32_t removed_count = 0;
int removed_count = 0;
SDArchiverLLNode *node = list->head;
int32_t iter_removed = 0;
int iter_removed = 0;
while (node) {
if (iter_removed == 0) {
node = node->next;
@ -186,7 +178,7 @@ int simple_archiver_list_remove_once(SDArchiverLinkedList *list,
return 0;
}
void *simple_archiver_list_get(const SDArchiverLinkedList *list,
void *simple_archiver_list_get(SDArchiverLinkedList *list,
int (*data_check_fn)(void *, void *),
void *user_data) {
if (!list) {

View file

@ -1,26 +1,24 @@
// 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.
//
// `linked_list.h` is the header for a linked list data structure.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `linked_list.h` is the header for a linked list data structure.
*/
#ifndef 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 {
struct SDArchiverLLNode *next;
struct SDArchiverLLNode *prev;
@ -31,15 +29,10 @@ typedef struct SDArchiverLLNode {
typedef struct SDArchiverLinkedList {
SDArchiverLLNode *head;
SDArchiverLLNode *tail;
size_t count;
unsigned int count;
} SDArchiverLinkedList;
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);
/// 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.
/// data_check_fn must return non-zero if the data passed to it is to be
/// returned.
void *simple_archiver_list_get(const SDArchiverLinkedList *list,
void *simple_archiver_list_get(SDArchiverLinkedList *list,
int (*data_check_fn)(void *, void *),
void *user_data);

View file

@ -1,52 +1,52 @@
// 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.
//
// `priority_heap.c` is the source for a priority heap implementation.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `priority_heap.c` is the source for a priority heap implementation.
*/
#include "priority_heap.h"
#include <stdlib.h>
void simple_archiver_priority_heap_internal_realloc(
SDArchiverPHeap *priority_heap) {
SDArchiverPHeap new_priority_heap;
SDArchiverPHeap **priority_heap) {
SDArchiverPHeap *new_priority_heap = malloc(sizeof(SDArchiverPHeap));
new_priority_heap.capacity = priority_heap->capacity * 2;
new_priority_heap.size = 0;
new_priority_heap.less_fn = priority_heap->less_fn;
new_priority_heap->capacity = (*priority_heap)->capacity * 2;
new_priority_heap->size = 0;
new_priority_heap->less_fn = (*priority_heap)->less_fn;
new_priority_heap.nodes =
calloc(new_priority_heap.capacity, sizeof(SDArchiverPHNode));
new_priority_heap->nodes =
calloc(new_priority_heap->capacity, sizeof(SDArchiverPHNode));
for (size_t idx = 1; idx < priority_heap->size + 1; ++idx) {
if (priority_heap->nodes[idx].is_valid != 0) {
for (unsigned int idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
if ((*priority_heap)->nodes[idx].is_valid != 0) {
simple_archiver_priority_heap_insert(
&new_priority_heap, priority_heap->nodes[idx].priority,
priority_heap->nodes[idx].data,
priority_heap->nodes[idx].data_cleanup_fn);
priority_heap->nodes[idx].is_valid = 0;
&new_priority_heap, (*priority_heap)->nodes[idx].priority,
(*priority_heap)->nodes[idx].data,
(*priority_heap)->nodes[idx].data_cleanup_fn);
(*priority_heap)->nodes[idx].is_valid = 0;
}
}
free(priority_heap->nodes);
simple_archiver_priority_heap_free(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;
}
@ -64,7 +64,7 @@ SDArchiverPHeap *simple_archiver_priority_heap_init(void) {
}
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));
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;
}
void simple_archiver_priority_heap_free_single_ptr(
SDArchiverPHeap *priority_heap) {
if (priority_heap) {
for (size_t idx = 1; idx < priority_heap->size + 1; ++idx) {
if (priority_heap->nodes[idx].is_valid != 0) {
if (priority_heap->nodes[idx].data_cleanup_fn) {
priority_heap->nodes[idx].data_cleanup_fn(
priority_heap->nodes[idx].data);
} else {
free(priority_heap->nodes[idx].data);
}
priority_heap->nodes[idx].is_valid = 0;
}
}
free(priority_heap->nodes);
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);
for (unsigned int idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
if ((*priority_heap)->nodes[idx].is_valid != 0) {
if ((*priority_heap)->nodes[idx].data_cleanup_fn) {
(*priority_heap)
->nodes[idx]
.data_cleanup_fn((*priority_heap)->nodes[idx].data);
} else {
free((*priority_heap)->nodes[idx].data);
}
(*priority_heap)->nodes[idx].is_valid = 0;
}
}
free((*priority_heap)->nodes);
free(*priority_heap);
*priority_heap = NULL;
}
}
void simple_archiver_priority_heap_insert(SDArchiverPHeap *priority_heap,
int64_t priority, void *data,
void simple_archiver_priority_heap_insert(SDArchiverPHeap **priority_heap,
long long priority, void *data,
void (*data_cleanup_fn)(void *)) {
if (!priority_heap) {
if (!priority_heap || !*priority_heap) {
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);
}
size_t hole = priority_heap->size + 1;
unsigned int hole = (*priority_heap)->size + 1;
while (hole > 1 &&
priority_heap->less_fn(priority,
priority_heap->nodes[hole / 2].priority) != 0) {
priority_heap->nodes[hole] = priority_heap->nodes[hole / 2];
(*priority_heap)
->less_fn(priority,
(*priority_heap)->nodes[hole / 2].priority) != 0) {
(*priority_heap)->nodes[hole] = (*priority_heap)->nodes[hole / 2];
hole /= 2;
}
priority_heap->nodes[hole].priority = priority;
priority_heap->nodes[hole].data = data;
priority_heap->nodes[hole].data_cleanup_fn = data_cleanup_fn;
priority_heap->nodes[hole].is_valid = 1;
(*priority_heap)->nodes[hole].priority = priority;
(*priority_heap)->nodes[hole].data = data;
(*priority_heap)->nodes[hole].data_cleanup_fn = data_cleanup_fn;
(*priority_heap)->nodes[hole].is_valid = 1;
++priority_heap->size;
++(*priority_heap)->size;
}
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];
priority_heap->nodes[priority_heap->size].is_valid = 0;
size_t hole = 1;
unsigned int hole = 1;
while (hole * 2 + 1 <= priority_heap->size) {
if (priority_heap->nodes[hole * 2].is_valid != 0 &&
priority_heap->nodes[hole * 2 + 1].is_valid != 0) {

View file

@ -1,31 +1,28 @@
// 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.
//
// `priority_heap.h` is the header for a priority heap implementation.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `priority_heap.h` is the header for a priority heap implementation.
*/
#ifndef 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
typedef struct SDArchiverPHNode {
int64_t priority;
long long priority;
void *data;
void (*data_cleanup_fn)(void *);
/// Is non-zero if valid.
@ -34,29 +31,23 @@ typedef struct SDArchiverPHNode {
typedef struct SDArchiverPHeap {
SDArchiverPHNode *nodes;
uint64_t capacity;
uint64_t size;
int (*less_fn)(int64_t, int64_t);
unsigned long long capacity;
unsigned long long size;
int (*less_fn)(long long, long long);
} SDArchiverPHeap;
/// Default "less" function to determine if a has higher priority than b.
/// 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_less_fn(
int (*less_fn)(int64_t, int64_t));
/// 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);
int (*less_fn)(long long, long long));
void simple_archiver_priority_heap_free(SDArchiverPHeap **priority_heap);
/// If data_cleanup_fn is NULL, then "free()" is used on data when freed.
void simple_archiver_priority_heap_insert(SDArchiverPHeap *priority_heap,
int64_t priority, void *data,
void simple_archiver_priority_heap_insert(SDArchiverPHeap **priority_heap,
long long priority, void *data,
void (*data_cleanup_fn)(void *));
/// Returns NULL if empty or if priority_heap is NULL.

View file

@ -1,37 +1,32 @@
// 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.
//
// `data_structures/test.c` is the source for testing data structure code.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `data_structures/test.c` is the source for testing data structure code.
*/
// Standard library includes.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Local includes.
#include "../algorithms/linear_congruential_gen.h"
#include "hash_map.h"
#include "linked_list.h"
#include "priority_heap.h"
#define SDARCHIVER_DS_TEST_HASH_MAP_ITER_SIZE 100
static int32_t checks_checked = 0;
static int32_t checks_passed = 0;
static int checks_checked = 0;
static int checks_passed = 0;
#define CHECK_TRUE(x) \
do { \
@ -66,27 +61,7 @@ int get_three_fn(void *data, __attribute__((unused)) void *ud) {
return strcmp(data, "three") == 0 ? 1 : 0;
}
int more_fn(int64_t a, int64_t 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 more_fn(long long a, long long b) { return a > b ? 1 : 0; }
int main(void) {
// Test LinkedList.
@ -141,37 +116,37 @@ int main(void) {
hash_map = simple_archiver_hash_map_init();
{
int32_t *value, *key;
int *value, *key;
for (uint32_t idx = 0; idx < 20; ++idx) {
value = malloc(sizeof(int32_t));
key = malloc(sizeof(int32_t));
for (unsigned int idx = 0; idx < 20; ++idx) {
value = malloc(sizeof(int));
key = malloc(sizeof(int));
*value = 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);
}
}
int32_t value, key;
int value, key;
void *value_ptr;
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(memcmp(value_ptr, &value, sizeof(int32_t)) == 0);
CHECK_TRUE(memcmp(value_ptr, &value, sizeof(int)) == 0);
}
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;
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) {
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) {
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 {
CHECK_TRUE(value_ptr == NULL);
}
@ -181,97 +156,18 @@ int main(void) {
// Rehash test for Memcheck.
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) {
uint32_t *copy_value = malloc(sizeof(uint32_t));
unsigned int *copy_value = malloc(sizeof(unsigned int));
*copy_value = idx;
uint32_t *copy_key = malloc(sizeof(uint32_t));
unsigned int *copy_key = malloc(sizeof(unsigned int));
*copy_key = idx;
simple_archiver_hash_map_insert(hash_map, copy_value, copy_key,
sizeof(uint32_t), NULL, NULL);
simple_archiver_hash_map_insert(&hash_map, copy_value, copy_key,
sizeof(unsigned int), NULL, NULL);
}
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.
{
SDArchiverPHeap *priority_heap = simple_archiver_priority_heap_init();
@ -280,13 +176,13 @@ int main(void) {
priority_heap = simple_archiver_priority_heap_init();
// Just 3 elements.
for (uint32_t idx = 0; idx < 3; ++idx) {
uint32_t *data = malloc(sizeof(uint32_t));
for (unsigned int idx = 0; idx < 3; ++idx) {
unsigned int *data = malloc(sizeof(unsigned int));
*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) {
uint32_t *data = simple_archiver_priority_heap_top(priority_heap);
for (unsigned int idx = 0; idx < 3; ++idx) {
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
CHECK_TRUE(*data == idx);
if (*data != idx) {
printf("idx is %u, data is %u\n", idx, *data);
@ -300,16 +196,16 @@ int main(void) {
}
// 100 elements.
uint32_t max = 100;
unsigned int max = 100;
for (uint32_t idx = 0; idx < max; ++idx) {
uint32_t *data = malloc(sizeof(uint32_t));
for (unsigned int idx = 0; idx < max; ++idx) {
unsigned int *data = malloc(sizeof(unsigned int));
*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) {
uint32_t *data = simple_archiver_priority_heap_top(priority_heap);
for (unsigned int idx = 0; idx < max; ++idx) {
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
CHECK_TRUE(*data == idx);
data = simple_archiver_priority_heap_pop(priority_heap);
CHECK_TRUE(*data == idx);
@ -317,14 +213,14 @@ int main(void) {
}
// Insert in reverse order.
for (uint32_t idx = max; idx-- > 0;) {
uint32_t *data = malloc(sizeof(uint32_t));
for (unsigned int idx = max; idx-- > 0;) {
unsigned int *data = malloc(sizeof(unsigned int));
*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) {
uint32_t *data = simple_archiver_priority_heap_top(priority_heap);
for (unsigned int idx = 0; idx < max; ++idx) {
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
CHECK_TRUE(*data == idx);
data = simple_archiver_priority_heap_pop(priority_heap);
CHECK_TRUE(*data == idx);
@ -332,30 +228,30 @@ int main(void) {
}
// Insert in random order.
uint32_t *array = malloc(sizeof(uint32_t) * max);
for (uint32_t idx = 0; idx < max; ++idx) {
unsigned int *array = malloc(sizeof(unsigned int) * max);
for (unsigned int idx = 0; idx < max; ++idx) {
array[idx] = idx;
}
// Deterministic randomization.
for (uint32_t idx = max - 1; idx-- > 0;) {
uint32_t other_idx =
simple_archiver_algo_lcg_defaults(idx) % (uint64_t)(idx + 1);
for (unsigned int idx = max - 1; idx-- > 0;) {
unsigned int other_idx = simple_archiver_algo_lcg_defaults(idx) %
(unsigned long long)(idx + 1);
if (max - 1 != other_idx) {
uint32_t temp = array[max - 1];
unsigned int temp = array[max - 1];
array[max - 1] = array[other_idx];
array[other_idx] = temp;
}
}
// Insert the deterministically randomized array.
for (uint32_t idx = 0; idx < max; ++idx) {
simple_archiver_priority_heap_insert(priority_heap, array[idx],
for (unsigned int idx = 0; idx < max; ++idx) {
simple_archiver_priority_heap_insert(&priority_heap, array[idx],
array + idx, no_free_fn);
}
for (uint32_t idx = 0; idx < max; ++idx) {
uint32_t *data = simple_archiver_priority_heap_top(priority_heap);
for (unsigned int idx = 0; idx < max; ++idx) {
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
CHECK_TRUE(*data == idx);
if (*data != idx) {
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.
priority_heap = simple_archiver_priority_heap_init();
for (uint32_t idx = 0; idx < max; ++idx) {
uint32_t *data = malloc(sizeof(uint32_t));
for (unsigned int idx = 0; idx < max; ++idx) {
unsigned int *data = malloc(sizeof(unsigned int));
*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);
// Reverse priority.
priority_heap = simple_archiver_priority_heap_init_less_fn(more_fn);
for (uint32_t idx = 0; idx < max; ++idx) {
uint32_t *data = malloc(sizeof(uint32_t));
for (unsigned int idx = 0; idx < max; ++idx) {
unsigned int *data = malloc(sizeof(unsigned int));
*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;) {
uint32_t *data = simple_archiver_priority_heap_top(priority_heap);
for (unsigned int idx = max; idx-- > 0;) {
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
CHECK_TRUE(*data == idx);
data = simple_archiver_priority_heap_pop(priority_heap);
CHECK_TRUE(*data == idx);
@ -400,30 +296,30 @@ int main(void) {
// Insert in random order with reverse-priority-heap.
priority_heap = simple_archiver_priority_heap_init_less_fn(more_fn);
array = malloc(sizeof(uint32_t) * max);
for (uint32_t idx = 0; idx < max; ++idx) {
array = malloc(sizeof(unsigned int) * max);
for (unsigned int idx = 0; idx < max; ++idx) {
array[idx] = idx;
}
// Deterministic randomization.
for (uint32_t idx = max - 1; idx-- > 0;) {
uint32_t other_idx =
simple_archiver_algo_lcg_defaults(idx) % (uint64_t)(idx + 1);
for (unsigned int idx = max - 1; idx-- > 0;) {
unsigned int other_idx = simple_archiver_algo_lcg_defaults(idx) %
(unsigned long long)(idx + 1);
if (max - 1 != other_idx) {
uint32_t temp = array[max - 1];
unsigned int temp = array[max - 1];
array[max - 1] = array[other_idx];
array[other_idx] = temp;
}
}
// Insert the deterministically randomized array.
for (uint32_t idx = 0; idx < max; ++idx) {
simple_archiver_priority_heap_insert(priority_heap, array[idx],
for (unsigned int idx = 0; idx < max; ++idx) {
simple_archiver_priority_heap_insert(&priority_heap, array[idx],
array + idx, no_free_fn);
}
for (uint32_t idx = max; idx-- > 0;) {
uint32_t *data = simple_archiver_priority_heap_top(priority_heap);
for (unsigned int idx = max; idx-- > 0;) {
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
CHECK_TRUE(*data == idx);
if (*data != idx) {
printf("idx is %u, data is %u\n", idx, *data);

View file

@ -1,257 +1,56 @@
// 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.
//
// `helpers.c` is the source for helpful/utility functions.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `helpers.c` is the source for helpful/utility functions.
*/
#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) {
union {
uint32_t i;
unsigned int i;
char c[4];
} bint = {0x01020304};
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) {
uint8_t c = ((uint8_t *)value)[0];
((uint8_t *)value)[0] = ((uint8_t *)value)[1];
((uint8_t *)value)[1] = c;
unsigned char c = ((unsigned char *)value)[0];
((unsigned char *)value)[0] = ((unsigned char *)value)[1];
((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) {
for (uint32_t i = 0; i < 2; ++i) {
uint8_t c = ((uint8_t *)value)[i];
((uint8_t *)value)[i] = ((uint8_t *)value)[3 - i];
((uint8_t *)value)[3 - i] = c;
for (unsigned int i = 0; i < 2; ++i) {
unsigned char c = ((unsigned char *)value)[i];
((unsigned char *)value)[i] = ((unsigned char *)value)[3 - i];
((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) {
for (uint32_t i = 0; i < 4; ++i) {
uint8_t c = ((uint8_t *)value)[i];
((uint8_t *)value)[i] = ((uint8_t *)value)[7 - i];
((uint8_t *)value)[7 - i] = c;
for (unsigned int i = 0; i < 4; ++i) {
unsigned char c = ((unsigned char *)value)[i];
((unsigned char *)value)[i] = ((unsigned char *)value)[7 - i];
((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;
}

View file

@ -1,66 +1,36 @@
// 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.
//
// `helpers.h` is the header for helpful/utility functions.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `helpers.h` is the header for helpful/utility functions.
*/
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_HELPERS_H_
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_HELPERS_H_
// Standard library includes.
#include <stdint.h>
#include <stdio.h>
static const uint32_t MAX_SYMBOLIC_LINK_SIZE = 512;
static const unsigned int MAX_SYMBOLIC_LINK_SIZE = 512;
/// Returns non-zero if this system is big-endian.
int simple_archiver_helper_is_big_endian(void);
/// 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.
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.
void simple_archiver_helper_64_bit_be(uint64_t *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);
void simple_archiver_helper_64_bit_be(unsigned long long *value);
#endif

View file

@ -1,181 +1,50 @@
// 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.
//
// `main.c` is the entry-point of this software/program.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `main.c` is the entry-point of this software/program.
*/
#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"
int print_list_fn(void *data, __attribute__((unused)) void *ud) {
const SDArchiverFileInfo *file_info = data;
if (file_info->link_dest == NULL) {
fprintf(stderr, " REGULAR FILE: %s\n", file_info->filename);
printf(" REGULAR FILE: %s\n", file_info->filename);
} else {
fprintf(stderr, " SYMBOLIC LINK: %s -> %s\n", file_info->filename,
printf(" SYMBOLIC LINK: %s -> %s\n", file_info->filename,
file_info->link_dest);
}
return 0;
}
int main(int argc, const char **argv) {
__attribute__((
cleanup(simple_archiver_free_parsed))) SDArchiverParsed parsed =
simple_archiver_create_parsed();
if (simple_archiver_parse_args(argc, argv, &parsed)) {
fprintf(stderr, "Failed to parse args.\n");
return 7;
}
if (!parsed.filename && (parsed.flags & 0x10) == 0) {
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;
}
}
__attribute__((cleanup(simple_archiver_free_parsed)))
SDArchiverParsed parsed = simple_archiver_create_parsed();
simple_archiver_parse_args(argc, argv, &parsed);
SDArchiverParsedStatus parsed_status;
__attribute__((cleanup(simple_archiver_list_free)))
SDArchiverLinkedList *filenames =
simple_archiver_parsed_to_filenames(&parsed, &parsed_status);
if (!filenames || parsed_status != SDAPS_SUCCESS) {
fprintf(stderr, "ERROR: %s!\n",
simple_archiver_parsed_status_to_str(parsed_status));
return 8;
}
simple_archiver_parsed_to_filenames(&parsed);
if (filenames->count > 0) {
fprintf(stderr, "Filenames:\n");
puts("Filenames:");
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;
}

View file

@ -1,20 +1,20 @@
// 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.
//
// `parser.c` is the source file for parsing args.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `parser.c` is the source file for parsing args.
*/
#include "parser.h"
@ -31,6 +31,12 @@
#include <sys/stat.h>
#include <sys/types.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
#include "data_structures/hash_map.h"
@ -39,15 +45,15 @@
#include "parser_internal.h"
/// 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) {
size_t idx = 0;
size_t known_good_idx = 0;
const size_t length = strlen(filename);
unsigned int idx = 0;
unsigned int known_good_idx = 0;
const unsigned int length = strlen(filename);
// 0b0001 - checked that idx char is '.'
// 0b0010 - checked that idx char is '/'
size_t flags = 0;
unsigned int flags = 0;
for (; idx < length; ++idx) {
if ((flags & 3) == 0) {
@ -61,7 +67,11 @@ size_t simple_archiver_parser_internal_get_first_non_current_idx(
} else if ((flags & 3) == 1) {
if (filename[idx] == 0) {
return known_good_idx;
#if SIMPLE_ARCHIVER_PLATFORM != SIMPLE_ARCHIVER_PLATFORM_WINDOWS
} else if (filename[idx] == '/') {
#else
} else if (filename[idx] == '\\') {
#endif
flags |= 2;
} else {
return idx - 1;
@ -69,7 +79,11 @@ size_t simple_archiver_parser_internal_get_first_non_current_idx(
} else if ((flags & 3) == 3) {
if (filename[idx] == 0) {
return known_good_idx;
#if SIMPLE_ARCHIVER_PLATFORM != SIMPLE_ARCHIVER_PLATFORM_WINDOWS
} else if (filename[idx] == '/') {
#else
} else if (filename[idx] == '\\') {
#endif
continue;
} else if (filename[idx] == '.') {
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) {
size_t len = strlen(filename);
size_t idx;
int len = strlen(filename);
int idx;
for (idx = len; idx-- > 0;) {
if (filename[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) {
SDArchiverFileInfo *file_info = data;
if (file_info) {
@ -123,6 +165,8 @@ int list_get_last_fn(void *data, void *ud) {
return 0;
}
void container_no_free_fn(__attribute__((unused)) void *data) { return; }
int list_remove_same_str_fn(void *data, void *ud) {
if (strcmp((char *)data, (char *)ud) == 0) {
return 1;
@ -131,86 +175,26 @@ int list_remove_same_str_fn(void *data, void *ud) {
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) {
fprintf(stderr, "Usage flags:\n");
fprintf(stderr, "-c : create archive file\n");
fprintf(stderr, "-x : extract archive file\n");
fprintf(stderr, "-t : examine archive file\n");
fprintf(stderr, "-f <filename> : filename to work on\n");
fprintf(stderr,
" Use \"-f -\" to work on stdout when creating archive or stdin "
"when reading archive\n");
fprintf(stderr, " NOTICE: \"-f\" is not affected by \"-C\"!\n");
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");
puts("Usage flags:");
puts("-c : create archive file");
puts("-x : extract archive file");
puts("-f <filename> : filename to work on");
puts("--compressor <full_compress_cmd> : requires --decompressor");
puts("--decompressor <full_decompress_cmd> : requires --compressor");
puts("-- : specifies remaining arguments are files to archive/extract");
puts("If creating archive file, remaining args specify files to archive.");
puts("If extracting archive file, remaining args specify files to extract.");
}
SDArchiverParsed simple_archiver_create_parsed(void) {
SDArchiverParsed parsed;
parsed.flags = 0x40;
parsed.flags = 0;
parsed.filename = NULL;
parsed.compressor = NULL;
parsed.decompressor = NULL;
parsed.working_files = NULL;
parsed.temp_dir = NULL;
parsed.user_cwd = NULL;
parsed.write_version = 1;
parsed.minimum_chunk_size = 4194304;
return parsed;
}
@ -234,159 +218,52 @@ int simple_archiver_parse_args(int argc, const char **argv,
--argc;
++argv;
int_fast8_t is_remaining_args = 0;
int is_remaining_args = 0;
while (argc > 0) {
if (!is_remaining_args) {
if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0) {
simple_archiver_free_parsed(out);
simple_archiver_print_usage();
exit(0);
} else if (strcmp(argv[0], "-c") == 0) {
// unset first two bits.
out->flags &= 0xFFFFFFFC;
if (strcmp(argv[0], "-c") == 0) {
// unset first bit.
out->flags &= 0xFFFFFFFE;
} else if (strcmp(argv[0], "-x") == 0) {
// unset first two bits.
out->flags &= 0xFFFFFFFC;
// set first bit.
out->flags |= 0x1;
} else if (strcmp(argv[0], "-t") == 0) {
// unset first two bits.
out->flags &= 0xFFFFFFFC;
// set second bit.
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;
} else if (strcmp(argv[0], "-f") == 0 && argc > 1) {
int size = strlen(argv[1]) + 1;
out->filename = malloc(size);
strncpy(out->filename, argv[1], size);
}
--argc;
++argv;
} else if (strcmp(argv[0], "-C") == 0) {
if (argc < 2) {
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;
} else if (strcmp(argv[0], "--compressor") == 0 && argc > 1) {
int size = strlen(argv[1]) + 1;
out->compressor = malloc(size);
strncpy(out->compressor, argv[1], size);
--argc;
++argv;
} else if (strcmp(argv[0], "--decompressor") == 0) {
if (argc < 2) {
fprintf(stderr, "--decompressor specfied but missing argument!\n");
simple_archiver_print_usage();
return 1;
}
size_t size = strlen(argv[1]) + 1;
} else if (strcmp(argv[0], "--decompressor") == 0 && argc > 1) {
int size = strlen(argv[1]) + 1;
out->decompressor = malloc(size);
strncpy(out->decompressor, argv[1], size);
--argc;
++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) {
is_remaining_args = 1;
} else if (argv[0][0] != '-') {
is_remaining_args = 1;
continue;
} else {
fprintf(stderr, "ERROR: Got invalid arg \"%s\"!\n", argv[0]);
simple_archiver_print_usage();
return 1;
}
} else {
if (out->working_files == NULL) {
out->working_files = malloc(sizeof(char *) * 2);
size_t arg_idx =
simple_archiver_parser_internal_get_first_non_current_idx(argv[0]);
size_t arg_length = strlen(argv[0] + arg_idx) + 1;
unsigned int arg_idx =
simple_archiver_parser_internal_filename_idx(argv[0]);
int arg_length = strlen(argv[0] + arg_idx) + 1;
out->working_files[0] = malloc(arg_length);
strncpy(out->working_files[0], argv[0] + arg_idx, arg_length);
simple_archiver_parser_internal_remove_end_slash(out->working_files[0]);
out->working_files[1] = NULL;
} else {
size_t working_size = 1;
int working_size = 1;
char **ptr = out->working_files;
while (ptr && *ptr) {
++working_size;
@ -399,9 +276,9 @@ int simple_archiver_parse_args(int argc, const char **argv,
// Set new actual last element to NULL.
out->working_files[working_size] = NULL;
size_t arg_idx =
simple_archiver_parser_internal_get_first_non_current_idx(argv[0]);
size_t size = strlen(argv[0] + arg_idx) + 1;
unsigned int arg_idx =
simple_archiver_parser_internal_filename_idx(argv[0]);
int size = strlen(argv[0] + arg_idx) + 1;
// Set last element to the arg.
out->working_files[working_size - 1] = malloc(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;
}
if (!out->temp_dir) {
out->temp_dir = "./";
}
return 0;
}
@ -437,7 +310,7 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed) {
}
if (parsed->working_files) {
char **ptr = parsed->working_files;
uint32_t idx = 0;
unsigned int idx = 0;
while (ptr[idx]) {
free(ptr[idx]);
++idx;
@ -448,7 +321,7 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed) {
}
SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
const SDArchiverParsed *parsed, SDArchiverParsedStatus *status_out) {
const SDArchiverParsed *parsed) {
SDArchiverLinkedList *files_list = simple_archiver_list_init();
__attribute__((cleanup(simple_archiver_hash_map_free)))
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 || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
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) {
struct stat st;
memset(&st, 0, sizeof(struct stat));
char *file_path = *iter;
fstatat(AT_FDCWD, file_path, &st, AT_SYMLINK_NOFOLLOW);
fstatat(AT_FDCWD, *iter, &st, AT_SYMLINK_NOFOLLOW);
if ((st.st_mode & S_IFMT) == S_IFREG || (st.st_mode & S_IFMT) == S_IFLNK) {
// Is a regular file or a symbolic link.
size_t len = strlen(file_path) + 1;
int len = strlen(*iter) + 1;
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) {
SDArchiverFileInfo *file_info = malloc(sizeof(SDArchiverFileInfo));
file_info->filename = filename;
if ((st.st_mode & S_IFMT) == S_IFLNK) {
// Is a symlink.
file_info->link_dest = malloc(MAX_SYMBOLIC_LINK_SIZE);
ssize_t count = readlinkat(AT_FDCWD, filename, file_info->link_dest,
MAX_SYMBOLIC_LINK_SIZE - 1);
@ -493,42 +351,19 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
file_info->link_dest[count] = 0;
} else {
// Failure.
fprintf(stderr,
"WARNING: Could not get link info for file \"%s\"!\n",
file_info->filename);
free(file_info->link_dest);
free(file_info);
free(filename);
continue;
}
} else {
// Is a regular file.
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_internal_free_file_info_fn);
simple_archiver_hash_map_insert(
hash_map, &hash_map_sentinel, filename, len - 1,
simple_archiver_helper_datastructure_cleanup_nop,
simple_archiver_helper_datastructure_cleanup_nop);
simple_archiver_hash_map_insert(&hash_map, &hash_map_sentinel, filename,
len - 1, container_no_free_fn,
container_no_free_fn);
} else {
free(filename);
}
@ -536,9 +371,7 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
// Is a directory.
__attribute__((cleanup(simple_archiver_list_free)))
SDArchiverLinkedList *dir_list = simple_archiver_list_init();
simple_archiver_list_add(
dir_list, file_path,
simple_archiver_helper_datastructure_cleanup_nop);
simple_archiver_list_add(dir_list, *iter, container_no_free_fn);
char *next;
while (dir_list->count != 0) {
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) {
continue;
}
// fprintf(stderr, "dir entry in %s is %s\n", next,
// dir_entry->d_name);
size_t combined_size = strlen(next) + strlen(dir_entry->d_name) + 2;
printf("dir entry in %s is %s\n", next, dir_entry->d_name);
int combined_size = strlen(next) + strlen(dir_entry->d_name) + 2;
char *combined_path = malloc(combined_size);
snprintf(combined_path, combined_size, "%s/%s", next,
dir_entry->d_name);
size_t valid_idx =
simple_archiver_parser_internal_get_first_non_current_idx(
combined_path);
unsigned int valid_idx =
simple_archiver_parser_internal_filename_idx(combined_path);
if (valid_idx > 0) {
char *new_path = malloc(combined_size - valid_idx);
strncpy(new_path, combined_path + valid_idx,
@ -582,7 +413,6 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
malloc(sizeof(SDArchiverFileInfo));
file_info->filename = combined_path;
if ((st.st_mode & S_IFMT) == S_IFLNK) {
// Is a symlink.
file_info->link_dest = malloc(MAX_SYMBOLIC_LINK_SIZE);
ssize_t count =
readlinkat(AT_FDCWD, combined_path, file_info->link_dest,
@ -599,36 +429,15 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
continue;
}
} else {
// Is a regular file.
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(
files_list, file_info,
simple_archiver_internal_free_file_info_fn);
simple_archiver_hash_map_insert(
hash_map, &hash_map_sentinel, combined_path,
combined_size - 1,
simple_archiver_helper_datastructure_cleanup_nop,
simple_archiver_helper_datastructure_cleanup_nop);
&hash_map, &hash_map_sentinel, combined_path,
combined_size - 1, container_no_free_fn,
container_no_free_fn);
} else {
free(combined_path);
}
@ -651,48 +460,88 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
// 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
// Remove leading "./" entries from files_list.
for (SDArchiverLLNode *iter = files_list->head->next;
iter != files_list->tail; iter = iter->next) {
SDArchiverFileInfo *file_info = iter->data;
// Remove leading "./" entries from files_list.
size_t idx = simple_archiver_parser_internal_get_first_non_current_idx(
file_info->filename);
unsigned int idx =
simple_archiver_parser_internal_filename_idx(file_info->filename);
if (idx > 0) {
size_t len = strlen(file_info->filename) + 1 - idx;
int len = strlen(file_info->filename) + 1 - idx;
char *substr = malloc(len);
strncpy(substr, file_info->filename + idx, len);
free(file_info->filename);
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;
}

View file

@ -1,44 +1,31 @@
// 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.
//
// `parser.h` is the header for parsing args.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `parser.h` is the header for parsing args.
*/
#ifndef 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"
typedef struct SDArchiverParsed {
/// Each bit is a flag.
/// 0b xxxx xx00 - is creating.
/// 0b xxxx xx01 - is extracting.
/// 0b xxxx xx10 - is checking/examining.
/// 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;
/// 0b0 - is creating.
/// 0b1 - is extracting.
unsigned int flags;
/// Null-terminated string.
char *filename;
/// Null-terminated string.
@ -47,17 +34,8 @@ typedef struct SDArchiverParsed {
char *decompressor;
/// Null-terminated strings in array of strings.
/// Last entry should be NULL.
/// Determines a "white-list" of files to extract when extracting.
/// Not used when extracting.
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;
typedef struct SDArchiverFileInfo {
@ -66,14 +44,6 @@ typedef struct SDArchiverFileInfo {
char *link_dest;
} 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);
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.
/// This means the user should have a SDArchiverParsed variable
/// and it should be passed with e.g. "&var".
/// Returns 0 on success.
int simple_archiver_parse_args(int argc, const char **argv,
SDArchiverParsed *out);
@ -89,6 +58,6 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed);
/// Each entry in the linked list is an SDArchiverFileInfo object.
SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
const SDArchiverParsed *parsed, SDArchiverParsedStatus *status_out);
const SDArchiverParsed *parsed);
#endif

View file

@ -1,27 +1,26 @@
// 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.
//
// `parser_internal.h` is the header for parsing args with internal functions.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `parser_internal.h` is the header for parsing args with internal functions.
*/
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_INTERNAL_H_
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_INTERNAL_H_
#include "parser.h"
size_t simple_archiver_parser_internal_get_first_non_current_idx(
const char *filename);
unsigned int simple_archiver_parser_internal_filename_idx(const char *filename);
#endif

View file

@ -1,21 +1,21 @@
// 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.
//
// `platforms.h` is the header that determines what platform this program is
// compiled for.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `platforms.h` is the header that determines what platform this program is
* compiled for.
*/
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_PLATFORMS_H_
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_PLATFORMS_H_

View file

@ -1,34 +1,30 @@
// 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.
//
// `test.c` is the source for testing code.
/*
* Copyright 2024 Stephen Seo
*
* 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.
*
* `test.c` is the source for testing code.
*/
// Standard library includes.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Local includes.
#include "archiver.h"
#include "helpers.h"
#include "parser_internal.h"
static int32_t checks_checked = 0;
static int32_t checks_passed = 0;
static int checks_checked = 0;
static int checks_passed = 0;
#define CHECK_TRUE(x) \
do { \
@ -48,55 +44,38 @@ static int32_t checks_passed = 0;
++checks_passed; \
} \
} 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) {
// Test parser.
{
size_t idx =
simple_archiver_parser_internal_get_first_non_current_idx("test");
unsigned int idx = simple_archiver_parser_internal_filename_idx("test");
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);
idx = simple_archiver_parser_internal_get_first_non_current_idx("././test");
idx = simple_archiver_parser_internal_filename_idx("././test");
CHECK_TRUE(idx == 4);
idx = simple_archiver_parser_internal_get_first_non_current_idx(
"././//././//./test");
idx = simple_archiver_parser_internal_filename_idx("././//././//./test");
CHECK_TRUE(idx == 14);
idx = simple_archiver_parser_internal_get_first_non_current_idx(
"/././//././//./test");
idx = simple_archiver_parser_internal_filename_idx("/././//././//./test");
CHECK_TRUE(idx == 0);
idx = simple_archiver_parser_internal_get_first_non_current_idx(
".derp/.//././//./test");
idx = simple_archiver_parser_internal_filename_idx(".derp/.//././//./test");
CHECK_TRUE(idx == 0);
idx = simple_archiver_parser_internal_get_first_non_current_idx(
"././/.derp/.///./test");
idx = simple_archiver_parser_internal_filename_idx("././/.derp/.///./test");
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);
idx = simple_archiver_parser_internal_get_first_non_current_idx(
"././/.//.//.");
idx = simple_archiver_parser_internal_filename_idx("././/.//.//.");
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);
SDArchiverParsed parsed = simple_archiver_create_parsed();
@ -109,7 +88,7 @@ int main(void) {
CHECK_TRUE(strcmp("doop", parsed.working_files[1]) == 0);
CHECK_TRUE(parsed.working_files[2] == NULL);
CHECK_TRUE(parsed.filename == NULL);
CHECK_TRUE(parsed.flags == 0x40);
CHECK_TRUE(parsed.flags == 0);
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(parsed.working_files[3] == NULL);
CHECK_TRUE(strcmp("the_filename", parsed.filename) == 0);
CHECK_TRUE(parsed.flags == 0x41);
CHECK_TRUE(parsed.flags == 1);
simple_archiver_free_parsed(&parsed);
}
@ -135,139 +114,62 @@ int main(void) {
{
// Only if system is little-endian.
if (simple_archiver_helper_is_big_endian() == 0) {
uint16_t u16 = 0x0102;
CHECK_TRUE(((uint8_t *)&u16)[0] == 2);
CHECK_TRUE(((uint8_t *)&u16)[1] == 1);
unsigned short u16 = 0x0102;
CHECK_TRUE(((unsigned char *)&u16)[0] == 2);
CHECK_TRUE(((unsigned char *)&u16)[1] == 1);
simple_archiver_helper_16_bit_be(&u16);
CHECK_TRUE(((uint8_t *)&u16)[0] == 1);
CHECK_TRUE(((uint8_t *)&u16)[1] == 2);
CHECK_TRUE(((unsigned char *)&u16)[0] == 1);
CHECK_TRUE(((unsigned char *)&u16)[1] == 2);
simple_archiver_helper_16_bit_be(&u16);
CHECK_TRUE(((uint8_t *)&u16)[0] == 2);
CHECK_TRUE(((uint8_t *)&u16)[1] == 1);
CHECK_TRUE(((unsigned char *)&u16)[0] == 2);
CHECK_TRUE(((unsigned char *)&u16)[1] == 1);
uint32_t u32 = 0x01020304;
CHECK_TRUE(((uint8_t *)&u32)[0] == 4);
CHECK_TRUE(((uint8_t *)&u32)[1] == 3);
CHECK_TRUE(((uint8_t *)&u32)[2] == 2);
CHECK_TRUE(((uint8_t *)&u32)[3] == 1);
unsigned int u32 = 0x01020304;
CHECK_TRUE(((unsigned char *)&u32)[0] == 4);
CHECK_TRUE(((unsigned char *)&u32)[1] == 3);
CHECK_TRUE(((unsigned char *)&u32)[2] == 2);
CHECK_TRUE(((unsigned char *)&u32)[3] == 1);
simple_archiver_helper_32_bit_be(&u32);
CHECK_TRUE(((uint8_t *)&u32)[0] == 1);
CHECK_TRUE(((uint8_t *)&u32)[1] == 2);
CHECK_TRUE(((uint8_t *)&u32)[2] == 3);
CHECK_TRUE(((uint8_t *)&u32)[3] == 4);
CHECK_TRUE(((unsigned char *)&u32)[0] == 1);
CHECK_TRUE(((unsigned char *)&u32)[1] == 2);
CHECK_TRUE(((unsigned char *)&u32)[2] == 3);
CHECK_TRUE(((unsigned char *)&u32)[3] == 4);
simple_archiver_helper_32_bit_be(&u32);
CHECK_TRUE(((uint8_t *)&u32)[0] == 4);
CHECK_TRUE(((uint8_t *)&u32)[1] == 3);
CHECK_TRUE(((uint8_t *)&u32)[2] == 2);
CHECK_TRUE(((uint8_t *)&u32)[3] == 1);
CHECK_TRUE(((unsigned char *)&u32)[0] == 4);
CHECK_TRUE(((unsigned char *)&u32)[1] == 3);
CHECK_TRUE(((unsigned char *)&u32)[2] == 2);
CHECK_TRUE(((unsigned char *)&u32)[3] == 1);
uint64_t u64 = 0x010203040a0b0c0d;
CHECK_TRUE(((uint8_t *)&u64)[0] == 0xd);
CHECK_TRUE(((uint8_t *)&u64)[1] == 0xc);
CHECK_TRUE(((uint8_t *)&u64)[2] == 0xb);
CHECK_TRUE(((uint8_t *)&u64)[3] == 0xa);
CHECK_TRUE(((uint8_t *)&u64)[4] == 0x4);
CHECK_TRUE(((uint8_t *)&u64)[5] == 0x3);
CHECK_TRUE(((uint8_t *)&u64)[6] == 0x2);
CHECK_TRUE(((uint8_t *)&u64)[7] == 0x1);
unsigned long long u64 = 0x010203040a0b0c0d;
CHECK_TRUE(((unsigned char *)&u64)[0] == 0xd);
CHECK_TRUE(((unsigned char *)&u64)[1] == 0xc);
CHECK_TRUE(((unsigned char *)&u64)[2] == 0xb);
CHECK_TRUE(((unsigned char *)&u64)[3] == 0xa);
CHECK_TRUE(((unsigned char *)&u64)[4] == 0x4);
CHECK_TRUE(((unsigned char *)&u64)[5] == 0x3);
CHECK_TRUE(((unsigned char *)&u64)[6] == 0x2);
CHECK_TRUE(((unsigned char *)&u64)[7] == 0x1);
simple_archiver_helper_64_bit_be(&u64);
CHECK_TRUE(((uint8_t *)&u64)[0] == 0x1);
CHECK_TRUE(((uint8_t *)&u64)[1] == 0x2);
CHECK_TRUE(((uint8_t *)&u64)[2] == 0x3);
CHECK_TRUE(((uint8_t *)&u64)[3] == 0x4);
CHECK_TRUE(((uint8_t *)&u64)[4] == 0xa);
CHECK_TRUE(((uint8_t *)&u64)[5] == 0xb);
CHECK_TRUE(((uint8_t *)&u64)[6] == 0xc);
CHECK_TRUE(((uint8_t *)&u64)[7] == 0xd);
CHECK_TRUE(((unsigned char *)&u64)[0] == 0x1);
CHECK_TRUE(((unsigned char *)&u64)[1] == 0x2);
CHECK_TRUE(((unsigned char *)&u64)[2] == 0x3);
CHECK_TRUE(((unsigned char *)&u64)[3] == 0x4);
CHECK_TRUE(((unsigned char *)&u64)[4] == 0xa);
CHECK_TRUE(((unsigned char *)&u64)[5] == 0xb);
CHECK_TRUE(((unsigned char *)&u64)[6] == 0xc);
CHECK_TRUE(((unsigned char *)&u64)[7] == 0xd);
simple_archiver_helper_64_bit_be(&u64);
CHECK_TRUE(((uint8_t *)&u64)[0] == 0xd);
CHECK_TRUE(((uint8_t *)&u64)[1] == 0xc);
CHECK_TRUE(((uint8_t *)&u64)[2] == 0xb);
CHECK_TRUE(((uint8_t *)&u64)[3] == 0xa);
CHECK_TRUE(((uint8_t *)&u64)[4] == 0x4);
CHECK_TRUE(((uint8_t *)&u64)[5] == 0x3);
CHECK_TRUE(((uint8_t *)&u64)[6] == 0x2);
CHECK_TRUE(((uint8_t *)&u64)[7] == 0x1);
CHECK_TRUE(((unsigned char *)&u64)[0] == 0xd);
CHECK_TRUE(((unsigned char *)&u64)[1] == 0xc);
CHECK_TRUE(((unsigned char *)&u64)[2] == 0xb);
CHECK_TRUE(((unsigned char *)&u64)[3] == 0xa);
CHECK_TRUE(((unsigned char *)&u64)[4] == 0x4);
CHECK_TRUE(((unsigned char *)&u64)[5] == 0x3);
CHECK_TRUE(((unsigned char *)&u64)[6] == 0x2);
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 passed: %u\n", checks_passed);
return checks_passed == checks_checked ? 0 : 1;

Binary file not shown.

Binary file not shown.