mirror of
https://github.com/wahyd4/forego.git
synced 2026-08-09 05:07:05 +10:00
Fix up release process (#82)
* remove godeps * remove dist for now * add colortext * godeps * vendor * remove unneeded fixture * fix vendor * switch to circle * dont run example during tests * no circle.yml * circle release * fix equinox download * equinox release * fix shell * fix shell * fix shell * fix shell * release from circle-release to test * release from circle-release * release from circle-release * download key * remove update for now * back to var for key * and back to url * clean up makefile * date versions * badge * only master branch releases
This commit is contained in:
-12
@@ -1,12 +0,0 @@
|
||||
sudo: false
|
||||
|
||||
language: go
|
||||
go:
|
||||
- 1.6
|
||||
|
||||
env:
|
||||
global:
|
||||
- PATH=$HOME/gopath/bin:$PATH
|
||||
|
||||
script:
|
||||
- make test
|
||||
Generated
+5
-29
@@ -1,38 +1,14 @@
|
||||
{
|
||||
"ImportPath": "github.com/ddollar/forego",
|
||||
"GoVersion": "go1.6",
|
||||
"GodepVersion": "v60",
|
||||
"GodepVersion": "v61",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "bitbucket.org/kardianos/osext",
|
||||
"Comment": "null-13",
|
||||
"Rev": "5d3ddcf53a508cc2f7404eaebf546ef2cb5cdb6e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/daviddengcn/go-colortext",
|
||||
"Rev": "8386cd264821f95e9bc124a0a339560d1a899cad"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ddollar/dist",
|
||||
"Comment": "v0.2.0-2-gf559761",
|
||||
"Rev": "f559761fd36c52dc90d7d2c0bc537e6d96b0edc5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ddollar/go-update",
|
||||
"Rev": "4733469d35539f410e0e84552944fa0df7e72c98"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/kr/binarydist",
|
||||
"Rev": "9955b0ab8708602d411341e55fffd7e0700f86bd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/kr/pretty",
|
||||
"Comment": "go.weekly.2011-12-22-18-gbc9499c",
|
||||
"Rev": "bc9499caa0f45ee5edb2f0209fbd61fbf3d9018f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/kr/text",
|
||||
"Rev": "6807e777504f54ad073ecef66747de158294b639"
|
||||
"Rev": "3b18c8575a432453d41fdafb340099fff5bba2f7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/subosito/gotenv",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
BIN = forego
|
||||
SRC = $(shell ls *.go)
|
||||
|
||||
.PHONY: all build clean install test lint
|
||||
.PHONY: all build clean lint release test
|
||||
|
||||
all: build
|
||||
|
||||
@@ -10,15 +10,16 @@ build: $(BIN)
|
||||
clean:
|
||||
rm -f $(BIN)
|
||||
|
||||
install: forego
|
||||
cp $< ${GOPATH}/bin/
|
||||
|
||||
lint: $(SRC)
|
||||
go fmt
|
||||
|
||||
release:
|
||||
curl -s https://bin.equinox.io/a/gSD5wcgebYp/release-tool-1.8.7-linux-amd64.tar.gz | sudo tar xz -C /usr/local/bin
|
||||
curl -s $(EQUINOX_KEY_URL) -o /tmp/equinox.key
|
||||
equinox release --version=$(shell date +%Y%m%d%H%M%S) --platforms="darwin_386 darwin_amd64 linux_386 linux_amd64 windows_386 windows_amd64" --channel=stable --signing-key=/tmp/equinox.key --app=$(EQUINOX_APP) --token=$(EQUINOX_TOKEN)
|
||||
|
||||
test: lint build
|
||||
go test ./... -cover
|
||||
cd eg && ../forego start
|
||||
go test -v -race -cover ./...
|
||||
|
||||
$(BIN): $(SRC)
|
||||
go build -o $@
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
## forego
|
||||
|
||||
<a href="https://travis-ci.org/ddollar/forego">
|
||||
<img align="right" src="https://travis-ci.org/ddollar/forego.svg?branch=master">
|
||||
<a href="https://circleci.com/gh/ddollar/forego">
|
||||
<img align="right" src="https://circleci.com/gh/ddollar/forego.svg?style=svg">
|
||||
</a>
|
||||
|
||||
Foreman in Go.
|
||||
|
||||
### Installation
|
||||
|
||||
##### OS X (Homebrew)
|
||||
##### Downloads
|
||||
|
||||
brew install forego
|
||||
Forego uses [Equinox](https://equinox.io) to automatically build binaries for various platforms.
|
||||
|
||||
##### Precompiled Binaries
|
||||
|
||||
* [Linux](https://godist.herokuapp.com/projects/ddollar/forego/releases/current/linux-amd64/forego)
|
||||
* [OSX](https://godist.herokuapp.com/projects/ddollar/forego/releases/current/darwin-amd64/forego)
|
||||
* [Windows](https://godist.herokuapp.com/projects/ddollar/forego/releases/current/windows-amd64/forego.exe)
|
||||
See the [downloads page](https://dl.equinox.io/convox/forego/stable).
|
||||
|
||||
##### Compile from Source
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
test:
|
||||
override:
|
||||
- make test
|
||||
|
||||
deployment:
|
||||
master:
|
||||
branch: master
|
||||
commands:
|
||||
- make release
|
||||
@@ -1,8 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ddollar/forego/Godeps/_workspace/src/github.com/subosito/gotenv"
|
||||
"os"
|
||||
|
||||
"github.com/subosito/gotenv"
|
||||
)
|
||||
|
||||
type Config map[string]string
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
writey1: writey
|
||||
writey2: writey
|
||||
@@ -1,42 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
print("Foo")
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
println("Bar")
|
||||
|
||||
print("Baz")
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
println("Qux")
|
||||
|
||||
fmt.Fprintln(os.Stdout, "This is on \x1b[32mstdout")
|
||||
|
||||
os.Stdout.Close()
|
||||
|
||||
s := rand.Intn(3) + 1
|
||||
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case <-c:
|
||||
if rand.Intn(4) == 1 {
|
||||
println("IGNORING EXIT")
|
||||
time.Sleep(100 * time.Second)
|
||||
}
|
||||
println("Got SIGTERM")
|
||||
case <-time.After(time.Duration(s) * time.Second):
|
||||
println("Timed out")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import "os"
|
||||
var commands = []*Command{
|
||||
cmdStart,
|
||||
cmdRun,
|
||||
cmdUpdate,
|
||||
// cmdUpdate,
|
||||
cmdVersion,
|
||||
cmdHelp,
|
||||
}
|
||||
|
||||
@@ -4,10 +4,11 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/daviddengcn/go-colortext"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
ct "github.com/daviddengcn/go-colortext"
|
||||
)
|
||||
|
||||
type OutletFactory struct {
|
||||
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
_ "github.com/kr/pretty"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ddollar/dist"
|
||||
)
|
||||
|
||||
var cmdUpdate = &Command{
|
||||
Run: runUpdate,
|
||||
Usage: "update",
|
||||
@@ -22,15 +17,15 @@ func init() {
|
||||
}
|
||||
|
||||
func runUpdate(cmd *Command, args []string) {
|
||||
if Version == "dev" {
|
||||
fmt.Println("ERROR: can't update dev version")
|
||||
return
|
||||
}
|
||||
d := dist.NewDist("ddollar/forego", Version)
|
||||
to, err := d.Update()
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %s\n", err)
|
||||
} else {
|
||||
fmt.Printf("updated to %s\n", to)
|
||||
}
|
||||
// if Version == "dev" {
|
||||
// fmt.Println("ERROR: can't update dev version")
|
||||
// return
|
||||
// }
|
||||
// d := dist.NewDist("ddollar/forego", Version)
|
||||
// to, err := d.Update()
|
||||
// if err != nil {
|
||||
// fmt.Printf("ERROR: %s\n", err)
|
||||
// } else {
|
||||
// fmt.Printf("updated to %s\n", to)
|
||||
// }
|
||||
}
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
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
@@ -1,32 +0,0 @@
|
||||
// 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
@@ -1,20 +0,0 @@
|
||||
// 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
@@ -1,25 +0,0 @@
|
||||
// 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
@@ -1,79 +0,0 @@
|
||||
// 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
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
// 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
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2015, David Deng
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of go-colortext nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+9
-5
@@ -1,17 +1,21 @@
|
||||
go-colortext package
|
||||
go-colortext package [](http://go-search.org/view?id=github.com%2Fdaviddengcn%2Fgo-colortext)
|
||||
====================
|
||||
|
||||
This is a package to change the color of the text and background in the console, working both in windows and other systems.
|
||||
This is a package to change the color of the text and background in the console, working both under Windows and other systems.
|
||||
|
||||
Under windows, the console APIs are used, and otherwise ANSI text is used.
|
||||
Under Windows, the console APIs are used. Otherwise, ANSI texts are output.
|
||||
|
||||
Docs: http://godoc.org/github.com/daviddengcn/go-colortext ([packages that import ct](http://go-search.org/view?id=github.com%2fdaviddengcn%2fgo-colortext))
|
||||
|
||||
Usage:
|
||||
```go
|
||||
Foreground(Green, false)
|
||||
fmt.Println("Green text starts here...")
|
||||
ChangeColor(Red, true, White, false)
|
||||
fmt.Println(...)
|
||||
ChangeColor(Green, false, None, false)
|
||||
fmt.Println(...)
|
||||
ResetColor()
|
||||
```
|
||||
|
||||
LICENSE
|
||||
=======
|
||||
BSD license
|
||||
|
||||
+10
@@ -35,3 +35,13 @@ func ResetColor() {
|
||||
func ChangeColor(fg Color, fgBright bool, bg Color, bgBright bool) {
|
||||
changeColor(fg, fgBright, bg, bgBright)
|
||||
}
|
||||
|
||||
// Foreground changes the foreground color.
|
||||
func Foreground(cl Color, bright bool) {
|
||||
ChangeColor(cl, bright, None, false)
|
||||
}
|
||||
|
||||
// Background changes the background color.
|
||||
func Background(cl Color, bright bool) {
|
||||
ChangeColor(None, false, cl, bright)
|
||||
}
|
||||
|
||||
-187
@@ -1,187 +0,0 @@
|
||||
// Handle updating new releases from a godist server
|
||||
package dist
|
||||
|
||||
import (
|
||||
"bitbucket.org/kardianos/osext"
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ddollar/go-update"
|
||||
"github.com/kr/binarydist"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var DigicertHighAssuranceCert = `-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
||||
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
||||
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
||||
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
||||
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
||||
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
||||
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
||||
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
||||
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
||||
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
||||
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
||||
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
||||
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
||||
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
||||
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
+OkuE6N36B9K
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
type Dist struct {
|
||||
Host string
|
||||
Name string
|
||||
Project string
|
||||
Version string
|
||||
}
|
||||
|
||||
type Release struct {
|
||||
Version string
|
||||
Url string
|
||||
}
|
||||
|
||||
// Initialize a new godist client, speciying a project name
|
||||
// e.g. "ddollar/forego"
|
||||
func NewDist(project string, version string) (d *Dist) {
|
||||
d = new(Dist)
|
||||
d.Host = "https://godist.herokuapp.com"
|
||||
d.Name = strings.Split(project, "/")[1]
|
||||
d.Project = project
|
||||
d.Version = version
|
||||
return
|
||||
}
|
||||
|
||||
// Update the currently running binary to the latest version
|
||||
func (d *Dist) Update() (to string, err error) {
|
||||
releases, err := d.fetchReleases()
|
||||
if len(releases) < 1 {
|
||||
return "", errors.New("no releases")
|
||||
}
|
||||
to = releases[0].Version
|
||||
return to, d.UpdateTo(to)
|
||||
}
|
||||
|
||||
// Update the currently running binary to a specific version
|
||||
func (d *Dist) UpdateTo(to string) (err error) {
|
||||
if d.Version == to {
|
||||
return errors.New("nothing to update")
|
||||
}
|
||||
binary, _ := osext.Executable()
|
||||
reader, err := os.Open(binary)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
url := fmt.Sprintf("%s/projects/%s/diff/%s/%s/%s-%s", d.Host, d.Project, d.Version, to, runtime.GOOS, runtime.GOARCH)
|
||||
patch, err := d.httpGet(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writer := new(bytes.Buffer)
|
||||
err = binarydist.Patch(reader, writer, bytes.NewReader(patch))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reader.Close()
|
||||
err, _ = update.FromStream(writer)
|
||||
return
|
||||
}
|
||||
|
||||
// Update to a specific version regardless of the starting version
|
||||
func (d *Dist) FullUpdate(to string) (err error) {
|
||||
url := fmt.Sprintf("%s/projects/%s/releases/%s/%s-%s/%s", d.Host, d.Project, to, runtime.GOOS, runtime.GOARCH, d.Name)
|
||||
reader, err := d.httpGet(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err, _ = update.FromStream(bytes.NewReader(reader))
|
||||
return
|
||||
}
|
||||
|
||||
func (d *Dist) fetchReleases() (releases []Release, err error) {
|
||||
body, err := d.httpGet(fmt.Sprintf("%s/projects/%s/releases/%s-%s", d.Host, d.Project, runtime.GOOS, runtime.GOARCH))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(body, &releases)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *Dist) httpClient() (client *http.Client) {
|
||||
chain := d.rootCertificate()
|
||||
config := tls.Config{}
|
||||
config.RootCAs = x509.NewCertPool()
|
||||
for _, cert := range chain.Certificate {
|
||||
x509Cert, err := x509.ParseCertificate(cert)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config.RootCAs.AddCert(x509Cert)
|
||||
}
|
||||
config.BuildNameToCertificate()
|
||||
tr := http.Transport{TLSClientConfig: &config}
|
||||
client = &http.Client{Transport: &tr}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *Dist) httpGet(url string) ([]byte, error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("User-Agent", fmt.Sprintf("%s/%s dist/%s (%s-%s)", d.Name, d.Version, Version, runtime.GOOS, runtime.GOARCH))
|
||||
res, err := d.httpClient().Do(req)
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, errors.New(string(body))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.ReadAll(res.Body)
|
||||
}
|
||||
|
||||
func (d *Dist) updateFromUrl(url string) (err error) {
|
||||
client := d.httpClient()
|
||||
res, err := client.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
err, _ = update.FromStream(res.Body)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *Dist) rootCertificate() (cert tls.Certificate) {
|
||||
certPEMBlock := []byte(DigicertHighAssuranceCert)
|
||||
var certDERBlock *pem.Block
|
||||
for {
|
||||
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
|
||||
if certDERBlock == nil {
|
||||
break
|
||||
}
|
||||
if certDERBlock.Type == "CERTIFICATE" {
|
||||
cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
package dist
|
||||
|
||||
const (
|
||||
Version = "0.2.0"
|
||||
)
|
||||
-137
@@ -1,137 +0,0 @@
|
||||
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
@@ -1,455 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"bitbucket.org/kardianos/osext"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"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
@@ -1 +0,0 @@
|
||||
test.*
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
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
@@ -1,7 +0,0 @@
|
||||
# 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
@@ -1,40 +0,0 @@
|
||||
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
|
||||
}
|
||||
-408
@@ -1,408 +0,0 @@
|
||||
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
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
// 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
@@ -1,53 +0,0 @@
|
||||
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
@@ -1,109 +0,0 @@
|
||||
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
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
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
|
||||
}
|
||||
Generated
-4
@@ -1,4 +0,0 @@
|
||||
[568].out
|
||||
_go*
|
||||
_test*
|
||||
_obj
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
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.
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
package pretty
|
||||
|
||||
import "github.com/kr/pretty"
|
||||
|
||||
Package pretty provides pretty-printing for Go values.
|
||||
|
||||
Documentation
|
||||
|
||||
http://godoc.org/github.com/kr/pretty
|
||||
-148
@@ -1,148 +0,0 @@
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type sbuf []string
|
||||
|
||||
func (s *sbuf) Write(b []byte) (int, error) {
|
||||
*s = append(*s, string(b))
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Diff returns a slice where each element describes
|
||||
// a difference between a and b.
|
||||
func Diff(a, b interface{}) (desc []string) {
|
||||
Fdiff((*sbuf)(&desc), a, b)
|
||||
return desc
|
||||
}
|
||||
|
||||
// Fdiff writes to w a description of the differences between a and b.
|
||||
func Fdiff(w io.Writer, a, b interface{}) {
|
||||
diffWriter{w: w}.diff(reflect.ValueOf(a), reflect.ValueOf(b))
|
||||
}
|
||||
|
||||
type diffWriter struct {
|
||||
w io.Writer
|
||||
l string // label
|
||||
}
|
||||
|
||||
func (w diffWriter) printf(f string, a ...interface{}) {
|
||||
var l string
|
||||
if w.l != "" {
|
||||
l = w.l + ": "
|
||||
}
|
||||
fmt.Fprintf(w.w, l+f, a...)
|
||||
}
|
||||
|
||||
func (w diffWriter) diff(av, bv reflect.Value) {
|
||||
if !av.IsValid() && bv.IsValid() {
|
||||
w.printf("nil != %#v", bv.Interface())
|
||||
return
|
||||
}
|
||||
if av.IsValid() && !bv.IsValid() {
|
||||
w.printf("%#v != nil", av.Interface())
|
||||
return
|
||||
}
|
||||
if !av.IsValid() && !bv.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
at := av.Type()
|
||||
bt := bv.Type()
|
||||
if at != bt {
|
||||
w.printf("%v != %v", at, bt)
|
||||
return
|
||||
}
|
||||
|
||||
// numeric types, including bool
|
||||
if at.Kind() < reflect.Array {
|
||||
a, b := av.Interface(), bv.Interface()
|
||||
if a != b {
|
||||
w.printf("%#v != %#v", a, b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch at.Kind() {
|
||||
case reflect.String:
|
||||
a, b := av.Interface(), bv.Interface()
|
||||
if a != b {
|
||||
w.printf("%q != %q", a, b)
|
||||
}
|
||||
case reflect.Ptr:
|
||||
switch {
|
||||
case av.IsNil() && !bv.IsNil():
|
||||
w.printf("nil != %v", bv.Interface())
|
||||
case !av.IsNil() && bv.IsNil():
|
||||
w.printf("%v != nil", av.Interface())
|
||||
case !av.IsNil() && !bv.IsNil():
|
||||
w.diff(av.Elem(), bv.Elem())
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i := 0; i < av.NumField(); i++ {
|
||||
w.relabel(at.Field(i).Name).diff(av.Field(i), bv.Field(i))
|
||||
}
|
||||
case reflect.Map:
|
||||
ak, both, bk := keyDiff(av.MapKeys(), bv.MapKeys())
|
||||
for _, k := range ak {
|
||||
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
|
||||
w.printf("%q != (missing)", av.MapIndex(k))
|
||||
}
|
||||
for _, k := range both {
|
||||
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
|
||||
w.diff(av.MapIndex(k), bv.MapIndex(k))
|
||||
}
|
||||
for _, k := range bk {
|
||||
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
|
||||
w.printf("(missing) != %q", bv.MapIndex(k))
|
||||
}
|
||||
case reflect.Interface:
|
||||
w.diff(reflect.ValueOf(av.Interface()), reflect.ValueOf(bv.Interface()))
|
||||
default:
|
||||
if !reflect.DeepEqual(av.Interface(), bv.Interface()) {
|
||||
w.printf("%# v != %# v", Formatter(av.Interface()), Formatter(bv.Interface()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d diffWriter) relabel(name string) (d1 diffWriter) {
|
||||
d1 = d
|
||||
if d.l != "" && name[0] != '[' {
|
||||
d1.l += "."
|
||||
}
|
||||
d1.l += name
|
||||
return d1
|
||||
}
|
||||
|
||||
func keyDiff(a, b []reflect.Value) (ak, both, bk []reflect.Value) {
|
||||
for _, av := range a {
|
||||
inBoth := false
|
||||
for _, bv := range b {
|
||||
if reflect.DeepEqual(av.Interface(), bv.Interface()) {
|
||||
inBoth = true
|
||||
both = append(both, av)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !inBoth {
|
||||
ak = append(ak, av)
|
||||
}
|
||||
}
|
||||
for _, bv := range b {
|
||||
inBoth := false
|
||||
for _, av := range a {
|
||||
if reflect.DeepEqual(av.Interface(), bv.Interface()) {
|
||||
inBoth = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !inBoth {
|
||||
bk = append(bk, bv)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
-300
@@ -1,300 +0,0 @@
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kr/text"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
const (
|
||||
limit = 50
|
||||
)
|
||||
|
||||
type formatter struct {
|
||||
x interface{}
|
||||
force bool
|
||||
quote bool
|
||||
}
|
||||
|
||||
// Formatter makes a wrapper, f, that will format x as go source with line
|
||||
// breaks and tabs. Object f responds to the "%v" formatting verb when both the
|
||||
// "#" and " " (space) flags are set, for example:
|
||||
//
|
||||
// fmt.Sprintf("%# v", Formatter(x))
|
||||
//
|
||||
// If one of these two flags is not set, or any other verb is used, f will
|
||||
// format x according to the usual rules of package fmt.
|
||||
// In particular, if x satisfies fmt.Formatter, then x.Format will be called.
|
||||
func Formatter(x interface{}) (f fmt.Formatter) {
|
||||
return formatter{x: x, quote: true}
|
||||
}
|
||||
|
||||
func (fo formatter) String() string {
|
||||
return fmt.Sprint(fo.x) // unwrap it
|
||||
}
|
||||
|
||||
func (fo formatter) passThrough(f fmt.State, c rune) {
|
||||
s := "%"
|
||||
for i := 0; i < 128; i++ {
|
||||
if f.Flag(i) {
|
||||
s += string(i)
|
||||
}
|
||||
}
|
||||
if w, ok := f.Width(); ok {
|
||||
s += fmt.Sprintf("%d", w)
|
||||
}
|
||||
if p, ok := f.Precision(); ok {
|
||||
s += fmt.Sprintf(".%d", p)
|
||||
}
|
||||
s += string(c)
|
||||
fmt.Fprintf(f, s, fo.x)
|
||||
}
|
||||
|
||||
func (fo formatter) Format(f fmt.State, c rune) {
|
||||
if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') {
|
||||
w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0)
|
||||
p := &printer{tw: w, Writer: w}
|
||||
p.printValue(reflect.ValueOf(fo.x), true, fo.quote)
|
||||
w.Flush()
|
||||
return
|
||||
}
|
||||
fo.passThrough(f, c)
|
||||
}
|
||||
|
||||
type printer struct {
|
||||
io.Writer
|
||||
tw *tabwriter.Writer
|
||||
}
|
||||
|
||||
func (p *printer) indent() *printer {
|
||||
q := *p
|
||||
q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0)
|
||||
q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'})
|
||||
return &q
|
||||
}
|
||||
|
||||
func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) {
|
||||
if showType {
|
||||
io.WriteString(p, v.Type().String())
|
||||
fmt.Fprintf(p, "(%#v)", x)
|
||||
} else {
|
||||
fmt.Fprintf(p, "%#v", x)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) printValue(v reflect.Value, showType, quote bool) {
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
p.printInline(v, v.Bool(), showType)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
p.printInline(v, v.Int(), showType)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
p.printInline(v, v.Uint(), showType)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
p.printInline(v, v.Float(), showType)
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
fmt.Fprintf(p, "%#v", v.Complex())
|
||||
case reflect.String:
|
||||
p.fmtString(v.String(), quote)
|
||||
case reflect.Map:
|
||||
t := v.Type()
|
||||
if showType {
|
||||
io.WriteString(p, t.String())
|
||||
}
|
||||
writeByte(p, '{')
|
||||
if nonzero(v) {
|
||||
expand := !canInline(v.Type())
|
||||
pp := p
|
||||
if expand {
|
||||
writeByte(p, '\n')
|
||||
pp = p.indent()
|
||||
}
|
||||
keys := v.MapKeys()
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
showTypeInStruct := true
|
||||
k := keys[i]
|
||||
mv := v.MapIndex(k)
|
||||
pp.printValue(k, false, true)
|
||||
writeByte(pp, ':')
|
||||
if expand {
|
||||
writeByte(pp, '\t')
|
||||
}
|
||||
showTypeInStruct = t.Elem().Kind() == reflect.Interface
|
||||
pp.printValue(mv, showTypeInStruct, true)
|
||||
if expand {
|
||||
io.WriteString(pp, ",\n")
|
||||
} else if i < v.Len()-1 {
|
||||
io.WriteString(pp, ", ")
|
||||
}
|
||||
}
|
||||
if expand {
|
||||
pp.tw.Flush()
|
||||
}
|
||||
}
|
||||
writeByte(p, '}')
|
||||
case reflect.Struct:
|
||||
t := v.Type()
|
||||
if showType {
|
||||
io.WriteString(p, t.String())
|
||||
}
|
||||
writeByte(p, '{')
|
||||
if nonzero(v) {
|
||||
expand := !canInline(v.Type())
|
||||
pp := p
|
||||
if expand {
|
||||
writeByte(p, '\n')
|
||||
pp = p.indent()
|
||||
}
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
showTypeInStruct := true
|
||||
if f := t.Field(i); f.Name != "" {
|
||||
io.WriteString(pp, f.Name)
|
||||
writeByte(pp, ':')
|
||||
if expand {
|
||||
writeByte(pp, '\t')
|
||||
}
|
||||
showTypeInStruct = f.Type.Kind() == reflect.Interface
|
||||
}
|
||||
pp.printValue(getField(v, i), showTypeInStruct, true)
|
||||
if expand {
|
||||
io.WriteString(pp, ",\n")
|
||||
} else if i < v.NumField()-1 {
|
||||
io.WriteString(pp, ", ")
|
||||
}
|
||||
}
|
||||
if expand {
|
||||
pp.tw.Flush()
|
||||
}
|
||||
}
|
||||
writeByte(p, '}')
|
||||
case reflect.Interface:
|
||||
switch e := v.Elem(); {
|
||||
case e.Kind() == reflect.Invalid:
|
||||
io.WriteString(p, "nil")
|
||||
case e.IsValid():
|
||||
p.printValue(e, showType, true)
|
||||
default:
|
||||
io.WriteString(p, v.Type().String())
|
||||
io.WriteString(p, "(nil)")
|
||||
}
|
||||
case reflect.Array, reflect.Slice:
|
||||
t := v.Type()
|
||||
if showType {
|
||||
io.WriteString(p, t.String())
|
||||
}
|
||||
if v.Kind() == reflect.Slice && v.IsNil() && showType {
|
||||
io.WriteString(p, "(nil)")
|
||||
break
|
||||
}
|
||||
if v.Kind() == reflect.Slice && v.IsNil() {
|
||||
io.WriteString(p, "nil")
|
||||
break
|
||||
}
|
||||
writeByte(p, '{')
|
||||
expand := !canInline(v.Type())
|
||||
pp := p
|
||||
if expand {
|
||||
writeByte(p, '\n')
|
||||
pp = p.indent()
|
||||
}
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
showTypeInSlice := t.Elem().Kind() == reflect.Interface
|
||||
pp.printValue(v.Index(i), showTypeInSlice, true)
|
||||
if expand {
|
||||
io.WriteString(pp, ",\n")
|
||||
} else if i < v.Len()-1 {
|
||||
io.WriteString(pp, ", ")
|
||||
}
|
||||
}
|
||||
if expand {
|
||||
pp.tw.Flush()
|
||||
}
|
||||
writeByte(p, '}')
|
||||
case reflect.Ptr:
|
||||
e := v.Elem()
|
||||
if !e.IsValid() {
|
||||
writeByte(p, '(')
|
||||
io.WriteString(p, v.Type().String())
|
||||
io.WriteString(p, ")(nil)")
|
||||
} else {
|
||||
writeByte(p, '&')
|
||||
p.printValue(e, true, true)
|
||||
}
|
||||
case reflect.Chan:
|
||||
x := v.Pointer()
|
||||
if showType {
|
||||
writeByte(p, '(')
|
||||
io.WriteString(p, v.Type().String())
|
||||
fmt.Fprintf(p, ")(%#v)", x)
|
||||
} else {
|
||||
fmt.Fprintf(p, "%#v", x)
|
||||
}
|
||||
case reflect.Func:
|
||||
io.WriteString(p, v.Type().String())
|
||||
io.WriteString(p, " {...}")
|
||||
case reflect.UnsafePointer:
|
||||
p.printInline(v, v.Pointer(), showType)
|
||||
case reflect.Invalid:
|
||||
io.WriteString(p, "nil")
|
||||
}
|
||||
}
|
||||
|
||||
func canInline(t reflect.Type) bool {
|
||||
switch t.Kind() {
|
||||
case reflect.Map:
|
||||
return !canExpand(t.Elem())
|
||||
case reflect.Struct:
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
if canExpand(t.Field(i).Type) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Interface:
|
||||
return false
|
||||
case reflect.Array, reflect.Slice:
|
||||
return !canExpand(t.Elem())
|
||||
case reflect.Ptr:
|
||||
return false
|
||||
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func canExpand(t reflect.Type) bool {
|
||||
switch t.Kind() {
|
||||
case reflect.Map, reflect.Struct,
|
||||
reflect.Interface, reflect.Array, reflect.Slice,
|
||||
reflect.Ptr:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *printer) fmtString(s string, quote bool) {
|
||||
if quote {
|
||||
s = strconv.Quote(s)
|
||||
}
|
||||
io.WriteString(p, s)
|
||||
}
|
||||
|
||||
func tryDeepEqual(a, b interface{}) bool {
|
||||
defer func() { recover() }()
|
||||
return reflect.DeepEqual(a, b)
|
||||
}
|
||||
|
||||
func writeByte(w io.Writer, b byte) {
|
||||
w.Write([]byte{b})
|
||||
}
|
||||
|
||||
func getField(v reflect.Value, i int) reflect.Value {
|
||||
val := v.Field(i)
|
||||
if val.Kind() == reflect.Interface && !val.IsNil() {
|
||||
val = val.Elem()
|
||||
}
|
||||
return val
|
||||
}
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
// Package pretty provides pretty-printing for Go values. This is
|
||||
// useful during debugging, to avoid wrapping long output lines in
|
||||
// the terminal.
|
||||
//
|
||||
// It provides a function, Formatter, that can be used with any
|
||||
// function that accepts a format string. It also provides
|
||||
// convenience wrappers for functions in packages fmt and log.
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
// Errorf is a convenience wrapper for fmt.Errorf.
|
||||
//
|
||||
// Calling Errorf(f, x, y) is equivalent to
|
||||
// fmt.Errorf(f, Formatter(x), Formatter(y)).
|
||||
func Errorf(format string, a ...interface{}) error {
|
||||
return fmt.Errorf(format, wrap(a, false)...)
|
||||
}
|
||||
|
||||
// Fprintf is a convenience wrapper for fmt.Fprintf.
|
||||
//
|
||||
// Calling Fprintf(w, f, x, y) is equivalent to
|
||||
// fmt.Fprintf(w, f, Formatter(x), Formatter(y)).
|
||||
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error error) {
|
||||
return fmt.Fprintf(w, format, wrap(a, false)...)
|
||||
}
|
||||
|
||||
// Log is a convenience wrapper for log.Printf.
|
||||
//
|
||||
// Calling Log(x, y) is equivalent to
|
||||
// log.Print(Formatter(x), Formatter(y)), but each operand is
|
||||
// formatted with "%# v".
|
||||
func Log(a ...interface{}) {
|
||||
log.Print(wrap(a, true)...)
|
||||
}
|
||||
|
||||
// Logf is a convenience wrapper for log.Printf.
|
||||
//
|
||||
// Calling Logf(f, x, y) is equivalent to
|
||||
// log.Printf(f, Formatter(x), Formatter(y)).
|
||||
func Logf(format string, a ...interface{}) {
|
||||
log.Printf(format, wrap(a, false)...)
|
||||
}
|
||||
|
||||
// Logln is a convenience wrapper for log.Printf.
|
||||
//
|
||||
// Calling Logln(x, y) is equivalent to
|
||||
// log.Println(Formatter(x), Formatter(y)), but each operand is
|
||||
// formatted with "%# v".
|
||||
func Logln(a ...interface{}) {
|
||||
log.Println(wrap(a, true)...)
|
||||
}
|
||||
|
||||
// Print pretty-prints its operands and writes to standard output.
|
||||
//
|
||||
// Calling Print(x, y) is equivalent to
|
||||
// fmt.Print(Formatter(x), Formatter(y)), but each operand is
|
||||
// formatted with "%# v".
|
||||
func Print(a ...interface{}) (n int, errno error) {
|
||||
return fmt.Print(wrap(a, true)...)
|
||||
}
|
||||
|
||||
// Printf is a convenience wrapper for fmt.Printf.
|
||||
//
|
||||
// Calling Printf(f, x, y) is equivalent to
|
||||
// fmt.Printf(f, Formatter(x), Formatter(y)).
|
||||
func Printf(format string, a ...interface{}) (n int, errno error) {
|
||||
return fmt.Printf(format, wrap(a, false)...)
|
||||
}
|
||||
|
||||
// Println pretty-prints its operands and writes to standard output.
|
||||
//
|
||||
// Calling Print(x, y) is equivalent to
|
||||
// fmt.Println(Formatter(x), Formatter(y)), but each operand is
|
||||
// formatted with "%# v".
|
||||
func Println(a ...interface{}) (n int, errno error) {
|
||||
return fmt.Println(wrap(a, true)...)
|
||||
}
|
||||
|
||||
// Sprintf is a convenience wrapper for fmt.Sprintf.
|
||||
//
|
||||
// Calling Sprintf(f, x, y) is equivalent to
|
||||
// fmt.Sprintf(f, Formatter(x), Formatter(y)).
|
||||
func Sprintf(format string, a ...interface{}) string {
|
||||
return fmt.Sprintf(format, wrap(a, false)...)
|
||||
}
|
||||
|
||||
func wrap(a []interface{}, force bool) []interface{} {
|
||||
w := make([]interface{}, len(a))
|
||||
for i, x := range a {
|
||||
w[i] = formatter{x: x, force: force}
|
||||
}
|
||||
return w
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func nonzero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() != 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() != 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() != 0
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return v.Complex() != complex(0, 0)
|
||||
case reflect.String:
|
||||
return v.String() != ""
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if nonzero(getField(v, i)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case reflect.Array:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if nonzero(v.Index(i)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case reflect.Map, reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Chan, reflect.Func:
|
||||
return !v.IsNil()
|
||||
case reflect.UnsafePointer:
|
||||
return v.Pointer() != 0
|
||||
}
|
||||
return true
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
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.
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
This is a Go package for manipulating paragraphs of text.
|
||||
|
||||
See http://go.pkgdoc.org/github.com/kr/text for full documentation.
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
// Package text provides rudimentary functions for manipulating text in
|
||||
// paragraphs.
|
||||
package text
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Indent inserts prefix at the beginning of each non-empty line of s. The
|
||||
// end-of-line marker is NL.
|
||||
func Indent(s, prefix string) string {
|
||||
return string(IndentBytes([]byte(s), []byte(prefix)))
|
||||
}
|
||||
|
||||
// IndentBytes inserts prefix at the beginning of each non-empty line of b.
|
||||
// The end-of-line marker is NL.
|
||||
func IndentBytes(b, prefix []byte) []byte {
|
||||
var res []byte
|
||||
bol := true
|
||||
for _, c := range b {
|
||||
if bol && c != '\n' {
|
||||
res = append(res, prefix...)
|
||||
}
|
||||
res = append(res, c)
|
||||
bol = c == '\n'
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Writer indents each line of its input.
|
||||
type indentWriter struct {
|
||||
w io.Writer
|
||||
bol bool
|
||||
pre [][]byte
|
||||
sel int
|
||||
off int
|
||||
}
|
||||
|
||||
// NewIndentWriter makes a new write filter that indents the input
|
||||
// lines. Each line is prefixed in order with the corresponding
|
||||
// element of pre. If there are more lines than elements, the last
|
||||
// element of pre is repeated for each subsequent line.
|
||||
func NewIndentWriter(w io.Writer, pre ...[]byte) io.Writer {
|
||||
return &indentWriter{
|
||||
w: w,
|
||||
pre: pre,
|
||||
bol: true,
|
||||
}
|
||||
}
|
||||
|
||||
// The only errors returned are from the underlying indentWriter.
|
||||
func (w *indentWriter) Write(p []byte) (n int, err error) {
|
||||
for _, c := range p {
|
||||
if w.bol {
|
||||
var i int
|
||||
i, err = w.w.Write(w.pre[w.sel][w.off:])
|
||||
w.off += i
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
_, err = w.w.Write([]byte{c})
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
n++
|
||||
w.bol = c == '\n'
|
||||
if w.bol {
|
||||
w.off = 0
|
||||
if w.sel < len(w.pre)-1 {
|
||||
w.sel++
|
||||
}
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
)
|
||||
|
||||
var (
|
||||
nl = []byte{'\n'}
|
||||
sp = []byte{' '}
|
||||
)
|
||||
|
||||
const defaultPenalty = 1e5
|
||||
|
||||
// Wrap wraps s into a paragraph of lines of length lim, with minimal
|
||||
// raggedness.
|
||||
func Wrap(s string, lim int) string {
|
||||
return string(WrapBytes([]byte(s), lim))
|
||||
}
|
||||
|
||||
// WrapBytes wraps b into a paragraph of lines of length lim, with minimal
|
||||
// raggedness.
|
||||
func WrapBytes(b []byte, lim int) []byte {
|
||||
words := bytes.Split(bytes.Replace(bytes.TrimSpace(b), nl, sp, -1), sp)
|
||||
var lines [][]byte
|
||||
for _, line := range WrapWords(words, 1, lim, defaultPenalty) {
|
||||
lines = append(lines, bytes.Join(line, sp))
|
||||
}
|
||||
return bytes.Join(lines, nl)
|
||||
}
|
||||
|
||||
// WrapWords is the low-level line-breaking algorithm, useful if you need more
|
||||
// control over the details of the text wrapping process. For most uses, either
|
||||
// Wrap or WrapBytes will be sufficient and more convenient.
|
||||
//
|
||||
// WrapWords splits a list of words into lines with minimal "raggedness",
|
||||
// treating each byte as one unit, accounting for spc units between adjacent
|
||||
// words on each line, and attempting to limit lines to lim units. Raggedness
|
||||
// is the total error over all lines, where error is the square of the
|
||||
// difference of the length of the line and lim. Too-long lines (which only
|
||||
// happen when a single word is longer than lim units) have pen penalty units
|
||||
// added to the error.
|
||||
func WrapWords(words [][]byte, spc, lim, pen int) [][][]byte {
|
||||
n := len(words)
|
||||
|
||||
length := make([][]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
length[i] = make([]int, n)
|
||||
length[i][i] = len(words[i])
|
||||
for j := i + 1; j < n; j++ {
|
||||
length[i][j] = length[i][j-1] + spc + len(words[j])
|
||||
}
|
||||
}
|
||||
|
||||
nbrk := make([]int, n)
|
||||
cost := make([]int, n)
|
||||
for i := range cost {
|
||||
cost[i] = math.MaxInt32
|
||||
}
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
if length[i][n-1] <= lim {
|
||||
cost[i] = 0
|
||||
nbrk[i] = n
|
||||
} else {
|
||||
for j := i + 1; j < n; j++ {
|
||||
d := lim - length[i][j-1]
|
||||
c := d*d + cost[j]
|
||||
if length[i][j-1] > lim {
|
||||
c += pen // too-long lines get a worse penalty
|
||||
}
|
||||
if c < cost[i] {
|
||||
cost[i] = c
|
||||
nbrk[i] = j
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lines [][][]byte
|
||||
i := 0
|
||||
for i < n {
|
||||
lines = append(lines, words[i:nbrk[i]])
|
||||
i = nbrk[i]
|
||||
}
|
||||
return lines
|
||||
}
|
||||
Reference in New Issue
Block a user