Commit 6b84c8e04c

Andrew Kelley <andrew@ziglang.org>
2024-03-07 03:19:45
zig std: implement tar format
1 parent c427685
Changed files (1)
lib
compiler
lib/compiler/std-docs.zig
@@ -1,6 +1,7 @@
 const builtin = @import("builtin");
 const std = @import("std");
 const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
 
 pub fn main() !void {
     var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
@@ -140,7 +141,6 @@ fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void {
         },
     });
     const w = response.writer();
-    try w.writeAll("tar header");
 
     var std_dir = try context.lib_dir.openDir("std", .{ .iterate = true });
     defer std_dir.close();
@@ -159,13 +159,27 @@ fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void {
             else => continue,
         }
 
-        try w.writeAll(entry.path);
-
         var file = try std_dir.openFile(entry.path, .{});
         defer file.close();
 
+        const stat = try file.stat();
+        const padding = p: {
+            const remainder = stat.size % 512;
+            break :p if (remainder > 0) 512 - remainder else 0;
+        };
+        comptime assert(@sizeOf(TarHeader) == 512);
+
+        var file_header = TarHeader.init();
+        file_header.typeflag = .regular;
+        try file_header.setPath("std", entry.path);
+        try file_header.setSize(stat.size);
+        try file_header.updateChecksum();
+        try w.writeAll(std.mem.asBytes(&file_header));
         try w.writeFile(file);
+        try w.writeByteNTimes(0, padding);
     }
+    // intentionally omitting the pointless trailer
+    //try w.writeByteNTimes(0, 512 * 2);
     try response.end();
 }
 
@@ -369,3 +383,77 @@ fn openBrowserTabThread(gpa: Allocator, url: []const u8) !void {
     try child.spawn();
     _ = try child.wait();
 }
+
+/// Forked from https://github.com/mattnite/tar/blob/main/src/main.zig which is
+/// MIT licensed.
+pub const TarHeader = extern struct {
+    name: [100]u8,
+    mode: [7:0]u8,
+    uid: [7:0]u8,
+    gid: [7:0]u8,
+    size: [11:0]u8,
+    mtime: [11:0]u8,
+    checksum: [7:0]u8,
+    typeflag: FileType,
+    linkname: [100]u8,
+    magic: [5:0]u8,
+    version: [2]u8,
+    uname: [31:0]u8,
+    gname: [31:0]u8,
+    devmajor: [7:0]u8,
+    devminor: [7:0]u8,
+    prefix: [155]u8,
+    pad: [12]u8,
+
+    const FileType = enum(u8) {
+        regular = '0',
+        hard_link = '1',
+        symbolic_link = '2',
+        character = '3',
+        block = '4',
+        directory = '5',
+        fifo = '6',
+        reserved = '7',
+        pax_global = 'g',
+        extended = 'x',
+        _,
+    };
+
+    fn init() TarHeader {
+        var ret = std.mem.zeroes(TarHeader);
+        ret.magic = [_:0]u8{ 'u', 's', 't', 'a', 'r' };
+        ret.version = [_:0]u8{ '0', '0' };
+        return ret;
+    }
+
+    fn setPath(self: *TarHeader, prefix: []const u8, path: []const u8) !void {
+        if (prefix.len + 1 + path.len > 100) {
+            var i: usize = 0;
+            while (i < path.len and path.len - i > 100) {
+                while (path[i] != '/') : (i += 1) {}
+            }
+
+            _ = try std.fmt.bufPrint(&self.prefix, "{s}/{s}", .{ prefix, path[0..i] });
+            _ = try std.fmt.bufPrint(&self.name, "{s}", .{path[i + 1 ..]});
+        } else {
+            _ = try std.fmt.bufPrint(&self.name, "{s}/{s}", .{ prefix, path });
+        }
+    }
+
+    fn setSize(self: *TarHeader, size: u64) !void {
+        _ = try std.fmt.bufPrint(&self.size, "{o:0>11}", .{size});
+    }
+
+    fn updateChecksum(self: *TarHeader) !void {
+        const offset = @offsetOf(TarHeader, "checksum");
+        var checksum: usize = 0;
+        for (std.mem.asBytes(self), 0..) |val, i| {
+            checksum += if (i >= offset and i < offset + @sizeOf(@TypeOf(self.checksum)))
+                ' '
+            else
+                val;
+        }
+
+        _ = try std.fmt.bufPrint(&self.checksum, "{o:0>7}", .{checksum});
+    }
+};