[
  {
    "path": "Doc/AUTHORS.md",
    "content": "###### Notice\n\n*This is the official list of **config** authors for copyright purposes.*\n\n*This file is distinct from the CONTRIBUTORS file. See the latter for an\nexplanation.*\n\n*Names should be added to this file as: `Organization`;\n`[Name](web address)` or `Name <email>` for individuals*\n\n*Please keep the list sorted.*\n\n* * *\n\n[Jonas mg](https://github.com/kless)\n[Miguel Branco](https://github.com/msbranco)\n[Rob Figueiredo](https://github.com/robfig)\n[Tom Bruggeman](https://github.com/tmbrggmn)\n\n"
  },
  {
    "path": "Doc/CONTRIBUTORS.md",
    "content": "###### Notice\n\n*This is the official list of people who can contribute (and typically have\ncontributed) code to the **config** repository.*\n\n*The AUTHORS file lists the copyright holders; this file lists people. For\nexample, the employees of an organization are listed here but not in AUTHORS,\nbecause the organization holds the copyright.*\n\n*Names should be added to this file as: `[Name](web address)` or `Name <email>`*\n\n*Please keep the list sorted.*\n\n* * *\n\n### Initial author\n\n[Miguel Branco](https://github.com/msbranco)\n\n### Maintainer\n\n[Rob Figueiredo](https://github.com/robfig)\n\n### Other authors\n\n[Jonas mg](https://github.com/kless)\n[Tom Bruggeman](https://github.com/tmbrggmn)\n\n"
  },
  {
    "path": "Doc/LICENSE_Apache.txt",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n"
  },
  {
    "path": "Doc/NEWS.md",
    "content": "###### Notice\n\n*This file documents the changes in **config** versions that are listed below.*\n\n*Items should be added to this file as:*\n\n\t### YYYY-MM-DD  Release\n\n\t+ Additional changes.\n\n\t+ More changes.\n\n* * *\n\n### 2011-??-??  v0.9.6\n\n+ Changed to line comments.\n\n\n### 2010-09-15  v0.9.5\n\n+ Sections, options and values are all case-sensitive.\n\n+ Changed API:\n\n  Type *File* -> *Config*  \n  *NewFile()* -> *NewDefault*  \n  *ReadFile()* -> *ReadDefault*\n\n+ Added functions, *New()*, *Read()*, which allow to choose the character of\ncomment and separator, and the spaces around separator.\n\n+ Better error handling.\n\n+ Both sections and options are showed by its input order.\n\n\n### 2010-08-22  v0.9\n\n+ The files has been splitted, formatted via *gomft*.\n\n+ Methods use *self* to refer to its own type.\n\n+ *Get* has been removed from the functions names.\n\n+ Fixed some errors. All tests are passed.\n\n+ At write the header in configuration file, it is added the comment character\nafter of each new line.\n\n+ Better documentation.\n\n"
  },
  {
    "path": "README.md",
    "content": "config\n======\n\nThis package implements a basic configuration file parser language which\nprovides a structure similar to what you would find on Microsoft Windows INI\nfiles.\n\nThe configuration file consists of sections, led by a \"*[section]*\" header and\nfollowed by \"*name: value*\" entries; \"*name=value*\" is also accepted. Note that\nleading whitespace is removed from values. The optional values can contain\nformat strings which refer to other values in the same section, or values in a\nspecial *DEFAULT* section. Additional defaults can be provided on initialization\nand retrieval. Comments are indicated by \";\" or \"#\"; a comment may begin\nanywhere on a line, including on the same line after parameters or section\ndeclarations.\n\nFor example:\n\n\t[My Section]\n\tfoodir: %(dir)s/whatever\n\tdir=foo\n\nwould resolve the \"*%(dir)s*\" to the value of \"*dir*\" (*foo* in this case). All\nreference expansions are done on demand.\n\nThe functionality and workflow is loosely based on the *configparser* package of\nthe Python Standard Library.\n\n## Installation\n\n\tgo get github.com/robfig/config\n\n## Operating instructions\n\nGiven a sample configuration file:\n\n\t[DEFAULT]\n\thost: www.example.com\n\tprotocol: http://\n\tbase-url: %(protocol)s%(host)s\n\n\t[service-1]\n\turl: %(base-url)s/some/path\n\tdelegation: on\n\tmaxclients: 200 # do not set this higher\n\tcomments: This is a multi-line\n\t\tentry\t# And this is a comment\n\nTo read this configuration file, do:\n\n\tc, _ := config.ReadDefault(\"config.cfg\")\n\n\tc.String(\"service-1\", \"url\")\n\t// result is string \"http://www.example.com/some/path\"\n\n\tc.Int(\"service-1\", \"maxclients\")\n\t// result is int 200\n\n\tc.Bool(\"service-1\", \"delegation\")\n\t// result is bool true\n\n\tc.String(\"service-1\", \"comments\")\n\t// result is string \"This is a multi-line\\nentry\"\n\nNote the support for unfolding variables (such as *%(base-url)s*), which are read\nfrom the special (reserved) section name *[DEFAULT]*.\n\nA new configuration file can also be created with:\n\n\tc := config.NewDefault()\n\tc.AddSection(\"Section\")\n\tc.AddOption(\"Section\", \"option\", \"value\")\n\tc.WriteFile(\"config.cfg\", 0644, \"A header for this file\")\n\nThis results in the file:\n\n\t# A header for this file\n\n\t[Section]\n\toption: value\n\nNote that sections, options and values are all case-sensitive.\n\n## License\n\nThe source files are distributed under the [Mozilla Public License, version 2.0](http://mozilla.org/MPL/2.0/),\nunless otherwise noted.  \nPlease read the [FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html)\nif you have further questions regarding the license.\n\n"
  },
  {
    "path": "all_test.go",
    "content": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage config\n\nimport (\n\t\"bufio\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n)\n\nconst (\n\ttmpFilename    = \"testdata/__test.go\"\n\tsourceFilename = \"testdata/source.cfg\"\n\ttargetFilename = \"testdata/target.cfg\"\n)\n\nfunc testGet(t *testing.T, c *Config, section string, option string,\n\texpected interface{}) {\n\tok := false\n\tswitch expected.(type) {\n\tcase string:\n\t\tv, _ := c.String(section, option)\n\t\tif v == expected.(string) {\n\t\t\tok = true\n\t\t}\n\tcase int:\n\t\tv, _ := c.Int(section, option)\n\t\tif v == expected.(int) {\n\t\t\tok = true\n\t\t}\n\tcase bool:\n\t\tv, _ := c.Bool(section, option)\n\t\tif v == expected.(bool) {\n\t\t\tok = true\n\t\t}\n\tdefault:\n\t\tt.Fatalf(\"Bad test case\")\n\t}\n\tif !ok {\n\t\tv, _ := c.String(section, option)\n\t\tt.Errorf(\"Get failure: expected different value for %s %s (expected: [%#v] got: [%#v])\", section, option, expected, v)\n\t}\n}\n\n// TestInMemory creates configuration representation and run multiple tests in-memory.\nfunc TestInMemory(t *testing.T) {\n\tc := NewDefault()\n\n\t// == Test empty structure\n\n\t// should be empty\n\tif len(c.Sections()) != 1 {\n\t\tt.Errorf(\"Sections failure: invalid length\")\n\t}\n\n\t// test presence of missing section\n\tif c.HasSection(\"no-section\") {\n\t\tt.Errorf(\"HasSection failure: invalid section\")\n\t}\n\n\t// get options for missing section\n\t_, err := c.Options(\"no-section\")\n\tif err == nil {\n\t\tt.Errorf(\"Options failure: invalid section\")\n\t}\n\n\t// test presence of option for missing section\n\tif c.HasOption(\"no-section\", \"no-option\") {\n\t\tt.Errorf(\"HasSection failure: invalid/section/option\")\n\t}\n\n\t// get value from missing section/option\n\t_, err = c.String(\"no-section\", \"no-option\")\n\tif err == nil {\n\t\tt.Errorf(\"String failure: got value for missing section/option\")\n\t}\n\n\t// get value from missing section/option\n\t_, err = c.Int(\"no-section\", \"no-option\")\n\tif err == nil {\n\t\tt.Errorf(\"Int failure: got value for missing section/option\")\n\t}\n\n\t// remove missing section\n\tif c.RemoveSection(\"no-section\") {\n\t\tt.Errorf(\"RemoveSection failure: removed missing section\")\n\t}\n\n\t// remove missing section/option\n\tif c.RemoveOption(\"no-section\", \"no-option\") {\n\t\tt.Errorf(\"RemoveOption failure: removed missing section/option\")\n\t}\n\n\t// == Fill up structure\n\n\t// add section\n\tif !c.AddSection(\"section1\") {\n\t\tt.Errorf(\"AddSection failure: false on first insert\")\n\t}\n\n\t// re-add same section\n\tif c.AddSection(\"section1\") {\n\t\tt.Errorf(\"AddSection failure: true on second insert\")\n\t}\n\n\t// default section always exists\n\tif c.AddSection(DEFAULT_SECTION) {\n\t\tt.Errorf(\"AddSection failure: true on default section insert\")\n\t}\n\n\t// add option/value\n\tif !c.AddOption(\"section1\", \"option1\", \"value1\") {\n\t\tt.Errorf(\"AddOption failure: false on first insert\")\n\t}\n\ttestGet(t, c, \"section1\", \"option1\", \"value1\") // read it back\n\n\t// overwrite value\n\tif c.AddOption(\"section1\", \"option1\", \"value2\") {\n\t\tt.Errorf(\"AddOption failure: true on second insert\")\n\t}\n\ttestGet(t, c, \"section1\", \"option1\", \"value2\") // read it back again\n\n\t// remove option/value\n\tif !c.RemoveOption(\"section1\", \"option1\") {\n\t\tt.Errorf(\"RemoveOption failure: false on first remove\")\n\t}\n\n\t// remove again\n\tif c.RemoveOption(\"section1\", \"option1\") {\n\t\tt.Errorf(\"RemoveOption failure: true on second remove\")\n\t}\n\n\t// read it back again\n\t_, err = c.String(\"section1\", \"option1\")\n\tif err == nil {\n\t\tt.Errorf(\"String failure: got value for removed section/option\")\n\t}\n\n\t// remove existing section\n\tif !c.RemoveSection(\"section1\") {\n\t\tt.Errorf(\"RemoveSection failure: false on first remove\")\n\t}\n\n\t// remove again\n\tif c.RemoveSection(\"section1\") {\n\t\tt.Errorf(\"RemoveSection failure: true on second remove\")\n\t}\n\n\t// == Test types\n\n\t// add section\n\tif !c.AddSection(\"section2\") {\n\t\tt.Errorf(\"AddSection failure: false on first insert\")\n\t}\n\n\t// add number\n\tif !c.AddOption(\"section2\", \"test-number\", \"666\") {\n\t\tt.Errorf(\"AddOption failure: false on first insert\")\n\t}\n\ttestGet(t, c, \"section2\", \"test-number\", 666) // read it back\n\n\t// add 'yes' (bool)\n\tif !c.AddOption(\"section2\", \"test-yes\", \"yes\") {\n\t\tt.Errorf(\"AddOption failure: false on first insert\")\n\t}\n\ttestGet(t, c, \"section2\", \"test-yes\", true) // read it back\n\n\t// add 'false' (bool)\n\tif !c.AddOption(\"section2\", \"test-false\", \"false\") {\n\t\tt.Errorf(\"AddOption failure: false on first insert\")\n\t}\n\ttestGet(t, c, \"section2\", \"test-false\", false) // read it back\n\n\t// == Test cycle\n\n\tc.AddOption(DEFAULT_SECTION, \"opt1\", \"%(opt2)s\")\n\tc.AddOption(DEFAULT_SECTION, \"opt2\", \"%(opt1)s\")\n\n\t_, err = c.String(DEFAULT_SECTION, \"opt1\")\n\tif err == nil {\n\t\tt.Errorf(\"String failure: no error for cycle\")\n\t} else if strings.Index(err.Error(), \"cycle\") < 0 {\n\t\tt.Errorf(\"String failure: incorrect error for cycle\")\n\t}\n}\n\n// TestReadFile creates a 'tough' configuration file and test (read) parsing.\nfunc TestReadFile(t *testing.T) {\n\tfile, err := os.Create(tmpFilename)\n\tif err != nil {\n\t\tt.Fatal(\"Test cannot run because cannot write temporary file: \" + tmpFilename)\n\t}\n\n\terr = os.Setenv(\"GO_CONFIGFILE_TEST_ENV_VAR\", \"configvalue12345\")\n\tif err != nil {\n\t\tt.Fatalf(\"Test cannot run because cannot set environment variable GO_CONFIGFILE_TEST_ENV_VAR: %#v\", err)\n\t}\n\n\tbuf := bufio.NewWriter(file)\n\tbuf.WriteString(\"optionInDefaultSection=true\\n\")\n\tbuf.WriteString(\"[section-1]\\n\")\n\tbuf.WriteString(\"option1=value1 ; This is a comment\\n\")\n\tbuf.WriteString(\"option2 : 2#Not a comment\\t#Now this is a comment after a TAB\\n\")\n\tbuf.WriteString(\"  # Let me put another comment\\n\")\n\tbuf.WriteString(\"option3= line1\\n line2: \\n\\tline3=v # Comment multiline with := in value\\n\")\n\tbuf.WriteString(\"; Another comment\\n\")\n\tbuf.WriteString(\"[\" + DEFAULT_SECTION + \"]\\n\")\n\tbuf.WriteString(\"variable1=small\\n\")\n\tbuf.WriteString(\"variable2=a_part_of_a_%(variable1)s_test\\n\")\n\tbuf.WriteString(\"[secTION-2]\\n\")\n\tbuf.WriteString(\"IS-flag-TRUE=Yes\\n\")\n\tbuf.WriteString(\"[section-1] # comment on section header\\n\") // continue again [section-1]\n\tbuf.WriteString(\"option4=this_is_%(variable2)s.\\n\")\n\tbuf.WriteString(\"envoption1=this_uses_${GO_CONFIGFILE_TEST_ENV_VAR}_env\\n\")\n\tbuf.WriteString(\"optionInDefaultSection=false\")\n\tbuf.Flush()\n\tfile.Close()\n\n\tc, err := ReadDefault(tmpFilename)\n\tif err != nil {\n\t\tt.Fatalf(\"ReadDefault failure: %s\", err)\n\t}\n\n\t// check number of sections\n\tif len(c.Sections()) != 3 {\n\t\tt.Errorf(\"Sections failure: wrong number of sections\")\n\t}\n\n\t// check number of options 6 of [section-1] plus 2 of [default]\n\topts, err := c.Options(\"section-1\")\n\tif len(opts) != 8 {\n\t\tt.Errorf(\"Options failure: wrong number of options: %d\", len(opts))\n\t}\n\n\ttestGet(t, c, \"section-1\", \"option1\", \"value1\")\n\ttestGet(t, c, \"section-1\", \"option2\", \"2#Not a comment\")\n\ttestGet(t, c, \"section-1\", \"option3\", \"line1\\nline2:\\nline3=v\")\n\ttestGet(t, c, \"section-1\", \"option4\", \"this_is_a_part_of_a_small_test.\")\n\ttestGet(t, c, \"section-1\", \"envoption1\", \"this_uses_configvalue12345_env\")\n\ttestGet(t, c, \"section-1\", \"optionInDefaultSection\", false)\n\ttestGet(t, c, \"section-2\", \"optionInDefaultSection\", true)\n\ttestGet(t, c, \"secTION-2\", \"IS-flag-TRUE\", true) // case-sensitive\n}\n\n// TestWriteReadFile tests writing and reading back a configuration file.\nfunc TestWriteReadFile(t *testing.T) {\n\tcw := NewDefault()\n\n\t// write file; will test only read later on\n\tcw.AddSection(\"First-Section\")\n\tcw.AddOption(\"First-Section\", \"option1\", \"value option1\")\n\tcw.AddOption(\"First-Section\", \"option2\", \"2\")\n\n\tcw.AddOption(\"\", \"host\", \"www.example.com\")\n\tcw.AddOption(DEFAULT_SECTION, \"protocol\", \"https://\")\n\tcw.AddOption(DEFAULT_SECTION, \"base-url\", \"%(protocol)s%(host)s\")\n\n\tcw.AddOption(\"Another-Section\", \"useHTTPS\", \"y\")\n\tcw.AddOption(\"Another-Section\", \"url\", \"%(base-url)s/some/path\")\n\n\tcw.WriteFile(tmpFilename, 0644, \"Test file for test-case\")\n\n\t// read back file and test\n\tcr, err := ReadDefault(tmpFilename)\n\tif err != nil {\n\t\tt.Fatalf(\"ReadDefault failure: %s\", err)\n\t}\n\n\ttestGet(t, cr, \"First-Section\", \"option1\", \"value option1\")\n\ttestGet(t, cr, \"First-Section\", \"option2\", 2)\n\ttestGet(t, cr, \"Another-Section\", \"useHTTPS\", true)\n\ttestGet(t, cr, \"Another-Section\", \"url\", \"https://www.example.com/some/path\")\n\n\tdefer os.Remove(tmpFilename)\n}\n\n// TestSectionOptions tests read options in a section without default options.\nfunc TestSectionOptions(t *testing.T) {\n\tcw := NewDefault()\n\n\t// write file; will test only read later on\n\tcw.AddSection(\"First-Section\")\n\tcw.AddOption(\"First-Section\", \"option1\", \"value option1\")\n\tcw.AddOption(\"First-Section\", \"option2\", \"2\")\n\n\tcw.AddOption(\"\", \"host\", \"www.example.com\")\n\tcw.AddOption(DEFAULT_SECTION, \"protocol\", \"https://\")\n\tcw.AddOption(DEFAULT_SECTION, \"base-url\", \"%(protocol)s%(host)s\")\n\n\tcw.AddOption(\"Another-Section\", \"useHTTPS\", \"y\")\n\tcw.AddOption(\"Another-Section\", \"url\", \"%(base-url)s/some/path\")\n\n\tcw.WriteFile(tmpFilename, 0644, \"Test file for test-case\")\n\n\t// read back file and test\n\tcr, err := ReadDefault(tmpFilename)\n\tif err != nil {\n\t\tt.Fatalf(\"ReadDefault failure: %s\", err)\n\t}\n\n\toptions, err := cr.SectionOptions(\"First-Section\")\n\n\tif err != nil {\n\t\tt.Fatalf(\"SectionOptions failure: %s\", err)\n\t}\n\n\tif len(options) != 2 {\n\t\tt.Fatalf(\"SectionOptions reads wrong data: %v\", options)\n\t}\n\n\texpected := map[string]bool{\n\t\t\"option1\": true,\n\t\t\"option2\": true,\n\t}\n\tactual := map[string]bool{}\n\n\tfor _, v := range options {\n\t\tactual[v] = true\n\t}\n\n\tif !reflect.DeepEqual(expected, actual) {\n\t\tt.Fatalf(\"SectionOptions reads wrong data: %v\", options)\n\t}\n\n\toptions, err = cr.SectionOptions(DEFAULT_SECTION)\n\n\tif err != nil {\n\t\tt.Fatalf(\"SectionOptions failure: %s\", err)\n\t}\n\n\texpected = map[string]bool{\n\t\t\"host\":     true,\n\t\t\"protocol\": true,\n\t\t\"base-url\": true,\n\t}\n\tactual = map[string]bool{}\n\n\tfor _, v := range options {\n\t\tactual[v] = true\n\t}\n\n\tif !reflect.DeepEqual(expected, actual) {\n\t\tt.Fatalf(\"SectionOptions reads wrong data: %v\", options)\n\t}\n\n\tdefer os.Remove(tmpFilename)\n}\n\n// TestMerge tests merging 2 configurations.\nfunc TestMerge(t *testing.T) {\n\ttarget, error := ReadDefault(targetFilename)\n\tif error != nil {\n\t\tt.Fatalf(\"Unable to read target config file '%s'\", targetFilename)\n\t}\n\n\tsource, error := ReadDefault(sourceFilename)\n\tif error != nil {\n\t\tt.Fatalf(\"Unable to read source config file '%s'\", sourceFilename)\n\t}\n\n\ttarget.Merge(source)\n\n\t// Assert whether a regular option was merged from source -> target\n\tif result, _ := target.String(DEFAULT_SECTION, \"one\"); result != \"source1\" {\n\t\tt.Errorf(\"Expected 'one' to be '1' but instead it was '%s'\", result)\n\t}\n\t// Assert that a non-existent option in source was not overwritten\n\tif result, _ := target.String(DEFAULT_SECTION, \"five\"); result != \"5\" {\n\t\tt.Errorf(\"Expected 'five' to be '5' but instead it was '%s'\", result)\n\t}\n\t// Assert that a folded option was correctly unfolded\n\tif result, _ := target.String(DEFAULT_SECTION, \"two_+_three\"); result != \"source2 + source3\" {\n\t\tt.Errorf(\"Expected 'two_+_three' to be 'source2 + source3' but instead it was '%s'\", result)\n\t}\n\tif result, _ := target.String(DEFAULT_SECTION, \"four\"); result != \"4\" {\n\t\tt.Errorf(\"Expected 'four' to be '4' but instead it was '%s'\", result)\n\t}\n\n\t// Assert that a section option has been merged\n\tif result, _ := target.String(\"X\", \"x.one\"); result != \"sourcex1\" {\n\t\tt.Errorf(\"Expected '[X] x.one' to be 'sourcex1' but instead it was '%s'\", result)\n\t}\n\tif result, _ := target.String(\"X\", \"x.four\"); result != \"x4\" {\n\t\tt.Errorf(\"Expected '[X] x.four' to be 'x4' but instead it was '%s'\", result)\n\t}\n}\n"
  },
  {
    "path": "config.go",
    "content": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage config\n\nimport (\n\t\"regexp\"\n\t\"strings\"\n)\n\nconst (\n\t// Default section name.\n\tDEFAULT_SECTION = \"DEFAULT\"\n\t// Maximum allowed depth when recursively substituing variable names.\n\t_DEPTH_VALUES = 200\n\n\tDEFAULT_COMMENT       = \"# \"\n\tALTERNATIVE_COMMENT   = \"; \"\n\tDEFAULT_SEPARATOR     = \":\"\n\tALTERNATIVE_SEPARATOR = \"=\"\n)\n\nvar (\n\t// Strings accepted as boolean.\n\tboolString = map[string]bool{\n\t\t\"t\":     true,\n\t\t\"true\":  true,\n\t\t\"y\":     true,\n\t\t\"yes\":   true,\n\t\t\"on\":    true,\n\t\t\"1\":     true,\n\t\t\"f\":     false,\n\t\t\"false\": false,\n\t\t\"n\":     false,\n\t\t\"no\":    false,\n\t\t\"off\":   false,\n\t\t\"0\":     false,\n\t}\n\n\tvarRegExp    = regexp.MustCompile(`%\\(([a-zA-Z0-9_.\\-]+)\\)s`) // %(variable)s\n\tenvVarRegExp = regexp.MustCompile(`\\${([a-zA-Z0-9_.\\-]+)}`)   // ${envvar}\n)\n\n// Config is the representation of configuration settings.\ntype Config struct {\n\tcomment   string\n\tseparator string\n\n\t// Sections order\n\tlastIdSection int            // Last section identifier\n\tidSection     map[string]int // Section : position\n\n\t// The last option identifier used for each section.\n\tlastIdOption map[string]int // Section : last identifier\n\n\t// Section -> option : value\n\tdata map[string]map[string]*tValue\n}\n\n// tValue holds the input position for a value.\ntype tValue struct {\n\tposition int    // Option order\n\tv        string // value\n}\n\n// New creates an empty configuration representation.\n// This representation can be filled with AddSection and AddOption and then\n// saved to a file using WriteFile.\n//\n// == Arguments\n//\n// comment: has to be `DEFAULT_COMMENT` or `ALTERNATIVE_COMMENT`\n// separator: has to be `DEFAULT_SEPARATOR` or `ALTERNATIVE_SEPARATOR`\n// preSpace: indicate if is inserted a space before of the separator\n// postSpace: indicate if is added a space after of the separator\nfunc New(comment, separator string, preSpace, postSpace bool) *Config {\n\tif comment != DEFAULT_COMMENT && comment != ALTERNATIVE_COMMENT {\n\t\tpanic(\"comment character not valid\")\n\t}\n\n\tif separator != DEFAULT_SEPARATOR && separator != ALTERNATIVE_SEPARATOR {\n\t\tpanic(\"separator character not valid\")\n\t}\n\n\t// == Get spaces around separator\n\tif preSpace {\n\t\tseparator = \" \" + separator\n\t}\n\n\tif postSpace {\n\t\tseparator += \" \"\n\t}\n\t//==\n\n\tc := new(Config)\n\n\tc.comment = comment\n\tc.separator = separator\n\tc.idSection = make(map[string]int)\n\tc.lastIdOption = make(map[string]int)\n\tc.data = make(map[string]map[string]*tValue)\n\n\tc.AddSection(DEFAULT_SECTION) // Default section always exists.\n\n\treturn c\n}\n\n// NewDefault creates a configuration representation with values by default.\nfunc NewDefault() *Config {\n\treturn New(DEFAULT_COMMENT, DEFAULT_SEPARATOR, false, true)\n}\n\n// Merge merges the given configuration \"source\" with this one (\"target\").\n//\n// Merging means that any option (under any section) from source that is not in\n// target will be copied into target. When the target already has an option with\n// the same name and section then it is overwritten (i.o.w. the source wins).\nfunc (target *Config) Merge(source *Config) {\n\tif source == nil || source.data == nil || len(source.data) == 0 {\n\t\treturn\n\t}\n\n\tfor section, option := range source.data {\n\t\tfor optionName, optionValue := range option {\n\t\t\ttarget.AddOption(section, optionName, optionValue.v)\n\t\t}\n\t}\n}\n\n// == Utility\n\nfunc stripComments(l string) string {\n\t// Comments are preceded by space or TAB\n\tfor _, c := range []string{\" ;\", \"\\t;\", \" #\", \"\\t#\"} {\n\t\tif i := strings.Index(l, c); i != -1 {\n\t\t\tl = l[0:i]\n\t\t}\n\t}\n\treturn l\n}\n"
  },
  {
    "path": "error.go",
    "content": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage config\n\ntype SectionError string\n\nfunc (e SectionError) Error() string {\n\treturn \"section not found: \" + string(e)\n}\n\ntype OptionError string\n\nfunc (e OptionError) Error() string {\n\treturn \"option not found: \" + string(e)\n}\n"
  },
  {
    "path": "option.go",
    "content": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage config\n\nimport \"errors\"\n\n// AddOption adds a new option and value to the configuration.\n//\n// If the section is nil then uses the section by default; if it does not exist,\n// it is created in advance.\n//\n// It returns true if the option and value were inserted, and false if the value\n// was overwritten.\nfunc (c *Config) AddOption(section string, option string, value string) bool {\n\tc.AddSection(section) // Make sure section exists\n\n\tif section == \"\" {\n\t\tsection = DEFAULT_SECTION\n\t}\n\n\t_, ok := c.data[section][option]\n\n\tc.data[section][option] = &tValue{c.lastIdOption[section], value}\n\tc.lastIdOption[section]++\n\n\treturn !ok\n}\n\n// RemoveOption removes a option and value from the configuration.\n// It returns true if the option and value were removed, and false otherwise,\n// including if the section did not exist.\nfunc (c *Config) RemoveOption(section string, option string) bool {\n\tif _, ok := c.data[section]; !ok {\n\t\treturn false\n\t}\n\n\t_, ok := c.data[section][option]\n\tdelete(c.data[section], option)\n\n\treturn ok\n}\n\n// HasOption checks if the configuration has the given option in the section.\n// It returns false if either the option or section do not exist.\nfunc (c *Config) HasOption(section string, option string) bool {\n\tif _, ok := c.data[section]; !ok {\n\t\treturn false\n\t}\n\n\t_, okd := c.data[DEFAULT_SECTION][option]\n\t_, oknd := c.data[section][option]\n\n\treturn okd || oknd\n}\n\n// Options returns the list of options available in the given section.\n// It returns an error if the section does not exist and an empty list if the\n// section is empty. Options within the default section are also included.\nfunc (c *Config) Options(section string) (options []string, err error) {\n\tif _, ok := c.data[section]; !ok {\n\t\treturn nil, errors.New(SectionError(section).Error())\n\t}\n\n\t// Keep a map of option names we've seen to deduplicate.\n\toptionMap := make(map[string]struct{},\n\t\tlen(c.data[DEFAULT_SECTION])+len(c.data[section]))\n\tfor s, _ := range c.data[DEFAULT_SECTION] {\n\t\toptionMap[s] = struct{}{}\n\t}\n\tfor s, _ := range c.data[section] {\n\t\toptionMap[s] = struct{}{}\n\t}\n\n\t// Get the keys.\n\ti := 0\n\toptions = make([]string, len(optionMap))\n\tfor k, _ := range optionMap {\n\t\toptions[i] = k\n\t\ti++\n\t}\n\n\treturn options, nil\n}\n\n// SectionOptions returns only the list of options available in the given section.\n// Unlike Options, SectionOptions doesn't return options in default section.\n// It returns an error if the section doesn't exist.\nfunc (c *Config) SectionOptions(section string) (options []string, err error) {\n\tif _, ok := c.data[section]; !ok {\n\t\treturn nil, errors.New(SectionError(section).Error())\n\t}\n\n\toptions = make([]string, len(c.data[section]))\n\ti := 0\n\tfor s, _ := range c.data[section] {\n\t\toptions[i] = s\n\t\ti++\n\t}\n\n\treturn options, nil\n}\n"
  },
  {
    "path": "read.go",
    "content": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage config\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"os\"\n\t\"strings\"\n\t\"unicode\"\n)\n\n// _read is the base to read a file and get the configuration representation.\n// That representation can be queried with GetString, etc.\nfunc _read(fname string, c *Config) (*Config, error) {\n\tfile, err := os.Open(fname)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err = c.read(bufio.NewReader(file)); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err = file.Close(); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn c, nil\n}\n\n// Read reads a configuration file and returns its representation.\n// All arguments, except `fname`, are related to `New()`\nfunc Read(fname string, comment, separator string, preSpace, postSpace bool) (*Config, error) {\n\treturn _read(fname, New(comment, separator, preSpace, postSpace))\n}\n\n// ReadDefault reads a configuration file and returns its representation.\n// It uses values by default.\nfunc ReadDefault(fname string) (*Config, error) {\n\treturn _read(fname, NewDefault())\n}\n\n// * * *\n\nfunc (c *Config) read(buf *bufio.Reader) (err error) {\n\tvar section, option string\n\tvar scanner = bufio.NewScanner(buf)\n\tfor scanner.Scan() {\n\t\tl := strings.TrimRightFunc(stripComments(scanner.Text()), unicode.IsSpace)\n\n\t\t// Switch written for readability (not performance)\n\t\tswitch {\n\t\t// Empty line and comments\n\t\tcase len(l) == 0, l[0] == '#', l[0] == ';':\n\t\t\tcontinue\n\n\t\t// New section. The [ must be at the start of the line\n\t\tcase l[0] == '[' && l[len(l)-1] == ']':\n\t\t\toption = \"\" // reset multi-line value\n\t\t\tsection = strings.TrimSpace(l[1 : len(l)-1])\n\t\t\tc.AddSection(section)\n\n\t\t// Continuation of multi-line value\n\t\t// starts with whitespace, we're in a section and working on an option\n\t\tcase section != \"\" && option != \"\" && (l[0] == ' ' || l[0] == '\\t'):\n\t\t\tprev, _ := c.RawString(section, option)\n\t\t\tvalue := strings.TrimSpace(l)\n\t\t\tc.AddOption(section, option, prev+\"\\n\"+value)\n\n\t\t// Other alternatives\n\t\tdefault:\n\t\t\ti := strings.IndexAny(l, \"=:\")\n\n\t\t\tswitch {\n\t\t\t// Option and value\n\t\t\tcase i > 0 && l[0] != ' ' && l[0] != '\\t': // found an =: and it's not a multiline continuation\n\t\t\t\toption = strings.TrimSpace(l[0:i])\n\t\t\t\tvalue := strings.TrimSpace(l[i+1:])\n\t\t\t\tc.AddOption(section, option, value)\n\n\t\t\tdefault:\n\t\t\t\treturn errors.New(\"could not parse line: \" + l)\n\t\t\t}\n\t\t}\n\t}\n\treturn scanner.Err()\n}\n"
  },
  {
    "path": "section.go",
    "content": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage config\n\n// AddSection adds a new section to the configuration.\n//\n// If the section is nil then uses the section by default which it's already\n// created.\n//\n// It returns true if the new section was inserted, and false if the section\n// already existed.\nfunc (c *Config) AddSection(section string) bool {\n\t// DEFAULT_SECTION\n\tif section == \"\" {\n\t\treturn false\n\t}\n\n\tif _, ok := c.data[section]; ok {\n\t\treturn false\n\t}\n\n\tc.data[section] = make(map[string]*tValue)\n\n\t// Section order\n\tc.idSection[section] = c.lastIdSection\n\tc.lastIdSection++\n\n\treturn true\n}\n\n// RemoveSection removes a section from the configuration.\n// It returns true if the section was removed, and false if section did not exist.\nfunc (c *Config) RemoveSection(section string) bool {\n\t_, ok := c.data[section]\n\n\t// Default section cannot be removed.\n\tif !ok || section == DEFAULT_SECTION {\n\t\treturn false\n\t}\n\n\tfor o, _ := range c.data[section] {\n\t\tdelete(c.data[section], o) // *value\n\t}\n\tdelete(c.data, section)\n\n\tdelete(c.lastIdOption, section)\n\tdelete(c.idSection, section)\n\n\treturn true\n}\n\n// HasSection checks if the configuration has the given section.\n// (The default section always exists.)\nfunc (c *Config) HasSection(section string) bool {\n\t_, ok := c.data[section]\n\n\treturn ok\n}\n\n// Sections returns the list of sections in the configuration.\n// (The default section always exists).\nfunc (c *Config) Sections() (sections []string) {\n\tsections = make([]string, len(c.idSection))\n\tpos := 0 // Position in sections\n\n\tfor i := 0; i < c.lastIdSection; i++ {\n\t\tfor section, id := range c.idSection {\n\t\t\tif id == i {\n\t\t\t\tsections[pos] = section\n\t\t\t\tpos++\n\t\t\t}\n\t\t}\n\t}\n\n\treturn sections\n}\n"
  },
  {
    "path": "testdata/source.cfg",
    "content": "one=source1\ntwo=source2\nthree=source3\nfour=4\n\ntwo_+_four=%(two)s + %(four)s\n\n[X]\nx.one=sourcex1\nx.two=sourcex2\nx.three=sourcex3\n\n[Y]\ny.one=sourcey1\ny.two=sourcey2\ny.three=sourcey3\n\n"
  },
  {
    "path": "testdata/target.cfg",
    "content": "one=1\ntwo=2\nthree=3\nfive=5\n\ntwo_+_three=%(two)s + %(three)s\n\n[X]\nx.one=x1\nx.two=x2\nx.three=x3\nx.four=x4\n\n[Y]\ny.one=y1\ny.two=y2\ny.three=y3\ny.four=y4\n\n"
  },
  {
    "path": "type.go",
    "content": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Substitutes values, calculated by callback, on matching regex\nfunc (c *Config) computeVar(beforeValue *string, regx *regexp.Regexp, headsz, tailsz int, withVar func(*string) string) (*string, error) {\n\tvar i int\n\tcomputedVal := beforeValue\n\tfor i = 0; i < _DEPTH_VALUES; i++ { // keep a sane depth\n\n\t\tvr := regx.FindStringSubmatchIndex(*computedVal)\n\t\tif len(vr) == 0 {\n\t\t\tbreak\n\t\t}\n\n\t\tvarname := (*computedVal)[vr[headsz]:vr[headsz+1]]\n\t\tvarVal := withVar(&varname)\n\t\tif varVal == \"\" {\n\t\t\treturn &varVal, errors.New(fmt.Sprintf(\"Option not found: %s\", varname))\n\t\t}\n\n\t\t// substitute by new value and take off leading '%(' and trailing ')s'\n\t\t//  %(foo)s => headsz=2, tailsz=2\n\t\t//  ${foo}  => headsz=2, tailsz=1\n\t\tnewVal := (*computedVal)[0:vr[headsz]-headsz] + varVal + (*computedVal)[vr[headsz+1]+tailsz:]\n\t\tcomputedVal = &newVal\n\t}\n\n\tif i == _DEPTH_VALUES {\n\t\tretVal := \"\"\n\t\treturn &retVal,\n\t\t\tfmt.Errorf(\"Possible cycle while unfolding variables: max depth of %d reached\", _DEPTH_VALUES)\n\t}\n\n\treturn computedVal, nil\n}\n\n// Bool has the same behaviour as String but converts the response to bool.\n// See \"boolString\" for string values converted to bool.\nfunc (c *Config) Bool(section string, option string) (value bool, err error) {\n\tsv, err := c.String(section, option)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tvalue, ok := boolString[strings.ToLower(sv)]\n\tif !ok {\n\t\treturn false, errors.New(\"could not parse bool value: \" + sv)\n\t}\n\n\treturn value, nil\n}\n\n// Float has the same behaviour as String but converts the response to float.\nfunc (c *Config) Float(section string, option string) (value float64, err error) {\n\tsv, err := c.String(section, option)\n\tif err == nil {\n\t\tvalue, err = strconv.ParseFloat(sv, 64)\n\t}\n\n\treturn value, err\n}\n\n// Int has the same behaviour as String but converts the response to int.\nfunc (c *Config) Int(section string, option string) (value int, err error) {\n\tsv, err := c.String(section, option)\n\tif err == nil {\n\t\tvalue, err = strconv.Atoi(sv)\n\t}\n\n\treturn value, err\n}\n\n// RawString gets the (raw) string value for the given option in the section.\n// The raw string value is not subjected to unfolding, which was illustrated in\n// the beginning of this documentation.\n//\n// It returns an error if either the section or the option do not exist.\nfunc (c *Config) RawString(section string, option string) (value string, err error) {\n\tif _, ok := c.data[section]; ok {\n\t\tif tValue, ok := c.data[section][option]; ok {\n\t\t\treturn tValue.v, nil\n\t\t}\n\t}\n\treturn c.RawStringDefault(option)\n}\n\n// RawStringDefault gets the (raw) string value for the given option from the\n// DEFAULT section.\n//\n// It returns an error if the option does not exist in the DEFAULT section.\nfunc (c *Config) RawStringDefault(option string) (value string, err error) {\n\tif tValue, ok := c.data[DEFAULT_SECTION][option]; ok {\n\t\treturn tValue.v, nil\n\t}\n\treturn \"\", OptionError(option)\n}\n\n// String gets the string value for the given option in the section.\n// If the value needs to be unfolded (see e.g. %(host)s example in the beginning\n// of this documentation), then String does this unfolding automatically, up to\n// _DEPTH_VALUES number of iterations.\n//\n// It returns an error if either the section or the option do not exist, or the\n// unfolding cycled.\nfunc (c *Config) String(section string, option string) (value string, err error) {\n\tvalue, err = c.RawString(section, option)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// % variables\n\tcomputedVal, err := c.computeVar(&value, varRegExp, 2, 2, func(varName *string) string {\n\t\tlowerVar := *varName\n\t\t// search variable in default section as well as current section\n\t\tvarVal, _ := c.data[DEFAULT_SECTION][lowerVar]\n\t\tif _, ok := c.data[section][lowerVar]; ok {\n\t\t\tvarVal = c.data[section][lowerVar]\n\t\t}\n\t\treturn varVal.v\n\t})\n\tvalue = *computedVal\n\n\tif err != nil {\n\t\treturn value, err\n\t}\n\n\t// $ environment variables\n\tcomputedVal, err = c.computeVar(&value, envVarRegExp, 2, 1, func(varName *string) string {\n\t\treturn os.Getenv(*varName)\n\t})\n\tvalue = *computedVal\n\treturn value, err\n}\n"
  },
  {
    "path": "write.go",
    "content": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage config\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\n// WriteFile saves the configuration representation to a file.\n// The desired file permissions must be passed as in os.Open. The header is a\n// string that is saved as a comment in the first line of the file.\nfunc (c *Config) WriteFile(fname string, perm os.FileMode, header string) error {\n\tfile, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbuf := bufio.NewWriter(file)\n\tif err = c.write(buf, header); err != nil {\n\t\treturn err\n\t}\n\tbuf.Flush()\n\n\treturn file.Close()\n}\n\nfunc (c *Config) write(buf *bufio.Writer, header string) (err error) {\n\tif header != \"\" {\n\t\t// Add comment character after of each new line.\n\t\tif i := strings.Index(header, \"\\n\"); i != -1 {\n\t\t\theader = strings.Replace(header, \"\\n\", \"\\n\"+c.comment, -1)\n\t\t}\n\n\t\tif _, err = buf.WriteString(c.comment + header + \"\\n\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor _, orderedSection := range c.Sections() {\n\t\tfor section, sectionMap := range c.data {\n\t\t\tif section == orderedSection {\n\n\t\t\t\t// Skip default section if empty.\n\t\t\t\tif section == DEFAULT_SECTION && len(sectionMap) == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif _, err = buf.WriteString(\"\\n[\" + section + \"]\\n\"); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\t// Follow the input order in options.\n\t\t\t\tfor i := 0; i < c.lastIdOption[section]; i++ {\n\t\t\t\t\tfor option, tValue := range sectionMap {\n\n\t\t\t\t\t\tif tValue.position == i {\n\t\t\t\t\t\t\tif _, err = buf.WriteString(fmt.Sprint(\n\t\t\t\t\t\t\t\toption, c.separator, tValue.v, \"\\n\")); err != nil {\n\t\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tc.RemoveOption(section, option)\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif _, err = buf.WriteString(\"\\n\"); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  }
]