txtar is a small Zig 0.16 library and command-line tool for Go-style text archives.
A txtar archive is plain text: an optional comment followed by file entries. Each file starts with a marker line, then its contents.
this is the archive comment
-- foo.txt --
hello
-- dir/bar.txt --
world
This package is modeled after Go's golang.org/x/tools/txtar, but uses Zig ownership conventions and std.Io streaming APIs.
Use parse and format when the whole archive comfortably fits in memory.
const std = @import("std");
const txtar = @import("txtar");
pub fn example(gpa: std.mem.Allocator, input: []const u8) ![]u8 {
var archive = try txtar.parse(gpa, input);
defer archive.deinit(gpa);
// archive.comment is owned by archive.
// archive.files contains owned name/data slices.
return txtar.format(gpa, archive);
}parse returns an owned txtar.Archive:
pub const Archive = struct {
pub const empty: Archive = .{ .comment = "", .files = &.{} };
comment: []const u8,
files: []const File,
};
pub const File = struct {
name: []const u8,
data: []const u8,
};Call archive.deinit(gpa) when done. The allocator passed to deinit must be the same allocator passed to parse.
Both parsing and formatting normalize a missing trailing newline. That matches Go's txtar behavior: comments and file contents are line-oriented, so an archive ending in world is treated like one ending in world\n.
format assumes the archive is valid txtar data: file names should be non-empty, and comments or file contents should not contain marker lines unless you intend them to start a new file entry when parsed again.
Use Reader and Writer when you want to avoid loading every file into memory.
var reader: std.Io.Reader = .fixed(input);
var archive: txtar.Reader = .init(&reader);
var comment_writer: std.Io.Writer.Discarding = .init(&.{});
try archive.writeCommentTo(&comment_writer.writer);
while (try archive.next()) |entry| {
std.debug.print("file: {s}\n", .{entry.name});
try entry.writeTo(output_writer);
}To write an archive incrementally:
var archive: txtar.Writer = .init(output_writer);
try archive.writeComment("generated by my tool\n");
var entry = try archive.beginEntry("foo.txt");
try entry.writeAll("hello\n");
try entry.finish();EntryWriter.finish appends a newline when the entry data does not already end in one. If you start an entry, finish it before starting the next one.
brew install --cask mattrobenolt/stuff/txtarThe repository also builds a txtar command.
Create an archive:
txtar -c -f archive.txtar LICENSE src/root.zigExtract an archive:
txtar -x -f archive.txtar -C outVerbose mode prints added or extracted paths. Short flags can be combined, and -f/-C may take their value as either the rest of the same argument or the next argument:
txtar -cvf archive.txtar src
txtar -xvf archive.txtar -C outThe extractor rejects absolute paths and .. path components.
The project requires Zig 0.16.0. zig build test runs the library and CLI tests.
nix develop
zig build test
zig build
zig build run -- -cf sample.txtar LICENSE