2020-10-11 13:08:13 +02:00
|
|
|
package misc
|
2016-11-15 16:23:20 +01:00
|
|
|
|
|
|
|
import (
|
2018-07-01 16:03:26 +02:00
|
|
|
"bufio"
|
2020-10-22 11:20:20 +02:00
|
|
|
"bytes"
|
2018-07-01 16:03:26 +02:00
|
|
|
"errors"
|
2018-11-04 23:07:08 +01:00
|
|
|
"flag"
|
2018-07-01 16:03:26 +02:00
|
|
|
"fmt"
|
2019-02-12 00:17:21 +01:00
|
|
|
"io"
|
2016-11-15 16:23:20 +01:00
|
|
|
"io/ioutil"
|
2019-02-11 23:52:19 +01:00
|
|
|
"net/http"
|
2018-07-01 16:03:26 +02:00
|
|
|
"os"
|
2018-11-04 23:07:08 +01:00
|
|
|
"os/exec"
|
2019-01-12 22:26:31 +01:00
|
|
|
"os/user"
|
2016-11-15 16:23:20 +01:00
|
|
|
"path/filepath"
|
2018-11-04 23:07:08 +01:00
|
|
|
"runtime"
|
2016-11-19 00:55:13 +01:00
|
|
|
"strings"
|
2016-11-15 16:23:20 +01:00
|
|
|
)
|
|
|
|
|
2018-07-01 16:03:26 +02:00
|
|
|
func DecorateError(s string, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return errors.New(s + ": " + err.Error())
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-15 16:23:20 +01:00
|
|
|
func WriteFile(filename string, data string) error {
|
|
|
|
return ioutil.WriteFile(filename, []byte(data), 0644)
|
|
|
|
}
|
|
|
|
|
2018-11-04 23:07:08 +01:00
|
|
|
func AppendToFile(filename string, data string) error {
|
|
|
|
f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
|
|
|
|
if err != nil {
|
|
|
|
return errors.New("AppendToFile " + filename + ": " + err.Error())
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
_, err = f.WriteString(data)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-11-15 16:23:20 +01:00
|
|
|
func ReadFile(filename string) (string, error) {
|
|
|
|
data, err := ioutil.ReadFile(filename)
|
|
|
|
return string(data), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func TrimExt(path string) string {
|
|
|
|
extension := filepath.Ext(path)
|
|
|
|
return path[0 : len(path)-len(extension)]
|
|
|
|
}
|
2016-11-19 00:55:13 +01:00
|
|
|
|
|
|
|
func ListFilesExt(dir string, ext string) []string {
|
|
|
|
list := make([]string, 0)
|
|
|
|
ext = strings.ToUpper(ext)
|
|
|
|
files, err := ioutil.ReadDir(dir)
|
|
|
|
if err == nil {
|
|
|
|
for _, file := range files {
|
|
|
|
if !file.IsDir() {
|
|
|
|
if strings.ToUpper(filepath.Ext(file.Name())) == ext {
|
2019-01-03 17:11:50 +01:00
|
|
|
list = append(list, filepath.Join(dir, file.Name()))
|
2016-11-19 00:55:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list
|
|
|
|
}
|
2018-07-01 16:03:26 +02:00
|
|
|
|
2018-12-08 20:13:16 +01:00
|
|
|
func RecListFilesExt(dir string, ext string) []string {
|
|
|
|
list := make([]string, 0)
|
|
|
|
traverse := func(path string, info os.FileInfo, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if info.IsDir() {
|
|
|
|
list = append(list, ListFilesExt(path, ext)...)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
filepath.Walk(dir, traverse)
|
|
|
|
return list
|
|
|
|
}
|
|
|
|
|
2018-07-01 16:03:26 +02:00
|
|
|
func PathExists(path string) bool {
|
|
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
|
|
return false
|
|
|
|
} else {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-23 15:51:20 +01:00
|
|
|
func DirsWithPrefix(path string, prefix string) ([]string, error) {
|
|
|
|
dirs := []string{}
|
|
|
|
files, err := ioutil.ReadDir(path)
|
|
|
|
if err != nil {
|
|
|
|
return dirs, err
|
|
|
|
}
|
|
|
|
for _, file := range files {
|
|
|
|
if file.IsDir() {
|
|
|
|
if name := file.Name(); strings.HasPrefix(name, prefix) {
|
|
|
|
dirs = append(dirs, name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dirs, nil
|
|
|
|
}
|
|
|
|
|
2019-01-12 22:26:31 +01:00
|
|
|
func HomeDir() (string, error) {
|
|
|
|
d := ""
|
|
|
|
currentUser, err := user.Current()
|
|
|
|
if err == nil {
|
|
|
|
d = currentUser.HomeDir
|
|
|
|
}
|
|
|
|
return d, err
|
|
|
|
}
|
|
|
|
|
2018-07-01 16:03:26 +02:00
|
|
|
func AskFor(question string) (string, error) {
|
|
|
|
fmt.Print(question + ": ")
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
|
|
s, err := reader.ReadString('\n')
|
|
|
|
if err == nil {
|
|
|
|
return strings.TrimSpace(s), nil
|
|
|
|
} else {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-22 19:50:52 +01:00
|
|
|
func OptDo(err error, f func() error) error {
|
|
|
|
if err == nil {
|
|
|
|
return f()
|
|
|
|
} else {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-22 20:12:34 +01:00
|
|
|
func OptPanic(err error) {
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-01 16:03:26 +02:00
|
|
|
func IntMin(x, y int) int {
|
|
|
|
if x < y {
|
|
|
|
return x
|
|
|
|
} else {
|
|
|
|
return y
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func IntMax(x, y int) int {
|
|
|
|
if x > y {
|
|
|
|
return x
|
|
|
|
} else {
|
|
|
|
return y
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func StrSliceAt(sl []string, i int) string {
|
|
|
|
if i < len(sl) {
|
|
|
|
return sl[i]
|
|
|
|
} else {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func StrSlice(sl []string, start int, end int) []string {
|
|
|
|
bound := func(i int) int { return IntMin(IntMax(0, i), len(sl)) }
|
|
|
|
start = bound(start)
|
|
|
|
end = bound(end)
|
|
|
|
return sl[start:end]
|
|
|
|
}
|
2018-11-04 23:07:08 +01:00
|
|
|
|
|
|
|
func OpenInDefaultApp(filename string, wait bool) error {
|
|
|
|
var cmd *exec.Cmd
|
2018-12-10 17:06:59 +01:00
|
|
|
goos := runtime.GOOS
|
|
|
|
switch goos {
|
2018-11-04 23:07:08 +01:00
|
|
|
case "windows":
|
|
|
|
cmd = exec.Command(filepath.Join(os.Getenv("SYSTEMROOT"), "System32", "rundll32.exe"), "url.dll,FileProtocolHandler", filename)
|
|
|
|
case "darwin":
|
|
|
|
cmd = exec.Command("open", filename)
|
|
|
|
default:
|
|
|
|
cmd = exec.Command("xdg-open", filename)
|
|
|
|
}
|
|
|
|
if wait {
|
2018-12-10 17:06:59 +01:00
|
|
|
err := cmd.Run()
|
|
|
|
if goos == "windows" {
|
|
|
|
AskFor("Press Enter when you're done")
|
|
|
|
}
|
|
|
|
return err
|
2018-11-04 23:07:08 +01:00
|
|
|
} else {
|
|
|
|
return cmd.Start()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-10 17:16:26 +01:00
|
|
|
func OpenInEditor(filename string, wait bool) error {
|
|
|
|
var err error
|
|
|
|
goos := runtime.GOOS
|
|
|
|
if ed := os.Getenv("EDITOR"); ed != "" {
|
|
|
|
cmd := exec.Command(ed, filename)
|
|
|
|
cmd.Env = os.Environ()
|
|
|
|
if wait {
|
|
|
|
err = cmd.Run()
|
|
|
|
if goos == "windows" {
|
|
|
|
AskFor("Press Enter when you're done")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = cmd.Start()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = errors.New("EDITOR not set in environment")
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-11-04 23:07:08 +01:00
|
|
|
type CommandFunc func(args []string) error
|
|
|
|
|
|
|
|
type CommandFlagsInit func(s *flag.FlagSet)
|
|
|
|
|
|
|
|
type CommandInitExecFunc func() (CommandFlagsInit, CommandFunc)
|
|
|
|
|
|
|
|
type Command struct {
|
2020-05-29 10:10:30 +02:00
|
|
|
Cmd string
|
|
|
|
Exec CommandFunc
|
|
|
|
Desc string
|
|
|
|
Flags *flag.FlagSet
|
2018-11-04 23:07:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewCommandWithFlags(cmd string, f CommandInitExecFunc, desc string) Command {
|
|
|
|
flagsInit, exec := f()
|
|
|
|
var flags *flag.FlagSet
|
|
|
|
if flagsInit != nil {
|
|
|
|
flags = flag.NewFlagSet(cmd, flag.ExitOnError)
|
|
|
|
flagsInit(flags)
|
|
|
|
}
|
|
|
|
c := Command{cmd, exec, desc, flags}
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewCommand(cmd string, f CommandFunc, desc string) Command {
|
|
|
|
return NewCommandWithFlags(cmd, func() (CommandFlagsInit, CommandFunc) { return nil, f }, desc)
|
|
|
|
}
|
|
|
|
|
|
|
|
type commandCollection map[string]Command
|
|
|
|
|
2018-12-09 18:14:38 +01:00
|
|
|
func usageAndExit(cmds []Command) {
|
2018-12-09 23:57:43 +01:00
|
|
|
app := os.Args[0]
|
|
|
|
fmt.Println(app)
|
2018-12-09 10:06:27 +01:00
|
|
|
fmt.Println()
|
2018-11-04 23:07:08 +01:00
|
|
|
fmt.Println("Possible commands:")
|
|
|
|
for _, cmd := range cmds {
|
2020-05-29 10:10:30 +02:00
|
|
|
fmt.Println(" " + cmd.Cmd)
|
|
|
|
fmt.Println(" \t" + cmd.Desc)
|
2018-11-04 23:07:08 +01:00
|
|
|
}
|
|
|
|
fmt.Println()
|
2018-12-09 23:57:43 +01:00
|
|
|
fmt.Println("for detailed information type '" + app + " help COMMAND'")
|
2018-11-04 23:07:08 +01:00
|
|
|
os.Exit(-1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func Execute(possibleCommands []Command) error {
|
|
|
|
commands := commandCollection{}
|
|
|
|
for _, c := range possibleCommands {
|
2020-05-29 10:10:30 +02:00
|
|
|
commands[c.Cmd] = c
|
2018-11-04 23:07:08 +01:00
|
|
|
}
|
|
|
|
arglen := len(os.Args)
|
|
|
|
if arglen <= 1 {
|
2018-12-09 18:14:38 +01:00
|
|
|
usageAndExit(possibleCommands)
|
2018-11-04 23:07:08 +01:00
|
|
|
}
|
|
|
|
cmdStr := os.Args[1]
|
|
|
|
if cmdStr == "help" {
|
|
|
|
if arglen == 2 {
|
2018-12-09 18:14:38 +01:00
|
|
|
usageAndExit(possibleCommands)
|
2018-11-04 23:07:08 +01:00
|
|
|
} else {
|
|
|
|
cmd, ok := commands[os.Args[2]]
|
|
|
|
if !ok {
|
2018-12-09 18:14:38 +01:00
|
|
|
usageAndExit(possibleCommands)
|
2018-11-04 23:07:08 +01:00
|
|
|
} else {
|
2020-05-29 10:10:30 +02:00
|
|
|
fmt.Println("Description of " + cmd.Cmd + ":")
|
|
|
|
fmt.Println(" " + cmd.Desc)
|
|
|
|
if cmd.Flags != nil {
|
2018-11-04 23:07:08 +01:00
|
|
|
fmt.Println("Possible flags:")
|
2020-05-29 10:10:30 +02:00
|
|
|
cmd.Flags.PrintDefaults()
|
2018-11-04 23:07:08 +01:00
|
|
|
} else {
|
|
|
|
fmt.Println("No flags")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cmd, ok := commands[cmdStr]
|
|
|
|
if !ok {
|
2018-12-09 18:14:38 +01:00
|
|
|
usageAndExit(possibleCommands)
|
2018-11-04 23:07:08 +01:00
|
|
|
}
|
2020-05-29 10:10:30 +02:00
|
|
|
if cmd.Flags != nil {
|
|
|
|
cmd.Flags.Parse(os.Args[2:])
|
|
|
|
return cmd.Exec(cmd.Flags.Args())
|
2018-11-04 23:07:08 +01:00
|
|
|
} else {
|
2020-05-29 10:10:30 +02:00
|
|
|
return cmd.Exec(os.Args[2:])
|
2018-11-04 23:07:08 +01:00
|
|
|
}
|
|
|
|
}
|
2019-01-13 10:24:46 +01:00
|
|
|
|
|
|
|
func Notify(head string, body string) error {
|
|
|
|
var cmd *exec.Cmd
|
|
|
|
goos := runtime.GOOS
|
|
|
|
switch goos {
|
|
|
|
case "windows":
|
|
|
|
cmd = nil
|
|
|
|
case "darwin":
|
|
|
|
cmd = exec.Command("osascript", "-e", `display notification "`+body+`" with title "`+head+`"`)
|
|
|
|
default:
|
|
|
|
cmd = exec.Command("notify-send", head, body)
|
|
|
|
}
|
|
|
|
if cmd != nil {
|
|
|
|
return cmd.Start()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2019-02-11 23:52:19 +01:00
|
|
|
|
2020-10-22 11:20:20 +02:00
|
|
|
func DownloadAll(url string, limit int64) ([]byte, error) {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
err := download(url, &buf, limit)
|
|
|
|
return buf.Bytes(), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func DownloadToFile(url string, dest string, limit int64) error {
|
|
|
|
file, err := os.Create(dest)
|
2019-02-11 23:52:19 +01:00
|
|
|
if err != nil {
|
2020-10-22 11:20:20 +02:00
|
|
|
return errors.New("DownloadToFile: " + err.Error())
|
2019-02-11 23:52:19 +01:00
|
|
|
}
|
2020-10-22 11:20:20 +02:00
|
|
|
defer file.Close()
|
|
|
|
return download(url, file, limit)
|
2019-02-11 23:52:19 +01:00
|
|
|
}
|
2019-02-12 00:17:21 +01:00
|
|
|
|
2020-10-22 11:20:20 +02:00
|
|
|
func download(url string, dest io.Writer, limit int64) error {
|
2019-02-12 00:17:21 +01:00
|
|
|
resp, err := http.Get(url)
|
|
|
|
if err != nil {
|
2020-10-22 11:20:20 +02:00
|
|
|
return errors.New("download: " + err.Error())
|
2019-02-12 00:17:21 +01:00
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
2020-10-22 11:20:20 +02:00
|
|
|
var reader io.Reader
|
|
|
|
if limit > 0 {
|
|
|
|
reader = io.LimitReader(resp.Body, limit)
|
|
|
|
} else {
|
|
|
|
reader = resp.Body
|
2019-02-12 00:17:21 +01:00
|
|
|
}
|
2020-10-22 11:20:20 +02:00
|
|
|
_, err = io.Copy(dest, reader)
|
2019-02-12 00:17:21 +01:00
|
|
|
return err
|
|
|
|
}
|