mirror of
https://github.com/ziglang/zig.git
synced 2026-02-17 14:59:14 +00:00
add std.tar for tar file unpacking
This commit is contained in:
parent
4f6981bbe3
commit
585b9970ef
@ -21,6 +21,7 @@ pub const EnumMap = enums.EnumMap;
|
||||
pub const EnumSet = enums.EnumSet;
|
||||
pub const HashMap = hash_map.HashMap;
|
||||
pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
|
||||
pub const Ini = @import("Ini.zig");
|
||||
pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;
|
||||
pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
|
||||
pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
|
||||
@ -85,6 +86,7 @@ pub const rand = @import("rand.zig");
|
||||
pub const sort = @import("sort.zig");
|
||||
pub const simd = @import("simd.zig");
|
||||
pub const ascii = @import("ascii.zig");
|
||||
pub const tar = @import("tar.zig");
|
||||
pub const testing = @import("testing.zig");
|
||||
pub const time = @import("time.zig");
|
||||
pub const tz = @import("tz.zig");
|
||||
|
||||
144
lib/std/tar.zig
Normal file
144
lib/std/tar.zig
Normal file
@ -0,0 +1,144 @@
|
||||
pub const Options = struct {};
|
||||
|
||||
pub const Header = struct {
|
||||
bytes: *const [512]u8,
|
||||
|
||||
pub const FileType = enum(u8) {
|
||||
normal = '0',
|
||||
hard_link = '1',
|
||||
symbolic_link = '2',
|
||||
character_special = '3',
|
||||
block_special = '4',
|
||||
directory = '5',
|
||||
fifo = '6',
|
||||
contiguous = '7',
|
||||
global_extended_header = 'g',
|
||||
extended_header = 'x',
|
||||
_,
|
||||
};
|
||||
|
||||
pub fn fileSize(header: Header) !u64 {
|
||||
const raw = header.bytes[124..][0..12];
|
||||
const ltrimmed = std.mem.trimLeft(u8, raw, "0");
|
||||
const rtrimmed = std.mem.trimRight(u8, ltrimmed, "\x00");
|
||||
if (rtrimmed.len == 0) return 0;
|
||||
return std.fmt.parseInt(u64, rtrimmed, 8);
|
||||
}
|
||||
|
||||
pub fn is_ustar(header: Header) bool {
|
||||
return std.mem.eql(u8, header.bytes[257..][0..6], "ustar\x00");
|
||||
}
|
||||
|
||||
/// Includes prefix concatenated, if any.
|
||||
/// Return value may point into Header buffer, or might point into the
|
||||
/// argument buffer.
|
||||
/// TODO: check against "../" and other nefarious things
|
||||
pub fn fullFileName(header: Header, buffer: *[255]u8) ![]const u8 {
|
||||
const n = name(header);
|
||||
if (!is_ustar(header))
|
||||
return n;
|
||||
const p = prefix(header);
|
||||
if (p.len == 0)
|
||||
return n;
|
||||
std.mem.copy(u8, buffer[0..p.len], p);
|
||||
buffer[p.len] = '/';
|
||||
std.mem.copy(u8, buffer[p.len + 1 ..], n);
|
||||
return buffer[0 .. p.len + 1 + n.len];
|
||||
}
|
||||
|
||||
pub fn name(header: Header) []const u8 {
|
||||
return str(header, 0, 0 + 100);
|
||||
}
|
||||
|
||||
pub fn prefix(header: Header) []const u8 {
|
||||
return str(header, 345, 345 + 155);
|
||||
}
|
||||
|
||||
pub fn fileType(header: Header) FileType {
|
||||
const result = @intToEnum(FileType, header.bytes[156]);
|
||||
return if (result == @intToEnum(FileType, 0)) .normal else result;
|
||||
}
|
||||
|
||||
fn str(header: Header, start: usize, end: usize) []const u8 {
|
||||
var i: usize = start;
|
||||
while (i < end) : (i += 1) {
|
||||
if (header.bytes[i] == 0) break;
|
||||
}
|
||||
return header.bytes[start..i];
|
||||
}
|
||||
};
|
||||
|
||||
pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void {
|
||||
_ = options;
|
||||
var file_name_buffer: [255]u8 = undefined;
|
||||
var buffer: [512 * 8]u8 = undefined;
|
||||
var start: usize = 0;
|
||||
var end: usize = 0;
|
||||
header: while (true) {
|
||||
if (buffer.len - start < 1024) {
|
||||
std.mem.copy(u8, &buffer, buffer[start..end]);
|
||||
end -= start;
|
||||
start = 0;
|
||||
}
|
||||
const ask_header = @min(buffer.len - end, 1024 -| (end - start));
|
||||
end += try reader.readAtLeast(buffer[end..], ask_header);
|
||||
switch (end - start) {
|
||||
0 => return,
|
||||
1...511 => return error.UnexpectedEndOfStream,
|
||||
else => {},
|
||||
}
|
||||
const header: Header = .{ .bytes = buffer[start..][0..512] };
|
||||
start += 512;
|
||||
const file_size = try header.fileSize();
|
||||
const rounded_file_size = std.mem.alignForwardGeneric(u64, file_size, 512);
|
||||
const pad_len = rounded_file_size - file_size;
|
||||
const file_name = try header.fullFileName(&file_name_buffer);
|
||||
switch (header.fileType()) {
|
||||
.directory => {
|
||||
try dir.makeDir(file_name);
|
||||
},
|
||||
.normal => {
|
||||
if (file_size == 0 and file_name.len == 0) return;
|
||||
|
||||
var file = try dir.createFile(file_name, .{});
|
||||
defer file.close();
|
||||
|
||||
var file_off: usize = 0;
|
||||
while (true) {
|
||||
if (buffer.len - start < 1024) {
|
||||
std.mem.copy(u8, &buffer, buffer[start..end]);
|
||||
end -= start;
|
||||
start = 0;
|
||||
}
|
||||
// Ask for the rounded up file size + 512 for the next header.
|
||||
const ask = @min(
|
||||
buffer.len - end,
|
||||
rounded_file_size + 512 - file_off -| (end - start),
|
||||
);
|
||||
end += try reader.readAtLeast(buffer[end..], ask);
|
||||
if (end - start < ask) return error.UnexpectedEndOfStream;
|
||||
const slice = buffer[start..@min(file_size - file_off + start, end)];
|
||||
try file.writeAll(slice);
|
||||
file_off += slice.len;
|
||||
start += slice.len;
|
||||
if (file_off >= file_size) {
|
||||
start += pad_len;
|
||||
// Guaranteed since we use a buffer divisible by 512.
|
||||
assert(start <= end);
|
||||
continue :header;
|
||||
}
|
||||
}
|
||||
},
|
||||
.global_extended_header, .extended_header => {
|
||||
start += rounded_file_size;
|
||||
if (start > end) return error.TarHeadersTooBig;
|
||||
},
|
||||
.hard_link => return error.TarUnsupportedFileType,
|
||||
.symbolic_link => return error.TarUnsupportedFileType,
|
||||
else => return error.TarUnsupportedFileType,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std = @import("std.zig");
|
||||
const assert = std.debug.assert;
|
||||
Loading…
x
Reference in New Issue
Block a user