<commit_before>package main

import (
	"io/ioutil"
	"os"
	"strings"

	log "github.com/Sirupsen/logrus"
)

type SshConfigFileSection struct {
	Host         string
	ForwardAgent string
	User         string
	HostName     string
	Port         string
}

// parseSshConfigFileSection parses a section from the ~/.ssh/config file
func parseSshConfigFileSection(content string) *SshConfigFileSection {
	section := &SshConfigFileSection{}

	for n, line := range strings.Split(content, "\n") {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}

		if n == 0 {
			section.Host = line
		} else if strings.HasPrefix(line, "ForwardAgent") {
			section.ForwardAgent = strings.TrimSpace(strings.TrimPrefix(line, "ForwardAgent"))
		} else if strings.HasPrefix(line, "User") {
			section.User = strings.TrimSpace(strings.TrimPrefix(line, "User"))
		} else if strings.HasPrefix(line, "HostName") {
			section.HostName = strings.TrimSpace(strings.TrimPrefix(line, "HostName"))
		} else if strings.HasPrefix(line, "Port") {
			section.Port = strings.TrimSpace(strings.TrimPrefix(line, "Port"))
		}
	}
	log.Debugf("parsed ssh config file section: %s", section.Host)
	return section
}

// parseSshConfigFile parses the ~/.ssh/config file and build a list of section
func parseSshConfigFile(path string) (map[string]*SshConfigFileSection, error) {

	sections := make(map[string]*SshConfigFileSection)

	_, err := os.Stat(path)

	if err != nil && os.IsNotExist(err) {
		log.Debugf("cannot find ssh config file: %s", path)
		return sections, nil
	}

	log.Debugf("parsing ssh config file: %s", path)
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	for _, split := range strings.Split(string(content), "Host ") {
		split = strings.TrimSpace(split)
		if split == "" {
			continue
		}

		section := parseSshConfigFileSection(split)
		sections[section.Host] = section
	}

	return sections, nil
}
<commit_msg>refactor(config): Use oneliner<commit_after>package main

import (
	"io/ioutil"
	"os"
	"strings"

	log "github.com/Sirupsen/logrus"
)

type SshConfigFileSection struct {
	Host         string
	ForwardAgent string
	User         string
	HostName     string
	Port         string
}

// parseSshConfigFileSection parses a section from the ~/.ssh/config file
func parseSshConfigFileSection(content string) *SshConfigFileSection {
	section := &SshConfigFileSection{}

	for n, line := range strings.Split(content, "\n") {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}

		if n == 0 {
			section.Host = line
		} else if strings.HasPrefix(line, "ForwardAgent") {
			section.ForwardAgent = strings.TrimSpace(strings.TrimPrefix(line, "ForwardAgent"))
		} else if strings.HasPrefix(line, "User") {
			section.User = strings.TrimSpace(strings.TrimPrefix(line, "User"))
		} else if strings.HasPrefix(line, "HostName") {
			section.HostName = strings.TrimSpace(strings.TrimPrefix(line, "HostName"))
		} else if strings.HasPrefix(line, "Port") {
			section.Port = strings.TrimSpace(strings.TrimPrefix(line, "Port"))
		}
	}
	log.Debugf("parsed ssh config file section: %s", section.Host)
	return section
}

// parseSshConfigFile parses the ~/.ssh/config file and build a list of section
func parseSshConfigFile(path string) (map[string]*SshConfigFileSection, error) {

	sections := make(map[string]*SshConfigFileSection)

	if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
		log.Debugf("cannot find ssh config file: %s", path)
		return sections, nil
	}

	log.Debugf("parsing ssh config file: %s", path)
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	for _, split := range strings.Split(string(content), "Host ") {
		split = strings.TrimSpace(split)
		if split == "" {
			continue
		}

		section := parseSshConfigFileSection(split)
		sections[section.Host] = section
	}

	return sections, nil
}
<|endoftext|>


<commit_before>package townsita

import (
	"encoding/json"
	"log"
	"os"
)

const defaultConfigFile = "./config/listboard.json"

type Config struct{}

func NewConfig() *Config {
	return &Config{}
}

func (c *Config) Load(args []string) error {
	fileName := defaultConfigFile
	if len(args) > 1 {
		fileName = args[1]
	}
	log.Printf("Loading config from %s", fileName)
	file, err := os.Open(fileName)
	if err != nil {
		return err
	}
	decoder := json.NewDecoder(file)
	if err := decoder.Decode(c); err != nil {
		return err
	}
	return nil
}

func (c *Config) templatePath(templateName string) string {
	return "./templates/" + templateName
}
<commit_msg>Config file name<commit_after>package townsita

import (
	"encoding/json"
	"log"
	"os"
)

const defaultConfigFile = "./config/townsita.json"

type Config struct{}

func NewConfig() *Config {
	return &Config{}
}

func (c *Config) Load(args []string) error {
	fileName := defaultConfigFile
	if len(args) > 1 {
		fileName = args[1]
	}
	log.Printf("Loading config from %s", fileName)
	file, err := os.Open(fileName)
	if err != nil {
		return err
	}
	decoder := json.NewDecoder(file)
	if err := decoder.Decode(c); err != nil {
		return err
	}
	return nil
}

func (c *Config) templatePath(templateName string) string {
	return "./templates/" + templateName
}
<|endoftext|>


<commit_before>package main

import (
    "log"
    "strconv"

    "github.com/briankassouf/cfg"
)

type Configuration struct {
    vars map[string]string
}

func initConfig() Configuration {
    mymap := make(map[string]string)
    err := cfg.Load("sockets.conf", mymap)
    if err != nil {
        log.Panic(err)
    }

    return Configuration{mymap}
}

func (this *Configuration) Get(name string) string {
    val, ok := this.vars[name]
    if !ok {
        log.Panicf("Config Error: variable '%s' not found", name)
    }

    return val;
}

func (this *Configuration) GetInt(name string) int {
    val, ok := this.vars[name]
    if !ok {
        log.Panicf("Config Error: variable '%s' not found", name)
    }

    i, err := strconv.Atoi(val)
    if err != nil {
        log.Panicf("Config Error: '%s' could not be cast as an int", name)
    }

    return i
}<commit_msg>change where config file is read from<commit_after>package main

import (
    "log"
    "strconv"

    "github.com/briankassouf/cfg"
)

type Configuration struct {
    vars map[string]string
}

func initConfig() Configuration {
    mymap := make(map[string]string)
    err := cfg.Load("/var/log/incus.conf", mymap)
    if err != nil {
        log.Panic(err)
    }

    return Configuration{mymap}
}

func (this *Configuration) Get(name string) string {
    val, ok := this.vars[name]
    if !ok {
        log.Panicf("Config Error: variable '%s' not found", name)
    }

    return val;
}

func (this *Configuration) GetInt(name string) int {
    val, ok := this.vars[name]
    if !ok {
        log.Panicf("Config Error: variable '%s' not found", name)
    }

    i, err := strconv.Atoi(val)
    if err != nil {
        log.Panicf("Config Error: '%s' could not be cast as an int", name)
    }

    return i
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.

package main

import (
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/coreos/go-etcd/etcd"
	"github.com/miekg/dns"
)

// Config provides options to the SkyDNS resolver.
type Config struct {
	// The ip:port SkyDNS should be listening on for incoming DNS requests.
	DnsAddr string `json:"dns_addr,omitempty"`
	// The domain SkyDNS is authoritative for, defaults to skydns.local.
	Domain string `json:"domain,omitempty"`
	DNSSEC string `json:"dnssec,omitempty"`
	// Round robin A/AAAA replies. Default is true.
	RoundRobin bool `json:"round_robin,omitempty"`
	// List of ip:port, seperated by commas of recursive nameservers to forward queries to.
	Nameservers []string      `json:"nameservers,omitempty"`
	ReadTimeout time.Duration `json:"read_timeout,omitempty"`
	// Default priority on SRV records when none is given. Defaults to 10.
	Priority uint16 `json:"priority"`
	// Default TTL, in seconds, when none is given in etcd. Defaults to 3600.
	Ttl uint32 `json:"ttl,omitempty"`
	// Minimum TTL, in seconds, for NXDOMAIN responses. Defaults to 300.
	MinTtl uint32 `json:"min_ttl,omitempty"`

	// DNSSEC key material
	PubKey       *dns.DNSKEY    `json:"-"`
	KeyTag       uint16         `json:"-"`
	PrivKey      dns.PrivateKey `json:"-"`
	DomainLabels int            `json:"-"`
}

func LoadConfig(client *etcd.Client) (*Config, error) {
	config := &Config{ReadTimeout: 0, Domain: "", DnsAddr: "", DNSSEC: ""}
	n, err := client.Get("/skydns/config", false, false)
	if err != nil {
		c, err := dns.ClientConfigFromFile("/etc/resolv.conf")
		if err != nil {
			return nil, err
		}
		for _, s := range c.Servers {
			config.Nameservers = append(config.Nameservers, net.JoinHostPort(s, c.Port))
		}
		return config, nil
	}
	if err := json.Unmarshal([]byte(n.Node.Value), &config); err != nil {
		return nil, err
	}
	if err := setDefaults(config); err != nil {
		return nil, err
	}
	return config, nil
}

func setDefaults(config *Config) error {
	if config.ReadTimeout == 0 {
		config.ReadTimeout = 2 * time.Second
	}
	if config.DnsAddr == "" {
		config.DnsAddr = "127.0.0.1:53"
	}
	if config.Domain == "" {
		config.Domain = "skydns.local"
	}
	if config.MinTtl == 0 {
		config.MinTtl = 60
	}
	if config.Ttl == 0 {
		config.Ttl = 3600
	}
	if config.Priority == 0 {
		config.Priority = 10
	}

	if len(config.Nameservers) == 0 {
		c, err := dns.ClientConfigFromFile("/etc/resolv.conf")
		if err != nil {
			return err
		}
		for _, s := range c.Servers {
			config.Nameservers = append(config.Nameservers, net.JoinHostPort(s, c.Port))
		}
	}
	config.Domain = dns.Fqdn(strings.ToLower(config.Domain))
	config.DomainLabels = dns.CountLabel(config.Domain)
	if config.DNSSEC != "" {
		// For some reason the + are replaces by spaces in etcd. Re-replace them
		keyfile := strings.Replace(config.DNSSEC, " ",  "+", -1)
		k, p, err := ParseKeyFile(keyfile)
		if err != nil {
			return err
		}
		if k.Header().Name != dns.Fqdn(config.Domain) {
			return fmt.Errorf("ownername of DNSKEY must match SkyDNS domain")
		}
		k.Header().Ttl = config.Ttl
		config.PubKey = k
		config.KeyTag = k.KeyTag()
		config.PrivKey = p
	}
	return nil
}
<commit_msg>More testing, fix private key parsing<commit_after>// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.

package main

import (
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/coreos/go-etcd/etcd"
	"github.com/miekg/dns"
)

// Config provides options to the SkyDNS resolver.
type Config struct {
	// The ip:port SkyDNS should be listening on for incoming DNS requests.
	DnsAddr string `json:"dns_addr,omitempty"`
	// The domain SkyDNS is authoritative for, defaults to skydns.local.
	Domain string `json:"domain,omitempty"`
	DNSSEC string `json:"dnssec,omitempty"`
	// Round robin A/AAAA replies. Default is true.
	RoundRobin bool `json:"round_robin,omitempty"`
	// List of ip:port, seperated by commas of recursive nameservers to forward queries to.
	Nameservers []string      `json:"nameservers,omitempty"`
	ReadTimeout time.Duration `json:"read_timeout,omitempty"`
	// Default priority on SRV records when none is given. Defaults to 10.
	Priority uint16 `json:"priority"`
	// Default TTL, in seconds, when none is given in etcd. Defaults to 3600.
	Ttl uint32 `json:"ttl,omitempty"`
	// Minimum TTL, in seconds, for NXDOMAIN responses. Defaults to 300.
	MinTtl uint32 `json:"min_ttl,omitempty"`

	// DNSSEC key material
	PubKey       *dns.DNSKEY    `json:"-"`
	KeyTag       uint16         `json:"-"`
	PrivKey      dns.PrivateKey `json:"-"`
	DomainLabels int            `json:"-"`
}

func LoadConfig(client *etcd.Client) (*Config, error) {
	config := &Config{ReadTimeout: 0, Domain: "", DnsAddr: "", DNSSEC: ""}
	n, err := client.Get("/skydns/config", false, false)
	if err != nil {
		c, err := dns.ClientConfigFromFile("/etc/resolv.conf")
		if err != nil {
			return nil, err
		}
		for _, s := range c.Servers {
			config.Nameservers = append(config.Nameservers, net.JoinHostPort(s, c.Port))
		}
		return config, nil
	}
	if err := json.Unmarshal([]byte(n.Node.Value), &config); err != nil {
		return nil, err
	}
	if err := setDefaults(config); err != nil {
		return nil, err
	}
	return config, nil
}

func setDefaults(config *Config) error {
	if config.ReadTimeout == 0 {
		config.ReadTimeout = 2 * time.Second
	}
	if config.DnsAddr == "" {
		config.DnsAddr = "127.0.0.1:53"
	}
	if config.Domain == "" {
		config.Domain = "skydns.local"
	}
	if config.MinTtl == 0 {
		config.MinTtl = 60
	}
	if config.Ttl == 0 {
		config.Ttl = 3600
	}
	if config.Priority == 0 {
		config.Priority = 10
	}

	if len(config.Nameservers) == 0 {
		c, err := dns.ClientConfigFromFile("/etc/resolv.conf")
		if err != nil {
			return err
		}
		for _, s := range c.Servers {
			config.Nameservers = append(config.Nameservers, net.JoinHostPort(s, c.Port))
		}
	}
	config.Domain = dns.Fqdn(strings.ToLower(config.Domain))
	config.DomainLabels = dns.CountLabel(config.Domain)
	if config.DNSSEC != "" {
		// For some reason the + are replaced by spaces in etcd. Re-replace them.
		keyfile := strings.Replace(config.DNSSEC, " ",  "+", -1)
		k, p, err := ParseKeyFile(keyfile)
		if err != nil {
			return err
		}
		if k.Header().Name != dns.Fqdn(config.Domain) {
			return fmt.Errorf("ownername of DNSKEY must match SkyDNS domain")
		}
		k.Header().Ttl = config.Ttl
		config.PubKey = k
		config.KeyTag = k.KeyTag()
		config.PrivKey = p
	}
	return nil
}
<|endoftext|>


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

package bleve

import (
	"expvar"
	"io/ioutil"
	"log"
	"time"

	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/index/scorch"
	"github.com/blevesearch/bleve/index/store/gtreap"
	"github.com/blevesearch/bleve/registry"
	"github.com/blevesearch/bleve/search/highlight/highlighter/html"
)

var bleveExpVar = expvar.NewMap("bleve")

type configuration struct {
	Cache                  *registry.Cache
	DefaultHighlighter     string
	DefaultKVStore         string
	DefaultMemKVStore      string
	DefaultIndexType       string
	SlowSearchLogThreshold time.Duration
	analysisQueue          *index.AnalysisQueue
}

func (c *configuration) SetAnalysisQueueSize(n int) {
	c.analysisQueue = index.NewAnalysisQueue(n)
}

func newConfiguration() *configuration {
	return &configuration{
		Cache:         registry.NewCache(),
		analysisQueue: index.NewAnalysisQueue(4),
	}
}

// Config contains library level configuration
var Config *configuration

func init() {
	bootStart := time.Now()

	// build the default configuration
	Config = newConfiguration()

	// set the default highlighter
	Config.DefaultHighlighter = html.Name

	// default kv store
	Config.DefaultKVStore = ""

	// default mem only kv store
	Config.DefaultMemKVStore = gtreap.Name

	// default index
	Config.DefaultIndexType = scorch.Name

	bootDuration := time.Since(bootStart)
	bleveExpVar.Add("bootDuration", int64(bootDuration))
	indexStats = NewIndexStats()
	bleveExpVar.Set("indexes", indexStats)

	initDisk()
}

var logger = log.New(ioutil.Discard, "bleve", log.LstdFlags)

// SetLog sets the logger used for logging
// by default log messages are sent to ioutil.Discard
func SetLog(l *log.Logger) {
	logger = l
}
<commit_msg>switch back to upsidedown as default index before merge to master<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 		http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bleve

import (
	"expvar"
	"io/ioutil"
	"log"
	"time"

	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/index/store/gtreap"
	"github.com/blevesearch/bleve/index/upsidedown"
	"github.com/blevesearch/bleve/registry"
	"github.com/blevesearch/bleve/search/highlight/highlighter/html"

	// force import of scorch so its accessible by default
	_ "github.com/blevesearch/bleve/index/scorch"
)

var bleveExpVar = expvar.NewMap("bleve")

type configuration struct {
	Cache                  *registry.Cache
	DefaultHighlighter     string
	DefaultKVStore         string
	DefaultMemKVStore      string
	DefaultIndexType       string
	SlowSearchLogThreshold time.Duration
	analysisQueue          *index.AnalysisQueue
}

func (c *configuration) SetAnalysisQueueSize(n int) {
	c.analysisQueue = index.NewAnalysisQueue(n)
}

func newConfiguration() *configuration {
	return &configuration{
		Cache:         registry.NewCache(),
		analysisQueue: index.NewAnalysisQueue(4),
	}
}

// Config contains library level configuration
var Config *configuration

func init() {
	bootStart := time.Now()

	// build the default configuration
	Config = newConfiguration()

	// set the default highlighter
	Config.DefaultHighlighter = html.Name

	// default kv store
	Config.DefaultKVStore = ""

	// default mem only kv store
	Config.DefaultMemKVStore = gtreap.Name

	// default index
	Config.DefaultIndexType = upsidedown.Name

	bootDuration := time.Since(bootStart)
	bleveExpVar.Add("bootDuration", int64(bootDuration))
	indexStats = NewIndexStats()
	bleveExpVar.Set("indexes", indexStats)

	initDisk()
}

var logger = log.New(ioutil.Discard, "bleve", log.LstdFlags)

// SetLog sets the logger used for logging
// by default log messages are sent to ioutil.Discard
func SetLog(l *log.Logger) {
	logger = l
}
<|endoftext|>


<commit_before>package lessgo

import (
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"strings"

	confpkg "github.com/lessgo/lessgo/config"
	"github.com/lessgo/lessgo/logs"
)

type (
	// Config is the main struct for Config
	config struct {
		AppName     string // Application name
		Info        Info   // Application info
		Debug       bool   // enable/disable debug mode.
		CrossDomain bool
		MaxMemoryMB int64 // 文件上传默认内存缓存大小，单位MB
		Listen      Listen
		Session     SessionConfig
		Log         LogConfig
		FileCache   FileCacheConfig
	}
	Info struct {
		Version           string
		Description       string
		Email             string
		TermsOfServiceUrl string
		License           string
		LicenseUrl        string
	}
	// Listen holds for http and https related config
	Listen struct {
		Graceful      bool // Graceful means use graceful module to start the server
		Address       string
		ReadTimeout   int64
		WriteTimeout  int64
		EnableHTTPS   bool
		HTTPSKeyFile  string
		HTTPSCertFile string
	}
	// SessionConfig holds session related config
	SessionConfig struct {
		SessionOn               bool
		SessionProvider         string
		SessionName             string
		SessionGCMaxLifetime    int64
		SessionProviderConfig   string
		SessionCookieLifeTime   int
		SessionAutoSetCookie    bool
		SessionDomain           string
		EnableSidInHttpHeader   bool //	enable store/get the sessionId into/from http headers
		SessionNameInHttpHeader string
		EnableSidInUrlQuery     bool //	enable get the sessionId from Url Query params
	}

	// LogConfig holds Log related config
	LogConfig struct {
		Level     int
		AsyncChan int64
	}
	FileCacheConfig struct {
		CacheSecond       int64 // 静态资源缓存监测频率与缓存动态释放的最大时长，单位秒，默认600秒
		SingleFileAllowMB int64 // 允许的最大文件，单位MB
		MaxCapMB          int64 // 最大缓存总量，单位MB
	}
)

// 项目固定目录文件名称
const (
	BIZ_HANDLER_DIR = "bizhandler"
	BIZ_MODEL_DIR   = "bizmodel"
	BIZ_VIEW_DIR    = "bizview"
	SYS_HANDLER_DIR = "syshandler"
	SYS_MODEL_DIR   = "sysmodel"
	SYS_VIEW_DIR    = "sysview"
	STATIC_DIR      = "static"
	IMG_DIR         = STATIC_DIR + "/img"
	JS_DIR          = STATIC_DIR + "/js"
	CSS_DIR         = STATIC_DIR + "/css"
	TPL_DIR         = STATIC_DIR + "/tpl"
	PLUGIN_DIR      = STATIC_DIR + "/plugin"
	UPLOADS_DIR     = "uploads"
	COMMON_DIR      = "common"
	MIDDLEWARE_DIR  = "middleware"
	ROUTER_DIR      = "router"

	TPL_EXT         = ".tpl"
	STATIC_HTML_EXT = ".html"

	CONFIG_DIR        = "config"
	APPCONFIG_FILE    = CONFIG_DIR + "/app.config"
	ROUTERCONFIG_FILE = CONFIG_DIR + "/virtrouter.config"
	LOG_FILE          = "logger/lessgo.log"
)

func newConfig() *config {
	return &config{
		AppName: "lessgo",
		Info: Info{
			Version:     "0.4.0",
			Description: "A simple, stable, efficient and flexible web framework.",
			// Host:              "127.0.0.1:8080",
			Email:             "henrylee_cn@foxmail.com",
			TermsOfServiceUrl: "https://github.com/lessgo/lessgo",
			License:           "MIT",
			LicenseUrl:        "https://github.com/lessgo/lessgo/raw/master/doc/LICENSE",
		},
		Debug:       true,
		CrossDomain: false,
		MaxMemoryMB: 64, // 64MB
		Listen: Listen{
			Graceful:      false,
			Address:       "0.0.0.0:8080",
			ReadTimeout:   0,
			WriteTimeout:  0,
			EnableHTTPS:   false,
			HTTPSCertFile: "",
			HTTPSKeyFile:  "",
		},
		Session: SessionConfig{
			SessionOn:               false,
			SessionProvider:         "memory",
			SessionName:             "lessgosessionID",
			SessionGCMaxLifetime:    3600,
			SessionProviderConfig:   "",
			SessionCookieLifeTime:   0, //set cookie default is the browser life
			SessionAutoSetCookie:    true,
			SessionDomain:           "",
			EnableSidInHttpHeader:   false, //	enable store/get the sessionId into/from http headers
			SessionNameInHttpHeader: "Lessgosessionid",
			EnableSidInUrlQuery:     false, //	enable get the sessionId from Url Query params
		},

		FileCache: FileCacheConfig{
			CacheSecond:       600, // 600s
			SingleFileAllowMB: 64,  // 64MB
			MaxCapMB:          256, // 256MB
		},
		Log: LogConfig{
			Level:     logs.DEBUG,
			AsyncChan: 1000,
		},
	}
}

func (this *config) LoadMainConfig(fname string) (err error) {
	iniconf, err := confpkg.NewConfig("ini", fname)
	if err == nil {
		os.Remove(fname)
		ReadSingleConfig("system", Config, iniconf)
		ReadSingleConfig("filecache", &this.FileCache, iniconf)
		ReadSingleConfig("info", &this.Info, iniconf)
		ReadSingleConfig("listen", &this.Listen, iniconf)
		ReadSingleConfig("log", &this.Log, iniconf)
		ReadSingleConfig("session", &this.Session, iniconf)
	}
	os.MkdirAll(filepath.Dir(fname), 0777)
	f, err := os.Create(fname)
	if err != nil {
		return err
	}
	f.Close()
	iniconf, err = confpkg.NewConfig("ini", fname)
	if err != nil {
		return err
	}
	WriteSingleConfig("system", Config, iniconf)
	WriteSingleConfig("filecache", &this.FileCache, iniconf)
	WriteSingleConfig("info", &this.Info, iniconf)
	WriteSingleConfig("listen", &this.Listen, iniconf)
	WriteSingleConfig("log", &this.Log, iniconf)
	WriteSingleConfig("session", &this.Session, iniconf)

	return iniconf.SaveConfigFile(fname)
}

func ReadSingleConfig(section string, p interface{}, iniconf confpkg.Configer) {
	pt := reflect.TypeOf(p)
	if pt.Kind() != reflect.Ptr {
		return
	}
	pt = pt.Elem()
	if pt.Kind() != reflect.Struct {
		return
	}
	pv := reflect.ValueOf(p).Elem()

	for i := 0; i < pt.NumField(); i++ {
		pf := pv.Field(i)
		if !pf.CanSet() {
			continue
		}
		name := pt.Field(i).Name
		fullname := getfullname(section, name)
		switch pf.Kind() {
		case reflect.String:
			str := iniconf.DefaultString(fullname, pf.String())
			switch name {
			case "TableFix", "ColumnFix":
				pf.SetString(strings.ToLower(str))
			default:
				pf.SetString(str)
			}

		case reflect.Int, reflect.Int64:
			num := int64(iniconf.DefaultInt64(fullname, pf.Int()))
			switch fullname {
			case "system::maxmemorymb",
				"filecache::cachesecond", "filecache::singlefileallowmb", "filecache::maxcapmb",
				"listen::readtimeout", "listen::writetimeout",
				"session::sessiongcmaxlifetime", "session::sessioncookielifetime",
				"log::asyncchan":
				if num > 0 {
					pf.SetInt(num)
				}
			case "log::level":
				str := logLevelString(int(num))
				str2 := iniconf.DefaultString(fullname, str)
				num = int64(logLevelInt(str2))
				if num != -10 {
					pf.SetInt(num)
				}
			default:
				pf.SetInt(num)
			}

		case reflect.Bool:
			pf.SetBool(iniconf.DefaultBool(fullname, pf.Bool()))
		}
	}
}

func WriteSingleConfig(section string, p interface{}, iniconf confpkg.Configer) {
	pt := reflect.TypeOf(p)
	if pt.Kind() != reflect.Ptr {
		return
	}
	pt = pt.Elem()
	if pt.Kind() != reflect.Struct {
		return
	}
	pv := reflect.ValueOf(p).Elem()

	for i := 0; i < pt.NumField(); i++ {
		pf := pv.Field(i)
		if !pf.CanSet() {
			continue
		}
		fullname := getfullname(section, pt.Field(i).Name)
		switch pf.Kind() {
		case reflect.String, reflect.Int, reflect.Int64, reflect.Bool:
			switch fullname {
			case "log::level":
				iniconf.Set(fullname, logLevelString(int(pf.Int())))
			default:
				iniconf.Set(fullname, fmt.Sprint(pf.Interface()))
			}
		}
	}
}

// section name and key name case insensitive
func getfullname(section, name string) string {
	if section == "" {
		return strings.ToLower(name)
	}
	return strings.ToLower(section + "::" + name)
}

func logLevelInt(l string) int {
	switch strings.ToLower(l) {
	case "debug":
		return logs.DEBUG
	case "info":
		return logs.INFO
	case "warn":
		return logs.WARN
	case "error":
		return logs.ERROR
	case "fatal":
		return logs.FATAL
	case "off":
		return logs.OFF
	}
	return -10
}

func logLevelString(l int) string {
	switch l {
	case logs.DEBUG:
		return "debug"
	case logs.INFO:
		return "info"
	case logs.WARN:
		return "warn"
	case logs.ERROR:
		return "error"
	case logs.FATAL:
		return "fatal"
	case logs.OFF:
		return "off"
	}
	return "error"
}
<commit_msg>允许log::asyncchan配置为0<commit_after>package lessgo

import (
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"strings"

	confpkg "github.com/lessgo/lessgo/config"
	"github.com/lessgo/lessgo/logs"
)

type (
	// Config is the main struct for Config
	config struct {
		AppName     string // Application name
		Info        Info   // Application info
		Debug       bool   // enable/disable debug mode.
		CrossDomain bool
		MaxMemoryMB int64 // 文件上传默认内存缓存大小，单位MB
		Listen      Listen
		Session     SessionConfig
		Log         LogConfig
		FileCache   FileCacheConfig
	}
	Info struct {
		Version           string
		Description       string
		Email             string
		TermsOfServiceUrl string
		License           string
		LicenseUrl        string
	}
	// Listen holds for http and https related config
	Listen struct {
		Graceful      bool // Graceful means use graceful module to start the server
		Address       string
		ReadTimeout   int64
		WriteTimeout  int64
		EnableHTTPS   bool
		HTTPSKeyFile  string
		HTTPSCertFile string
	}
	// SessionConfig holds session related config
	SessionConfig struct {
		SessionOn               bool
		SessionProvider         string
		SessionName             string
		SessionGCMaxLifetime    int64
		SessionProviderConfig   string
		SessionCookieLifeTime   int
		SessionAutoSetCookie    bool
		SessionDomain           string
		EnableSidInHttpHeader   bool //	enable store/get the sessionId into/from http headers
		SessionNameInHttpHeader string
		EnableSidInUrlQuery     bool //	enable get the sessionId from Url Query params
	}

	// LogConfig holds Log related config
	LogConfig struct {
		Level     int
		AsyncChan int64
	}
	FileCacheConfig struct {
		CacheSecond       int64 // 静态资源缓存监测频率与缓存动态释放的最大时长，单位秒，默认600秒
		SingleFileAllowMB int64 // 允许的最大文件，单位MB
		MaxCapMB          int64 // 最大缓存总量，单位MB
	}
)

// 项目固定目录文件名称
const (
	BIZ_HANDLER_DIR = "bizhandler"
	BIZ_MODEL_DIR   = "bizmodel"
	BIZ_VIEW_DIR    = "bizview"
	SYS_HANDLER_DIR = "syshandler"
	SYS_MODEL_DIR   = "sysmodel"
	SYS_VIEW_DIR    = "sysview"
	STATIC_DIR      = "static"
	IMG_DIR         = STATIC_DIR + "/img"
	JS_DIR          = STATIC_DIR + "/js"
	CSS_DIR         = STATIC_DIR + "/css"
	TPL_DIR         = STATIC_DIR + "/tpl"
	PLUGIN_DIR      = STATIC_DIR + "/plugin"
	UPLOADS_DIR     = "uploads"
	COMMON_DIR      = "common"
	MIDDLEWARE_DIR  = "middleware"
	ROUTER_DIR      = "router"

	TPL_EXT         = ".tpl"
	STATIC_HTML_EXT = ".html"

	CONFIG_DIR        = "config"
	APPCONFIG_FILE    = CONFIG_DIR + "/app.config"
	ROUTERCONFIG_FILE = CONFIG_DIR + "/virtrouter.config"
	LOG_FILE          = "logger/lessgo.log"
)

func newConfig() *config {
	return &config{
		AppName: "lessgo",
		Info: Info{
			Version:     "0.4.0",
			Description: "A simple, stable, efficient and flexible web framework.",
			// Host:              "127.0.0.1:8080",
			Email:             "henrylee_cn@foxmail.com",
			TermsOfServiceUrl: "https://github.com/lessgo/lessgo",
			License:           "MIT",
			LicenseUrl:        "https://github.com/lessgo/lessgo/raw/master/doc/LICENSE",
		},
		Debug:       true,
		CrossDomain: false,
		MaxMemoryMB: 64, // 64MB
		Listen: Listen{
			Graceful:      false,
			Address:       "0.0.0.0:8080",
			ReadTimeout:   0,
			WriteTimeout:  0,
			EnableHTTPS:   false,
			HTTPSCertFile: "",
			HTTPSKeyFile:  "",
		},
		Session: SessionConfig{
			SessionOn:               false,
			SessionProvider:         "memory",
			SessionName:             "lessgosessionID",
			SessionGCMaxLifetime:    3600,
			SessionProviderConfig:   "",
			SessionCookieLifeTime:   0, //set cookie default is the browser life
			SessionAutoSetCookie:    true,
			SessionDomain:           "",
			EnableSidInHttpHeader:   false, //	enable store/get the sessionId into/from http headers
			SessionNameInHttpHeader: "Lessgosessionid",
			EnableSidInUrlQuery:     false, //	enable get the sessionId from Url Query params
		},

		FileCache: FileCacheConfig{
			CacheSecond:       600, // 600s
			SingleFileAllowMB: 64,  // 64MB
			MaxCapMB:          256, // 256MB
		},
		Log: LogConfig{
			Level:     logs.DEBUG,
			AsyncChan: 1000,
		},
	}
}

func (this *config) LoadMainConfig(fname string) (err error) {
	iniconf, err := confpkg.NewConfig("ini", fname)
	if err == nil {
		os.Remove(fname)
		ReadSingleConfig("system", Config, iniconf)
		ReadSingleConfig("filecache", &this.FileCache, iniconf)
		ReadSingleConfig("info", &this.Info, iniconf)
		ReadSingleConfig("listen", &this.Listen, iniconf)
		ReadSingleConfig("log", &this.Log, iniconf)
		ReadSingleConfig("session", &this.Session, iniconf)
	}
	os.MkdirAll(filepath.Dir(fname), 0777)
	f, err := os.Create(fname)
	if err != nil {
		return err
	}
	f.Close()
	iniconf, err = confpkg.NewConfig("ini", fname)
	if err != nil {
		return err
	}
	WriteSingleConfig("system", Config, iniconf)
	WriteSingleConfig("filecache", &this.FileCache, iniconf)
	WriteSingleConfig("info", &this.Info, iniconf)
	WriteSingleConfig("listen", &this.Listen, iniconf)
	WriteSingleConfig("log", &this.Log, iniconf)
	WriteSingleConfig("session", &this.Session, iniconf)

	return iniconf.SaveConfigFile(fname)
}

func ReadSingleConfig(section string, p interface{}, iniconf confpkg.Configer) {
	pt := reflect.TypeOf(p)
	if pt.Kind() != reflect.Ptr {
		return
	}
	pt = pt.Elem()
	if pt.Kind() != reflect.Struct {
		return
	}
	pv := reflect.ValueOf(p).Elem()

	for i := 0; i < pt.NumField(); i++ {
		pf := pv.Field(i)
		if !pf.CanSet() {
			continue
		}
		name := pt.Field(i).Name
		fullname := getfullname(section, name)
		switch pf.Kind() {
		case reflect.String:
			str := iniconf.DefaultString(fullname, pf.String())
			switch name {
			case "TableFix", "ColumnFix":
				pf.SetString(strings.ToLower(str))
			default:
				pf.SetString(str)
			}

		case reflect.Int, reflect.Int64:
			num := int64(iniconf.DefaultInt64(fullname, pf.Int()))
			switch fullname {
			case "system::maxmemorymb",
				"filecache::cachesecond", "filecache::singlefileallowmb", "filecache::maxcapmb",
				"listen::readtimeout", "listen::writetimeout",
				"session::sessiongcmaxlifetime", "session::sessioncookielifetime":
				if num > 0 {
					pf.SetInt(num)
				}
			case "log::asyncchan":
				if num >= 0 {
					pf.SetInt(num)
				}
			case "log::level":
				str := logLevelString(int(num))
				str2 := iniconf.DefaultString(fullname, str)
				num = int64(logLevelInt(str2))
				if num != -10 {
					pf.SetInt(num)
				}
			default:
				pf.SetInt(num)
			}

		case reflect.Bool:
			pf.SetBool(iniconf.DefaultBool(fullname, pf.Bool()))
		}
	}
}

func WriteSingleConfig(section string, p interface{}, iniconf confpkg.Configer) {
	pt := reflect.TypeOf(p)
	if pt.Kind() != reflect.Ptr {
		return
	}
	pt = pt.Elem()
	if pt.Kind() != reflect.Struct {
		return
	}
	pv := reflect.ValueOf(p).Elem()

	for i := 0; i < pt.NumField(); i++ {
		pf := pv.Field(i)
		if !pf.CanSet() {
			continue
		}
		fullname := getfullname(section, pt.Field(i).Name)
		switch pf.Kind() {
		case reflect.String, reflect.Int, reflect.Int64, reflect.Bool:
			switch fullname {
			case "log::level":
				iniconf.Set(fullname, logLevelString(int(pf.Int())))
			default:
				iniconf.Set(fullname, fmt.Sprint(pf.Interface()))
			}
		}
	}
}

// section name and key name case insensitive
func getfullname(section, name string) string {
	if section == "" {
		return strings.ToLower(name)
	}
	return strings.ToLower(section + "::" + name)
}

func logLevelInt(l string) int {
	switch strings.ToLower(l) {
	case "debug":
		return logs.DEBUG
	case "info":
		return logs.INFO
	case "warn":
		return logs.WARN
	case "error":
		return logs.ERROR
	case "fatal":
		return logs.FATAL
	case "off":
		return logs.OFF
	}
	return -10
}

func logLevelString(l int) string {
	switch l {
	case logs.DEBUG:
		return "debug"
	case logs.INFO:
		return "info"
	case logs.WARN:
		return "warn"
	case logs.ERROR:
		return "error"
	case logs.FATAL:
		return "fatal"
	case logs.OFF:
		return "off"
	}
	return "error"
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/url"

	"github.com/nahanni/go-ucl"
)

type SomaConfig struct {
	Environment string       `json:"environment"`
	ReadOnly    bool         `json:"readonly,string"`
	Database    SomaDbConfig `json:"database"`
	Daemon      SomaDaemon   `json:"daemon"`
}

type SomaDbConfig struct {
	Host    string `json:"host"`
	User    string `json:"user"`
	Name    string `json:"name"`
	Port    string `json:"port"`
	Pass    string `json:"password"`
	Timeout string `json:"timeout"`
	TlsMode string `json:"tlsmode"`
}

type SomaDaemon struct {
	url    *url.URL `json:"-"`
	Listen string   `json:"listen"`
	Port   string   `json:"port"`
	Tls    bool     `json:"tls,string"`
	Cert   string   `json:"cert-file"`
	Key    string   `json:"key-file"`
}

func (c *SomaConfig) readConfigFile(fname string) error {
	file, err := ioutil.ReadFile(fname)
	if err != nil {
		return err
	}

	log.Printf("Loading configuration from %s", fname)

	// UCL parses into map[string]interface{}
	fileBytes := bytes.NewBuffer([]byte(file))
	parser := ucl.NewParser(fileBytes)
	uclData, err := parser.Ucl()
	if err != nil {
		log.Fatal("UCL error: ", err)
	}

	// take detour via JSON to load UCL into struct
	uclJson, err := json.Marshal(uclData)
	if err != nil {
		log.Fatal(err)
	}
	json.Unmarshal([]byte(uclJson), &c)

	return nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Update configuration with auth section<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/url"

	"github.com/nahanni/go-ucl"
)

type SomaConfig struct {
	Environment string         `json:"environment"`
	ReadOnly    bool           `json:"readonly,string"`
	Database    SomaDbConfig   `json:"database"`
	Daemon      SomaDaemon     `json:"daemon"`
	Auth        SomaAuthConfig `json:"authentication"`
}

type SomaDbConfig struct {
	Host    string `json:"host"`
	User    string `json:"user"`
	Name    string `json:"name"`
	Port    string `json:"port"`
	Pass    string `json:"password"`
	Timeout string `json:"timeout"`
	TlsMode string `json:"tlsmode"`
}

type SomaDaemon struct {
	url    *url.URL `json:"-"`
	Listen string   `json:"listen"`
	Port   string   `json:"port"`
	Tls    bool     `json:"tls,string"`
	Cert   string   `json:"cert-file"`
	Key    string   `json:"key-file"`
}

type SomaAuthConfig struct {
	KexExpirySeconds   uint64 `json:"kex_expiry,string"`
	TokenExpirySeconds uint64 `json:"token_expiry,string"`
	// dd if=/dev/random bs=1M count=1 2>/dev/null | sha512
	TokenSeed string `json:"token_seed"`
	TokenKey  string `json:"token_key"`
}

func (c *SomaConfig) readConfigFile(fname string) error {
	file, err := ioutil.ReadFile(fname)
	if err != nil {
		return err
	}

	log.Printf("Loading configuration from %s", fname)

	// UCL parses into map[string]interface{}
	fileBytes := bytes.NewBuffer([]byte(file))
	parser := ucl.NewParser(fileBytes)
	uclData, err := parser.Ucl()
	if err != nil {
		log.Fatal("UCL error: ", err)
	}

	// take detour via JSON to load UCL into struct
	uclJson, err := json.Marshal(uclData)
	if err != nil {
		log.Fatal(err)
	}
	json.Unmarshal([]byte(uclJson), &c)

	return nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package config

import (
    "encoding/json"
    "strings"
    "strconv"
    "os"
	"io"
)

type Config map[string]interface{}

// GetString return string config value
func (conf Config) GetString(path string) string {

    result := conf.Get(path)
    if result == nil {
        return ""
    }

    // Если строка, то это результат
    switch val := result.(type) {
    case string: return val

    default:
        return ""
    }
}

// GetArray return array config value
func (conf Config) GetArray(path string) []interface{} {

    result := conf.Get(path)
    if result == nil {
        return []interface{}{}
    }

    switch val := result.(type) {
    case []interface{}: return val

    default:
        return []interface{}{}
    }
}

// GetBool return bool config value
func (conf Config) GetBool(path string) bool {

    result := conf.Get(path)
    if result == nil {
        return false
    }

    switch val := result.(type) {
    case bool: return val

    default:
        return false
    }
}

// GetInt return int64 config value. It may be in hex & oct variants
func (conf Config) GetInt(path string) int64 {

	result := conf.Get(path)
	if result == nil {
		return 0
	}

	switch val := result.(type) {
	case int: return int64(val)
	case int64: return val
	case json.Number:
		if res, err := strconv.ParseInt(string(val), 0, 64); err != nil {
			return 0
		} else {
			return res
		}

	default:
		return 0
	}
}

// GetFloat64 return float64 config value
func (conf Config) GetFloat64(path string) float64 {

    result := conf.Get(path)
    if result == nil {
        return 0
    }

    switch val := result.(type) {
    case float64: return val
    case int: return float64(val)
    case int64: return float64(val)
    case json.Number:
        if res, err := strconv.ParseFloat(string(val), 64); err != nil {
            return 0
        } else {
            return res
        }

    default:
        return 0
    }
}

// Get return config value by dotted path in json tree. Path should be like this "root.option.item"
func (conf Config) Get(path string) interface{} {
    items := strings.Split(path, ".")

    idx := 0
    value := map[string]interface{}(conf)

    // Перебор до предпоследнего элемента
    for idx < len(items) - 1 {
        tmp, ok := value[items[idx]]
        if !ok {
            return ""
        }

        value = tmp.(map[string]interface{})
        idx++
    }

    // Последний элемент
    return value[items[idx]]
}

// New create config from file
func New(filename string) Config {
    file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}

    return NewFromIO(file)
}

// NewFromIO create config from io.Reader
func NewFromIO(input io.Reader) Config {
	decoder := json.NewDecoder(input)
	decoder.UseNumber()

	res := make(Config)
	decoder.Decode(&res)

	if err := decoder.Decode(&res); err != nil {
		panic(err)
	}

	return res
}
<commit_msg>add: support context<commit_after>package config

import (
	"encoding/json"
	"strings"
	"strconv"
	"os"
	"io"
	"context"
)

type Config map[string]interface{}

// GetString return string config value
func (conf Config) GetString(path string) string {

	result := conf.Get(path)
	if result == nil {
		return ""
	}

	// Если строка, то это результат
	switch val := result.(type) {
	case string: return val

	default:
		return ""
	}
}

// GetArray return array config value
func (conf Config) GetArray(path string) []interface{} {

	result := conf.Get(path)
	if result == nil {
		return []interface{}{}
	}

	switch val := result.(type) {
	case []interface{}: return val

	default:
		return []interface{}{}
	}
}

// GetBool return bool config value
func (conf Config) GetBool(path string) bool {

	result := conf.Get(path)
	if result == nil {
		return false
	}

	switch val := result.(type) {
	case bool: return val

	default:
		return false
	}
}

// GetInt return int64 config value. It may be in hex & oct variants
func (conf Config) GetInt(path string) int64 {

	result := conf.Get(path)
	if result == nil {
		return 0
	}

	switch val := result.(type) {
	case int: return int64(val)
	case int64: return val
	case json.Number:
		if res, err := strconv.ParseInt(string(val), 0, 64); err != nil {
			return 0
		} else {
			return res
		}

	default:
		return 0
	}
}

// GetFloat64 return float64 config value
func (conf Config) GetFloat64(path string) float64 {

	result := conf.Get(path)
	if result == nil {
		return 0
	}

	switch val := result.(type) {
	case float64: return val
	case int: return float64(val)
	case int64: return float64(val)
	case json.Number:
		if res, err := strconv.ParseFloat(string(val), 64); err != nil {
			return 0
		} else {
			return res
		}

	default:
		return 0
	}
}

// Get return config value by dotted path in json tree. Path should be like this "root.option.item"
func (conf Config) Get(path string) interface{} {
	items := strings.Split(path, ".")

	idx := 0
	value := map[string]interface{}(conf)

	// Перебор до предпоследнего элемента
	for idx < len(items) - 1 {
		tmp, ok := value[items[idx]]
		if !ok {
			return ""
		}

		value = tmp.(map[string]interface{})
		idx++
	}

	// Последний элемент
	return value[items[idx]]
}

// New create config from file
func New(filename string) Config {
	file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}

	return NewFromIO(file)
}

// NewFromIO create config from io.Reader
func NewFromIO(input io.Reader) Config {
	decoder := json.NewDecoder(input)
	decoder.UseNumber()

	res := make(Config)
	if err := decoder.Decode(&res); err != nil {
		panic(err)
	}

	return res
}

type key int

const keyConfig key = iota

// NewContext create new context with config
func NewContext(ctx context.Context, filename string) context.Context {
	return context.WithValue(ctx, keyConfig, New(filename))
}

// FromContext return config from context
func FromContext(ctx context.Context) (Config, bool) {
	value, ok := ctx.Value(keyConfig).(Config)
	return value, ok
}<|endoftext|>


<commit_before>// Copyright 2013 config authors. 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.
package config

import (
	"fmt"
	"github.com/howeyc/fsnotify"
	"io"
	"io/ioutil"
	"launchpad.net/goyaml"
	"os"
	"strconv"
	"strings"
	"sync"
)

var (
	configs map[interface{}]interface{}
	mut     sync.RWMutex
)

func readConfigBytes(data []byte, out interface{}) error {
	return goyaml.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 {
	mut.Lock()
	defer mut.Unlock()
	return readConfigBytes(data, &configs)
}

func readConfigFile(filePath string, out interface{}) error {
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}
	return readConfigBytes(data, out)
}

// 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 {
	return readConfigFile(filePath, &configs)
}

// 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() {
					var tmp map[interface{}]interface{}
					if readConfigFile(filePath, &tmp) == nil {
						mut.Lock()
						configs = tmp
						mut.Unlock()
					}
				}
			case <-w.Error: // just ignore errors
			}
		}
	}()
	return nil
}

// 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 {
	mut.RLock()
	b, err := goyaml.Marshal(configs)
	mut.RUnlock()
	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 eror if the key is undefined.
//
// The key is composed by 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.
func Get(key string) (interface{}, error) {
	keys := strings.Split(key, ":")
	mut.RLock()
	conf, ok := configs[keys[0]]
	mut.RUnlock()
	if !ok {
		return nil, fmt.Errorf("key %q not found", key)
	}
	for _, k := range keys[1:] {
		conf, ok = conf.(map[interface{}]interface{})[k]
		if !ok {
			return nil, fmt.Errorf("key %q not found", key)
		}
	}
	return conf, nil
}

// GetString works like Get, but doing a string type assertion before return
// 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 doing a int type assertion before return
// 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
	}
	return 0, &invalidValue{key, "int"}
}

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"}
}

// 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 item.(type) {
			case int:
				result[i] = strconv.Itoa(item.(int))
			case bool:
				result[i] = strconv.FormatBool(item.(bool))
			case float64:
				result[i] = strconv.FormatFloat(item.(float64), 'f', -1, 64)
			case string:
				result[i] = item.(string)
			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,
		}
	}
	mut.Lock()
	configs = mergeMaps(configs, last)
	mut.Unlock()
}

// Unset removes a key from the configuration map. It returns 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
	mut.Lock()
	defer mut.Unlock()
	m := configs
	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)
	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>config: expand lock on Get<commit_after>// Copyright 2013 config authors. 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.
package config

import (
	"fmt"
	"github.com/howeyc/fsnotify"
	"io"
	"io/ioutil"
	"launchpad.net/goyaml"
	"os"
	"strconv"
	"strings"
	"sync"
)

var (
	configs map[interface{}]interface{}
	mut     sync.RWMutex
)

func readConfigBytes(data []byte, out interface{}) error {
	return goyaml.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 {
	mut.Lock()
	defer mut.Unlock()
	return readConfigBytes(data, &configs)
}

func readConfigFile(filePath string, out interface{}) error {
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}
	return readConfigBytes(data, out)
}

// 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 {
	return readConfigFile(filePath, &configs)
}

// 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() {
					var tmp map[interface{}]interface{}
					if readConfigFile(filePath, &tmp) == nil {
						mut.Lock()
						configs = tmp
						mut.Unlock()
					}
				}
			case <-w.Error: // just ignore errors
			}
		}
	}()
	return nil
}

// 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 {
	mut.RLock()
	b, err := goyaml.Marshal(configs)
	mut.RUnlock()
	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 eror if the key is undefined.
//
// The key is composed by 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.
func Get(key string) (interface{}, error) {
	keys := strings.Split(key, ":")
	mut.RLock()
	defer mut.RUnlock()
	conf, ok := configs[keys[0]]
	if !ok {
		return nil, fmt.Errorf("key %q not found", key)
	}
	for _, k := range keys[1:] {
		conf, ok = conf.(map[interface{}]interface{})[k]
		if !ok {
			return nil, fmt.Errorf("key %q not found", key)
		}
	}
	return conf, nil
}

// GetString works like Get, but doing a string type assertion before return
// 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 doing a int type assertion before return
// 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
	}
	return 0, &invalidValue{key, "int"}
}

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"}
}

// 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 item.(type) {
			case int:
				result[i] = strconv.Itoa(item.(int))
			case bool:
				result[i] = strconv.FormatBool(item.(bool))
			case float64:
				result[i] = strconv.FormatFloat(item.(float64), 'f', -1, 64)
			case string:
				result[i] = item.(string)
			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,
		}
	}
	mut.Lock()
	configs = mergeMaps(configs, last)
	mut.Unlock()
}

// Unset removes a key from the configuration map. It returns 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
	mut.Lock()
	defer mut.Unlock()
	m := configs
	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)
	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 (
	"fmt"
	"os"
	"path/filepath"
	"strconv"

	// keep 3rd-party imports separate from stdlib with an empty line
	flag "github.com/ogier/pflag"
	"github.com/vaughan0/go-ini"
)

// boot2docker config.
var B2D struct {
	// NOTE: separate sections with blank lines so gofmt doesn't change
	// indentation all the time.

	// basic config
	VBM      string // VirtualBox management utility
	SSH      string // SSH client executable
	VM       string // virtual machine name
	Dir      string // boot2docker directory
	ISO      string // boot2docker ISO image path
	Disk     string // VM disk image path
	DiskSize uint   // VM disk image size (MB)
	Memory   uint   // VM memory size (MB)

	// NAT network: port forwarding
	SSHPort    uint16 // host SSH port (forward to port 22 in VM)
	DockerPort uint16 // host Docker port (forward to port 4243 in VM)

	// host-only network
	HostIP         string
	DHCPIP         string
	NetworkMask    string
	LowerIPAddress string
	UpperIPAddress string
	DHCPEnabled    string
}

func getCfgDir(name string) (string, error) {
	if b2dDir := os.Getenv("BOOT2DOCKER_DIR"); b2dDir != "" {
		return b2dDir, nil
	}

	// *nix
	if home := os.Getenv("HOME"); home != "" {
		return filepath.Join(home, name), nil
	}

	// Windows
	for _, env := range []string{
		"APPDATA",
		"LOCALAPPDATA",
		"USERPROFILE",
	} {
		if val := os.Getenv(env); val != "" {
			return filepath.Join(val, "boot2docker"), nil
		}
	}
	// Fallback to current working directory as a last resort
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	return filepath.Join(cwd, name), nil
}

// Read configuration from both profile and flags. Flags override profile.
func config() (err error) {

	if B2D.Dir, err = getCfgDir(".boot2docker"); err != nil {
		return fmt.Errorf("failed to get current directory: %s", err)
	}

	filename := os.Getenv("BOOT2DOCKER_PROFILE")
	if filename == "" {
		filename = filepath.Join(B2D.Dir, "profile")
	}
	profile, err := getProfile(filename)
	if err != nil && !os.IsNotExist(err) { // undefined/empty profile works
		return err
	}

	B2D.VBM = profile.Get("", "vbm", "VBoxManage")
	B2D.SSH = profile.Get("", "ssh", "ssh")
	B2D.VM = profile.Get("", "vm", "boot2docker-vm")
	B2D.ISO = profile.Get("", "iso", filepath.Join(B2D.Dir, "boot2docker.iso"))
	B2D.Disk = profile.Get("", "disk", filepath.Join(B2D.Dir, "boot2docker.vmdk"))

	if diskSize, err := strconv.ParseUint(profile.Get("", "disksize", "20000"), 10, 32); err != nil {
		return fmt.Errorf("invalid disk image size: %s", err)
	} else {
		B2D.DiskSize = uint(diskSize)
	}

	if memory, err := strconv.ParseUint(profile.Get("", "memory", "1024"), 10, 32); err != nil {
		return fmt.Errorf("invalid memory size: %s", err)
	} else {
		B2D.Memory = uint(memory)
	}

	if sshPort, err := strconv.ParseUint(profile.Get("", "sshport", "2022"), 10, 16); err != nil {
		return fmt.Errorf("invalid SSH port: %s", err)
	} else {
		B2D.SSHPort = uint16(sshPort)
	}

	if dockerPort, err := strconv.ParseUint(profile.Get("", "dockerport", "4243"), 10, 16); err != nil {
		return fmt.Errorf("invalid DockerPort: %s", err)
	} else {
		B2D.DockerPort = uint16(dockerPort)
	}

	// Host only networking settings
	B2D.HostIP = profile.Get("", "hostiP", "192.168.59.3")
	B2D.DHCPIP = profile.Get("", "dhcpip", "192.168.59.99")
	B2D.NetworkMask = profile.Get("", "netmask", "255.255.255.0")
	B2D.LowerIPAddress = profile.Get("", "lowerip", "192.168.59.103")
	B2D.UpperIPAddress = profile.Get("", "upperip", "192.168.59.254")
	B2D.DHCPEnabled = profile.Get("", "dhcp", "Yes")

	// Commandline flags override profile settings.
	flag.StringVar(&B2D.VBM, "vbm", B2D.VBM, "Path to VirtualBox management utility")
	flag.StringVar(&B2D.SSH, "ssh", B2D.SSH, "Path to SSH client utility")
	flag.StringVarP(&B2D.Dir, "dir", "d", B2D.Dir, "boot2docker config directory")
	flag.StringVar(&B2D.ISO, "iso", B2D.ISO, "Path to boot2docker ISO image")
	flag.StringVar(&B2D.Disk, "disk", B2D.Disk, "Path to boot2docker disk image")
	flag.UintVarP(&B2D.DiskSize, "disksize", "s", B2D.DiskSize, "boot2docker disk image size (in MB)")
	flag.UintVarP(&B2D.Memory, "memory", "m", B2D.Memory, "Virtual machine memory size (in MB)")
	flag.Var(newUint16Value(B2D.SSHPort, &B2D.SSHPort), "sshport", "Host SSH port (forward to port 22 in VM)")
	flag.Var(newUint16Value(B2D.DockerPort, &B2D.DockerPort), "dockerport", "Host Docker port (forward to port 4243 in VM)")
	flag.StringVar(&B2D.HostIP, "hostip", B2D.HostIP, "VirtualBox host-only network IP address")
	flag.StringVar(&B2D.NetworkMask, "netmask", B2D.NetworkMask, "VirtualBox host-only network mask")
	flag.StringVar(&B2D.DHCPEnabled, "dhcp", B2D.DHCPEnabled, "Enable VirtualBox host-only network DHCP")
	flag.StringVar(&B2D.DHCPIP, "dhcpip", B2D.DHCPIP, "VirtualBox host-only network DHCP server address")
	flag.StringVar(&B2D.LowerIPAddress, "lowerip", B2D.LowerIPAddress, "VirtualBox host-only network DHCP lower bound")
	flag.StringVar(&B2D.UpperIPAddress, "upperip", B2D.UpperIPAddress, "VirtualBox host-only network DHCP upper bound")

	flag.Parse()

	// Name of VM is the second argument.
	if vm := flag.Arg(1); vm != "" {
		B2D.VM = vm
	}
	return
}

// boot2docker configuration profile.
type Profile struct {
	ini.File
}

func getProfile(filename string) (*Profile, error) {
	f, err := ini.LoadFile(filename)
	return &Profile{f}, err
}

func (f *Profile) Get(section, key, fallback string) string {
	if val, ok := f.File.Get(section, key); ok {
		return os.ExpandEnv(val)
	}
	return fallback
}

// The missing flag.Uint16Var value type.
type uint16Value uint16

func newUint16Value(val uint16, p *uint16) *uint16Value {
	*p = val
	return (*uint16Value)(p)
}
func (i *uint16Value) String() string { return fmt.Sprintf("%d", *i) }
func (i *uint16Value) Set(s string) error {
	v, err := strconv.ParseUint(s, 10, 16)
	*i = uint16Value(v)
	return err
}
func (i *uint16Value) Get() interface{} {
	return uint16(*i)
}
<commit_msg>Added back missing host-only flags<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"

	// keep 3rd-party imports separate from stdlib with an empty line
	flag "github.com/ogier/pflag"
	"github.com/vaughan0/go-ini"
)

// boot2docker config.
var B2D struct {
	// NOTE: separate sections with blank lines so gofmt doesn't change
	// indentation all the time.

	// basic config
	VBM      string // VirtualBox management utility
	SSH      string // SSH client executable
	VM       string // virtual machine name
	Dir      string // boot2docker directory
	ISO      string // boot2docker ISO image path
	Disk     string // VM disk image path
	DiskSize uint   // VM disk image size (MB)
	Memory   uint   // VM memory size (MB)

	// NAT network: port forwarding
	SSHPort    uint16 // host SSH port (forward to port 22 in VM)
	DockerPort uint16 // host Docker port (forward to port 4243 in VM)

	// host-only network
	HostIP         string
	DHCPIP         string
	NetworkMask    string
	LowerIPAddress string
	UpperIPAddress string
	DHCPEnabled    string
}

func getCfgDir(name string) (string, error) {
	if b2dDir := os.Getenv("BOOT2DOCKER_DIR"); b2dDir != "" {
		return b2dDir, nil
	}

	// *nix
	if home := os.Getenv("HOME"); home != "" {
		return filepath.Join(home, name), nil
	}

	// Windows
	for _, env := range []string{
		"APPDATA",
		"LOCALAPPDATA",
		"USERPROFILE",
	} {
		if val := os.Getenv(env); val != "" {
			return filepath.Join(val, "boot2docker"), nil
		}
	}
	// Fallback to current working directory as a last resort
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	return filepath.Join(cwd, name), nil
}

// Read configuration from both profile and flags. Flags override profile.
func config() (err error) {

	if B2D.Dir, err = getCfgDir(".boot2docker"); err != nil {
		return fmt.Errorf("failed to get current directory: %s", err)
	}

	filename := os.Getenv("BOOT2DOCKER_PROFILE")
	if filename == "" {
		filename = filepath.Join(B2D.Dir, "profile")
	}
	profile, err := getProfile(filename)
	if err != nil && !os.IsNotExist(err) { // undefined/empty profile works
		return err
	}

	B2D.VBM = profile.Get("", "vbm", "VBoxManage")
	B2D.SSH = profile.Get("", "ssh", "ssh")
	B2D.VM = profile.Get("", "vm", "boot2docker-vm")
	B2D.ISO = profile.Get("", "iso", filepath.Join(B2D.Dir, "boot2docker.iso"))
	B2D.Disk = profile.Get("", "disk", filepath.Join(B2D.Dir, "boot2docker.vmdk"))

	if diskSize, err := strconv.ParseUint(profile.Get("", "disksize", "20000"), 10, 32); err != nil {
		return fmt.Errorf("invalid disk image size: %s", err)
	} else {
		B2D.DiskSize = uint(diskSize)
	}

	if memory, err := strconv.ParseUint(profile.Get("", "memory", "1024"), 10, 32); err != nil {
		return fmt.Errorf("invalid memory size: %s", err)
	} else {
		B2D.Memory = uint(memory)
	}

	if sshPort, err := strconv.ParseUint(profile.Get("", "sshport", "2022"), 10, 16); err != nil {
		return fmt.Errorf("invalid SSH port: %s", err)
	} else {
		B2D.SSHPort = uint16(sshPort)
	}

	if dockerPort, err := strconv.ParseUint(profile.Get("", "dockerport", "4243"), 10, 16); err != nil {
		return fmt.Errorf("invalid DockerPort: %s", err)
	} else {
		B2D.DockerPort = uint16(dockerPort)
	}

	// Host only networking settings
	B2D.HostIP = profile.Get("", "hostiP", "192.168.59.3")
	B2D.DHCPIP = profile.Get("", "dhcpip", "192.168.59.99")
	B2D.NetworkMask = profile.Get("", "netmask", "255.255.255.0")
	B2D.LowerIPAddress = profile.Get("", "lowerip", "192.168.59.103")
	B2D.UpperIPAddress = profile.Get("", "upperip", "192.168.59.254")
	B2D.DHCPEnabled = profile.Get("", "dhcp", "Yes")

	// Commandline flags override profile settings.
	flag.StringVar(&B2D.VBM, "vbm", B2D.VBM, "Path to VirtualBox management utility")
	flag.StringVar(&B2D.SSH, "ssh", B2D.SSH, "Path to SSH client utility")
	flag.StringVarP(&B2D.Dir, "dir", "d", B2D.Dir, "boot2docker config directory")
	flag.StringVar(&B2D.ISO, "iso", B2D.ISO, "Path to boot2docker ISO image")
	flag.StringVar(&B2D.Disk, "disk", B2D.Disk, "Path to boot2docker disk image")
	flag.UintVarP(&B2D.DiskSize, "disksize", "s", B2D.DiskSize, "boot2docker disk image size (in MB)")
	flag.UintVarP(&B2D.Memory, "memory", "m", B2D.Memory, "Virtual machine memory size (in MB)")
	flag.Var(newUint16Value(B2D.SSHPort, &B2D.SSHPort), "sshport", "Host SSH port (forward to port 22 in VM)")
	flag.Var(newUint16Value(B2D.DockerPort, &B2D.DockerPort), "dockerport", "Host Docker port (forward to port 4243 in VM)")
	flag.StringVar(&B2D.HostIP, "hostip", B2D.HostIP, "VirtualBox host-only network IP address")
	flag.StringVar(&B2D.DHCPIP, "dhcpip", B2D.DHCPIP, "VirtualBox host-only network DHCP address")
	flag.StringVar(&B2D.NetworkMask, "networkmask", B2D.NetworkMask, "VirtualBox host-only network mask")
	flag.StringVar(&B2D.LowerIPAddress, "lowerip", B2D.LowerIPAddress, "VirtualBox host-only network DHCP lower bound")
	flag.StringVar(&B2D.UpperIPAddress, "uppwerip", B2D.UpperIPAddress, "VirtualBox host-only network DHCP upper bound")
	flag.StringVar(&B2D.DHCPEnabled, "dhcpenabled", B2D.DHCPEnabled, "Enable VirtualBox host-only network DHCP")

	flag.Parse()

	// Name of VM is the second argument.
	if vm := flag.Arg(1); vm != "" {
		B2D.VM = vm
	}
	return
}

// boot2docker configuration profile.
type Profile struct {
	ini.File
}

func getProfile(filename string) (*Profile, error) {
	f, err := ini.LoadFile(filename)
	return &Profile{f}, err
}

func (f *Profile) Get(section, key, fallback string) string {
	if val, ok := f.File.Get(section, key); ok {
		return os.ExpandEnv(val)
	}
	return fallback
}

// The missing flag.Uint16Var value type.
type uint16Value uint16

func newUint16Value(val uint16, p *uint16) *uint16Value {
	*p = val
	return (*uint16Value)(p)
}
func (i *uint16Value) String() string { return fmt.Sprintf("%d", *i) }
func (i *uint16Value) Set(s string) error {
	v, err := strconv.ParseUint(s, 10, 16)
	*i = uint16Value(v)
	return err
}
func (i *uint16Value) Get() interface{} {
	return uint16(*i)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"sort"
	"strings"

	"golang.org/x/exp/inotify"
)

const (
	watchEvents = inotify.IN_ATTRIB | inotify.IN_CLOSE_WRITE | inotify.IN_CREATE | inotify.IN_DELETE | inotify.IN_MOVED_FROM | inotify.IN_MOVED_TO
)

var (
	nameRE    = regexp.MustCompile("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$")
	featureRE = regexp.MustCompile("^[a-zA-Z0-9-_]+$")
)

func watchConfig(dir string, re *regexp.Regexp, log *Log, handler func(filenames []string)) (err error) {
	if dir == "" {
		return
	}

	w, err := inotify.NewWatcher()
	if err != nil {
		return
	}

	if err = w.AddWatch(dir, inotify.IN_ONLYDIR|watchEvents); err != nil {
		w.Close()
		return
	}

	scanConfig(dir, re, handler, log)

	go scanConfigLoop(dir, re, handler, w, log)

	return
}

func scanConfigLoop(dir string, re *regexp.Regexp, handler func(filenames []string), w *inotify.Watcher, log *Log) {
	for {
		select {
		case <-w.Event:
			scanConfig(dir, re, handler, log)

		case err := <-w.Error:
			log.Error(err)
		}
	}
}

func scanConfig(dir string, re *regexp.Regexp, handler func(filenames []string), log *Log) {
	infos, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Error(err)
		return
	}

	var filenames []string

	for _, info := range infos {
		if !info.IsDir() && re.MatchString(info.Name()) {
			filenames = append(filenames, info.Name())
		}
	}

	handler(filenames)
}

func initNameConfig(local *LocalNode, arg, dir string, notify chan<- struct{}, log *Log) error {
	argNames := strings.Fields(arg)

	return watchConfig(dir, nameRE, log, func(filenames []string) {
		var names []string
		copy(names, argNames)

		for _, filename := range filenames {
			name := strings.ToLower(filename)
			found := false

			for _, x := range names {
				if x == name {
					found = true
					break
				}
			}

			if !found {
				names = append(names, name)
			}
		}

		if local.updateNames(names) {
			select {
			case notify <- struct{}{}:
			default:
			}

			log.Infof("local names: %s", strings.Join(names, " "))
		}
	})
}

func initFeatureConfig(local *LocalNode, arg, dir string, notify chan<- struct{}, log *Log) (err error) {
	var argFeatures map[string]*json.RawMessage

	if arg != "" {
		if err = json.Unmarshal([]byte(arg), &argFeatures); err != nil {
			return
		}
	} else {
		argFeatures = make(map[string]*json.RawMessage)
	}

	return watchConfig(dir, featureRE, log, func(filenames []string) {
		features := make(map[string]*json.RawMessage)

		for name, value := range argFeatures {
			features[name] = value
		}

		for _, name := range filenames {
			path := filepath.Join(dir, name)

			data, err := ioutil.ReadFile(path)
			if err != nil {
				log.Error(err)
				continue
			}

			if len(bytes.TrimSpace(data)) == 0 {
				delete(features, name)
				continue
			}

			var value *json.RawMessage

			if err = json.Unmarshal(data, &value); err != nil {
				log.Errorf("%s: %s", path, err)
				continue
			}

			features[name] = value
		}

		if local.updateFeatures(features) {
			select {
			case notify <- struct{}{}:
			default:
			}

			var names []string

			for name := range features {
				names = append(names, name)
			}

			sort.Strings(names)

			log.Infof("local features: %s", strings.Join(names, " "))
		}
	})
}
<commit_msg>actually use names specified on command-line<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"sort"
	"strings"

	"golang.org/x/exp/inotify"
)

const (
	watchEvents = inotify.IN_ATTRIB | inotify.IN_CLOSE_WRITE | inotify.IN_CREATE | inotify.IN_DELETE | inotify.IN_MOVED_FROM | inotify.IN_MOVED_TO
)

var (
	nameRE    = regexp.MustCompile("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$")
	featureRE = regexp.MustCompile("^[a-zA-Z0-9-_]+$")
)

func watchConfig(dir string, re *regexp.Regexp, log *Log, handler func(filenames []string)) (err error) {
	if dir == "" {
		return
	}

	w, err := inotify.NewWatcher()
	if err != nil {
		return
	}

	if err = w.AddWatch(dir, inotify.IN_ONLYDIR|watchEvents); err != nil {
		w.Close()
		return
	}

	scanConfig(dir, re, handler, log)

	go scanConfigLoop(dir, re, handler, w, log)

	return
}

func scanConfigLoop(dir string, re *regexp.Regexp, handler func(filenames []string), w *inotify.Watcher, log *Log) {
	for {
		select {
		case <-w.Event:
			scanConfig(dir, re, handler, log)

		case err := <-w.Error:
			log.Error(err)
		}
	}
}

func scanConfig(dir string, re *regexp.Regexp, handler func(filenames []string), log *Log) {
	infos, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Error(err)
		return
	}

	var filenames []string

	for _, info := range infos {
		if !info.IsDir() && re.MatchString(info.Name()) {
			filenames = append(filenames, info.Name())
		}
	}

	handler(filenames)
}

func initNameConfig(local *LocalNode, arg, dir string, notify chan<- struct{}, log *Log) error {
	argNames := strings.Fields(arg)

	return watchConfig(dir, nameRE, log, func(filenames []string) {
		names := make([]string, len(argNames))
		copy(names, argNames)

		for _, filename := range filenames {
			name := strings.ToLower(filename)
			found := false

			for _, x := range names {
				if x == name {
					found = true
					break
				}
			}

			if !found {
				names = append(names, name)
			}
		}

		if local.updateNames(names) {
			select {
			case notify <- struct{}{}:
			default:
			}

			log.Infof("local names: %s", strings.Join(names, " "))
		}
	})
}

func initFeatureConfig(local *LocalNode, arg, dir string, notify chan<- struct{}, log *Log) (err error) {
	var argFeatures map[string]*json.RawMessage

	if arg != "" {
		if err = json.Unmarshal([]byte(arg), &argFeatures); err != nil {
			return
		}
	} else {
		argFeatures = make(map[string]*json.RawMessage)
	}

	return watchConfig(dir, featureRE, log, func(filenames []string) {
		features := make(map[string]*json.RawMessage)

		for name, value := range argFeatures {
			features[name] = value
		}

		for _, name := range filenames {
			path := filepath.Join(dir, name)

			data, err := ioutil.ReadFile(path)
			if err != nil {
				log.Error(err)
				continue
			}

			if len(bytes.TrimSpace(data)) == 0 {
				delete(features, name)
				continue
			}

			var value *json.RawMessage

			if err = json.Unmarshal(data, &value); err != nil {
				log.Errorf("%s: %s", path, err)
				continue
			}

			features[name] = value
		}

		if local.updateFeatures(features) {
			select {
			case notify <- struct{}{}:
			default:
			}

			var names []string

			for name := range features {
				names = append(names, name)
			}

			sort.Strings(names)

			log.Infof("local features: %s", strings.Join(names, " "))
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"runtime"
	"text/template"
	"time"
)

// Config for gometalinter. This can be loaded from a JSON file with --config.
type Config struct { // nolint: aligncheck
	// A map of linter name to "<command>:<pattern>".
	//
	// <command> should always include {path} as the target directory to execute. Globs in <command>
	// are expanded by gometalinter (not by the shell).
	Linters map[string]string

	// The set of linters that should be enabled.
	Enable  []string
	Disable []string

	// A map of linter name to message that is displayed. This is useful when linters display text
	// that is useful only in isolation, such as errcheck which just reports the construct.
	MessageOverride map[string]string
	Severity        map[string]string
	VendoredLinters bool
	Format          string
	Fast            bool
	Install         bool
	Update          bool
	Force           bool
	DownloadOnly    bool
	Debug           bool
	Concurrency     int
	Exclude         []string
	Include         []string
	Skip            []string
	Vendor          bool
	Cyclo           int
	LineLength      int
	MinConfidence   float64
	MinOccurrences  int
	MinConstLength  int
	DuplThreshold   int
	Sort            []string
	Test            bool
	Deadline        jsonDuration
	Errors          bool
	JSON            bool
	Checkstyle      bool
	EnableGC        bool
	Aggregate       bool
	EnableAll       bool
}

type jsonDuration time.Duration

func (td *jsonDuration) UnmarshalJSON(raw []byte) error {
	var durationAsString string
	if err := json.Unmarshal(raw, &durationAsString); err != nil {
		return err
	}
	duration, err := time.ParseDuration(durationAsString)
	*td = jsonDuration(duration)
	return err
}

// Duration returns the value as a time.Duration
func (td *jsonDuration) Duration() time.Duration {
	return time.Duration(*td)
}

// Configuration defaults.
var (
	vetRe = `^(?:vet:.*?\.go:\s+(?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*))|(?:(?P<path>.*?\.go):(?P<line>\d+):\s*(?P<message>.*))$`

	// TODO: should be a field on Config struct
	formatTemplate = &template.Template{}
	installMap     = map[string]string{
		"aligncheck":  "github.com/opennota/check/cmd/aligncheck",
		"deadcode":    "github.com/tsenart/deadcode",
		"dupl":        "github.com/mibk/dupl",
		"errcheck":    "github.com/kisielk/errcheck",
		"gas":         "github.com/GoASTScanner/gas",
		"goconst":     "github.com/jgautheron/goconst/cmd/goconst",
		"gocyclo":     "github.com/alecthomas/gocyclo",
		"goimports":   "golang.org/x/tools/cmd/goimports",
		"golint":      "github.com/golang/lint/golint",
		"gosimple":    "honnef.co/go/tools/cmd/gosimple",
		"gotype":      "golang.org/x/tools/cmd/gotype",
		"ineffassign": "github.com/gordonklaus/ineffassign",
		"interfacer":  "github.com/mvdan/interfacer/cmd/interfacer",
		"lll":         "github.com/walle/lll/cmd/lll",
		"megacheck":   "honnef.co/go/tools/cmd/megacheck",
		"misspell":    "github.com/client9/misspell/cmd/misspell",
		"safesql":     "github.com/stripe/safesql",
		"staticcheck": "honnef.co/go/tools/cmd/staticcheck",
		"structcheck": "github.com/opennota/check/cmd/structcheck",
		"unconvert":   "github.com/mdempsky/unconvert",
		"unparam":     "github.com/mvdan/unparam",
		"unused":      "honnef.co/go/tools/cmd/unused",
		"varcheck":    "github.com/opennota/check/cmd/varcheck",
	}
	slowLinters = []string{"structcheck", "varcheck", "errcheck", "aligncheck", "testify", "test", "interfacer", "unconvert", "deadcode", "safesql", "staticcheck", "unparam", "unused", "gosimple", "megacheck"}
	sortKeys    = []string{"none", "path", "line", "column", "severity", "message", "linter"}

	linterTakesFiles = newStringSet("dupl", "gofmt", "goimports", "lll", "misspell")

	linterTakesFilesGroupedByPackage = newStringSet("vet", "vetshadow")

	linterTakesPackagePaths = newStringSet(
		"errcheck",
		"aligncheck",
		"errcheck",
		"gosimple",
		"interfacer",
		"megacheck",
		"safesql",
		"staticcheck",
		"structcheck",
		"test",
		"testify",
		"unconvert",
		"unparam",
		"unused",
		"varcheck",
	)

	// Linter definitions.
	linterDefinitions = map[string]string{
		"aligncheck":  `aligncheck:^(?:[^:]+: )?(?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.+)$`,
		"deadcode":    `deadcode:^deadcode: (?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)$`,
		"dupl":        `dupl -plumbing -threshold {duplthreshold}:^(?P<path>.*?\.go):(?P<line>\d+)-\d+:\s*(?P<message>.*)$`,
		"errcheck":    `errcheck -abspath:PATH:LINE:COL:MESSAGE`,
		"gas":         `gas -fmt=csv:^(?P<path>.*?\.go),(?P<line>\d+),(?P<message>[^,]+,[^,]+,[^,]+)`,
		"goconst":     `goconst -min-occurrences {min_occurrences} -min-length {min_const_length}:PATH:LINE:COL:MESSAGE`,
		"gocyclo":     `gocyclo -over {mincyclo}:^(?P<cyclo>\d+)\s+\S+\s(?P<function>\S+)\s+(?P<path>.*?\.go):(?P<line>\d+):(\d+)$`,
		"gofmt":       `gofmt -l -s:^(?P<path>.*?\.go)$`,
		"goimports":   `goimports -l:^(?P<path>.*?\.go)$`,
		"golint":      "golint -min_confidence {min_confidence}:PATH:LINE:COL:MESSAGE",
		"gosimple":    "gosimple:PATH:LINE:COL:MESSAGE",
		"gotype":      "gotype -e {tests=-a}:PATH:LINE:COL:MESSAGE",
		"ineffassign": `ineffassign -n:PATH:LINE:COL:MESSAGE`,
		"interfacer":  `interfacer:PATH:LINE:COL:MESSAGE`,
		"lll":         `lll -g -l {maxlinelength}:PATH:LINE:MESSAGE`,
		"megacheck":   "megacheck:PATH:LINE:COL:MESSAGE",
		"misspell":    "misspell -j 1:PATH:LINE:COL:MESSAGE",
		"safesql":     `safesql:^- (?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+)$`,
		"staticcheck": "staticcheck:PATH:LINE:COL:MESSAGE",
		"structcheck": `structcheck {tests=-t}:^(?:[^:]+: )?(?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.+)$`,
		"test":        `go test:^--- FAIL: .*$\s+(?P<path>.*?\.go):(?P<line>\d+): (?P<message>.*)$`,
		"testify":     `go test:Location:\s+(?P<path>.*?\.go):(?P<line>\d+)$\s+Error:\s+(?P<message>[^\n]+)`,
		"unconvert":   "unconvert:PATH:LINE:COL:MESSAGE",
		"unparam":     `unparam:PATH:LINE:COL:MESSAGE`,
		"unused":      `unused:PATH:LINE:COL:MESSAGE`,
		"varcheck":    `varcheck:^(?:[^:]+: )?(?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)$`,
		"vet":         `go tool vet:` + vetRe,
		"vetshadow":   `go tool vet --shadow:` + vetRe,
	}

	config = &Config{
		Format: "{{.Path}}:{{.Line}}:{{if .Col}}{{.Col}}{{end}}:{{.Severity}}: {{.Message}} ({{.Linter}})",

		Severity: map[string]string{
			"gotype":  "error",
			"test":    "error",
			"testify": "error",
			"vet":     "error",
		},
		MessageOverride: map[string]string{
			"errcheck":    "error return value not checked ({message})",
			"gocyclo":     "cyclomatic complexity {cyclo} of function {function}() is high (> {mincyclo})",
			"gofmt":       "file is not gofmted with -s",
			"goimports":   "file is not goimported",
			"safesql":     "potentially unsafe SQL statement",
			"structcheck": "unused struct field {message}",
			"unparam":     "parameter {message}",
			"varcheck":    "unused variable or constant {message}",
		},
		Enable: []string{
			"aligncheck",
			"deadcode",
			"errcheck",
			"gas",
			"goconst",
			"gocyclo",
			"golint",
			"gotype",
			"ineffassign",
			"interfacer",
			"megacheck",
			"structcheck",
			"unconvert",
			"varcheck",
			"vet",
			"vetshadow",
		},
		VendoredLinters: true,
		Concurrency:     runtime.NumCPU(),
		Cyclo:           10,
		LineLength:      80,
		MinConfidence:   0.8,
		MinOccurrences:  3,
		MinConstLength:  3,
		DuplThreshold:   50,
		Sort:            []string{"none"},
		Deadline:        jsonDuration(time.Second * 30),
	}
)
<commit_msg>fixup! gotype uses the `-t` flag to enable linting of test files<commit_after>package main

import (
	"encoding/json"
	"runtime"
	"text/template"
	"time"
)

// Config for gometalinter. This can be loaded from a JSON file with --config.
type Config struct { // nolint: aligncheck
	// A map of linter name to "<command>:<pattern>".
	//
	// <command> should always include {path} as the target directory to execute. Globs in <command>
	// are expanded by gometalinter (not by the shell).
	Linters map[string]string

	// The set of linters that should be enabled.
	Enable  []string
	Disable []string

	// A map of linter name to message that is displayed. This is useful when linters display text
	// that is useful only in isolation, such as errcheck which just reports the construct.
	MessageOverride map[string]string
	Severity        map[string]string
	VendoredLinters bool
	Format          string
	Fast            bool
	Install         bool
	Update          bool
	Force           bool
	DownloadOnly    bool
	Debug           bool
	Concurrency     int
	Exclude         []string
	Include         []string
	Skip            []string
	Vendor          bool
	Cyclo           int
	LineLength      int
	MinConfidence   float64
	MinOccurrences  int
	MinConstLength  int
	DuplThreshold   int
	Sort            []string
	Test            bool
	Deadline        jsonDuration
	Errors          bool
	JSON            bool
	Checkstyle      bool
	EnableGC        bool
	Aggregate       bool
	EnableAll       bool
}

type jsonDuration time.Duration

func (td *jsonDuration) UnmarshalJSON(raw []byte) error {
	var durationAsString string
	if err := json.Unmarshal(raw, &durationAsString); err != nil {
		return err
	}
	duration, err := time.ParseDuration(durationAsString)
	*td = jsonDuration(duration)
	return err
}

// Duration returns the value as a time.Duration
func (td *jsonDuration) Duration() time.Duration {
	return time.Duration(*td)
}

// Configuration defaults.
var (
	vetRe = `^(?:vet:.*?\.go:\s+(?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*))|(?:(?P<path>.*?\.go):(?P<line>\d+):\s*(?P<message>.*))$`

	// TODO: should be a field on Config struct
	formatTemplate = &template.Template{}
	installMap     = map[string]string{
		"aligncheck":  "github.com/opennota/check/cmd/aligncheck",
		"deadcode":    "github.com/tsenart/deadcode",
		"dupl":        "github.com/mibk/dupl",
		"errcheck":    "github.com/kisielk/errcheck",
		"gas":         "github.com/GoASTScanner/gas",
		"goconst":     "github.com/jgautheron/goconst/cmd/goconst",
		"gocyclo":     "github.com/alecthomas/gocyclo",
		"goimports":   "golang.org/x/tools/cmd/goimports",
		"golint":      "github.com/golang/lint/golint",
		"gosimple":    "honnef.co/go/tools/cmd/gosimple",
		"gotype":      "golang.org/x/tools/cmd/gotype",
		"ineffassign": "github.com/gordonklaus/ineffassign",
		"interfacer":  "github.com/mvdan/interfacer/cmd/interfacer",
		"lll":         "github.com/walle/lll/cmd/lll",
		"megacheck":   "honnef.co/go/tools/cmd/megacheck",
		"misspell":    "github.com/client9/misspell/cmd/misspell",
		"safesql":     "github.com/stripe/safesql",
		"staticcheck": "honnef.co/go/tools/cmd/staticcheck",
		"structcheck": "github.com/opennota/check/cmd/structcheck",
		"unconvert":   "github.com/mdempsky/unconvert",
		"unparam":     "github.com/mvdan/unparam",
		"unused":      "honnef.co/go/tools/cmd/unused",
		"varcheck":    "github.com/opennota/check/cmd/varcheck",
	}
	slowLinters = []string{"structcheck", "varcheck", "errcheck", "aligncheck", "testify", "test", "interfacer", "unconvert", "deadcode", "safesql", "staticcheck", "unparam", "unused", "gosimple", "megacheck"}
	sortKeys    = []string{"none", "path", "line", "column", "severity", "message", "linter"}

	linterTakesFiles = newStringSet("dupl", "gofmt", "goimports", "lll", "misspell")

	linterTakesFilesGroupedByPackage = newStringSet("vet", "vetshadow")

	linterTakesPackagePaths = newStringSet(
		"errcheck",
		"aligncheck",
		"errcheck",
		"gosimple",
		"interfacer",
		"megacheck",
		"safesql",
		"staticcheck",
		"structcheck",
		"test",
		"testify",
		"unconvert",
		"unparam",
		"unused",
		"varcheck",
	)

	// Linter definitions.
	linterDefinitions = map[string]string{
		"aligncheck":  `aligncheck:^(?:[^:]+: )?(?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.+)$`,
		"deadcode":    `deadcode:^deadcode: (?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)$`,
		"dupl":        `dupl -plumbing -threshold {duplthreshold}:^(?P<path>.*?\.go):(?P<line>\d+)-\d+:\s*(?P<message>.*)$`,
		"errcheck":    `errcheck -abspath:PATH:LINE:COL:MESSAGE`,
		"gas":         `gas -fmt=csv:^(?P<path>.*?\.go),(?P<line>\d+),(?P<message>[^,]+,[^,]+,[^,]+)`,
		"goconst":     `goconst -min-occurrences {min_occurrences} -min-length {min_const_length}:PATH:LINE:COL:MESSAGE`,
		"gocyclo":     `gocyclo -over {mincyclo}:^(?P<cyclo>\d+)\s+\S+\s(?P<function>\S+)\s+(?P<path>.*?\.go):(?P<line>\d+):(\d+)$`,
		"gofmt":       `gofmt -l -s:^(?P<path>.*?\.go)$`,
		"goimports":   `goimports -l:^(?P<path>.*?\.go)$`,
		"golint":      "golint -min_confidence {min_confidence}:PATH:LINE:COL:MESSAGE",
		"gosimple":    "gosimple:PATH:LINE:COL:MESSAGE",
		"gotype":      "gotype -e {tests=-t}:PATH:LINE:COL:MESSAGE",
		"ineffassign": `ineffassign -n:PATH:LINE:COL:MESSAGE`,
		"interfacer":  `interfacer:PATH:LINE:COL:MESSAGE`,
		"lll":         `lll -g -l {maxlinelength}:PATH:LINE:MESSAGE`,
		"megacheck":   "megacheck:PATH:LINE:COL:MESSAGE",
		"misspell":    "misspell -j 1:PATH:LINE:COL:MESSAGE",
		"safesql":     `safesql:^- (?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+)$`,
		"staticcheck": "staticcheck:PATH:LINE:COL:MESSAGE",
		"structcheck": `structcheck {tests=-t}:^(?:[^:]+: )?(?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.+)$`,
		"test":        `go test:^--- FAIL: .*$\s+(?P<path>.*?\.go):(?P<line>\d+): (?P<message>.*)$`,
		"testify":     `go test:Location:\s+(?P<path>.*?\.go):(?P<line>\d+)$\s+Error:\s+(?P<message>[^\n]+)`,
		"unconvert":   "unconvert:PATH:LINE:COL:MESSAGE",
		"unparam":     `unparam:PATH:LINE:COL:MESSAGE`,
		"unused":      `unused:PATH:LINE:COL:MESSAGE`,
		"varcheck":    `varcheck:^(?:[^:]+: )?(?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.*)$`,
		"vet":         `go tool vet:` + vetRe,
		"vetshadow":   `go tool vet --shadow:` + vetRe,
	}

	config = &Config{
		Format: "{{.Path}}:{{.Line}}:{{if .Col}}{{.Col}}{{end}}:{{.Severity}}: {{.Message}} ({{.Linter}})",

		Severity: map[string]string{
			"gotype":  "error",
			"test":    "error",
			"testify": "error",
			"vet":     "error",
		},
		MessageOverride: map[string]string{
			"errcheck":    "error return value not checked ({message})",
			"gocyclo":     "cyclomatic complexity {cyclo} of function {function}() is high (> {mincyclo})",
			"gofmt":       "file is not gofmted with -s",
			"goimports":   "file is not goimported",
			"safesql":     "potentially unsafe SQL statement",
			"structcheck": "unused struct field {message}",
			"unparam":     "parameter {message}",
			"varcheck":    "unused variable or constant {message}",
		},
		Enable: []string{
			"aligncheck",
			"deadcode",
			"errcheck",
			"gas",
			"goconst",
			"gocyclo",
			"golint",
			"gotype",
			"ineffassign",
			"interfacer",
			"megacheck",
			"structcheck",
			"unconvert",
			"varcheck",
			"vet",
			"vetshadow",
		},
		VendoredLinters: true,
		Concurrency:     runtime.NumCPU(),
		Cyclo:           10,
		LineLength:      80,
		MinConfidence:   0.8,
		MinOccurrences:  3,
		MinConstLength:  3,
		DuplThreshold:   50,
		Sort:            []string{"none"},
		Deadline:        jsonDuration(time.Second * 30),
	}
)
<|endoftext|>