summaryrefslogtreecommitdiff
path: root/src/lib/archive.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/archive.c')
-rw-r--r--src/lib/archive.c54
1 files changed, 54 insertions, 0 deletions
diff --git a/src/lib/archive.c b/src/lib/archive.c
new file mode 100644
index 0000000..2dcd2ec
--- /dev/null
+++ b/src/lib/archive.c
@@ -0,0 +1,54 @@
+#include <lib/archive.h>
+
+#include <err.h>
+#include <lib/url.h>
+
+#include <assert.h>
+
+#include <archive.h>
+#include <archive_entry.h>
+
+int pkg_extract(char *archive_path, struct url *out_dir)
+{
+ assert(out_dir != NULL);
+
+ struct archive *read = archive_read_new();
+ struct archive *disk = archive_write_disk_new();
+ // Read opts
+ archive_read_support_filter_zstd(read);
+ archive_read_support_format_tar(read);
+
+ archive_read_open_filename(read, archive_path, 65535);
+ // Write opts
+ archive_write_disk_set_options(disk, ARCHIVE_EXTRACT_TIME);
+
+ // tmp archive entry (for iterating read)
+ struct archive_entry *entry;
+
+ while (archive_read_next_header(read, &entry) == ARCHIVE_OK) {
+ const char *entry_path = archive_entry_pathname(entry);
+
+ struct url fullpath = {0};
+ url_copy(out_dir, &fullpath);
+ url_append(&fullpath, entry_path);
+
+ archive_entry_set_pathname(entry, fullpath.buffer);
+
+ archive_write_header(disk, entry);
+
+ const void *buf;
+ size_t len;
+ off_t offset;
+
+ while(archive_read_data_block(read, &buf, &len, &offset) == ARCHIVE_OK)
+ archive_write_data_block(disk, buf, len, offset);
+
+ archive_write_finish_entry(disk);
+ url_free(&fullpath);
+ }
+
+ archive_free(disk);
+ archive_free(read);
+
+ return SUCCESS;
+}