From cad01cb1f8e32cf7725505af06d6ffcc6c612045 Mon Sep 17 00:00:00 2001 From: r Date: Wed, 1 Jan 2020 15:18:04 +0000 Subject: [PATCH] Add command line flag to specify config file --- default.conf => bloat.conf | 0 main.go | 19 +++++- util/getopt.go | 122 +++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) rename default.conf => bloat.conf (100%) create mode 100644 util/getopt.go diff --git a/default.conf b/bloat.conf similarity index 100% rename from default.conf rename to bloat.conf diff --git a/main.go b/main.go index e29f53a..423d06a 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,11 @@ import ( "web/renderer" "web/repository" "web/service" + "web/util" +) + +var ( + configFile = "bloat.conf" ) func init() { @@ -21,7 +26,19 @@ func init() { } func main() { - config, err := config.ParseFile("default.conf") + opts, _, err := util.Getopts(os.Args, "f:") + if err != nil { + log.Fatal(err) + } + + for _, opt := range opts { + switch opt.Option { + case 'f': + configFile = opt.Value + } + } + + config, err := config.ParseFile(configFile) if err != nil { log.Fatal(err) } diff --git a/util/getopt.go b/util/getopt.go new file mode 100644 index 0000000..10926a8 --- /dev/null +++ b/util/getopt.go @@ -0,0 +1,122 @@ +/* +Copyright 2019 Drew DeVault + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package util + +import ( + "fmt" + "os" +) + +// In the case of "-o example", Option is 'o' and "example" is Value. For +// options which do not take an argument, Value is "". +type Option struct { + Option rune + Value string +} + +// This is returned when an unknown option is found in argv, but not in the +// option spec. +type UnknownOptionError rune + +func (e UnknownOptionError) Error() string { + return fmt.Sprintf("%s: unknown option -%c", os.Args[0], rune(e)) +} + +// This is returned when an option with a mandatory argument is missing that +// argument. +type MissingOptionError rune + +func (e MissingOptionError) Error() string { + return fmt.Sprintf("%s: expected argument for -%c", os.Args[0], rune(e)) +} + +// Getopts implements a POSIX-compatible options interface. +// +// Returns a slice of options and the index of the first non-option argument. +// +// If an error is returned, you must print it to stderr to be POSIX complaint. +func Getopts(argv []string, spec string) ([]Option, int, error) { + optmap := make(map[rune]bool) + runes := []rune(spec) + for i, rn := range spec { + if rn == ':' { + if i == 0 { + continue + } + optmap[runes[i-1]] = true + } else { + optmap[rn] = false + } + } + + var ( + i int + opts []Option + ) + for i = 1; i < len(argv); i++ { + arg := argv[i] + runes = []rune(arg) + if len(arg) == 0 || arg == "-" { + break + } + if arg[0] != '-' { + break + } + if arg == "--" { + i++ + break + } + for j, opt := range runes[1:] { + if optopt, ok := optmap[opt]; !ok { + opts = append(opts, Option{'?', ""}) + return opts, i, UnknownOptionError(opt) + } else if optopt { + if j+1 < len(runes)-1 { + opts = append(opts, Option{opt, string(runes[j+2:])}) + break + } else { + if i+1 >= len(argv) { + if len(spec) >= 1 && spec[0] == ':' { + opts = append(opts, Option{':', string(opt)}) + } else { + return opts, i, MissingOptionError(opt) + } + } else { + opts = append(opts, Option{opt, argv[i+1]}) + i++ + } + } + } else { + opts = append(opts, Option{opt, ""}) + } + } + } + return opts, i, nil +}