From 7d7e0bfcd3ce4f1bc89c85854490bc822e7857b3 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Thu, 26 Apr 2018 09:13:13 -0500 Subject: [PATCH] Add simple 'read-only' mode. It just closes the database as soon as it's done loading it. To refresh, you have to close out and reopen. --- bolt_model.go | 15 +++++++++ boltbrowser.go | 87 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 15 deletions(-) diff --git a/bolt_model.go b/bolt_model.go index ceffabf..61ee838 100644 --- a/bolt_model.go +++ b/bolt_model.go @@ -364,6 +364,9 @@ func addBucketFromBoltBucket(path []string, bb *BoltBucket) error { } func deleteKey(path []string) error { + if AppArgs.ReadOnly { + return errors.New("DB is in Read-Only Mode") + } err := db.Update(func(tx *bolt.Tx) error { // len(b.path)-1 is the key we need to delete, // the rest are buckets leading to that key @@ -469,6 +472,9 @@ func renameBucket(path []string, name string) error { } func updatePairKey(path []string, k string) error { + if AppArgs.ReadOnly { + return errors.New("DB is in Read-Only Mode") + } err := db.Update(func(tx *bolt.Tx) error { // len(b.path)-1 is the key for the pair we're updating, // the rest are buckets leading to that key @@ -498,6 +504,9 @@ func updatePairKey(path []string, k string) error { } func updatePairValue(path []string, v string) error { + if AppArgs.ReadOnly { + return errors.New("DB is in Read-Only Mode") + } err := db.Update(func(tx *bolt.Tx) error { // len(b.GetPath())-1 is the key for the pair we're updating, // the rest are buckets leading to that key @@ -521,6 +530,9 @@ func updatePairValue(path []string, v string) error { } func insertBucket(path []string, n string) error { + if AppArgs.ReadOnly { + return errors.New("DB is in Read-Only Mode") + } // Inserts a new bucket named 'n' at 'path' err := db.Update(func(tx *bolt.Tx) error { if len(path) == 0 { @@ -558,6 +570,9 @@ func insertBucket(path []string, n string) error { } func insertPair(path []string, k string, v string) error { + if AppArgs.ReadOnly { + return errors.New("DB is in Read-Only Mode") + } // Insert a new pair k => v at path err := db.Update(func(tx *bolt.Tx) error { if len(path) == 0 { diff --git a/boltbrowser.go b/boltbrowser.go index 392cbb6..251c990 100644 --- a/boltbrowser.go +++ b/boltbrowser.go @@ -1,9 +1,10 @@ package main import ( - "flag" + "errors" "fmt" "os" + "strings" "time" "github.com/boltdb/bolt" @@ -20,27 +21,79 @@ var currentFilename string const DefaultDBOpenTimeout = time.Second -var args struct { +var AppArgs struct { DBOpenTimeout time.Duration + ReadOnly bool } func init() { - flag.DurationVar(&args.DBOpenTimeout, "timeout", DefaultDBOpenTimeout, "DB file open timeout") - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] \nOptions:\n", ProgramName) - flag.PrintDefaults() + AppArgs.DBOpenTimeout = DefaultDBOpenTimeout + AppArgs.ReadOnly = false +} + +func parseArgs() { + var err error + if len(os.Args) == 1 { + printUsage(nil) } + parms := os.Args[1:] + for i := range parms { + // All 'option' arguments start with "-" + if !strings.HasPrefix(parms[i], "-") { + databaseFiles = append(databaseFiles, parms[i]) + continue + } + if strings.Contains(parms[i], "=") { + // Key/Value pair Arguments + pts := strings.Split(parms[i], "=") + key, val := pts[0], pts[1] + switch key { + case "-timeout": + AppArgs.DBOpenTimeout, err = time.ParseDuration(val) + if err != nil { + // See if we can successfully parse by adding a 's' + AppArgs.DBOpenTimeout, err = time.ParseDuration(val + "s") + } + // If err is still not nil, print usage + if err != nil { + printUsage(err) + } + case "-readonly", "-ro": + if val == "true" { + AppArgs.ReadOnly = true + } + case "-help": + printUsage(nil) + default: + printUsage(errors.New("Invalid option")) + } + } else { + // Single-word arguments + switch parms[i] { + case "-readonly", "-ro": + AppArgs.ReadOnly = true + case "-help": + printUsage(nil) + default: + printUsage(errors.New("Invalid option")) + } + } + } +} + +func printUsage(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + } + fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] \nOptions:\n", ProgramName) + fmt.Fprintf(os.Stderr, " -timeout=duration\n DB file open timeout (default 1s)\n") + fmt.Fprintf(os.Stderr, " -ro, -readonly \n Open the DB in read-only mode\n") } func main() { var err error - flag.Parse() - - if flag.NArg() == 0 { - flag.Usage() - os.Exit(2) - } + parseArgs() err = termbox.Init() if err != nil { @@ -50,10 +103,9 @@ func main() { style := defaultStyle() termbox.SetOutputMode(termbox.Output256) - databaseFiles := flag.Args() for _, databaseFile := range databaseFiles { currentFilename = databaseFile - db, err = bolt.Open(databaseFile, 0600, &bolt.Options{Timeout: args.DBOpenTimeout}) + db, err = bolt.Open(databaseFile, 0600, &bolt.Options{Timeout: AppArgs.DBOpenTimeout}) if err == bolt.ErrTimeout { termbox.Close() fmt.Printf("File %s is locked. Make sure it's not used by another app and try again\n", databaseFile) @@ -65,12 +117,17 @@ func main() { } else { termbox.Close() fmt.Printf("Error reading file: %q\n", err.Error()) - os.Exit(111) + os.Exit(1) } } // First things first, load the database into memory memBolt.refreshDatabase() + if AppArgs.ReadOnly { + // If we're opening it in readonly mode, close it now + db.Close() + } + // Kick off the UI loop mainLoop(memBolt, style) defer db.Close()