file.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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. if f.BlockMode {
  124. f.lock.RLock()
  125. defer f.lock.RUnlock()
  126. }
  127. sections := make([]*Section, len(f.sectionList))
  128. for i, name := range f.sectionList {
  129. sections[i] = f.sections[name]
  130. }
  131. return sections
  132. }
  133. // ChildSections returns a list of child sections of given section name.
  134. func (f *File) ChildSections(name string) []*Section {
  135. return f.Section(name).ChildSections()
  136. }
  137. // SectionStrings returns list of section names.
  138. func (f *File) SectionStrings() []string {
  139. list := make([]string, len(f.sectionList))
  140. copy(list, f.sectionList)
  141. return list
  142. }
  143. // DeleteSection deletes a section.
  144. func (f *File) DeleteSection(name string) {
  145. if f.BlockMode {
  146. f.lock.Lock()
  147. defer f.lock.Unlock()
  148. }
  149. if len(name) == 0 {
  150. name = DEFAULT_SECTION
  151. }
  152. for i, s := range f.sectionList {
  153. if s == name {
  154. f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
  155. delete(f.sections, name)
  156. return
  157. }
  158. }
  159. }
  160. func (f *File) reload(s dataSource) error {
  161. r, err := s.ReadCloser()
  162. if err != nil {
  163. return err
  164. }
  165. defer r.Close()
  166. return f.parse(r)
  167. }
  168. // Reload reloads and parses all data sources.
  169. func (f *File) Reload() (err error) {
  170. for _, s := range f.dataSources {
  171. if err = f.reload(s); err != nil {
  172. // In loose mode, we create an empty default section for nonexistent files.
  173. if os.IsNotExist(err) && f.options.Loose {
  174. f.parse(bytes.NewBuffer(nil))
  175. continue
  176. }
  177. return err
  178. }
  179. }
  180. return nil
  181. }
  182. // Append appends one or more data sources and reloads automatically.
  183. func (f *File) Append(source interface{}, others ...interface{}) error {
  184. ds, err := parseDataSource(source)
  185. if err != nil {
  186. return err
  187. }
  188. f.dataSources = append(f.dataSources, ds)
  189. for _, s := range others {
  190. ds, err = parseDataSource(s)
  191. if err != nil {
  192. return err
  193. }
  194. f.dataSources = append(f.dataSources, ds)
  195. }
  196. return f.Reload()
  197. }
  198. func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
  199. equalSign := "="
  200. if PrettyFormat || PrettyEqual {
  201. equalSign = " = "
  202. }
  203. // Use buffer to make sure target is safe until finish encoding.
  204. buf := bytes.NewBuffer(nil)
  205. for i, sname := range f.sectionList {
  206. sec := f.Section(sname)
  207. if len(sec.Comment) > 0 {
  208. if sec.Comment[0] != '#' && sec.Comment[0] != ';' {
  209. sec.Comment = "; " + sec.Comment
  210. } else {
  211. sec.Comment = sec.Comment[:1] + " " + strings.TrimSpace(sec.Comment[1:])
  212. }
  213. if _, err := buf.WriteString(sec.Comment + LineBreak); err != nil {
  214. return nil, err
  215. }
  216. }
  217. if i > 0 || DefaultHeader {
  218. if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
  219. return nil, err
  220. }
  221. } else {
  222. // Write nothing if default section is empty
  223. if len(sec.keyList) == 0 {
  224. continue
  225. }
  226. }
  227. if sec.isRawSection {
  228. if _, err := buf.WriteString(sec.rawBody); err != nil {
  229. return nil, err
  230. }
  231. if PrettySection {
  232. // Put a line between sections
  233. if _, err := buf.WriteString(LineBreak); err != nil {
  234. return nil, err
  235. }
  236. }
  237. continue
  238. }
  239. // Count and generate alignment length and buffer spaces using the
  240. // longest key. Keys may be modifed if they contain certain characters so
  241. // we need to take that into account in our calculation.
  242. alignLength := 0
  243. if PrettyFormat {
  244. for _, kname := range sec.keyList {
  245. keyLength := len(kname)
  246. // First case will surround key by ` and second by """
  247. if strings.ContainsAny(kname, "\"=:") {
  248. keyLength += 2
  249. } else if strings.Contains(kname, "`") {
  250. keyLength += 6
  251. }
  252. if keyLength > alignLength {
  253. alignLength = keyLength
  254. }
  255. }
  256. }
  257. alignSpaces := bytes.Repeat([]byte(" "), alignLength)
  258. KEY_LIST:
  259. for _, kname := range sec.keyList {
  260. key := sec.Key(kname)
  261. if len(key.Comment) > 0 {
  262. if len(indent) > 0 && sname != DEFAULT_SECTION {
  263. buf.WriteString(indent)
  264. }
  265. if key.Comment[0] != '#' && key.Comment[0] != ';' {
  266. key.Comment = "; " + key.Comment
  267. } else {
  268. key.Comment = key.Comment[:1] + " " + strings.TrimSpace(key.Comment[1:])
  269. }
  270. // Support multiline comments
  271. key.Comment = strings.Replace(key.Comment, "\n", "\n; ", -1)
  272. if _, err := buf.WriteString(key.Comment + LineBreak); err != nil {
  273. return nil, err
  274. }
  275. }
  276. if len(indent) > 0 && sname != DEFAULT_SECTION {
  277. buf.WriteString(indent)
  278. }
  279. switch {
  280. case key.isAutoIncrement:
  281. kname = "-"
  282. case strings.ContainsAny(kname, "\"=:"):
  283. kname = "`" + kname + "`"
  284. case strings.Contains(kname, "`"):
  285. kname = `"""` + kname + `"""`
  286. }
  287. for _, val := range key.ValueWithShadows() {
  288. if _, err := buf.WriteString(kname); err != nil {
  289. return nil, err
  290. }
  291. if key.isBooleanType {
  292. if kname != sec.keyList[len(sec.keyList)-1] {
  293. buf.WriteString(LineBreak)
  294. }
  295. continue KEY_LIST
  296. }
  297. // Write out alignment spaces before "=" sign
  298. if PrettyFormat {
  299. buf.Write(alignSpaces[:alignLength-len(kname)])
  300. }
  301. // In case key value contains "\n", "`", "\"", "#" or ";"
  302. if strings.ContainsAny(val, "\n`") {
  303. val = `"""` + val + `"""`
  304. } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
  305. val = "`" + val + "`"
  306. }
  307. if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil {
  308. return nil, err
  309. }
  310. }
  311. for _, val := range key.nestedValues {
  312. if _, err := buf.WriteString(indent + " " + val + LineBreak); err != nil {
  313. return nil, err
  314. }
  315. }
  316. }
  317. if PrettySection {
  318. // Put a line between sections
  319. if _, err := buf.WriteString(LineBreak); err != nil {
  320. return nil, err
  321. }
  322. }
  323. }
  324. return buf, nil
  325. }
  326. // WriteToIndent writes content into io.Writer with given indention.
  327. // If PrettyFormat has been set to be true,
  328. // it will align "=" sign with spaces under each section.
  329. func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) {
  330. buf, err := f.writeToBuffer(indent)
  331. if err != nil {
  332. return 0, err
  333. }
  334. return buf.WriteTo(w)
  335. }
  336. // WriteTo writes file content into io.Writer.
  337. func (f *File) WriteTo(w io.Writer) (int64, error) {
  338. return f.WriteToIndent(w, "")
  339. }
  340. // SaveToIndent writes content to file system with given value indention.
  341. func (f *File) SaveToIndent(filename, indent string) error {
  342. // Note: Because we are truncating with os.Create,
  343. // so it's safer to save to a temporary file location and rename afte done.
  344. buf, err := f.writeToBuffer(indent)
  345. if err != nil {
  346. return err
  347. }
  348. return ioutil.WriteFile(filename, buf.Bytes(), 0666)
  349. }
  350. // SaveTo writes content to file system.
  351. func (f *File) SaveTo(filename string) error {
  352. return f.SaveToIndent(filename, "")
  353. }