<commit_before>package main

import (
	"flag"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"log"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/lukechampine/ply/importer"
	"github.com/lukechampine/ply/types"
)

// A specializer is an ast.Visitor that generates specialized versions of each
// generic ply function and rewrites the callsites to use their corresponding
// specialized function.
type specializer struct {
	types map[ast.Expr]types.TypeAndValue
	fset  *token.FileSet
	pkg   *ast.Package
}

func hasMethod(recv ast.Expr, method string, exprTypes map[ast.Expr]types.TypeAndValue) bool {
	// TODO: use set.Lookup instead of searching manually
	set := types.NewMethodSet(exprTypes[recv].Type)
	for i := 0; i < set.Len(); i++ {
		if set.At(i).Obj().(*types.Func).Name() == method {
			return true
		}
	}
	return false
}

func (s specializer) addDecl(filename, code string) {
	if _, ok := s.pkg.Files[filename]; ok {
		// check for existence first, because parsing is expensive
		return
	}
	// add package header to code
	code = "package " + s.pkg.Name + code
	f, err := parser.ParseFile(s.fset, "", code, 0)
	if err != nil {
		log.Fatal(err)
	}
	s.pkg.Files[filename] = f
}

func (s specializer) Visit(node ast.Node) ast.Visitor {
	switch n := node.(type) {
	case *ast.CallExpr:
		switch fn := n.Fun.(type) {
		case *ast.Ident:
			if gen, ok := funcGenerators[fn.Name]; ok {
				if v := s.types[n].Value; v != nil {
					// some functions (namely max/min) may evaluate to a
					// constant, in which case we should replace the call with
					// a constant expression.
					//
					// NOTE: This is a bit of a hack, since n is still
					// technically a CallExpr. Ideally we would rewrite it to
					// a BasicLit (?), but that would require access to its
					// parent node.
					fn.Name = ""
					n.Args = []ast.Expr{ast.NewIdent(v.ExactString())}
					break
				}

				name, code, rewrite := gen(fn, n.Args, s.types)
				s.addDecl(name, code)
				rewrite(n)
			}

		case *ast.SelectorExpr:
			// Detect and construct a pipeline if possible. Otherwise,
			// generate a single method.
			var chain []*ast.CallExpr
			cur := n
			for {
				chain = append(chain, cur)
				if sel, ok := cur.Fun.(*ast.SelectorExpr); !ok {
					break
				} else if cur, ok = sel.X.(*ast.CallExpr); !ok {
					break
				}
			}
			if p := buildPipeline(chain, s.types); p != nil {
				name, code, rewrite := p.gen()
				s.addDecl(name, code)
				rewrite(n)
			} else if gen, ok := methodGenerators[fn.Sel.Name]; ok && !hasMethod(fn.X, fn.Sel.Name, s.types) {
				name, code, rewrite := gen(fn, n.Args, s.types)
				s.addDecl(name, code)
				rewrite(n)
			}
		}
	}
	return s
}

func main() {
	log.SetFlags(0)
	flag.Parse()

	// parse each supplied file
	fset := token.NewFileSet()
	var files []*ast.File
	plyFiles := make(map[string]*ast.File)
	for _, arg := range flag.Args() {
		f, err := parser.ParseFile(fset, arg, nil, parser.ParseComments)
		if err != nil {
			log.Fatal(err)
		}
		files = append(files, f)
		if filepath.Ext(arg) == ".ply" {
			plyFiles[arg] = f
		}
	}

	// type-check the package
	info := types.Info{
		Types: make(map[ast.Expr]types.TypeAndValue),
	}
	var conf types.Config
	conf.Importer = importer.Default()
	pkg, err := conf.Check("", fset, files, &info)
	if err != nil {
		log.Fatal(err)
	}

	// walk the AST of each .ply file in the package, generating ply functions
	// and rewriting their callsites
	spec := specializer{
		types: info.Types,
		fset:  fset,
		pkg: &ast.Package{
			Name:  pkg.Name(),
			Files: make(map[string]*ast.File),
		},
	}
	for _, f := range plyFiles {
		ast.Walk(spec, f)
	}

	// combine generated ply functions into a single file and write it to the
	// current directory
	merged := ast.MergePackageFiles(spec.pkg, ast.FilterFuncDuplicates|ast.FilterImportDuplicates)
	implFile, err := os.Create("ply_impls.go")
	if err != nil {
		log.Fatal(err)
	}
	printer.Fprint(implFile, fset, merged)

	// output a .go file for each .ply file
	for path, f := range plyFiles {
		goFile, err := os.Create(path + ".go")
		if err != nil {
			log.Fatal(err)
		}
		printer.Fprint(goFile, fset, f)
	}

	// invoke the Go compiler
	cmd := exec.Command("go", "build")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>safer type assertion in pipeline detection<commit_after>package main

import (
	"flag"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"log"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/lukechampine/ply/importer"
	"github.com/lukechampine/ply/types"
)

// A specializer is an ast.Visitor that generates specialized versions of each
// generic ply function and rewrites the callsites to use their corresponding
// specialized function.
type specializer struct {
	types map[ast.Expr]types.TypeAndValue
	fset  *token.FileSet
	pkg   *ast.Package
}

func hasMethod(recv ast.Expr, method string, exprTypes map[ast.Expr]types.TypeAndValue) bool {
	// TODO: use set.Lookup instead of searching manually
	set := types.NewMethodSet(exprTypes[recv].Type)
	for i := 0; i < set.Len(); i++ {
		if set.At(i).Obj().(*types.Func).Name() == method {
			return true
		}
	}
	return false
}

func (s specializer) addDecl(filename, code string) {
	if _, ok := s.pkg.Files[filename]; ok {
		// check for existence first, because parsing is expensive
		return
	}
	// add package header to code
	code = "package " + s.pkg.Name + code
	f, err := parser.ParseFile(s.fset, "", code, 0)
	if err != nil {
		log.Fatal(err)
	}
	s.pkg.Files[filename] = f
}

func (s specializer) Visit(node ast.Node) ast.Visitor {
	switch n := node.(type) {
	case *ast.CallExpr:
		switch fn := n.Fun.(type) {
		case *ast.Ident:
			if gen, ok := funcGenerators[fn.Name]; ok {
				if v := s.types[n].Value; v != nil {
					// some functions (namely max/min) may evaluate to a
					// constant, in which case we should replace the call with
					// a constant expression.
					//
					// NOTE: This is a bit of a hack, since n is still
					// technically a CallExpr. Ideally we would rewrite it to
					// a BasicLit (?), but that would require access to its
					// parent node.
					fn.Name = ""
					n.Args = []ast.Expr{ast.NewIdent(v.ExactString())}
					break
				}

				name, code, rewrite := gen(fn, n.Args, s.types)
				s.addDecl(name, code)
				rewrite(n)
			}

		case *ast.SelectorExpr:
			// Detect and construct a pipeline if possible. Otherwise,
			// generate a single method.
			var chain []*ast.CallExpr
			cur := n
			for ok := true; ok; cur, ok = cur.Fun.(*ast.SelectorExpr).X.(*ast.CallExpr) {
				if _, ok := cur.Fun.(*ast.SelectorExpr); !ok {
					break
				}
				chain = append(chain, cur)
			}
			if p := buildPipeline(chain, s.types); p != nil {
				name, code, rewrite := p.gen()
				s.addDecl(name, code)
				rewrite(n)
			} else if gen, ok := methodGenerators[fn.Sel.Name]; ok && !hasMethod(fn.X, fn.Sel.Name, s.types) {
				name, code, rewrite := gen(fn, n.Args, s.types)
				s.addDecl(name, code)
				rewrite(n)
			}
		}
	}
	return s
}

func main() {
	log.SetFlags(0)
	flag.Parse()

	// parse each supplied file
	fset := token.NewFileSet()
	var files []*ast.File
	plyFiles := make(map[string]*ast.File)
	for _, arg := range flag.Args() {
		f, err := parser.ParseFile(fset, arg, nil, parser.ParseComments)
		if err != nil {
			log.Fatal(err)
		}
		files = append(files, f)
		if filepath.Ext(arg) == ".ply" {
			plyFiles[arg] = f
		}
	}

	// type-check the package
	info := types.Info{
		Types: make(map[ast.Expr]types.TypeAndValue),
	}
	var conf types.Config
	conf.Importer = importer.Default()
	pkg, err := conf.Check("", fset, files, &info)
	if err != nil {
		log.Fatal(err)
	}

	// walk the AST of each .ply file in the package, generating ply functions
	// and rewriting their callsites
	spec := specializer{
		types: info.Types,
		fset:  fset,
		pkg: &ast.Package{
			Name:  pkg.Name(),
			Files: make(map[string]*ast.File),
		},
	}
	for _, f := range plyFiles {
		ast.Walk(spec, f)
	}

	// combine generated ply functions into a single file and write it to the
	// current directory
	merged := ast.MergePackageFiles(spec.pkg, ast.FilterFuncDuplicates|ast.FilterImportDuplicates)
	implFile, err := os.Create("ply_impls.go")
	if err != nil {
		log.Fatal(err)
	}
	printer.Fprint(implFile, fset, merged)

	// output a .go file for each .ply file
	for path, f := range plyFiles {
		goFile, err := os.Create(path + ".go")
		if err != nil {
			log.Fatal(err)
		}
		printer.Fprint(goFile, fset, f)
	}

	// invoke the Go compiler
	cmd := exec.Command("go", "build")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


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

package main

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

func typeMap(t *types.Tuple) map[string]types.Type {
	m := make(map[string]types.Type, t.Len())
	for i := 0; i < t.Len(); i++ {
		p := t.At(i)
		m[p.Name()] = p.Type()
	}
	return m
}

func init() {
	if err := typesInit(); err != nil {
		log.Fatal(err)
	}
}

type call struct {
	params  []interface{}
	results []interface{}
}

var toToken = map[string]token.Token{
	"byte":    token.INT,
	"int":     token.INT,
	"int8":    token.INT,
	"int16":   token.INT,
	"int32":   token.INT,
	"int64":   token.INT,
	"uint":    token.INT,
	"uint8":   token.INT,
	"uint16":  token.INT,
	"uint32":  token.INT,
	"uint64":  token.INT,
	"string":  token.STRING,
	"float32": token.FLOAT,
	"float64": token.FLOAT,
}

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

func paramsMatch(types1 []types.Type, args2 []interface{}) bool {
	if len(types1) != len(args2) {
		return false
	}
	for i, t1 := range types1 {
		a2 := args2[i]
		if !paramEqual(t1, a2) {
			return false
		}
	}
	return true
}

func resultEqual(t1 types.Type, e2 interface{}) bool {
	switch x := e2.(type) {
	case string:
		return t1.String() == x
	case nil:
		// assigning to _
		return true
	default:
		return false
	}
}

func resultsMatch(types1 []types.Type, exps2 []interface{}) bool {
	if len(exps2) == 0 {
		return true
	}
	if len(types1) != len(exps2) {
		return false
	}
	for i, t1 := range types1 {
		e2 := exps2[i]
		if !resultEqual(t1, e2) {
			return false
		}
	}
	return true
}

func interfaceMatching(calls map[string]call) string {
	matchesIface := func(decls map[string]funcDecl) bool {
		if len(calls) > len(decls) {
			return false
		}
		for n, d := range decls {
			c, e := calls[n]
			if !e {
				return false
			}
			if !paramsMatch(d.params, c.params) {
				return false
			}
			if !resultsMatch(d.results, c.results) {
				return false
			}
		}
		return true
	}
	for name, decls := range parsed {
		if matchesIface(decls) {
			return name
		}
	}
	return ""
}

func main() {
	flag.Parse()
	p := &goPkg{
		fset: token.NewFileSet(),
	}
	for _, fp := range flag.Args() {
		if err := p.parsePath(fp); err != nil {
			log.Fatal(err)
		}
	}
	if err := p.check(os.Stdout); err != nil {
		log.Fatal(err)
	}
}

type goPkg struct {
	fset  *token.FileSet
	files []*ast.File
}

func (p *goPkg) parsePath(fp string) error {
	f, err := os.Open(fp)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if err := p.parseReader(fp, f); err != nil {
		log.Fatal(err)
	}
	return nil
}

func (p *goPkg) parseReader(name string, r io.Reader) error {
	f, err := parser.ParseFile(p.fset, name, r, 0)
	if err != nil {
		return err
	}
	p.files = append(p.files, f)
	return nil
}

func (p *goPkg) check(w io.Writer) error {
	conf := types.Config{Importer: importer.Default()}
	pkg, err := conf.Check("", p.fset, p.files, nil)
	if err != nil {
		return err
	}

	v := &Visitor{
		w:      w,
		fset:   p.fset,
		scopes: []*types.Scope{pkg.Scope()},
	}
	for _, f := range p.files {
		ast.Walk(v, f)
	}
	return nil
}

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

	nodes []ast.Node

	params map[string]types.Type
	used   map[string]map[string]call
}

func (v *Visitor) scope() *types.Scope {
	return v.scopes[len(v.scopes)-1]
}

func scopeName(e ast.Expr) string {
	switch x := e.(type) {
	case *ast.Ident:
		return x.Name
	case *ast.StarExpr:
		return scopeName(x.X)
	default:
		return ""
	}
}

func (v *Visitor) recvFuncType(tname, fname string) *types.Func {
	_, obj := v.scope().LookupParent(tname, token.NoPos)
	st, ok := obj.(*types.TypeName)
	if !ok {
		return nil
	}
	named, ok := st.Type().(*types.Named)
	if !ok {
		return nil
	}
	for i := 0; i < named.NumMethods(); i++ {
		f := named.Method(i)
		if f.Name() == fname {
			return f
		}
	}
	return nil
}

func (v *Visitor) funcType(fd *ast.FuncDecl) *types.Func {
	fname := fd.Name.Name
	if fd.Recv == nil {
		f, ok := v.scope().Lookup(fname).(*types.Func)
		if !ok {
			return nil
		}
		return f
	}
	if len(fd.Recv.List) > 1 {
		return nil
	}
	tname := scopeName(fd.Recv.List[0].Type)
	return v.recvFuncType(tname, fname)
}

type assignCall struct {
	left  []ast.Expr
	right *ast.CallExpr
}

func assignCalls(as *ast.AssignStmt) []assignCall {
	if len(as.Rhs) == 1 {
		ce, ok := as.Rhs[0].(*ast.CallExpr)
		if !ok {
			return nil
		}
		return []assignCall{
			{
				left:  as.Lhs,
				right: ce,
			},
		}
	}
	var calls []assignCall
	for i, right := range as.Rhs {
		ce, ok := right.(*ast.CallExpr)
		if !ok {
			continue
		}
		calls = append(calls, assignCall{
			left:  []ast.Expr{as.Lhs[i]},
			right: ce,
		})
	}
	return calls
}

func (v *Visitor) Visit(node ast.Node) ast.Visitor {
	switch x := node.(type) {
	case *ast.File:
	case *ast.FuncDecl:
		f := v.funcType(x)
		if f == nil {
			return nil
		}
		v.scopes = append(v.scopes, f.Scope())
		sign := f.Type().(*types.Signature)
		v.params = typeMap(sign.Params())
		v.used = make(map[string]map[string]call, 0)
	case *ast.BlockStmt:
	case *ast.ExprStmt:
	case *ast.AssignStmt:
		calls := assignCalls(x)
		for _, c := range calls {
			v.onCall(c.left, c.right)
		}
		return nil
	case *ast.CallExpr:
		v.onCall(nil, 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.params[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.params = nil
		v.used = nil
	default:
		return nil
	}
	if node != nil {
		v.nodes = append(v.nodes, node)
	}
	return v
}

func (v *Visitor) getType(name string) interface{} {
	_, obj := v.scope().LookupParent(name, token.NoPos)
	if obj == nil {
		return nil
	}
	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:
		return v.getType(x.Name)
	case *ast.BasicLit:
		return x.Kind
	default:
		return nil
	}
}

func (v *Visitor) onCall(results []ast.Expr, ce *ast.CallExpr) {
	sel, ok := ce.Fun.(*ast.SelectorExpr)
	if !ok {
		return
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return
	}
	right := sel.Sel
	vname := left.Name
	tname, _ := v.descType(left).(string)
	fname := right.Name
	c := call{}
	f := v.recvFuncType(tname, fname)
	if f != nil {
		sign := f.Type().(*types.Signature)
		results := sign.Results()
		for i := 0; i < results.Len(); i++ {
			v := results.At(i)
			c.results = append(c.results, v.Type().String())
		}
	} else {
		for _, r := range results {
			c.results = append(c.results, v.descType(r))
		}

	}
	for _, a := range ce.Args {
		c.params = append(c.params, v.descType(a))
	}
	if _, e := v.used[vname]; !e {
		v.used[vname] = make(map[string]call)
	}
	v.used[vname][fname] = c
}
<commit_msg>Fix nil pointer dereference<commit_after>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

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

func typeMap(t *types.Tuple) map[string]types.Type {
	m := make(map[string]types.Type, t.Len())
	for i := 0; i < t.Len(); i++ {
		p := t.At(i)
		m[p.Name()] = p.Type()
	}
	return m
}

func init() {
	if err := typesInit(); err != nil {
		log.Fatal(err)
	}
}

type call struct {
	params  []interface{}
	results []interface{}
}

var toToken = map[string]token.Token{
	"byte":    token.INT,
	"int":     token.INT,
	"int8":    token.INT,
	"int16":   token.INT,
	"int32":   token.INT,
	"int64":   token.INT,
	"uint":    token.INT,
	"uint8":   token.INT,
	"uint16":  token.INT,
	"uint32":  token.INT,
	"uint64":  token.INT,
	"string":  token.STRING,
	"float32": token.FLOAT,
	"float64": token.FLOAT,
}

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

func paramsMatch(types1 []types.Type, args2 []interface{}) bool {
	if len(types1) != len(args2) {
		return false
	}
	for i, t1 := range types1 {
		a2 := args2[i]
		if !paramEqual(t1, a2) {
			return false
		}
	}
	return true
}

func resultEqual(t1 types.Type, e2 interface{}) bool {
	switch x := e2.(type) {
	case string:
		return t1.String() == x
	case nil:
		// assigning to _
		return true
	default:
		return false
	}
}

func resultsMatch(types1 []types.Type, exps2 []interface{}) bool {
	if len(exps2) == 0 {
		return true
	}
	if len(types1) != len(exps2) {
		return false
	}
	for i, t1 := range types1 {
		e2 := exps2[i]
		if !resultEqual(t1, e2) {
			return false
		}
	}
	return true
}

func interfaceMatching(calls map[string]call) string {
	matchesIface := func(decls map[string]funcDecl) bool {
		if len(calls) > len(decls) {
			return false
		}
		for n, d := range decls {
			c, e := calls[n]
			if !e {
				return false
			}
			if !paramsMatch(d.params, c.params) {
				return false
			}
			if !resultsMatch(d.results, c.results) {
				return false
			}
		}
		return true
	}
	for name, decls := range parsed {
		if matchesIface(decls) {
			return name
		}
	}
	return ""
}

func main() {
	flag.Parse()
	p := &goPkg{
		fset: token.NewFileSet(),
	}
	for _, fp := range flag.Args() {
		if err := p.parsePath(fp); err != nil {
			log.Fatal(err)
		}
	}
	if err := p.check(os.Stdout); err != nil {
		log.Fatal(err)
	}
}

type goPkg struct {
	fset  *token.FileSet
	files []*ast.File
}

func (p *goPkg) parsePath(fp string) error {
	f, err := os.Open(fp)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if err := p.parseReader(fp, f); err != nil {
		log.Fatal(err)
	}
	return nil
}

func (p *goPkg) parseReader(name string, r io.Reader) error {
	f, err := parser.ParseFile(p.fset, name, r, 0)
	if err != nil {
		return err
	}
	p.files = append(p.files, f)
	return nil
}

func (p *goPkg) check(w io.Writer) error {
	conf := types.Config{Importer: importer.Default()}
	pkg, err := conf.Check("", p.fset, p.files, nil)
	if err != nil {
		return err
	}

	v := &Visitor{
		w:      w,
		fset:   p.fset,
		scopes: []*types.Scope{pkg.Scope()},
	}
	for _, f := range p.files {
		ast.Walk(v, f)
	}
	return nil
}

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

	nodes []ast.Node

	params map[string]types.Type
	used   map[string]map[string]call
}

func (v *Visitor) scope() *types.Scope {
	return v.scopes[len(v.scopes)-1]
}

func scopeName(e ast.Expr) string {
	switch x := e.(type) {
	case *ast.Ident:
		return x.Name
	case *ast.StarExpr:
		return scopeName(x.X)
	default:
		return ""
	}
}

func (v *Visitor) recvFuncType(tname, fname string) *types.Func {
	_, obj := v.scope().LookupParent(tname, token.NoPos)
	st, ok := obj.(*types.TypeName)
	if !ok {
		return nil
	}
	named, ok := st.Type().(*types.Named)
	if !ok {
		return nil
	}
	for i := 0; i < named.NumMethods(); i++ {
		f := named.Method(i)
		if f.Name() == fname {
			return f
		}
	}
	return nil
}

func (v *Visitor) funcType(fd *ast.FuncDecl) *types.Func {
	fname := fd.Name.Name
	if fd.Recv == nil {
		f, ok := v.scope().Lookup(fname).(*types.Func)
		if !ok {
			return nil
		}
		return f
	}
	if len(fd.Recv.List) > 1 {
		return nil
	}
	tname := scopeName(fd.Recv.List[0].Type)
	return v.recvFuncType(tname, fname)
}

type assignCall struct {
	left  []ast.Expr
	right *ast.CallExpr
}

func assignCalls(as *ast.AssignStmt) []assignCall {
	if len(as.Rhs) == 1 {
		ce, ok := as.Rhs[0].(*ast.CallExpr)
		if !ok {
			return nil
		}
		return []assignCall{
			{
				left:  as.Lhs,
				right: ce,
			},
		}
	}
	var calls []assignCall
	for i, right := range as.Rhs {
		ce, ok := right.(*ast.CallExpr)
		if !ok {
			continue
		}
		calls = append(calls, assignCall{
			left:  []ast.Expr{as.Lhs[i]},
			right: ce,
		})
	}
	return calls
}

func (v *Visitor) Visit(node ast.Node) ast.Visitor {
	switch x := node.(type) {
	case *ast.File:
	case *ast.FuncDecl:
		f := v.funcType(x)
		if f == nil {
			return nil
		}
		v.scopes = append(v.scopes, f.Scope())
		sign := f.Type().(*types.Signature)
		v.params = typeMap(sign.Params())
		v.used = make(map[string]map[string]call, 0)
	case *ast.BlockStmt:
	case *ast.ExprStmt:
	case *ast.AssignStmt:
		calls := assignCalls(x)
		for _, c := range calls {
			v.onCall(c.left, c.right)
		}
		return nil
	case *ast.CallExpr:
		v.onCall(nil, 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
			}
			param, e := v.params[name]
			if !e {
				continue
			}
			if iface == param.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.params = nil
		v.used = nil
	default:
		return nil
	}
	if node != nil {
		v.nodes = append(v.nodes, node)
	}
	return v
}

func (v *Visitor) getType(name string) interface{} {
	_, obj := v.scope().LookupParent(name, token.NoPos)
	if obj == nil {
		return nil
	}
	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:
		return v.getType(x.Name)
	case *ast.BasicLit:
		return x.Kind
	default:
		return nil
	}
}

func (v *Visitor) onCall(results []ast.Expr, ce *ast.CallExpr) {
	sel, ok := ce.Fun.(*ast.SelectorExpr)
	if !ok {
		return
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return
	}
	right := sel.Sel
	vname := left.Name
	tname, _ := v.descType(left).(string)
	fname := right.Name
	c := call{}
	f := v.recvFuncType(tname, fname)
	if f != nil {
		sign := f.Type().(*types.Signature)
		results := sign.Results()
		for i := 0; i < results.Len(); i++ {
			v := results.At(i)
			c.results = append(c.results, v.Type().String())
		}
	} else {
		for _, r := range results {
			c.results = append(c.results, v.descType(r))
		}

	}
	for _, a := range ce.Args {
		c.params = append(c.params, v.descType(a))
	}
	if _, e := v.used[vname]; !e {
		v.used[vname] = make(map[string]call)
	}
	v.used[vname][fname] = c
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"runtime/debug"
	"strconv"
	"syscall"
)

var (
	templates *template.Template
	blaster   *Blaster

	flag_R        = flag.Uint("r", 17, "Red GPIO pin")
	flag_G        = flag.Uint("g", 22, "Green GPIO pin")
	flag_B        = flag.Uint("b", 27, "Blue GPIO pin")
	flag_Cooldown = flag.Uint("cooldown", 10, "Milliseconds cooldown between requests")
)

type RGB struct {
	R uint8
	G uint8
	B uint8
}

func (c RGB) String() string {
	return fmt.Sprintf("#%02x%02x%02x", c.R, c.G, c.B)
}

func mustParseTemplates() {
	var err error
	templates, err = template.ParseGlob("templates/*.html")

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

func attemptPiBlasterStart() error {
	cmd := exec.Command("pi-blaster")
	return cmd.Run()
}

func mustOpenPiBlaster() *os.File {
	file, err := os.OpenFile("/dev/pi-blaster", os.O_RDWR, os.ModeNamedPipe)

	if err != nil {
		if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ENOENT {
			err = attemptPiBlasterStart()
		}
	}

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

	return file
}

type Blaster struct {
	pipe  *os.File
	Input chan RGB
	Color chan func(RGB)
	shake chan struct{}

	r, g, b uint8
}

func NewBlaster() *Blaster {
	return &Blaster{
		Input: make(chan RGB),
		Color: make(chan func(RGB)),
		shake: make(chan struct{}),
	}
}

func (b *Blaster) Run() {
	b.pipe = mustOpenPiBlaster()
	defer b.pipe.Close()

	for {
		select {
		case c := <-b.Input:
			b.setAll(c)
		case c := <-b.Color:
			c(RGB{b.r, b.g, b.b})
		}
	}
}

func (b *Blaster) setPin(pin uint, val float64) error {
	chanCmd := fmt.Sprintf("%d=%f\n", pin, val)

	b.pipe.Write([]byte(chanCmd))
	return nil
}

func (b *Blaster) setChannelInteger(pin uint, val uint8) error {
	switch {
	case val > 255:
		return errors.New("can't go over 255. sorry mate.")
	case val < 0:
		return errors.New("can't go below 0. sorry mate.")
	default:
		fval := float64(val) / 255.0
		return b.setPin(pin, fval)
	}
}

func (b *Blaster) setRed(val uint8) error {
	return b.setChannelInteger(*flag_R, val)
}

func (b *Blaster) setGreen(val uint8) error {
	return b.setChannelInteger(*flag_G, val)
}

func (b *Blaster) setBlue(val uint8) error {
	return b.setChannelInteger(*flag_B, val)
}

type setterFunc func(v uint8) error

func (b *Blaster) setAll(c RGB) {
	b.setRed(c.R)
	b.setGreen(c.G)
	b.setBlue(c.B)
}

func errorHandler(w http.ResponseWriter, r *http.Request) {
	if err := recover(); err != nil {
		w.WriteHeader(401)

		fmt.Fprintf(w, "Oh...:(\n\n")

		if e, ok := err.(error); ok {
			w.Write([]byte(e.Error()))
			w.Write([]byte{'\n', '\n'})
			w.Write(debug.Stack())
		} else {
			fmt.Fprintf(w, "%s\n\n", err)
		}

		log.Println(
			"panic catched:", err,
			"\nRequest data:", r)
	}
}

func parseUint8OrZero(s string) uint8 {
	i, err := strconv.ParseUint(s, 10, 8)
	if err != nil {
		return 0
	}
	return uint8(i)
}

func actionHandler(w http.ResponseWriter, r *http.Request) {
	defer errorHandler(w, r)

	values, err := url.ParseQuery(r.URL.RawQuery)

	if err != nil {
		panic(err)
	}

	action := values.Get("action")

	switch action {
	case "lighter":
		blaster.Color <- func(c RGB) {
			blaster.Input <- RGB{c.R + 10, c.G + 10, c.B + 10}
		}
	case "darker":
		blaster.Color <- func(c RGB) {
			blaster.Input <- RGB{c.R - 10, c.G - 10, c.B - 10}
		}
	case "off":
		blaster.Input <- RGB{0, 0, 0}
	case "set":
		r := parseUint8OrZero(values.Get("r"))
		g := parseUint8OrZero(values.Get("g"))
		b := parseUint8OrZero(values.Get("b"))

		blaster.Input <- RGB{r, g, b}
	}
}

func currentColorHandler(w http.ResponseWriter, r *http.Request) {
	defer errorHandler(w, r)
	w.Header().Set("Content-Type", "text/plain")

	c := make(chan RGB)

	blaster.Color <- func(color RGB) {
		c <- color
	}

	w.Write([]byte((<-c).String()))
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	defer errorHandler(w, r)
	templates.ExecuteTemplate(w, "index.html", nil)
}

func main() {
	flag.Parse()

	blaster = NewBlaster()
	go blaster.Run()

	blaster.Input <- RGB{}

	mustParseTemplates()

	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/do", actionHandler)
	http.HandleFunc("/color", currentColorHandler)
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("templates"))))

	log.Fatal(http.ListenAndServe(":1337", nil))
}
<commit_msg>Store color internally<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"runtime/debug"
	"strconv"
	"syscall"
)

var (
	templates *template.Template
	blaster   *Blaster

	flag_R        = flag.Uint("r", 17, "Red GPIO pin")
	flag_G        = flag.Uint("g", 22, "Green GPIO pin")
	flag_B        = flag.Uint("b", 27, "Blue GPIO pin")
	flag_Cooldown = flag.Uint("cooldown", 10, "Milliseconds cooldown between requests")
)

type RGB struct {
	R uint8
	G uint8
	B uint8
}

func (c RGB) String() string {
	return fmt.Sprintf("#%02x%02x%02x", c.R, c.G, c.B)
}

func mustParseTemplates() {
	var err error
	templates, err = template.ParseGlob("templates/*.html")

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

func attemptPiBlasterStart() error {
	cmd := exec.Command("pi-blaster")
	return cmd.Run()
}

func mustOpenPiBlaster() *os.File {
	file, err := os.OpenFile("/dev/pi-blaster", os.O_RDWR, os.ModeNamedPipe)

	if err != nil {
		if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ENOENT {
			err = attemptPiBlasterStart()
		}
	}

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

	return file
}

type Blaster struct {
	pipe  *os.File
	Input chan RGB
	Color chan chan RGB
	shake chan struct{}

	r, g, b uint8
}

func NewBlaster() *Blaster {
	return &Blaster{
		Input: make(chan RGB),
		Color: make(chan chan RGB),
		shake: make(chan struct{}),
	}
}

func (b *Blaster) Run() {
	b.pipe = mustOpenPiBlaster()
	defer b.pipe.Close()

	for {
		select {
		case c := <-b.Input:
			b.setAll(c)
		case c := <-b.Color:
			go func(c chan RGB) { c <- RGB{b.r, b.g, b.b} }(c)
		}
	}
}

func (b *Blaster) setPin(pin uint, val float64) error {
	chanCmd := fmt.Sprintf("%d=%f\n", pin, val)

	b.pipe.Write([]byte(chanCmd))
	return nil
}

func (b *Blaster) setChannelInteger(pin uint, val uint8) error {
	switch {
	case val > 255:
		return errors.New("can't go over 255. sorry mate.")
	case val < 0:
		return errors.New("can't go below 0. sorry mate.")
	default:
		fval := float64(val) / 255.0
		return b.setPin(pin, fval)
	}
}

func (b *Blaster) setRed(val uint8) error {
	b.r = val
	return b.setChannelInteger(*flag_R, val)
}

func (b *Blaster) setGreen(val uint8) error {
	b.g = val
	return b.setChannelInteger(*flag_G, val)
}

func (b *Blaster) setBlue(val uint8) error {
	b.b = val
	return b.setChannelInteger(*flag_B, val)
}

type setterFunc func(v uint8) error

func (b *Blaster) setAll(c RGB) {
	b.setRed(c.R)
	b.setGreen(c.G)
	b.setBlue(c.B)
}

func errorHandler(w http.ResponseWriter, r *http.Request) {
	if err := recover(); err != nil {
		w.WriteHeader(401)

		fmt.Fprintf(w, "Oh...:(\n\n")

		if e, ok := err.(error); ok {
			w.Write([]byte(e.Error()))
			w.Write([]byte{'\n', '\n'})
			w.Write(debug.Stack())
		} else {
			fmt.Fprintf(w, "%s\n\n", err)
		}

		log.Println(
			"panic catched:", err,
			"\nRequest data:", r)
	}
}

func parseUint8OrZero(s string) uint8 {
	i, err := strconv.ParseUint(s, 10, 8)
	if err != nil {
		return 0
	}
	return uint8(i)
}

func withRequestedColor(f func(color RGB)) {
	c := make(chan RGB)
	defer close(c)
	blaster.Color <- c
	f(<-c)
}

func actionHandler(w http.ResponseWriter, r *http.Request) {
	defer errorHandler(w, r)

	values, err := url.ParseQuery(r.URL.RawQuery)

	if err != nil {
		panic(err)
	}

	action := values.Get("action")

	switch action {
	case "lighter":
		withRequestedColor(func(c RGB) {
			blaster.Input <- RGB{c.R + 10, c.G + 10, c.B + 10}
		})
	case "darker":
		withRequestedColor(func(c RGB) {
			blaster.Input <- RGB{c.R - 10, c.G - 10, c.B - 10}
		})
	case "off":
		blaster.Input <- RGB{0, 0, 0}
	case "set":
		r := parseUint8OrZero(values.Get("r"))
		g := parseUint8OrZero(values.Get("g"))
		b := parseUint8OrZero(values.Get("b"))

		blaster.Input <- RGB{r, g, b}
	}
}

func currentColorHandler(w http.ResponseWriter, r *http.Request) {
	defer errorHandler(w, r)
	w.Header().Set("Content-Type", "text/plain")

	withRequestedColor(func(c RGB) {
		w.Write([]byte(c.String()))
	})
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	defer errorHandler(w, r)
	templates.ExecuteTemplate(w, "index.html", nil)
}

func main() {
	flag.Parse()

	blaster = NewBlaster()
	go blaster.Run()

	blaster.Input <- RGB{}

	mustParseTemplates()

	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/do", actionHandler)
	http.HandleFunc("/color", currentColorHandler)
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("templates"))))

	log.Fatal(http.ListenAndServe(":1337", nil))
}
<|endoftext|>


<commit_before>// Copyright 2013 Jesse Allen. All rights reserved
// Released under the MIT license found in the LICENSE file.

package main

import (
	"flag"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/lazyengineering/gobase/layouts"
)

// Important metadata
var (
	ServerAddr   = flag.String("server-addr", ":5050", "Server Address to listen on")
	GATrackingID = flag.String("ga-tracking-id", "", "Google Analytics Tracking ID")
)

var Layout *layouts.Layout

func init() {
	t := time.Now() // measure bootstrap time
	defer func() {
		log.Printf("\x1b[1;32mBootstrapped:\x1b[0m \x1b[34m%8d\x1b[0mµs", time.Since(t).Nanoseconds()/1000)
	}()
	var (
		NoTimestamp        = flag.Bool("no-timestamp", false, "When set to true, removes timestamp from log statements")
		StaticDir          = flag.String("static-dir", "static", "Static Assets folder")
		LayoutTemplateGlob = flag.String("layouts", "static/templates/layouts/*.html", "Pattern for layout templates")
		HelperTemplateGlob = flag.String("helpers", "static/templates/helpers/*.html", "Pattern for helper templates")
	)

	// set from environment where available before parsing (allows flags to overrule env)
	flag.VisitAll(func(f *flag.Flag) {
		switch f.Name {
		case "server-addr": // special case because it doesn't map directly
			if port := os.Getenv("PORT"); len(port) > 0 {
				f.Value.Set(":" + port)
			}
		default:
			if v := os.Getenv(strings.ToUpper(strings.Replace(f.Name, "-", "_", -1))); len(v) > 0 {
				f.Value.Set(v)
			}
		}
	})

	flag.Parse()

	if *NoTimestamp {
		log.SetFlags(0)
	}

	// Static Asset Serving
	staticServer := NoIndex(func(h http.Handler) http.Handler {
		// add 1 day caching headers to static assets
		return http.HandlerFunc(func(r http.ResponseWriter, q *http.Request) {
			r.Header().Set("Cache-Control", "public, max-age=86400")
			r.Header().Set("Expires", time.Now().Add(24*time.Hour).Format(time.RFC1123))
			h.ServeHTTP(r, q)
		})
	}(http.FileServer(http.Dir(*StaticDir))))
	Handle("/js/", staticServer)
	Handle("/css/", staticServer)
	Handle("/fonts/", staticServer)
	Handle("/img/", staticServer)
	Handle("/favicon.ico", staticServer)

	// Layouts
	Layout = layouts.New(layouts.BasicFunctionMap(), "bootstrap.html", *LayoutTemplateGlob, *HelperTemplateGlob)

	// Actual Web Application Handlers
	HandleNoSubPaths("/", Layout.Act(hello, Error500, layouts.NoVolatility, "static/templates/hello/*.html"))
}

// Log and Handle http requests
func Handle(path string, h http.Handler) {
	if strings.HasSuffix(path, "/") { // redirect for directories
		indexRedirect := http.RedirectHandler(path, http.StatusMovedPermanently)
		Handle(path+"index.html", indexRedirect)
		Handle(path+"index.htm", indexRedirect)
		Handle(path+"index.php", indexRedirect) // not that anybody would think...
	}
	http.HandleFunc(path, func(r http.ResponseWriter, q *http.Request) {
		t := time.Now()
		h.ServeHTTP(r, q)
		s := time.Since(t).Nanoseconds() / 1000 // time in µs
		message := "\x1b[1;36mServed: \x1b[0m"
		if s > 10000 { // > 10ms is a "long" request, mark in red with a *
			message = "\x1b[1;31mServed*:\x1b[0m"
		}
		log.Printf("%s \x1b[34m%8d\x1b[0mµs \x1b[33m%s\x1b[0m", message, s, q.URL.String())
	})
}

func HandleFunc(path string, h http.HandlerFunc) {
	Handle(path, http.HandlerFunc(h))
}

func HandleNoSubPaths(path string, h http.Handler) {
	Handle(path, NoSubPaths(path, h))
}

func NoIndex(h http.Handler) http.Handler {
	return http.HandlerFunc(func(r http.ResponseWriter, q *http.Request) {
		if strings.HasSuffix(q.URL.Path, "/") {
			Error403(r, q)
			return
		}
		h.ServeHTTP(r, q)
	})
}

func NoSubPaths(path string, h http.Handler) http.Handler {
	return http.HandlerFunc(func(r http.ResponseWriter, q *http.Request) {
		if q.URL.Path != path {
			Error404(r, q)
			return
		}
		h.ServeHTTP(r, q)
	})
}

func main() {
	log.Println("\x1b[32mlistening at \x1b[1;32m" + *ServerAddr + "\x1b[32m...\x1b[0m")
	log.Fatalln("Fatal Error:", http.ListenAndServe(*ServerAddr, nil))
}

type Nav struct {
	*http.Request
}

func (n Nav) IsCurrent(p string) bool {
	return p == n.Request.URL.Path
}

func hello(req *http.Request) (map[string]interface{}, error) {
	return map[string]interface{}{
		"Title":        "Hello World",
		"BodyClass":    "hello",
		"Nav":          Nav{req},
		"GATrackingID": *GATrackingID,
	}, nil
}
<commit_msg>Update to layouts API<commit_after>// Copyright 2013 Jesse Allen. All rights reserved
// Released under the MIT license found in the LICENSE file.

package main

import (
	"flag"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/lazyengineering/gobase/layouts"
)

// Important metadata
var (
	ServerAddr   = flag.String("server-addr", ":5050", "Server Address to listen on")
	GATrackingID = flag.String("ga-tracking-id", "", "Google Analytics Tracking ID")
)

var Layout *layouts.Layout

func init() {
	t := time.Now() // measure bootstrap time
	defer func() {
		log.Printf("\x1b[1;32mBootstrapped:\x1b[0m \x1b[34m%8d\x1b[0mµs", time.Since(t).Nanoseconds()/1000)
	}()
	var (
		NoTimestamp        = flag.Bool("no-timestamp", false, "When set to true, removes timestamp from log statements")
		StaticDir          = flag.String("static-dir", "static", "Static Assets folder")
		LayoutTemplateGlob = flag.String("layouts", "static/templates/layouts/*.html", "Pattern for layout templates")
		HelperTemplateGlob = flag.String("helpers", "static/templates/helpers/*.html", "Pattern for helper templates")
	)

	// set from environment where available before parsing (allows flags to overrule env)
	flag.VisitAll(func(f *flag.Flag) {
		switch f.Name {
		case "server-addr": // special case because it doesn't map directly
			if port := os.Getenv("PORT"); len(port) > 0 {
				f.Value.Set(":" + port)
			}
		default:
			if v := os.Getenv(strings.ToUpper(strings.Replace(f.Name, "-", "_", -1))); len(v) > 0 {
				f.Value.Set(v)
			}
		}
	})

	flag.Parse()

	if *NoTimestamp {
		log.SetFlags(0)
	}

	// Static Asset Serving
	staticServer := NoIndex(func(h http.Handler) http.Handler {
		// add 1 day caching headers to static assets
		return http.HandlerFunc(func(r http.ResponseWriter, q *http.Request) {
			r.Header().Set("Cache-Control", "public, max-age=86400")
			r.Header().Set("Expires", time.Now().Add(24*time.Hour).Format(time.RFC1123))
			h.ServeHTTP(r, q)
		})
	}(http.FileServer(http.Dir(*StaticDir))))
	Handle("/js/", staticServer)
	Handle("/css/", staticServer)
	Handle("/fonts/", staticServer)
	Handle("/img/", staticServer)
	Handle("/favicon.ico", staticServer)

	// Layouts
	var err error
	Layout, err = layouts.New(layouts.BasicFunctionMap(), "bootstrap.html", *LayoutTemplateGlob, *HelperTemplateGlob)
	if err != nil {
		// this is a fatal condition
		panic(err)
	}

	// Actual Web Application Handlers
	HandleNoSubPaths("/", Layout.Act(hello, Error500, layouts.NoVolatility, "static/templates/hello/*.html"))
}

// Log and Handle http requests
func Handle(path string, h http.Handler) {
	if strings.HasSuffix(path, "/") { // redirect for directories
		indexRedirect := http.RedirectHandler(path, http.StatusMovedPermanently)
		Handle(path+"index.html", indexRedirect)
		Handle(path+"index.htm", indexRedirect)
		Handle(path+"index.php", indexRedirect) // not that anybody would think...
	}
	http.HandleFunc(path, func(r http.ResponseWriter, q *http.Request) {
		t := time.Now()
		h.ServeHTTP(r, q)
		s := time.Since(t).Nanoseconds() / 1000 // time in µs
		message := "\x1b[1;36mServed: \x1b[0m"
		if s > 10000 { // > 10ms is a "long" request, mark in red with a *
			message = "\x1b[1;31mServed*:\x1b[0m"
		}
		log.Printf("%s \x1b[34m%8d\x1b[0mµs \x1b[33m%s\x1b[0m", message, s, q.URL.String())
	})
}

func HandleFunc(path string, h http.HandlerFunc) {
	Handle(path, http.HandlerFunc(h))
}

func HandleNoSubPaths(path string, h http.Handler) {
	Handle(path, NoSubPaths(path, h))
}

func NoIndex(h http.Handler) http.Handler {
	return http.HandlerFunc(func(r http.ResponseWriter, q *http.Request) {
		if strings.HasSuffix(q.URL.Path, "/") {
			Error403(r, q)
			return
		}
		h.ServeHTTP(r, q)
	})
}

func NoSubPaths(path string, h http.Handler) http.Handler {
	return http.HandlerFunc(func(r http.ResponseWriter, q *http.Request) {
		if q.URL.Path != path {
			Error404(r, q)
			return
		}
		h.ServeHTTP(r, q)
	})
}

func main() {
	log.Println("\x1b[32mlistening at \x1b[1;32m" + *ServerAddr + "\x1b[32m...\x1b[0m")
	log.Fatalln("Fatal Error:", http.ListenAndServe(*ServerAddr, nil))
}

type Nav struct {
	*http.Request
}

func (n Nav) IsCurrent(p string) bool {
	return p == n.Request.URL.Path
}

func hello(req *http.Request) (map[string]interface{}, error) {
	return map[string]interface{}{
		"Title":        "Hello World",
		"BodyClass":    "hello",
		"Nav":          Nav{req},
		"GATrackingID": *GATrackingID,
	}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/HouzuoGuo/laitos/daemon/maintenance"
	"github.com/HouzuoGuo/laitos/launcher"
	"github.com/HouzuoGuo/laitos/launcher/encarchive"
	"github.com/HouzuoGuo/laitos/launcher/passwdserver"
	"github.com/HouzuoGuo/laitos/misc"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"time"
)

const (
	ProfilerHTTPPort = 19151 // ProfilerHTTPPort is to be listened by net/http/pprof HTTP server when benchmark is turned on
)

var logger = misc.Logger{ComponentName: "main", ComponentID: []misc.LoggerIDField{{"PID", os.Getpid()}}}

/*
ExtractEncryptedArchive is a distinct routine of laitos main program, it reads password from standard input, decrypts
the encrypted archive file and extract it into the destination directory.
*/
func ExtractEncryptedArchive(destDir, archivePath string) {
	reader := bufio.NewReader(os.Stdin)
	fmt.Println("Please enter password to decrypt archive (no echo):")
	misc.SetTermEcho(false)
	password, _, err := reader.ReadLine()
	misc.SetTermEcho(true)
	if err != nil {
		misc.DefaultLogger.Abort("ExtractEncryptedArchive", "main", err, "failed to read password")
		return
	}
	/*
		This time, the temp file does not have to live in a ramdisk, because the extracted content does not have to be
		in the memory anyways.
	*/
	tmpFile, err := ioutil.TempFile("", "laitos-launcher-utility-extract")
	if err != nil {
		misc.DefaultLogger.Abort("ExtractEncryptedArchive", "main", err, "failed to create temporary file")
		return
	}
	tmpFile.Close()
	defer func() {
		if err := os.Remove(tmpFile.Name()); err != nil && !os.IsNotExist(err) {
			misc.DefaultLogger.Info("ExtractEncryptedArchive", "main", err, "failed to delete temporary file")
		}
	}()
	password = []byte(strings.TrimSpace(string(password)))
	err = encarchive.Extract(archivePath, tmpFile.Name(), destDir, password)
	if err == nil {
		fmt.Println("Success")
	} else {
		fmt.Println("Error: ", err.Error())
	}
}

/*
MakeEncryptedArchive is a distinct routine of laitos main program, it reads password from standard input and uses it to
encrypt the directory and archive into a single file.
*/
func MakeEncryptedArchive(srcDir, archivePath string) {
	reader := bufio.NewReader(os.Stdin)
	fmt.Println("Please enter a password to encrypt the archive (no echo):")
	misc.SetTermEcho(false)
	password, _, err := reader.ReadLine()
	misc.SetTermEcho(true)
	if err != nil {
		misc.DefaultLogger.Abort("ExtractEncryptedArchive", "main", err, "failed to read password")
		return
	}
	password = []byte(strings.TrimSpace(string(password)))
	err = encarchive.Archive(srcDir, archivePath, password)
	if err == nil {
		fmt.Println("Success")
	} else {
		fmt.Println("Error: ", err.Error())
	}
}

/*
StartPasswordWebServer is a distinct routine of laitos main program, it starts a simple web server to accept a password
input in order to decrypt laitos program data and launch the daemons.
*/
func StartPasswordWebServer(port int, url, archivePath string) {
	ws := passwdserver.WebServer{
		Port:            port,
		URL:             url,
		ArchiveFilePath: archivePath,
	}
	/*
		On Amazon ElasitcBeanstalk, application update cannot reliably kill the old program prior to launching the new
		version, which means the web server often runs into port conflicts. Therefore, make at most 10 attempts at
		starting the web server.
	*/
	for i := 0; i < 10; i++ {
		if err := ws.Start(); err == nil {
			// Upon success, wait almost indefinitely (~5 years) because this is the main routine of this CLI action.
			time.Sleep(5 * 365 * 24 * time.Hour)
		} else {
			// Retry upon failure
			logger.Info("StartPasswordWebServer", "main", err, "failed to start the web server (attempt %d)", i)
			time.Sleep(5 * time.Second)
		}
	}
	logger.Abort("StartPasswordWebServer", "main", nil, "failed to start the web server after many attempts")
	return

}

/*
main runs one of several distinct routines as dictated by input command line flags:

- Utilities for maintaining encrypted program data archive (-datautil=extract|archive).

- Password input web server that accepts a password input to launch laitos daemons in supervisor mode by decrypting its
  program data (-pwdserver & -pwdserverport= & -pwdserverdata= & -pwdserverurl=).

- Supervisor runs laitos daemons in a seperate process and re-launches them in case of crash. Supervisor is turned on by
  default (-supervisor=true).

- Whether launched by supervisor or launched independently, start daemons (-config= & -daemons=).

- Benchmark routine that runs after daemons have been launched.
*/
func main() {
	// Process command line flags
	var daemonList string
	var disableConflicts, debug, benchmark bool
	var gomaxprocs int
	flag.StringVar(&misc.ConfigFilePath, launcher.ConfigFlagName, "", "(Mandatory) path to configuration file in JSON syntax")
	flag.StringVar(&daemonList, launcher.DaemonsFlagName, "", "(Mandatory) comma-separated daemons to start (dnsd, httpd, insecurehttpd, maintenance, plainsocket, smtpd, telegram, autounlock)")
	flag.BoolVar(&disableConflicts, "disableconflicts", false, "(Optional) automatically stop and disable other daemon programs that may cause port usage conflicts")
	flag.BoolVar(&debug, "debug", false, "(Optional) print goroutine stack traces upon receiving interrupt signal")
	flag.BoolVar(&benchmark, "benchmark", false, fmt.Sprintf("(Optional) continuously run benchmark routines on active daemons while exposing net/http/pprof on port %d", ProfilerHTTPPort))
	flag.IntVar(&gomaxprocs, "gomaxprocs", 0, "(Optional) set gomaxprocs")
	// Encrypted data archive launcher (password input server) flags
	var pwdServer bool
	var pwdServerPort int
	var pwdServerData string
	var pwdServerURL string
	flag.BoolVar(&pwdServer, passwdserver.CLIFlag, false, "(Optional) launch web server to accept password for decrypting encrypted program data")
	flag.IntVar(&pwdServerPort, passwdserver.CLIFlag+"port", 80, "(Optional) port number of the password web server")
	flag.StringVar(&pwdServerData, passwdserver.CLIFlag+"data", "", "(Optional) location of encrypted program data archive")
	flag.StringVar(&pwdServerURL, passwdserver.CLIFlag+"url", "", "(Optional) password input URL")
	// Encrypted data archive utility flags
	var dataUtil, dataUtilDir, dataUtilFile string
	flag.StringVar(&dataUtil, "datautil", "", "(Optional) program data encryption utility: extract|archive")
	flag.StringVar(&dataUtilDir, "datautildir", "", "(Optional) program data encryption utility: extract destination or archive source directory")
	flag.StringVar(&dataUtilFile, "datautilfile", "", "(Optional) program data encryption utility: extract from or archive file location")
	// Internal supervisor flag
	var isSupervisor = true
	flag.BoolVar(&isSupervisor, launcher.SupervisorFlagName, true, "(Internal use only) enter supervisor mode")

	flag.Parse()

	// Common diagnosis and security practices
	misc.LockMemory()
	ReseedPseudoRandAndInBackground()
	if debug {
		DumpGoroutinesOnInterrupt()
	}

	// ========================================================================
	// Utility routines - maintain encrypted laitos program data, no need to run any daemon.
	// ========================================================================
	if dataUtil != "" {
		switch dataUtil {
		case "extract":
			ExtractEncryptedArchive(dataUtilDir, dataUtilFile)
		case "archive":
			MakeEncryptedArchive(dataUtilDir, dataUtilFile)
		default:
			logger.Abort("main", "", nil, "please provide mode of operation (extract|archive) for parameter \"-datautil\"")
		}
		return
	}

	// ========================================================================
	// Password input web server - start the web server to accept password input for decrypting program data.
	// ========================================================================
	if pwdServer {
		StartPasswordWebServer(pwdServerPort, pwdServerURL, pwdServerData)
		return
	}
	/*
		If the password web server succeeded in decrypting program data, it will launch laitos daemons under supervisor;
		if the server is not relevant/involved in user's deployment, the user may simply ignore its program flags and
		launch laitos daemons right away.
		Be ware that supervisor is always turned on by default.
		Here come the preparation for both supervisor and daemons:
	*/
	// Parse configuration JSON file
	if misc.ConfigFilePath == "" {
		logger.Abort("main", "", nil, "please provide a configuration file (-config)")
		return
	}
	var err error
	misc.ConfigFilePath, err = filepath.Abs(misc.ConfigFilePath)
	if err != nil {
		logger.Abort("main", "", err, "failed to determine absolute path of config file \"%s\"", misc.ConfigFilePath)
	}
	var config launcher.Config
	configBytes, err := ioutil.ReadFile(misc.ConfigFilePath)
	if err != nil {
		logger.Abort("main", "", err, "failed to read config file \"%s\"", misc.ConfigFilePath)
		return
	}
	if err := config.DeserialiseFromJSON(configBytes); err != nil {
		logger.Abort("main", "", err, "failed to deserialise config file \"%s\"", misc.ConfigFilePath)
		return
	}
	// Figure out what daemons are to be started
	daemonNames := regexp.MustCompile(`\w+`).FindAllString(daemonList, -1)
	if daemonNames == nil || len(daemonNames) == 0 {
		logger.Abort("main", "", nil, "please provide comma-separated list of daemon services to start (-daemons).")
		return
	}
	// Make sure all daemon names are valid
	for _, daemonName := range daemonNames {
		var found bool
		for _, goodName := range launcher.AllDaemons {
			if daemonName == goodName {
				found = true
			}
		}
		if !found {
			logger.Abort("main", "", err, "unknown daemon name \"%s\"", daemonName)
		}
	}

	// ========================================================================
	// Supervisor routine - launch an independent laitos process to run daemons.
	// The command line flag is turned on by default so that laitos daemons are
	// always protected by the supervisor by default. There is no good reason
	// for a user to turn it off manually.
	// ========================================================================
	if isSupervisor {
		supervisor := &launcher.Supervisor{
			CLIFlags:               os.Args[1:],
			NotificationRecipients: config.SupervisorNotificationRecipients,
			MailClient:             config.MailClient,
			DaemonNames:            daemonNames,
		}
		supervisor.Start()
		return
	}

	// ========================================================================
	// Daemon routine - launch all daemons at once.
	// ========================================================================
	// Prepare environmental changes
	if gomaxprocs > 0 {
		oldGomaxprocs := runtime.GOMAXPROCS(gomaxprocs)
		logger.Warning("main", "", nil, "GOMAXPROCS has been changed from %d to %d", oldGomaxprocs, gomaxprocs)
	} else {
		logger.Warning("main", "", nil, "GOMAXPROCS is unchanged at %d", runtime.GOMAXPROCS(0))
	}
	if disableConflicts {
		DisableConflicts()
	}
	PrepareUtilitiesAndInBackground()

	daemonErrs := make(chan error, len(daemonNames))
	for _, daemonName := range daemonNames {
		// Daemons are started asynchronously because the order of startup does not matter.
		switch daemonName {
		case launcher.DNSDName:
			go func() {
				daemonErrs <- config.GetDNSD().StartAndBlock()
			}()
		case launcher.HTTPDName:
			go func() {
				daemonErrs <- config.GetHTTPD().StartAndBlockWithTLS()
			}()
		case launcher.InsecureHTTPDName:
			go func() {
				/*
					There is not an independent port settings for launching both TLS-enabled and TLS-free HTTP servers
					at the same time. If user really wishes to launch both at the same time, the TLS-free HTTP server
					will fallback to use port number 80.
				*/
				daemonErrs <- config.GetHTTPD().StartAndBlockNoTLS(80)
			}()
		case launcher.MaintenanceName:
			go func() {
				daemonErrs <- config.GetMaintenance().StartAndBlock()
			}()
		case launcher.PlainSocketName:
			go func() {
				daemonErrs <- config.GetPlainSocketDaemon().StartAndBlock()
			}()
		case launcher.SMTPDName:
			go func() {
				daemonErrs <- config.GetMailDaemon().StartAndBlock()
			}()
		case launcher.SOCKDName:
			go func() {
				daemonErrs <- config.GetSockDaemon().StartAndBlock()
			}()
		case launcher.TelegramName:
			go func() {
				daemonErrs <- config.GetTelegramBot().StartAndBlock()
			}()
		case launcher.AutoUnlockName:
			go func() {
				daemonErrs <- config.GetAutoUnlock().StartAndBlock()
			}()
		}
	}

	if benchmark {
		// Wait a while for daemons to settle and maintenance to run its first round, then run benchmark in the background.
		logger.Info("main", "", nil, "benchmark is about to commence in 30 seconds")
		time.Sleep(maintenance.InitialDelaySec * 2)
		bench := launcher.Benchmark{
			Config:      &config,
			DaemonNames: daemonNames,
			Logger:      logger,
			HTTPPort:    ProfilerHTTPPort,
		}
		go bench.RunBenchmarkAndProfiler()
	}

	// Wait for daemons to quit (they really should not).
	for i := 0; i < len(daemonNames); i++ {
		err := <-daemonErrs
		logger.Warning("main", "", err, "a daemon has encountered an error and failed to start")
	}
	logger.Abort("main", "", nil, "all daemons have failed to start, laitos will now exit.")
}
<commit_msg>retry harder at starting password web server<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/HouzuoGuo/laitos/daemon/maintenance"
	"github.com/HouzuoGuo/laitos/launcher"
	"github.com/HouzuoGuo/laitos/launcher/encarchive"
	"github.com/HouzuoGuo/laitos/launcher/passwdserver"
	"github.com/HouzuoGuo/laitos/misc"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"time"
)

const (
	ProfilerHTTPPort = 19151 // ProfilerHTTPPort is to be listened by net/http/pprof HTTP server when benchmark is turned on
)

var logger = misc.Logger{ComponentName: "main", ComponentID: []misc.LoggerIDField{{"PID", os.Getpid()}}}

/*
ExtractEncryptedArchive is a distinct routine of laitos main program, it reads password from standard input, decrypts
the encrypted archive file and extract it into the destination directory.
*/
func ExtractEncryptedArchive(destDir, archivePath string) {
	reader := bufio.NewReader(os.Stdin)
	fmt.Println("Please enter password to decrypt archive (no echo):")
	misc.SetTermEcho(false)
	password, _, err := reader.ReadLine()
	misc.SetTermEcho(true)
	if err != nil {
		misc.DefaultLogger.Abort("ExtractEncryptedArchive", "main", err, "failed to read password")
		return
	}
	/*
		This time, the temp file does not have to live in a ramdisk, because the extracted content does not have to be
		in the memory anyways.
	*/
	tmpFile, err := ioutil.TempFile("", "laitos-launcher-utility-extract")
	if err != nil {
		misc.DefaultLogger.Abort("ExtractEncryptedArchive", "main", err, "failed to create temporary file")
		return
	}
	tmpFile.Close()
	defer func() {
		if err := os.Remove(tmpFile.Name()); err != nil && !os.IsNotExist(err) {
			misc.DefaultLogger.Info("ExtractEncryptedArchive", "main", err, "failed to delete temporary file")
		}
	}()
	password = []byte(strings.TrimSpace(string(password)))
	err = encarchive.Extract(archivePath, tmpFile.Name(), destDir, password)
	if err == nil {
		fmt.Println("Success")
	} else {
		fmt.Println("Error: ", err.Error())
	}
}

/*
MakeEncryptedArchive is a distinct routine of laitos main program, it reads password from standard input and uses it to
encrypt the directory and archive into a single file.
*/
func MakeEncryptedArchive(srcDir, archivePath string) {
	reader := bufio.NewReader(os.Stdin)
	fmt.Println("Please enter a password to encrypt the archive (no echo):")
	misc.SetTermEcho(false)
	password, _, err := reader.ReadLine()
	misc.SetTermEcho(true)
	if err != nil {
		misc.DefaultLogger.Abort("ExtractEncryptedArchive", "main", err, "failed to read password")
		return
	}
	password = []byte(strings.TrimSpace(string(password)))
	err = encarchive.Archive(srcDir, archivePath, password)
	if err == nil {
		fmt.Println("Success")
	} else {
		fmt.Println("Error: ", err.Error())
	}
}

/*
StartPasswordWebServer is a distinct routine of laitos main program, it starts a simple web server to accept a password
input in order to decrypt laitos program data and launch the daemons.
*/
func StartPasswordWebServer(port int, url, archivePath string) {
	ws := passwdserver.WebServer{
		Port:            port,
		URL:             url,
		ArchiveFilePath: archivePath,
	}
	/*
		On Amazon ElasitcBeanstalk, application update cannot reliably kill the old program prior to launching the new
		version, which means the web server often runs into port conflicts. Therefore, keep trying for 3 minutes.
	*/
	for i := 0; i < 30; i++ {
		if err := ws.Start(); err == nil {
			// Upon success, wait almost indefinitely (~5 years) because this is the main routine of this CLI action.
			time.Sleep(5 * 365 * 24 * time.Hour)
		} else {
			// Retry upon failure
			logger.Info("StartPasswordWebServer", "main", err, "failed to start the web server (attempt %d)", i)
			time.Sleep(6 * time.Second)
		}
	}
	logger.Abort("StartPasswordWebServer", "main", nil, "failed to start the web server after many attempts")
	return

}

/*
main runs one of several distinct routines as dictated by input command line flags:

- Utilities for maintaining encrypted program data archive (-datautil=extract|archive).

- Password input web server that accepts a password input to launch laitos daemons in supervisor mode by decrypting its
  program data (-pwdserver & -pwdserverport= & -pwdserverdata= & -pwdserverurl=).

- Supervisor runs laitos daemons in a seperate process and re-launches them in case of crash. Supervisor is turned on by
  default (-supervisor=true).

- Whether launched by supervisor or launched independently, start daemons (-config= & -daemons=).

- Benchmark routine that runs after daemons have been launched.
*/
func main() {
	// Process command line flags
	var daemonList string
	var disableConflicts, debug, benchmark bool
	var gomaxprocs int
	flag.StringVar(&misc.ConfigFilePath, launcher.ConfigFlagName, "", "(Mandatory) path to configuration file in JSON syntax")
	flag.StringVar(&daemonList, launcher.DaemonsFlagName, "", "(Mandatory) comma-separated daemons to start (dnsd, httpd, insecurehttpd, maintenance, plainsocket, smtpd, telegram, autounlock)")
	flag.BoolVar(&disableConflicts, "disableconflicts", false, "(Optional) automatically stop and disable other daemon programs that may cause port usage conflicts")
	flag.BoolVar(&debug, "debug", false, "(Optional) print goroutine stack traces upon receiving interrupt signal")
	flag.BoolVar(&benchmark, "benchmark", false, fmt.Sprintf("(Optional) continuously run benchmark routines on active daemons while exposing net/http/pprof on port %d", ProfilerHTTPPort))
	flag.IntVar(&gomaxprocs, "gomaxprocs", 0, "(Optional) set gomaxprocs")
	// Encrypted data archive launcher (password input server) flags
	var pwdServer bool
	var pwdServerPort int
	var pwdServerData string
	var pwdServerURL string
	flag.BoolVar(&pwdServer, passwdserver.CLIFlag, false, "(Optional) launch web server to accept password for decrypting encrypted program data")
	flag.IntVar(&pwdServerPort, passwdserver.CLIFlag+"port", 80, "(Optional) port number of the password web server")
	flag.StringVar(&pwdServerData, passwdserver.CLIFlag+"data", "", "(Optional) location of encrypted program data archive")
	flag.StringVar(&pwdServerURL, passwdserver.CLIFlag+"url", "", "(Optional) password input URL")
	// Encrypted data archive utility flags
	var dataUtil, dataUtilDir, dataUtilFile string
	flag.StringVar(&dataUtil, "datautil", "", "(Optional) program data encryption utility: extract|archive")
	flag.StringVar(&dataUtilDir, "datautildir", "", "(Optional) program data encryption utility: extract destination or archive source directory")
	flag.StringVar(&dataUtilFile, "datautilfile", "", "(Optional) program data encryption utility: extract from or archive file location")
	// Internal supervisor flag
	var isSupervisor = true
	flag.BoolVar(&isSupervisor, launcher.SupervisorFlagName, true, "(Internal use only) enter supervisor mode")

	flag.Parse()

	// Common diagnosis and security practices
	misc.LockMemory()
	ReseedPseudoRandAndInBackground()
	if debug {
		DumpGoroutinesOnInterrupt()
	}

	// ========================================================================
	// Utility routines - maintain encrypted laitos program data, no need to run any daemon.
	// ========================================================================
	if dataUtil != "" {
		switch dataUtil {
		case "extract":
			ExtractEncryptedArchive(dataUtilDir, dataUtilFile)
		case "archive":
			MakeEncryptedArchive(dataUtilDir, dataUtilFile)
		default:
			logger.Abort("main", "", nil, "please provide mode of operation (extract|archive) for parameter \"-datautil\"")
		}
		return
	}

	// ========================================================================
	// Password input web server - start the web server to accept password input for decrypting program data.
	// ========================================================================
	if pwdServer {
		StartPasswordWebServer(pwdServerPort, pwdServerURL, pwdServerData)
		return
	}
	/*
		If the password web server succeeded in decrypting program data, it will launch laitos daemons under supervisor;
		if the server is not relevant/involved in user's deployment, the user may simply ignore its program flags and
		launch laitos daemons right away.
		Be ware that supervisor is always turned on by default.
		Here come the preparation for both supervisor and daemons:
	*/
	// Parse configuration JSON file
	if misc.ConfigFilePath == "" {
		logger.Abort("main", "", nil, "please provide a configuration file (-config)")
		return
	}
	var err error
	misc.ConfigFilePath, err = filepath.Abs(misc.ConfigFilePath)
	if err != nil {
		logger.Abort("main", "", err, "failed to determine absolute path of config file \"%s\"", misc.ConfigFilePath)
	}
	var config launcher.Config
	configBytes, err := ioutil.ReadFile(misc.ConfigFilePath)
	if err != nil {
		logger.Abort("main", "", err, "failed to read config file \"%s\"", misc.ConfigFilePath)
		return
	}
	if err := config.DeserialiseFromJSON(configBytes); err != nil {
		logger.Abort("main", "", err, "failed to deserialise config file \"%s\"", misc.ConfigFilePath)
		return
	}
	// Figure out what daemons are to be started
	daemonNames := regexp.MustCompile(`\w+`).FindAllString(daemonList, -1)
	if daemonNames == nil || len(daemonNames) == 0 {
		logger.Abort("main", "", nil, "please provide comma-separated list of daemon services to start (-daemons).")
		return
	}
	// Make sure all daemon names are valid
	for _, daemonName := range daemonNames {
		var found bool
		for _, goodName := range launcher.AllDaemons {
			if daemonName == goodName {
				found = true
			}
		}
		if !found {
			logger.Abort("main", "", err, "unknown daemon name \"%s\"", daemonName)
		}
	}

	// ========================================================================
	// Supervisor routine - launch an independent laitos process to run daemons.
	// The command line flag is turned on by default so that laitos daemons are
	// always protected by the supervisor by default. There is no good reason
	// for a user to turn it off manually.
	// ========================================================================
	if isSupervisor {
		supervisor := &launcher.Supervisor{
			CLIFlags:               os.Args[1:],
			NotificationRecipients: config.SupervisorNotificationRecipients,
			MailClient:             config.MailClient,
			DaemonNames:            daemonNames,
		}
		supervisor.Start()
		return
	}

	// ========================================================================
	// Daemon routine - launch all daemons at once.
	// ========================================================================
	// Prepare environmental changes
	if gomaxprocs > 0 {
		oldGomaxprocs := runtime.GOMAXPROCS(gomaxprocs)
		logger.Warning("main", "", nil, "GOMAXPROCS has been changed from %d to %d", oldGomaxprocs, gomaxprocs)
	} else {
		logger.Warning("main", "", nil, "GOMAXPROCS is unchanged at %d", runtime.GOMAXPROCS(0))
	}
	if disableConflicts {
		DisableConflicts()
	}
	PrepareUtilitiesAndInBackground()

	daemonErrs := make(chan error, len(daemonNames))
	for _, daemonName := range daemonNames {
		// Daemons are started asynchronously because the order of startup does not matter.
		switch daemonName {
		case launcher.DNSDName:
			go func() {
				daemonErrs <- config.GetDNSD().StartAndBlock()
			}()
		case launcher.HTTPDName:
			go func() {
				daemonErrs <- config.GetHTTPD().StartAndBlockWithTLS()
			}()
		case launcher.InsecureHTTPDName:
			go func() {
				/*
					There is not an independent port settings for launching both TLS-enabled and TLS-free HTTP servers
					at the same time. If user really wishes to launch both at the same time, the TLS-free HTTP server
					will fallback to use port number 80.
				*/
				daemonErrs <- config.GetHTTPD().StartAndBlockNoTLS(80)
			}()
		case launcher.MaintenanceName:
			go func() {
				daemonErrs <- config.GetMaintenance().StartAndBlock()
			}()
		case launcher.PlainSocketName:
			go func() {
				daemonErrs <- config.GetPlainSocketDaemon().StartAndBlock()
			}()
		case launcher.SMTPDName:
			go func() {
				daemonErrs <- config.GetMailDaemon().StartAndBlock()
			}()
		case launcher.SOCKDName:
			go func() {
				daemonErrs <- config.GetSockDaemon().StartAndBlock()
			}()
		case launcher.TelegramName:
			go func() {
				daemonErrs <- config.GetTelegramBot().StartAndBlock()
			}()
		case launcher.AutoUnlockName:
			go func() {
				daemonErrs <- config.GetAutoUnlock().StartAndBlock()
			}()
		}
	}

	if benchmark {
		// Wait a while for daemons to settle and maintenance to run its first round, then run benchmark in the background.
		logger.Info("main", "", nil, "benchmark is about to commence in 30 seconds")
		time.Sleep(maintenance.InitialDelaySec * 2)
		bench := launcher.Benchmark{
			Config:      &config,
			DaemonNames: daemonNames,
			Logger:      logger,
			HTTPPort:    ProfilerHTTPPort,
		}
		go bench.RunBenchmarkAndProfiler()
	}

	// Wait for daemons to quit (they really should not).
	for i := 0; i < len(daemonNames); i++ {
		err := <-daemonErrs
		logger.Warning("main", "", err, "a daemon has encountered an error and failed to start")
	}
	logger.Abort("main", "", nil, "all daemons have failed to start, laitos will now exit.")
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"flag"
	"go/format"
	"os"
	"os/exec"
	"path"
	"strings"
	"text/template"
)

const (
	templatePrelude = `// Generated by running
//      buildconstants
// DO NOT EDIT

package {{.GOPKG}}

const (
	MustRunBuildConstants = 0
`

	templatePostlude = ")"
)

type cmd struct {
	Var  string
	Line string
	echo bool // get value from env
}

// read commands from a text file.  Each command has its own line
//   and the lines are of the form `VAR = echo $FOO`
//   where $VAR is the env variable and everything after '=' will be evaluated for the value
func cmdRead(cmdfile string) ([]cmd, error) {

	f, err := os.Open(cmdfile)
	if err != nil {
		return []cmd{}, err
	}
	defer f.Close()
	var cmds []cmd
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		splits := strings.Split(scanner.Text(), "=")
		if len(splits) < 2 {
			continue
		}
		line := strings.TrimSpace(strings.Join(splits[1:], " "))
		c := cmd{
			Var:  strings.TrimSpace(splits[0]),
			Line: line,
			echo: strings.HasPrefix(line, "$"),
		}
		cmds = append(cmds, c)
	}
	return cmds, nil
}

// envTemplate builds the template string of the environment variables.
//  It needs to be in the same order in case a later command depends on
//  the value of a previous command
func envTemplate(ins []cmd) string {
	var s string
	for _, in := range ins {
		s = s + " " + in.Var + " = \"{{." + in.Var + "}}\"\n"
	}
	return s
}

// do the cmd by expanding in the shell and running (as needed)
func do(command cmd) (string, error) {
	expanded := os.Expand(command.Line, os.Getenv)
	var out string
	if command.echo {
		out = expanded
	} else {
		split := strings.Split(expanded, " ")
		cmd := exec.Command(split[0], split[1:]...)
		bout, err := cmd.Output()
		if err != nil {
			return "", err
		}
		out = string(bout)
	}

	val := strings.TrimSpace(out)
	err := os.Setenv(command.Var, val)
	if err != nil {
		return "", err
	}
	return val, nil
}

// get current package's name
func pkg() string {
	pkgCmd := exec.Command("go", "list", ".")
	out, err := pkgCmd.Output()
	if err != nil {
		return "main"
	}
	_, packageName := path.Split(strings.TrimSpace(string(out)))
	return packageName
}

func main() {

	cmdfile := flag.String("i", "commands.txt", "file of commands to be run")
	fname := flag.String("o", "", "output file")
	packageName := flag.String("package", pkg(), "package the generated file will be in.")
	flag.Parse()

	incmds, err := cmdRead(*cmdfile)
	if err != nil {
		os.Exit(1)
	}

	outcmds := make(map[string]string, len(incmds)+1) //since we add GOPKG
	outcmds["GOPKG"] = *packageName

	for _, cmd := range incmds {
		val, err := do(cmd)
		if err != nil {
			val = ""
		}
		outcmds[cmd.Var] = val
	}

	templ := templatePrelude + envTemplate(incmds) + templatePostlude

	t := template.Must(template.New("templ").Parse(templ))

	var buf bytes.Buffer
	err = t.Execute(&buf, outcmds)
	if err != nil {
		os.Exit(1)
	}
	fmted, err := format.Source(buf.Bytes())
	if err != nil {
		os.Exit(1)
	}

	f := os.Stdout
	if *fname != "" {
		f, err = os.Create(*fname)
		if err != nil {
			os.Exit(1)
		}
	}
	defer f.Close()
	f.Write(fmted)
}
<commit_msg>Add cmdReader and split more functions<commit_after>package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"go/format"
	"io"
	"os"
	"os/exec"
	"path"
	"strings"
	"text/template"
)

const (
	templatePrelude = `// Generated by running
//      buildconstants
// DO NOT EDIT

package {{.GOPKG}}

const (
	MustRunBuildConstants = 0
`

	templatePostlude = ")"
)

type cmdReader struct {
	scanner   *bufio.Scanner
	separator string
}

// newCmdReader creates a cmdReader for making cmds from r
func newCmdReader(r io.Reader, sep string) *cmdReader {
	return &cmdReader{scanner: bufio.NewScanner(r), separator: sep}
}

// read creates the cmds from the reader originally passed in.
//   Each cmd will be separated from their Line by the r's sep.
func (r cmdReader) read() ([]cmd, error) {
	var cmds []cmd
	for r.scanner.Scan() {
		k, v, err := lineSplit(r.scanner.Text(), r.separator)
		if err != nil {
			continue
		}
		c := parseCmd(k, v)
		cmds = append(cmds, c)
	}
	return cmds, nil
}

// lineSplit splits the line by sep, err on invalid lines
func lineSplit(line, sep string) (string, string, error) {
	splits := strings.Split(line, sep)
	if len(splits) < 2 {
		return "", "", errors.New("invalid line")
	}
	rest := strings.TrimSpace(strings.Join(splits[1:], " "))
	return splits[0], rest, nil
}

// parseCmd builds a cmd from the key and val, and checks if it is just an
//   environment variable or a command
func parseCmd(key, val string) cmd {
	return cmd{
		Var:  strings.TrimSpace(key),
		Line: val,
		echo: isEnvVar(val),
	}
}

// isEnvVar checks if s looks like an environment variable
func isEnvVar(s string) bool {
	return strings.HasPrefix(s, "$")
}

type cmd struct {
	Var  string
	Line string
	echo bool // get value from env
}

// read commands from a text file.  Each command has its own line
//   and the lines are of the form `VAR = echo $FOO`
//   where $VAR is the env variable and everything after '=' will be evaluated for the value
func cmdRead(cmdfile string) ([]cmd, error) {

	f, err := os.Open(cmdfile)
	if err != nil {
		return []cmd{}, err
	}
	defer f.Close()

	cr := newCmdReader(f, "=")
	return cr.read()
}

// envTemplate builds the template string of the environment variables.
//  It needs to be in the same order in case a later command depends on
//  the value of a previous command
func envTemplate(ins []cmd) string {
	var s string
	for _, in := range ins {
		s = s + " " + in.Var + " = \"{{." + in.Var + "}}\"\n"
	}
	return s
}

// do the cmd by expanding in the shell and running (as needed)
func do(command cmd) (string, error) {
	expanded := os.Expand(command.Line, os.Getenv)
	var out string
	if command.echo {
		out = expanded
	} else {
		split := strings.Split(expanded, " ")
		cmd := exec.Command(split[0], split[1:]...)
		bout, err := cmd.Output()
		if err != nil {
			return "", err
		}
		out = string(bout)
	}

	val := strings.TrimSpace(out)
	err := os.Setenv(command.Var, val)
	if err != nil {
		return "", err
	}
	return val, nil
}

// get current package's name
func pkg() string {
	pkgCmd := exec.Command("go", "list", ".")
	out, err := pkgCmd.Output()
	if err != nil {
		return "main"
	}
	_, packageName := path.Split(strings.TrimSpace(string(out)))
	return packageName
}

func main() {

	cmdfile := flag.String("i", "commands.txt", "file of commands to be run")
	fname := flag.String("o", "", "output file")
	packageName := flag.String("package", pkg(), "package the generated file will be in.")
	flag.Parse()

	incmds, err := cmdRead(*cmdfile)
	if err != nil {
		os.Exit(1)
	}

	outcmds := make(map[string]string, len(incmds)+1) //since we add GOPKG
	outcmds["GOPKG"] = *packageName

	for _, cmd := range incmds {
		val, err := do(cmd)
		if err != nil {
			val = ""
		}
		outcmds[cmd.Var] = val
	}

	templ := templatePrelude + envTemplate(incmds) + templatePostlude

	t := template.Must(template.New("templ").Parse(templ))

	var buf bytes.Buffer
	err = t.Execute(&buf, outcmds)
	if err != nil {
		os.Exit(1)
	}
	fmted, err := format.Source(buf.Bytes())
	if err != nil {
		os.Exit(1)
	}

	f := os.Stdout
	if *fname != "" {
		f, err = os.Create(*fname)
		if err != nil {
			os.Exit(1)
		}
	}
	defer f.Close()
	f.Write(fmted)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"github.com/driusan/dgit/cmd"
	"github.com/driusan/dgit/git"
	"os"
)

var InvalidArgument error = errors.New("Invalid argument to function")

func requiresGitDir(cmd string) bool {
	switch cmd {
	case "init", "clone":
		return false
	default:
		return true
	}
}

var subcommand, subcommandUsage string

func main() {
	workdir := flag.String("work-tree", "", "specify the working directory of git")
	gitdir := flag.String("git-dir", "", "specify the repository of git")
	dir := flag.String("C", "", "chdir before starting git")

	flag.Usage = func() {
		if subcommand == "" {
			subcommand = "subcommand"
		}
		if subcommandUsage == "" {
			subcommandUsage = fmt.Sprintf("%s [global options] %s [options]\n", os.Args[0], subcommand)
		}
		fmt.Fprintf(os.Stderr, "Usage: %s\n", subcommandUsage)
		fmt.Fprintf(os.Stderr, "\nGlobal options:\n\n")
		flag.PrintDefaults()
	}
	flag.Parse()
	args := flag.Args()
	if len(args) < 1 {
		flag.Usage()
		os.Exit(1)
	}
	if *dir != "" {
		if err := os.Chdir(*dir); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}
	c, err := git.NewClient(*gitdir, *workdir)
	subcommand = args[0]
	args = args[1:]

	if err != nil && requiresGitDir(subcommand) {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(3)
	}
	if c != nil && c.GitDir == "" && requiresGitDir(subcommand) {
		fmt.Fprint(os.Stderr, "Could not find .git directory\n")
		os.Exit(4)
	}

	switch subcommand {
	case "init":
		if err := cmd.Init(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "branch":
		cmd.Branch(c, args)
	case "checkout":
		if err := cmd.Checkout(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "checkout-index":
		if err := cmd.CheckoutIndexCmd(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "cat-file":
		if err := cmd.CatFile(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "add":
		if err := cmd.Add(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "commit":
		sha1, err := cmd.Commit(c, args)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		} else {
			fmt.Printf("%s\n", sha1)
		}
	case "commit-tree":
		sha1, err := cmd.CommitTree(c, args)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		} else {
			fmt.Printf("%s\n", sha1)
		}
	case "write-tree":
		sha1 := cmd.WriteTree(c, args)
		fmt.Printf("%s\n", sha1)
	case "update-ref":
		if err := cmd.UpdateRef(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "log":
		cmd.Log(c, args)
	case "symbolic-ref":
		val, err := cmd.SymbolicRef(c, args)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
		fmt.Printf("%s\n", val)
	case "clone":
		if err := cmd.Clone(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(2)

		}
	case "config":
		cmd.Config(c, args)
	case "fetch":
		cmd.Fetch(c, args)
	case "reset":
		if err := cmd.Reset(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(2)
		}
	case "merge-file":
		if err := cmd.MergeFile(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(2)
		}
	case "merge":
		if err := cmd.Merge(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(2)
		}
	case "merge-base":
		switch c, err := cmd.MergeBase(c, args); err {
		case cmd.Ancestor:
			os.Exit(0)
		case cmd.NonAncestor:
			os.Exit(1)
		default:
			if err != nil {
				fmt.Fprintf(os.Stderr, "%v\n", err)
				os.Exit(2)
			}
			fmt.Printf("%v\n", c)
		}
	case "rev-parse":
		commits, err := cmd.RevParse(c, args)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
		for _, sha := range commits {
			if sha.Excluded {
				fmt.Print("^")
			}
			fmt.Println(sha.Id.String())
		}

	case "rev-list":
		cmd.RevList(c, args)
	case "hash-object":
		cmd.HashObject(c, args)
	case "status":
		if err := cmd.Status(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "ls-tree":
		if err := cmd.LsTree(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "push":
		cmd.Push(c, args)
	case "pack-objects":
		cmd.PackObjects(c, os.Stdin, args)
	case "send-pack":
		cmd.SendPack(c, args)
	case "read-tree":
		if err := cmd.ReadTree(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "diff":
		if err := cmd.Diff(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "diff-files":
		if err := cmd.DiffFiles(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "diff-index":
		if err := cmd.DiffIndex(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "diff-tree":
		if err := cmd.DiffTree(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "ls-files":
		if err := cmd.LsFiles(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "index-pack":
		if err := cmd.IndexPack(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "update-index":
		if err := cmd.UpdateIndex(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "unpack-objects":
		if err := cmd.UnpackObjects(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "grep":
		if err := cmd.Grep(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "apply":
		if err := cmd.Apply(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "revert":
		if err := cmd.Revert(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	default:
		fmt.Fprintf(os.Stderr, "Unknown git command %s.\n", subcommand)
	}
}
<commit_msg>Exit code of 1 when there is an unknown git command<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"github.com/driusan/dgit/cmd"
	"github.com/driusan/dgit/git"
	"os"
)

var InvalidArgument error = errors.New("Invalid argument to function")

func requiresGitDir(cmd string) bool {
	switch cmd {
	case "init", "clone":
		return false
	default:
		return true
	}
}

var subcommand, subcommandUsage string

func main() {
	workdir := flag.String("work-tree", "", "specify the working directory of git")
	gitdir := flag.String("git-dir", "", "specify the repository of git")
	dir := flag.String("C", "", "chdir before starting git")

	flag.Usage = func() {
		if subcommand == "" {
			subcommand = "subcommand"
		}
		if subcommandUsage == "" {
			subcommandUsage = fmt.Sprintf("%s [global options] %s [options]\n", os.Args[0], subcommand)
		}
		fmt.Fprintf(os.Stderr, "Usage: %s\n", subcommandUsage)
		fmt.Fprintf(os.Stderr, "\nGlobal options:\n\n")
		flag.PrintDefaults()
	}
	flag.Parse()
	args := flag.Args()
	if len(args) < 1 {
		flag.Usage()
		os.Exit(1)
	}
	if *dir != "" {
		if err := os.Chdir(*dir); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}
	c, err := git.NewClient(*gitdir, *workdir)
	subcommand = args[0]
	args = args[1:]

	if err != nil && requiresGitDir(subcommand) {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(3)
	}
	if c != nil && c.GitDir == "" && requiresGitDir(subcommand) {
		fmt.Fprint(os.Stderr, "Could not find .git directory\n")
		os.Exit(4)
	}

	switch subcommand {
	case "init":
		if err := cmd.Init(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "branch":
		cmd.Branch(c, args)
	case "checkout":
		if err := cmd.Checkout(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "checkout-index":
		if err := cmd.CheckoutIndexCmd(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "cat-file":
		if err := cmd.CatFile(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "add":
		if err := cmd.Add(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "commit":
		sha1, err := cmd.Commit(c, args)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		} else {
			fmt.Printf("%s\n", sha1)
		}
	case "commit-tree":
		sha1, err := cmd.CommitTree(c, args)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		} else {
			fmt.Printf("%s\n", sha1)
		}
	case "write-tree":
		sha1 := cmd.WriteTree(c, args)
		fmt.Printf("%s\n", sha1)
	case "update-ref":
		if err := cmd.UpdateRef(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "log":
		cmd.Log(c, args)
	case "symbolic-ref":
		val, err := cmd.SymbolicRef(c, args)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
		fmt.Printf("%s\n", val)
	case "clone":
		if err := cmd.Clone(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(2)

		}
	case "config":
		cmd.Config(c, args)
	case "fetch":
		cmd.Fetch(c, args)
	case "reset":
		if err := cmd.Reset(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(2)
		}
	case "merge-file":
		if err := cmd.MergeFile(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(2)
		}
	case "merge":
		if err := cmd.Merge(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(2)
		}
	case "merge-base":
		switch c, err := cmd.MergeBase(c, args); err {
		case cmd.Ancestor:
			os.Exit(0)
		case cmd.NonAncestor:
			os.Exit(1)
		default:
			if err != nil {
				fmt.Fprintf(os.Stderr, "%v\n", err)
				os.Exit(2)
			}
			fmt.Printf("%v\n", c)
		}
	case "rev-parse":
		commits, err := cmd.RevParse(c, args)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
		for _, sha := range commits {
			if sha.Excluded {
				fmt.Print("^")
			}
			fmt.Println(sha.Id.String())
		}

	case "rev-list":
		cmd.RevList(c, args)
	case "hash-object":
		cmd.HashObject(c, args)
	case "status":
		if err := cmd.Status(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "ls-tree":
		if err := cmd.LsTree(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "push":
		cmd.Push(c, args)
	case "pack-objects":
		cmd.PackObjects(c, os.Stdin, args)
	case "send-pack":
		cmd.SendPack(c, args)
	case "read-tree":
		if err := cmd.ReadTree(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "diff":
		if err := cmd.Diff(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "diff-files":
		if err := cmd.DiffFiles(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "diff-index":
		if err := cmd.DiffIndex(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "diff-tree":
		if err := cmd.DiffTree(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "ls-files":
		if err := cmd.LsFiles(c, args); err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(4)
		}
	case "index-pack":
		if err := cmd.IndexPack(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "update-index":
		if err := cmd.UpdateIndex(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "unpack-objects":
		if err := cmd.UnpackObjects(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "grep":
		if err := cmd.Grep(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "apply":
		if err := cmd.Apply(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	case "revert":
		if err := cmd.Revert(c, args); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(4)
		}
	default:
		fmt.Fprintf(os.Stderr, "Unknown git command %s.\n", subcommand)
                os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"code.google.com/p/gcfg"
)

type config struct {
	Main struct {
		URL      string
		Interval int
		Wait     int
		Filename string
	}
}

func writeit(file string, when string, what string) {
	f, _ := os.OpenFile(file, os.O_APPEND|os.O_WRONLY, 0600)
	defer f.Close()
	f.WriteString("<!-- redirect loop detected, restarted at: " + when + " Status: " + what + " -->\n")
}

func main() {
	var cfg config
	gcfg.ReadFileInto(&cfg, "config.gcfg")

	recovered := true

	ticker := time.NewTicker(time.Second * time.Duration(cfg.Main.Interval))

	for now := range ticker.C {
		func() {
			resp, err := http.Get(cfg.Main.URL)
			defer resp.Body.Close()

			if err != nil && strings.Contains(err.Error(), "redirects") {
				if recovered {
					recovered = false
					writeit(cfg.Main.Filename, now.Format(time.RFC1123), strconv.Itoa(resp.StatusCode))
					fmt.Println(resp.StatusCode, "at", now, " - Updating", cfg.Main.Filename, "and waiting for", (cfg.Main.Wait), "seconds")
				} else {
					fmt.Println(resp.StatusCode, "at", now, " - Not recovered from previous failure, trying again in", (cfg.Main.Wait), "seconds")
				}
				time.Sleep(time.Duration(cfg.Main.Wait) * time.Second)
			} else if err != nil {
				fmt.Println("Unknown error while trying to receive", cfg.Main.URL, ". Waiting for", (cfg.Main.Wait), "seconds")
				time.Sleep(time.Duration(cfg.Main.Wait) * time.Second)
			} else {
				recovered = true
				fmt.Println(resp.StatusCode, "at", now)
			}
		}()
	}
}
<commit_msg>Made file extension and line breaks windows friendly<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"code.google.com/p/gcfg"
)

type config struct {
	Main struct {
		URL      string
		Interval int
		Wait     int
		Filename string
	}
}

func writeit(file string, when string, what string) {
	f, _ := os.OpenFile(file, os.O_APPEND|os.O_WRONLY, 0600)
	defer f.Close()
	f.WriteString("<!-- redirect loop detected, restarted at: " + when + " Status: " + what + " -->\r\n")
}

func main() {
	var cfg config
	gcfg.ReadFileInto(&cfg, "config.ini")

	recovered := true

	ticker := time.NewTicker(time.Second * time.Duration(cfg.Main.Interval))

	for now := range ticker.C {
		func() {
			resp, err := http.Get(cfg.Main.URL)
			defer resp.Body.Close()

			if err != nil && strings.Contains(err.Error(), "redirects") {
				if recovered {
					recovered = false
					writeit(cfg.Main.Filename, now.Format(time.RFC1123), strconv.Itoa(resp.StatusCode))
					fmt.Println(resp.StatusCode, "at", now, " - Updating", cfg.Main.Filename, "and waiting for", (cfg.Main.Wait), "seconds")
				} else {
					fmt.Println(resp.StatusCode, "at", now, " - Not recovered from previous failure, trying again in", (cfg.Main.Wait), "seconds")
				}
				time.Sleep(time.Duration(cfg.Main.Wait) * time.Second)
			} else if err != nil {
				fmt.Println("Unknown error while trying to receive", cfg.Main.URL, ". Waiting for", (cfg.Main.Wait), "seconds")
				time.Sleep(time.Duration(cfg.Main.Wait) * time.Second)
			} else {
				recovered = true
				fmt.Println(resp.StatusCode, "at", now)
			}
		}()
	}
}
<|endoftext|>