mirror of
https://github.com/taigrr/go-selfupdate
synced 2025-01-18 04:33:12 -08:00
more deps
This commit is contained in:
parent
969b8d5ba6
commit
a88bde6abc
5
Godeps/Readme
generated
Normal file
5
Godeps/Readme
generated
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
This directory tree is generated automatically by godep.
|
||||||
|
|
||||||
|
Please do not edit.
|
||||||
|
|
||||||
|
See https://github.com/tools/godep for more information.
|
2
Godeps/_workspace/.gitignore
generated
vendored
Normal file
2
Godeps/_workspace/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/pkg
|
||||||
|
/bin
|
20
Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE
generated
vendored
Normal file
20
Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2012 Daniel Theophanes
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source
|
||||||
|
distribution.
|
32
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go
generated
vendored
Normal file
32
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Extensions to the standard "os" package.
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import "path/filepath"
|
||||||
|
|
||||||
|
// Executable returns an absolute path that can be used to
|
||||||
|
// re-invoke the current program.
|
||||||
|
// It may not be valid after the current program exits.
|
||||||
|
func Executable() (string, error) {
|
||||||
|
p, err := executable()
|
||||||
|
return filepath.Clean(p), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns same path as Executable, returns just the folder
|
||||||
|
// path. Excludes the executable name.
|
||||||
|
func ExecutableFolder() (string, error) {
|
||||||
|
p, err := Executable()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
folder, _ := filepath.Split(p)
|
||||||
|
return folder, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depricated. Same as Executable().
|
||||||
|
func GetExePath() (exePath string, err error) {
|
||||||
|
return Executable()
|
||||||
|
}
|
20
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go
generated
vendored
Normal file
20
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func executable() (string, error) {
|
||||||
|
f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return syscall.Fd2path(int(f.Fd()))
|
||||||
|
}
|
25
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go
generated
vendored
Normal file
25
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build linux netbsd openbsd
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func executable() (string, error) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
return os.Readlink("/proc/self/exe")
|
||||||
|
case "netbsd":
|
||||||
|
return os.Readlink("/proc/curproc/exe")
|
||||||
|
case "openbsd":
|
||||||
|
return os.Readlink("/proc/curproc/file")
|
||||||
|
}
|
||||||
|
return "", errors.New("ExecPath not implemented for " + runtime.GOOS)
|
||||||
|
}
|
79
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_sysctl.go
generated
vendored
Normal file
79
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_sysctl.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build darwin freebsd
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var initCwd, initCwdErr = os.Getwd()
|
||||||
|
|
||||||
|
func executable() (string, error) {
|
||||||
|
var mib [4]int32
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "freebsd":
|
||||||
|
mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
|
||||||
|
case "darwin":
|
||||||
|
mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
|
||||||
|
}
|
||||||
|
|
||||||
|
n := uintptr(0)
|
||||||
|
// Get length.
|
||||||
|
_, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||||
|
if errNum != 0 {
|
||||||
|
return "", errNum
|
||||||
|
}
|
||||||
|
if n == 0 { // This shouldn't happen.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
buf := make([]byte, n)
|
||||||
|
_, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||||
|
if errNum != 0 {
|
||||||
|
return "", errNum
|
||||||
|
}
|
||||||
|
if n == 0 { // This shouldn't happen.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
for i, v := range buf {
|
||||||
|
if v == 0 {
|
||||||
|
buf = buf[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
execPath := string(buf)
|
||||||
|
// execPath will not be empty due to above checks.
|
||||||
|
// Try to get the absolute path if the execPath is not rooted.
|
||||||
|
if execPath[0] != '/' {
|
||||||
|
execPath, err = getAbs(execPath)
|
||||||
|
if err != nil {
|
||||||
|
return execPath, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For darwin KERN_PROCARGS may return the path to a symlink rather than the
|
||||||
|
// actual executable.
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
if execPath, err = filepath.EvalSymlinks(execPath); err != nil {
|
||||||
|
return execPath, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return execPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAbs(execPath string) (string, error) {
|
||||||
|
if initCwdErr != nil {
|
||||||
|
return execPath, initCwdErr
|
||||||
|
}
|
||||||
|
// The execPath may begin with a "../" or a "./" so clean it first.
|
||||||
|
// Join the two paths, trailing and starting slashes undetermined, so use
|
||||||
|
// the generic Join function.
|
||||||
|
return filepath.Join(initCwd, filepath.Clean(execPath)), nil
|
||||||
|
}
|
79
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go
generated
vendored
Normal file
79
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build darwin linux freebsd netbsd windows
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
oexec "os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH"
|
||||||
|
|
||||||
|
func TestExecPath(t *testing.T) {
|
||||||
|
ep, err := Executable()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ExecPath failed: %v", err)
|
||||||
|
}
|
||||||
|
// we want fn to be of the form "dir/prog"
|
||||||
|
dir := filepath.Dir(filepath.Dir(ep))
|
||||||
|
fn, err := filepath.Rel(dir, ep)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("filepath.Rel: %v", err)
|
||||||
|
}
|
||||||
|
cmd := &oexec.Cmd{}
|
||||||
|
// make child start with a relative program path
|
||||||
|
cmd.Dir = dir
|
||||||
|
cmd.Path = fn
|
||||||
|
// forge argv[0] for child, so that we can verify we could correctly
|
||||||
|
// get real path of the executable without influenced by argv[0].
|
||||||
|
cmd.Args = []string{"-", "-test.run=XXXX"}
|
||||||
|
cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)}
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("exec(self) failed: %v", err)
|
||||||
|
}
|
||||||
|
outs := string(out)
|
||||||
|
if !filepath.IsAbs(outs) {
|
||||||
|
t.Fatalf("Child returned %q, want an absolute path", out)
|
||||||
|
}
|
||||||
|
if !sameFile(outs, ep) {
|
||||||
|
t.Fatalf("Child returned %q, not the same file as %q", out, ep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sameFile(fn1, fn2 string) bool {
|
||||||
|
fi1, err := os.Stat(fn1)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
fi2, err := os.Stat(fn2)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return os.SameFile(fi1, fi2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if e := os.Getenv(execPath_EnvVar); e != "" {
|
||||||
|
// first chdir to another path
|
||||||
|
dir := "/"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
dir = filepath.VolumeName(".")
|
||||||
|
}
|
||||||
|
os.Chdir(dir)
|
||||||
|
if ep, err := Executable(); err != nil {
|
||||||
|
fmt.Fprint(os.Stderr, "ERROR: ", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(os.Stderr, ep)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
34
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_windows.go
generated
vendored
Normal file
34
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_windows.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel = syscall.MustLoadDLL("kernel32.dll")
|
||||||
|
getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetModuleFileName() with hModule = NULL
|
||||||
|
func executable() (exePath string, err error) {
|
||||||
|
return getModuleFileName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getModuleFileName() (string, error) {
|
||||||
|
var n uint32
|
||||||
|
b := make([]uint16, syscall.MAX_PATH)
|
||||||
|
size := uint32(len(b))
|
||||||
|
|
||||||
|
r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
|
||||||
|
n = uint32(r0)
|
||||||
|
if n == 0 {
|
||||||
|
return "", e1
|
||||||
|
}
|
||||||
|
return string(utf16.Decode(b[0:n])), nil
|
||||||
|
}
|
137
Godeps/_workspace/src/github.com/inconshreveable/go-update/README
generated
vendored
Normal file
137
Godeps/_workspace/src/github.com/inconshreveable/go-update/README
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
PACKAGE DOCUMENTATION
|
||||||
|
|
||||||
|
package update
|
||||||
|
import "github.com/inconshreveable/go-update"
|
||||||
|
|
||||||
|
Package update allows a program to "self-update", replacing its
|
||||||
|
executable file with new bytes.
|
||||||
|
|
||||||
|
Package update provides the facility to create user experiences like
|
||||||
|
auto-updating or user-approved updates which manifest as user prompts in
|
||||||
|
commercial applications with copy similar to "Restart to being using the
|
||||||
|
new version of X".
|
||||||
|
|
||||||
|
Updating your program to a new version is as easy as:
|
||||||
|
|
||||||
|
err := update.FromUrl("http://release.example.com/2.0/myprogram")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Update failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
The most low-level API is FromStream() which updates the current
|
||||||
|
executable with the bytes read from an io.Reader.
|
||||||
|
|
||||||
|
Additional APIs are provided for common update strategies which include
|
||||||
|
updating from a file with FromFile() and updating from the internet with
|
||||||
|
FromUrl().
|
||||||
|
|
||||||
|
Using the more advaced Download.UpdateFromUrl() API gives you the
|
||||||
|
ability to resume an interrupted download to enable large updates to
|
||||||
|
complete even over intermittent or slow connections. This API also
|
||||||
|
enables more fine-grained control over how the update is downloaded from
|
||||||
|
the internet as well as access to download progress,
|
||||||
|
|
||||||
|
|
||||||
|
VARIABLES
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Returned when the remote server indicates that no download is available
|
||||||
|
UpdateUnavailable error
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
FUNCTIONS
|
||||||
|
|
||||||
|
func FromFile(filepath string) (err error)
|
||||||
|
FromFile reads the contents of the given file and uses them to update
|
||||||
|
the current program's executable file by calling FromStream().
|
||||||
|
|
||||||
|
func FromStream(newBinary io.Reader) (err error)
|
||||||
|
FromStream reads the contents of the supplied io.Reader newBinary and
|
||||||
|
uses them to update the current program's executable file.
|
||||||
|
|
||||||
|
FromStream performs the following actions to ensure a cross-platform
|
||||||
|
safe update:
|
||||||
|
|
||||||
|
- Renames the current program's executable file from
|
||||||
|
/path/to/program-name to /path/to/.program-name.old
|
||||||
|
|
||||||
|
- Opens the now-empty path /path/to/program-name with mode 0755 and
|
||||||
|
copies the contents of newBinary into the file.
|
||||||
|
|
||||||
|
- If the copy is successful, it erases /path/to/.program.old. If this
|
||||||
|
operation fails, no error is reported.
|
||||||
|
|
||||||
|
- If the copy is unsuccessful, it attempts to rename
|
||||||
|
/path/to/.program-name.old back to /path/to/program-name. If this
|
||||||
|
operation fails, the error is not reported in order to not mask the
|
||||||
|
error that caused the rename recovery attempt.
|
||||||
|
|
||||||
|
func FromUrl(url string) error
|
||||||
|
FromUrl downloads the contents of the given url and uses them to update
|
||||||
|
the current program's executable file. It is a convenience function
|
||||||
|
which is equivalent to
|
||||||
|
|
||||||
|
NewDownload().UpdateFromUrl(url)
|
||||||
|
|
||||||
|
See Download.UpdateFromUrl for more details.
|
||||||
|
|
||||||
|
|
||||||
|
TYPES
|
||||||
|
|
||||||
|
type Download struct {
|
||||||
|
// net/http.Client to use when downloading the update.
|
||||||
|
// If nil, a default http.Client is used
|
||||||
|
HttpClient *http.Client
|
||||||
|
|
||||||
|
// Path on the file system to dowload the update to
|
||||||
|
// If empty, a temporary file is used.
|
||||||
|
// After the download begins, this path will be set
|
||||||
|
// so that the client can use it to resume aborted
|
||||||
|
// downloads
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// Progress returns the percentage of the download
|
||||||
|
// completed as an integer between 0 and 100
|
||||||
|
Progress chan (int)
|
||||||
|
|
||||||
|
// HTTP Method to use in the download request. Default is "GET"
|
||||||
|
Method string
|
||||||
|
}
|
||||||
|
Type Download encapsulates the necessary parameters and state needed to
|
||||||
|
download an update from the internet. Create an instance with the
|
||||||
|
NewDownload() factory function.
|
||||||
|
|
||||||
|
|
||||||
|
func NewDownload() *Download
|
||||||
|
NewDownload initializes a new Download object
|
||||||
|
|
||||||
|
|
||||||
|
func (d *Download) UpdateFromUrl(url string) (err error)
|
||||||
|
UpdateFromUrl downloads the given url from the internet to a file on
|
||||||
|
disk and then calls FromStream() to update the current program's
|
||||||
|
executable file with the contents of that file.
|
||||||
|
|
||||||
|
If the update is successful, the downloaded file will be erased from
|
||||||
|
disk. Otherwise, it will remain in d.Path to allow the download to
|
||||||
|
resume later or be skipped entirely.
|
||||||
|
|
||||||
|
Only HTTP/1.1 servers that implement the Range header are supported.
|
||||||
|
|
||||||
|
UpdateFromUrl() uses HTTP status codes to determine what action to take.
|
||||||
|
|
||||||
|
- The HTTP server should return 200 or 206 for the update to be
|
||||||
|
downloaded.
|
||||||
|
|
||||||
|
- The HTTP server should return 204 if no update is available at this
|
||||||
|
time. This will cause UpdateFromUrl to return the error
|
||||||
|
UpdateUnavailable.
|
||||||
|
|
||||||
|
- If the HTTP server returns a 3XX redirect, it will be followed
|
||||||
|
according to d.HttpClient's redirect policy.
|
||||||
|
|
||||||
|
- Any other HTTP status code will cause UpdateFromUrl to return an
|
||||||
|
error.
|
||||||
|
|
||||||
|
|
||||||
|
|
455
Godeps/_workspace/src/github.com/inconshreveable/go-update/update.go
generated
vendored
Normal file
455
Godeps/_workspace/src/github.com/inconshreveable/go-update/update.go
generated
vendored
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
/*
|
||||||
|
Package update allows a program to "self-update", replacing its executable file
|
||||||
|
with new bytes.
|
||||||
|
|
||||||
|
Package update provides the facility to create user experiences like auto-updating
|
||||||
|
or user-approved updates which manifest as user prompts in commercial applications
|
||||||
|
with copy similar to "Restart to being using the new version of X".
|
||||||
|
|
||||||
|
Updating your program to a new version is as easy as:
|
||||||
|
|
||||||
|
err := update.FromUrl("http://release.example.com/2.0/myprogram")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Update failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
The most low-level API is FromStream() which updates the current executable
|
||||||
|
with the bytes read from an io.Reader.
|
||||||
|
|
||||||
|
Additional APIs are provided for common update strategies which include
|
||||||
|
updating from a file with FromFile() and updating from the internet with
|
||||||
|
FromUrl().
|
||||||
|
|
||||||
|
Using the more advaced Download.UpdateFromUrl() API gives you the ability
|
||||||
|
to resume an interrupted download to enable large updates to complete even
|
||||||
|
over intermittent or slow connections. This API also enables more fine-grained
|
||||||
|
control over how the update is downloaded from the internet as well as access to
|
||||||
|
download progress,
|
||||||
|
*/
|
||||||
|
package update
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"bitbucket.org/kardianos/osext"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MeteredReader struct {
|
||||||
|
rd io.ReadCloser
|
||||||
|
totalSize int64
|
||||||
|
progress chan int
|
||||||
|
totalRead int64
|
||||||
|
ticks int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MeteredReader) Close() error {
|
||||||
|
return m.rd.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MeteredReader) Read(b []byte) (n int, err error) {
|
||||||
|
chunkSize := (m.totalSize / 100) + 1
|
||||||
|
lenB := int64(len(b))
|
||||||
|
|
||||||
|
var nChunk int
|
||||||
|
for start := int64(0); start < lenB; start += int64(nChunk) {
|
||||||
|
end := start + chunkSize
|
||||||
|
if end > lenB {
|
||||||
|
end = lenB
|
||||||
|
}
|
||||||
|
|
||||||
|
nChunk, err = m.rd.Read(b[start:end])
|
||||||
|
|
||||||
|
n += nChunk
|
||||||
|
m.totalRead += int64(nChunk)
|
||||||
|
|
||||||
|
if m.totalRead > (m.ticks * chunkSize) {
|
||||||
|
m.ticks += 1
|
||||||
|
// try to send on channel, but don't block if it's full
|
||||||
|
select {
|
||||||
|
case m.progress <- int(m.ticks + 1):
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// give the progress channel consumer a chance to run
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We wrap the round tripper when making requests
|
||||||
|
// because we need to add headers to the requests we make
|
||||||
|
// even when they are requests made after a redirect
|
||||||
|
type RoundTripper struct {
|
||||||
|
RoundTripFn func(*http.Request) (*http.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||||
|
return rt.RoundTripFn(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type Download encapsulates the necessary parameters and state
|
||||||
|
// needed to download an update from the internet. Create an instance
|
||||||
|
// with the NewDownload() factory function.
|
||||||
|
//
|
||||||
|
// You may only use a Download once,
|
||||||
|
type Download struct {
|
||||||
|
// net/http.Client to use when downloading the update.
|
||||||
|
// If nil, a default http.Client is used
|
||||||
|
HttpClient *http.Client
|
||||||
|
|
||||||
|
// Path on the file system to dowload the update to
|
||||||
|
// If empty, a temporary file is used.
|
||||||
|
// After the download begins, this path will be set
|
||||||
|
// so that the client can use it to resume aborted
|
||||||
|
// downloads
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// Progress returns the percentage of the download
|
||||||
|
// completed as an integer between 0 and 100
|
||||||
|
Progress chan (int)
|
||||||
|
|
||||||
|
// HTTP Method to use in the download request. Default is "GET"
|
||||||
|
Method string
|
||||||
|
|
||||||
|
// HTTP URL to issue the download request to
|
||||||
|
Url string
|
||||||
|
|
||||||
|
// Set to true when the server confirms a new version is available
|
||||||
|
// even if the updating process encounters an error later on
|
||||||
|
Available bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDownload initializes a new Download object
|
||||||
|
func NewDownload(url string) *Download {
|
||||||
|
return &Download{
|
||||||
|
HttpClient: new(http.Client),
|
||||||
|
Progress: make(chan int),
|
||||||
|
Method: "GET",
|
||||||
|
Url: url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Download) sharedHttp(offset int64) (resp *http.Response, err error) {
|
||||||
|
// create the download request
|
||||||
|
req, err := http.NewRequest(d.Method, d.Url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have to add headers like this so they get used across redirects
|
||||||
|
trans := d.HttpClient.Transport
|
||||||
|
if trans == nil {
|
||||||
|
trans = http.DefaultTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
d.HttpClient.Transport = &RoundTripper{
|
||||||
|
RoundTripFn: func(r *http.Request) (*http.Response, error) {
|
||||||
|
// add header for download continuation
|
||||||
|
if offset > 0 {
|
||||||
|
r.Header.Add("Range", fmt.Sprintf("%d-", offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ask for gzipped content so that net/http won't unzip it for us
|
||||||
|
// and destroy the content length header we need for progress calculations
|
||||||
|
r.Header.Add("Accept-Encoding", "gzip")
|
||||||
|
|
||||||
|
return trans.RoundTrip(r)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// issue the download request
|
||||||
|
return d.HttpClient.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Download) Check() (available bool, err error) {
|
||||||
|
resp, err := d.sharedHttp(0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
switch resp.StatusCode {
|
||||||
|
// ok
|
||||||
|
case 200, 206:
|
||||||
|
available = true
|
||||||
|
|
||||||
|
// no update available
|
||||||
|
case 204:
|
||||||
|
available = false
|
||||||
|
|
||||||
|
// server error
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Non 2XX response when downloading update: %s", resp.Status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get() downloads the given url from the internet to a file on disk
|
||||||
|
// and then calls FromStream() to update the current program's executable file
|
||||||
|
// with the contents of that file.
|
||||||
|
//
|
||||||
|
// If the update is successful, the downloaded file will be erased from disk.
|
||||||
|
// Otherwise, it will remain in d.Path to allow the download to resume later
|
||||||
|
// or be skipped entirely.
|
||||||
|
//
|
||||||
|
// Only HTTP/1.1 servers that implement the Range header support resuming a
|
||||||
|
// partially completed download.
|
||||||
|
//
|
||||||
|
// UpdateFromUrl() uses HTTP status codes to determine what action to take.
|
||||||
|
//
|
||||||
|
// - The HTTP server should return 200 or 206 for the update to be downloaded.
|
||||||
|
//
|
||||||
|
// - The HTTP server should return 204 if no update is available at this time.
|
||||||
|
//
|
||||||
|
// - If the HTTP server returns a 3XX redirect, it will be followed
|
||||||
|
// according to d.HttpClient's redirect policy.
|
||||||
|
//
|
||||||
|
// - Any other HTTP status code will cause UpdateFromUrl to return an error.
|
||||||
|
func (d *Download) Get() (err error) {
|
||||||
|
var offset int64 = 0
|
||||||
|
var fp *os.File
|
||||||
|
|
||||||
|
// Close the progress channel whenever this function completes
|
||||||
|
defer close(d.Progress)
|
||||||
|
|
||||||
|
// open a file where we will stream the downloaded update to
|
||||||
|
// we do this first because if the caller specified a non-empty dlpath
|
||||||
|
// we need to determine how large it is in order to resume the download
|
||||||
|
if d.Path == "" {
|
||||||
|
// no dlpath specified, use a random tempfile
|
||||||
|
fp, err = ioutil.TempFile("", "update")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
|
||||||
|
// remember the path
|
||||||
|
d.Path = fp.Name()
|
||||||
|
} else {
|
||||||
|
fp, err = os.OpenFile(d.Path, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
|
||||||
|
// determine the file size so we can resume the download, if possible
|
||||||
|
var fi os.FileInfo
|
||||||
|
fi, err = fp.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = fi.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
// start downloading the file
|
||||||
|
resp, err := d.sharedHttp(offset)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
switch resp.StatusCode {
|
||||||
|
// ok
|
||||||
|
case 200, 206:
|
||||||
|
d.Available = true
|
||||||
|
|
||||||
|
// no update available
|
||||||
|
case 204:
|
||||||
|
return
|
||||||
|
|
||||||
|
// server error
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Non 2XX response when downloading update: %s", resp.Status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine how much we have to download
|
||||||
|
// net/http sets this to -1 when it is unknown
|
||||||
|
clength := resp.ContentLength
|
||||||
|
|
||||||
|
// Read the content from the response body
|
||||||
|
rd := resp.Body
|
||||||
|
|
||||||
|
// meter the rate at which we download content for
|
||||||
|
// progress reporting if we know how much to expect
|
||||||
|
if clength > 0 {
|
||||||
|
rd = &MeteredReader{rd: rd, totalSize: clength, progress: d.Progress}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decompress the content if necessary
|
||||||
|
if resp.Header.Get("Content-Encoding") == "gzip" {
|
||||||
|
rd, err = gzip.NewReader(rd)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download the update
|
||||||
|
_, err = io.Copy(fp, rd)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Download) GetAndUpdate() (err error, errRecover error) {
|
||||||
|
// check before we download if this will work
|
||||||
|
if err = SanityCheck(); err != nil {
|
||||||
|
// keep the contract that d.Progress will close whenever Get() terminates
|
||||||
|
close(d.Progress)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// download the update
|
||||||
|
if err = d.Get(); err != nil || !d.Available {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the update
|
||||||
|
if err, errRecover = FromFile(d.Path); err != nil || errRecover != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the temporary file
|
||||||
|
os.Remove(d.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromUrl downloads the contents of the given url and uses them to update
|
||||||
|
// the current program's executable file. It is a convenience function which is equivalent to
|
||||||
|
//
|
||||||
|
// NewDownload(url).GetAndUpdate()
|
||||||
|
//
|
||||||
|
// See Download.Get() for more details.
|
||||||
|
func FromUrl(url string) (err error, errRecover error) {
|
||||||
|
return NewDownload(url).GetAndUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromFile reads the contents of the given file and uses them
|
||||||
|
// to update the current program's executable file by calling FromStream().
|
||||||
|
func FromFile(filepath string) (err error, errRecover error) {
|
||||||
|
// open the new binary
|
||||||
|
fp, err := os.Open(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
|
||||||
|
// do the update
|
||||||
|
return FromStream(fp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStream reads the contents of the supplied io.Reader newBinary
|
||||||
|
// and uses them to update the current program's executable file.
|
||||||
|
//
|
||||||
|
// FromStream performs the following actions to ensure a cross-platform safe
|
||||||
|
// update:
|
||||||
|
//
|
||||||
|
// - Creates a new file, /path/to/.program-name.new with mode 0755 and copies
|
||||||
|
// the contents of newBinary into the file
|
||||||
|
//
|
||||||
|
// - Renames the current program's executable file from /path/to/program-name
|
||||||
|
// to /path/to/.program-name.old
|
||||||
|
//
|
||||||
|
// - Renames /path/to/.program-name.new to /path/to/program-name
|
||||||
|
//
|
||||||
|
// - If the rename is successful, it erases /path/to/.program.old. If this operation
|
||||||
|
// fails, no error is reported.
|
||||||
|
//
|
||||||
|
// - If the rename is unsuccessful, it attempts to rename /path/to/.program-name.old
|
||||||
|
// back to /path/to/program-name. If this operation fails, the error is not reported
|
||||||
|
// in order to not mask the error that caused the rename recovery attempt.
|
||||||
|
func FromStream(newBinary io.Reader) (err error, errRecover error) {
|
||||||
|
// get the path to the executable
|
||||||
|
thisExecPath, err := osext.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the directory the executable exists in
|
||||||
|
execDir := filepath.Dir(thisExecPath)
|
||||||
|
execName := filepath.Base(thisExecPath)
|
||||||
|
|
||||||
|
// Copy the contents of of newbinary to a the new executable file
|
||||||
|
newExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.new", execName))
|
||||||
|
fp, err := os.OpenFile(newExecPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
_, err = io.Copy(fp, newBinary)
|
||||||
|
|
||||||
|
// if we don't call fp.Close(), windows won't let us move the new executable
|
||||||
|
// because the file will still be "in use"
|
||||||
|
fp.Close()
|
||||||
|
|
||||||
|
// this is where we'll move the executable to so that we can swap in the updated replacement
|
||||||
|
oldExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.old", execName))
|
||||||
|
|
||||||
|
// delete any existing old exec file - this is necessary on Windows for two reasons:
|
||||||
|
// 1. after a successful update, windows can't remove the .old file because the process is still running
|
||||||
|
// 2. windows rename operations fail if the destination file already exists
|
||||||
|
_ = os.Remove(oldExecPath)
|
||||||
|
|
||||||
|
// move the existing executable to a new file in the same directory
|
||||||
|
err = os.Rename(thisExecPath, oldExecPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// move the new exectuable in to become the new program
|
||||||
|
err = os.Rename(newExecPath, thisExecPath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// copy unsuccessful
|
||||||
|
errRecover = os.Rename(oldExecPath, thisExecPath)
|
||||||
|
} else {
|
||||||
|
// copy successful, remove the old binary
|
||||||
|
_ = os.Remove(oldExecPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SanityCheck() attempts to determine whether an in-place executable update could
|
||||||
|
// succeed by performing preliminary checks (to establish valid permissions, etc).
|
||||||
|
// This helps avoid downloading updates when we know the update can't be successfully
|
||||||
|
// applied later.
|
||||||
|
func SanityCheck() (err error) {
|
||||||
|
// get the path to the executable
|
||||||
|
thisExecPath, err := osext.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the directory the executable exists in
|
||||||
|
execDir := filepath.Dir(thisExecPath)
|
||||||
|
execName := filepath.Base(thisExecPath)
|
||||||
|
|
||||||
|
// attempt to open a file in the executable's directory
|
||||||
|
newExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.new", execName))
|
||||||
|
fp, err := os.OpenFile(newExecPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fp.Close()
|
||||||
|
|
||||||
|
_ = os.Remove(newExecPath)
|
||||||
|
return
|
||||||
|
}
|
1
Godeps/_workspace/src/github.com/kr/binarydist/.gitignore
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/kr/binarydist/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
test.*
|
22
Godeps/_workspace/src/github.com/kr/binarydist/License
generated
vendored
Normal file
22
Godeps/_workspace/src/github.com/kr/binarydist/License
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012 Keith Rarick
|
||||||
|
|
||||||
|
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.
|
7
Godeps/_workspace/src/github.com/kr/binarydist/Readme.md
generated
vendored
Normal file
7
Godeps/_workspace/src/github.com/kr/binarydist/Readme.md
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# binarydist
|
||||||
|
|
||||||
|
Package binarydist implements binary diff and patch as described on
|
||||||
|
<http://www.daemonology.net/bsdiff/>. It reads and writes files
|
||||||
|
compatible with the tools there.
|
||||||
|
|
||||||
|
Documentation at <http://go.pkgdoc.org/github.com/kr/binarydist>.
|
40
Godeps/_workspace/src/github.com/kr/binarydist/bzip2.go
generated
vendored
Normal file
40
Godeps/_workspace/src/github.com/kr/binarydist/bzip2.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type bzip2Writer struct {
|
||||||
|
c *exec.Cmd
|
||||||
|
w io.WriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w bzip2Writer) Write(b []byte) (int, error) {
|
||||||
|
return w.w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w bzip2Writer) Close() error {
|
||||||
|
if err := w.w.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return w.c.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package compress/bzip2 implements only decompression,
|
||||||
|
// so we'll fake it by running bzip2 in another process.
|
||||||
|
func newBzip2Writer(w io.Writer) (wc io.WriteCloser, err error) {
|
||||||
|
var bw bzip2Writer
|
||||||
|
bw.c = exec.Command("bzip2", "-c")
|
||||||
|
bw.c.Stdout = w
|
||||||
|
|
||||||
|
if bw.w, err = bw.c.StdinPipe(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = bw.c.Start(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bw, nil
|
||||||
|
}
|
93
Godeps/_workspace/src/github.com/kr/binarydist/common_test.go
generated
vendored
Normal file
93
Godeps/_workspace/src/github.com/kr/binarydist/common_test.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mustOpen(path string) *os.File {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustReadAll(r io.Reader) []byte {
|
||||||
|
b, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileCmp(a, b *os.File) int64 {
|
||||||
|
sa, err := a.Seek(0, 2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sb, err := b.Seek(0, 2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sa != sb {
|
||||||
|
return sa
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = a.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = b.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pa, err := ioutil.ReadAll(a)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pb, err := ioutil.ReadAll(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range pa {
|
||||||
|
if pa[i] != pb[i] {
|
||||||
|
return int64(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustWriteRandFile(path string, size int) *os.File {
|
||||||
|
p := make([]byte, size)
|
||||||
|
_, err := rand.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = f.Write(p)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = f.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
408
Godeps/_workspace/src/github.com/kr/binarydist/diff.go
generated
vendored
Normal file
408
Godeps/_workspace/src/github.com/kr/binarydist/diff.go
generated
vendored
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func swap(a []int, i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
|
func split(I, V []int, start, length, h int) {
|
||||||
|
var i, j, k, x, jj, kk int
|
||||||
|
|
||||||
|
if length < 16 {
|
||||||
|
for k = start; k < start+length; k += j {
|
||||||
|
j = 1
|
||||||
|
x = V[I[k]+h]
|
||||||
|
for i = 1; k+i < start+length; i++ {
|
||||||
|
if V[I[k+i]+h] < x {
|
||||||
|
x = V[I[k+i]+h]
|
||||||
|
j = 0
|
||||||
|
}
|
||||||
|
if V[I[k+i]+h] == x {
|
||||||
|
swap(I, k+i, k+j)
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i = 0; i < j; i++ {
|
||||||
|
V[I[k+i]] = k + j - 1
|
||||||
|
}
|
||||||
|
if j == 1 {
|
||||||
|
I[k] = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x = V[I[start+length/2]+h]
|
||||||
|
jj = 0
|
||||||
|
kk = 0
|
||||||
|
for i = start; i < start+length; i++ {
|
||||||
|
if V[I[i]+h] < x {
|
||||||
|
jj++
|
||||||
|
}
|
||||||
|
if V[I[i]+h] == x {
|
||||||
|
kk++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jj += start
|
||||||
|
kk += jj
|
||||||
|
|
||||||
|
i = start
|
||||||
|
j = 0
|
||||||
|
k = 0
|
||||||
|
for i < jj {
|
||||||
|
if V[I[i]+h] < x {
|
||||||
|
i++
|
||||||
|
} else if V[I[i]+h] == x {
|
||||||
|
swap(I, i, jj+j)
|
||||||
|
j++
|
||||||
|
} else {
|
||||||
|
swap(I, i, kk+k)
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for jj+j < kk {
|
||||||
|
if V[I[jj+j]+h] == x {
|
||||||
|
j++
|
||||||
|
} else {
|
||||||
|
swap(I, jj+j, kk+k)
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if jj > start {
|
||||||
|
split(I, V, start, jj-start, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 0; i < kk-jj; i++ {
|
||||||
|
V[I[jj+i]] = kk - 1
|
||||||
|
}
|
||||||
|
if jj == kk-1 {
|
||||||
|
I[jj] = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if start+length > kk {
|
||||||
|
split(I, V, kk, start+length-kk, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func qsufsort(obuf []byte) []int {
|
||||||
|
var buckets [256]int
|
||||||
|
var i, h int
|
||||||
|
I := make([]int, len(obuf)+1)
|
||||||
|
V := make([]int, len(obuf)+1)
|
||||||
|
|
||||||
|
for _, c := range obuf {
|
||||||
|
buckets[c]++
|
||||||
|
}
|
||||||
|
for i = 1; i < 256; i++ {
|
||||||
|
buckets[i] += buckets[i-1]
|
||||||
|
}
|
||||||
|
copy(buckets[1:], buckets[:])
|
||||||
|
buckets[0] = 0
|
||||||
|
|
||||||
|
for i, c := range obuf {
|
||||||
|
buckets[c]++
|
||||||
|
I[buckets[c]] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
I[0] = len(obuf)
|
||||||
|
for i, c := range obuf {
|
||||||
|
V[i] = buckets[c]
|
||||||
|
}
|
||||||
|
|
||||||
|
V[len(obuf)] = 0
|
||||||
|
for i = 1; i < 256; i++ {
|
||||||
|
if buckets[i] == buckets[i-1]+1 {
|
||||||
|
I[buckets[i]] = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
I[0] = -1
|
||||||
|
|
||||||
|
for h = 1; I[0] != -(len(obuf) + 1); h += h {
|
||||||
|
var n int
|
||||||
|
for i = 0; i < len(obuf)+1; {
|
||||||
|
if I[i] < 0 {
|
||||||
|
n -= I[i]
|
||||||
|
i -= I[i]
|
||||||
|
} else {
|
||||||
|
if n != 0 {
|
||||||
|
I[i-n] = -n
|
||||||
|
}
|
||||||
|
n = V[I[i]] + 1 - i
|
||||||
|
split(I, V, i, n, h)
|
||||||
|
i += n
|
||||||
|
n = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n != 0 {
|
||||||
|
I[i-n] = -n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 0; i < len(obuf)+1; i++ {
|
||||||
|
I[V[i]] = i
|
||||||
|
}
|
||||||
|
return I
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchlen(a, b []byte) (i int) {
|
||||||
|
for i < len(a) && i < len(b) && a[i] == b[i] {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func search(I []int, obuf, nbuf []byte, st, en int) (pos, n int) {
|
||||||
|
if en-st < 2 {
|
||||||
|
x := matchlen(obuf[I[st]:], nbuf)
|
||||||
|
y := matchlen(obuf[I[en]:], nbuf)
|
||||||
|
|
||||||
|
if x > y {
|
||||||
|
return I[st], x
|
||||||
|
} else {
|
||||||
|
return I[en], y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x := st + (en-st)/2
|
||||||
|
if bytes.Compare(obuf[I[x]:], nbuf) < 0 {
|
||||||
|
return search(I, obuf, nbuf, x, en)
|
||||||
|
} else {
|
||||||
|
return search(I, obuf, nbuf, st, x)
|
||||||
|
}
|
||||||
|
panic("unreached")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diff computes the difference between old and new, according to the bsdiff
|
||||||
|
// algorithm, and writes the result to patch.
|
||||||
|
func Diff(old, new io.Reader, patch io.Writer) error {
|
||||||
|
obuf, err := ioutil.ReadAll(old)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nbuf, err := ioutil.ReadAll(new)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pbuf, err := diffBytes(obuf, nbuf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = patch.Write(pbuf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func diffBytes(obuf, nbuf []byte) ([]byte, error) {
|
||||||
|
var patch seekBuffer
|
||||||
|
err := diff(obuf, nbuf, &patch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return patch.buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func diff(obuf, nbuf []byte, patch io.WriteSeeker) error {
|
||||||
|
var lenf int
|
||||||
|
I := qsufsort(obuf)
|
||||||
|
db := make([]byte, len(nbuf))
|
||||||
|
eb := make([]byte, len(nbuf))
|
||||||
|
var dblen, eblen int
|
||||||
|
|
||||||
|
var hdr header
|
||||||
|
hdr.Magic = magic
|
||||||
|
hdr.NewSize = int64(len(nbuf))
|
||||||
|
err := binary.Write(patch, signMagLittleEndian{}, &hdr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the differences, writing ctrl as we go
|
||||||
|
pfbz2, err := newBzip2Writer(patch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var scan, pos, length int
|
||||||
|
var lastscan, lastpos, lastoffset int
|
||||||
|
for scan < len(nbuf) {
|
||||||
|
var oldscore int
|
||||||
|
scan += length
|
||||||
|
for scsc := scan; scan < len(nbuf); scan++ {
|
||||||
|
pos, length = search(I, obuf, nbuf[scan:], 0, len(obuf))
|
||||||
|
|
||||||
|
for ; scsc < scan+length; scsc++ {
|
||||||
|
if scsc+lastoffset < len(obuf) &&
|
||||||
|
obuf[scsc+lastoffset] == nbuf[scsc] {
|
||||||
|
oldscore++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length == oldscore && length != 0) || length > oldscore+8 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if scan+lastoffset < len(obuf) && obuf[scan+lastoffset] == nbuf[scan] {
|
||||||
|
oldscore--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if length != oldscore || scan == len(nbuf) {
|
||||||
|
var s, Sf int
|
||||||
|
lenf = 0
|
||||||
|
for i := 0; lastscan+i < scan && lastpos+i < len(obuf); {
|
||||||
|
if obuf[lastpos+i] == nbuf[lastscan+i] {
|
||||||
|
s++
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
if s*2-i > Sf*2-lenf {
|
||||||
|
Sf = s
|
||||||
|
lenf = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lenb := 0
|
||||||
|
if scan < len(nbuf) {
|
||||||
|
var s, Sb int
|
||||||
|
for i := 1; (scan >= lastscan+i) && (pos >= i); i++ {
|
||||||
|
if obuf[pos-i] == nbuf[scan-i] {
|
||||||
|
s++
|
||||||
|
}
|
||||||
|
if s*2-i > Sb*2-lenb {
|
||||||
|
Sb = s
|
||||||
|
lenb = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastscan+lenf > scan-lenb {
|
||||||
|
overlap := (lastscan + lenf) - (scan - lenb)
|
||||||
|
s := 0
|
||||||
|
Ss := 0
|
||||||
|
lens := 0
|
||||||
|
for i := 0; i < overlap; i++ {
|
||||||
|
if nbuf[lastscan+lenf-overlap+i] == obuf[lastpos+lenf-overlap+i] {
|
||||||
|
s++
|
||||||
|
}
|
||||||
|
if nbuf[scan-lenb+i] == obuf[pos-lenb+i] {
|
||||||
|
s--
|
||||||
|
}
|
||||||
|
if s > Ss {
|
||||||
|
Ss = s
|
||||||
|
lens = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lenf += lens - overlap
|
||||||
|
lenb -= lens
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < lenf; i++ {
|
||||||
|
db[dblen+i] = nbuf[lastscan+i] - obuf[lastpos+i]
|
||||||
|
}
|
||||||
|
for i := 0; i < (scan-lenb)-(lastscan+lenf); i++ {
|
||||||
|
eb[eblen+i] = nbuf[lastscan+lenf+i]
|
||||||
|
}
|
||||||
|
|
||||||
|
dblen += lenf
|
||||||
|
eblen += (scan - lenb) - (lastscan + lenf)
|
||||||
|
|
||||||
|
err = binary.Write(pfbz2, signMagLittleEndian{}, int64(lenf))
|
||||||
|
if err != nil {
|
||||||
|
pfbz2.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
val := (scan - lenb) - (lastscan + lenf)
|
||||||
|
err = binary.Write(pfbz2, signMagLittleEndian{}, int64(val))
|
||||||
|
if err != nil {
|
||||||
|
pfbz2.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
val = (pos - lenb) - (lastpos + lenf)
|
||||||
|
err = binary.Write(pfbz2, signMagLittleEndian{}, int64(val))
|
||||||
|
if err != nil {
|
||||||
|
pfbz2.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
lastscan = scan - lenb
|
||||||
|
lastpos = pos - lenb
|
||||||
|
lastoffset = pos - scan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = pfbz2.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute size of compressed ctrl data
|
||||||
|
l64, err := patch.Seek(0, 1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hdr.CtrlLen = int64(l64 - 32)
|
||||||
|
|
||||||
|
// Write compressed diff data
|
||||||
|
pfbz2, err = newBzip2Writer(patch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
n, err := pfbz2.Write(db[:dblen])
|
||||||
|
if err != nil {
|
||||||
|
pfbz2.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n != dblen {
|
||||||
|
pfbz2.Close()
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
err = pfbz2.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute size of compressed diff data
|
||||||
|
n64, err := patch.Seek(0, 1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hdr.DiffLen = n64 - l64
|
||||||
|
|
||||||
|
// Write compressed extra data
|
||||||
|
pfbz2, err = newBzip2Writer(patch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
n, err = pfbz2.Write(eb[:eblen])
|
||||||
|
if err != nil {
|
||||||
|
pfbz2.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n != eblen {
|
||||||
|
pfbz2.Close()
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
err = pfbz2.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek to the beginning, write the header, and close the file
|
||||||
|
_, err = patch.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = binary.Write(patch, signMagLittleEndian{}, &hdr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
67
Godeps/_workspace/src/github.com/kr/binarydist/diff_test.go
generated
vendored
Normal file
67
Godeps/_workspace/src/github.com/kr/binarydist/diff_test.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var diffT = []struct {
|
||||||
|
old *os.File
|
||||||
|
new *os.File
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
old: mustWriteRandFile("test.old", 1e3),
|
||||||
|
new: mustWriteRandFile("test.new", 1e3),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: mustOpen("testdata/sample.old"),
|
||||||
|
new: mustOpen("testdata/sample.new"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDiff(t *testing.T) {
|
||||||
|
for _, s := range diffT {
|
||||||
|
got, err := ioutil.TempFile("/tmp", "bspatch.")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.Remove(got.Name())
|
||||||
|
|
||||||
|
exp, err := ioutil.TempFile("/tmp", "bspatch.")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("bsdiff", s.old.Name(), s.new.Name(), exp.Name())
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
err = cmd.Run()
|
||||||
|
os.Remove(exp.Name())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Diff(s.old, s.new, got)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = got.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
gotBuf := mustReadAll(got)
|
||||||
|
expBuf := mustReadAll(exp)
|
||||||
|
|
||||||
|
if !bytes.Equal(gotBuf, expBuf) {
|
||||||
|
t.Fail()
|
||||||
|
t.Logf("diff %s %s", s.old.Name(), s.new.Name())
|
||||||
|
t.Logf("%s: len(got) = %d", got.Name(), len(gotBuf))
|
||||||
|
t.Logf("%s: len(exp) = %d", exp.Name(), len(expBuf))
|
||||||
|
i := matchlen(gotBuf, expBuf)
|
||||||
|
t.Logf("produced different output at pos %d; %d != %d", i, gotBuf[i], expBuf[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
Godeps/_workspace/src/github.com/kr/binarydist/doc.go
generated
vendored
Normal file
24
Godeps/_workspace/src/github.com/kr/binarydist/doc.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Package binarydist implements binary diff and patch as described on
|
||||||
|
// http://www.daemonology.net/bsdiff/. It reads and writes files
|
||||||
|
// compatible with the tools there.
|
||||||
|
package binarydist
|
||||||
|
|
||||||
|
var magic = [8]byte{'B', 'S', 'D', 'I', 'F', 'F', '4', '0'}
|
||||||
|
|
||||||
|
// File format:
|
||||||
|
// 0 8 "BSDIFF40"
|
||||||
|
// 8 8 X
|
||||||
|
// 16 8 Y
|
||||||
|
// 24 8 sizeof(newfile)
|
||||||
|
// 32 X bzip2(control block)
|
||||||
|
// 32+X Y bzip2(diff block)
|
||||||
|
// 32+X+Y ??? bzip2(extra block)
|
||||||
|
// with control block a set of triples (x,y,z) meaning "add x bytes
|
||||||
|
// from oldfile to x bytes from the diff block; copy y bytes from the
|
||||||
|
// extra block; seek forwards in oldfile by z bytes".
|
||||||
|
type header struct {
|
||||||
|
Magic [8]byte
|
||||||
|
CtrlLen int64
|
||||||
|
DiffLen int64
|
||||||
|
NewSize int64
|
||||||
|
}
|
53
Godeps/_workspace/src/github.com/kr/binarydist/encoding.go
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/kr/binarydist/encoding.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
// SignMagLittleEndian is the numeric encoding used by the bsdiff tools.
|
||||||
|
// It implements binary.ByteOrder using a sign-magnitude format
|
||||||
|
// and little-endian byte order. Only methods Uint64 and String
|
||||||
|
// have been written; the rest panic.
|
||||||
|
type signMagLittleEndian struct{}
|
||||||
|
|
||||||
|
func (signMagLittleEndian) Uint16(b []byte) uint16 { panic("unimplemented") }
|
||||||
|
|
||||||
|
func (signMagLittleEndian) PutUint16(b []byte, v uint16) { panic("unimplemented") }
|
||||||
|
|
||||||
|
func (signMagLittleEndian) Uint32(b []byte) uint32 { panic("unimplemented") }
|
||||||
|
|
||||||
|
func (signMagLittleEndian) PutUint32(b []byte, v uint32) { panic("unimplemented") }
|
||||||
|
|
||||||
|
func (signMagLittleEndian) Uint64(b []byte) uint64 {
|
||||||
|
y := int64(b[0]) |
|
||||||
|
int64(b[1])<<8 |
|
||||||
|
int64(b[2])<<16 |
|
||||||
|
int64(b[3])<<24 |
|
||||||
|
int64(b[4])<<32 |
|
||||||
|
int64(b[5])<<40 |
|
||||||
|
int64(b[6])<<48 |
|
||||||
|
int64(b[7]&0x7f)<<56
|
||||||
|
|
||||||
|
if b[7]&0x80 != 0 {
|
||||||
|
y = -y
|
||||||
|
}
|
||||||
|
return uint64(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (signMagLittleEndian) PutUint64(b []byte, v uint64) {
|
||||||
|
x := int64(v)
|
||||||
|
neg := x < 0
|
||||||
|
if neg {
|
||||||
|
x = -x
|
||||||
|
}
|
||||||
|
|
||||||
|
b[0] = byte(x)
|
||||||
|
b[1] = byte(x >> 8)
|
||||||
|
b[2] = byte(x >> 16)
|
||||||
|
b[3] = byte(x >> 24)
|
||||||
|
b[4] = byte(x >> 32)
|
||||||
|
b[5] = byte(x >> 40)
|
||||||
|
b[6] = byte(x >> 48)
|
||||||
|
b[7] = byte(x >> 56)
|
||||||
|
if neg {
|
||||||
|
b[7] |= 0x80
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (signMagLittleEndian) String() string { return "signMagLittleEndian" }
|
109
Godeps/_workspace/src/github.com/kr/binarydist/patch.go
generated
vendored
Normal file
109
Godeps/_workspace/src/github.com/kr/binarydist/patch.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/bzip2"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrCorrupt = errors.New("corrupt patch")
|
||||||
|
|
||||||
|
// Patch applies patch to old, according to the bspatch algorithm,
|
||||||
|
// and writes the result to new.
|
||||||
|
func Patch(old io.Reader, new io.Writer, patch io.Reader) error {
|
||||||
|
var hdr header
|
||||||
|
err := binary.Read(patch, signMagLittleEndian{}, &hdr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if hdr.Magic != magic {
|
||||||
|
return ErrCorrupt
|
||||||
|
}
|
||||||
|
if hdr.CtrlLen < 0 || hdr.DiffLen < 0 || hdr.NewSize < 0 {
|
||||||
|
return ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrlbuf := make([]byte, hdr.CtrlLen)
|
||||||
|
_, err = io.ReadFull(patch, ctrlbuf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cpfbz2 := bzip2.NewReader(bytes.NewReader(ctrlbuf))
|
||||||
|
|
||||||
|
diffbuf := make([]byte, hdr.DiffLen)
|
||||||
|
_, err = io.ReadFull(patch, diffbuf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dpfbz2 := bzip2.NewReader(bytes.NewReader(diffbuf))
|
||||||
|
|
||||||
|
// The entire rest of the file is the extra block.
|
||||||
|
epfbz2 := bzip2.NewReader(patch)
|
||||||
|
|
||||||
|
obuf, err := ioutil.ReadAll(old)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nbuf := make([]byte, hdr.NewSize)
|
||||||
|
|
||||||
|
var oldpos, newpos int64
|
||||||
|
for newpos < hdr.NewSize {
|
||||||
|
var ctrl struct{ Add, Copy, Seek int64 }
|
||||||
|
err = binary.Read(cpfbz2, signMagLittleEndian{}, &ctrl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity-check
|
||||||
|
if newpos+ctrl.Add > hdr.NewSize {
|
||||||
|
return ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read diff string
|
||||||
|
_, err = io.ReadFull(dpfbz2, nbuf[newpos:newpos+ctrl.Add])
|
||||||
|
if err != nil {
|
||||||
|
return ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add old data to diff string
|
||||||
|
for i := int64(0); i < ctrl.Add; i++ {
|
||||||
|
if oldpos+i >= 0 && oldpos+i < int64(len(obuf)) {
|
||||||
|
nbuf[newpos+i] += obuf[oldpos+i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust pointers
|
||||||
|
newpos += ctrl.Add
|
||||||
|
oldpos += ctrl.Add
|
||||||
|
|
||||||
|
// Sanity-check
|
||||||
|
if newpos+ctrl.Copy > hdr.NewSize {
|
||||||
|
return ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read extra string
|
||||||
|
_, err = io.ReadFull(epfbz2, nbuf[newpos:newpos+ctrl.Copy])
|
||||||
|
if err != nil {
|
||||||
|
return ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust pointers
|
||||||
|
newpos += ctrl.Copy
|
||||||
|
oldpos += ctrl.Seek
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the new file
|
||||||
|
for len(nbuf) > 0 {
|
||||||
|
n, err := new.Write(nbuf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nbuf = nbuf[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
62
Godeps/_workspace/src/github.com/kr/binarydist/patch_test.go
generated
vendored
Normal file
62
Godeps/_workspace/src/github.com/kr/binarydist/patch_test.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPatch(t *testing.T) {
|
||||||
|
mustWriteRandFile("test.old", 1e3)
|
||||||
|
mustWriteRandFile("test.new", 1e3)
|
||||||
|
|
||||||
|
got, err := ioutil.TempFile("/tmp", "bspatch.")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.Remove(got.Name())
|
||||||
|
|
||||||
|
err = exec.Command("bsdiff", "test.old", "test.new", "test.patch").Run()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Patch(mustOpen("test.old"), got, mustOpen("test.patch"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ref, err := got.Seek(0, 2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("got %d bytes", ref)
|
||||||
|
if n := fileCmp(got, mustOpen("test.new")); n > -1 {
|
||||||
|
t.Fatalf("produced different output at pos %d", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPatchHk(t *testing.T) {
|
||||||
|
got, err := ioutil.TempFile("/tmp", "bspatch.")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.Remove(got.Name())
|
||||||
|
|
||||||
|
err = Patch(mustOpen("testdata/sample.old"), got, mustOpen("testdata/sample.patch"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ref, err := got.Seek(0, 2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("got %d bytes", ref)
|
||||||
|
if n := fileCmp(got, mustOpen("testdata/sample.new")); n > -1 {
|
||||||
|
t.Fatalf("produced different output at pos %d", n)
|
||||||
|
}
|
||||||
|
}
|
43
Godeps/_workspace/src/github.com/kr/binarydist/seek.go
generated
vendored
Normal file
43
Godeps/_workspace/src/github.com/kr/binarydist/seek.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type seekBuffer struct {
|
||||||
|
buf []byte
|
||||||
|
pos int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *seekBuffer) Write(p []byte) (n int, err error) {
|
||||||
|
n = copy(b.buf[b.pos:], p)
|
||||||
|
if n == len(p) {
|
||||||
|
b.pos += n
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
b.buf = append(b.buf, p[n:]...)
|
||||||
|
b.pos += len(p)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *seekBuffer) Seek(offset int64, whence int) (ret int64, err error) {
|
||||||
|
var abs int64
|
||||||
|
switch whence {
|
||||||
|
case 0:
|
||||||
|
abs = offset
|
||||||
|
case 1:
|
||||||
|
abs = int64(b.pos) + offset
|
||||||
|
case 2:
|
||||||
|
abs = int64(len(b.buf)) + offset
|
||||||
|
default:
|
||||||
|
return 0, errors.New("binarydist: invalid whence")
|
||||||
|
}
|
||||||
|
if abs < 0 {
|
||||||
|
return 0, errors.New("binarydist: negative position")
|
||||||
|
}
|
||||||
|
if abs >= 1<<31 {
|
||||||
|
return 0, errors.New("binarydist: position out of range")
|
||||||
|
}
|
||||||
|
b.pos = int(abs)
|
||||||
|
return abs, nil
|
||||||
|
}
|
33
Godeps/_workspace/src/github.com/kr/binarydist/sort_test.go
generated
vendored
Normal file
33
Godeps/_workspace/src/github.com/kr/binarydist/sort_test.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sortT = [][]byte{
|
||||||
|
mustRandBytes(1000),
|
||||||
|
mustReadAll(mustOpen("test.old")),
|
||||||
|
[]byte("abcdefabcdef"),
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQsufsort(t *testing.T) {
|
||||||
|
for _, s := range sortT {
|
||||||
|
I := qsufsort(s)
|
||||||
|
for i := 1; i < len(I); i++ {
|
||||||
|
if bytes.Compare(s[I[i-1]:], s[I[i]:]) > 0 {
|
||||||
|
t.Fatalf("unsorted at %d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustRandBytes(n int) []byte {
|
||||||
|
b := make([]byte, n)
|
||||||
|
_, err := rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
BIN
Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.new
generated
vendored
Normal file
BIN
Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.new
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.old
generated
vendored
Normal file
BIN
Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.old
generated
vendored
Normal file
Binary file not shown.
BIN
Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.patch
generated
vendored
Normal file
BIN
Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.patch
generated
vendored
Normal file
Binary file not shown.
19
selfupdate/Godeps/Godeps.json
generated
Normal file
19
selfupdate/Godeps/Godeps.json
generated
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"ImportPath": "github.com/sanbornm/go-selfupdate/selfupdate",
|
||||||
|
"GoVersion": "go1.3",
|
||||||
|
"Deps": [
|
||||||
|
{
|
||||||
|
"ImportPath": "bitbucket.org/kardianos/osext",
|
||||||
|
"Comment": "null-13",
|
||||||
|
"Rev": "5d3ddcf53a508cc2f7404eaebf546ef2cb5cdb6e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/inconshreveable/go-update",
|
||||||
|
"Rev": "3f0466666779bd2143f368a207b0641f0ed536e8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/kr/binarydist",
|
||||||
|
"Rev": "9955b0ab8708602d411341e55fffd7e0700f86bd"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
5
selfupdate/Godeps/Readme
generated
Normal file
5
selfupdate/Godeps/Readme
generated
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
This directory tree is generated automatically by godep.
|
||||||
|
|
||||||
|
Please do not edit.
|
||||||
|
|
||||||
|
See https://github.com/tools/godep for more information.
|
2
selfupdate/Godeps/_workspace/.gitignore
generated
vendored
Normal file
2
selfupdate/Godeps/_workspace/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/pkg
|
||||||
|
/bin
|
20
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE
generated
vendored
Normal file
20
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2012 Daniel Theophanes
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source
|
||||||
|
distribution.
|
32
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go
generated
vendored
Normal file
32
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Extensions to the standard "os" package.
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import "path/filepath"
|
||||||
|
|
||||||
|
// Executable returns an absolute path that can be used to
|
||||||
|
// re-invoke the current program.
|
||||||
|
// It may not be valid after the current program exits.
|
||||||
|
func Executable() (string, error) {
|
||||||
|
p, err := executable()
|
||||||
|
return filepath.Clean(p), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns same path as Executable, returns just the folder
|
||||||
|
// path. Excludes the executable name.
|
||||||
|
func ExecutableFolder() (string, error) {
|
||||||
|
p, err := Executable()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
folder, _ := filepath.Split(p)
|
||||||
|
return folder, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depricated. Same as Executable().
|
||||||
|
func GetExePath() (exePath string, err error) {
|
||||||
|
return Executable()
|
||||||
|
}
|
20
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go
generated
vendored
Normal file
20
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func executable() (string, error) {
|
||||||
|
f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return syscall.Fd2path(int(f.Fd()))
|
||||||
|
}
|
25
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go
generated
vendored
Normal file
25
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build linux netbsd openbsd
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func executable() (string, error) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
return os.Readlink("/proc/self/exe")
|
||||||
|
case "netbsd":
|
||||||
|
return os.Readlink("/proc/curproc/exe")
|
||||||
|
case "openbsd":
|
||||||
|
return os.Readlink("/proc/curproc/file")
|
||||||
|
}
|
||||||
|
return "", errors.New("ExecPath not implemented for " + runtime.GOOS)
|
||||||
|
}
|
79
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_sysctl.go
generated
vendored
Normal file
79
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_sysctl.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build darwin freebsd
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var initCwd, initCwdErr = os.Getwd()
|
||||||
|
|
||||||
|
func executable() (string, error) {
|
||||||
|
var mib [4]int32
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "freebsd":
|
||||||
|
mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
|
||||||
|
case "darwin":
|
||||||
|
mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
|
||||||
|
}
|
||||||
|
|
||||||
|
n := uintptr(0)
|
||||||
|
// Get length.
|
||||||
|
_, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||||
|
if errNum != 0 {
|
||||||
|
return "", errNum
|
||||||
|
}
|
||||||
|
if n == 0 { // This shouldn't happen.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
buf := make([]byte, n)
|
||||||
|
_, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||||
|
if errNum != 0 {
|
||||||
|
return "", errNum
|
||||||
|
}
|
||||||
|
if n == 0 { // This shouldn't happen.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
for i, v := range buf {
|
||||||
|
if v == 0 {
|
||||||
|
buf = buf[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
execPath := string(buf)
|
||||||
|
// execPath will not be empty due to above checks.
|
||||||
|
// Try to get the absolute path if the execPath is not rooted.
|
||||||
|
if execPath[0] != '/' {
|
||||||
|
execPath, err = getAbs(execPath)
|
||||||
|
if err != nil {
|
||||||
|
return execPath, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For darwin KERN_PROCARGS may return the path to a symlink rather than the
|
||||||
|
// actual executable.
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
if execPath, err = filepath.EvalSymlinks(execPath); err != nil {
|
||||||
|
return execPath, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return execPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAbs(execPath string) (string, error) {
|
||||||
|
if initCwdErr != nil {
|
||||||
|
return execPath, initCwdErr
|
||||||
|
}
|
||||||
|
// The execPath may begin with a "../" or a "./" so clean it first.
|
||||||
|
// Join the two paths, trailing and starting slashes undetermined, so use
|
||||||
|
// the generic Join function.
|
||||||
|
return filepath.Join(initCwd, filepath.Clean(execPath)), nil
|
||||||
|
}
|
79
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go
generated
vendored
Normal file
79
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build darwin linux freebsd netbsd windows
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
oexec "os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH"
|
||||||
|
|
||||||
|
func TestExecPath(t *testing.T) {
|
||||||
|
ep, err := Executable()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ExecPath failed: %v", err)
|
||||||
|
}
|
||||||
|
// we want fn to be of the form "dir/prog"
|
||||||
|
dir := filepath.Dir(filepath.Dir(ep))
|
||||||
|
fn, err := filepath.Rel(dir, ep)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("filepath.Rel: %v", err)
|
||||||
|
}
|
||||||
|
cmd := &oexec.Cmd{}
|
||||||
|
// make child start with a relative program path
|
||||||
|
cmd.Dir = dir
|
||||||
|
cmd.Path = fn
|
||||||
|
// forge argv[0] for child, so that we can verify we could correctly
|
||||||
|
// get real path of the executable without influenced by argv[0].
|
||||||
|
cmd.Args = []string{"-", "-test.run=XXXX"}
|
||||||
|
cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)}
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("exec(self) failed: %v", err)
|
||||||
|
}
|
||||||
|
outs := string(out)
|
||||||
|
if !filepath.IsAbs(outs) {
|
||||||
|
t.Fatalf("Child returned %q, want an absolute path", out)
|
||||||
|
}
|
||||||
|
if !sameFile(outs, ep) {
|
||||||
|
t.Fatalf("Child returned %q, not the same file as %q", out, ep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sameFile(fn1, fn2 string) bool {
|
||||||
|
fi1, err := os.Stat(fn1)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
fi2, err := os.Stat(fn2)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return os.SameFile(fi1, fi2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if e := os.Getenv(execPath_EnvVar); e != "" {
|
||||||
|
// first chdir to another path
|
||||||
|
dir := "/"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
dir = filepath.VolumeName(".")
|
||||||
|
}
|
||||||
|
os.Chdir(dir)
|
||||||
|
if ep, err := Executable(); err != nil {
|
||||||
|
fmt.Fprint(os.Stderr, "ERROR: ", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(os.Stderr, ep)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
34
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_windows.go
generated
vendored
Normal file
34
selfupdate/Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_windows.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package osext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel = syscall.MustLoadDLL("kernel32.dll")
|
||||||
|
getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetModuleFileName() with hModule = NULL
|
||||||
|
func executable() (exePath string, err error) {
|
||||||
|
return getModuleFileName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getModuleFileName() (string, error) {
|
||||||
|
var n uint32
|
||||||
|
b := make([]uint16, syscall.MAX_PATH)
|
||||||
|
size := uint32(len(b))
|
||||||
|
|
||||||
|
r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
|
||||||
|
n = uint32(r0)
|
||||||
|
if n == 0 {
|
||||||
|
return "", e1
|
||||||
|
}
|
||||||
|
return string(utf16.Decode(b[0:n])), nil
|
||||||
|
}
|
137
selfupdate/Godeps/_workspace/src/github.com/inconshreveable/go-update/README
generated
vendored
Normal file
137
selfupdate/Godeps/_workspace/src/github.com/inconshreveable/go-update/README
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
PACKAGE DOCUMENTATION
|
||||||
|
|
||||||
|
package update
|
||||||
|
import "github.com/inconshreveable/go-update"
|
||||||
|
|
||||||
|
Package update allows a program to "self-update", replacing its
|
||||||
|
executable file with new bytes.
|
||||||
|
|
||||||
|
Package update provides the facility to create user experiences like
|
||||||
|
auto-updating or user-approved updates which manifest as user prompts in
|
||||||
|
commercial applications with copy similar to "Restart to being using the
|
||||||
|
new version of X".
|
||||||
|
|
||||||
|
Updating your program to a new version is as easy as:
|
||||||
|
|
||||||
|
err := update.FromUrl("http://release.example.com/2.0/myprogram")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Update failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
The most low-level API is FromStream() which updates the current
|
||||||
|
executable with the bytes read from an io.Reader.
|
||||||
|
|
||||||
|
Additional APIs are provided for common update strategies which include
|
||||||
|
updating from a file with FromFile() and updating from the internet with
|
||||||
|
FromUrl().
|
||||||
|
|
||||||
|
Using the more advaced Download.UpdateFromUrl() API gives you the
|
||||||
|
ability to resume an interrupted download to enable large updates to
|
||||||
|
complete even over intermittent or slow connections. This API also
|
||||||
|
enables more fine-grained control over how the update is downloaded from
|
||||||
|
the internet as well as access to download progress,
|
||||||
|
|
||||||
|
|
||||||
|
VARIABLES
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Returned when the remote server indicates that no download is available
|
||||||
|
UpdateUnavailable error
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
FUNCTIONS
|
||||||
|
|
||||||
|
func FromFile(filepath string) (err error)
|
||||||
|
FromFile reads the contents of the given file and uses them to update
|
||||||
|
the current program's executable file by calling FromStream().
|
||||||
|
|
||||||
|
func FromStream(newBinary io.Reader) (err error)
|
||||||
|
FromStream reads the contents of the supplied io.Reader newBinary and
|
||||||
|
uses them to update the current program's executable file.
|
||||||
|
|
||||||
|
FromStream performs the following actions to ensure a cross-platform
|
||||||
|
safe update:
|
||||||
|
|
||||||
|
- Renames the current program's executable file from
|
||||||
|
/path/to/program-name to /path/to/.program-name.old
|
||||||
|
|
||||||
|
- Opens the now-empty path /path/to/program-name with mode 0755 and
|
||||||
|
copies the contents of newBinary into the file.
|
||||||
|
|
||||||
|
- If the copy is successful, it erases /path/to/.program.old. If this
|
||||||
|
operation fails, no error is reported.
|
||||||
|
|
||||||
|
- If the copy is unsuccessful, it attempts to rename
|
||||||
|
/path/to/.program-name.old back to /path/to/program-name. If this
|
||||||
|
operation fails, the error is not reported in order to not mask the
|
||||||
|
error that caused the rename recovery attempt.
|
||||||
|
|
||||||
|
func FromUrl(url string) error
|
||||||
|
FromUrl downloads the contents of the given url and uses them to update
|
||||||
|
the current program's executable file. It is a convenience function
|
||||||
|
which is equivalent to
|
||||||
|
|
||||||
|
NewDownload().UpdateFromUrl(url)
|
||||||
|
|
||||||
|
See Download.UpdateFromUrl for more details.
|
||||||
|
|
||||||
|
|
||||||
|
TYPES
|
||||||
|
|
||||||
|
type Download struct {
|
||||||
|
// net/http.Client to use when downloading the update.
|
||||||
|
// If nil, a default http.Client is used
|
||||||
|
HttpClient *http.Client
|
||||||
|
|
||||||
|
// Path on the file system to dowload the update to
|
||||||
|
// If empty, a temporary file is used.
|
||||||
|
// After the download begins, this path will be set
|
||||||
|
// so that the client can use it to resume aborted
|
||||||
|
// downloads
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// Progress returns the percentage of the download
|
||||||
|
// completed as an integer between 0 and 100
|
||||||
|
Progress chan (int)
|
||||||
|
|
||||||
|
// HTTP Method to use in the download request. Default is "GET"
|
||||||
|
Method string
|
||||||
|
}
|
||||||
|
Type Download encapsulates the necessary parameters and state needed to
|
||||||
|
download an update from the internet. Create an instance with the
|
||||||
|
NewDownload() factory function.
|
||||||
|
|
||||||
|
|
||||||
|
func NewDownload() *Download
|
||||||
|
NewDownload initializes a new Download object
|
||||||
|
|
||||||
|
|
||||||
|
func (d *Download) UpdateFromUrl(url string) (err error)
|
||||||
|
UpdateFromUrl downloads the given url from the internet to a file on
|
||||||
|
disk and then calls FromStream() to update the current program's
|
||||||
|
executable file with the contents of that file.
|
||||||
|
|
||||||
|
If the update is successful, the downloaded file will be erased from
|
||||||
|
disk. Otherwise, it will remain in d.Path to allow the download to
|
||||||
|
resume later or be skipped entirely.
|
||||||
|
|
||||||
|
Only HTTP/1.1 servers that implement the Range header are supported.
|
||||||
|
|
||||||
|
UpdateFromUrl() uses HTTP status codes to determine what action to take.
|
||||||
|
|
||||||
|
- The HTTP server should return 200 or 206 for the update to be
|
||||||
|
downloaded.
|
||||||
|
|
||||||
|
- The HTTP server should return 204 if no update is available at this
|
||||||
|
time. This will cause UpdateFromUrl to return the error
|
||||||
|
UpdateUnavailable.
|
||||||
|
|
||||||
|
- If the HTTP server returns a 3XX redirect, it will be followed
|
||||||
|
according to d.HttpClient's redirect policy.
|
||||||
|
|
||||||
|
- Any other HTTP status code will cause UpdateFromUrl to return an
|
||||||
|
error.
|
||||||
|
|
||||||
|
|
||||||
|
|
455
selfupdate/Godeps/_workspace/src/github.com/inconshreveable/go-update/update.go
generated
vendored
Normal file
455
selfupdate/Godeps/_workspace/src/github.com/inconshreveable/go-update/update.go
generated
vendored
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
/*
|
||||||
|
Package update allows a program to "self-update", replacing its executable file
|
||||||
|
with new bytes.
|
||||||
|
|
||||||
|
Package update provides the facility to create user experiences like auto-updating
|
||||||
|
or user-approved updates which manifest as user prompts in commercial applications
|
||||||
|
with copy similar to "Restart to being using the new version of X".
|
||||||
|
|
||||||
|
Updating your program to a new version is as easy as:
|
||||||
|
|
||||||
|
err := update.FromUrl("http://release.example.com/2.0/myprogram")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Update failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
The most low-level API is FromStream() which updates the current executable
|
||||||
|
with the bytes read from an io.Reader.
|
||||||
|
|
||||||
|
Additional APIs are provided for common update strategies which include
|
||||||
|
updating from a file with FromFile() and updating from the internet with
|
||||||
|
FromUrl().
|
||||||
|
|
||||||
|
Using the more advaced Download.UpdateFromUrl() API gives you the ability
|
||||||
|
to resume an interrupted download to enable large updates to complete even
|
||||||
|
over intermittent or slow connections. This API also enables more fine-grained
|
||||||
|
control over how the update is downloaded from the internet as well as access to
|
||||||
|
download progress,
|
||||||
|
*/
|
||||||
|
package update
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"bitbucket.org/kardianos/osext"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MeteredReader struct {
|
||||||
|
rd io.ReadCloser
|
||||||
|
totalSize int64
|
||||||
|
progress chan int
|
||||||
|
totalRead int64
|
||||||
|
ticks int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MeteredReader) Close() error {
|
||||||
|
return m.rd.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MeteredReader) Read(b []byte) (n int, err error) {
|
||||||
|
chunkSize := (m.totalSize / 100) + 1
|
||||||
|
lenB := int64(len(b))
|
||||||
|
|
||||||
|
var nChunk int
|
||||||
|
for start := int64(0); start < lenB; start += int64(nChunk) {
|
||||||
|
end := start + chunkSize
|
||||||
|
if end > lenB {
|
||||||
|
end = lenB
|
||||||
|
}
|
||||||
|
|
||||||
|
nChunk, err = m.rd.Read(b[start:end])
|
||||||
|
|
||||||
|
n += nChunk
|
||||||
|
m.totalRead += int64(nChunk)
|
||||||
|
|
||||||
|
if m.totalRead > (m.ticks * chunkSize) {
|
||||||
|
m.ticks += 1
|
||||||
|
// try to send on channel, but don't block if it's full
|
||||||
|
select {
|
||||||
|
case m.progress <- int(m.ticks + 1):
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// give the progress channel consumer a chance to run
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We wrap the round tripper when making requests
|
||||||
|
// because we need to add headers to the requests we make
|
||||||
|
// even when they are requests made after a redirect
|
||||||
|
type RoundTripper struct {
|
||||||
|
RoundTripFn func(*http.Request) (*http.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||||
|
return rt.RoundTripFn(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type Download encapsulates the necessary parameters and state
|
||||||
|
// needed to download an update from the internet. Create an instance
|
||||||
|
// with the NewDownload() factory function.
|
||||||
|
//
|
||||||
|
// You may only use a Download once,
|
||||||
|
type Download struct {
|
||||||
|
// net/http.Client to use when downloading the update.
|
||||||
|
// If nil, a default http.Client is used
|
||||||
|
HttpClient *http.Client
|
||||||
|
|
||||||
|
// Path on the file system to dowload the update to
|
||||||
|
// If empty, a temporary file is used.
|
||||||
|
// After the download begins, this path will be set
|
||||||
|
// so that the client can use it to resume aborted
|
||||||
|
// downloads
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// Progress returns the percentage of the download
|
||||||
|
// completed as an integer between 0 and 100
|
||||||
|
Progress chan (int)
|
||||||
|
|
||||||
|
// HTTP Method to use in the download request. Default is "GET"
|
||||||
|
Method string
|
||||||
|
|
||||||
|
// HTTP URL to issue the download request to
|
||||||
|
Url string
|
||||||
|
|
||||||
|
// Set to true when the server confirms a new version is available
|
||||||
|
// even if the updating process encounters an error later on
|
||||||
|
Available bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDownload initializes a new Download object
|
||||||
|
func NewDownload(url string) *Download {
|
||||||
|
return &Download{
|
||||||
|
HttpClient: new(http.Client),
|
||||||
|
Progress: make(chan int),
|
||||||
|
Method: "GET",
|
||||||
|
Url: url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Download) sharedHttp(offset int64) (resp *http.Response, err error) {
|
||||||
|
// create the download request
|
||||||
|
req, err := http.NewRequest(d.Method, d.Url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have to add headers like this so they get used across redirects
|
||||||
|
trans := d.HttpClient.Transport
|
||||||
|
if trans == nil {
|
||||||
|
trans = http.DefaultTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
d.HttpClient.Transport = &RoundTripper{
|
||||||
|
RoundTripFn: func(r *http.Request) (*http.Response, error) {
|
||||||
|
// add header for download continuation
|
||||||
|
if offset > 0 {
|
||||||
|
r.Header.Add("Range", fmt.Sprintf("%d-", offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ask for gzipped content so that net/http won't unzip it for us
|
||||||
|
// and destroy the content length header we need for progress calculations
|
||||||
|
r.Header.Add("Accept-Encoding", "gzip")
|
||||||
|
|
||||||
|
return trans.RoundTrip(r)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// issue the download request
|
||||||
|
return d.HttpClient.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Download) Check() (available bool, err error) {
|
||||||
|
resp, err := d.sharedHttp(0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
switch resp.StatusCode {
|
||||||
|
// ok
|
||||||
|
case 200, 206:
|
||||||
|
available = true
|
||||||
|
|
||||||
|
// no update available
|
||||||
|
case 204:
|
||||||
|
available = false
|
||||||
|
|
||||||
|
// server error
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Non 2XX response when downloading update: %s", resp.Status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get() downloads the given url from the internet to a file on disk
|
||||||
|
// and then calls FromStream() to update the current program's executable file
|
||||||
|
// with the contents of that file.
|
||||||
|
//
|
||||||
|
// If the update is successful, the downloaded file will be erased from disk.
|
||||||
|
// Otherwise, it will remain in d.Path to allow the download to resume later
|
||||||
|
// or be skipped entirely.
|
||||||
|
//
|
||||||
|
// Only HTTP/1.1 servers that implement the Range header support resuming a
|
||||||
|
// partially completed download.
|
||||||
|
//
|
||||||
|
// UpdateFromUrl() uses HTTP status codes to determine what action to take.
|
||||||
|
//
|
||||||
|
// - The HTTP server should return 200 or 206 for the update to be downloaded.
|
||||||
|
//
|
||||||
|
// - The HTTP server should return 204 if no update is available at this time.
|
||||||
|
//
|
||||||
|
// - If the HTTP server returns a 3XX redirect, it will be followed
|
||||||
|
// according to d.HttpClient's redirect policy.
|
||||||
|
//
|
||||||
|
// - Any other HTTP status code will cause UpdateFromUrl to return an error.
|
||||||
|
func (d *Download) Get() (err error) {
|
||||||
|
var offset int64 = 0
|
||||||
|
var fp *os.File
|
||||||
|
|
||||||
|
// Close the progress channel whenever this function completes
|
||||||
|
defer close(d.Progress)
|
||||||
|
|
||||||
|
// open a file where we will stream the downloaded update to
|
||||||
|
// we do this first because if the caller specified a non-empty dlpath
|
||||||
|
// we need to determine how large it is in order to resume the download
|
||||||
|
if d.Path == "" {
|
||||||
|
// no dlpath specified, use a random tempfile
|
||||||
|
fp, err = ioutil.TempFile("", "update")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
|
||||||
|
// remember the path
|
||||||
|
d.Path = fp.Name()
|
||||||
|
} else {
|
||||||
|
fp, err = os.OpenFile(d.Path, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
|
||||||
|
// determine the file size so we can resume the download, if possible
|
||||||
|
var fi os.FileInfo
|
||||||
|
fi, err = fp.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = fi.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
// start downloading the file
|
||||||
|
resp, err := d.sharedHttp(offset)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
switch resp.StatusCode {
|
||||||
|
// ok
|
||||||
|
case 200, 206:
|
||||||
|
d.Available = true
|
||||||
|
|
||||||
|
// no update available
|
||||||
|
case 204:
|
||||||
|
return
|
||||||
|
|
||||||
|
// server error
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Non 2XX response when downloading update: %s", resp.Status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine how much we have to download
|
||||||
|
// net/http sets this to -1 when it is unknown
|
||||||
|
clength := resp.ContentLength
|
||||||
|
|
||||||
|
// Read the content from the response body
|
||||||
|
rd := resp.Body
|
||||||
|
|
||||||
|
// meter the rate at which we download content for
|
||||||
|
// progress reporting if we know how much to expect
|
||||||
|
if clength > 0 {
|
||||||
|
rd = &MeteredReader{rd: rd, totalSize: clength, progress: d.Progress}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decompress the content if necessary
|
||||||
|
if resp.Header.Get("Content-Encoding") == "gzip" {
|
||||||
|
rd, err = gzip.NewReader(rd)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download the update
|
||||||
|
_, err = io.Copy(fp, rd)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Download) GetAndUpdate() (err error, errRecover error) {
|
||||||
|
// check before we download if this will work
|
||||||
|
if err = SanityCheck(); err != nil {
|
||||||
|
// keep the contract that d.Progress will close whenever Get() terminates
|
||||||
|
close(d.Progress)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// download the update
|
||||||
|
if err = d.Get(); err != nil || !d.Available {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the update
|
||||||
|
if err, errRecover = FromFile(d.Path); err != nil || errRecover != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the temporary file
|
||||||
|
os.Remove(d.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromUrl downloads the contents of the given url and uses them to update
|
||||||
|
// the current program's executable file. It is a convenience function which is equivalent to
|
||||||
|
//
|
||||||
|
// NewDownload(url).GetAndUpdate()
|
||||||
|
//
|
||||||
|
// See Download.Get() for more details.
|
||||||
|
func FromUrl(url string) (err error, errRecover error) {
|
||||||
|
return NewDownload(url).GetAndUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromFile reads the contents of the given file and uses them
|
||||||
|
// to update the current program's executable file by calling FromStream().
|
||||||
|
func FromFile(filepath string) (err error, errRecover error) {
|
||||||
|
// open the new binary
|
||||||
|
fp, err := os.Open(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
|
||||||
|
// do the update
|
||||||
|
return FromStream(fp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStream reads the contents of the supplied io.Reader newBinary
|
||||||
|
// and uses them to update the current program's executable file.
|
||||||
|
//
|
||||||
|
// FromStream performs the following actions to ensure a cross-platform safe
|
||||||
|
// update:
|
||||||
|
//
|
||||||
|
// - Creates a new file, /path/to/.program-name.new with mode 0755 and copies
|
||||||
|
// the contents of newBinary into the file
|
||||||
|
//
|
||||||
|
// - Renames the current program's executable file from /path/to/program-name
|
||||||
|
// to /path/to/.program-name.old
|
||||||
|
//
|
||||||
|
// - Renames /path/to/.program-name.new to /path/to/program-name
|
||||||
|
//
|
||||||
|
// - If the rename is successful, it erases /path/to/.program.old. If this operation
|
||||||
|
// fails, no error is reported.
|
||||||
|
//
|
||||||
|
// - If the rename is unsuccessful, it attempts to rename /path/to/.program-name.old
|
||||||
|
// back to /path/to/program-name. If this operation fails, the error is not reported
|
||||||
|
// in order to not mask the error that caused the rename recovery attempt.
|
||||||
|
func FromStream(newBinary io.Reader) (err error, errRecover error) {
|
||||||
|
// get the path to the executable
|
||||||
|
thisExecPath, err := osext.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the directory the executable exists in
|
||||||
|
execDir := filepath.Dir(thisExecPath)
|
||||||
|
execName := filepath.Base(thisExecPath)
|
||||||
|
|
||||||
|
// Copy the contents of of newbinary to a the new executable file
|
||||||
|
newExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.new", execName))
|
||||||
|
fp, err := os.OpenFile(newExecPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
_, err = io.Copy(fp, newBinary)
|
||||||
|
|
||||||
|
// if we don't call fp.Close(), windows won't let us move the new executable
|
||||||
|
// because the file will still be "in use"
|
||||||
|
fp.Close()
|
||||||
|
|
||||||
|
// this is where we'll move the executable to so that we can swap in the updated replacement
|
||||||
|
oldExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.old", execName))
|
||||||
|
|
||||||
|
// delete any existing old exec file - this is necessary on Windows for two reasons:
|
||||||
|
// 1. after a successful update, windows can't remove the .old file because the process is still running
|
||||||
|
// 2. windows rename operations fail if the destination file already exists
|
||||||
|
_ = os.Remove(oldExecPath)
|
||||||
|
|
||||||
|
// move the existing executable to a new file in the same directory
|
||||||
|
err = os.Rename(thisExecPath, oldExecPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// move the new exectuable in to become the new program
|
||||||
|
err = os.Rename(newExecPath, thisExecPath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// copy unsuccessful
|
||||||
|
errRecover = os.Rename(oldExecPath, thisExecPath)
|
||||||
|
} else {
|
||||||
|
// copy successful, remove the old binary
|
||||||
|
_ = os.Remove(oldExecPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SanityCheck() attempts to determine whether an in-place executable update could
|
||||||
|
// succeed by performing preliminary checks (to establish valid permissions, etc).
|
||||||
|
// This helps avoid downloading updates when we know the update can't be successfully
|
||||||
|
// applied later.
|
||||||
|
func SanityCheck() (err error) {
|
||||||
|
// get the path to the executable
|
||||||
|
thisExecPath, err := osext.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the directory the executable exists in
|
||||||
|
execDir := filepath.Dir(thisExecPath)
|
||||||
|
execName := filepath.Base(thisExecPath)
|
||||||
|
|
||||||
|
// attempt to open a file in the executable's directory
|
||||||
|
newExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.new", execName))
|
||||||
|
fp, err := os.OpenFile(newExecPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fp.Close()
|
||||||
|
|
||||||
|
_ = os.Remove(newExecPath)
|
||||||
|
return
|
||||||
|
}
|
1
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/.gitignore
generated
vendored
Normal file
1
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
test.*
|
22
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/License
generated
vendored
Normal file
22
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/License
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012 Keith Rarick
|
||||||
|
|
||||||
|
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.
|
7
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/Readme.md
generated
vendored
Normal file
7
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/Readme.md
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# binarydist
|
||||||
|
|
||||||
|
Package binarydist implements binary diff and patch as described on
|
||||||
|
<http://www.daemonology.net/bsdiff/>. It reads and writes files
|
||||||
|
compatible with the tools there.
|
||||||
|
|
||||||
|
Documentation at <http://go.pkgdoc.org/github.com/kr/binarydist>.
|
40
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/bzip2.go
generated
vendored
Normal file
40
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/bzip2.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type bzip2Writer struct {
|
||||||
|
c *exec.Cmd
|
||||||
|
w io.WriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w bzip2Writer) Write(b []byte) (int, error) {
|
||||||
|
return w.w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w bzip2Writer) Close() error {
|
||||||
|
if err := w.w.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return w.c.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package compress/bzip2 implements only decompression,
|
||||||
|
// so we'll fake it by running bzip2 in another process.
|
||||||
|
func newBzip2Writer(w io.Writer) (wc io.WriteCloser, err error) {
|
||||||
|
var bw bzip2Writer
|
||||||
|
bw.c = exec.Command("bzip2", "-c")
|
||||||
|
bw.c.Stdout = w
|
||||||
|
|
||||||
|
if bw.w, err = bw.c.StdinPipe(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = bw.c.Start(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bw, nil
|
||||||
|
}
|
93
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/common_test.go
generated
vendored
Normal file
93
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/common_test.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mustOpen(path string) *os.File {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustReadAll(r io.Reader) []byte {
|
||||||
|
b, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileCmp(a, b *os.File) int64 {
|
||||||
|
sa, err := a.Seek(0, 2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sb, err := b.Seek(0, 2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sa != sb {
|
||||||
|
return sa
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = a.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = b.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pa, err := ioutil.ReadAll(a)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pb, err := ioutil.ReadAll(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range pa {
|
||||||
|
if pa[i] != pb[i] {
|
||||||
|
return int64(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustWriteRandFile(path string, size int) *os.File {
|
||||||
|
p := make([]byte, size)
|
||||||
|
_, err := rand.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = f.Write(p)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = f.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
408
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/diff.go
generated
vendored
Normal file
408
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/diff.go
generated
vendored
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func swap(a []int, i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
|
func split(I, V []int, start, length, h int) {
|
||||||
|
var i, j, k, x, jj, kk int
|
||||||
|
|
||||||
|
if length < 16 {
|
||||||
|
for k = start; k < start+length; k += j {
|
||||||
|
j = 1
|
||||||
|
x = V[I[k]+h]
|
||||||
|
for i = 1; k+i < start+length; i++ {
|
||||||
|
if V[I[k+i]+h] < x {
|
||||||
|
x = V[I[k+i]+h]
|
||||||
|
j = 0
|
||||||
|
}
|
||||||
|
if V[I[k+i]+h] == x {
|
||||||
|
swap(I, k+i, k+j)
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i = 0; i < j; i++ {
|
||||||
|
V[I[k+i]] = k + j - 1
|
||||||
|
}
|
||||||
|
if j == 1 {
|
||||||
|
I[k] = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x = V[I[start+length/2]+h]
|
||||||
|
jj = 0
|
||||||
|
kk = 0
|
||||||
|
for i = start; i < start+length; i++ {
|
||||||
|
if V[I[i]+h] < x {
|
||||||
|
jj++
|
||||||
|
}
|
||||||
|
if V[I[i]+h] == x {
|
||||||
|
kk++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jj += start
|
||||||
|
kk += jj
|
||||||
|
|
||||||
|
i = start
|
||||||
|
j = 0
|
||||||
|
k = 0
|
||||||
|
for i < jj {
|
||||||
|
if V[I[i]+h] < x {
|
||||||
|
i++
|
||||||
|
} else if V[I[i]+h] == x {
|
||||||
|
swap(I, i, jj+j)
|
||||||
|
j++
|
||||||
|
} else {
|
||||||
|
swap(I, i, kk+k)
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for jj+j < kk {
|
||||||
|
if V[I[jj+j]+h] == x {
|
||||||
|
j++
|
||||||
|
} else {
|
||||||
|
swap(I, jj+j, kk+k)
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if jj > start {
|
||||||
|
split(I, V, start, jj-start, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 0; i < kk-jj; i++ {
|
||||||
|
V[I[jj+i]] = kk - 1
|
||||||
|
}
|
||||||
|
if jj == kk-1 {
|
||||||
|
I[jj] = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if start+length > kk {
|
||||||
|
split(I, V, kk, start+length-kk, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func qsufsort(obuf []byte) []int {
|
||||||
|
var buckets [256]int
|
||||||
|
var i, h int
|
||||||
|
I := make([]int, len(obuf)+1)
|
||||||
|
V := make([]int, len(obuf)+1)
|
||||||
|
|
||||||
|
for _, c := range obuf {
|
||||||
|
buckets[c]++
|
||||||
|
}
|
||||||
|
for i = 1; i < 256; i++ {
|
||||||
|
buckets[i] += buckets[i-1]
|
||||||
|
}
|
||||||
|
copy(buckets[1:], buckets[:])
|
||||||
|
buckets[0] = 0
|
||||||
|
|
||||||
|
for i, c := range obuf {
|
||||||
|
buckets[c]++
|
||||||
|
I[buckets[c]] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
I[0] = len(obuf)
|
||||||
|
for i, c := range obuf {
|
||||||
|
V[i] = buckets[c]
|
||||||
|
}
|
||||||
|
|
||||||
|
V[len(obuf)] = 0
|
||||||
|
for i = 1; i < 256; i++ {
|
||||||
|
if buckets[i] == buckets[i-1]+1 {
|
||||||
|
I[buckets[i]] = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
I[0] = -1
|
||||||
|
|
||||||
|
for h = 1; I[0] != -(len(obuf) + 1); h += h {
|
||||||
|
var n int
|
||||||
|
for i = 0; i < len(obuf)+1; {
|
||||||
|
if I[i] < 0 {
|
||||||
|
n -= I[i]
|
||||||
|
i -= I[i]
|
||||||
|
} else {
|
||||||
|
if n != 0 {
|
||||||
|
I[i-n] = -n
|
||||||
|
}
|
||||||
|
n = V[I[i]] + 1 - i
|
||||||
|
split(I, V, i, n, h)
|
||||||
|
i += n
|
||||||
|
n = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n != 0 {
|
||||||
|
I[i-n] = -n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 0; i < len(obuf)+1; i++ {
|
||||||
|
I[V[i]] = i
|
||||||
|
}
|
||||||
|
return I
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchlen(a, b []byte) (i int) {
|
||||||
|
for i < len(a) && i < len(b) && a[i] == b[i] {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func search(I []int, obuf, nbuf []byte, st, en int) (pos, n int) {
|
||||||
|
if en-st < 2 {
|
||||||
|
x := matchlen(obuf[I[st]:], nbuf)
|
||||||
|
y := matchlen(obuf[I[en]:], nbuf)
|
||||||
|
|
||||||
|
if x > y {
|
||||||
|
return I[st], x
|
||||||
|
} else {
|
||||||
|
return I[en], y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x := st + (en-st)/2
|
||||||
|
if bytes.Compare(obuf[I[x]:], nbuf) < 0 {
|
||||||
|
return search(I, obuf, nbuf, x, en)
|
||||||
|
} else {
|
||||||
|
return search(I, obuf, nbuf, st, x)
|
||||||
|
}
|
||||||
|
panic("unreached")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diff computes the difference between old and new, according to the bsdiff
|
||||||
|
// algorithm, and writes the result to patch.
|
||||||
|
func Diff(old, new io.Reader, patch io.Writer) error {
|
||||||
|
obuf, err := ioutil.ReadAll(old)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nbuf, err := ioutil.ReadAll(new)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pbuf, err := diffBytes(obuf, nbuf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = patch.Write(pbuf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func diffBytes(obuf, nbuf []byte) ([]byte, error) {
|
||||||
|
var patch seekBuffer
|
||||||
|
err := diff(obuf, nbuf, &patch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return patch.buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func diff(obuf, nbuf []byte, patch io.WriteSeeker) error {
|
||||||
|
var lenf int
|
||||||
|
I := qsufsort(obuf)
|
||||||
|
db := make([]byte, len(nbuf))
|
||||||
|
eb := make([]byte, len(nbuf))
|
||||||
|
var dblen, eblen int
|
||||||
|
|
||||||
|
var hdr header
|
||||||
|
hdr.Magic = magic
|
||||||
|
hdr.NewSize = int64(len(nbuf))
|
||||||
|
err := binary.Write(patch, signMagLittleEndian{}, &hdr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the differences, writing ctrl as we go
|
||||||
|
pfbz2, err := newBzip2Writer(patch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var scan, pos, length int
|
||||||
|
var lastscan, lastpos, lastoffset int
|
||||||
|
for scan < len(nbuf) {
|
||||||
|
var oldscore int
|
||||||
|
scan += length
|
||||||
|
for scsc := scan; scan < len(nbuf); scan++ {
|
||||||
|
pos, length = search(I, obuf, nbuf[scan:], 0, len(obuf))
|
||||||
|
|
||||||
|
for ; scsc < scan+length; scsc++ {
|
||||||
|
if scsc+lastoffset < len(obuf) &&
|
||||||
|
obuf[scsc+lastoffset] == nbuf[scsc] {
|
||||||
|
oldscore++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length == oldscore && length != 0) || length > oldscore+8 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if scan+lastoffset < len(obuf) && obuf[scan+lastoffset] == nbuf[scan] {
|
||||||
|
oldscore--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if length != oldscore || scan == len(nbuf) {
|
||||||
|
var s, Sf int
|
||||||
|
lenf = 0
|
||||||
|
for i := 0; lastscan+i < scan && lastpos+i < len(obuf); {
|
||||||
|
if obuf[lastpos+i] == nbuf[lastscan+i] {
|
||||||
|
s++
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
if s*2-i > Sf*2-lenf {
|
||||||
|
Sf = s
|
||||||
|
lenf = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lenb := 0
|
||||||
|
if scan < len(nbuf) {
|
||||||
|
var s, Sb int
|
||||||
|
for i := 1; (scan >= lastscan+i) && (pos >= i); i++ {
|
||||||
|
if obuf[pos-i] == nbuf[scan-i] {
|
||||||
|
s++
|
||||||
|
}
|
||||||
|
if s*2-i > Sb*2-lenb {
|
||||||
|
Sb = s
|
||||||
|
lenb = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastscan+lenf > scan-lenb {
|
||||||
|
overlap := (lastscan + lenf) - (scan - lenb)
|
||||||
|
s := 0
|
||||||
|
Ss := 0
|
||||||
|
lens := 0
|
||||||
|
for i := 0; i < overlap; i++ {
|
||||||
|
if nbuf[lastscan+lenf-overlap+i] == obuf[lastpos+lenf-overlap+i] {
|
||||||
|
s++
|
||||||
|
}
|
||||||
|
if nbuf[scan-lenb+i] == obuf[pos-lenb+i] {
|
||||||
|
s--
|
||||||
|
}
|
||||||
|
if s > Ss {
|
||||||
|
Ss = s
|
||||||
|
lens = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lenf += lens - overlap
|
||||||
|
lenb -= lens
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < lenf; i++ {
|
||||||
|
db[dblen+i] = nbuf[lastscan+i] - obuf[lastpos+i]
|
||||||
|
}
|
||||||
|
for i := 0; i < (scan-lenb)-(lastscan+lenf); i++ {
|
||||||
|
eb[eblen+i] = nbuf[lastscan+lenf+i]
|
||||||
|
}
|
||||||
|
|
||||||
|
dblen += lenf
|
||||||
|
eblen += (scan - lenb) - (lastscan + lenf)
|
||||||
|
|
||||||
|
err = binary.Write(pfbz2, signMagLittleEndian{}, int64(lenf))
|
||||||
|
if err != nil {
|
||||||
|
pfbz2.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
val := (scan - lenb) - (lastscan + lenf)
|
||||||
|
err = binary.Write(pfbz2, signMagLittleEndian{}, int64(val))
|
||||||
|
if err != nil {
|
||||||
|
pfbz2.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
val = (pos - lenb) - (lastpos + lenf)
|
||||||
|
err = binary.Write(pfbz2, signMagLittleEndian{}, int64(val))
|
||||||
|
if err != nil {
|
||||||
|
pfbz2.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
lastscan = scan - lenb
|
||||||
|
lastpos = pos - lenb
|
||||||
|
lastoffset = pos - scan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = pfbz2.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute size of compressed ctrl data
|
||||||
|
l64, err := patch.Seek(0, 1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hdr.CtrlLen = int64(l64 - 32)
|
||||||
|
|
||||||
|
// Write compressed diff data
|
||||||
|
pfbz2, err = newBzip2Writer(patch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
n, err := pfbz2.Write(db[:dblen])
|
||||||
|
if err != nil {
|
||||||
|
pfbz2.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n != dblen {
|
||||||
|
pfbz2.Close()
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
err = pfbz2.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute size of compressed diff data
|
||||||
|
n64, err := patch.Seek(0, 1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hdr.DiffLen = n64 - l64
|
||||||
|
|
||||||
|
// Write compressed extra data
|
||||||
|
pfbz2, err = newBzip2Writer(patch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
n, err = pfbz2.Write(eb[:eblen])
|
||||||
|
if err != nil {
|
||||||
|
pfbz2.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n != eblen {
|
||||||
|
pfbz2.Close()
|
||||||
|
return io.ErrShortWrite
|
||||||
|
}
|
||||||
|
err = pfbz2.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek to the beginning, write the header, and close the file
|
||||||
|
_, err = patch.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = binary.Write(patch, signMagLittleEndian{}, &hdr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
67
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/diff_test.go
generated
vendored
Normal file
67
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/diff_test.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var diffT = []struct {
|
||||||
|
old *os.File
|
||||||
|
new *os.File
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
old: mustWriteRandFile("test.old", 1e3),
|
||||||
|
new: mustWriteRandFile("test.new", 1e3),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: mustOpen("testdata/sample.old"),
|
||||||
|
new: mustOpen("testdata/sample.new"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDiff(t *testing.T) {
|
||||||
|
for _, s := range diffT {
|
||||||
|
got, err := ioutil.TempFile("/tmp", "bspatch.")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.Remove(got.Name())
|
||||||
|
|
||||||
|
exp, err := ioutil.TempFile("/tmp", "bspatch.")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("bsdiff", s.old.Name(), s.new.Name(), exp.Name())
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
err = cmd.Run()
|
||||||
|
os.Remove(exp.Name())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Diff(s.old, s.new, got)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = got.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
gotBuf := mustReadAll(got)
|
||||||
|
expBuf := mustReadAll(exp)
|
||||||
|
|
||||||
|
if !bytes.Equal(gotBuf, expBuf) {
|
||||||
|
t.Fail()
|
||||||
|
t.Logf("diff %s %s", s.old.Name(), s.new.Name())
|
||||||
|
t.Logf("%s: len(got) = %d", got.Name(), len(gotBuf))
|
||||||
|
t.Logf("%s: len(exp) = %d", exp.Name(), len(expBuf))
|
||||||
|
i := matchlen(gotBuf, expBuf)
|
||||||
|
t.Logf("produced different output at pos %d; %d != %d", i, gotBuf[i], expBuf[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/doc.go
generated
vendored
Normal file
24
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/doc.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Package binarydist implements binary diff and patch as described on
|
||||||
|
// http://www.daemonology.net/bsdiff/. It reads and writes files
|
||||||
|
// compatible with the tools there.
|
||||||
|
package binarydist
|
||||||
|
|
||||||
|
var magic = [8]byte{'B', 'S', 'D', 'I', 'F', 'F', '4', '0'}
|
||||||
|
|
||||||
|
// File format:
|
||||||
|
// 0 8 "BSDIFF40"
|
||||||
|
// 8 8 X
|
||||||
|
// 16 8 Y
|
||||||
|
// 24 8 sizeof(newfile)
|
||||||
|
// 32 X bzip2(control block)
|
||||||
|
// 32+X Y bzip2(diff block)
|
||||||
|
// 32+X+Y ??? bzip2(extra block)
|
||||||
|
// with control block a set of triples (x,y,z) meaning "add x bytes
|
||||||
|
// from oldfile to x bytes from the diff block; copy y bytes from the
|
||||||
|
// extra block; seek forwards in oldfile by z bytes".
|
||||||
|
type header struct {
|
||||||
|
Magic [8]byte
|
||||||
|
CtrlLen int64
|
||||||
|
DiffLen int64
|
||||||
|
NewSize int64
|
||||||
|
}
|
53
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/encoding.go
generated
vendored
Normal file
53
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/encoding.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
// SignMagLittleEndian is the numeric encoding used by the bsdiff tools.
|
||||||
|
// It implements binary.ByteOrder using a sign-magnitude format
|
||||||
|
// and little-endian byte order. Only methods Uint64 and String
|
||||||
|
// have been written; the rest panic.
|
||||||
|
type signMagLittleEndian struct{}
|
||||||
|
|
||||||
|
func (signMagLittleEndian) Uint16(b []byte) uint16 { panic("unimplemented") }
|
||||||
|
|
||||||
|
func (signMagLittleEndian) PutUint16(b []byte, v uint16) { panic("unimplemented") }
|
||||||
|
|
||||||
|
func (signMagLittleEndian) Uint32(b []byte) uint32 { panic("unimplemented") }
|
||||||
|
|
||||||
|
func (signMagLittleEndian) PutUint32(b []byte, v uint32) { panic("unimplemented") }
|
||||||
|
|
||||||
|
func (signMagLittleEndian) Uint64(b []byte) uint64 {
|
||||||
|
y := int64(b[0]) |
|
||||||
|
int64(b[1])<<8 |
|
||||||
|
int64(b[2])<<16 |
|
||||||
|
int64(b[3])<<24 |
|
||||||
|
int64(b[4])<<32 |
|
||||||
|
int64(b[5])<<40 |
|
||||||
|
int64(b[6])<<48 |
|
||||||
|
int64(b[7]&0x7f)<<56
|
||||||
|
|
||||||
|
if b[7]&0x80 != 0 {
|
||||||
|
y = -y
|
||||||
|
}
|
||||||
|
return uint64(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (signMagLittleEndian) PutUint64(b []byte, v uint64) {
|
||||||
|
x := int64(v)
|
||||||
|
neg := x < 0
|
||||||
|
if neg {
|
||||||
|
x = -x
|
||||||
|
}
|
||||||
|
|
||||||
|
b[0] = byte(x)
|
||||||
|
b[1] = byte(x >> 8)
|
||||||
|
b[2] = byte(x >> 16)
|
||||||
|
b[3] = byte(x >> 24)
|
||||||
|
b[4] = byte(x >> 32)
|
||||||
|
b[5] = byte(x >> 40)
|
||||||
|
b[6] = byte(x >> 48)
|
||||||
|
b[7] = byte(x >> 56)
|
||||||
|
if neg {
|
||||||
|
b[7] |= 0x80
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (signMagLittleEndian) String() string { return "signMagLittleEndian" }
|
109
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/patch.go
generated
vendored
Normal file
109
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/patch.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/bzip2"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrCorrupt = errors.New("corrupt patch")
|
||||||
|
|
||||||
|
// Patch applies patch to old, according to the bspatch algorithm,
|
||||||
|
// and writes the result to new.
|
||||||
|
func Patch(old io.Reader, new io.Writer, patch io.Reader) error {
|
||||||
|
var hdr header
|
||||||
|
err := binary.Read(patch, signMagLittleEndian{}, &hdr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if hdr.Magic != magic {
|
||||||
|
return ErrCorrupt
|
||||||
|
}
|
||||||
|
if hdr.CtrlLen < 0 || hdr.DiffLen < 0 || hdr.NewSize < 0 {
|
||||||
|
return ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrlbuf := make([]byte, hdr.CtrlLen)
|
||||||
|
_, err = io.ReadFull(patch, ctrlbuf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cpfbz2 := bzip2.NewReader(bytes.NewReader(ctrlbuf))
|
||||||
|
|
||||||
|
diffbuf := make([]byte, hdr.DiffLen)
|
||||||
|
_, err = io.ReadFull(patch, diffbuf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dpfbz2 := bzip2.NewReader(bytes.NewReader(diffbuf))
|
||||||
|
|
||||||
|
// The entire rest of the file is the extra block.
|
||||||
|
epfbz2 := bzip2.NewReader(patch)
|
||||||
|
|
||||||
|
obuf, err := ioutil.ReadAll(old)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nbuf := make([]byte, hdr.NewSize)
|
||||||
|
|
||||||
|
var oldpos, newpos int64
|
||||||
|
for newpos < hdr.NewSize {
|
||||||
|
var ctrl struct{ Add, Copy, Seek int64 }
|
||||||
|
err = binary.Read(cpfbz2, signMagLittleEndian{}, &ctrl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity-check
|
||||||
|
if newpos+ctrl.Add > hdr.NewSize {
|
||||||
|
return ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read diff string
|
||||||
|
_, err = io.ReadFull(dpfbz2, nbuf[newpos:newpos+ctrl.Add])
|
||||||
|
if err != nil {
|
||||||
|
return ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add old data to diff string
|
||||||
|
for i := int64(0); i < ctrl.Add; i++ {
|
||||||
|
if oldpos+i >= 0 && oldpos+i < int64(len(obuf)) {
|
||||||
|
nbuf[newpos+i] += obuf[oldpos+i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust pointers
|
||||||
|
newpos += ctrl.Add
|
||||||
|
oldpos += ctrl.Add
|
||||||
|
|
||||||
|
// Sanity-check
|
||||||
|
if newpos+ctrl.Copy > hdr.NewSize {
|
||||||
|
return ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read extra string
|
||||||
|
_, err = io.ReadFull(epfbz2, nbuf[newpos:newpos+ctrl.Copy])
|
||||||
|
if err != nil {
|
||||||
|
return ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust pointers
|
||||||
|
newpos += ctrl.Copy
|
||||||
|
oldpos += ctrl.Seek
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the new file
|
||||||
|
for len(nbuf) > 0 {
|
||||||
|
n, err := new.Write(nbuf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nbuf = nbuf[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
62
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/patch_test.go
generated
vendored
Normal file
62
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/patch_test.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPatch(t *testing.T) {
|
||||||
|
mustWriteRandFile("test.old", 1e3)
|
||||||
|
mustWriteRandFile("test.new", 1e3)
|
||||||
|
|
||||||
|
got, err := ioutil.TempFile("/tmp", "bspatch.")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.Remove(got.Name())
|
||||||
|
|
||||||
|
err = exec.Command("bsdiff", "test.old", "test.new", "test.patch").Run()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Patch(mustOpen("test.old"), got, mustOpen("test.patch"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ref, err := got.Seek(0, 2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("got %d bytes", ref)
|
||||||
|
if n := fileCmp(got, mustOpen("test.new")); n > -1 {
|
||||||
|
t.Fatalf("produced different output at pos %d", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPatchHk(t *testing.T) {
|
||||||
|
got, err := ioutil.TempFile("/tmp", "bspatch.")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.Remove(got.Name())
|
||||||
|
|
||||||
|
err = Patch(mustOpen("testdata/sample.old"), got, mustOpen("testdata/sample.patch"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ref, err := got.Seek(0, 2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("got %d bytes", ref)
|
||||||
|
if n := fileCmp(got, mustOpen("testdata/sample.new")); n > -1 {
|
||||||
|
t.Fatalf("produced different output at pos %d", n)
|
||||||
|
}
|
||||||
|
}
|
43
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/seek.go
generated
vendored
Normal file
43
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/seek.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type seekBuffer struct {
|
||||||
|
buf []byte
|
||||||
|
pos int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *seekBuffer) Write(p []byte) (n int, err error) {
|
||||||
|
n = copy(b.buf[b.pos:], p)
|
||||||
|
if n == len(p) {
|
||||||
|
b.pos += n
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
b.buf = append(b.buf, p[n:]...)
|
||||||
|
b.pos += len(p)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *seekBuffer) Seek(offset int64, whence int) (ret int64, err error) {
|
||||||
|
var abs int64
|
||||||
|
switch whence {
|
||||||
|
case 0:
|
||||||
|
abs = offset
|
||||||
|
case 1:
|
||||||
|
abs = int64(b.pos) + offset
|
||||||
|
case 2:
|
||||||
|
abs = int64(len(b.buf)) + offset
|
||||||
|
default:
|
||||||
|
return 0, errors.New("binarydist: invalid whence")
|
||||||
|
}
|
||||||
|
if abs < 0 {
|
||||||
|
return 0, errors.New("binarydist: negative position")
|
||||||
|
}
|
||||||
|
if abs >= 1<<31 {
|
||||||
|
return 0, errors.New("binarydist: position out of range")
|
||||||
|
}
|
||||||
|
b.pos = int(abs)
|
||||||
|
return abs, nil
|
||||||
|
}
|
33
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/sort_test.go
generated
vendored
Normal file
33
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/sort_test.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package binarydist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sortT = [][]byte{
|
||||||
|
mustRandBytes(1000),
|
||||||
|
mustReadAll(mustOpen("test.old")),
|
||||||
|
[]byte("abcdefabcdef"),
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQsufsort(t *testing.T) {
|
||||||
|
for _, s := range sortT {
|
||||||
|
I := qsufsort(s)
|
||||||
|
for i := 1; i < len(I); i++ {
|
||||||
|
if bytes.Compare(s[I[i-1]:], s[I[i]:]) > 0 {
|
||||||
|
t.Fatalf("unsorted at %d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustRandBytes(n int) []byte {
|
||||||
|
b := make([]byte, n)
|
||||||
|
_, err := rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
BIN
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.new
generated
vendored
Normal file
BIN
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.new
generated
vendored
Normal file
Binary file not shown.
BIN
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.old
generated
vendored
Normal file
BIN
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.old
generated
vendored
Normal file
Binary file not shown.
BIN
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.patch
generated
vendored
Normal file
BIN
selfupdate/Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.patch
generated
vendored
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user