| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- package flags
- import (
- "bufio"
- "fmt"
- "io"
- "os"
- "reflect"
- "strings"
- )
- type iniValue struct {
- Name string
- Value string
- }
- type iniSection []iniValue
- type ini map[string]iniSection
- func readFullLine(reader *bufio.Reader) (string, error) {
- var line []byte
- for {
- l, more, err := reader.ReadLine()
- if err != nil {
- return "", err
- }
- if line == nil && !more {
- return string(l), nil
- }
- line = append(line, l...)
- if !more {
- break
- }
- }
- return string(line), nil
- }
- func optionIniName(option *Option) string {
- name := option.tag.Get("_read-ini-name")
- if len(name) != 0 {
- return name
- }
- name = option.tag.Get("ini-name")
- if len(name) != 0 {
- return name
- }
- return option.field.Name
- }
- func writeGroupIni(group *Group, namespace string, writer io.Writer, options IniOptions) {
- var sname string
- if len(namespace) != 0 {
- sname = namespace + "." + group.ShortDescription
- } else {
- sname = group.ShortDescription
- }
- sectionwritten := false
- comments := (options & IniIncludeComments) != IniNone
- for _, option := range group.options {
- if option.isFunc() {
- continue
- }
- if len(option.tag.Get("no-ini")) != 0 {
- continue
- }
- val := option.value
- if (options&IniIncludeDefaults) == IniNone &&
- reflect.DeepEqual(val, option.defaultValue) {
- continue
- }
- if !sectionwritten {
- fmt.Fprintf(writer, "[%s]\n", sname)
- sectionwritten = true
- }
- if comments {
- fmt.Fprintf(writer, "; %s\n", option.Description)
- }
- oname := optionIniName(option)
- switch val.Type().Kind() {
- case reflect.Slice:
- for idx := 0; idx < val.Len(); idx++ {
- v, _ := convertToString(val.Index(idx), option.tag)
- fmt.Fprintf(writer, "%s = %s\n", oname, v)
- }
- if val.Len() == 0 {
- fmt.Fprintf(writer, "; %s =\n", oname)
- }
- case reflect.Map:
- for _, key := range val.MapKeys() {
- k, _ := convertToString(key, option.tag)
- v, _ := convertToString(val.MapIndex(key), option.tag)
- fmt.Fprintf(writer, "%s = %s:%s\n", oname, k, v)
- }
- if val.Len() == 0 {
- fmt.Fprintf(writer, "; %s =\n", oname)
- }
- default:
- v, _ := convertToString(val, option.tag)
- if len(v) != 0 {
- fmt.Fprintf(writer, "%s = %s\n", oname, v)
- } else {
- fmt.Fprintf(writer, "%s =\n", oname)
- }
- }
- if comments {
- fmt.Fprintln(writer)
- }
- }
- if sectionwritten && !comments {
- fmt.Fprintln(writer)
- }
- }
- func writeCommandIni(command *Command, namespace string, writer io.Writer, options IniOptions) {
- command.eachGroup(func(group *Group) {
- writeGroupIni(group, namespace, writer, options)
- })
- for _, c := range command.commands {
- var nns string
- if len(namespace) != 0 {
- nns = c.Name + "." + nns
- } else {
- nns = c.Name
- }
- writeCommandIni(c, nns, writer, options)
- }
- }
- func writeIni(parser *IniParser, writer io.Writer, options IniOptions) {
- writeCommandIni(parser.parser.Command, "", writer, options)
- }
- func readIniFromFile(filename string) (ini, error) {
- file, err := os.Open(filename)
- if err != nil {
- return nil, err
- }
- defer file.Close()
- return readIni(file, filename)
- }
- func readIni(contents io.Reader, filename string) (ini, error) {
- ret := make(ini)
- reader := bufio.NewReader(contents)
- // Empty global section
- section := make(iniSection, 0, 10)
- sectionname := ""
- ret[sectionname] = section
- var lineno uint
- for {
- line, err := readFullLine(reader)
- if err == io.EOF {
- break
- }
- if err != nil {
- return nil, err
- }
- lineno++
- line = strings.TrimSpace(line)
- // Skip empty lines and lines starting with ; (comments)
- if len(line) == 0 || line[0] == ';' {
- continue
- }
- if line[0] == '[' {
- if line[0] != '[' || line[len(line)-1] != ']' {
- return nil, &IniError{
- Message: "malformed section header",
- File: filename,
- LineNumber: lineno,
- }
- }
- name := strings.TrimSpace(line[1 : len(line)-1])
- if len(name) == 0 {
- return nil, &IniError{
- Message: "empty section name",
- File: filename,
- LineNumber: lineno,
- }
- }
- sectionname = name
- section = ret[name]
- if section == nil {
- section = make(iniSection, 0, 10)
- ret[name] = section
- }
- continue
- }
- // Parse option here
- keyval := strings.SplitN(line, "=", 2)
- if len(keyval) != 2 {
- return nil, &IniError{
- Message: fmt.Sprintf("malformed key=value (%s)", line),
- File: filename,
- LineNumber: lineno,
- }
- }
- name := strings.TrimSpace(keyval[0])
- value := strings.TrimSpace(keyval[1])
- section = append(section, iniValue{
- Name: name,
- Value: value,
- })
- ret[sectionname] = section
- }
- return ret, nil
- }
- func (i *IniParser) matchingGroups(name string) []*Group {
- if len(name) == 0 {
- var ret []*Group
- i.parser.eachGroup(func(g *Group) {
- ret = append(ret, g)
- })
- return ret
- }
- g := i.parser.groupByName(name)
- if g != nil {
- return []*Group{g}
- }
- return nil
- }
- func (i *IniParser) parse(ini ini) error {
- p := i.parser
- for name, section := range ini {
- groups := i.matchingGroups(name)
- if len(groups) == 0 {
- return newError(ErrUnknownGroup,
- fmt.Sprintf("could not find option group `%s'", name))
- }
- for _, inival := range section {
- var opt *Option
- for _, group := range groups {
- opt = group.optionByName(inival.Name, func(o *Option, n string) bool {
- return strings.ToLower(o.tag.Get("ini-name")) == strings.ToLower(n)
- })
- if opt != nil && len(opt.tag.Get("no-ini")) != 0 {
- opt = nil
- }
- if opt != nil {
- break
- }
- }
- if opt == nil {
- if (p.Options & IgnoreUnknown) == None {
- return newError(ErrUnknownFlag,
- fmt.Sprintf("unknown option: %s", inival.Name))
- }
- continue
- }
- pval := &inival.Value
- if !opt.canArgument() && len(inival.Value) == 0 {
- pval = nil
- }
- if err := opt.set(pval); err != nil {
- return wrapError(err)
- }
- opt.tag.Set("_read-ini-name", inival.Name)
- }
- }
- return nil
- }
|