init
This commit is contained in:
commit
1ec6d72a8f
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Mac
|
||||
.DS_Store
|
29
.travis.yml
Normal file
29
.travis.yml
Normal file
@ -0,0 +1,29 @@
|
||||
language: go
|
||||
sudo: required
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: master
|
||||
include:
|
||||
# Supported versions of Go: https://golang.org/dl/
|
||||
- go: '1.11.x'
|
||||
- go: '1.12.x'
|
||||
- go: master
|
||||
|
||||
go_import_path: github.com/yi-ge/unxz
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/pkg
|
||||
|
||||
before_install:
|
||||
- echo "TRAVIS_GO_VERSION=${TRAVIS_GO_VERSION}"
|
||||
|
||||
install:
|
||||
- go mod install
|
||||
|
||||
script:
|
||||
- make test COVERAGE_DIR=/tmp/coverage
|
||||
|
||||
after_success:
|
||||
- goveralls -service=travis-ci -coverprofile /tmp/coverage/combined.txt
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Yige
|
||||
|
||||
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.
|
3
REAMDE.md
Normal file
3
REAMDE.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Unxz
|
||||
|
||||
[](https://godoc.org/github.com/yi-ge/unxz)
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module github.com/yi-ge/unxz
|
||||
|
||||
go 1.12
|
||||
|
||||
require github.com/ulikunitz/xz v0.5.6
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
|
||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
0
test/a.txt
Normal file
0
test/a.txt
Normal file
1
test/b.txt
Normal file
1
test/b.txt
Normal file
@ -0,0 +1 @@
|
||||
1
|
1
test/c.txt
Normal file
1
test/c.txt
Normal file
@ -0,0 +1 @@
|
||||
2
|
1
test/d.txt
Symbolic link
1
test/d.txt
Symbolic link
@ -0,0 +1 @@
|
||||
dir/d.txt
|
1
test/dir/c.txt
Normal file
1
test/dir/c.txt
Normal file
@ -0,0 +1 @@
|
||||
2
|
1
test/dir/d.txt
Normal file
1
test/dir/d.txt
Normal file
@ -0,0 +1 @@
|
||||
3
|
1
test/dir/dir/e.txt
Normal file
1
test/dir/dir/e.txt
Normal file
@ -0,0 +1 @@
|
||||
4
|
1
test/dir/dir/f.txt
Symbolic link
1
test/dir/dir/f.txt
Symbolic link
@ -0,0 +1 @@
|
||||
../f.txt
|
1
test/dir/e.txt
Normal file
1
test/dir/e.txt
Normal file
@ -0,0 +1 @@
|
||||
4
|
1
test/dir/f.txt
Normal file
1
test/dir/f.txt
Normal file
@ -0,0 +1 @@
|
||||
5
|
0
test/out/a.txt
Normal file
0
test/out/a.txt
Normal file
1
test/out/b.txt
Normal file
1
test/out/b.txt
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
test/out/c.txt
Normal file
2
test/out/c.txt
Normal file
@ -0,0 +1,2 @@
|
||||
2
|
||||
123
|
1
test/out/d.txt
Symbolic link
1
test/out/d.txt
Symbolic link
@ -0,0 +1 @@
|
||||
dir/d.txt
|
2
test/out/dir/c.txt
Normal file
2
test/out/dir/c.txt
Normal file
@ -0,0 +1,2 @@
|
||||
2
|
||||
123
|
1
test/out/dir/d.txt
Normal file
1
test/out/dir/d.txt
Normal file
@ -0,0 +1 @@
|
||||
3
|
1
test/out/dir/dir/e.txt
Normal file
1
test/out/dir/dir/e.txt
Normal file
@ -0,0 +1 @@
|
||||
4abcde
|
1
test/out/dir/dir/f.txt
Symbolic link
1
test/out/dir/dir/f.txt
Symbolic link
@ -0,0 +1 @@
|
||||
../f.txt
|
1
test/out/dir/e.txt
Normal file
1
test/out/dir/e.txt
Normal file
@ -0,0 +1 @@
|
||||
4abcde
|
1
test/out/dir/f.txt
Normal file
1
test/out/dir/f.txt
Normal file
@ -0,0 +1 @@
|
||||
5
|
BIN
test/t.tar.xz
Normal file
BIN
test/t.tar.xz
Normal file
Binary file not shown.
170
unxz.go
Normal file
170
unxz.go
Normal file
@ -0,0 +1,170 @@
|
||||
package unxz
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/ulikunitz/xz"
|
||||
)
|
||||
|
||||
var (
|
||||
linkFiles = []linkFile{}
|
||||
errHeaderType = errors.New("unxz: invalid tar header type")
|
||||
)
|
||||
|
||||
// Unxz - Unxz struct.
|
||||
type Unxz struct {
|
||||
Src string
|
||||
Dest string
|
||||
}
|
||||
|
||||
type linkFile struct {
|
||||
filePath string
|
||||
targetPath string
|
||||
}
|
||||
|
||||
// New - Create a new Unxz.
|
||||
func New(src string, dest string) Unxz {
|
||||
return Unxz{src, dest}
|
||||
}
|
||||
|
||||
// Extract - Extract *.tar.xz package.
|
||||
func (uz Unxz) Extract() error {
|
||||
destPath := uz.Dest
|
||||
|
||||
xzFile, err := os.Open(uz.Src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer xzFile.Close()
|
||||
|
||||
r := bufio.NewReader(xzFile)
|
||||
xr, err := xz.NewReader(r)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tr := tar.NewReader(xr)
|
||||
|
||||
os.Mkdir(destPath, 0755)
|
||||
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = untarFile(tr, hdr, destPath, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range linkFiles {
|
||||
err = writeLink(item.filePath, item.targetPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeFile(filePath string, in io.Reader, fm os.FileMode) error {
|
||||
err := os.MkdirAll(filepath.Dir(filePath), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
err = out.Chmod(fm)
|
||||
if err != nil && runtime.GOOS != "windows" {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeSymbolicLink(filePath string, targetPath string) error {
|
||||
err := os.MkdirAll(filepath.Dir(filePath), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Symlink(targetPath, filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeLink(filePath string, targetPath string) error {
|
||||
err := os.MkdirAll(filepath.Dir(filePath), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Link(targetPath, filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func untarFile(tr *tar.Reader, header *tar.Header, destPath string, stripInnerFolder bool) error {
|
||||
fileName := header.Name
|
||||
filePath := filepath.Join(destPath, fileName)
|
||||
|
||||
if stripInnerFolder {
|
||||
slashIndex := strings.Index(fileName, "/")
|
||||
if slashIndex != -1 {
|
||||
fileName = fileName[slashIndex+1:]
|
||||
}
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
err := os.MkdirAll(filePath, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
case tar.TypeReg, tar.TypeRegA:
|
||||
return writeFile(filePath, tr, header.FileInfo().Mode())
|
||||
case tar.TypeLink:
|
||||
linkFiles = append(linkFiles, linkFile{
|
||||
filePath,
|
||||
filepath.Join(destPath, header.Linkname),
|
||||
})
|
||||
return nil
|
||||
case tar.TypeSymlink:
|
||||
return writeSymbolicLink(filePath, header.Linkname)
|
||||
case tar.TypeXGlobalHeader:
|
||||
return nil
|
||||
default:
|
||||
return errHeaderType
|
||||
}
|
||||
}
|
227
unxz_test.go
Normal file
227
unxz_test.go
Normal file
@ -0,0 +1,227 @@
|
||||
package unxz
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func currentDir() string {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
func TestUnxz(t *testing.T) {
|
||||
filePath := filepath.FromSlash(path.Join(currentDir(), "./test/t.tar.xz"))
|
||||
outDir := filepath.FromSlash(path.Join(currentDir(), "./test/out") + "/")
|
||||
|
||||
os.RemoveAll(outDir)
|
||||
|
||||
unxz := New(filePath, outDir)
|
||||
err := unxz.Extract()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
aFile, err := os.Open(path.Join(currentDir(), "./test/a.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer aFile.Close()
|
||||
|
||||
aFileContent, err := ioutil.ReadAll(aFile)
|
||||
|
||||
aOutFile, err := os.Open(path.Join(currentDir(), "./test/out/a.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer aOutFile.Close()
|
||||
|
||||
aOutFileContent, err := ioutil.ReadAll(aOutFile)
|
||||
|
||||
if string(aFileContent) != string(aOutFileContent) {
|
||||
t.Fatal("Unxz file content error.")
|
||||
}
|
||||
|
||||
if string(aFileContent) != "" {
|
||||
t.Fatal("Unxz file content error.")
|
||||
}
|
||||
|
||||
bFile, err := os.Open(path.Join(currentDir(), "./test/b.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer bFile.Close()
|
||||
|
||||
bFileContent, err := ioutil.ReadAll(bFile)
|
||||
|
||||
bOutFile, err := os.Open(path.Join(currentDir(), "./test/out/b.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer bOutFile.Close()
|
||||
|
||||
bOutFileContent, err := ioutil.ReadAll(bOutFile)
|
||||
|
||||
if string(bFileContent) != string(bOutFileContent) {
|
||||
t.Log(string(bFileContent))
|
||||
t.Log(string(bOutFileContent))
|
||||
t.Fatal("Unxz file content error.")
|
||||
}
|
||||
|
||||
cOutFile, err := os.OpenFile(path.Join(currentDir(), "./test/out/c.txt"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cOutFile.Close()
|
||||
|
||||
cOutFileContent, err := ioutil.ReadAll(cOutFile)
|
||||
|
||||
cOutLinkFile, err := os.Open(path.Join(currentDir(), "./test/out/dir/c.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cOutLinkFile.Close()
|
||||
|
||||
cOutLinkFileContent, err := ioutil.ReadAll(cOutLinkFile)
|
||||
|
||||
if string(cOutFileContent) != string(cOutLinkFileContent) {
|
||||
t.Fatal("Unxz file content error or link file content error.")
|
||||
}
|
||||
|
||||
t.Log("Old c.txt content:", string(cOutFileContent))
|
||||
t.Log("Change c.txt content.")
|
||||
_, err = cOutFile.WriteString("123")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cOutFileNew, err := os.Open(path.Join(currentDir(), "./test/out/c.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cOutFileNew.Close()
|
||||
|
||||
cOutFileContentNew, err := ioutil.ReadAll(cOutFileNew)
|
||||
t.Log("New c.txt content:", string(cOutFileContentNew))
|
||||
|
||||
cOutLinkFileNew, err := os.Open(path.Join(currentDir(), "./test/out/dir/c.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cOutLinkFileNew.Close()
|
||||
|
||||
cOutLinkFileContentNew, err := ioutil.ReadAll(cOutLinkFileNew)
|
||||
|
||||
if string(cOutFileContentNew) != string(cOutLinkFileContentNew) {
|
||||
t.Fatal("Unxz file link error.")
|
||||
}
|
||||
|
||||
// test d
|
||||
targetURL, err := filepath.EvalSymlinks(path.Join(currentDir(), "./test/out/d.txt"))
|
||||
|
||||
if targetURL != path.Join(currentDir(), "./test/out/dir/d.txt") {
|
||||
t.Fatal("Unxz file Symlink error.")
|
||||
}
|
||||
|
||||
dOutSymlinkFile, err := os.Open(path.Join(currentDir(), "./test/out/d.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer dOutSymlinkFile.Close()
|
||||
|
||||
dOutSymlinkFileContent, err := ioutil.ReadAll(dOutSymlinkFile)
|
||||
|
||||
dOutFile, err := os.Open(path.Join(currentDir(), "./test/out/dir/d.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer dOutFile.Close()
|
||||
|
||||
dOutFileContent, err := ioutil.ReadAll(dOutFile)
|
||||
|
||||
if string(dOutSymlinkFileContent) != string(dOutFileContent) {
|
||||
t.Fatal("The content of symlink file is not equal to that of target file")
|
||||
}
|
||||
|
||||
eOutFile, err := os.OpenFile(path.Join(currentDir(), "./test/out/dir/dir/e.txt"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer eOutFile.Close()
|
||||
|
||||
eOutFileContent, err := ioutil.ReadAll(eOutFile)
|
||||
|
||||
eOutLinkFile, err := os.Open(path.Join(currentDir(), "./test/out/dir/e.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer eOutLinkFile.Close()
|
||||
|
||||
eOutLinkFileContent, err := ioutil.ReadAll(eOutLinkFile)
|
||||
|
||||
if string(eOutFileContent) != string(eOutLinkFileContent) {
|
||||
t.Fatal("Unxz file content error or link file content error.")
|
||||
}
|
||||
|
||||
t.Log("Old dir/dir/e.txt content:", string(eOutFileContent))
|
||||
t.Log("Change dir/dir/e.txt content.")
|
||||
_, err = eOutFile.WriteString("abcde")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
eOutFileNew, err := os.Open(path.Join(currentDir(), "./test/out/dir/dir/e.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer eOutFileNew.Close()
|
||||
|
||||
eOutFileContentNew, err := ioutil.ReadAll(eOutFileNew)
|
||||
t.Log("New dir/dir/e.txt content:", string(eOutFileContentNew))
|
||||
|
||||
eOutLinkFileNew, err := os.Open(path.Join(currentDir(), "./test/out/dir/e.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer eOutLinkFileNew.Close()
|
||||
|
||||
eOutLinkFileContentNew, err := ioutil.ReadAll(eOutLinkFileNew)
|
||||
|
||||
if string(eOutFileContentNew) != string(eOutLinkFileContentNew) {
|
||||
t.Fatal("Unxz file link error.")
|
||||
}
|
||||
|
||||
// test f
|
||||
inDirTargetURL, err := filepath.EvalSymlinks(path.Join(currentDir(), "./test/out/dir/dir/f.txt"))
|
||||
t.Log("F outSymlinkFile link:", inDirTargetURL)
|
||||
|
||||
if inDirTargetURL != path.Join(currentDir(), "./test/out/dir/f.txt") {
|
||||
t.Fatal("Unxz file Symlink error.")
|
||||
}
|
||||
|
||||
fOutSymlinkFile, err := os.Open(path.Join(currentDir(), "./test/out/dir/dir/f.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer fOutSymlinkFile.Close()
|
||||
|
||||
fOutSymlinkFileContent, err := ioutil.ReadAll(fOutSymlinkFile)
|
||||
|
||||
fOutFile, err := os.Open(path.Join(currentDir(), "./test/out/dir/f.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer fOutFile.Close()
|
||||
|
||||
fOutFileContent, err := ioutil.ReadAll(fOutFile)
|
||||
|
||||
if string(fOutSymlinkFileContent) != string(fOutFileContent) {
|
||||
t.Fatal("The content of symlink file is not equal to that of target file")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user