<commit_before>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"log"
	"os"
	"regexp"
	"strings"
)

type method struct {
	args []interface{}
}

var parsed map[string]map[string]method

var (
	fClose = "Close()"
	fRead  = "Read([]byte)"
	fWrite = "Write([]byte)"
	fSeek  = "Seek(int64, int)"
	known  = map[string][]string{
		"io.Closer":      {fClose},
		"io.ReadCloser":  {fRead, fClose},
		"io.ReadWriter":  {fRead, fWrite},
		"io.Reader":      {fRead},
		"io.Seeker":      {fSeek},
		"io.WriteCloser": {fWrite, fClose},
		"io.Writer":      {fWrite},
	}
)

func manualInit() {
	funcRegex := regexp.MustCompile(`^(.*)\((.*)\)`)
	parsed = make(map[string]map[string]method, len(known))
	for iface, declStrs := range known {
		parsed[iface] = make(map[string]method, len(declStrs))
		for _, s := range declStrs {
			parts := funcRegex.FindStringSubmatch(s)
			name := parts[1]
			m := method{}
			for _, t := range strings.Split(parts[2], ", ") {
				if t == "" {
					continue
				}
				m.args = append(m.args, t)
			}
			parsed[iface][name] = m
		}
	}
}

var suggested = [...]string{
	"io.Closer",
	"io.ReadCloser",
	"io.ReadWriter",
	"io.Reader",
	"io.Seeker",
	"io.WriteCloser",
	"io.Writer",
}

func typesInit() {
	parsed = make(map[string]map[string]method, len(suggested))
	pkg := types.NewPackage(".", "main")
	imp := importer.Default()
	p1, err := imp.Import("io")
	if err != nil {
		log.Fatal(err)
	}
	pkg.SetImports([]*types.Package{p1})
	fmt.Println(pkg.Imports())
	fset := token.NewFileSet()
	for _, v := range suggested {
		tv, err := types.Eval(fset, pkg, token.NoPos, v)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(tv)
	}
}

func init() {
	manualInit()
	// Should be much simpler via go/types, but even if we pass it
	// the imported "io" package, it still errors:
	// 	undeclared name: io
	//typesInit()
}

var toToken = map[string]token.Token{
	"int":   token.INT,
	"int32": token.INT,
	"int64": token.INT,
}

func argEqual(s1 string, a2 interface{}) bool {
	switch x := a2.(type) {
	case string:
		return s1 == x
	case token.Token:
		return toToken[s1] == x
	default:
		fmt.Printf("%T\n", x)
		return false
	}
}

func argsMatch(args1, args2 []interface{}) bool {
	if len(args1) != len(args2) {
		return false
	}
	for i, a1 := range args1 {
		a2 := args2[i]
		s1 := a1.(string)
		if !argEqual(s1, a2) {
			return false
		}
	}
	return true
}

func interfaceMatching(methods map[string]method) string {
	matchesIface := func(decls map[string]method) bool {
		if len(methods) > len(decls) {
			return false
		}
		for n, d := range decls {
			m, e := methods[n]
			if !e {
				return false
			}
			if !argsMatch(d.args, m.args) {
				return false
			}
		}
		return true
	}
	for name, decls := range parsed {
		if matchesIface(decls) {
			return name
		}
	}
	return ""
}

func main() {
	parseFile("stdin.go", os.Stdin, os.Stdout)
}

func parseFile(name string, r io.Reader, w io.Writer) {
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, name, r, 0)
	if err != nil {
		log.Fatal(err)
	}

	conf := types.Config{Importer: importer.Default()}
	pkg, err := conf.Check("", fset, []*ast.File{f}, nil)
	if err != nil {
		log.Fatal(err)
	}

	v := &Visitor{
		w:      w,
		fset:   fset,
		scopes: []*types.Scope{pkg.Scope()},
	}
	ast.Walk(v, f)
}

type Visitor struct {
	w      io.Writer
	fset   *token.FileSet
	scopes []*types.Scope

	nodes []ast.Node

	args map[string]types.Type
	used map[string]map[string]method
}

func (v *Visitor) Visit(node ast.Node) ast.Visitor {
	switch x := node.(type) {
	case *ast.File:
	case *ast.FuncDecl:
		name := x.Name.Name
		scope := v.scopes[len(v.scopes)-1]
		f := scope.Lookup(name).(*types.Func)

		v.scopes = append(v.scopes, f.Scope())
		sign := f.Type().(*types.Signature)
		params := sign.Params()

		v.args = make(map[string]types.Type, params.Len())
		for i := 0; i < params.Len(); i++ {
			p := params.At(i)
			v.args[p.Name()] = p.Type()
		}
		v.used = make(map[string]map[string]method, 0)
	case *ast.BlockStmt:
	case *ast.ExprStmt:
	case *ast.CallExpr:
		v.onCall(x)
	case nil:
		top := v.nodes[len(v.nodes)-1]
		v.nodes = v.nodes[:len(v.nodes)-1]
		if _, ok := top.(*ast.FuncDecl); !ok {
			return nil
		}
		v.scopes = v.scopes[:len(v.scopes)-1]
		for name, methods := range v.used {
			iface := interfaceMatching(methods)
			if iface == "" {
				continue
			}
			if iface == v.args[name].String() {
				continue
			}
			pos := v.fset.Position(top.Pos())
			fmt.Fprintf(v.w, "%s:%d: %s can be %s\n",
				pos.Filename, pos.Line, name, iface)
		}
		v.args = nil
		v.used = nil
	default:
		return nil
	}
	if node != nil {
		v.nodes = append(v.nodes, node)
	}
	return v
}

func getType(scope *types.Scope, name string) interface{} {
	if scope == nil {
		return nil
	}
	obj := scope.Lookup(name)
	if obj == nil {
		return getType(scope.Parent(), name)
	}
	switch x := obj.(type) {
	case *types.Var:
		return x.Type().String()
	default:
		return nil
	}
}

func (v *Visitor) descType(e ast.Expr) interface{} {
	switch x := e.(type) {
	case *ast.Ident:
		scope := v.scopes[len(v.scopes)-1]
		return getType(scope, x.Name)
	case *ast.BasicLit:
		return x.Kind
	default:
		return nil
	}
}

func (v *Visitor) onCall(c *ast.CallExpr) {
	sel, ok := c.Fun.(*ast.SelectorExpr)
	if !ok {
		return
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return
	}
	right := sel.Sel
	vname := left.Name
	fname := right.Name
	m := method{}
	for _, a := range c.Args {
		m.args = append(m.args, v.descType(a))
	}
	if _, e := v.used[vname]; !e {
		v.used[vname] = make(map[string]method)
	}
	v.used[vname][fname] = m
}
<commit_msg>Finally get go/types init method working<commit_after>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"log"
	"os"
)

type method struct {
	args []interface{}
}

var parsed map[string]map[string]method

var suggested = [...]string{
	"io.Closer",
	"io.ReadCloser",
	"io.ReadWriter",
	"io.Reader",
	"io.Seeker",
	"io.WriteCloser",
	"io.Writer",
}

func typesInit() {
	fset := token.NewFileSet()
	// Simple program that imports and uses all needed packages
	const typesProgram = `
	package types
	import "io"
	func foo(r io.Reader) {
	}
	`
	f, err := parser.ParseFile(fset, "foo.go", typesProgram, 0)
	if err != nil {
		log.Fatal(err)
	}

	conf := types.Config{Importer: importer.Default()}
	pkg, err := conf.Check("", fset, []*ast.File{f}, nil)
	if err != nil {
		log.Fatal(err)
	}
	pos := pkg.Scope().Lookup("foo").Pos()

	parsed = make(map[string]map[string]method, len(suggested))
	for _, v := range suggested {
		tv, err := types.Eval(fset, pkg, pos, v)
		if err != nil {
			log.Fatal(err)
		}
		t := tv.Type
		if !types.IsInterface(t) {
			log.Fatalf("%s is not an interface", v)
		}
		named := t.(*types.Named)
		ifname := named.String()
		iface := named.Underlying().(*types.Interface)
		parsed[ifname] = make(map[string]method, iface.NumMethods())
		for i := 0; i < iface.NumMethods(); i++ {
			m := method{}
			f := iface.Method(i)
			fname := f.Name()
			sign := f.Type().(*types.Signature)
			params := sign.Params()
			for i := 0; i < params.Len(); i++ {
				p := params.At(i)
				m.args = append(m.args, p.Type())
			}
			parsed[ifname][fname] = m
		}
	}
}

func init() {
	typesInit()
}

var toToken = map[string]token.Token{
	"int":   token.INT,
	"int32": token.INT,
	"int64": token.INT,
}

func argEqual(t1 types.Type, a2 interface{}) bool {
	switch x := a2.(type) {
	case string:
		return t1.String() == x
	case token.Token:
		return toToken[t1.String()] == x
	default:
		return false
	}
}

func argsMatch(args1, args2 []interface{}) bool {
	if len(args1) != len(args2) {
		return false
	}
	for i, a1 := range args1 {
		a2 := args2[i]
		t1 := a1.(types.Type)
		if !argEqual(t1, a2) {
			return false
		}
	}
	return true
}

func interfaceMatching(methods map[string]method) string {
	matchesIface := func(decls map[string]method) bool {
		if len(methods) > len(decls) {
			return false
		}
		for n, d := range decls {
			m, e := methods[n]
			if !e {
				return false
			}
			if !argsMatch(d.args, m.args) {
				return false
			}
		}
		return true
	}
	for name, decls := range parsed {
		if matchesIface(decls) {
			return name
		}
	}
	return ""
}

func main() {
	parseFile("stdin.go", os.Stdin, os.Stdout)
}

func parseFile(name string, r io.Reader, w io.Writer) {
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, name, r, 0)
	if err != nil {
		log.Fatal(err)
	}

	conf := types.Config{Importer: importer.Default()}
	pkg, err := conf.Check("", fset, []*ast.File{f}, nil)
	if err != nil {
		log.Fatal(err)
	}

	v := &Visitor{
		w:      w,
		fset:   fset,
		scopes: []*types.Scope{pkg.Scope()},
	}
	ast.Walk(v, f)
}

type Visitor struct {
	w      io.Writer
	fset   *token.FileSet
	scopes []*types.Scope

	nodes []ast.Node

	args map[string]types.Type
	used map[string]map[string]method
}

func (v *Visitor) Visit(node ast.Node) ast.Visitor {
	switch x := node.(type) {
	case *ast.File:
	case *ast.FuncDecl:
		name := x.Name.Name
		scope := v.scopes[len(v.scopes)-1]
		f := scope.Lookup(name).(*types.Func)

		v.scopes = append(v.scopes, f.Scope())
		sign := f.Type().(*types.Signature)
		params := sign.Params()

		v.args = make(map[string]types.Type, params.Len())
		for i := 0; i < params.Len(); i++ {
			p := params.At(i)
			v.args[p.Name()] = p.Type()
		}
		v.used = make(map[string]map[string]method, 0)
	case *ast.BlockStmt:
	case *ast.ExprStmt:
	case *ast.CallExpr:
		v.onCall(x)
	case nil:
		top := v.nodes[len(v.nodes)-1]
		v.nodes = v.nodes[:len(v.nodes)-1]
		if _, ok := top.(*ast.FuncDecl); !ok {
			return nil
		}
		v.scopes = v.scopes[:len(v.scopes)-1]
		for name, methods := range v.used {
			iface := interfaceMatching(methods)
			if iface == "" {
				continue
			}
			if iface == v.args[name].String() {
				continue
			}
			pos := v.fset.Position(top.Pos())
			fmt.Fprintf(v.w, "%s:%d: %s can be %s\n",
				pos.Filename, pos.Line, name, iface)
		}
		v.args = nil
		v.used = nil
	default:
		return nil
	}
	if node != nil {
		v.nodes = append(v.nodes, node)
	}
	return v
}

func getType(scope *types.Scope, name string) interface{} {
	if scope == nil {
		return nil
	}
	obj := scope.Lookup(name)
	if obj == nil {
		return getType(scope.Parent(), name)
	}
	switch x := obj.(type) {
	case *types.Var:
		return x.Type().String()
	default:
		return nil
	}
}

func (v *Visitor) descType(e ast.Expr) interface{} {
	switch x := e.(type) {
	case *ast.Ident:
		scope := v.scopes[len(v.scopes)-1]
		return getType(scope, x.Name)
	case *ast.BasicLit:
		return x.Kind
	default:
		return nil
	}
}

func (v *Visitor) onCall(c *ast.CallExpr) {
	sel, ok := c.Fun.(*ast.SelectorExpr)
	if !ok {
		return
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return
	}
	right := sel.Sel
	vname := left.Name
	fname := right.Name
	m := method{}
	for _, a := range c.Args {
		m.args = append(m.args, v.descType(a))
	}
	if _, e := v.used[vname]; !e {
		v.used[vname] = make(map[string]method)
	}
	v.used[vname][fname] = m
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"path"
	"runtime"

	"github.com/itchio/butler/comm"

	"gopkg.in/alecthomas/kingpin.v2"
)

// #cgo windows LDFLAGS: -Wl,--allow-multiple-definition -static
import "C"

var (
	version = "head" // set by command-line on CI release builds
	app     = kingpin.New("butler", "Your happy little itch.io helper")

	dlCmd = app.Command("dl", "Download a file (resumes if can, checks hashes)").Hidden()

	untarCmd = app.Command("untar", "Extract a .tar file").Hidden()
	wipeCmd  = app.Command("wipe", "Completely remove a directory (rm -rf)").Hidden()
	dittoCmd = app.Command("ditto", "Create a mirror (incl. symlinks) of a directory into another dir (rsync -az)").Hidden()
	mkdirCmd = app.Command("mkdir", "Create an empty directory and all required parent directories (mkdir -p)").Hidden()

	walkCmd = app.Command("walk", "Print TLC tree for given directory as JSON").Hidden()

	pushCmd   = app.Command("push", "Upload a new build to itch.io. See `butler help push`.")
	fetchCmd  = app.Command("fetch", "Fetch the latest build of a channel from itch.io")
	statusCmd = app.Command("status", "Show status of channels for given target")

	signCmd   = app.Command("sign", "(Advanced) Generate a signature file for a given directory. Useful for integrity checks and remote diff generation.")
	verifyCmd = app.Command("verify", "(Advanced) Use a signature to verify the integrity of a directory")
	diffCmd   = app.Command("diff", "(Advanced) Compute the difference between two directories (fast) or .zip archives (slow). Stores the patch in `patch.pwr`, and a signature in `patch.pwr.sig` for integrity checks and further diff.")
	applyCmd  = app.Command("apply", "(Advanced) Use a patch to patch a directory to a new version")
)

var appArgs = struct {
	json       *bool
	quiet      *bool
	verbose    *bool
	timestamps *bool
	noProgress *bool
	panic      *bool

	identity *string
	address  *string
}{
	app.Flag("json", "Enable machine-readable JSON-lines output").Hidden().Short('j').Bool(),
	app.Flag("quiet", "Hide progress indicators & other extra info").Hidden().Short('q').Bool(),
	app.Flag("verbose", "Be very chatty about what's happening").Short('v').Bool(),
	app.Flag("timestamps", "Prefix all output by timestamps (for logging purposes)").Hidden().Bool(),
	app.Flag("noprogress", "Doesn't show progress bars").Hidden().Bool(),
	app.Flag("panic", "Panic on error").Hidden().Bool(),

	app.Flag("identity", "Path to your itch.io API token").Default(defaultKeyPath()).Short('i').String(),
	app.Flag("address", "itch.io server to talk to").Default("https://itch.io").Short('a').Hidden().String(),
}

var dlArgs = struct {
	url  *string
	dest *string

	thorough *bool
}{
	dlCmd.Arg("url", "Address to download from").Required().String(),
	dlCmd.Arg("dest", "File to write downloaded data to").Required().String(),

	dlCmd.Flag("thorough", "Check all available hashes").Bool(),
}

func defaultKeyPath() string {
	dir := ".itch"
	home := os.Getenv("HOME")
	if home == "" {
		home = os.Getenv("USERPROFILE")
	}

	if runtime.GOOS == "darwin" {
		home = path.Join(home, "Library", "Application Support")
		dir = "itch"
	}

	return path.Join(home, dir, "wharf_creds")
}

var pushArgs = struct {
	src    *string
	target *string
}{
	pushCmd.Arg("src", "Directory to upload. May also be a zip archive (slower)").Required().ExistingFileOrDir(),
	pushCmd.Arg("target", "Where to push, for example 'leafo/xmoon:win-64'. Targets are of the form project:channel, where project is username/game or game_id.").Required().String(),
}

var fetchArgs = struct {
	target *string
	out    *string
}{
	fetchCmd.Arg("target", "Which user/project:channel to fetch from, for example 'leafo/xmoon:win-64'. Targets are of the form project:channel where project is username/game or game_id.").Required().String(),
	fetchCmd.Arg("out", "Directory to fetch and extract build to").Required().String(),
}

var statusArgs = struct {
	target *string
}{
	statusCmd.Arg("target", "Which user/project:channel to fetch from, for example 'leafo/xmoon:win-64'. Targets are of the form project:channel where project is username/game or game_id.").Required().String(),
}

var untarArgs = struct {
	file *string
	dir  *string
}{
	untarCmd.Arg("file", "Path of the .tar archive to extract").Required().String(),
	untarCmd.Flag("dir", "An optional directory to which to extract files (defaults to CWD)").Default(".").Short('d').String(),
}

var wipeArgs = struct {
	path *string
}{
	wipeCmd.Arg("path", "Path to completely remove, including its contents").Required().String(),
}

var mkdirArgs = struct {
	path *string
}{
	mkdirCmd.Arg("path", "Directory to create").Required().String(),
}

var dittoArgs = struct {
	src *string
	dst *string
}{
	dittoCmd.Arg("src", "Directory to mirror").Required().ExistingFileOrDir(),
	dittoCmd.Arg("dst", "Path where to create a mirror").Required().String(),
}

var walkArgs = struct {
	src *string
}{
	walkCmd.Arg("src", "Directory to walk").Required().ExistingFileOrDir(),
}

var diffArgs = struct {
	old   *string
	new   *string
	patch *string

	verify *bool

	quality *int
}{
	diffCmd.Arg("old", "Directory or .zip archive (slower) with older files, or signature file generated from old directory.").Required().String(),
	diffCmd.Arg("new", "Directory or .zip archive (slower) with newer files").Required().ExistingFileOrDir(),
	diffCmd.Arg("patch", "Path to write the patch file (recommended extension is `.pwr`) The signature file will be written to the same path, with .sig added to the end.").Default("patch.pwr").String(),

	diffCmd.Flag("verify", "Make sure generated patch applies cleanly by applying it (slower)").Bool(),

	diffCmd.Flag("quality", "Compression quality").Hidden().Default("1").Int(),
}

var applyArgs = struct {
	patch *string
	old   *string

	dir     *string
	verify  *string
	reverse *string
	inplace *bool
}{
	applyCmd.Arg("patch", "Patch file (.pwr), previously generated with the `diff` command.").Required().ExistingFileOrDir(),
	applyCmd.Arg("old", "Directory to patch").Required().ExistingFileOrDir(),

	applyCmd.Flag("dir", "Directory to create newer files in, instead of working in-place").Short('d').String(),
	applyCmd.Flag("verify", "When given, verifies patch application on-the-fly, and abort if any integrity check fails").String(),
	applyCmd.Flag("reverse", "When given, generates a reverse patch to allow rolling back later, along with its signature").String(),
	applyCmd.Flag("inplace", "Apply patch directly to old directory. Required for safety").Bool(),
}

var verifyArgs = struct {
	signature *string
	output    *string
}{
	verifyCmd.Arg("signature", "Path to read signature file from").Required().String(),
	verifyCmd.Arg("dir", "Path of directory to verify").Required().String(),
}

var signArgs = struct {
	output    *string
	signature *string
}{
	signCmd.Arg("dir", "Path of directory to sign").Required().String(),
	signCmd.Arg("signature", "Path to write signature to").Required().String(),
}

func must(err error) {
	if err != nil {
		comm.Die(err.Error())
	}
}

func main() {
	app.HelpFlag.Short('h')
	app.Version(version)
	app.VersionFlag.Short('V')

	cmd, err := app.Parse(os.Args[1:])
	if *appArgs.timestamps {
		log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
	} else {
		log.SetFlags(0)
	}

	if *appArgs.quiet {
		*appArgs.noProgress = true
		*appArgs.verbose = false
	}

	if !isTerminal() {
		*appArgs.noProgress = true
	}
	comm.Configure(*appArgs.noProgress, *appArgs.quiet, *appArgs.verbose, *appArgs.json, *appArgs.panic)

	switch kingpin.MustParse(cmd, err) {
	case dlCmd.FullCommand():
		dl(*dlArgs.url, *dlArgs.dest)

	case pushCmd.FullCommand():
		push(*pushArgs.src, *pushArgs.target)

	case fetchCmd.FullCommand():
		fetch(*fetchArgs.target, *fetchArgs.out)

	case statusCmd.FullCommand():
		status(*statusArgs.target)

	case untarCmd.FullCommand():
		untar(*untarArgs.file, *untarArgs.dir)

	case wipeCmd.FullCommand():
		wipe(*wipeArgs.path)

	case mkdirCmd.FullCommand():
		mkdir(*mkdirArgs.path)

	case dittoCmd.FullCommand():
		ditto(*dittoArgs.src, *dittoArgs.dst)

	case walkCmd.FullCommand():
		walk(*walkArgs.src)

	case diffCmd.FullCommand():
		diff(*diffArgs.old, *diffArgs.new, *diffArgs.patch, *diffArgs.quality)

	case applyCmd.FullCommand():
		apply(*applyArgs.patch, *applyArgs.old, *applyArgs.dir, *applyArgs.inplace)

	case verifyCmd.FullCommand():
		verify(*verifyArgs.signature, *verifyArgs.output)

	case signCmd.FullCommand():
		sign(*signArgs.output, *signArgs.signature)
	}
}
<commit_msg>Can't decide<commit_after>package main

import (
	"log"
	"os"
	"path"
	"runtime"

	"github.com/itchio/butler/comm"

	"gopkg.in/alecthomas/kingpin.v2"
)

// #cgo windows LDFLAGS: -Wl,--allow-multiple-definition -static
import "C"

var (
	version = "head" // set by command-line on CI release builds
	app     = kingpin.New("butler", "Your happy little itch.io helper")

	dlCmd = app.Command("dl", "Download a file (resumes if can, checks hashes)").Hidden()

	untarCmd = app.Command("untar", "Extract a .tar file").Hidden()
	wipeCmd  = app.Command("wipe", "Completely remove a directory (rm -rf)").Hidden()
	dittoCmd = app.Command("ditto", "Create a mirror (incl. symlinks) of a directory into another dir (rsync -az)").Hidden()
	mkdirCmd = app.Command("mkdir", "Create an empty directory and all required parent directories (mkdir -p)").Hidden()

	walkCmd = app.Command("walk", "Print TLC tree for given directory as JSON").Hidden()

	pushCmd   = app.Command("push", "Upload a new build to itch.io. See `butler help push`.")
	fetchCmd  = app.Command("fetch", "Fetch the latest build of a channel from itch.io")
	statusCmd = app.Command("status", "Show status of channels for given target")

	signCmd   = app.Command("sign", "(Advanced) Generate a signature file for a given directory. Useful for integrity checks and remote diff generation.")
	verifyCmd = app.Command("verify", "(Advanced) Use a signature to verify the integrity of a directory")
	diffCmd   = app.Command("diff", "(Advanced) Compute the difference between two directories (fast) or .zip archives (slow). Stores the patch in `patch.pwr`, and a signature in `patch.pwr.sig` for integrity checks and further diff.")
	applyCmd  = app.Command("apply", "(Advanced) Use a patch to patch a directory to a new version")
)

var appArgs = struct {
	json       *bool
	quiet      *bool
	verbose    *bool
	timestamps *bool
	noProgress *bool
	panic      *bool

	identity *string
	address  *string
}{
	app.Flag("json", "Enable machine-readable JSON-lines output").Hidden().Short('j').Bool(),
	app.Flag("quiet", "Hide progress indicators & other extra info").Hidden().Short('q').Bool(),
	app.Flag("verbose", "Be very chatty about what's happening").Short('v').Bool(),
	app.Flag("timestamps", "Prefix all output by timestamps (for logging purposes)").Hidden().Bool(),
	app.Flag("noprogress", "Doesn't show progress bars").Hidden().Bool(),
	app.Flag("panic", "Panic on error").Hidden().Bool(),

	app.Flag("identity", "Path to your itch.io API token").Default(defaultKeyPath()).Short('i').String(),
	app.Flag("address", "itch.io server to talk to").Default("https://itch.io").Short('a').Hidden().String(),
}

var dlArgs = struct {
	url  *string
	dest *string

	thorough *bool
}{
	dlCmd.Arg("url", "Address to download from").Required().String(),
	dlCmd.Arg("dest", "File to write downloaded data to").Required().String(),

	dlCmd.Flag("thorough", "Check all available hashes").Bool(),
}

func defaultKeyPath() string {
	dir := ".itch"
	home := os.Getenv("HOME")
	if home == "" {
		home = os.Getenv("USERPROFILE")
	}

	if runtime.GOOS == "darwin" {
		home = path.Join(home, "Library", "Application Support")
		dir = "itch"
	}

	return path.Join(home, dir, "butler_creds")
}

var pushArgs = struct {
	src    *string
	target *string
}{
	pushCmd.Arg("src", "Directory to upload. May also be a zip archive (slower)").Required().ExistingFileOrDir(),
	pushCmd.Arg("target", "Where to push, for example 'leafo/xmoon:win-64'. Targets are of the form project:channel, where project is username/game or game_id.").Required().String(),
}

var fetchArgs = struct {
	target *string
	out    *string
}{
	fetchCmd.Arg("target", "Which user/project:channel to fetch from, for example 'leafo/xmoon:win-64'. Targets are of the form project:channel where project is username/game or game_id.").Required().String(),
	fetchCmd.Arg("out", "Directory to fetch and extract build to").Required().String(),
}

var statusArgs = struct {
	target *string
}{
	statusCmd.Arg("target", "Which user/project:channel to fetch from, for example 'leafo/xmoon:win-64'. Targets are of the form project:channel where project is username/game or game_id.").Required().String(),
}

var untarArgs = struct {
	file *string
	dir  *string
}{
	untarCmd.Arg("file", "Path of the .tar archive to extract").Required().String(),
	untarCmd.Flag("dir", "An optional directory to which to extract files (defaults to CWD)").Default(".").Short('d').String(),
}

var wipeArgs = struct {
	path *string
}{
	wipeCmd.Arg("path", "Path to completely remove, including its contents").Required().String(),
}

var mkdirArgs = struct {
	path *string
}{
	mkdirCmd.Arg("path", "Directory to create").Required().String(),
}

var dittoArgs = struct {
	src *string
	dst *string
}{
	dittoCmd.Arg("src", "Directory to mirror").Required().ExistingFileOrDir(),
	dittoCmd.Arg("dst", "Path where to create a mirror").Required().String(),
}

var walkArgs = struct {
	src *string
}{
	walkCmd.Arg("src", "Directory to walk").Required().ExistingFileOrDir(),
}

var diffArgs = struct {
	old   *string
	new   *string
	patch *string

	verify *bool

	quality *int
}{
	diffCmd.Arg("old", "Directory or .zip archive (slower) with older files, or signature file generated from old directory.").Required().String(),
	diffCmd.Arg("new", "Directory or .zip archive (slower) with newer files").Required().ExistingFileOrDir(),
	diffCmd.Arg("patch", "Path to write the patch file (recommended extension is `.pwr`) The signature file will be written to the same path, with .sig added to the end.").Default("patch.pwr").String(),

	diffCmd.Flag("verify", "Make sure generated patch applies cleanly by applying it (slower)").Bool(),

	diffCmd.Flag("quality", "Compression quality").Hidden().Default("1").Int(),
}

var applyArgs = struct {
	patch *string
	old   *string

	dir     *string
	verify  *string
	reverse *string
	inplace *bool
}{
	applyCmd.Arg("patch", "Patch file (.pwr), previously generated with the `diff` command.").Required().ExistingFileOrDir(),
	applyCmd.Arg("old", "Directory to patch").Required().ExistingFileOrDir(),

	applyCmd.Flag("dir", "Directory to create newer files in, instead of working in-place").Short('d').String(),
	applyCmd.Flag("verify", "When given, verifies patch application on-the-fly, and abort if any integrity check fails").String(),
	applyCmd.Flag("reverse", "When given, generates a reverse patch to allow rolling back later, along with its signature").String(),
	applyCmd.Flag("inplace", "Apply patch directly to old directory. Required for safety").Bool(),
}

var verifyArgs = struct {
	signature *string
	output    *string
}{
	verifyCmd.Arg("signature", "Path to read signature file from").Required().String(),
	verifyCmd.Arg("dir", "Path of directory to verify").Required().String(),
}

var signArgs = struct {
	output    *string
	signature *string
}{
	signCmd.Arg("dir", "Path of directory to sign").Required().String(),
	signCmd.Arg("signature", "Path to write signature to").Required().String(),
}

func must(err error) {
	if err != nil {
		comm.Die(err.Error())
	}
}

func main() {
	app.HelpFlag.Short('h')
	app.Version(version)
	app.VersionFlag.Short('V')

	cmd, err := app.Parse(os.Args[1:])
	if *appArgs.timestamps {
		log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
	} else {
		log.SetFlags(0)
	}

	if *appArgs.quiet {
		*appArgs.noProgress = true
		*appArgs.verbose = false
	}

	if !isTerminal() {
		*appArgs.noProgress = true
	}
	comm.Configure(*appArgs.noProgress, *appArgs.quiet, *appArgs.verbose, *appArgs.json, *appArgs.panic)

	switch kingpin.MustParse(cmd, err) {
	case dlCmd.FullCommand():
		dl(*dlArgs.url, *dlArgs.dest)

	case pushCmd.FullCommand():
		push(*pushArgs.src, *pushArgs.target)

	case fetchCmd.FullCommand():
		fetch(*fetchArgs.target, *fetchArgs.out)

	case statusCmd.FullCommand():
		status(*statusArgs.target)

	case untarCmd.FullCommand():
		untar(*untarArgs.file, *untarArgs.dir)

	case wipeCmd.FullCommand():
		wipe(*wipeArgs.path)

	case mkdirCmd.FullCommand():
		mkdir(*mkdirArgs.path)

	case dittoCmd.FullCommand():
		ditto(*dittoArgs.src, *dittoArgs.dst)

	case walkCmd.FullCommand():
		walk(*walkArgs.src)

	case diffCmd.FullCommand():
		diff(*diffArgs.old, *diffArgs.new, *diffArgs.patch, *diffArgs.quality)

	case applyCmd.FullCommand():
		apply(*applyArgs.patch, *applyArgs.old, *applyArgs.dir, *applyArgs.inplace)

	case verifyCmd.FullCommand():
		verify(*verifyArgs.signature, *verifyArgs.output)

	case signCmd.FullCommand():
		sign(*signArgs.output, *signArgs.signature)
	}
}
<|endoftext|>


<commit_before>package main

// #include "HelloWorld.h"
import "C"

import "fmt"

func main() {
	helloWorld()
}

// helloWorld calls the hello_world() function defined in the HelloWorld.c
// file, converts the character array to a native Go string, and prints it.
func helloWorld() {
	ptr := C.hello_world()
	fmt.Println(C.GoString(ptr))
}
<commit_msg>Updated comment<commit_after>package main

// #include "HelloWorld.h"
import "C"

import "fmt"

func main() {
	helloWorld()
}

// helloWorld calls the hello_world() function defined in the HelloWorld.c
// file, converts the character array that is returned to a native Go
// string, and writes it to stdout.
func helloWorld() {
	ptr := C.hello_world()
	fmt.Println(C.GoString(ptr))
}
<|endoftext|>


<commit_before>package main

import (
  "crypto/x509"
  "encoding/pem"
  "fmt"
  "io"
  "io/ioutil"
  "os"
  "path/filepath"
//  "code.google.com/p/go.crypto/ssh/terminal"
//  "math/big"
//  "time"
)

const (
  RsaLowerLength = 2048
  RsaUpperLength = 4096
  TypeLabelRSA   = "RSA PRIVATE KEY"
  TypeLabelECDSA = "EC PRIVATE KEY"
  TypeLabelCSR   = "CERTIFICATE REQUEST"
)

var (
  EcdsaLength = []int{224, 256, 384, 521}
)

func main() {
  if len(os.Args) == 1 {
    crash_with_help(1, "No module selected!")
  }
  switch os.Args[1] {
  case "create-private": create_private_key()
  case "create-cert-sign": create_sign_request()
  case "help": print_modules()
  case "info": info_on_file()
  case "sign": sign_request()
  default: crash_with_help(1, "Command not supported!")
  }
}

// get information on file (private key, sign request, certificate, ...)
func info_on_file() {}
// sign a certificate request to create a new certificate
func sign_request() {}

// load the private key stored at `path`
func load_private_key(path string) PrivateKey {
  if path == "" {
    crash_with_help(2, "No path to private key supplied!")
  }

  file, err := os.Open(path)
  if err != nil {
    crash_with_help(3, fmt.Sprintf("Error when opening private key: %s", err))
  }
  defer file.Close()

  data, err := ioutil.ReadAll(file)
  if err != nil {
    crash_with_help(3, fmt.Sprintf("Error when reading private key: %s", err))
  }

  block, _ := pem.Decode(data)
  if block.Type == TypeLabelRSA {
    return load_private_key_rsa(block)
  } else if block.Type == TypeLabelECDSA {
    return load_private_key_ecdsa(block)
  } else {
    crash_with_help(2, "No valid private key file! Only RSA and ECDSA keys are allowed!")
    return nil
  }
}

func load_private_key_rsa(block *pem.Block) PrivateKey {
  key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
  if err != nil {
    crash_with_help(3, fmt.Sprintf("Error parsing private key: %s", err))
  }
  return key
}
func load_private_key_ecdsa(block *pem.Block) PrivateKey {
  key, err := x509.ParseECPrivateKey(block.Bytes)
  if err != nil {
    crash_with_help(3, fmt.Sprintf("Error parsing private key: %s", err))
  }
  return key
}

// open stream for given path
func open_output_stream(path string) (io.WriteCloser, error) {
  if path == "STDOUT" {
    return os.Stdout, nil
  } else {
    var err error
    output_stream, err := os.OpenFile(path, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0600)
    if err != nil {
      return nil, err
    }
    return output_stream, nil
  }
}

// print the module help
func print_modules() {
  fmt.Printf(`Usage: %s command args
where 'command' is one of:
    create-private    create a new private key
    create-cert-sign  create a new certificate sign request
    help              show this help
    info              get info on a file
    sign              sign a certificate request
`, filepath.Base(os.Args[0]))
  fmt.Println()
}

func crash_with_help(code int, message string) {
  fmt.Fprintln(os.Stderr, message)
  print_modules()
  os.Exit(code)
}

//  fmt.Println("Lets create a cert!")
//  template := &x509.Certificate{
//    SerialNumber:  big.NewInt(1),
//    Subject: pkix.Name{
//      Organization: []string{"Acme Co"},
//    },
//    NotBefore: time.Now(),
//    NotAfter: time.Now().Add(365 * 24 * time.Hour),
//    KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
//    ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
//    BasicConstraintsValid: true,
//  }
//  priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
//  fmt.Println(priv.PublicKey, err)
//  raw_string, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
//  cert, err := x509.ParseCertificate(raw_string)
//  fmt.Println(cert, err)
//
//  // read a password or so
//  password, err := terminal.ReadPassword(0)
//  fmt.Println(string(password), err)
//}
<commit_msg>remove old code from comments<commit_after>package main

import (
  "crypto/x509"
  "encoding/pem"
  "fmt"
  "io"
  "io/ioutil"
  "os"
  "path/filepath"
)

const (
  RsaLowerLength = 2048
  RsaUpperLength = 4096
  TypeLabelRSA   = "RSA PRIVATE KEY"
  TypeLabelECDSA = "EC PRIVATE KEY"
  TypeLabelCSR   = "CERTIFICATE REQUEST"
)

var (
  EcdsaLength = []int{224, 256, 384, 521}
)

func main() {
  if len(os.Args) == 1 {
    crash_with_help(1, "No module selected!")
  }
  switch os.Args[1] {
  case "create-private": create_private_key()
  case "create-cert-sign": create_sign_request()
  case "help": print_modules()
  case "info": info_on_file()
  case "sign": sign_request()
  default: crash_with_help(1, "Command not supported!")
  }
}

// get information on file (private key, sign request, certificate, ...)
func info_on_file() {}
// sign a certificate request to create a new certificate
func sign_request() {}

// load the private key stored at `path`
func load_private_key(path string) PrivateKey {
  if path == "" {
    crash_with_help(2, "No path to private key supplied!")
  }

  file, err := os.Open(path)
  if err != nil {
    crash_with_help(3, fmt.Sprintf("Error when opening private key: %s", err))
  }
  defer file.Close()

  data, err := ioutil.ReadAll(file)
  if err != nil {
    crash_with_help(3, fmt.Sprintf("Error when reading private key: %s", err))
  }

  block, _ := pem.Decode(data)
  if block.Type == TypeLabelRSA {
    return load_private_key_rsa(block)
  } else if block.Type == TypeLabelECDSA {
    return load_private_key_ecdsa(block)
  } else {
    crash_with_help(2, "No valid private key file! Only RSA and ECDSA keys are allowed!")
    return nil
  }
}

func load_private_key_rsa(block *pem.Block) PrivateKey {
  key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
  if err != nil {
    crash_with_help(3, fmt.Sprintf("Error parsing private key: %s", err))
  }
  return key
}
func load_private_key_ecdsa(block *pem.Block) PrivateKey {
  key, err := x509.ParseECPrivateKey(block.Bytes)
  if err != nil {
    crash_with_help(3, fmt.Sprintf("Error parsing private key: %s", err))
  }
  return key
}

// open stream for given path
func open_output_stream(path string) (io.WriteCloser, error) {
  if path == "STDOUT" {
    return os.Stdout, nil
  } else {
    var err error
    output_stream, err := os.OpenFile(path, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0600)
    if err != nil {
      return nil, err
    }
    return output_stream, nil
  }
}

// print the module help
func print_modules() {
  fmt.Printf(`Usage: %s command args
where 'command' is one of:
    create-private    create a new private key
    create-cert-sign  create a new certificate sign request
    help              show this help
    info              get info on a file
    sign              sign a certificate request
`, filepath.Base(os.Args[0]))
  fmt.Println()
}

func crash_with_help(code int, message string) {
  fmt.Fprintln(os.Stderr, message)
  print_modules()
  os.Exit(code)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"math/rand"
	"net"
	"os"
	"runtime"
	"sync"
	"sync/atomic"
	"time"
)

// global transactionID, infohash and peerID counters
var (
	tid *uint32
	ih  *uint64
	pid *uint64
	cid *uint32
)

//flags
var (
	timeout    int
	requests   int64
	clients    int
	target     string
	keepAlive  bool
	scrapeMode bool
)

type Configuration struct {
	url        string
	requests   int64
	period     time.Duration
	keepAlive  bool
	scrapeMode bool
}

type Result struct {
	connectAttempts int64
	failedConnects  int64
	requests        int64
	success         int64
	failed          int64
}

type Client struct {
	conn     net.Conn
	result   *Result
	infohash *uint64
	peerId   *uint64
	transId  *uint32
}

func init() {
	flag.IntVar(&timeout, "t", 0, "Period of testing (in seconds)")
	flag.Int64Var(&requests, "r", -1, "Number of requests per client, -1=unlimited")
	flag.IntVar(&clients, "c", 100, "Number of concurrent clients")
	flag.StringVar(&target, "u", "", "Target URL (e.g. 127.0.0.1:12345 or tracker.example.org:1337)")
	flag.BoolVar(&keepAlive, "k", false, "Re-use connection IDs")
	flag.BoolVar(&scrapeMode, "s", false, "Scrape instead of announcing")

	var infoh uint64 = uint64(rand.Int63())
	ih = &infoh

	var peerid uint64 = uint64(rand.Int63())
	pid = &peerid

	var transid uint32 = rand.Uint32()
	tid = &transid

	var c uint32
	cid = &c
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.Parse()

	c := newConfig()
	t := make(chan struct{})
	if c.period > 0 {
		go func() {
			<-time.After(c.period)
			close(t)
		}()
	}

	wg := &sync.WaitGroup{}
	results := make([]*Result, clients)

	fmt.Printf("Dispatching %d clients\n", clients)
	wg.Add(clients)
	startTime := time.Now()
	for i := 0; i < clients; i++ {
		result := &Result{}
		results[i] = result

		//fmt.Printf("Starting client %d...\n", i)

		conn, err := net.Dial("udp4", c.url)
		if err != nil {
			panic(err)
		}

		client := &Client{
			conn:     conn,
			result:   result,
			infohash: ih,
			peerId:   pid,
			transId:  tid,
		}

		go client.do(c, t, wg)
	}

	fmt.Println("Waiting for results...")
	wg.Wait()
	printResults(results, startTime)
}

func newConfig() *Configuration {
	if target == "" {
		flag.Usage()
		os.Exit(1)
	}

	if requests <= 0 && timeout <= 0 {
		flag.Usage()
		os.Exit(1)
	}

	if requests > 0 && timeout > 0 {
		flag.Usage()
		os.Exit(1)
	}

	configuration := &Configuration{
		url:        target,
		requests:   int64((1 << 63) - 1),
		period:     time.Duration(0) * time.Second,
		keepAlive:  keepAlive,
		scrapeMode: scrapeMode,
	}

	if timeout > 0 {
		configuration.period = time.Duration(timeout) * time.Second
	}

	if requests != -1 {
		configuration.requests = requests
	}

	return configuration
}

func printResults(results []*Result, startTime time.Time) {
	var requests int64
	var success int64
	var failed int64
	var connectAttempts int64
	var failedConnects int64

	for _, result := range results {
		requests += result.requests
		success += result.success
		failed += result.failed
		connectAttempts += result.connectAttempts
		failedConnects += result.failedConnects
	}

	elapsed := time.Since(startTime).Seconds()

	if elapsed == 0 {
		elapsed = 1
	}

	fmt.Println()
	fmt.Printf("Requests:                       %10d\n", requests)
	fmt.Printf("Successful requests:            %10d\n", success)
	fmt.Printf("failed requests:                %10d\n", failed)
	fmt.Printf("Connect attempts:               %10d\n", connectAttempts)
	fmt.Printf("Failed connects:                %10d\n", failedConnects)
	fmt.Printf("Successful requests rate:       %10.0f hits/sec\n", float64(success)/elapsed)
	fmt.Printf("Test time:                      %10.2f sec\n", elapsed)
}

func (c *Client) do(conf *Configuration, t chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()

	//clientId := atomic.AddUint32(cid, 1)

	/*defer func() {
		fmt.Printf("client %d done\n", clientId)
	}()*/

	//prepare packet
	packet, err := prepareAnnounce()

	//prepare a receive buffer
	buf := make([]byte, 1024)

	//get a transaction ID
	transactionID := atomic.AddUint32(c.transId, 1)

	var connId uint64
	connIdExpires := time.After(time.Duration(0))

	//perform requests
loop:
	for c.result.requests < conf.requests {
		//check if time has expired
		select {
		case <-t:
			return
		default:
		}

		c.conn.SetReadDeadline(time.Now().Add(2 * time.Second))

		//check if we have to (re)connect
		if conf.keepAlive {
			select {
			case <-connIdExpires:
				//(re)connect
				transactionID = atomic.AddUint32(c.transId, 1)
				connId, err = c.connect(transactionID)
				if err != nil {
					connIdExpires = time.After(time.Duration(0)) //we want to try this again ASAP
					continue loop
				}
				connIdExpires = time.After(time.Minute)
			default: //still valid
			}
		} else {
			//reconnect
			transactionID = atomic.AddUint32(c.transId, 1)
			connId, err = c.connect(transactionID)
			if err != nil {
				continue loop
			}
		}

		transactionID = atomic.AddUint32(c.transId, 1)
		infohash := atomic.AddUint64(c.infohash, 1)
		peerId := atomic.AddUint64(c.peerId, 1)

		err = c.announce(buf, packet, transactionID, connId, infohash, peerId)
		c.result.requests++
		if err != nil {
			//fmt.Println(err)
			c.result.failed++
		} else {
			c.result.success++
		}
	}
}

func prepareAnnounce() ([]byte, error) {
	bbuf := bytes.NewBuffer(nil)

	// connection id
	_, err := bbuf.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0})
	if err != nil {
		return nil, err
	}

	// action
	_, err = bbuf.Write([]byte{0, 0, 0, 0x01})
	if err != nil {
		return nil, err
	}

	// transaction ID
	_, err = bbuf.Write([]byte{0, 0, 0, 0})
	if err != nil {
		return nil, err
	}

	// infohash
	_, err = bbuf.WriteString("xxxxxxxxxxxxxxxxxxxx")
	if err != nil {
		return nil, err
	}

	// peer ID
	_, err = bbuf.WriteString("xxxxxxxxxxxxxxxxxxxx")
	if err != nil {
		return nil, err
	}

	// downloaded
	_, err = bbuf.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0})
	if err != nil {
		return nil, err
	}

	// left
	_, err = bbuf.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0x01})
	if err != nil {
		return nil, err
	}

	// uploaded
	_, err = bbuf.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0})
	if err != nil {
		return nil, err
	}

	// event
	_, err = bbuf.Write([]byte{0, 0, 0, 0x02})
	if err != nil {
		return nil, err
	}

	// IP Address
	_, err = bbuf.Write([]byte{0, 0, 0, 0})
	if err != nil {
		return nil, err
	}

	// key (?)
	_, err = bbuf.Write([]byte{0, 0, 0, 0})
	if err != nil {
		return nil, err
	}

	// numwant
	_, err = bbuf.Write([]byte{0xFF, 0xFF, 0xFF, 0xFF})
	if err != nil {
		return nil, err
	}

	// port
	_, err = bbuf.Write([]byte{0x05, 0x00})
	if err != nil {
		return nil, err
	}

	return bbuf.Bytes(), nil
}

func (c *Client) announce(buf, packet []byte, transactionID uint32, connectionID, infohash, peerID uint64) error {
	binary.BigEndian.PutUint64(packet[0:8], connectionID)
	binary.BigEndian.PutUint32(packet[12:16], transactionID)

	infohashString := fmt.Sprintf("%020x", infohash)
	peerIdString := fmt.Sprintf("%020x", peerID)

	for i := 0; i < 20; i++ {
		packet[16+i] = infohashString[i] //put infohash
		packet[36+i] = peerIdString[i]   //put peerID
	}

	//send
	n, err := c.conn.Write(packet)
	if err != nil {
		return err
	}
	if n != 98 {
		return errors.New("announce: Did not send 98 bytes")
	}

	//receive response
	n, err = c.conn.Read(buf)
	if err != nil {
		return err
	}
	if n < 20 {
		return errors.New("announce: Did not receive at least 20 bytes")
	}

	// parse action
	action := binary.BigEndian.Uint32(buf[:4])
	if action != 1 {
		if action == 3 {
			errVal := string(buf[8:n])
			return fmt.Errorf("announce: tracker responded with error: %s", errVal)
		}
		return errors.New("announce: tracker responded with action != 1")
	}

	transID := binary.BigEndian.Uint32(buf[4:8])
	if transID != transactionID {
		return errors.New("announce: transaction IDs do not match")
	}

	return nil
}

func (c *Client) connect(transactionID uint32) (u uint64, err error) {
	c.result.connectAttempts++
	defer func() {
		if err != nil {
			c.result.failedConnects++
		}
	}()
	buf := make([]byte, 16)

	buf[2] = 0x04
	buf[3] = 0x17
	buf[4] = 0x27
	buf[5] = 0x10
	buf[6] = 0x19
	buf[7] = 0x80

	binary.BigEndian.PutUint32(buf[12:16], transactionID)

	n, err := c.conn.Write(buf)
	if err != nil {
		return 0, err
	}

	if n != 16 {
		return 0, errors.New("connect: Did not send 16 bytes")
	}

	buf = make([]byte, 64)
	n, err = c.conn.Read(buf)
	if err != nil {
		return 0, err
	}

	if n != 16 {
		return 0, errors.New("connect: Did not receive 16 bytes")
	}

	b := buf[:n]

	action := binary.BigEndian.Uint32(b[:4])
	if action != 0 {
		return 0, errors.New("connect: action != 0")
	}

	transID := binary.BigEndian.Uint32(b[4:8])
	if transID != transactionID {
		return 0, errors.New("connect: transaction IDs do not match")
	}

	connID := binary.BigEndian.Uint64(b[8:16])
	return connID, nil
}
<commit_msg>improved error reporting<commit_after>package main

import (
	"bytes"
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"math/rand"
	"net"
	"os"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

// global transactionID, infohash and peerID counters
var (
	tid *uint32
	ih  *uint64
	pid *uint64
	cid *uint32
)

//flags
var (
	timeout    int
	requests   int64
	clients    int
	target     string
	keepAlive  bool
	scrapeMode bool
)

type Configuration struct {
	url        string
	requests   int64
	period     time.Duration
	keepAlive  bool
	scrapeMode bool
}

type Result struct {
	connectAttempts int64
	failedConnects  int64
	requests        int64
	success         int64
	failed          int64
	errors          map[string]int64
}

type Client struct {
	conn     net.Conn
	result   *Result
	infohash *uint64
	peerId   *uint64
	transId  *uint32
}

func init() {
	flag.IntVar(&timeout, "t", 0, "Period of testing (in seconds)")
	flag.Int64Var(&requests, "r", -1, "Number of requests per client, -1=unlimited")
	flag.IntVar(&clients, "c", 100, "Number of concurrent clients")
	flag.StringVar(&target, "u", "", "Target URL (e.g. 127.0.0.1:12345 or tracker.example.org:1337)")
	flag.BoolVar(&keepAlive, "k", false, "Re-use connection IDs")
	flag.BoolVar(&scrapeMode, "s", false, "Scrape instead of announcing")

	var infoh uint64 = uint64(rand.Int63())
	ih = &infoh

	var peerid uint64 = uint64(rand.Int63())
	pid = &peerid

	var transid uint32 = rand.Uint32()
	tid = &transid

	var c uint32
	cid = &c
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.Parse()

	c := newConfig()
	t := make(chan struct{})
	if c.period > 0 {
		go func() {
			<-time.After(c.period)
			close(t)
		}()
	}

	wg := &sync.WaitGroup{}
	results := make([]*Result, clients)

	fmt.Printf("Dispatching %d clients\n", clients)
	wg.Add(clients)
	startTime := time.Now()
	for i := 0; i < clients; i++ {
		result := &Result{
			errors: make(map[string]int64),
		}
		results[i] = result

		//fmt.Printf("Starting client %d...\n", i)

		conn, err := net.Dial("udp4", c.url)
		if err != nil {
			panic(err)
		}

		client := &Client{
			conn:     conn,
			result:   result,
			infohash: ih,
			peerId:   pid,
			transId:  tid,
		}

		go client.do(c, t, wg)
	}

	fmt.Println("Waiting for results...")
	wg.Wait()
	printResults(results, startTime)
}

func newConfig() *Configuration {
	if target == "" {
		flag.Usage()
		os.Exit(1)
	}

	if requests <= 0 && timeout <= 0 {
		flag.Usage()
		os.Exit(1)
	}

	if requests > 0 && timeout > 0 {
		flag.Usage()
		os.Exit(1)
	}

	configuration := &Configuration{
		url:        target,
		requests:   int64((1 << 63) - 1),
		period:     time.Duration(0) * time.Second,
		keepAlive:  keepAlive,
		scrapeMode: scrapeMode,
	}

	if timeout > 0 {
		configuration.period = time.Duration(timeout) * time.Second
	}

	if requests != -1 {
		configuration.requests = requests
	}

	return configuration
}

func printResults(results []*Result, startTime time.Time) {
	var requests int64
	var success int64
	var failed int64
	var connectAttempts int64
	var failedConnects int64
	errors := make(map[string]int64)

	for _, result := range results {
		requests += result.requests
		success += result.success
		failed += result.failed
		connectAttempts += result.connectAttempts
		failedConnects += result.failedConnects
		for err, count := range result.errors {
			_, exists := errors[err]
			if !exists {
				errors[err] = 0
			}
			errors[err] += count
		}
	}

	elapsed := time.Since(startTime).Seconds()

	if elapsed == 0 {
		elapsed = 1
	}

	fmt.Println()
	fmt.Printf("Requests:                       %10d\n", requests)
	fmt.Printf("Successful requests:            %10d\n", success)
	fmt.Printf("failed requests:                %10d\n", failed)
	fmt.Printf("Connect attempts:               %10d\n", connectAttempts)
	fmt.Printf("Failed connects:                %10d\n", failedConnects)
	fmt.Printf("Successful requests rate:       %10.0f hits/sec\n", float64(success)/elapsed)
	fmt.Printf("Test time:                      %10.2f sec\n", elapsed)
	fmt.Println("Encountere errors:")
	for err, count := range errors {
		fmt.Printf("    %30s  %10d\n", err, count)
	}
}

func (r *Result) incrementError(err string) {
	_, exists := r.errors[err]
	if !exists {
		r.errors[err] = 0
	}
	r.errors[err]++
}

func (c *Client) do(conf *Configuration, t chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()

	//clientId := atomic.AddUint32(cid, 1)

	/*defer func() {
		fmt.Printf("client %d done\n", clientId)
	}()*/

	//prepare packet
	packet, err := prepareAnnounce()

	//prepare a receive buffer
	buf := make([]byte, 1024)

	//get a transaction ID
	transactionID := atomic.AddUint32(c.transId, 1)

	var connId uint64
	connIdExpires := time.After(time.Duration(0))

	//perform requests
loop:
	for c.result.requests < conf.requests {
		//check if time has expired
		select {
		case <-t:
			return
		default:
		}

		c.conn.SetReadDeadline(time.Now().Add(2 * time.Second))

		//check if we have to (re)connect
		if conf.keepAlive {
			select {
			case <-connIdExpires:
				//(re)connect
				transactionID = atomic.AddUint32(c.transId, 1)
				connId, err = c.connect(transactionID)
				if err != nil {
					c.result.incrementError(err.Error())
					connIdExpires = time.After(time.Duration(0)) //we want to try this again ASAP
					continue loop
				}
				connIdExpires = time.After(time.Minute)
			default: //still valid
			}
		} else {
			//reconnect
			transactionID = atomic.AddUint32(c.transId, 1)
			connId, err = c.connect(transactionID)
			if err != nil {
				c.result.incrementError(err.Error())
				continue loop
			}
		}

		transactionID = atomic.AddUint32(c.transId, 1)
		infohash := atomic.AddUint64(c.infohash, 1)
		peerId := atomic.AddUint64(c.peerId, 1)

		err = c.announce(buf, packet, transactionID, connId, infohash, peerId)
		c.result.requests++
		if err != nil {
			c.result.incrementError(err.Error())
			c.result.failed++
		} else {
			c.result.success++
		}
	}
}

func prepareAnnounce() ([]byte, error) {
	bbuf := bytes.NewBuffer(nil)

	// connection id
	_, err := bbuf.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0})
	if err != nil {
		return nil, err
	}

	// action
	_, err = bbuf.Write([]byte{0, 0, 0, 0x01})
	if err != nil {
		return nil, err
	}

	// transaction ID
	_, err = bbuf.Write([]byte{0, 0, 0, 0})
	if err != nil {
		return nil, err
	}

	// infohash
	_, err = bbuf.WriteString("xxxxxxxxxxxxxxxxxxxx")
	if err != nil {
		return nil, err
	}

	// peer ID
	_, err = bbuf.WriteString("xxxxxxxxxxxxxxxxxxxx")
	if err != nil {
		return nil, err
	}

	// downloaded
	_, err = bbuf.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0})
	if err != nil {
		return nil, err
	}

	// left
	_, err = bbuf.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0x01})
	if err != nil {
		return nil, err
	}

	// uploaded
	_, err = bbuf.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0})
	if err != nil {
		return nil, err
	}

	// event
	_, err = bbuf.Write([]byte{0, 0, 0, 0x02})
	if err != nil {
		return nil, err
	}

	// IP Address
	_, err = bbuf.Write([]byte{0, 0, 0, 0})
	if err != nil {
		return nil, err
	}

	// key (?)
	_, err = bbuf.Write([]byte{0, 0, 0, 0})
	if err != nil {
		return nil, err
	}

	// numwant
	_, err = bbuf.Write([]byte{0xFF, 0xFF, 0xFF, 0xFF})
	if err != nil {
		return nil, err
	}

	// port
	_, err = bbuf.Write([]byte{0x05, 0x00})
	if err != nil {
		return nil, err
	}

	return bbuf.Bytes(), nil
}

func (c *Client) announce(buf, packet []byte, transactionID uint32, connectionID, infohash, peerID uint64) error {
	binary.BigEndian.PutUint64(packet[0:8], connectionID)
	binary.BigEndian.PutUint32(packet[12:16], transactionID)

	infohashString := fmt.Sprintf("%020x", infohash)
	peerIdString := fmt.Sprintf("%020x", peerID)

	for i := 0; i < 20; i++ {
		packet[16+i] = infohashString[i] //put infohash
		packet[36+i] = peerIdString[i]   //put peerID
	}

	//send
	n, err := c.conn.Write(packet)
	if err != nil {
		return err
	}
	if n != 98 {
		return errors.New("announce: Did not send 98 bytes")
	}

	//receive response
	n, err = c.conn.Read(buf)
	if err != nil {
		if strings.HasSuffix(err.Error(), "i/o timeout") {
			return errors.New("announce: I/O timeout on receive")
		}
		return fmt.Errorf("announce: %s", err)
	}
	if n < 20 {
		return errors.New("announce: Did not receive at least 20 bytes")
	}

	// parse action
	action := binary.BigEndian.Uint32(buf[:4])
	if action != 1 {
		if action == 3 {
			errVal := string(buf[8:n])
			return fmt.Errorf("announce: tracker responded with error: %s", errVal)
		}
		return errors.New("announce: tracker responded with action != 1")
	}

	transID := binary.BigEndian.Uint32(buf[4:8])
	if transID != transactionID {
		return errors.New("announce: transaction IDs do not match")
	}

	return nil
}

func (c *Client) connect(transactionID uint32) (u uint64, err error) {
	c.result.connectAttempts++
	defer func() {
		if err != nil {
			c.result.failedConnects++
		}
	}()
	buf := make([]byte, 16)

	buf[2] = 0x04
	buf[3] = 0x17
	buf[4] = 0x27
	buf[5] = 0x10
	buf[6] = 0x19
	buf[7] = 0x80

	binary.BigEndian.PutUint32(buf[12:16], transactionID)

	n, err := c.conn.Write(buf)
	if err != nil {
		return 0, err
	}

	if n != 16 {
		return 0, errors.New("connect: Did not send 16 bytes")
	}

	buf = make([]byte, 64)
	n, err = c.conn.Read(buf)
	if err != nil {
		if strings.HasSuffix(err.Error(), "i/o timeout") {
			return 0, errors.New("connect: I/O timeout on receive")
		}
		return 0, fmt.Errorf("connect: %s", err)
	}

	if n != 16 {
		return 0, errors.New("connect: Did not receive 16 bytes")
	}

	b := buf[:n]

	action := binary.BigEndian.Uint32(b[:4])
	if action != 0 {
		return 0, errors.New("connect: action != 0")
	}

	transID := binary.BigEndian.Uint32(b[4:8])
	if transID != transactionID {
		return 0, errors.New("connect: transaction IDs do not match")
	}

	connID := binary.BigEndian.Uint64(b[8:16])
	return connID, nil
}
<|endoftext|>


<commit_before>// Search engine for IPFS using Elasticsearch, RabbitMQ and Tika.
package main

import (
	"context"
	"fmt"
	"github.com/ipfs-search/ipfs-search/commands"
	"github.com/ipfs-search/ipfs-search/config"
	"gopkg.in/urfave/cli.v1"
	"log"
	"os"
	"os/signal"
	"syscall"
)

func main() {
	// Prefix logging with filename and line number: "d.go:23"
	// log.SetFlags(log.Lshortfile)

	// Logging w/o prefix
	log.SetFlags(0)

	app := cli.NewApp()
	app.Name = "ipfs-search"
	app.Usage = "IPFS search engine."

	app.Commands = []cli.Command{
		{
			Name:    "add",
			Aliases: []string{"a"},
			Usage:   "add `HASH` to crawler queue",
			Action:  add,
		},
		{
			Name:    "crawl",
			Aliases: []string{"c"},
			Usage:   "start crawler",
			Action:  crawl,
		},
		{
			Name:    "config",
			Aliases: []string{},
			Usage:   "configuration",
			Subcommands: []cli.Command{
				{
					Name:   "generate",
					Usage:  "generate default configuration",
					Action: generateConfig,
				},
				{
					Name:   "check",
					Usage:  "check configuration",
					Action: checkConfig,
				},
				{
					Name:   "dump",
					Usage:  "dump current configuration to stdout",
					Action: dumpConfig,
				},
			},
		},
	}

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "config, c",
			Usage: "Load configuration from `FILE`",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

func getConfig(c *cli.Context) (*config.Config, error) {
	configFile := c.GlobalString("config")

	cfg, err := config.Get(configFile)
	if err != nil {
		return nil, err
	}

	err = cfg.Check()
	if err != nil {
		return nil, err
	}

	return cfg, nil
}

func checkConfig(c *cli.Context) error {
	_, err := getConfig(c)
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	fmt.Println("Configuration checked.")

	return nil
}

func generateConfig(c *cli.Context) error {
	cfg := config.Default()

	configFile := c.GlobalString("config")
	if configFile == "" {
		return cli.NewExitError("Configuration file not specified. Use the \"-c\" option.", 1)
	}

	fmt.Printf("Writing default configuration to: %s\n", configFile)
	return cfg.Write(configFile)
}

func dumpConfig(c *cli.Context) error {
	cfg, err := getConfig(c)
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	return cfg.Dump()
}

func add(c *cli.Context) error {
	ctx, cancel := context.WithCancel(context.Background())

	// Allow SIGTERM / Control-C quit through context
	onSigTerm(cancel)

	if c.NArg() != 1 {
		return cli.NewExitError("Please supply one hash as argument.", 1)
	}
	hash := c.Args().Get(0)

	cfg, err := getConfig(c)
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	fmt.Printf("Adding hash '%s' to queue\n", hash)

	err = commands.AddHash(ctx, cfg, hash)
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	return nil
}

// onSigTerm calls f() when SIGTERM (control-C) is received
func onSigTerm(f func()) {
	sigChan := make(chan os.Signal, 2)
	signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)

	var fail = func() {
		<-sigChan
		os.Exit(1)
	}

	var quit = func() {
		<-sigChan

		go fail()

		fmt.Println("Received SIGTERM, quitting... One more SIGTERM and we'll abort!")
		f()
	}

	go quit()
}

func crawl(c *cli.Context) error {
	fmt.Println("Starting worker")

	ctx, cancel := context.WithCancel(context.Background())

	// Allow SIGTERM / Control-C quit through context
	onSigTerm(cancel)

	cfg, err := getConfig(c)
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	err = commands.Crawl(ctx, cfg)
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	return nil
}
<commit_msg>Formatting.<commit_after>// Search engine for IPFS using Elasticsearch, RabbitMQ and Tika.
package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/ipfs-search/ipfs-search/commands"
	"github.com/ipfs-search/ipfs-search/config"
	"gopkg.in/urfave/cli.v1"
)

func main() {
	// Prefix logging with filename and line number: "d.go:23"
	// log.SetFlags(log.Lshortfile)

	// Logging w/o prefix
	log.SetFlags(0)

	app := cli.NewApp()
	app.Name = "ipfs-search"
	app.Usage = "IPFS search engine."

	app.Commands = []cli.Command{
		{
			Name:    "add",
			Aliases: []string{"a"},
			Usage:   "add `HASH` to crawler queue",
			Action:  add,
		},
		{
			Name:    "crawl",
			Aliases: []string{"c"},
			Usage:   "start crawler",
			Action:  crawl,
		},
		{
			Name:    "config",
			Aliases: []string{},
			Usage:   "configuration",
			Subcommands: []cli.Command{
				{
					Name:   "generate",
					Usage:  "generate default configuration",
					Action: generateConfig,
				},
				{
					Name:   "check",
					Usage:  "check configuration",
					Action: checkConfig,
				},
				{
					Name:   "dump",
					Usage:  "dump current configuration to stdout",
					Action: dumpConfig,
				},
			},
		},
	}

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "config, c",
			Usage: "Load configuration from `FILE`",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

func getConfig(c *cli.Context) (*config.Config, error) {
	configFile := c.GlobalString("config")

	cfg, err := config.Get(configFile)
	if err != nil {
		return nil, err
	}

	err = cfg.Check()
	if err != nil {
		return nil, err
	}

	return cfg, nil
}

func checkConfig(c *cli.Context) error {
	_, err := getConfig(c)
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	fmt.Println("Configuration checked.")

	return nil
}

func generateConfig(c *cli.Context) error {
	cfg := config.Default()

	configFile := c.GlobalString("config")
	if configFile == "" {
		return cli.NewExitError("Configuration file not specified. Use the \"-c\" option.", 1)
	}

	fmt.Printf("Writing default configuration to: %s\n", configFile)
	return cfg.Write(configFile)
}

func dumpConfig(c *cli.Context) error {
	cfg, err := getConfig(c)
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	return cfg.Dump()
}

func add(c *cli.Context) error {
	ctx, cancel := context.WithCancel(context.Background())

	// Allow SIGTERM / Control-C quit through context
	onSigTerm(cancel)

	if c.NArg() != 1 {
		return cli.NewExitError("Please supply one hash as argument.", 1)
	}
	hash := c.Args().Get(0)

	cfg, err := getConfig(c)
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	fmt.Printf("Adding hash '%s' to queue\n", hash)

	err = commands.AddHash(ctx, cfg, hash)
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	return nil
}

// onSigTerm calls f() when SIGTERM (control-C) is received
func onSigTerm(f func()) {
	sigChan := make(chan os.Signal, 2)
	signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)

	var fail = func() {
		<-sigChan
		os.Exit(1)
	}

	var quit = func() {
		<-sigChan

		go fail()

		fmt.Println("Received SIGTERM, quitting... One more SIGTERM and we'll abort!")
		f()
	}

	go quit()
}

func crawl(c *cli.Context) error {
	fmt.Println("Starting worker")

	ctx, cancel := context.WithCancel(context.Background())

	// Allow SIGTERM / Control-C quit through context
	onSigTerm(cancel)

	cfg, err := getConfig(c)
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	err = commands.Crawl(ctx, cfg)
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/tj/go-spin"
)

var (
	outputPath = flag.String("out", "", "Specify a `dir` for the output.")
	noCGO      = flag.Bool("nocgo", false, "Do not include a cgo-specific header in resulting files.")
	ccDefs     = flag.Bool("ccdefs", false, "Use built-in defines from a hosted C-compiler.")
	ccIncl     = flag.Bool("ccincl", false, "Use built-in sys include paths from a hosted C-compiler.")
	maxMem     = flag.String("maxmem", "0x7fffffff", "Specifies platform's memory cap the generated code.")
	fancy      = flag.Bool("fancy", true, "Enable fancy output in the term.")
	nostamp    = flag.Bool("nostamp", false, "Disable printing timestamps in the output files.")
	debug      = flag.Bool("debug", false, "Enable some debug info.")
)

const logo = `Copyright (c) 2015-2017 Maxim Kupriianov <max@kc.vc>
Based on a C99 compiler front end by Jan Mercl <0xjnml@gmail.com>
`

func init() {
	if *debug {
		log.SetFlags(log.Lshortfile)
	} else {
		log.SetFlags(0)
	}
	flag.Usage = func() {
		fmt.Println(logo)
		fmt.Printf("Usage: %s package1.yml [package2.yml] ...\n", os.Args[0])
		fmt.Println("See https://github.com/xlab/c-for-go for examples and documentation.\n")
		fmt.Println("Options:")
		flag.PrintDefaults()
	}
	flag.Parse()
	if len(flag.Args()) == 0 {
		flag.Usage()
		fmt.Println()
		log.Fatalln("[ERR] no package configuration files have been provided.")
	}
}

func main() {
	s := spin.New()

	var wg sync.WaitGroup
	doneChan := make(chan struct{})
	for _, cfgPath := range getConfigPaths() {
		if *fancy {
			wg.Add(1)
			go func() {
				for {
					select {
					case <-doneChan:
						doneChan = make(chan struct{})
						fmt.Printf("\r  \033[36mprocessing %s\033[m done.\n", cfgPath)
						wg.Done()
						return
					default:
						fmt.Printf("\r  \033[36mprocessing %s\033[m %s", cfgPath, s.Next())
						time.Sleep(100 * time.Millisecond)
					}
				}
			}()
		}

		var t0 time.Time
		if *debug {
			t0 = time.Now()
		}
		process, err := NewProcess(cfgPath, *outputPath)
		if err != nil {
			log.Fatalln("[ERR]", err)
		}
		process.Generate(*noCGO)
		if err := process.Flush(*noCGO); err != nil {
			log.Fatalln("[ERR]", err)
		}
		if *debug {
			fmt.Printf("done in %v\n", time.Now().Sub(t0))
		}
		if *fancy {
			close(doneChan)
			wg.Wait()
		}
	}
}

func getConfigPaths() (paths []string) {
	for _, path := range flag.Args() {
		if info, err := os.Stat(path); err != nil {
			log.Fatalln("[ERR] cannot locate the specified path:", path)
		} else if info.IsDir() {
			if path, ok := configFromDir(path); ok {
				paths = append(paths, path)
				continue
			}
			log.Fatalln("[ERR] cannot find any config file in:", path)
		}
		paths = append(paths, path)
	}
	return
}

func configFromDir(path string) (string, bool) {
	possibleNames := []string{"c-for-go.yaml", "c-for-go.yml"}
	if base := filepath.Base(path); len(base) > 0 {
		possibleNames = append(possibleNames,
			fmt.Sprintf("%s.yaml", base), fmt.Sprintf("%s.yml", base))
	}
	for _, name := range possibleNames {
		path := filepath.Join(path, name)
		if info, err := os.Stat(path); err == nil && !info.IsDir() {
			return path, true
		}
	}
	return "", false
}
<commit_msg>use explicit double-\n to avoid golint warning<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/tj/go-spin"
)

var (
	outputPath = flag.String("out", "", "Specify a `dir` for the output.")
	noCGO      = flag.Bool("nocgo", false, "Do not include a cgo-specific header in resulting files.")
	ccDefs     = flag.Bool("ccdefs", false, "Use built-in defines from a hosted C-compiler.")
	ccIncl     = flag.Bool("ccincl", false, "Use built-in sys include paths from a hosted C-compiler.")
	maxMem     = flag.String("maxmem", "0x7fffffff", "Specifies platform's memory cap the generated code.")
	fancy      = flag.Bool("fancy", true, "Enable fancy output in the term.")
	nostamp    = flag.Bool("nostamp", false, "Disable printing timestamps in the output files.")
	debug      = flag.Bool("debug", false, "Enable some debug info.")
)

const logo = `Copyright (c) 2015-2017 Maxim Kupriianov <max@kc.vc>
Based on a C99 compiler front end by Jan Mercl <0xjnml@gmail.com>
`

func init() {
	if *debug {
		log.SetFlags(log.Lshortfile)
	} else {
		log.SetFlags(0)
	}
	flag.Usage = func() {
		fmt.Println(logo)
		fmt.Printf("Usage: %s package1.yml [package2.yml] ...\n", os.Args[0])
		fmt.Printf("See https://github.com/xlab/c-for-go for examples and documentation.\n\n")
		fmt.Println("Options:")
		flag.PrintDefaults()
	}
	flag.Parse()
	if len(flag.Args()) == 0 {
		flag.Usage()
		fmt.Println()
		log.Fatalln("[ERR] no package configuration files have been provided.")
	}
}

func main() {
	s := spin.New()

	var wg sync.WaitGroup
	doneChan := make(chan struct{})
	for _, cfgPath := range getConfigPaths() {
		if *fancy {
			wg.Add(1)
			go func() {
				for {
					select {
					case <-doneChan:
						doneChan = make(chan struct{})
						fmt.Printf("\r  \033[36mprocessing %s\033[m done.\n", cfgPath)
						wg.Done()
						return
					default:
						fmt.Printf("\r  \033[36mprocessing %s\033[m %s", cfgPath, s.Next())
						time.Sleep(100 * time.Millisecond)
					}
				}
			}()
		}

		var t0 time.Time
		if *debug {
			t0 = time.Now()
		}
		process, err := NewProcess(cfgPath, *outputPath)
		if err != nil {
			log.Fatalln("[ERR]", err)
		}
		process.Generate(*noCGO)
		if err := process.Flush(*noCGO); err != nil {
			log.Fatalln("[ERR]", err)
		}
		if *debug {
			fmt.Printf("done in %v\n", time.Now().Sub(t0))
		}
		if *fancy {
			close(doneChan)
			wg.Wait()
		}
	}
}

func getConfigPaths() (paths []string) {
	for _, path := range flag.Args() {
		if info, err := os.Stat(path); err != nil {
			log.Fatalln("[ERR] cannot locate the specified path:", path)
		} else if info.IsDir() {
			if path, ok := configFromDir(path); ok {
				paths = append(paths, path)
				continue
			}
			log.Fatalln("[ERR] cannot find any config file in:", path)
		}
		paths = append(paths, path)
	}
	return
}

func configFromDir(path string) (string, bool) {
	possibleNames := []string{"c-for-go.yaml", "c-for-go.yml"}
	if base := filepath.Base(path); len(base) > 0 {
		possibleNames = append(possibleNames,
			fmt.Sprintf("%s.yaml", base), fmt.Sprintf("%s.yml", base))
	}
	for _, name := range possibleNames {
		path := filepath.Join(path, name)
		if info, err := os.Stat(path); err == nil && !info.IsDir() {
			return path, true
		}
	}
	return "", false
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"runtime/debug"
	"strings"
)

// Version is the version of the v4 cli.
// This is set by a build flag in the `Rakefile`.
// If it is set to `dev` it will not autoupdate.
var Version = "dev"

// Channel is the git branch the code was compiled on.
// This is set by a build flag in the `Rakefile` based on the git branch.
var Channel = "?"

var cli = &Cli{}

// BuiltinPlugins are the core plugins that will be autoinstalled
var BuiltinPlugins = []string{
	"heroku-apps",
	"heroku-cli-addons",
	"heroku-fork",
	"heroku-git",
	"heroku-local",
	"heroku-orgs",
	"heroku-pipelines",
	"heroku-run",
	"heroku-spaces",
	"heroku-status",
}

func init() {
	cli.Topics = TopicSet{
		authTopic,
		commandsTopic,
		debugTopic,
		loginTopic,
		logoutTopic,
		pluginsTopic,
		twoFactorTopic,
		twoFactorTopicAlias,
		updateTopic,
		versionTopic,
		whichTopic,
		whoamiTopic,
	}
	cli.Commands = CommandSet{
		authLoginCmd,
		authLogoutCmd,
		authTokenCmd,
		commandsListCmd,
		debugErrlogCmd,
		loginCmd,
		logoutCmd,
		pluginsInstallCmd,
		pluginsLinkCmd,
		pluginsListCmd,
		pluginsUninstallCmd,
		twoFactorCmd,
		twoFactorCmdAlias,
		twoFactorDisableCmd,
		twoFactorDisableCmdAlias,
		twoFactorGenerateCmd,
		twoFactorGenerateCmdAlias,
		updateCmd,
		versionCmd,
		whichCmd,
		whoamiAuthCmd,
		whoamiCmd,
	}
}

func main() {
	defer handlePanic()
	handleSignal(os.Interrupt, func() {
		if !swallowSigint {
			showCursor()
			os.Exit(1)
		}
	})
	runtime.GOMAXPROCS(1) // more procs causes runtime: failed to create new OS thread on Ubuntu
	ShowDebugInfo()
	if !(len(os.Args) >= 2 && os.Args[1] == "update") {
		// skip blocking update if the command is to update
		// otherwise it will update twice
		Update(Channel, "block")
	}
	SubmitAnalytics()
	SetupNode()

	// try running as a core command
	cli.Run(os.Args, false)

	// Command wasn't found so load the plugins and try again
	SetupBuiltinPlugins()
	TriggerBackgroundUpdate()
	cli.LoadPlugins(GetPlugins())
	cli.Run(os.Args, true)
	Exit(0)
}

func handlePanic() {
	if rec := recover(); rec != nil {
		err, ok := rec.(error)
		if !ok {
			err = errors.New(rec.(string))
		}
		Errln("ERROR:", err)
		if Channel == "" {
			debug.PrintStack()
		} else {
			rollbar(err, "critical")
		}
		TriggerBackgroundUpdate()
		Exit(1)
	}
}

// ShowDebugInfo prints debugging information if HEROKU_DEBUG=1
func ShowDebugInfo() {
	if !isDebugging() {
		return
	}
	info := []string{version(), binPath}
	if len(os.Args) > 1 {
		info = append(info, fmt.Sprintf("cmd: %s", os.Args[1]))
	}
	proxy := getProxy()
	if proxy != nil {
		info = append(info, fmt.Sprintf("proxy: %s", proxy))
	}
	Debugln(strings.Join(info, " "))
}

func getProxy() *url.URL {
	req, err := http.NewRequest("GET", "https://api.heroku.com", nil)
	WarnIfError(err)
	proxy, err := http.ProxyFromEnvironment(req)
	WarnIfError(err)
	return proxy
}
<commit_msg>fix debug channel<commit_after>package main

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"runtime/debug"
	"strings"
)

// Version is the version of the v4 cli.
// This is set by a build flag in the `Rakefile`.
// If it is set to `dev` it will not autoupdate.
var Version = "dev"

// Channel is the git branch the code was compiled on.
// This is set by a build flag in the `Rakefile` based on the git branch.
var Channel = "?"

var cli = &Cli{}

// BuiltinPlugins are the core plugins that will be autoinstalled
var BuiltinPlugins = []string{
	"heroku-apps",
	"heroku-cli-addons",
	"heroku-fork",
	"heroku-git",
	"heroku-local",
	"heroku-orgs",
	"heroku-pipelines",
	"heroku-run",
	"heroku-spaces",
	"heroku-status",
}

func init() {
	cli.Topics = TopicSet{
		authTopic,
		commandsTopic,
		debugTopic,
		loginTopic,
		logoutTopic,
		pluginsTopic,
		twoFactorTopic,
		twoFactorTopicAlias,
		updateTopic,
		versionTopic,
		whichTopic,
		whoamiTopic,
	}
	cli.Commands = CommandSet{
		authLoginCmd,
		authLogoutCmd,
		authTokenCmd,
		commandsListCmd,
		debugErrlogCmd,
		loginCmd,
		logoutCmd,
		pluginsInstallCmd,
		pluginsLinkCmd,
		pluginsListCmd,
		pluginsUninstallCmd,
		twoFactorCmd,
		twoFactorCmdAlias,
		twoFactorDisableCmd,
		twoFactorDisableCmdAlias,
		twoFactorGenerateCmd,
		twoFactorGenerateCmdAlias,
		updateCmd,
		versionCmd,
		whichCmd,
		whoamiAuthCmd,
		whoamiCmd,
	}
}

func main() {
	defer handlePanic()
	handleSignal(os.Interrupt, func() {
		if !swallowSigint {
			showCursor()
			os.Exit(1)
		}
	})
	runtime.GOMAXPROCS(1) // more procs causes runtime: failed to create new OS thread on Ubuntu
	ShowDebugInfo()
	if !(len(os.Args) >= 2 && os.Args[1] == "update") {
		// skip blocking update if the command is to update
		// otherwise it will update twice
		Update(Channel, "block")
	}
	SubmitAnalytics()
	SetupNode()

	// try running as a core command
	cli.Run(os.Args, false)

	// Command wasn't found so load the plugins and try again
	SetupBuiltinPlugins()
	TriggerBackgroundUpdate()
	cli.LoadPlugins(GetPlugins())
	cli.Run(os.Args, true)
	Exit(0)
}

func handlePanic() {
	if rec := recover(); rec != nil {
		err, ok := rec.(error)
		if !ok {
			err = errors.New(rec.(string))
		}
		Errln("ERROR:", err)
		if Channel == "?" {
			debug.PrintStack()
		} else {
			rollbar(err, "critical")
		}
		TriggerBackgroundUpdate()
		Exit(1)
	}
}

// ShowDebugInfo prints debugging information if HEROKU_DEBUG=1
func ShowDebugInfo() {
	if !isDebugging() {
		return
	}
	info := []string{version(), binPath}
	if len(os.Args) > 1 {
		info = append(info, fmt.Sprintf("cmd: %s", os.Args[1]))
	}
	proxy := getProxy()
	if proxy != nil {
		info = append(info, fmt.Sprintf("proxy: %s", proxy))
	}
	Debugln(strings.Join(info, " "))
}

func getProxy() *url.URL {
	req, err := http.NewRequest("GET", "https://api.heroku.com", nil)
	WarnIfError(err)
	proxy, err := http.ProxyFromEnvironment(req)
	WarnIfError(err)
	return proxy
}
<|endoftext|>


<commit_before>package main

import (
	_ "expvar"
	"net/http"
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/pat"
	"github.com/meatballhat/negroni-logrus"
	"github.com/phyber/negroni-gzip/gzip"
	"gitlab.com/mattbostock/timeoff/handler"
)

var (
	addr = os.Getenv("ADDR")
	m    = pat.New()
	n    = negroni.New(negroni.NewRecovery(), negroni.NewStatic(http.Dir("assets")))
	l    = negronilogrus.NewMiddleware()
)

func init() {
	n.Use(gzip.Gzip(gzip.BestCompression))
	n.Use(l)
	n.UseHandler(m)
	handler.SetLogger(l.Logger)

	if len(os.Getenv("DEBUG")) > 0 {
		l.Logger.Level = logrus.DebugLevel
	}

	m.Add("GET", "/debug/vars", http.DefaultServeMux)

	if addr == "" {
		addr = ":3000"
	}
}

func main() {
	tlsCert := os.Getenv("TLS_CERT")
	tlsKey := os.Getenv("TLS_KEY")

	l.Logger.Infof("Listening on %s", addr)

	if tlsCert == "" && tlsKey == "" {
		l.Logger.Warningln(noTLSCertificateError)
		l.Logger.Fatal(http.ListenAndServe(addr, n))
	} else {
		l.Logger.Infoln("Listening with TLS")
		l.Logger.Fatal(http.ListenAndServeTLS(addr, tlsCert, tlsKey, n))
	}
}

const noTLSCertificateError = "No TLS certficiate supplied. Consider setting TLS_CERT " +
	"and TLS_KEY environment variables to enable TLS."
<commit_msg>Move all configuration env vars to top<commit_after>package main

import (
	_ "expvar"
	"net/http"
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/pat"
	"github.com/meatballhat/negroni-logrus"
	"github.com/phyber/negroni-gzip/gzip"
	"gitlab.com/mattbostock/timeoff/handler"
)

var (
	addr    = os.Getenv("ADDR")
	debug   = len(os.Getenv("DEBUG")) > 0
	m       = pat.New()
	n       = negroni.New(negroni.NewRecovery(), negroni.NewStatic(http.Dir("assets")))
	l       = negronilogrus.NewMiddleware()
	tlsCert = os.Getenv("TLS_CERT")
	tlsKey  = os.Getenv("TLS_KEY")
)

func init() {
	n.Use(gzip.Gzip(gzip.BestCompression))
	n.Use(l)
	n.UseHandler(m)
	handler.SetLogger(l.Logger)

	if debug {
		l.Logger.Level = logrus.DebugLevel
	}

	m.Add("GET", "/debug/vars", http.DefaultServeMux)

	if addr == "" {
		addr = ":3000"
	}
}

func main() {
	l.Logger.Infof("Listening on %s", addr)

	if tlsCert == "" && tlsKey == "" {
		l.Logger.Warningln(noTLSCertificateError)
		l.Logger.Fatal(http.ListenAndServe(addr, n))
	} else {
		l.Logger.Infoln("Listening with TLS")
		l.Logger.Fatal(http.ListenAndServeTLS(addr, tlsCert, tlsKey, n))
	}
}

const noTLSCertificateError = "No TLS certficiate supplied. Consider setting TLS_CERT " +
	"and TLS_KEY environment variables to enable TLS."
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"runtime"
	"sync"

	pickle "github.com/kisielk/og-rek"
)

var Debug int

var Config = struct {
	Backends []string
	MaxProcs int
	Port     int

	mu          sync.RWMutex
	metricPaths map[string][]string
}{
	MaxProcs:    1,
	Port:        8080,
	metricPaths: make(map[string][]string),
}

type serverResponse struct {
	server   string
	response []byte
}

func multiGet(servers []string, uri string) []serverResponse {

	ch := make(chan serverResponse)

	for _, server := range servers {
		go func(server string, ch chan<- serverResponse) {

			u, err := url.Parse(server + uri)
			if err != nil {
				log.Fatal(err)
			}
			req := http.Request{
				URL:    u,
				Header: make(http.Header),
			}

			if Debug > 2 {
				d, _ := httputil.DumpRequest(&req, false)
				log.Println(string(d))
			}

			resp, err := http.DefaultClient.Do(&req)

			if Debug > 2 {
				d, _ := httputil.DumpResponse(resp, true)
				log.Println(string(d))
			}

			if err != nil || resp.StatusCode != 200 {
				log.Println("got status code", resp.StatusCode, "while querying", server)
				ch <- serverResponse{server, nil}
			}
			defer resp.Body.Close()

			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				ch <- serverResponse{server, nil}
			}

			ch <- serverResponse{server, body}
		}(server, ch)
	}

	var response []serverResponse

	for i := 0; i < len(servers); i++ {
		r := <-ch
		if r.response != nil {
			response = append(response, r)
		}
	}

	return response
}

func findHandler(w http.ResponseWriter, req *http.Request) {

	responses := multiGet(Config.Backends, req.URL.RequestURI())

	// metric -> [server1, ... ]
	paths := make(map[string][]string)

	var metrics []map[interface{}]interface{}
	for _, r := range responses {
		d := pickle.NewDecoder(bytes.NewReader(r.response))
		metric, err := d.Decode()
		if err != nil {
			log.Println("error during decode:", err)
			if Debug > 1 {
				log.Println("\n" + hex.Dump(r.response))
			}
			continue
		}

		for _, m := range metric.([]interface{}) {
			mm := m.(map[interface{}]interface{})
			name := mm["metric_path"].(string)
			p, ok := paths[name]
			if !ok {
				// we haven't seen this name yet
				// add the metric to the list of metrics to return
				metrics = append(metrics, mm)
			}
			// add the server to the list of servers that know about this metric
			p = append(p, r.server)
			paths[name] = p
		}
	}

	// update our cache of which servers have which metrics
	Config.mu.Lock()
	for k, v := range paths {
		Config.metricPaths[k] = v
	}
	Config.mu.Unlock()

	w.Header().Set("Content-Type", "application/pickle")

	pEnc := pickle.NewEncoder(w)
	pEnc.Encode(metrics)
}

func renderHandler(w http.ResponseWriter, req *http.Request) {

	req.ParseForm()
	target := req.FormValue("target")

	var serverList []string
	var ok bool

	Config.mu.RLock()
	// lookup the server list for this metric, or use all the servers if it's unknown
	if serverList, ok = Config.metricPaths[target]; !ok {
		serverList = Config.Backends
	}
	Config.mu.RUnlock()

	responses := multiGet(serverList, req.URL.RequestURI())

	if len(responses) == 1 {
		w.Header().Set("Content-Type", "application/pickle")
		w.Write(responses[0].response)
	}

	// decode everything
	var decoded [][]interface{}
	for _, r := range responses {
		d := pickle.NewDecoder(bytes.NewReader(r.response))
		metric, err := d.Decode()
		if err != nil {
			log.Println("error during decode:", err)
			continue
		}

		marray := metric.([]interface{})
		decoded = append(decoded, marray)
	}

	if len(decoded) == 1 {
		w.Header().Set("Content-Type", "application/pickle")
		w.Write(responses[0].response)
	}

	// TODO: check len(d) == 1
	base := decoded[0][0].(map[interface{}]interface{})
	values := base["values"].([]interface{})

	for i := 0; i < len(values); i++ {
		if _, ok := values[i].(pickle.None); ok {
			// find one in the other values arrays
		replacenone:
			for other := 1; other < len(decoded); other++ {
				m := decoded[other][0].(map[interface{}]interface{})
				ovalues := m["values"].([]interface{})
				if _, ok := ovalues[i].(pickle.None); !ok {
					values[i] = ovalues[i]
					break replacenone
				}
			}
		}
	}

	// the first response is where we've been filling in our data, so we're ok just to serialize it as our response
	w.Header().Set("Content-Type", "application/pickle")
	e := pickle.NewEncoder(w)
	e.Encode(decoded[0])
}

func main() {

	configFile := flag.String("c", "", "config file (json)")
	port := flag.Int("p", 0, "port to listen on")
	maxprocs := flag.Int("maxprocs", 0, "GOMAXPROCS")
	flag.IntVar(&Debug, "d", 0, "enable debug logging")

	flag.Parse()

	if *configFile == "" {
		log.Fatal("missing config file")
	}

	cfgjs, err := ioutil.ReadFile(*configFile)
	if err != nil {
		log.Fatal("unable to load config file:", err)
	}

	// strip out the comment header block that begins with '#'
	// as soon as we see a line that starts with something _other_ than '#', we're done
	idx := 0
	for cfgjs[0] == '#' {
		idx = bytes.Index(cfgjs, []byte("\n"))
		if idx == -1 || idx+1 == len(cfgjs) {
			log.Fatal("error removing header comment from ", *configFile)
		}
		cfgjs = cfgjs[idx+1:]
	}

	err = json.Unmarshal(cfgjs, &Config)
	if err != nil {
		log.Fatal("error parsing config file: ", err)
	}

	// command line overrides config file

	if *port != 0 {
		Config.Port = *port
	}

	if *maxprocs != 0 {
		Config.MaxProcs = *maxprocs
	}

	log.Println("setting GOMAXPROCS=", Config.MaxProcs)
	runtime.GOMAXPROCS(Config.MaxProcs)

	http.HandleFunc("/metrics/find/", findHandler)
	http.HandleFunc("/render/", renderHandler)

	portStr := fmt.Sprintf(":%d", Config.Port)
	log.Println("listening on", portStr)
	log.Fatal(http.ListenAndServe(portStr, nil))
}
<commit_msg>complain loudly if we have no backends in the config file<commit_after>package main

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"runtime"
	"sync"

	pickle "github.com/kisielk/og-rek"
)

var Debug int

var Config = struct {
	Backends []string
	MaxProcs int
	Port     int

	mu          sync.RWMutex
	metricPaths map[string][]string
}{
	MaxProcs:    1,
	Port:        8080,
	metricPaths: make(map[string][]string),
}

type serverResponse struct {
	server   string
	response []byte
}

func multiGet(servers []string, uri string) []serverResponse {

	ch := make(chan serverResponse)

	for _, server := range servers {
		go func(server string, ch chan<- serverResponse) {

			u, err := url.Parse(server + uri)
			if err != nil {
				log.Fatal(err)
			}
			req := http.Request{
				URL:    u,
				Header: make(http.Header),
			}

			if Debug > 2 {
				d, _ := httputil.DumpRequest(&req, false)
				log.Println(string(d))
			}

			resp, err := http.DefaultClient.Do(&req)

			if Debug > 2 {
				d, _ := httputil.DumpResponse(resp, true)
				log.Println(string(d))
			}

			if err != nil || resp.StatusCode != 200 {
				log.Println("got status code", resp.StatusCode, "while querying", server)
				ch <- serverResponse{server, nil}
			}
			defer resp.Body.Close()

			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				ch <- serverResponse{server, nil}
			}

			ch <- serverResponse{server, body}
		}(server, ch)
	}

	var response []serverResponse

	for i := 0; i < len(servers); i++ {
		r := <-ch
		if r.response != nil {
			response = append(response, r)
		}
	}

	return response
}

func findHandler(w http.ResponseWriter, req *http.Request) {

	responses := multiGet(Config.Backends, req.URL.RequestURI())

	// metric -> [server1, ... ]
	paths := make(map[string][]string)

	var metrics []map[interface{}]interface{}
	for _, r := range responses {
		d := pickle.NewDecoder(bytes.NewReader(r.response))
		metric, err := d.Decode()
		if err != nil {
			log.Println("error during decode:", err)
			if Debug > 1 {
				log.Println("\n" + hex.Dump(r.response))
			}
			continue
		}

		for _, m := range metric.([]interface{}) {
			mm := m.(map[interface{}]interface{})
			name := mm["metric_path"].(string)
			p, ok := paths[name]
			if !ok {
				// we haven't seen this name yet
				// add the metric to the list of metrics to return
				metrics = append(metrics, mm)
			}
			// add the server to the list of servers that know about this metric
			p = append(p, r.server)
			paths[name] = p
		}
	}

	// update our cache of which servers have which metrics
	Config.mu.Lock()
	for k, v := range paths {
		Config.metricPaths[k] = v
	}
	Config.mu.Unlock()

	w.Header().Set("Content-Type", "application/pickle")

	pEnc := pickle.NewEncoder(w)
	pEnc.Encode(metrics)
}

func renderHandler(w http.ResponseWriter, req *http.Request) {

	req.ParseForm()
	target := req.FormValue("target")

	var serverList []string
	var ok bool

	Config.mu.RLock()
	// lookup the server list for this metric, or use all the servers if it's unknown
	if serverList, ok = Config.metricPaths[target]; !ok {
		serverList = Config.Backends
	}
	Config.mu.RUnlock()

	responses := multiGet(serverList, req.URL.RequestURI())

	if len(responses) == 1 {
		w.Header().Set("Content-Type", "application/pickle")
		w.Write(responses[0].response)
	}

	// decode everything
	var decoded [][]interface{}
	for _, r := range responses {
		d := pickle.NewDecoder(bytes.NewReader(r.response))
		metric, err := d.Decode()
		if err != nil {
			log.Println("error during decode:", err)
			continue
		}

		marray := metric.([]interface{})
		decoded = append(decoded, marray)
	}

	if len(decoded) == 1 {
		w.Header().Set("Content-Type", "application/pickle")
		w.Write(responses[0].response)
	}

	// TODO: check len(d) == 1
	base := decoded[0][0].(map[interface{}]interface{})
	values := base["values"].([]interface{})

	for i := 0; i < len(values); i++ {
		if _, ok := values[i].(pickle.None); ok {
			// find one in the other values arrays
		replacenone:
			for other := 1; other < len(decoded); other++ {
				m := decoded[other][0].(map[interface{}]interface{})
				ovalues := m["values"].([]interface{})
				if _, ok := ovalues[i].(pickle.None); !ok {
					values[i] = ovalues[i]
					break replacenone
				}
			}
		}
	}

	// the first response is where we've been filling in our data, so we're ok just to serialize it as our response
	w.Header().Set("Content-Type", "application/pickle")
	e := pickle.NewEncoder(w)
	e.Encode(decoded[0])
}

func main() {

	configFile := flag.String("c", "", "config file (json)")
	port := flag.Int("p", 0, "port to listen on")
	maxprocs := flag.Int("maxprocs", 0, "GOMAXPROCS")
	flag.IntVar(&Debug, "d", 0, "enable debug logging")

	flag.Parse()

	if *configFile == "" {
		log.Fatal("missing config file")
	}

	cfgjs, err := ioutil.ReadFile(*configFile)
	if err != nil {
		log.Fatal("unable to load config file:", err)
	}

	// strip out the comment header block that begins with '#'
	// as soon as we see a line that starts with something _other_ than '#', we're done
	idx := 0
	for cfgjs[0] == '#' {
		idx = bytes.Index(cfgjs, []byte("\n"))
		if idx == -1 || idx+1 == len(cfgjs) {
			log.Fatal("error removing header comment from ", *configFile)
		}
		cfgjs = cfgjs[idx+1:]
	}

	err = json.Unmarshal(cfgjs, &Config)
	if err != nil {
		log.Fatal("error parsing config file: ", err)
	}

	if len(Config.Backends) == 0 {
		log.Fatal("no Backends loaded -- exiting")
	}

	// command line overrides config file

	if *port != 0 {
		Config.Port = *port
	}

	if *maxprocs != 0 {
		Config.MaxProcs = *maxprocs
	}

	log.Println("setting GOMAXPROCS=", Config.MaxProcs)
	runtime.GOMAXPROCS(Config.MaxProcs)

	http.HandleFunc("/metrics/find/", findHandler)
	http.HandleFunc("/render/", renderHandler)

	portStr := fmt.Sprintf(":%d", Config.Port)
	log.Println("listening on", portStr)
	log.Fatal(http.ListenAndServe(portStr, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/mitchellh/cli"
)

var (
	ui        *cli.ColoredUi
	regLength = 32
	binStr    string
)

func initUi() error {
	ui = new(cli.ColoredUi)
	if ui == nil {
		fmt.Printf("error of ui\n")
		return errors.New("failed to new cli")
	}

	bui := new(cli.BasicUi)
	bui.Reader = os.Stdin
	bui.Writer = os.Stdout
	bui.ErrorWriter = os.Stderr

	ui.Ui = bui
	ui.OutputColor = cli.UiColorNone
	ui.InfoColor = cli.UiColorGreen
	ui.ErrorColor = cli.UiColorRed
	ui.WarnColor = cli.UiColorYellow

	return nil
}

func genBinStr(val int64, regLen int) string {
	s := strconv.FormatInt(val, 2)
	l := len(s)
	bin := strings.Repeat("0", regLen-l)
	bin = bin + s
	return bin
}

func formateBinStr(bin string) string {
	var s string
	strlen := len(bin)
	count := 0

	for i := strlen - 1; i >= 0; i-- {
		if count%4 == 0 && count != 0 {
			s = "," + s
		}
		count++
		s = string(bin[i]) + s
	}
	return s
}

func printAllFormat(bin string) {
	dec, err := strconv.ParseInt(bin, 2, 64)
	if err != nil {
		fmt.Println("convert subbin to decimal failed:", err)
		return
	}

	fmt.Println("bin:", formateBinStr(bin))
	fmt.Println("dec:", dec)
	fmt.Printf("hex: 0x%x\n", dec)
}

func showBits(start, end int, val int64, regLen int) {
	start_index := regLen - 1 - end
	end_index := regLen - 1 - start
	subbin := binStr[start_index : end_index+1]

	printAllFormat(subbin)
}

func handleViewReg(val int64, input string, regLen int) {
	var start, end int
	if strings.Contains(input, ":") {
		index := strings.Split(input, ":")
		if len(index) == 0 {
			ui.Error("invalid range pattern")
			return
		}

		v, err := strconv.ParseInt(index[0], 0, 64)
		if err != nil {
			ui.Error(fmt.Sprintf("parse range start index failed, %v", err))
			return
		}
		start = int(v)
		v, err = strconv.ParseInt(index[1], 0, 64)
		if err != nil {
			ui.Error(fmt.Sprintf("parse range end index failed, %v", err))
			return
		}
		end = int(v)
		if start > end {
			start, end = end, start
		}
	} else {
		v, err := strconv.ParseInt(input, 0, 64)
		if err != nil {
			ui.Error(fmt.Sprintf("parse single index failed, %v", err))
			return
		}
		start = int(v)
		end = int(v)
	}

	if start < 0 || end >= regLen {
		ui.Error(fmt.Sprintf("range is invalid, [%d, %d].", 0, regLen-1))
		return
	}
	showBits(start, end, val, regLen)
}

func handleInput(val int64, input string, regLen int) (exit bool) {
	exit = false
	switch input {
	case "exit":
		exit = true
		return
	case "help":
		printUsage()
	case "print":
		printAllFormat(binStr)
	default:
		handleViewReg(val, input, regLen)
	}

	return
}

func printUsage() {
	ui.Output("Usage:")
	ui.Output("  help     : print this message.")
	ui.Output("  exit     : exit this program.")
	ui.Output("  print    : print input value.")
	ui.Output("  <patter> : bit number or range of bits, like 21, 12:14 or 14:12.")
}

func main() {
	var valStr string
	flag.IntVar(&regLength, "l", 32, "register length.")
	flag.StringVar(&valStr, "v", "xx", "input value, 10 or 0x03.")
	flag.Parse()

	if valStr == "xx" {
		flag.Usage()
		os.Exit(1)
	}
	val, err := strconv.ParseInt(valStr, 0, 64)
	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}
	binStr = genBinStr(val, regLength)
	printAllFormat(binStr)

	if err := initUi(); err != nil {
		fmt.Println(err)
		os.Exit(3)
	}

	for {
		fmt.Println()
		input, err := ui.Ask(">>>")
		if err != nil {
			fmt.Println(err)
		}

		if handleInput(val, input, regLength) {
			break
		}
	}
}
<commit_msg>add 'value' command to update value<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/mitchellh/cli"
)

var (
	ui     *cli.ColoredUi
	regLen = 32
	binStr string
	value  int64
)

func initUi() error {
	ui = new(cli.ColoredUi)
	if ui == nil {
		fmt.Printf("error of ui\n")
		return errors.New("failed to new cli")
	}

	bui := new(cli.BasicUi)
	bui.Reader = os.Stdin
	bui.Writer = os.Stdout
	bui.ErrorWriter = os.Stderr

	ui.Ui = bui
	ui.OutputColor = cli.UiColorNone
	ui.InfoColor = cli.UiColorGreen
	ui.ErrorColor = cli.UiColorRed
	ui.WarnColor = cli.UiColorYellow

	return nil
}

func genBinStr(val int64) string {
	s := strconv.FormatInt(val, 2)
	l := len(s)
	bin := strings.Repeat("0", regLen-l)
	bin = bin + s
	return bin
}

func formateBinStr(bin string) string {
	var s string
	strlen := len(bin)
	count := 0

	for i := strlen - 1; i >= 0; i-- {
		if count%4 == 0 && count != 0 {
			s = "," + s
		}
		count++
		s = string(bin[i]) + s
	}
	return s
}

func printAllFormat(bin string) {
	dec, err := strconv.ParseInt(bin, 2, 64)
	if err != nil {
		ui.Error(fmt.Sprintf("convert subbin to decimal failed: %v", err))
		return
	}

	fmt.Println("bin:", formateBinStr(bin))
	fmt.Println("dec:", dec)
	fmt.Printf("hex: 0x%x\n", dec)
}

func showBits(start, end int) {
	start_index := regLen - 1 - end
	end_index := regLen - 1 - start
	subbin := binStr[start_index : end_index+1]

	printAllFormat(subbin)
}

func handleViewReg(input string) {
	var start, end int
	if strings.Contains(input, ":") {
		index := strings.Split(input, ":")
		if len(index) == 0 {
			ui.Error("invalid range pattern")
			return
		}

		v, err := strconv.ParseInt(index[0], 0, 64)
		if err != nil {
			ui.Error(fmt.Sprintf("parse range start index failed, %v", err))
			return
		}
		start = int(v)
		v, err = strconv.ParseInt(index[1], 0, 64)
		if err != nil {
			ui.Error(fmt.Sprintf("parse range end index failed, %v", err))
			return
		}
		end = int(v)
		if start > end {
			start, end = end, start
		}
	} else {
		v, err := strconv.ParseInt(input, 0, 64)
		if err != nil {
			ui.Error(fmt.Sprintf("parse single index failed, %v", err))
			return
		}
		start = int(v)
		end = int(v)
	}

	if start < 0 || end >= regLen {
		ui.Error(fmt.Sprintf("range is invalid, [%d, %d].", 0, regLen-1))
		return
	}
	showBits(start, end)
}

func changeValue() string {
	s, err := ui.Ask("input value:")
	if err != nil {
		ui.Error(fmt.Sprintf("get input failed: %v", err))
		return ""
	}

	val, err := strconv.ParseInt(s, 0, 64)
	if err != nil {
		ui.Error(fmt.Sprintf("convert to Int failed: %v", err))
		return ""
	}
	return genBinStr(val)
}

func handleInput(input string) (exit bool) {
	exit = false
	switch input {
	case "exit":
		exit = true
		return
	case "help", "h":
		printUsage()
	case "print", "p":
		printAllFormat(binStr)
	case "value", "v":
		binStr = changeValue()
		printAllFormat(binStr)
	default:
		handleViewReg(input)
	}

	return
}

func printUsage() {
	ui.Output("Usage:")
	ui.Output("  [h]elp   : print this message.")
	ui.Output("  [p]rint  : print input value.")
	ui.Output("  [v]alue  : input value.")
	ui.Output("  <patter> : bit number or range of bits, like 21, 12:14 or 14:12.")
	ui.Output("  exit     : exit this program.")
}

func main() {
	flag.IntVar(&regLen, "l", 32, "register length.")
	flag.Parse()

	if len(os.Args) == 2 && (os.Args[1] == "help" || os.Args[1] == "-h") {
		flag.Usage()
		os.Exit(0)
	}

	if err := initUi(); err != nil {
		fmt.Println(err)
		os.Exit(3)
	}

	for {
		fmt.Println()
		input, err := ui.Ask(">>>")
		if err != nil {
			fmt.Println(err)
		}

		if handleInput(input) {
			break
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"

	"github.com/CMGS/go-dockerclient"
)

var debugMode bool

func debug(v ...interface{}) {
	if debugMode {
		log.Println(v...)
	}
}

func assert(err error, context string) {
	if err != nil {
		log.Fatal(context+": ", err)
	}
}

func pid(path string) {
	if err := ioutil.WriteFile(path, []byte(strconv.Itoa(os.Getpid())), 0755); err != nil {
		log.Println("Save app config failed", err)
	}
}

func main() {
	flag.BoolVar(&debugMode, "DEBUG", false, "enable debug")
	endpoint := flag.String("docker", "unix:///var/run/docker.sock", "docker location")
	routes := flag.String("routes", "/var/lib/lenz", "routes path")
	forwards := flag.String("forwards", "", "log forward location, separate by comma")
	pidFile := flag.String("pidfile", "/var/run/lenz.pid", "pid file")
	flag.Parse()

	client, err := docker.NewClient(*endpoint)
	assert(err, "docker")
	attacher := NewAttachManager(client)
	router := NewRouteManager(attacher)

	if *forwards != "" {
		log.Println("routing all to " + *forwards)
		target := Target{Addrs: strings.Split(*forwards, ",")}
		route := Route{Target: &target}
		route.loadBackends()
		router.Add(&route)
	}

	if _, err := os.Stat(*routes); err == nil {
		log.Println("loading and persisting routes in " + *routes)
		assert(router.Load(RouteFileStore(*routes)), "persistor")
	}

	pid(*pidFile)
	var c = make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)
	signal.Notify(c, syscall.SIGHUP)
	for {
		log.Println("Catch", <-c)
		break
	}
}
<commit_msg>simple reload<commit_after>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"

	"github.com/CMGS/go-dockerclient"
)

var debugMode bool

func debug(v ...interface{}) {
	if debugMode {
		log.Println(v...)
	}
}

func assert(err error, context string) {
	if err != nil {
		log.Fatal(context+": ", err)
	}
}

func pid(path string) {
	if err := ioutil.WriteFile(path, []byte(strconv.Itoa(os.Getpid())), 0755); err != nil {
		log.Println("Save app config failed", err)
	}
}

func main() {
	flag.BoolVar(&debugMode, "DEBUG", false, "enable debug")
	endpoint := flag.String("docker", "unix:///var/run/docker.sock", "docker location")
	routes := flag.String("routes", "/var/lib/lenz", "routes path")
	forwards := flag.String("forwards", "", "log forward location, separate by comma")
	pidFile := flag.String("pidfile", "/var/run/lenz.pid", "pid file")
	flag.Parse()

	client, err := docker.NewClient(*endpoint)
	assert(err, "docker")
	attacher := NewAttachManager(client)
	router := NewRouteManager(attacher)

	if *forwards != "" {
		log.Println("routing all to " + *forwards)
		target := Target{Addrs: strings.Split(*forwards, ",")}
		route := Route{Target: &target}
		route.loadBackends()
		router.Add(&route)
	}

	if _, err := os.Stat(*routes); err == nil {
		log.Println("loading and persisting routes in " + *routes)
		assert(router.Load(RouteFileStore(*routes)), "persistor")
	}

	pid(*pidFile)
	var c = make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)
	signal.Notify(c, syscall.SIGHUP)
	for {
		s := <-c
		log.Println("Catch", s)
		switch s {
		case syscall.SIGHUP:
			assert(router.Load(RouteFileStore(*routes)), "persistor")
		default:
			os.Exit(0)
		}
	}
}
<|endoftext|>