Full Code of robfig/config for AI

master 0f78529c8c7e cached
15 files
47.7 KB
12.4k tokens
47 symbols
1 requests
Download .txt
Repository: robfig/config
Branch: master
Commit: 0f78529c8c7e
Files: 15
Total size: 47.7 KB

Directory structure:
gitextract_86_t_bv3/

├── Doc/
│   ├── AUTHORS.md
│   ├── CONTRIBUTORS.md
│   ├── LICENSE_Apache.txt
│   └── NEWS.md
├── README.md
├── all_test.go
├── config.go
├── error.go
├── option.go
├── read.go
├── section.go
├── testdata/
│   ├── source.cfg
│   └── target.cfg
├── type.go
└── write.go

================================================
FILE CONTENTS
================================================

================================================
FILE: Doc/AUTHORS.md
================================================
###### Notice

*This is the official list of **config** authors for copyright purposes.*

*This file is distinct from the CONTRIBUTORS file. See the latter for an
explanation.*

*Names should be added to this file as: `Organization`;
`[Name](web address)` or `Name <email>` for individuals*

*Please keep the list sorted.*

* * *

[Jonas mg](https://github.com/kless)
[Miguel Branco](https://github.com/msbranco)
[Rob Figueiredo](https://github.com/robfig)
[Tom Bruggeman](https://github.com/tmbrggmn)



================================================
FILE: Doc/CONTRIBUTORS.md
================================================
###### Notice

*This is the official list of people who can contribute (and typically have
contributed) code to the **config** repository.*

*The AUTHORS file lists the copyright holders; this file lists people. For
example, the employees of an organization are listed here but not in AUTHORS,
because the organization holds the copyright.*

*Names should be added to this file as: `[Name](web address)` or `Name <email>`*

*Please keep the list sorted.*

* * *

### Initial author

[Miguel Branco](https://github.com/msbranco)

### Maintainer

[Rob Figueiredo](https://github.com/robfig)

### Other authors

[Jonas mg](https://github.com/kless)
[Tom Bruggeman](https://github.com/tmbrggmn)



================================================
FILE: Doc/LICENSE_Apache.txt
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.



================================================
FILE: Doc/NEWS.md
================================================
###### Notice

*This file documents the changes in **config** versions that are listed below.*

*Items should be added to this file as:*

	### YYYY-MM-DD  Release

	+ Additional changes.

	+ More changes.

* * *

### 2011-??-??  v0.9.6

+ Changed to line comments.


### 2010-09-15  v0.9.5

+ Sections, options and values are all case-sensitive.

+ Changed API:

  Type *File* -> *Config*  
  *NewFile()* -> *NewDefault*  
  *ReadFile()* -> *ReadDefault*

+ Added functions, *New()*, *Read()*, which allow to choose the character of
comment and separator, and the spaces around separator.

+ Better error handling.

+ Both sections and options are showed by its input order.


### 2010-08-22  v0.9

+ The files has been splitted, formatted via *gomft*.

+ Methods use *self* to refer to its own type.

+ *Get* has been removed from the functions names.

+ Fixed some errors. All tests are passed.

+ At write the header in configuration file, it is added the comment character
after of each new line.

+ Better documentation.



================================================
FILE: README.md
================================================
config
======

This package implements a basic configuration file parser language which
provides a structure similar to what you would find on Microsoft Windows INI
files.

The configuration file consists of sections, led by a "*[section]*" header and
followed by "*name: value*" entries; "*name=value*" is also accepted. Note that
leading whitespace is removed from values. The optional values can contain
format strings which refer to other values in the same section, or values in a
special *DEFAULT* section. Additional defaults can be provided on initialization
and retrieval. Comments are indicated by ";" or "#"; a comment may begin
anywhere on a line, including on the same line after parameters or section
declarations.

For example:

	[My Section]
	foodir: %(dir)s/whatever
	dir=foo

would resolve the "*%(dir)s*" to the value of "*dir*" (*foo* in this case). All
reference expansions are done on demand.

The functionality and workflow is loosely based on the *configparser* package of
the Python Standard Library.

## Installation

	go get github.com/robfig/config

## Operating instructions

Given a sample configuration file:

	[DEFAULT]
	host: www.example.com
	protocol: http://
	base-url: %(protocol)s%(host)s

	[service-1]
	url: %(base-url)s/some/path
	delegation: on
	maxclients: 200 # do not set this higher
	comments: This is a multi-line
		entry	# And this is a comment

To read this configuration file, do:

	c, _ := config.ReadDefault("config.cfg")

	c.String("service-1", "url")
	// result is string "http://www.example.com/some/path"

	c.Int("service-1", "maxclients")
	// result is int 200

	c.Bool("service-1", "delegation")
	// result is bool true

	c.String("service-1", "comments")
	// result is string "This is a multi-line\nentry"

Note the support for unfolding variables (such as *%(base-url)s*), which are read
from the special (reserved) section name *[DEFAULT]*.

A new configuration file can also be created with:

	c := config.NewDefault()
	c.AddSection("Section")
	c.AddOption("Section", "option", "value")
	c.WriteFile("config.cfg", 0644, "A header for this file")

This results in the file:

	# A header for this file

	[Section]
	option: value

Note that sections, options and values are all case-sensitive.

## License

The source files are distributed under the [Mozilla Public License, version 2.0](http://mozilla.org/MPL/2.0/),
unless otherwise noted.  
Please read the [FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html)
if you have further questions regarding the license.



================================================
FILE: all_test.go
================================================
// Copyright 2009  The "config" Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"bufio"
	"os"
	"reflect"
	"strings"
	"testing"
)

const (
	tmpFilename    = "testdata/__test.go"
	sourceFilename = "testdata/source.cfg"
	targetFilename = "testdata/target.cfg"
)

func testGet(t *testing.T, c *Config, section string, option string,
	expected interface{}) {
	ok := false
	switch expected.(type) {
	case string:
		v, _ := c.String(section, option)
		if v == expected.(string) {
			ok = true
		}
	case int:
		v, _ := c.Int(section, option)
		if v == expected.(int) {
			ok = true
		}
	case bool:
		v, _ := c.Bool(section, option)
		if v == expected.(bool) {
			ok = true
		}
	default:
		t.Fatalf("Bad test case")
	}
	if !ok {
		v, _ := c.String(section, option)
		t.Errorf("Get failure: expected different value for %s %s (expected: [%#v] got: [%#v])", section, option, expected, v)
	}
}

// TestInMemory creates configuration representation and run multiple tests in-memory.
func TestInMemory(t *testing.T) {
	c := NewDefault()

	// == Test empty structure

	// should be empty
	if len(c.Sections()) != 1 {
		t.Errorf("Sections failure: invalid length")
	}

	// test presence of missing section
	if c.HasSection("no-section") {
		t.Errorf("HasSection failure: invalid section")
	}

	// get options for missing section
	_, err := c.Options("no-section")
	if err == nil {
		t.Errorf("Options failure: invalid section")
	}

	// test presence of option for missing section
	if c.HasOption("no-section", "no-option") {
		t.Errorf("HasSection failure: invalid/section/option")
	}

	// get value from missing section/option
	_, err = c.String("no-section", "no-option")
	if err == nil {
		t.Errorf("String failure: got value for missing section/option")
	}

	// get value from missing section/option
	_, err = c.Int("no-section", "no-option")
	if err == nil {
		t.Errorf("Int failure: got value for missing section/option")
	}

	// remove missing section
	if c.RemoveSection("no-section") {
		t.Errorf("RemoveSection failure: removed missing section")
	}

	// remove missing section/option
	if c.RemoveOption("no-section", "no-option") {
		t.Errorf("RemoveOption failure: removed missing section/option")
	}

	// == Fill up structure

	// add section
	if !c.AddSection("section1") {
		t.Errorf("AddSection failure: false on first insert")
	}

	// re-add same section
	if c.AddSection("section1") {
		t.Errorf("AddSection failure: true on second insert")
	}

	// default section always exists
	if c.AddSection(DEFAULT_SECTION) {
		t.Errorf("AddSection failure: true on default section insert")
	}

	// add option/value
	if !c.AddOption("section1", "option1", "value1") {
		t.Errorf("AddOption failure: false on first insert")
	}
	testGet(t, c, "section1", "option1", "value1") // read it back

	// overwrite value
	if c.AddOption("section1", "option1", "value2") {
		t.Errorf("AddOption failure: true on second insert")
	}
	testGet(t, c, "section1", "option1", "value2") // read it back again

	// remove option/value
	if !c.RemoveOption("section1", "option1") {
		t.Errorf("RemoveOption failure: false on first remove")
	}

	// remove again
	if c.RemoveOption("section1", "option1") {
		t.Errorf("RemoveOption failure: true on second remove")
	}

	// read it back again
	_, err = c.String("section1", "option1")
	if err == nil {
		t.Errorf("String failure: got value for removed section/option")
	}

	// remove existing section
	if !c.RemoveSection("section1") {
		t.Errorf("RemoveSection failure: false on first remove")
	}

	// remove again
	if c.RemoveSection("section1") {
		t.Errorf("RemoveSection failure: true on second remove")
	}

	// == Test types

	// add section
	if !c.AddSection("section2") {
		t.Errorf("AddSection failure: false on first insert")
	}

	// add number
	if !c.AddOption("section2", "test-number", "666") {
		t.Errorf("AddOption failure: false on first insert")
	}
	testGet(t, c, "section2", "test-number", 666) // read it back

	// add 'yes' (bool)
	if !c.AddOption("section2", "test-yes", "yes") {
		t.Errorf("AddOption failure: false on first insert")
	}
	testGet(t, c, "section2", "test-yes", true) // read it back

	// add 'false' (bool)
	if !c.AddOption("section2", "test-false", "false") {
		t.Errorf("AddOption failure: false on first insert")
	}
	testGet(t, c, "section2", "test-false", false) // read it back

	// == Test cycle

	c.AddOption(DEFAULT_SECTION, "opt1", "%(opt2)s")
	c.AddOption(DEFAULT_SECTION, "opt2", "%(opt1)s")

	_, err = c.String(DEFAULT_SECTION, "opt1")
	if err == nil {
		t.Errorf("String failure: no error for cycle")
	} else if strings.Index(err.Error(), "cycle") < 0 {
		t.Errorf("String failure: incorrect error for cycle")
	}
}

// TestReadFile creates a 'tough' configuration file and test (read) parsing.
func TestReadFile(t *testing.T) {
	file, err := os.Create(tmpFilename)
	if err != nil {
		t.Fatal("Test cannot run because cannot write temporary file: " + tmpFilename)
	}

	err = os.Setenv("GO_CONFIGFILE_TEST_ENV_VAR", "configvalue12345")
	if err != nil {
		t.Fatalf("Test cannot run because cannot set environment variable GO_CONFIGFILE_TEST_ENV_VAR: %#v", err)
	}

	buf := bufio.NewWriter(file)
	buf.WriteString("optionInDefaultSection=true\n")
	buf.WriteString("[section-1]\n")
	buf.WriteString("option1=value1 ; This is a comment\n")
	buf.WriteString("option2 : 2#Not a comment\t#Now this is a comment after a TAB\n")
	buf.WriteString("  # Let me put another comment\n")
	buf.WriteString("option3= line1\n line2: \n\tline3=v # Comment multiline with := in value\n")
	buf.WriteString("; Another comment\n")
	buf.WriteString("[" + DEFAULT_SECTION + "]\n")
	buf.WriteString("variable1=small\n")
	buf.WriteString("variable2=a_part_of_a_%(variable1)s_test\n")
	buf.WriteString("[secTION-2]\n")
	buf.WriteString("IS-flag-TRUE=Yes\n")
	buf.WriteString("[section-1] # comment on section header\n") // continue again [section-1]
	buf.WriteString("option4=this_is_%(variable2)s.\n")
	buf.WriteString("envoption1=this_uses_${GO_CONFIGFILE_TEST_ENV_VAR}_env\n")
	buf.WriteString("optionInDefaultSection=false")
	buf.Flush()
	file.Close()

	c, err := ReadDefault(tmpFilename)
	if err != nil {
		t.Fatalf("ReadDefault failure: %s", err)
	}

	// check number of sections
	if len(c.Sections()) != 3 {
		t.Errorf("Sections failure: wrong number of sections")
	}

	// check number of options 6 of [section-1] plus 2 of [default]
	opts, err := c.Options("section-1")
	if len(opts) != 8 {
		t.Errorf("Options failure: wrong number of options: %d", len(opts))
	}

	testGet(t, c, "section-1", "option1", "value1")
	testGet(t, c, "section-1", "option2", "2#Not a comment")
	testGet(t, c, "section-1", "option3", "line1\nline2:\nline3=v")
	testGet(t, c, "section-1", "option4", "this_is_a_part_of_a_small_test.")
	testGet(t, c, "section-1", "envoption1", "this_uses_configvalue12345_env")
	testGet(t, c, "section-1", "optionInDefaultSection", false)
	testGet(t, c, "section-2", "optionInDefaultSection", true)
	testGet(t, c, "secTION-2", "IS-flag-TRUE", true) // case-sensitive
}

// TestWriteReadFile tests writing and reading back a configuration file.
func TestWriteReadFile(t *testing.T) {
	cw := NewDefault()

	// write file; will test only read later on
	cw.AddSection("First-Section")
	cw.AddOption("First-Section", "option1", "value option1")
	cw.AddOption("First-Section", "option2", "2")

	cw.AddOption("", "host", "www.example.com")
	cw.AddOption(DEFAULT_SECTION, "protocol", "https://")
	cw.AddOption(DEFAULT_SECTION, "base-url", "%(protocol)s%(host)s")

	cw.AddOption("Another-Section", "useHTTPS", "y")
	cw.AddOption("Another-Section", "url", "%(base-url)s/some/path")

	cw.WriteFile(tmpFilename, 0644, "Test file for test-case")

	// read back file and test
	cr, err := ReadDefault(tmpFilename)
	if err != nil {
		t.Fatalf("ReadDefault failure: %s", err)
	}

	testGet(t, cr, "First-Section", "option1", "value option1")
	testGet(t, cr, "First-Section", "option2", 2)
	testGet(t, cr, "Another-Section", "useHTTPS", true)
	testGet(t, cr, "Another-Section", "url", "https://www.example.com/some/path")

	defer os.Remove(tmpFilename)
}

// TestSectionOptions tests read options in a section without default options.
func TestSectionOptions(t *testing.T) {
	cw := NewDefault()

	// write file; will test only read later on
	cw.AddSection("First-Section")
	cw.AddOption("First-Section", "option1", "value option1")
	cw.AddOption("First-Section", "option2", "2")

	cw.AddOption("", "host", "www.example.com")
	cw.AddOption(DEFAULT_SECTION, "protocol", "https://")
	cw.AddOption(DEFAULT_SECTION, "base-url", "%(protocol)s%(host)s")

	cw.AddOption("Another-Section", "useHTTPS", "y")
	cw.AddOption("Another-Section", "url", "%(base-url)s/some/path")

	cw.WriteFile(tmpFilename, 0644, "Test file for test-case")

	// read back file and test
	cr, err := ReadDefault(tmpFilename)
	if err != nil {
		t.Fatalf("ReadDefault failure: %s", err)
	}

	options, err := cr.SectionOptions("First-Section")

	if err != nil {
		t.Fatalf("SectionOptions failure: %s", err)
	}

	if len(options) != 2 {
		t.Fatalf("SectionOptions reads wrong data: %v", options)
	}

	expected := map[string]bool{
		"option1": true,
		"option2": true,
	}
	actual := map[string]bool{}

	for _, v := range options {
		actual[v] = true
	}

	if !reflect.DeepEqual(expected, actual) {
		t.Fatalf("SectionOptions reads wrong data: %v", options)
	}

	options, err = cr.SectionOptions(DEFAULT_SECTION)

	if err != nil {
		t.Fatalf("SectionOptions failure: %s", err)
	}

	expected = map[string]bool{
		"host":     true,
		"protocol": true,
		"base-url": true,
	}
	actual = map[string]bool{}

	for _, v := range options {
		actual[v] = true
	}

	if !reflect.DeepEqual(expected, actual) {
		t.Fatalf("SectionOptions reads wrong data: %v", options)
	}

	defer os.Remove(tmpFilename)
}

// TestMerge tests merging 2 configurations.
func TestMerge(t *testing.T) {
	target, error := ReadDefault(targetFilename)
	if error != nil {
		t.Fatalf("Unable to read target config file '%s'", targetFilename)
	}

	source, error := ReadDefault(sourceFilename)
	if error != nil {
		t.Fatalf("Unable to read source config file '%s'", sourceFilename)
	}

	target.Merge(source)

	// Assert whether a regular option was merged from source -> target
	if result, _ := target.String(DEFAULT_SECTION, "one"); result != "source1" {
		t.Errorf("Expected 'one' to be '1' but instead it was '%s'", result)
	}
	// Assert that a non-existent option in source was not overwritten
	if result, _ := target.String(DEFAULT_SECTION, "five"); result != "5" {
		t.Errorf("Expected 'five' to be '5' but instead it was '%s'", result)
	}
	// Assert that a folded option was correctly unfolded
	if result, _ := target.String(DEFAULT_SECTION, "two_+_three"); result != "source2 + source3" {
		t.Errorf("Expected 'two_+_three' to be 'source2 + source3' but instead it was '%s'", result)
	}
	if result, _ := target.String(DEFAULT_SECTION, "four"); result != "4" {
		t.Errorf("Expected 'four' to be '4' but instead it was '%s'", result)
	}

	// Assert that a section option has been merged
	if result, _ := target.String("X", "x.one"); result != "sourcex1" {
		t.Errorf("Expected '[X] x.one' to be 'sourcex1' but instead it was '%s'", result)
	}
	if result, _ := target.String("X", "x.four"); result != "x4" {
		t.Errorf("Expected '[X] x.four' to be 'x4' but instead it was '%s'", result)
	}
}


================================================
FILE: config.go
================================================
// Copyright 2009  The "config" Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"regexp"
	"strings"
)

const (
	// Default section name.
	DEFAULT_SECTION = "DEFAULT"
	// Maximum allowed depth when recursively substituing variable names.
	_DEPTH_VALUES = 200

	DEFAULT_COMMENT       = "# "
	ALTERNATIVE_COMMENT   = "; "
	DEFAULT_SEPARATOR     = ":"
	ALTERNATIVE_SEPARATOR = "="
)

var (
	// Strings accepted as boolean.
	boolString = map[string]bool{
		"t":     true,
		"true":  true,
		"y":     true,
		"yes":   true,
		"on":    true,
		"1":     true,
		"f":     false,
		"false": false,
		"n":     false,
		"no":    false,
		"off":   false,
		"0":     false,
	}

	varRegExp    = regexp.MustCompile(`%\(([a-zA-Z0-9_.\-]+)\)s`) // %(variable)s
	envVarRegExp = regexp.MustCompile(`\${([a-zA-Z0-9_.\-]+)}`)   // ${envvar}
)

// Config is the representation of configuration settings.
type Config struct {
	comment   string
	separator string

	// Sections order
	lastIdSection int            // Last section identifier
	idSection     map[string]int // Section : position

	// The last option identifier used for each section.
	lastIdOption map[string]int // Section : last identifier

	// Section -> option : value
	data map[string]map[string]*tValue
}

// tValue holds the input position for a value.
type tValue struct {
	position int    // Option order
	v        string // value
}

// New creates an empty configuration representation.
// This representation can be filled with AddSection and AddOption and then
// saved to a file using WriteFile.
//
// == Arguments
//
// comment: has to be `DEFAULT_COMMENT` or `ALTERNATIVE_COMMENT`
// separator: has to be `DEFAULT_SEPARATOR` or `ALTERNATIVE_SEPARATOR`
// preSpace: indicate if is inserted a space before of the separator
// postSpace: indicate if is added a space after of the separator
func New(comment, separator string, preSpace, postSpace bool) *Config {
	if comment != DEFAULT_COMMENT && comment != ALTERNATIVE_COMMENT {
		panic("comment character not valid")
	}

	if separator != DEFAULT_SEPARATOR && separator != ALTERNATIVE_SEPARATOR {
		panic("separator character not valid")
	}

	// == Get spaces around separator
	if preSpace {
		separator = " " + separator
	}

	if postSpace {
		separator += " "
	}
	//==

	c := new(Config)

	c.comment = comment
	c.separator = separator
	c.idSection = make(map[string]int)
	c.lastIdOption = make(map[string]int)
	c.data = make(map[string]map[string]*tValue)

	c.AddSection(DEFAULT_SECTION) // Default section always exists.

	return c
}

// NewDefault creates a configuration representation with values by default.
func NewDefault() *Config {
	return New(DEFAULT_COMMENT, DEFAULT_SEPARATOR, false, true)
}

// Merge merges the given configuration "source" with this one ("target").
//
// Merging means that any option (under any section) from source that is not in
// target will be copied into target. When the target already has an option with
// the same name and section then it is overwritten (i.o.w. the source wins).
func (target *Config) Merge(source *Config) {
	if source == nil || source.data == nil || len(source.data) == 0 {
		return
	}

	for section, option := range source.data {
		for optionName, optionValue := range option {
			target.AddOption(section, optionName, optionValue.v)
		}
	}
}

// == Utility

func stripComments(l string) string {
	// Comments are preceded by space or TAB
	for _, c := range []string{" ;", "\t;", " #", "\t#"} {
		if i := strings.Index(l, c); i != -1 {
			l = l[0:i]
		}
	}
	return l
}


================================================
FILE: error.go
================================================
// Copyright 2009  The "config" Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

type SectionError string

func (e SectionError) Error() string {
	return "section not found: " + string(e)
}

type OptionError string

func (e OptionError) Error() string {
	return "option not found: " + string(e)
}


================================================
FILE: option.go
================================================
// Copyright 2009  The "config" Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import "errors"

// AddOption adds a new option and value to the configuration.
//
// If the section is nil then uses the section by default; if it does not exist,
// it is created in advance.
//
// It returns true if the option and value were inserted, and false if the value
// was overwritten.
func (c *Config) AddOption(section string, option string, value string) bool {
	c.AddSection(section) // Make sure section exists

	if section == "" {
		section = DEFAULT_SECTION
	}

	_, ok := c.data[section][option]

	c.data[section][option] = &tValue{c.lastIdOption[section], value}
	c.lastIdOption[section]++

	return !ok
}

// RemoveOption removes a option and value from the configuration.
// It returns true if the option and value were removed, and false otherwise,
// including if the section did not exist.
func (c *Config) RemoveOption(section string, option string) bool {
	if _, ok := c.data[section]; !ok {
		return false
	}

	_, ok := c.data[section][option]
	delete(c.data[section], option)

	return ok
}

// HasOption checks if the configuration has the given option in the section.
// It returns false if either the option or section do not exist.
func (c *Config) HasOption(section string, option string) bool {
	if _, ok := c.data[section]; !ok {
		return false
	}

	_, okd := c.data[DEFAULT_SECTION][option]
	_, oknd := c.data[section][option]

	return okd || oknd
}

// Options returns the list of options available in the given section.
// It returns an error if the section does not exist and an empty list if the
// section is empty. Options within the default section are also included.
func (c *Config) Options(section string) (options []string, err error) {
	if _, ok := c.data[section]; !ok {
		return nil, errors.New(SectionError(section).Error())
	}

	// Keep a map of option names we've seen to deduplicate.
	optionMap := make(map[string]struct{},
		len(c.data[DEFAULT_SECTION])+len(c.data[section]))
	for s, _ := range c.data[DEFAULT_SECTION] {
		optionMap[s] = struct{}{}
	}
	for s, _ := range c.data[section] {
		optionMap[s] = struct{}{}
	}

	// Get the keys.
	i := 0
	options = make([]string, len(optionMap))
	for k, _ := range optionMap {
		options[i] = k
		i++
	}

	return options, nil
}

// SectionOptions returns only the list of options available in the given section.
// Unlike Options, SectionOptions doesn't return options in default section.
// It returns an error if the section doesn't exist.
func (c *Config) SectionOptions(section string) (options []string, err error) {
	if _, ok := c.data[section]; !ok {
		return nil, errors.New(SectionError(section).Error())
	}

	options = make([]string, len(c.data[section]))
	i := 0
	for s, _ := range c.data[section] {
		options[i] = s
		i++
	}

	return options, nil
}


================================================
FILE: read.go
================================================
// Copyright 2009  The "config" Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"bufio"
	"errors"
	"os"
	"strings"
	"unicode"
)

// _read is the base to read a file and get the configuration representation.
// That representation can be queried with GetString, etc.
func _read(fname string, c *Config) (*Config, error) {
	file, err := os.Open(fname)
	if err != nil {
		return nil, err
	}

	if err = c.read(bufio.NewReader(file)); err != nil {
		return nil, err
	}

	if err = file.Close(); err != nil {
		return nil, err
	}

	return c, nil
}

// Read reads a configuration file and returns its representation.
// All arguments, except `fname`, are related to `New()`
func Read(fname string, comment, separator string, preSpace, postSpace bool) (*Config, error) {
	return _read(fname, New(comment, separator, preSpace, postSpace))
}

// ReadDefault reads a configuration file and returns its representation.
// It uses values by default.
func ReadDefault(fname string) (*Config, error) {
	return _read(fname, NewDefault())
}

// * * *

func (c *Config) read(buf *bufio.Reader) (err error) {
	var section, option string
	var scanner = bufio.NewScanner(buf)
	for scanner.Scan() {
		l := strings.TrimRightFunc(stripComments(scanner.Text()), unicode.IsSpace)

		// Switch written for readability (not performance)
		switch {
		// Empty line and comments
		case len(l) == 0, l[0] == '#', l[0] == ';':
			continue

		// New section. The [ must be at the start of the line
		case l[0] == '[' && l[len(l)-1] == ']':
			option = "" // reset multi-line value
			section = strings.TrimSpace(l[1 : len(l)-1])
			c.AddSection(section)

		// Continuation of multi-line value
		// starts with whitespace, we're in a section and working on an option
		case section != "" && option != "" && (l[0] == ' ' || l[0] == '\t'):
			prev, _ := c.RawString(section, option)
			value := strings.TrimSpace(l)
			c.AddOption(section, option, prev+"\n"+value)

		// Other alternatives
		default:
			i := strings.IndexAny(l, "=:")

			switch {
			// Option and value
			case i > 0 && l[0] != ' ' && l[0] != '\t': // found an =: and it's not a multiline continuation
				option = strings.TrimSpace(l[0:i])
				value := strings.TrimSpace(l[i+1:])
				c.AddOption(section, option, value)

			default:
				return errors.New("could not parse line: " + l)
			}
		}
	}
	return scanner.Err()
}


================================================
FILE: section.go
================================================
// Copyright 2009  The "config" Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

// AddSection adds a new section to the configuration.
//
// If the section is nil then uses the section by default which it's already
// created.
//
// It returns true if the new section was inserted, and false if the section
// already existed.
func (c *Config) AddSection(section string) bool {
	// DEFAULT_SECTION
	if section == "" {
		return false
	}

	if _, ok := c.data[section]; ok {
		return false
	}

	c.data[section] = make(map[string]*tValue)

	// Section order
	c.idSection[section] = c.lastIdSection
	c.lastIdSection++

	return true
}

// RemoveSection removes a section from the configuration.
// It returns true if the section was removed, and false if section did not exist.
func (c *Config) RemoveSection(section string) bool {
	_, ok := c.data[section]

	// Default section cannot be removed.
	if !ok || section == DEFAULT_SECTION {
		return false
	}

	for o, _ := range c.data[section] {
		delete(c.data[section], o) // *value
	}
	delete(c.data, section)

	delete(c.lastIdOption, section)
	delete(c.idSection, section)

	return true
}

// HasSection checks if the configuration has the given section.
// (The default section always exists.)
func (c *Config) HasSection(section string) bool {
	_, ok := c.data[section]

	return ok
}

// Sections returns the list of sections in the configuration.
// (The default section always exists).
func (c *Config) Sections() (sections []string) {
	sections = make([]string, len(c.idSection))
	pos := 0 // Position in sections

	for i := 0; i < c.lastIdSection; i++ {
		for section, id := range c.idSection {
			if id == i {
				sections[pos] = section
				pos++
			}
		}
	}

	return sections
}


================================================
FILE: testdata/source.cfg
================================================
one=source1
two=source2
three=source3
four=4

two_+_four=%(two)s + %(four)s

[X]
x.one=sourcex1
x.two=sourcex2
x.three=sourcex3

[Y]
y.one=sourcey1
y.two=sourcey2
y.three=sourcey3



================================================
FILE: testdata/target.cfg
================================================
one=1
two=2
three=3
five=5

two_+_three=%(two)s + %(three)s

[X]
x.one=x1
x.two=x2
x.three=x3
x.four=x4

[Y]
y.one=y1
y.two=y2
y.three=y3
y.four=y4



================================================
FILE: type.go
================================================
// Copyright 2009  The "config" Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"errors"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
)

// Substitutes values, calculated by callback, on matching regex
func (c *Config) computeVar(beforeValue *string, regx *regexp.Regexp, headsz, tailsz int, withVar func(*string) string) (*string, error) {
	var i int
	computedVal := beforeValue
	for i = 0; i < _DEPTH_VALUES; i++ { // keep a sane depth

		vr := regx.FindStringSubmatchIndex(*computedVal)
		if len(vr) == 0 {
			break
		}

		varname := (*computedVal)[vr[headsz]:vr[headsz+1]]
		varVal := withVar(&varname)
		if varVal == "" {
			return &varVal, errors.New(fmt.Sprintf("Option not found: %s", varname))
		}

		// substitute by new value and take off leading '%(' and trailing ')s'
		//  %(foo)s => headsz=2, tailsz=2
		//  ${foo}  => headsz=2, tailsz=1
		newVal := (*computedVal)[0:vr[headsz]-headsz] + varVal + (*computedVal)[vr[headsz+1]+tailsz:]
		computedVal = &newVal
	}

	if i == _DEPTH_VALUES {
		retVal := ""
		return &retVal,
			fmt.Errorf("Possible cycle while unfolding variables: max depth of %d reached", _DEPTH_VALUES)
	}

	return computedVal, nil
}

// Bool has the same behaviour as String but converts the response to bool.
// See "boolString" for string values converted to bool.
func (c *Config) Bool(section string, option string) (value bool, err error) {
	sv, err := c.String(section, option)
	if err != nil {
		return false, err
	}

	value, ok := boolString[strings.ToLower(sv)]
	if !ok {
		return false, errors.New("could not parse bool value: " + sv)
	}

	return value, nil
}

// Float has the same behaviour as String but converts the response to float.
func (c *Config) Float(section string, option string) (value float64, err error) {
	sv, err := c.String(section, option)
	if err == nil {
		value, err = strconv.ParseFloat(sv, 64)
	}

	return value, err
}

// Int has the same behaviour as String but converts the response to int.
func (c *Config) Int(section string, option string) (value int, err error) {
	sv, err := c.String(section, option)
	if err == nil {
		value, err = strconv.Atoi(sv)
	}

	return value, err
}

// RawString gets the (raw) string value for the given option in the section.
// The raw string value is not subjected to unfolding, which was illustrated in
// the beginning of this documentation.
//
// It returns an error if either the section or the option do not exist.
func (c *Config) RawString(section string, option string) (value string, err error) {
	if _, ok := c.data[section]; ok {
		if tValue, ok := c.data[section][option]; ok {
			return tValue.v, nil
		}
	}
	return c.RawStringDefault(option)
}

// RawStringDefault gets the (raw) string value for the given option from the
// DEFAULT section.
//
// It returns an error if the option does not exist in the DEFAULT section.
func (c *Config) RawStringDefault(option string) (value string, err error) {
	if tValue, ok := c.data[DEFAULT_SECTION][option]; ok {
		return tValue.v, nil
	}
	return "", OptionError(option)
}

// String gets the string value for the given option in the section.
// If the value needs to be unfolded (see e.g. %(host)s example in the beginning
// of this documentation), then String does this unfolding automatically, up to
// _DEPTH_VALUES number of iterations.
//
// It returns an error if either the section or the option do not exist, or the
// unfolding cycled.
func (c *Config) String(section string, option string) (value string, err error) {
	value, err = c.RawString(section, option)
	if err != nil {
		return "", err
	}

	// % variables
	computedVal, err := c.computeVar(&value, varRegExp, 2, 2, func(varName *string) string {
		lowerVar := *varName
		// search variable in default section as well as current section
		varVal, _ := c.data[DEFAULT_SECTION][lowerVar]
		if _, ok := c.data[section][lowerVar]; ok {
			varVal = c.data[section][lowerVar]
		}
		return varVal.v
	})
	value = *computedVal

	if err != nil {
		return value, err
	}

	// $ environment variables
	computedVal, err = c.computeVar(&value, envVarRegExp, 2, 1, func(varName *string) string {
		return os.Getenv(*varName)
	})
	value = *computedVal
	return value, err
}


================================================
FILE: write.go
================================================
// Copyright 2009  The "config" Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

// WriteFile saves the configuration representation to a file.
// The desired file permissions must be passed as in os.Open. The header is a
// string that is saved as a comment in the first line of the file.
func (c *Config) WriteFile(fname string, perm os.FileMode, header string) error {
	file, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
	if err != nil {
		return err
	}

	buf := bufio.NewWriter(file)
	if err = c.write(buf, header); err != nil {
		return err
	}
	buf.Flush()

	return file.Close()
}

func (c *Config) write(buf *bufio.Writer, header string) (err error) {
	if header != "" {
		// Add comment character after of each new line.
		if i := strings.Index(header, "\n"); i != -1 {
			header = strings.Replace(header, "\n", "\n"+c.comment, -1)
		}

		if _, err = buf.WriteString(c.comment + header + "\n"); err != nil {
			return err
		}
	}

	for _, orderedSection := range c.Sections() {
		for section, sectionMap := range c.data {
			if section == orderedSection {

				// Skip default section if empty.
				if section == DEFAULT_SECTION && len(sectionMap) == 0 {
					continue
				}

				if _, err = buf.WriteString("\n[" + section + "]\n"); err != nil {
					return err
				}

				// Follow the input order in options.
				for i := 0; i < c.lastIdOption[section]; i++ {
					for option, tValue := range sectionMap {

						if tValue.position == i {
							if _, err = buf.WriteString(fmt.Sprint(
								option, c.separator, tValue.v, "\n")); err != nil {
								return err
							}
							c.RemoveOption(section, option)
							break
						}
					}
				}
			}
		}
	}

	if _, err = buf.WriteString("\n"); err != nil {
		return err
	}

	return nil
}
Download .txt
gitextract_86_t_bv3/

├── Doc/
│   ├── AUTHORS.md
│   ├── CONTRIBUTORS.md
│   ├── LICENSE_Apache.txt
│   └── NEWS.md
├── README.md
├── all_test.go
├── config.go
├── error.go
├── option.go
├── read.go
├── section.go
├── testdata/
│   ├── source.cfg
│   └── target.cfg
├── type.go
└── write.go
Download .txt
SYMBOL INDEX (47 symbols across 8 files)

FILE: all_test.go
  constant tmpFilename (line 26) | tmpFilename    = "testdata/__test.go"
  constant sourceFilename (line 27) | sourceFilename = "testdata/source.cfg"
  constant targetFilename (line 28) | targetFilename = "testdata/target.cfg"
  function testGet (line 31) | func testGet(t *testing.T, c *Config, section string, option string,
  function TestInMemory (line 60) | func TestInMemory(t *testing.T) {
  function TestReadFile (line 202) | func TestReadFile(t *testing.T) {
  function TestWriteReadFile (line 260) | func TestWriteReadFile(t *testing.T) {
  function TestSectionOptions (line 292) | func TestSectionOptions(t *testing.T) {
  function TestMerge (line 364) | func TestMerge(t *testing.T) {

FILE: config.go
  constant DEFAULT_SECTION (line 24) | DEFAULT_SECTION = "DEFAULT"
  constant _DEPTH_VALUES (line 26) | _DEPTH_VALUES = 200
  constant DEFAULT_COMMENT (line 28) | DEFAULT_COMMENT       = "# "
  constant ALTERNATIVE_COMMENT (line 29) | ALTERNATIVE_COMMENT   = "; "
  constant DEFAULT_SEPARATOR (line 30) | DEFAULT_SEPARATOR     = ":"
  constant ALTERNATIVE_SEPARATOR (line 31) | ALTERNATIVE_SEPARATOR = "="
  type Config (line 56) | type Config struct
    method Merge (line 129) | func (target *Config) Merge(source *Config) {
  type tValue (line 72) | type tValue struct
  function New (line 87) | func New(comment, separator string, preSpace, postSpace bool) *Config {
  function NewDefault (line 120) | func NewDefault() *Config {
  function stripComments (line 143) | func stripComments(l string) string {

FILE: error.go
  type SectionError (line 17) | type SectionError
    method Error (line 19) | func (e SectionError) Error() string {
  type OptionError (line 23) | type OptionError
    method Error (line 25) | func (e OptionError) Error() string {

FILE: option.go
  method AddOption (line 26) | func (c *Config) AddOption(section string, option string, value string) ...
  method RemoveOption (line 44) | func (c *Config) RemoveOption(section string, option string) bool {
  method HasOption (line 57) | func (c *Config) HasOption(section string, option string) bool {
  method Options (line 71) | func (c *Config) Options(section string) (options []string, err error) {
  method SectionOptions (line 100) | func (c *Config) SectionOptions(section string) (options []string, err e...

FILE: read.go
  function _read (line 27) | func _read(fname string, c *Config) (*Config, error) {
  function Read (line 46) | func Read(fname string, comment, separator string, preSpace, postSpace b...
  function ReadDefault (line 52) | func ReadDefault(fname string) (*Config, error) {
  method read (line 58) | func (c *Config) read(buf *bufio.Reader) (err error) {

FILE: section.go
  method AddSection (line 24) | func (c *Config) AddSection(section string) bool {
  method RemoveSection (line 45) | func (c *Config) RemoveSection(section string) bool {
  method HasSection (line 66) | func (c *Config) HasSection(section string) bool {
  method Sections (line 74) | func (c *Config) Sections() (sections []string) {

FILE: type.go
  method computeVar (line 27) | func (c *Config) computeVar(beforeValue *string, regx *regexp.Regexp, he...
  method Bool (line 61) | func (c *Config) Bool(section string, option string) (value bool, err er...
  method Float (line 76) | func (c *Config) Float(section string, option string) (value float64, er...
  method Int (line 86) | func (c *Config) Int(section string, option string) (value int, err erro...
  method RawString (line 100) | func (c *Config) RawString(section string, option string) (value string,...
  method RawStringDefault (line 113) | func (c *Config) RawStringDefault(option string) (value string, err erro...
  method String (line 127) | func (c *Config) String(section string, option string) (value string, er...

FILE: write.go
  method WriteFile (line 27) | func (c *Config) WriteFile(fname string, perm os.FileMode, header string...
  method write (line 42) | func (c *Config) write(buf *bufio.Writer, header string) (err error) {
Condensed preview — 15 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (53K chars).
[
  {
    "path": "Doc/AUTHORS.md",
    "chars": 503,
    "preview": "###### Notice\n\n*This is the official list of **config** authors for copyright purposes.*\n\n*This file is distinct from th"
  },
  {
    "path": "Doc/CONTRIBUTORS.md",
    "chars": 692,
    "preview": "###### Notice\n\n*This is the official list of people who can contribute (and typically have\ncontributed) code to the **co"
  },
  {
    "path": "Doc/LICENSE_Apache.txt",
    "chars": 11358,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "Doc/NEWS.md",
    "chars": 1027,
    "preview": "###### Notice\n\n*This file documents the changes in **config** versions that are listed below.*\n\n*Items should be added t"
  },
  {
    "path": "README.md",
    "chars": 2518,
    "preview": "config\n======\n\nThis package implements a basic configuration file parser language which\nprovides a structure similar to "
  },
  {
    "path": "all_test.go",
    "chars": 11892,
    "preview": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may"
  },
  {
    "path": "config.go",
    "chars": 4077,
    "preview": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may"
  },
  {
    "path": "error.go",
    "chars": 831,
    "preview": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may"
  },
  {
    "path": "option.go",
    "chars": 3371,
    "preview": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may"
  },
  {
    "path": "read.go",
    "chars": 2898,
    "preview": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may"
  },
  {
    "path": "section.go",
    "chars": 2268,
    "preview": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may"
  },
  {
    "path": "testdata/source.cfg",
    "chars": 181,
    "preview": "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=s"
  },
  {
    "path": "testdata/target.cfg",
    "chars": 149,
    "preview": "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."
  },
  {
    "path": "type.go",
    "chars": 4731,
    "preview": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may"
  },
  {
    "path": "write.go",
    "chars": 2351,
    "preview": "// Copyright 2009  The \"config\" Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may"
  }
]

About this extraction

This page contains the full source code of the robfig/config GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 15 files (47.7 KB), approximately 12.4k tokens, and a symbol index with 47 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!