2015-05-18 14:47:08 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2018-04-26 14:13:13 +00:00
|
|
|
"errors"
|
2015-05-18 14:47:08 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
2018-04-26 14:13:13 +00:00
|
|
|
"strings"
|
2017-10-13 11:57:28 +00:00
|
|
|
"time"
|
2015-09-17 16:49:57 +00:00
|
|
|
|
|
|
|
"github.com/boltdb/bolt"
|
|
|
|
"github.com/nsf/termbox-go"
|
2015-05-18 14:47:08 +00:00
|
|
|
)
|
|
|
|
|
2017-04-04 21:08:08 +00:00
|
|
|
var ProgramName = "boltbrowser"
|
2019-10-16 22:41:29 +00:00
|
|
|
var VersionNum = 2.0
|
2015-05-18 14:47:08 +00:00
|
|
|
|
2017-04-04 21:19:07 +00:00
|
|
|
var databaseFiles []string
|
2015-05-18 14:47:08 +00:00
|
|
|
var db *bolt.DB
|
|
|
|
var memBolt *BoltDB
|
|
|
|
|
2017-04-04 21:41:15 +00:00
|
|
|
var currentFilename string
|
|
|
|
|
2017-10-13 11:57:28 +00:00
|
|
|
const DefaultDBOpenTimeout = time.Second
|
|
|
|
|
2018-04-26 14:13:13 +00:00
|
|
|
var AppArgs struct {
|
2017-10-13 12:01:25 +00:00
|
|
|
DBOpenTimeout time.Duration
|
2018-04-26 14:13:13 +00:00
|
|
|
ReadOnly bool
|
2019-12-23 11:26:45 +00:00
|
|
|
NoValue bool
|
2017-10-13 12:01:25 +00:00
|
|
|
}
|
|
|
|
|
2017-10-12 04:47:00 +00:00
|
|
|
func init() {
|
2018-04-26 14:13:13 +00:00
|
|
|
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
|
|
|
|
}
|
2019-12-23 11:26:45 +00:00
|
|
|
case "-no-value":
|
|
|
|
if val == "true" {
|
|
|
|
AppArgs.NoValue = true
|
|
|
|
}
|
2018-04-26 14:13:13 +00:00
|
|
|
case "-help":
|
|
|
|
printUsage(nil)
|
|
|
|
default:
|
|
|
|
printUsage(errors.New("Invalid option"))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Single-word arguments
|
|
|
|
switch parms[i] {
|
|
|
|
case "-readonly", "-ro":
|
|
|
|
AppArgs.ReadOnly = true
|
2019-12-23 11:26:45 +00:00
|
|
|
case "-no-value":
|
|
|
|
AppArgs.NoValue = true
|
2018-04-26 14:13:13 +00:00
|
|
|
case "-help":
|
|
|
|
printUsage(nil)
|
|
|
|
default:
|
|
|
|
printUsage(errors.New("Invalid option"))
|
|
|
|
}
|
|
|
|
}
|
2017-10-12 04:47:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-26 14:13:13 +00:00
|
|
|
func printUsage(err error) {
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, err.Error())
|
|
|
|
}
|
|
|
|
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] <filename(s)>\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")
|
2019-12-23 11:26:45 +00:00
|
|
|
fmt.Fprintf(os.Stderr, " -no-value \n Do not display a value in left pane\n")
|
2018-04-26 14:13:13 +00:00
|
|
|
}
|
|
|
|
|
2015-05-18 14:47:08 +00:00
|
|
|
func main() {
|
|
|
|
var err error
|
|
|
|
|
2018-04-26 14:13:13 +00:00
|
|
|
parseArgs()
|
2015-05-18 14:47:08 +00:00
|
|
|
|
|
|
|
err = termbox.Init()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
defer termbox.Close()
|
|
|
|
style := defaultStyle()
|
|
|
|
termbox.SetOutputMode(termbox.Output256)
|
|
|
|
|
2017-04-04 21:19:07 +00:00
|
|
|
for _, databaseFile := range databaseFiles {
|
2017-04-04 21:41:15 +00:00
|
|
|
currentFilename = databaseFile
|
2018-04-26 14:13:13 +00:00
|
|
|
db, err = bolt.Open(databaseFile, 0600, &bolt.Options{Timeout: AppArgs.DBOpenTimeout})
|
2017-10-13 11:57:28 +00:00
|
|
|
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)
|
|
|
|
os.Exit(1)
|
|
|
|
} else if err != nil {
|
2017-04-05 03:31:40 +00:00
|
|
|
if len(databaseFiles) > 1 {
|
|
|
|
mainLoop(nil, style)
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
termbox.Close()
|
|
|
|
fmt.Printf("Error reading file: %q\n", err.Error())
|
2018-04-26 14:13:13 +00:00
|
|
|
os.Exit(1)
|
2017-04-05 03:31:40 +00:00
|
|
|
}
|
2017-04-04 21:19:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// First things first, load the database into memory
|
|
|
|
memBolt.refreshDatabase()
|
2018-04-26 14:13:13 +00:00
|
|
|
if AppArgs.ReadOnly {
|
|
|
|
// If we're opening it in readonly mode, close it now
|
|
|
|
db.Close()
|
|
|
|
}
|
|
|
|
|
2017-05-18 13:17:58 +00:00
|
|
|
// Kick off the UI loop
|
2017-04-04 21:19:07 +00:00
|
|
|
mainLoop(memBolt, style)
|
|
|
|
defer db.Close()
|
|
|
|
}
|
2016-05-05 19:50:04 +00:00
|
|
|
}
|