Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7793021260 | |||
| 692562818c | |||
| 9e19346fc0 | |||
| cb827169dc | |||
| 027d0173bc | |||
| 6f19b69bbd | |||
| 7e118bfdb0 | |||
|
|
e0868841bf | ||
|
|
c558405d11 | ||
|
|
a1eb035af7 | ||
| 5eedcff042 | |||
|
|
6ac8eb04b4 | ||
|
|
4a4e4cd3fd | ||
|
|
1207093a56 | ||
| 2b6ae03d1a | |||
| ef0f14a512 | |||
| 6ae393ebf2 | |||
| 76d88c220d | |||
|
|
40e015373f | ||
| 50c226b726 | |||
| 070ffb9dff | |||
| 5ac05bd298 | |||
| cf1edf2d31 | |||
|
|
03e8958dd7 | ||
|
|
6cef585071 | ||
| 06678499d4 | |||
| fad17065fe | |||
| 63e0cbeacb | |||
| 231b98dd68 | |||
|
|
160a42ec26 | ||
|
|
b6b33e00c8 | ||
|
|
9e1aed257b | ||
|
|
411907c0ad | ||
|
|
06c7f8f42f | ||
|
|
8b638065d1 | ||
|
|
9ac378eaa5 | ||
|
|
eaaaabe439 | ||
|
|
4122f01644 | ||
|
|
263a5d3973 | ||
|
|
afef3eea62 |
26
.travis.yml
Normal file
26
.travis.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
arch:
|
||||||
|
- amd64
|
||||||
|
- ppc64le
|
||||||
|
sudo: false
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- tip
|
||||||
|
- 1.9
|
||||||
|
jobs:
|
||||||
|
exclude:
|
||||||
|
- go: 1.9
|
||||||
|
arch: amd64
|
||||||
|
- go: 1.9
|
||||||
|
arch: ppc64le
|
||||||
|
script:
|
||||||
|
- go get golang.org/x/lint/golint
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
- go get github.com/kisom/goutils/...
|
||||||
|
- go test -cover github.com/kisom/goutils/...
|
||||||
|
- golint github.com/kisom/goutils/...
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
recipients:
|
||||||
|
- coder@kyleisom.net
|
||||||
|
on_success: change
|
||||||
|
on_failure: change
|
||||||
@@ -1,3 +1,11 @@
|
|||||||
|
Release 1.2.1 - 2018-09-15
|
||||||
|
|
||||||
|
+ Add missing format argument to Errorf call in kgz.
|
||||||
|
|
||||||
|
Release 1.2.0 - 2018-09-15
|
||||||
|
|
||||||
|
+ Adds the kgz command line utility.
|
||||||
|
|
||||||
Release 1.1.0 - 2017-11-16
|
Release 1.1.0 - 2017-11-16
|
||||||
|
|
||||||
+ A number of new command line utilities were added
|
+ A number of new command line utilities were added
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ Contents:
|
|||||||
csrpubdump/ Dump the public key from an X.509 certificate request.
|
csrpubdump/ Dump the public key from an X.509 certificate request.
|
||||||
fragment/ Print a fragment of a file.
|
fragment/ Print a fragment of a file.
|
||||||
jlp/ JSON linter/prettifier.
|
jlp/ JSON linter/prettifier.
|
||||||
|
kgz/ Custom gzip compressor / decompressor that handles 99%
|
||||||
|
of my use cases.
|
||||||
pem2bin/ Dump the binary body of a PEM-encoded block.
|
pem2bin/ Dump the binary body of a PEM-encoded block.
|
||||||
pembody/ Print the body of a PEM certificate.
|
pembody/ Print the body of a PEM certificate.
|
||||||
pemit/ Dump data to a PEM file.
|
pemit/ Dump data to a PEM file.
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/kisom/goutils/assert"
|
"git.sr.ht/~kisom/goutils/assert"
|
||||||
"golang.org/x/crypto/blake2b"
|
"golang.org/x/crypto/blake2b"
|
||||||
"golang.org/x/crypto/blake2s"
|
"golang.org/x/crypto/blake2s"
|
||||||
"golang.org/x/crypto/md4"
|
"golang.org/x/crypto/md4"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/kisom/goutils/assert"
|
"git.sr.ht/~kisom/goutils/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSecureHash(t *testing.T) {
|
func TestSecureHash(t *testing.T) {
|
||||||
@@ -97,7 +97,7 @@ func TestHash32(t *testing.T) {
|
|||||||
h.Write(data)
|
h.Write(data)
|
||||||
sum, ok = h.Sum32()
|
sum, ok = h.Sum32()
|
||||||
assert.BoolT(t, ok, algo+" should be able to return a Sum32")
|
assert.BoolT(t, ok, algo+" should be able to return a Sum32")
|
||||||
assert.BoolT(t, expected != sum, fmt.Sprintf("%s returned %d but shouldn't have", algo, sum, expected))
|
assert.BoolT(t, expected != sum, fmt.Sprintf("%s returned %d but shouldn't have", algo, sum))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHash64(t *testing.T) {
|
func TestHash64(t *testing.T) {
|
||||||
@@ -129,7 +129,7 @@ func TestHash64(t *testing.T) {
|
|||||||
h.Write(data)
|
h.Write(data)
|
||||||
sum, ok = h.Sum64()
|
sum, ok = h.Sum64()
|
||||||
assert.BoolT(t, ok, algo+" should be able to return a Sum64")
|
assert.BoolT(t, ok, algo+" should be able to return a Sum64")
|
||||||
assert.BoolT(t, expected != sum, fmt.Sprintf("%s returned %d but shouldn't have", algo, sum, expected))
|
assert.BoolT(t, expected != sum, fmt.Sprintf("%s returned %d but shouldn't have", algo, sum))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListLengthSanity(t *testing.T) {
|
func TestListLengthSanity(t *testing.T) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
)
|
)
|
||||||
|
|
||||||
var hasPort = regexp.MustCompile(`:\d+$`)
|
var hasPort = regexp.MustCompile(`:\d+$`)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cloudflare/cfssl/helpers"
|
"github.com/cloudflare/cfssl/helpers"
|
||||||
@@ -83,6 +84,7 @@ func keyUsages(ku x509.KeyUsage) string {
|
|||||||
uses = append(uses, s)
|
uses = append(uses, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sort.Strings(uses)
|
||||||
|
|
||||||
return strings.Join(uses, ", ")
|
return strings.Join(uses, ", ")
|
||||||
}
|
}
|
||||||
@@ -92,6 +94,7 @@ func extUsage(ext []x509.ExtKeyUsage) string {
|
|||||||
for i := range ext {
|
for i := range ext {
|
||||||
ns = append(ns, extKeyUsages[ext[i]])
|
ns = append(ns, extKeyUsages[ext[i]])
|
||||||
}
|
}
|
||||||
|
sort.Strings(ns)
|
||||||
|
|
||||||
return strings.Join(ns, ", ")
|
return strings.Join(ns, ", ")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cloudflare/cfssl/helpers"
|
"github.com/cloudflare/cfssl/helpers"
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
"github.com/kisom/goutils/lib"
|
"git.sr.ht/~kisom/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
var warnOnly bool
|
var warnOnly bool
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
|
|
||||||
"github.com/cloudflare/cfssl/helpers"
|
"github.com/cloudflare/cfssl/helpers"
|
||||||
"github.com/cloudflare/cfssl/revoke"
|
"github.com/cloudflare/cfssl/revoke"
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
"github.com/kisom/goutils/lib"
|
"git.sr.ht/~kisom/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func printRevocation(cert *x509.Certificate) {
|
func printRevocation(cert *x509.Certificate) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/kisom/goutils/lib"
|
"git.sr.ht/~kisom/goutils/lib"
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"golang.org/x/crypto/ssh/agent"
|
"golang.org/x/crypto/ssh/agent"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/kisom/goutils/lib"
|
"git.sr.ht/~kisom/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func prettify(file string, validateOnly bool) error {
|
func prettify(file string, validateOnly bool) error {
|
||||||
|
|||||||
23
cmd/kgz/README
Normal file
23
cmd/kgz/README
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
kgz
|
||||||
|
|
||||||
|
kgz is like gzip, but supports compressing and decompressing to a different
|
||||||
|
directory than the source file is in.
|
||||||
|
|
||||||
|
Usage: kgz [-l] source [target]
|
||||||
|
|
||||||
|
If target is a directory, the basename of the sourcefile will be used
|
||||||
|
as the target filename. Compression and decompression is selected
|
||||||
|
based on whether the source filename ends in ".gz".
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-l level Compression level (0-9). Only meaninful when
|
||||||
|
compressing a file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
182
cmd/kgz/main.go
Normal file
182
cmd/kgz/main.go
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/flate"
|
||||||
|
"compress/gzip"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const gzipExt = ".gz"
|
||||||
|
|
||||||
|
func compress(path, target string, level int) error {
|
||||||
|
sourceFile, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "opening file for read")
|
||||||
|
}
|
||||||
|
defer sourceFile.Close()
|
||||||
|
|
||||||
|
destFile, err := os.Create(target)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "opening file for write")
|
||||||
|
}
|
||||||
|
defer destFile.Close()
|
||||||
|
|
||||||
|
gzipCompressor, err := gzip.NewWriterLevel(destFile, level)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "invalid compression level")
|
||||||
|
}
|
||||||
|
defer gzipCompressor.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(gzipCompressor, sourceFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "compressing file")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "stat(2)ing destination file")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func uncompress(path, target string) error {
|
||||||
|
sourceFile, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "opening file for read")
|
||||||
|
}
|
||||||
|
defer sourceFile.Close()
|
||||||
|
|
||||||
|
gzipUncompressor, err := gzip.NewReader(sourceFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "reading gzip headers")
|
||||||
|
}
|
||||||
|
defer gzipUncompressor.Close()
|
||||||
|
|
||||||
|
destFile, err := os.Create(target)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "opening file for write")
|
||||||
|
}
|
||||||
|
defer destFile.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(destFile, gzipUncompressor)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "uncompressing file")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func usage(w io.Writer) {
|
||||||
|
fmt.Fprintf(w, `Usage: %s [-l] source [target]
|
||||||
|
|
||||||
|
kgz is like gzip, but supports compressing and decompressing to a different
|
||||||
|
directory than the source file is in.
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-l level Compression level (0-9). Only meaninful when
|
||||||
|
compressing a file.
|
||||||
|
`, os.Args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.Usage = func() { usage(os.Stderr) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDir(path string) bool {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err == nil {
|
||||||
|
defer file.Close()
|
||||||
|
stat, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat.IsDir() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathForUncompressing(source, dest string) (string, error) {
|
||||||
|
if !isDir(dest) {
|
||||||
|
return dest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
source = filepath.Base(source)
|
||||||
|
if !strings.HasSuffix(source, gzipExt) {
|
||||||
|
return "", errors.Errorf("%s is a not gzip-compressed file", source)
|
||||||
|
}
|
||||||
|
outFile := source[:len(source)-len(gzipExt)]
|
||||||
|
outFile = filepath.Join(dest, outFile)
|
||||||
|
return outFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathForCompressing(source, dest string) (string, error) {
|
||||||
|
if !isDir(dest) {
|
||||||
|
return dest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
source = filepath.Base(source)
|
||||||
|
if strings.HasSuffix(source, gzipExt) {
|
||||||
|
return "", errors.Errorf("%s is a gzip-compressed file", source)
|
||||||
|
}
|
||||||
|
|
||||||
|
dest = filepath.Join(dest, source+gzipExt)
|
||||||
|
return dest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var level int
|
||||||
|
var path string
|
||||||
|
var target = "."
|
||||||
|
|
||||||
|
flag.IntVar(&level, "l", flate.DefaultCompression, "compression level")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if flag.NArg() < 1 || flag.NArg() > 2 {
|
||||||
|
usage(os.Stderr)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
path = flag.Arg(0)
|
||||||
|
if flag.NArg() == 2 {
|
||||||
|
target = flag.Arg(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(path, gzipExt) {
|
||||||
|
target, err := pathForUncompressing(path, target)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = uncompress(path, target)
|
||||||
|
if err != nil {
|
||||||
|
os.Remove(target)
|
||||||
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target, err := pathForCompressing(path, target)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = compress(path, target, level)
|
||||||
|
if err != nil {
|
||||||
|
os.Remove(target)
|
||||||
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
cmd/parts/README
Normal file
9
cmd/parts/README
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
parts: simple parts database for electronic components
|
||||||
|
|
||||||
|
Usage: parts [id] -- query the database for a part
|
||||||
|
parts [-c class] [id] [description] -- store a part in the database
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-f path Path to parts database (default is
|
||||||
|
/home/kyle/.parts.json).
|
||||||
|
|
||||||
142
cmd/parts/main.go
Normal file
142
cmd/parts/main.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
|
)
|
||||||
|
|
||||||
|
const dbVersion = "1"
|
||||||
|
|
||||||
|
var dbFile = filepath.Join(os.Getenv("HOME"), ".parts.json")
|
||||||
|
var partsDB = &database{Version: dbVersion}
|
||||||
|
|
||||||
|
type part struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Class string `json:"class,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p part) String() string {
|
||||||
|
return fmt.Sprintf("%s: %s", p.Name, p.Description)
|
||||||
|
}
|
||||||
|
|
||||||
|
type database struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
LastUpdate int64 `json:"json"`
|
||||||
|
Parts map[string]part `json:"parts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func help(w io.Writer) {
|
||||||
|
fmt.Fprintf(w, `Usage: parts [id] -- query the database for a part
|
||||||
|
parts [-c class] [id] [description] -- store a part in the database
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-f path Path to parts database (default is
|
||||||
|
%s).
|
||||||
|
|
||||||
|
`, dbFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadDatabase() {
|
||||||
|
data, err := ioutil.ReadFile(dbFile)
|
||||||
|
if err != nil && os.IsNotExist(err) {
|
||||||
|
partsDB = &database{
|
||||||
|
Version: dbVersion,
|
||||||
|
Parts: map[string]part{},
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
die.If(err)
|
||||||
|
|
||||||
|
err = json.Unmarshal(data, partsDB)
|
||||||
|
die.If(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findPart(partName string) {
|
||||||
|
partName = strings.ToLower(partName)
|
||||||
|
for name, part := range partsDB.Parts {
|
||||||
|
if strings.Contains(strings.ToLower(name), partName) {
|
||||||
|
fmt.Println(part.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeDB() {
|
||||||
|
data, err := json.Marshal(partsDB)
|
||||||
|
die.If(err)
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(dbFile, data, 0644)
|
||||||
|
die.If(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storePart(name, class, description string) {
|
||||||
|
p, exists := partsDB.Parts[name]
|
||||||
|
if exists {
|
||||||
|
fmt.Printf("warning: replacing part %s\n", name)
|
||||||
|
fmt.Printf("\t%s\n", p.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
partsDB.Parts[name] = part{
|
||||||
|
Name: name,
|
||||||
|
Class: class,
|
||||||
|
Description: description,
|
||||||
|
}
|
||||||
|
|
||||||
|
writeDB()
|
||||||
|
}
|
||||||
|
|
||||||
|
func listParts() {
|
||||||
|
parts := make([]string, 0, len(partsDB.Parts))
|
||||||
|
for partName := range partsDB.Parts {
|
||||||
|
parts = append(parts, partName)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(parts)
|
||||||
|
for _, partName := range parts {
|
||||||
|
fmt.Println(partsDB.Parts[partName].String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var class string
|
||||||
|
var helpFlag bool
|
||||||
|
|
||||||
|
flag.StringVar(&class, "c", "", "device class")
|
||||||
|
flag.StringVar(&dbFile, "f", dbFile, "`path` to database")
|
||||||
|
flag.BoolVar(&helpFlag, "h", false, "Print a help message.")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if helpFlag {
|
||||||
|
help(os.Stdout)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDatabase()
|
||||||
|
|
||||||
|
switch flag.NArg() {
|
||||||
|
case 0:
|
||||||
|
help(os.Stdout)
|
||||||
|
return
|
||||||
|
case 1:
|
||||||
|
partName := flag.Arg(0)
|
||||||
|
if partName == "list" {
|
||||||
|
listParts()
|
||||||
|
} else {
|
||||||
|
findPart(flag.Arg(0))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
description := strings.Join(flag.Args()[1:], " ")
|
||||||
|
storePart(flag.Arg(0), class, description)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/kisom/goutils/lib"
|
"git.sr.ht/~kisom/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/kisom/goutils/assert"
|
"git.sr.ht/~kisom/goutils/assert"
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
"github.com/kisom/goutils/lib"
|
"git.sr.ht/~kisom/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func usage(w io.Writer) {
|
func usage(w io.Writer) {
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kisom/goutils/fileutil"
|
"git.sr.ht/~kisom/goutils/fileutil"
|
||||||
"github.com/kisom/goutils/lib"
|
"git.sr.ht/~kisom/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func hashName(path, encodedHash string) string {
|
func hashName(path, encodedHash string) string {
|
||||||
@@ -46,7 +46,6 @@ func newName(path string) (string, error) {
|
|||||||
func move(dst, src string, force bool) (err error) {
|
func move(dst, src string, force bool) (err error) {
|
||||||
if fileutil.FileDoesExist(dst) && !force {
|
if fileutil.FileDoesExist(dst) && !force {
|
||||||
return fmt.Errorf("%s exists (pass the -f flag to overwrite)", dst)
|
return fmt.Errorf("%s exists (pass the -f flag to overwrite)", dst)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
dstFile, err := os.Create(dst)
|
dstFile, err := os.Create(dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -4,31 +4,16 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/kisom/goutils/ahash"
|
"git.sr.ht/~kisom/goutils/ahash"
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
"github.com/kisom/goutils/lib"
|
"git.sr.ht/~kisom/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func fetch(remote string) ([]byte, error) {
|
|
||||||
resp, err := http.Get(remote)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return body, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func usage(w io.Writer) {
|
func usage(w io.Writer) {
|
||||||
fmt.Fprintf(w, `Usage: %s [-a algo] [-h] [-l set] urls...
|
fmt.Fprintf(w, `Usage: %s [-a algo] [-h] [-l set] urls...
|
||||||
Compute the hash over each URL.
|
Compute the hash over each URL.
|
||||||
@@ -91,13 +76,19 @@ func main() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := fetch(remote)
|
resp, err := http.Get(remote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "fetching %s", remote)
|
lib.Warn(err, "fetching %s", remote)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
sum, err := ahash.Sum(algo, body)
|
if err != nil {
|
||||||
|
lib.Warn(err, "fetching %s", remote)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sum, err := ahash.SumReader(algo, resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Err(lib.ExitFailure, err, "while hashing data")
|
lib.Err(lib.ExitFailure, err, "while hashing data")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
"github.com/kisom/goutils/logging"
|
"git.sr.ht/~kisom/goutils/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -67,6 +67,10 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func walkFile(path string, info os.FileInfo, err error) error {
|
func walkFile(path string, info os.FileInfo, err error) error {
|
||||||
|
if ignores[path] {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
if !sourceRegexp.MatchString(path) {
|
if !sourceRegexp.MatchString(path) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -97,10 +101,24 @@ func walkFile(path string, info os.FileInfo, err error) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ignores = map[string]bool{}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
var ignoreLine string
|
||||||
|
var noVendor bool
|
||||||
|
flag.StringVar(&ignoreLine, "i", "", "comma-separated list of directories to ignore")
|
||||||
|
flag.BoolVar(&noVendor, "nv", false, "ignore the vendor directory")
|
||||||
flag.BoolVar(&debug, "v", false, "log debugging information")
|
flag.BoolVar(&debug, "v", false, "log debugging information")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if noVendor {
|
||||||
|
ignores["vendor"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, word := range strings.Split(ignoreLine, ",") {
|
||||||
|
ignores[strings.TrimSpace(word)] = true
|
||||||
|
}
|
||||||
|
|
||||||
err := filepath.Walk(".", walkFile)
|
err := filepath.Walk(".", walkFile)
|
||||||
die.If(err)
|
die.If(err)
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
"github.com/kisom/goutils/lib"
|
"git.sr.ht/~kisom/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func usage(w io.Writer) {
|
func usage(w io.Writer) {
|
||||||
|
|||||||
46
cmd/sprox/main.go
Normal file
46
cmd/sprox/main.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
|
)
|
||||||
|
|
||||||
|
func proxy(conn net.Conn, inside string) error {
|
||||||
|
proxyConn, err := net.Dial("tcp", inside)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer proxyConn.Close()
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
io.Copy(conn, proxyConn)
|
||||||
|
}()
|
||||||
|
_, err = io.Copy(proxyConn, conn)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var outside, inside string
|
||||||
|
flag.StringVar(&outside, "f", "8080", "outside port")
|
||||||
|
flag.StringVar(&inside, "p", "4000", "inside port")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
l, err := net.Listen("tcp", "0.0.0.0:"+outside)
|
||||||
|
die.If(err)
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
go proxy(conn, "127.0.0.1:"+inside)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
"github.com/kisom/goutils/lib"
|
"git.sr.ht/~kisom/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/kisom/goutils/die"
|
"git.sr.ht/~kisom/goutils/die"
|
||||||
)
|
)
|
||||||
|
|
||||||
var validPEMs = map[string]bool{
|
var validPEMs = map[string]bool{
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ Flags:
|
|||||||
|
|
||||||
func usageExamples() {
|
func usageExamples() {
|
||||||
usage(os.Stdout)
|
usage(os.Stdout)
|
||||||
fmt.Println(`
|
fmt.Printf(`
|
||||||
Examples (note that the examples are done in the America/Los_Angeles /
|
Examples (note that the examples are done in the America/Los_Angeles /
|
||||||
PST8PDT time zone):
|
PST8PDT time zone):
|
||||||
|
|
||||||
@@ -134,6 +134,7 @@ PST8PDT time zone):
|
|||||||
(Converting from GMT (offset +0000) to UTC (offset +0000).)
|
(Converting from GMT (offset +0000) to UTC (offset +0000).)
|
||||||
==================================================================
|
==================================================================
|
||||||
2016-06-14 23:46 = 2016-06-14 23:46
|
2016-06-14 23:46 = 2016-06-14 23:46
|
||||||
|
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
110
config/config.go
Normal file
110
config/config.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
// Package config implements a simple global configuration system that
|
||||||
|
// supports a file with key=value pairs and environment variables. Note
|
||||||
|
// that the config system is global.
|
||||||
|
//
|
||||||
|
// This package is intended to be used for small daemons: some configuration
|
||||||
|
// file is optionally populated at program start, then this is used to
|
||||||
|
// transparently look up configuration values from either that file or the
|
||||||
|
// environment.
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NB: Rather than define a singleton type, everything is defined at
|
||||||
|
// the top-level
|
||||||
|
|
||||||
|
var (
|
||||||
|
vars = map[string]string{}
|
||||||
|
prefix = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetEnvPrefix sets the prefix for all environment variables; it's
|
||||||
|
// assumed to not be needed for files.
|
||||||
|
func SetEnvPrefix(pfx string) {
|
||||||
|
prefix = pfx
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLine(line string) {
|
||||||
|
if strings.HasPrefix(line, "#") || line == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lineParts := strings.SplitN(line, "=", 2)
|
||||||
|
if len(lineParts) != 2 {
|
||||||
|
log.Print("skipping line: ", line)
|
||||||
|
return // silently ignore empty keys
|
||||||
|
}
|
||||||
|
|
||||||
|
lineParts[0] = strings.TrimSpace(lineParts[0])
|
||||||
|
lineParts[1] = strings.TrimSpace(lineParts[1])
|
||||||
|
vars[lineParts[0]] = lineParts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFile scans the file at path for key=value pairs and adds them
|
||||||
|
// to the configuration.
|
||||||
|
func LoadFile(path string) error {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.TrimSpace(scanner.Text())
|
||||||
|
addLine(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = scanner.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves a value from either a configuration file or the
|
||||||
|
// environment. Note that values from a file will override environment
|
||||||
|
// variables.
|
||||||
|
func Get(key string) string {
|
||||||
|
if v, ok := vars[key]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return os.Getenv(prefix + key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefault retrieves a value from either a configuration file or
|
||||||
|
// the environment. Note that value from a file will override
|
||||||
|
// environment variables. If a value isn't found (e.g. Get returns an
|
||||||
|
// empty string), the default value will be used.
|
||||||
|
func GetDefault(key, def string) string {
|
||||||
|
if v := Get(key); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// Require retrieves a value from either a configuration file or the
|
||||||
|
// environment. If the key isn't present, it will call log.Fatal, printing
|
||||||
|
// the missing key.
|
||||||
|
func Require(key string) string {
|
||||||
|
if v, ok := vars[key]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := os.LookupEnv(prefix + key)
|
||||||
|
if !ok {
|
||||||
|
var envMessage string
|
||||||
|
if prefix != "" {
|
||||||
|
envMessage = " (note: looked for the key " + prefix + key
|
||||||
|
envMessage += " in the local env)"
|
||||||
|
}
|
||||||
|
log.Fatalf("missing required configuration value %s%s", key, envMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
66
config/config_test.go
Normal file
66
config/config_test.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testFilePath = "testdata/test.env"
|
||||||
|
|
||||||
|
// Keys
|
||||||
|
kOrder = "ORDER"
|
||||||
|
kSpecies = "SPECIES"
|
||||||
|
kName = "COMMON_NAME"
|
||||||
|
|
||||||
|
// Env
|
||||||
|
eOrder = "corvus"
|
||||||
|
eSpecies = "corvus corax"
|
||||||
|
eName = "northern raven"
|
||||||
|
|
||||||
|
// File
|
||||||
|
fOrder = "stringiformes"
|
||||||
|
fSpecies = "strix aluco"
|
||||||
|
// Name isn't set in the file to test fall through.
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
os.Setenv(kOrder, eOrder)
|
||||||
|
os.Setenv(kSpecies, eSpecies)
|
||||||
|
os.Setenv(kName, eName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadEnvOnly(t *testing.T) {
|
||||||
|
order := Get(kOrder)
|
||||||
|
species := Get(kSpecies)
|
||||||
|
if order != eOrder {
|
||||||
|
t.Errorf("want %s, have %s", eOrder, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
if species != eSpecies {
|
||||||
|
t.Errorf("want %s, have %s", eSpecies, species)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadFile(t *testing.T) {
|
||||||
|
err := LoadFile(testFilePath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
order := Get(kOrder)
|
||||||
|
species := Get(kSpecies)
|
||||||
|
name := Get(kName)
|
||||||
|
|
||||||
|
if order != fOrder {
|
||||||
|
t.Errorf("want %s, have %s", fOrder, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
if species != fSpecies {
|
||||||
|
t.Errorf("want %s, have %s", fSpecies, species)
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != eName {
|
||||||
|
t.Errorf("want %s, have %s", eName, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
2
config/testdata/test.env
vendored
Normal file
2
config/testdata/test.env
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ORDER=stringiformes
|
||||||
|
SPECIES=strix aluco
|
||||||
76
dbg/dbg.go
Normal file
76
dbg/dbg.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// Package dbg implements a debug printer.
|
||||||
|
package dbg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A DebugPrinter is a drop-in replacement for fmt.Print*, and also acts as
|
||||||
|
// an io.WriteCloser when enabled.
|
||||||
|
type DebugPrinter struct {
|
||||||
|
// If Enabled is false, the print statements won't do anything.
|
||||||
|
Enabled bool
|
||||||
|
out io.WriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close satisfies the Closer interface.
|
||||||
|
func (dbg *DebugPrinter) Close() error {
|
||||||
|
return dbg.out.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write satisfies the Writer interface.
|
||||||
|
func (dbg *DebugPrinter) Write(p []byte) (int, error) {
|
||||||
|
if dbg.Enabled {
|
||||||
|
return dbg.out.Write(p)
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new DebugPrinter on os.Stdout.
|
||||||
|
func New() *DebugPrinter {
|
||||||
|
return &DebugPrinter{
|
||||||
|
out: os.Stdout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFile sets up a new DebugPrinter to a file, truncating it if it exists.
|
||||||
|
func ToFile(path string) (*DebugPrinter, error) {
|
||||||
|
file, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DebugPrinter{
|
||||||
|
out: file,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// To sets up a new DebugPrint to an io.WriteCloser.
|
||||||
|
func To(w io.WriteCloser) *DebugPrinter {
|
||||||
|
return &DebugPrinter{
|
||||||
|
out: w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print calls fmt.Print if Enabled is true.
|
||||||
|
func (dbg DebugPrinter) Print(v ...interface{}) {
|
||||||
|
if dbg.Enabled {
|
||||||
|
fmt.Fprint(dbg.out, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println calls fmt.Println if Enabled is true.
|
||||||
|
func (dbg DebugPrinter) Println(v ...interface{}) {
|
||||||
|
if dbg.Enabled {
|
||||||
|
fmt.Fprintln(dbg.out, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf calls fmt.Printf if Enabled is true.
|
||||||
|
func (dbg DebugPrinter) Printf(format string, v ...interface{}) {
|
||||||
|
if dbg.Enabled {
|
||||||
|
fmt.Fprintf(dbg.out, format, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
120
dbg/dbg_test.go
Normal file
120
dbg/dbg_test.go
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package dbg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.sr.ht/~kisom/goutils/testio"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
buf := testio.NewBufCloser(nil)
|
||||||
|
dbg := New()
|
||||||
|
dbg.out = buf
|
||||||
|
|
||||||
|
dbg.Print("hello")
|
||||||
|
dbg.Println("hello")
|
||||||
|
dbg.Printf("hello %s", "world")
|
||||||
|
require.Equal(t, 0, buf.Len())
|
||||||
|
|
||||||
|
dbg.Enabled = true
|
||||||
|
dbg.Print("hello") // +5
|
||||||
|
dbg.Println("hello") // +6
|
||||||
|
dbg.Printf("hello %s", "world") // +11
|
||||||
|
require.Equal(t, 22, buf.Len())
|
||||||
|
|
||||||
|
err := dbg.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTo(t *testing.T) {
|
||||||
|
buf := testio.NewBufCloser(nil)
|
||||||
|
dbg := To(buf)
|
||||||
|
|
||||||
|
dbg.Print("hello")
|
||||||
|
dbg.Println("hello")
|
||||||
|
dbg.Printf("hello %s", "world")
|
||||||
|
require.Equal(t, 0, buf.Len())
|
||||||
|
|
||||||
|
dbg.Enabled = true
|
||||||
|
dbg.Print("hello") // +5
|
||||||
|
dbg.Println("hello") // +6
|
||||||
|
dbg.Printf("hello %s", "world") // +11
|
||||||
|
|
||||||
|
require.Equal(t, 22, buf.Len())
|
||||||
|
|
||||||
|
err := dbg.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToFile(t *testing.T) {
|
||||||
|
testFile, err := ioutil.TempFile("", "dbg")
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = testFile.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testFileName := testFile.Name()
|
||||||
|
defer os.Remove(testFileName)
|
||||||
|
|
||||||
|
dbg, err := ToFile(testFileName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
dbg.Print("hello")
|
||||||
|
dbg.Println("hello")
|
||||||
|
dbg.Printf("hello %s", "world")
|
||||||
|
|
||||||
|
stat, err := os.Stat(testFileName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.EqualValues(t, 0, stat.Size())
|
||||||
|
|
||||||
|
dbg.Enabled = true
|
||||||
|
dbg.Print("hello") // +5
|
||||||
|
dbg.Println("hello") // +6
|
||||||
|
dbg.Printf("hello %s", "world") // +11
|
||||||
|
|
||||||
|
stat, err = os.Stat(testFileName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.EqualValues(t, 22, stat.Size())
|
||||||
|
|
||||||
|
err = dbg.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriting(t *testing.T) {
|
||||||
|
data := []byte("hello, world")
|
||||||
|
buf := testio.NewBufCloser(nil)
|
||||||
|
dbg := To(buf)
|
||||||
|
|
||||||
|
n, err := dbg.Write(data)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, 0, n)
|
||||||
|
|
||||||
|
dbg.Enabled = true
|
||||||
|
n, err = dbg.Write(data)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, 12, n)
|
||||||
|
|
||||||
|
err = dbg.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToFileError(t *testing.T) {
|
||||||
|
testFile, err := ioutil.TempFile("", "dbg")
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = testFile.Chmod(0400)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = testFile.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testFileName := testFile.Name()
|
||||||
|
|
||||||
|
_, err = ToFile(testFileName)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
err = os.Remove(testFileName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
15
go.mod
Normal file
15
go.mod
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
module git.sr.ht/~kisom/goutils
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/cloudflare/cfssl v1.5.0
|
||||||
|
github.com/kisom/goutils v1.1.0
|
||||||
|
github.com/kr/text v0.2.0
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/pkg/sftp v1.12.0
|
||||||
|
github.com/stretchr/testify v1.6.1
|
||||||
|
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392
|
||||||
|
golang.org/x/sys v0.0.0-20201126233918-771906719818
|
||||||
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
|
)
|
||||||
105
go.sum
Normal file
105
go.sum
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
bitbucket.org/liamstask/goose v0.0.0-20150115234039-8488cc47d90c/go.mod h1:hSVuE3qU7grINVSwrmzHfpg9k87ALBk+XaualNyUzI4=
|
||||||
|
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
||||||
|
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
||||||
|
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||||
|
github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
|
||||||
|
github.com/cloudflare/backoff v0.0.0-20161212185259-647f3cdfc87a/go.mod h1:rzgs2ZOiguV6/NpiDgADjRLPNyZlApIWxKpkT+X8SdY=
|
||||||
|
github.com/cloudflare/cfssl v1.5.0 h1:vFJDAvQgFSRbCn9zg8KpSrrEZrBAQ4KO5oNK7SXEyb0=
|
||||||
|
github.com/cloudflare/cfssl v1.5.0/go.mod h1:sPPkBS5L8l8sRc/IOO1jG51Xb34u+TYhL6P//JdODMQ=
|
||||||
|
github.com/cloudflare/go-metrics v0.0.0-20151117154305-6a9aea36fb41/go.mod h1:eaZPlJWD+G9wseg1BuRXlHnjntPMrywMsyxf+LTOdP4=
|
||||||
|
github.com/cloudflare/redoctober v0.0.0-20171127175943-746a508df14c/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/getsentry/raven-go v0.0.0-20180121060056-563b81fc02b7/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||||
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
|
||||||
|
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||||
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548/go.mod h1:hGT6jSUVzF6no3QaDSMLGLEHtHSBSefs+MgcDWnmhmo=
|
||||||
|
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||||
|
github.com/kisielk/sqlstruct v0.0.0-20150923205031-648daed35d49/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||||
|
github.com/kisom/goutils v1.1.0 h1:z4HEOgAnFq+e1+O4QdVsyDPatJDu5Ei/7w7DRbYjsIA=
|
||||||
|
github.com/kisom/goutils v1.1.0/go.mod h1:+UBTfd78habUYWFbNWTJNG+jNG/i/lGURakr4A/yNRw=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||||
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/kylelemons/go-gypsy v0.0.0-20160905020020-08cad365cd28/go.mod h1:T/T7jsxVqf9k/zYOqbgNAsANsjxTd1Yq3htjDhQ1H0c=
|
||||||
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
|
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
|
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
|
||||||
|
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
|
||||||
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/sftp v1.12.0 h1:/f3b24xrDhkhddlaobPe2JgBqfdt+gC/NYl0QY9IOuI=
|
||||||
|
github.com/pkg/sftp v1.12.0/go.mod h1:fUqqXB5vEgVCZ131L+9say31RAri6aF6KDViawhxKK8=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||||
|
github.com/weppos/publicsuffix-go v0.4.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
||||||
|
github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
||||||
|
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||||
|
github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
|
||||||
|
github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is=
|
||||||
|
github.com/zmap/zcrypto v0.0.0-20200513165325-16679db567ff/go.mod h1:TxpejqcVKQjQaVVmMGfzx5HnmFMdIU+vLtaCyPBfGI4=
|
||||||
|
github.com/zmap/zcrypto v0.0.0-20200911161511-43ff0ea04f21/go.mod h1:TxpejqcVKQjQaVVmMGfzx5HnmFMdIU+vLtaCyPBfGI4=
|
||||||
|
github.com/zmap/zlint/v2 v2.2.1/go.mod h1:ixPWsdq8qLxYRpNUTbcKig3R7WgmspsHGLhCCs6rFAM=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200124225646-8b5121be2f68/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc=
|
||||||
|
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201126233918-771906719818 h1:f1CIuDlJhwANEC2MM87MBEVMr3jl5bifgsfj90XAF9c=
|
||||||
|
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build freebsd darwin netbsd
|
// +build freebsd darwin,386 netbsd
|
||||||
|
|
||||||
package lib
|
package lib
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build unix linux openbsd
|
// +build unix linux openbsd darwin,amd64
|
||||||
|
|
||||||
package lib
|
package lib
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/kisom/goutils/assert"
|
"git.sr.ht/~kisom/goutils/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// some CA certs I found on my computerbox.
|
// some CA certs I found on my computerbox.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kisom/goutils/logging"
|
"git.sr.ht/~kisom/goutils/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = logging.NewConsole()
|
var log = logging.NewConsole()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package logging_test
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kisom/goutils/logging"
|
"git.sr.ht/~kisom/goutils/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = logging.NewConsole()
|
var log = logging.NewConsole()
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ func (fl *File) Close() error {
|
|||||||
if fl.fe != nil {
|
if fl.fe != nil {
|
||||||
return fl.fe.Close()
|
return fl.fe.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFile creates a new Logger that writes all logs to the file
|
// NewFile creates a new Logger that writes all logs to the file
|
||||||
@@ -57,7 +59,7 @@ func NewSplitFile(outpath, errpath string, overwrite bool) (*File, error) {
|
|||||||
if overwrite {
|
if overwrite {
|
||||||
fl.fo, err = os.Create(outpath)
|
fl.fo, err = os.Create(outpath)
|
||||||
} else {
|
} else {
|
||||||
fl.fo, err = os.OpenFile(outpath, os.O_WRONLY|os.O_APPEND, 0644)
|
fl.fo, err = os.OpenFile(outpath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -67,7 +69,7 @@ func NewSplitFile(outpath, errpath string, overwrite bool) (*File, error) {
|
|||||||
if overwrite {
|
if overwrite {
|
||||||
fl.fe, err = os.Create(errpath)
|
fl.fe, err = os.Create(errpath)
|
||||||
} else {
|
} else {
|
||||||
fl.fe, err = os.OpenFile(errpath, os.O_WRONLY|os.O_APPEND, 0644)
|
fl.fe, err = os.OpenFile(errpath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ type Logger interface {
|
|||||||
Status() error
|
Status() error
|
||||||
|
|
||||||
// Close gives the Logger the opportunity to perform any cleanup.
|
// Close gives the Logger the opportunity to perform any cleanup.
|
||||||
Close()
|
Close() error
|
||||||
|
|
||||||
// Log messages consist of four components:
|
// Log messages consist of four components:
|
||||||
//
|
//
|
||||||
@@ -276,4 +276,94 @@ func (lw *LogWriter) SetLevel(l Level) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close is a no-op that satisfies the Logger interface.
|
// Close is a no-op that satisfies the Logger interface.
|
||||||
func (lw *LogWriter) Close() {}
|
func (lw *LogWriter) Close() error { return nil }
|
||||||
|
|
||||||
|
// Multi allows combining of loggers.
|
||||||
|
type Multi struct {
|
||||||
|
loggers []Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMulti(loggers ...Logger) *Multi {
|
||||||
|
return &Multi{loggers: loggers}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multi) SetLevel(level Level) {
|
||||||
|
for _, l := range m.loggers {
|
||||||
|
l.SetLevel(level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multi) Good() bool {
|
||||||
|
good := true
|
||||||
|
for _, l := range m.loggers {
|
||||||
|
good = good && l.Good()
|
||||||
|
}
|
||||||
|
|
||||||
|
return good
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multi) Status() error {
|
||||||
|
for _, l := range m.loggers {
|
||||||
|
if err := l.Status(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multi) Close() error {
|
||||||
|
for _, l := range m.loggers {
|
||||||
|
l.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multi) Debug(actor, event string, attrs map[string]string) {
|
||||||
|
for _, l := range m.loggers {
|
||||||
|
l.Debug(actor, event, attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multi) Info(actor, event string, attrs map[string]string) {
|
||||||
|
for _, l := range m.loggers {
|
||||||
|
l.Info(actor, event, attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multi) Warn(actor, event string, attrs map[string]string) {
|
||||||
|
for _, l := range m.loggers {
|
||||||
|
l.Warn(actor, event, attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multi) Error(actor, event string, attrs map[string]string) {
|
||||||
|
for _, l := range m.loggers {
|
||||||
|
l.Error(actor, event, attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multi) Critical(actor, event string, attrs map[string]string) {
|
||||||
|
for _, l := range m.loggers {
|
||||||
|
l.Critical(actor, event, attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multi) Fatal(actor, event string, attrs map[string]string) {
|
||||||
|
for _, l := range m.loggers {
|
||||||
|
l.Fatal(actor, event, attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multi) FatalCode(exitcode int, actor, event string, attrs map[string]string) {
|
||||||
|
for _, l := range m.loggers {
|
||||||
|
l.FatalCode(exitcode, actor, event, attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multi) FatalNoDie(actor, event string, attrs map[string]string) {
|
||||||
|
for _, l := range m.loggers {
|
||||||
|
l.FatalNoDie(actor, event, attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,3 +53,12 @@ func TestDestroyLogFiles(t *testing.T) {
|
|||||||
os.Remove("fw2.log")
|
os.Remove("fw2.log")
|
||||||
os.Remove("fw2.err")
|
os.Remove("fw2.err")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMulti(t *testing.T) {
|
||||||
|
c1 := NewConsole()
|
||||||
|
c2 := NewConsole()
|
||||||
|
m := NewMulti(c1, c2)
|
||||||
|
if !m.Good() {
|
||||||
|
t.Fatal("failed to set up multi logger")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/kisom/goutils/assert"
|
"git.sr.ht/~kisom/goutils/assert"
|
||||||
"github.com/kisom/goutils/testio"
|
"git.sr.ht/~kisom/goutils/testio"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMWC(t *testing.T) {
|
func TestMWC(t *testing.T) {
|
||||||
|
|||||||
49
rand/rand.go
Normal file
49
rand/rand.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Package rand contains utilities for interacting with math/rand, including
|
||||||
|
// seeding from a random sed.
|
||||||
|
package rand
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
mrand "math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CryptoUint64 generates a cryptographically-secure 64-bit integer.
|
||||||
|
func CryptoUint64() (uint64, error) {
|
||||||
|
bs := make([]byte, 8)
|
||||||
|
_, err := rand.Read(bs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return binary.BigEndian.Uint64(bs), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seed initialises the non-cryptographic PRNG with a random,
|
||||||
|
// cryptographically secure value. This is done just as a good
|
||||||
|
// way to make this random. The returned 64-bit value is the seed.
|
||||||
|
func Seed() (uint64, error) {
|
||||||
|
seed, err := CryptoUint64()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: this is permitted.
|
||||||
|
mrand.Seed(int64(seed))
|
||||||
|
return seed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int is a wrapper for math.Int so only one package needs to be imported.
|
||||||
|
func Int() int {
|
||||||
|
return mrand.Int()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intn is a wrapper for math.Intn so only one package needs to be imported.
|
||||||
|
func Intn(max int) int {
|
||||||
|
return mrand.Intn(max)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intn2 returns a random value between min and max, inclusive.
|
||||||
|
func Intn2(min, max int) int {
|
||||||
|
return Intn(max-min) + min
|
||||||
|
}
|
||||||
74
rand/rand_test.go
Normal file
74
rand/rand_test.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package rand
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
mrand "math/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCryptoUint64(t *testing.T) {
|
||||||
|
n1, err := CryptoUint64()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n2, err := CryptoUint64()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This has such a low chance of occurring that it's likely to be
|
||||||
|
// indicative of a bad CSPRNG.
|
||||||
|
if n1 == n2 {
|
||||||
|
t.Fatalf("repeated random uint64s: %d", n1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntn(t *testing.T) {
|
||||||
|
expected := []int{3081, 4887, 4847, 1059, 3081}
|
||||||
|
mrand.Seed(1)
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
n := Intn2(1000, 5000)
|
||||||
|
|
||||||
|
if n != expected[i] {
|
||||||
|
fmt.Printf("invalid sequence at %d: expected %d, have %d", i, expected[i], n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSeed(t *testing.T) {
|
||||||
|
seed1, err := Seed()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var seed2 uint64
|
||||||
|
n1 := Int()
|
||||||
|
tries := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
seed2, err = Seed()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if seed1 != seed2 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
tries++
|
||||||
|
|
||||||
|
if tries > 3 {
|
||||||
|
t.Fatal("can't generate two unique seeds")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n2 := Int()
|
||||||
|
|
||||||
|
// Again, this not impossible, merely statistically improbably and a
|
||||||
|
// potential canary for RNG issues.
|
||||||
|
if n1 == n2 {
|
||||||
|
t.Fatalf("repeated integers fresh from two unique seeds: %d/%d -> %d",
|
||||||
|
seed1, seed2, n1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -168,7 +168,7 @@ func TestRWByte(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c != 42 {
|
if c != 42 {
|
||||||
t.Fatal("Expected 42, have %d", c)
|
t.Fatalf("Expected 42, have %d", c)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = buf.ReadByte()
|
_, err = buf.ReadByte()
|
||||||
|
|||||||
50
seekbuf/seekbuf.go
Normal file
50
seekbuf/seekbuf.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package seekbuf
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
// Buffer is a ReadWriteCloser that supports seeking. It's intended to
|
||||||
|
// replicate the functionality of bytes.Buffer that I use in my projects.
|
||||||
|
//
|
||||||
|
// Note that the seeking is limited to the read marker; all writes are
|
||||||
|
// append-only.
|
||||||
|
type Buffer struct {
|
||||||
|
data []byte
|
||||||
|
pos int
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(data []byte) *Buffer {
|
||||||
|
return &Buffer{
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) Read(p []byte) (int, error) {
|
||||||
|
if b.pos >= len(b.data) {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
n := copy(p, b.data[b.pos:])
|
||||||
|
b.pos += n
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) Write(p []byte) (int, error) {
|
||||||
|
b.data = append(b.data, p...)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek sets the read pointer to pos.
|
||||||
|
func (b *Buffer) Seek(pos int) {
|
||||||
|
b.pos = pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewind resets the read pointer to 0.
|
||||||
|
func (b *Buffer) Rewind() {
|
||||||
|
b.pos = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close clears all the data out of the buffer and sets the read position to 0.
|
||||||
|
func (b *Buffer) Close() error {
|
||||||
|
b.Clear()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -194,6 +194,11 @@ func (buf *BufCloser) Bytes() []byte {
|
|||||||
return buf.buf.Bytes()
|
return buf.buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Len returns the length of the buffer.
|
||||||
|
func (buf *BufCloser) Len() int {
|
||||||
|
return buf.buf.Len()
|
||||||
|
}
|
||||||
|
|
||||||
// NewBufCloser creates and initializes a new BufCloser using buf as
|
// NewBufCloser creates and initializes a new BufCloser using buf as
|
||||||
// its initial contents. It is intended to prepare a BufCloser to read
|
// its initial contents. It is intended to prepare a BufCloser to read
|
||||||
// existing data. It can also be used to size the internal buffer for
|
// existing data. It can also be used to size the internal buffer for
|
||||||
|
|||||||
Reference in New Issue
Block a user