// Copyright 2009 The "config" Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package config import ( "regexp" "strings" ) const ( // Default section name. DEFAULT_SECTION = "DEFAULT" // Maximum allowed depth when recursively substituing variable names. _DEPTH_VALUES = 200 DEFAULT_COMMENT = "# " ALTERNATIVE_COMMENT = "; " DEFAULT_SEPARATOR = ":" ALTERNATIVE_SEPARATOR = "=" ) var ( // Strings accepted as boolean. boolString = map[string]bool{ "t": true, "true": true, "y": true, "yes": true, "on": true, "1": true, "f": false, "false": false, "n": false, "no": false, "off": false, "0": false, } varRegExp = regexp.MustCompile(`%\(([a-zA-Z0-9_.\-]+)\)s`) // %(variable)s envVarRegExp = regexp.MustCompile(`\${([a-zA-Z0-9_.\-]+)}`) // ${envvar} ) // Config is the representation of configuration settings. type Config struct { comment string separator string // Sections order lastIdSection int // Last section identifier idSection map[string]int // Section : position // The last option identifier used for each section. lastIdOption map[string]int // Section : last identifier // Section -> option : value data map[string]map[string]*tValue } // tValue holds the input position for a value. type tValue struct { position int // Option order v string // value } // New creates an empty configuration representation. // This representation can be filled with AddSection and AddOption and then // saved to a file using WriteFile. // // == Arguments // // comment: has to be `DEFAULT_COMMENT` or `ALTERNATIVE_COMMENT` // separator: has to be `DEFAULT_SEPARATOR` or `ALTERNATIVE_SEPARATOR` // preSpace: indicate if is inserted a space before of the separator // postSpace: indicate if is added a space after of the separator func New(comment, separator string, preSpace, postSpace bool) *Config { if comment != DEFAULT_COMMENT && comment != ALTERNATIVE_COMMENT { panic("comment character not valid") } if separator != DEFAULT_SEPARATOR && separator != ALTERNATIVE_SEPARATOR { panic("separator character not valid") } // == Get spaces around separator if preSpace { separator = " " + separator } if postSpace { separator += " " } //== c := new(Config) c.comment = comment c.separator = separator c.idSection = make(map[string]int) c.lastIdOption = make(map[string]int) c.data = make(map[string]map[string]*tValue) c.AddSection(DEFAULT_SECTION) // Default section always exists. return c } // NewDefault creates a configuration representation with values by default. func NewDefault() *Config { return New(DEFAULT_COMMENT, DEFAULT_SEPARATOR, false, true) } // Merge merges the given configuration "source" with this one ("target"). // // Merging means that any option (under any section) from source that is not in // target will be copied into target. When the target already has an option with // the same name and section then it is overwritten (i.o.w. the source wins). func (target *Config) Merge(source *Config) { if source == nil || source.data == nil || len(source.data) == 0 { return } for section, option := range source.data { for optionName, optionValue := range option { target.AddOption(section, optionName, optionValue.v) } } } // == Utility func stripComments(l string) string { // Comments are preceded by space or TAB for _, c := range []string{" ;", "\t;", " #", "\t#"} { if i := strings.Index(l, c); i != -1 { l = l[0:i] } } return l }