<commit_before>
package jianGoMeSHi

import (
	"fmt"
)

func selfLinkDescriptor(m map[string]*DescriptorRecord){
	var nd *Node
	root := nd.Init()

	for _, v := range m {
		//linkSeeRelated(m, v)
		linkConceptRelations(m,v)
		makeTree(root, v)
	}

	root.traverse(0)
}

func makeTree(root *Node, desc *DescriptorRecord){
	addDescriptor(root, desc)
}

func linkSeeRelated(m map[string]*DescriptorRecord, desc *DescriptorRecord){
	seeRelatedList := desc.SeeRelatedList
	if &seeRelatedList != nil{
		fmt.Println("")
		seeRelatedDescriptors := seeRelatedList.SeeRelatedDescriptor
		for _, srd := range seeRelatedDescriptors{
			refDesc, ok := m[srd.DescriptorReferredTo.DescriptorUI]
			if ok{
				srd.DescriptorReferredTo.DescriptorRecord = refDesc
				//fmt.Println(desc.DescriptorUI, desc.DescriptorName, "links to", srd.DescriptorReferredTo.DescriptorUI, srd.DescriptorReferredTo.DescriptorName)
			}
		}
	}
}

func linkConceptRelations(m map[string]*DescriptorRecord, desc *DescriptorRecord){
	conceptList := desc.ConceptList
	if &conceptList != nil{
		fmt.Println("")
		concepts := conceptList.Concept
		for _, concept := range concepts{
			conceptRelationList := concept.ConceptRelationList
			if &conceptRelationList != nil{
				_ = conceptRelationList.ConceptRelation
				//conceptRelations := conceptRelationList.ConceptRelation
				//for _, conceptRelation := range conceptRelations{
					//fmt.Println(concept.ConceptName, " -- ", conceptRelation.RelationName, conceptRelation.Concept1UI, conceptRelation.Concept2UI)
				//}
			}
		}
	}
}<commit_msg>Changed selfLinkDescriptor() to SelfLinkDescriptor()<commit_after>
package jianGoMeSHi

import (
	"fmt"
)

func SelfLinkDescriptor(m map[string]*DescriptorRecord){
	var nd *Node
	root := nd.Init()

	for _, v := range m {
		//linkSeeRelated(m, v)
		linkConceptRelations(m,v)
		makeTree(root, v)
	}

	root.traverse(0)
}

func makeTree(root *Node, desc *DescriptorRecord){
	addDescriptor(root, desc)
}

func linkSeeRelated(m map[string]*DescriptorRecord, desc *DescriptorRecord){
	seeRelatedList := desc.SeeRelatedList
	if &seeRelatedList != nil{
		fmt.Println("")
		seeRelatedDescriptors := seeRelatedList.SeeRelatedDescriptor
		for _, srd := range seeRelatedDescriptors{
			refDesc, ok := m[srd.DescriptorReferredTo.DescriptorUI]
			if ok{
				srd.DescriptorReferredTo.DescriptorRecord = refDesc
				//fmt.Println(desc.DescriptorUI, desc.DescriptorName, "links to", srd.DescriptorReferredTo.DescriptorUI, srd.DescriptorReferredTo.DescriptorName)
			}
		}
	}
}

func linkConceptRelations(m map[string]*DescriptorRecord, desc *DescriptorRecord){
	conceptList := desc.ConceptList
	if &conceptList != nil{
		fmt.Println("")
		concepts := conceptList.Concept
		for _, concept := range concepts{
			conceptRelationList := concept.ConceptRelationList
			if &conceptRelationList != nil{
				_ = conceptRelationList.ConceptRelation
				//conceptRelations := conceptRelationList.ConceptRelation
				//for _, conceptRelation := range conceptRelations{
					//fmt.Println(concept.ConceptName, " -- ", conceptRelation.RelationName, conceptRelation.Concept1UI, conceptRelation.Concept2UI)
				//}
			}
		}
	}
}<|endoftext|>


<commit_before>package main

import "fmt"

type Fund struct {
    // balance is unexported (private), because it's lowercase
    balance int
}

// A regular function returning a pointer to a fund
func NewFund(initialBalance int) *Fund {
    // We can return a pointer to a new struct without worrying about
    // whether it's on the stack or heap: Go figures that out for us.
    return &Fund{
        balance: initialBalance,
    }
}

// Methods start with a *receiver*, in this case a Fund pointer
func (f *Fund) Balance() int {
    return f.balance
}

func (f *Fund) Withdraw(amount int) {
    f.balance -= amount
}

func main() {
    fund := NewFund(1000)

    // Burn through them one at a time until they are all gone
    for i := 0; i < fund.Balance(); i++ {
        fund.Withdraw(1)
        fmt.Printf("%d\n", fund.Balance())
    }

    fmt.Printf("\n")
}
<commit_msg>the example of the tutorial doesn't work :-(<commit_after>package funding

type Fund struct {
    // balance is unexported (private), because it's lowercase
    balance int
}

// A regular function returning a pointer to a fund
func NewFund(initialBalance int) *Fund {
    // We can return a pointer to a new struct without worrying about
    // whether it's on the stack or heap: Go figures that out for us.
    return &Fund{
        balance: initialBalance,
    }
}

// Methods start with a *receiver*, in this case a Fund pointer
func (f *Fund) Balance() int {
    return f.balance
}

func (f *Fund) Withdraw(amount int) {
    f.balance -= amount
}
<|endoftext|>


<commit_before>// 30 december 2012
package main

import (
	"os"
	"github.com/hanwen/go-fuse/fuse"
	"path/filepath"
	"strings"
	"errors"
	"log"
)

var errNoSuchGame = errors.New("no such game")
var errGameNotFound = errors.New("game not found")

// note to self: this needs to be an embed in a struct as DefaultFileSystem will implement the  methods I don't override here and have them return fuse.ENOSYS
type mamefuse struct {
	fuse.DefaultFileSystem
}

func getgame(gamename string) (*Game, fuse.Status) {
	g, ok := games[gamename]
	if !ok {				// not a valid game
		log.Printf("no such game %s\n", gamename)
		return nil, fuse.EINVAL
	}
	good, err := g.Find()
	if err != nil {
		log.Printf("error finding game %s: %v\n", gamename, err)
		return nil, fuse.EIO
	} else if !good {
		log.Printf("game %s not found\n", gamename)
		return nil, fuse.ENOENT
	}
	return g, fuse.OK
}

func getloopbackfile(filename string) (*fuse.LoopbackFile, fuse.Status) {
	f, err := os.Open(filename)
	if err != nil {
		log.Printf("error opening file %s: %v\n", filename, err)
		return nil, fuse.EIO		// TODO too drastic?
	}
	// according to the go-fuse source (fuse/file.go), fuse.LoopbackFile will take ownership of our *os.FIle, calling Close() on it itself
	return &fuse.LoopbackFile{
		File:	f,
	}, fuse.OK
}

func getattr(filename string) (*fuse.Attr, fuse.Status) {
	stat, err := os.Stat(filename)
	if err != nil {
		log.Printf("error geting stats of file %s: %v\n", filename, err)
		return nil, fuse.EIO		// TODO too drastic?
	}
	return fuse.ToAttr(stat), fuse.OK	// TODO mask out write bits?
}

// to avoid recreating the string each time getchdparts() is called
const sepstr = string(filepath.Separator)

func getchdparts(name string) (gamename string, chdname string) {
	// I know MAME won't hand me pathnames that aren't well-formed but Clean() should make them safe to split like this in general...
	parts := strings.Split(filepath.Clean(name), sepstr)
	if len(parts) < 2 {	// invalid
		return "", ""
	}
	gamename = parts[len(parts) - 2]
	chdname = parts[len(parts) - 1]
	chdname = chdname[:len(chdname) - 4]		// strip .chd so we can find it in the Game structure (the MAME XML file doesn't have the extension)
	return
}

func (fs *mamefuse) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame(gamename)
		if err != fuse.OK {
			return nil, err
		}
		return getattr(g.ROMLoc)
	case ".chd":
		gamename, chdname := getchdparts(name)
		if gamename == "" {		// we need a game name to disambiguate
			return nil, fuse.ENOENT
		}
		g, err := getgame(gamename)
		if err != fuse.OK {
			return nil, err
		}
		return getattr(g.CHDLoc[chdname])
	default:
		// is it a folder that stores CHDs?
		if _, ok := games[basename]; ok {		// yes
			return &fuse.Attr{
				Mode:	fuse.S_IFDIR | 0755,		// TODO mask out write bits?
			}, fuse.OK
		}
		// no; fall out
	}
	return nil, fuse.ENOENT		// any other file is invalid
}

func (fs *mamefuse) Open(name string, flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile(g.ROMLoc)
	case ".chd":				// CHD
		gamename, chdname := getchdparts(name)
		if gamename == "" {		// we need a game name to disambiguate
			return nil, fuse.ENOENT
		}
		g, err := getgame(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile(g.CHDLoc[chdname])
	}
	return nil, fuse.ENOENT		// otherwise 404
}

func (fs *mamefuse) OpenDir(name string, context *fuse.Context) (c []fuse.DirEntry, code fuse.Status) {
	// TODO
	return nil, fuse.ENOENT
}
<commit_msg>No, not too drastic; we already know the file both exists and contains the data we need if we reached this point, so something bad had to have happened between the Find() and the accesses<commit_after>// 30 december 2012
package main

import (
	"os"
	"github.com/hanwen/go-fuse/fuse"
	"path/filepath"
	"strings"
	"errors"
	"log"
)

var errNoSuchGame = errors.New("no such game")
var errGameNotFound = errors.New("game not found")

// note to self: this needs to be an embed in a struct as DefaultFileSystem will implement the  methods I don't override here and have them return fuse.ENOSYS
type mamefuse struct {
	fuse.DefaultFileSystem
}

func getgame(gamename string) (*Game, fuse.Status) {
	g, ok := games[gamename]
	if !ok {				// not a valid game
		log.Printf("no such game %s\n", gamename)
		return nil, fuse.EINVAL
	}
	good, err := g.Find()
	if err != nil {
		log.Printf("error finding game %s: %v\n", gamename, err)
		return nil, fuse.EIO
	} else if !good {
		log.Printf("game %s not found\n", gamename)
		return nil, fuse.ENOENT
	}
	return g, fuse.OK
}

func getloopbackfile(filename string) (*fuse.LoopbackFile, fuse.Status) {
	f, err := os.Open(filename)
	if err != nil {
		log.Printf("error opening file %s: %v\n", filename, err)
		return nil, fuse.EIO
	}
	// according to the go-fuse source (fuse/file.go), fuse.LoopbackFile will take ownership of our *os.FIle, calling Close() on it itself
	return &fuse.LoopbackFile{
		File:	f,
	}, fuse.OK
}

func getattr(filename string) (*fuse.Attr, fuse.Status) {
	stat, err := os.Stat(filename)
	if err != nil {
		log.Printf("error geting stats of file %s: %v\n", filename, err)
		return nil, fuse.EIO
	}
	return fuse.ToAttr(stat), fuse.OK	// TODO mask out write bits?
}

// to avoid recreating the string each time getchdparts() is called
const sepstr = string(filepath.Separator)

func getchdparts(name string) (gamename string, chdname string) {
	// I know MAME won't hand me pathnames that aren't well-formed but Clean() should make them safe to split like this in general...
	parts := strings.Split(filepath.Clean(name), sepstr)
	if len(parts) < 2 {	// invalid
		return "", ""
	}
	gamename = parts[len(parts) - 2]
	chdname = parts[len(parts) - 1]
	chdname = chdname[:len(chdname) - 4]		// strip .chd so we can find it in the Game structure (the MAME XML file doesn't have the extension)
	return
}

func (fs *mamefuse) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame(gamename)
		if err != fuse.OK {
			return nil, err
		}
		return getattr(g.ROMLoc)
	case ".chd":
		gamename, chdname := getchdparts(name)
		if gamename == "" {		// we need a game name to disambiguate
			return nil, fuse.ENOENT
		}
		g, err := getgame(gamename)
		if err != fuse.OK {
			return nil, err
		}
		return getattr(g.CHDLoc[chdname])
	default:
		// is it a folder that stores CHDs?
		if _, ok := games[basename]; ok {		// yes
			return &fuse.Attr{
				Mode:	fuse.S_IFDIR | 0755,		// TODO mask out write bits?
			}, fuse.OK
		}
		// no; fall out
	}
	return nil, fuse.ENOENT		// any other file is invalid
}

func (fs *mamefuse) Open(name string, flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile(g.ROMLoc)
	case ".chd":				// CHD
		gamename, chdname := getchdparts(name)
		if gamename == "" {		// we need a game name to disambiguate
			return nil, fuse.ENOENT
		}
		g, err := getgame(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile(g.CHDLoc[chdname])
	}
	return nil, fuse.ENOENT		// otherwise 404
}

func (fs *mamefuse) OpenDir(name string, context *fuse.Context) (c []fuse.DirEntry, code fuse.Status) {
	// TODO
	return nil, fuse.ENOENT
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	debug                        bool
	verbose                      bool
	info                         bool
	quiet                        bool
	force                        bool
	usemove                      bool
	usecacheFallback             bool
	retryGitCommands             bool
	pfMode                       bool
	pfLocation                   string
	dryRun                       bool
	check4update                 bool
	checkSum                     bool
	gitObjectSyntaxNotSupported  bool
	moduleDirParam               string
	cacheDirParam                string
	branchParam                  string
	tags                         bool
	outputNameParam              string
	moduleParam                  string
	configFile                   string
	config                       ConfigSettings
	mutex                        sync.Mutex
	empty                        struct{}
	syncGitCount                 int
	syncForgeCount               int
	needSyncGitCount             int
	needSyncForgeCount           int
	needSyncDirs                 []string
	needSyncEnvs                 map[string]struct{}
	syncGitTime                  float64
	syncForgeTime                float64
	ioGitTime                    float64
	ioForgeTime                  float64
	forgeJSONParseTime           float64
	metadataJSONParseTime        float64
	gmetadataJSONParseTime       float64
	buildtime                    string
	uniqueForgeModules           map[string]ForgeModule
	latestForgeModules           LatestForgeModules
	maxworker                    int
	maxExtractworker             int
	forgeModuleDeprecationNotice string
)

// LatestForgeModules contains a map of unique Forge modules
// that should be the latest versions of them
type LatestForgeModules struct {
	sync.RWMutex
	m map[string]string
}

// ConfigSettings contains the key value pairs from the g10k config file
type ConfigSettings struct {
	CacheDir                    string `yaml:"cachedir"`
	ForgeCacheDir               string
	ModulesCacheDir             string
	EnvCacheDir                 string
	Git                         Git
	Forge                       Forge
	Sources                     map[string]Source
	Timeout                     int      `yaml:"timeout"`
	IgnoreUnreachableModules    bool     `yaml:"ignore_unreachable_modules"`
	Maxworker                   int      `yaml:"maxworker"`
	MaxExtractworker            int      `yaml:"maxextractworker"`
	UseCacheFallback            bool     `yaml:"use_cache_fallback"`
	RetryGitCommands            bool     `yaml:"retry_git_commands"`
	GitObjectSyntaxNotSupported bool     `yaml:"git_object_syntax_not_supported"`
	PostRunCommand              []string `yaml:"postrun"`
}

// Forge is a simple struct that contains the base URL of
// the Forge that g10k should use. Defaults to: https://forgeapi.puppetlabs.com
type Forge struct {
	Baseurl string `yaml:"baseurl"`
}

// Git is a simple struct that contains the optional SSH private key to
// use for authentication
type Git struct {
	privateKey string `yaml:"private_key"`
}

// Source contains basic information about a Puppet environment repository
type Source struct {
	Remote                      string
	Basedir                     string
	Prefix                      string
	PrivateKey                  string `yaml:"private_key"`
	ForceForgeVersions          bool   `yaml:"force_forge_versions"`
	WarnMissingBranch           bool   `yaml:"warn_if_branch_is_missing"`
	ExitIfUnreachable           bool   `yaml:"exit_if_unreachable"`
	AutoCorrectEnvironmentNames string `yaml:"invalid_branches"`
}

// Puppetfile contains the key value pairs from the Puppetfile
type Puppetfile struct {
	forgeBaseURL      string
	forgeCacheTTL     time.Duration
	forgeModules      map[string]ForgeModule
	gitModules        map[string]GitModule
	privateKey        string
	source            string
	workDir           string
	moduleDirs        []string
	controlRepoBranch string
}

// ForgeModule contains information (Version, Name, Author, md5 checksum, file size of the tar.gz archive, Forge BaseURL if custom) about a Puppetlabs Forge module
type ForgeModule struct {
	version   string
	name      string
	author    string
	md5sum    string
	fileSize  int64
	baseURL   string
	cacheTTL  time.Duration
	sha256sum string
	moduleDir string
}

// GitModule contains information about a Git Puppet module
type GitModule struct {
	privateKey        string
	git               string
	branch            string
	tag               string
	commit            string
	ref               string
	link              bool
	ignoreUnreachable bool
	fallback          []string
	installPath       string
	local             bool
	moduleDir         string
}

// ForgeResult is returned by queryForgeAPI and contains if and which version of the Puppetlabs Forge module needs to be downloaded
type ForgeResult struct {
	needToGet     bool
	versionNumber string
	md5sum        string
	fileSize      int64
}

// ExecResult contains the exit code and output of an external command (e.g. git)
type ExecResult struct {
	returnCode int
	output     string
}

func init() {
	// initialize global maps
	needSyncEnvs = make(map[string]struct{})
	uniqueForgeModules = make(map[string]ForgeModule)
}

func main() {

	var (
		configFileFlag = flag.String("config", "", "which config file to use")
		versionFlag    = flag.Bool("version", false, "show build time and version number")
	)
	flag.StringVar(&branchParam, "branch", "", "which git branch of the Puppet environment to update, e.g. core_foobar")
	flag.BoolVar(&tags, "tags", false, "to pull tags as well as branches")
	flag.StringVar(&outputNameParam, "outputname", "", "overwrite the environment name if -branch is specified")
	flag.StringVar(&moduleParam, "module", "", "which module of the Puppet environment to update, e.g. stdlib")
	flag.StringVar(&moduleDirParam, "moduledir", "", "allows overriding of Puppetfile specific moduledir setting, the folder in which Puppet modules will be extracted")
	flag.StringVar(&cacheDirParam, "cachedir", "", "allows overriding of the g10k config file cachedir setting, the folder in which g10k will download git repositories and Forge modules")
	flag.IntVar(&maxworker, "maxworker", 50, "how many Goroutines are allowed to run in parallel for Git and Forge module resolving")
	flag.IntVar(&maxExtractworker, "maxextractworker", 20, "how many Goroutines are allowed to run in parallel for local Git and Forge module extracting processes (git clone, untar and gunzip)")
	flag.BoolVar(&pfMode, "puppetfile", false, "install all modules from Puppetfile in cwd")
	flag.StringVar(&pfLocation, "puppetfilelocation", "./Puppetfile", "which Puppetfile to use in -puppetfile mode")
	flag.BoolVar(&force, "force", false, "purge the Puppet environment directory and do a full sync")
	flag.BoolVar(&dryRun, "dryrun", false, "do not modify anything, just print what would be changed")
	flag.BoolVar(&usemove, "usemove", false, "do not use hardlinks to populate your Puppet environments with Puppetlabs Forge modules. Instead uses simple move commands and purges the Forge cache directory after each run! (Useful for g10k runs inside a Docker container)")
	flag.BoolVar(&check4update, "check4update", false, "only check if the is newer version of the Puppet module avaialable. Does implicitly set dryrun to true")
	flag.BoolVar(&checkSum, "checksum", false, "get the md5 check sum for each Puppetlabs Forge module and verify the integrity of the downloaded archive. Increases g10k run time!")
	flag.BoolVar(&debug, "debug", false, "log debug output, defaults to false")
	flag.BoolVar(&verbose, "verbose", false, "log verbose output, defaults to false")
	flag.BoolVar(&info, "info", false, "log info output, defaults to false")
	flag.BoolVar(&quiet, "quiet", false, "no output, defaults to false")
	flag.BoolVar(&usecacheFallback, "usecachefallback", false, "if g10k should try to use its cache for sources and modules instead of failing")
	flag.BoolVar(&retryGitCommands, "retrygitcommands", false, "if g10k should purge the local repository and retry a failed git command (clone or remote update) instead of failing")
	flag.BoolVar(&gitObjectSyntaxNotSupported, "gitobjectsyntaxnotsupported", false, "if your git version is too old to support reference syntax like master^{object} use this setting to revert to the older syntax")
	flag.Parse()

	configFile = *configFileFlag
	version := *versionFlag

	if version {
		fmt.Println("g10k version 0.5.3 Build time:", buildtime, "UTC")
		os.Exit(0)
	}

	if check4update {
		dryRun = true
	}

	// check for git executable dependency
	if _, err := exec.LookPath("git"); err != nil {
		Fatalf("Error: could not find 'git' executable in PATH")
	}

	target := ""
	before := time.Now()
	if len(configFile) > 0 {
		if usemove {
			Fatalf("Error: -usemove parameter is only allowed in -puppetfile mode!")
		}
		if pfMode {
			Fatalf("Error: -puppetfile parameter is not allowed with -config parameter!")
		}
		if (len(outputNameParam) > 0) && (len(branchParam) == 0) {
			Fatalf("Error: -outputname specified without -branch!")
		}
		if usecacheFallback {
			config.UseCacheFallback = true
		}
		Debugf("Using as config file: " + configFile)
		config = readConfigfile(configFile)
		target = configFile
		if len(branchParam) > 0 {
			resolvePuppetEnvironment(branchParam, tags, outputNameParam)
			target += " with branch " + branchParam
		} else {
			resolvePuppetEnvironment("", tags, "")
		}
	} else {
		if pfMode {
			Debugf("Trying to use as Puppetfile: " + pfLocation)
			sm := make(map[string]Source)
			sm["cmdlineparam"] = Source{Basedir: "./"}
			cachedir := "/tmp/g10k"
			if len(os.Getenv("g10k_cachedir")) > 0 {
				cachedir = os.Getenv("g10k_cachedir")
				cachedir = checkDirAndCreate(cachedir, "cachedir environment variable g10k_cachedir")
				Debugf("Found environment variable g10k_cachedir set to: " + cachedir)
			} else if len(cacheDirParam) > 0 {
				Debugf("Using -cachedir parameter set to : " + cacheDirParam)
				cachedir = checkDirAndCreate(cacheDirParam, "cachedir CLI param")
			} else {
				cachedir = checkDirAndCreate(cachedir, "cachedir default value")
			}
			forgeDefaultSettings := Forge{Baseurl: "https://forgeapi.puppetlabs.com"}
			config = ConfigSettings{CacheDir: cachedir, ForgeCacheDir: cachedir, ModulesCacheDir: cachedir, EnvCacheDir: cachedir, Sources: sm, Forge: forgeDefaultSettings, Maxworker: maxworker, UseCacheFallback: usecacheFallback, MaxExtractworker: maxExtractworker, RetryGitCommands: retryGitCommands, GitObjectSyntaxNotSupported: gitObjectSyntaxNotSupported}
			target = pfLocation
			puppetfile := readPuppetfile(target, "", "cmdlineparam", false, false)
			puppetfile.workDir = "./"
			pfm := make(map[string]Puppetfile)
			pfm["cmdlineparam"] = puppetfile
			resolvePuppetfile(pfm)
		} else {
			Fatalf("Error: you need to specify at least a config file or use the Puppetfile mode\nExample call: " + os.Args[0] + " -config test.yaml or " + os.Args[0] + " -puppetfile\n")
		}
	}

	if usemove {
		// we can not reuse the Forge cache at all when -usemove gets used, because we can not delete the -latest link for some reason
		defer purgeDir(config.ForgeCacheDir, "main() -puppetfile mode with -usemove parameter")
	}

	Debugf("Forge response JSON parsing took " + strconv.FormatFloat(forgeJSONParseTime, 'f', 4, 64) + " seconds")
	Debugf("Forge modules metadata.json parsing took " + strconv.FormatFloat(metadataJSONParseTime, 'f', 4, 64) + " seconds")

	if !check4update && !quiet {
		if len(forgeModuleDeprecationNotice) > 0 {
			Warnf(strings.TrimSuffix(forgeModuleDeprecationNotice, "\n"))
		}
		fmt.Println("Synced", target, "with", syncGitCount, "git repositories and", syncForgeCount, "Forge modules in "+strconv.FormatFloat(time.Since(before).Seconds(), 'f', 1, 64)+"s with git ("+strconv.FormatFloat(syncGitTime, 'f', 1, 64)+"s sync, I/O", strconv.FormatFloat(ioGitTime, 'f', 1, 64)+"s) and Forge ("+strconv.FormatFloat(syncForgeTime, 'f', 1, 64)+"s query+download, I/O", strconv.FormatFloat(ioForgeTime, 'f', 1, 64)+"s) using", strconv.Itoa(config.Maxworker), "resolv and", strconv.Itoa(config.MaxExtractworker), "extract workers")
	}
	if dryRun && (needSyncForgeCount > 0 || needSyncGitCount > 0) {
		os.Exit(1)
	}

	checkForAndExecutePostrunCommand()
}
<commit_msg>bump version to v0.5.4<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	debug                        bool
	verbose                      bool
	info                         bool
	quiet                        bool
	force                        bool
	usemove                      bool
	usecacheFallback             bool
	retryGitCommands             bool
	pfMode                       bool
	pfLocation                   string
	dryRun                       bool
	check4update                 bool
	checkSum                     bool
	gitObjectSyntaxNotSupported  bool
	moduleDirParam               string
	cacheDirParam                string
	branchParam                  string
	tags                         bool
	outputNameParam              string
	moduleParam                  string
	configFile                   string
	config                       ConfigSettings
	mutex                        sync.Mutex
	empty                        struct{}
	syncGitCount                 int
	syncForgeCount               int
	needSyncGitCount             int
	needSyncForgeCount           int
	needSyncDirs                 []string
	needSyncEnvs                 map[string]struct{}
	syncGitTime                  float64
	syncForgeTime                float64
	ioGitTime                    float64
	ioForgeTime                  float64
	forgeJSONParseTime           float64
	metadataJSONParseTime        float64
	gmetadataJSONParseTime       float64
	buildtime                    string
	uniqueForgeModules           map[string]ForgeModule
	latestForgeModules           LatestForgeModules
	maxworker                    int
	maxExtractworker             int
	forgeModuleDeprecationNotice string
)

// LatestForgeModules contains a map of unique Forge modules
// that should be the latest versions of them
type LatestForgeModules struct {
	sync.RWMutex
	m map[string]string
}

// ConfigSettings contains the key value pairs from the g10k config file
type ConfigSettings struct {
	CacheDir                    string `yaml:"cachedir"`
	ForgeCacheDir               string
	ModulesCacheDir             string
	EnvCacheDir                 string
	Git                         Git
	Forge                       Forge
	Sources                     map[string]Source
	Timeout                     int      `yaml:"timeout"`
	IgnoreUnreachableModules    bool     `yaml:"ignore_unreachable_modules"`
	Maxworker                   int      `yaml:"maxworker"`
	MaxExtractworker            int      `yaml:"maxextractworker"`
	UseCacheFallback            bool     `yaml:"use_cache_fallback"`
	RetryGitCommands            bool     `yaml:"retry_git_commands"`
	GitObjectSyntaxNotSupported bool     `yaml:"git_object_syntax_not_supported"`
	PostRunCommand              []string `yaml:"postrun"`
}

// Forge is a simple struct that contains the base URL of
// the Forge that g10k should use. Defaults to: https://forgeapi.puppetlabs.com
type Forge struct {
	Baseurl string `yaml:"baseurl"`
}

// Git is a simple struct that contains the optional SSH private key to
// use for authentication
type Git struct {
	privateKey string `yaml:"private_key"`
}

// Source contains basic information about a Puppet environment repository
type Source struct {
	Remote                      string
	Basedir                     string
	Prefix                      string
	PrivateKey                  string `yaml:"private_key"`
	ForceForgeVersions          bool   `yaml:"force_forge_versions"`
	WarnMissingBranch           bool   `yaml:"warn_if_branch_is_missing"`
	ExitIfUnreachable           bool   `yaml:"exit_if_unreachable"`
	AutoCorrectEnvironmentNames string `yaml:"invalid_branches"`
}

// Puppetfile contains the key value pairs from the Puppetfile
type Puppetfile struct {
	forgeBaseURL      string
	forgeCacheTTL     time.Duration
	forgeModules      map[string]ForgeModule
	gitModules        map[string]GitModule
	privateKey        string
	source            string
	workDir           string
	moduleDirs        []string
	controlRepoBranch string
}

// ForgeModule contains information (Version, Name, Author, md5 checksum, file size of the tar.gz archive, Forge BaseURL if custom) about a Puppetlabs Forge module
type ForgeModule struct {
	version   string
	name      string
	author    string
	md5sum    string
	fileSize  int64
	baseURL   string
	cacheTTL  time.Duration
	sha256sum string
	moduleDir string
}

// GitModule contains information about a Git Puppet module
type GitModule struct {
	privateKey        string
	git               string
	branch            string
	tag               string
	commit            string
	ref               string
	link              bool
	ignoreUnreachable bool
	fallback          []string
	installPath       string
	local             bool
	moduleDir         string
}

// ForgeResult is returned by queryForgeAPI and contains if and which version of the Puppetlabs Forge module needs to be downloaded
type ForgeResult struct {
	needToGet     bool
	versionNumber string
	md5sum        string
	fileSize      int64
}

// ExecResult contains the exit code and output of an external command (e.g. git)
type ExecResult struct {
	returnCode int
	output     string
}

func init() {
	// initialize global maps
	needSyncEnvs = make(map[string]struct{})
	uniqueForgeModules = make(map[string]ForgeModule)
}

func main() {

	var (
		configFileFlag = flag.String("config", "", "which config file to use")
		versionFlag    = flag.Bool("version", false, "show build time and version number")
	)
	flag.StringVar(&branchParam, "branch", "", "which git branch of the Puppet environment to update, e.g. core_foobar")
	flag.BoolVar(&tags, "tags", false, "to pull tags as well as branches")
	flag.StringVar(&outputNameParam, "outputname", "", "overwrite the environment name if -branch is specified")
	flag.StringVar(&moduleParam, "module", "", "which module of the Puppet environment to update, e.g. stdlib")
	flag.StringVar(&moduleDirParam, "moduledir", "", "allows overriding of Puppetfile specific moduledir setting, the folder in which Puppet modules will be extracted")
	flag.StringVar(&cacheDirParam, "cachedir", "", "allows overriding of the g10k config file cachedir setting, the folder in which g10k will download git repositories and Forge modules")
	flag.IntVar(&maxworker, "maxworker", 50, "how many Goroutines are allowed to run in parallel for Git and Forge module resolving")
	flag.IntVar(&maxExtractworker, "maxextractworker", 20, "how many Goroutines are allowed to run in parallel for local Git and Forge module extracting processes (git clone, untar and gunzip)")
	flag.BoolVar(&pfMode, "puppetfile", false, "install all modules from Puppetfile in cwd")
	flag.StringVar(&pfLocation, "puppetfilelocation", "./Puppetfile", "which Puppetfile to use in -puppetfile mode")
	flag.BoolVar(&force, "force", false, "purge the Puppet environment directory and do a full sync")
	flag.BoolVar(&dryRun, "dryrun", false, "do not modify anything, just print what would be changed")
	flag.BoolVar(&usemove, "usemove", false, "do not use hardlinks to populate your Puppet environments with Puppetlabs Forge modules. Instead uses simple move commands and purges the Forge cache directory after each run! (Useful for g10k runs inside a Docker container)")
	flag.BoolVar(&check4update, "check4update", false, "only check if the is newer version of the Puppet module avaialable. Does implicitly set dryrun to true")
	flag.BoolVar(&checkSum, "checksum", false, "get the md5 check sum for each Puppetlabs Forge module and verify the integrity of the downloaded archive. Increases g10k run time!")
	flag.BoolVar(&debug, "debug", false, "log debug output, defaults to false")
	flag.BoolVar(&verbose, "verbose", false, "log verbose output, defaults to false")
	flag.BoolVar(&info, "info", false, "log info output, defaults to false")
	flag.BoolVar(&quiet, "quiet", false, "no output, defaults to false")
	flag.BoolVar(&usecacheFallback, "usecachefallback", false, "if g10k should try to use its cache for sources and modules instead of failing")
	flag.BoolVar(&retryGitCommands, "retrygitcommands", false, "if g10k should purge the local repository and retry a failed git command (clone or remote update) instead of failing")
	flag.BoolVar(&gitObjectSyntaxNotSupported, "gitobjectsyntaxnotsupported", false, "if your git version is too old to support reference syntax like master^{object} use this setting to revert to the older syntax")
	flag.Parse()

	configFile = *configFileFlag
	version := *versionFlag

	if version {
		fmt.Println("g10k version 0.5.4 Build time:", buildtime, "UTC")
		os.Exit(0)
	}

	if check4update {
		dryRun = true
	}

	// check for git executable dependency
	if _, err := exec.LookPath("git"); err != nil {
		Fatalf("Error: could not find 'git' executable in PATH")
	}

	target := ""
	before := time.Now()
	if len(configFile) > 0 {
		if usemove {
			Fatalf("Error: -usemove parameter is only allowed in -puppetfile mode!")
		}
		if pfMode {
			Fatalf("Error: -puppetfile parameter is not allowed with -config parameter!")
		}
		if (len(outputNameParam) > 0) && (len(branchParam) == 0) {
			Fatalf("Error: -outputname specified without -branch!")
		}
		if usecacheFallback {
			config.UseCacheFallback = true
		}
		Debugf("Using as config file: " + configFile)
		config = readConfigfile(configFile)
		target = configFile
		if len(branchParam) > 0 {
			resolvePuppetEnvironment(branchParam, tags, outputNameParam)
			target += " with branch " + branchParam
		} else {
			resolvePuppetEnvironment("", tags, "")
		}
	} else {
		if pfMode {
			Debugf("Trying to use as Puppetfile: " + pfLocation)
			sm := make(map[string]Source)
			sm["cmdlineparam"] = Source{Basedir: "./"}
			cachedir := "/tmp/g10k"
			if len(os.Getenv("g10k_cachedir")) > 0 {
				cachedir = os.Getenv("g10k_cachedir")
				cachedir = checkDirAndCreate(cachedir, "cachedir environment variable g10k_cachedir")
				Debugf("Found environment variable g10k_cachedir set to: " + cachedir)
			} else if len(cacheDirParam) > 0 {
				Debugf("Using -cachedir parameter set to : " + cacheDirParam)
				cachedir = checkDirAndCreate(cacheDirParam, "cachedir CLI param")
			} else {
				cachedir = checkDirAndCreate(cachedir, "cachedir default value")
			}
			forgeDefaultSettings := Forge{Baseurl: "https://forgeapi.puppetlabs.com"}
			config = ConfigSettings{CacheDir: cachedir, ForgeCacheDir: cachedir, ModulesCacheDir: cachedir, EnvCacheDir: cachedir, Sources: sm, Forge: forgeDefaultSettings, Maxworker: maxworker, UseCacheFallback: usecacheFallback, MaxExtractworker: maxExtractworker, RetryGitCommands: retryGitCommands, GitObjectSyntaxNotSupported: gitObjectSyntaxNotSupported}
			target = pfLocation
			puppetfile := readPuppetfile(target, "", "cmdlineparam", false, false)
			puppetfile.workDir = "./"
			pfm := make(map[string]Puppetfile)
			pfm["cmdlineparam"] = puppetfile
			resolvePuppetfile(pfm)
		} else {
			Fatalf("Error: you need to specify at least a config file or use the Puppetfile mode\nExample call: " + os.Args[0] + " -config test.yaml or " + os.Args[0] + " -puppetfile\n")
		}
	}

	if usemove {
		// we can not reuse the Forge cache at all when -usemove gets used, because we can not delete the -latest link for some reason
		defer purgeDir(config.ForgeCacheDir, "main() -puppetfile mode with -usemove parameter")
	}

	Debugf("Forge response JSON parsing took " + strconv.FormatFloat(forgeJSONParseTime, 'f', 4, 64) + " seconds")
	Debugf("Forge modules metadata.json parsing took " + strconv.FormatFloat(metadataJSONParseTime, 'f', 4, 64) + " seconds")

	if !check4update && !quiet {
		if len(forgeModuleDeprecationNotice) > 0 {
			Warnf(strings.TrimSuffix(forgeModuleDeprecationNotice, "\n"))
		}
		fmt.Println("Synced", target, "with", syncGitCount, "git repositories and", syncForgeCount, "Forge modules in "+strconv.FormatFloat(time.Since(before).Seconds(), 'f', 1, 64)+"s with git ("+strconv.FormatFloat(syncGitTime, 'f', 1, 64)+"s sync, I/O", strconv.FormatFloat(ioGitTime, 'f', 1, 64)+"s) and Forge ("+strconv.FormatFloat(syncForgeTime, 'f', 1, 64)+"s query+download, I/O", strconv.FormatFloat(ioForgeTime, 'f', 1, 64)+"s) using", strconv.Itoa(config.Maxworker), "resolv and", strconv.Itoa(config.MaxExtractworker), "extract workers")
	}
	if dryRun && (needSyncForgeCount > 0 || needSyncGitCount > 0) {
		os.Exit(1)
	}

	checkForAndExecutePostrunCommand()
}
<|endoftext|>


<commit_before>package mpool

import (
	"errors"

	log "github.com/funkygao/log4go"
)

var ErrorMessageOverflow = errors.New("message overflow")

// Message encapsulates the messages that we exchange back and forth.
type Message struct {
	Body    []byte
	bodyBuf []byte

	slabSize int
}

type slabClass struct {
	maxSize int
	ch      chan *Message
}

// round n up to a multiple of a.  a must be a power of 2.
func round(n, a int) int {
	return (n + a - 1) &^ (a - 1)
}

var messagePool = []slabClass{
	{maxSize: 256, ch: make(chan *Message, 20<<10)},     // 5MB   = 256 * 20K
	{maxSize: 1024, ch: make(chan *Message, 50<<10)},    // 50MB  = 1K * 50K
	{maxSize: 2 << 10, ch: make(chan *Message, 50<<10)}, // 100MB = 2K * 50K
	{maxSize: 4 << 10, ch: make(chan *Message, 50<<10)}, // 200MB = 4K * 50K
	{maxSize: 8 << 10, ch: make(chan *Message, 4<<10)},  // 32MB  = 8K * 4K
	{maxSize: 64 << 10, ch: make(chan *Message, 1<<10)}, // 64MB  = 64K * 1K
	{maxSize: 256 << 10, ch: make(chan *Message, 1<<7)}, // 32MB  = 256K * 128
}

// NewMessage is the supported way to obtain a new Message.  This makes
// use of a "slab allocator" which greatly reduces the load on the
// garbage collector.
func NewMessage(size int) *Message {
	var ch chan *Message
	for _, slabClass := range messagePool { // TODO binary search
		if size <= slabClass.maxSize {
			ch = slabClass.ch
			size = slabClass.maxSize
			break
		}
	}

	var msg *Message
	select {
	case msg = <-ch:
	default:
		// message pool empty:
		// too busy or size greater than largest slab class
		log.Trace("allocating message memory pool: %dB", size)

		msg = &Message{}
		msg.slabSize = size
		msg.bodyBuf = make([]byte, 0, msg.slabSize)
	}

	msg.Body = msg.bodyBuf
	return msg
}

// Free decrements the reference count on a message, and releases its
// resources if no further references remain.  While this is not
// strictly necessary thanks to GC, doing so allows for the resources to
// be recycled without engaging GC.  This can have rather substantial
// benefits for performance.
func (this *Message) Free() (recycled bool) {
	var ch chan *Message
	for _, slab := range messagePool {
		if this.slabSize == slab.maxSize {
			ch = slab.ch
			break
		}
	}

	select {
	case ch <- this:
	default:
		// should never happen
		log.Critical("slab class[%d] full, silently drop", this.slabSize)
	}

	return true
}
<commit_msg>mpool trigger GC is normal: not never happens<commit_after>package mpool

import (
	"errors"

	log "github.com/funkygao/log4go"
)

var ErrorMessageOverflow = errors.New("message overflow")

// Message encapsulates the messages that we exchange back and forth.
type Message struct {
	Body    []byte
	bodyBuf []byte

	slabSize int
}

type slabClass struct {
	maxSize int
	ch      chan *Message
}

// round n up to a multiple of a.  a must be a power of 2.
func round(n, a int) int {
	return (n + a - 1) &^ (a - 1)
}

var messagePool = []slabClass{
	{maxSize: 256, ch: make(chan *Message, 20<<10)},     // 5MB   = 256 * 20K
	{maxSize: 1024, ch: make(chan *Message, 50<<10)},    // 50MB  = 1K * 50K
	{maxSize: 2 << 10, ch: make(chan *Message, 50<<10)}, // 100MB = 2K * 50K
	{maxSize: 4 << 10, ch: make(chan *Message, 50<<10)}, // 200MB = 4K * 50K
	{maxSize: 8 << 10, ch: make(chan *Message, 4<<10)},  // 32MB  = 8K * 4K
	{maxSize: 64 << 10, ch: make(chan *Message, 1<<10)}, // 64MB  = 64K * 1K
	{maxSize: 256 << 10, ch: make(chan *Message, 1<<7)}, // 32MB  = 256K * 128
}

// NewMessage is the supported way to obtain a new Message.  This makes
// use of a "slab allocator" which greatly reduces the load on the
// garbage collector.
func NewMessage(size int) *Message {
	var ch chan *Message
	for _, slabClass := range messagePool { // TODO binary search
		if size <= slabClass.maxSize {
			ch = slabClass.ch
			size = slabClass.maxSize
			break
		}
	}

	var msg *Message
	select {
	case msg = <-ch:
	default:
		// message pool empty:
		// too busy or size greater than largest slab class
		log.Trace("allocating message memory pool: %dB", size)

		msg = &Message{}
		msg.slabSize = size
		msg.bodyBuf = make([]byte, 0, msg.slabSize)
	}

	msg.Body = msg.bodyBuf
	return msg
}

// Free decrements the reference count on a message, and releases its
// resources if no further references remain.  While this is not
// strictly necessary thanks to GC, doing so allows for the resources to
// be recycled without engaging GC.  This can have rather substantial
// benefits for performance.
func (this *Message) Free() (recycled bool) {
	var ch chan *Message
	for _, slab := range messagePool {
		if this.slabSize == slab.maxSize {
			ch = slab.ch
			break
		}
	}

	select {
	case ch <- this:
	default:
		// e,g. channel size 10, NewMessage alloc 20 messages while nobody free's
		// then 1-10 free ok, but 11-20 will trigger this branch
		log.Warn("slab class[%d] full, fallback to GC", this.slabSize)
		//this.bodyBuf = nil // FIXME should try it on after test, disabled because I am timid
	}

	return true
}
<|endoftext|>


<commit_before>package lnwallet

import (
	"sync"

	"li.lan/labs/plasma/channeldb"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
)

// ChannelContribution...
type ChannelContribution struct {
	// Amount of funds contributed to the funding transaction.
	FundingAmount btcutil.Amount

	// Inputs to the funding transaction.
	Inputs []*wire.TxIn

	// Outputs to be used in the case that the total value of the fund
	// ing inputs is greather than the total potential channel capacity.
	ChangeOutputs []*wire.TxOut

	// The key to be used for the funding transaction's P2SH multi-sig
	// 2-of-2 output.
	MultiSigKey *btcec.PublicKey

	// The key to be used for this party's version of the commitment
	// transaction.
	CommitKey *btcec.PublicKey

	// Address to be used for delivery of cleared channel funds in the scenario
	// of a cooperative channel closure.
	DeliveryAddress btcutil.Address

	// Hash to be used as the revocation for the initial version of this
	// party's commitment transaction.
	RevocationHash [wire.HashSize]byte

	// The delay (in blocks) to be used for the pay-to-self output in this
	// party's version of the commitment transaction.
	CsvDelay uint32
}

// ChannelReservation...
type ChannelReservation struct {
	fundingType FundingType

	// This mutex MUST be held when either reading or modifying any of the
	// fields below.
	sync.RWMutex

	// For CLTV it is nLockTime, for CSV it's nSequence, for segwit it's
	// not needed
	fundingLockTime uint32

	// In order of sorted inputs. Sorting is done in accordance
	// to BIP-69: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
	ourFundingSigs   [][]byte
	theirFundingSigs [][]byte

	// Our signature for their version of the commitment transaction.
	ourCommitmentSig   []byte
	theirCommitmentSig []byte

	ourContribution   *ChannelContribution
	theirContribution *ChannelContribution

	partialState *channeldb.OpenChannel

	// The ID of this reservation, used to uniquely track the reservation
	// throughout its lifetime.
	reservationID uint64

	// A channel which will be sent on once the channel is considered
	// 'open'. A channel is open once the funding transaction has reached
	// a sufficient number of confirmations.
	chanOpen chan *LightningChannel

	wallet *LightningWallet
}

// newChannelReservation...
func newChannelReservation(t FundingType, fundingAmt btcutil.Amount,
	minFeeRate btcutil.Amount, wallet *LightningWallet, id uint64) *ChannelReservation {
	// TODO(roasbeef): CSV here, or on delay?
	return &ChannelReservation{
		fundingType: t,
		ourContribution: &ChannelContribution{
			FundingAmount: fundingAmt,
		},
		theirContribution: &ChannelContribution{
			FundingAmount: fundingAmt,
		},
		partialState: &channeldb.OpenChannel{
			// TODO(roasbeef): assumes balanced symmetric channels.
			Capacity:     fundingAmt * 2,
			OurBalance:   fundingAmt,
			TheirBalance: fundingAmt,
			MinFeePerKb:  minFeeRate,
		},
		reservationID: id,
		wallet:        wallet,
	}
}

// OurContribution...
// NOTE: This SHOULD NOT be modified.
func (r *ChannelReservation) OurContribution() *ChannelContribution {
	r.RLock()
	defer r.RUnlock()
	return r.ourContribution
}

// ProcessContribution...
func (r *ChannelReservation) ProcessContribution(theirContribution *ChannelContribution) error {
	errChan := make(chan error, 1)

	r.wallet.msgChan <- &addContributionMsg{
		pendingFundingID: r.reservationID,
		contribution:     theirContribution,
		err:              errChan,
	}

	return <-errChan
}

func (r *ChannelReservation) TheirContribution() *ChannelContribution {
	r.RLock()
	defer r.RUnlock()
	return r.theirContribution
}

// OurSignatures...
func (r *ChannelReservation) OurSignatures() ([][]byte, []byte) {
	r.RLock()
	defer r.RUnlock()
	return r.ourFundingSigs, r.ourCommitmentSig
}

// CompleteFundingReservation...
// TODO(roasbeef): add commit sig also
func (r *ChannelReservation) CompleteReservation(fundingSigs [][]byte, commitmentSig []byte) error {
	errChan := make(chan error, 1)

	r.wallet.msgChan <- &addCounterPartySigsMsg{
		pendingFundingID:   r.reservationID,
		theirFundingSigs:   fundingSigs,
		theirCommitmentSig: commitmentSig,
		err:                errChan,
	}

	return <-errChan
}

// OurSignatures...
func (r *ChannelReservation) TheirSignatures() ([][]byte, []byte) {
	r.RLock()
	defer r.RUnlock()
	return r.theirFundingSigs, r.theirCommitmentSig
}

// FinalFundingTransaction...
func (r *ChannelReservation) FinalFundingTx() *wire.MsgTx {
	r.RLock()
	defer r.RUnlock()
	return r.partialState.FundingTx
}

// RequestFundingReserveCancellation...
// TODO(roasbeef): also return mutated state?
func (r *ChannelReservation) Cancel() error {
	errChan := make(chan error, 1)
	r.wallet.msgChan <- &fundingReserveCancelMsg{
		pendingFundingID: r.reservationID,
		err:              errChan,
	}

	return <-errChan
}

// WaitForChannelOpen...
func (r *ChannelReservation) WaitForChannelOpen() *LightningChannel {
	return nil
}

/*//FundRequest serialize
//(reading from ChannelReservation directly to reduce the amount of copies)
//We can move this stuff to another file too if it's too big...
func (r *ChannelReservation) SerializeFundRequest() ([]byte, error) {
	var err error

	//Buffer to dump in the serialized data
	b := new(bytes.Buffer)

	//Fund Request
	err = b.WriteByte(0x30)
	if err != nil {
		return nil, err
	}

	//ChannelType (1)
	//Default to current type
	err = b.WriteByte(uint8(0))
	if err != nil {
		return nil, err
	}

	//RequesterFundingAmount - The amount we are going to fund (8)
	//check for positive values
	err = binary.Write(b, binary.BigEndian, r.FundingAmount)
	if err != nil {
		return nil, err
	}

	//RequesterChannelMinCapacity (8)
	//The amount needed to accept and sign the channel commit later
	err = binary.Write(b, binary.BigEndian, r.MinTotalFundingAmount)
	if err != nil {
		return nil, err
	}

	//RevocationHash (20)
	//Our revocation hash being contributed (for CLTV/CSV)
	_, err = b.Write(btcutil.Hash160(r.ourRevocation))
	if err != nil {
		return nil, err
	}

	//CommitPubkey (33)
	//Our public key being used for the commitment
	ourPubKey := r.ourKey.PubKey().SerializeCompressed()
	if len(ourPubKey) != 33 { //validation, can remove later? (NO UNCOMPRESSED KEYS!)
		return nil, fmt.Errorf("Serialize FundReq: our Pubkey length incorrect")
	}
	_, err = b.Write(ourPubKey)
	if err != nil {
		return nil, err
	}

	//DeliveryPkHash (20)
	//For now it's a P2PKH, but we will add an extra byte later for the
	//option for P2SH
	//This is the address to send funds to when complete or refunded
	_, err = b.Write(r.ourDeliveryAddress)
	if err != nil {
		return nil, err
	}

	//ReserveAmount (8)
	//Our own reserve amount
	err = binary.Write(b, binary.BigEndian, r.ReserveAmount)
	if err != nil {
		return nil, err
	}

	//Minimum transaction fee per kb (8)
	err = binary.Write(b, binary.BigEndian, r.MinFeePerKb)
	if err != nil {
		return nil, err
	}

	//LockTime (4)
	err = binary.Write(b, binary.BigEndian, r.FundingLockTime)
	if err != nil {
		return nil, err
	}

	//Fee payer (default to split currently) (1)
	err = binary.Write(b, binary.BigEndian, 0)
	if err != nil {
		return nil, err
	}

	//ChangePkScript
	//Length (1)
	changeScriptLength := len(r.ourChange[0].PkScript)
	if changeScriptLength > 255 {
		return nil, fmt.Errorf("Your changeScriptLength is too long!")
	}
	err = binary.Write(b, binary.BigEndian, uint8(changeScriptLength))
	if err != nil {
		return nil, err
	}
	//For now it's a P2PKH, but we will add an extra byte later for the
	//option for P2SH
	//This is the address to send change to (only allow one)
	//ChangePkScript (length of script)
	_, err = b.Write(r.ourChange[0].PkScript)
	if err != nil {
		return nil, err
	}

	//Append the unsigned(!!) txins
	//First one byte for the amount of txins (1)
	if len(r.ourInputs) > 127 {
		return nil, fmt.Errorf("Too many txins")
	}
	err = b.WriteByte(uint8(len(r.ourInputs)))
	if err != nil {
		return nil, err
	}
	//Append the actual Txins (NumOfTxins * 36)
	//Do not include the sequence number to eliminate funny business
	for _, in := range r.ourInputs {
		//Hash
		_, err = b.Write(in.PreviousOutPoint.Hash.Bytes())
		if err != nil {
			return nil, err
		}
		//Index
		err = binary.Write(b, binary.BigEndian, in.PreviousOutPoint.Index)
		if err != nil {
			return nil, err
		}
	}

	return b.Bytes(), err
}

func (r *ChannelReservation) DeserializeFundRequest(wireMsg []byte) error {
	//Make sure we're not overwriting stuff...
	//Update the channelState to 1 before progressing if you want to re-do it.
	//Assumes only one thread is writing at a time
	if r.channelState > 1 {
		return fmt.Errorf("FundRequest: Channel State Mismatch")
	}

	var err error

	b := bytes.NewBuffer(wireMsg)
	msgid, _ := b.ReadByte()
	if msgid != 0x30 {
		return fmt.Errorf("Cannot deserialize: not a funding request")
	}

	if err != nil {
		return err
	}
	//Update the channel state as complete
	r.channelState = 2

	return nil
}

//Validation on the data being supplied from the fund request
func (r *ChannelReservation) ValidateFundRequest(wireMsg []byte) error {
	return nil
}

//Serialize CSV Revocation
//After the Commitment Transaction has been created, send a message to revoke this tx
func (r *ChannelReservation) SerializeCSVRefundRevocation() ([]byte, error) {
	return nil, nil
}

//Deserialize CSV Revocation
//Validate the revocation, after this step, the channel is fully set up
func (r *ChannelReservation) DeserializeCSVRefundRevocation() error {
	return nil
}*/

// * finish reset of tests
// * comment out stuff that'll need a node.
// * start on commitment side
//   * implement rusty's shachain
//   * set up logic to get notification from node when funding tx gets 6 deep.
//     * prob spawn into ChainNotifier struct
//   * create builder for initial funding transaction
//     * fascade through the wallet, for signing and such.
//   * channel should have active namespace to it's bucket, query at that point fo past commits etc
<commit_msg>lnwallet: remove draft wire structs from reservation.go, now lives in lnwire<commit_after>package lnwallet

import (
	"sync"

	"li.lan/labs/plasma/channeldb"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
)

// ChannelContribution...
type ChannelContribution struct {
	// Amount of funds contributed to the funding transaction.
	FundingAmount btcutil.Amount

	// Inputs to the funding transaction.
	Inputs []*wire.TxIn

	// Outputs to be used in the case that the total value of the fund
	// ing inputs is greather than the total potential channel capacity.
	ChangeOutputs []*wire.TxOut

	// The key to be used for the funding transaction's P2SH multi-sig
	// 2-of-2 output.
	MultiSigKey *btcec.PublicKey

	// The key to be used for this party's version of the commitment
	// transaction.
	CommitKey *btcec.PublicKey

	// Address to be used for delivery of cleared channel funds in the scenario
	// of a cooperative channel closure.
	DeliveryAddress btcutil.Address

	// Hash to be used as the revocation for the initial version of this
	// party's commitment transaction.
	RevocationHash [wire.HashSize]byte

	// The delay (in blocks) to be used for the pay-to-self output in this
	// party's version of the commitment transaction.
	CsvDelay uint32
}

// ChannelReservation...
type ChannelReservation struct {
	fundingType FundingType

	// This mutex MUST be held when either reading or modifying any of the
	// fields below.
	sync.RWMutex

	// For CLTV it is nLockTime, for CSV it's nSequence, for segwit it's
	// not needed
	fundingLockTime uint32

	// In order of sorted inputs. Sorting is done in accordance
	// to BIP-69: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
	ourFundingSigs   [][]byte
	theirFundingSigs [][]byte

	// Our signature for their version of the commitment transaction.
	ourCommitmentSig   []byte
	theirCommitmentSig []byte

	ourContribution   *ChannelContribution
	theirContribution *ChannelContribution

	partialState *channeldb.OpenChannel

	// The ID of this reservation, used to uniquely track the reservation
	// throughout its lifetime.
	reservationID uint64

	// A channel which will be sent on once the channel is considered
	// 'open'. A channel is open once the funding transaction has reached
	// a sufficient number of confirmations.
	chanOpen chan *LightningChannel

	wallet *LightningWallet
}

// newChannelReservation...
func newChannelReservation(t FundingType, fundingAmt btcutil.Amount,
	minFeeRate btcutil.Amount, wallet *LightningWallet, id uint64) *ChannelReservation {
	// TODO(roasbeef): CSV here, or on delay?
	return &ChannelReservation{
		fundingType: t,
		ourContribution: &ChannelContribution{
			FundingAmount: fundingAmt,
		},
		theirContribution: &ChannelContribution{
			FundingAmount: fundingAmt,
		},
		partialState: &channeldb.OpenChannel{
			// TODO(roasbeef): assumes balanced symmetric channels.
			Capacity:     fundingAmt * 2,
			OurBalance:   fundingAmt,
			TheirBalance: fundingAmt,
			MinFeePerKb:  minFeeRate,
		},
		reservationID: id,
		wallet:        wallet,
	}
}

// OurContribution...
// NOTE: This SHOULD NOT be modified.
func (r *ChannelReservation) OurContribution() *ChannelContribution {
	r.RLock()
	defer r.RUnlock()
	return r.ourContribution
}

// ProcessContribution...
func (r *ChannelReservation) ProcessContribution(theirContribution *ChannelContribution) error {
	errChan := make(chan error, 1)

	r.wallet.msgChan <- &addContributionMsg{
		pendingFundingID: r.reservationID,
		contribution:     theirContribution,
		err:              errChan,
	}

	return <-errChan
}

func (r *ChannelReservation) TheirContribution() *ChannelContribution {
	r.RLock()
	defer r.RUnlock()
	return r.theirContribution
}

// OurSignatures...
func (r *ChannelReservation) OurSignatures() ([][]byte, []byte) {
	r.RLock()
	defer r.RUnlock()
	return r.ourFundingSigs, r.ourCommitmentSig
}

// CompleteFundingReservation...
func (r *ChannelReservation) CompleteReservation(fundingSigs [][]byte,
	commitmentSig []byte) error {

	errChan := make(chan error, 1)

	r.wallet.msgChan <- &addCounterPartySigsMsg{
		pendingFundingID:   r.reservationID,
		theirFundingSigs:   fundingSigs,
		theirCommitmentSig: commitmentSig,
		err:                errChan,
	}

	return <-errChan
}

// OurSignatures...
func (r *ChannelReservation) TheirSignatures() ([][]byte, []byte) {
	r.RLock()
	defer r.RUnlock()
	return r.theirFundingSigs, r.theirCommitmentSig
}

// FinalFundingTransaction...
func (r *ChannelReservation) FinalFundingTx() *wire.MsgTx {
	r.RLock()
	defer r.RUnlock()
	return r.partialState.FundingTx
}

// RequestFundingReserveCancellation...
func (r *ChannelReservation) Cancel() error {
	errChan := make(chan error, 1)
	r.wallet.msgChan <- &fundingReserveCancelMsg{
		pendingFundingID: r.reservationID,
		err:              errChan,
	}

	return <-errChan
}

// WaitForChannelOpen...
func (r *ChannelReservation) WaitForChannelOpen() *LightningChannel {
	return nil
}

// * finish reset of tests
// * comment out stuff that'll need a node.
// * start on commitment side
//   * implement rusty's shachain
//   * set up logic to get notification from node when funding tx gets 6 deep.
//     * prob spawn into ChainNotifier struct
//   * create builder for initial funding transaction
//     * fascade through the wallet, for signing and such.
//   * channel should have active namespace to it's bucket, query at that point fo past commits etc
<|endoftext|>


<commit_before>// Copyright (c) 2016 by Richard A. Wilkes. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, version 2.0.

package ui

import (
	"bytes"
	"fmt"
	"reflect"
	"time"
)

// Possible KeyMask values.
const (
	CapsLockKeyMask KeyMask = 1 << iota
	ShiftKeyMask
	ControlKeyMask
	OptionKeyMask
	CommandKeyMask   // On platforms that don't have a distinct command key, this will also be set if the Control key is pressed.
	NonStickyKeyMask = ShiftKeyMask | ControlKeyMask | OptionKeyMask | CommandKeyMask
	AllKeyMask       = CapsLockKeyMask | NonStickyKeyMask
)

// KeyMask contains flags indicating which modifier keys were down when an event occurred.
type KeyMask int

const (
	// PaintEvent is generated when a widget needs to be drawn.
	PaintEvent = iota
	// MouseDownEvent is generated when a mouse button is pressed on a widget.
	MouseDownEvent
	// MouseDraggedEvent is generated when the mouse is moved within a widget while a mouse button
	// is down.
	MouseDraggedEvent
	// MouseUpEvent is generated when the mouse button is released after a mouse button press
	// occurred within a widget.
	MouseUpEvent
	// MouseEnteredEvent is generated when the mouse enters a widget.
	MouseEnteredEvent
	// MouseMovedEvent is generated when the mouse moves within a widget, except when a mouse
	// button is also down (a MouseDraggedEvent is generated for that).
	MouseMovedEvent
	// MouseExitedEvent is generated when the mouse exits a widget.
	MouseExitedEvent
	// MouseWheelEvent is generated when the mouse wheel is used over a widget.
	MouseWheelEvent
	// FocusGainedEvent is generated when a widget gains the keyboard focus.
	FocusGainedEvent
	// FocusLostEvent is generated when a widget loses the keyboard focus.
	FocusLostEvent
	// KeyDownEvent is generated when a key is pressed.
	KeyDownEvent
	// KeyTypedEvent is generated when a rune has been generated on the keyboard.
	KeyTypedEvent
	// KeyUpEvent is generated when a key is released.
	KeyUpEvent
	// ToolTipEvent is generated when a tooltip is being requested for the widget.
	ToolTipEvent
	// ResizeEvent is generated when a widget is resized.
	ResizeEvent
	// UserEvent should be used as the base value for application custom events.
	UserEvent = 10000
)

// EventHandler is called to handle a single event.
type EventHandler func(event *Event)

// Event holds the data associated with an event.
type Event struct {
	Type         int       // Valid for all events.
	When         time.Time // Valid for all events.
	Target       Widget    // Valid for all events
	GC           Graphics  // Valid only for PaintEvent.
	DirtyRect    Rect      // Valid only for PaintEvent.
	Where        Point     // In window coordinates. Valid for MouseDownEvent, MouseDraggedEvent, MouseUpEvent, MouseEnteredEvent, MouseMovedEvent, and MouseWheelEvent.
	Delta        Point     // Valid only for MouseWheelEvent. The amount scrolled in each direction.
	KeyModifiers KeyMask   // Valid for MouseDownEvent, MouseDraggedEvent, MouseUpEvent, MouseEnteredEvent, MouseMovedEvent, MouseExitedEvent, MouseWheelEvent, KeyDownEvent, KeyTypedEvent, and KeyUpEvent.
	Button       int       // Valid only for MouseDownEvent. The button that is down.
	Clicks       int       // Valid only for MouseDownEvent. The number of consecutive clicks in the widget.
	ToolTip      string    // Valid only for ToolTipEvent. Set this to the text to display for the tooltip.
	KeyCode      int       // Valid for KeyDownEvent and KeyUpEvent.
	KeyTyped     rune      // Valid only for KeyTypedEvent.
	Repeat       bool      // Valid for KeyDownEvent and KeyTypedEvent. Set to true if the key was auto-generated.
	CascadeUp    bool      // Valid for all events. true if this event should be cascaded up to parents.
	Discard      bool      // Valid for MouseDownEvent and KeyDownEvent. Set to true to if the event should be ignored (i.e. don't do processing that would have side-effects).
	Done         bool      // Valid for all events. Set to true to stop processing this event.
}

// Dispatch the event. If there is more than one handler for the event type registered with the
// target, they will each be given a chance to handle the event in order. Should one of them set
// the Done flag on the event, processing will halt immediately. Once the target has been given an
// opportunity to process the event, if the event's CascadeUp flag is set, its parent will then be
// given the chance. This will continue until there are no more parents, the event's Done flag is
// set, or the event's CascadeUp flag is unset.
func (event *Event) Dispatch() {
	target := event.Target
	for target != nil {
		if handlers, ok := target.EventHandlers()[event.Type]; ok {
			for _, handler := range handlers {
				handler(event)
				if event.Done {
					return
				}
			}
		}
		if !event.CascadeUp {
			return
		}
		target = target.Parent()
	}
}

// String implements fmt.Stringer.
func (event *Event) String() string {
	var buffer bytes.Buffer
	switch event.Type {
	case PaintEvent:
		buffer.WriteString("PaintEvent")
	case MouseDownEvent:
		buffer.WriteString("MouseDownEvent")
	case MouseDraggedEvent:
		buffer.WriteString("MouseDraggedEvent")
	case MouseUpEvent:
		buffer.WriteString("MouseUpEvent")
	case MouseEnteredEvent:
		buffer.WriteString("MouseEnteredEvent")
	case MouseMovedEvent:
		buffer.WriteString("MouseMovedEvent")
	case MouseExitedEvent:
		buffer.WriteString("MouseExitedEvent")
	case MouseWheelEvent:
		buffer.WriteString("MouseWheelEvent")
	case FocusGainedEvent:
		buffer.WriteString("FocusGainedEvent")
	case FocusLostEvent:
		buffer.WriteString("FocusLostEvent")
	case KeyDownEvent:
		buffer.WriteString("KeyDownEvent")
	case KeyTypedEvent:
		buffer.WriteString("KeyTypedEvent")
	case KeyUpEvent:
		buffer.WriteString("KeyUpEvent")
	case ToolTipEvent:
		buffer.WriteString("ToolTipEvent")
	case ResizeEvent:
		buffer.WriteString("ResizeEvent")
	default:
		buffer.WriteString(fmt.Sprintf("Custom%dEvent", event.Type))
	}
	buffer.WriteString(fmt.Sprintf("[When: %v, Target: %v", event.When, reflect.ValueOf(event.Target).Pointer()))
	switch event.Type {
	case PaintEvent:
		buffer.WriteString(fmt.Sprintf(", DirtyRect: %v", event.DirtyRect))
	case MouseDownEvent:
		buffer.WriteString(fmt.Sprintf(", Where: %v, KeyModifiers: %s, Button: %d, Clicks: %d", event.Where, event.keyModifiersAsString(), event.Button, event.Clicks))
	case MouseDraggedEvent:
		buffer.WriteString(fmt.Sprintf(", Where: %v, KeyModifiers: %s", event.Where, event.keyModifiersAsString()))
	case MouseUpEvent:
		buffer.WriteString(fmt.Sprintf(", Where: %v, KeyModifiers: %s", event.Where, event.keyModifiersAsString()))
	case MouseEnteredEvent:
		buffer.WriteString(fmt.Sprintf(", Where: %v, KeyModifiers: %s", event.Where, event.keyModifiersAsString()))
	case MouseMovedEvent:
		buffer.WriteString(fmt.Sprintf(", Where: %v, KeyModifiers: %s", event.Where, event.keyModifiersAsString()))
	case MouseExitedEvent:
		buffer.WriteString(fmt.Sprintf(", KeyModifiers: %s", event.keyModifiersAsString()))
	case MouseWheelEvent:
		buffer.WriteString(fmt.Sprintf(", Where: %v, Delta: %v, KeyModifiers: %s", event.Where, event.Delta, event.keyModifiersAsString()))
	case KeyDownEvent:
		buffer.WriteString(fmt.Sprintf(", KeyModifiers: %s, KeyCode: %d", event.keyModifiersAsString(), event.KeyCode))
		if event.Repeat {
			buffer.WriteString(", Repeat")
		}
	case KeyTypedEvent:
		buffer.WriteString(fmt.Sprintf(", KeyModifiers: %s, KeyCode: %d, KeyTyped: %v", event.keyModifiersAsString(), event.KeyCode, event.KeyTyped))
		if event.Repeat {
			buffer.WriteString(", Repeat")
		}
	case KeyUpEvent:
		buffer.WriteString(fmt.Sprintf(", KeyModifiers: %s, KeyCode: %d", event.keyModifiersAsString(), event.KeyCode))
		if event.Repeat {
			buffer.WriteString(", Repeat")
		}
	}
	if event.CascadeUp {
		buffer.WriteString(", CascadeUp")
	}
	if event.Discard && (event.Type == MouseDownEvent || event.Type == KeyDownEvent) {
		buffer.WriteString(", Discard")
	}
	if event.Done {
		buffer.WriteString(", Done")
	}
	buffer.WriteString("]")
	return buffer.String()
}

func (event *Event) keyModifiersAsString() string {
	var buffer bytes.Buffer
	event.appendKeyModifier(&buffer, CapsLockKeyMask, "CapsLock")
	event.appendKeyModifier(&buffer, ShiftKeyMask, "Shift")
	event.appendKeyModifier(&buffer, ControlKeyMask, "Control")
	event.appendKeyModifier(&buffer, OptionKeyMask, "Option")
	event.appendKeyModifier(&buffer, CommandKeyMask, "Command")
	if buffer.Len() == 0 {
		buffer.WriteString("None")
	}
	return buffer.String()
}

func (event *Event) appendKeyModifier(buffer *bytes.Buffer, mask KeyMask, name string) {
	if event.KeyModifiers&mask == mask {
		if buffer.Len() > 0 {
			buffer.WriteString(" | ")
		}
		buffer.WriteString(name)
	}
}
<commit_msg>Add helpers to test for modifier keys being down<commit_after>// Copyright (c) 2016 by Richard A. Wilkes. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, version 2.0.

package ui

import (
	"bytes"
	"fmt"
	"reflect"
	"time"
)

// Possible KeyMask values.
const (
	CapsLockKeyMask KeyMask = 1 << iota
	ShiftKeyMask
	ControlKeyMask
	OptionKeyMask
	CommandKeyMask   // On platforms that don't have a distinct command key, this will also be set if the Control key is pressed.
	NonStickyKeyMask = ShiftKeyMask | ControlKeyMask | OptionKeyMask | CommandKeyMask
	AllKeyMask       = CapsLockKeyMask | NonStickyKeyMask
)

// KeyMask contains flags indicating which modifier keys were down when an event occurred.
type KeyMask int

const (
	// PaintEvent is generated when a widget needs to be drawn.
	PaintEvent = iota
	// MouseDownEvent is generated when a mouse button is pressed on a widget.
	MouseDownEvent
	// MouseDraggedEvent is generated when the mouse is moved within a widget while a mouse button
	// is down.
	MouseDraggedEvent
	// MouseUpEvent is generated when the mouse button is released after a mouse button press
	// occurred within a widget.
	MouseUpEvent
	// MouseEnteredEvent is generated when the mouse enters a widget.
	MouseEnteredEvent
	// MouseMovedEvent is generated when the mouse moves within a widget, except when a mouse
	// button is also down (a MouseDraggedEvent is generated for that).
	MouseMovedEvent
	// MouseExitedEvent is generated when the mouse exits a widget.
	MouseExitedEvent
	// MouseWheelEvent is generated when the mouse wheel is used over a widget.
	MouseWheelEvent
	// FocusGainedEvent is generated when a widget gains the keyboard focus.
	FocusGainedEvent
	// FocusLostEvent is generated when a widget loses the keyboard focus.
	FocusLostEvent
	// KeyDownEvent is generated when a key is pressed.
	KeyDownEvent
	// KeyTypedEvent is generated when a rune has been generated on the keyboard.
	KeyTypedEvent
	// KeyUpEvent is generated when a key is released.
	KeyUpEvent
	// ToolTipEvent is generated when a tooltip is being requested for the widget.
	ToolTipEvent
	// ResizeEvent is generated when a widget is resized.
	ResizeEvent
	// UserEvent should be used as the base value for application custom events.
	UserEvent = 10000
)

// EventHandler is called to handle a single event.
type EventHandler func(event *Event)

// Event holds the data associated with an event.
type Event struct {
	Type         int       // Valid for all events.
	When         time.Time // Valid for all events.
	Target       Widget    // Valid for all events
	GC           Graphics  // Valid only for PaintEvent.
	DirtyRect    Rect      // Valid only for PaintEvent.
	Where        Point     // In window coordinates. Valid for MouseDownEvent, MouseDraggedEvent, MouseUpEvent, MouseEnteredEvent, MouseMovedEvent, and MouseWheelEvent.
	Delta        Point     // Valid only for MouseWheelEvent. The amount scrolled in each direction.
	KeyModifiers KeyMask   // Valid for MouseDownEvent, MouseDraggedEvent, MouseUpEvent, MouseEnteredEvent, MouseMovedEvent, MouseExitedEvent, MouseWheelEvent, KeyDownEvent, KeyTypedEvent, and KeyUpEvent.
	Button       int       // Valid only for MouseDownEvent. The button that is down.
	Clicks       int       // Valid only for MouseDownEvent. The number of consecutive clicks in the widget.
	ToolTip      string    // Valid only for ToolTipEvent. Set this to the text to display for the tooltip.
	KeyCode      int       // Valid for KeyDownEvent and KeyUpEvent.
	KeyTyped     rune      // Valid only for KeyTypedEvent.
	Repeat       bool      // Valid for KeyDownEvent and KeyTypedEvent. Set to true if the key was auto-generated.
	CascadeUp    bool      // Valid for all events. true if this event should be cascaded up to parents.
	Discard      bool      // Valid for MouseDownEvent and KeyDownEvent. Set to true to if the event should be ignored (i.e. don't do processing that would have side-effects).
	Done         bool      // Valid for all events. Set to true to stop processing this event.
}

// Dispatch the event. If there is more than one handler for the event type registered with the
// target, they will each be given a chance to handle the event in order. Should one of them set
// the Done flag on the event, processing will halt immediately. Once the target has been given an
// opportunity to process the event, if the event's CascadeUp flag is set, its parent will then be
// given the chance. This will continue until there are no more parents, the event's Done flag is
// set, or the event's CascadeUp flag is unset.
func (event *Event) Dispatch() {
	target := event.Target
	for target != nil {
		if handlers, ok := target.EventHandlers()[event.Type]; ok {
			for _, handler := range handlers {
				handler(event)
				if event.Done {
					return
				}
			}
		}
		if !event.CascadeUp {
			return
		}
		target = target.Parent()
	}
}

// ShiftDown returns true if the shift key is being pressed.
func (event *Event) ShiftDown() bool {
	return event.KeyModifiers&ShiftKeyMask == ShiftKeyMask
}

// OptionDown returns true if the option/alt key is being pressed.
func (event *Event) OptionDown() bool {
	return event.KeyModifiers&OptionKeyMask == OptionKeyMask
}

// ControlDown returns true if the control key is being pressed.
func (event *Event) ControlDown() bool {
	return event.KeyModifiers&ControlKeyMask == ControlKeyMask
}

// CommandDown returns true if the command/meta key is being pressed.
func (event *Event) CommandDown() bool {
	return event.KeyModifiers&CommandKeyMask == CommandKeyMask
}

// CapsLockDown returns true if the caps lock key is being pressed.
func (event *Event) CapsLockDown() bool {
	return event.KeyModifiers&CapsLockKeyMask == CapsLockKeyMask
}

// String implements fmt.Stringer.
func (event *Event) String() string {
	var buffer bytes.Buffer
	switch event.Type {
	case PaintEvent:
		buffer.WriteString("PaintEvent")
	case MouseDownEvent:
		buffer.WriteString("MouseDownEvent")
	case MouseDraggedEvent:
		buffer.WriteString("MouseDraggedEvent")
	case MouseUpEvent:
		buffer.WriteString("MouseUpEvent")
	case MouseEnteredEvent:
		buffer.WriteString("MouseEnteredEvent")
	case MouseMovedEvent:
		buffer.WriteString("MouseMovedEvent")
	case MouseExitedEvent:
		buffer.WriteString("MouseExitedEvent")
	case MouseWheelEvent:
		buffer.WriteString("MouseWheelEvent")
	case FocusGainedEvent:
		buffer.WriteString("FocusGainedEvent")
	case FocusLostEvent:
		buffer.WriteString("FocusLostEvent")
	case KeyDownEvent:
		buffer.WriteString("KeyDownEvent")
	case KeyTypedEvent:
		buffer.WriteString("KeyTypedEvent")
	case KeyUpEvent:
		buffer.WriteString("KeyUpEvent")
	case ToolTipEvent:
		buffer.WriteString("ToolTipEvent")
	case ResizeEvent:
		buffer.WriteString("ResizeEvent")
	default:
		buffer.WriteString(fmt.Sprintf("Custom%dEvent", event.Type))
	}
	buffer.WriteString(fmt.Sprintf("[When: %v, Target: %v", event.When, reflect.ValueOf(event.Target).Pointer()))
	switch event.Type {
	case PaintEvent:
		buffer.WriteString(fmt.Sprintf(", DirtyRect: %v", event.DirtyRect))
	case MouseDownEvent:
		buffer.WriteString(fmt.Sprintf(", Where: %v, KeyModifiers: %s, Button: %d, Clicks: %d", event.Where, event.keyModifiersAsString(), event.Button, event.Clicks))
	case MouseDraggedEvent:
		buffer.WriteString(fmt.Sprintf(", Where: %v, KeyModifiers: %s", event.Where, event.keyModifiersAsString()))
	case MouseUpEvent:
		buffer.WriteString(fmt.Sprintf(", Where: %v, KeyModifiers: %s", event.Where, event.keyModifiersAsString()))
	case MouseEnteredEvent:
		buffer.WriteString(fmt.Sprintf(", Where: %v, KeyModifiers: %s", event.Where, event.keyModifiersAsString()))
	case MouseMovedEvent:
		buffer.WriteString(fmt.Sprintf(", Where: %v, KeyModifiers: %s", event.Where, event.keyModifiersAsString()))
	case MouseExitedEvent:
		buffer.WriteString(fmt.Sprintf(", KeyModifiers: %s", event.keyModifiersAsString()))
	case MouseWheelEvent:
		buffer.WriteString(fmt.Sprintf(", Where: %v, Delta: %v, KeyModifiers: %s", event.Where, event.Delta, event.keyModifiersAsString()))
	case KeyDownEvent:
		buffer.WriteString(fmt.Sprintf(", KeyModifiers: %s, KeyCode: %d", event.keyModifiersAsString(), event.KeyCode))
		if event.Repeat {
			buffer.WriteString(", Repeat")
		}
	case KeyTypedEvent:
		buffer.WriteString(fmt.Sprintf(", KeyModifiers: %s, KeyCode: %d, KeyTyped: %v", event.keyModifiersAsString(), event.KeyCode, event.KeyTyped))
		if event.Repeat {
			buffer.WriteString(", Repeat")
		}
	case KeyUpEvent:
		buffer.WriteString(fmt.Sprintf(", KeyModifiers: %s, KeyCode: %d", event.keyModifiersAsString(), event.KeyCode))
		if event.Repeat {
			buffer.WriteString(", Repeat")
		}
	}
	if event.CascadeUp {
		buffer.WriteString(", CascadeUp")
	}
	if event.Discard && (event.Type == MouseDownEvent || event.Type == KeyDownEvent) {
		buffer.WriteString(", Discard")
	}
	if event.Done {
		buffer.WriteString(", Done")
	}
	buffer.WriteString("]")
	return buffer.String()
}

func (event *Event) keyModifiersAsString() string {
	var buffer bytes.Buffer
	event.appendKeyModifier(&buffer, CapsLockKeyMask, "CapsLock")
	event.appendKeyModifier(&buffer, ShiftKeyMask, "Shift")
	event.appendKeyModifier(&buffer, ControlKeyMask, "Control")
	event.appendKeyModifier(&buffer, OptionKeyMask, "Option")
	event.appendKeyModifier(&buffer, CommandKeyMask, "Command")
	if buffer.Len() == 0 {
		buffer.WriteString("None")
	}
	return buffer.String()
}

func (event *Event) appendKeyModifier(buffer *bytes.Buffer, mask KeyMask, name string) {
	if event.KeyModifiers&mask == mask {
		if buffer.Len() > 0 {
			buffer.WriteString(" | ")
		}
		buffer.WriteString(name)
	}
}
<|endoftext|>


<commit_before>// Copyright Praegressus Limited. All rights reserved.
//
// 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.

// +build !plan9,!solaris

package main

import (
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"hash/fnv"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"syscall"

	"gopkg.in/fsnotify.v1"
	"gopkg.in/yaml.v2"
)

var (
	config     *configuration
	logFile    *os.File
	rules      map[fsnotify.Op][]*rule
	rulesMu    = &sync.RWMutex{}
	matches    map[int64]*match
	matchesMu  = &sync.RWMutex{}
	errNoRules = errors.New("no rules")
	configFile = flag.String("config", ".gawp", "Configuration file")
	hasher64   = fnv.New64a()
	hasher64Mu = &sync.Mutex{}
)

// Gawp configuration
type configuration struct {
	recursive, verbose bool
	workers            int
	logFile            string
}

type rule struct {
	match *regexp.Regexp
	cmds  []string
}

type match struct {
	mu   *sync.Mutex
	rule *rule
	cmds [][]string
}

func main() {
	flag.Parse()
	log.SetFlags(log.Ldate | log.Lmicroseconds)

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))

	if err != nil {
		log.Fatal(err)
	}

	defer logFile.Close()

	if err = load(dir, *configFile); err != nil {
		log.Fatalf("unable to load configuration file: %s (%s)", *configFile, err)
	}

	// Operating system threads that can execute user-level Go code simultaneously
	if config.workers > 1 {
		n := runtime.NumCPU()

		if config.workers < n {
			n = config.workers
		}

		runtime.GOMAXPROCS(n)
	}

	// File system notifications
	watcher, err := fsnotify.NewWatcher()

	if err != nil {
		log.Fatal(err)
	}

	defer watcher.Close()

	if config.recursive {
		c := 0

		// Watch root and child directories
		filepath.Walk(dir+"/", func(path string, f os.FileInfo, err error) error {
			if !f.IsDir() {
				return nil
			}

			if n := path[len(dir):]; n != "/" && n[1] == '.' {
				return nil
			}

			if err := watcher.Add(path); err != nil {
				log.Printf("unable to watch path: %s (%s)", path, err)
			}

			c++

			return nil
		})

		log.Printf("watching %d directories", c)
	} else if err = watcher.Add(dir); err != nil {
		//  Only watch the root dir
		log.Fatal(err)
	}

	// Disable file system notifications for the log file
	if config.logFile != "" {
		watcher.Remove(config.logFile)
	}

	// We should always be able to run atleast 1 job
	if config.workers < 1 {
		config.workers = 1
	}

	var (
		signals  = make(chan os.Signal, 2)             // OS signal capture
		throttle = make(chan struct{}, config.workers) // Worker throttle
		wg       = &sync.WaitGroup{}
	)

	// Handle signals for clean shutdown
	signal.Notify(signals, os.Interrupt, syscall.SIGTERM)

	// Wait for workers on shutdown
	defer wg.Wait()

	for {
		select {
		case event := <-watcher.Events:
			filename := event.Name[len(dir)+1:]

			if config.verbose {
				log.Println(event.String())
			}

			throttle <- struct{}{}

			// Reload config file
			if filename == *configFile {
				// Wait for active workers
				wg.Wait()

				log.Println("reloading config file")

				if err = load(dir, filename); err != nil {
					log.Fatal(err)
				}

				<-throttle

				continue
			}

			wg.Add(1)

			go worker(throttle, wg, event.Op, filename)

		case err = <-watcher.Errors:
			log.Println("fsnotify error:", err)

		case <-signals:
			return
		}
	}
}

func worker(throttle chan struct{}, wg *sync.WaitGroup, e fsnotify.Op, f string) {
	defer func() {
		<-throttle

		wg.Done()
	}()

	m := findMatch(e, f)

	if m == nil {
		return
	}

	// Atomicity for the given match
	m.mu.Lock()

	defer m.mu.Unlock()

	var cmd *exec.Cmd

	for i, c := range m.cmds {
		if len(c) == 1 {
			cmd = exec.Command(c[0])
		} else {
			cmd = exec.Command(c[0], c[1:]...)
		}

		if b, err := cmd.Output(); err != nil {
			log.Printf("command (%s) error: %s", c, err)
		} else if len(b) > 0 {
			log.Printf("%s\n%s", m.rule.cmds[i], b)
		}
	}
}

// findMatch attempts to find a rule match for file path
// On success caches the match for fast future lookups
func findMatch(e fsnotify.Op, f string) *match {
	var (
		m *match
		h = hash64(e, f)
	)

	// Fast map lookup, circumvent regular expressions
	matchesMu.RLock()
	c, exists := matches[h]
	matchesMu.RUnlock()

	if exists {
		return c
	}

	// Cache for fast lookup
	defer func() {
		matchesMu.Lock()
		matches[h] = m
		matchesMu.Unlock()
	}()

	rulesMu.RLock()

	defer rulesMu.RUnlock()

	// Check there's rules associated with the event type
	r, exists := rules[e]

	if !exists {
		return nil
	}

	// Test each rule for a match
	for _, c := range r {
		s := c.match.FindAllStringSubmatch(f, -1)

		if s == nil {
			continue
		}

		m = &match{&sync.Mutex{}, c, nil}

		for _, cmd := range c.cmds {
			for i := range s[0] {
				if i == 0 {
					continue
				}

				cmd = strings.Replace(cmd, "$"+strconv.Itoa(i), s[0][i], -1)
			}

			m.cmds = append(m.cmds, strings.Fields(strings.Replace(cmd, "$file", f, -1)))
		}

		break
	}

	return m
}

// loads the Gawp config file and handles the loading of rules
func load(dir, f string) error {
	// Init/reset config, rules and matches cache
	config = &configuration{}
	rules = map[fsnotify.Op][]*rule{}
	matches = map[int64]*match{}

	// Open config file
	h, err := os.Open(dir + "/" + f)

	if err != nil {
		return err
	}

	defer h.Close()

	b, err := ioutil.ReadAll(h)

	if err != nil {
		return err
	}

	var c map[string]interface{}

	if err = yaml.Unmarshal(b, &c); err != nil {
		return err
	}

	if len(c) == 0 {
		return errNoRules
	}

	for k, v := range c {
		switch k {
		case "recursive":
			config.recursive, _ = v.(bool)

		case "verbose":
			config.verbose, _ = v.(bool)

		case "workers":
			config.workers, _ = v.(int)

		case "logfile":
			config.logFile, _ = v.(string)

		default:
			if err = parseRules(k, v); err != nil {
				return err
			}
		}
	}

	if config.workers < 1 {
		config.workers = 1
	}

	if err = setLogFile(dir, config.logFile); err != nil {
		return err
	}

	return nil
}

func parseRules(s string, i interface{}) (err error) {
	e := parseEvents(s)

	if len(e) == 0 {
		return nil
	}

	switch c := i.(type) {
	case map[interface{}]interface{}:
		for k, v := range c {
			// Regular expression
			m, ok := k.(string)

			if !ok {
				return nil
			}

			// Commands
			p, ok := v.([]interface{})

			if !ok || len(p) == 0 {
				return nil
			}

			r := &rule{}

			if r.match, err = regexp.Compile(m); err != nil {
				return fmt.Errorf("rule compilation error: %s (%s)", m, err)
			}

			for _, c := range p {
				cmd, ok := c.(string)

				if !ok || cmd == "" {
					continue
				}

				if cmd = strings.TrimSpace(cmd); cmd == "" {
					continue
				}

				r.cmds = append(r.cmds, cmd)
			}

			if len(r.cmds) == 0 {
				continue
			}

			// Add the rule to each event bucket
			for _, c := range e {
				rules[c] = append(rules[c], r)
			}
		}
	}

	return nil
}

func parseEvents(s string) (e []fsnotify.Op) {
	for _, v := range strings.Split(s, ",") {
		switch strings.ToLower(strings.TrimSpace(v)) {
		case "create":
			e = append(e, fsnotify.Create)
		case "write":
			e = append(e, fsnotify.Write)
		case "rename":
			e = append(e, fsnotify.Rename)
		case "remove":
			e = append(e, fsnotify.Remove)
		case "chmod":
			e = append(e, fsnotify.Chmod)
		}
	}

	return
}

func setLogFile(dir, f string) (err error) {
	if f == "" {
		return nil
	}

	// Relative path
	if f[0] != '/' {
		f = dir + "/" + f
	}

	// Force log file rotation, no side effects
	logFile.Close()

	if logFile, err = os.OpenFile(f, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666); err != nil {
		return err
	}

	log.SetOutput(logFile)

	return nil
}

// hash64 returns the hash of the given string as int64
func hash64(e fsnotify.Op, s string) int64 {
	hasher64Mu.Lock()

	defer hasher64Mu.Unlock()

	hasher64.Reset()
	binary.Write(hasher64, binary.LittleEndian, e)
	hasher64.Write([]byte(s))

	return int64(hasher64.Sum64())
}
<commit_msg>Use FNV's uint64 hash without redundant conversion to int64.<commit_after>// Copyright Praegressus Limited. All rights reserved.
//
// 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.

// +build !plan9,!solaris

package main

import (
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"hash/fnv"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"syscall"

	"gopkg.in/fsnotify.v1"
	"gopkg.in/yaml.v2"
)

var (
	config     *configuration
	logFile    *os.File
	rules      map[fsnotify.Op][]*rule
	rulesMu    = &sync.RWMutex{}
	matches    map[uint64]*match
	matchesMu  = &sync.RWMutex{}
	errNoRules = errors.New("no rules")
	configFile = flag.String("config", ".gawp", "Configuration file")
	hasher64   = fnv.New64a()
	hasher64Mu = &sync.Mutex{}
)

// Gawp configuration
type configuration struct {
	recursive, verbose bool
	workers            int
	logFile            string
}

type rule struct {
	match *regexp.Regexp
	cmds  []string
}

type match struct {
	mu   *sync.Mutex
	rule *rule
	cmds [][]string
}

func main() {
	flag.Parse()
	log.SetFlags(log.Ldate | log.Lmicroseconds)

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))

	if err != nil {
		log.Fatal(err)
	}

	defer logFile.Close()

	if err = load(dir, *configFile); err != nil {
		log.Fatalf("unable to load configuration file: %s (%s)", *configFile, err)
	}

	// Operating system threads that can execute user-level Go code simultaneously
	if config.workers > 1 {
		n := runtime.NumCPU()

		if config.workers < n {
			n = config.workers
		}

		runtime.GOMAXPROCS(n)
	}

	// File system notifications
	watcher, err := fsnotify.NewWatcher()

	if err != nil {
		log.Fatal(err)
	}

	defer watcher.Close()

	if config.recursive {
		c := 0

		// Watch root and child directories
		filepath.Walk(dir+"/", func(path string, f os.FileInfo, err error) error {
			if !f.IsDir() {
				return nil
			}

			if n := path[len(dir):]; n != "/" && n[1] == '.' {
				return nil
			}

			if err := watcher.Add(path); err != nil {
				log.Printf("unable to watch path: %s (%s)", path, err)
			}

			c++

			return nil
		})

		log.Printf("watching %d directories", c)
	} else if err = watcher.Add(dir); err != nil {
		//  Only watch the root dir
		log.Fatal(err)
	}

	// Disable file system notifications for the log file
	if config.logFile != "" {
		watcher.Remove(config.logFile)
	}

	// We should always be able to run atleast 1 job
	if config.workers < 1 {
		config.workers = 1
	}

	var (
		signals  = make(chan os.Signal, 2)             // OS signal capture
		throttle = make(chan struct{}, config.workers) // Worker throttle
		wg       = &sync.WaitGroup{}
	)

	// Handle signals for clean shutdown
	signal.Notify(signals, os.Interrupt, syscall.SIGTERM)

	// Wait for workers on shutdown
	defer wg.Wait()

	for {
		select {
		case event := <-watcher.Events:
			filename := event.Name[len(dir)+1:]

			if config.verbose {
				log.Println(event.String())
			}

			throttle <- struct{}{}

			// Reload config file
			if filename == *configFile {
				// Wait for active workers
				wg.Wait()

				log.Println("reloading config file")

				if err = load(dir, filename); err != nil {
					log.Fatal(err)
				}

				<-throttle

				continue
			}

			wg.Add(1)

			go worker(throttle, wg, event.Op, filename)

		case err = <-watcher.Errors:
			log.Println("fsnotify error:", err)

		case <-signals:
			return
		}
	}
}

func worker(throttle chan struct{}, wg *sync.WaitGroup, e fsnotify.Op, f string) {
	defer func() {
		<-throttle

		wg.Done()
	}()

	m := findMatch(e, f)

	if m == nil {
		return
	}

	// Atomicity for the given match
	m.mu.Lock()

	defer m.mu.Unlock()

	var cmd *exec.Cmd

	for i, c := range m.cmds {
		if len(c) == 1 {
			cmd = exec.Command(c[0])
		} else {
			cmd = exec.Command(c[0], c[1:]...)
		}

		if b, err := cmd.Output(); err != nil {
			log.Printf("command (%s) error: %s", c, err)
		} else if len(b) > 0 {
			log.Printf("%s\n%s", m.rule.cmds[i], b)
		}
	}
}

// findMatch attempts to find a rule match for file path
// On success caches the match for fast future lookups
func findMatch(e fsnotify.Op, f string) *match {
	var (
		m *match
		h = hash64(e, f)
	)

	// Fast map lookup, circumvent regular expressions
	matchesMu.RLock()
	c, exists := matches[h]
	matchesMu.RUnlock()

	if exists {
		return c
	}

	// Cache for fast lookup
	defer func() {
		matchesMu.Lock()
		matches[h] = m
		matchesMu.Unlock()
	}()

	rulesMu.RLock()

	defer rulesMu.RUnlock()

	// Check there's rules associated with the event type
	r, exists := rules[e]

	if !exists {
		return nil
	}

	// Test each rule for a match
	for _, c := range r {
		s := c.match.FindAllStringSubmatch(f, -1)

		if s == nil {
			continue
		}

		m = &match{&sync.Mutex{}, c, nil}

		for _, cmd := range c.cmds {
			for i := range s[0] {
				if i == 0 {
					continue
				}

				cmd = strings.Replace(cmd, "$"+strconv.Itoa(i), s[0][i], -1)
			}

			m.cmds = append(m.cmds, strings.Fields(strings.Replace(cmd, "$file", f, -1)))
		}

		break
	}

	return m
}

// loads the Gawp config file and handles the loading of rules
func load(dir, f string) error {
	// Init/reset config, rules and matches cache
	config = &configuration{}
	rules = map[fsnotify.Op][]*rule{}
	matches = map[uint64]*match{}

	// Open config file
	h, err := os.Open(dir + "/" + f)

	if err != nil {
		return err
	}

	defer h.Close()

	b, err := ioutil.ReadAll(h)

	if err != nil {
		return err
	}

	var c map[string]interface{}

	if err = yaml.Unmarshal(b, &c); err != nil {
		return err
	}

	if len(c) == 0 {
		return errNoRules
	}

	for k, v := range c {
		switch k {
		case "recursive":
			config.recursive, _ = v.(bool)

		case "verbose":
			config.verbose, _ = v.(bool)

		case "workers":
			config.workers, _ = v.(int)

		case "logfile":
			config.logFile, _ = v.(string)

		default:
			if err = parseRules(k, v); err != nil {
				return err
			}
		}
	}

	if config.workers < 1 {
		config.workers = 1
	}

	if err = setLogFile(dir, config.logFile); err != nil {
		return err
	}

	return nil
}

func parseRules(s string, i interface{}) (err error) {
	e := parseEvents(s)

	if len(e) == 0 {
		return nil
	}

	switch c := i.(type) {
	case map[interface{}]interface{}:
		for k, v := range c {
			// Regular expression
			m, ok := k.(string)

			if !ok {
				return nil
			}

			// Commands
			p, ok := v.([]interface{})

			if !ok || len(p) == 0 {
				return nil
			}

			r := &rule{}

			if r.match, err = regexp.Compile(m); err != nil {
				return fmt.Errorf("rule compilation error: %s (%s)", m, err)
			}

			for _, c := range p {
				cmd, ok := c.(string)

				if !ok || cmd == "" {
					continue
				}

				if cmd = strings.TrimSpace(cmd); cmd == "" {
					continue
				}

				r.cmds = append(r.cmds, cmd)
			}

			if len(r.cmds) == 0 {
				continue
			}

			// Add the rule to each event bucket
			for _, c := range e {
				rules[c] = append(rules[c], r)
			}
		}
	}

	return nil
}

func parseEvents(s string) (e []fsnotify.Op) {
	for _, v := range strings.Split(s, ",") {
		switch strings.ToLower(strings.TrimSpace(v)) {
		case "create":
			e = append(e, fsnotify.Create)
		case "write":
			e = append(e, fsnotify.Write)
		case "rename":
			e = append(e, fsnotify.Rename)
		case "remove":
			e = append(e, fsnotify.Remove)
		case "chmod":
			e = append(e, fsnotify.Chmod)
		}
	}

	return
}

func setLogFile(dir, f string) (err error) {
	if f == "" {
		return nil
	}

	// Relative path
	if f[0] != '/' {
		f = dir + "/" + f
	}

	// Force log file rotation, no side effects
	logFile.Close()

	if logFile, err = os.OpenFile(f, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666); err != nil {
		return err
	}

	log.SetOutput(logFile)

	return nil
}

// hash64 returns the hash of the given FS operation & string as uint64
func hash64(e fsnotify.Op, s string) uint64 {
	hasher64Mu.Lock()

	defer hasher64Mu.Unlock()

	hasher64.Reset()
	binary.Write(hasher64, binary.LittleEndian, e)
	hasher64.Write([]byte(s))

	return hasher64.Sum64()
}
<|endoftext|>


<commit_before>package gelf

import (
	"fmt"
	"net"
	"errors"
)

type Config struct {
	Enabled bool `json:"enabled"`
	Net string `json:"net"`
	Addr string `json:"addr"`
	Workers int `json:"workers"`
}

var gSendChannel chan []byte

func Start(config Config) error {
	if !config.Enabled {
		fmt.Printf("GELF logging is disabled\n")
		return nil
	}

	if len(config.Net) == 0 {
		return errors.New("Missing network family")
	}

	if len(config.Addr) == 0 {
		return errors.New("Missing address")
	}

	if config.Workers == 0 {
		config.Workers = 4
	} else if config.Workers < 1 || config.Workers > 16 {
		return fmt.Errorf("Bad worker count %d", config.Workers)
	}

	raddr, err := net.ResolveUDPAddr(config.Net, config.Addr)
	if err != nil {
		return err
	}

	conn, err := net.DialUDP(config.Net, nil, raddr)
	if err != nil {
		return err
	}

	gSendChannel = make(chan []byte, 16)

	for i := 0; i < config.Workers; i++ {
		go worker(gSendChannel, conn)
	}

	fmt.Printf("GELF logger start, net = %s, addr = %s\n", config.Net, config.Addr)

	return nil
}

func Send(packet []byte) {
	if gSendChannel != nil {
		select {
			case gSendChannel <- packet:
		}
	}
}

func SendString(packet string) {
	Send([]byte(packet))
}

func worker(c chan []byte, conn *net.UDPConn) {
	for {
		packet := <- c
		conn.Write(packet)
		fmt.Printf("%s\n", string(packet))
	}
}
<commit_msg>Rename Send to SendBytes<commit_after>package gelf

import (
	"fmt"
	"net"
	"errors"
)

type Config struct {
	Enabled bool `json:"enabled"`
	Net string `json:"net"`
	Addr string `json:"addr"`
	Workers int `json:"workers"`
}

var gSendChannel chan []byte

func Start(config Config) error {
	if !config.Enabled {
		fmt.Printf("GELF logging is disabled\n")
		return nil
	}

	if len(config.Net) == 0 {
		return errors.New("Missing network family")
	}

	if len(config.Addr) == 0 {
		return errors.New("Missing address")
	}

	if config.Workers == 0 {
		config.Workers = 4
	} else if config.Workers < 1 || config.Workers > 16 {
		return fmt.Errorf("Bad worker count %d", config.Workers)
	}

	raddr, err := net.ResolveUDPAddr(config.Net, config.Addr)
	if err != nil {
		return err
	}

	conn, err := net.DialUDP(config.Net, nil, raddr)
	if err != nil {
		return err
	}

	gSendChannel = make(chan []byte, 16)

	for i := 0; i < config.Workers; i++ {
		go worker(gSendChannel, conn)
	}

	fmt.Printf("GELF logger start, net = %s, addr = %s\n", config.Net, config.Addr)

	return nil
}

func SendBytes(packet []byte) {
	if gSendChannel != nil {
		select {
			case gSendChannel <- packet:
		}
	}
}

func SendString(packet string) {
	SendBytes([]byte(packet))
}

func worker(c chan []byte, conn *net.UDPConn) {
	for {
		packet := <- c
		conn.Write(packet)
	}
}
<|endoftext|>