Skip to content

Commit

Permalink
Add getopt (short flags only)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vadim Vygonets committed Sep 10, 2012
1 parent 67b4de8 commit 15eb440
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 15 deletions.
15 changes: 10 additions & 5 deletions conf.go
Expand Up @@ -181,10 +181,13 @@ func (v *Uint64Value) String() string { return strconv.FormatUint(uint64(*v), 10
// Var describes a configuration variable and has pointers to corresponding
// (Go) variables. Slice of Var is used for calling Parse().
type Var struct {
Name string // name of configuration variable
Flag rune // short option
Name string // name of configuration variable / long option
Val Value // Value to set
Required bool // variable is required to be set in conf file
set bool // has been set
Bare bool // command line option takes no argument
set bool // has been set from conf file
flagSet bool // has been set from command line
}

type parser struct {
Expand Down Expand Up @@ -250,9 +253,11 @@ func (p *parser) setValue(value string) error {
if v.set {
return p.newError(errAlreadyDef)
}
if err := v.Val.Set(value); err != nil {
return &ParseError{p.file, p.line, p.ident,
p.value, err}
if !v.flagSet {
if err := v.Val.Set(value); err != nil {
return &ParseError{p.file, p.line,
p.ident, p.value, err}
}
}
v.set = true
return nil
Expand Down
36 changes: 26 additions & 10 deletions example/example.go
Expand Up @@ -13,6 +13,7 @@ import (
)

var (
confFile = "example.conf"
bval bool
sval = "default value"
nval uint64
Expand All @@ -32,25 +33,40 @@ func (key *netKeyValue) Set(s string) error {

//func (key *netKeyValue) String() string { return fmt.Sprintf("%x", *key) }

func readConf(conffile string) error {
var vars = []conf.Var{
// cmd-line only:
//{Flag: 'h', Val: (*conf.StringValue)(&confFile)},
{Flag: 'c', Val: (*conf.StringValue)(&confFile)},
// cmd-line and conf-file:
{Flag: 's', Name: "string", Val: (*conf.StringValue)(&sval)},
{Flag: 'n', Name: "number", Val: (*conf.Uint64Value)(&nval)},
{Flag: 'b', Name: "bool", Val: (*conf.BoolValue)(&bval), Bare: true},
{Flag: 'k', Name: "key", Val: (*netKeyValue)(&netKey), Required: true},
// conf-file only:
}

func readConf(conffile string, vars []conf.Var) error {
f, err := os.Open(conffile)
if err != nil {
return err
}
defer f.Close()
return conf.Parse(f, conffile, []conf.Var{
{Name: "string", Val: (*conf.StringValue)(&sval)},
{Name: "number", Val: (*conf.Uint64Value)(&nval)},
{Name: "bool", Val: (*conf.BoolValue)(&bval)},
{Name: "key", Val: (*netKeyValue)(&netKey), Required: true},
})
return conf.Parse(f, conffile, vars)
}

func main() {
if err := readConf("example.conf"); err != nil {
fmt.Printf("*** start:\nconffile: %s\nstring: %s\nnumber: %d\nbool: %v\nkey: %x\n",
confFile, sval, nval, bval, netKey)
if err := conf.GetOpt(vars); err != nil {
fmt.Printf("%s\n", err)
return
}
fmt.Printf("*** after GetOpt:\nconffile: %s\nstring: %s\nnumber: %d\nbool: %v\nkey: %x\n",
confFile, sval, nval, bval, netKey)
if err := readConf(confFile, vars[1:]); err != nil {
fmt.Printf("%s\n", err)
return
}
fmt.Printf("string: %s\nnumber: %d\nbool: %v\nkey: %x\n",
sval, nval, bval, netKey)
fmt.Printf("*** after Parse:\nconffile: %s\nstring: %s\nnumber: %d\nbool: %v\nkey: %x\n",
confFile, sval, nval, bval, netKey)
}
92 changes: 92 additions & 0 deletions getopt.go
@@ -0,0 +1,92 @@
// Copyright 2012 Vadim Vygonets
// This program is free software. It comes without any warranty, to
// the extent permitted by applicable law. You can redistribute it
// and/or modify it under the terms of the Do What The Fuck You Want
// To Public License, Version 2, as published by Sam Hocevar. See
// the LICENSE file or http://sam.zoy.org/wtfpl/ for more details.

package conf

import (
"errors"
"os"
"unicode/utf8"
)

var (
errIllOpt = errors.New("illegal option")
errNoArg = errors.New("option requires an argument")
)

var Args []string

// FlagError represents the error.
type FlagError struct {
Flag rune // flag
Value string // value
Err error // error
}

// Error prints FlagError as follows, if Value is not empty:
// Err -- Value
// otherwise:
// Err -- Flag
func (e *FlagError) Error() string {
if e.Value != "" {
return e.Err.Error() + " -- " + e.Value
}
return e.Err.Error() + " -- " + string(e.Flag)
}

// newError creates FlagError from f, v and e
func newError(f rune, v string, e error) *FlagError {
return &FlagError{f, v, e}
}

func getFlag(flag rune, vars []Var) *Var {
for i := range vars {
if vars[i].Flag == flag {
return &vars[i]
}
}
return nil
}

func GetOpt(vars []Var) error {
Args = make([]string, len(os.Args)-1)
copy(Args, os.Args[1:])
for len(Args) > 0 && len(Args[0]) > 1 && Args[0][0] == '-' {
if Args[0] == "--" {
Args = Args[1:]
break
}
var this, p string
this, Args = Args[0][1:], Args[1:]
for len(this) > 0 {
flag, size := utf8.DecodeRuneInString(this)
this = this[size:]
if flag == utf8.RuneError {
return newError(flag, "", errSyntax)
}
v := getFlag(flag, vars)
if v == nil {
return newError(flag, "", errIllOpt)
}
switch {
case v.Bare:
p = "true"
case this != "":
p, this = this, ""
case len(Args) != 0:
p, Args = Args[0], Args[1:]
default:
return newError(flag, "", errNoArg)
}
if err := v.Val.Set(p); err != nil {
return newError(flag, p, err)
}
v.flagSet = true
}
}
return nil
}

0 comments on commit 15eb440

Please sign in to comment.