Initial orientator

This commit is contained in:
2026-05-13 19:52:51 +03:00
commit 49fb60f831
24 changed files with 1919 additions and 0 deletions

160
internal/extract/archive.go Normal file
View File

@@ -0,0 +1,160 @@
// Package extract iterates over the contents of common archive formats
// (zip, tar, tar.gz, 7z, rar) without extracting to disk.
package extract
import (
"archive/tar"
"archive/zip"
"bytes"
"compress/gzip"
"errors"
"fmt"
"io"
"path/filepath"
"strings"
"github.com/bodgit/sevenzip"
"github.com/nwaples/rardecode/v2"
)
type Entry struct {
Name string
Data []byte
}
type WalkFn func(Entry) error
// Walk dispatches by extension and calls fn for each file entry.
func Walk(name string, data []byte, fn WalkFn) error {
ext := strings.ToLower(filepath.Ext(name))
switch ext {
case ".zip":
return walkZip(data, fn)
case ".tar":
return walkTar(bytes.NewReader(data), fn)
case ".gz", ".tgz":
return walkTarGz(data, fn)
case ".7z":
return walk7z(data, fn)
case ".rar":
return walkRar(data, fn)
}
return fmt.Errorf("unsupported archive: %s", ext)
}
// IsArchive reports whether name has an extension we know how to extract.
func IsArchive(name string) bool {
switch strings.ToLower(filepath.Ext(name)) {
case ".zip", ".tar", ".gz", ".tgz", ".7z", ".rar":
return true
}
return false
}
func walkZip(data []byte, fn WalkFn) error {
r, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
if err != nil {
return err
}
for _, f := range r.File {
if f.FileInfo().IsDir() {
continue
}
rc, err := f.Open()
if err != nil {
return err
}
buf, err := io.ReadAll(rc)
rc.Close()
if err != nil {
return err
}
if err := fn(Entry{Name: f.Name, Data: buf}); err != nil {
return err
}
}
return nil
}
func walkTar(r io.Reader, fn WalkFn) error {
tr := tar.NewReader(r)
for {
hdr, err := tr.Next()
if errors.Is(err, io.EOF) {
return nil
}
if err != nil {
return err
}
if hdr.Typeflag != tar.TypeReg {
continue
}
buf, err := io.ReadAll(tr)
if err != nil {
return err
}
if err := fn(Entry{Name: hdr.Name, Data: buf}); err != nil {
return err
}
}
}
func walkTarGz(data []byte, fn WalkFn) error {
gz, err := gzip.NewReader(bytes.NewReader(data))
if err != nil {
return err
}
defer gz.Close()
return walkTar(gz, fn)
}
func walk7z(data []byte, fn WalkFn) error {
r, err := sevenzip.NewReader(bytes.NewReader(data), int64(len(data)))
if err != nil {
return err
}
for _, f := range r.File {
if f.FileInfo().IsDir() {
continue
}
rc, err := f.Open()
if err != nil {
return err
}
buf, err := io.ReadAll(rc)
rc.Close()
if err != nil {
return err
}
if err := fn(Entry{Name: f.Name, Data: buf}); err != nil {
return err
}
}
return nil
}
func walkRar(data []byte, fn WalkFn) error {
r, err := rardecode.NewReader(bytes.NewReader(data))
if err != nil {
return err
}
for {
hdr, err := r.Next()
if errors.Is(err, io.EOF) {
return nil
}
if err != nil {
return err
}
if hdr.IsDir {
continue
}
buf, err := io.ReadAll(r)
if err != nil {
return err
}
if err := fn(Entry{Name: hdr.Name, Data: buf}); err != nil {
return err
}
}
}