File: uncompress.go

package info (click to toggle)
golang-github-rhysd-go-github-selfupdate 1.2.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 348 kB
  • sloc: sh: 21; makefile: 6
file content (136 lines) | stat: -rw-r--r-- 3,840 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package selfupdate

import (
	"archive/tar"
	"archive/zip"
	"bytes"
	"compress/gzip"
	"fmt"
	"github.com/ulikunitz/xz"
	"io"
	"io/ioutil"
	"path/filepath"
	"runtime"
	"strings"
)

func matchExecutableName(cmd, target string) bool {
	if cmd == target {
		return true
	}

	o, a := runtime.GOOS, runtime.GOARCH

	// When the contained executable name is full name (e.g. foo_darwin_amd64),
	// it is also regarded as a target executable file. (#19)
	for _, d := range []rune{'_', '-'} {
		c := fmt.Sprintf("%s%c%s%c%s", cmd, d, o, d, a)
		if o == "windows" {
			c += ".exe"
		}
		if c == target {
			return true
		}
	}

	return false
}

func unarchiveTar(src io.Reader, url, cmd string) (io.Reader, error) {
	t := tar.NewReader(src)
	for {
		h, err := t.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, fmt.Errorf("Failed to unarchive .tar file: %s", err)
		}
		_, name := filepath.Split(h.Name)
		if matchExecutableName(cmd, name) {
			log.Println("Executable file", h.Name, "was found in tar archive")
			return t, nil
		}
	}

	return nil, fmt.Errorf("File '%s' for the command is not found in %s", cmd, url)
}

// UncompressCommand uncompresses the given source. Archive and compression format is
// automatically detected from 'url' parameter, which represents the URL of asset.
// This returns a reader for the uncompressed command given by 'cmd'. '.zip',
// '.tar.gz', '.tar.xz', '.tgz', '.gz' and '.xz' are supported.
func UncompressCommand(src io.Reader, url, cmd string) (io.Reader, error) {
	if strings.HasSuffix(url, ".zip") {
		log.Println("Uncompressing zip file", url)

		// Zip format requires its file size for uncompressing.
		// So we need to read the HTTP response into a buffer at first.
		buf, err := ioutil.ReadAll(src)
		if err != nil {
			return nil, fmt.Errorf("Failed to create buffer for zip file: %s", err)
		}

		r := bytes.NewReader(buf)
		z, err := zip.NewReader(r, r.Size())
		if err != nil {
			return nil, fmt.Errorf("Failed to uncompress zip file: %s", err)
		}

		for _, file := range z.File {
			_, name := filepath.Split(file.Name)
			if !file.FileInfo().IsDir() && matchExecutableName(cmd, name) {
				log.Println("Executable file", file.Name, "was found in zip archive")
				return file.Open()
			}
		}

		return nil, fmt.Errorf("File '%s' for the command is not found in %s", cmd, url)
	} else if strings.HasSuffix(url, ".tar.gz") || strings.HasSuffix(url, ".tgz") {
		log.Println("Uncompressing tar.gz file", url)

		gz, err := gzip.NewReader(src)
		if err != nil {
			return nil, fmt.Errorf("Failed to uncompress .tar.gz file: %s", err)
		}

		return unarchiveTar(gz, url, cmd)
	} else if strings.HasSuffix(url, ".gzip") || strings.HasSuffix(url, ".gz") {
		log.Println("Uncompressing gzip file", url)

		r, err := gzip.NewReader(src)
		if err != nil {
			return nil, fmt.Errorf("Failed to uncompress gzip file downloaded from %s: %s", url, err)
		}

		name := r.Header.Name
		if !matchExecutableName(cmd, name) {
			return nil, fmt.Errorf("File name '%s' does not match to command '%s' found in %s", name, cmd, url)
		}

		log.Println("Executable file", name, "was found in gzip file")
		return r, nil
	} else if strings.HasSuffix(url, ".tar.xz") {
		log.Println("Uncompressing tar.xz file", url)

		xzip, err := xz.NewReader(src)
		if err != nil {
			return nil, fmt.Errorf("Failed to uncompress .tar.xz file: %s", err)
		}

		return unarchiveTar(xzip, url, cmd)
	} else if strings.HasSuffix(url, ".xz") {
		log.Println("Uncompressing xzip file", url)

		xzip, err := xz.NewReader(src)
		if err != nil {
			return nil, fmt.Errorf("Failed to uncompress xzip file downloaded from %s: %s", url, err)
		}

		log.Println("Uncompressed file from xzip is assumed to be an executable", cmd)
		return xzip, nil
	}

	log.Println("Uncompression is not needed", url)
	return src, nil
}