Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mholt committed Nov 19, 2024
0 parents commit 1805df0
Show file tree
Hide file tree
Showing 34 changed files with 5,036 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# These are supported funding model platforms

github: [mholt] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
41 changes: 41 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
name: Bug report
about: For behaviors which violate documentation or cause incorrect results
title: ''
labels: ''
assignees: ''

---

<!--
This template is for bug reports! (If your issue doesn't fit this template, it's probably a feature request instead.)
To fill out this template, simply replace these comments with your answers.
Please do not skip questions; this will slow down the resolution process.
-->

## What version of the package or command are you using?
<!-- A commit sha or tag is fine -->


## What are you trying to do?
<!-- Please describe clearly what you are trying to do thoroughly enough so that a reader with no context can repeat the same process. -->


## What steps did you take?
<!-- Explain exactly how we can reproduce this bug; attach sample archive files if relevant -->


## What did you expect to happen, and what actually happened instead?
<!-- Please make it clear what the bug actually is -->


## How do you think this should be fixed?
<!-- Being specific by linking to lines of code and even suggesting changes will yield fastest resolution -->


## Please link to any related issues, pull requests, and/or discussion
<!-- This will help add crucial context to your report -->


## Bonus: What do you use this package for, and do you have any other suggestions or feedback?
<!-- We'd like to know! -->
28 changes: 28 additions & 0 deletions .github/ISSUE_TEMPLATE/generic-feature-request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: Generic feature request
about: Suggest an idea for this project
title: ''
labels: feature request
assignees: ''

---

<!--
This issue template is for feature requests! If you are reporting a bug instead, please switch templates.
To fill this out, simply replace these comments with your answers.
-->

## What would you like to have changed?
<!-- Describe the feature or enhancement you are requesting -->


## Why is this feature a useful, necessary, and/or important addition to this project?
<!-- Please justify why this change adds value to the project, considering the added maintenance burden and complexity the change introduces -->


## What alternatives are there, or what are you doing in the meantime to work around the lack of this feature?
<!-- We want to get an idea of what is being done in practice, or how other projects support your feature -->


## Please link to any relevant issues, pull requests, or other discussions.
<!-- This adds crucial context to your feature request and can speed things up -->
32 changes: 32 additions & 0 deletions .github/ISSUE_TEMPLATE/new-format-request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
name: New format request
about: Request a new archival or compression format
title: ''
labels: ''
assignees: ''

---

<!--
This template is specifically for adding support for a new archive or compression format to the library. Please, precisely one format per issue.
To fill this out, replace these comments with your answers.
-->

## Introduce the format you are requesting.
<!-- What is it called, what is it used for, etc? Some background information. -->


## What do YOU use this format for?
<!-- We want to know YOUR specific use cases; why do YOU need this format? -->


## What is the format's conventional file extension(s)?
<!-- Don't overthink this one, it's a simple question. -->


## Please link to the format's formal or official specification(s).
<!-- If there isn't a formal spec, link to the most official documentation for the format. Note that unstandardized formats are less likely to be added unless it is in high-enough demand. -->


## Which Go libraries could be used to implement this format?
<!-- This project itself does not actually implement low-level format reading and writing algorithms, so link to pure-Go libraries that do. Dependencies that use cgo or invoke external commands are not eligible for this project. -->
23 changes: 23 additions & 0 deletions .github/workflows/macos-latest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Mac

on: [push, pull_request]

jobs:

build-and-test:

strategy:
matrix:
go-version: [1.23]
runs-on: macos-latest
steps:
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

- name: Checkout code
uses: actions/checkout@v4

- name: Test
run: go test -v -race ./...
23 changes: 23 additions & 0 deletions .github/workflows/ubuntu-latest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Linux

on: [push, pull_request]

jobs:

build-and-test:

strategy:
matrix:
go-version: [1.23]
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

- name: Checkout code
uses: actions/checkout@v4

- name: Test
run: go test -v -race ./...
23 changes: 23 additions & 0 deletions .github/workflows/windows-latest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Windows

on: [push, pull_request]

jobs:

build-and-test:

strategy:
matrix:
go-version: [1.23]
runs-on: windows-latest
steps:
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

- name: Checkout code
uses: actions/checkout@v4

- name: Test
run: go test -v -race ./...
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
_gitignore
129 changes: 129 additions & 0 deletions 7z.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package archives

import (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/fs"
"log"
"path"
"strings"

"github.com/bodgit/sevenzip"
)

func init() {
RegisterFormat(SevenZip{})

// looks like the sevenzip package registers a lot of decompressors for us automatically:
// https://github.com/bodgit/sevenzip/blob/46c5197162c784318b98b9a3f80289a9aa1ca51a/register.go#L38-L61
}

type SevenZip struct {
// If true, errors encountered during reading or writing
// a file within an archive will be logged and the
// operation will continue on remaining files.
ContinueOnError bool

// The password, if dealing with an encrypted archive.
Password string
}

func (z SevenZip) Extension() string { return ".7z" }

func (z SevenZip) Match(_ context.Context, filename string, stream io.Reader) (MatchResult, error) {
var mr MatchResult

// match filename
if strings.Contains(strings.ToLower(filename), z.Extension()) {
mr.ByName = true
}

// match file header
buf, err := readAtMost(stream, len(sevenZipHeader))
if err != nil {
return mr, err
}
mr.ByStream = bytes.Equal(buf, sevenZipHeader)

return mr, nil
}

// Archive is not implemented for 7z because I do not know of a pure-Go 7z writer.

// Extract extracts files from z, implementing the Extractor interface. Uniquely, however,
// sourceArchive must be an io.ReaderAt and io.Seeker, which are oddly disjoint interfaces
// from io.Reader which is what the method signature requires. We chose this signature for
// the interface because we figure you can Read() from anything you can ReadAt() or Seek()
// with. Due to the nature of the zip archive format, if sourceArchive is not an io.Seeker
// and io.ReaderAt, an error is returned.
func (z SevenZip) Extract(ctx context.Context, sourceArchive io.Reader, handleFile FileHandler) error {
sra, ok := sourceArchive.(seekReaderAt)
if !ok {
return fmt.Errorf("input type must be an io.ReaderAt and io.Seeker because of zip format constraints")
}

size, err := streamSizeBySeeking(sra)
if err != nil {
return fmt.Errorf("determining stream size: %w", err)
}

zr, err := sevenzip.NewReaderWithPassword(sra, size, z.Password)
if err != nil {
return err
}

// important to initialize to non-nil, empty value due to how fileIsIncluded works
skipDirs := skipList{}

for i, f := range zr.File {
f := f // make a copy for the Open closure
if err := ctx.Err(); err != nil {
return err // honor context cancellation
}

if fileIsIncluded(skipDirs, f.Name) {
continue
}

fi := f.FileInfo()
file := FileInfo{
FileInfo: fi,
Header: f.FileHeader,
NameInArchive: f.Name,
Open: func() (fs.File, error) {
openedFile, err := f.Open()
if err != nil {
return nil, err
}
return fileInArchive{openedFile, fi}, nil
},
}

err := handleFile(ctx, file)
if errors.Is(err, fs.SkipDir) {
// if a directory, skip this path; if a file, skip the folder path
dirPath := f.Name
if !file.IsDir() {
dirPath = path.Dir(f.Name) + "/"
}
skipDirs.add(dirPath)
} else if err != nil {
if z.ContinueOnError {
log.Printf("[ERROR] %s: %v", f.Name, err)
continue
}
return fmt.Errorf("handling file %d: %s: %w", i, f.Name, err)
}
}

return nil
}

// https://py7zr.readthedocs.io/en/latest/archive_format.html#signature
var sevenZipHeader = []byte("7z\xBC\xAF\x27\x1C")

// Interface guard
var _ Extractor = SevenZip{}
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2016 Matthew Holt

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading

0 comments on commit 1805df0

Please sign in to comment.