diff --git a/cliphist.go b/cliphist.go index 46f9860..6ffa3bb 100644 --- a/cliphist.go +++ b/cliphist.go @@ -44,34 +44,40 @@ func main() { maxItems := flag.Uint64("max-items", 750, "maximum number of items to store") maxDedupeSearch := flag.Uint64("max-dedupe-search", 100, "maximum number of last items to look through when finding duplicates") previewWidth := flag.Uint("preview-width", 100, "maximum number of characters to preview") + dbPath := flag.String("db-path", "$XDG_CACHE_HOME/cliphist/db", "path to db") configPath := flag.String("config-path", "$XDG_CONFIG_HOME/cliphist/config", "overwrite config path to use instead of cli flags") flag.Parse() flagconf.ParseEnv() flagconf.ParseConfig(*configPath) + *dbPath = os.ExpandEnv(*dbPath) + var err error switch flag.Arg(0) { case "store": switch os.Getenv("CLIPBOARD_STATE") { // from man wl-clipboard case "sensitive": case "clear": - err = deleteLast() + err = deleteLast(*dbPath) default: - err = store(os.Stdin, *maxDedupeSearch, *maxItems) + err = store(*dbPath, os.Stdin, *maxDedupeSearch, *maxItems) } case "list": - err = list(os.Stdout, *previewWidth) + err = list(*dbPath, os.Stdout, *previewWidth) case "decode": - err = decode(os.Stdin, os.Stdout, flag.Arg(1)) + err = decode(*dbPath, os.Stdin, os.Stdout, flag.Arg(1)) case "delete-query": - err = deleteQuery(flag.Arg(1)) + err = deleteQuery(*dbPath, flag.Arg(1)) case "delete": - err = delete(os.Stdin) + err = delete(*dbPath, os.Stdin) case "wipe": - err = wipe() + err = wipe(*dbPath) case "version": - fmt.Fprint(os.Stderr, version) + fmt.Fprintf(os.Stderr, "%s\t%s\n", "version", strings.TrimSpace(version)) + flag.VisitAll(func(f *flag.Flag) { + fmt.Fprintf(os.Stderr, "%s\t%s\n", f.Name, f.Value) + }) default: flag.Usage() os.Exit(1) @@ -82,7 +88,7 @@ func main() { } } -func store(in io.Reader, maxDedupeSearch, maxItems uint64) error { +func store(dbPath string, in io.Reader, maxDedupeSearch, maxItems uint64) error { input, err := io.ReadAll(in) if err != nil { return fmt.Errorf("read stdin: %w", err) @@ -91,7 +97,7 @@ func store(in io.Reader, maxDedupeSearch, maxItems uint64) error { return nil } - db, err := initDB() + db, err := initDB(dbPath) if err != nil { return fmt.Errorf("opening db: %w", err) } @@ -166,8 +172,8 @@ func deduplicate(b *bolt.Bucket, input []byte, maxDedupeSearch uint64) error { return nil } -func list(out io.Writer, previewWidth uint) error { - db, err := initDBReadOnly() +func list(dbPath string, out io.Writer, previewWidth uint) error { + db, err := initDBReadOnly(dbPath) if err != nil { return fmt.Errorf("opening db: %w", err) } @@ -201,7 +207,7 @@ func extractID(input string) (uint64, error) { return uint64(id), nil } -func decode(in io.Reader, out io.Writer, input string) error { +func decode(dbPath string, in io.Reader, out io.Writer, input string) error { if input == "" { inp, err := io.ReadAll(in) if err != nil { @@ -214,7 +220,7 @@ func decode(in io.Reader, out io.Writer, input string) error { return fmt.Errorf("extracting id: %w", err) } - db, err := initDBReadOnly() + db, err := initDBReadOnly(dbPath) if err != nil { return fmt.Errorf("opening db: %w", err) } @@ -234,12 +240,12 @@ func decode(in io.Reader, out io.Writer, input string) error { return nil } -func deleteQuery(query string) error { +func deleteQuery(dbPath string, query string) error { if query == "" { return fmt.Errorf("please provide a query") } - db, err := initDB() + db, err := initDB(dbPath) if err != nil { return fmt.Errorf("opening db: %w", err) } @@ -265,8 +271,8 @@ func deleteQuery(query string) error { return nil } -func deleteLast() error { - db, err := initDB() +func deleteLast(dbPath string) error { + db, err := initDB(dbPath) if err != nil { return fmt.Errorf("opening db: %w", err) } @@ -289,12 +295,12 @@ func deleteLast() error { return nil } -func delete(in io.Reader) error { +func delete(dbPath string, in io.Reader) error { input, err := io.ReadAll(in) // drain stdin before opening and locking db if err != nil { return fmt.Errorf("read stdin: %w", err) } - db, err := initDB() + db, err := initDB(dbPath) if err != nil { return fmt.Errorf("opening db: %w", err) } @@ -323,8 +329,8 @@ func delete(in io.Reader) error { return nil } -func wipe() error { - db, err := initDB() +func wipe(dbPath string) error { + db, err := initDB(dbPath) if err != nil { return fmt.Errorf("opening db: %w", err) } @@ -350,28 +356,22 @@ func wipe() error { const bucketKey = "b" -func initDB() (*bolt.DB, error) { return initDBOption(false) } -func initDBReadOnly() (*bolt.DB, error) { return initDBOption(true) } +func initDB(path string) (*bolt.DB, error) { return initDBOption(path, false) } +func initDBReadOnly(path string) (*bolt.DB, error) { return initDBOption(path, true) } -func initDBOption(ro bool) (*bolt.DB, error) { - userCacheDir, err := os.UserCacheDir() - if err != nil { - return nil, fmt.Errorf("get cache dir: %w", err) - } - cacheDir := filepath.Join(userCacheDir, "cliphist") - if err := os.MkdirAll(cacheDir, 0700); err != nil { +func initDBOption(path string, ro bool) (*bolt.DB, error) { + if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { return nil, fmt.Errorf("create cache dir: %w", err) } - dbPath := filepath.Join(cacheDir, "db") // https://github.com/etcd-io/bbolt/issues/98 if ro { - if _, err := os.Stat(dbPath); errors.Is(err, os.ErrNotExist) { + if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { return nil, errors.New("please store something first") } } - db, err := bolt.Open(dbPath, 0644, &bolt.Options{ + db, err := bolt.Open(path, 0644, &bolt.Options{ ReadOnly: ro, Timeout: 1 * time.Second, }) diff --git a/go.mod b/go.mod index 96ec317..974d8d0 100644 --- a/go.mod +++ b/go.mod @@ -5,11 +5,11 @@ go 1.20 require ( github.com/rogpeppe/go-internal v1.12.0 go.etcd.io/bbolt v1.3.9 - go.senan.xyz/flagconf v0.1.4 + go.senan.xyz/flagconf v0.1.8 golang.org/x/image v0.15.0 ) require ( - golang.org/x/sys v0.17.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/tools v0.20.0 // indirect ) diff --git a/go.sum b/go.sum index 2a699d9..359c1c8 100644 --- a/go.sum +++ b/go.sum @@ -5,13 +5,13 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -go.senan.xyz/flagconf v0.1.4 h1:j92ZqK4o299UBH2wVjhV6VevyhIHwrPSkzgj/+ShDCA= -go.senan.xyz/flagconf v0.1.4/go.mod h1:CGD/sgYWiTacz1ojgsQRwErqLxtShWMpBxxnsJI6yaE= +go.senan.xyz/flagconf v0.1.8 h1:0HadvAEXHYJOGGdO6cHz2Ok4vWawaM64m5ldSjLoVUw= +go.senan.xyz/flagconf v0.1.8/go.mod h1:NqOFfSwJvNWXOTUabcRZ8mPK9+sJmhStJhqtEt74wNQ= golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/testdata/version.txtar b/testdata/version.txtar index cf59d14..6f54000 100644 --- a/testdata/version.txtar +++ b/testdata/version.txtar @@ -1,2 +1,2 @@ exec cliphist version -stderr '^\d+\.\d+\.\d+$' +stderr '^version\t\d+\.\d+\.\d+$'