Compare commits

...

70 commits

Author SHA1 Message Date
2252b7d732 Refactor data_structures/hash_map rehash fn
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-09-11 12:46:27 +09:00
88d228644e Cleanup related to change of hash-map insert fn
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-09-09 17:10:17 +09:00
3408183b39 Refactor hash_map insert to use single ptr
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 6s
Previous implementation used a double ptr for inserting into a hash map.
This refactoring allows for only needing to use a single ptr.

Fixes #17 .
2024-09-09 11:25:00 +09:00
425fa77900 Fixes related to compiler warnings
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
Fix integer mismatch usages/comparisons.

Use "size_t" instead of "unsigned int" for data structures.
2024-09-06 14:28:31 +09:00
4cd660ffd5 hash_map: Use const where applicable
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-09-04 15:30:37 +09:00
dd1a8abdd4 hash_map iter fn: const key-value, update unittest
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 52s
2024-09-01 15:40:17 +09:00
aeb8eff350 Impl. hash_map iter, clang-format
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 52s
2024-09-01 15:13:26 +09:00
2e46790ece Switch to ISC License
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 52s
2024-08-30 11:36:11 +09:00
4f0fdfa602 Cleanup/refactorings
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
Moved common functions to helpers.h, and similar cleanup/refactorings.
2024-07-29 16:31:10 +09:00
c30f2f3fd2 Add NULL check when printing cwd on temp file fail
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-07-26 16:25:02 +09:00
4e6b2b8f5a More verbose log when failing to create temp file
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-07-26 16:23:50 +09:00
49bd4b5c76 Improve file-count logs
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 51s
2024-07-26 16:18:07 +09:00
4479fdce62 Add check if file(s) are readable when archiving
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 52s
Also some minor changes like more error logs and refactorings.
2024-07-26 16:01:39 +09:00
bdbbf7dc16 "Fix" windows build
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
TODO:
    Native Windows implementation.
2024-07-26 13:23:51 +09:00
40567d5a3a Fix "-C <dir>" usage
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
TODO:
    Merge re-used functions into "helpers".
2024-07-26 13:17:11 +09:00
c095c40644 Update README.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 6s
2024-07-26 12:42:43 +09:00
d1609849a1 Impl. "-C <dir>", refactorings
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-07-26 12:39:56 +09:00
b96d26de81 Update README.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-07-25 10:46:00 +09:00
0b63dd12ee Impl. being able to set dir for temporary files
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 11s
2024-07-25 10:42:31 +09:00
c55864a51b Refactor parser, setup for different temp dir 2024-07-25 10:26:34 +09:00
a133b3a49d Minor help-text/doc fix
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-07-24 15:27:05 +09:00
ffa5885ed6 Update README.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-24 15:24:44 +09:00
c1dde797b5 Update README.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-07-24 15:22:39 +09:00
ae4bcff24a Update README.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-07-24 15:21:04 +09:00
852c931d8b Comment out debug print
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-07-24 15:05:01 +09:00
55cd3b2659 "Fix" windows build 2024-07-24 15:03:34 +09:00
d8068a6f5f Impl. link extracting
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 52s
2024-07-24 14:52:35 +09:00
b28e384149 Add abs_path check when referring out of archive
If absolute paths are enabled, and a link points to something outside of
the archive, a flag is set on the symlink entry to prefer absolute
paths.
2024-07-24 14:31:38 +09:00
c6d2d50c0f Create "set" of filenames for use when archiving 2024-07-24 14:05:05 +09:00
2f4c0d3679 Split off code into internal helper function
Function gets absolute path to given filename.
2024-07-24 14:04:19 +09:00
9d105bc3a5 Minor tweak to includes in archiver 2024-07-24 14:03:03 +09:00
b90b7ae64f Update file_format.md 2024-07-24 13:33:46 +09:00
e9c327a62f Add "--no-abs-symlink" to not store absolute paths 2024-07-24 13:32:39 +09:00
d939939723 Impl. archiving abs/rel-paths for symbolic links
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
TODO:
    Choosing the ideal link based on if link points to an archived file.
    Extracting (creating) symbolic links from archive.
2024-07-23 15:28:59 +09:00
4b730b4824 Set user permission bits if non-Unix platform
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 51s
2024-07-23 14:19:40 +09:00
3dc9e88e24 Fix improper handling of unsupported symlinks
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
TODO:
    Archive symlinks properly.
    Extract symlinks properly.
2024-07-22 14:01:01 +09:00
dcff34e06b Support "-f -" to read/write archive stdin/stdout
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 6s
2024-07-19 16:45:51 +09:00
613354034d Use SIGPIPE handling on UNIX platforms
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-19 12:15:20 +09:00
c1c4f048cb Improve error handling
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
Handle SIGPIPE.
2024-07-19 12:13:54 +09:00
e7db978a3c More robust error handling
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-07-19 11:29:14 +09:00
d30c30995f Fix robust cleanup of temporary compressed file
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-18 22:56:12 +09:00
5f8d6b0c0b More robust handling of bad compressor cmd
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-18 22:32:04 +09:00
72d2db378f Do not create archive file on failure 2024-07-18 22:09:03 +09:00
7046dfb1e5 More robust cleanup of temporary file 2024-07-18 22:08:48 +09:00
1d85ddd0e9 Improve error print
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-18 18:55:42 +09:00
ecb0c751d8 Print permissions when in "-t" mode
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 3s
2024-07-18 15:57:45 +09:00
99e1a2d850 Fix error when checking with "-t"
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-18 15:49:22 +09:00
c4ff2347ff Improve error printing
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-07-18 15:46:27 +09:00
c18e772928 Remove unnecessary "include"s
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-18 15:36:09 +09:00
735d2b4f7a Comment out debug print
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-18 15:32:48 +09:00
4985672ea2 Impl. extract "white-list"
When extracting, the given positional args will determine a "white-list"
of files to extract. If no positional args are given, then all files in
the archive will be extracted.
2024-07-18 15:30:05 +09:00
0a6afe6148 Improve error handling when parsing/extracting
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-18 14:39:01 +09:00
5b3a7e3994 Fix extracting when no compressor was used
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-18 14:17:50 +09:00
4cb4184338 Store/restore file permissions
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-18 14:02:15 +09:00
17f89e5a68 "Fix" windows build
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-18 13:33:35 +09:00
da2a0f7b1c Implement extracting from archive
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
TODO:
    Storing file permissions to be extracted with same permissions.
    Archiving and extracting symbolic links.
2024-07-18 13:27:32 +09:00
0449ab389e Add helper to create dirs from filepath 2024-07-18 11:53:00 +09:00
af235ff878 Use waitpid on compress to avoid defunct processes
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 3s
2024-07-17 19:00:34 +09:00
fe04dc7018 Output when writing file to archive
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-17 17:22:44 +09:00
be67425d21 Impl. compressing with arbitrary command
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
TODO:
    Archive extracting.
    Create archive with symbolic links.
2024-07-17 16:37:32 +09:00
bbdcd660a5 Minor fixes
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-17 15:01:39 +09:00
ea845f2552 Impl. setup for de/compression
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
Some setup code in preparation of doing the actual file compression when
creating an archive.
2024-07-17 14:32:39 +09:00
0299129ea6 "-t": Note that file is compressed if compressed
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 3s
2024-07-17 13:35:04 +09:00
2364f53649 Impl. "-t" command (printing archive info)
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
TODO:
    Write symbolic links into archive when creating.
    Use De/compressor cmds when creating archive.
    Extracting archive.
2024-07-17 13:30:05 +09:00
58daa1130d Update parser flags to allow "checking" with "-t"
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 3s
2024-07-17 12:13:33 +09:00
875e4bb2a4 Add "status progress" when archiving
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-07-16 16:44:54 +09:00
41fde43eed Impl. basic functionality "MVP"
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 48s
"MinimumViableProduct", resolves #2 .

TODO:
  Support de/compressor cmds when creating archive.
  Support symbolic links when creating archive.
  Support extracting archive.
2024-07-16 16:16:58 +09:00
4670f0f3c1 big-endian convert: Use stdint.h
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
Replace "unsigned short" with uint16_t, "unsigned int" with uint32_t,
and "unsigned long long" with uint64_t.
2024-07-15 17:00:49 +09:00
cb79e4e45c Minor tweaks to file format
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 3s
2024-07-15 15:43:14 +09:00
efffb8c147 Add "--overwrite-create"
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 45s
Default behavior is now to NOT overwrite existing archive files for
storing output unless "--overwrite-create" is specified.
2024-07-11 16:56:23 +09:00
24 changed files with 3185 additions and 627 deletions

View file

@ -7,6 +7,7 @@ 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

210
LICENSE
View file

@ -1,201 +1,15 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
ISC License
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Copyright (c) 2024 Stephen Seo
1. Definitions.
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.
"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.
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.

View file

@ -1,6 +1,7 @@
# Simple Archiver
This program is not yet finished! You can track progress
This program ~~is not yet~~ almost finished! Basic functionality is implemented
and only some advanced features are missing. 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
@ -9,6 +10,30 @@ 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
--decompressor <full_decompress_cmd> : requires --compressor
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
--temp-files-dir <dir> : where to store temporary files created when compressing (defaults to current working directory)
-- : 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.
## LICENSE Information
Uses the [Apache License 2.0](https://choosealicense.com/licenses/apache-2.0).
Uses the [ISC License](https://choosealicense.com/licenses/isc/).

View file

@ -7,6 +7,7 @@ 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 \
@ -16,6 +17,7 @@ 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,5 +1,7 @@
# File Format
Note that any unused bytes/bits should be zeroed-out before being written.
## Format Version 0
File extension is "*.simplearchive"
@ -43,8 +45,18 @@ 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. Currently unused.
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.
3. The third byte.
1. Currently unused.
4. The fourth byte.
@ -61,5 +73,6 @@ 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".
1. 8 bytes 64-bit unsigned integer "size of filename in this archive file"
in big-endian.
2. X bytes file data (length defined by previous value).

View file

@ -1,21 +1,21 @@
/*
* 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.
*/
// 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.
#include "linear_congruential_gen.h"

View file

@ -1,21 +1,21 @@
/*
* 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.
*/
// 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.
#ifndef SEODISPARATE_COM_ALGORITHMS_LINEAR_CONGRUENTIAL_GEN_H_
#define SEODISPARATE_COM_ALGORITHMS_LINEAR_CONGRUENTIAL_GEN_H_

1913
src/archiver.c Normal file

File diff suppressed because it is too large Load diff

73
src/archiver.h Normal file
View file

@ -0,0 +1,73 @@
// 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_
#include <stdio.h>
#include "data_structures/hash_map.h"
#include "data_structures/linked_list.h"
#include "parser.h"
typedef struct SDArchiverState {
/*
*/
unsigned int flags;
const SDArchiverParsed *parsed;
FILE *out_f;
SDArchiverHashMap *map;
size_t count;
size_t max;
size_t digits;
} SDArchiverState;
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
};
/// 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);
/// Returns zero on success.
int simple_archiver_parse_archive_info(FILE *in_f, int 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);
#endif

View file

@ -1,20 +1,20 @@
/*
* 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.
*/
// 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.
#include "hash_map.h"
@ -26,16 +26,22 @@
typedef struct SDArchiverHashMapData {
void *value;
void *key;
unsigned int key_size;
size_t key_size;
void (*value_cleanup_fn)(void *);
void (*key_cleanup_fn)(void *);
} SDArchiverHashMapData;
typedef struct SDArchiverHashMapKeyData {
void *key;
unsigned int key_size;
size_t 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) {
@ -69,11 +75,11 @@ int simple_archiver_hash_map_internal_pick_in_list(void *data, void *ud) {
}
unsigned long long simple_archiver_hash_map_internal_key_to_hash(
void *key, unsigned int key_size) {
const void *key, size_t 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) {
size_t count = 0;
for (size_t idx = 0; idx < key_size; ++idx) {
temp |= ((unsigned long long)*((unsigned char *)key + idx)) << (8 * count);
++count;
if (count >= 8) {
@ -95,28 +101,27 @@ 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 || !*hash_map) {
int simple_archiver_hash_map_internal_rehash(SDArchiverHashMap *hash_map) {
if (!hash_map) {
return 1;
}
SDArchiverHashMap *new_hash_map = malloc(sizeof(SDArchiverHashMap));
new_hash_map->buckets_size = (*hash_map)->buckets_size * 2;
SDArchiverHashMap new_hash_map;
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 (unsigned int 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 (size_t 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 (unsigned int bucket_idx = 0; bucket_idx < (*hash_map)->buckets_size;
for (size_t 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,
@ -127,7 +132,14 @@ int simple_archiver_hash_map_internal_rehash(SDArchiverHashMap **hash_map) {
}
}
simple_archiver_hash_map_free(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.
*hash_map = new_hash_map;
return 0;
@ -139,7 +151,7 @@ SDArchiverHashMap *simple_archiver_hash_map_init(void) {
// 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 (unsigned int idx = 0; idx < hash_map->buckets_size; ++idx) {
for (size_t idx = 0; idx < hash_map->buckets_size; ++idx) {
hash_map->buckets[idx] = simple_archiver_list_init();
}
hash_map->count = 0;
@ -149,7 +161,7 @@ SDArchiverHashMap *simple_archiver_hash_map_init(void) {
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) {
for (size_t idx = 0; idx < (*hash_map)->buckets_size; ++idx) {
SDArchiverLinkedList **linked_list = (*hash_map)->buckets + idx;
simple_archiver_list_free(linked_list);
}
@ -161,11 +173,11 @@ void simple_archiver_hash_map_free(SDArchiverHashMap **hash_map) {
}
}
int simple_archiver_hash_map_insert(SDArchiverHashMap **hash_map, void *value,
void *key, unsigned int key_size,
int simple_archiver_hash_map_insert(SDArchiverHashMap *hash_map, void *value,
void *key, size_t key_size,
void (*value_cleanup_fn)(void *),
void (*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);
}
@ -178,13 +190,13 @@ int simple_archiver_hash_map_insert(SDArchiverHashMap **hash_map, void *value,
unsigned long long hash =
simple_archiver_hash_map_internal_key_to_hash(key, key_size) %
(*hash_map)->buckets_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) {
@ -207,8 +219,8 @@ int simple_archiver_hash_map_insert(SDArchiverHashMap **hash_map, void *value,
}
}
void *simple_archiver_hash_map_get(SDArchiverHashMap *hash_map, void *key,
unsigned int key_size) {
void *simple_archiver_hash_map_get(const SDArchiverHashMap *hash_map,
const void *key, size_t key_size) {
unsigned long long hash =
simple_archiver_hash_map_internal_key_to_hash(key, key_size) %
hash_map->buckets_size;
@ -228,7 +240,7 @@ void *simple_archiver_hash_map_get(SDArchiverHashMap *hash_map, void *key,
}
int simple_archiver_hash_map_remove(SDArchiverHashMap *hash_map, void *key,
unsigned int key_size) {
size_t key_size) {
unsigned long long hash =
simple_archiver_hash_map_internal_key_to_hash(key, key_size) %
hash_map->buckets_size;
@ -248,3 +260,34 @@ 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,32 +1,34 @@
/*
* 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.
*/
// 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.
#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
#include <stddef.h>
#include "linked_list.h"
typedef struct SDArchiverHashMap {
SDArchiverLinkedList **buckets;
unsigned int buckets_size;
unsigned int count;
size_t buckets_size;
size_t count;
} SDArchiverHashMap;
SDArchiverHashMap *simple_archiver_hash_map_init(void);
@ -37,18 +39,31 @@ 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.
int simple_archiver_hash_map_insert(SDArchiverHashMap **hash_map, void *value,
void *key, unsigned int key_size,
/// 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,
void (*value_cleanup_fn)(void *),
void (*key_cleanup_fn)(void *));
/// Returns NULL if not found.
void *simple_archiver_hash_map_get(SDArchiverHashMap *hash_map, void *key,
unsigned int key_size);
void *simple_archiver_hash_map_get(const SDArchiverHashMap *hash_map,
const void *key, size_t 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,
unsigned int key_size);
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);
#endif

View file

@ -1,20 +1,20 @@
/*
* 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.
*/
// 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.
#include "linked_list.h"
@ -178,7 +178,7 @@ int simple_archiver_list_remove_once(SDArchiverLinkedList *list,
return 0;
}
void *simple_archiver_list_get(SDArchiverLinkedList *list,
void *simple_archiver_list_get(const SDArchiverLinkedList *list,
int (*data_check_fn)(void *, void *),
void *user_data) {
if (!list) {

View file

@ -1,24 +1,26 @@
/*
* 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.
*/
// 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.
#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;
@ -29,7 +31,7 @@ typedef struct SDArchiverLLNode {
typedef struct SDArchiverLinkedList {
SDArchiverLLNode *head;
SDArchiverLLNode *tail;
unsigned int count;
size_t count;
} SDArchiverLinkedList;
SDArchiverLinkedList *simple_archiver_list_init(void);
@ -62,7 +64,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(SDArchiverLinkedList *list,
void *simple_archiver_list_get(const SDArchiverLinkedList *list,
int (*data_check_fn)(void *, void *),
void *user_data);

View file

@ -1,20 +1,20 @@
/*
* 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.
*/
// 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.
#include "priority_heap.h"
@ -31,7 +31,7 @@ void simple_archiver_priority_heap_internal_realloc(
new_priority_heap->nodes =
calloc(new_priority_heap->capacity, sizeof(SDArchiverPHNode));
for (unsigned int idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
for (size_t idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
if ((*priority_heap)->nodes[idx].is_valid != 0) {
simple_archiver_priority_heap_insert(
&new_priority_heap, (*priority_heap)->nodes[idx].priority,
@ -79,7 +79,7 @@ SDArchiverPHeap *simple_archiver_priority_heap_init_less_fn(
void simple_archiver_priority_heap_free(SDArchiverPHeap **priority_heap) {
if (priority_heap && *priority_heap) {
for (unsigned int idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
for (size_t idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
if ((*priority_heap)->nodes[idx].is_valid != 0) {
if ((*priority_heap)->nodes[idx].data_cleanup_fn) {
(*priority_heap)
@ -109,7 +109,7 @@ void simple_archiver_priority_heap_insert(SDArchiverPHeap **priority_heap,
simple_archiver_priority_heap_internal_realloc(priority_heap);
}
unsigned int hole = (*priority_heap)->size + 1;
size_t hole = (*priority_heap)->size + 1;
while (hole > 1 &&
(*priority_heap)
@ -146,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;
unsigned int hole = 1;
size_t 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,20 +1,20 @@
/*
* 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.
*/
// 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.
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_PRIORITY_HEAP_H_
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_PRIORITY_HEAP_H_

View file

@ -1,20 +1,20 @@
/*
* 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.
*/
// 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.
#include <stdio.h>
#include <stdlib.h>
@ -25,6 +25,8 @@
#include "linked_list.h"
#include "priority_heap.h"
#define SDARCHIVER_DS_TEST_HASH_MAP_ITER_SIZE 100
static int checks_checked = 0;
static int checks_passed = 0;
@ -63,6 +65,26 @@ int get_three_fn(void *data, __attribute__((unused)) void *ud) {
int more_fn(long long a, long long b) { return a > b ? 1 : 0; }
int hash_map_iter_check_fn(__attribute__((unused)) const void *key,
__attribute__((unused)) size_t key_size,
const void *value, void *ud) {
char *found_buf = ud;
const size_t real_value = (const size_t)value;
if (real_value < SDARCHIVER_DS_TEST_HASH_MAP_ITER_SIZE) {
found_buf[real_value] += 1;
return 0;
} else {
return 1;
}
}
int hash_map_iter_check_fn2(__attribute__((unused)) const void *key,
__attribute__((unused)) size_t key_size,
__attribute__((unused)) const void *value,
__attribute__((unused)) void *ud) {
return 2;
}
int main(void) {
// Test LinkedList.
{
@ -123,8 +145,8 @@ int main(void) {
key = malloc(sizeof(int));
*value = idx;
*key = idx;
simple_archiver_hash_map_insert(&hash_map, value, key, sizeof(int),
NULL, NULL);
simple_archiver_hash_map_insert(hash_map, value, key, sizeof(int), NULL,
NULL);
}
}
@ -162,10 +184,38 @@ int main(void) {
*copy_value = idx;
unsigned int *copy_key = malloc(sizeof(unsigned int));
*copy_key = idx;
simple_archiver_hash_map_insert(&hash_map, copy_value, copy_key,
simple_archiver_hash_map_insert(hash_map, copy_value, copy_key,
sizeof(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 (unsigned int 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 (unsigned int idx = 0; idx < SDARCHIVER_DS_TEST_HASH_MAP_ITER_SIZE;
++idx) {
CHECK_TRUE(found[idx] == 1);
}
simple_archiver_hash_map_free(&hash_map);
}
// Test PriorityHeap.

View file

@ -1,33 +1,84 @@
/*
* 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.
*/
// 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.
#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 {
unsigned int i;
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1 ? 1 : 0;
}
void simple_archiver_helper_16_bit_be(unsigned short *value) {
void simple_archiver_helper_16_bit_be(uint16_t *value) {
if (simple_archiver_helper_is_big_endian() == 0) {
unsigned char c = ((unsigned char *)value)[0];
((unsigned char *)value)[0] = ((unsigned char *)value)[1];
@ -35,7 +86,7 @@ void simple_archiver_helper_16_bit_be(unsigned short *value) {
}
}
void simple_archiver_helper_32_bit_be(unsigned int *value) {
void simple_archiver_helper_32_bit_be(uint32_t *value) {
if (simple_archiver_helper_is_big_endian() == 0) {
for (unsigned int i = 0; i < 2; ++i) {
unsigned char c = ((unsigned char *)value)[i];
@ -45,7 +96,7 @@ void simple_archiver_helper_32_bit_be(unsigned int *value) {
}
}
void simple_archiver_helper_64_bit_be(unsigned long long *value) {
void simple_archiver_helper_64_bit_be(uint64_t *value) {
if (simple_archiver_helper_is_big_endian() == 0) {
for (unsigned int i = 0; i < 4; ++i) {
unsigned char c = ((unsigned char *)value)[i];
@ -54,3 +105,153 @@ void simple_archiver_helper_64_bit_be(unsigned long long *value) {
}
}
}
char **simple_archiver_helper_cmd_string_to_argv(const char *cmd) {
unsigned int capacity = 16;
unsigned int 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);
unsigned int word_capacity = 16;
unsigned int 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,36 +1,65 @@
/*
* 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.
*/
// 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.
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_HELPERS_H_
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_HELPERS_H_
#include <stdint.h>
#include <stdio.h>
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(unsigned short *value);
void simple_archiver_helper_16_bit_be(uint16_t *value);
/// Swaps value from/to big-endian. Nop on big-endian systems.
void simple_archiver_helper_32_bit_be(unsigned int *value);
void simple_archiver_helper_32_bit_be(uint32_t *value);
/// Swaps value from/to big-endian. Nop on big-endian systems.
void simple_archiver_helper_64_bit_be(unsigned long long *value);
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);
#endif

View file

@ -1,50 +1,174 @@
/*
* 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.
*/
// 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.
#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) {
printf(" REGULAR FILE: %s\n", file_info->filename);
fprintf(stderr, " REGULAR FILE: %s\n", file_info->filename);
} else {
printf(" SYMBOLIC LINK: %s -> %s\n", file_info->filename,
fprintf(stderr, " 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;
}
__attribute__((cleanup(simple_archiver_free_parsed)))
SDArchiverParsed parsed = simple_archiver_create_parsed();
simple_archiver_parse_args(argc, argv, &parsed);
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_list_free)))
SDArchiverLinkedList *filenames =
simple_archiver_parsed_to_filenames(&parsed);
if (!filenames) {
fprintf(stderr,
"ERROR: Failed to resolve filenames from positional arguments!\n");
return 8;
}
puts("Filenames:");
if (filenames->count > 0) {
fprintf(stderr, "Filenames:\n");
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(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(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(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(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(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(ret);
fprintf(stderr, " %s\n", error_str);
}
}
}
return 0;
}

View file

@ -1,20 +1,20 @@
/*
* 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.
*/
// 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.
#include "parser.h"
@ -39,15 +39,15 @@
#include "parser_internal.h"
/// Gets the first non "./"-like character in the filename.
unsigned int simple_archiver_parser_internal_filename_idx(
size_t simple_archiver_parser_internal_get_first_non_current_idx(
const char *filename) {
unsigned int idx = 0;
unsigned int known_good_idx = 0;
const unsigned int length = strlen(filename);
size_t idx = 0;
size_t known_good_idx = 0;
const size_t length = strlen(filename);
// 0b0001 - checked that idx char is '.'
// 0b0010 - checked that idx char is '/'
unsigned int flags = 0;
size_t flags = 0;
for (; idx < length; ++idx) {
if ((flags & 3) == 0) {
@ -90,8 +90,8 @@ unsigned int simple_archiver_parser_internal_filename_idx(
}
void simple_archiver_parser_internal_remove_end_slash(char *filename) {
int len = strlen(filename);
int idx;
size_t len = strlen(filename);
size_t idx;
for (idx = len; idx-- > 0;) {
if (filename[idx] != '/') {
++idx;
@ -123,8 +123,6 @@ 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;
@ -134,15 +132,40 @@ int list_remove_same_str_fn(void *data, void *ud) {
}
void simple_archiver_print_usage(void) {
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.");
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\n");
fprintf(stderr,
"--decompressor <full_decompress_cmd> : requires --compressor\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,
"--temp-files-dir <dir> : where to store temporary files created "
"when compressing (defaults to current working directory)\n");
fprintf(stderr,
"-- : specifies remaining arguments are files to archive/extract\n");
fprintf(
stderr,
"If creating archive file, remaining args specify files to archive.\n");
fprintf(
stderr,
"If extracting archive file, remaining args specify files to extract.\n");
}
SDArchiverParsed simple_archiver_create_parsed(void) {
@ -153,6 +176,8 @@ SDArchiverParsed simple_archiver_create_parsed(void) {
parsed.compressor = NULL;
parsed.decompressor = NULL;
parsed.working_files = NULL;
parsed.temp_dir = NULL;
parsed.user_cwd = NULL;
return parsed;
}
@ -180,30 +205,89 @@ int simple_archiver_parse_args(int argc, const char **argv,
while (argc > 0) {
if (!is_remaining_args) {
if (strcmp(argv[0], "-c") == 0) {
// unset first bit.
out->flags &= 0xFFFFFFFE;
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;
} 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], "-f") == 0 && argc > 1) {
int size = strlen(argv[1]) + 1;
} 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;
out->filename = malloc(size);
strncpy(out->filename, argv[1], size);
}
--argc;
++argv;
} else if (strcmp(argv[0], "--compressor") == 0 && argc > 1) {
int size = strlen(argv[1]) + 1;
} 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;
out->compressor = malloc(size);
strncpy(out->compressor, argv[1], size);
--argc;
++argv;
} else if (strcmp(argv[0], "--decompressor") == 0 && argc > 1) {
int size = strlen(argv[1]) + 1;
} 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;
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], "--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 (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) {
is_remaining_args = 1;
} else if (argv[0][0] != '-') {
@ -213,15 +297,15 @@ int simple_archiver_parse_args(int argc, const char **argv,
} else {
if (out->working_files == NULL) {
out->working_files = malloc(sizeof(char *) * 2);
unsigned int arg_idx =
simple_archiver_parser_internal_filename_idx(argv[0]);
int arg_length = strlen(argv[0] + arg_idx) + 1;
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;
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 {
int working_size = 1;
size_t working_size = 1;
char **ptr = out->working_files;
while (ptr && *ptr) {
++working_size;
@ -234,9 +318,9 @@ int simple_archiver_parse_args(int argc, const char **argv,
// Set new actual last element to NULL.
out->working_files[working_size] = NULL;
unsigned int arg_idx =
simple_archiver_parser_internal_filename_idx(argv[0]);
int size = strlen(argv[0] + arg_idx) + 1;
size_t arg_idx =
simple_archiver_parser_internal_get_first_non_current_idx(argv[0]);
size_t 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);
@ -249,6 +333,10 @@ int simple_archiver_parse_args(int argc, const char **argv,
++argv;
}
if (!out->temp_dir) {
out->temp_dir = "./";
}
return 0;
}
@ -287,19 +375,31 @@ 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);
return NULL;
}
}
for (char **iter = parsed->working_files; iter && *iter; ++iter) {
struct stat st;
memset(&st, 0, sizeof(struct stat));
fstatat(AT_FDCWD, *iter, &st, AT_SYMLINK_NOFOLLOW);
char *file_path = *iter;
fstatat(AT_FDCWD, file_path, &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.
int len = strlen(*iter) + 1;
size_t len = strlen(file_path) + 1;
char *filename = malloc(len);
strncpy(filename, *iter, len);
strncpy(filename, file_path, 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);
@ -309,19 +409,42 @@ 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, container_no_free_fn,
container_no_free_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);
} else {
free(filename);
}
@ -329,7 +452,9 @@ 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, *iter, container_no_free_fn);
simple_archiver_list_add(
dir_list, file_path,
simple_archiver_helper_datastructure_cleanup_nop);
char *next;
while (dir_list->count != 0) {
simple_archiver_list_get(dir_list, list_get_last_fn, &next);
@ -345,13 +470,15 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
strcmp(dir_entry->d_name, "..") == 0) {
continue;
}
printf("dir entry in %s is %s\n", next, dir_entry->d_name);
int combined_size = strlen(next) + strlen(dir_entry->d_name) + 2;
// 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;
char *combined_path = malloc(combined_size);
snprintf(combined_path, combined_size, "%s/%s", next,
dir_entry->d_name);
unsigned int valid_idx =
simple_archiver_parser_internal_filename_idx(combined_path);
size_t valid_idx =
simple_archiver_parser_internal_get_first_non_current_idx(
combined_path);
if (valid_idx > 0) {
char *new_path = malloc(combined_size - valid_idx);
strncpy(new_path, combined_path + valid_idx,
@ -371,6 +498,7 @@ 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,
@ -387,15 +515,36 @@ 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, container_no_free_fn,
container_no_free_fn);
hash_map, &hash_map_sentinel, combined_path,
combined_size - 1,
simple_archiver_helper_datastructure_cleanup_nop,
simple_archiver_helper_datastructure_cleanup_nop);
} else {
free(combined_path);
}
@ -420,19 +569,42 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
}
#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;
unsigned int idx =
simple_archiver_parser_internal_filename_idx(file_info->filename);
// Remove leading "./" entries from files_list.
size_t idx = simple_archiver_parser_internal_get_first_non_current_idx(
file_info->filename);
if (idx > 0) {
int len = strlen(file_info->filename) + 1 - idx;
size_t 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 slash_found = 0;
int 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;
}
}
return files_list;

View file

@ -1,20 +1,20 @@
/*
* 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.
*/
// 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.
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_H_
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_H_
@ -23,8 +23,14 @@
typedef struct SDArchiverParsed {
/// Each bit is a flag.
/// 0b0 - is creating.
/// 0b1 - is extracting.
/// 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.
unsigned int flags;
/// Null-terminated string.
char *filename;
@ -34,8 +40,13 @@ typedef struct SDArchiverParsed {
char *decompressor;
/// Null-terminated strings in array of strings.
/// Last entry should be NULL.
/// Not used when extracting.
/// Determines a "white-list" of files to extract 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;
} SDArchiverParsed;
typedef struct SDArchiverFileInfo {
@ -51,6 +62,7 @@ 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);

View file

@ -1,26 +1,27 @@
/*
* 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.
*/
// 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.
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_INTERNAL_H_
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_PARSER_INTERNAL_H_
#include "parser.h"
unsigned int simple_archiver_parser_internal_filename_idx(const char *filename);
size_t simple_archiver_parser_internal_get_first_non_current_idx(
const char *filename);
#endif

View file

@ -1,21 +1,21 @@
/*
* 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.
*/
// 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.
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_PLATFORMS_H_
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_PLATFORMS_H_

View file

@ -1,20 +1,20 @@
/*
* 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.
*/
// 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.
#include <stdio.h>
#include <stdlib.h>
@ -44,38 +44,55 @@ static int 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.
{
unsigned int idx = simple_archiver_parser_internal_filename_idx("test");
unsigned int idx =
simple_archiver_parser_internal_get_first_non_current_idx("test");
CHECK_TRUE(idx == 0);
idx = simple_archiver_parser_internal_filename_idx("./test");
idx = simple_archiver_parser_internal_get_first_non_current_idx("./test");
CHECK_TRUE(idx == 2);
idx = simple_archiver_parser_internal_filename_idx("././test");
idx = simple_archiver_parser_internal_get_first_non_current_idx("././test");
CHECK_TRUE(idx == 4);
idx = simple_archiver_parser_internal_filename_idx("././//././//./test");
idx = simple_archiver_parser_internal_get_first_non_current_idx(
"././//././//./test");
CHECK_TRUE(idx == 14);
idx = simple_archiver_parser_internal_filename_idx("/././//././//./test");
idx = simple_archiver_parser_internal_get_first_non_current_idx(
"/././//././//./test");
CHECK_TRUE(idx == 0);
idx = simple_archiver_parser_internal_filename_idx(".derp/.//././//./test");
idx = simple_archiver_parser_internal_get_first_non_current_idx(
".derp/.//././//./test");
CHECK_TRUE(idx == 0);
idx = simple_archiver_parser_internal_filename_idx("././/.derp/.///./test");
idx = simple_archiver_parser_internal_get_first_non_current_idx(
"././/.derp/.///./test");
CHECK_TRUE(idx == 5);
idx = simple_archiver_parser_internal_filename_idx("././/.//.//./");
idx = simple_archiver_parser_internal_get_first_non_current_idx(
"././/.//.//./");
CHECK_TRUE(idx == 11);
idx = simple_archiver_parser_internal_filename_idx("././/.//.//.");
idx = simple_archiver_parser_internal_get_first_non_current_idx(
"././/.//.//.");
CHECK_TRUE(idx == 11);
idx = simple_archiver_parser_internal_filename_idx("././/.//.//");
idx = simple_archiver_parser_internal_get_first_non_current_idx(
"././/.//.//");
CHECK_TRUE(idx == 8);
SDArchiverParsed parsed = simple_archiver_create_parsed();
@ -114,7 +131,7 @@ int main(void) {
{
// Only if system is little-endian.
if (simple_archiver_helper_is_big_endian() == 0) {
unsigned short u16 = 0x0102;
uint16_t u16 = 0x0102;
CHECK_TRUE(((unsigned char *)&u16)[0] == 2);
CHECK_TRUE(((unsigned char *)&u16)[1] == 1);
simple_archiver_helper_16_bit_be(&u16);
@ -124,7 +141,7 @@ int main(void) {
CHECK_TRUE(((unsigned char *)&u16)[0] == 2);
CHECK_TRUE(((unsigned char *)&u16)[1] == 1);
unsigned int u32 = 0x01020304;
uint32_t u32 = 0x01020304;
CHECK_TRUE(((unsigned char *)&u32)[0] == 4);
CHECK_TRUE(((unsigned char *)&u32)[1] == 3);
CHECK_TRUE(((unsigned char *)&u32)[2] == 2);
@ -140,7 +157,7 @@ int main(void) {
CHECK_TRUE(((unsigned char *)&u32)[2] == 2);
CHECK_TRUE(((unsigned char *)&u32)[3] == 1);
unsigned long long u64 = 0x010203040a0b0c0d;
uint64_t u64 = 0x010203040a0b0c0d;
CHECK_TRUE(((unsigned char *)&u64)[0] == 0xd);
CHECK_TRUE(((unsigned char *)&u64)[1] == 0xc);
CHECK_TRUE(((unsigned char *)&u64)[2] == 0xb);
@ -170,6 +187,57 @@ int main(void) {
}
}
// 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.";
unsigned int 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);
}
printf("Checks checked: %u\n", checks_checked);
printf("Checks passed: %u\n", checks_passed);
return checks_passed == checks_checked ? 0 : 1;