summaryrefslogtreecommitdiff
path: root/libarchive.zig
blob: ff45344f1ba15cdcbd38ad5039b93b841640d1dd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
pub const c = @cImport({
    @cInclude("archive.h");
    @cInclude("archive_entry.h");
});

const std = @import("std");

pub const Entry = struct {
    raw: *c.archive_entry,

    pub fn pathname(self: Entry) [:0]const u8 {
        return std.mem.span(c.archive_entry_pathname(self.raw));
    }

    pub fn setPathname(self: *Entry, new_pathname: [:0]const u8) void {
        c.archive_entry_set_pathname(self.raw, new_pathname.ptr);
    }
};

pub const Read = struct {
    raw: *c.archive,

    pub fn init() !Read {
        if (c.archive_read_new()) |raw| {
            return Read{ .raw = raw };
        } else {
            std.log.err("archive_read_new failed", .{});
            return error.LibArchiveError;
        }
    }

    pub fn deinit(self: *Read) void {
        if (c.archive_read_free(self.raw) != c.ARCHIVE_OK) {
            std.log.warn("archive_read_free failed", .{});
        }
    }

    pub const Filter = enum {
        all,
        bzip2,
        compress,
        grzip,
        gzip,
        lrzip,
        lz4,
        lzma,
        lzop,
        none,
        rpm,
        uu,
        xz,
        zstd,
    };

    pub fn supportFilter(self: *Read, comptime filter: Filter) !void {
        const fn_name = comptime "archive_read_support_filter_" ++ @tagName(filter);
        const f = comptime @field(c, fn_name);
        if (f(self.raw) != c.ARCHIVE_OK) {
            std.log.err(fn_name ++ ": {s}", .{c.archive_error_string(self.raw)});
            return error.LibArchiveError;
        }
    }

    pub const Format = enum {
        @"7zip",
        all,
        ar,
        cab,
        cpio,
        empty,
        iso9660,
        lha,
        mtree,
        rar,
        raw,
        tar,
        xar,
        zip,
    };

    pub fn supportFormat(self: *Read, comptime format: Format) !void {
        const fn_name = comptime "archive_read_support_format_" ++ @tagName(format);
        const f = comptime @field(c, fn_name);
        if (f(self.raw) != c.ARCHIVE_OK) {
            std.log.err(fn_name ++ ": {s}", .{c.archive_error_string(self.raw)});
            return error.LibArchiveError;
        }
    }

    pub fn openFilename(self: *Read, filename: [:0]const u8, block_size: usize) !void {
        if (c.archive_read_open_filename(self.raw, filename.ptr, block_size) != c.ARCHIVE_OK) {
            std.log.err("archive_read_open_filename: {s}", .{c.archive_error_string(self.raw)});
            return error.LibArchiveError;
        }
    }

    pub fn openMemory(self: *Read, buffer: []const u8) !void {
        if (c.archive_read_open_memory(self.raw, buffer.ptr, buffer.len) != c.ARCHIVE_OK) {
            std.log.err("archive_read_open_memory: {s}", .{c.archive_error_string(self.raw)});
            return error.LibArchiveError;
        }
    }

    pub fn nextHeader(self: *Read) !?Entry {
        var header_raw: ?*c.archive_entry = undefined;
        var r = c.ARCHIVE_RETRY;
        while (r == c.ARCHIVE_RETRY) {
            r = c.archive_read_next_header(self.raw, &header_raw);
        }

        if (r == c.ARCHIVE_WARN) {
            std.log.warn("archive_read_next_header: {s}", .{c.archive_error_string(self.raw)});
            r = c.ARCHIVE_OK;
        }

        if (r == c.ARCHIVE_EOF) {
            return null;
        }

        if (r != c.ARCHIVE_OK or header_raw == null) {
            std.log.err("archive_read_next_header: {s}", .{c.archive_error_string(self.raw)});
            return error.LibArchiveError;
        }

        return Entry{ .raw = header_raw.? };
    }

    // TODO: Replace flags with enum
    pub fn extract(self: *Read, entry: Entry, flags: c_int) !void {
        var r = c.ARCHIVE_RETRY;
        while (r == c.ARCHIVE_RETRY) {
            r = c.archive_read_extract(self.raw, entry.raw, flags);
        }

        if (r == c.ARCHIVE_WARN) {
            std.log.warn("archive_read_extract: {s}", .{c.archive_error_string(self.raw)});
        } else if (r != c.ARCHIVE_OK) {
            std.log.err("archive_read_extract: {s}", .{c.archive_error_string(self.raw)});
            return error.LibArchiveError;
        }
    }
};