<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/url"
	"os"
	"strings"

	"github.com/Clever/clever-cli/clevertable"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	clevergo "gopkg.in/Clever/clever-go.v1"
	"gopkg.in/Clever/optimus.v3"
	csvSink "gopkg.in/Clever/optimus.v3/sinks/csv"
	jsonSink "gopkg.in/Clever/optimus.v3/sinks/json"
	"gopkg.in/Clever/optimus.v3/transformer"
)

var acceptedEndpoints = []string{"students", "schools", "sections", "teachers"}
var acceptedActions = []string{"list", "get"}

func validEndpoint(endpoint string) bool {
	for _, accepted := range acceptedEndpoints {
		if endpoint == accepted {
			return true
		}
	}
	return false
}

func exitWithArgError(msg string) {
	fmt.Fprintf(os.Stderr, "%s\n\n", msg)
	flag.Usage()
	os.Exit(1)
}

func main() {
	baseUsage := func() {
		fmt.Fprintf(os.Stderr, "Usage of %s: [options] endpoint action [action options]\n\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "options:\n")
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nendpoints: %s\n\n", strings.Join(acceptedEndpoints, " "))
		fmt.Fprintf(os.Stderr, "actions: %s\n\n", strings.Join(acceptedActions, " "))
	}
	host := flag.String("host", "https://api.clever.com", "base URL of Clever API")
	token := flag.String("token", "", "API token to use for authentication (required)")
	output := flag.String("output", "csv", "output method. supported options: csv, json")
	help := flag.Bool("help", false, "if true, display help and exit")

	// Action-specifc flags
	listFlags := flag.NewFlagSet("list options", flag.ExitOnError)
	listUsage := func() {
		fmt.Fprintln(os.Stderr, "list:")
		listFlags.PrintDefaults()
	}
	where := listFlags.String("where", "", "a JSON-stringified where query parameter")

	flag.Usage = func() {
		baseUsage()
		fmt.Fprint(os.Stderr, "action options:\n\n")
		listUsage()
	}
	flag.Parse()

	if *help {
		flag.Usage()
		os.Exit(0)
	}

	for _, required := range [][2]string{{"host", *host}, {"token", *token}, {"output", *output}} {
		name, value := required[0], required[1]
		if len(value) == 0 {
			exitWithArgError(fmt.Sprintf("must provide '%s'", name))
		}
	}

	var sink optimus.Sink
	switch *output {
	case "csv":
		sink = csvSink.New(os.Stdout)
	case "json":
		sink = jsonSink.New(os.Stdout)
	default:
		exitWithArgError(fmt.Sprintf("'%s' is not a valid output", *output))
	}

	if len(flag.Args()) < 2 {
		exitWithArgError("need at least two arguments: endpoint and action")
	}

	endpoint := flag.Args()[0]
	if !validEndpoint(endpoint) {
		exitWithArgError(fmt.Sprintf("'%s' is not a valid endpoint", endpoint))
	}

	client := oauth2.NewClient(context.TODO(), oauth2.StaticTokenSource(&oauth2.Token{
		AccessToken: *token,
	}))
	clever := clevergo.New(client, *host)

	action := flag.Args()[1]

	var table optimus.Table

	switch action {
	case "list":
		if len(flag.Args()) > 2 {
			listFlags.Parse(flag.Args()[2:])
		} else {
			listFlags.Parse([]string{})
		}
		var params url.Values
		if *where != "" {
			params = url.Values{"where": []string{*where}}
		}
		table = clevertable.NewList(endpoint, params, clever)
	case "get":
		if len(flag.Args()) != 3 {
			exitWithArgError(fmt.Sprintf("get action requires an <id> argument"))
		}
		id := flag.Args()[2]
		table = clevertable.NewGet(endpoint, id, clever)
	default:
		exitWithArgError(fmt.Sprintf("'%s' is not a valid action", action))
	}

	if err := transformer.New(table).
		Map(clevertable.FlattenRow).
		Map(clevertable.StringifyArrayVals).
		Sink(sink); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>support student contacts<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/url"
	"os"
	"strings"

	"github.com/Clever/clever-cli/clevertable"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	clevergo "gopkg.in/Clever/clever-go.v1"
	"gopkg.in/Clever/optimus.v3"
	csvSink "gopkg.in/Clever/optimus.v3/sinks/csv"
	jsonSink "gopkg.in/Clever/optimus.v3/sinks/json"
	"gopkg.in/Clever/optimus.v3/transformer"
)

var acceptedEndpoints = []string{"students", "schools", "sections", "teachers", "contacts"}
var acceptedActions = []string{"list", "get"}

func validEndpoint(endpoint string) bool {
	for _, accepted := range acceptedEndpoints {
		if endpoint == accepted {
			return true
		}
	}
	return false
}

func exitWithArgError(msg string) {
	fmt.Fprintf(os.Stderr, "%s\n\n", msg)
	flag.Usage()
	os.Exit(1)
}

func main() {
	baseUsage := func() {
		fmt.Fprintf(os.Stderr, "Usage of %s: [options] endpoint action [action options]\n\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "options:\n")
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nendpoints: %s\n\n", strings.Join(acceptedEndpoints, " "))
		fmt.Fprintf(os.Stderr, "actions: %s\n\n", strings.Join(acceptedActions, " "))
	}
	host := flag.String("host", "https://api.clever.com", "base URL of Clever API")
	token := flag.String("token", "", "API token to use for authentication (required)")
	output := flag.String("output", "csv", "output method. supported options: csv, json")
	help := flag.Bool("help", false, "if true, display help and exit")

	// Action-specifc flags
	listFlags := flag.NewFlagSet("list options", flag.ExitOnError)
	listUsage := func() {
		fmt.Fprintln(os.Stderr, "list:")
		listFlags.PrintDefaults()
	}
	where := listFlags.String("where", "", "a JSON-stringified where query parameter")

	flag.Usage = func() {
		baseUsage()
		fmt.Fprint(os.Stderr, "action options:\n\n")
		listUsage()
	}
	flag.Parse()

	if *help {
		flag.Usage()
		os.Exit(0)
	}

	for _, required := range [][2]string{{"host", *host}, {"token", *token}, {"output", *output}} {
		name, value := required[0], required[1]
		if len(value) == 0 {
			exitWithArgError(fmt.Sprintf("must provide '%s'", name))
		}
	}

	var sink optimus.Sink
	switch *output {
	case "csv":
		sink = csvSink.New(os.Stdout)
	case "json":
		sink = jsonSink.New(os.Stdout)
	default:
		exitWithArgError(fmt.Sprintf("'%s' is not a valid output", *output))
	}

	if len(flag.Args()) < 2 {
		exitWithArgError("need at least two arguments: endpoint and action")
	}

	endpoint := flag.Args()[0]
	if !validEndpoint(endpoint) {
		exitWithArgError(fmt.Sprintf("'%s' is not a valid endpoint", endpoint))
	}

	client := oauth2.NewClient(context.TODO(), oauth2.StaticTokenSource(&oauth2.Token{
		AccessToken: *token,
	}))
	clever := clevergo.New(client, *host)

	action := flag.Args()[1]

	var table optimus.Table

	switch action {
	case "list":
		if len(flag.Args()) > 2 {
			listFlags.Parse(flag.Args()[2:])
		} else {
			listFlags.Parse([]string{})
		}
		var params url.Values
		if *where != "" {
			params = url.Values{"where": []string{*where}}
		}
		table = clevertable.NewList(endpoint, params, clever)
	case "get":
		if len(flag.Args()) != 3 {
			exitWithArgError(fmt.Sprintf("get action requires an <id> argument"))
		}
		id := flag.Args()[2]
		table = clevertable.NewGet(endpoint, id, clever)
	default:
		exitWithArgError(fmt.Sprintf("'%s' is not a valid action", action))
	}

	if err := transformer.New(table).
		Map(clevertable.FlattenRow).
		Map(clevertable.StringifyArrayVals).
		Sink(sink); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"os"
	"path/filepath"
	"regexp"
)

var (
	verbose = flag.Bool("v", false, "print the names of packages as they are checked")
)

func init() {
	if err := typesInit(); err != nil {
		errExit(err)
	}
}

func main() {
	flag.Parse()
	if err := checkPaths(flag.Args(), os.Stdout); err != nil {
		errExit(err)
	}
}

func errExit(err error) {
	fmt.Fprintf(os.Stderr, "%v\n", err)
	os.Exit(1)
}

func typesMatch(wanted, got []types.Type) bool {
	if len(wanted) != len(got) {
		return false
	}
	for i, w := range wanted {
		g := got[i]
		if !types.ConvertibleTo(g, w) {
			return false
		}
	}
	return true
}

func resultsMatch(wanted, got []types.Type) bool {
	if len(got) == 0 {
		return true
	}
	return typesMatch(wanted, got)
}

func usedMatch(t types.Type, usedAs []types.Type) bool {
	if len(usedAs) < 1 {
		return true
	}
	for _, u := range usedAs {
		if !types.ConvertibleTo(t, u) {
			return false
		}
	}
	return true
}

func interfaceMatching(calls map[string]funcSign, usedAs []types.Type) string {
	matchesIface := func(iface ifaceSign) bool {
		if len(calls) > len(iface.funcs) {
			return false
		}
		if !usedMatch(iface.t, usedAs) {
			return false
		}
		for name, f := range iface.funcs {
			c, e := calls[name]
			if !e {
				return false
			}
			if !typesMatch(f.params, c.params) {
				return false
			}
			if !resultsMatch(f.results, c.results) {
				return false
			}
		}
		return true
	}
	for name, iface := range parsed {
		if matchesIface(iface) {
			return name
		}
	}
	return ""
}

var skipDir = regexp.MustCompile(`^(testdata|vendor|_.*)$`)

func getDirs(d string, recursive bool) ([]string, error) {
	var dirs []string
	walkFn := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if skipDir.MatchString(info.Name()) {
			return filepath.SkipDir
		}
		if info.IsDir() {
			dirs = append(dirs, path)
			if !recursive {
				return filepath.SkipDir
			}
		}
		return nil
	}
	if err := filepath.Walk(d, walkFn); err != nil {
		return nil, err
	}
	return dirs, nil
}

func getPkgs(p string) ([]*build.Package, []string, error) {
	recursive := filepath.Base(p) == "..."
	if !recursive {
		info, err := os.Stat(p)
		if err != nil {
			return nil, nil, err
		}
		if !info.IsDir() {
			pkg := &build.Package{
				Name:    "stdin",
				GoFiles: []string{p},
			}
			return []*build.Package{pkg}, []string{"."}, nil
		}
	}
	d := p
	if recursive {
		d = p[:len(p)-4]
	}
	dirs, err := getDirs(d, recursive)
	if err != nil {
		return nil, nil, err
	}
	wd, err := os.Getwd()
	if err != nil {
		return nil, nil, err
	}
	var pkgs []*build.Package
	var basedirs []string
	for _, d := range dirs {
		pkg, err := build.Import("./"+d, wd, 0)
		if _, ok := err.(*build.NoGoError); ok {
			continue
		}
		if err != nil {
			return nil, nil, err
		}
		pkgs = append(pkgs, pkg)
		basedirs = append(basedirs, d)
	}
	return pkgs, basedirs, nil
}

func checkPaths(paths []string, w io.Writer) error {
	conf := &types.Config{Importer: importer.Default()}
	for _, p := range paths {
		pkgs, basedirs, err := getPkgs(p)
		if err != nil {
			return err
		}
		for i, pkg := range pkgs {
			basedir := basedirs[i]
			if err := checkPkg(conf, pkg, basedir, w); err != nil {
				return err
			}
		}
	}
	return nil
}

func checkPkg(conf *types.Config, pkg *build.Package, basedir string, w io.Writer) error {
	if *verbose {
		fmt.Fprintln(w, basedir)
	}
	gp := &goPkg{
		Package: pkg,
		fset:    token.NewFileSet(),
	}
	for _, p := range pkg.GoFiles {
		fp := filepath.Join(basedir, p)
		if err := gp.parsePath(fp); err != nil {
			return err
		}
	}
	if err := gp.check(conf, w); err != nil {
		return err
	}
	return nil
}

type goPkg struct {
	*build.Package

	fset  *token.FileSet
	files []*ast.File
}

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

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

func (gp *goPkg) check(conf *types.Config, w io.Writer) error {
	info := &types.Info{
		Types: make(map[ast.Expr]types.TypeAndValue),
		Defs:  make(map[*ast.Ident]types.Object),
		Uses:  make(map[*ast.Ident]types.Object),
	}
	_, err := conf.Check(gp.Name, gp.fset, gp.files, info)
	if err != nil {
		return err
	}
	v := &Visitor{
		Info: info,
		w:    w,
		fset: gp.fset,
	}
	for _, f := range gp.files {
		ast.Walk(v, f)
	}
	return nil
}

type Visitor struct {
	*types.Info

	w    io.Writer
	fset *token.FileSet

	nodes []ast.Node

	params map[string]types.Type
	called map[string]map[string]funcSign

	recUsed bool
	usedAs  map[string][]types.Type
}

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

func paramType(sign *types.Signature, i int) types.Type {
	params := sign.Params()
	extra := sign.Variadic() && i >= params.Len()-1
	if !extra {
		return params.At(i).Type()
	}
	stype := params.At(params.Len() - 1).Type().(*types.Slice)
	return stype.Elem()
}

func (v *Visitor) Visit(node ast.Node) ast.Visitor {
	var top ast.Node
	if len(v.nodes) > 0 {
		top = v.nodes[len(v.nodes)-1]
	}
	switch x := node.(type) {
	case *ast.File:
	case *ast.FuncDecl:
		f := v.Defs[x.Name].(*types.Func)
		sign := f.Type().(*types.Signature)
		v.params = typeMap(sign.Params())
		v.called = make(map[string]map[string]funcSign)
		v.usedAs = make(map[string][]types.Type)
	case *ast.BlockStmt:
		v.recUsed = true
	case *ast.CallExpr:
		if !v.recUsed {
			break
		}
		if v.usedAs == nil {
			break
		}
		sign := funcSignature(v.Types[x.Fun].Type)
		if sign == nil {
			break
		}
		for i, e := range x.Args {
			id, ok := e.(*ast.Ident)
			if !ok {
				continue
			}
			name := id.Name
			ptype := paramType(sign, i)
			v.usedAs[name] = append(v.usedAs[name], ptype)
		}
		if wasParamCall := v.onCall(x); wasParamCall {
			return nil
		}
	case nil:
		v.nodes = v.nodes[:len(v.nodes)-1]
		if _, ok := top.(*ast.FuncDecl); ok {
			v.funcEnded(top.Pos())
			v.params = nil
			v.called = nil
			v.usedAs = nil
			v.recUsed = false
		}
	}
	if node != nil {
		v.nodes = append(v.nodes, node)
	}
	return v
}

func funcSignature(t types.Type) *types.Signature {
	switch x := t.(type) {
	case *types.Signature:
		return x
	case *types.Basic:
		// maybe we want to do something with these?
		return nil
	default:
		return funcSignature(t.Underlying())
	}
}

func (v *Visitor) onCall(ce *ast.CallExpr) bool {
	if v.called == nil {
		return false
	}
	sel, ok := ce.Fun.(*ast.SelectorExpr)
	if !ok {
		return false
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return false
	}
	vname := left.Name
	if _, e := v.params[vname]; !e {
		return false
	}
	sign := funcSignature(v.Types[ce.Fun].Type)
	if sign == nil {
		return false
	}
	c := funcSign{}
	results := sign.Results()
	for i := 0; i < results.Len(); i++ {
		v := results.At(i)
		c.results = append(c.results, v.Type())
	}
	for _, a := range ce.Args {
		c.params = append(c.params, v.Types[a].Type)
	}
	if _, e := v.called[vname]; !e {
		v.called[vname] = make(map[string]funcSign)
	}
	fname := sel.Sel.Name
	v.called[vname][fname] = c
	return true
}

func (v *Visitor) funcEnded(pos token.Pos) {
	for name, methods := range v.called {
		usedAs := v.usedAs[name]
		iface := interfaceMatching(methods, usedAs)
		if iface == "" {
			continue
		}
		param, e := v.params[name]
		if !e {
			continue
		}
		if iface == param.String() {
			continue
		}
		pos := v.fset.Position(pos)
		fmt.Fprintf(v.w, "%s:%d: %s can be %s\n",
			pos.Filename, pos.Line, name, iface)
	}
}
<commit_msg>Always recurse into func calls<commit_after>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"os"
	"path/filepath"
	"regexp"
)

var (
	verbose = flag.Bool("v", false, "print the names of packages as they are checked")
)

func init() {
	if err := typesInit(); err != nil {
		errExit(err)
	}
}

func main() {
	flag.Parse()
	if err := checkPaths(flag.Args(), os.Stdout); err != nil {
		errExit(err)
	}
}

func errExit(err error) {
	fmt.Fprintf(os.Stderr, "%v\n", err)
	os.Exit(1)
}

func typesMatch(wanted, got []types.Type) bool {
	if len(wanted) != len(got) {
		return false
	}
	for i, w := range wanted {
		g := got[i]
		if !types.ConvertibleTo(g, w) {
			return false
		}
	}
	return true
}

func resultsMatch(wanted, got []types.Type) bool {
	if len(got) == 0 {
		return true
	}
	return typesMatch(wanted, got)
}

func usedMatch(t types.Type, usedAs []types.Type) bool {
	if len(usedAs) < 1 {
		return true
	}
	for _, u := range usedAs {
		if !types.ConvertibleTo(t, u) {
			return false
		}
	}
	return true
}

func interfaceMatching(calls map[string]funcSign, usedAs []types.Type) string {
	matchesIface := func(iface ifaceSign) bool {
		if len(calls) > len(iface.funcs) {
			return false
		}
		if !usedMatch(iface.t, usedAs) {
			return false
		}
		for name, f := range iface.funcs {
			c, e := calls[name]
			if !e {
				return false
			}
			if !typesMatch(f.params, c.params) {
				return false
			}
			if !resultsMatch(f.results, c.results) {
				return false
			}
		}
		return true
	}
	for name, iface := range parsed {
		if matchesIface(iface) {
			return name
		}
	}
	return ""
}

var skipDir = regexp.MustCompile(`^(testdata|vendor|_.*)$`)

func getDirs(d string, recursive bool) ([]string, error) {
	var dirs []string
	walkFn := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if skipDir.MatchString(info.Name()) {
			return filepath.SkipDir
		}
		if info.IsDir() {
			dirs = append(dirs, path)
			if !recursive {
				return filepath.SkipDir
			}
		}
		return nil
	}
	if err := filepath.Walk(d, walkFn); err != nil {
		return nil, err
	}
	return dirs, nil
}

func getPkgs(p string) ([]*build.Package, []string, error) {
	recursive := filepath.Base(p) == "..."
	if !recursive {
		info, err := os.Stat(p)
		if err != nil {
			return nil, nil, err
		}
		if !info.IsDir() {
			pkg := &build.Package{
				Name:    "stdin",
				GoFiles: []string{p},
			}
			return []*build.Package{pkg}, []string{"."}, nil
		}
	}
	d := p
	if recursive {
		d = p[:len(p)-4]
	}
	dirs, err := getDirs(d, recursive)
	if err != nil {
		return nil, nil, err
	}
	wd, err := os.Getwd()
	if err != nil {
		return nil, nil, err
	}
	var pkgs []*build.Package
	var basedirs []string
	for _, d := range dirs {
		pkg, err := build.Import("./"+d, wd, 0)
		if _, ok := err.(*build.NoGoError); ok {
			continue
		}
		if err != nil {
			return nil, nil, err
		}
		pkgs = append(pkgs, pkg)
		basedirs = append(basedirs, d)
	}
	return pkgs, basedirs, nil
}

func checkPaths(paths []string, w io.Writer) error {
	conf := &types.Config{Importer: importer.Default()}
	for _, p := range paths {
		pkgs, basedirs, err := getPkgs(p)
		if err != nil {
			return err
		}
		for i, pkg := range pkgs {
			basedir := basedirs[i]
			if err := checkPkg(conf, pkg, basedir, w); err != nil {
				return err
			}
		}
	}
	return nil
}

func checkPkg(conf *types.Config, pkg *build.Package, basedir string, w io.Writer) error {
	if *verbose {
		fmt.Fprintln(w, basedir)
	}
	gp := &goPkg{
		Package: pkg,
		fset:    token.NewFileSet(),
	}
	for _, p := range pkg.GoFiles {
		fp := filepath.Join(basedir, p)
		if err := gp.parsePath(fp); err != nil {
			return err
		}
	}
	if err := gp.check(conf, w); err != nil {
		return err
	}
	return nil
}

type goPkg struct {
	*build.Package

	fset  *token.FileSet
	files []*ast.File
}

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

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

func (gp *goPkg) check(conf *types.Config, w io.Writer) error {
	info := &types.Info{
		Types: make(map[ast.Expr]types.TypeAndValue),
		Defs:  make(map[*ast.Ident]types.Object),
		Uses:  make(map[*ast.Ident]types.Object),
	}
	_, err := conf.Check(gp.Name, gp.fset, gp.files, info)
	if err != nil {
		return err
	}
	v := &Visitor{
		Info: info,
		w:    w,
		fset: gp.fset,
	}
	for _, f := range gp.files {
		ast.Walk(v, f)
	}
	return nil
}

type Visitor struct {
	*types.Info

	w    io.Writer
	fset *token.FileSet

	nodes []ast.Node

	params map[string]types.Type
	called map[string]map[string]funcSign

	recUsed bool
	usedAs  map[string][]types.Type
}

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

func paramType(sign *types.Signature, i int) types.Type {
	params := sign.Params()
	extra := sign.Variadic() && i >= params.Len()-1
	if !extra {
		return params.At(i).Type()
	}
	stype := params.At(params.Len() - 1).Type().(*types.Slice)
	return stype.Elem()
}

func (v *Visitor) Visit(node ast.Node) ast.Visitor {
	var top ast.Node
	if len(v.nodes) > 0 {
		top = v.nodes[len(v.nodes)-1]
	}
	switch x := node.(type) {
	case *ast.File:
	case *ast.FuncDecl:
		f := v.Defs[x.Name].(*types.Func)
		sign := f.Type().(*types.Signature)
		v.params = typeMap(sign.Params())
		v.called = make(map[string]map[string]funcSign)
		v.usedAs = make(map[string][]types.Type)
	case *ast.BlockStmt:
		v.recUsed = true
	case *ast.CallExpr:
		if !v.recUsed {
			break
		}
		if v.usedAs == nil {
			break
		}
		sign := funcSignature(v.Types[x.Fun].Type)
		if sign == nil {
			break
		}
		for i, e := range x.Args {
			id, ok := e.(*ast.Ident)
			if !ok {
				continue
			}
			name := id.Name
			ptype := paramType(sign, i)
			v.usedAs[name] = append(v.usedAs[name], ptype)
		}
		v.onCall(x)
	case nil:
		v.nodes = v.nodes[:len(v.nodes)-1]
		if _, ok := top.(*ast.FuncDecl); ok {
			v.funcEnded(top.Pos())
			v.params = nil
			v.called = nil
			v.usedAs = nil
			v.recUsed = false
		}
	}
	if node != nil {
		v.nodes = append(v.nodes, node)
	}
	return v
}

func funcSignature(t types.Type) *types.Signature {
	switch x := t.(type) {
	case *types.Signature:
		return x
	case *types.Basic:
		// maybe we want to do something with these?
		return nil
	default:
		return funcSignature(t.Underlying())
	}
}

func (v *Visitor) onCall(ce *ast.CallExpr) {
	if v.called == nil {
		return
	}
	sel, ok := ce.Fun.(*ast.SelectorExpr)
	if !ok {
		return
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return
	}
	vname := left.Name
	if _, e := v.params[vname]; !e {
		return
	}
	sign := funcSignature(v.Types[ce.Fun].Type)
	if sign == nil {
		return
	}
	c := funcSign{}
	results := sign.Results()
	for i := 0; i < results.Len(); i++ {
		v := results.At(i)
		c.results = append(c.results, v.Type())
	}
	for _, a := range ce.Args {
		c.params = append(c.params, v.Types[a].Type)
	}
	if _, e := v.called[vname]; !e {
		v.called[vname] = make(map[string]funcSign)
	}
	fname := sel.Sel.Name
	v.called[vname][fname] = c
	return
}

func (v *Visitor) funcEnded(pos token.Pos) {
	for name, methods := range v.called {
		usedAs := v.usedAs[name]
		iface := interfaceMatching(methods, usedAs)
		if iface == "" {
			continue
		}
		param, e := v.params[name]
		if !e {
			continue
		}
		if iface == param.String() {
			continue
		}
		pos := v.fset.Position(pos)
		fmt.Fprintf(v.w, "%s:%d: %s can be %s\n",
			pos.Filename, pos.Line, name, iface)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Niklas Wolber
// This file is licensed under the MIT license.
// See the LICENSE file for more information.

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"

	scheduler "github.com/robfig/cron"

	"golang.org/x/net/context"

	_ "net/http/pprof"
)

func main() {
	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	conf, err := readConfig("config.json")
	if err != nil {
		log.Println("config:", err)
	}

	log.Printf("%#v\n", conf)
	log.SetFlags(log.Flags() | log.Lshortfile)

	cron := scheduler.New()

	start := time.Now()
	f, err := prepare(conf)
	stop := time.Now()
	log.Println("job preparation took", stop.Sub(start))
	if err != nil {
		log.Fatalln(err)
	}

	cron.AddFunc(conf.Schedule, func() {
		ctx, cancel := context.WithCancel(context.Background())
		ctx = context.WithValue(ctx, outputKey, os.Stdout)
		defer cancel()

		start := time.Now()
		_, err = f(ctx)
		stop := time.Now()
		if err != nil {
			log.Println("execution failed", err)
		} else {
			log.Println("execution complete")
		}
		log.Println("execution took", stop.Sub(start))
	})
	cron.Start()
	defer cron.Stop()

	s := <-signals
	fmt.Println("Got signal:", s)

	log.Println("fin")
}
<commit_msg>Add "once" as a possible schedule<commit_after>// Copyright (c) 2016 Niklas Wolber
// This file is licensed under the MIT license.
// See the LICENSE file for more information.

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"

	scheduler "github.com/robfig/cron"

	"golang.org/x/net/context"

	_ "net/http/pprof"
)

func main() {
	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	conf, err := readConfig("config.json")
	if err != nil {
		log.Println("config:", err)
	}

	log.Printf("%#v\n", conf)
	log.SetFlags(log.Flags() | log.Lshortfile)

	cron := scheduler.New()

	start := time.Now()
	f, err := prepare(conf)
	stop := time.Now()
	log.Println("job preparation took", stop.Sub(start))
	if err != nil {
		log.Fatalln(err)
	}

	exec := func() {
		ctx, cancel := context.WithCancel(context.Background())
		ctx = context.WithValue(ctx, outputKey, os.Stdout)
		defer cancel()

		start := time.Now()
		_, err = f(ctx)
		stop := time.Now()
		if err != nil {
			log.Println("execution failed", err)
		} else {
			log.Println("execution complete")
		}
		log.Println("execution took", stop.Sub(start))
	}

	if conf.Schedule == "once" {
		exec()
	} else {
		cron.AddFunc(conf.Schedule, exec)
		cron.Start()
		defer cron.Stop()
	}
	s := <-signals
	fmt.Println("Got signal:", s)

	log.Println("fin")
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"log"
	"net/http"

	"github.com/digitalocean/godo"
	"github.com/metalmatze/digitalocean_exporter/collector"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/common/version"
	"golang.org/x/oauth2"
)

// TokenSource has the AccessToken which is passes to the godo client.
type TokenSource struct {
	AccessToken string
}

// Token returns a token or an error.
func (t *TokenSource) Token() (*oauth2.Token, error) {
	token := &oauth2.Token{
		AccessToken: t.AccessToken,
	}
	return token, nil
}

func main() {
	var (
		listenAddress = flag.String("web.listen-address", ":9100", "Address on which to expose metrics and web interface.")
		metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		token         = flag.String("do.token", "", "Token you got from DigitalOcean for the API (read-only).")
	)
	flag.Parse()

	tokenSource := &TokenSource{
		AccessToken: *token,
	}
	oauthClient := oauth2.NewClient(context.TODO(), tokenSource)
	client := godo.NewClient(oauthClient)

	log.Println("Starting digitalocean_exporter", version.Info())

	prometheus.MustRegister(collector.NewAccountCollector(client))
	prometheus.MustRegister(collector.NewDropletCollector(client))
	prometheus.MustRegister(collector.NewFloatingIPCollector(client))
	prometheus.MustRegister(collector.NewImageCollector(client))
	prometheus.MustRegister(collector.NewKeyCollector(client))
	prometheus.MustRegister(collector.NewSnapshotCollector(client))
	prometheus.MustRegister(collector.NewVolumeCollector(client))

	http.Handle(*metricsPath, promhttp.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
			<title>DigitalOcean Exporter</title>

DigitalOcean Exporter

<a href="` + *metricsPath + `">Metrics

			`))
	})

	log.Println("Listening on", *listenAddress)
	if err := http.ListenAndServe(*listenAddress, nil); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Pick the next free port number from prom's list<commit_after>package main

import (
	"context"
	"flag"
	"log"
	"net/http"

	"github.com/digitalocean/godo"
	"github.com/metalmatze/digitalocean_exporter/collector"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/common/version"
	"golang.org/x/oauth2"
)

// TokenSource has the AccessToken which is passes to the godo client.
type TokenSource struct {
	AccessToken string
}

// Token returns a token or an error.
func (t *TokenSource) Token() (*oauth2.Token, error) {
	token := &oauth2.Token{
		AccessToken: t.AccessToken,
	}
	return token, nil
}

func main() {
	var (
		listenAddress = flag.String("web.listen-address", ":9211", "Address on which to expose metrics and web interface.")
		metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		token         = flag.String("do.token", "", "Token you got from DigitalOcean for the API (read-only).")
	)
	flag.Parse()

	tokenSource := &TokenSource{
		AccessToken: *token,
	}
	oauthClient := oauth2.NewClient(context.TODO(), tokenSource)
	client := godo.NewClient(oauthClient)

	log.Println("Starting digitalocean_exporter", version.Info())

	prometheus.MustRegister(collector.NewAccountCollector(client))
	prometheus.MustRegister(collector.NewDropletCollector(client))
	prometheus.MustRegister(collector.NewFloatingIPCollector(client))
	prometheus.MustRegister(collector.NewImageCollector(client))
	prometheus.MustRegister(collector.NewKeyCollector(client))
	prometheus.MustRegister(collector.NewSnapshotCollector(client))
	prometheus.MustRegister(collector.NewVolumeCollector(client))

	http.Handle(*metricsPath, promhttp.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
			<title>DigitalOcean Exporter</title>

DigitalOcean Exporter

<a href="` + *metricsPath + `">Metrics

			`))
	})

	log.Println("Listening on", *listenAddress)
	if err := http.ListenAndServe(*listenAddress, nil); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/ashwanthkumar/slack-go-webhook"
	"github.com/rimaulana/plexgoslack/tmdb"
)

var tmdbConn *tmdb.TMDb
var conf Config

// MDb explanation
type MDb struct {
	APIKey string `toml:"api_key"`
}

// SlackCfg explanation
type SlackCfg struct {
	WebHook []string `toml:"webhooks"`
}

// PlexLibrary explanation
type PlexLibrary struct {
	Root    string `toml:"root"`
	Section int    `toml:"section"`
}

// Config explanation
type Config struct {
	Tmdb    MDb                    `toml:"tmdb"`
	PlexURL string                 `toml:"plex_url"`
	Plex    map[string]PlexLibrary `toml:"plex"`
	Slack   SlackCfg               `toml:"slack"`
}

// PostToSlack documentation
func PostToSlack(message tmdb.MovieInfo) {
	text := fmt.Sprintf("New movie is now available on <%sweb/index.html|Plex>", conf.PlexURL)
	test := fmt.Sprintf("%s (%s)", message.Title, message.Year)
	head := "Synopsis"
	atth1 := slack.Attachment{
		Title:    &test,
		ImageUrl: &message.Thumbnail,
	}
	atth2 := slack.Attachment{
		Title: &head,
		Text:  &message.Synopsis,
	}
	payload := slack.Payload{
		Text:        text,
		Attachments: []slack.Attachment{atth1, atth2},
	}
	for _, hook := range conf.Slack.WebHook {
		err := slack.Send(hook, "", payload)
		log.Println("Send", message.Title, "info to Slack")
		if len(err) > 0 {
			log.Printf("error: %s\n", err)
		}
	}
}

// Diff documentation
func Diff(a, b []os.FileInfo) []string {
	mb := map[string]bool{}
	for _, x := range a {
		mb[x.Name()] = true
	}
	ab := []string{}
	for _, x := range b {
		if _, ok := mb[x.Name()]; !ok {
			ab = append(ab, x.Name())
		}
	}
	return ab
}

// Analyze documentation
func Analyze(path string) (tmdb.MovieInfo, error) {
	regex := regexp.MustCompile("((?:[^\\/]+)(?:(?:\\S+\\s+)))\\(([0-9]{4})\\)\\/?$")
	result := regex.FindStringSubmatch(path)
	if len(result) == 3 {
		res, err := tmdbConn.GetInfo(strings.TrimSpace(result[1]), strings.TrimSpace(result[2]))
		if err != nil {
			return tmdb.MovieInfo{}, err
		}
		return res, nil
	}
	return tmdb.MovieInfo{}, errors.New("Path doesn't match regex")
}

// UpdateRepo documentation
func UpdateRepo(invoker <-chan int) {
	var DataMap map[int]time.Time
	for inv := range invoker {
		if _, ok := DataMap[inv]; ok {
			tnow := time.Now()
			if elapsed := tnow.Sub(DataMap[inv]); elapsed >= 5 {
				DataMap[inv] = tnow
				if _, err := exec.Command("/bin/bash", "-c", fmt.Sprintf("sudo -u plex -E -H \"$LD_LIBRARY_PATH/Plex Media Scanner\" --scan --refresh --section %d --force", inv)).Output(); err != nil {
					log.Println("Error :", err)
				}
			} else {
				log.Println("Updated:", elapsed, "ago")
			}
		}
	}
}

// Watcher documentation
func Watcher(root string, section int, invoker chan<- int) {
	log.Println("info: monitoring folder", root)
	files, err := ioutil.ReadDir(root)
	if err != nil {
		log.Fatal(err)
	}
	for {
		files2, err := ioutil.ReadDir(root)
		if err != nil {
			log.Println("error:", err)
		}
		diff := Diff(files, files2)
		isNew := false
		for _, newMovie := range diff {
			log.Println("info: detected", newMovie)
			res, err := Analyze(newMovie)
			if err != nil {
				log.Println("error:", err)
			} else {
				go PostToSlack(res)
				isNew = true
			}
		}
		if isNew {
			invoker <- section
		}
		files = files2
		time.Sleep(time.Second * 5)
	}
}

// LoadConfig documentation
func LoadConfig(RootDirectory string) (Config, error) {
	var Result Config
	log.Println("Reading config file")
	if RawConfig, err := ioutil.ReadFile(RootDirectory + "\\config.toml"); err == nil {
		if _, errs := toml.Decode(string(RawConfig[:]), &Result); errs != nil {
			return Result, errs
		}
	} else {
		return Result, err
	}
	return Result, nil
}

func main() {
	// Getting configuration file
	if root, err := filepath.Abs(filepath.Dir(os.Args[0])); err == nil {
		if cfg, errs := LoadConfig(root); errs == nil {
			conf = cfg
		} else {
			log.Fatal("Error: ", errs)
		}
	} else {
		log.Fatal("Error: ", err)
	}

	tmdbConn = tmdb.New(conf.Tmdb.APIKey)
	invoker := make(chan int, 100)
	done := make(chan bool)

	go UpdateRepo(invoker)
	for fldr := range conf.Plex {
		go Watcher(conf.Plex[fldr].Root, conf.Plex[fldr].Section, invoker)
	}
	<-done
}
<commit_msg>added logic when map is empty<commit_after>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/ashwanthkumar/slack-go-webhook"
	"github.com/rimaulana/plexgoslack/tmdb"
)

var tmdbConn *tmdb.TMDb
var conf Config

// MDb explanation
type MDb struct {
	APIKey string `toml:"api_key"`
}

// SlackCfg explanation
type SlackCfg struct {
	WebHook []string `toml:"webhooks"`
}

// PlexLibrary explanation
type PlexLibrary struct {
	Root    string `toml:"root"`
	Section int    `toml:"section"`
}

// Config explanation
type Config struct {
	Tmdb    MDb                    `toml:"tmdb"`
	PlexURL string                 `toml:"plex_url"`
	Plex    map[string]PlexLibrary `toml:"plex"`
	Slack   SlackCfg               `toml:"slack"`
}

// PostToSlack documentation
func PostToSlack(message tmdb.MovieInfo) {
	text := fmt.Sprintf("New movie is now available on <%sweb/index.html|Plex>", conf.PlexURL)
	test := fmt.Sprintf("%s (%s)", message.Title, message.Year)
	head := "Synopsis"
	atth1 := slack.Attachment{
		Title:    &test,
		ImageUrl: &message.Thumbnail,
	}
	atth2 := slack.Attachment{
		Title: &head,
		Text:  &message.Synopsis,
	}
	payload := slack.Payload{
		Text:        text,
		Attachments: []slack.Attachment{atth1, atth2},
	}
	for _, hook := range conf.Slack.WebHook {
		err := slack.Send(hook, "", payload)
		log.Println("Send", message.Title, "info to Slack")
		if len(err) > 0 {
			log.Printf("error: %s\n", err)
		}
	}
}

// Diff documentation
func Diff(a, b []os.FileInfo) []string {
	mb := map[string]bool{}
	for _, x := range a {
		mb[x.Name()] = true
	}
	ab := []string{}
	for _, x := range b {
		if _, ok := mb[x.Name()]; !ok {
			ab = append(ab, x.Name())
		}
	}
	return ab
}

// Analyze documentation
func Analyze(path string) (tmdb.MovieInfo, error) {
	regex := regexp.MustCompile("((?:[^\\/]+)(?:(?:\\S+\\s+)))\\(([0-9]{4})\\)\\/?$")
	result := regex.FindStringSubmatch(path)
	if len(result) == 3 {
		res, err := tmdbConn.GetInfo(strings.TrimSpace(result[1]), strings.TrimSpace(result[2]))
		if err != nil {
			return tmdb.MovieInfo{}, err
		}
		return res, nil
	}
	return tmdb.MovieInfo{}, errors.New("Path doesn't match regex")
}

// UpdateRepo documentation
func UpdateRepo(invoker <-chan int) {
	DataMap := make(map[int]time.Time)
	log.Println("Updater daemon status Running")
	for true {
		inv := <-invoker
		log.Println("Updater daemon invoked by watcher")
		command := fmt.Sprintf("sudo -u plex -E -H \"$LD_LIBRARY_PATH/Plex Media Scanner\" --scan --refresh --section %d", inv)
		if _, ok := DataMap[inv]; ok {
			tnow := time.Now()
			if elapsed := tnow.Sub(DataMap[inv]); elapsed >= 5 {
				DataMap[inv] = tnow
				if _, err := exec.Command("/bin/bash", "-c", command).Output(); err != nil {
					log.Println("Error :", err)
				}
			} else {
				log.Println("Updated:", elapsed, "ago")
			}
		} else {
			DataMap[inv] = time.Now()
			if _, err := exec.Command("/bin/bash", "-c", command).Output(); err != nil {
				log.Println("Error :", err)
			}
		}
	}
}

// Watcher documentation
func Watcher(root string, section int, invoker chan<- int) {
	log.Println("info: monitoring folder", root)
	files, err := ioutil.ReadDir(root)
	if err != nil {
		log.Fatal(err)
	}
	for {
		files2, err := ioutil.ReadDir(root)
		if err != nil {
			log.Println("error:", err)
		}
		diff := Diff(files, files2)
		isNew := false
		for _, newMovie := range diff {
			log.Println("info: detected", newMovie)
			res, err := Analyze(newMovie)
			if err != nil {
				log.Println("error:", err)
			} else {
				go PostToSlack(res)
				isNew = true
			}
		}
		if isNew {
			invoker <- section
		}
		files = files2
		time.Sleep(time.Second * 5)
	}
}

// LoadConfig documentation
func LoadConfig(RootDirectory string) (Config, error) {
	var Result Config
	log.Println("Reading config file")
	if RawConfig, err := ioutil.ReadFile(RootDirectory + "/config.toml"); err == nil {
		if _, errs := toml.Decode(string(RawConfig[:]), &Result); errs != nil {
			return Result, errs
		}
	} else {
		return Result, err
	}
	return Result, nil
}

func main() {
	// Getting configuration file
	if root, err := filepath.Abs(filepath.Dir(os.Args[0])); err == nil {
		if cfg, errs := LoadConfig(root); errs == nil {
			conf = cfg
		} else {
			log.Fatal("Error: ", errs)
		}
	} else {
		log.Fatal("Error: ", err)
	}

	tmdbConn = tmdb.New(conf.Tmdb.APIKey)
	invoker := make(chan int, 100)
	done := make(chan bool)

	go UpdateRepo(invoker)
	for fldr := range conf.Plex {
		go Watcher(conf.Plex[fldr].Root, conf.Plex[fldr].Section, invoker)
	}
	<-done
}
<|endoftext|>


<commit_before>package adapter

import (
	"flag"
	"math/rand"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/lestrrat/go-server-starter/listener"
	"github.com/shogo82148/go-gracedown"
)

func Main() {
	rand.Seed(time.Now().UnixNano())

	c, err := parseConfig()
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Fatal("error while parsing configure")
		os.Exit(1)
	}

	startWatchSignal()
	l, err := getListener(c)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Fatal("listen error")
		os.Exit(1)
	}

	s, err := NewServer(*c)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Fatal("init server error")
		os.Exit(1)
	}

	gracedown.Serve(l, LoggingHandler(s))
}

func parseConfig() (*Config, error) {
	var configFile string
	flag.StringVar(&configFile, "c", "", "configuration file")
	flag.StringVar(&configFile, "config", "", "configuration file")
	flag.Parse()

	c := NewConfig()
	if err := c.LoadEnv(); err != nil {
		return nil, err
	}
	if configFile != "" {
		if err := c.LoadYaml(configFile); err != nil {
			return nil, err
		}
	}

	return c, nil
}

func startWatchSignal() {
	signal_chan := make(chan os.Signal)
	signal.Notify(signal_chan, syscall.SIGTERM)
	go func() {
		for {
			s := <-signal_chan
			if s == syscall.SIGTERM {
				logrus.Info("received SIGTERM")
				gracedown.Close()
			}
		}
	}()
}

func getListener(c *Config) (net.Listener, error) {
	listeners, err := listener.ListenAll()
	if err != nil && err != listener.ErrNoListeningTarget {
		panic(err)
	}
	if err != listener.ErrNoListeningTarget {
		return listeners[0], nil
	}

	// Fallback if not running under Server::Starter
	return net.Listen("tcp", c.Address)
}
<commit_msg>add showing version flag<commit_after>package adapter

import (
	"flag"
	"fmt"
	"math/rand"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/lestrrat/go-server-starter/listener"
	"github.com/shogo82148/go-gracedown"
)

const Version = "0.1.0"

func Main() {
	rand.Seed(time.Now().UnixNano())

	var configFile string
	var showVersion bool
	flag.StringVar(&configFile, "c", "", "configuration file")
	flag.StringVar(&configFile, "config", "", "configuration file")
	flag.BoolVar(&showVersion, "v", false, "show version information")
	flag.BoolVar(&showVersion, "version", false, "show version information")
	flag.Parse()

	if showVersion {
		fmt.Println("go-nginx-oauth2-adapter", Version)
		return
	}

	c, err := parseConfig(configFile)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Fatal("error while parsing configure")
		os.Exit(1)
	}

	startWatchSignal()
	l, err := getListener(c)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Fatal("listen error")
		os.Exit(1)
	}

	s, err := NewServer(*c)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Fatal("init server error")
		os.Exit(1)
	}

	gracedown.Serve(l, LoggingHandler(s))
}

func parseConfig(configFile string) (*Config, error) {
	c := NewConfig()
	if err := c.LoadEnv(); err != nil {
		return nil, err
	}
	if configFile != "" {
		if err := c.LoadYaml(configFile); err != nil {
			return nil, err
		}
	}

	return c, nil
}

func startWatchSignal() {
	signal_chan := make(chan os.Signal)
	signal.Notify(signal_chan, syscall.SIGTERM)
	go func() {
		for {
			s := <-signal_chan
			if s == syscall.SIGTERM {
				logrus.Info("received SIGTERM")
				gracedown.Close()
			}
		}
	}()
}

func getListener(c *Config) (net.Listener, error) {
	listeners, err := listener.ListenAll()
	if err != nil && err != listener.ErrNoListeningTarget {
		panic(err)
	}
	if err != listener.ErrNoListeningTarget {
		return listeners[0], nil
	}

	// Fallback if not running under Server::Starter
	return net.Listen("tcp", c.Address)
}
<|endoftext|>


<commit_before>// elvish is an experimental Unix shell. It tries to incorporate a powerful
// programming language with an extensible, friendly user interface.
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"os/user"
	"unicode/utf8"

	"github.com/xiaq/elvish/edit"
	"github.com/xiaq/elvish/eval"
	"github.com/xiaq/elvish/parse"
	"github.com/xiaq/elvish/util"
)

const (
	sigchSize = 32
)

// TODO(xiaq): Currently only the editor deals with signals.
func interact() {
	ev := eval.NewEvaluator()
	cmdNum := 0

	username := "???"
	user, err := user.Current()
	if err == nil {
		username = user.Username
	}
	hostname, err := os.Hostname()
	if err != nil {
		hostname = "???"
	}
	rpromptStr := username + "@" + hostname

	sigch := make(chan os.Signal, sigchSize)
	signal.Notify(sigch)

	ed := edit.NewEditor(os.Stdin, ev, sigch)

	for {
		cmdNum++
		name := fmt.Sprintf("<tty %d>", cmdNum)

		prompt := func() string {
			return util.Getwd() + "> "
		}
		rprompt := func() string {
			return rpromptStr
		}

		lr := ed.ReadLine(prompt, rprompt)

		if lr.EOF {
			break
		} else if lr.Err != nil {
			fmt.Println("Editor error:", lr.Err)
			fmt.Println("My pid is", os.Getpid())
		}

		n, pe := parse.Parse(name, lr.Line)
		if pe != nil {
			fmt.Print(pe.(*util.ContextualError).Pprint())
			continue
		}

		ee := ev.Eval(name, lr.Line, n)
		if ee != nil {
			fmt.Print(ee.(*util.ContextualError).Pprint())
			continue
		}
	}
}

func script(name string) {
	file, err := os.Open(name)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	bytes, err := ioutil.ReadAll(file)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if !utf8.Valid(bytes) {
		fmt.Fprintf(os.Stderr, "source %v is not valid UTF-8\n", name)
		os.Exit(1)
	}
	src := string(bytes)

	ev := eval.NewEvaluator()

	n, pe := parse.Parse(name, src)
	if pe != nil {
		fmt.Print(pe.(*util.ContextualError).Pprint())
		os.Exit(1)
	}

	ee := ev.Eval(name, src, n)
	if ee != nil {
		fmt.Print(ee.(*util.ContextualError).Pprint())
		os.Exit(1)
	}
}

var usage = `Usage:
    elvish
    elvish <script>
`

func main() {
	switch len(os.Args) {
	case 1:
		interact()
	case 2:
		script(os.Args[1])
	default:
		fmt.Fprintf(os.Stderr, usage)
		os.Exit(1)
	}
}
<commit_msg>main.go: Don't assume Evaluator.Eval returns ContextualError<commit_after>// elvish is an experimental Unix shell. It tries to incorporate a powerful
// programming language with an extensible, friendly user interface.
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"os/user"
	"unicode/utf8"

	"github.com/xiaq/elvish/edit"
	"github.com/xiaq/elvish/eval"
	"github.com/xiaq/elvish/parse"
	"github.com/xiaq/elvish/util"
)

const (
	sigchSize = 32
)

// TODO(xiaq): Currently only the editor deals with signals.
func interact() {
	ev := eval.NewEvaluator()
	cmdNum := 0

	username := "???"
	user, err := user.Current()
	if err == nil {
		username = user.Username
	}
	hostname, err := os.Hostname()
	if err != nil {
		hostname = "???"
	}
	rpromptStr := username + "@" + hostname

	sigch := make(chan os.Signal, sigchSize)
	signal.Notify(sigch)

	ed := edit.NewEditor(os.Stdin, ev, sigch)

	for {
		cmdNum++
		name := fmt.Sprintf("<tty %d>", cmdNum)

		prompt := func() string {
			return util.Getwd() + "> "
		}
		rprompt := func() string {
			return rpromptStr
		}

		lr := ed.ReadLine(prompt, rprompt)

		if lr.EOF {
			break
		} else if lr.Err != nil {
			fmt.Println("Editor error:", lr.Err)
			fmt.Println("My pid is", os.Getpid())
		}

		n, pe := parse.Parse(name, lr.Line)
		if pe != nil {
			fmt.Print(pe.(*util.ContextualError).Pprint())
			continue
		}

		ee := ev.Eval(name, lr.Line, n)
		if ee != nil {
			if ce, ok := ee.(*util.ContextualError); ok {
				fmt.Print(ce.Pprint())
			} else {
				fmt.Println(ee)
			}
			continue
		}
	}
}

func script(name string) {
	file, err := os.Open(name)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	bytes, err := ioutil.ReadAll(file)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if !utf8.Valid(bytes) {
		fmt.Fprintf(os.Stderr, "source %v is not valid UTF-8\n", name)
		os.Exit(1)
	}
	src := string(bytes)

	ev := eval.NewEvaluator()

	n, pe := parse.Parse(name, src)
	if pe != nil {
		fmt.Print(pe.(*util.ContextualError).Pprint())
		os.Exit(1)
	}

	ee := ev.Eval(name, src, n)
	if ee != nil {
		fmt.Print(ee.(*util.ContextualError).Pprint())
		os.Exit(1)
	}
}

var usage = `Usage:
    elvish
    elvish <script>
`

func main() {
	switch len(os.Args) {
	case 1:
		interact()
	case 2:
		script(os.Args[1])
	default:
		fmt.Fprintf(os.Stderr, usage)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Package main is used as an entry point of
// 'sunplate' toolkit. It validates user input parameters
// and runs subcommands.
package main

import (
	"os"

	"github.com/anonx/sunplate/command"
	"github.com/anonx/sunplate/generation"
	"github.com/anonx/sunplate/help"
	"github.com/anonx/sunplate/log"
)

// Handlers is a map of registered commands
// 'sunplate' toolkit supports.
var Handlers = map[string]command.Handler{
	"generate": generation.Start,
	"help":     help.Start,
}

func main() {
	// Do not show stacktrace if something goes wrong.
	defer catchPanic()

	// Show header message.
	log.Trace.Println(header)

	// Validate input parameters and find out what user wants to run.
	ct, err := command.NewType(os.Args[1:])
	if err != nil {
		// Validation failed because of incorrect arguments number,
		// try to show help menu instead.
		ct, err = command.NewType([]string{"help", "info"})
	}
	err = ct.Register(Handlers)
	if err != nil {
		// Validation failed because requested handler does not exist.
		log.Warn.Printf("Unknown command '%s'.\nRun 'sunplate help' for usage.", os.Args[1])
	}
}

func catchPanic() {
	if err := recover(); err != nil {
		log.Error.Fatal(err)
	}
}

func init() {
	// Set a base path of 'generation' package's files.
	generation.BasePath = "./generation"
}

var header = `~
~ https://github.com/anonx/sunplate
~
                        _       _
                       | |     | |
  ___ _   _ _ __  _ __ | | __ _| |_ ___
 / __| | | | '_ \| '_ \| |/ _' | __/ _ \
 \__ \ |_| | | | | |_) | | (_| | ||  __/
 |___/\__,_|_| |_| .__/|_|\__,_|\__\___|
                 | |
                 |_|
`
<commit_msg>Fix a path to system files used for generation<commit_after>// Package main is used as an entry point of
// 'sunplate' toolkit. It validates user input parameters
// and runs subcommands.
package main

import (
	"go/build"
	"os"
	"path/filepath"

	"github.com/anonx/sunplate/command"
	"github.com/anonx/sunplate/generation"
	"github.com/anonx/sunplate/help"
	"github.com/anonx/sunplate/log"
)

// Handlers is a map of registered commands
// 'sunplate' toolkit supports.
var Handlers = map[string]command.Handler{
	"generate": generation.Start,
	"help":     help.Start,
}

func main() {
	// Do not show stacktrace if something goes wrong.
	defer catchPanic()

	// Show header message.
	log.Trace.Println(header)

	// Validate input parameters and find out what user wants to run.
	ct, err := command.NewType(os.Args[1:])
	if err != nil {
		// Validation failed because of incorrect arguments number,
		// try to show help menu instead.
		ct, err = command.NewType([]string{"help", "info"})
	}
	err = ct.Register(Handlers)
	if err != nil {
		// Validation failed because requested handler does not exist.
		log.Warn.Printf("Unknown command '%s'.\nRun 'sunplate help' for usage.", os.Args[1])
	}
}

func catchPanic() {
	if err := recover(); err != nil {
		log.Error.Fatal(err)
	}
}

func init() {
	// Set a base path of 'generation' package's files.
	generation.BasePath = filepath.Join(
		build.Default.GOPATH, "src/github.com/anonx/sunplate/generation",
	)
}

var header = `~
~ https://github.com/anonx/sunplate
~
                        _       _
                       | |     | |
  ___ _   _ _ __  _ __ | | __ _| |_ ___
 / __| | | | '_ \| '_ \| |/ _' | __/ _ \
 \__ \ |_| | | | | |_) | | (_| | ||  __/
 |___/\__,_|_| |_| .__/|_|\__,_|\__\___|
                 | |
                 |_|
`
<|endoftext|>


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

package main

import (
	"flag"
	"html/template"
	"math/rand"
	"mime"
	"net/http"
	"runtime"
	"strconv"
	"time"

	"github.com/b3log/wide/conf"
	"github.com/b3log/wide/editor"
	"github.com/b3log/wide/event"
	"github.com/b3log/wide/file"
	"github.com/b3log/wide/i18n"
	"github.com/b3log/wide/notification"
	"github.com/b3log/wide/output"
	"github.com/b3log/wide/session"
	"github.com/b3log/wide/shell"
	"github.com/b3log/wide/util"
	"github.com/golang/glog"
)

// The only one init function in Wide.
func init() {
	confPath := flag.String("conf", "conf/wide.json", "path of wide.json")
	confIP := flag.String("ip", "", "ip to visit")
	confPort := flag.String("port", "", "port to visit")
	confServer := flag.String("server", "", "this will overwrite Wide.Server if specified")
	confChannel := flag.String("channel", "", "this will overwrite Wide.XXXChannel if specified")
	confStat := flag.Bool("stat", false, "whether report statistics periodically")
	confDocker := flag.Bool("docker", false, "whether run in a docker container")

	flag.Set("alsologtostderr", "true")
	flag.Set("stderrthreshold", "INFO")
	flag.Set("v", "3")

	flag.Parse()

	i18n.Load()

	event.Load()

	conf.Load(*confPath, *confIP, *confPort, *confServer, *confChannel, *confDocker)

	conf.FixedTimeCheckEnv()
	conf.FixedTimeSave()

	session.FixedTimeRelease()

	if *confStat {
		session.FixedTimeReport()
	}
}

// indexHandler handles request of Wide index.
func indexHandler(w http.ResponseWriter, r *http.Request) {
	httpSession, _ := session.HTTPSession.Get(r, "wide-session")

	if httpSession.IsNew {
		http.Redirect(w, r, "/login", http.StatusFound)

		return
	}

	httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
	httpSession.Save(r, w)

	// create a Wide session
	rand.Seed(time.Now().UnixNano())
	sid := strconv.Itoa(rand.Int())
	wideSession := session.WideSessions.New(httpSession, sid)

	username := httpSession.Values["username"].(string)
	user := conf.Wide.GetUser(username)
	if nil == user {
		glog.Warningf("Not found user [%s]", username)

		http.Redirect(w, r, "/login", http.StatusFound)

		return
	}

	locale := user.Locale

	wideSessions := session.WideSessions.GetByUsername(username)

	model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
		"session": wideSession, "latestSessionContent": user.LatestSessionContent,
		"pathSeparator": conf.PathSeparator, "codeMirrorVer": conf.CodeMirrorVer}

	glog.V(3).Infof("User [%s] has [%d] sessions", username, len(wideSessions))

	t, err := template.ParseFiles("views/index.html")

	if nil != err {
		glog.Error(err)
		http.Error(w, err.Error(), 500)

		return
	}

	t.Execute(w, model)
}

// serveSingle registers the handler function for the given pattern and filename.
func serveSingle(pattern string, filename string) {
	http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, filename)
	})
}

// startHandler handles request of start page.
func startHandler(w http.ResponseWriter, r *http.Request) {
	httpSession, _ := session.HTTPSession.Get(r, "wide-session")

	if httpSession.IsNew {
		http.Redirect(w, r, "/login", http.StatusFound)

		return
	}

	httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
	httpSession.Save(r, w)

	username := httpSession.Values["username"].(string)
	locale := conf.Wide.GetUser(username).Locale
	userWorkspace := conf.Wide.GetUserWorkspace(username)

	sid := r.URL.Query()["sid"][0]
	wSession := session.WideSessions.Get(sid)
	if nil == wSession {
		glog.Errorf("Session [%s] not found", sid)
	}

	model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
		"username": username, "workspace": userWorkspace, "ver": conf.WideVersion, "session": wSession}

	t, err := template.ParseFiles("views/start.html")

	if nil != err {
		glog.Error(err)
		http.Error(w, err.Error(), 500)

		return
	}

	t.Execute(w, model)
}

// keyboardShortcutsHandler handles request of keyboard shortcuts page.
func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) {
	httpSession, _ := session.HTTPSession.Get(r, "wide-session")

	if httpSession.IsNew {
		http.Redirect(w, r, "/login", http.StatusFound)

		return
	}

	httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
	httpSession.Save(r, w)

	username := httpSession.Values["username"].(string)
	locale := conf.Wide.GetUser(username).Locale

	model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale}

	t, err := template.ParseFiles("views/keyboard_shortcuts.html")

	if nil != err {
		glog.Error(err)
		http.Error(w, err.Error(), 500)

		return
	}

	t.Execute(w, model)
}

// aboutHandle handles request of about page.
func aboutHandler(w http.ResponseWriter, r *http.Request) {
	httpSession, _ := session.HTTPSession.Get(r, "wide-session")

	if httpSession.IsNew {
		http.Redirect(w, r, "/login", http.StatusFound)

		return
	}

	httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
	httpSession.Save(r, w)

	username := httpSession.Values["username"].(string)
	locale := conf.Wide.GetUser(username).Locale

	model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
		"ver": conf.WideVersion, "goos": runtime.GOOS, "goarch": runtime.GOARCH, "gover": runtime.Version()}

	t, err := template.ParseFiles("views/about.html")

	if nil != err {
		glog.Error(err)
		http.Error(w, err.Error(), 500)

		return
	}

	t.Execute(w, model)
}

// Main.
func main() {
	runtime.GOMAXPROCS(conf.Wide.MaxProcs)

	initMime()

	defer glog.Flush()

	// IDE
	http.HandleFunc("/", handlerWrapper(indexHandler))
	http.HandleFunc("/start", handlerWrapper(startHandler))
	http.HandleFunc("/about", handlerWrapper(aboutHandler))
	http.HandleFunc("/keyboard_shortcuts", handlerWrapper(keyboardShortcutsHandler))

	// static resources
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
	serveSingle("/favicon.ico", "./static/favicon.ico")

	// workspaces
	for _, user := range conf.Wide.Users {
		http.Handle("/workspace/"+user.Name+"/",
			http.StripPrefix("/workspace/"+user.Name+"/", http.FileServer(http.Dir(user.GetWorkspace()))))
	}

	// session
	http.HandleFunc("/session/ws", handlerWrapper(session.WSHandler))
	http.HandleFunc("/session/save", handlerWrapper(session.SaveContent))

	// run
	http.HandleFunc("/build", handlerWrapper(output.BuildHandler))
	http.HandleFunc("/run", handlerWrapper(output.RunHandler))
	http.HandleFunc("/stop", handlerWrapper(output.StopHandler))
	http.HandleFunc("/go/test", handlerWrapper(output.GoTestHandler))
	http.HandleFunc("/go/get", handlerWrapper(output.GoGetHandler))
	http.HandleFunc("/go/install", handlerWrapper(output.GoInstallHandler))
	http.HandleFunc("/output/ws", handlerWrapper(output.WSHandler))

	// file tree
	http.HandleFunc("/files", handlerWrapper(file.GetFiles))
	http.HandleFunc("/file", handlerWrapper(file.GetFile))
	http.HandleFunc("/file/save", handlerWrapper(file.SaveFile))
	http.HandleFunc("/file/new", handlerWrapper(file.NewFile))
	http.HandleFunc("/file/remove", handlerWrapper(file.RemoveFile))
	http.HandleFunc("/file/rename", handlerWrapper(file.RenameFile))
	http.HandleFunc("/file/search/text", handlerWrapper(file.SearchText))
	http.HandleFunc("/file/find/name", handlerWrapper(file.Find))

	// editor
	http.HandleFunc("/editor/ws", handlerWrapper(editor.WSHandler))
	http.HandleFunc("/go/fmt", handlerWrapper(editor.GoFmtHandler))
	http.HandleFunc("/autocomplete", handlerWrapper(editor.AutocompleteHandler))
	http.HandleFunc("/exprinfo", handlerWrapper(editor.GetExprInfoHandler))
	http.HandleFunc("/find/decl", handlerWrapper(editor.FindDeclarationHandler))
	http.HandleFunc("/find/usages", handlerWrapper(editor.FindUsagesHandler))

	// shell
	http.HandleFunc("/shell/ws", handlerWrapper(shell.WSHandler))
	http.HandleFunc("/shell", handlerWrapper(shell.IndexHandler))

	// notification
	http.HandleFunc("/notification/ws", handlerWrapper(notification.WSHandler))

	// user
	http.HandleFunc("/login", handlerWrapper(session.LoginHandler))
	http.HandleFunc("/logout", handlerWrapper(session.LogoutHandler))
	http.HandleFunc("/signup", handlerWrapper(session.SignUpUser))
	http.HandleFunc("/preference", handlerWrapper(session.PreferenceHandler))

	glog.Infof("Wide is running [%s]", conf.Wide.Server)

	err := http.ListenAndServe(conf.Wide.Server, nil)
	if err != nil {
		glog.Fatal(err)
	}
}

// handlerWrapper wraps the HTTP Handler for some common processes.
//
//  1. panic recover
//  2. request stopwatch
//  3. i18n
func handlerWrapper(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	handler := panicRecover(f)
	handler = stopwatch(handler)
	handler = i18nLoad(handler)

	return handler
}

// i18nLoad wraps the i18n process.
func i18nLoad(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		i18n.Load()

		handler(w, r)
	}
}

// stopwatch wraps the request stopwatch process.
func stopwatch(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		defer func() {
			glog.V(5).Infof("[%s] [%s]", r.RequestURI, time.Since(start))
		}()

		handler(w, r)
	}
}

// panicRecover wraps the panic recover process.
func panicRecover(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		defer util.Recover()

		handler(w, r)
	}
}

// initMime initializes mime types.
//
// We can't get the mime types on some OS (such as Windows XP) by default, so initializes them here.
func initMime() {
	mime.AddExtensionType(".css", "text/css")
	mime.AddExtensionType(".js", "application/x-javascript")
	mime.AddExtensionType(".json", "application/json")
}
<commit_msg>check run<commit_after>// Copyright (c) 2014, B3log
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"flag"
	"html/template"
	"math/rand"
	"mime"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/b3log/wide/conf"
	"github.com/b3log/wide/editor"
	"github.com/b3log/wide/event"
	"github.com/b3log/wide/file"
	"github.com/b3log/wide/i18n"
	"github.com/b3log/wide/notification"
	"github.com/b3log/wide/output"
	"github.com/b3log/wide/session"
	"github.com/b3log/wide/shell"
	"github.com/b3log/wide/util"
	"github.com/golang/glog"
)

// The only one init function in Wide.
func init() {
	confPath := flag.String("conf", "conf/wide.json", "path of wide.json")
	confIP := flag.String("ip", "", "ip to visit")
	confPort := flag.String("port", "", "port to visit")
	confServer := flag.String("server", "", "this will overwrite Wide.Server if specified")
	confChannel := flag.String("channel", "", "this will overwrite Wide.XXXChannel if specified")
	confStat := flag.Bool("stat", false, "whether report statistics periodically")
	confDocker := flag.Bool("docker", false, "whether run in a docker container")

	flag.Set("alsologtostderr", "true")
	flag.Set("stderrthreshold", "INFO")
	flag.Set("v", "3")

	flag.Parse()

	wd := util.OS.Pwd()
	if strings.HasPrefix(wd, os.TempDir()) {
		glog.Error("Wide can't run in the OS' temp directory and it can't run with `go run`")

		os.Exit(-1)
	}

	i18n.Load()

	event.Load()

	conf.Load(*confPath, *confIP, *confPort, *confServer, *confChannel, *confDocker)

	conf.FixedTimeCheckEnv()
	conf.FixedTimeSave()

	session.FixedTimeRelease()

	if *confStat {
		session.FixedTimeReport()
	}
}

// indexHandler handles request of Wide index.
func indexHandler(w http.ResponseWriter, r *http.Request) {
	httpSession, _ := session.HTTPSession.Get(r, "wide-session")

	if httpSession.IsNew {
		http.Redirect(w, r, "/login", http.StatusFound)

		return
	}

	httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
	httpSession.Save(r, w)

	// create a Wide session
	rand.Seed(time.Now().UnixNano())
	sid := strconv.Itoa(rand.Int())
	wideSession := session.WideSessions.New(httpSession, sid)

	username := httpSession.Values["username"].(string)
	user := conf.Wide.GetUser(username)
	if nil == user {
		glog.Warningf("Not found user [%s]", username)

		http.Redirect(w, r, "/login", http.StatusFound)

		return
	}

	locale := user.Locale

	wideSessions := session.WideSessions.GetByUsername(username)

	model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
		"session": wideSession, "latestSessionContent": user.LatestSessionContent,
		"pathSeparator": conf.PathSeparator, "codeMirrorVer": conf.CodeMirrorVer}

	glog.V(3).Infof("User [%s] has [%d] sessions", username, len(wideSessions))

	t, err := template.ParseFiles("views/index.html")

	if nil != err {
		glog.Error(err)
		http.Error(w, err.Error(), 500)

		return
	}

	t.Execute(w, model)
}

// serveSingle registers the handler function for the given pattern and filename.
func serveSingle(pattern string, filename string) {
	http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, filename)
	})
}

// startHandler handles request of start page.
func startHandler(w http.ResponseWriter, r *http.Request) {
	httpSession, _ := session.HTTPSession.Get(r, "wide-session")

	if httpSession.IsNew {
		http.Redirect(w, r, "/login", http.StatusFound)

		return
	}

	httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
	httpSession.Save(r, w)

	username := httpSession.Values["username"].(string)
	locale := conf.Wide.GetUser(username).Locale
	userWorkspace := conf.Wide.GetUserWorkspace(username)

	sid := r.URL.Query()["sid"][0]
	wSession := session.WideSessions.Get(sid)
	if nil == wSession {
		glog.Errorf("Session [%s] not found", sid)
	}

	model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
		"username": username, "workspace": userWorkspace, "ver": conf.WideVersion, "session": wSession}

	t, err := template.ParseFiles("views/start.html")

	if nil != err {
		glog.Error(err)
		http.Error(w, err.Error(), 500)

		return
	}

	t.Execute(w, model)
}

// keyboardShortcutsHandler handles request of keyboard shortcuts page.
func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) {
	httpSession, _ := session.HTTPSession.Get(r, "wide-session")

	if httpSession.IsNew {
		http.Redirect(w, r, "/login", http.StatusFound)

		return
	}

	httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
	httpSession.Save(r, w)

	username := httpSession.Values["username"].(string)
	locale := conf.Wide.GetUser(username).Locale

	model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale}

	t, err := template.ParseFiles("views/keyboard_shortcuts.html")

	if nil != err {
		glog.Error(err)
		http.Error(w, err.Error(), 500)

		return
	}

	t.Execute(w, model)
}

// aboutHandle handles request of about page.
func aboutHandler(w http.ResponseWriter, r *http.Request) {
	httpSession, _ := session.HTTPSession.Get(r, "wide-session")

	if httpSession.IsNew {
		http.Redirect(w, r, "/login", http.StatusFound)

		return
	}

	httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
	httpSession.Save(r, w)

	username := httpSession.Values["username"].(string)
	locale := conf.Wide.GetUser(username).Locale

	model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
		"ver": conf.WideVersion, "goos": runtime.GOOS, "goarch": runtime.GOARCH, "gover": runtime.Version()}

	t, err := template.ParseFiles("views/about.html")

	if nil != err {
		glog.Error(err)
		http.Error(w, err.Error(), 500)

		return
	}

	t.Execute(w, model)
}

// Main.
func main() {
	runtime.GOMAXPROCS(conf.Wide.MaxProcs)

	initMime()

	defer glog.Flush()

	// IDE
	http.HandleFunc("/", handlerWrapper(indexHandler))
	http.HandleFunc("/start", handlerWrapper(startHandler))
	http.HandleFunc("/about", handlerWrapper(aboutHandler))
	http.HandleFunc("/keyboard_shortcuts", handlerWrapper(keyboardShortcutsHandler))

	// static resources
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
	serveSingle("/favicon.ico", "./static/favicon.ico")

	// workspaces
	for _, user := range conf.Wide.Users {
		http.Handle("/workspace/"+user.Name+"/",
			http.StripPrefix("/workspace/"+user.Name+"/", http.FileServer(http.Dir(user.GetWorkspace()))))
	}

	// session
	http.HandleFunc("/session/ws", handlerWrapper(session.WSHandler))
	http.HandleFunc("/session/save", handlerWrapper(session.SaveContent))

	// run
	http.HandleFunc("/build", handlerWrapper(output.BuildHandler))
	http.HandleFunc("/run", handlerWrapper(output.RunHandler))
	http.HandleFunc("/stop", handlerWrapper(output.StopHandler))
	http.HandleFunc("/go/test", handlerWrapper(output.GoTestHandler))
	http.HandleFunc("/go/get", handlerWrapper(output.GoGetHandler))
	http.HandleFunc("/go/install", handlerWrapper(output.GoInstallHandler))
	http.HandleFunc("/output/ws", handlerWrapper(output.WSHandler))

	// file tree
	http.HandleFunc("/files", handlerWrapper(file.GetFiles))
	http.HandleFunc("/file", handlerWrapper(file.GetFile))
	http.HandleFunc("/file/save", handlerWrapper(file.SaveFile))
	http.HandleFunc("/file/new", handlerWrapper(file.NewFile))
	http.HandleFunc("/file/remove", handlerWrapper(file.RemoveFile))
	http.HandleFunc("/file/rename", handlerWrapper(file.RenameFile))
	http.HandleFunc("/file/search/text", handlerWrapper(file.SearchText))
	http.HandleFunc("/file/find/name", handlerWrapper(file.Find))

	// editor
	http.HandleFunc("/editor/ws", handlerWrapper(editor.WSHandler))
	http.HandleFunc("/go/fmt", handlerWrapper(editor.GoFmtHandler))
	http.HandleFunc("/autocomplete", handlerWrapper(editor.AutocompleteHandler))
	http.HandleFunc("/exprinfo", handlerWrapper(editor.GetExprInfoHandler))
	http.HandleFunc("/find/decl", handlerWrapper(editor.FindDeclarationHandler))
	http.HandleFunc("/find/usages", handlerWrapper(editor.FindUsagesHandler))

	// shell
	http.HandleFunc("/shell/ws", handlerWrapper(shell.WSHandler))
	http.HandleFunc("/shell", handlerWrapper(shell.IndexHandler))

	// notification
	http.HandleFunc("/notification/ws", handlerWrapper(notification.WSHandler))

	// user
	http.HandleFunc("/login", handlerWrapper(session.LoginHandler))
	http.HandleFunc("/logout", handlerWrapper(session.LogoutHandler))
	http.HandleFunc("/signup", handlerWrapper(session.SignUpUser))
	http.HandleFunc("/preference", handlerWrapper(session.PreferenceHandler))

	glog.Infof("Wide is running [%s]", conf.Wide.Server)

	err := http.ListenAndServe(conf.Wide.Server, nil)
	if err != nil {
		glog.Fatal(err)
	}
}

// handlerWrapper wraps the HTTP Handler for some common processes.
//
//  1. panic recover
//  2. request stopwatch
//  3. i18n
func handlerWrapper(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	handler := panicRecover(f)
	handler = stopwatch(handler)
	handler = i18nLoad(handler)

	return handler
}

// i18nLoad wraps the i18n process.
func i18nLoad(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		i18n.Load()

		handler(w, r)
	}
}

// stopwatch wraps the request stopwatch process.
func stopwatch(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		defer func() {
			glog.V(5).Infof("[%s] [%s]", r.RequestURI, time.Since(start))
		}()

		handler(w, r)
	}
}

// panicRecover wraps the panic recover process.
func panicRecover(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		defer util.Recover()

		handler(w, r)
	}
}

// initMime initializes mime types.
//
// We can't get the mime types on some OS (such as Windows XP) by default, so initializes them here.
func initMime() {
	mime.AddExtensionType(".css", "text/css")
	mime.AddExtensionType(".js", "application/x-javascript")
	mime.AddExtensionType(".json", "application/json")
}
<|endoftext|>


<commit_before>package main

import (
  "flag"
  "fmt"
)

var cshostPtr *string
var cstokenPtr *string
var csschemePtr *string
var upPtr *bool

func main() {

    cshostPtr = flag.String("cshost", "localhost", "cloushark host")
    cstokenPtr = flag.String("cstoken", "xxxxxxx", "cloushark api token")
    csschemePtr = flag.String("csscheme", "https", "cloushark scheme http|https")
    redisnode := flag.String("redisnode", "127.0.0.1", "Hostname|IP of redis server.  Default localhost")
    redisport := flag.Int("redisport", 6379, "Port of redis server. Default 6379")
    redischannel := flag.String("redischannel", "capture", "Redis channel to subscribe to.  Default capture")

    upPtr = flag.Bool("upload", false, "Upload pcap")
    flag.Parse()

    // Channel for thread sync
    done := make(chan bool)

    go func()  {
        fmt.Println("Starting Redis Thread")
        subToRedis(*redisnode, *redisport, *redischannel)
        done <- true
    }()

    fmt.Println("Exiting?")
    <- done
}
<commit_msg>fix cloudshark typos<commit_after>package main

import (
  "flag"
  "fmt"
)

var cshostPtr *string
var cstokenPtr *string
var csschemePtr *string
var upPtr *bool

func main() {

    cshostPtr = flag.String("cshost", "localhost", "cloudshark host")
    cstokenPtr = flag.String("cstoken", "xxxxxxx", "cloudshark api token")
    csschemePtr = flag.String("csscheme", "https", "cloudshark scheme http|https")
    redisnode := flag.String("redisnode", "127.0.0.1", "Hostname|IP of redis server.  Default localhost")
    redisport := flag.Int("redisport", 6379, "Port of redis server. Default 6379")
    redischannel := flag.String("redischannel", "capture", "Redis channel to subscribe to.  Default capture")

    upPtr = flag.Bool("upload", false, "Upload pcap")
    flag.Parse()

    // Channel for thread sync
    done := make(chan bool)

    go func()  {
        fmt.Println("Starting Redis Thread")
        subToRedis(*redisnode, *redisport, *redischannel)
        done <- true
    }()

    fmt.Println("Exiting?")
    <- done
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net"
	"net/http"
	"net/http/cgi"
	"net/http/fcgi"
	"os"
	"os/exec"
	"os/signal"
	"strings"
)

var (
	socketPath  = flag.String("socketpath", "/var/run/gobalt.socket", "path to listening socket")
	socketUser  = flag.String("socketuser", "www-data", "owner for socket")
	socketGroup = flag.String("socketgroup", "www-data", "owner for socket")
)

type Handler struct {
}

func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	log.Println(req.URL.String())
	url := req.URL
	path := url.Path

	slice := strings.Split(path, "/")
	filepath := strings.Join(slice[4:], "/")
	// We either execute "sh -c something" (when not root),
	// or "su -c something" (when root).
	cgipath := "sh"
	user, ok := req.Header["Cobalt-User"]
	if !ok {
		user = []string{"databox"}
	}

	// Prevent access via /listing
	if slice[3] != "http" && slice[3] != "cgi-bin" {
		http.NotFound(rw, req)
		return
	}

	// If path begins with /http, then serve a listing.
	if strings.HasSuffix(path, "/") && slice[3] == "http" {
		prefix := strings.Join(slice[:4], "/")
		root := http.Dir("/ebs/home/" + user[0] + "/http/")

		staticHandler := http.StripPrefix(prefix, http.FileServer(root))
		staticHandler.ServeHTTP(rw, req)
		return
	}
	// This setup prevents shell injection.
	// Try first /home/cgi-bin, then /home/tool/cgi-bin
	code := `
		if [ -x /home/cgi-bin/"$1" ] && [ ! -d /home/cgi-bin/"$1" ]; then
			cd /home/cgi-bin && /home/cgi-bin/"$1"
		elif [ -x /home/tool/cgi-bin/"$1" ] && [ ! -d /home/tool/cgi-bin/"$1" ]; then
			cd /home/tool/cgi-bin && /home/tool/cgi-bin/"$1"
		else
			P="$1"

			while :
			do
				# echo "Searching for $P/default"
				if [ -x "/home/cgi-bin/$P/default" ]; then
					# echo Found $P/default
					cd /home/cgi-bin && /home/cgi-bin/"$P"/default
					exit 0
				fi
				if [ -x "/home/tool/cgi-bin/$P/default" ]; then
					# echo Found $P/default
					cd /home/tool/cgi-bin && /home/tool/cgi-bin/"$P"/default
					exit 0
				fi
				if [ "$P" == "." ]; then
					break
				fi
				P="$(dirname "$P")"

			done

			echo Status: 404 Not Found
			echo
			echo 404 Not Found
		fi
	`
	cgiargs := []string{"-c", code, user[0], "--", "-", filepath}
	if os.Getuid() == 0 {
		cgipath = "su"
	}
	var err error
	cgipath, err = exec.LookPath(cgipath)
	if err != nil {
		log.Panic(err)
	}
	// on Dir: In the usual case where we're su'ing into a
	// box, setting Dir has no effect because the PAM
	// chroot module changes the current directory
	// (to be / in the box's chrooted environment).
	// on $HOME: The cgi module only sets certain
	// environment variables, and leaves HOME unset.
	// We set the directory within the command.
	handler := &cgi.Handler{
		Path: cgipath,
		Args: cgiargs,
		Env: []string{
			"SCRIPT_NAME=" + path,
			"SCRIPT_FILENAME=" + "/home/cgi-bin/" + filepath,
			"SERVER_SOFTWARE=github.com/scraperwiki/gobalt",
		}}
	handler.ServeHTTP(rw, req)
}

func main() {
	flag.Parse()
	_ = os.Remove(*socketPath)
	l, err := net.Listen("unix", *socketPath)
	if err != nil {
		log.Panic(err)
	}
	defer l.Close()
	err = exec.Command("chown", *socketUser+":"+*socketGroup, *socketPath).Run()
	if err != nil {
		log.Panic(err)
	}
	handler := &Handler{}
	log.Println("Serving on", *socketPath)

	go func() {
		err = fcgi.Serve(l, handler)
		if err != nil {
			log.Panic(err)
		}
	}()

	sig := make(chan os.Signal)
	signal.Notify(sig, os.Interrupt)
	<-sig
}
<commit_msg>'==' is not portable. *sigh*<commit_after>package main

import (
	"flag"
	"log"
	"net"
	"net/http"
	"net/http/cgi"
	"net/http/fcgi"
	"os"
	"os/exec"
	"os/signal"
	"strings"
)

var (
	socketPath  = flag.String("socketpath", "/var/run/gobalt.socket", "path to listening socket")
	socketUser  = flag.String("socketuser", "www-data", "owner for socket")
	socketGroup = flag.String("socketgroup", "www-data", "owner for socket")
)

type Handler struct {
}

func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	log.Println(req.URL.String())
	url := req.URL
	path := url.Path

	slice := strings.Split(path, "/")
	filepath := strings.Join(slice[4:], "/")
	// We either execute "sh -c something" (when not root),
	// or "su -c something" (when root).
	cgipath := "sh"
	user, ok := req.Header["Cobalt-User"]
	if !ok {
		user = []string{"databox"}
	}

	// Prevent access via /listing
	if slice[3] != "http" && slice[3] != "cgi-bin" {
		http.NotFound(rw, req)
		return
	}

	// If path begins with /http, then serve a listing.
	if strings.HasSuffix(path, "/") && slice[3] == "http" {
		prefix := strings.Join(slice[:4], "/")
		root := http.Dir("/ebs/home/" + user[0] + "/http/")

		staticHandler := http.StripPrefix(prefix, http.FileServer(root))
		staticHandler.ServeHTTP(rw, req)
		return
	}
	// This setup prevents shell injection.
	// Try first /home/cgi-bin, then /home/tool/cgi-bin
	code := `
		if [ -x /home/cgi-bin/"$1" ] && [ ! -d /home/cgi-bin/"$1" ]; then
			cd /home/cgi-bin && /home/cgi-bin/"$1"
		elif [ -x /home/tool/cgi-bin/"$1" ] && [ ! -d /home/tool/cgi-bin/"$1" ]; then
			cd /home/tool/cgi-bin && /home/tool/cgi-bin/"$1"
		else
			P="$1"

			while :
			do
				# echo "Searching for $P/default"
				if [ -x "/home/cgi-bin/$P/default" ]; then
					# echo Found $P/default
					cd /home/cgi-bin && /home/cgi-bin/"$P"/default
					exit 0
				fi
				if [ -x "/home/tool/cgi-bin/$P/default" ]; then
					# echo Found $P/default
					cd /home/tool/cgi-bin && /home/tool/cgi-bin/"$P"/default
					exit 0
				fi
				if [ "$P" = "." ]; then
					break
				fi
				P="$(dirname "$P")"

			done

			echo Status: 404 Not Found
			echo
			echo 404 Not Found
		fi
	`
	cgiargs := []string{"-c", code, user[0], "--", "-", filepath}
	if os.Getuid() == 0 {
		cgipath = "su"
	}
	var err error
	cgipath, err = exec.LookPath(cgipath)
	if err != nil {
		log.Panic(err)
	}
	// on Dir: In the usual case where we're su'ing into a
	// box, setting Dir has no effect because the PAM
	// chroot module changes the current directory
	// (to be / in the box's chrooted environment).
	// on $HOME: The cgi module only sets certain
	// environment variables, and leaves HOME unset.
	// We set the directory within the command.
	handler := &cgi.Handler{
		Path: cgipath,
		Args: cgiargs,
		Env: []string{
			"SCRIPT_NAME=" + path,
			"SCRIPT_FILENAME=" + "/home/cgi-bin/" + filepath,
			"SERVER_SOFTWARE=github.com/scraperwiki/gobalt",
		}}
	handler.ServeHTTP(rw, req)
}

func main() {
	flag.Parse()
	_ = os.Remove(*socketPath)
	l, err := net.Listen("unix", *socketPath)
	if err != nil {
		log.Panic(err)
	}
	defer l.Close()
	err = exec.Command("chown", *socketUser+":"+*socketGroup, *socketPath).Run()
	if err != nil {
		log.Panic(err)
	}
	handler := &Handler{}
	log.Println("Serving on", *socketPath)

	go func() {
		err = fcgi.Serve(l, handler)
		if err != nil {
			log.Panic(err)
		}
	}()

	sig := make(chan os.Signal)
	signal.Notify(sig, os.Interrupt)
	<-sig
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
	"os"
)

func main() {
	r := mux.NewRouter()
	r.StrictSlash(false)

	// TODO: route "/public" to "/public/" instead of 404
	r.Methods("GET").PathPrefix("/public/").Handler(http.StripPrefix("/public/", http.FileServer(http.Dir("./public"))))

	// TODO: route "/w" to "/w/" (both of which should route to "/") instead of 404
	r.Methods("GET").PathPrefix("/w/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/w/" {
			http.Redirect(w, r, "..", 301)
			return
		}
		fmt.Fprintln(w, "Welcome to the wiki")
	})

	http.ListenAndServe(os.Getenv("GOOSE_PORT"), r)
}
<commit_msg>Add wiki page retrieval<commit_after>package main

import (
	"fmt"
	"github.com/gorilla/mux"
	"github.com/tummychow/goose/document"
	_ "github.com/tummychow/goose/document/file"
	"net/http"
	"net/url"
	"os"
)

// Initializes the DocumentStore instance that the application will use. If the
// initialization is unsuccessful, the program will exit from this function.
// The backend data must be a URI stored in the GOOSE_BACKEND environment
// variable.
func initializeStore() document.DocumentStore {
	backendURI := os.Getenv("GOOSE_BACKEND")
	if len(backendURI) == 0 {
		fmt.Println("GOOSE_BACKEND not defined")
		os.Exit(1)
	}

	ret, err := document.NewStore(backendURI)
	if err != nil {
		fmt.Printf("Error while initializing GOOSE_BACKEND=%q\n%v\n", backendURI, err)
		os.Exit(1)
	}
	return ret
}

func main() {
	masterStore := initializeStore()
	defer masterStore.Close()
	_, err := masterStore.Get("/foo/bar")
	if _, ok := err.(document.DocumentNotFoundError); ok {
		masterStore.Update("/foo/bar", "Welcome to the page foo bar")
	}

	r := mux.NewRouter()
	r.StrictSlash(false)

	// TODO: route "/public" to "/public/" instead of 404
	r.Methods("GET").PathPrefix("/public/").Handler(http.StripPrefix("/public/", http.FileServer(http.Dir("./public"))))

	// TODO: route "/w" to "/w/" (both of which should route to "/") instead of 404
	r.Methods("GET").PathPrefix("/w/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/w/" {
			http.Redirect(w, r, "..", 301)
			return
		}

		targetName, err := url.QueryUnescape(r.URL.Path[2:])
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		store, err := masterStore.Copy()
		if err != nil {
			http.Error(w, "Could not copy DocumentStore", 500)
			return
		}
		defer store.Close()

		doc, err := store.Get(targetName)
		if _, ok := err.(document.DocumentNotFoundError); ok {
			w.WriteHeader(404)
			fmt.Fprintf(w, "You requested page %q, but it doesn't exist", targetName)
			return
		} else if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		fmt.Fprintf(w, "Requested\n%q\n\nName\n%v\n\nTimestamp\n%v\n\nContents\n%v", targetName, doc.Name, doc.Timestamp.Local().Format("Jan 2 2006 15:04:05"), doc.Content)
	})

	http.ListenAndServe(os.Getenv("GOOSE_PORT"), r)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"runtime"
)

const visibleRows = 20

func readAllCandidates(r io.Reader) []Candidate {
	scanner := bufio.NewScanner(r)
	var lines []Candidate
	for scanner.Scan() {
		lines = append(lines, NewCandidate(scanner.Text()))
	}
	return lines
}

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

	picker := NewPicker(readAllCandidates(os.Stdin), visibleRows)

	tty := NewTTY()
	defer tty.Restore()

	terminal := NewTerminal(tty)
	terminal.ConfigTerminal()

	// XXX: can we grab this from the view?
	query := ""
	view := picker.Answer(query)
	terminal.MakeRoom(view.Height)
	for {
		terminal.Draw(view)

		// XXX: check this
		key := tty.ReadByte()
		switch key {
		case Ctrl_N:
			view.Down()
		case Ctrl_P:
			view.Up()
		case Backspace:
			if len(query) > 0 {
				query = query[:len(query)-1]
			}
			view = picker.Answer(query)

		// XXX: check this! Especially how to read from tty
		case LF:
			terminal.MoveBottom()
			fmt.Println(view.Selected())
			return
		case Ctrl_U, Ctrl_W:
			query = ""
			view = picker.Answer(query)
		default:
			query = query + string(key)
			view = picker.Answer(query)
		}
	}
}
<commit_msg>Grab query from previous view<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"runtime"
)

const visibleRows = 20

func appendChar(s string, b byte) string {
	return s + string(b)
}

func backspace(s string) string {
	if l := len(s); l > 0 {
		return s[:l-1]
	}
	return s
}

func readAllCandidates(r io.Reader) []Candidate {
	scanner := bufio.NewScanner(r)
	var lines []Candidate
	for scanner.Scan() {
		lines = append(lines, NewCandidate(scanner.Text()))
	}
	return lines
}

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

	picker := NewPicker(readAllCandidates(os.Stdin), visibleRows)

	tty := NewTTY()
	defer tty.Restore()

	terminal := NewTerminal(tty)
	terminal.ConfigTerminal()

	view := picker.Answer("")
	terminal.MakeRoom(view.Height)
	for {
		terminal.Draw(view)

		// XXX: check this
		key := tty.ReadByte()
		switch key {
		case Ctrl_N:
			view.Down()
		case Ctrl_P:
			view.Up()
		case Backspace:
			view = picker.Answer(backspace(view.Query))

		// XXX: check this! Especially how to read from tty
		case LF:
			terminal.MoveBottom()
			fmt.Println(view.Selected())
			return
		case Ctrl_U, Ctrl_W:
			view = picker.Answer("")
		default:
			view = picker.Answer(appendChar(view.Query, key))
		}
	}
}
<|endoftext|>