aboutsummaryrefslogtreecommitdiff
path: root/src/libarchive/libarchive.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/libarchive/libarchive.zig')
-rw-r--r--src/libarchive/libarchive.zig634
1 files changed, 634 insertions, 0 deletions
diff --git a/src/libarchive/libarchive.zig b/src/libarchive/libarchive.zig
new file mode 100644
index 000000000..ef8a2acfa
--- /dev/null
+++ b/src/libarchive/libarchive.zig
@@ -0,0 +1,634 @@
+// @link "../deps/libarchive.a"
+
+const lib = @import("./libarchive-bindings.zig");
+usingnamespace @import("../global.zig");
+const std = @import("std");
+const struct_archive = lib.struct_archive;
+pub const Seek = enum(c_int) {
+ set = std.os.SEEK_SET,
+ current = std.os.SEEK_CUR,
+ end = std.os.SEEK_END,
+};
+
+pub const Flags = struct {
+ pub const Extract = enum(c_int) {
+ owner = lib.ARCHIVE_EXTRACT_OWNER,
+ perm = lib.ARCHIVE_EXTRACT_PERM,
+ time = lib.ARCHIVE_EXTRACT_TIME,
+ no_overwrite = lib.ARCHIVE_EXTRACT_NO_OVERWRITE,
+ unlink = lib.ARCHIVE_EXTRACT_UNLINK,
+ acl = lib.ARCHIVE_EXTRACT_ACL,
+ fflags = lib.ARCHIVE_EXTRACT_FFLAGS,
+ xattr = lib.ARCHIVE_EXTRACT_XATTR,
+ secure_symlinks = lib.ARCHIVE_EXTRACT_SECURE_SYMLINKS,
+ secure_nodotdot = lib.ARCHIVE_EXTRACT_SECURE_NODOTDOT,
+ no_autodir = lib.ARCHIVE_EXTRACT_NO_AUTODIR,
+ no_overwrite_newer = lib.ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER,
+ sparse = lib.ARCHIVE_EXTRACT_SPARSE,
+ mac_metadata = lib.ARCHIVE_EXTRACT_MAC_METADATA,
+ no_hfs_compression = lib.ARCHIVE_EXTRACT_NO_HFS_COMPRESSION,
+ hfs_compression_forced = lib.ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED,
+ secure_noabsolutepaths = lib.ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS,
+ clear_nochange_fflags = lib.ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS,
+ safe_writes = lib.ARCHIVE_EXTRACT_SAFE_WRITES,
+ };
+
+ pub const Compression = enum(c_int) {
+ none = lib.ARCHIVE_COMPRESSION_NONE,
+ gzip = lib.ARCHIVE_COMPRESSION_GZIP,
+ bzip2 = lib.ARCHIVE_COMPRESSION_BZIP2,
+ compress = lib.ARCHIVE_COMPRESSION_COMPRESS,
+ program = lib.ARCHIVE_COMPRESSION_PROGRAM,
+ lzma = lib.ARCHIVE_COMPRESSION_LZMA,
+ xz = lib.ARCHIVE_COMPRESSION_XZ,
+ uu = lib.ARCHIVE_COMPRESSION_UU,
+ rpm = lib.ARCHIVE_COMPRESSION_RPM,
+ lzip = lib.ARCHIVE_COMPRESSION_LZIP,
+ lrzip = lib.ARCHIVE_COMPRESSION_LRZIP,
+ };
+
+ pub const Format = enum(c_int) {
+ base_mask = lib.ARCHIVE_FORMAT_BASE_MASK,
+ cpio = lib.ARCHIVE_FORMAT_CPIO,
+ cpio_posix = lib.ARCHIVE_FORMAT_CPIO_POSIX,
+ cpio_bin_le = lib.ARCHIVE_FORMAT_CPIO_BIN_LE,
+ cpio_bin_be = lib.ARCHIVE_FORMAT_CPIO_BIN_BE,
+ cpio_svr4_nocrc = lib.ARCHIVE_FORMAT_CPIO_SVR4_NOCRC,
+ cpio_svr4_crc = lib.ARCHIVE_FORMAT_CPIO_SVR4_CRC,
+ cpio_afio_large = lib.ARCHIVE_FORMAT_CPIO_AFIO_LARGE,
+ cpio_pwb = lib.ARCHIVE_FORMAT_CPIO_PWB,
+ shar = lib.ARCHIVE_FORMAT_SHAR,
+ shar_base = lib.ARCHIVE_FORMAT_SHAR_BASE,
+ shar_dump = lib.ARCHIVE_FORMAT_SHAR_DUMP,
+ tar = lib.ARCHIVE_FORMAT_TAR,
+ tar_ustar = lib.ARCHIVE_FORMAT_TAR_USTAR,
+ tar_pax_interchange = lib.ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE,
+ tar_pax_restricted = lib.ARCHIVE_FORMAT_TAR_PAX_RESTRICTED,
+ tar_gnutar = lib.ARCHIVE_FORMAT_TAR_GNUTAR,
+ iso9660 = lib.ARCHIVE_FORMAT_ISO9660,
+ iso9660_rockridge = lib.ARCHIVE_FORMAT_ISO9660_ROCKRIDGE,
+ zip = lib.ARCHIVE_FORMAT_ZIP,
+ empty = lib.ARCHIVE_FORMAT_EMPTY,
+ ar = lib.ARCHIVE_FORMAT_AR,
+ ar_gnu = lib.ARCHIVE_FORMAT_AR_GNU,
+ ar_bsd = lib.ARCHIVE_FORMAT_AR_BSD,
+ mtree = lib.ARCHIVE_FORMAT_MTREE,
+ raw = lib.ARCHIVE_FORMAT_RAW,
+ xar = lib.ARCHIVE_FORMAT_XAR,
+ lha = lib.ARCHIVE_FORMAT_LHA,
+ cab = lib.ARCHIVE_FORMAT_CAB,
+ rar = lib.ARCHIVE_FORMAT_RAR,
+ @"7zip" = lib.ARCHIVE_FORMAT_7ZIP,
+ warc = lib.ARCHIVE_FORMAT_WARC,
+ rar_v5 = lib.ARCHIVE_FORMAT_RAR_V5,
+ };
+
+ pub const Filter = enum(c_int) {
+ none = lib.ARCHIVE_FILTER_NONE,
+ gzip = lib.ARCHIVE_FILTER_GZIP,
+ bzip2 = lib.ARCHIVE_FILTER_BZIP2,
+ compress = lib.ARCHIVE_FILTER_COMPRESS,
+ program = lib.ARCHIVE_FILTER_PROGRAM,
+ lzma = lib.ARCHIVE_FILTER_LZMA,
+ xz = lib.ARCHIVE_FILTER_XZ,
+ uu = lib.ARCHIVE_FILTER_UU,
+ rpm = lib.ARCHIVE_FILTER_RPM,
+ lzip = lib.ARCHIVE_FILTER_LZIP,
+ lrzip = lib.ARCHIVE_FILTER_LRZIP,
+ lzop = lib.ARCHIVE_FILTER_LZOP,
+ grzip = lib.ARCHIVE_FILTER_GRZIP,
+ lz4 = lib.ARCHIVE_FILTER_LZ4,
+ zstd = lib.ARCHIVE_FILTER_ZSTD,
+ };
+
+ pub const EntryDigest = enum(c_int) {
+ md5 = lib.ARCHIVE_ENTRY_DIGEST_MD5,
+ rmd160 = lib.ARCHIVE_ENTRY_DIGEST_RMD160,
+ sha1 = lib.ARCHIVE_ENTRY_DIGEST_SHA1,
+ sha256 = lib.ARCHIVE_ENTRY_DIGEST_SHA256,
+ sha384 = lib.ARCHIVE_ENTRY_DIGEST_SHA384,
+ sha512 = lib.ARCHIVE_ENTRY_DIGEST_SHA512,
+ };
+
+ pub const EntryACL = enum(c_int) {
+ entry_acl_execute = lib.ARCHIVE_ENTRY_ACL_EXECUTE,
+ write = lib.ARCHIVE_ENTRY_ACL_WRITE,
+ read = lib.ARCHIVE_ENTRY_ACL_READ,
+ read_data = lib.ARCHIVE_ENTRY_ACL_READ_DATA,
+ list_directory = lib.ARCHIVE_ENTRY_ACL_LIST_DIRECTORY,
+ write_data = lib.ARCHIVE_ENTRY_ACL_WRITE_DATA,
+ add_file = lib.ARCHIVE_ENTRY_ACL_ADD_FILE,
+ append_data = lib.ARCHIVE_ENTRY_ACL_APPEND_DATA,
+ add_subdirectory = lib.ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
+ read_named_attrs = lib.ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS,
+ write_named_attrs = lib.ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS,
+ delete_child = lib.ARCHIVE_ENTRY_ACL_DELETE_CHILD,
+ read_attributes = lib.ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES,
+ write_attributes = lib.ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES,
+ delete = lib.ARCHIVE_ENTRY_ACL_DELETE,
+ read_acl = lib.ARCHIVE_ENTRY_ACL_READ_ACL,
+ write_acl = lib.ARCHIVE_ENTRY_ACL_WRITE_ACL,
+ write_owner = lib.ARCHIVE_ENTRY_ACL_WRITE_OWNER,
+ synchronize = lib.ARCHIVE_ENTRY_ACL_SYNCHRONIZE,
+ perms_posix1_e = lib.ARCHIVE_ENTRY_ACL_PERMS_POSIX1E,
+ perms_nfs4 = lib.ARCHIVE_ENTRY_ACL_PERMS_NFS4,
+ entry_inherited = lib.ARCHIVE_ENTRY_ACL_ENTRY_INHERITED,
+ entry_file_inherit = lib.ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT,
+ entry_directory_inherit = lib.ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT,
+ entry_no_propagate_inherit = lib.ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT,
+ entry_inherit_only = lib.ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY,
+ entry_successful_access = lib.ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS,
+ entry_failed_access = lib.ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS,
+ inheritance_nfs4 = lib.ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4,
+ type_access = lib.ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ type_default = lib.ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
+ type_allow = lib.ARCHIVE_ENTRY_ACL_TYPE_ALLOW,
+ type_deny = lib.ARCHIVE_ENTRY_ACL_TYPE_DENY,
+ type_audit = lib.ARCHIVE_ENTRY_ACL_TYPE_AUDIT,
+ type_alarm = lib.ARCHIVE_ENTRY_ACL_TYPE_ALARM,
+ type_posix1_e = lib.ARCHIVE_ENTRY_ACL_TYPE_POSIX1E,
+ type_nfs4 = lib.ARCHIVE_ENTRY_ACL_TYPE_NFS4,
+ user = lib.ARCHIVE_ENTRY_ACL_USER,
+ user_obj = lib.ARCHIVE_ENTRY_ACL_USER_OBJ,
+ group = lib.ARCHIVE_ENTRY_ACL_GROUP,
+ group_obj = lib.ARCHIVE_ENTRY_ACL_GROUP_OBJ,
+ mask = lib.ARCHIVE_ENTRY_ACL_MASK,
+ other = lib.ARCHIVE_ENTRY_ACL_OTHER,
+ everyone = lib.ARCHIVE_ENTRY_ACL_EVERYONE,
+ style_extra_id = lib.ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID,
+ style_mark_default = lib.ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT,
+ style_solaris = lib.ARCHIVE_ENTRY_ACL_STYLE_SOLARIS,
+ style_separator_comma = lib.ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA,
+ style_compact = lib.ARCHIVE_ENTRY_ACL_STYLE_COMPACT,
+ };
+};
+
+pub const Status = enum(c_int) {
+ eof = lib.ARCHIVE_EOF,
+ ok = lib.ARCHIVE_OK,
+ retry = lib.ARCHIVE_RETRY,
+ warn = lib.ARCHIVE_WARN,
+ failed = lib.ARCHIVE_FAILED,
+ fatal = lib.ARCHIVE_FATAL,
+};
+
+pub fn NewStream(comptime SeekableStream: type) type {
+ return struct {
+ const Stream = @This();
+ source: SeekableStream,
+
+ pub inline fn fromCtx(ctx: *c_void) *Stream {
+ return @ptrCast(*Stream, @alignCast(@alignOf(*Stream), ctx));
+ }
+
+ pub fn archive_read_callback(
+ archive: *struct_archive,
+ ctx_: *c_void,
+ buffer: [*c]*const c_void,
+ ) callconv(.C) lib.la_ssize_t {
+ var this = fromCtx(ctx_);
+ }
+
+ pub fn archive_skip_callback(
+ archive: *struct_archive,
+ ctx_: *c_void,
+ offset: lib.la_int64_,
+ ) callconv(.C) lib.la_int64_t {
+ var this = fromCtx(ctx_);
+ }
+
+ pub fn archive_seek_callback(
+ archive: *struct_archive,
+ ctx_: *c_void,
+ offset: lib.la_int64_t,
+ whence: c_int,
+ ) callconv(.C) lib.la_int64_t {
+ var this = fromCtx(ctx_);
+ }
+
+ pub fn archive_write_callback(
+ archive: *struct_archive,
+ ctx_: *c_void,
+ buffer: *const c_void,
+ len: usize,
+ ) callconv(.C) lib.la_ssize_t {
+ var this = fromCtx(ctx_);
+ }
+ pub fn archive_open_callback(
+ archive: *struct_archive,
+ ctx_: *c_void,
+ ) callconv(.C) c_int {
+ var this = fromCtx(ctx_);
+ }
+
+ pub fn archive_close_callback(
+ archive: *struct_archive,
+ ctx_: *c_void,
+ ) callconv(.C) c_int {
+ var this = fromCtx(ctx_);
+ }
+ pub fn archive_free_callback(
+ archive: *struct_archive,
+ ctx_: *c_void,
+ ) callconv(.C) c_int {
+ var this = fromCtx(ctx_);
+ }
+
+ pub fn archive_switch_callback(
+ archive: *struct_archive,
+ ctx1: *c_void,
+ ctx2: *c_void,
+ ) callconv(.C) c_int {
+ var this = fromCtx(ctx1);
+ var that = fromCtx(ctx2);
+ }
+ };
+}
+
+pub const BufferReadStream = struct {
+ const Stream = @This();
+ buf: []const u8,
+ pos: usize = 0,
+
+ block_size: usize = 16384,
+
+ archive: *struct_archive,
+ reading: bool = false,
+
+ pub fn init(this: *BufferReadStream, buf: []const u8) void {
+ this.* = BufferReadStream{
+ .buf = buf,
+ .pos = 0,
+ .archive = lib.archive_read_new(),
+ .reading = false,
+ };
+ }
+
+ pub fn deinit(this: *BufferReadStream) void {
+ _ = lib.archive_read_close(this.archive);
+ _ = lib.archive_read_free(this.archive);
+ }
+
+ pub fn openRead(this: *BufferReadStream) c_int {
+ // lib.archive_read_set_open_callback(this.archive, this.);
+ // _ = lib.archive_read_set_read_callback(this.archive, archive_read_callback);
+ // _ = lib.archive_read_set_seek_callback(this.archive, archive_seek_callback);
+ // _ = lib.archive_read_set_skip_callback(this.archive, archive_skip_callback);
+ // _ = lib.archive_read_set_close_callback(this.archive, archive_close_callback);
+ // // lib.archive_read_set_switch_callback(this.archive, this.archive_s);
+ // _ = lib.archive_read_set_callback_data(this.archive, this);
+
+ this.reading = true;
+
+ _ = lib.archive_read_support_format_tar(this.archive);
+ _ = lib.archive_read_support_format_gnutar(this.archive);
+ _ = lib.archive_read_support_filter_gzip(this.archive);
+ _ = lib.archive_read_support_filter_none(this.archive);
+
+ const rc = lib.archive_read_open_memory(this.archive, this.buf.ptr, this.buf.len);
+
+ // _ = lib.archive_read_support_compression_all(this.archive);
+
+ return rc;
+ }
+
+ pub inline fn bufLeft(this: BufferReadStream) []const u8 {
+ return this.buf[this.pos..];
+ }
+
+ pub inline fn fromCtx(ctx: *c_void) *Stream {
+ return @ptrCast(*Stream, @alignCast(@alignOf(*Stream), ctx));
+ }
+
+ pub fn archive_close_callback(
+ archive: *struct_archive,
+ ctx_: *c_void,
+ ) callconv(.C) c_int {
+ return 0;
+ }
+
+ pub fn archive_read_callback(
+ archive: *struct_archive,
+ ctx_: *c_void,
+ buffer: [*c]*const c_void,
+ ) callconv(.C) lib.la_ssize_t {
+ var this = fromCtx(ctx_);
+ const remaining = this.bufLeft();
+ if (remaining.len == 0) return 0;
+
+ const diff = std.math.min(remaining.len, this.block_size);
+ buffer.* = remaining[0..diff].ptr;
+ this.pos += diff;
+ return @intCast(isize, diff);
+ }
+
+ pub fn archive_skip_callback(
+ archive: *struct_archive,
+ ctx_: *c_void,
+ offset: lib.la_int64_t,
+ ) callconv(.C) lib.la_int64_t {
+ var this = fromCtx(ctx_);
+
+ const buflen = @intCast(isize, this.buf.len);
+ const pos = @intCast(isize, this.pos);
+
+ const proposed = pos + offset;
+ const new_pos = std.math.min(std.math.max(proposed, 0), buflen - 1);
+ this.pos = @intCast(usize, this.pos);
+ return new_pos - pos;
+ }
+
+ pub fn archive_seek_callback(
+ archive: *struct_archive,
+ ctx_: *c_void,
+ offset: lib.la_int64_t,
+ whence: c_int,
+ ) callconv(.C) lib.la_int64_t {
+ var this = fromCtx(ctx_);
+
+ const buflen = @intCast(isize, this.buf.len);
+ const pos = @intCast(isize, this.pos);
+
+ switch (@intToEnum(Seek, whence)) {
+ Seek.current => {
+ const new_pos = std.math.max(std.math.min(pos + offset, buflen - 1), 0);
+ this.pos = @intCast(usize, new_pos);
+ return new_pos;
+ },
+ Seek.end => {
+ const new_pos = std.math.max(std.math.min(buflen - offset, buflen), 0);
+ this.pos = @intCast(usize, new_pos);
+ return new_pos;
+ },
+ Seek.set => {
+ const new_pos = std.math.max(std.math.min(offset, buflen - 1), 0);
+ this.pos = @intCast(usize, new_pos);
+ return new_pos;
+ },
+ }
+ }
+
+ // pub fn archive_write_callback(
+ // archive: *struct_archive,
+ // ctx_: *c_void,
+ // buffer: *const c_void,
+ // len: usize,
+ // ) callconv(.C) lib.la_ssize_t {
+ // var this = fromCtx(ctx_);
+ // }
+
+ // pub fn archive_close_callback(
+ // archive: *struct_archive,
+ // ctx_: *c_void,
+ // ) callconv(.C) c_int {
+ // var this = fromCtx(ctx_);
+ // }
+ // pub fn archive_free_callback(
+ // archive: *struct_archive,
+ // ctx_: *c_void,
+ // ) callconv(.C) c_int {
+ // var this = fromCtx(ctx_);
+ // }
+
+ // pub fn archive_switch_callback(
+ // archive: *struct_archive,
+ // ctx1: *c_void,
+ // ctx2: *c_void,
+ // ) callconv(.C) c_int {
+ // var this = fromCtx(ctx1);
+ // var that = fromCtx(ctx2);
+ // }
+};
+
+pub const Archive = struct {
+ // impl: *lib.archive = undefined,
+ // buf: []const u8 = undefined,
+ // dir: FileDescriptorType = 0,
+
+ pub const Context = struct {
+ pluckers: []Plucker = &[_]Plucker{},
+ overwrite_list: std.StringArrayHashMap(void),
+ all_files: EntryMap,
+ pub const EntryMap = std.ArrayHashMap(u64, [*c]u8, U64Context, false);
+
+ pub const U64Context = struct {
+ pub fn hash(ctx: @This(), k: u64) u32 {
+ return @truncate(u32, k);
+ }
+ pub fn eql(ctx: @This(), a: u64, b: u64) bool {
+ return a == b;
+ }
+ };
+ };
+
+ pub const Plucker = struct {
+ contents: MutableString,
+ filename_hash: u64 = 0,
+ found: bool = false,
+ fd: FileDescriptorType = 0,
+ pub fn init(filepath: string, estimated_size: usize, allocator: *std.mem.Allocator) !Plucker {
+ return Plucker{
+ .contents = try MutableString.init(allocator, estimated_size),
+ .filename_hash = std.hash.Wyhash.hash(0, filepath),
+ .fd = 0,
+ .found = false,
+ };
+ }
+ };
+
+ pub fn getOverwritingFileList(
+ file_buffer: []const u8,
+ root: []const u8,
+ ctx: *Archive.Context,
+ comptime FilePathAppender: type,
+ appender: FilePathAppender,
+ comptime depth_to_skip: usize,
+ ) !void {
+ var entry: *lib.archive_entry = undefined;
+ var ext: *lib.archive = undefined;
+
+ const flags = @enumToInt(Flags.Extract.time) | @enumToInt(Flags.Extract.perm) | @enumToInt(Flags.Extract.acl) | @enumToInt(Flags.Extract.fflags);
+ var stream: BufferReadStream = undefined;
+ stream.init(file_buffer);
+ defer stream.deinit();
+ _ = stream.openRead();
+ var archive = stream.archive;
+ const dir: std.fs.Dir = brk: {
+ const cwd = std.fs.cwd();
+
+ // if the destination doesn't exist, we skip the whole thing since nothing can overwrite it.
+ if (std.fs.path.isAbsolute(root)) {
+ break :brk std.fs.openDirAbsolute(root, .{ .iterate = true }) catch return;
+ } else {
+ break :brk cwd.openDir(root, .{ .iterate = true }) catch return;
+ }
+ };
+
+ loop: while (true) {
+ const r = @intToEnum(Status, lib.archive_read_next_header(archive, &entry));
+
+ switch (r) {
+ Status.eof => break :loop,
+ Status.failed, Status.fatal, Status.retry => return error.Fail,
+ else => {
+ // do not use the utf8 name there
+ // it will require us to pull in libiconv
+ // though we should probably validate the utf8 here nonetheless
+ var pathname: [:0]const u8 = std.mem.sliceTo(lib.archive_entry_pathname(entry).?, 0);
+ var tokenizer = std.mem.tokenize(u8, std.mem.span(pathname), std.fs.path.sep_str);
+ comptime var depth_i: usize = 0;
+ inline while (depth_i < depth_to_skip) : (depth_i += 1) {
+ if (tokenizer.next() == null) continue :loop;
+ }
+
+ var pathname_ = tokenizer.rest();
+ pathname = std.mem.sliceTo(pathname_.ptr[0..pathname_.len :0], 0);
+ const dirname = std.mem.trim(u8, std.fs.path.dirname(std.mem.span(pathname)) orelse "", std.fs.path.sep_str);
+
+ const size = @intCast(usize, std.math.max(lib.archive_entry_size(entry), 0));
+ if (size > 0) {
+ var opened = dir.openFileZ(pathname, .{ .write = true }) catch continue :loop;
+ var stat = try opened.stat();
+
+ if (stat.size > 0) {
+ const is_already_top_level = dirname.len == 0;
+ const path_to_use_: string = brk: {
+ const __pathname: string = std.mem.span(pathname);
+
+ if (is_already_top_level) break :brk __pathname;
+
+ const index = std.mem.indexOfScalar(u8, __pathname, std.fs.path.sep).?;
+ break :brk __pathname[0..index];
+ };
+ var temp_buf: [1024]u8 = undefined;
+ std.mem.copy(u8, &temp_buf, path_to_use_);
+ var path_to_use: string = temp_buf[0..path_to_use_.len];
+ if (!is_already_top_level) {
+ temp_buf[path_to_use_.len] = std.fs.path.sep;
+ path_to_use = temp_buf[0 .. path_to_use_.len + 1];
+ }
+
+ var overwrite_entry = try ctx.overwrite_list.getOrPut(path_to_use);
+ if (!overwrite_entry.found_existing) {
+ overwrite_entry.key_ptr.* = try appender.append(@TypeOf(path_to_use), path_to_use);
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+
+ pub fn extractToDisk(
+ file_buffer: []const u8,
+ root: []const u8,
+ ctx: ?*Archive.Context,
+ comptime FilePathAppender: type,
+ appender: FilePathAppender,
+ comptime depth_to_skip: usize,
+ comptime close_handles: bool,
+ comptime log: bool,
+ ) !u32 {
+ var entry: *lib.archive_entry = undefined;
+ var ext: *lib.archive = undefined;
+
+ const flags = @enumToInt(Flags.Extract.time) | @enumToInt(Flags.Extract.perm) | @enumToInt(Flags.Extract.acl) | @enumToInt(Flags.Extract.fflags);
+ var stream: BufferReadStream = undefined;
+ stream.init(file_buffer);
+ defer stream.deinit();
+ _ = stream.openRead();
+ var archive = stream.archive;
+ var count: u32 = 0;
+
+ const dir: std.fs.Dir = brk: {
+ const cwd = std.fs.cwd();
+ cwd.makePath(
+ root,
+ ) catch {};
+
+ if (std.fs.path.isAbsolute(root)) {
+ break :brk try std.fs.openDirAbsolute(root, .{ .iterate = true });
+ } else {
+ break :brk try cwd.openDir(root, .{ .iterate = true });
+ }
+ };
+
+ defer if (comptime close_handles) dir.close();
+
+ loop: while (true) {
+ const r = @intToEnum(Status, lib.archive_read_next_header(archive, &entry));
+
+ switch (r) {
+ Status.eof => break :loop,
+ Status.failed, Status.fatal, Status.retry => return error.Fail,
+ else => {
+ var pathname: [:0]const u8 = std.mem.sliceTo(lib.archive_entry_pathname(entry).?, 0);
+ var tokenizer = std.mem.tokenize(u8, std.mem.span(pathname), std.fs.path.sep_str);
+ comptime var depth_i: usize = 0;
+
+ inline while (depth_i < depth_to_skip) : (depth_i += 1) {
+ if (tokenizer.next() == null) continue :loop;
+ }
+
+ var pathname_ = tokenizer.rest();
+ pathname = @intToPtr([*]const u8, @ptrToInt(pathname_.ptr))[0..pathname_.len :0];
+
+ const mask = lib.archive_entry_filetype(entry);
+ const size = @intCast(usize, std.math.max(lib.archive_entry_size(entry), 0));
+ if (size > 0) {
+ const slice = std.mem.span(pathname);
+
+ if (comptime log) {
+ Output.prettyln(" {s}", .{pathname});
+ }
+
+ const file = dir.createFileZ(pathname, .{ .truncate = true }) catch |err| brk: {
+ switch (err) {
+ error.FileNotFound => {
+ try dir.makePath(std.fs.path.dirname(slice) orelse return err);
+ break :brk try dir.createFileZ(pathname, .{ .truncate = true });
+ },
+ else => {
+ return err;
+ },
+ }
+ };
+ count += 1;
+
+ _ = C.fchmod(file.handle, lib.archive_entry_perm(entry));
+
+ if (ctx) |ctx_| {
+ const hash: u64 = if (ctx_.pluckers.len > 0)
+ std.hash.Wyhash.hash(0, slice)
+ else
+ @as(u64, 0);
+
+ if (comptime FilePathAppender != void) {
+ var result = ctx.?.all_files.getOrPutAdapted(hash, Context.U64Context{}) catch unreachable;
+ if (!result.found_existing) {
+ result.value_ptr.* = (try appender.appendMutable(@TypeOf(slice), slice)).ptr;
+ }
+ }
+
+ for (ctx_.pluckers) |*plucker_| {
+ if (plucker_.filename_hash == hash) {
+ try plucker_.contents.inflate(size);
+ plucker_.contents.list.expandToCapacity();
+ var read = lib.archive_read_data(archive, plucker_.contents.list.items.ptr, size);
+ try plucker_.contents.inflate(@intCast(usize, read));
+ plucker_.found = read > 0;
+ plucker_.fd = file.handle;
+ continue :loop;
+ }
+ }
+ }
+
+ _ = lib.archive_read_data_into_fd(archive, file.handle);
+ }
+ },
+ }
+ }
+
+ return count;
+ }
+};