Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 06678499d4 | |||
| fad17065fe | |||
| 63e0cbeacb | |||
| 231b98dd68 | |||
|
|
160a42ec26 | ||
|
|
b6b33e00c8 | ||
|
|
9e1aed257b | ||
|
|
411907c0ad | ||
|
|
06c7f8f42f | ||
|
|
8b638065d1 | ||
|
|
9ac378eaa5 | ||
|
|
eaaaabe439 | ||
|
|
4122f01644 | ||
|
|
263a5d3973 | ||
|
|
afef3eea62 |
17
.travis.yml
Normal file
17
.travis.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- tip
|
||||
- 1.9
|
||||
script:
|
||||
- go get github.com/golang/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
|
||||
|
||||
+ A number of new command line utilities were added
|
||||
|
||||
@@ -3,6 +3,9 @@ GOUTILS
|
||||
This is a collection of small utility code I've written in Go; the `cmd/`
|
||||
directory has a number of command-line utilities. Rather than keep all
|
||||
of these in superfluous repositories of their own, I'm putting them here.
|
||||
Note that for packaging purposes, the goutils-pkg repo should be used: it
|
||||
pins the library versions to working copies and vendors all depdencies. See
|
||||
https://github.com/kisom/goutils-pkg for more details.
|
||||
|
||||
Contents:
|
||||
|
||||
@@ -25,6 +28,8 @@ Contents:
|
||||
csrpubdump/ Dump the public key from an X.509 certificate request.
|
||||
fragment/ Print a fragment of a file.
|
||||
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.
|
||||
pembody/ Print the body of a PEM certificate.
|
||||
pemit/ Dump data to a PEM file.
|
||||
|
||||
@@ -97,7 +97,7 @@ func TestHash32(t *testing.T) {
|
||||
h.Write(data)
|
||||
sum, ok = h.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) {
|
||||
@@ -129,7 +129,7 @@ func TestHash64(t *testing.T) {
|
||||
h.Write(data)
|
||||
sum, ok = h.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) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudflare/cfssl/helpers"
|
||||
@@ -83,6 +84,7 @@ func keyUsages(ku x509.KeyUsage) string {
|
||||
uses = append(uses, s)
|
||||
}
|
||||
}
|
||||
sort.Strings(uses)
|
||||
|
||||
return strings.Join(uses, ", ")
|
||||
}
|
||||
@@ -92,6 +94,7 @@ func extUsage(ext []x509.ExtKeyUsage) string {
|
||||
for i := range ext {
|
||||
ns = append(ns, extKeyUsages[ext[i]])
|
||||
}
|
||||
sort.Strings(ns)
|
||||
|
||||
return strings.Join(ns, ", ")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,6 @@ func newName(path string) (string, error) {
|
||||
func move(dst, src string, force bool) (err error) {
|
||||
if fileutil.FileDoesExist(dst) && !force {
|
||||
return fmt.Errorf("%s exists (pass the -f flag to overwrite)", dst)
|
||||
return nil
|
||||
}
|
||||
dstFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -15,20 +14,6 @@ import (
|
||||
"github.com/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) {
|
||||
fmt.Fprintf(w, `Usage: %s [-a algo] [-h] [-l set] urls...
|
||||
Compute the hash over each URL.
|
||||
@@ -91,13 +76,19 @@ func main() {
|
||||
continue
|
||||
}
|
||||
|
||||
body, err := fetch(remote)
|
||||
resp, err := http.Get(remote)
|
||||
if err != nil {
|
||||
lib.Warn(err, "fetching %s", remote)
|
||||
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 {
|
||||
lib.Err(lib.ExitFailure, err, "while hashing data")
|
||||
}
|
||||
|
||||
@@ -67,6 +67,10 @@ func init() {
|
||||
}
|
||||
|
||||
func walkFile(path string, info os.FileInfo, err error) error {
|
||||
if ignores[path] {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
if !sourceRegexp.MatchString(path) {
|
||||
return nil
|
||||
}
|
||||
@@ -97,10 +101,24 @@ func walkFile(path string, info os.FileInfo, err error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ignores = map[string]bool{}
|
||||
|
||||
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.Parse()
|
||||
|
||||
if noVendor {
|
||||
ignores["vendor"] = true
|
||||
}
|
||||
|
||||
for _, word := range strings.Split(ignoreLine, ",") {
|
||||
ignores[strings.TrimSpace(word)] = true
|
||||
}
|
||||
|
||||
err := filepath.Walk(".", walkFile)
|
||||
die.If(err)
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ Flags:
|
||||
|
||||
func usageExamples() {
|
||||
usage(os.Stdout)
|
||||
fmt.Println(`
|
||||
fmt.Printf(`
|
||||
Examples (note that the examples are done in the America/Los_Angeles /
|
||||
PST8PDT time zone):
|
||||
|
||||
@@ -134,6 +134,7 @@ PST8PDT time zone):
|
||||
(Converting from GMT (offset +0000) to UTC (offset +0000).)
|
||||
==================================================================
|
||||
2016-06-14 23:46 = 2016-06-14 23:46
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ func (fl *File) Close() error {
|
||||
if fl.fe != nil {
|
||||
return fl.fe.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewFile creates a new Logger that writes all logs to the file
|
||||
|
||||
@@ -21,7 +21,7 @@ type Logger interface {
|
||||
Status() error
|
||||
|
||||
// Close gives the Logger the opportunity to perform any cleanup.
|
||||
Close()
|
||||
Close() error
|
||||
|
||||
// Log messages consist of four components:
|
||||
//
|
||||
@@ -276,4 +276,4 @@ func (lw *LogWriter) SetLevel(l Level) {
|
||||
}
|
||||
|
||||
// Close is a no-op that satisfies the Logger interface.
|
||||
func (lw *LogWriter) Close() {}
|
||||
func (lw *LogWriter) Close() error { return nil }
|
||||
|
||||
@@ -168,7 +168,7 @@ func TestRWByte(t *testing.T) {
|
||||
}
|
||||
|
||||
if c != 42 {
|
||||
t.Fatal("Expected 42, have %d", c)
|
||||
t.Fatalf("Expected 42, have %d", c)
|
||||
}
|
||||
|
||||
_, err = buf.ReadByte()
|
||||
|
||||
Reference in New Issue
Block a user