stuffit

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 29, 2026 License: LGPL-2.1 Imports: 14 Imported by: 0

Documentation

Overview

Package stuffit parses and extracts classic StuffIt archives.

Supported formats:

  • Classic SIT! archives (StuffIt 1.5 through 5.5)
  • StuffIt 5.x archives (the "StuffIt (c)1997-..." signature)
  • MacBinary-wrapped .sit files
  • AppleSingle-wrapped .sit files
  • BinHex 4.0 (.hqx) wrappers that contain a StuffIt archive

StuffIt X (.sitx) is not supported.

The package never writes files itself. Developers supply their own fork handling in one of two ways:

  • Push: pass a ForkWriter (or the function-based Handler) to Extract or ExtractSelected. WriteDataFork and WriteResourceFork are independent hooks. Implement DirectoryWriter as well if you need folder entries.
  • Pull: iterate Archive.Entries and call File.OpenFork with entry.DataFork or entry.ResourceFork to stream each fork yourself.

Typical usage:

f, err := stuffit.Open("archive.sit")
if err != nil {
	return err
}
defer f.Close()

err = f.Extract(stuffit.Handler{
	DataFork: func(entry stuffit.Entry, r io.Reader) error {
		// persist the data fork
		return nil
	},
	ResourceFork: func(entry stuffit.Entry, r io.Reader) error {
		// persist the resource fork (AppleDouble, xattr, ...)
		return nil
	},
}, stuffit.ExtractOptions{})

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func SetDebug

func SetDebug(enabled bool)

SetDebug enables or disables package debug logging.

Types

type AppleSingleMetadata

type AppleSingleMetadata struct {
	Name             string
	Comment          string
	FileType         uint32
	Creator          uint32
	FinderFlags      uint16
	DataForkSize     uint32
	ResourceForkSize uint32
	// Double is true for AppleDouble (resource-fork sidecar layout).
	Double bool
}

AppleSingleMetadata is recorded when the archive is wrapped in AppleSingle or AppleDouble (magic 0x00051600 / 0x00051607).

type Archive

type Archive struct {
	Format    FormatVersion
	Comment   string
	Encrypted bool
	Wrappers  []ArchiveWrapper
	Entries   []Entry
	// contains filtered or unexported fields
}

Archive is the parsed catalog of a StuffIt file.

type ArchiveWrapper

type ArchiveWrapper struct {
	MacBinary   *MacBinaryMetadata
	BinHex      *BinHexMetadata
	AppleSingle *AppleSingleMetadata
}

ArchiveWrapper describes an outer encoding that contained the StuffIt payload.

type BinHexMetadata

type BinHexMetadata struct {
	Name             string
	FileType         uint32
	Creator          uint32
	Flags            uint16
	DataForkSize     uint32
	ResourceForkSize uint32
}

BinHexMetadata is recorded when the archive is wrapped in BinHex 4.0.

type CompressionMethod

type CompressionMethod uint8

CompressionMethod is the per-fork StuffIt compression identifier stored in the archive catalog.

const (
	// MethodNone stores the fork uncompressed (StuffIt method 0).
	MethodNone CompressionMethod = 0

	// MethodRLE90 is PackIt/BinHex-style run-length encoding (method 1).
	// Byte 0x90 is an escape: 0x90 0x00 is a literal 0x90; 0x90 N repeats
	// the previous byte so it occurs N times in total.
	MethodRLE90 CompressionMethod = 1

	// MethodCompressLZW is Unix-compress-style LZW (method 2, "Compress").
	// Codes are LSB-first, 9–14 bits, with a clear code of 256.
	MethodCompressLZW CompressionMethod = 2

	// MethodHuffman is classic StuffIt Huffman coding (method 3). The
	// bitstream starts with an explicit binary tree, then one symbol per
	// walk of that tree.
	MethodHuffman CompressionMethod = 3

	// MethodLZAH is LZSS with an adaptive Huffman tree (method 5), similar
	// to LZHUF. Window is 4 KiB; 314 leaves cover literals and match lengths.
	MethodLZAH CompressionMethod = 5

	// MethodFixedHuffman is StuffIt's "Faster" compressor (method 6): blocks
	// of a fixed Huffman codebook plus a byte translation table, followed by
	// Apple PackBits. Some blocks skip Huffman and are PackBits only.
	MethodFixedHuffman CompressionMethod = 6

	// MethodMW is the Miller–Wegman compact dictionary coder (method 8).
	// Codes grow from 9 bits; values below 256 are literals and higher
	// values are pointers into a 16 KiB dictionary of earlier strings.
	MethodMW CompressionMethod = 8

	// MethodLZHuffman is StuffIt's "Better" compressor (method 13): LZ77
	// with Huffman-coded literals, match lengths, and offsets. Window is
	// 64 KiB. Tables may be sent in-stream or chosen from five presets.
	MethodLZHuffman CompressionMethod = 13

	// MethodInstaller14 is the LZ77+Huffman codec used by StuffIt Installer
	// Maker (method 14). Window is 256 KiB; each block carries two Huffman
	// trees for literals/lengths and for match offsets.
	MethodInstaller14 CompressionMethod = 14

	// MethodArsenic15 is the bzip2-like compressor used by StuffIt 5.5
	// (method 15): Burrows–Wheeler transform, move-to-front, and an
	// adaptive arithmetic coder.
	MethodArsenic15 CompressionMethod = 15
)

func (CompressionMethod) Name

func (m CompressionMethod) Name() string

Name returns a human-readable label for the compression method.

type DataForkFunc

type DataForkFunc func(entry Entry, r ForkReader) error

DataForkFunc writes one data fork.

type DirectoryFunc

type DirectoryFunc func(entry Entry) error

DirectoryFunc is called for each folder entry when Handler.Directory is set.

type DirectoryWriter

type DirectoryWriter interface {
	CreateDirectory(entry Entry) error
}

DirectoryWriter may be implemented alongside ForkWriter to receive folder entries. Parser.Extract and Parser.ExtractSelected skip directories when the sink does not implement this interface.

type Entry

type Entry struct {
	Name         string
	Path         string
	IsDirectory  bool
	ModTime      time.Time
	CreationTime time.Time
	FileType     uint32
	FileCreator  uint32
	FinderFlags  uint16
	Comment      string
	DataFork     *Fork
	ResourceFork *Fork
}

Entry is one file or directory recorded in the archive catalog.

type ExtractOptions

type ExtractOptions struct {
	// Password is required for StuffIt 5 archives (or entries) that are
	// encrypted. Classic SIT encryption is not supported.
	Password []byte
}

ExtractOptions controls decryption during extraction.

type File

type File struct {
	// Archive is the parsed catalog. It is never nil after a successful Open.
	Archive *Archive
	// contains filtered or unexported fields
}

File is an opened StuffIt archive. Close it when extraction is finished.

func Open

func Open(path string) (*File, error)

Open opens path, detects the format (classic SIT, StuffIt 5, MacBinary, AppleSingle, or BinHex), and parses the catalog. The underlying file stays open so forks can be extracted; call Close when done.

func (*File) Close

func (f *File) Close() error

Close releases the underlying file. It is safe to call more than once.

func (*File) Extract

func (f *File) Extract(sink ForkWriter, opts ExtractOptions) error

Extract writes every file fork through sink. Folder entries are forwarded only when sink implements DirectoryWriter.

func (*File) ExtractSelected

func (f *File) ExtractSelected(sink ForkWriter, opts ExtractOptions, onlyPaths []string) error

ExtractSelected is like Extract but only visits entries whose archive path is listed in onlyPaths. An empty onlyPaths list extracts everything.

func (*File) OpenFork

func (f *File) OpenFork(fork *Fork, opts ExtractOptions) (ForkReader, error)

OpenFork returns a streaming reader for one Macintosh fork. Pass entry.DataFork or entry.ResourceFork from this archive. The caller must consume the reader fully; CRC16 is checked when the stream reaches EOF.

Example
package main

import (
	"io"

	"github.com/ObsoleteMadness/StuffIt-Go/stuffit"
)

func main() {
	f, err := stuffit.Open("archive.sit")
	if err != nil {
		return
	}
	defer f.Close()

	for _, e := range f.Archive.Entries {
		if e.DataFork != nil {
			r, err := f.OpenFork(e.DataFork, stuffit.ExtractOptions{})
			if err != nil {
				return
			}
			_, _ = io.Copy(io.Discard, r)
		}
		if e.ResourceFork != nil {
			r, err := f.OpenFork(e.ResourceFork, stuffit.ExtractOptions{})
			if err != nil {
				return
			}
			_, _ = io.Copy(io.Discard, r)
		}
	}
}

func (*File) Parser

func (f *File) Parser() *Parser

Parser returns the parser used to read fork payloads.

type Fork

type Fork struct {
	Kind             ForkKind
	Method           CompressionMethod
	CompressedSize   uint32
	UncompressedSize uint32
	CRC16            uint16
	// DataOffset is the absolute byte offset of the compressed fork in the
	// archive source passed to [NewParser].
	DataOffset  int64
	IsEncrypted bool
	// EntryKey is the per-entry RC4 salt used by StuffIt 5 encryption.
	// It is empty for unencrypted forks.
	EntryKey []byte
}

Fork describes one Macintosh fork of an archive entry.

type ForkKind

type ForkKind int

ForkKind identifies whether a Fork is a Macintosh data or resource fork.

const (
	// ForkData is the Macintosh data fork.
	ForkData ForkKind = iota
	// ForkResource is the Macintosh resource fork.
	ForkResource
)

func (ForkKind) String

func (k ForkKind) String() string

String returns "data", "resource", or "unknown".

type ForkReader

type ForkReader interface {
	io.Reader
}

ForkReader is the decompressed (and decrypted, if needed) byte stream for one Macintosh fork. Callers typically copy it with io.Copy.

type ForkWriter

type ForkWriter interface {
	WriteDataFork(entry Entry, r ForkReader) error
	WriteResourceFork(entry Entry, r ForkReader) error
}

ForkWriter is the primary hook for custom Macintosh fork handling. The package never writes files itself; implement this interface to store forks however you want (AppleDouble sidecars, extended attributes, a custom database, etc.).

Each method is called at most once per entry that actually has that fork. r is a streaming reader of the decompressed bytes and should be consumed before the method returns. Any unread remainder is drained by Extract so CRC verification still runs.

See Handler for a function-based adapter, File.OpenFork to pull a single fork yourself, and DirectoryWriter if you also need folder entries.

type FormatVersion

type FormatVersion int

FormatVersion identifies the on-disk StuffIt catalog format.

const (
	// FormatSIT1 is a classic SIT! archive (StuffIt 1.5 through 5.5).
	FormatSIT1 FormatVersion = 1
	// FormatSIT5 is a StuffIt 5.x archive ("StuffIt (c)1997-..." signature).
	FormatSIT5 FormatVersion = 5
)

func (FormatVersion) String

func (v FormatVersion) String() string

String returns a short name for the archive format.

type Handler

type Handler struct {
	DataFork     DataForkFunc
	ResourceFork ResourceForkFunc
	Directory    DirectoryFunc
}

Handler is a function-based ForkWriter (and optional DirectoryWriter) for callers that do not want to define a named type. Set DataFork and ResourceFork independently; a nil callback skips that fork (the stream is still consumed). Set Directory if you want folder entries.

Example
package main

import (
	"io"
	"os"

	"github.com/ObsoleteMadness/StuffIt-Go/stuffit"
)

func main() {
	f, err := stuffit.Open("archive.sit")
	if err != nil {
		return
	}
	defer f.Close()

	err = f.Extract(stuffit.Handler{
		DataFork: func(entry stuffit.Entry, r stuffit.ForkReader) error {
			out, err := os.Create(entry.Path)
			if err != nil {
				return err
			}
			defer out.Close()
			_, err = io.Copy(out, r)
			return err
		},
		ResourceFork: func(entry stuffit.Entry, r stuffit.ForkReader) error {
			// AppleDouble sidecar, xattr, or any other resource-fork store.
			_, err := io.Copy(io.Discard, r)
			return err
		},
	}, stuffit.ExtractOptions{})
	_ = err
}

func (Handler) CreateDirectory

func (h Handler) CreateDirectory(entry Entry) error

CreateDirectory implements DirectoryWriter.

func (Handler) WriteDataFork

func (h Handler) WriteDataFork(entry Entry, r ForkReader) error

WriteDataFork implements ForkWriter.

func (Handler) WriteResourceFork

func (h Handler) WriteResourceFork(entry Entry, r ForkReader) error

WriteResourceFork implements ForkWriter.

type MacBinaryMetadata

type MacBinaryMetadata struct {
	Name string
}

MacBinaryMetadata is recorded when the archive is wrapped in a MacBinary header.

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

func NewParser

func NewParser(r io.ReaderAt, size int64) *Parser

NewParser reads a StuffIt archive (or a MacBinary / AppleSingle / BinHex wrapper) from r. size is the number of readable bytes starting at offset 0.

func (*Parser) Extract

func (p *Parser) Extract(arc *Archive, sink ForkWriter, opts ExtractOptions) error

Extract writes every file fork in arc through sink. Folder entries are forwarded only when sink implements DirectoryWriter.

func (*Parser) ExtractSelected

func (p *Parser) ExtractSelected(arc *Archive, sink ForkWriter, opts ExtractOptions, onlyPaths []string) error

ExtractSelected writes forks for entries whose archive path is listed in onlyPaths. An empty or nil onlyPaths list extracts every file. Resource forks are delivered before data forks so callers can create AppleDouble sidecars (or equivalent metadata) first.

func (*Parser) OpenFork

func (p *Parser) OpenFork(arc *Archive, fork *Fork, opts ExtractOptions) (ForkReader, error)

OpenFork returns a streaming reader for one Macintosh fork. fork should be an entry's DataFork or ResourceFork from arc. The caller must consume the reader fully; CRC16 is checked when the stream reaches EOF.

func (*Parser) Parse

func (p *Parser) Parse() (*Archive, error)

Parse detects the container and catalog format, then returns the archive listing. Fork payloads stay in r until Extract or ExtractSelected is called.

type ResourceForkFunc

type ResourceForkFunc func(entry Entry, r ForkReader) error

ResourceForkFunc writes one resource fork.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL