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.

file.go 10KB

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