You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

392 lines
9.4 KiB

  1. // Copyright 2017 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
  15. import (
  16. "bytes"
  17. "errors"
  18. "fmt"
  19. "io"
  20. "io/ioutil"
  21. "os"
  22. "strings"
  23. "sync"
  24. )
  25. // File represents a combination of a or more INI file(s) in memory.
  26. type File struct {
  27. options LoadOptions
  28. dataSources []dataSource
  29. // Should make things safe, but sometimes doesn't matter.
  30. BlockMode bool
  31. lock sync.RWMutex
  32. // To keep data in order.
  33. sectionList []string
  34. // Actual data is stored here.
  35. sections map[string]*Section
  36. NameMapper
  37. ValueMapper
  38. }
  39. // newFile initializes File object with given data sources.
  40. func newFile(dataSources []dataSource, opts LoadOptions) *File {
  41. return &File{
  42. BlockMode: true,
  43. dataSources: dataSources,
  44. sections: make(map[string]*Section),
  45. sectionList: make([]string, 0, 10),
  46. options: opts,
  47. }
  48. }
  49. // Empty returns an empty file object.
  50. func Empty() *File {
  51. // Ignore error here, we sure our data is good.
  52. f, _ := Load([]byte(""))
  53. return f
  54. }
  55. // NewSection creates a new section.
  56. func (f *File) NewSection(name string) (*Section, error) {
  57. if len(name) == 0 {
  58. return nil, errors.New("error creating new section: empty section name")
  59. } else if f.options.Insensitive && name != DEFAULT_SECTION {
  60. name = strings.ToLower(name)
  61. }
  62. if f.BlockMode {
  63. f.lock.Lock()
  64. defer f.lock.Unlock()
  65. }
  66. if inSlice(name, f.sectionList) {
  67. return f.sections[name], nil
  68. }
  69. f.sectionList = append(f.sectionList, name)
  70. f.sections[name] = newSection(f, name)
  71. return f.sections[name], nil
  72. }
  73. // NewRawSection creates a new section with an unparseable body.
  74. func (f *File) NewRawSection(name, body string) (*Section, error) {
  75. section, err := f.NewSection(name)
  76. if err != nil {
  77. return nil, err
  78. }
  79. section.isRawSection = true
  80. section.rawBody = body
  81. return section, nil
  82. }
  83. // NewSections creates a list of sections.
  84. func (f *File) NewSections(names ...string) (err error) {
  85. for _, name := range names {
  86. if _, err = f.NewSection(name); err != nil {
  87. return err
  88. }
  89. }
  90. return nil
  91. }
  92. // GetSection returns section by given name.
  93. func (f *File) GetSection(name string) (*Section, error) {
  94. if len(name) == 0 {
  95. name = DEFAULT_SECTION
  96. }
  97. if f.options.Insensitive {
  98. name = strings.ToLower(name)
  99. }
  100. if f.BlockMode {
  101. f.lock.RLock()
  102. defer f.lock.RUnlock()
  103. }
  104. sec := f.sections[name]
  105. if sec == nil {
  106. return nil, fmt.Errorf("section '%s' does not exist", name)
  107. }
  108. return sec, nil
  109. }
  110. // Section assumes named section exists and returns a zero-value when not.
  111. func (f *File) Section(name string) *Section {
  112. sec, err := f.GetSection(name)
  113. if err != nil {
  114. // Note: It's OK here because the only possible error is empty section name,
  115. // but if it's empty, this piece of code won't be executed.
  116. sec, _ = f.NewSection(name)
  117. return sec
  118. }
  119. return sec
  120. }
  121. // Section returns list of Section.
  122. func (f *File) Sections() []*Section {
  123. sections := make([]*Section, len(f.sectionList))
  124. for i := range f.sectionList {
  125. sections[i] = f.Section(f.sectionList[i])
  126. }
  127. return sections
  128. }
  129. // ChildSections returns a list of child sections of given section name.
  130. func (f *File) ChildSections(name string) []*Section {
  131. return f.Section(name).ChildSections()
  132. }
  133. // SectionStrings returns list of section names.
  134. func (f *File) SectionStrings() []string {
  135. list := make([]string, len(f.sectionList))
  136. copy(list, f.sectionList)
  137. return list
  138. }
  139. // DeleteSection deletes a section.
  140. func (f *File) DeleteSection(name string) {
  141. if f.BlockMode {
  142. f.lock.Lock()
  143. defer f.lock.Unlock()
  144. }
  145. if len(name) == 0 {
  146. name = DEFAULT_SECTION
  147. }
  148. for i, s := range f.sectionList {
  149. if s == name {
  150. f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
  151. delete(f.sections, name)
  152. return
  153. }
  154. }
  155. }
  156. func (f *File) reload(s dataSource) error {
  157. r, err := s.ReadCloser()
  158. if err != nil {
  159. return err
  160. }
  161. defer r.Close()
  162. return f.parse(r)
  163. }
  164. // Reload reloads and parses all data sources.
  165. func (f *File) Reload() (err error) {
  166. for _, s := range f.dataSources {
  167. if err = f.reload(s); err != nil {
  168. // In loose mode, we create an empty default section for nonexistent files.
  169. if os.IsNotExist(err) && f.options.Loose {
  170. f.parse(bytes.NewBuffer(nil))
  171. continue
  172. }
  173. return err
  174. }
  175. }
  176. return nil
  177. }
  178. // Append appends one or more data sources and reloads automatically.
  179. func (f *File) Append(source interface{}, others ...interface{}) error {
  180. ds, err := parseDataSource(source)
  181. if err != nil {
  182. return err
  183. }
  184. f.dataSources = append(f.dataSources, ds)
  185. for _, s := range others {
  186. ds, err = parseDataSource(s)
  187. if err != nil {
  188. return err
  189. }
  190. f.dataSources = append(f.dataSources, ds)
  191. }
  192. return f.Reload()
  193. }
  194. func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
  195. equalSign := "="
  196. if PrettyFormat {
  197. equalSign = " = "
  198. }
  199. // Use buffer to make sure target is safe until finish encoding.
  200. buf := bytes.NewBuffer(nil)
  201. for i, sname := range f.sectionList {
  202. sec := f.Section(sname)
  203. if len(sec.Comment) > 0 {
  204. if sec.Comment[0] != '#' && sec.Comment[0] != ';' {
  205. sec.Comment = "; " + sec.Comment
  206. } else {
  207. sec.Comment = sec.Comment[:1] + " " + strings.TrimSpace(sec.Comment[1:])
  208. }
  209. if _, err := buf.WriteString(sec.Comment + LineBreak); err != nil {
  210. return nil, err
  211. }
  212. }
  213. if i > 0 || DefaultHeader {
  214. if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
  215. return nil, err
  216. }
  217. } else {
  218. // Write nothing if default section is empty
  219. if len(sec.keyList) == 0 {
  220. continue
  221. }
  222. }
  223. if sec.isRawSection {
  224. if _, err := buf.WriteString(sec.rawBody); err != nil {
  225. return nil, err
  226. }
  227. if PrettySection {
  228. // Put a line between sections
  229. if _, err := buf.WriteString(LineBreak); err != nil {
  230. return nil, err
  231. }
  232. }
  233. continue
  234. }
  235. // Count and generate alignment length and buffer spaces using the
  236. // longest key. Keys may be modifed if they contain certain characters so
  237. // we need to take that into account in our calculation.
  238. alignLength := 0
  239. if PrettyFormat {
  240. for _, kname := range sec.keyList {
  241. keyLength := len(kname)
  242. // First case will surround key by ` and second by """
  243. if strings.ContainsAny(kname, "\"=:") {
  244. keyLength += 2
  245. } else if strings.Contains(kname, "`") {
  246. keyLength += 6
  247. }
  248. if keyLength > alignLength {
  249. alignLength = keyLength
  250. }
  251. }
  252. }
  253. alignSpaces := bytes.Repeat([]byte(" "), alignLength)
  254. KEY_LIST:
  255. for _, kname := range sec.keyList {
  256. key := sec.Key(kname)
  257. if len(key.Comment) > 0 {
  258. if len(indent) > 0 && sname != DEFAULT_SECTION {
  259. buf.WriteString(indent)
  260. }
  261. if key.Comment[0] != '#' && key.Comment[0] != ';' {
  262. key.Comment = "; " + key.Comment
  263. } else {
  264. key.Comment = key.Comment[:1] + " " + strings.TrimSpace(key.Comment[1:])
  265. }
  266. if _, err := buf.WriteString(key.Comment + LineBreak); err != nil {
  267. return nil, err
  268. }
  269. }
  270. if len(indent) > 0 && sname != DEFAULT_SECTION {
  271. buf.WriteString(indent)
  272. }
  273. switch {
  274. case key.isAutoIncrement:
  275. kname = "-"
  276. case strings.ContainsAny(kname, "\"=:"):
  277. kname = "`" + kname + "`"
  278. case strings.Contains(kname, "`"):
  279. kname = `"""` + kname + `"""`
  280. }
  281. for _, val := range key.ValueWithShadows() {
  282. if _, err := buf.WriteString(kname); err != nil {
  283. return nil, err
  284. }
  285. if key.isBooleanType {
  286. if kname != sec.keyList[len(sec.keyList)-1] {
  287. buf.WriteString(LineBreak)
  288. }
  289. continue KEY_LIST
  290. }
  291. // Write out alignment spaces before "=" sign
  292. if PrettyFormat {
  293. buf.Write(alignSpaces[:alignLength-len(kname)])
  294. }
  295. // In case key value contains "\n", "`", "\"", "#" or ";"
  296. if strings.ContainsAny(val, "\n`") {
  297. val = `"""` + val + `"""`
  298. } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
  299. val = "`" + val + "`"
  300. }
  301. if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil {
  302. return nil, err
  303. }
  304. }
  305. }
  306. if PrettySection {
  307. // Put a line between sections
  308. if _, err := buf.WriteString(LineBreak); err != nil {
  309. return nil, err
  310. }
  311. }
  312. }
  313. return buf, nil
  314. }
  315. // WriteToIndent writes content into io.Writer with given indention.
  316. // If PrettyFormat has been set to be true,
  317. // it will align "=" sign with spaces under each section.
  318. func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) {
  319. buf, err := f.writeToBuffer(indent)
  320. if err != nil {
  321. return 0, err
  322. }
  323. return buf.WriteTo(w)
  324. }
  325. // WriteTo writes file content into io.Writer.
  326. func (f *File) WriteTo(w io.Writer) (int64, error) {
  327. return f.WriteToIndent(w, "")
  328. }
  329. // SaveToIndent writes content to file system with given value indention.
  330. func (f *File) SaveToIndent(filename, indent string) error {
  331. // Note: Because we are truncating with os.Create,
  332. // so it's safer to save to a temporary file location and rename afte done.
  333. buf, err := f.writeToBuffer(indent)
  334. if err != nil {
  335. return err
  336. }
  337. return ioutil.WriteFile(filename, buf.Bytes(), 0666)
  338. }
  339. // SaveTo writes content to file system.
  340. func (f *File) SaveTo(filename string) error {
  341. return f.SaveToIndent(filename, "")
  342. }