lbcwallet/config.go
2013-08-21 10:37:30 -04:00

133 lines
4 KiB
Go

package main
import (
"errors"
"fmt"
"github.com/conformal/go-flags"
"os"
"path/filepath"
"strings"
)
const (
defaultConfigFilename = "btcwallet.conf"
defaultBtcdPort = 8334
defaultLogLevel = "info"
defaultServerPort = 8332
)
var (
defaultConfigFile = filepath.Join(btcwalletHomeDir(), defaultConfigFilename)
)
type config struct {
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
BtcdPort int `short:"b" long:"btcdport" description:"Port to connect to btcd on"`
DebugLevel string `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}"`
ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"`
SvrPort int `short:"p" long:"serverport" description:"Port to serve frontend websocket connections on"`
WalletFile string `short:"f" long:"walletfile" description:"Path to wallet file"`
}
// btcwalletHomeDir returns an OS appropriate home directory for btcwallet.
func btcwalletHomeDir() string {
// Search for Windows APPDATA first. This won't exist on POSIX OSes.
appData := os.Getenv("APPDATA")
if appData != "" {
return filepath.Join(appData, "btcwallet")
}
// Fall back to standard HOME directory that works for most POSIX OSes.
home := os.Getenv("HOME")
if home != "" {
return filepath.Join(home, ".btcwallet")
}
// In the worst case, use the current directory.
return "."
}
// filesExists reports whether the named file or directory exists.
func fileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// loadConfig initializes and parses the config using a config file and command
// line options.
//
// The configuration proceeds as follows:
// 1) Start with a default config with sane settings
// 2) Pre-parse the command line to check for an alternative config file
// 3) Load configuration file overwriting defaults with any specified options
// 4) Parse CLI options and overwrite/add any specified options
//
// The above results in btcwallet functioning properly without any config
// settings while still allowing the user to override settings with config files
// and command line options. Command line options always take precedence.
func loadConfig() (*config, []string, error) {
// Default config.
cfg := config{
DebugLevel: defaultLogLevel,
ConfigFile: defaultConfigFile,
BtcdPort: defaultBtcdPort,
SvrPort: defaultServerPort,
}
// A config file in the current directory takes precedence.
if fileExists(defaultConfigFilename) {
cfg.ConfigFile = defaultConfigFile
}
// Pre-parse the command line options to see if an alternative config
// file or the version flag was specified.
preCfg := cfg
preParser := flags.NewParser(&preCfg, flags.Default)
_, err := preParser.Parse()
if err != nil {
if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
preParser.WriteHelp(os.Stderr)
}
return nil, nil, err
}
// Show the version and exit if the version flag was specified.
if preCfg.ShowVersion {
appName := filepath.Base(os.Args[0])
appName = strings.TrimSuffix(appName, filepath.Ext(appName))
fmt.Println(appName, "version", version())
os.Exit(0)
}
// Load additional config from file.
parser := flags.NewParser(&cfg, flags.Default)
err = parser.ParseIniFile(preCfg.ConfigFile)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return nil, nil, err
}
log.Warnf("%v", err)
}
// Parse command line options again to ensure they take precedence.
remainingArgs, err := parser.Parse()
if err != nil {
if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
parser.WriteHelp(os.Stderr)
}
return nil, nil, err
}
// wallet file must be valid
if !fileExists(cfg.WalletFile) {
return &cfg, nil, errors.New("Wallet file does not exist.")
}
return &cfg, remainingArgs, nil
}