commit 90078264a1d894e6023b52127651c6a9e78ff734
parent b96121c45547968bb30eb1cb3be2e653f3362ac7
Author: Drew DeVault <sir@cmpwn.com>
Date: Sat, 1 Jun 2024 11:47:30 +0200
format::tar: move to extlib
Its new home is here:
https://git.sr.ht/~sircmpwn/hare-tar
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
3 files changed, 0 insertions(+), 284 deletions(-)
diff --git a/format/tar/README b/format/tar/README
@@ -1,8 +0,0 @@
-This module provides an implementation of the tar archive format for Unix. The
-specific format implemented is USTAR, however, it is capable of reading most tar
-variants which are backwards-compatible with the original format (e.g. GNU tar).
-
-To read an archive, use [[read]] to create a reader, and [[next]] to enumerate
-its entries. The return value from [[next]] contains the file metadata and is an
-[[io::stream]] that you may read the file contents from. You may call [[skip]]
-to skip an archive entry without reading it.
diff --git a/format/tar/reader.ha b/format/tar/reader.ha
@@ -1,215 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// (c) Hare authors <https://harelang.org>
-
-use bytes;
-use endian;
-use io;
-use memio;
-use strconv;
-use types::c;
-
-export type reader = struct {
- src: io::handle,
- name: [255]u8,
-};
-
-// Creates a new reader for a tar file. Use [[next]] to iterate through entries
-// present in the tar file.
-export fn read(src: io::handle) reader = {
- return reader {
- src = src,
- ...
- };
-};
-
-// Returns the next entry from a tar [[reader]]. Parts of this structure
-// (specifically the file name) are borrowed from the reader itself and will not
-// be valid after subsequent calls.
-//
-// If the return value is a file (i.e. entry.etype == entry_type::FILE), the
-// caller must either call [[io::read]] using the return value until it returns
-// [[io::EOF]], or call [[skip]] to seek to the next entry in the archive.
-//
-// Note that reading from the header will modify the file size.
-export fn next(rd: *reader) (entry | error | io::EOF) = {
- static let buf: [BLOCKSZ]u8 = [0...];
- io::readall(rd.src, buf)?;
-
- if (zeroed(buf)) {
- io::readall(rd.src, buf)?;
- if (!zeroed(buf)) {
- return invalid;
- };
- return io::EOF;
- };
-
- const reader = memio::fixed(buf);
- const name = readstr(&reader, 100);
- const mode = readoct(&reader, 8)?;
- const uid = readoct(&reader, 8)?;
- const gid = readoct(&reader, 8)?;
- const fsize = readsize(&reader, 12)?;
- const mtime = readoct(&reader, 12)?;
- const checksum = readoct(&reader, 8)?;
- const etype = readoct(&reader, 1)?: entry_type;
- const link = readstr(&reader, 100);
-
- let ent = entry {
- vtable = if (etype == entry_type::FILE) &file_vtable
- else &nonfile_vtable,
- src = rd.src,
- orig = fsize,
- remain = fsize,
- name = name,
- mode = mode,
- uid = uid,
- gid = gid,
- fsize = fsize,
- mtime = mtime,
- checksum = checksum,
- etype = etype,
- link = link,
- ...
- };
-
- const ustar = readstr(&reader, 6);
- if (ustar != "ustar") {
- ent.name = name;
- return ent;
- };
-
- const version = readstr(&reader, 2);
- // XXX: We could check the version here
- ent.uname = readstr(&reader, 32);
- ent.gname = readstr(&reader, 32);
- ent.devmajor = readoct(&reader, 8)?;
- ent.devminor = readoct(&reader, 8)?;
- const prefix = readstr(&reader, 155);
- let writer = memio::fixed(rd.name);
- memio::join(&writer, prefix, name)!;
- ent.name = memio::string(&writer)!;
- return ent;
-};
-
-// Seeks the underlying tar file to the entry following this one.
-export fn skip(ent: *entry) (void | io::error) = {
- let amt = ent.remain;
- if (amt % BLOCKSZ != 0) {
- amt += BLOCKSZ - (amt % BLOCKSZ);
- };
- match (io::seek(ent.src, amt: io::off, io::whence::CUR)) {
- case io::off =>
- return;
- case io::error => void;
- };
- io::copy(io::empty, ent)?;
-};
-
-const file_vtable: io::vtable = io::vtable {
- reader = &file_read,
- seeker = &file_seek,
- ...
-};
-
-const nonfile_vtable: io::vtable = io::vtable { ... };
-
-fn file_read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
- let ent = s: *ent_reader;
- assert(ent.vtable == &file_vtable);
- if (ent.remain == 0) {
- return io::EOF;
- };
-
- let z = len(buf);
- if (z > ent.remain) {
- z = ent.remain;
- };
- z = match (io::read(ent.src, buf[..z])?) {
- case let z: size =>
- yield z;
- case io::EOF =>
- // TODO: Truncated flag
- return io::EOF;
- };
- ent.remain -= z;
-
- // Read until we reach the block size
- if (ent.remain == 0 && ent.orig % BLOCKSZ != 0) {
- static let buf: [BLOCKSZ]u8 = [0...];
- io::readall(ent.src, buf[..BLOCKSZ - (ent.orig % BLOCKSZ)])?;
- };
-
- return z;
-};
-
-fn file_seek(
- s: *io::stream,
- off: io::off,
- w: io::whence,
-) (io::off | io::error) = {
- let ent = s: *ent_reader;
- assert(ent.vtable == &file_vtable);
-
- const orig = ent.orig: io::off;
- const cur = (ent.orig - ent.remain): io::off;
- let new = switch (w) {
- case io::whence::SET =>
- yield off;
- case io::whence::CUR =>
- yield cur + off;
- case io::whence::END =>
- yield orig + off;
- };
-
- if (new < 0) {
- new = 0;
- } else if (new > orig) {
- new = orig;
- };
-
- const rel = new - cur;
- io::seek(ent.src, rel, io::whence::CUR)?;
-
- ent.remain = (orig - new): size;
- return new;
-};
-
-fn readstr(rd: *memio::stream, ln: size) str = {
- const buf = match (memio::borrowedread(rd, ln)) {
- case let buf: []u8 =>
- assert(len(buf) == ln);
- yield buf;
- case io::EOF =>
- abort();
- };
- return c::tostr(buf: *[*]u8: *const c::char)!;
-};
-
-fn readoct(rd: *memio::stream, ln: size) (uint | invalid) = {
- const string = readstr(rd, ln);
- match (strconv::stou(string, strconv::base::OCT)) {
- case let u: uint =>
- return u;
- case =>
- return invalid;
- };
-};
-
-fn readsize(rd: *memio::stream, ln: size) (size | invalid) = {
- const string = readstr(rd, ln);
- match (strconv::stoz(string, strconv::base::OCT)) {
- case let z: size =>
- return z;
- case =>
- return invalid;
- };
-};
-
-fn zeroed(buf: []u8) bool = {
- for (let i = 0z; i < len(buf); i += 1) {
- if (buf[i] != 0) {
- return false;
- };
- };
- return true;
-};
diff --git a/format/tar/types.ha b/format/tar/types.ha
@@ -1,61 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// (c) Hare authors <https://harelang.org>
-
-use io;
-
-// The size of each block in a tar file.
-export def BLOCKSZ: size = 512;
-
-// A file or directory in a tar file.
-export type entry = struct {
- ent_reader,
- name: str,
- mode: uint,
- uid: uint,
- gid: uint,
- fsize: size,
- mtime: uint,
- checksum: uint,
- etype: entry_type,
- link: str,
- uname: str,
- gname: str,
- devmajor: u64,
- devminor: u64,
-};
-
-export type ent_reader = struct {
- vtable: io::stream,
- src: io::handle,
- orig: size,
- remain: size,
-};
-
-// A tar file entry. Note that some systems create tarballs with additional
-// vendor-specific values for the entry type, so a default case is recommended
-// when switching against this.
-export type entry_type = enum u8 {
- FILE,
- HARDLINK,
- SYMLINK,
- CHARDEV,
- BLOCKDEV,
- DIRECTORY,
- FIFO,
-};
-
-// Returned if the source file does not contain a valid ustar archive.
-export type invalid = !void;
-
-// Tagged union of all possible error types.
-export type error = !(invalid | io::error);
-
-// Converts an [[error]] to a human-friendly representation.
-export fn strerror(err: error) const str = {
- match (err) {
- case invalid =>
- return "Tar file is invalid";
- case let err: io::error =>
- return io::strerror(err);
- };
-};