<commit_before>// Copyright 2013 The Author - Unknown. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// goconfig is a easy-use comments-support configuration file parser.
package goconfig

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"sync"
)

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

	// Get Errors
	SectionNotFound = iota
	// Read Errors
	BlankSection
	// Get and Read Errors
	CouldNotParse
)

var (
	lock *sync.RWMutex
	// Line break
	LineBreak = "\r\n"
	// %(variable)s
	varRegExp = regexp.MustCompile(`%\(([a-zA-Z0-9_.\-]+)\)s`)
)

// ConfigFile is the representation of configuration settings.
// The public interface is entirely through methods.
type ConfigFile struct {
	data            map[string]map[string]string // Section -> key : value
	sectionList     []string                     // Section list
	keyList         map[string][]string          // Section -> Key list
	sectionComments map[string]string            // Sections comments
	keyComments     map[string]map[string]string // Keys comments
}

// newConfigFile creates an empty configuration representation.
// This representation can be filled with AddSection and AddKey and then
// saved to a file using SaveConfigFile.
func newConfigFile() *ConfigFile {
	c := new(ConfigFile)
	c.data = make(map[string]map[string]string)
	c.keyList = make(map[string][]string)
	c.sectionComments = make(map[string]string)
	c.keyComments = make(map[string]map[string]string)
	return c
}

// SetValue adds a new section-key-value to the configuration.
// If value is an empty string(0 length), it will remove its section-key and its comments!
// It returns true if the key and value were inserted or removed, and false if the value was overwritten.
// If the section does not exist in advance, it is created.
func (c *ConfigFile) SetValue(section, key, value string) bool {
	lock.Lock()
	defer lock.Unlock()

	// Check if section exists
	if _, ok := c.data[section]; ok {
		// Section exists
		// Check length of value
		if len(value) == 0 {
			// Check if key exists
			if _, ok := c.data[section][key]; ok {
				// Execute remove operation
				delete(c.data[section], key)
				// Remove comments of key
				c.SetKeyComments(section, key, "")
				// Get index of key
				i := 0
				for _, keyName := range c.keyList[section] {
					if keyName == key {
						break
					}
					i++
				}
				// Remove from key list
				c.keyList[section] =
					append(c.keyList[section][:i], c.keyList[section][i+1:]...)
			}

			// Not exists can be seen as remove
			return true
		}
	} else {
		// Section not exists
		// Check length of value
		if len(value) == 0 {
			// Not exists can be seen as remove
			return true
		} else {
			// Execute add operation
			c.data[section] = make(map[string]string)
			// Append section to list
			c.sectionList = append(c.sectionList, section)
		}
	}

	// Check if key exists
	_, ok := c.data[section][key]
	c.data[section][key] = value
	if !ok {
		// If not exists, append to key list
		c.keyList[section] = append(c.keyList[section], key)
	}
	return !ok
}

// GetValue returns the value of key available in the given section.
// If the value needs to be unfolded (see e.g. %(google)s example in the GoConfig_test.go),
// then String does this unfolding automatically, up to
// _DEPTH_VALUES number of iterations.
// It returns an error if the section does not exist and empty string value
// It returns an empty string if the (default)key does not exist and nil error.
func (c *ConfigFile) GetValue(section, key string) (string, error) {
	lock.RLock()
	defer lock.RUnlock()

	// Check if section exists
	if _, ok := c.data[section]; !ok {
		// Section does not exist.
		return "", GetError{SectionNotFound, section}
	}

	// Section exists.
	// Check if key exists.
	value, ok := c.data[section][key]
	if !ok {
		// Check if it is a sub-section.
		if i := strings.LastIndex(section, "."); i > -1 {
			return c.GetValue(section[:i], key)
		}

		// Return empty value.
		return "", nil
	}<commit_msg>Bug fix<commit_after>// Copyright 2013 The Author - Unknown. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// goconfig is a easy-use comments-support configuration file parser.
package goconfig

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"sync"
)

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

	// Get Errors
	SectionNotFound = iota
	// Read Errors
	BlankSection
	// Get and Read Errors
	CouldNotParse
)

var (
	// Line break
	LineBreak = "\r\n"
	// %(variable)s
	varRegExp = regexp.MustCompile(`%\(([a-zA-Z0-9_.\-]+)\)s`)
)

// ConfigFile is the representation of configuration settings.
// The public interface is entirely through methods.
type ConfigFile struct {
	lock            *sync.RWMutex
	data            map[string]map[string]string // Section -> key : value
	sectionList     []string                     // Section list
	keyList         map[string][]string          // Section -> Key list
	sectionComments map[string]string            // Sections comments
	keyComments     map[string]map[string]string // Keys comments
}

// newConfigFile creates an empty configuration representation.
// This representation can be filled with AddSection and AddKey and then
// saved to a file using SaveConfigFile.
func newConfigFile() *ConfigFile {
	c := new(ConfigFile)
	c.lock = new(sync.RWMutex)
	c.data = make(map[string]map[string]string)
	c.keyList = make(map[string][]string)
	c.sectionComments = make(map[string]string)
	c.keyComments = make(map[string]map[string]string)
	return c
}

// SetValue adds a new section-key-value to the configuration.
// If value is an empty string(0 length), it will remove its section-key and its comments!
// It returns true if the key and value were inserted or removed, and false if the value was overwritten.
// If the section does not exist in advance, it is created.
func (c *ConfigFile) SetValue(section, key, value string) bool {
	c.lock.Lock()
	defer c.lock.Unlock()

	// Check if section exists
	if _, ok := c.data[section]; ok {
		// Section exists
		// Check length of value
		if len(value) == 0 {
			// Check if key exists
			if _, ok := c.data[section][key]; ok {
				// Execute remove operation
				delete(c.data[section], key)
				// Remove comments of key
				c.SetKeyComments(section, key, "")
				// Get index of key
				i := 0
				for _, keyName := range c.keyList[section] {
					if keyName == key {
						break
					}
					i++
				}
				// Remove from key list
				c.keyList[section] =
					append(c.keyList[section][:i], c.keyList[section][i+1:]...)
			}

			// Not exists can be seen as remove
			return true
		}
	} else {
		// Section not exists
		// Check length of value
		if len(value) == 0 {
			// Not exists can be seen as remove
			return true
		} else {
			// Execute add operation
			c.data[section] = make(map[string]string)
			// Append section to list
			c.sectionList = append(c.sectionList, section)
		}
	}

	// Check if key exists
	_, ok := c.data[section][key]
	c.data[section][key] = value
	if !ok {
		// If not exists, append to key list
		c.keyList[section] = append(c.keyList[section], key)
	}
	return !ok
}

// GetValue returns the value of key available in the given section.
// If the value needs to be unfolded (see e.g. %(google)s example in the GoConfig_test.go),
// then String does this unfolding automatically, up to
// _DEPTH_VALUES number of iterations.
// It returns an error if the section does not exist and empty string value
// It returns an empty string if the (default)key does not exist and nil error.
func (c *ConfigFile) GetValue(section, key string) (string, error) {
	c.lock.RLock()
	defer c.lock.RUnlock()

	// Check if section exists
	if _, ok := c.data[section]; !ok {
		// Section does not exist.
		return "", GetError{SectionNotFound, section}
	}

	// Section exists.
	// Check if key exists.
	value, ok := c.data[section][key]
	if !ok {
		// Check if it is a sub-section.
		if i := strings.LastIndex(section, "."); i > -1 {
			return c.GetValue(section[:i], key)
		}

		// Return empty value.
		return "", nil
	}
{'fraction_non_alphanumeric': 0.11635107776586877, 'fraction_numerical': 0.0035727045373347625, 'mean_word_length': 2.952, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '267085', 'n_tokens_mistral': 2855, 'n_tokens_neox': 2598, 'n_words': 935}