<commit_before>package main

import (
	"errors"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"unicode/utf8"

	"github.com/charlievieth/gocode"
)

type GoCode struct {
	Autoinst      bool
	InstallSuffix string
	Env           map[string]string
	Home          string
	Dir           string
	Builtins      bool
	Fn            string
	Src           string
	Pos           int
	calltip       bool `json:"-"` // ignore
}

// Separate type so we can init Calltip to true
type GoCodeCalltip struct {
	GoCode
}

func (g *GoCodeCalltip) Init() {
	g.calltip = true
}

type GoCodeResponse struct {
	Candidates []gocode.Candidate
}

var lastCalltip struct {
	sync.Mutex
	Path string
	Src  string
	Off  int
	Res  GoCodeResponse
}

func useLastCalltip(path, src string, off int) (res GoCodeResponse, ok bool) {
	lastCalltip.Lock()
	if off == lastCalltip.Off && path == lastCalltip.Path && src == lastCalltip.Src {
		res = lastCalltip.Res
		ok = true
	}
	lastCalltip.Unlock()
	return
}

func setLastCalltip(path, src string, off int, res GoCodeResponse) {
	lastCalltip.Lock()
	if off != lastCalltip.Off || path != lastCalltip.Path || src != lastCalltip.Src {
		lastCalltip.Off = off
		lastCalltip.Path = path
		lastCalltip.Src = src
		lastCalltip.Res = res
	}
	lastCalltip.Unlock()
	return
}

func (g *GoCode) Call() (interface{}, string) {
	off, err := g.bytePos()
	if err != nil {
		return g.response(nil, err, false)
	}
	path := g.filepath()
	if g.calltip {
		if res, ok := useLastCalltip(path, g.Src, off); ok {
			return res, ""
		}
		gr, err := g.calltips(path, []byte(g.Src), off)
		res, errStr := g.response(gr, err, false)
		setLastCalltip(path, g.Src, off, res)
		return res, errStr
	}
	return g.response(g.complete(path, []byte(g.Src), off), nil, true)
}

var NoGocodeCandidates = []gocode.Candidate{}

func (g *GoCode) response(res []gocode.Candidate, err error, install bool) (GoCodeResponse, string) {
	if res == nil || len(res) == 0 {
		if install && g.Autoinst {
			autoInstall(AutoInstOptions{
				Src:           g.Src,
				Env:           g.Env,
				InstallSuffix: g.InstallSuffix,
			})
		}
		res = NoGocodeCandidates
	}
	var errStr string
	if err != nil {
		errStr = err.Error()
	}
	return GoCodeResponse{Candidates: res}, errStr
}

// Matching GoSublime's behavior here...
func (g *GoCode) filepath() string {
	if filepath.IsAbs(g.Fn) {
		return filepath.Clean(g.Fn)
	}
	var base string
	if g.Dir != "" {
		base = g.Dir
	} else {
		base = g.Home
	}
	var name string
	if g.Fn != "" {
		name = g.Fn
	} else {
		name = "_.go"
	}
	return filepath.Join(base, name)
}

func (g *GoCode) complete(filename string, src []byte, offset int) []gocode.Candidate {
	conf := gocode.Config{
		GOROOT:        g.GOROOT(),
		GOPATH:        g.GOPATH(),
		InstallSuffix: g.InstallSuffix,
		Builtins:      g.Builtins,
	}
	return conf.Complete(src, filename, offset)
}

func (g *GoCode) GOPATH() string {
	if g.Env != nil {
		if s, ok := g.Env["GOPATH"]; ok && s != "" {
			return s
		}
	}
	return os.Getenv("GOPATH")
}

func (g *GoCode) GOROOT() string {
	if g.Env != nil {
		if s, ok := g.Env["GOROOT"]; ok && s != "" {
			return s
		}
	}
	return runtime.GOROOT()
}

// TODO: cache results and potentially cache AST ad file set.
func (g *GoCode) calltips(filename string, src []byte, offset int) ([]gocode.Candidate, error) {
	fset := token.NewFileSet()
	af, err := parser.ParseFile(fset, filename, src, 0)
	if af == nil {
		return nil, err
	}
	pos, err := g.convertOffset(af, fset, offset)
	if err != nil {
		return nil, err
	}
	id, err := g.findCaller(af, fset, pos)
	if err != nil {
		return nil, err
	}
	end := fset.Position(id.End())
	if !end.IsValid() {
		return nil, errors.New("calltip: invalid end pos")
	}
	if err := g.validLine(fset, pos, end); err != nil {
		return nil, err
	}
	cl := g.complete(filename, src, end.Offset)
	for i := 0; i < len(cl); i++ {
		if strings.EqualFold(id.Name, cl[i].Name) {
			return cl[i : i+1], nil
		}
	}
	return nil, errors.New("calltip: no candidates")
}

// Matching GoSublime's behavior here, see if this can be removed.
func (g *GoCode) validLine(fset *token.FileSet, off token.Pos, end token.Position) error {
	line := fset.Position(off).Line
	if end.Line == line || line == 0 {
		return nil
	}
	return fmt.Errorf("calltip: line mismatch end (%d) line (%d)", end.Line, line)
}

func (g *GoCode) convertOffset(af *ast.File, fset *token.FileSet, offset int) (token.Pos, error) {
	f := fset.File(af.Pos())
	if f == nil {
		return token.NoPos, errors.New("calltip: ast file not in fileset")
	}
	// Prevent Pos from panicking
	if offset > f.Size() {
		return token.NoPos, errors.New("calltip: illegal file offset")
	}
	return f.Pos(offset), nil
}

func (g *GoCode) findCaller(af *ast.File, fset *token.FileSet, pos token.Pos) (*ast.Ident, error) {
	v := calltipVisitor{
		pos:  pos,
		fset: fset,
	}
	ast.Walk(&v, af)
	if v.expr == nil || v.expr.Fun == nil {
		return nil, errors.New("calltip: nil CallExpr")
	}
	switch x := v.expr.Fun.(type) {
	case *ast.Ident:
		return x, nil
	case *ast.SelectorExpr:
		return x.Sel, nil
	}
	return nil, errors.New("calltip: invalid CallExpr.Fun")
}

// calltipVisitor satisfies the ast.Visitor interface.
type calltipVisitor struct {
	pos  token.Pos
	fset *token.FileSet
	expr *ast.CallExpr
}

func (v *calltipVisitor) Visit(node ast.Node) (w ast.Visitor) {
	if node == nil || v.expr != nil {
		return v
	}
	if x, ok := node.(*ast.CallExpr); ok {
		pos := x.Pos()
		end := x.End()
		if pos.IsValid() && end.IsValid() {
			if pos <= v.pos && v.pos <= end {
				v.expr = x
				return nil
			}
		}
	}
	return v
}

func (g *GoCode) bytePos() (int, error) {
	s := g.Src
	off := g.Pos
	if len(s) == 0 || len(s) <= off || off < 0 {
		return -1, errors.New("gocode: nil source")
	}
	i := 0
	var n int
	for n = 0; i < len(s) && n < off; n++ {
		if s[i] < utf8.RuneSelf {
			i++
		} else {
			_, size := utf8.DecodeRuneInString(s[i:])
			i += size
		}
	}
	if n == off && i < len(s) {
		return i, nil
	}
	return -1, fmt.Errorf("gocode: invalid offset: %d", g.Pos)
}

func init() {
	registry.Register("gocode_complete", func(b *Broker) Caller {
		return &GoCode{}
	})

	registry.Register("gocode_calltip", func(b *Broker) Caller {
		return &GoCode{calltip: true}
	})
}

// Initial implementation of calltips cache
/*

import (
	"fmt"
	"sync"

	"github.com/golang/groupcache/lru"
)

type Candidate struct {
	Name  string `json:"name"`
	Type  string `json:"type"`
	Class string `json:"class"`
}

type FileEntry struct {
	Name       string            // file name
	Source     string            // file source
	Candidates map[int]Candidate // decl offset => Candidate
	mu         sync.RWMutex
}

func NewFileEntry(name, source string) *FileEntry {
	return &FileEntry{
		Name:       name,
		Source:     source,
		Candidates: make(map[int]Candidate),
	}
}

func (f *FileEntry) Get(pos int) (c Candidate, ok bool) {
	f.mu.RLock()
	if f.Candidates != nil {
		c, ok = f.Candidates[pos]
	}
	f.mu.RUnlock()
	return
}

func (f *FileEntry) Add(pos int, c Candidate) {
	f.mu.Lock()
	if f.Candidates == nil {
		f.Candidates = make(map[int]Candidate)
	}
	f.Candidates[pos] = c
	f.mu.Unlock()
}

type Cache struct {
	cache *lru.Cache
	mu    sync.Mutex
}

func New() *Cache {
	return &Cache{cache: lru.New(128)}
}

func (c *Cache) Get(name, source string, pos int) (Candidate, bool) {
	c.mu.Lock()
	defer c.mu.Unlock()
	v, ok := c.cache.Get(name)
	if !ok {
		return Candidate{}, false
	}
	e := v.(*FileEntry)
	if e.Source != source {
		return Candidate{}, false
	}
	return e.Get(pos)
}

func (c *Cache) Add(name, source string, pos int, can Candidate) {
	c.mu.Lock()
	defer c.mu.Unlock()
	var e *FileEntry
	if v, ok := c.cache.Get(name); ok {
		e = v.(*FileEntry)
		if e.Source != source {
			c.cache.Remove(name)
			e = nil
		}
	}
	if e == nil {
		e = NewFileEntry(name, source)
	}
	e.Add(pos, can)
	c.cache.Add(name, e)
}

func main() {
	c := New()
	c.Add("name", "source", 1, Candidate{Name: "c1", Type: "t1", Class: "c1"})
	fmt.Println(c.Get("name", "source", 1))
	c.Add("name", "source new", 1, Candidate{Name: "c1", Type: "t1", Class: "c1"})
	fmt.Println(c.Get("name", "source", 1))
	fmt.Println(c.Get("name", "source new", 1))
}
*/
<commit_msg>gocode: Cache parsed AST for calltips<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"unicode/utf8"

	"git.vieth.io/mgo_old/lru"
	"github.com/charlievieth/gocode"
)

type GoCode struct {
	Autoinst      bool
	InstallSuffix string
	Env           map[string]string
	Home          string
	Dir           string
	Builtins      bool
	Fn            string
	Src           string
	Pos           int
	calltip       bool `json:"-"` // ignore
}

// Separate type so we can init Calltip to true
type GoCodeCalltip struct {
	GoCode
}

func (g *GoCodeCalltip) Init() {
	g.calltip = true
}

var calltipCache = NewAstCache(200)

func init() {
	registry.Register("gocode_complete", func(b *Broker) Caller {
		return &GoCode{}
	})

	registry.Register("gocode_calltip", func(b *Broker) Caller {
		return &GoCode{calltip: true}
	})
}

type GoCodeResponse struct {
	Candidates []gocode.Candidate
}

var lastCalltip struct {
	sync.Mutex
	Path string
	Src  string
	Off  int
	Res  GoCodeResponse
}

func useLastCalltip(path, src string, off int) (res GoCodeResponse, ok bool) {
	lastCalltip.Lock()
	if off == lastCalltip.Off && path == lastCalltip.Path && src == lastCalltip.Src {
		res = lastCalltip.Res
		ok = true
	}
	lastCalltip.Unlock()
	return
}

func setLastCalltip(path, src string, off int, res GoCodeResponse) {
	lastCalltip.Lock()
	if off != lastCalltip.Off || path != lastCalltip.Path || src != lastCalltip.Src {
		lastCalltip.Off = off
		lastCalltip.Path = path
		lastCalltip.Src = src
		lastCalltip.Res = res
	}
	lastCalltip.Unlock()
	return
}

func (g *GoCode) Call() (interface{}, string) {
	off, err := g.bytePos()
	if err != nil {
		return g.response(nil, err, false)
	}
	path := g.filepath()
	if g.calltip {
		if res, ok := useLastCalltip(path, g.Src, off); ok {
			return res, ""
		}
		gr, err := g.calltips(path, []byte(g.Src), off)
		res, errStr := g.response(gr, err, false)
		setLastCalltip(path, g.Src, off, res)
		return res, errStr
	}
	return g.response(g.complete(path, []byte(g.Src), off), nil, true)
}

var NoGocodeCandidates = []gocode.Candidate{}

func (g *GoCode) response(res []gocode.Candidate, err error, install bool) (GoCodeResponse, string) {
	if res == nil || len(res) == 0 {
		if install && g.Autoinst {
			autoInstall(AutoInstOptions{
				Src:           g.Src,
				Env:           g.Env,
				InstallSuffix: g.InstallSuffix,
			})
		}
		res = NoGocodeCandidates
	}
	var errStr string
	if err != nil {
		errStr = err.Error()
	}
	return GoCodeResponse{Candidates: res}, errStr
}

// Matching GoSublime's behavior here...
func (g *GoCode) filepath() string {
	if filepath.IsAbs(g.Fn) {
		return filepath.Clean(g.Fn)
	}
	var base string
	if g.Dir != "" {
		base = g.Dir
	} else {
		base = g.Home
	}
	var name string
	if g.Fn != "" {
		name = g.Fn
	} else {
		name = "_.go"
	}
	return filepath.Join(base, name)
}

func (g *GoCode) complete(filename string, src []byte, offset int) []gocode.Candidate {
	conf := gocode.Config{
		GOROOT:        g.GOROOT(),
		GOPATH:        g.GOPATH(),
		InstallSuffix: g.InstallSuffix,
		Builtins:      g.Builtins,
	}
	return conf.Complete(src, filename, offset)
}

func (g *GoCode) GOPATH() string {
	if g.Env != nil {
		if s, ok := g.Env["GOPATH"]; ok && s != "" {
			return s
		}
	}
	return os.Getenv("GOPATH")
}

func (g *GoCode) GOROOT() string {
	if g.Env != nil {
		if s, ok := g.Env["GOROOT"]; ok && s != "" {
			return s
		}
	}
	return runtime.GOROOT()
}

// TODO: cache results and potentially cache AST ad file set.
func (g *GoCode) calltips(filename string, src []byte, offset int) ([]gocode.Candidate, error) {
	fset, af, err := calltipCache.ParseFile(filename, src)
	if af == nil {
		return nil, err
	}
	pos, err := g.convertOffset(af, fset, offset)
	if err != nil {
		return nil, err
	}
	id, err := g.findCaller(af, fset, pos)
	if err != nil {
		return nil, err
	}
	end := fset.Position(id.End())
	if !end.IsValid() {
		return nil, errors.New("calltip: invalid end pos")
	}
	if err := g.validLine(fset, pos, end); err != nil {
		return nil, err
	}
	cl := g.complete(filename, src, end.Offset)
	for i := 0; i < len(cl); i++ {
		if strings.EqualFold(id.Name, cl[i].Name) {
			return cl[i : i+1], nil
		}
	}
	return nil, errors.New("calltip: no candidates")
}

// Matching GoSublime's behavior here, see if this can be removed.
func (g *GoCode) validLine(fset *token.FileSet, off token.Pos, end token.Position) error {
	line := fset.Position(off).Line
	if end.Line == line || line == 0 {
		return nil
	}
	return fmt.Errorf("calltip: line mismatch end (%d) line (%d)", end.Line, line)
}

func (g *GoCode) convertOffset(af *ast.File, fset *token.FileSet, offset int) (token.Pos, error) {
	f := fset.File(af.Pos())
	if f == nil {
		return token.NoPos, errors.New("calltip: ast file not in fileset")
	}
	// Prevent Pos from panicking
	if offset > f.Size() {
		return token.NoPos, errors.New("calltip: illegal file offset")
	}
	return f.Pos(offset), nil
}

func (g *GoCode) findCaller(af *ast.File, fset *token.FileSet, pos token.Pos) (*ast.Ident, error) {
	v := calltipVisitor{
		pos:  pos,
		fset: fset,
	}
	ast.Walk(&v, af)
	if v.expr == nil || v.expr.Fun == nil {
		return nil, errors.New("calltip: nil CallExpr")
	}
	switch x := v.expr.Fun.(type) {
	case *ast.Ident:
		return x, nil
	case *ast.SelectorExpr:
		return x.Sel, nil
	}
	return nil, errors.New("calltip: invalid CallExpr.Fun")
}

// calltipVisitor satisfies the ast.Visitor interface.
type calltipVisitor struct {
	pos  token.Pos
	fset *token.FileSet
	expr *ast.CallExpr
}

func (v *calltipVisitor) Visit(node ast.Node) (w ast.Visitor) {
	if node == nil || v.expr != nil {
		return v
	}
	if x, ok := node.(*ast.CallExpr); ok {
		pos := x.Pos()
		end := x.End()
		if pos.IsValid() && end.IsValid() {
			if pos <= v.pos && v.pos <= end {
				v.expr = x
				return nil
			}
		}
	}
	return v
}

func (g *GoCode) bytePos() (int, error) {
	s := g.Src
	off := g.Pos
	if len(s) == 0 || len(s) <= off || off < 0 {
		return -1, errors.New("gocode: nil source")
	}
	i := 0
	var n int
	for n = 0; i < len(s) && n < off; n++ {
		if s[i] < utf8.RuneSelf {
			i++
		} else {
			_, size := utf8.DecodeRuneInString(s[i:])
			i += size
		}
	}
	if n == off && i < len(s) {
		return i, nil
	}
	return -1, fmt.Errorf("gocode: invalid offset: %d", g.Pos)
}

type AstEntry struct {
	Name    string
	Body    []byte
	File    *ast.File
	FileSet *token.FileSet
}

type AstCache struct {
	cache *lru.Cache
	mu    sync.Mutex
}

func NewAstCache(maxEntries int) *AstCache {
	return &AstCache{
		cache: lru.New(maxEntries),
	}
}

func (c *AstCache) Add(filename string, ent *AstEntry) {
	c.mu.Lock()
	c.cache.Add(filename, ent)
	c.mu.Unlock()
}

func (c *AstCache) Get(filename string) (*AstEntry, bool) {
	c.mu.Lock()
	v, ok := c.cache.Get(filename)
	c.mu.Unlock()
	if ok {
		return v.(*AstEntry), true
	}
	return nil, false
}

func (c *AstCache) Remove(filename string) {
	c.mu.Lock()
	c.cache.Remove(filename)
	c.mu.Unlock()
}

func (c *AstCache) ParseFile(filename string, src interface{}) (*token.FileSet, *ast.File, error) {
	text, err := readSource(filename, src)
	if err != nil {
		return nil, nil, err
	}
	if e, ok := c.Get(filename); ok {
		if bytes.Equal(e.Body, text) {
			return e.FileSet, e.File, nil
		}
		c.Remove(filename)
	}
	fset := token.NewFileSet()
	af, err := parser.ParseFile(fset, filename, text, 0)
	if af == nil {
		return nil, nil, err
	}
	c.Add(filename, &AstEntry{
		Name:    filename,
		Body:    text,
		File:    af,
		FileSet: fset,
	})
	return fset, af, nil
}

func readSource(filename string, src interface{}) ([]byte, error) {
	if src != nil {
		switch s := src.(type) {
		case string:
			return []byte(s), nil
		case []byte:
			return s, nil
		case *bytes.Buffer:
			// is io.Reader, but src is already available in []byte form
			if s != nil {
				return s.Bytes(), nil
			}
		case io.Reader:
			var buf bytes.Buffer
			if _, err := io.Copy(&buf, s); err != nil {
				return nil, err
			}
			return buf.Bytes(), nil
		}
		return nil, errors.New("invalid source")
	}
	return ioutil.ReadFile(filename)
}

// Initial implementation of calltips cache
/*

import (
	"fmt"
	"sync"

	"github.com/golang/groupcache/lru"
)

type Candidate struct {
	Name  string `json:"name"`
	Type  string `json:"type"`
	Class string `json:"class"`
}

type FileEntry struct {
	Name       string            // file name
	Source     string            // file source
	Candidates map[int]Candidate // decl offset => Candidate
	mu         sync.RWMutex
}

func NewFileEntry(name, source string) *FileEntry {
	return &FileEntry{
		Name:       name,
		Source:     source,
		Candidates: make(map[int]Candidate),
	}
}

func (f *FileEntry) Get(pos int) (c Candidate, ok bool) {
	f.mu.RLock()
	if f.Candidates != nil {
		c, ok = f.Candidates[pos]
	}
	f.mu.RUnlock()
	return
}

func (f *FileEntry) Add(pos int, c Candidate) {
	f.mu.Lock()
	if f.Candidates == nil {
		f.Candidates = make(map[int]Candidate)
	}
	f.Candidates[pos] = c
	f.mu.Unlock()
}

type Cache struct {
	cache *lru.Cache
	mu    sync.Mutex
}

func New() *Cache {
	return &Cache{cache: lru.New(128)}
}

func (c *Cache) Get(name, source string, pos int) (Candidate, bool) {
	c.mu.Lock()
	defer c.mu.Unlock()
	v, ok := c.cache.Get(name)
	if !ok {
		return Candidate{}, false
	}
	e := v.(*FileEntry)
	if e.Source != source {
		return Candidate{}, false
	}
	return e.Get(pos)
}

func (c *Cache) Add(name, source string, pos int, can Candidate) {
	c.mu.Lock()
	defer c.mu.Unlock()
	var e *FileEntry
	if v, ok := c.cache.Get(name); ok {
		e = v.(*FileEntry)
		if e.Source != source {
			c.cache.Remove(name)
			e = nil
		}
	}
	if e == nil {
		e = NewFileEntry(name, source)
	}
	e.Add(pos, can)
	c.cache.Add(name, e)
}

func main() {
	c := New()
	c.Add("name", "source", 1, Candidate{Name: "c1", Type: "t1", Class: "c1"})
	fmt.Println(c.Get("name", "source", 1))
	c.Add("name", "source new", 1, Candidate{Name: "c1", Type: "t1", Class: "c1"})
	fmt.Println(c.Get("name", "source", 1))
	fmt.Println(c.Get("name", "source new", 1))
}
*/
<|endoftext|>


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

// This file contains support functionality for godoc.

package main

import (
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"
	"utf8"
)

// An RWValue wraps a value and permits mutually exclusive
// access to it and records the time the value was last set.
//
type RWValue struct {
	mutex     sync.RWMutex
	value     interface{}
	timestamp int64 // time of last set(), in seconds since epoch
}

func (v *RWValue) set(value interface{}) {
	v.mutex.Lock()
	v.value = value
	v.timestamp = time.Seconds()
	v.mutex.Unlock()
}

func (v *RWValue) get() (interface{}, int64) {
	v.mutex.RLock()
	defer v.mutex.RUnlock()
	return v.value, v.timestamp
}

// TODO(gri) For now, using os.Getwd() is ok here since the functionality
//           based on this code is not invoked for the appengine version,
//           but this is fragile. Determine what the right thing to do is,
//           here (possibly have some Getwd-equivalent in FileSystem).
var cwd, _ = os.Getwd() // ignore errors

// canonicalizePaths takes a list of (directory/file) paths and returns
// the list of corresponding absolute paths in sorted (increasing) order.
// Relative paths are assumed to be relative to the current directory,
// empty and duplicate paths as well as paths for which filter(path) is
// false are discarded. filter may be nil in which case it is not used.
//
func canonicalizePaths(list []string, filter func(path string) bool) []string {
	i := 0
	for _, path := range list {
		path = strings.TrimSpace(path)
		if len(path) == 0 {
			continue // ignore empty paths (don't assume ".")
		}
		// len(path) > 0: normalize path
		if filepath.IsAbs(path) {
			path = filepath.Clean(path)
		} else {
			path = filepath.Join(cwd, path)
		}
		// we have a non-empty absolute path
		if filter != nil && !filter(path) {
			continue
		}
		// keep the path
		list[i] = path
		i++
	}
	list = list[0:i]

	// sort the list and remove duplicate entries
	sort.Strings(list)
	i = 0
	prev := ""
	for _, path := range list {
		if path != prev {
			list[i] = path
			i++
			prev = path
		}
	}

	return list[0:i]
}

// writeFileAtomically writes data to a temporary file and then
// atomically renames that file to the file named by filename.
//
func writeFileAtomically(filename string, data []byte) os.Error {
	// TODO(gri) this won't work on appengine
	f, err := ioutil.TempFile(filepath.Split(filename))
	if err != nil {
		return err
	}
	n, err := f.Write(data)
	f.Close()
	if err != nil {
		return err
	}
	if n < len(data) {
		return io.ErrShortWrite
	}
	return os.Rename(f.Name(), filename)
}

// isText returns true if a significant prefix of s looks like correct UTF-8;
// that is, if it is likely that s is human-readable text.
//
func isText(s []byte) bool {
	const max = 1024 // at least utf8.UTFMax
	if len(s) > max {
		s = s[0:max]
	}
	for i, c := range string(s) {
		if i+utf8.UTFMax > len(s) {
			// last char may be incomplete - ignore
			break
		}
		if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' {
			// decoding error or control character - not a text file
			return false
		}
	}
	return true
}

// TODO(gri): Should have a mapping from extension to handler, eventually.

// textExt[x] is true if the extension x indicates a text file, and false otherwise.
var textExt = map[string]bool{
	".css": false, // must be served raw
	".js":  false, // must be served raw
}

// isTextFile returns true if the file has a known extension indicating
// a text file, or if a significant chunk of the specified file looks like
// correct UTF-8; that is, if it is likely that the file contains human-
// readable text.
//
func isTextFile(filename string) bool {
	// if the extension is known, use it for decision making
	if isText, found := textExt[filepath.Ext(filename)]; found {
		return isText
	}

	// the extension is not known; read an initial chunk
	// of the file and check if it looks like text
	f, err := fs.Open(filename)
	if err != nil {
		return false
	}
	defer f.Close()

	var buf [1024]byte
	n, err := f.Read(buf[0:])
	if err != nil {
		return false
	}

	return isText(buf[0:n])
}
<commit_msg>godoc: allow form feed in text files<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains support functionality for godoc.

package main

import (
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"
	"utf8"
)

// An RWValue wraps a value and permits mutually exclusive
// access to it and records the time the value was last set.
//
type RWValue struct {
	mutex     sync.RWMutex
	value     interface{}
	timestamp int64 // time of last set(), in seconds since epoch
}

func (v *RWValue) set(value interface{}) {
	v.mutex.Lock()
	v.value = value
	v.timestamp = time.Seconds()
	v.mutex.Unlock()
}

func (v *RWValue) get() (interface{}, int64) {
	v.mutex.RLock()
	defer v.mutex.RUnlock()
	return v.value, v.timestamp
}

// TODO(gri) For now, using os.Getwd() is ok here since the functionality
//           based on this code is not invoked for the appengine version,
//           but this is fragile. Determine what the right thing to do is,
//           here (possibly have some Getwd-equivalent in FileSystem).
var cwd, _ = os.Getwd() // ignore errors

// canonicalizePaths takes a list of (directory/file) paths and returns
// the list of corresponding absolute paths in sorted (increasing) order.
// Relative paths are assumed to be relative to the current directory,
// empty and duplicate paths as well as paths for which filter(path) is
// false are discarded. filter may be nil in which case it is not used.
//
func canonicalizePaths(list []string, filter func(path string) bool) []string {
	i := 0
	for _, path := range list {
		path = strings.TrimSpace(path)
		if len(path) == 0 {
			continue // ignore empty paths (don't assume ".")
		}
		// len(path) > 0: normalize path
		if filepath.IsAbs(path) {
			path = filepath.Clean(path)
		} else {
			path = filepath.Join(cwd, path)
		}
		// we have a non-empty absolute path
		if filter != nil && !filter(path) {
			continue
		}
		// keep the path
		list[i] = path
		i++
	}
	list = list[0:i]

	// sort the list and remove duplicate entries
	sort.Strings(list)
	i = 0
	prev := ""
	for _, path := range list {
		if path != prev {
			list[i] = path
			i++
			prev = path
		}
	}

	return list[0:i]
}

// writeFileAtomically writes data to a temporary file and then
// atomically renames that file to the file named by filename.
//
func writeFileAtomically(filename string, data []byte) os.Error {
	// TODO(gri) this won't work on appengine
	f, err := ioutil.TempFile(filepath.Split(filename))
	if err != nil {
		return err
	}
	n, err := f.Write(data)
	f.Close()
	if err != nil {
		return err
	}
	if n < len(data) {
		return io.ErrShortWrite
	}
	return os.Rename(f.Name(), filename)
}

// isText returns true if a significant prefix of s looks like correct UTF-8;
// that is, if it is likely that s is human-readable text.
//
func isText(s []byte) bool {
	const max = 1024 // at least utf8.UTFMax
	if len(s) > max {
		s = s[0:max]
	}
	for i, c := range string(s) {
		if i+utf8.UTFMax > len(s) {
			// last char may be incomplete - ignore
			break
		}
		if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' {
			// decoding error or control character - not a text file
			return false
		}
	}
	return true
}

// TODO(gri): Should have a mapping from extension to handler, eventually.

// textExt[x] is true if the extension x indicates a text file, and false otherwise.
var textExt = map[string]bool{
	".css": false, // must be served raw
	".js":  false, // must be served raw
}

// isTextFile returns true if the file has a known extension indicating
// a text file, or if a significant chunk of the specified file looks like
// correct UTF-8; that is, if it is likely that the file contains human-
// readable text.
//
func isTextFile(filename string) bool {
	// if the extension is known, use it for decision making
	if isText, found := textExt[filepath.Ext(filename)]; found {
		return isText
	}

	// the extension is not known; read an initial chunk
	// of the file and check if it looks like text
	f, err := fs.Open(filename)
	if err != nil {
		return false
	}
	defer f.Close()

	var buf [1024]byte
	n, err := f.Read(buf[0:])
	if err != nil {
		return false
	}

	return isText(buf[0:n])
}
<|endoftext|>


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

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/scanner"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime/pprof"
	"strings"
)

var (
	// main operation modes
	list        = flag.Bool("l", false, "list files whose formatting differs from gofmt's")
	write       = flag.Bool("w", false, "write result to (source) file instead of stdout")
	rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')")
	simplifyAST = flag.Bool("s", false, "simplify code")
	doDiff      = flag.Bool("d", false, "display diffs instead of rewriting files")
	allErrors   = flag.Bool("e", false, "report all errors (not just the first 10 on different lines)")

	// debugging
	cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
)

const (
	tabWidth    = 8
	printerMode = printer.UseSpaces | printer.TabIndent
)

var (
	fileSet    = token.NewFileSet() // per process FileSet
	exitCode   = 0
	rewrite    func(*ast.File) *ast.File
	parserMode parser.Mode
)

func report(err error) {
	scanner.PrintError(os.Stderr, err)
	exitCode = 2
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: gofmt [flags] [path ...]\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func initParserMode() {
	parserMode = parser.ParseComments
	if *allErrors {
		parserMode |= parser.AllErrors
	}
}

func isGoFile(f os.FileInfo) bool {
	// ignore non-Go files
	name := f.Name()
	return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}

// If in == nil, the source is the contents of the file with the given filename.
func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
	if in == nil {
		f, err := os.Open(filename)
		if err != nil {
			return err
		}
		defer f.Close()
		in = f
	}

	src, err := ioutil.ReadAll(in)
	if err != nil {
		return err
	}

	file, adjust, err := parse(fileSet, filename, src, stdin)
	if err != nil {
		return err
	}

	if rewrite != nil {
		if adjust == nil {
			file = rewrite(file)
		} else {
			fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n")
		}
	}

	ast.SortImports(fileSet, file)

	if *simplifyAST {
		simplify(file)
	}

	var buf bytes.Buffer
	err = (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(&buf, fileSet, file)
	if err != nil {
		return err
	}
	res := buf.Bytes()
	if adjust != nil {
		res = adjust(src, res)
	}

	if !bytes.Equal(src, res) {
		// formatting has changed
		if *list {
			fmt.Fprintln(out, filename)
		}
		if *write {
			err = ioutil.WriteFile(filename, res, 0)
			if err != nil {
				return err
			}
		}
		if *doDiff {
			data, err := diff(src, res)
			if err != nil {
				return fmt.Errorf("computing diff: %s", err)
			}
			fmt.Printf("diff %s gofmt/%s\n", filename, filename)
			out.Write(data)
		}
	}

	if !*list && !*write && !*doDiff {
		_, err = out.Write(res)
	}

	return err
}

func visitFile(path string, f os.FileInfo, err error) error {
	if err == nil && isGoFile(f) {
		err = processFile(path, nil, os.Stdout, false)
	}
	if err != nil {
		report(err)
	}
	return nil
}

func walkDir(path string) {
	filepath.Walk(path, visitFile)
}

func main() {
	// call gofmtMain in a separate function
	// so that it can use defer and have them
	// run before the exit.
	gofmtMain()
	os.Exit(exitCode)
}

func gofmtMain() {
	flag.Usage = usage
	flag.Parse()

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "creating cpu profile: %s\n", err)
			exitCode = 2
			return
		}
		defer f.Close()
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	initParserMode()
	initRewrite()

	if flag.NArg() == 0 {
		if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil {
			report(err)
		}
		return
	}

	for i := 0; i < flag.NArg(); i++ {
		path := flag.Arg(i)
		switch dir, err := os.Stat(path); {
		case err != nil:
			report(err)
		case dir.IsDir():
			walkDir(path)
		default:
			if err := processFile(path, nil, os.Stdout, false); err != nil {
				report(err)
			}
		}
	}
}

func diff(b1, b2 []byte) (data []byte, err error) {
	f1, err := ioutil.TempFile("", "gofmt")
	if err != nil {
		return
	}
	defer os.Remove(f1.Name())
	defer f1.Close()

	f2, err := ioutil.TempFile("", "gofmt")
	if err != nil {
		return
	}
	defer os.Remove(f2.Name())
	defer f2.Close()

	f1.Write(b1)
	f2.Write(b2)

	data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
	if len(data) > 0 {
		// diff exits with a non-zero status when the files don't match.
		// Ignore that failure as long as we get output.
		err = nil
	}
	return

}

// parse parses src, which was read from filename,
// as a Go source file or statement list.
func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.File, func(orig, src []byte) []byte, error) {
	// Try as whole source file.
	file, err := parser.ParseFile(fset, filename, src, parserMode)
	if err == nil {
		return file, nil, nil
	}
	// If the error is that the source file didn't begin with a
	// package line and this is standard input, fall through to
	// try as a source fragment.  Stop and return on any other error.
	if !stdin || !strings.Contains(err.Error(), "expected 'package'") {
		return nil, nil, err
	}

	// If this is a declaration list, make it a source file
	// by inserting a package clause.
	// Insert using a ;, not a newline, so that the line numbers
	// in psrc match the ones in src.
	psrc := append([]byte("package p;"), src...)
	file, err = parser.ParseFile(fset, filename, psrc, parserMode)
	if err == nil {
		adjust := func(orig, src []byte) []byte {
			// Remove the package clause.
			// Gofmt has turned the ; into a \n.
			src = src[len("package p\n"):]
			return matchSpace(orig, src)
		}
		return file, adjust, nil
	}
	// If the error is that the source file didn't begin with a
	// declaration, fall through to try as a statement list.
	// Stop and return on any other error.
	if !strings.Contains(err.Error(), "expected declaration") {
		return nil, nil, err
	}

	// If this is a statement list, make it a source file
	// by inserting a package clause and turning the list
	// into a function body.  This handles expressions too.
	// Insert using a ;, not a newline, so that the line numbers
	// in fsrc match the ones in src.
	fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '}')
	file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
	if err == nil {
		adjust := func(orig, src []byte) []byte {
			// Remove the wrapping.
			// Gofmt has turned the ; into a \n\n.
			src = src[len("package p\n\nfunc _() {"):]
			src = src[:len(src)-len("\n}\n")]
			// Gofmt has also indented the function body one level.
			// Remove that indent.
			src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1)
			return matchSpace(orig, src)
		}
		return file, adjust, nil
	}

	// Failed, and out of options.
	return nil, nil, err
}

func cutSpace(b []byte) (before, middle, after []byte) {
	i := 0
	for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') {
		i++
	}
	j := len(b)
	for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') {
		j--
	}
	if i <= j {
		return b[:i], b[i:j], b[j:]
	}
	return nil, nil, b[j:]
}

// matchSpace reformats src to use the same space context as orig.
// 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src.
// 2) matchSpace copies the indentation of the first non-blank line in orig
//    to every non-blank line in src.
// 3) matchSpace copies the trailing space from orig and uses it in place
//   of src's trailing space.
func matchSpace(orig []byte, src []byte) []byte {
	before, _, after := cutSpace(orig)
	i := bytes.LastIndex(before, []byte{'\n'})
	before, indent := before[:i+1], before[i+1:]

	_, src, _ = cutSpace(src)

	var b bytes.Buffer
	b.Write(before)
	for len(src) > 0 {
		line := src
		if i := bytes.IndexByte(line, '\n'); i >= 0 {
			line, src = line[:i+1], line[i+1:]
		} else {
			src = nil
		}
		if len(line) > 0 && line[0] != '\n' { // not blank
			b.Write(indent)
		}
		b.Write(line)
	}
	b.Write(after)
	return b.Bytes()
}
<commit_msg>cmd/gofmt: don't permit -w with stdin<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/scanner"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime/pprof"
	"strings"
)

var (
	// main operation modes
	list        = flag.Bool("l", false, "list files whose formatting differs from gofmt's")
	write       = flag.Bool("w", false, "write result to (source) file instead of stdout")
	rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')")
	simplifyAST = flag.Bool("s", false, "simplify code")
	doDiff      = flag.Bool("d", false, "display diffs instead of rewriting files")
	allErrors   = flag.Bool("e", false, "report all errors (not just the first 10 on different lines)")

	// debugging
	cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
)

const (
	tabWidth    = 8
	printerMode = printer.UseSpaces | printer.TabIndent
)

var (
	fileSet    = token.NewFileSet() // per process FileSet
	exitCode   = 0
	rewrite    func(*ast.File) *ast.File
	parserMode parser.Mode
)

func report(err error) {
	scanner.PrintError(os.Stderr, err)
	exitCode = 2
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: gofmt [flags] [path ...]\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func initParserMode() {
	parserMode = parser.ParseComments
	if *allErrors {
		parserMode |= parser.AllErrors
	}
}

func isGoFile(f os.FileInfo) bool {
	// ignore non-Go files
	name := f.Name()
	return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}

// If in == nil, the source is the contents of the file with the given filename.
func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
	if in == nil {
		f, err := os.Open(filename)
		if err != nil {
			return err
		}
		defer f.Close()
		in = f
	}

	src, err := ioutil.ReadAll(in)
	if err != nil {
		return err
	}

	file, adjust, err := parse(fileSet, filename, src, stdin)
	if err != nil {
		return err
	}

	if rewrite != nil {
		if adjust == nil {
			file = rewrite(file)
		} else {
			fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n")
		}
	}

	ast.SortImports(fileSet, file)

	if *simplifyAST {
		simplify(file)
	}

	var buf bytes.Buffer
	err = (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(&buf, fileSet, file)
	if err != nil {
		return err
	}
	res := buf.Bytes()
	if adjust != nil {
		res = adjust(src, res)
	}

	if !bytes.Equal(src, res) {
		// formatting has changed
		if *list {
			fmt.Fprintln(out, filename)
		}
		if *write {
			err = ioutil.WriteFile(filename, res, 0644)
			if err != nil {
				return err
			}
		}
		if *doDiff {
			data, err := diff(src, res)
			if err != nil {
				return fmt.Errorf("computing diff: %s", err)
			}
			fmt.Printf("diff %s gofmt/%s\n", filename, filename)
			out.Write(data)
		}
	}

	if !*list && !*write && !*doDiff {
		_, err = out.Write(res)
	}

	return err
}

func visitFile(path string, f os.FileInfo, err error) error {
	if err == nil && isGoFile(f) {
		err = processFile(path, nil, os.Stdout, false)
	}
	if err != nil {
		report(err)
	}
	return nil
}

func walkDir(path string) {
	filepath.Walk(path, visitFile)
}

func main() {
	// call gofmtMain in a separate function
	// so that it can use defer and have them
	// run before the exit.
	gofmtMain()
	os.Exit(exitCode)
}

func gofmtMain() {
	flag.Usage = usage
	flag.Parse()

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "creating cpu profile: %s\n", err)
			exitCode = 2
			return
		}
		defer f.Close()
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	initParserMode()
	initRewrite()

	if flag.NArg() == 0 {
		if *write {
			fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input")
			exitCode = 2
			return
		}
		if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil {
			report(err)
		}
		return
	}

	for i := 0; i < flag.NArg(); i++ {
		path := flag.Arg(i)
		switch dir, err := os.Stat(path); {
		case err != nil:
			report(err)
		case dir.IsDir():
			walkDir(path)
		default:
			if err := processFile(path, nil, os.Stdout, false); err != nil {
				report(err)
			}
		}
	}
}

func diff(b1, b2 []byte) (data []byte, err error) {
	f1, err := ioutil.TempFile("", "gofmt")
	if err != nil {
		return
	}
	defer os.Remove(f1.Name())
	defer f1.Close()

	f2, err := ioutil.TempFile("", "gofmt")
	if err != nil {
		return
	}
	defer os.Remove(f2.Name())
	defer f2.Close()

	f1.Write(b1)
	f2.Write(b2)

	data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
	if len(data) > 0 {
		// diff exits with a non-zero status when the files don't match.
		// Ignore that failure as long as we get output.
		err = nil
	}
	return

}

// parse parses src, which was read from filename,
// as a Go source file or statement list.
func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.File, func(orig, src []byte) []byte, error) {
	// Try as whole source file.
	file, err := parser.ParseFile(fset, filename, src, parserMode)
	if err == nil {
		return file, nil, nil
	}
	// If the error is that the source file didn't begin with a
	// package line and this is standard input, fall through to
	// try as a source fragment.  Stop and return on any other error.
	if !stdin || !strings.Contains(err.Error(), "expected 'package'") {
		return nil, nil, err
	}

	// If this is a declaration list, make it a source file
	// by inserting a package clause.
	// Insert using a ;, not a newline, so that the line numbers
	// in psrc match the ones in src.
	psrc := append([]byte("package p;"), src...)
	file, err = parser.ParseFile(fset, filename, psrc, parserMode)
	if err == nil {
		adjust := func(orig, src []byte) []byte {
			// Remove the package clause.
			// Gofmt has turned the ; into a \n.
			src = src[len("package p\n"):]
			return matchSpace(orig, src)
		}
		return file, adjust, nil
	}
	// If the error is that the source file didn't begin with a
	// declaration, fall through to try as a statement list.
	// Stop and return on any other error.
	if !strings.Contains(err.Error(), "expected declaration") {
		return nil, nil, err
	}

	// If this is a statement list, make it a source file
	// by inserting a package clause and turning the list
	// into a function body.  This handles expressions too.
	// Insert using a ;, not a newline, so that the line numbers
	// in fsrc match the ones in src.
	fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '}')
	file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
	if err == nil {
		adjust := func(orig, src []byte) []byte {
			// Remove the wrapping.
			// Gofmt has turned the ; into a \n\n.
			src = src[len("package p\n\nfunc _() {"):]
			src = src[:len(src)-len("\n}\n")]
			// Gofmt has also indented the function body one level.
			// Remove that indent.
			src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1)
			return matchSpace(orig, src)
		}
		return file, adjust, nil
	}

	// Failed, and out of options.
	return nil, nil, err
}

func cutSpace(b []byte) (before, middle, after []byte) {
	i := 0
	for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') {
		i++
	}
	j := len(b)
	for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') {
		j--
	}
	if i <= j {
		return b[:i], b[i:j], b[j:]
	}
	return nil, nil, b[j:]
}

// matchSpace reformats src to use the same space context as orig.
// 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src.
// 2) matchSpace copies the indentation of the first non-blank line in orig
//    to every non-blank line in src.
// 3) matchSpace copies the trailing space from orig and uses it in place
//   of src's trailing space.
func matchSpace(orig []byte, src []byte) []byte {
	before, _, after := cutSpace(orig)
	i := bytes.LastIndex(before, []byte{'\n'})
	before, indent := before[:i+1], before[i+1:]

	_, src, _ = cutSpace(src)

	var b bytes.Buffer
	b.Write(before)
	for len(src) > 0 {
		line := src
		if i := bytes.IndexByte(line, '\n'); i >= 0 {
			line, src = line[:i+1], line[i+1:]
		} else {
			src = nil
		}
		if len(line) > 0 && line[0] != '\n' { // not blank
			b.Write(indent)
		}
		b.Write(line)
	}
	b.Write(after)
	return b.Bytes()
}
<|endoftext|>


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

package main

import (
	"bytes"
	"fmt"
	"rand"
	"sort"
	"sortingo"
	"strings"
	"testing"
)

// Largest data size used in the micro benchmarks.
const largeDataSize = 400

// sorterNames are the name of the sort algorithms in the desired run order.
var sorterNames = []string{"Binsert", "Comb", "Gnome", "Heap", "Insert", "MkQ", "Quick", "Select", "Shell"}
// sorters maps sort algorithm names to implementing functions.
var sorters = make(map[string]func([]string))
// sortSizes are the different sizes of data used in testing, in the desired run order.
var sortSizes = []int{10, 20, 50, 100, 400}
// dataSetNames are the names of the data sets, in the desired run order.
var dataSetNames = []string{"Repeat", "RepeatCycle", "Random", "PseudoWords", "SmallAlphabet", "Genome"}
// dataSets contains the various data sets that are used in testing.
var dataSets = make(map[string][]string)

// init sets up the benchmark data structures.
func init() {
	sorters["Binsert"] = sortingo.BinaryInsertionSort
	sorters["Comb"] = sortingo.CombSort
	sorters["Gnome"] = sortingo.GnomeSort
	sorters["Heap"] = sortingo.HeapSort
	sorters["Insert"] = sortingo.InsertionSort
	sorters["MkQ"] = sortingo.MultikeyQuickSort
	sorters["Quick"] = sort.SortStrings
	sorters["Select"] = sortingo.SelectionSort
	sorters["Shell"] = sortingo.ShellSort

	// Generate the repeated strings test data.
	repeatedStrings := make([]string, largeDataSize)
	a100 := strings.Repeat("A", 100)
	for idx := range repeatedStrings {
		repeatedStrings[idx] = a100
	}
	dataSets["Repeat"] = repeatedStrings

	// Generate a repeating cycle of strings.
	strs := make([]string, len(a100))
	for i := range strs {
		strs[i] = a100[0 : i+1]
	}
	repeatedCycleStrings := make([]string, largeDataSize)
	c := 0
	for i := range repeatedCycleStrings {
		repeatedCycleStrings[i] = strs[c]
		if c++; c >= len(strs) {
			c = 0
		}
	}
	dataSets["RepeatCycle"] = repeatedCycleStrings

	// Generate a set of random strings, each of length 100.
	randomStrings := make([]string, largeDataSize)
	for i := range randomStrings {
		bb := bytes.NewBuffer(make([]byte, 0, 100))
		for j := 0; j < 100; j++ {
			d := rand.Intn(95)
			bb.WriteRune(' ' + d)
		}
		randomStrings[i] = string(bb.Bytes())
	}
	dataSets["Random"] = randomStrings

	// Generate a set of unique pseudo words.
	uniqueWords := make([]string, largeDataSize)
	wordExists := make(map[string]bool)
	for i := range uniqueWords {
		var s string
		// Loop until a unique random word is generated.
		for {
			// Each word is from 1 to 28 characters long.
			l := 1 + rand.Intn(27)
			bb := bytes.NewBuffer(make([]byte, 0, l))
			// Each word consists only of the lowercase letters.
			for j := 0; j < l; j++ {
				d := rand.Intn(26)
				bb.WriteRune('a' + d)
			}
			s = string(bb.Bytes())
			if !wordExists[s] {
				break
			}
		}
		uniqueWords[i] = s
		wordExists[s] = true
	}
	dataSets["PseudoWords"] = uniqueWords

	// Generate a set of random strings, each of length 100,
	// consisting of a small alphabet of characters.
	smallAlphaStrings := make([]string, largeDataSize)
	for i := range smallAlphaStrings {
		l := 1 + rand.Intn(100)
		bb := bytes.NewBuffer(make([]byte, 0, l))
		for j := 0; j < l; j++ {
			d := rand.Intn(9)
			bb.WriteRune('a' + d)
		}
		smallAlphaStrings[i] = string(bb.Bytes())
	}
	dataSets["SmallAlphabet"] = smallAlphaStrings

	// Generate a set of random "genome" strings, each of length 9,
	// consisting of the letters a, c, g, t.
	genomeStrings := make([]string, largeDataSize)
	for i := range genomeStrings {
		bb := bytes.NewBuffer(make([]byte, 0, 9))
		for j := 0; j < 9; j++ {
			d := rand.Intn(4)
			switch d {
			case 0:
				bb.WriteRune('a')
			case 1:
				bb.WriteRune('c')
			case 2:
				bb.WriteRune('g')
			case 3:
				bb.WriteRune('t')
			}
		}
		genomeStrings[i] = string(bb.Bytes())
	}
	dataSets["Genome"] = genomeStrings
}

// main runs the micro benchmarks on the "slower" sorting algorithms
// using small data sets (under 500 elements).
func main() {
	// For each type of data set...
	// and each data set size...
	// and each sort implementation...
	// run the sort via the testing package benchmark facility.
	for _, dataSetName := range dataSetNames {
		fmt.Printf("%s...\n", dataSetName)
		dataSet := dataSets[dataSetName]
		for _, size := range sortSizes {
			input := make([]string, size)
			fmt.Printf("\t%d...\n", size)
			for _, sorterName := range sorterNames {
				sorter := sorters[sorterName]
				fmt.Printf("\t\t%-10s:\t", sorterName)
				harness := func(b *testing.B) {
					for i := 0; i < b.N; i++ {
						b.StopTimer()
						copy(input, dataSet)
						b.StartTimer()
						sorter(input)
					}
				}
				result := testing.Benchmark(harness)
				fmt.Println(result)
			}
		}
	}
}
<commit_msg>Adding command line flags for mbench command.<commit_after>//
// Copyright 2011 Nathan Fiedler. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"rand"
	"regexp"
	"sort"
	"sortingo"
	"strings"
	"testing"
)

// Largest data size used in the micro benchmarks.
const largeDataSize = 400

// sorterNames are the name of the sort algorithms in the desired run order.
var sorterNames = []string{"Binsert", "Comb", "Gnome", "Heap", "Insert", "MkQ", "Quick", "Select", "Shell"}
// sorters maps sort algorithm names to implementing functions.
var sorters = make(map[string]func([]string))
// sortSizes are the different sizes of data used in testing, in the desired run order.
var sortSizes = []int{10, 20, 50, 100, 400}
// dataSetNames are the names of the data sets, in the desired run order.
var dataSetNames = []string{"Repeat", "RepeatCycle", "Random", "PseudoWords", "SmallAlphabet", "Genome"}
// dataSets contains the various data sets that are used in testing.
var dataSets = make(map[string][]string)

// init sets up the benchmark data structures.
func init() {
	sorters["Binsert"] = sortingo.BinaryInsertionSort
	sorters["Comb"] = sortingo.CombSort
	sorters["Gnome"] = sortingo.GnomeSort
	sorters["Heap"] = sortingo.HeapSort
	sorters["Insert"] = sortingo.InsertionSort
	sorters["MkQ"] = sortingo.MultikeyQuickSort
	sorters["Quick"] = sort.SortStrings
	sorters["Select"] = sortingo.SelectionSort
	sorters["Shell"] = sortingo.ShellSort

	// Generate the repeated strings test data.
	repeatedStrings := make([]string, largeDataSize)
	a100 := strings.Repeat("A", 100)
	for idx := range repeatedStrings {
		repeatedStrings[idx] = a100
	}
	dataSets["Repeat"] = repeatedStrings

	// Generate a repeating cycle of strings.
	strs := make([]string, len(a100))
	for i := range strs {
		strs[i] = a100[0 : i+1]
	}
	repeatedCycleStrings := make([]string, largeDataSize)
	c := 0
	for i := range repeatedCycleStrings {
		repeatedCycleStrings[i] = strs[c]
		if c++; c >= len(strs) {
			c = 0
		}
	}
	dataSets["RepeatCycle"] = repeatedCycleStrings

	// Generate a set of random strings, each of length 100.
	randomStrings := make([]string, largeDataSize)
	for i := range randomStrings {
		bb := bytes.NewBuffer(make([]byte, 0, 100))
		for j := 0; j < 100; j++ {
			d := rand.Intn(95)
			bb.WriteRune(' ' + d)
		}
		randomStrings[i] = string(bb.Bytes())
	}
	dataSets["Random"] = randomStrings

	// Generate a set of unique pseudo words.
	uniqueWords := make([]string, largeDataSize)
	wordExists := make(map[string]bool)
	for i := range uniqueWords {
		var s string
		// Loop until a unique random word is generated.
		for {
			// Each word is from 1 to 28 characters long.
			l := 1 + rand.Intn(27)
			bb := bytes.NewBuffer(make([]byte, 0, l))
			// Each word consists only of the lowercase letters.
			for j := 0; j < l; j++ {
				d := rand.Intn(26)
				bb.WriteRune('a' + d)
			}
			s = string(bb.Bytes())
			if !wordExists[s] {
				break
			}
		}
		uniqueWords[i] = s
		wordExists[s] = true
	}
	dataSets["PseudoWords"] = uniqueWords

	// Generate a set of random strings, each of length 100,
	// consisting of a small alphabet of characters.
	smallAlphaStrings := make([]string, largeDataSize)
	for i := range smallAlphaStrings {
		l := 1 + rand.Intn(100)
		bb := bytes.NewBuffer(make([]byte, 0, l))
		for j := 0; j < l; j++ {
			d := rand.Intn(9)
			bb.WriteRune('a' + d)
		}
		smallAlphaStrings[i] = string(bb.Bytes())
	}
	dataSets["SmallAlphabet"] = smallAlphaStrings

	// Generate a set of random "genome" strings, each of length 9,
	// consisting of the letters a, c, g, t.
	genomeStrings := make([]string, largeDataSize)
	for i := range genomeStrings {
		bb := bytes.NewBuffer(make([]byte, 0, 9))
		for j := 0; j < 9; j++ {
			d := rand.Intn(4)
			switch d {
			case 0:
				bb.WriteRune('a')
			case 1:
				bb.WriteRune('c')
			case 2:
				bb.WriteRune('g')
			case 3:
				bb.WriteRune('t')
			}
		}
		genomeStrings[i] = string(bb.Bytes())
	}
	dataSets["Genome"] = genomeStrings
}

func usage() {
        fmt.Println("Usage: mbench [options]")
        fmt.Println("\t--data <regex>")
        fmt.Println("\t\tSelect the data set whose name matches the regular expression.")
        fmt.Println("\t\tFor example, '--data random' would use only the random data set.")
        fmt.Println("\t--help")
        fmt.Println("\t\tDisplay this usage information.")
        fmt.Println("\t--list")
        fmt.Println("\t\tDisplay a list of the supported data sets and sorting algorithms.")
        fmt.Println("\t--sort <regex>")
        fmt.Println("\t\tSelect the sort algorithms whose name matches the regular")
        fmt.Println("\t\texpression. For example, '--sort (comb|insert)' would run")
        fmt.Println("\t\tboth versions of the insertion and comb sort algorithms.")
}

// main runs the micro benchmarks on the "slower" sorting algorithms
// using small data sets (under 500 elements).
func main() {
	var help = flag.Bool("help", false, "show usage information")
	var list = flag.Bool("list", false, "list supported data sets and algorithms")
	var data = flag.String("data", "", "regex to select data sets to sort")
	var algo = flag.String("sort", "", "regex to select sorting algorithms")
	flag.Parse()

	if *help {
		usage()
		os.Exit(0)
	}

	if *list {
	        fmt.Println("Data sets")
		for _, dataSetName := range dataSetNames {
                        fmt.Printf("\t%s\n", dataSetName)
                }
                fmt.Println("Sorting algorithms")
		for _, sorterName := range sorterNames {
                        fmt.Printf("\t%s\n", sorterName)
                }
                os.Exit(0)
	}

	if *data != "" {
		re, err := regexp.Compile(strings.ToLower(*data))
		if err != nil {
			fmt.Printf("%s in '%s' of --data flag\n", err, *data)
			os.Exit(1)
		}
		newData := make([]string, 0, len(dataSetNames))
		for _, dataSetName := range dataSetNames {
			if idx := re.FindStringIndex(strings.ToLower(dataSetName)); idx != nil {
				newData = append(newData, dataSetName)
			}
		}
		dataSetNames = newData
	}

	if *algo != "" {
		re, err := regexp.Compile(strings.ToLower(*algo))
		if err != nil {
			fmt.Printf("%s in '%s' of --sort flag\n", err, *algo)
			os.Exit(1)
		}
		newSort := make([]string, 0, len(sorterNames))
		for _, sorterName := range sorterNames {
			if idx := re.FindStringIndex(strings.ToLower(sorterName)); idx != nil {
				newSort = append(newSort, sorterName)
			}
		}
		sorterNames = newSort
	}

        // Avoid recreating the input arrays over and over again.
	inputSets := make(map[int][]string)
	for _, size := range sortSizes {
		inputSets[size] = make([]string, size)
	}

	// For each type of data set...
	// and each data set size...
	// and each sort implementation...
	// run the sort via the testing package benchmark facility.
	for _, dataSetName := range dataSetNames {
		fmt.Printf("%s...\n", dataSetName)
		dataSet := dataSets[dataSetName]
		for _, size := range sortSizes {
			input := inputSets[size]
			fmt.Printf("\t%d...\n", size)
			for _, sorterName := range sorterNames {
				sorter := sorters[sorterName]
				fmt.Printf("\t\t%-10s:\t", sorterName)
				harness := func(b *testing.B) {
					for i := 0; i < b.N; i++ {
						b.StopTimer()
						copy(input, dataSet)
						b.StartTimer()
						sorter(input)
					}
				}
				result := testing.Benchmark(harness)
				fmt.Println(result)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

// We want to replace much of this with use of containerd tools
// and also using the Docker API not shelling out

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os/exec"
	"strings"
)

func dockerRun(args ...string) ([]byte, error) {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return []byte{}, errors.New("Docker does not seem to be installed")
	}
	args = append([]string{"run", "--rm"}, args...)
	cmd := exec.Command(docker, args...)

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return []byte{}, err
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return []byte{}, err
	}

	err = cmd.Start()
	if err != nil {
		return []byte{}, err
	}

	stdout, err := ioutil.ReadAll(stdoutPipe)
	if err != nil {
		return []byte{}, err
	}

	stderr, err := ioutil.ReadAll(stderrPipe)
	if err != nil {
		return []byte{}, err
	}

	err = cmd.Wait()
	if err != nil {
		return []byte{}, fmt.Errorf("%v: %s", err, stderr)
	}

	return stdout, nil
}

func dockerRunInput(input io.Reader, args ...string) ([]byte, error) {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return []byte{}, errors.New("Docker does not seem to be installed")
	}
	args = append([]string{"run", "--rm", "-i"}, args...)
	cmd := exec.Command(docker, args...)
	cmd.Stdin = input

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return []byte{}, err
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return []byte{}, err
	}

	err = cmd.Start()
	if err != nil {
		return []byte{}, err
	}

	stdout, err := ioutil.ReadAll(stdoutPipe)
	if err != nil {
		return []byte{}, err
	}

	stderr, err := ioutil.ReadAll(stderrPipe)
	if err != nil {
		return []byte{}, err
	}

	err = cmd.Wait()
	if err != nil {
		return []byte{}, fmt.Errorf("%v: %s", err, stderr)
	}

	return stdout, nil
}

func dockerCreate(image string) (string, error) {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return "", errors.New("Docker does not seem to be installed")
	}
	// we do not ever run the container, so /dev/null is used as command
	args := []string{"create", image, "/dev/null"}
	cmd := exec.Command(docker, args...)

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return "", err
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return "", err
	}

	err = cmd.Start()
	if err != nil {
		return "", err
	}

	stdout, err := ioutil.ReadAll(stdoutPipe)
	if err != nil {
		return "", err
	}

	stderr, err := ioutil.ReadAll(stderrPipe)
	if err != nil {
		return "", err
	}

	err = cmd.Wait()
	if err != nil {
		return "", fmt.Errorf("%s: %s", err, stderr)
	}

	container := strings.TrimSpace(string(stdout))
	return container, nil
}

func dockerExport(container string) ([]byte, error) {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return []byte{}, errors.New("Docker does not seem to be installed")
	}
	args := []string{"export", container}
	cmd := exec.Command(docker, args...)

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return []byte{}, err
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return []byte{}, err
	}

	err = cmd.Start()
	if err != nil {
		return []byte{}, err
	}

	stdout, err := ioutil.ReadAll(stdoutPipe)
	if err != nil {
		return []byte{}, err
	}

	stderr, err := ioutil.ReadAll(stderrPipe)
	if err != nil {
		return []byte{}, err
	}

	err = cmd.Wait()
	if err != nil {
		return []byte{}, fmt.Errorf("%v: %s", err, stderr)
	}

	return stdout, nil
}

func dockerRm(container string) error {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return errors.New("Docker does not seem to be installed")
	}
	args := []string{"rm", container}
	cmd := exec.Command(docker, args...)

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return err
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	err = cmd.Start()
	if err != nil {
		return err
	}

	_, err = ioutil.ReadAll(stdoutPipe)
	if err != nil {
		return err
	}

	stderr, err := ioutil.ReadAll(stderrPipe)
	if err != nil {
		return err
	}

	err = cmd.Wait()
	if err != nil {
		return fmt.Errorf("%s: %s", err, stderr)
	}

	return nil
}

func dockerPull(image string) error {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return errors.New("Docker does not seem to be installed")
	}
	args := []string{"pull", image}
	cmd := exec.Command(docker, args...)

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return err
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	err = cmd.Start()
	if err != nil {
		return err
	}

	_, err = ioutil.ReadAll(stdoutPipe)
	if err != nil {
		return err
	}

	stderr, err := ioutil.ReadAll(stderrPipe)
	if err != nil {
		return err
	}

	err = cmd.Wait()
	if err != nil {
		return fmt.Errorf("%s: %s", err, stderr)
	}

	return nil
}
<commit_msg>fmt string fixes<commit_after>package main

// We want to replace much of this with use of containerd tools
// and also using the Docker API not shelling out

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os/exec"
	"strings"
)

func dockerRun(args ...string) ([]byte, error) {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return []byte{}, errors.New("Docker does not seem to be installed")
	}
	args = append([]string{"run", "--rm"}, args...)
	cmd := exec.Command(docker, args...)

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return []byte{}, err
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return []byte{}, err
	}

	err = cmd.Start()
	if err != nil {
		return []byte{}, err
	}

	stdout, err := ioutil.ReadAll(stdoutPipe)
	if err != nil {
		return []byte{}, err
	}

	stderr, err := ioutil.ReadAll(stderrPipe)
	if err != nil {
		return []byte{}, err
	}

	err = cmd.Wait()
	if err != nil {
		return []byte{}, fmt.Errorf("%v: %s", err, stderr)
	}

	return stdout, nil
}

func dockerRunInput(input io.Reader, args ...string) ([]byte, error) {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return []byte{}, errors.New("Docker does not seem to be installed")
	}
	args = append([]string{"run", "--rm", "-i"}, args...)
	cmd := exec.Command(docker, args...)
	cmd.Stdin = input

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return []byte{}, err
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return []byte{}, err
	}

	err = cmd.Start()
	if err != nil {
		return []byte{}, err
	}

	stdout, err := ioutil.ReadAll(stdoutPipe)
	if err != nil {
		return []byte{}, err
	}

	stderr, err := ioutil.ReadAll(stderrPipe)
	if err != nil {
		return []byte{}, err
	}

	err = cmd.Wait()
	if err != nil {
		return []byte{}, fmt.Errorf("%v: %s", err, stderr)
	}

	return stdout, nil
}

func dockerCreate(image string) (string, error) {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return "", errors.New("Docker does not seem to be installed")
	}
	// we do not ever run the container, so /dev/null is used as command
	args := []string{"create", image, "/dev/null"}
	cmd := exec.Command(docker, args...)

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return "", err
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return "", err
	}

	err = cmd.Start()
	if err != nil {
		return "", err
	}

	stdout, err := ioutil.ReadAll(stdoutPipe)
	if err != nil {
		return "", err
	}

	stderr, err := ioutil.ReadAll(stderrPipe)
	if err != nil {
		return "", err
	}

	err = cmd.Wait()
	if err != nil {
		return "", fmt.Errorf("%v: %s", err, stderr)
	}

	container := strings.TrimSpace(string(stdout))
	return container, nil
}

func dockerExport(container string) ([]byte, error) {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return []byte{}, errors.New("Docker does not seem to be installed")
	}
	args := []string{"export", container}
	cmd := exec.Command(docker, args...)

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return []byte{}, err
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return []byte{}, err
	}

	err = cmd.Start()
	if err != nil {
		return []byte{}, err
	}

	stdout, err := ioutil.ReadAll(stdoutPipe)
	if err != nil {
		return []byte{}, err
	}

	stderr, err := ioutil.ReadAll(stderrPipe)
	if err != nil {
		return []byte{}, err
	}

	err = cmd.Wait()
	if err != nil {
		return []byte{}, fmt.Errorf("%v: %s", err, stderr)
	}

	return stdout, nil
}

func dockerRm(container string) error {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return errors.New("Docker does not seem to be installed")
	}
	args := []string{"rm", container}
	cmd := exec.Command(docker, args...)

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return err
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	err = cmd.Start()
	if err != nil {
		return err
	}

	_, err = ioutil.ReadAll(stdoutPipe)
	if err != nil {
		return err
	}

	stderr, err := ioutil.ReadAll(stderrPipe)
	if err != nil {
		return err
	}

	err = cmd.Wait()
	if err != nil {
		return fmt.Errorf("%v: %s", err, stderr)
	}

	return nil
}

func dockerPull(image string) error {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return errors.New("Docker does not seem to be installed")
	}
	args := []string{"pull", image}
	cmd := exec.Command(docker, args...)

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return err
	}

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	err = cmd.Start()
	if err != nil {
		return err
	}

	_, err = ioutil.ReadAll(stdoutPipe)
	if err != nil {
		return err
	}

	stderr, err := ioutil.ReadAll(stderrPipe)
	if err != nil {
		return err
	}

	err = cmd.Wait()
	if err != nil {
		return fmt.Errorf("%v: %s", err, stderr)
	}

	return nil
}
<|endoftext|>


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

package main

import (
	"debug/dwarf"
	"debug/gosym"
	"flag"
	"fmt"
	"os"
	"regexp"
	"strings"
	"sync"

	"cmd/internal/objfile"
	"cmd/internal/pprof/commands"
	"cmd/internal/pprof/driver"
	"cmd/internal/pprof/fetch"
	"cmd/internal/pprof/plugin"
	"cmd/internal/pprof/profile"
	"cmd/internal/pprof/symbolizer"
	"cmd/internal/pprof/symbolz"
)

func main() {
	var extraCommands map[string]*commands.Command // no added Go-specific commands
	if err := driver.PProf(flags{}, fetch.Fetcher, symbolize, new(objTool), plugin.StandardUI(), extraCommands); err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(2)
	}
}

// symbolize attempts to symbolize profile p.
// If the source is a local binary, it tries using symbolizer and obj.
// If the source is a URL, it fetches symbol information using symbolz.
func symbolize(mode, source string, p *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error {
	remote, local := true, true
	for _, o := range strings.Split(strings.ToLower(mode), ":") {
		switch o {
		case "none", "no":
			return nil
		case "local":
			remote, local = false, true
		case "remote":
			remote, local = true, false
		default:
			ui.PrintErr("ignoring unrecognized symbolization option: " + mode)
			ui.PrintErr("expecting -symbolize=[local|remote|none][:force]")
			fallthrough
		case "", "force":
			// Ignore these options, -force is recognized by symbolizer.Symbolize
		}
	}

	var err error
	if local {
		// Symbolize using binutils.
		if err = symbolizer.Symbolize(mode, p, obj, ui); err == nil {
			return nil
		}
	}
	if remote {
		err = symbolz.Symbolize(source, fetch.PostURL, p)
	}
	return err
}

// flags implements the driver.FlagPackage interface using the builtin flag package.
type flags struct {
}

func (flags) Bool(o string, d bool, c string) *bool {
	return flag.Bool(o, d, c)
}

func (flags) Int(o string, d int, c string) *int {
	return flag.Int(o, d, c)
}

func (flags) Float64(o string, d float64, c string) *float64 {
	return flag.Float64(o, d, c)
}

func (flags) String(o, d, c string) *string {
	return flag.String(o, d, c)
}

func (flags) Parse(usage func()) []string {
	flag.Usage = usage
	flag.Parse()
	args := flag.Args()
	if len(args) == 0 {
		usage()
	}
	return args
}

func (flags) ExtraUsage() string {
	return ""
}

// objTool implements plugin.ObjTool using Go libraries
// (instead of invoking GNU binutils).
type objTool struct {
	mu          sync.Mutex
	disasmCache map[string]*objfile.Disasm
}

func (*objTool) Open(name string, start uint64) (plugin.ObjFile, error) {
	of, err := objfile.Open(name)
	if err != nil {
		return nil, err
	}
	f := &file{
		name: name,
		file: of,
	}
	if start != 0 {
		if load, err := of.LoadAddress(); err == nil {
			f.offset = start - load
		}
	}
	return f, nil
}

func (*objTool) Demangle(names []string) (map[string]string, error) {
	// No C++, nothing to demangle.
	return make(map[string]string), nil
}

func (t *objTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
	d, err := t.cachedDisasm(file)
	if err != nil {
		return nil, err
	}
	var asm []plugin.Inst
	d.Decode(start, end, func(pc, size uint64, file string, line int, text string) {
		asm = append(asm, plugin.Inst{Addr: pc, File: file, Line: line, Text: text})
	})
	return asm, nil
}

func (t *objTool) cachedDisasm(file string) (*objfile.Disasm, error) {
	t.mu.Lock()
	defer t.mu.Unlock()
	if t.disasmCache == nil {
		t.disasmCache = make(map[string]*objfile.Disasm)
	}
	d := t.disasmCache[file]
	if d != nil {
		return d, nil
	}
	f, err := objfile.Open(file)
	if err != nil {
		return nil, err
	}
	d, err = f.Disasm()
	f.Close()
	if err != nil {
		return nil, err
	}
	t.disasmCache[file] = d
	return d, nil
}

func (*objTool) SetConfig(config string) {
	// config is usually used to say what binaries to invoke.
	// Ignore entirely.
}

// file implements plugin.ObjFile using Go libraries
// (instead of invoking GNU binutils).
// A file represents a single executable being analyzed.
type file struct {
	name   string
	offset uint64
	sym    []objfile.Sym
	file   *objfile.File
	pcln   *gosym.Table

	triedDwarf bool
	dwarf      *dwarf.Data
}

func (f *file) Name() string {
	return f.name
}

func (f *file) Base() uint64 {
	// No support for shared libraries.
	return 0
}

func (f *file) BuildID() string {
	// No support for build ID.
	return ""
}

func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) {
	if f.pcln == nil {
		pcln, err := f.file.PCLineTable()
		if err != nil {
			return nil, err
		}
		f.pcln = pcln
	}
	addr -= f.offset
	file, line, fn := f.pcln.PCToLine(addr)
	if fn != nil {
		frame := []plugin.Frame{
			{
				Func: fn.Name,
				File: file,
				Line: line,
			},
		}
		return frame, nil
	}

	frames := f.dwarfSourceLine(addr)
	if frames != nil {
		return frames, nil
	}

	return nil, fmt.Errorf("no line information for PC=%#x", addr)
}

// dwarfSourceLine tries to get file/line information using DWARF.
// This is for C functions that appear in the profile.
// Returns nil if there is no information available.
func (f *file) dwarfSourceLine(addr uint64) []plugin.Frame {
	if f.dwarf == nil && !f.triedDwarf {
		// Ignore any error--we don't care exactly why there
		// is no DWARF info.
		f.dwarf, _ = f.file.DWARF()
		f.triedDwarf = true
	}

	if f.dwarf != nil {
		r := f.dwarf.Reader()
		unit, err := r.SeekPC(addr)
		if err == nil {
			if frames := f.dwarfSourceLineEntry(r, unit, addr); frames != nil {
				return frames
			}
		}
	}

	return nil
}

// dwarfSourceLineEntry tries to get file/line information from a
// DWARF compilation unit. Returns nil if it doesn't find anything.
func (f *file) dwarfSourceLineEntry(r *dwarf.Reader, entry *dwarf.Entry, addr uint64) []plugin.Frame {
	lines, err := f.dwarf.LineReader(entry)
	if err != nil {
		return nil
	}
	var lentry dwarf.LineEntry
	if err := lines.SeekPC(addr, &lentry); err != nil {
		return nil
	}

	// Try to find the function name.
	name := ""
FindName:
	for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() {
		if entry.Tag == dwarf.TagSubprogram {
			ranges, err := f.dwarf.Ranges(entry)
			if err != nil {
				return nil
			}
			for _, pcs := range ranges {
				if pcs[0] <= addr && addr < pcs[1] {
					var ok bool
					// TODO: AT_linkage_name, AT_MIPS_linkage_name.
					name, ok = entry.Val(dwarf.AttrName).(string)
					if ok {
						break FindName
					}
				}
			}
		}
	}

	// TODO: Report inlined functions.

	frames := []plugin.Frame{
		{
			Func: name,
			File: lentry.File.Name,
			Line: lentry.Line,
		},
	}

	return frames
}

func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
	if f.sym == nil {
		sym, err := f.file.Symbols()
		if err != nil {
			return nil, err
		}
		f.sym = sym
	}
	var out []*plugin.Sym
	for _, s := range f.sym {
		if (r == nil || r.MatchString(s.Name)) && (addr == 0 || s.Addr <= addr && addr < s.Addr+uint64(s.Size)) {
			out = append(out, &plugin.Sym{
				Name:  []string{s.Name},
				File:  f.name,
				Start: s.Addr,
				End:   s.Addr + uint64(s.Size) - 1,
			})
		}
	}
	return out, nil
}

func (f *file) Close() error {
	f.file.Close()
	return nil
}
<commit_msg>cmd/pprof: ignore symbols with address 0 and size 0<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"debug/dwarf"
	"debug/gosym"
	"flag"
	"fmt"
	"os"
	"regexp"
	"strings"
	"sync"

	"cmd/internal/objfile"
	"cmd/internal/pprof/commands"
	"cmd/internal/pprof/driver"
	"cmd/internal/pprof/fetch"
	"cmd/internal/pprof/plugin"
	"cmd/internal/pprof/profile"
	"cmd/internal/pprof/symbolizer"
	"cmd/internal/pprof/symbolz"
)

func main() {
	var extraCommands map[string]*commands.Command // no added Go-specific commands
	if err := driver.PProf(flags{}, fetch.Fetcher, symbolize, new(objTool), plugin.StandardUI(), extraCommands); err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(2)
	}
}

// symbolize attempts to symbolize profile p.
// If the source is a local binary, it tries using symbolizer and obj.
// If the source is a URL, it fetches symbol information using symbolz.
func symbolize(mode, source string, p *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error {
	remote, local := true, true
	for _, o := range strings.Split(strings.ToLower(mode), ":") {
		switch o {
		case "none", "no":
			return nil
		case "local":
			remote, local = false, true
		case "remote":
			remote, local = true, false
		default:
			ui.PrintErr("ignoring unrecognized symbolization option: " + mode)
			ui.PrintErr("expecting -symbolize=[local|remote|none][:force]")
			fallthrough
		case "", "force":
			// Ignore these options, -force is recognized by symbolizer.Symbolize
		}
	}

	var err error
	if local {
		// Symbolize using binutils.
		if err = symbolizer.Symbolize(mode, p, obj, ui); err == nil {
			return nil
		}
	}
	if remote {
		err = symbolz.Symbolize(source, fetch.PostURL, p)
	}
	return err
}

// flags implements the driver.FlagPackage interface using the builtin flag package.
type flags struct {
}

func (flags) Bool(o string, d bool, c string) *bool {
	return flag.Bool(o, d, c)
}

func (flags) Int(o string, d int, c string) *int {
	return flag.Int(o, d, c)
}

func (flags) Float64(o string, d float64, c string) *float64 {
	return flag.Float64(o, d, c)
}

func (flags) String(o, d, c string) *string {
	return flag.String(o, d, c)
}

func (flags) Parse(usage func()) []string {
	flag.Usage = usage
	flag.Parse()
	args := flag.Args()
	if len(args) == 0 {
		usage()
	}
	return args
}

func (flags) ExtraUsage() string {
	return ""
}

// objTool implements plugin.ObjTool using Go libraries
// (instead of invoking GNU binutils).
type objTool struct {
	mu          sync.Mutex
	disasmCache map[string]*objfile.Disasm
}

func (*objTool) Open(name string, start uint64) (plugin.ObjFile, error) {
	of, err := objfile.Open(name)
	if err != nil {
		return nil, err
	}
	f := &file{
		name: name,
		file: of,
	}
	if start != 0 {
		if load, err := of.LoadAddress(); err == nil {
			f.offset = start - load
		}
	}
	return f, nil
}

func (*objTool) Demangle(names []string) (map[string]string, error) {
	// No C++, nothing to demangle.
	return make(map[string]string), nil
}

func (t *objTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
	d, err := t.cachedDisasm(file)
	if err != nil {
		return nil, err
	}
	var asm []plugin.Inst
	d.Decode(start, end, func(pc, size uint64, file string, line int, text string) {
		asm = append(asm, plugin.Inst{Addr: pc, File: file, Line: line, Text: text})
	})
	return asm, nil
}

func (t *objTool) cachedDisasm(file string) (*objfile.Disasm, error) {
	t.mu.Lock()
	defer t.mu.Unlock()
	if t.disasmCache == nil {
		t.disasmCache = make(map[string]*objfile.Disasm)
	}
	d := t.disasmCache[file]
	if d != nil {
		return d, nil
	}
	f, err := objfile.Open(file)
	if err != nil {
		return nil, err
	}
	d, err = f.Disasm()
	f.Close()
	if err != nil {
		return nil, err
	}
	t.disasmCache[file] = d
	return d, nil
}

func (*objTool) SetConfig(config string) {
	// config is usually used to say what binaries to invoke.
	// Ignore entirely.
}

// file implements plugin.ObjFile using Go libraries
// (instead of invoking GNU binutils).
// A file represents a single executable being analyzed.
type file struct {
	name   string
	offset uint64
	sym    []objfile.Sym
	file   *objfile.File
	pcln   *gosym.Table

	triedDwarf bool
	dwarf      *dwarf.Data
}

func (f *file) Name() string {
	return f.name
}

func (f *file) Base() uint64 {
	// No support for shared libraries.
	return 0
}

func (f *file) BuildID() string {
	// No support for build ID.
	return ""
}

func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) {
	if f.pcln == nil {
		pcln, err := f.file.PCLineTable()
		if err != nil {
			return nil, err
		}
		f.pcln = pcln
	}
	addr -= f.offset
	file, line, fn := f.pcln.PCToLine(addr)
	if fn != nil {
		frame := []plugin.Frame{
			{
				Func: fn.Name,
				File: file,
				Line: line,
			},
		}
		return frame, nil
	}

	frames := f.dwarfSourceLine(addr)
	if frames != nil {
		return frames, nil
	}

	return nil, fmt.Errorf("no line information for PC=%#x", addr)
}

// dwarfSourceLine tries to get file/line information using DWARF.
// This is for C functions that appear in the profile.
// Returns nil if there is no information available.
func (f *file) dwarfSourceLine(addr uint64) []plugin.Frame {
	if f.dwarf == nil && !f.triedDwarf {
		// Ignore any error--we don't care exactly why there
		// is no DWARF info.
		f.dwarf, _ = f.file.DWARF()
		f.triedDwarf = true
	}

	if f.dwarf != nil {
		r := f.dwarf.Reader()
		unit, err := r.SeekPC(addr)
		if err == nil {
			if frames := f.dwarfSourceLineEntry(r, unit, addr); frames != nil {
				return frames
			}
		}
	}

	return nil
}

// dwarfSourceLineEntry tries to get file/line information from a
// DWARF compilation unit. Returns nil if it doesn't find anything.
func (f *file) dwarfSourceLineEntry(r *dwarf.Reader, entry *dwarf.Entry, addr uint64) []plugin.Frame {
	lines, err := f.dwarf.LineReader(entry)
	if err != nil {
		return nil
	}
	var lentry dwarf.LineEntry
	if err := lines.SeekPC(addr, &lentry); err != nil {
		return nil
	}

	// Try to find the function name.
	name := ""
FindName:
	for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() {
		if entry.Tag == dwarf.TagSubprogram {
			ranges, err := f.dwarf.Ranges(entry)
			if err != nil {
				return nil
			}
			for _, pcs := range ranges {
				if pcs[0] <= addr && addr < pcs[1] {
					var ok bool
					// TODO: AT_linkage_name, AT_MIPS_linkage_name.
					name, ok = entry.Val(dwarf.AttrName).(string)
					if ok {
						break FindName
					}
				}
			}
		}
	}

	// TODO: Report inlined functions.

	frames := []plugin.Frame{
		{
			Func: name,
			File: lentry.File.Name,
			Line: lentry.Line,
		},
	}

	return frames
}

func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
	if f.sym == nil {
		sym, err := f.file.Symbols()
		if err != nil {
			return nil, err
		}
		f.sym = sym
	}
	var out []*plugin.Sym
	for _, s := range f.sym {
		// Ignore a symbol with address 0 and size 0.
		// An ELF STT_FILE symbol will look like that.
		if s.Addr == 0 && s.Size == 0 {
			continue
		}
		if (r == nil || r.MatchString(s.Name)) && (addr == 0 || s.Addr <= addr && addr < s.Addr+uint64(s.Size)) {
			out = append(out, &plugin.Sym{
				Name:  []string{s.Name},
				File:  f.name,
				Start: s.Addr,
				End:   s.Addr + uint64(s.Size) - 1,
			})
		}
	}
	return out, nil
}

func (f *file) Close() error {
	f.file.Close()
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"flag"
	"log"
	"net/http"
	"os"
	"time"

	"golang.org/x/net/context"

	entryStore "entry_storage"
	userStore "user_storage"
	voteStore "vote_storage"

	"handlers"

	_ "github.com/go-sql-driver/mysql"
)

func logHandler(next http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		next.ServeHTTP(w, r)
		log.Printf(
			"%s\t%s\t%s\t%s\t%s",
			r.RemoteAddr,
			time.Now().Format("2006-01-02T15:04:05 -0700"),
			r.Method,
			r.RequestURI,
			time.Since(start),
		)
	}
}

func jsonHandler(next http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		next.ServeHTTP(w, r)
	}
}

type ContextAdapter struct {
	ctx     context.Context
	handler handlers.ContextHandler
}

func (ca *ContextAdapter) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	ca.handler.ServeHTTPContext(ca.ctx, rw, req)
}

func main() {
	var mux = http.DefaultServeMux

	var (
		driverName string
		dataSource string
	)
	flag.StringVar(&driverName, "driver", "mysql", "database driver to use. see github.com/rubenv/sql-migrate for details.")
	flag.StringVar(&dataSource, "datasource", "", "datasource to be used with the database driver. mysql/pg REVDSN")
	flag.Parse()

	if dataSource == "" {
		log.Fatalf("missing data source! please re-run with --help for details")
		os.Exit(1)
	}

	var db, err = sql.Open(driverName, dataSource)
	if err != nil {
		log.Panicf("failed to connect to database")
	}
	defer db.Close()

	var userStorage = userStore.New(db)
	var entryStorage = entryStore.New(db)
	var voteStorage = voteStore.New(db)

	var rootContext = handlers.NewRootContext(db)

	mux.Handle("/users/register", http.StripPrefix("/users/register", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.ContextHandlerFunc(handlers.UsersRegister),
	}))
	mux.Handle("/users/login", http.StripPrefix("/users/login", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.ContextHandlerFunc(handlers.UserLogin),
	}))
	mux.Handle("/users/logout", http.StripPrefix("/users/logout", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.ContextHandlerFunc(handlers.UserLogout)),
	}))
	mux.Handle("/users/password", http.StripPrefix("/users/password", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.ContextHandlerFunc(handlers.UserChangePassword)),
	}))
	mux.Handle("/users/email", http.StripPrefix("/users/email", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.ContextHandlerFunc(handlers.UserChangeEmail)),
	}))
	// TODO(rr) add support for password forgotten requests /users/forgot/request
	// TODO(rr) add support for password reset requests /users/forgot/reset

	mux.Handle("/entries/", http.StripPrefix("/entries/", http.Handler(&handlers.EntriesHandler{
		UserStorage:  userStorage,
		EntryStorage: entryStorage,
		DB:           db,
		VoteStorage:  voteStorage,
	})))

	mux.Handle("/teams/list", http.StripPrefix("/teams/list", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.ContextHandlerFunc(handlers.TeamsList)),
	}))
	mux.Handle("/teams/create", http.StripPrefix("/teams/create", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.ContextHandlerFunc(handlers.TeamCreate)),
	}))
	mux.Handle("/teams/join", http.StripPrefix("/teams/join", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.WithTeam(handlers.ContextHandlerFunc(handlers.TeamJoin))),
	}))
	mux.Handle("/teams/leave", http.StripPrefix("/teams/leave", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.WithTeam(handlers.ContextHandlerFunc(handlers.TeamLeave))),
	}))
	mux.Handle("/teams/set_role", http.StripPrefix("/teams/set_role", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.WithTeam(handlers.ContextHandlerFunc(handlers.TeamSetRole))),
	}))
	mux.Handle("/teams/remove_member", http.StripPrefix("/teams/remove_member", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.WithTeam(handlers.ContextHandlerFunc(handlers.TeamRemoveMember))),
	}))
	mux.Handle("/teams/set_access_key", http.StripPrefix("/teams/set_access_key", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.WithTeam(handlers.ContextHandlerFunc(handlers.TeamSetAccessKey))),
	}))
	mux.Handle("/teams/list_members", http.StripPrefix("/teams/list_members", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.WithTeam(handlers.ContextHandlerFunc(handlers.TeamListMember))),
	}))

	var listen = ":8000"
	log.Printf("Listening on %q", listen)
	http.ListenAndServe(listen, logHandler(jsonHandler(mux)))
}
<commit_msg>add listen configuration flag to the server<commit_after>package main

import (
	"database/sql"
	"flag"
	"log"
	"net/http"
	"os"
	"time"

	"golang.org/x/net/context"

	entryStore "entry_storage"
	userStore "user_storage"
	voteStore "vote_storage"

	"handlers"

	_ "github.com/go-sql-driver/mysql"
)

func logHandler(next http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		next.ServeHTTP(w, r)
		log.Printf(
			"%s\t%s\t%s\t%s\t%s",
			r.RemoteAddr,
			time.Now().Format("2006-01-02T15:04:05 -0700"),
			r.Method,
			r.RequestURI,
			time.Since(start),
		)
	}
}

func jsonHandler(next http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		next.ServeHTTP(w, r)
	}
}

type ContextAdapter struct {
	ctx     context.Context
	handler handlers.ContextHandler
}

func (ca *ContextAdapter) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	ca.handler.ServeHTTPContext(ca.ctx, rw, req)
}

func main() {
	var mux = http.DefaultServeMux

	var (
		driverName string
		dataSource string
		listen     string
	)
	flag.StringVar(&driverName, "driver", "mysql", "database driver to use. see github.com/rubenv/sql-migrate for details.")
	flag.StringVar(&dataSource, "datasource", "", "datasource to be used with the database driver. mysql/pg REVDSN")
	flag.StringVar(&listen, "listen", ":8000", "interface & port to listen on")
	flag.Parse()

	if dataSource == "" {
		log.Fatalf("missing data source! please re-run with --help for details")
		os.Exit(1)
	}

	var db, err = sql.Open(driverName, dataSource)
	if err != nil {
		log.Panicf("failed to connect to database")
	}
	defer db.Close()

	var userStorage = userStore.New(db)
	var entryStorage = entryStore.New(db)
	var voteStorage = voteStore.New(db)

	var rootContext = handlers.NewRootContext(db)

	mux.Handle("/users/register", http.StripPrefix("/users/register", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.ContextHandlerFunc(handlers.UsersRegister),
	}))
	mux.Handle("/users/login", http.StripPrefix("/users/login", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.ContextHandlerFunc(handlers.UserLogin),
	}))
	mux.Handle("/users/logout", http.StripPrefix("/users/logout", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.ContextHandlerFunc(handlers.UserLogout)),
	}))
	mux.Handle("/users/password", http.StripPrefix("/users/password", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.ContextHandlerFunc(handlers.UserChangePassword)),
	}))
	mux.Handle("/users/email", http.StripPrefix("/users/email", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.ContextHandlerFunc(handlers.UserChangeEmail)),
	}))
	// TODO(rr) add support for password forgotten requests /users/forgot/request
	// TODO(rr) add support for password reset requests /users/forgot/reset

	mux.Handle("/entries/", http.StripPrefix("/entries/", http.Handler(&handlers.EntriesHandler{
		UserStorage:  userStorage,
		EntryStorage: entryStorage,
		DB:           db,
		VoteStorage:  voteStorage,
	})))

	mux.Handle("/teams/list", http.StripPrefix("/teams/list", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.ContextHandlerFunc(handlers.TeamsList)),
	}))
	mux.Handle("/teams/create", http.StripPrefix("/teams/create", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.ContextHandlerFunc(handlers.TeamCreate)),
	}))
	mux.Handle("/teams/join", http.StripPrefix("/teams/join", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.WithTeam(handlers.ContextHandlerFunc(handlers.TeamJoin))),
	}))
	mux.Handle("/teams/leave", http.StripPrefix("/teams/leave", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.WithTeam(handlers.ContextHandlerFunc(handlers.TeamLeave))),
	}))
	mux.Handle("/teams/set_role", http.StripPrefix("/teams/set_role", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.WithTeam(handlers.ContextHandlerFunc(handlers.TeamSetRole))),
	}))
	mux.Handle("/teams/remove_member", http.StripPrefix("/teams/remove_member", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.WithTeam(handlers.ContextHandlerFunc(handlers.TeamRemoveMember))),
	}))
	mux.Handle("/teams/set_access_key", http.StripPrefix("/teams/set_access_key", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.WithTeam(handlers.ContextHandlerFunc(handlers.TeamSetAccessKey))),
	}))
	mux.Handle("/teams/list_members", http.StripPrefix("/teams/list_members", &ContextAdapter{
		ctx:     rootContext,
		handler: handlers.Authenticated(handlers.WithTeam(handlers.ContextHandlerFunc(handlers.TeamListMember))),
	}))

	log.Printf("Listening on %q", listen)
	http.ListenAndServe(listen, logHandler(jsonHandler(mux)))
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strings"
)

type TimeSrv struct {
	Name string
	Time chan string
}

func NewTimeSrv(name string) *TimeSrv {
	srv := new(TimeSrv)
	srv.Name = name
	srv.Time = make(chan string)
	return srv
}

func main() {
	servers := make([]*TimeSrv, 0, len(os.Args)-1)
	for _, param := range os.Args[1:] {
		args := strings.Split(param, "=")
		conn, err := net.Dial("tcp", args[1])
		if err != nil {
			log.Fatal(err)
		}
		s := NewTimeSrv(args[0])
		servers = append(servers, s)
		go handleConn(conn, s)
	}
	for {
		var curTime string
		for _, s := range servers {
			curTime += (" " + <-s.Time + " ")
		}
		fmt.Fprintf(os.Stdout, "\r%s", curTime)
	}
}

func mustCopy(dst io.Writer, src io.Reader) {
	if _, err := io.Copy(dst, src); err != nil {
		log.Fatal(err)
	}
}

func handleConn(conn net.Conn, srv *TimeSrv) {
	reader := bufio.NewReader(conn)
	defer conn.Close()
	defer close(srv.Time)
	for {
		line, _, err := reader.ReadLine()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		srv.Time <- string(line)
	}
}
<commit_msg>[8.1] small code clean up<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strings"
	"time"
)

// timeSrv represents time server connection with chan for reading time.
type timeSrv struct {
	Name string
	Time chan string
	Conn net.Conn
}

// newTimeSrv initializes new time server with underlying channel.
func newTimeSrv(name string, conn net.Conn) *timeSrv {
	srv := new(timeSrv)
	srv.Name = name
	srv.Conn = conn
	srv.Time = make(chan string)
	return srv
}

func main() {
	servers := make([]*timeSrv, 0, len(os.Args)-1)

	for _, param := range os.Args[1:] {
		args := strings.Split(param, "=")
		conn, err := net.Dial("tcp", args[1])
		if err != nil {
			log.Fatal(err)
		}

		s := newTimeSrv(args[0], conn)
		servers = append(servers, s)
		go fetchTime(s)
	}

	var names string
	for _, name := range servers {
		names += "\t" + name.Name
	}
	fmt.Println(names)

	for {
		time.Sleep(time.Second)
		var time string
		for _, s := range servers {
			t, ok := <-s.Time
			if !ok {
				t = "##:##:##"
			}
			time += "\t" + t
		}
		fmt.Printf("\r%s", time)
	}
}

func fetchTime(ts *timeSrv) {
	defer ts.Conn.Close()
	defer close(ts.Time)

	reader := bufio.NewReader(ts.Conn)

	for {
		line, _, err := reader.ReadLine()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		ts.Time <- string(line)
	}
}
<|endoftext|>