<commit_before>/*
 * This file is part of remco.
 * © 2016 The Remco Authors
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

package main

import (
	"context"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/HeavyHorst/remco/backends"
	backendErrors "github.com/HeavyHorst/remco/backends/error"
	"github.com/HeavyHorst/remco/log"
	"github.com/HeavyHorst/remco/template"
	"github.com/Sirupsen/logrus"
	"github.com/naoina/toml"
)

type resource struct {
	Template []*template.ProcessConfig
	Backend  backends.Config
}

// configuration is the representation of an config file
type configuration struct {
	LogLevel   string `toml:"log_level"`
	LogFormat  string `toml:"log_format"`
	IncludeDir string `toml:"include_dir"`
	Resource   []resource
}

func readFileAndExpandEnv(path string) ([]byte, error) {
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		return buf, err
	}
	// expand the environment variables
	buf = []byte(os.ExpandEnv(string(buf)))
	return buf, nil
}

// newConfiguration reads the file at `path`, expand the environment variables
// and unmarshals it to a new configuration struct.
// it returns an error if any.
func newConfiguration(path string) (configuration, error) {
	var c configuration

	buf, err := readFileAndExpandEnv(path)
	if err != nil {
		return c, err
	}

	if err := toml.Unmarshal(buf, &c); err != nil {
		return c, err
	}

	c.configureLogger()

	if c.IncludeDir != "" {
		files, err := ioutil.ReadDir(c.IncludeDir)
		if err != nil {
			return c, err
		}
		for _, file := range files {
			buf, err := readFileAndExpandEnv(filepath.Join(c.IncludeDir, file.Name()))
			if err != nil {
				return c, err
			}
			var r resource
			if err := toml.Unmarshal(buf, &r); err != nil {
				return c, err
			}
			c.Resource = append(c.Resource, r)
		}
	}

	return c, nil
}

// configureLogger configures the global logger
// for example it sets the log level and log formatting
func (c *configuration) configureLogger() {
	if c.LogLevel != "" {
		err := log.SetLevel(c.LogLevel)
		if err != nil {
			log.Error(err)
		}
	}
	if c.LogFormat != "" {
		log.SetFormatter(c.LogFormat)
	}
}

// run connects to all given backends and starts the template processing as defined in the config file
func (c *configuration) run(stop chan struct{}) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	done := make(chan struct{})

	wait := sync.WaitGroup{}
	for _, v := range c.Resource {
		var backendList template.Backends

		// try to connect to all supported backends
		// add them to the backendList on success
		// log an error otherwise
		for _, config := range v.Backend.GetBackends() {
			b, err := config.Connect()
			if err == nil {
				backendList = append(backendList, b)
			} else if err != backendErrors.ErrNilConfig {
				log.WithFields(logrus.Fields{
					"backend": b.Name,
				}).Error(err)
			}
		}

		// create a new template resource with given backends and template processing configuration
		t, err := template.NewResource(backendList, v.Template)
		if err != nil {
			log.Error(err.Error())
			continue
		}

		wait.Add(1)
		go func() {
			defer wait.Done()
			// make sure that all backend clients are closed cleanly
			defer backendList.Close()
			t.Monitor(ctx)
		}()
	}

	go func() {
		// If there is no goroutine left - quit
		wait.Wait()
		close(done)
	}()

	for {
		select {
		case <-stop:
			cancel()
			wait.Wait()
			return
		case <-done:
			return
		}
	}
}
<commit_msg>include_dir: only read .toml files<commit_after>/*
 * This file is part of remco.
 * © 2016 The Remco Authors
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

package main

import (
	"context"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/HeavyHorst/remco/backends"
	backendErrors "github.com/HeavyHorst/remco/backends/error"
	"github.com/HeavyHorst/remco/log"
	"github.com/HeavyHorst/remco/template"
	"github.com/Sirupsen/logrus"
	"github.com/naoina/toml"
)

type resource struct {
	Template []*template.ProcessConfig
	Backend  backends.Config
}

// configuration is the representation of an config file
type configuration struct {
	LogLevel   string `toml:"log_level"`
	LogFormat  string `toml:"log_format"`
	IncludeDir string `toml:"include_dir"`
	Resource   []resource
}

func readFileAndExpandEnv(path string) ([]byte, error) {
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		return buf, err
	}
	// expand the environment variables
	buf = []byte(os.ExpandEnv(string(buf)))
	return buf, nil
}

// newConfiguration reads the file at `path`, expand the environment variables
// and unmarshals it to a new configuration struct.
// it returns an error if any.
func newConfiguration(path string) (configuration, error) {
	var c configuration

	buf, err := readFileAndExpandEnv(path)
	if err != nil {
		return c, err
	}

	if err := toml.Unmarshal(buf, &c); err != nil {
		return c, err
	}

	c.configureLogger()

	if c.IncludeDir != "" {
		files, err := ioutil.ReadDir(c.IncludeDir)
		if err != nil {
			return c, err
		}
		for _, file := range files {
			if strings.HasSuffix(file.Name(), ".toml") {
				fp := filepath.Join(c.IncludeDir, file.Name())
				log.WithFields(logrus.Fields{
					"path": fp,
				}).Info("Loading resource configuration")
				buf, err := readFileAndExpandEnv(fp)
				if err != nil {
					return c, err
				}
				var r resource
				if err := toml.Unmarshal(buf, &r); err != nil {
					return c, err
				}
				// don't add empty resources
				if len(r.Template) > 0 {
					c.Resource = append(c.Resource, r)
				}
			}
		}
	}

	return c, nil
}

// configureLogger configures the global logger
// for example it sets the log level and log formatting
func (c *configuration) configureLogger() {
	if c.LogLevel != "" {
		err := log.SetLevel(c.LogLevel)
		if err != nil {
			log.Error(err)
		}
	}
	if c.LogFormat != "" {
		log.SetFormatter(c.LogFormat)
	}
}

// run connects to all given backends and starts the template processing as defined in the config file
func (c *configuration) run(stop chan struct{}) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	done := make(chan struct{})

	wait := sync.WaitGroup{}
	for _, v := range c.Resource {
		var backendList template.Backends

		// try to connect to all supported backends
		// add them to the backendList on success
		// log an error otherwise
		for _, config := range v.Backend.GetBackends() {
			b, err := config.Connect()
			if err == nil {
				backendList = append(backendList, b)
			} else if err != backendErrors.ErrNilConfig {
				log.WithFields(logrus.Fields{
					"backend": b.Name,
				}).Error(err)
			}
		}

		// create a new template resource with given backends and template processing configuration
		t, err := template.NewResource(backendList, v.Template)
		if err != nil {
			log.Error(err.Error())
			continue
		}

		wait.Add(1)
		go func() {
			defer wait.Done()
			// make sure that all backend clients are closed cleanly
			defer backendList.Close()
			t.Monitor(ctx)
		}()
	}

	go func() {
		// If there is no goroutine left - quit
		wait.Wait()
		close(done)
	}()

	for {
		select {
		case <-stop:
			cancel()
			wait.Wait()
			return
		case <-done:
			return
		}
	}
}
<|endoftext|>


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

// Package config provide configuration facilities, handling configuration
// files in yaml format.
//
// This package has been optimized for reads, so functions write functions (Set
// and Unset) are really slow when compared to Get functions.
package config

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/howeyc/fsnotify"
	"gopkg.in/yaml.v1"
)

var ErrMismatchConf = errors.New("Your conf is wrong:")

type configuration struct {
	data map[interface{}]interface{}
	sync.RWMutex
}

func (c *configuration) Store(data map[interface{}]interface{}) {
	c.Lock()
	defer c.Unlock()
	c.store(data)
}

func (c *configuration) store(data map[interface{}]interface{}) {
	c.data = data
}

func (c *configuration) Data() map[interface{}]interface{} {
	c.RLock()
	defer c.RUnlock()
	return c.data
}

var configs configuration

func readConfigBytes(data []byte, out interface{}) error {
	return yaml.Unmarshal(data, out)
}

// ReadConfigBytes receives a slice of bytes and builds the internal
// configuration object.
//
// If the given slice is not a valid yaml file, ReadConfigBytes returns a
// non-nil error.
func ReadConfigBytes(data []byte) error {
	var newConfig map[interface{}]interface{}
	err := readConfigBytes(data, &newConfig)
	if err == nil {
		configs.Store(newConfig)
	}
	return err
}

// ReadConfigFile reads the content of a file and calls ReadConfigBytes to
// build the internal configuration object.
//
// It returns error if it can not read the given file or if the file contents
// is not valid yaml.
func ReadConfigFile(filePath string) error {
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}
	return ReadConfigBytes(data)
}

// ReadAndWatchConfigFile reads and watchs for changes in the configuration
// file. Whenever the file change, and its contents are valid YAML, the
// configuration gets updated. With this function, daemons that use this
// package may reload configuration without restarting.
func ReadAndWatchConfigFile(filePath string) error {
	err := ReadConfigFile(filePath)
	if err != nil {
		return err
	}
	w, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}
	err = w.Watch(filePath)
	if err != nil {
		return err
	}
	go func() {
		for {
			select {
			case e := <-w.Event:
				if e.IsModify() {
					ReadConfigFile(filePath)
				}
			case <-w.Error: // just ignore errors
			}
		}
	}()
	return nil
}

// Bytes serialize the configuration in YAML format.
func Bytes() ([]byte, error) {
	return yaml.Marshal(configs.Data())
}

// WriteConfigFile writes the configuration to the disc, using the given path.
// The configuration is serialized in YAML format.
//
// This function will create the file if it does not exist, setting permissions
// to "perm".
func WriteConfigFile(filePath string, perm os.FileMode) error {
	b, err := Bytes()
	if err != nil {
		return err
	}
	f, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_EXCL, perm)
	if err != nil {
		return err
	}
	defer f.Close()
	n, err := f.Write(b)
	if err != nil {
		return err
	}
	if n != len(b) {
		return io.ErrShortWrite
	}
	return nil
}

// Get returns the value for the given key, or an error if the key is undefined.
//
// The key is composed of all the key names separated by :, in case of nested
// keys. For example, suppose we have the following configuration yaml:
//
//   databases:
//     mysql:
//       host: localhost
//       port: 3306
//
// The key "databases:mysql:host" would return "localhost", while the key
// "port" would return an error.
//
// Get will expand the value with environment values, ex.:
//
//   mongo: $MONGOURI
//
// If there is an environment variable MONGOURI=localhost/test, the key "mongo"
// would return "localhost/test"
func Get(key string) (interface{}, error) {
	keys := strings.Split(key, ":")
	configs.RLock()
	defer configs.RUnlock()
	conf, ok := configs.data[keys[0]]
	if !ok {
		return nil, fmt.Errorf("key %q not found", key)
	}
	for _, k := range keys[1:] {
		_, ok = conf.(map[interface{}]interface{})
		if !ok {
			return nil, ErrMismatchConf
		}
		if conf, ok = conf.(map[interface{}]interface{})[k]; !ok {
			return nil, fmt.Errorf("key %q not found", key)
		}
	}
	if v, ok := conf.(string); ok {
		return os.ExpandEnv(v), nil
	}
	return conf, nil
}

// GetString works like Get, but does an string type assertion before returning
// the value.
//
// It returns error if the key is undefined or if it is not a string.
func GetString(key string) (string, error) {
	value, err := Get(key)
	if err != nil {
		return "", err
	}
	if v, ok := value.(string); ok {
		return v, nil
	}
	return "", &invalidValue{key, "string"}
}

// GetInt works like Get, but does an int type assertion and attempts string
// conversion before returning the value.
//
// It returns error if the key is undefined or if it is not a int.
func GetInt(key string) (int, error) {
	value, err := Get(key)
	if err != nil {
		return 0, err
	}
	if v, ok := value.(int); ok {
		return v, nil
	} else if v, ok := value.(string); ok {
		if i, err := strconv.ParseInt(v, 10, 64); err == nil {
			return int(i), nil
		}
	}
	return 0, &invalidValue{key, "int"}
}

// GetFloat works like Get, but does a float type assertion and attempts string
// conversion before returning the value.
//
// It returns error if the key is undefined or if it is not a float.
func GetFloat(key string) (float64, error) {
	value, err := Get(key)
	if err != nil {
		return 0, err
	}
	switch v := value.(type) {
	case float64:
		return v, nil
	case float32:
		return float64(v), nil
	case int:
		return float64(v), nil
	case string:
		if floatVal, err := strconv.ParseFloat(v, 64); err == nil {
			return floatVal, nil
		}
	}
	return 0, &invalidValue{key, "float"}
}

// GetUint parses and returns an unsigned integer from the config file.
func GetUint(key string) (uint, error) {
	value, err := Get(key)
	if err != nil {
		return 0, err
	}
	if v, ok := value.(int); ok {
		if v < 0 {
			return 0, &invalidValue{key, "uint"}
		}
		return uint(v), nil
	}
	return 0, &invalidValue{key, "uint"}
}

// GetDuration parses and returns a duration from the config file. It may be an
// integer or a number specifying the amount of nanoseconds.
//
// Here are some examples of valid durations:
//
//  - 1h30m0s
//  - 1e9 (one second)
//  - 100e6 (one hundred milliseconds)
//  - 1 (one nanosecond)
//  - 1000000000 (one billion nanoseconds, or one second)
func GetDuration(key string) (time.Duration, error) {
	value, err := Get(key)
	if err != nil {
		return 0, err
	}
	switch v := value.(type) {
	case int:
		return time.Duration(v), nil
	case float64:
		return time.Duration(v), nil
	case string:
		if value, err := time.ParseDuration(value.(string)); err == nil {
			return value, nil
		}
	}
	return 0, &invalidValue{key, "duration"}
}

// GetBool does a type assertion before returning the requested value
func GetBool(key string) (bool, error) {
	value, err := Get(key)
	if err != nil {
		return false, err
	}
	if v, ok := value.(bool); ok {
		return v, nil
	}
	return false, &invalidValue{key, "boolean"}
}

// GetList works like Get, but returns a slice of strings instead. It must be
// written down in the config as YAML lists.
//
// Here are two example of YAML lists:
//
//   names:
//     - Mary
//     - John
//     - Paul
//     - Petter
//
// If GetList find an item that is not a string (for example 5.08734792), it
// will convert the item.
func GetList(key string) ([]string, error) {
	value, err := Get(key)
	if err != nil {
		return nil, err
	}
	switch value.(type) {
	case []interface{}:
		v := value.([]interface{})
		result := make([]string, len(v))
		for i, item := range v {
			switch v := item.(type) {
			case int:
				result[i] = strconv.Itoa(v)
			case bool:
				result[i] = strconv.FormatBool(v)
			case float64:
				result[i] = strconv.FormatFloat(v, 'f', -1, 64)
			case string:
				result[i] = v
			default:
				result[i] = fmt.Sprintf("%v", item)
			}
		}
		return result, nil
	case []string:
		return value.([]string), nil
	}
	return nil, &invalidValue{key, "list"}
}

// mergeMaps takes two maps and merge its keys and values recursively.
//
// In case of conflicts, the function picks value from map2.
func mergeMaps(map1, map2 map[interface{}]interface{}) map[interface{}]interface{} {
	result := make(map[interface{}]interface{})
	for k, v2 := range map2 {
		if v1, ok := map1[k]; !ok {
			result[k] = v2
		} else {
			map1, ok1 := v1.(map[interface{}]interface{})
			map2, ok2 := v2.(map[interface{}]interface{})
			if ok1 && ok2 {
				result[k] = mergeMaps(map1, map2)
			} else {
				result[k] = v2
			}
		}
	}
	for k, v := range map1 {
		if v2, ok := map2[k]; !ok {
			result[k] = v
		} else {
			map1, ok1 := v.(map[interface{}]interface{})
			map2, ok2 := v2.(map[interface{}]interface{})
			if ok1 && ok2 {
				result[k] = mergeMaps(map1, map2)
			}
		}
	}
	return result
}

// Set redefines or defines a value for a key. The key has the same format that
// it has in Get and GetString.
//
// Values defined by this function affects only runtime informatin, nothing
// defined by Set is persisted in the filesystem or any database.
func Set(key string, value interface{}) {
	parts := strings.Split(key, ":")
	last := map[interface{}]interface{}{
		parts[len(parts)-1]: value,
	}
	for i := len(parts) - 2; i >= 0; i-- {
		last = map[interface{}]interface{}{
			parts[i]: last,
		}
	}
	configs.Lock()
	defer configs.Unlock()
	configs.store(mergeMaps(configs.data, last))
}

// Unset removes a key from the configuration map. It returns an error if the
// key is not defined.
//
// Calling this function does not remove a key from a configuration file, only
// from the in-memory configuration object.
func Unset(key string) error {
	var i int
	var part string
	configs.Lock()
	defer configs.Unlock()
	data := configs.data
	m := make(map[interface{}]interface{}, len(data))
	for k, v := range data {
		m[k] = v
	}
	root := m
	parts := strings.Split(key, ":")
	for i, part = range parts {
		if item, ok := m[part]; ok {
			if nm, ok := item.(map[interface{}]interface{}); ok && i < len(parts)-1 {
				m = nm
			} else {
				break
			}
		} else {
			return fmt.Errorf("key %q not found", key)
		}
	}
	delete(m, part)
	configs.store(root)
	return nil
}

type invalidValue struct {
	key  string
	kind string
}

func (e *invalidValue) Error() string {
	return fmt.Sprintf("value for the key %q is not a %s", e.key, e.kind)
}
<commit_msg>format integer into string in GetString method<commit_after>// Copyright 2015 Globo.com. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package config provide configuration facilities, handling configuration
// files in yaml format.
//
// This package has been optimized for reads, so functions write functions (Set
// and Unset) are really slow when compared to Get functions.
package config

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/howeyc/fsnotify"
	"gopkg.in/yaml.v1"
)

var ErrMismatchConf = errors.New("Your conf is wrong:")

type configuration struct {
	data map[interface{}]interface{}
	sync.RWMutex
}

func (c *configuration) Store(data map[interface{}]interface{}) {
	c.Lock()
	defer c.Unlock()
	c.store(data)
}

func (c *configuration) store(data map[interface{}]interface{}) {
	c.data = data
}

func (c *configuration) Data() map[interface{}]interface{} {
	c.RLock()
	defer c.RUnlock()
	return c.data
}

var configs configuration

func readConfigBytes(data []byte, out interface{}) error {
	return yaml.Unmarshal(data, out)
}

// ReadConfigBytes receives a slice of bytes and builds the internal
// configuration object.
//
// If the given slice is not a valid yaml file, ReadConfigBytes returns a
// non-nil error.
func ReadConfigBytes(data []byte) error {
	var newConfig map[interface{}]interface{}
	err := readConfigBytes(data, &newConfig)
	if err == nil {
		configs.Store(newConfig)
	}
	return err
}

// ReadConfigFile reads the content of a file and calls ReadConfigBytes to
// build the internal configuration object.
//
// It returns error if it can not read the given file or if the file contents
// is not valid yaml.
func ReadConfigFile(filePath string) error {
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}
	return ReadConfigBytes(data)
}

// ReadAndWatchConfigFile reads and watchs for changes in the configuration
// file. Whenever the file change, and its contents are valid YAML, the
// configuration gets updated. With this function, daemons that use this
// package may reload configuration without restarting.
func ReadAndWatchConfigFile(filePath string) error {
	err := ReadConfigFile(filePath)
	if err != nil {
		return err
	}
	w, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}
	err = w.Watch(filePath)
	if err != nil {
		return err
	}
	go func() {
		for {
			select {
			case e := <-w.Event:
				if e.IsModify() {
					ReadConfigFile(filePath)
				}
			case <-w.Error: // just ignore errors
			}
		}
	}()
	return nil
}

// Bytes serialize the configuration in YAML format.
func Bytes() ([]byte, error) {
	return yaml.Marshal(configs.Data())
}

// WriteConfigFile writes the configuration to the disc, using the given path.
// The configuration is serialized in YAML format.
//
// This function will create the file if it does not exist, setting permissions
// to "perm".
func WriteConfigFile(filePath string, perm os.FileMode) error {
	b, err := Bytes()
	if err != nil {
		return err
	}
	f, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_EXCL, perm)
	if err != nil {
		return err
	}
	defer f.Close()
	n, err := f.Write(b)
	if err != nil {
		return err
	}
	if n != len(b) {
		return io.ErrShortWrite
	}
	return nil
}

// Get returns the value for the given key, or an error if the key is undefined.
//
// The key is composed of all the key names separated by :, in case of nested
// keys. For example, suppose we have the following configuration yaml:
//
//   databases:
//     mysql:
//       host: localhost
//       port: 3306
//
// The key "databases:mysql:host" would return "localhost", while the key
// "port" would return an error.
//
// Get will expand the value with environment values, ex.:
//
//   mongo: $MONGOURI
//
// If there is an environment variable MONGOURI=localhost/test, the key "mongo"
// would return "localhost/test"
func Get(key string) (interface{}, error) {
	keys := strings.Split(key, ":")
	configs.RLock()
	defer configs.RUnlock()
	conf, ok := configs.data[keys[0]]
	if !ok {
		return nil, fmt.Errorf("key %q not found", key)
	}
	for _, k := range keys[1:] {
		_, ok = conf.(map[interface{}]interface{})
		if !ok {
			return nil, ErrMismatchConf
		}
		if conf, ok = conf.(map[interface{}]interface{})[k]; !ok {
			return nil, fmt.Errorf("key %q not found", key)
		}
	}
	if v, ok := conf.(string); ok {
		return os.ExpandEnv(v), nil
	}
	return conf, nil
}

// GetString works like Get, but does an string type assertion before returning
// the value.
//
// It returns error if the key is undefined or if it is not a string.
func GetString(key string) (string, error) {
	value, err := Get(key)
	if err != nil {
		return "", err
	}
	switch v := value.(type) {
                case int:
                        return strconv.Itoa(v), nil
                case int64:
                        return strconv.FormatInt(v, 10), nil
                default:
                        if v, ok := value.(string); ok {
                                return v, nil
                        }
        }
	return "", &invalidValue{key, "string"}
}

// GetInt works like Get, but does an int type assertion and attempts string
// conversion before returning the value.
//
// It returns error if the key is undefined or if it is not a int.
func GetInt(key string) (int, error) {
	value, err := Get(key)
	if err != nil {
		return 0, err
	}
	if v, ok := value.(int); ok {
		return v, nil
	} else if v, ok := value.(string); ok {
		if i, err := strconv.ParseInt(v, 10, 64); err == nil {
			return int(i), nil
		}
	}
	return 0, &invalidValue{key, "int"}
}

// GetFloat works like Get, but does a float type assertion and attempts string
// conversion before returning the value.
//
// It returns error if the key is undefined or if it is not a float.
func GetFloat(key string) (float64, error) {
	value, err := Get(key)
	if err != nil {
		return 0, err
	}
	switch v := value.(type) {
	case float64:
		return v, nil
	case float32:
		return float64(v), nil
	case int:
		return float64(v), nil
	case string:
		if floatVal, err := strconv.ParseFloat(v, 64); err == nil {
			return floatVal, nil
		}
	}
	return 0, &invalidValue{key, "float"}
}

// GetUint parses and returns an unsigned integer from the config file.
func GetUint(key string) (uint, error) {
	value, err := Get(key)
	if err != nil {
		return 0, err
	}
	if v, ok := value.(int); ok {
		if v < 0 {
			return 0, &invalidValue{key, "uint"}
		}
		return uint(v), nil
	}
	return 0, &invalidValue{key, "uint"}
}

// GetDuration parses and returns a duration from the config file. It may be an
// integer or a number specifying the amount of nanoseconds.
//
// Here are some examples of valid durations:
//
//  - 1h30m0s
//  - 1e9 (one second)
//  - 100e6 (one hundred milliseconds)
//  - 1 (one nanosecond)
//  - 1000000000 (one billion nanoseconds, or one second)
func GetDuration(key string) (time.Duration, error) {
	value, err := Get(key)
	if err != nil {
		return 0, err
	}
	switch v := value.(type) {
	case int:
		return time.Duration(v), nil
	case float64:
		return time.Duration(v), nil
	case string:
		if value, err := time.ParseDuration(value.(string)); err == nil {
			return value, nil
		}
	}
	return 0, &invalidValue{key, "duration"}
}

// GetBool does a type assertion before returning the requested value
func GetBool(key string) (bool, error) {
	value, err := Get(key)
	if err != nil {
		return false, err
	}
	if v, ok := value.(bool); ok {
		return v, nil
	}
	return false, &invalidValue{key, "boolean"}
}

// GetList works like Get, but returns a slice of strings instead. It must be
// written down in the config as YAML lists.
//
// Here are two example of YAML lists:
//
//   names:
//     - Mary
//     - John
//     - Paul
//     - Petter
//
// If GetList find an item that is not a string (for example 5.08734792), it
// will convert the item.
func GetList(key string) ([]string, error) {
	value, err := Get(key)
	if err != nil {
		return nil, err
	}
	switch value.(type) {
	case []interface{}:
		v := value.([]interface{})
		result := make([]string, len(v))
		for i, item := range v {
			switch v := item.(type) {
			case int:
				result[i] = strconv.Itoa(v)
			case bool:
				result[i] = strconv.FormatBool(v)
			case float64:
				result[i] = strconv.FormatFloat(v, 'f', -1, 64)
			case string:
				result[i] = v
			default:
				result[i] = fmt.Sprintf("%v", item)
			}
		}
		return result, nil
	case []string:
		return value.([]string), nil
	}
	return nil, &invalidValue{key, "list"}
}

// mergeMaps takes two maps and merge its keys and values recursively.
//
// In case of conflicts, the function picks value from map2.
func mergeMaps(map1, map2 map[interface{}]interface{}) map[interface{}]interface{} {
	result := make(map[interface{}]interface{})
	for k, v2 := range map2 {
		if v1, ok := map1[k]; !ok {
			result[k] = v2
		} else {
			map1, ok1 := v1.(map[interface{}]interface{})
			map2, ok2 := v2.(map[interface{}]interface{})
			if ok1 && ok2 {
				result[k] = mergeMaps(map1, map2)
			} else {
				result[k] = v2
			}
		}
	}
	for k, v := range map1 {
		if v2, ok := map2[k]; !ok {
			result[k] = v
		} else {
			map1, ok1 := v.(map[interface{}]interface{})
			map2, ok2 := v2.(map[interface{}]interface{})
			if ok1 && ok2 {
				result[k] = mergeMaps(map1, map2)
			}
		}
	}
	return result
}

// Set redefines or defines a value for a key. The key has the same format that
// it has in Get and GetString.
//
// Values defined by this function affects only runtime informatin, nothing
// defined by Set is persisted in the filesystem or any database.
func Set(key string, value interface{}) {
	parts := strings.Split(key, ":")
	last := map[interface{}]interface{}{
		parts[len(parts)-1]: value,
	}
	for i := len(parts) - 2; i >= 0; i-- {
		last = map[interface{}]interface{}{
			parts[i]: last,
		}
	}
	configs.Lock()
	defer configs.Unlock()
	configs.store(mergeMaps(configs.data, last))
}

// Unset removes a key from the configuration map. It returns an error if the
// key is not defined.
//
// Calling this function does not remove a key from a configuration file, only
// from the in-memory configuration object.
func Unset(key string) error {
	var i int
	var part string
	configs.Lock()
	defer configs.Unlock()
	data := configs.data
	m := make(map[interface{}]interface{}, len(data))
	for k, v := range data {
		m[k] = v
	}
	root := m
	parts := strings.Split(key, ":")
	for i, part = range parts {
		if item, ok := m[part]; ok {
			if nm, ok := item.(map[interface{}]interface{}); ok && i < len(parts)-1 {
				m = nm
			} else {
				break
			}
		} else {
			return fmt.Errorf("key %q not found", key)
		}
	}
	delete(m, part)
	configs.store(root)
	return nil
}

type invalidValue struct {
	key  string
	kind string
}

func (e *invalidValue) Error() string {
	return fmt.Sprintf("value for the key %q is not a %s", e.key, e.kind)
}
<|endoftext|>


<commit_before>package main

import (
	cfg "./configfile"
	"reflect"
	"path"
	"fmt"
	"os"
)

var Config = struct {
	ProposeBuiltins bool "propose-builtins"
}{
	false,
}

func WriteValue(v reflect.Value, name string, c *cfg.ConfigFile) {
	switch v.(type) {
	case *reflect.BoolValue, *reflect.StringValue,
	     *reflect.IntValue, *reflect.FloatValue:
		c.AddOption(cfg.DefaultSection, name, fmt.Sprint(v.Interface()))
	default:
		panic("Unknown value type")
	}
}

func ReadValue(v reflect.Value, name string, c *cfg.ConfigFile) {
	if !c.HasOption(cfg.DefaultSection, name) {
		return
	}
	switch t := v.(type) {
	case *reflect.BoolValue:
		v, err := c.GetBool(cfg.DefaultSection, name)
		if err == nil {
			t.Set(v)
		}
	case *reflect.StringValue:
		v, err := c.GetString(cfg.DefaultSection, name)
		if err == nil {
			t.Set(v)
		}
	case *reflect.IntValue:
		v, err := c.GetInt(cfg.DefaultSection, name)
		if err == nil {
			t.Set(int64(v))
		}
	case *reflect.FloatValue:
		v, err := c.GetFloat(cfg.DefaultSection, name)
		if err == nil {
			t.Set(float64(v))
		}
	default:
		panic("Unknown value type")
	}
}

func WriteConfig(v interface{}) os.Error {
	const errstr = "WriteConfig expects a pointer to a struct value as an argument"

	ptr, ok := reflect.NewValue(v).(*reflect.PtrValue)
	if !ok {
		return os.NewError(errstr)
	}

	str, ok := ptr.Elem().(*reflect.StructValue)
	if !ok {
		return os.NewError(errstr)
	}

	typ := str.Type().(*reflect.StructType)

	c := cfg.NewConfigFile()
	for i := 0; i < str.NumField(); i++ {
		v := str.Field(i)
		name := typ.Field(i).Tag
		WriteValue(v, name, c)
	}

	MakeSureConfigDirExists()
	err := c.WriteConfigFile(ConfigFile(), 0644, "gocode config file")
	if err != nil {
		return err
	}

	return nil
}

func ReadConfig(v interface{}) os.Error {
	c, err := cfg.ReadConfigFile(ConfigFile())
	if err != nil {
		return err
	}

	const errstr = "ReadConfig expects a pointer to a struct value as an argument"

	ptr, ok := reflect.NewValue(v).(*reflect.PtrValue)
	if !ok {
		return os.NewError(errstr)
	}

	str, ok := ptr.Elem().(*reflect.StructValue)
	if !ok {
		return os.NewError(errstr)
	}

	typ := str.Type().(*reflect.StructType)
	for i := 0; i < str.NumField(); i++ {
		v := str.Field(i)
		name := typ.Field(i).Tag
		ReadValue(v, name, c)
	}

	return nil
}

func XDGHomeDir() string {
	xdghome := os.Getenv("XDG_CONFIG_HOME")
	if xdghome == "" {
		xdghome = path.Join(os.Getenv("HOME"), ".config")
	}
	return xdghome
}

func MakeSureConfigDirExists() {
	dir := path.Join(XDGHomeDir(), "gocode")
	if !fileExists(dir) {
		os.MkdirAll(dir, 0755)
	}
}

func ConfigFile() string {
	return path.Join(XDGHomeDir(), "gocode", "config.ini")
}
<commit_msg>Remove unnecessary panics from config.go.<commit_after>package main

import (
	cfg "./configfile"
	"reflect"
	"path"
	"fmt"
	"os"
)

var Config = struct {
	ProposeBuiltins bool "propose-builtins"
}{
	false,
}

func WriteValue(v reflect.Value, name string, c *cfg.ConfigFile) {
	switch v.(type) {
	case *reflect.BoolValue, *reflect.StringValue,
	     *reflect.IntValue, *reflect.FloatValue:
		c.AddOption(cfg.DefaultSection, name, fmt.Sprint(v.Interface()))
	}
}

func ReadValue(v reflect.Value, name string, c *cfg.ConfigFile) {
	if !c.HasOption(cfg.DefaultSection, name) {
		return
	}
	switch t := v.(type) {
	case *reflect.BoolValue:
		v, err := c.GetBool(cfg.DefaultSection, name)
		if err == nil {
			t.Set(v)
		}
	case *reflect.StringValue:
		v, err := c.GetString(cfg.DefaultSection, name)
		if err == nil {
			t.Set(v)
		}
	case *reflect.IntValue:
		v, err := c.GetInt(cfg.DefaultSection, name)
		if err == nil {
			t.Set(int64(v))
		}
	case *reflect.FloatValue:
		v, err := c.GetFloat(cfg.DefaultSection, name)
		if err == nil {
			t.Set(float64(v))
		}
	}
}

func WriteConfig(v interface{}) os.Error {
	const errstr = "WriteConfig expects a pointer to a struct value as an argument"

	ptr, ok := reflect.NewValue(v).(*reflect.PtrValue)
	if !ok {
		return os.NewError(errstr)
	}

	str, ok := ptr.Elem().(*reflect.StructValue)
	if !ok {
		return os.NewError(errstr)
	}

	typ := str.Type().(*reflect.StructType)

	c := cfg.NewConfigFile()
	for i := 0; i < str.NumField(); i++ {
		v := str.Field(i)
		name := typ.Field(i).Tag
		WriteValue(v, name, c)
	}

	MakeSureConfigDirExists()
	err := c.WriteConfigFile(ConfigFile(), 0644, "gocode config file")
	if err != nil {
		return err
	}

	return nil
}

func ReadConfig(v interface{}) os.Error {
	c, err := cfg.ReadConfigFile(ConfigFile())
	if err != nil {
		return err
	}

	const errstr = "ReadConfig expects a pointer to a struct value as an argument"

	ptr, ok := reflect.NewValue(v).(*reflect.PtrValue)
	if !ok {
		return os.NewError(errstr)
	}

	str, ok := ptr.Elem().(*reflect.StructValue)
	if !ok {
		return os.NewError(errstr)
	}

	typ := str.Type().(*reflect.StructType)
	for i := 0; i < str.NumField(); i++ {
		v := str.Field(i)
		name := typ.Field(i).Tag
		ReadValue(v, name, c)
	}

	return nil
}

func XDGHomeDir() string {
	xdghome := os.Getenv("XDG_CONFIG_HOME")
	if xdghome == "" {
		xdghome = path.Join(os.Getenv("HOME"), ".config")
	}
	return xdghome
}

func MakeSureConfigDirExists() {
	dir := path.Join(XDGHomeDir(), "gocode")
	if !fileExists(dir) {
		os.MkdirAll(dir, 0755)
	}
}

func ConfigFile() string {
	return path.Join(XDGHomeDir(), "gocode", "config.ini")
}
<|endoftext|>


<commit_before>package materials

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

type materialscommonsConfig struct {
	api      string
	url      string
	download string
}

type serverConfig struct {
	port           uint
	address        string
	webdir         string
	updateInterval time.Duration
}

type userConfig struct {
	*User
	defaultProject string
}

type config struct {
	materialscommons materialscommonsConfig
	server           serverConfig
	user             userConfig
}

type configFile map[string]interface{}

var defaultSettings = map[string]interface{}{
	"server_address":        "localhost",
	"server_port":           uint(8081),
	"update_check_interval": 4 * time.Hour,
	"MCURL":                 "https://materialscommons.org",
	"MCAPIURL":              "https://api.materialscommons.org",
	"MCDOWNLOADURL":         "https://download.materialscommons.org",
}

var Config config

//*********************************************************
// Create on Initialize() for the materials package
// that encompasses all the other initialization, such
// as projects, and .user
//*********************************************************
func ConfigInitialize(user *User) {
	Config.user.User = user
	Config.setConfigOverrides()
}

func (c *config) setConfigOverrides() {
	configFromFile, _ := readConfigFile(c.user.DotMaterialsPath())
	c.server.port = getConfigUint("server_port", "MATERIALS_PORT", configFromFile)
	c.server.address = getConfigStr("server_address", "MATERIALS_ADDRESS", configFromFile)
	updateInterval := getConfigDuration("update_check_interval", "MATERIALS_UPDATE_CHECK_INTERVAL", configFromFile)
	c.server.updateInterval = updateInterval
	c.materialscommons.api = getDefaultedConfigStr("MCAPIURL", "MCAPIURL")
	c.materialscommons.url = getDefaultedConfigStr("MCURL", "MCURL")
	c.materialscommons.download = getDefaultedConfigStr("MCDOWNLOADURL", "MCDOWNLOADURL")

	webdir := os.Getenv("MATERIALS_WEBDIR")
	if webdir == "" {
		webdir = filepath.Join(c.user.DotMaterialsPath(), "website")
	}

	c.server.webdir = webdir

	cf := configFromFile
	defaultProject, ok := cf["default_project"].(string)
	if ok {
		c.user.defaultProject = defaultProject
	}
}

func getConfigUint(jsonName, envName string, c configFile) uint {
	envVal, err := strconv.ParseUint(os.Getenv(envName), 0, 32)
	jsonVal, ok := c[jsonName].(uint)

	switch {
	case err == nil:
		return uint(envVal)
	case ok && jsonVal != 0:
		return jsonVal
	default:
		val, _ := defaultSettings[jsonName].(uint)
		return val
	}
}

func getConfigDuration(jsonName, envName string, c configFile) time.Duration {
	envVal, err := strconv.ParseUint(os.Getenv(envName), 0, 32)
	jsonVal, ok := c[jsonName].(time.Duration)

	switch {
	case err == nil:
		return time.Duration(envVal)
	case ok && jsonVal != 0:
		return jsonVal
	default:
		val, _ := defaultSettings[jsonName].(time.Duration)
		return val
	}
}

func getConfigStr(jsonName, envName string, c configFile) string {
	envVal := os.Getenv(envName)
	jsonVal, ok := c[jsonName].(string)

	switch {
	case envVal != "":
		return envVal
	case ok && jsonVal != "":
		return jsonVal
	default:
		val, _ := defaultSettings[jsonName].(string)
		return val
	}
}

func getDefaultedConfigStr(envName, settingsName string) string {
	envVal := os.Getenv(envName)
	if envVal == "" {
		return defaultSettings[settingsName].(string)
	}

	return envVal
}

func readConfigFile(dotmaterialsPath string) (cf configFile, err error) {
	configPath := configPath(dotmaterialsPath)
	bytes, err := ioutil.ReadFile(configPath)
	var config configFile
	cf = config

	if err != nil {
		return cf, err
	}

	if err := json.Unmarshal(bytes, &config); err != nil {
		return cf, err
	}

	return config, nil
}

func configPath(path string) string {
	return filepath.Join(path, ".config")
}

func writeConfigFile(config configFile, dotmaterialsPath string) error {
	return nil
}

func (c config) MCUrl() string {
	return c.materialscommons.url
}

func (c config) MCApi() string {
	return c.materialscommons.api
}

func (c config) MCDownload() string {
	return c.materialscommons.download
}

func (c config) ServerPort() uint {
	return c.server.port
}

func (c config) ServerAddress() string {
	return c.server.address
}

func (c config) WebDir() string {
	return c.server.webdir
}

func (c config) DotMaterials() string {
	return c.user.DotMaterialsPath()
}

func (c config) DefaultProject() string {
	return c.user.defaultProject
}

// Constructs the url to access an api service. Includes the
// apikey. Prepends a "/" if needed.
func (c config) ApiUrlPath(service string) string {
	if string(service[0]) != "/" {
		service = "/" + service
	}
	uri := c.materialscommons.api + service + "?apikey=" + c.user.Apikey
	return uri
}
<commit_msg>Add setters for configuration items that can be specified on the command line.<commit_after>package materials

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

type materialscommonsConfig struct {
	api      string
	url      string
	download string
}

type serverConfig struct {
	port           uint
	address        string
	webdir         string
	updateInterval time.Duration
}

type userConfig struct {
	*User
	defaultProject string
}

type config struct {
	materialscommons materialscommonsConfig
	server           serverConfig
	user             userConfig
}

type configFile map[string]interface{}

var defaultSettings = map[string]interface{}{
	"server_address":        "localhost",
	"server_port":           uint(8081),
	"update_check_interval": 4 * time.Hour,
	"MCURL":                 "https://materialscommons.org",
	"MCAPIURL":              "https://api.materialscommons.org",
	"MCDOWNLOADURL":         "https://download.materialscommons.org",
}

var Config config

//*********************************************************
// Create on Initialize() for the materials package
// that encompasses all the other initialization, such
// as projects, and .user
//*********************************************************
func ConfigInitialize(user *User) {
	Config.user.User = user
	Config.setConfigOverrides()
}

func (c *config) setConfigOverrides() {
	configFromFile, _ := readConfigFile(c.user.DotMaterialsPath())
	c.server.port = getConfigUint("server_port", "MATERIALS_PORT", configFromFile)
	c.server.address = getConfigStr("server_address", "MATERIALS_ADDRESS", configFromFile)
	updateInterval := getConfigDuration("update_check_interval", "MATERIALS_UPDATE_CHECK_INTERVAL", configFromFile)
	c.server.updateInterval = updateInterval
	c.materialscommons.api = getDefaultedConfigStr("MCAPIURL", "MCAPIURL")
	c.materialscommons.url = getDefaultedConfigStr("MCURL", "MCURL")
	c.materialscommons.download = getDefaultedConfigStr("MCDOWNLOADURL", "MCDOWNLOADURL")

	webdir := os.Getenv("MATERIALS_WEBDIR")
	if webdir == "" {
		webdir = filepath.Join(c.user.DotMaterialsPath(), "website")
	}

	c.server.webdir = webdir

	cf := configFromFile
	defaultProject, ok := cf["default_project"].(string)
	if ok {
		c.user.defaultProject = defaultProject
	}
}

func getConfigUint(jsonName, envName string, c configFile) uint {
	envVal, err := strconv.ParseUint(os.Getenv(envName), 0, 32)
	jsonVal, ok := c[jsonName].(uint)

	switch {
	case err == nil:
		return uint(envVal)
	case ok && jsonVal != 0:
		return jsonVal
	default:
		val, _ := defaultSettings[jsonName].(uint)
		return val
	}
}

func getConfigDuration(jsonName, envName string, c configFile) time.Duration {
	envVal, err := strconv.ParseUint(os.Getenv(envName), 0, 32)
	jsonVal, ok := c[jsonName].(time.Duration)

	switch {
	case err == nil:
		return time.Duration(envVal)
	case ok && jsonVal != 0:
		return jsonVal
	default:
		val, _ := defaultSettings[jsonName].(time.Duration)
		return val
	}
}

func getConfigStr(jsonName, envName string, c configFile) string {
	envVal := os.Getenv(envName)
	jsonVal, ok := c[jsonName].(string)

	switch {
	case envVal != "":
		return envVal
	case ok && jsonVal != "":
		return jsonVal
	default:
		val, _ := defaultSettings[jsonName].(string)
		return val
	}
}

func getDefaultedConfigStr(envName, settingsName string) string {
	envVal := os.Getenv(envName)
	if envVal == "" {
		return defaultSettings[settingsName].(string)
	}

	return envVal
}

func readConfigFile(dotmaterialsPath string) (cf configFile, err error) {
	configPath := configPath(dotmaterialsPath)
	bytes, err := ioutil.ReadFile(configPath)
	var config configFile
	cf = config

	if err != nil {
		return cf, err
	}

	if err := json.Unmarshal(bytes, &config); err != nil {
		return cf, err
	}

	return config, nil
}

func configPath(path string) string {
	return filepath.Join(path, ".config")
}

func writeConfigFile(config configFile, dotmaterialsPath string) error {
	return nil
}

func (c config) MCUrl() string {
	return c.materialscommons.url
}

func (c config) MCApi() string {
	return c.materialscommons.api
}

func (c config) MCDownload() string {
	return c.materialscommons.download
}

func (c config) ServerPort() uint {
	return c.server.port
}

func (c *config) SetServerPort(port uint) {
	c.server.port = port
}

func (c config) ServerAddress() string {
	return c.server.address
}

func (c *config) SetServerAddress(address string) {
	c.server.address = address
}

func (c config) WebDir() string {
	return c.server.webdir
}

func (c config) DotMaterials() string {
	return c.user.DotMaterialsPath()
}

func (c config) DefaultProject() string {
	return c.user.defaultProject
}

// Constructs the url to access an api service. Includes the
// apikey. Prepends a "/" if needed.
func (c config) ApiUrlPath(service string) string {
	if string(service[0]) != "/" {
		service = "/" + service
	}
	uri := c.materialscommons.api + service + "?apikey=" + c.user.Apikey
	return uri
}
<|endoftext|>


<commit_before>package ini

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

type item struct {
	haveValue bool
	value     string
	comment   string
}

const keyNotFound = "key not found in config file"

type Config struct {
	keys   []string
	config map[string]map[string]item
	fname  string
	mu     sync.RWMutex
}

func New() *Config {
	c := new(Config)
	c.config = make(map[string]map[string]item)
	c.keys = []string{}
	return c
}

func spl(q string) []string {
	r := []string{}
	r = strings.Split(q, ".")
	return r
}

func (c *Config) Save() {
	if c.fname != "" {
		c.Write(c.fname)
		return
	}
	panic("must read config first")

}

func (c *Config) Read(filename string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.fname = filename
	f, err := os.Open(filename)
	defer f.Close()

	if err != nil {
		panic("failed open config file: " + err.Error())
	}

	c.keys = []string{}
	c.config = make(map[string]map[string]item)

	rs := bufio.NewScanner(f)

	openB := false

	tag := ""
	lineN := 0
	for rs.Scan() {
		lineN++
		t := strings.Trim(rs.Text(), " \n")

		if len(t) > 0 {
			if t[0] == '#' {
				continue
			}
		}

		if len(t) > 0 && !(strings.Contains(t, "{") || strings.Contains(t, "}")) && !openB {
			panic("Syntax error on line: " + strconv.Itoa(lineN))

		}

		if strings.Contains(t, "{") && openB {
			panic("Syntax error on line: " + strconv.Itoa(lineN))
		}

		if strings.Contains(t, "{") {
			tag = t[0:strings.Index(t, "{")]
			tag = strings.Trim(tag, " ")
			openB = true
			c.config[tag] = make(map[string]item)
			c.keys = append(c.keys, tag)
			continue
		}

		if strings.Contains(t, "}") && !openB {
			panic("Syntax error on line: " + strconv.Itoa(lineN))
		}

		if strings.Contains(t, "}") {
			openB = false
			tag = ""
			continue
		}

		if tag != "" {
			item := item{}

			comment := ""

			if strings.Contains(t, " //") {
				c_tmp := strings.SplitN(t, " //", 2)
				comment = strings.Trim(c_tmp[1], " ")
				t = c_tmp[0]
			}

			if strings.Contains(t, "=") {
				opts := strings.SplitN(t, "=", 2)
				opts[0] = strings.Trim(opts[0], " ")
				opts[1] = strings.Trim(opts[1], " ")
				item.value = opts[1]
				item.haveValue = true
				item.comment = comment
				c.config[tag][opts[0]] = item
			} else {
				item.value = ""
				item.haveValue = false
				item.comment = comment
				c.config[tag][t] = item
			}
		}

	}
}

func (c *Config) GetKeysDefault(section string, d []string) []string {
	r, e := c.GetKeys(section)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetKeysListDefault(section string, d []string) []string {
	r, e := c.GetKeysList(section)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetStringDefault(section, key, d string) string {
	r, e := c.GetString(section, key)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetBoolDefault(section, key string, d bool) bool {
	r, e := c.GetBool(section, key)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetFloatDefault(section, key string, d float32) float32 {
	r, e := c.GetFloat(section, key)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetIntDefault(section, key string, d int) int {
	r, e := c.GetInt(section, key)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetFloat64Default(section, key string, d float64) float64 {
	r, e := c.GetFloat64(section, key)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetInt64Default(section, key string, d int64) int64 {
	r, e := c.GetInt64(section, key)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) Sections() []string {
	c.mu.RLock()
	defer c.mu.RUnlock()
	list := []string{}

	for k, _ := range c.config {
		list = append(list, k)
	}
	return list
}

func (c *Config) GetKeys(section string) ([]string, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	list := []string{}
	if _, ok := c.config[section]; !ok {
		return []string{}, fmt.Errorf(keyNotFound)
	}
	for k, _ := range c.config[section] {
		list = append(list, k)
	}
	return list, nil
}

func (c *Config) GetKeysList(section string) ([]string, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	list := []string{}

	if _, ok := c.config[section]; !ok {
		return []string{}, fmt.Errorf(keyNotFound)
	}

	for k, v := range c.config[section] {
		if v.haveValue == true {
			continue
		}
		list = append(list, k)
	}

	return list, nil
}

func (c *Config) GetString(section, key string) (string, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	r, ok := c.config[section][key]
	if !ok {
		return "", fmt.Errorf(keyNotFound)
	}
	return r.value, nil
}

func (c *Config) GetBool(section, key string) (bool, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	v, ok := c.config[section][key]
	if !ok {
		return false, fmt.Errorf(keyNotFound)
	}
	r, e := strconv.ParseBool(v.value)
	if e != nil {
		return false, fmt.Errorf(keyNotFound)
	}
	return r, nil
}

func (c *Config) GetInt(section, key string) (int, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	tmp, ok := c.config[section][key]
	if !ok {
		return 0, fmt.Errorf(keyNotFound)
	}

	r, e := strconv.ParseInt(tmp.value, 16, 32)

	if e != nil {
		return 0, fmt.Errorf("%v", e)
	}
	return int(r), nil
}

func (c *Config) GetFloat(section, key string) (float32, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	tmp, ok := c.config[section][key]
	if !ok {
		return 0, fmt.Errorf(keyNotFound)
	}

	r, e := strconv.ParseFloat(tmp.value, 64)

	if e != nil {
		return 0, e
	}
	return float32(r), nil
}

func (c *Config) GetInt64(section, key string) (int64, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	tmp, ok := c.config[section][key]
	if !ok {
		return 0, fmt.Errorf(keyNotFound)
	}

	r, e := strconv.ParseInt(tmp.value, 16, 64)

	if e != nil {
		return 0, fmt.Errorf("%v", e)
	}
	return r, nil
}

func (c *Config) GetFloat64(section, key string) (float64, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	tmp, ok := c.config[section][key]
	if !ok {
		return 0, fmt.Errorf(keyNotFound)
	}

	r, e := strconv.ParseFloat(tmp.value, 64)

	if e != nil {
		return 0, e
	}
	return r, nil
}

func (c *Config) Delete(section, key string) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	if len(key) == 0 {
		delete(c.config, section)
		ks := []string{}
		for _, v := range c.keys {
			if v == section {
				continue
			}
			ks = append(ks, v)
		}
		c.keys = ks
	} else {
		delete(c.config[section], key)
	}
}

func (c *Config) Set(section, key string, _value interface{}) {
	c.mu.Lock()
	defer c.mu.Unlock()

	i := item{}
	value := fmt.Sprintf("%v", _value)

	if len(value) == 0 {
		i.value = ""
		i.haveValue = false
	} else {
		i.value = value
		i.haveValue = true
	}

	if _, ok := c.config[section]; !ok {
		c.config[section] = make(map[string]item)
		c.keys = append(c.keys, section)
	}

	c.config[section][key] = i

}

func (c *Config) Exists(section, key string) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	if _, ok := c.config[section]; ok {
		if _, ok := c.config[section][key]; ok {
			return true
		}
	}
	return false
}

func (c *Config) Write(filename string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	f, err := os.Create(filename)
	defer f.Close()
	if err != nil {
		panic("failed save config " + err.Error())
	}

	for _, section := range c.keys {
		f.WriteString(fmt.Sprintln(section, "{"))
		for k, v := range c.config[section] {
			if !v.haveValue {
				continue
			}
			comment := ""
			if len(v.comment) > 0 {
				comment = "// " + v.comment
			}
			f.WriteString(fmt.Sprintf("  %s = %s %s\n", k, v.value, comment))
		}

		for k, v := range c.config[section] {
			if v.haveValue {
				continue
			}
			comment := ""
			if len(v.comment) > 0 {
				comment = "// " + v.comment
			}
			f.WriteString(fmt.Sprintf("  %s %s\n", k, comment))
		}
		f.WriteString(fmt.Sprintln("}"))
		f.WriteString(fmt.Sprintln())
	}

}
<commit_msg>maybe reset<commit_after>package ini

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

type item struct {
	haveValue bool
	value     string
	comment   string
}

const keyNotFound = "key not found in config file"

type Config struct {
	keys   []string
	config map[string]map[string]item
	fname  string
	mu     sync.RWMutex
}

func New() *Config {
	c := new(Config)
	c.config = make(map[string]map[string]item)
	c.keys = []string{}
	return c
}

func spl(q string) []string {
	r := []string{}
	r = strings.Split(q, ".")
	return r
}

func (c *Config) Save() {
	if c.fname != "" {
		c.Write(c.fname)
		return
	}
	panic("must read config first")

}

func (c *Config) Reset() {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.keys = []string{}
	c.config = make(map[string]map[string]item)
}

func (c *Config) Read(filename string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.fname = filename
	f, err := os.Open(filename)
	defer f.Close()

	if err != nil {
		panic("failed open config file: " + err.Error())
	}

	c.keys = []string{}
	c.config = make(map[string]map[string]item)

	rs := bufio.NewScanner(f)

	openB := false

	tag := ""
	lineN := 0
	for rs.Scan() {
		lineN++
		t := strings.Trim(rs.Text(), " \n")

		if len(t) > 0 {
			if t[0] == '#' {
				continue
			}
		}

		if len(t) > 0 && !(strings.Contains(t, "{") || strings.Contains(t, "}")) && !openB {
			panic("Syntax error on line: " + strconv.Itoa(lineN))

		}

		if strings.Contains(t, "{") && openB {
			panic("Syntax error on line: " + strconv.Itoa(lineN))
		}

		if strings.Contains(t, "{") {
			tag = t[0:strings.Index(t, "{")]
			tag = strings.Trim(tag, " ")
			openB = true
			c.config[tag] = make(map[string]item)
			c.keys = append(c.keys, tag)
			continue
		}

		if strings.Contains(t, "}") && !openB {
			panic("Syntax error on line: " + strconv.Itoa(lineN))
		}

		if strings.Contains(t, "}") {
			openB = false
			tag = ""
			continue
		}

		if tag != "" {
			item := item{}

			comment := ""

			if strings.Contains(t, " //") {
				c_tmp := strings.SplitN(t, " //", 2)
				comment = strings.Trim(c_tmp[1], " ")
				t = c_tmp[0]
			}

			if strings.Contains(t, "=") {
				opts := strings.SplitN(t, "=", 2)
				opts[0] = strings.Trim(opts[0], " ")
				opts[1] = strings.Trim(opts[1], " ")
				item.value = opts[1]
				item.haveValue = true
				item.comment = comment
				c.config[tag][opts[0]] = item
			} else {
				item.value = ""
				item.haveValue = false
				item.comment = comment
				c.config[tag][t] = item
			}
		}

	}
}

func (c *Config) GetKeysDefault(section string, d []string) []string {
	r, e := c.GetKeys(section)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetKeysListDefault(section string, d []string) []string {
	r, e := c.GetKeysList(section)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetStringDefault(section, key, d string) string {
	r, e := c.GetString(section, key)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetBoolDefault(section, key string, d bool) bool {
	r, e := c.GetBool(section, key)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetFloatDefault(section, key string, d float32) float32 {
	r, e := c.GetFloat(section, key)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetIntDefault(section, key string, d int) int {
	r, e := c.GetInt(section, key)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetFloat64Default(section, key string, d float64) float64 {
	r, e := c.GetFloat64(section, key)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) GetInt64Default(section, key string, d int64) int64 {
	r, e := c.GetInt64(section, key)
	if e != nil {
		return d
	}
	return r
}

func (c *Config) Sections() []string {
	c.mu.RLock()
	defer c.mu.RUnlock()
	list := []string{}

	for k, _ := range c.config {
		list = append(list, k)
	}
	return list
}

func (c *Config) GetKeys(section string) ([]string, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	list := []string{}
	if _, ok := c.config[section]; !ok {
		return []string{}, fmt.Errorf(keyNotFound)
	}
	for k, _ := range c.config[section] {
		list = append(list, k)
	}
	return list, nil
}

func (c *Config) GetKeysList(section string) ([]string, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	list := []string{}

	if _, ok := c.config[section]; !ok {
		return []string{}, fmt.Errorf(keyNotFound)
	}

	for k, v := range c.config[section] {
		if v.haveValue == true {
			continue
		}
		list = append(list, k)
	}

	return list, nil
}

func (c *Config) GetString(section, key string) (string, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	r, ok := c.config[section][key]
	if !ok {
		return "", fmt.Errorf(keyNotFound)
	}
	return r.value, nil
}

func (c *Config) GetBool(section, key string) (bool, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	v, ok := c.config[section][key]
	if !ok {
		return false, fmt.Errorf(keyNotFound)
	}
	r, e := strconv.ParseBool(v.value)
	if e != nil {
		return false, fmt.Errorf(keyNotFound)
	}
	return r, nil
}

func (c *Config) GetInt(section, key string) (int, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	tmp, ok := c.config[section][key]
	if !ok {
		return 0, fmt.Errorf(keyNotFound)
	}

	r, e := strconv.ParseInt(tmp.value, 16, 32)

	if e != nil {
		return 0, fmt.Errorf("%v", e)
	}
	return int(r), nil
}

func (c *Config) GetFloat(section, key string) (float32, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	tmp, ok := c.config[section][key]
	if !ok {
		return 0, fmt.Errorf(keyNotFound)
	}

	r, e := strconv.ParseFloat(tmp.value, 64)

	if e != nil {
		return 0, e
	}
	return float32(r), nil
}

func (c *Config) GetInt64(section, key string) (int64, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	tmp, ok := c.config[section][key]
	if !ok {
		return 0, fmt.Errorf(keyNotFound)
	}

	r, e := strconv.ParseInt(tmp.value, 16, 64)

	if e != nil {
		return 0, fmt.Errorf("%v", e)
	}
	return r, nil
}

func (c *Config) GetFloat64(section, key string) (float64, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	tmp, ok := c.config[section][key]
	if !ok {
		return 0, fmt.Errorf(keyNotFound)
	}

	r, e := strconv.ParseFloat(tmp.value, 64)

	if e != nil {
		return 0, e
	}
	return r, nil
}

func (c *Config) Delete(section, key string) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	if len(key) == 0 {
		delete(c.config, section)
		ks := []string{}
		for _, v := range c.keys {
			if v == section {
				continue
			}
			ks = append(ks, v)
		}
		c.keys = ks
	} else {
		delete(c.config[section], key)
	}
}

func (c *Config) Set(section, key string, _value interface{}) {
	c.mu.Lock()
	defer c.mu.Unlock()

	i := item{}
	value := fmt.Sprintf("%v", _value)

	if len(value) == 0 {
		i.value = ""
		i.haveValue = false
	} else {
		i.value = value
		i.haveValue = true
	}

	if _, ok := c.config[section]; !ok {
		c.config[section] = make(map[string]item)
		c.keys = append(c.keys, section)
	}

	c.config[section][key] = i

}

func (c *Config) Exists(section, key string) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	if _, ok := c.config[section]; ok {
		if _, ok := c.config[section][key]; ok {
			return true
		}
	}
	return false
}

func (c *Config) Write(filename string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	f, err := os.Create(filename)
	defer f.Close()
	if err != nil {
		panic("failed save config " + err.Error())
	}

	for _, section := range c.keys {
		f.WriteString(fmt.Sprintln(section, "{"))
		for k, v := range c.config[section] {
			if !v.haveValue {
				continue
			}
			comment := ""
			if len(v.comment) > 0 {
				comment = "// " + v.comment
			}
			f.WriteString(fmt.Sprintf("  %s = %s %s\n", k, v.value, comment))
		}

		for k, v := range c.config[section] {
			if v.haveValue {
				continue
			}
			comment := ""
			if len(v.comment) > 0 {
				comment = "// " + v.comment
			}
			f.WriteString(fmt.Sprintf("  %s %s\n", k, comment))
		}
		f.WriteString(fmt.Sprintln("}"))
		f.WriteString(fmt.Sprintln())
	}

}
<|endoftext|>


<commit_before>package gnosis

import {
	"sync"
	"encoding/json"
}

var staticConfig *ConfigStruct
var configLock = new(sync.RWMutex)

type GlobalConfigStruct struct {
	port     string
	hostname string
}

type ServerConfigStruct struct {
	path        string
	prefix      string
	defaultPage string
	serverType  string
	restricted  []string
}

type ConfigStruct struct {
	global     GlobalConfigStruct
	mainserver ServerConfigStruct
	server     []ServerConfigStruct
}

func GetConfig() *ConfigStruct {
	configLock.RLock()
	defer configLock.RUnlock()
	return staticConfig
}

func LoadConfig(configFile string) bool {

	if configFile == "" {
		log.Println("no configuration file specified, using ./config.json")
		// return an empty config file
		configFile = "config.json"
	}
	//temp, err := mini.LoadConfiguration(configFile)

	//var fileContents []byte
	//var err error
	// have to read in the line into a byte[] array
	fileContents, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Println("Problem loading config file: ", err)
	}

	// UnMarshal the config file that was read in
	temp := new(ConfigStruct)
	err = json.Unmarshal(fileContents, temp)

	//Make sure you were able to read it in
	if err != nil {
		log.Println("parse config error: ", err)
		return false
	}

	configLock.Lock()
	staticConfig = temp
	configLock.Unlock()

	return true
}
<commit_msg>reworked config stuff a bit more<commit_after>package gnosis

import {
	"encoding/json"
	"sync"
	"io/ioutil"
	"log"
}

var staticConfig *ConfigStruct
var configLock = new(sync.RWMutex)

type GlobalConfigStruct struct {
	port     string
	hostname string
}

type ServerConfigStruct struct {
	path        string
	prefix      string
	defaultPage string
	serverType  string
	restricted  []string
}

type ConfigStruct struct {
	global     GlobalConfigStruct
	mainserver ServerConfigStruct
	server     []ServerConfigStruct
}

func GetConfig() *ConfigStruct {
	configLock.RLock()
	defer configLock.RUnlock()
	return staticConfig
}

func LoadConfig(configFile string) bool {

	if configFile == "" {
		log.Println("no configuration file specified, using ./config.json")
		// return an empty config file
		configFile = "config.json"
	}
	//temp, err := mini.LoadConfiguration(configFile)

	//var fileContents []byte
	//var err error
	// have to read in the line into a byte[] array
	fileContents, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Println("Problem loading config file: ", err)
	}

	// UnMarshal the config file that was read in
	temp := new(ConfigStruct)
	err = json.Unmarshal(fileContents, temp)

	//Make sure you were able to read it in
	if err != nil {
		log.Println("parse config error: ", err)
		return false
	}

	configLock.Lock()
	staticConfig = temp
	configLock.Unlock()

	return true
}
<|endoftext|>


<commit_before>package compiler

import (
	"fmt"

	"github.com/twtiger/gosecco/tree"
)

type compilerVisitor struct {
	c          *compiler
	terminalJF bool
	terminalJT bool
	negated    bool
	topLevel   bool
}

func (cv *compilerVisitor) getLower(k uint64) uint32 {
	return uint32(k)
}

func (cv *compilerVisitor) getUpper(k uint64) uint32 {
	return uint32(k >> 32)
}

func (cv *compilerVisitor) AcceptArgument(a tree.Argument) {
	cv.topLevel = false
	ix := argument[a.Index]
	switch a.Type {
	case tree.Hi:
		cv.c.loadAt(ix.upper)
	case tree.Low:
		cv.c.loadAt(ix.lower)
	default:
		panic(fmt.Sprintf("Incorrect argument type"))
	}
}

func (cv *compilerVisitor) AcceptArithmetic(a tree.Arithmetic) {
	cv.topLevel = false
	a.Left.Accept(cv)
	rightOperand := a.Right.(tree.NumericLiteral)
	cv.c.performArithmetic(a.Op, uint32(rightOperand.Value))
}

func (cv *compilerVisitor) AcceptBinaryNegation(tree.BinaryNegation) {
	cv.topLevel = false
}

func (cv *compilerVisitor) AcceptBooleanLiteral(val tree.BooleanLiteral) {
	if cv.topLevel {
		// TODO: compile here
	} else {
		panic(fmt.Sprintf("Programming error: there should never be any boolean literals left outside of the toplevel if the simplifier works correctly: syscall: %s - %s", cv.c.currentlyCompilingSyscall, tree.ExpressionString(cv.c.currentlyCompilingExpression)))
	}
	cv.topLevel = false
}

func (cv *compilerVisitor) AcceptCall(tree.Call) {
	panic(fmt.Sprintf("Programming error: there should never be any unexpanded calls if the unifier works correctly: syscall: %s - %s", cv.c.currentlyCompilingSyscall, tree.ExpressionString(cv.c.currentlyCompilingExpression)))
}

func detectSpecialCasesOn(e tree.Expression) (*tree.Argument, *tree.NumericLiteral, bool, bool) {
	switch et := e.(type) {
	case tree.Argument:
		if et.Type == tree.Full {
			return &et, nil, true, false
		}
	case tree.NumericLiteral:
		return nil, &et, false, true
	}
	return nil, nil, false, false
}

func detectSpecialCases(c tree.Comparison) (argL *tree.Argument, argR *tree.Argument, litL *tree.NumericLiteral, litR *tree.NumericLiteral, leftIsArg bool, rightIsArg bool, leftIsLit bool, rightIsLit bool) {
	argL, litL, leftIsArg, leftIsLit = detectSpecialCasesOn(c.Left)
	argR, litR, rightIsArg, rightIsLit = detectSpecialCasesOn(c.Right)
	return
}

// TODO refactor to replace jumpOnK with this.
func (cv *compilerVisitor) compareArgToLit(a *tree.Argument, l *tree.NumericLiteral, op tree.ComparisonType) {
	ix := argument[a.Index]
	cv.c.loadAt(ix.upper)
	cv.c.jumpOnKComparison(cv.getUpper(l.Value), op, cv.terminalJF, !cv.terminalJT, cv.negated, false)
	cv.c.loadAt(ix.lower)
	cv.c.jumpOnKComparison(cv.getLower(l.Value), op, cv.terminalJF, cv.terminalJT, cv.negated, false)
}

func (cv *compilerVisitor) compareExpressionToArg(a *tree.Argument, e tree.Expression, op tree.ComparisonType) {
	e.Accept(cv)
	cv.c.moveAtoX()
	lx := argument[a.Index]
	cv.c.loadAt(lx.upper)
	cv.c.jumpOnXComparison(op, true, false, cv.negated, false)
	cv.c.loadAt(lx.lower)
	cv.c.jumpOnXComparison(op, true, true, cv.negated, false)
}

func (cv *compilerVisitor) AcceptComparison(c tree.Comparison) {
	cv.topLevel = false
	argL, argR, litL, litR, leftArg, rightArg, leftLit, rightLit := detectSpecialCases(c)

	if leftArg && rightLit {
		cv.compareArgToLit(argL, litR, c.Op)
	}

	if leftLit && rightArg {
		cv.compareArgToLit(argR, litL, c.Op)
	}

	if leftArg && rightArg {
		rx := argument[argR.Index]
		lx := argument[argL.Index]

		cv.jumpOnXChained(rx, lx, c.Op, cv.getChainedJumps(hiTerm), cv.getChainedJumps(lowTerm))
	}

	if !rightArg && !rightLit && leftArg {
		cv.compareExpressionToArg(argL, c.Right, c.Op)
	}

	if !leftArg && !leftLit && rightArg {
		cv.compareExpressionToArg(argR, c.Left, c.Op)
	}

	if !leftLit && !leftArg && !rightLit && !rightArg {
		c.Left.Accept(cv)
		cv.c.moveAtoX()
		c.Right.Accept(cv)
		cv.c.jumpOnXComparison(c.Op, cv.terminalJF, cv.terminalJT, cv.negated, false)
	}
}

type jumpType string

const (
	hiTerm  = "hiTerm"
	lowTerm = "lowTerm"
	hi      = "hi"
	low     = "low"
	negHi   = "negHi"
	negLow  = "negLow"
)

func (cv *compilerVisitor) getChainedJumps(j jumpType) map[jumps]bool {
	hiT := map[jumps]bool{jf: true, jt: false, neg: cv.negated, chained: false}
	lowT := map[jumps]bool{jf: true, jt: true, neg: cv.negated, chained: false}
	hiJ := map[jumps]bool{jf: true, jt: true, neg: cv.negated, chained: true}
	lowJ := map[jumps]bool{jf: false, jt: true, neg: cv.negated, chained: false}
	negH := map[jumps]bool{jf: true, jt: true, neg: cv.negated, chained: true}
	negL := map[jumps]bool{jf: true, jt: false, neg: cv.negated, chained: false}

	allPoints := map[jumpType]map[jumps]bool{
		hiTerm:  hiT,
		lowTerm: lowT,
		hi:      hiJ,
		low:     lowJ,
		negHi:   negH,
		negLow:  negL,
	}
	return allPoints[j]
}

func (cv *compilerVisitor) jumpOnK(l uint64, ix argumentPosition, hiJumps map[jumps]bool, lowJumps map[jumps]bool) {
	cv.c.loadAt(ix.upper)
	cv.c.jumpOnKComparison(cv.getUpper(l), tree.EQL, hiJumps[jf], hiJumps[jt], hiJumps[neg], hiJumps[chained])
	cv.c.loadAt(ix.lower)
	cv.c.jumpOnKComparison(cv.getLower(l), tree.EQL, lowJumps[jf], lowJumps[jt], lowJumps[neg], lowJumps[chained])
}

func (cv *compilerVisitor) compareArgToNumeric(l uint64, ix argumentPosition, isLast bool) {
	if isLast {
		cv.jumpOnK(l, ix, cv.getChainedJumps(hiTerm), cv.getChainedJumps(lowTerm))
	} else {
		if cv.negated {
			cv.jumpOnK(l, ix, cv.getChainedJumps(negHi), cv.getChainedJumps(negLow))
		} else {
			cv.jumpOnK(l, ix, cv.getChainedJumps(hi), cv.getChainedJumps(low))
		}
	}
}

func (cv *compilerVisitor) jumpOnXChained(ix argumentPosition, rx argumentPosition, op tree.ComparisonType, hiJumps map[jumps]bool, lowJumps map[jumps]bool) {
	cv.c.loadAt(ix.upper)
	cv.c.moveAtoX()
	cv.c.loadAt(rx.upper)
	cv.c.jumpOnXComparison(op, hiJumps[jf], hiJumps[jt], hiJumps[neg], hiJumps[chained])

	cv.c.loadAt(ix.lower)
	cv.c.moveAtoX()
	cv.c.loadAt(rx.lower)
	cv.c.jumpOnXComparison(op, lowJumps[jf], lowJumps[jt], lowJumps[neg], lowJumps[chained])
}

func (cv *compilerVisitor) AcceptInclusion(c tree.Inclusion) {
	cv.topLevel = false
	if !c.Positive {
		cv.negated = true
	}

	switch et := c.Left.(type) {
	case tree.Argument:
		ix := argument[et.Index]
		for i, l := range c.Rights {
			isLast := i == len(c.Rights)-1
			switch k := l.(type) {
			case tree.NumericLiteral:
				cv.compareArgToNumeric(k.Value, ix, isLast)
			case tree.Argument:
				rx := argument[k.Index]
				if isLast {
					cv.jumpOnXChained(ix, rx, tree.EQL, cv.getChainedJumps(hiTerm), cv.getChainedJumps(lowTerm))
				} else {
					if cv.negated {
						cv.jumpOnXChained(ix, rx, tree.EQL, cv.getChainedJumps(negHi), cv.getChainedJumps(negLow))
					} else {
						cv.jumpOnXChained(ix, rx, tree.EQL, cv.getChainedJumps(hi), cv.getChainedJumps(low))
					}
				}
			}
		}
	case tree.NumericLiteral:
		for i, l := range c.Rights {
			k := l.(tree.Argument)
			ix := argument[k.Index]
			isLast := i == len(c.Rights)-1
			cv.compareArgToNumeric(et.Value, ix, isLast)
		}
	}
}

func (cv *compilerVisitor) AcceptNegation(c tree.Negation) {
	cv.topLevel = false
	cv.negated = true
	c.Operand.Accept(cv)
}

func (cv *compilerVisitor) AcceptNumericLiteral(l tree.NumericLiteral) {
}

func (cv *compilerVisitor) AcceptAnd(c tree.And) {
	cv.topLevel = false
	cv.terminalJT = !cv.terminalJT
	c.Left.Accept(cv)
	cv.terminalJT = !cv.terminalJT
	c.Right.Accept(cv)
}

func (cv *compilerVisitor) AcceptOr(c tree.Or) {
	cv.topLevel = false
	cv.terminalJF = false
	c.Left.Accept(cv)
	cv.terminalJF = true
	c.Right.Accept(cv)
}

func (cv *compilerVisitor) AcceptVariable(tree.Variable) {
	panic(fmt.Sprintf("Programming error: there should never be any unexpanded variables if the unifier works correctly: syscall: %s - %s", cv.c.currentlyCompilingSyscall, tree.ExpressionString(cv.c.currentlyCompilingExpression)))
}

// func peepHole(filters []unix.SockFilter) []unix.SockFilter {
// 	one, two, three := filters[0], filters[1], filters[2]
// 	if one.Code == BPF_LD|BPF_IMM && two.Code == BPF_MISC|BPF_TAX && three.Code&(BPF_JMP|BPF_X) != 0 {
// 		return []unix.SockFilter{
// 			unix.SockFilter{
// 				Code: (three.Code & ^BPF_X) | BPF_K,
// 				Jt:   three.Jt,
// 				Jf:   three.Jf,
// 				K:    one.K,
// 			},
// 		}
// 	}
// 	return filters
// }
<commit_msg>simplification of compiler visitor<commit_after>package compiler

import (
	"fmt"

	"github.com/twtiger/gosecco/tree"
)

type compilerVisitor struct {
	c          *compiler
	terminalJF bool
	terminalJT bool
	negated    bool
	topLevel   bool
}

func (cv *compilerVisitor) getLower(k uint64) uint32 {
	return uint32(k)
}

func (cv *compilerVisitor) getUpper(k uint64) uint32 {
	return uint32(k >> 32)
}

func (cv *compilerVisitor) AcceptArgument(a tree.Argument) {
	cv.topLevel = false
	ix := argument[a.Index]
	switch a.Type {
	case tree.Hi:
		cv.c.loadAt(ix.upper)
	case tree.Low:
		cv.c.loadAt(ix.lower)
	default:
		panic(fmt.Sprintf("Incorrect argument type"))
	}
}

func (cv *compilerVisitor) AcceptArithmetic(a tree.Arithmetic) {
	cv.topLevel = false
	a.Left.Accept(cv)
	rightOperand := a.Right.(tree.NumericLiteral)
	cv.c.performArithmetic(a.Op, uint32(rightOperand.Value))
}

func (cv *compilerVisitor) AcceptBinaryNegation(tree.BinaryNegation) {
	cv.topLevel = false
}

func (cv *compilerVisitor) AcceptBooleanLiteral(val tree.BooleanLiteral) {
	if cv.topLevel {
		// TODO: compile here
	} else {
		panic(fmt.Sprintf("Programming error: there should never be any boolean literals left outside of the toplevel if the simplifier works correctly: syscall: %s - %s", cv.c.currentlyCompilingSyscall, tree.ExpressionString(cv.c.currentlyCompilingExpression)))
	}
	cv.topLevel = false
}

func (cv *compilerVisitor) AcceptCall(tree.Call) {
	panic(fmt.Sprintf("Programming error: there should never be any unexpanded calls if the unifier works correctly: syscall: %s - %s", cv.c.currentlyCompilingSyscall, tree.ExpressionString(cv.c.currentlyCompilingExpression)))
}

func detectSpecialCasesOn(e tree.Expression) (*tree.Argument, *tree.NumericLiteral, bool, bool) {
	switch et := e.(type) {
	case tree.Argument:
		if et.Type == tree.Full {
			return &et, nil, true, false
		}
	case tree.NumericLiteral:
		return nil, &et, false, true
	}
	return nil, nil, false, false
}

func detectSpecialCases(c tree.Comparison) (argL *tree.Argument, argR *tree.Argument, litL *tree.NumericLiteral, litR *tree.NumericLiteral, leftIsArg bool, rightIsArg bool, leftIsLit bool, rightIsLit bool) {
	argL, litL, leftIsArg, leftIsLit = detectSpecialCasesOn(c.Left)
	argR, litR, rightIsArg, rightIsLit = detectSpecialCasesOn(c.Right)
	return
}

func (cv *compilerVisitor) compareExpressionToArg(a *tree.Argument, e tree.Expression, op tree.ComparisonType) {
	e.Accept(cv)
	cv.c.moveAtoX()
	lx := argument[a.Index]
	cv.c.loadAt(lx.upper)
	cv.c.jumpOnXComparison(op, true, false, cv.negated, false)
	cv.c.loadAt(lx.lower)
	cv.c.jumpOnXComparison(op, true, true, cv.negated, false)
}

func (cv *compilerVisitor) AcceptComparison(c tree.Comparison) {
	cv.topLevel = false
	argL, argR, litL, litR, leftArg, rightArg, leftLit, rightLit := detectSpecialCases(c)

	if leftArg && rightLit {
		ix := argument[argL.Index]
		cv.compareArgToNumeric(litR.Value, ix, c.Op, true)
	}

	if leftLit && rightArg {
		ix := argument[argR.Index]
		cv.compareArgToNumeric(litL.Value, ix, c.Op, true)
	}

	if leftArg && rightArg {
		rx := argument[argR.Index]
		lx := argument[argL.Index]

		cv.jumpOnXChained(rx, lx, c.Op, cv.getChainedJumps(hiTerm), cv.getChainedJumps(lowTerm))
	}

	if !rightArg && !rightLit && leftArg {
		cv.compareExpressionToArg(argL, c.Right, c.Op)
	}

	if !leftArg && !leftLit && rightArg {
		cv.compareExpressionToArg(argR, c.Left, c.Op)
	}

	if !leftLit && !leftArg && !rightLit && !rightArg {
		c.Left.Accept(cv)
		cv.c.moveAtoX()
		c.Right.Accept(cv)
		cv.c.jumpOnXComparison(c.Op, cv.terminalJF, cv.terminalJT, cv.negated, false)
	}
}

type jumpType string

const (
	hiTerm  = "hiTerm"
	lowTerm = "lowTerm"
	hi      = "hi"
	low     = "low"
	negHi   = "negHi"
	negLow  = "negLow"
)

// TODO on init
func (cv *compilerVisitor) getChainedJumps(j jumpType) map[jumps]bool {
	hiT := map[jumps]bool{jf: true, jt: false, neg: cv.negated, chained: false}
	lowT := map[jumps]bool{jf: true, jt: true, neg: cv.negated, chained: false}
	hiJ := map[jumps]bool{jf: true, jt: true, neg: cv.negated, chained: true}
	lowJ := map[jumps]bool{jf: false, jt: true, neg: cv.negated, chained: false}
	negH := map[jumps]bool{jf: true, jt: true, neg: cv.negated, chained: true}
	negL := map[jumps]bool{jf: true, jt: false, neg: cv.negated, chained: false}

	allPoints := map[jumpType]map[jumps]bool{
		hiTerm:  hiT,
		lowTerm: lowT,
		hi:      hiJ,
		low:     lowJ,
		negHi:   negH,
		negLow:  negL,
	}
	return allPoints[j]
}

func (cv *compilerVisitor) jumpOnK(l uint64, ix argumentPosition, op tree.ComparisonType, hiJumps map[jumps]bool, lowJumps map[jumps]bool) {
	cv.c.loadAt(ix.upper)
	cv.c.jumpOnKComparison(cv.getUpper(l), op, hiJumps[jf], hiJumps[jt], hiJumps[neg], hiJumps[chained])
	cv.c.loadAt(ix.lower)
	cv.c.jumpOnKComparison(cv.getLower(l), op, lowJumps[jf], lowJumps[jt], lowJumps[neg], lowJumps[chained])
}

func (cv *compilerVisitor) compareArgToNumeric(l uint64, ix argumentPosition, op tree.ComparisonType, isLast bool) {
	if isLast {
		cv.jumpOnK(l, ix, op, cv.getChainedJumps(hiTerm), cv.getChainedJumps(lowTerm))
	} else {
		if cv.negated {
			cv.jumpOnK(l, ix, op, cv.getChainedJumps(negHi), cv.getChainedJumps(negLow))
		} else {
			cv.jumpOnK(l, ix, op, cv.getChainedJumps(hi), cv.getChainedJumps(low))
		}
	}
}

func (cv *compilerVisitor) jumpOnXChained(ix argumentPosition, rx argumentPosition, op tree.ComparisonType, hiJumps map[jumps]bool, lowJumps map[jumps]bool) {
	cv.c.loadAt(ix.upper)
	cv.c.moveAtoX()
	cv.c.loadAt(rx.upper)
	cv.c.jumpOnXComparison(op, hiJumps[jf], hiJumps[jt], hiJumps[neg], hiJumps[chained])

	cv.c.loadAt(ix.lower)
	cv.c.moveAtoX()
	cv.c.loadAt(rx.lower)
	cv.c.jumpOnXComparison(op, lowJumps[jf], lowJumps[jt], lowJumps[neg], lowJumps[chained])
}

func (cv *compilerVisitor) AcceptInclusion(c tree.Inclusion) {
	cv.topLevel = false
	if !c.Positive {
		cv.negated = true
	}

	switch et := c.Left.(type) {
	case tree.Argument:
		ix := argument[et.Index]
		for i, l := range c.Rights {
			isLast := i == len(c.Rights)-1
			switch k := l.(type) {
			case tree.NumericLiteral:
				cv.compareArgToNumeric(k.Value, ix, tree.EQL, isLast)
			case tree.Argument:
				rx := argument[k.Index]
				if isLast {
					cv.jumpOnXChained(ix, rx, tree.EQL, cv.getChainedJumps(hiTerm), cv.getChainedJumps(lowTerm))
				} else {
					if cv.negated {
						cv.jumpOnXChained(ix, rx, tree.EQL, cv.getChainedJumps(negHi), cv.getChainedJumps(negLow))
					} else {
						cv.jumpOnXChained(ix, rx, tree.EQL, cv.getChainedJumps(hi), cv.getChainedJumps(low))
					}
				}
			}
		}
	case tree.NumericLiteral:
		for i, l := range c.Rights {
			k := l.(tree.Argument)
			ix := argument[k.Index]
			isLast := i == len(c.Rights)-1
			cv.compareArgToNumeric(et.Value, ix, tree.EQL, isLast)
		}
	}
}

func (cv *compilerVisitor) AcceptNegation(c tree.Negation) {
	cv.topLevel = false
	cv.negated = true
	c.Operand.Accept(cv)
}

func (cv *compilerVisitor) AcceptNumericLiteral(l tree.NumericLiteral) {
}

func (cv *compilerVisitor) AcceptAnd(c tree.And) {
	cv.topLevel = false
	cv.terminalJT = !cv.terminalJT
	c.Left.Accept(cv)
	cv.terminalJT = !cv.terminalJT
	c.Right.Accept(cv)
}

func (cv *compilerVisitor) AcceptOr(c tree.Or) {
	cv.topLevel = false
	cv.terminalJF = false
	c.Left.Accept(cv)
	cv.terminalJF = true
	c.Right.Accept(cv)
}

func (cv *compilerVisitor) AcceptVariable(tree.Variable) {
	panic(fmt.Sprintf("Programming error: there should never be any unexpanded variables if the unifier works correctly: syscall: %s - %s", cv.c.currentlyCompilingSyscall, tree.ExpressionString(cv.c.currentlyCompilingExpression)))
}

// func peepHole(filters []unix.SockFilter) []unix.SockFilter {
// 	one, two, three := filters[0], filters[1], filters[2]
// 	if one.Code == BPF_LD|BPF_IMM && two.Code == BPF_MISC|BPF_TAX && three.Code&(BPF_JMP|BPF_X) != 0 {
// 		return []unix.SockFilter{
// 			unix.SockFilter{
// 				Code: (three.Code & ^BPF_X) | BPF_K,
// 				Jt:   three.Jt,
// 				Jf:   three.Jf,
// 				K:    one.K,
// 			},
// 		}
// 	}
// 	return filters
// }
<|endoftext|>


<commit_before>package nopaste

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"gopkg.in/yaml.v1"
)

type Config struct {
	BaseURL  string       `yaml:"base_url"`
	Listen   string       `yaml:"listen"`
	DataDir  string       `yaml:"data_dir"`
	IRC      *IRCConfig   `yaml:"irc"`
	Slack    *SlackConfig `yaml:"slack"`
	filePath string
	Channels []string `yaml:"channels"`
}

type IRCConfig struct {
	Host     string `yaml:"host"`
	Port     int    `yaml:"port"`
	Secure   bool   `yaml:"secure"`
	Debug    bool   `yaml:"debug"`
	Password string `yaml:"password"`
	Nick     string `yaml:"nick"`
}

type SlackConfig struct {
	WebhookURL string `yaml:"webhook_url"`
}

func (c *Config) DataFilePath(id string) string {
	return fmt.Sprintf("%s/%s.txt", c.DataDir, id)
}

func (c *Config) AddChannel(channel string) {
	for _, _channel := range c.Channels {
		if channel == _channel {
			return
		}
	}
	c.Channels = append(c.Channels, channel)
	err := c.Save()
	if err != nil {
		log.Println(err)
	}
	return
}

func (c *Config) Save() error {
	log.Println("saving config file", c.filePath)
	data, err := yaml.Marshal(c)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(c.filePath, data, 0644)
}

func (c *Config) SetFilePath(path string) {
	c.filePath = path
}

func LoadConfig(file string) (*Config, error) {
	log.Println("loading config file", file)
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	content, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}
	c := Config{filePath: file}
	err = yaml.Unmarshal(content, &c)
	return &c, nil
}
<commit_msg>use yaml.v2<commit_after>package nopaste

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"gopkg.in/yaml.v2"
)

type Config struct {
	BaseURL  string       `yaml:"base_url"`
	Listen   string       `yaml:"listen"`
	DataDir  string       `yaml:"data_dir"`
	IRC      *IRCConfig   `yaml:"irc"`
	Slack    *SlackConfig `yaml:"slack"`
	filePath string
	Channels []string `yaml:"channels"`
}

type IRCConfig struct {
	Host     string `yaml:"host"`
	Port     int    `yaml:"port"`
	Secure   bool   `yaml:"secure"`
	Debug    bool   `yaml:"debug"`
	Password string `yaml:"password"`
	Nick     string `yaml:"nick"`
}

type SlackConfig struct {
	WebhookURL string `yaml:"webhook_url"`
}

func (c *Config) DataFilePath(id string) string {
	return fmt.Sprintf("%s/%s.txt", c.DataDir, id)
}

func (c *Config) AddChannel(channel string) {
	for _, _channel := range c.Channels {
		if channel == _channel {
			return
		}
	}
	c.Channels = append(c.Channels, channel)
	err := c.Save()
	if err != nil {
		log.Println(err)
	}
	return
}

func (c *Config) Save() error {
	log.Println("saving config file", c.filePath)
	data, err := yaml.Marshal(c)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(c.filePath, data, 0644)
}

func (c *Config) SetFilePath(path string) {
	c.filePath = path
}

func LoadConfig(file string) (*Config, error) {
	log.Println("loading config file", file)
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	content, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}
	c := Config{filePath: file}
	err = yaml.Unmarshal(content, &c)
	return &c, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/miekg/dns"
	"io/ioutil"
	"log"
	"net"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"
)

var configLastRead = map[string]time.Time{}

func configReader(dirName string, Zones Zones) {
	go func() {
		for {
			configReadDir(dirName, Zones)
			time.Sleep(5 * time.Second)
		}
	}()
}

func configReadDir(dirName string, Zones Zones) {
	dir, err := ioutil.ReadDir(dirName)
	if err != nil {
		panic(err)
	}

	var seenFiles = map[string]bool{}

	for _, file := range dir {
		fileName := file.Name()
		if !strings.HasSuffix(strings.ToLower(fileName), ".json") {
			continue
		}

		seenFiles[fileName] = true

		if lastRead, ok := configLastRead[fileName]; !ok || file.ModTime().After(lastRead) {
			log.Println("Updated file, going to read", fileName)
			configLastRead[fileName] = file.ModTime()
			zoneName := fileName[0:strings.LastIndex(fileName, ".")]
			//log.Println("FILE:", i, file, zoneName)
			config := readZoneFile(zoneName, path.Join(dirName, fileName))
			if config == nil {
				log.Println("config is nil")
			}
			if config != nil {
				Zones[zoneName] = config
				dns.HandleFunc(zoneName, setupServerFunc(config))
			}
		}

		// TODO(ask) Disable zones not seen in two subsequent runs
	}
}

func readZoneFile(zoneName, fileName string) *Zone {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("reading %s failed: %s", zoneName, err)
		}
	}()

	b, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Println("Could not read ", fileName, ": ", err)
		panic(err)
	}

	Zone := new(Zone)
	Zone.Labels = make(labels)
	Zone.LenLabels = dns.LenLabels(Zone.Origin)
	Zone.Origin = zoneName

	if err == nil {
		var objmap map[string]interface{}
		err := json.Unmarshal(b, &objmap)
		if err != nil {
			panic(err)
		}
		//log.Println(objmap)

		var data map[string]interface{}

		for k, v := range objmap {
			//log.Printf("k: %s v: %#v, T: %T\n", k, v, v)

			switch k {
			case "ttl", "serial":
				switch option := k; option {
				case "ttl":
					Zone.Options.Ttl = int(v.(float64))
				case "serial":
					Zone.Options.Serial = int(v.(float64))
				}
				continue

			case "data":
				data = v.(map[string]interface{})
			}
		}

		setupZoneData(data, Zone)

	}

	//log.Printf("ZO T: %T %s\n", Zones["0.us"], Zones["0.us"])

	//log.Println("IP", string(Zone.Regions["0.us"].IPv4[0].ip))

	return Zone
}

func setupZoneData(data map[string]interface{}, Zone *Zone) {

	var recordTypes = map[string]uint16{
		"a":    dns.TypeA,
		"aaaa": dns.TypeAAAA,
		"ns":   dns.TypeNS,
	}

	for dk, dv := range data {

		//log.Printf("K %s V %s TYPE-V %T\n", dk, dv, dv)

		Zone.Labels[dk] = new(Label)
		label := Zone.Labels[dk]

		// BUG(ask) Read 'ttl' value in label data

		for rType, dnsType := range recordTypes {

			var rdata = dv.(map[string]interface{})[rType]

			if rdata == nil {
				//log.Printf("No %s records for label %s\n", rType, dk)
				continue
			}

			//log.Printf("rdata %s TYPE-R %T\n", rdata, rdata)

			records := make(map[string][]interface{})

			switch rdata.(type) {
			case map[string]interface{}:
				// Handle map[ns2.example.net:<nil> ns1.example.net:<nil>]
				tmp := make([]interface{}, 0)
				for rdata_k, rdata_v := range rdata.(map[string]interface{}) {
					if rdata_v == nil {
						rdata_v = ""
					}
					tmp = append(tmp, []string{rdata_k, rdata_v.(string)})
				}
				records[rType] = tmp
			default:
				records[rType] = rdata.([]interface{})
			}

			//log.Printf("RECORDS %s TYPE-REC %T\n", Records, Records)

			if label.Records == nil {
				label.Records = make(map[uint16]Records)
				label.Weight = make(map[uint16]int)
			}

			label.Records[dnsType] = make(Records, len(records[rType]))

			for i := 0; i < len(records[rType]); i++ {

				//log.Printf("RT %T %#v\n", records[rType][i], records[rType][i])

				record := new(Record)

				var h dns.RR_Header
				// log.Println("TTL OPTIONS", Zone.Options.Ttl)
				h.Ttl = uint32(Zone.Options.Ttl)
				h.Class = dns.ClassINET
				h.Rrtype = dnsType

				switch dnsType {
				case dns.TypeA, dns.TypeAAAA:
					rec := records[rType][i].([]interface{})
					ip := rec[0].(string)
					var err error
					switch rec[1].(type) {
					case string:
						record.Weight, err = strconv.Atoi(rec[1].(string))
						if err != nil {
							panic("Error converting weight to integer")
						}
						label.Weight[dnsType] += record.Weight
					case float64:
						record.Weight = int(rec[1].(float64))
					}
					switch dnsType {
					case dns.TypeA:
						rr := &dns.RR_A{Hdr: h}
						rr.A = net.ParseIP(ip)
						if rr.A == nil {
							panic("Bad A record")
						}
						record.RR = rr
					case dns.TypeAAAA:
						rr := &dns.RR_AAAA{Hdr: h}
						rr.AAAA = net.ParseIP(ip)
						if rr.AAAA == nil {
							panic("Bad AAAA record")
						}
						record.RR = rr
					}
				case dns.TypeNS:
					rec := records[rType][i]
					rr := &dns.RR_NS{Hdr: h}

					switch rec.(type) {
					case string:
						rr.Ns = rec.(string)
					case []string:
						recl := rec.([]string)
						log.Println("RECL:", recl)
						rr.Ns = recl[0]
						if len(recl[1]) > 0 {
							log.Println("NS records with names syntax not supported")
						}
					default:
						log.Printf("Data: %T %#v\n", rec, rec)
						panic("Unrecognized NS format/syntax")
					}

					if h.Ttl < 43000 {
						h.Ttl = 43200
					}
					record.RR = rr

				default:
					log.Println("type:", rType)
					panic("Don't know how to handle this type")
				}

				if record.RR == nil {
					panic("record.RR is nil")
				}

				label.Records[dnsType][i] = *record
			}
			if label.Weight[dnsType] > 0 {
				sort.Sort(RecordsByWeight{label.Records[dnsType]})
			}
		}
	}

	setupSOA(Zone)

	//log.Println(Zones[k])
}

func setupSOA(Zone *Zone) {
	label := Zone.Labels[""]

	primaryNs := "ns"

	if record, ok := label.Records[dns.TypeNS]; ok {
		primaryNs = record[0].RR.(*dns.RR_NS).Ns
	}

	s := Zone.Origin + ". 3600 IN SOA " +
		primaryNs + " support.bitnames.com. " +
		strconv.Itoa(Zone.Options.Serial) +
		" 5400 5400 2419200 " +
		strconv.Itoa(Zone.Options.Ttl)

	log.Println("SOA: ", s)

	rr, err := dns.NewRR(s)

	if err != nil {
		log.Println("SOA Error", err)
		panic("Could not setup SOA")
	}

	record := Record{RR: rr}

	label.Records[dns.TypeSOA] = make([]Record, 1)
	label.Records[dns.TypeSOA][0] = record

}
<commit_msg>Start better error handling when reading zone data<commit_after>package main

import (
	"encoding/json"
	"github.com/miekg/dns"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path"
	"runtime/debug"
	"sort"
	"strconv"
	"strings"
	"time"
)

var configLastRead = map[string]time.Time{}

func configReader(dirName string, Zones Zones) {
	go func() {
		for {
			configReadDir(dirName, Zones)
			time.Sleep(5 * time.Second)
		}
	}()
}

func configReadDir(dirName string, Zones Zones) {
	dir, err := ioutil.ReadDir(dirName)
	if err != nil {
		panic(err)
	}

	var seenFiles = map[string]bool{}

	for _, file := range dir {
		fileName := file.Name()
		if !strings.HasSuffix(strings.ToLower(fileName), ".json") {
			continue
		}

		seenFiles[fileName] = true

		if lastRead, ok := configLastRead[fileName]; !ok || file.ModTime().After(lastRead) {
			log.Println("Updated file, going to read", fileName)
			configLastRead[fileName] = file.ModTime()
			zoneName := fileName[0:strings.LastIndex(fileName, ".")]
			//log.Println("FILE:", i, file, zoneName)
			config, err := readZoneFile(zoneName, path.Join(dirName, fileName))
			if config == nil || err != nil {
				log.Println("error reading file: ", err)
			}
			if config != nil && err == nil {
				Zones[zoneName] = config
				dns.HandleFunc(zoneName, setupServerFunc(config))
			}
		}

		// TODO(ask) Disable zones not seen in two subsequent runs
	}
}

func readZoneFile(zoneName, fileName string) (*Zone, error) {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("reading %s failed: %s", zoneName, err)
			debug.PrintStack()
		}
	}()

	fh, err := os.Open(fileName)
	if err != nil {
		log.Println("Could not read ", fileName, ": ", err)
		panic(err)
	}

	Zone := new(Zone)
	Zone.Labels = make(labels)
	Zone.LenLabels = dns.LenLabels(Zone.Origin)
	Zone.Origin = zoneName

	if err == nil {
		var objmap map[string]interface{}
		decoder := json.NewDecoder(fh)
		err := decoder.Decode(&objmap)
		if err != nil {
			panic(err)
		}
		//log.Println(objmap)

		var data map[string]interface{}

		for k, v := range objmap {
			//log.Printf("k: %s v: %#v, T: %T\n", k, v, v)

			switch k {
			case "ttl", "serial":
				switch option := k; option {
				case "ttl":
					Zone.Options.Ttl = int(v.(float64))
				case "serial":
					Zone.Options.Serial = int(v.(float64))
				}
				continue

			case "data":
				data = v.(map[string]interface{})
			}
		}

		setupZoneData(data, Zone)

	}

	//log.Printf("ZO T: %T %s\n", Zones["0.us"], Zones["0.us"])

	//log.Println("IP", string(Zone.Regions["0.us"].IPv4[0].ip))

	return Zone, nil
}

func setupZoneData(data map[string]interface{}, Zone *Zone) {

	var recordTypes = map[string]uint16{
		"a":    dns.TypeA,
		"aaaa": dns.TypeAAAA,
		"ns":   dns.TypeNS,
	}

	for dk, dv := range data {

		//log.Printf("K %s V %s TYPE-V %T\n", dk, dv, dv)

		Zone.Labels[dk] = new(Label)
		label := Zone.Labels[dk]

		// BUG(ask) Read 'ttl' value in label data

		for rType, dnsType := range recordTypes {

			var rdata = dv.(map[string]interface{})[rType]

			if rdata == nil {
				//log.Printf("No %s records for label %s\n", rType, dk)
				continue
			}

			//log.Printf("rdata %s TYPE-R %T\n", rdata, rdata)

			records := make(map[string][]interface{})

			switch rdata.(type) {
			case map[string]interface{}:
				// Handle map[ns2.example.net:<nil> ns1.example.net:<nil>]
				tmp := make([]interface{}, 0)
				for rdata_k, rdata_v := range rdata.(map[string]interface{}) {
					if rdata_v == nil {
						rdata_v = ""
					}
					tmp = append(tmp, []string{rdata_k, rdata_v.(string)})
				}
				records[rType] = tmp
			default:
				records[rType] = rdata.([]interface{})
			}

			//log.Printf("RECORDS %s TYPE-REC %T\n", Records, Records)

			if label.Records == nil {
				label.Records = make(map[uint16]Records)
				label.Weight = make(map[uint16]int)
			}

			label.Records[dnsType] = make(Records, len(records[rType]))

			for i := 0; i < len(records[rType]); i++ {

				//log.Printf("RT %T %#v\n", records[rType][i], records[rType][i])

				record := new(Record)

				var h dns.RR_Header
				// log.Println("TTL OPTIONS", Zone.Options.Ttl)
				h.Ttl = uint32(Zone.Options.Ttl)
				h.Class = dns.ClassINET
				h.Rrtype = dnsType

				switch dnsType {
				case dns.TypeA, dns.TypeAAAA:
					rec := records[rType][i].([]interface{})
					ip := rec[0].(string)
					var err error
					switch rec[1].(type) {
					case string:
						record.Weight, err = strconv.Atoi(rec[1].(string))
						if err != nil {
							panic("Error converting weight to integer")
						}
						label.Weight[dnsType] += record.Weight
					case float64:
						record.Weight = int(rec[1].(float64))
					}
					switch dnsType {
					case dns.TypeA:
						rr := &dns.RR_A{Hdr: h}
						rr.A = net.ParseIP(ip)
						if rr.A == nil {
							panic("Bad A record")
						}
						record.RR = rr
					case dns.TypeAAAA:
						rr := &dns.RR_AAAA{Hdr: h}
						rr.AAAA = net.ParseIP(ip)
						if rr.AAAA == nil {
							panic("Bad AAAA record")
						}
						record.RR = rr
					}
				case dns.TypeNS:
					rec := records[rType][i]
					rr := &dns.RR_NS{Hdr: h}

					switch rec.(type) {
					case string:
						rr.Ns = rec.(string)
					case []string:
						recl := rec.([]string)
						log.Println("RECL:", recl)
						rr.Ns = recl[0]
						if len(recl[1]) > 0 {
							log.Println("NS records with names syntax not supported")
						}
					default:
						log.Printf("Data: %T %#v\n", rec, rec)
						panic("Unrecognized NS format/syntax")
					}

					if h.Ttl < 43000 {
						h.Ttl = 43200
					}
					record.RR = rr

				default:
					log.Println("type:", rType)
					panic("Don't know how to handle this type")
				}

				if record.RR == nil {
					panic("record.RR is nil")
				}

				label.Records[dnsType][i] = *record
			}
			if label.Weight[dnsType] > 0 {
				sort.Sort(RecordsByWeight{label.Records[dnsType]})
			}
		}
	}

	setupSOA(Zone)

	//log.Println(Zones[k])
}

func setupSOA(Zone *Zone) {
	label := Zone.Labels[""]

	primaryNs := "ns"

	if record, ok := label.Records[dns.TypeNS]; ok {
		primaryNs = record[0].RR.(*dns.RR_NS).Ns
	}

	s := Zone.Origin + ". 3600 IN SOA " +
		primaryNs + " support.bitnames.com. " +
		strconv.Itoa(Zone.Options.Serial) +
		" 5400 5400 2419200 " +
		strconv.Itoa(Zone.Options.Ttl)

	log.Println("SOA: ", s)

	rr, err := dns.NewRR(s)

	if err != nil {
		log.Println("SOA Error", err)
		panic("Could not setup SOA")
	}

	record := Record{RR: rr}

	label.Records[dns.TypeSOA] = make([]Record, 1)
	label.Records[dns.TypeSOA][0] = record

}
<|endoftext|>


<commit_before>package messages

// Github config
type Github struct {
	Token  string `json:"token"`
	Labels []string
}

// Config internal to be shared between services
type Config struct {
	Github *Github `json:"github, omitempty"`
}

// GetIssuesList request message to get an issue list
type GetIssuesList struct {
	Status string `json:"status"`
	Org    string `json:"org"`
	Repo   string `json:"repo,omitempty"`
	Config `json:"config"`
}

// IssuesDetails request message to get an issue
type GetIssue struct {
	Issue  *Issue `json:"issue"`
	Config `json:"config"`
}

// CreateIssue request message to create an issue
type CreateIssue struct {
	Issue  *Issue `json:issue`
	Config `json:"config"`
}

// Setup request message to setup issue tracker
type Setup struct {
	Org    string   `json:"org"`
	Repo   string   `json:"repo"`
	States []string `json:"states"`
	Config `json:"config"`
}

// UpdateIssue request message to update an issue
type UpdateIssue struct {
	Issue  *Issue `json:issue`
	Status string `json:"state"`
	Config `json:"config"`
}
<commit_msg>Workflow to be injected<commit_after>package messages

// Github config
type Github struct {
	Token  string `json:"token"`
	Labels []string
}

// Config internal to be shared between services
type Config struct {
	Github *Github `json:"github, omitempty"`
}

// GetIssuesList request message to get an issue list
type GetIssuesList struct {
	Status string `json:"status"`
	Org    string `json:"org"`
	Repo   string `json:"repo,omitempty"`
	Config `json:"config"`
}

// IssuesDetails request message to get an issue
type GetIssue struct {
	Issue  *Issue `json:"issue"`
	Config `json:"config"`
}

// CreateIssue request message to create an issue
type CreateIssue struct {
	Issue  *Issue `json:issue`
	Config `json:"config"`
}

// Setup request message to setup issue tracker
type Setup struct {
	Org    string   `json:"org"`
	Repo   string   `json:"repo"`
	States []string `json:"states"`
	Config `json:"config"`
}

// UpdateIssue request message to update an issue
type UpdateIssue struct {
	Issue    *Issue `json:issue`
	Status   string `json:"state"`
	Config   `json:"config"`
	Workflow *[]Transition `json:"transitions"`
}

// Transition json representation
type Transition struct {
	From  string   `json:"from"`
	To    string   `json:"to"`
	Hooks []string `json:"hooks"`
}
<|endoftext|>


<commit_before>// !!! !!!
// WARNING: Code automatically generated. Editing discouraged.
// !!! !!!

package easygen

import (
	"flag"
	"fmt"
	"os"
)

////////////////////////////////////////////////////////////////////////////
// Constant and data type/structure definitions

const progname = "easygen" // os.Args[0]

// The Options struct defines the structure to hold the commandline values
type Options struct {
	HTML         bool   // treat the template file as html instead of text
	TemplateStr  string // template string (in text)
	TemplateFile string // .tmpl template file `name` (default: same as .yaml file)
	ExtYaml      string // `extension` of yaml file
	ExtTmpl      string // `extension` of template file
	StrFrom      string // replace from, the from string used for the replace function
	StrTo        string // replace to, the to string used for the replace function
	Debug        int    // debugging `level`
}

////////////////////////////////////////////////////////////////////////////
// Global variables definitions

// Opts holds the actual values from the command line parameters
var Opts = Options{ExtYaml: ".yaml", ExtTmpl: ".tmpl"}

////////////////////////////////////////////////////////////////////////////
// Commandline definitions

// Usage function shows help on commandline usage
func Usage() {
	fmt.Fprintf(os.Stderr,
		"\nUsage:\n %s [flags] YamlFileName [YamlFileName...]\n\nFlags:\n\n",
		progname)
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr,
		"\nYamlFileName: The name for the .yaml data and .tmpl template file\n\tOnly the name part, without extension. Can include the path as well.\n")
	os.Exit(0)
}
<commit_msg>- [-] deleted config.go<commit_after><|endoftext|>