122 lines
2.7 KiB
Go
122 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// readPassword reads a line from stdin with terminal echo disabled, if stdin is a tty.
|
|
func readPassword(prompt string) (string, error) {
|
|
fmt.Fprint(os.Stderr, prompt)
|
|
|
|
restore := func() {}
|
|
if cmd := exec.Command("stty", "-echo"); true {
|
|
cmd.Stdin = os.Stdin
|
|
if cmd.Run() == nil {
|
|
restore = func() {
|
|
r := exec.Command("stty", "echo")
|
|
r.Stdin = os.Stdin
|
|
r.Run()
|
|
}
|
|
}
|
|
}
|
|
defer restore()
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
line, err := reader.ReadString('\n')
|
|
fmt.Fprintln(os.Stderr)
|
|
if err != nil && line == "" {
|
|
return "", err
|
|
}
|
|
return strings.TrimRight(line, "\r\n"), nil
|
|
}
|
|
|
|
func readLine(prompt string) (string, error) {
|
|
fmt.Fprint(os.Stderr, prompt)
|
|
reader := bufio.NewReader(os.Stdin)
|
|
line, err := reader.ReadString('\n')
|
|
if err != nil && line == "" {
|
|
return "", err
|
|
}
|
|
return strings.TrimSpace(line), nil
|
|
}
|
|
|
|
// parseKV parses ["f_name=foo", "l_dep=1"] into {"f_name":"foo","l_dep":"1"}.
|
|
func parseKV(args []string) (map[string]interface{}, error) {
|
|
out := map[string]interface{}{}
|
|
for _, a := range args {
|
|
i := strings.IndexByte(a, '=')
|
|
if i < 0 {
|
|
return nil, fmt.Errorf("expected key=value, got %q", a)
|
|
}
|
|
out[a[:i]] = a[i+1:]
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func sortedKeys(m map[string]interface{}) []string {
|
|
keys := make([]string, 0, len(m))
|
|
for k := range m {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
return keys
|
|
}
|
|
|
|
func toStr(v interface{}) string {
|
|
if v == nil {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("%v", v)
|
|
}
|
|
|
|
// printIDs prints a []interface{} of numeric ids, one per line.
|
|
func printIDs(ids []interface{}) {
|
|
for _, id := range ids {
|
|
fmt.Println(toStr(id))
|
|
}
|
|
}
|
|
|
|
// printDevice pretty-prints one device record, using field labels when available.
|
|
func printDevice(dev map[string]interface{}, labels map[string]interface{}) {
|
|
lead := []string{"id", "f_name", "user", "grp", "rm", "ts"}
|
|
printed := map[string]bool{}
|
|
for _, k := range lead {
|
|
if v, ok := dev[k]; ok {
|
|
printDeviceField(k, labels, v)
|
|
printed[k] = true
|
|
}
|
|
}
|
|
for _, k := range sortedKeys(dev) {
|
|
if printed[k] {
|
|
continue
|
|
}
|
|
printDeviceField(k, labels, dev[k])
|
|
}
|
|
}
|
|
|
|
func printDeviceField(k string, labels map[string]interface{}, v interface{}) {
|
|
label := paint(colorStdout, colBlue, padRight(labelFor(k, labels)+":", 20))
|
|
if k == "id" {
|
|
fmt.Printf("%s %v\n", label, idText(fmt.Sprintf("%v", v)))
|
|
return
|
|
}
|
|
fmt.Printf("%s %v\n", label, v)
|
|
}
|
|
|
|
func labelFor(col string, labels map[string]interface{}) string {
|
|
if labels == nil {
|
|
return col
|
|
}
|
|
if meta, ok := labels[col].(map[string]interface{}); ok {
|
|
if name, ok := meta["name"].(string); ok && name != "" {
|
|
return name
|
|
}
|
|
}
|
|
return col
|
|
}
|