github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/goconfig/conf.go (about) 1 // Copyright 2013 Unknwon 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 // Package goconfig is a fully functional and comments-support configuration file(.ini) parser. 16 package goconfig 17 18 import ( 19 "fmt" 20 "regexp" 21 "runtime" 22 "strconv" 23 "strings" 24 "sync" 25 ) 26 27 const ( 28 // Default section name. 29 DEFAULT_SECTION = "DEFAULT" 30 // Maximum allowed depth when recursively substituing variable names. 31 _DEPTH_VALUES = 200 32 ) 33 34 // Parse error types. 35 const ( 36 ErrSectionNotFound = iota + 1 37 ErrKeyNotFound 38 ErrBlankSectionName 39 ErrCouldNotParse 40 ) 41 42 var LineBreak = "\n" 43 44 // Variable regexp pattern: %(variable)s 45 var varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`) 46 47 func init() { 48 if runtime.GOOS == "windows" { 49 LineBreak = "\r\n" 50 } 51 } 52 53 // A ConfigFile represents a INI formar configuration file. 54 type ConfigFile struct { 55 lock sync.RWMutex // Go map is not safe. 56 fileNames []string // Support mutil-files. 57 data map[string]map[string]string // Section -> key : value 58 59 // Lists can keep sections and keys in order. 60 sectionList []string // Section name list. 61 keyList map[string][]string // Section -> Key name list 62 63 sectionComments map[string]string // Sections comments. 64 keyComments map[string]map[string]string // Keys comments. 65 BlockMode bool // Indicates whether use lock or not. 66 } 67 68 // newConfigFile creates an empty configuration representation. 69 func newConfigFile(fileNames []string) *ConfigFile { 70 c := new(ConfigFile) 71 c.fileNames = fileNames 72 c.data = make(map[string]map[string]string) 73 c.keyList = make(map[string][]string) 74 c.sectionComments = make(map[string]string) 75 c.keyComments = make(map[string]map[string]string) 76 c.BlockMode = true 77 return c 78 } 79 80 // SetValue adds a new section-key-value to the configuration. 81 // It returns true if the key and value were inserted, 82 // or returns false if the value was overwritten. 83 // If the section does not exist in advance, it will be created. 84 func (c *ConfigFile) SetValue(section, key, value string) bool { 85 if c.BlockMode { 86 c.lock.Lock() 87 defer c.lock.Unlock() 88 } 89 90 // Blank section name represents DEFAULT section. 91 if len(section) == 0 { 92 section = DEFAULT_SECTION 93 } 94 95 // Check if section exists. 96 if _, ok := c.data[section]; !ok { 97 // Execute add operation. 98 c.data[section] = make(map[string]string) 99 // Append section to list. 100 c.sectionList = append(c.sectionList, section) 101 } 102 103 // Check if key exists. 104 _, ok := c.data[section][key] 105 c.data[section][key] = value 106 if !ok { 107 // If not exists, append to key list. 108 c.keyList[section] = append(c.keyList[section], key) 109 } 110 return !ok 111 } 112 113 // DeleteKey deletes the key in given section. 114 // It returns true if the key was deleted, 115 // or returns false if the section or key didn't exist. 116 func (c *ConfigFile) DeleteKey(section, key string) bool { 117 // Blank section name represents DEFAULT section. 118 if len(section) == 0 { 119 section = DEFAULT_SECTION 120 } 121 122 // Check if section exists. 123 if _, ok := c.data[section]; !ok { 124 return false 125 } 126 127 // Check if key exists. 128 if _, ok := c.data[section][key]; ok { 129 delete(c.data[section], key) 130 // Remove comments of key. 131 c.SetKeyComments(section, key, "") 132 // Get index of key. 133 i := 0 134 for _, keyName := range c.keyList[section] { 135 if keyName == key { 136 break 137 } 138 i++ 139 } 140 // Remove from key list. 141 c.keyList[section] = append(c.keyList[section][:i], c.keyList[section][i+1:]...) 142 return true 143 } 144 return false 145 } 146 147 // GetValue returns the value of key available in the given section. 148 // If the value needs to be unfolded 149 // (see e.g. %(google)s example in the GoConfig_test.go), 150 // then String does this unfolding automatically, up to 151 // _DEPTH_VALUES number of iterations. 152 // It returns an error and empty string value if the section does not exist, 153 // or key does not exist in DEFAULT and current sections. 154 func (c *ConfigFile) GetValue(section, key string) (string, error) { 155 if c.BlockMode { 156 c.lock.RLock() 157 defer c.lock.RUnlock() 158 } 159 160 // Blank section name represents DEFAULT section. 161 if len(section) == 0 { 162 section = DEFAULT_SECTION 163 } 164 165 // Check if section exists 166 if _, ok := c.data[section]; !ok { 167 // Section does not exist. 168 return "", getError{ErrSectionNotFound, section} 169 } 170 171 // Section exists. 172 // Check if key exists or empty value. 173 value, ok := c.data[section][key] 174 if !ok { 175 // Check if it is a sub-section. 176 if i := strings.LastIndex(section, "."); i > -1 { 177 return c.GetValue(section[:i], key) 178 } 179 180 // Return empty value. 181 return "", getError{ErrKeyNotFound, key} 182 } 183 184 // Key exists. 185 var i int 186 for i = 0; i < _DEPTH_VALUES; i++ { 187 vr := varPattern.FindString(value) 188 if len(vr) == 0 { 189 break 190 } 191 192 // Take off leading '%(' and trailing ')s'. 193 noption := strings.TrimLeft(vr, "%(") 194 noption = strings.TrimRight(noption, ")s") 195 196 // Search variable in default section. 197 nvalue, err := c.GetValue(DEFAULT_SECTION, noption) 198 if err != nil && section != DEFAULT_SECTION { 199 // Search in the same section. 200 if _, ok := c.data[section][noption]; ok { 201 nvalue = c.data[section][noption] 202 } 203 } 204 205 // Substitute by new value and take off leading '%(' and trailing ')s'. 206 value = strings.Replace(value, vr, nvalue, -1) 207 } 208 return value, nil 209 } 210 211 // Bool returns bool type value. 212 func (c *ConfigFile) Bool(section, key string) (bool, error) { 213 value, err := c.GetValue(section, key) 214 if err != nil { 215 return false, err 216 } 217 return strconv.ParseBool(value) 218 } 219 220 // Float64 returns float64 type value. 221 func (c *ConfigFile) Float64(section, key string) (float64, error) { 222 value, err := c.GetValue(section, key) 223 if err != nil { 224 return 0.0, err 225 } 226 return strconv.ParseFloat(value, 64) 227 } 228 229 // Int returns int type value. 230 func (c *ConfigFile) Int(section, key string) (int, error) { 231 value, err := c.GetValue(section, key) 232 if err != nil { 233 return 0, err 234 } 235 return strconv.Atoi(value) 236 } 237 238 // Int64 returns int64 type value. 239 func (c *ConfigFile) Int64(section, key string) (int64, error) { 240 value, err := c.GetValue(section, key) 241 if err != nil { 242 return 0, err 243 } 244 return strconv.ParseInt(value, 10, 64) 245 } 246 247 // MustValue always returns value without error. 248 // It returns empty string if error occurs, or the default value if given. 249 func (c *ConfigFile) MustValue(section, key string, defaultVal ...string) string { 250 val, err := c.GetValue(section, key) 251 if len(defaultVal) > 0 && (err != nil || len(val) == 0) { 252 return defaultVal[0] 253 } 254 return val 255 } 256 257 // MustValue always returns value without error, 258 // It returns empty string if error occurs, or the default value if given, 259 // and a bool value indicates whether default value is returned. 260 func (c *ConfigFile) MustValueSet(section, key string, defaultVal ...string) (string, bool) { 261 val, err := c.GetValue(section, key) 262 if len(defaultVal) > 0 && (err != nil || len(val) == 0) { 263 c.SetValue(section, key, defaultVal[0]) 264 return defaultVal[0], true 265 } 266 return val, false 267 } 268 269 // MustValueRange always returns value without error, 270 // it returns default value if error occurs or doesn't fit into range. 271 func (c *ConfigFile) MustValueRange(section, key, defaultVal string, candidates []string) string { 272 val, err := c.GetValue(section, key) 273 if err != nil || len(val) == 0 { 274 return defaultVal 275 } 276 277 for _, cand := range candidates { 278 if val == cand { 279 return val 280 } 281 } 282 return defaultVal 283 } 284 285 // MustValueArray always returns value array without error, 286 // it returns empty array if error occurs, split by delimiter otherwise. 287 func (c *ConfigFile) MustValueArray(section, key, delim string) []string { 288 val, err := c.GetValue(section, key) 289 if err != nil || len(val) == 0 { 290 return []string{} 291 } 292 293 vals := strings.Split(val, delim) 294 for i := range vals { 295 vals[i] = strings.TrimSpace(vals[i]) 296 } 297 return vals 298 } 299 300 // MustBool always returns value without error, 301 // it returns false if error occurs. 302 func (c *ConfigFile) MustBool(section, key string, defaultVal ...bool) bool { 303 val, err := c.Bool(section, key) 304 if len(defaultVal) > 0 && err != nil { 305 return defaultVal[0] 306 } 307 return val 308 } 309 310 // MustFloat64 always returns value without error, 311 // it returns 0.0 if error occurs. 312 func (c *ConfigFile) MustFloat64(section, key string, defaultVal ...float64) float64 { 313 value, err := c.Float64(section, key) 314 if len(defaultVal) > 0 && err != nil { 315 return defaultVal[0] 316 } 317 return value 318 } 319 320 // MustInt always returns value without error, 321 // it returns 0 if error occurs. 322 func (c *ConfigFile) MustInt(section, key string, defaultVal ...int) int { 323 value, err := c.Int(section, key) 324 if len(defaultVal) > 0 && err != nil { 325 return defaultVal[0] 326 } 327 return value 328 } 329 330 // MustInt64 always returns value without error, 331 // it returns 0 if error occurs. 332 func (c *ConfigFile) MustInt64(section, key string, defaultVal ...int64) int64 { 333 value, err := c.Int64(section, key) 334 if len(defaultVal) > 0 && err != nil { 335 return defaultVal[0] 336 } 337 return value 338 } 339 340 // GetSectionList returns the list of all sections 341 // in the same order in the file. 342 func (c *ConfigFile) GetSectionList() []string { 343 list := make([]string, len(c.sectionList)) 344 copy(list, c.sectionList) 345 return list 346 } 347 348 // GetKeyList returns the list of all keys in give section 349 // in the same order in the file. 350 // It returns nil if given section does not exist. 351 func (c *ConfigFile) GetKeyList(section string) []string { 352 // Blank section name represents DEFAULT section. 353 if len(section) == 0 { 354 section = DEFAULT_SECTION 355 } 356 357 // Check if section exists. 358 if _, ok := c.data[section]; !ok { 359 return nil 360 } 361 362 // Non-default section has a blank key as section keeper. 363 offset := 1 364 if section == DEFAULT_SECTION { 365 offset = 0 366 } 367 368 list := make([]string, len(c.keyList[section])-offset) 369 copy(list, c.keyList[section][offset:]) 370 return list 371 } 372 373 // DeleteSection deletes the entire section by given name. 374 // It returns true if the section was deleted, and false if the section didn't exist. 375 func (c *ConfigFile) DeleteSection(section string) bool { 376 // Blank section name represents DEFAULT section. 377 if len(section) == 0 { 378 section = DEFAULT_SECTION 379 } 380 381 // Check if section exists. 382 if _, ok := c.data[section]; !ok { 383 return false 384 } 385 386 delete(c.data, section) 387 // Remove comments of section. 388 c.SetSectionComments(section, "") 389 // Get index of section. 390 i := 0 391 for _, secName := range c.sectionList { 392 if secName == section { 393 break 394 } 395 i++ 396 } 397 // Remove from section and key list. 398 c.sectionList = append(c.sectionList[:i], c.sectionList[i+1:]...) 399 delete(c.keyList, section) 400 return true 401 } 402 403 // GetSection returns key-value pairs in given section. 404 // It section does not exist, returns nil and error. 405 func (c *ConfigFile) GetSection(section string) (map[string]string, error) { 406 // Blank section name represents DEFAULT section. 407 if len(section) == 0 { 408 section = DEFAULT_SECTION 409 } 410 411 // Check if section exists. 412 if _, ok := c.data[section]; !ok { 413 // Section does not exist. 414 return nil, getError{ErrSectionNotFound, section} 415 } 416 417 // Remove pre-defined key. 418 secMap := c.data[section] 419 delete(c.data[section], " ") 420 421 // Section exists. 422 return secMap, nil 423 } 424 425 // SetSectionComments adds new section comments to the configuration. 426 // If comments are empty(0 length), it will remove its section comments! 427 // It returns true if the comments were inserted or removed, 428 // or returns false if the comments were overwritten. 429 func (c *ConfigFile) SetSectionComments(section, comments string) bool { 430 // Blank section name represents DEFAULT section. 431 if len(section) == 0 { 432 section = DEFAULT_SECTION 433 } 434 435 if len(comments) == 0 { 436 if _, ok := c.sectionComments[section]; ok { 437 delete(c.sectionComments, section) 438 } 439 440 // Not exists can be seen as remove. 441 return true 442 } 443 444 // Check if comments exists. 445 _, ok := c.sectionComments[section] 446 if comments[0] != '#' && comments[0] != ';' { 447 comments = "; " + comments 448 } 449 c.sectionComments[section] = comments 450 return !ok 451 } 452 453 // SetKeyComments adds new section-key comments to the configuration. 454 // If comments are empty(0 length), it will remove its section-key comments! 455 // It returns true if the comments were inserted or removed, 456 // or returns false if the comments were overwritten. 457 // If the section does not exist in advance, it is created. 458 func (c *ConfigFile) SetKeyComments(section, key, comments string) bool { 459 // Blank section name represents DEFAULT section. 460 if len(section) == 0 { 461 section = DEFAULT_SECTION 462 } 463 464 // Check if section exists. 465 if _, ok := c.keyComments[section]; ok { 466 if len(comments) == 0 { 467 if _, ok := c.keyComments[section][key]; ok { 468 delete(c.keyComments[section], key) 469 } 470 471 // Not exists can be seen as remove. 472 return true 473 } 474 } else { 475 if len(comments) == 0 { 476 // Not exists can be seen as remove. 477 return true 478 } else { 479 // Execute add operation. 480 c.keyComments[section] = make(map[string]string) 481 } 482 } 483 484 // Check if key exists. 485 _, ok := c.keyComments[section][key] 486 if comments[0] != '#' && comments[0] != ';' { 487 comments = "; " + comments 488 } 489 c.keyComments[section][key] = comments 490 return !ok 491 } 492 493 // GetSectionComments returns the comments in the given section. 494 // It returns an empty string(0 length) if the comments do not exist. 495 func (c *ConfigFile) GetSectionComments(section string) (comments string) { 496 // Blank section name represents DEFAULT section. 497 if len(section) == 0 { 498 section = DEFAULT_SECTION 499 } 500 return c.sectionComments[section] 501 } 502 503 // GetKeyComments returns the comments of key in the given section. 504 // It returns an empty string(0 length) if the comments do not exist. 505 func (c *ConfigFile) GetKeyComments(section, key string) (comments string) { 506 // Blank section name represents DEFAULT section. 507 if len(section) == 0 { 508 section = DEFAULT_SECTION 509 } 510 511 if _, ok := c.keyComments[section]; ok { 512 return c.keyComments[section][key] 513 } 514 return "" 515 } 516 517 // getError occurs when get value in configuration file with invalid parameter. 518 type getError struct { 519 Reason int 520 Name string 521 } 522 523 // Error implements Error interface. 524 func (err getError) Error() string { 525 switch err.Reason { 526 case ErrSectionNotFound: 527 return fmt.Sprintf("section '%s' not found", err.Name) 528 case ErrKeyNotFound: 529 return fmt.Sprintf("key '%s' not found", err.Name) 530 } 531 return "invalid get error" 532 }