func loadConfig() (*config, []string, error) {
	cfg := config{
		ConfigFile: defaultConfigFile,
		RPCServer:  defaultRPCServer,
		RPCCert:    defaultRPCCertFile,
	}
	preCfg := cfg
	preParser := flags.NewParser(&preCfg, flags.HelpFlag)
	_, err := preParser.Parse()
	if err != nil {
		if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp {
			fmt.Fprintln(os.Stderr, err)
			fmt.Fprintln(os.Stderr, "")
			fmt.Fprintln(os.Stderr, "The special parameter `-` "+
				"indicates that a parameter should be read "+
				"from the\nnext unread line from standard "+
				"input.")
			return nil, nil, err
		}
	}
	appName := filepath.Base(os.Args[0])
	appName = strings.TrimSuffix(appName, filepath.Ext(appName))
	usageMessage := fmt.Sprintf("Use %s -h to show options", appName)
	if preCfg.ShowVersion {
		fmt.Println(appName, "version", version)
		os.Exit(0)
	}
	if preCfg.ListCommands {
		listCommands()
		os.Exit(0)
	}
	if _, err := os.Stat(preCfg.ConfigFile); os.IsNotExist(err) {
		var serverConfigPath string
		if preCfg.Wallet {
			serverConfigPath = filepath.Join(soterwalletHomeDir, "soterwallet.conf")
		} else {
			serverConfigPath = filepath.Join(soterdHomeDir, "soterd.conf")
		}
		err := createDefaultConfigFile(preCfg.ConfigFile, serverConfigPath)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error creating a default config file: %v\n", err)
		}
	}
	parser := flags.NewParser(&cfg, flags.Default)
	err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
	if err != nil {
		if _, ok := err.(*os.PathError); !ok {
			fmt.Fprintf(os.Stderr, "Error parsing config file: %v\n",
				err)
			fmt.Fprintln(os.Stderr, usageMessage)
			return nil, nil, err
		}
	}
	remainingArgs, err := parser.Parse()
	if err != nil {
		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			fmt.Fprintln(os.Stderr, usageMessage)
		}
		return nil, nil, err
	}
	numNets := 0
	if cfg.TestNet1 {
		numNets++
	}
	if cfg.SimNet {
		numNets++
	}
	if numNets > 1 {
		str := "%s: The testnet, and simnet params can't be used " +
			"together -- choose one of the two"
		err := fmt.Errorf(str, "loadConfig")
		fmt.Fprintln(os.Stderr, err)
		return nil, nil, err
	}
	if cfg.Wallet && cfg.RPCCert == defaultRPCCertFile {
		cfg.RPCCert = defaultWalletCertFile
	}
	cfg.RPCCert = cleanAndExpandPath(cfg.RPCCert)
	cfg.RPCServer = normalizeAddress(cfg.RPCServer, cfg.TestNet1,
		cfg.SimNet, cfg.Wallet)
	return &cfg, remainingArgs, nil
}
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 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.
package main
import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"github.com/soteria-dag/soterd/soterjson"
	"github.com/soteria-dag/soterd/soterutil"
	flags "github.com/jessevdk/go-flags"
)
const (
	unusableFlags = soterjson.UFWebsocketOnly | soterjson.UFNotification
)
var (
	soterdHomeDir         = soterutil.AppDataDir("soterd", false)
	soterctlHomeDir       = soterutil.AppDataDir("soterctl", false)
	soterwalletHomeDir    = soterutil.AppDataDir("soterwallet", false)
	defaultConfigFile     = filepath.Join(soterctlHomeDir, "soterctl.conf")
	defaultRPCServer      = "localhost"
	defaultRPCCertFile    = filepath.Join(soterdHomeDir, "rpc.cert")
	defaultWalletCertFile = filepath.Join(soterwalletHomeDir, "rpc.cert")
)
func listCommands() {
	const (
		categoryChain uint8 = iota
		categoryWallet
		numCategories
	)
	cmdMethods := soterjson.RegisteredCmdMethods()
	categorized := make([][]string, numCategories)
	for _, method := range cmdMethods {
		flags, err := soterjson.MethodUsageFlags(method)
		if err != nil {
			continue
		}
		if flags&unusableFlags != 0 {
			continue
		}
		usage, err := soterjson.MethodUsageText(method)
		if err != nil {
			continue
		}
		category := categoryChain
		if flags&soterjson.UFWalletOnly != 0 {
			category = categoryWallet
		}
		categorized[category] = append(categorized[category], usage)
	}
	categoryTitles := make([]string, numCategories)
	categoryTitles[categoryChain] = "Chain Server Commands:"
	categoryTitles[categoryWallet] = "Wallet Server Commands (--wallet):"
	for category := uint8(0); category < numCategories; category++ {
		fmt.Println(categoryTitles[category])
		for _, usage := range categorized[category] {
			fmt.Println(usage)
		}
		fmt.Println()
	}
}
type config struct {
	ShowVersion   bool   `short:"V" long:"version" description:"Display version information and exit"`
	ListCommands  bool   `short:"l" long:"listcommands" description:"List all of the supported commands and exit"`
	ConfigFile    string `short:"C" long:"configfile" description:"Path to configuration file"`
	RPCUser       string `short:"u" long:"rpcuser" description:"RPC username"`
	RPCPassword   string `short:"P" long:"rpcpass" default-mask:"-" description:"RPC password"`
	RPCServer     string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
	RPCCert       string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"`
	NoTLS         bool   `long:"notls" description:"Disable TLS"`
	Proxy         string `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"`
	ProxyUser     string `long:"proxyuser" description:"Username for proxy server"`
	ProxyPass     string `long:"proxypass" default-mask:"-" description:"Password for proxy server"`
	TestNet1      bool   `long:"testnet" description:"Connect to testnet"`
	SimNet        bool   `long:"simnet" description:"Connect to the simulation test network"`
	TLSSkipVerify bool   `long:"skipverify" description:"Do not verify tls certificates (not recommended!)"`
	Wallet        bool   `long:"wallet" description:"Connect to wallet"`
}
func normalizeAddress(addr string, useTestNet1, useSimNet, useWallet bool) string {
	_, _, err := net.SplitHostPort(addr)
	if err != nil {
		var defaultPort string
		switch {
		case useTestNet1:
			if useWallet {
				defaultPort = "5069"
			} else {
				defaultPort = "5071"
			}
		case useSimNet:
			if useWallet {
				defaultPort = "18554"
			} else {
				defaultPort = "18556"
			}
		default:
			if useWallet {
				defaultPort = "8332"
			} else {
				defaultPort = "8334"
			}
		}
		return net.JoinHostPort(addr, defaultPort)
	}
	return addr
}
func cleanAndExpandPath(path string) string {
	if strings.HasPrefix(path, "~") {
		homeDir := filepath.Dir(soterctlHomeDir)
		path = strings.Replace(path, "~", homeDir, 1)
	}
	return filepath.Clean(os.ExpandEnv(path))
}