~/bend-docscommunity

tar.bend checks

raw source on the hub · import 0x03330e0da0937f54ea73f35891d3540a/tar.bend as Tar

tar archives (POSIX ustar with PAX path and size records) of regular files and directories over Bytes. Source: https://github.com/paymog/bend-kit/tree/main/tar

2 imports
import Base
import 0x49814d83de8f70993a43e1002be29ecd/bytes.bend as Bytes

Types

type Entry source · line 8 · raw

Type

An archive is a list of entries in order. Names are raw bytes (UTF-8 by convention), kept byte for byte; a directory keeps whatever trailing '/' its name has. Nothing here touches the filesystem. import ./tar/tar.bend as Tar

type Dec source · line 72 · raw

Data

type Pax source · line 143 · raw

Type

Records from an 'x' header that apply to the next entry.

type Pst source · line 146 · raw

Type

type St source · line 253 · raw

Type

Definitions

def pad source · line 14 · raw

@+n:U32 -> U32

n rounded up to a whole 512-byte block; 0 when that would pass U32.

def wsum source · line 18 · raw

@+w:U32 -> U32

The sum of w's four bytes.

def bsum.go source · line 21 · raw

@n:Nat -> @r:Pair(Array<U32>, U32) -> @+k:U32 -> @+acc:U32 -> Pair(Array<U32>, U32)

def bsum source · line 31 · raw

@a:Array<U32> -> @+k:U32 -> Pair(Array<U32>, U32)

The sum of the 512 bytes from word k: 0 only for a zero block.

def oct.go source · line 35 · raw

@n:Nat -> @r:Pair(Array<U32>, U32) -> @+j:U32 -> @+ph:U32 -> @+acc:U32 -> @+ok:Bool -> Pair(Array<U32>, Pair(U32, Bool))

Octal digits after leading spaces, ended by NUL, space, or the field's end. ph: 0 leading, 1 digits, 2 done.

def oct source · line 51 · raw

@a:Array<U32> -> @+j:U32 -> @+w:U32 -> Pair(Array<U32>, Pair(U32, Bool))

The octal field of w bytes at j, and whether it is well formed and fits a U32.

def cstr.go source · line 54 · raw

@n:Nat -> @r:Pair(Array<U32>, U32) -> @+j:U32 -> @+k:U32 -> @+done:Bool -> Pair(Array<U32>, U32)

def cstr source · line 69 · raw

@a:Array<U32> -> @+j:U32 -> @+w:U32 -> Pair(Array<U32>, U32)

The count of bytes before the first NUL in the w bytes from j.

def dpk.of source · line 75 · raw

@r:Pair(Array<U32>, U32) -> Pair(Array<U32>, Pair(U32, Bool))

def dpk source · line 80 · raw

@ok:Bool -> @a:Array<U32> -> @+j:U32 -> Pair(Array<U32>, Pair(U32, Bool))

Byte j, and whether it is a decimal digit before end.

def dec.go source · line 87 · raw

@n:Nat -> @r:Pair(Array<U32>, Pair(U32, Bool)) -> @+j:U32 -> @+end:U32 -> @+acc:U32 -> @+ok:Bool -> Pair(Array<U32>, Dec)

def dec source · line 103 · raw

@a:Array<U32> -> @+j:U32 -> @+end:U32 -> Pair(Array<U32>, Dec)

Decimal digits from j up to end: the value, the index after the last digit, and whether it fits a U32.

def name.fin source · line 109 · raw

@+total:U32 -> @r:Pair(Array<U32>, Array<U32>) -> Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes)

Header i's name: prefix "/" name when the magic is POSIX ustar and the prefix is not empty.

def name.join source · line 113 · raw

@+i:U32 -> @+n:U32 -> @+pl:U32 -> @r:Pair(Array<U32>, Array<U32>) -> Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes)

def name.pre source · line 119 · raw

@+i:U32 -> @+n:U32 -> @r:Pair(Array<U32>, U32) -> Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes)

def pfx.len source · line 124 · raw

@ustar:Bool -> @a:Array<U32> -> @+i:U32 -> Pair(Array<U32>, U32)

def name.magic source · line 131 · raw

@+i:U32 -> @+n:U32 -> @r:Pair(Array<U32>, Bool) -> Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes)

def name.len source · line 135 · raw

@+i:U32 -> @r:Pair(Array<U32>, U32) -> Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes)

def header.name source · line 139 · raw

@a:Array<U32> -> @+i:U32 -> Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes)

def pr.opt source · line 151 · raw

@has:Bool -> @b:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> Maybe<&1, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes>

def pr.setpath source · line 159 · raw

@+next:U32 -> @+m:U32 -> @pax:Pax -> @r:Pair(Array<U32>, Array<U32>) -> Pair(Array<U32>, Pst)

An empty value unsets the keyword (POSIX pax), so the header's own name applies.

def pr.putsize source · line 164 · raw

@+next:U32 -> @+v:U32 -> @pax:Pax -> Pst

def pr.sized source · line 168 · raw

@ok:Bool -> @a:Array<U32> -> @+next:U32 -> @+v:U32 -> @pax:Pax -> Pair(Array<U32>, Pst)

def pr.setsize source · line 175 · raw

@+vs:U32 -> @+e:U32 -> @+next:U32 -> @pax:Pax -> @r:Pair(Array<U32>, Dec) -> Pair(Array<U32>, Pst)

def pr.size source · line 180 · raw

@hit:Bool -> @a:Array<U32> -> @+q:U32 -> @+e:U32 -> @+next:U32 -> @pax:Pax -> Pair(Array<U32>, Pst)

def pr.ksize source · line 187 · raw

@+q:U32 -> @+e:U32 -> @+next:U32 -> @pax:Pax -> @r:Pair(Array<U32>, Bool) -> Pair(Array<U32>, Pst)

def pr.path source · line 191 · raw

@hit:Bool -> @a:Array<U32> -> @+q:U32 -> @+e:U32 -> @+next:U32 -> @pax:Pax -> Pair(Array<U32>, Pst)

def pr.kpath source · line 199 · raw

@+q:U32 -> @+e:U32 -> @+next:U32 -> @pax:Pax -> @r:Pair(Array<U32>, Bool) -> Pair(Array<U32>, Pst)

def pr.rec source · line 204 · raw

@ok:Bool -> @a:Array<U32> -> @+q:U32 -> @+e:U32 -> @pax:Pax -> Pair(Array<U32>, Pst)

Unknown keywords (mtime, uid, ...) are skipped.

def pr.nl source · line 211 · raw

@+good:Bool -> @+q:U32 -> @+e:U32 -> @pax:Pax -> @r:Pair(Array<U32>, Bool) -> Pair(Array<U32>, Pst)

def pr.sp source · line 215 · raw

@+good:Bool -> @+q:U32 -> @+e:U32 -> @pax:Pax -> @r:Pair(Array<U32>, Bool) -> Pair(Array<U32>, Pst)

def pr.len source · line 220 · raw

@+p:U32 -> @+end:U32 -> @pax:Pax -> @r:Pair(Array<U32>, Dec) -> Pair(Array<U32>, Pst)

A record is "<len> <key>=<value>\n", len counting the whole record; q is the space, e the newline.

def pr.more source · line 227 · raw

@more:Bool -> @a:Array<U32> -> @+p:U32 -> @+end:U32 -> @pax:Pax -> Pair(Array<U32>, Pst)

def pr.run source · line 235 · raw

@fuel:Nat -> @r:Pair(Array<U32>, Pst) -> @+end:U32 -> Pair(Array<U32>, Maybe<&1, Pax>)

Each record takes at least three bytes; allow one step to reach the end and one to return it.

def pr.parse source · line 250 · raw

@a:Array<U32> -> @+s:U32 -> @+size:U32 -> @pax:Pax -> Pair(Array<U32>, Maybe<&1, Pax>)

def name.of source · line 258 · raw

@path:Maybe<&1, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes> -> @a:Array<U32> -> @+i:U32 -> Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes)

def file.data source · line 265 · raw

@+next:U32 -> @+size:U32 -> @name:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @acc:List<&1, Entry> -> @r:Pair(Array<U32>, Array<U32>) -> Pair(Array<U32>, St)

def file.name source · line 269 · raw

@+i:U32 -> @+size:U32 -> @+next:U32 -> @acc:List<&1, Entry> -> @r:Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes) -> Pair(Array<U32>, St)

def entry.file source · line 273 · raw

@+i:U32 -> @+size:U32 -> @+next:U32 -> @acc:List<&1, Entry> -> @pax:Pax -> @a:Array<U32> -> Pair(Array<U32>, St)

def dir.name source · line 277 · raw

@+next:U32 -> @acc:List<&1, Entry> -> @r:Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes) -> Pair(Array<U32>, St)

def entry.dir source · line 282 · raw

@+i:U32 -> @+next:U32 -> @acc:List<&1, Entry> -> @pax:Pax -> @a:Array<U32> -> Pair(Array<U32>, St)

A directory's size field is skipped over like data.

def step.pax source · line 286 · raw

@+next:U32 -> @acc:List<&1, Entry> -> @r:Pair(Array<U32>, Maybe<&1, Pax>) -> Pair(Array<U32>, St)

def step.type source · line 296 · raw

@+t:U32 -> @a:Array<U32> -> @+i:U32 -> @+size:U32 -> @+next:U32 -> @pax:Pax -> @acc:List<&1, Entry> -> Pair(Array<U32>, St)

'0' and NUL are regular files, '5' directories, 'x' PAX records for the next entry, 'g' global PAX records (skipped). Links, devices, FIFOs, GNU extensions and the rest fail.

def step.kind source · line 311 · raw

@fit:Bool -> @+t:U32 -> @a:Array<U32> -> @+i:U32 -> @+size:U32 -> @+next:U32 -> @pax:Pax -> @acc:List<&1, Entry> -> Pair(Array<U32>, St)

def step.fit source · line 319 · raw

@+len:U32 -> @+i:U32 -> @+size:U32 -> @+ok:Bool -> @pax:Pax -> @acc:List<&1, Entry> -> @r:Pair(Array<U32>, U32) -> Pair(Array<U32>, St)

The data and its padding to a whole block must be in the archive.

def step.size source · line 327 · raw

@+len:U32 -> @+i:U32 -> @pax:Pax -> @acc:List<&1, Entry> -> @r:Pair(Array<U32>, Pair(U32, Bool)) -> Pair(Array<U32>, St)

A PAX size overrides the header's field, which then need not parse.

def step.hdr source · line 332 · raw

@ok:Bool -> @a:Array<U32> -> @+len:U32 -> @+i:U32 -> @pax:Pax -> @acc:List<&1, Entry> -> Pair(Array<U32>, St)

def step.ck3 source · line 340 · raw

@+len:U32 -> @+i:U32 -> @+s:U32 -> @+ck:U32 -> @+ok:Bool -> @+x:U32 -> @pax:Pax -> @acc:List<&1, Entry> -> @r:Pair(Array<U32>, U32) -> Pair(Array<U32>, St)

The checksum counts the 8 checksum bytes (words 37 and 38) as spaces.

def step.ck2 source · line 344 · raw

@+len:U32 -> @+i:U32 -> @+s:U32 -> @+ck:U32 -> @+ok:Bool -> @pax:Pax -> @acc:List<&1, Entry> -> @r:Pair(Array<U32>, U32) -> Pair(Array<U32>, St)

def step.ck source · line 348 · raw

@+len:U32 -> @+i:U32 -> @+s:U32 -> @pax:Pax -> @acc:List<&1, Entry> -> @r:Pair(Array<U32>, Pair(U32, Bool)) -> Pair(Array<U32>, St)

def end.pax source · line 353 · raw

@pax:Pax -> @acc:List<&1, Entry> -> St

The end: a second zero block, with no PAX records left waiting for an entry.

def end.of source · line 361 · raw

@ok:Bool -> @pax:Pax -> @acc:List<&1, Entry> -> St

def end.sum source · line 368 · raw

@pax:Pax -> @acc:List<&1, Entry> -> @r:Pair(Array<U32>, U32) -> Pair(Array<U32>, St)

def end.fits source · line 372 · raw

@ok:Bool -> @a:Array<U32> -> @+i:U32 -> @pax:Pax -> @acc:List<&1, Entry> -> Pair(Array<U32>, St)

def step.zero source · line 379 · raw

@zero:Bool -> @a:Array<U32> -> @+len:U32 -> @+i:U32 -> @+s:U32 -> @pax:Pax -> @acc:List<&1, Entry> -> Pair(Array<U32>, St)

def step.sum source · line 386 · raw

@+len:U32 -> @+i:U32 -> @pax:Pax -> @acc:List<&1, Entry> -> @r:Pair(Array<U32>, U32) -> Pair(Array<U32>, St)

def step.fits source · line 390 · raw

@ok:Bool -> @a:Array<U32> -> @+len:U32 -> @+i:U32 -> @pax:Pax -> @acc:List<&1, Entry> -> Pair(Array<U32>, St)

def step source · line 397 · raw

@a:Array<U32> -> @+len:U32 -> @+i:U32 -> @pax:Pax -> @acc:List<&1, Entry> -> Pair(Array<U32>, St)

def run source · line 400 · raw

@fuel:Nat -> @r:Pair(Array<U32>, St) -> @+len:U32 -> Maybe<&1, List<&1, Entry>>

def decode source · line 418 · raw

@archive:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> Maybe<&1, List<&1, Entry>>

The entries in archive order. None for a bad header checksum, a malformed size or PAX record, an entry or its padding cut short, a missing end (two zero blocks), or any type but file, directory, and PAX records. Bytes after the end are ignored. fuel: each header step eats a block, so len / 512 + 2 steps are enough.

def put.str source · line 424 · raw

@s:String -> @a:Array<U32> -> @+j:U32 -> Array<U32>

def oct.put source · line 432 · raw

@n:Nat -> @a:Array<U32> -> @+j:U32 -> @+v:U32 -> Array<U32>

n octal digits of v ending at j, written back to front.

def dec.put source · line 440 · raw

@n:Nat -> @a:Array<U32> -> @+j:U32 -> @+v:U32 -> Array<U32>

n decimal digits of v ending at j, written back to front.

def digits.go source · line 447 · raw

@n:Nat -> @+v:U32 -> @+c:U32 -> U32

def digits source · line 455 · raw

@+v:U32 -> U32

The count of decimal digits in v.

def hdr.fields source · line 459 · raw

@+mode:U32 -> @+size:U32 -> @+t:U32 -> @a:Array<U32> -> Array<U32>

mode, uid 0, gid 0, size, mtime 0, the checksum as spaces, type, and the POSIX ustar magic.

def hdr.ck source · line 464 · raw

@r:Pair(Array<U32>, U32) -> Array<U32>

Six octal digits, NUL, space: the last space stays from the fields.

def hdr.of source · line 468 · raw

@+mode:U32 -> @+size:U32 -> @+t:U32 -> @r:Pair(Array<U32>, Array<U32>) -> Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes)

def hdr source · line 473 · raw

@src:Array<U32> -> @+n:U32 -> @+mode:U32 -> @+size:U32 -> @+t:U32 -> Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes)

A header block named by the first n bytes of src, handing src back.

def blk source · line 477 · raw

@b:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes

b with zero bytes up to the next whole block. Bytes past len are already 0, so only len and room grow.

def zeros source · line 482 · raw

@b:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @+n:U32 -> 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes

def rec.fin source · line 486 · raw

@+l:U32 -> @r:Pair(Array<U32>, Array<U32>) -> Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes)

def rec.len source · line 491 · raw

@+nl:U32 -> U32

A PAX path record's length: nl + 7 plus its own digits, found as in POSIX pax.

def pr.record source · line 496 · raw

@nb:Array<U32> -> @+nl:U32 -> Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes)

"<len> path=<name>\n".

def hdr.snd source · line 502 · raw

@r:Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes) -> 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes

def xhdr.of source · line 506 · raw

@+rl:U32 -> @b:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes

def enc.paxout source · line 510 · raw

@out:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @rec:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes

def enc.pax source · line 514 · raw

@out:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @r:Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes) -> Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes)

def enc.hdr source · line 518 · raw

@+dl:U32 -> @db:Array<U32> -> @out:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @r:Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes) -> 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes

def enc.main source · line 523 · raw

@+nl:U32 -> @+t:U32 -> @+mode:U32 -> @+dl:U32 -> @db:Array<U32> -> @r:Pair(Array<U32>, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes) -> 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes

The header keeps the name's first 100 bytes; a longer name rides in the PAX record before it.

def enc.long source · line 527 · raw

@long:Bool -> @nb:Array<U32> -> @+nl:U32 -> @out:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @+t:U32 -> @+mode:U32 -> @+dl:U32 -> @db:Array<U32> -> 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes

def enc.ok source · line 534 · raw

@ok:Bool -> @nb:Array<U32> -> @+nl:U32 -> @out:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @+t:U32 -> @+mode:U32 -> @+dl:U32 -> @db:Array<U32> -> Maybe<&1, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes>

def enc.room source · line 542 · raw

@+ol:U32 -> @+dl:U32 -> @+nl:U32 -> @+long:Bool -> Bool

Does an entry fit what is left of the U32 length, with room for the end blocks? Every step checks before it subtracts.

def enc.check source · line 553 · raw

@+t:U32 -> @+mode:U32 -> @+dl:U32 -> @db:Array<U32> -> @out:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @r:Pair(0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes, Maybe<&2, U32>) -> Maybe<&1, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes>

A name must be non-empty and hold no NUL, which the header's name field could not keep.

def enc source · line 560 · raw

@e:Entry -> @out:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> Maybe<&1, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes>

def enc.end source · line 568 · raw

@m:Maybe<&1, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes> -> Maybe<&1, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes>

def enc.go source · line 575 · raw

@xs:List<&1, Entry> -> @m:Maybe<&1, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes> -> Maybe<&1, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes>

def encode source · line 590 · raw

@entries:List<&1, Entry> -> Maybe<&1, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes>

A POSIX ustar archive of the entries in order, ended by two zero blocks. Files are mode 0644 and directories 0755, with uid, gid, and mtime 0. A name over 100 bytes goes in a PAX path record. None when a name is empty or holds a NUL byte, or when the archive would pass 4 GiB (U32 lengths). ustar's 8 GiB size field holds any U32 length, so encode never needs a PAX size.