ini.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. // Copyright 2014 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. // Package ini provides INI file read and write functionality in Go.
  15. package ini
  16. import (
  17. "bytes"
  18. "errors"
  19. "fmt"
  20. "io"
  21. "io/ioutil"
  22. "os"
  23. "regexp"
  24. "runtime"
  25. "strconv"
  26. "strings"
  27. "sync"
  28. "time"
  29. )
  30. const (
  31. // Name for default section. You can use this constant or the string literal.
  32. // In most of cases, an empty string is all you need to access the section.
  33. DEFAULT_SECTION = "DEFAULT"
  34. // Maximum allowed depth when recursively substituing variable names.
  35. _DEPTH_VALUES = 99
  36. _VERSION = "1.28.1"
  37. )
  38. // Version returns current package version literal.
  39. func Version() string {
  40. return _VERSION
  41. }
  42. var (
  43. // Delimiter to determine or compose a new line.
  44. // This variable will be changed to "\r\n" automatically on Windows
  45. // at package init time.
  46. LineBreak = "\n"
  47. // Variable regexp pattern: %(variable)s
  48. varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`)
  49. // Indicate whether to align "=" sign with spaces to produce pretty output
  50. // or reduce all possible spaces for compact format.
  51. PrettyFormat = true
  52. // Explicitly write DEFAULT section header
  53. DefaultHeader = false
  54. // Indicate whether to put a line between sections
  55. PrettySection = true
  56. )
  57. func init() {
  58. if runtime.GOOS == "windows" {
  59. LineBreak = "\r\n"
  60. }
  61. }
  62. func inSlice(str string, s []string) bool {
  63. for _, v := range s {
  64. if str == v {
  65. return true
  66. }
  67. }
  68. return false
  69. }
  70. // dataSource is an interface that returns object which can be read and closed.
  71. type dataSource interface {
  72. ReadCloser() (io.ReadCloser, error)
  73. }
  74. // sourceFile represents an object that contains content on the local file system.
  75. type sourceFile struct {
  76. name string
  77. }
  78. func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) {
  79. return os.Open(s.name)
  80. }
  81. type bytesReadCloser struct {
  82. reader io.Reader
  83. }
  84. func (rc *bytesReadCloser) Read(p []byte) (n int, err error) {
  85. return rc.reader.Read(p)
  86. }
  87. func (rc *bytesReadCloser) Close() error {
  88. return nil
  89. }
  90. // sourceData represents an object that contains content in memory.
  91. type sourceData struct {
  92. data []byte
  93. }
  94. func (s *sourceData) ReadCloser() (io.ReadCloser, error) {
  95. return ioutil.NopCloser(bytes.NewReader(s.data)), nil
  96. }
  97. // sourceReadCloser represents an input stream with Close method.
  98. type sourceReadCloser struct {
  99. reader io.ReadCloser
  100. }
  101. func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) {
  102. return s.reader, nil
  103. }
  104. // File represents a combination of a or more INI file(s) in memory.
  105. type File struct {
  106. // Should make things safe, but sometimes doesn't matter.
  107. BlockMode bool
  108. // Make sure data is safe in multiple goroutines.
  109. lock sync.RWMutex
  110. // Allow combination of multiple data sources.
  111. dataSources []dataSource
  112. // Actual data is stored here.
  113. sections map[string]*Section
  114. // To keep data in order.
  115. sectionList []string
  116. options LoadOptions
  117. NameMapper
  118. ValueMapper
  119. }
  120. // newFile initializes File object with given data sources.
  121. func newFile(dataSources []dataSource, opts LoadOptions) *File {
  122. return &File{
  123. BlockMode: true,
  124. dataSources: dataSources,
  125. sections: make(map[string]*Section),
  126. sectionList: make([]string, 0, 10),
  127. options: opts,
  128. }
  129. }
  130. func parseDataSource(source interface{}) (dataSource, error) {
  131. switch s := source.(type) {
  132. case string:
  133. return sourceFile{s}, nil
  134. case []byte:
  135. return &sourceData{s}, nil
  136. case io.ReadCloser:
  137. return &sourceReadCloser{s}, nil
  138. default:
  139. return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s)
  140. }
  141. }
  142. type LoadOptions struct {
  143. // Loose indicates whether the parser should ignore nonexistent files or return error.
  144. Loose bool
  145. // Insensitive indicates whether the parser forces all section and key names to lowercase.
  146. Insensitive bool
  147. // IgnoreContinuation indicates whether to ignore continuation lines while parsing.
  148. IgnoreContinuation bool
  149. // IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value.
  150. IgnoreInlineComment bool
  151. // AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing.
  152. // This type of keys are mostly used in my.cnf.
  153. AllowBooleanKeys bool
  154. // AllowShadows indicates whether to keep track of keys with same name under same section.
  155. AllowShadows bool
  156. // Some INI formats allow group blocks that store a block of raw content that doesn't otherwise
  157. // conform to key/value pairs. Specify the names of those blocks here.
  158. UnparseableSections []string
  159. }
  160. func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) {
  161. sources := make([]dataSource, len(others)+1)
  162. sources[0], err = parseDataSource(source)
  163. if err != nil {
  164. return nil, err
  165. }
  166. for i := range others {
  167. sources[i+1], err = parseDataSource(others[i])
  168. if err != nil {
  169. return nil, err
  170. }
  171. }
  172. f := newFile(sources, opts)
  173. if err = f.Reload(); err != nil {
  174. return nil, err
  175. }
  176. return f, nil
  177. }
  178. // Load loads and parses from INI data sources.
  179. // Arguments can be mixed of file name with string type, or raw data in []byte.
  180. // It will return error if list contains nonexistent files.
  181. func Load(source interface{}, others ...interface{}) (*File, error) {
  182. return LoadSources(LoadOptions{}, source, others...)
  183. }
  184. // LooseLoad has exactly same functionality as Load function
  185. // except it ignores nonexistent files instead of returning error.
  186. func LooseLoad(source interface{}, others ...interface{}) (*File, error) {
  187. return LoadSources(LoadOptions{Loose: true}, source, others...)
  188. }
  189. // InsensitiveLoad has exactly same functionality as Load function
  190. // except it forces all section and key names to be lowercased.
  191. func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) {
  192. return LoadSources(LoadOptions{Insensitive: true}, source, others...)
  193. }
  194. // InsensitiveLoad has exactly same functionality as Load function
  195. // except it allows have shadow keys.
  196. func ShadowLoad(source interface{}, others ...interface{}) (*File, error) {
  197. return LoadSources(LoadOptions{AllowShadows: true}, source, others...)
  198. }
  199. // Empty returns an empty file object.
  200. func Empty() *File {
  201. // Ignore error here, we sure our data is good.
  202. f, _ := Load([]byte(""))
  203. return f
  204. }
  205. // NewSection creates a new section.
  206. func (f *File) NewSection(name string) (*Section, error) {
  207. if len(name) == 0 {
  208. return nil, errors.New("error creating new section: empty section name")
  209. } else if f.options.Insensitive && name != DEFAULT_SECTION {
  210. name = strings.ToLower(name)
  211. }
  212. if f.BlockMode {
  213. f.lock.Lock()
  214. defer f.lock.Unlock()
  215. }
  216. if inSlice(name, f.sectionList) {
  217. return f.sections[name], nil
  218. }
  219. f.sectionList = append(f.sectionList, name)
  220. f.sections[name] = newSection(f, name)
  221. return f.sections[name], nil
  222. }
  223. // NewRawSection creates a new section with an unparseable body.
  224. func (f *File) NewRawSection(name, body string) (*Section, error) {
  225. section, err := f.NewSection(name)
  226. if err != nil {
  227. return nil, err
  228. }
  229. section.isRawSection = true
  230. section.rawBody = body
  231. return section, nil
  232. }
  233. // NewSections creates a list of sections.
  234. func (f *File) NewSections(names ...string) (err error) {
  235. for _, name := range names {
  236. if _, err = f.NewSection(name); err != nil {
  237. return err
  238. }
  239. }
  240. return nil
  241. }
  242. // GetSection returns section by given name.
  243. func (f *File) GetSection(name string) (*Section, error) {
  244. if len(name) == 0 {
  245. name = DEFAULT_SECTION
  246. } else if f.options.Insensitive {
  247. name = strings.ToLower(name)
  248. }
  249. if f.BlockMode {
  250. f.lock.RLock()
  251. defer f.lock.RUnlock()
  252. }
  253. sec := f.sections[name]
  254. if sec == nil {
  255. return nil, fmt.Errorf("section '%s' does not exist", name)
  256. }
  257. return sec, nil
  258. }
  259. // Section assumes named section exists and returns a zero-value when not.
  260. func (f *File) Section(name string) *Section {
  261. sec, err := f.GetSection(name)
  262. if err != nil {
  263. // Note: It's OK here because the only possible error is empty section name,
  264. // but if it's empty, this piece of code won't be executed.
  265. sec, _ = f.NewSection(name)
  266. return sec
  267. }
  268. return sec
  269. }
  270. // Section returns list of Section.
  271. func (f *File) Sections() []*Section {
  272. sections := make([]*Section, len(f.sectionList))
  273. for i := range f.sectionList {
  274. sections[i] = f.Section(f.sectionList[i])
  275. }
  276. return sections
  277. }
  278. // ChildSections returns a list of child sections of given section name.
  279. func (f *File) ChildSections(name string) []*Section {
  280. return f.Section(name).ChildSections()
  281. }
  282. // SectionStrings returns list of section names.
  283. func (f *File) SectionStrings() []string {
  284. list := make([]string, len(f.sectionList))
  285. copy(list, f.sectionList)
  286. return list
  287. }
  288. // DeleteSection deletes a section.
  289. func (f *File) DeleteSection(name string) {
  290. if f.BlockMode {
  291. f.lock.Lock()
  292. defer f.lock.Unlock()
  293. }
  294. if len(name) == 0 {
  295. name = DEFAULT_SECTION
  296. }
  297. for i, s := range f.sectionList {
  298. if s == name {
  299. f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
  300. delete(f.sections, name)
  301. return
  302. }
  303. }
  304. }
  305. func (f *File) reload(s dataSource) error {
  306. r, err := s.ReadCloser()
  307. if err != nil {
  308. return err
  309. }
  310. defer r.Close()
  311. return f.parse(r)
  312. }
  313. // Reload reloads and parses all data sources.
  314. func (f *File) Reload() (err error) {
  315. for _, s := range f.dataSources {
  316. if err = f.reload(s); err != nil {
  317. // In loose mode, we create an empty default section for nonexistent files.
  318. if os.IsNotExist(err) && f.options.Loose {
  319. f.parse(bytes.NewBuffer(nil))
  320. continue
  321. }
  322. return err
  323. }
  324. }
  325. return nil
  326. }
  327. // Append appends one or more data sources and reloads automatically.
  328. func (f *File) Append(source interface{}, others ...interface{}) error {
  329. ds, err := parseDataSource(source)
  330. if err != nil {
  331. return err
  332. }
  333. f.dataSources = append(f.dataSources, ds)
  334. for _, s := range others {
  335. ds, err = parseDataSource(s)
  336. if err != nil {
  337. return err
  338. }
  339. f.dataSources = append(f.dataSources, ds)
  340. }
  341. return f.Reload()
  342. }
  343. // WriteToIndent writes content into io.Writer with given indention.
  344. // If PrettyFormat has been set to be true,
  345. // it will align "=" sign with spaces under each section.
  346. func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
  347. equalSign := "="
  348. if PrettyFormat {
  349. equalSign = " = "
  350. }
  351. // Use buffer to make sure target is safe until finish encoding.
  352. buf := bytes.NewBuffer(nil)
  353. for i, sname := range f.sectionList {
  354. sec := f.Section(sname)
  355. if len(sec.Comment) > 0 {
  356. if sec.Comment[0] != '#' && sec.Comment[0] != ';' {
  357. sec.Comment = "; " + sec.Comment
  358. }
  359. if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil {
  360. return 0, err
  361. }
  362. }
  363. if i > 0 || DefaultHeader {
  364. if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
  365. return 0, err
  366. }
  367. } else {
  368. // Write nothing if default section is empty
  369. if len(sec.keyList) == 0 {
  370. continue
  371. }
  372. }
  373. if sec.isRawSection {
  374. if _, err = buf.WriteString(sec.rawBody); err != nil {
  375. return 0, err
  376. }
  377. continue
  378. }
  379. // Count and generate alignment length and buffer spaces using the
  380. // longest key. Keys may be modifed if they contain certain characters so
  381. // we need to take that into account in our calculation.
  382. alignLength := 0
  383. if PrettyFormat {
  384. for _, kname := range sec.keyList {
  385. keyLength := len(kname)
  386. // First case will surround key by ` and second by """
  387. if strings.ContainsAny(kname, "\"=:") {
  388. keyLength += 2
  389. } else if strings.Contains(kname, "`") {
  390. keyLength += 6
  391. }
  392. if keyLength > alignLength {
  393. alignLength = keyLength
  394. }
  395. }
  396. }
  397. alignSpaces := bytes.Repeat([]byte(" "), alignLength)
  398. KEY_LIST:
  399. for _, kname := range sec.keyList {
  400. key := sec.Key(kname)
  401. if len(key.Comment) > 0 {
  402. if len(indent) > 0 && sname != DEFAULT_SECTION {
  403. buf.WriteString(indent)
  404. }
  405. if key.Comment[0] != '#' && key.Comment[0] != ';' {
  406. key.Comment = "; " + key.Comment
  407. }
  408. if _, err = buf.WriteString(key.Comment + LineBreak); err != nil {
  409. return 0, err
  410. }
  411. }
  412. if len(indent) > 0 && sname != DEFAULT_SECTION {
  413. buf.WriteString(indent)
  414. }
  415. switch {
  416. case key.isAutoIncrement:
  417. kname = "-"
  418. case strings.ContainsAny(kname, "\"=:"):
  419. kname = "`" + kname + "`"
  420. case strings.Contains(kname, "`"):
  421. kname = `"""` + kname + `"""`
  422. }
  423. for _, val := range key.ValueWithShadows() {
  424. if _, err = buf.WriteString(kname); err != nil {
  425. return 0, err
  426. }
  427. if key.isBooleanType {
  428. if kname != sec.keyList[len(sec.keyList)-1] {
  429. buf.WriteString(LineBreak)
  430. }
  431. continue KEY_LIST
  432. }
  433. // Write out alignment spaces before "=" sign
  434. if PrettyFormat {
  435. buf.Write(alignSpaces[:alignLength-len(kname)])
  436. }
  437. // In case key value contains "\n", "`", "\"", "#" or ";"
  438. if strings.ContainsAny(val, "\n`") {
  439. val = `"""` + val + `"""`
  440. } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
  441. val = "`" + val + "`"
  442. }
  443. if _, err = buf.WriteString(equalSign + val + LineBreak); err != nil {
  444. return 0, err
  445. }
  446. }
  447. }
  448. if PrettySection {
  449. // Put a line between sections
  450. if _, err = buf.WriteString(LineBreak); err != nil {
  451. return 0, err
  452. }
  453. }
  454. }
  455. return buf.WriteTo(w)
  456. }
  457. // WriteTo writes file content into io.Writer.
  458. func (f *File) WriteTo(w io.Writer) (int64, error) {
  459. return f.WriteToIndent(w, "")
  460. }
  461. // SaveToIndent writes content to file system with given value indention.
  462. func (f *File) SaveToIndent(filename, indent string) error {
  463. // Note: Because we are truncating with os.Create,
  464. // so it's safer to save to a temporary file location and rename afte done.
  465. tmpPath := filename + "." + strconv.Itoa(time.Now().Nanosecond()) + ".tmp"
  466. defer os.Remove(tmpPath)
  467. fw, err := os.Create(tmpPath)
  468. if err != nil {
  469. return err
  470. }
  471. if _, err = f.WriteToIndent(fw, indent); err != nil {
  472. fw.Close()
  473. return err
  474. }
  475. fw.Close()
  476. // Remove old file and rename the new one.
  477. os.Remove(filename)
  478. return os.Rename(tmpPath, filename)
  479. }
  480. // SaveTo writes content to file system.
  481. func (f *File) SaveTo(filename string) error {
  482. return f.SaveToIndent(filename, "")
  483. }