std/archive/lib

std/archive/src/lib.trb

tar archives: Bytes.untarred reads what the tar programs in use write - POSIX ustar, pax (POSIX.1-2001) and GNU tar - and List.tarred writes ustar, with a pax header in front of an entry whose path or link does not fit ustar's fields. A package of the registry is a tar inside gzip (std/compression, docs/design/RELEASE.md section 7.2), and so is a release of the toolchain.

An entry is a regular file, a directory, a symbolic link or a hard link (TarKind), with the permission bits and the time of modification the archive gives it; a device and a pipe are refused. A path is kept as the archive writes it, ../x and /etc/x included: writing entries out is List.extract of std/archive/extract, which refuses every entry that would land outside of its directory.

The operations are members of the lists they work on, written where they are imported from: a member another package names is found in the module of the package that is imported, so use List.tarred, Buffer.untarred from "std/archive" needs them here.

Examples

use List.tarred, Buffer.untarred from "std/archive"

const entry = TarEntry "src/lib.trb", "public fn one(): Int \{\n  1\n\}\n".bytes()
const archive = List.of(entry).tarred().expect("a path ustar can hold")
print archive.untarred().map({ found => found.map({ _.path }).toList() })

type TarKind

type TarKind with Show, Equals, Hash

What an entry of an archive is.

case File

case File

A regular file: the bytes of the entry.

case Directory

case Directory

A directory. Its entry has no bytes.

type TarEntry

type TarEntry

One entry of an archive: its path, with / between the directories and without one at the end, its bytes, what it is, its permission bits the way POSIX writes them (0b111_101_101 is 0755, a program) and the time it was last written, in seconds since 1970. A file that is only a path and bytes is TarEntry(path, bytes): 0644, time 0.

field path

path: String

The path of the entry, with / between the directories and without one at the end.

field bytes

bytes: Bytes

The bytes of a file; empty for a directory and for a link.

field kind

kind: TarKind = TarKind.File

What the entry is; a file unless it says otherwise.

field mode

mode: Int = 0b110_100_100

The permission bits, the way POSIX writes them: 0b110_100_100 is 0644, which is also the default.

field modified

modified: Int = 0

The time the entry was last written, in seconds since 1970; 0 by default.

fn directory

static fn directory(path: String, mode: Int = 0b111_101_101): Self

A directory, 0755 unless mode says otherwise.

type ArchiveError

type ArchiveError with Show, Error

A path an archive cannot hold, or an archive that breaks the format: what, and the byte where it was found.

field reason

reason: String

What is wrong with the path or with the archive.

field offset

offset: Int

The byte of the archive where it was found, counted from its start.

fn show

fn show(): String

The reason and the byte, in one line: reason (at byte offset).

extend ArchiveError with From<ReadError>

extend ArchiveError with From<ReadError>

A read of std/binary that failed on the bytes of an archive, with its own words and offset.

fn from

static fn from(value: ReadError): Self

extend List<TarEntry>

extend List<TarEntry>

The operations on a list of entries, written where they are imported from (see the top of this file).

fn tarred

fn tarred(): Result<Bytes, ArchiveError>

The entries as a ustar archive, sorted by path - the hard links after the rest, so that what each names is unpacked before it - with nothing of the machine in it but what the entries say: no owner, no group, and every mode and time the entry's own. The same entries give the same bytes everywhere.

A path that fits ustar's fields - at most 100 bytes, or a prefix of 155 and a name of 100 split at a / - is written in them alone, so an archive of such paths is plain ustar that every tar reads. A longer path, a link target of more than 100 bytes, a size of 8 GiB or more and a time before 1970 or after 2242 go into a pax header (x) in front of the entry instead: pax is POSIX, every tar of the last twenty years reads it, and it has no limit of length - where GNU's own long names are GNU's alone.

Errors

  • ArchiveError for an empty path, two entries of one path, and a directory or a link with bytes.

extend Bytes

extend Bytes

The operation on the bytes of an archive.

fn untarred

fn untarred(): Result<List<TarEntry>, ArchiveError>

The entries of an archive, in the order it holds them: regular files, directories, symbolic links and hard links, each with its mode and its time. What the formats add around an entry is read into it - a path of GNU's long names (L, K), of ustar's prefix, or of a pax header (x for the next entry, g for every one after it: path, linkpath, size and mtime, every other key ignored) - and is no entry of its own. A trailing / of a directory is dropped.

Errors

  • ArchiveError for a header whose checksum does not match, a number that is none, a pax record that is not <length> <key>=<value>, a path that is not UTF-8, an archive that ends inside an entry or without its blocks of zeros, and an entry that is a device, a pipe, a sparse file or of a type this reader does not know.