<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/kr/pretty"
	"github.com/pkg/errors"
)

type StrategyCommon struct {
	System
	Logger
	ConfigGetter
	Downloader
	Runner
}

func (sc *StrategyCommon) Templater(version string, archMap map[string]string, system System) Templater {
	archKey := fmt.Sprintf("%s_%s", system.OS(), system.Arch())
	sc.Debugf("Arch key: %s", archKey)
	value := archMap[archKey]
	return Templater{
		Version:    version,
		OS:         system.OS(),
		Arch:       system.Arch(),
		MappedArch: value,
	}
}

type Strategy interface {
	Run([]string) error
}

type DockerData struct {
	Name        string
	Desc        string
	Version     string            `yaml:"version"`
	Image       string            `yaml:"image"`
	MountPwd    bool              `yaml:"mount_pwd"`
	DockerConn  bool              `yaml:"docker_conn"`
	Interactive bool              `yaml:"interactive"`
	Terminal    string            `yaml:"terminal"`
	PidHost     bool              `yaml:"pid_host"`
	ArchMap     map[string]string `yaml:"arch_map"`
}

type DockerStrategy struct {
	*StrategyCommon
	Data DockerData
}

type BinaryData struct {
	Name    string
	Desc    string
	Version string            `yaml:"version"`
	BaseUrl string            `yaml:"base_url"`
	ArchMap map[string]string `yaml:"arch_map"`
}

type BinaryStrategy struct {
	*StrategyCommon
	Data BinaryData
}

func (ds DockerStrategy) Run(extraArgs []string) error {
	// skip if docker not found
	if !ds.CheckCommand("docker", []string{"version"}) {
		return &SkipError{"docker not available"}
	}

	temp := ds.Templater(ds.Data.Version, ds.Data.ArchMap, ds.System)
	ds.Debugf("templater: %# v", pretty.Formatter(temp))

	image, err := temp.Template(ds.Data.Image)
	if err != nil {
		return errors.Wrap(err, "unable to template image name")
	}

	// TODO: add flag to force pulling image again
	// err = ds.PullDockerImage(image)
	// if err != nil {
	// 	return errors.Wrap(err, "can't pull image")
	// }

	args := []string{"run"}
	if ds.Data.Interactive {
		args = append(args, "-i")
	}
	// TODO: prompt the user for permission to do more invasive docker binding
	if ds.Data.DockerConn {
		args = append(args, "-v", "/var/run/docker.sock:/var/run/docker.sock")
	}
	if ds.Data.PidHost {
		args = append(args, "--pid", "host")
	}
	if ds.Data.Terminal != "" {
		// TODO: support 'auto' mode that autodetects if tty is present
		if ds.Data.Terminal == "always" {
			args = append(args, "-t")
		}
	}
	args = append(args, "--rm", image)
	args = append(args, extraArgs...)

	ds.RunCommand("docker", args)
	if err != nil {
		return errors.Wrap(err, "can't run image")
	}

	return nil
}

func (bs BinaryStrategy) DownloadPath() (string, error) {
	var downloadPath string
	if configDownloadPath, err := bs.Get("binary.download"); err == nil && len(configDownloadPath) > 0 {
		downloadPath = configDownloadPath
	} else if xdgDataHome := os.Getenv("XDG_DATA_HOME"); len(xdgDataHome) > 0 {
		downloadPath = filepath.Join(xdgDataHome, "holen", "bin")
	} else {
		var home string
		if home = os.Getenv("HOME"); len(home) == 0 {
			return "", fmt.Errorf("$HOME environment variable not found")
		}
		downloadPath = filepath.Join(home, ".local", "share", "holen", "bin")
	}
	os.MkdirAll(downloadPath, 0755)

	return downloadPath, nil
}

func (bs BinaryStrategy) Run(args []string) error {
	temp := bs.Templater(bs.Data.Version, bs.Data.ArchMap, bs.System)
	bs.Debugf("templater: %# v", pretty.Formatter(temp))

	url, err := temp.Template(bs.Data.BaseUrl)
	if err != nil {
		return errors.Wrap(err, "unable to template url")
	}

	downloadPath, err := bs.DownloadPath()
	if err != nil {
		return errors.Wrap(err, "unable to find download path")
	}
	localPath := filepath.Join(downloadPath, fmt.Sprintf("%s--%s", bs.Data.Name, bs.Data.Version))

	if _, err := os.Stat(localPath); os.IsNotExist(err) {
		err = bs.DownloadFile(url, localPath)
		if err != nil {
			return errors.Wrap(err, "can't download binary")
		}

		err = os.Chmod(localPath, 0755)
		if err != nil {
			return errors.Wrap(err, "unable to chmod binary")
		}
	}

	// TODO: checksum the binary

	err = bs.RunCommand(localPath, args)
	if err != nil {
		return errors.Wrap(err, "can't run binary")
	}

	return nil
}
<commit_msg>add debug message when skipping<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/kr/pretty"
	"github.com/pkg/errors"
)

type StrategyCommon struct {
	System
	Logger
	ConfigGetter
	Downloader
	Runner
}

func (sc *StrategyCommon) Templater(version string, archMap map[string]string, system System) Templater {
	archKey := fmt.Sprintf("%s_%s", system.OS(), system.Arch())
	sc.Debugf("Arch key: %s", archKey)
	value := archMap[archKey]
	return Templater{
		Version:    version,
		OS:         system.OS(),
		Arch:       system.Arch(),
		MappedArch: value,
	}
}

type Strategy interface {
	Run([]string) error
}

type DockerData struct {
	Name        string
	Desc        string
	Version     string            `yaml:"version"`
	Image       string            `yaml:"image"`
	MountPwd    bool              `yaml:"mount_pwd"`
	DockerConn  bool              `yaml:"docker_conn"`
	Interactive bool              `yaml:"interactive"`
	Terminal    string            `yaml:"terminal"`
	PidHost     bool              `yaml:"pid_host"`
	ArchMap     map[string]string `yaml:"arch_map"`
}

type DockerStrategy struct {
	*StrategyCommon
	Data DockerData
}

type BinaryData struct {
	Name    string
	Desc    string
	Version string            `yaml:"version"`
	BaseUrl string            `yaml:"base_url"`
	ArchMap map[string]string `yaml:"arch_map"`
}

type BinaryStrategy struct {
	*StrategyCommon
	Data BinaryData
}

func (ds DockerStrategy) Run(extraArgs []string) error {
	// skip if docker not found
	if !ds.CheckCommand("docker", []string{"version"}) {
		ds.Debugf("skipping, docker not available")
		return &SkipError{"docker not available"}
	}

	temp := ds.Templater(ds.Data.Version, ds.Data.ArchMap, ds.System)
	ds.Debugf("templater: %# v", pretty.Formatter(temp))

	image, err := temp.Template(ds.Data.Image)
	if err != nil {
		return errors.Wrap(err, "unable to template image name")
	}

	// TODO: add flag to force pulling image again
	// err = ds.PullDockerImage(image)
	// if err != nil {
	// 	return errors.Wrap(err, "can't pull image")
	// }

	args := []string{"run"}
	if ds.Data.Interactive {
		args = append(args, "-i")
	}
	// TODO: prompt the user for permission to do more invasive docker binding
	if ds.Data.DockerConn {
		args = append(args, "-v", "/var/run/docker.sock:/var/run/docker.sock")
	}
	if ds.Data.PidHost {
		args = append(args, "--pid", "host")
	}
	if ds.Data.Terminal != "" {
		// TODO: support 'auto' mode that autodetects if tty is present
		if ds.Data.Terminal == "always" {
			args = append(args, "-t")
		}
	}
	args = append(args, "--rm", image)
	args = append(args, extraArgs...)

	ds.RunCommand("docker", args)
	if err != nil {
		return errors.Wrap(err, "can't run image")
	}

	return nil
}

func (bs BinaryStrategy) DownloadPath() (string, error) {
	var downloadPath string
	if configDownloadPath, err := bs.Get("binary.download"); err == nil && len(configDownloadPath) > 0 {
		downloadPath = configDownloadPath
	} else if xdgDataHome := os.Getenv("XDG_DATA_HOME"); len(xdgDataHome) > 0 {
		downloadPath = filepath.Join(xdgDataHome, "holen", "bin")
	} else {
		var home string
		if home = os.Getenv("HOME"); len(home) == 0 {
			return "", fmt.Errorf("$HOME environment variable not found")
		}
		downloadPath = filepath.Join(home, ".local", "share", "holen", "bin")
	}
	os.MkdirAll(downloadPath, 0755)

	return downloadPath, nil
}

func (bs BinaryStrategy) Run(args []string) error {
	temp := bs.Templater(bs.Data.Version, bs.Data.ArchMap, bs.System)
	bs.Debugf("templater: %# v", pretty.Formatter(temp))

	url, err := temp.Template(bs.Data.BaseUrl)
	if err != nil {
		return errors.Wrap(err, "unable to template url")
	}

	downloadPath, err := bs.DownloadPath()
	if err != nil {
		return errors.Wrap(err, "unable to find download path")
	}
	localPath := filepath.Join(downloadPath, fmt.Sprintf("%s--%s", bs.Data.Name, bs.Data.Version))

	if _, err := os.Stat(localPath); os.IsNotExist(err) {
		err = bs.DownloadFile(url, localPath)
		if err != nil {
			return errors.Wrap(err, "can't download binary")
		}

		err = os.Chmod(localPath, 0755)
		if err != nil {
			return errors.Wrap(err, "unable to chmod binary")
		}
	}

	// TODO: checksum the binary

	err = bs.RunCommand(localPath, args)
	if err != nil {
		return errors.Wrap(err, "can't run binary")
	}

	return nil
}
<|endoftext|>


<commit_before>package terp

import (
	. "fmt"
	"log"
	"net/http"
	"strconv"
)

var Builtins map[string]Command = make(map[string]Command, 0)
var TBuiltins map[string]TCommand = make(map[string]TCommand, 0)

func (fr *Frame) initTBuiltins() {
	TBuiltins["+"] = MkChainingBinaryFlopTCmd(fr, 0.0, func(a, b float64) float64 { return a + b })
	TBuiltins["*"] = MkChainingBinaryFlopTCmd(fr, 1.0, func(a, b float64) float64 { return a * b })
	TBuiltins["-"] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return a - b })
	TBuiltins["/"] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return a / b })

	TBuiltins["=="] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return ToFloat(a == b) })
	TBuiltins["!="] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return ToFloat(a != b) })
	TBuiltins["<"] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return ToFloat(a < b) })
	TBuiltins["<="] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return ToFloat(a <= b) })
	TBuiltins[">"] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return ToFloat(a > b) })
	TBuiltins[">="] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return ToFloat(a >= b) })
	TBuiltins["must"] = tcmdMust

	TBuiltins["if"] = tcmdIf
	TBuiltins["get"] = newcmd(cmdGet)
	TBuiltins["set"] = newcmd(cmdSet)
	TBuiltins["puts"] = newcmd(cmdPuts)
	TBuiltins["proc"] = newcmd(cmdProc)
	TBuiltins["ls"] = newcmd(cmdLs)
	TBuiltins["slen"] = newcmd(cmdSLen)
	TBuiltins["llen"] = newcmd(cmdLLen)
	TBuiltins["list"] = newcmd(cmdList)
	TBuiltins["sat"] = newcmd(cmdSAt)
	TBuiltins["lat"] = newcmd(cmdLAt)
	TBuiltins["nil"] = newcmd(cmdNil)
	TBuiltins["http_handler"] = newcmd(cmdHttpHandler)
}
type BinaryFlop func(a, b float64) float64

func MkBinaryFlopCmd(fr *Frame, flop BinaryFlop) Command {
	return func(fr *Frame, argv List) Any {
		a, b := Argv2(argv)
		return flop(ToFloat(a), ToFloat(b))
	}
}

func MkBinaryFlopTCmd(fr *Frame, flop BinaryFlop) TCommand {
	return func(fr *Frame, argv []T) T {
		a, b := TArgv2(argv)
		return MkTf(flop(a.Float(), b.Float()))
	}
}

func MkChainingBinaryFlopCmd(fr *Frame, starter float64, flop BinaryFlop) Command {
	return func(fr *Frame, argv List) Any {
		z := starter // Be sure not to modify starter!  It is captured.
		for _, a := range argv[1:] {
			z = flop(z, ToFloat(a))
		}
		return z
	}
}

func MkChainingBinaryFlopTCmd(fr *Frame, starter float64, flop BinaryFlop) TCommand {
	return func(fr *Frame, argv []T) T {
		z := starter // Be sure not to modify starter!  It is captured.
		for _, a := range argv[1:] {
			z = flop(z, a.Float())
		}
		return MkTf(z)
	}
}

func TTruth(a T) bool {
	switch x := a.(type) {
	case Tf:
		return x.f != 0
	case Ts:
		return len(x.s) > 0
	case Tl:
		return len(x.l) > 0
	}
	// To Do: Value(nil) Value(false) are false.
	return true
}
func Truth(a Any) bool {
	switch x := a.(type) {
	case float64:
		return x != 0
	case float32:
		return x != 0
	case uint64:
		return x != 0
	case int64:
		return x != 0
	case int:
		return x != 0
	case uint:
		return x != 0
	case byte:
		return x != 0
	case rune:
		return x != 0
	case bool:
		return x
	case string:
		return len(x) != 0
	case List:
		return len(x) != 0
	case Dict:
		return len(x) != 0
	}
	panic(Sprintf("Case not supported in Truth(): %#v", a))
}

func ToFloat(a Any) float64 {
	switch x := a.(type) {
	case float64:
		return x
	case float32:
		return float64(x)
	case uint64:
		return float64(x)
	case int64:
		return float64(x)
	case int:
		return float64(x)
	case uint:
		return float64(x)
	case byte:
		return float64(x)
	case rune:
		return float64(x)
	case bool:
		log.Printf("BOOL %v", x)
		if x {
			return 1.0
		}
		return 0.0
	}
	f, err := strconv.ParseFloat(Str(a), 64)
	if err != nil {
		panic(Sprintf("Cannot Parse Float: %q", a))
	}
	return f
}

func Argv1(argv List) Any {
	if len(argv) != 1+1 {
		panic(Sprintf("Expected 1 arguments, but got %#v", argv))
	}
	return argv[1]
}

func Argv2(argv List) (Any, Any) {
	if len(argv) != 2+1 {
		panic(Sprintf("Expected 2 arguments, but got %#v", argv))
	}
	return argv[1], argv[2]
}

func Argv3(argv List) (Any, Any, Any) {
	if len(argv) != 3+1 {
		panic(Sprintf("Expected 3 arguments, but got %#v", argv))
	}
	return argv[1], argv[2], argv[3]
}

func TArgv1(argv []T) T {
	if len(argv) != 1+1 {
		panic(Sprintf("Expected 1 arguments, but got argv=%#v", argv))
	}
	return argv[1]
}

func TArgv2(argv []T) (T, T) {
	if len(argv) != 2+1 {
		panic(Sprintf("Expected 2 arguments, but got argv=%#v", argv))
	}
	return argv[1], argv[2]
}

func TArgv3(argv []T) (T, T, T) {
	if len(argv) != 3+1 {
		panic(Sprintf("Expected 3 arguments, but got argv=%#v", argv))
	}
	return argv[1], argv[2], argv[3]
}

func cmdMust(fr *Frame, argv List) Any {
	x := Str(argv[1])
	y := Str(argv[2])

	if x == y {
		return argv[2]
	}

	panic("FAILED: must: " + Repr(argv) + " -- x: " + x + " -- y: " + y)
}
func tcmdMust(fr *Frame, argv []T) T {
	xx, yy := TArgv2(argv)
	x := xx.String()
	y := yy.String()

	if x == y {
		return argv[2]
	}

	panic("FAILED: must: " + Repr(argv) + " -- x: " + x + " -- y: " + y)
}

func tcmdIf(fr *Frame, argv []T) T {
	if len(argv) < 3 {
		panic(Sprintf("Too few arguments for if: %#v", argv))
	}
	var cond, yes, no T

	switch len(argv) {
	case 5:
		if argv[3].String() != "else" {
			panic(Sprintf("Expected 'else' at argv[3]: %#v", argv))
		}
		cond, yes, no = argv[1], argv[2], argv[4]
	case 3:
		cond, yes = argv[1], argv[2]
	default:
		panic(Sprintf("Wrong len(argv) for if: %#v", argv))
	}

	if TTruth(fr.TEval(cond)) {
		return fr.TEval(yes)
	}

	if no != nil {
		return fr.TEval(no)
	}

	return MkTs("")
}

func cmdIf(fr *Frame, argv List) Any {
	if len(argv) < 3+1 {
		panic(Sprintf("Too few arguments for if: %#v", argv))
	}
	var cond, yes, no Any

	switch len(argv) {
	case 5:
		if argv[3] != "else" {
			panic(Sprintf("Expected 'else' at argv[3]: %#v", argv))
		}
		cond, yes, no = argv[1], argv[2], argv[4]
	case 3:
		cond, yes = argv[1], argv[2]
	default:
		panic(Sprintf("Wrong len(argv) for if: %#v", argv))
	}

	if Truth(fr.Eval(cond)) {
		return fr.Eval(yes)
	}

	if no != nil {
		return fr.Eval(no)
	}

	return nil
}

func cmdGet(fr *Frame, argv List) Any {
	name := Argv1(argv)
	return fr.GetVar(Str(name))
}

func cmdSet(fr *Frame, argv List) Any {
	name, x := Argv2(argv)
	fr.SetVar(Str(name), x)
	return x
}

func cmdPuts(fr *Frame, argv List) Any {
	out := Argv1(argv)
	Println(out)
	return nil
}

func cmdProc(fr *Frame, argv List) Any {
	// This is an old cmdProc that installs a new TCommand functions.
	// It's an ugly mix of old & new.   FIXME
	name, aa, body := Argv3(argv)
	alist := ParseList(aa)
	astrs := make([]string, len(alist))
	for i, arg := range alist {
		astr := Str(arg)
		if !IsLocal(astr) {
			panic(Sprintf("Cannot use nonlocal name %q for argument in proc", arg))
		}
		astrs[i] = astr
	}
	n := len(alist) + 1

	tcmd := func(fr2 *Frame, argv2 []T) T {
		if argv2 == nil {
			// Debug Data, if invoked with nil argv2.
			return MkTl(newlist(argv))
		}
		if len(argv2) != n {
			panic(Sprintf("Proc %q expects args %#v but got %#v", name, aa, argv2))
		}
		fr3 := fr2.NewFrame()
		for i, arg := range astrs {
			fr3.SetVar(arg, old(argv2[i+1]))
		}
		return fr3.TEval(new(body))
	}

	fr.G.TCmds[Str(name)] = tcmd
	return nil
}

func cmdLs(fr *Frame, argv List) Any {
	name := Argv1(argv)
	fn := fr.G.Cmds[Str(name)]
	return fn(fr, nil)
}

func cmdSLen(fr *Frame, argv List) Any {
	a := Argv1(argv)
	return float64(len(Str(a)))
}

func cmdLLen(fr *Frame, argv List) Any {
	a := Argv1(argv)
	return float64(len(ParseList(a)))
}

func cmdList(fr *Frame, argv List) Any {
	return argv[1:]
}

func cmdLAt(fr *Frame, argv List) Any {
	v, j := Argv2(argv)
	f := ToFloat(j)
	i := int(f)
	return ParseList(v)[i]
}

func cmdSAt(fr *Frame, argv List) Any {
	s, j := Argv2(argv)
	f := ToFloat(j)
	i := int(f)
	return Str(s)[i : i+1]
}

func cmdNil(fr *Frame, argv List) Any { // TODO
	return http.Handler(nil)
}

func cmdHttpHandler(fr *Frame, argv List) Any {
	return func (w http.ResponseWriter, r *http.Request) {
		v := make(List, len(argv)-1)
		copy(v, argv[1:])
		v = LAppend(v, w)
		v = LAppend(v, r)
		_ = fr.Apply(v)
	}
}

<commit_msg>replace cmdPuts with tcmdPuts.<commit_after>package terp

import (
	. "fmt"
	"log"
	"net/http"
	"strconv"
)

var Builtins map[string]Command = make(map[string]Command, 0)
var TBuiltins map[string]TCommand = make(map[string]TCommand, 0)

func (fr *Frame) initTBuiltins() {
	TBuiltins["+"] = MkChainingBinaryFlopTCmd(fr, 0.0, func(a, b float64) float64 { return a + b })
	TBuiltins["*"] = MkChainingBinaryFlopTCmd(fr, 1.0, func(a, b float64) float64 { return a * b })
	TBuiltins["-"] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return a - b })
	TBuiltins["/"] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return a / b })

	TBuiltins["=="] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return ToFloat(a == b) })
	TBuiltins["!="] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return ToFloat(a != b) })
	TBuiltins["<"] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return ToFloat(a < b) })
	TBuiltins["<="] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return ToFloat(a <= b) })
	TBuiltins[">"] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return ToFloat(a > b) })
	TBuiltins[">="] = MkBinaryFlopTCmd(fr, func(a, b float64) float64 { return ToFloat(a >= b) })
	TBuiltins["must"] = tcmdMust

	TBuiltins["if"] = tcmdIf
	TBuiltins["get"] = newcmd(cmdGet)
	TBuiltins["set"] = newcmd(cmdSet)
	TBuiltins["puts"] = tcmdPuts
	TBuiltins["proc"] = newcmd(cmdProc)
	TBuiltins["ls"] = newcmd(cmdLs)
	TBuiltins["slen"] = newcmd(cmdSLen)
	TBuiltins["llen"] = newcmd(cmdLLen)
	TBuiltins["list"] = newcmd(cmdList)
	TBuiltins["sat"] = newcmd(cmdSAt)
	TBuiltins["lat"] = newcmd(cmdLAt)
	TBuiltins["nil"] = newcmd(cmdNil)
	TBuiltins["http_handler"] = newcmd(cmdHttpHandler)
}
type BinaryFlop func(a, b float64) float64

func MkBinaryFlopCmd(fr *Frame, flop BinaryFlop) Command {
	return func(fr *Frame, argv List) Any {
		a, b := Argv2(argv)
		return flop(ToFloat(a), ToFloat(b))
	}
}

func MkBinaryFlopTCmd(fr *Frame, flop BinaryFlop) TCommand {
	return func(fr *Frame, argv []T) T {
		a, b := TArgv2(argv)
		return MkTf(flop(a.Float(), b.Float()))
	}
}

func MkChainingBinaryFlopCmd(fr *Frame, starter float64, flop BinaryFlop) Command {
	return func(fr *Frame, argv List) Any {
		z := starter // Be sure not to modify starter!  It is captured.
		for _, a := range argv[1:] {
			z = flop(z, ToFloat(a))
		}
		return z
	}
}

func MkChainingBinaryFlopTCmd(fr *Frame, starter float64, flop BinaryFlop) TCommand {
	return func(fr *Frame, argv []T) T {
		z := starter // Be sure not to modify starter!  It is captured.
		for _, a := range argv[1:] {
			z = flop(z, a.Float())
		}
		return MkTf(z)
	}
}

func TTruth(a T) bool {
	switch x := a.(type) {
	case Tf:
		return x.f != 0
	case Ts:
		return len(x.s) > 0
	case Tl:
		return len(x.l) > 0
	}
	// To Do: Value(nil) Value(false) are false.
	return true
}
func Truth(a Any) bool {
	switch x := a.(type) {
	case float64:
		return x != 0
	case float32:
		return x != 0
	case uint64:
		return x != 0
	case int64:
		return x != 0
	case int:
		return x != 0
	case uint:
		return x != 0
	case byte:
		return x != 0
	case rune:
		return x != 0
	case bool:
		return x
	case string:
		return len(x) != 0
	case List:
		return len(x) != 0
	case Dict:
		return len(x) != 0
	}
	panic(Sprintf("Case not supported in Truth(): %#v", a))
}

func ToFloat(a Any) float64 {
	switch x := a.(type) {
	case float64:
		return x
	case float32:
		return float64(x)
	case uint64:
		return float64(x)
	case int64:
		return float64(x)
	case int:
		return float64(x)
	case uint:
		return float64(x)
	case byte:
		return float64(x)
	case rune:
		return float64(x)
	case bool:
		log.Printf("BOOL %v", x)
		if x {
			return 1.0
		}
		return 0.0
	}
	f, err := strconv.ParseFloat(Str(a), 64)
	if err != nil {
		panic(Sprintf("Cannot Parse Float: %q", a))
	}
	return f
}

func Argv1(argv List) Any {
	if len(argv) != 1+1 {
		panic(Sprintf("Expected 1 arguments, but got %#v", argv))
	}
	return argv[1]
}

func Argv2(argv List) (Any, Any) {
	if len(argv) != 2+1 {
		panic(Sprintf("Expected 2 arguments, but got %#v", argv))
	}
	return argv[1], argv[2]
}

func Argv3(argv List) (Any, Any, Any) {
	if len(argv) != 3+1 {
		panic(Sprintf("Expected 3 arguments, but got %#v", argv))
	}
	return argv[1], argv[2], argv[3]
}

func TArgv1(argv []T) T {
	if len(argv) != 1+1 {
		panic(Sprintf("Expected 1 arguments, but got argv=%#v", argv))
	}
	return argv[1]
}

func TArgv2(argv []T) (T, T) {
	if len(argv) != 2+1 {
		panic(Sprintf("Expected 2 arguments, but got argv=%#v", argv))
	}
	return argv[1], argv[2]
}

func TArgv3(argv []T) (T, T, T) {
	if len(argv) != 3+1 {
		panic(Sprintf("Expected 3 arguments, but got argv=%#v", argv))
	}
	return argv[1], argv[2], argv[3]
}

func cmdMust(fr *Frame, argv List) Any {
	x := Str(argv[1])
	y := Str(argv[2])

	if x == y {
		return argv[2]
	}

	panic("FAILED: must: " + Repr(argv) + " -- x: " + x + " -- y: " + y)
}
func tcmdMust(fr *Frame, argv []T) T {
	xx, yy := TArgv2(argv)
	x := xx.String()
	y := yy.String()

	if x == y {
		return argv[2]
	}

	panic("FAILED: must: " + Repr(argv) + " -- x: " + x + " -- y: " + y)
}

func tcmdIf(fr *Frame, argv []T) T {
	if len(argv) < 3 {
		panic(Sprintf("Too few arguments for if: %#v", argv))
	}
	var cond, yes, no T

	switch len(argv) {
	case 5:
		if argv[3].String() != "else" {
			panic(Sprintf("Expected 'else' at argv[3]: %#v", argv))
		}
		cond, yes, no = argv[1], argv[2], argv[4]
	case 3:
		cond, yes = argv[1], argv[2]
	default:
		panic(Sprintf("Wrong len(argv) for if: %#v", argv))
	}

	if TTruth(fr.TEval(cond)) {
		return fr.TEval(yes)
	}

	if no != nil {
		return fr.TEval(no)
	}

	return MkTs("")
}

func cmdIf(fr *Frame, argv List) Any {
	if len(argv) < 3+1 {
		panic(Sprintf("Too few arguments for if: %#v", argv))
	}
	var cond, yes, no Any

	switch len(argv) {
	case 5:
		if argv[3] != "else" {
			panic(Sprintf("Expected 'else' at argv[3]: %#v", argv))
		}
		cond, yes, no = argv[1], argv[2], argv[4]
	case 3:
		cond, yes = argv[1], argv[2]
	default:
		panic(Sprintf("Wrong len(argv) for if: %#v", argv))
	}

	if Truth(fr.Eval(cond)) {
		return fr.Eval(yes)
	}

	if no != nil {
		return fr.Eval(no)
	}

	return nil
}

func cmdGet(fr *Frame, argv List) Any {
	name := Argv1(argv)
	return fr.GetVar(Str(name))
}

func cmdSet(fr *Frame, argv List) Any {
	name, x := Argv2(argv)
	fr.SetVar(Str(name), x)
	return x
}

func tcmdPuts(fr *Frame, argv []T) T {
	if len (argv) < 2 {
		panic(Sprintf("Too few arguments for puts: %#v", argv))
	}

	Println(argv[1])
	return MkTs("")
}

func cmdProc(fr *Frame, argv List) Any {
	// This is an old cmdProc that installs a new TCommand functions.
	// It's an ugly mix of old & new.   FIXME
	name, aa, body := Argv3(argv)
	alist := ParseList(aa)
	astrs := make([]string, len(alist))
	for i, arg := range alist {
		astr := Str(arg)
		if !IsLocal(astr) {
			panic(Sprintf("Cannot use nonlocal name %q for argument in proc", arg))
		}
		astrs[i] = astr
	}
	n := len(alist) + 1

	tcmd := func(fr2 *Frame, argv2 []T) T {
		if argv2 == nil {
			// Debug Data, if invoked with nil argv2.
			return MkTl(newlist(argv))
		}
		if len(argv2) != n {
			panic(Sprintf("Proc %q expects args %#v but got %#v", name, aa, argv2))
		}
		fr3 := fr2.NewFrame()
		for i, arg := range astrs {
			fr3.SetVar(arg, old(argv2[i+1]))
		}
		return fr3.TEval(new(body))
	}

	fr.G.TCmds[Str(name)] = tcmd
	return nil
}

func cmdLs(fr *Frame, argv List) Any {
	name := Argv1(argv)
	fn := fr.G.Cmds[Str(name)]
	return fn(fr, nil)
}

func cmdSLen(fr *Frame, argv List) Any {
	a := Argv1(argv)
	return float64(len(Str(a)))
}

func cmdLLen(fr *Frame, argv List) Any {
	a := Argv1(argv)
	return float64(len(ParseList(a)))
}

func cmdList(fr *Frame, argv List) Any {
	return argv[1:]
}

func cmdLAt(fr *Frame, argv List) Any {
	v, j := Argv2(argv)
	f := ToFloat(j)
	i := int(f)
	return ParseList(v)[i]
}

func cmdSAt(fr *Frame, argv List) Any {
	s, j := Argv2(argv)
	f := ToFloat(j)
	i := int(f)
	return Str(s)[i : i+1]
}

func cmdNil(fr *Frame, argv List) Any { // TODO
	return http.Handler(nil)
}

func cmdHttpHandler(fr *Frame, argv List) Any {
	return func (w http.ResponseWriter, r *http.Request) {
		v := make(List, len(argv)-1)
		copy(v, argv[1:])
		v = LAppend(v, w)
		v = LAppend(v, r)
		_ = fr.Apply(v)
	}
}

<|endoftext|>


<commit_before>package main

import (
	"errors"
	"io"
	"os"
	"net/url"

	"github.com/coreos/go-systemd/unit"
	"github.com/LEW21/siren/imagectl"
)

func Pull(uri, tag string, writer io.Writer) (image imagectl.Image, ret_tag string, ok bool) {
	EnsureSirenDirExists()

	defer func(){
		if r := recover(); r != nil {
			ok = false
		}
	}()

	var u *url.URL
	func(){
		task := NewTask(writer, "Parsing URI"); defer task.Finish()
		var err error
		u, err = url.Parse(uri)
		task.Require(err)
	}()

	fragment := u.Fragment
	u.Fragment = ""

	uri = u.String()
	repoRoot := "/var/lib/siren/" + unit.UnitNameEscape(uri)
	sourceRoot := repoRoot
	if fragment != "" {
		sourceRoot = repoRoot + "/" + fragment
	}

	fi, err := os.Stat(repoRoot)
	if err != nil {
		func(){
			task := NewTask(writer, "Cloning"); defer task.Finish()
			task.Require(task.RunCommand("git", "clone", uri, repoRoot))
		}()
	} else {
		func(){
			task := NewTask(writer, "Updating"); defer task.Finish()
			task.Assert(fi.IsDir(), errors.New(repoRoot + " is not a directory."))
			task.Require(task.RunCommand("git", "-C", repoRoot, "pull"))
		}()
	}

	return Build(sourceRoot, tag, writer)
}
<commit_msg>Pull: Require git+/git: prefix.<commit_after>package main

import (
	"errors"
	"io"
	"os"
	"net/url"
	"strings"

	"github.com/coreos/go-systemd/unit"
	"github.com/LEW21/siren/imagectl"
)

func Pull(uri, tag string, writer io.Writer) (image imagectl.Image, ret_tag string, ok bool) {
	EnsureSirenDirExists()

	defer func(){
		if r := recover(); r != nil {
			ok = false
		}
	}()

	var u *url.URL
	func(){
		task := NewTask(writer, "Parsing URI"); defer task.Finish()
		var err error
		u, err = url.Parse(uri)
		task.Require(err)

		if u.Scheme == "git" || strings.HasPrefix(u.Scheme, "git+") {
			u.Scheme = strings.TrimPrefix(u.Scheme, "git+")
		} else {
			task.Require(errors.New("Unsupported scheme: " + u.Scheme))
		}
	}()

	fragment := u.Fragment
	u.Fragment = ""

	uri = u.String()
	repoRoot := "/var/lib/siren/" + unit.UnitNameEscape(uri)
	sourceRoot := repoRoot
	if fragment != "" {
		sourceRoot = repoRoot + "/" + fragment
	}

	fi, err := os.Stat(repoRoot)
	if err != nil {
		func(){
			task := NewTask(writer, "Cloning"); defer task.Finish()
			task.Require(task.RunCommand("git", "clone", uri, repoRoot))
		}()
	} else {
		func(){
			task := NewTask(writer, "Updating"); defer task.Finish()
			task.Assert(fi.IsDir(), errors.New(repoRoot + " is not a directory."))
			task.Require(task.RunCommand("git", "-C", repoRoot, "pull"))
		}()
	}

	return Build(sourceRoot, tag, writer)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/pkg/errors"
	"log"
	"math/rand"
	"net"
	"os"
	"strings"
	"time"
)

const road_width = 40
const road_lenght = 20
const car_width = 14
const car_lenght = 7
const result_width = 76
const max_players_in_top = 10

type Config struct {
	Log                 *log.Logger
	AcidPath, ScorePath string
}

type Point struct {
	X, Y int
}

type Player struct {
	Name  string
	Score int64
}

type GameData struct {
	Roads      [][]byte
	Car, Clear []byte
	Top        []Player
}

type RoundData struct {
	player                                   Player
	CarPosition, bombPosition, bonusPosition Point
	BombFactor, BonusFactor, Speed           int
	GameOver                                 []byte
}

func generateRoad(reverse bool) []byte {
	road := make([]byte, road_width*road_lenght)
	midline := reverse
	for row := 0; row < road_lenght; row++ {
		for column := 0; column < road_width; column++ {
			var symbol byte
			if column == 0 || column == road_width-2 {
				symbol = byte('|')
			} else if column == road_width-1 {
				symbol = byte('\n')
			} else if column == road_width/2-1 {
				if midline {
					symbol = byte('|')
				} else {
					symbol = byte(' ')
				}
				midline = !midline
			} else {
				symbol = byte(' ')
			}
			road[row*road_width+column] = symbol
		}

	}
	return road
}

func getAcid(conf *Config, fileName string) ([]byte, error) {
	fileStat, err := os.Stat(conf.AcidPath + "/" + fileName)
	if err != nil {
		conf.Log.Printf("Acid %s does not exist: %v\n", fileName, err)
		return []byte{}, err
	}

	acid := make([]byte, fileStat.Size())
	f, err := os.OpenFile(conf.AcidPath+"/"+fileName, os.O_RDONLY, os.ModePerm)
	if err != nil {
		conf.Log.Printf("Error while opening %s: %v\n", fileName, err)
		os.Exit(1)
	}
	defer f.Close()

	f.Read(acid)

	return acid, nil
}

func updatePosition(conn net.Conn, position *Point) {
	for {
		direction := make([]byte, 1)

		_, err := conn.Read(direction)
		if err != nil {
			return
		}

		switch direction[0] {
		case 68:
			// Left
			position.X--
		case 67:
			// Right
			position.X++
		case 65:
			// Up
			position.Y--
		case 66:
			// Down
			position.Y++
		}
	}
}

func updateScore(roundData *RoundData) {
	for {
		roundData.player.Score++
		time.Sleep(time.Duration(roundData.Speed) * time.Millisecond)
	}

}

func readName(conf *Config, conn net.Conn) (string, error) {
	conn.Write([]byte("Enter your name:"))
	io := bufio.NewReader(conn)
	name, err := io.ReadString('\n')
	if err != nil {
		conf.Log.Println("Error while name reading", err)
		return "", err
	}
	if name == "" {
		conf.Log.Println("Empty name")
		return "", errors.New("Empty name")
	}
	if len(name) > result_width/2 {
		conf.Log.Println("Too long name")
		return "", errors.New("Too long name")
	}
	return strings.Replace(name, "\n", "", -1), nil
}

func gameOver(conf *Config, conn net.Conn, roundData *RoundData, gameData *GameData) {

	conn.Write(gameData.Clear)

	// First we print current player result:
	// Score
	scoreStr := fmt.Sprintf("%d", roundData.player.Score)
	for i, char := range scoreStr {
		roundData.GameOver[i] = byte(char)
	}
	//:
	roundData.GameOver[result_width/2] = byte(':')
	//Name
	for i, char := range []byte(roundData.player.Name) {
		roundData.GameOver[result_width-1-len(roundData.player.Name)+i] = byte(char)
	}

	// Then we check on which place is current player
	inserted := false
	for i, player := range gameData.Top {
		if roundData.player.Score >= player.Score {
			// Insert new record
			gameData.Top = append(gameData.Top[:i], append([]Player{roundData.player}, gameData.Top[i:]...)...)
			inserted = true
			break
		}
	}
	if !inserted {
		gameData.Top = append(gameData.Top, roundData.player)
	}

	// Remove slowest user if top is full
	if len(gameData.Top) >= max_players_in_top {
		gameData.Top = gameData.Top[:max_players_in_top]
	}

	//TOP
	copy(roundData.GameOver[1*result_width+result_width/2-1:2*result_width+result_width/2+2], []byte("TOP"))

	// Then print new top
	for place, player := range gameData.Top {
		// Score
		scoreStr := fmt.Sprintf("%d", player.Score)
		for i, char := range scoreStr {
			roundData.GameOver[(2+place)*result_width+i] = byte(char)
		}
		//:
		roundData.GameOver[(2+place)*result_width+result_width/2] = byte(':')
		//Name
		for i, char := range []byte(player.Name) {
			roundData.GameOver[(2+place)*result_width+result_width-1-len(player.Name)+i] = byte(char)
		}
	}

	conn.Write(roundData.GameOver)
	// We do not need to check for error because user should not care, but logs are written
	saveScore(conf, gameData)
}

func saveScore(conf *Config, gameData *GameData) error {
	b, err := json.Marshal(gameData.Top)
	if err != nil {
		conf.Log.Println(err)
		return err
	}

	// Being sure file was not there
	os.Remove(conf.ScorePath + "/score.json")
	scoreFile, err := os.OpenFile(conf.ScorePath+"/score.json", os.O_RDWR|os.O_CREATE, 0600)
	if err != nil {
		conf.Log.Println(err)
		return err
	}
	defer scoreFile.Close()

	scoreFile.Write(b)
	return nil
}

func round(conf *Config, conn net.Conn, gameData *GameData) {
	defer conn.Close()

	roundData := RoundData{}
	roundData.CarPosition = Point{road_width/2-car_width/2, road_lenght-car_lenght-1}
	roundData.bombPosition = Point{road_width, road_lenght}
	roundData.bonusPosition = Point{road_width, road_lenght}
	roundData.GameOver, _ = getAcid(conf, "game_over.txt")

	roundData.BombFactor = 10
	roundData.BonusFactor = 10
	roundData.Speed = 200

	name, err := readName(conf, conn)
	if err != nil {
		return
	}
	roundData.player.Name = name
	go updateScore(&roundData)
	go updatePosition(conn, &roundData.CarPosition)

	for {
		if roundData.CarPosition.X < 1 || roundData.CarPosition.X > 23 || roundData.CarPosition.Y < 1 || roundData.CarPosition.Y > 12 {
			// Hit the wall
			gameOver(conf, conn, &roundData, gameData)
			return
		} else if roundData.CarPosition.X <= roundData.bombPosition.X && roundData.CarPosition.X+car_width-1 > roundData.bombPosition.X &&
			roundData.CarPosition.Y < roundData.bombPosition.Y && roundData.CarPosition.Y+car_lenght-1 > roundData.bombPosition.Y {
			// Hit the bomb
			gameOver(conf, conn, &roundData, gameData)
			return
		} else if roundData.CarPosition.X <= roundData.bonusPosition.X && roundData.CarPosition.X+car_width-1 > roundData.bonusPosition.X &&
			roundData.CarPosition.Y < roundData.bonusPosition.Y && roundData.CarPosition.Y+car_lenght-1 > roundData.bonusPosition.Y {
			roundData.player.Score+=10
			roundData.bonusPosition = Point{road_width, road_lenght}
		}

		for i := range gameData.Roads {
			data := make([]byte, len(gameData.Roads[i]))
			copy(data, gameData.Roads[i])

			// Moving cursor at the beginning
			_, err := conn.Write(gameData.Clear)
			if err != nil {
				return
			}

			// Checking and updating complexity
			if roundData.player.Score > 100 && roundData.player.Score < 200 {
				roundData.Speed = 150
			} else if roundData.player.Score >= 200 && roundData.player.Score < 400 {
				roundData.BonusFactor = 5
				roundData.Speed = 100
			} else if roundData.player.Score >= 400 && roundData.player.Score < 600 {
				roundData.BonusFactor = 10
				roundData.BombFactor = 5
			} else if roundData.player.Score >= 600 {
				roundData.BonusFactor = 123
				roundData.BombFactor = 1
				roundData.Speed = 80
			}

			// Applying the bonus
			if roundData.bonusPosition.Y < road_lenght {
				data[roundData.bonusPosition.Y*road_width+roundData.bonusPosition.X] = byte('$')
				roundData.bonusPosition.Y++
			} else if rand.Int()%roundData.BonusFactor == 0 {
				roundData.bonusPosition.X, roundData.bonusPosition.Y = rand.Intn(road_width-3)+1, 0
			}

			// Applying the bomb
			if roundData.bombPosition.Y < road_lenght {
				data[roundData.bombPosition.Y*road_width+roundData.bombPosition.X] = byte('X')
				roundData.bombPosition.Y++
			} else if rand.Int()%roundData.BombFactor == 0 {
				roundData.bombPosition.X, roundData.bombPosition.Y = rand.Intn(road_width-3)+1, 0
			}

			// Applying the Car
			for line := 0; line < car_lenght; line++ {
				copy(data[
					((roundData.CarPosition.Y+line)*road_width+roundData.CarPosition.X):((roundData.CarPosition.Y+line)*road_width+roundData.CarPosition.X)+car_width-1],
					gameData.Car[line*car_width:line*car_width+car_width-1])
			}

			// Applying the score
			scoreStr := fmt.Sprintf("%d", roundData.player.Score)
			for i := range scoreStr {
				data[i] = byte(scoreStr[i])

			}

			_, err = conn.Write(data)
			if err != nil {
				return
			}
			time.Sleep(time.Duration(roundData.Speed) * time.Millisecond)
		}
	}
}

func main() {
	var logFile string
	conf := &Config{}

	flag.StringVar(&logFile, "l", "/var/log/race.log", "Log file")
	flag.StringVar(&conf.AcidPath, "a", "/Users/leoleovich/go/src/github.com/leoleovich/race/artifacts", "Artifacts location")
	flag.StringVar(&conf.ScorePath, "s", "/Users/leoleovich/go/src/github.com/leoleovich/race/artifacts", "Score location")
	flag.Parse()

	logfile, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
	conf.Log = log.New(logfile, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)

	l, err := net.Listen("tcp", ":4242")
	if err != nil {
		conf.Log.Println(err)
		os.Exit(2)
	}
	defer l.Close()

	gameData := GameData{}
	gameData.Roads = [][]byte{generateRoad(false), generateRoad(true)}
	gameData.Car, _ = getAcid(conf, "car.txt")
	gameData.Clear, _ = getAcid(conf, "clear.txt")
	scoreData, _ := getAcid(conf, "score.json")
	err = json.Unmarshal(scoreData, &gameData.Top)

	for {
		conn, err := l.Accept()
		if err != nil {
			conf.Log.Println("Failed to accept request", err)
		}
		go round(conf, conn, &gameData)
	}
}
<commit_msg>Fix of borders<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/pkg/errors"
	"log"
	"math/rand"
	"net"
	"os"
	"strings"
	"time"
)

const road_width = 40
const road_lenght = 20
const car_width = 14
const car_lenght = 7
const result_width = 76
const max_players_in_top = 10

type Config struct {
	Log                 *log.Logger
	AcidPath, ScorePath string
}

type Point struct {
	X, Y int
}

type Player struct {
	Name  string
	Score int64
}

type GameData struct {
	Roads      [][]byte
	Car, Clear []byte
	Top        []Player
}

type RoundData struct {
	player                                   Player
	CarPosition, bombPosition, bonusPosition Point
	BombFactor, BonusFactor, Speed           int
	GameOver                                 []byte
}

func generateRoad(reverse bool) []byte {
	road := make([]byte, road_width*road_lenght)
	midline := reverse
	for row := 0; row < road_lenght; row++ {
		for column := 0; column < road_width; column++ {
			var symbol byte
			if column == 0 || column == road_width-2 {
				symbol = byte('|')
			} else if column == road_width-1 {
				symbol = byte('\n')
			} else if column == road_width/2-1 {
				if midline {
					symbol = byte('|')
				} else {
					symbol = byte(' ')
				}
				midline = !midline
			} else {
				symbol = byte(' ')
			}
			road[row*road_width+column] = symbol
		}

	}
	return road
}

func getAcid(conf *Config, fileName string) ([]byte, error) {
	fileStat, err := os.Stat(conf.AcidPath + "/" + fileName)
	if err != nil {
		conf.Log.Printf("Acid %s does not exist: %v\n", fileName, err)
		return []byte{}, err
	}

	acid := make([]byte, fileStat.Size())
	f, err := os.OpenFile(conf.AcidPath+"/"+fileName, os.O_RDONLY, os.ModePerm)
	if err != nil {
		conf.Log.Printf("Error while opening %s: %v\n", fileName, err)
		os.Exit(1)
	}
	defer f.Close()

	f.Read(acid)

	return acid, nil
}

func updatePosition(conn net.Conn, position *Point) {
	for {
		direction := make([]byte, 1)

		_, err := conn.Read(direction)
		if err != nil {
			return
		}

		switch direction[0] {
		case 68:
			// Left
			position.X--
		case 67:
			// Right
			position.X++
		case 65:
			// Up
			position.Y--
		case 66:
			// Down
			position.Y++
		}
	}
}

func updateScore(roundData *RoundData) {
	for {
		roundData.player.Score++
		time.Sleep(time.Duration(roundData.Speed) * time.Millisecond)
	}

}

func readName(conf *Config, conn net.Conn) (string, error) {
	conn.Write([]byte("Enter your name:"))
	io := bufio.NewReader(conn)
	name, err := io.ReadString('\n')
	if err != nil {
		conf.Log.Println("Error while name reading", err)
		return "", err
	}
	if name == "" {
		conf.Log.Println("Empty name")
		return "", errors.New("Empty name")
	}
	if len(name) > result_width/2 {
		conf.Log.Println("Too long name")
		return "", errors.New("Too long name")
	}
	return strings.Replace(name, "\n", "", -1), nil
}

func gameOver(conf *Config, conn net.Conn, roundData *RoundData, gameData *GameData) {

	conn.Write(gameData.Clear)

	// First we print current player result:
	// Score
	scoreStr := fmt.Sprintf("%d", roundData.player.Score)
	for i, char := range scoreStr {
		roundData.GameOver[i] = byte(char)
	}
	//:
	roundData.GameOver[result_width/2] = byte(':')
	//Name
	for i, char := range []byte(roundData.player.Name) {
		roundData.GameOver[result_width-1-len(roundData.player.Name)+i] = byte(char)
	}

	// Then we check on which place is current player
	inserted := false
	for i, player := range gameData.Top {
		if roundData.player.Score >= player.Score {
			// Insert new record
			gameData.Top = append(gameData.Top[:i], append([]Player{roundData.player}, gameData.Top[i:]...)...)
			inserted = true
			break
		}
	}
	if !inserted {
		gameData.Top = append(gameData.Top, roundData.player)
	}

	// Remove slowest user if top is full
	if len(gameData.Top) >= max_players_in_top {
		gameData.Top = gameData.Top[:max_players_in_top]
	}

	//TOP
	copy(roundData.GameOver[1*result_width+result_width/2-1:2*result_width+result_width/2+2], []byte("TOP"))

	// Then print new top
	for place, player := range gameData.Top {
		// Score
		scoreStr := fmt.Sprintf("%d", player.Score)
		for i, char := range scoreStr {
			roundData.GameOver[(2+place)*result_width+i] = byte(char)
		}
		//:
		roundData.GameOver[(2+place)*result_width+result_width/2] = byte(':')
		//Name
		for i, char := range []byte(player.Name) {
			roundData.GameOver[(2+place)*result_width+result_width-1-len(player.Name)+i] = byte(char)
		}
	}

	conn.Write(roundData.GameOver)
	// We do not need to check for error because user should not care, but logs are written
	saveScore(conf, gameData)
}

func saveScore(conf *Config, gameData *GameData) error {
	b, err := json.Marshal(gameData.Top)
	if err != nil {
		conf.Log.Println(err)
		return err
	}

	// Being sure file was not there
	os.Remove(conf.ScorePath + "/score.json")
	scoreFile, err := os.OpenFile(conf.ScorePath+"/score.json", os.O_RDWR|os.O_CREATE, 0600)
	if err != nil {
		conf.Log.Println(err)
		return err
	}
	defer scoreFile.Close()

	scoreFile.Write(b)
	return nil
}

func round(conf *Config, conn net.Conn, gameData *GameData) {
	defer conn.Close()

	roundData := RoundData{}
	roundData.CarPosition = Point{road_width/2-car_width/2, road_lenght-car_lenght-1}
	roundData.bombPosition = Point{road_width, road_lenght}
	roundData.bonusPosition = Point{road_width, road_lenght}
	roundData.GameOver, _ = getAcid(conf, "game_over.txt")

	roundData.BombFactor = 10
	roundData.BonusFactor = 10
	roundData.Speed = 200

	name, err := readName(conf, conn)
	if err != nil {
		return
	}
	roundData.player.Name = name
	go updateScore(&roundData)
	go updatePosition(conn, &roundData.CarPosition)

	for {
		if roundData.CarPosition.X < 1 || roundData.CarPosition.X > road_width-car_width-1 || roundData.CarPosition.Y < 1 || roundData.CarPosition.Y > road_lenght-car_lenght-1 {
			// Hit the wall
			gameOver(conf, conn, &roundData, gameData)
			return
		} else if roundData.CarPosition.X <= roundData.bombPosition.X && roundData.CarPosition.X+car_width-1 > roundData.bombPosition.X &&
			roundData.CarPosition.Y < roundData.bombPosition.Y && roundData.CarPosition.Y+car_lenght-1 > roundData.bombPosition.Y {
			// Hit the bomb
			gameOver(conf, conn, &roundData, gameData)
			return
		} else if roundData.CarPosition.X <= roundData.bonusPosition.X && roundData.CarPosition.X+car_width-1 > roundData.bonusPosition.X &&
			roundData.CarPosition.Y < roundData.bonusPosition.Y && roundData.CarPosition.Y+car_lenght-1 > roundData.bonusPosition.Y {
			roundData.player.Score+=10
			roundData.bonusPosition = Point{road_width, road_lenght}
		}

		for i := range gameData.Roads {
			data := make([]byte, len(gameData.Roads[i]))
			copy(data, gameData.Roads[i])

			// Moving cursor at the beginning
			_, err := conn.Write(gameData.Clear)
			if err != nil {
				return
			}

			// Checking and updating complexity
			if roundData.player.Score > 100 && roundData.player.Score < 200 {
				roundData.Speed = 150
			} else if roundData.player.Score >= 200 && roundData.player.Score < 400 {
				roundData.BonusFactor = 5
				roundData.Speed = 100
			} else if roundData.player.Score >= 400 && roundData.player.Score < 600 {
				roundData.BonusFactor = 10
				roundData.BombFactor = 5
			} else if roundData.player.Score >= 600 {
				roundData.BonusFactor = 123
				roundData.BombFactor = 1
				roundData.Speed = 80
			}

			// Applying the bonus
			if roundData.bonusPosition.Y < road_lenght {
				data[roundData.bonusPosition.Y*road_width+roundData.bonusPosition.X] = byte('$')
				roundData.bonusPosition.Y++
			} else if rand.Int()%roundData.BonusFactor == 0 {
				roundData.bonusPosition.X, roundData.bonusPosition.Y = rand.Intn(road_width-3)+1, 0
			}

			// Applying the bomb
			if roundData.bombPosition.Y < road_lenght {
				data[roundData.bombPosition.Y*road_width+roundData.bombPosition.X] = byte('X')
				roundData.bombPosition.Y++
			} else if rand.Int()%roundData.BombFactor == 0 {
				roundData.bombPosition.X, roundData.bombPosition.Y = rand.Intn(road_width-3)+1, 0
			}

			// Applying the Car
			for line := 0; line < car_lenght; line++ {
				copy(data[
					((roundData.CarPosition.Y+line)*road_width+roundData.CarPosition.X):((roundData.CarPosition.Y+line)*road_width+roundData.CarPosition.X)+car_width-1],
					gameData.Car[line*car_width:line*car_width+car_width-1])
			}

			// Applying the score
			scoreStr := fmt.Sprintf("%d", roundData.player.Score)
			for i := range scoreStr {
				data[i] = byte(scoreStr[i])

			}

			_, err = conn.Write(data)
			if err != nil {
				return
			}
			time.Sleep(time.Duration(roundData.Speed) * time.Millisecond)
		}
	}
}

func main() {
	var logFile string
	conf := &Config{}

	flag.StringVar(&logFile, "l", "/var/log/race.log", "Log file")
	flag.StringVar(&conf.AcidPath, "a", "/Users/leoleovich/go/src/github.com/leoleovich/race/artifacts", "Artifacts location")
	flag.StringVar(&conf.ScorePath, "s", "/Users/leoleovich/go/src/github.com/leoleovich/race/artifacts", "Score location")
	flag.Parse()

	logfile, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
	conf.Log = log.New(logfile, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)

	l, err := net.Listen("tcp", ":4242")
	if err != nil {
		conf.Log.Println(err)
		os.Exit(2)
	}
	defer l.Close()

	gameData := GameData{}
	gameData.Roads = [][]byte{generateRoad(false), generateRoad(true)}
	gameData.Car, _ = getAcid(conf, "car.txt")
	gameData.Clear, _ = getAcid(conf, "clear.txt")
	scoreData, _ := getAcid(conf, "score.json")
	err = json.Unmarshal(scoreData, &gameData.Top)

	for {
		conn, err := l.Accept()
		if err != nil {
			conf.Log.Println("Failed to accept request", err)
		}
		go round(conf, conn, &gameData)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"log"
	"os"
	"flag"
	"time"
	"errors"
)

const road_width = 40
const car_width = 16

type Config struct {
	Log      *log.Logger
	AcidPath string
}

func getAcid(conf *Config, fileName string ) ([]byte, error) {
	gameOver := false
	fileStat, err := os.Stat(conf.AcidPath + "/" + fileName)
	if err != nil {
		gameOver = true
		fileName = "game_over.txt"
		fileStat, err = os.Stat(conf.AcidPath + "/" + fileName)
		if err != nil {
			conf.Log.Printf("Acid %s does not exist: %v\n", fileName, err)
		}
	}

	acid := make([]byte, fileStat.Size())
	f, err := os.OpenFile(conf.AcidPath + "/" + fileName, os.O_RDONLY, os.ModePerm)
	if err != nil {
		conf.Log.Printf("Error while opening %s: %v\n", fileName, err)
		os.Exit(1)
	}
	defer f.Close()

	f.Read(acid)

	if gameOver {
		return acid, errors.New("Game over")
	} else {
		return acid, nil
	}
}

func updatePosition(conn net.Conn, position *int) {
	direction := make([]byte, 1)

	conn.SetReadDeadline(time.Now().Add(time.Duration(10) * time.Millisecond))
	conn.Read(direction)

	switch direction[0] {
	case 68:
		// Left
		*position--
	case 67:
		// Right
		*position++
	}
}

func handleRequest(conf *Config, conn net.Conn) {
	defer conn.Close()

	position := 12

	car, _ := getAcid(conf, "car.txt")
	roadStraight, _ := getAcid(conf, "road_straight.txt")
	roadReverse, _ := getAcid(conf, "road_reverse.txt")
	clear, _ := getAcid(conf, "clear.txt")
	gameOver, _ := getAcid(conf, "game_over.txt")

	for {

		updatePosition(conn, &position)
		conf.Log.Println(position)
		if position < 1 || position > 23 {
			conn.Write(gameOver)
			return
		}

		data := make([]byte, len(roadStraight))
		copy(data, roadStraight)

		// Mask car to straight road
		for line:=0; line < 7 ;  line++ {
			copy(data[((12+line)*road_width+position):((12+line)*road_width+position)+15], car[line*car_width:line*car_width+15])
		}
		conn.Write(data)
		time.Sleep(200*time.Millisecond)
		conn.Write(clear)

		updatePosition(conn, &position)
		copy(data, roadReverse)

		// Mask car to reverse road
		for line:=0; line < 7 ;  line++ {
			copy(data[((12+line)*road_width+position):((12+line)*road_width+position)+15], car[line*car_width:line*car_width+15])
		}
		conn.Write(data)
		time.Sleep(200*time.Millisecond)
		conn.Write(clear)
	}

}

func main() {
	var logFile string
	conf := &Config{}

	flag.StringVar(&logFile, "l", "/var/log/race.log", "Log file")
	flag.StringVar(&conf.AcidPath, "s", "/Users/leoleovich/go/src/github.com/leoleovich/race/artifacts", "Story file")
	flag.Parse()

	logfile, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
	conf.Log = log.New(logfile, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)

	l, err := net.Listen("tcp", ":4242")
	if err != nil {
		conf.Log.Println(err)
		os.Exit(2)
	}
	defer l.Close()

	for {
		conn, err := l.Accept()
		if err != nil {
			conf.Log.Println("Failed to accept request", err)
		}
		go handleRequest(conf, conn)
	}
}<commit_msg>Go fmt<commit_after>package main

import (
	"errors"
	"flag"
	"log"
	"net"
	"os"
	"time"
)

const road_width = 40
const car_width = 16

type Config struct {
	Log      *log.Logger
	AcidPath string
}

func getAcid(conf *Config, fileName string) ([]byte, error) {
	gameOver := false
	fileStat, err := os.Stat(conf.AcidPath + "/" + fileName)
	if err != nil {
		gameOver = true
		fileName = "game_over.txt"
		fileStat, err = os.Stat(conf.AcidPath + "/" + fileName)
		if err != nil {
			conf.Log.Printf("Acid %s does not exist: %v\n", fileName, err)
		}
	}

	acid := make([]byte, fileStat.Size())
	f, err := os.OpenFile(conf.AcidPath+"/"+fileName, os.O_RDONLY, os.ModePerm)
	if err != nil {
		conf.Log.Printf("Error while opening %s: %v\n", fileName, err)
		os.Exit(1)
	}
	defer f.Close()

	f.Read(acid)

	if gameOver {
		return acid, errors.New("Game over")
	} else {
		return acid, nil
	}
}

func updatePosition(conn net.Conn, position *int) {
	direction := make([]byte, 1)

	conn.SetReadDeadline(time.Now().Add(time.Duration(10) * time.Millisecond))
	conn.Read(direction)

	switch direction[0] {
	case 68:
		// Left
		*position--
	case 67:
		// Right
		*position++
	}
}

func handleRequest(conf *Config, conn net.Conn) {
	defer conn.Close()

	position := 12

	car, _ := getAcid(conf, "car.txt")
	roadStraight, _ := getAcid(conf, "road_straight.txt")
	roadReverse, _ := getAcid(conf, "road_reverse.txt")
	clear, _ := getAcid(conf, "clear.txt")
	gameOver, _ := getAcid(conf, "game_over.txt")

	for {

		updatePosition(conn, &position)
		conf.Log.Println(position)
		if position < 1 || position > 23 {
			conn.Write(gameOver)
			return
		}

		data := make([]byte, len(roadStraight))
		copy(data, roadStraight)

		// Mask car to straight road
		for line := 0; line < 7; line++ {
			copy(data[((12+line)*road_width+position):((12+line)*road_width+position)+15], car[line*car_width:line*car_width+15])
		}
		conn.Write(data)
		time.Sleep(200 * time.Millisecond)
		conn.Write(clear)

		updatePosition(conn, &position)
		copy(data, roadReverse)

		// Mask car to reverse road
		for line := 0; line < 7; line++ {
			copy(data[((12+line)*road_width+position):((12+line)*road_width+position)+15], car[line*car_width:line*car_width+15])
		}
		conn.Write(data)
		time.Sleep(200 * time.Millisecond)
		conn.Write(clear)
	}

}

func main() {
	var logFile string
	conf := &Config{}

	flag.StringVar(&logFile, "l", "/var/log/race.log", "Log file")
	flag.StringVar(&conf.AcidPath, "s", "/Users/leoleovich/go/src/github.com/leoleovich/race/artifacts", "Story file")
	flag.Parse()

	logfile, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
	conf.Log = log.New(logfile, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)

	l, err := net.Listen("tcp", ":4242")
	if err != nil {
		conf.Log.Println(err)
		os.Exit(2)
	}
	defer l.Close()

	for {
		conn, err := l.Accept()
		if err != nil {
			conf.Log.Println("Failed to accept request", err)
		}
		go handleRequest(conf, conn)
	}
}
<|endoftext|>


<commit_before>package gogsrp

import "crypto/rand"

func ReadRand(n int) ([]byte, error) {
	bytes := make([]byte, n)
	_, err := rand.Read(bytes)
	return bytes, err
}
<commit_msg>refactor rand<commit_after>package gogsrp

import "crypto/rand"

func ReadRand(bytes int) ([]byte, error) {
	buffer := make([]byte, bytes)
	_, err := rand.Read(bytes)
	return buffer, err
}
<|endoftext|>


<commit_before>package hu

import (
	"bytes"
	"fmt"
	"io"
	"math/big"
	"strings"
	"unicode"
)

// The lexer bits inspired by those in the Go standard library.
//
// Copyright 2011 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.

// item represents a token or text string returned from the scanner.
type item struct {
	typ itemType
	val string
}

func (i item) String() string {
	switch {
	case i.typ == itemEOF:
		return "EOF"
	case i.typ == itemError:
		return i.val
	case len(i.val) > 10:
		return fmt.Sprintf("%.10q...", i.val)
	}
	return fmt.Sprintf("%q", i.val)
}

// itemType identifies the type of lex items.
type itemType uint

const (
	itemError itemType = iota // error occurred; value is text of error
	itemEOF
	itemNewline     //
	itemWord        // alphanumeric word
	itemNumber      // simple number, including imaginary
	itemPunctuation //
	itemString      // quoted string (includes quotes)
	itemOpenParenthesis
	itemCloseParenthesis
	itemOpenCurlyBrace
	itemCloseCurlyBrace
	itemQuote
	itemSpace
	itemPeriod
	itemPageBreak
	itemSection
)

// Make the types prettyprint.
var itemName = map[itemType]string{
	itemError:            "error",
	itemEOF:              "EOF",
	itemNewline:          "newline",
	itemWord:             "word",
	itemPunctuation:      "punctuation",
	itemNumber:           "number",
	itemString:           "string",
	itemOpenParenthesis:  "(",
	itemCloseParenthesis: ")",
	itemOpenCurlyBrace:   "{",
	itemCloseCurlyBrace:  "}",
	itemQuote:            "'",
	itemSpace:            "space",
	itemPeriod:           "period",
	itemPageBreak:        "page break",
	itemSection:          "§",
}

func (i itemType) String() string {
	s := itemName[i]
	if s == "" {
		return fmt.Sprintf("item%d", int(i))
	}
	return s
}

const eof = rune(-1)

// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*reader) stateFn

// reader holds the state of the scanner.
type reader struct {
	name      string         // the name of the input; used only for error reports.
	input     io.RuneScanner // the string being scanned.
	current   bytes.Buffer
	state     stateFn   // the next lexing function to enter
	width     int       // width of last rune read from input.
	items     chan item // channel of scanned items.
	token     [2]item   // two-token lookahead for parser.
	peekCount int
}

// next returns the next rune in the input.
func (l *reader) next() (rune rune) {
	rune, size, err := l.input.ReadRune()
	if err == nil {
		l.width, _ = l.current.WriteRune(rune)
		if size != l.width {
			fmt.Println("size: ", size, "width: ", l.width)
		}
		return rune
	} else {
		l.width = 0
		return eof
	}
	panic("")
}

// peek returns but does not consume the next rune in the input.
func (l *reader) peek() rune {
	rune := l.next()
	l.backup()
	return rune
}

// backup steps back one rune. Can only be called once per call of next.
func (l *reader) backup() {
	if l.width > 0 {
		err := l.input.UnreadRune()
		if err != nil {
			fmt.Println("err: ", err)
		}
		l.current.Truncate(l.current.Len() - l.width)
	}
}

// emit passes an item back to the client.
func (l *reader) emit(t itemType) {
	l.items <- item{t, l.current.String()}
	l.current.Reset()
	l.width = 0
}

// accept consumes the next rune if it's from the valid set.
func (l *reader) accept(valid string) bool {
	if strings.IndexRune(valid, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

// acceptRun consumes a run of runes from the valid set.
func (l *reader) acceptRun(valid string) {
	for strings.IndexRune(valid, l.next()) >= 0 {
	}
	l.backup()
}

// error returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.run.
func (l *reader) errorf(format string, args ...interface{}) stateFn {
	l.items <- item{itemError, fmt.Sprintf(format, args...)}
	return nil
}

// nextItem returns the next item from the input.
func (l *reader) nextItemFromInput() item {
	for {
		select {
		case item := <-l.items:
			return item
		default:
			l.state = l.state(l)
		}
	}
	panic("not reached")
}

// next returns the next item taking into account peek
func (l *reader) nextItem() item {
	if l.peekCount > 0 {
		l.peekCount--
	} else {
		l.token[0] = l.nextItemFromInput()
	}
	return l.token[l.peekCount]
}

// backup backs the input stream up one token.
func (l *reader) backupItem() {
	l.peekCount++
}

// peek returns but does not consume the next token.
func (l *reader) peekItem() item {
	if l.peekCount > 0 {
		return l.token[l.peekCount-1]
	}
	l.peekCount = 1
	l.token[0] = l.nextItemFromInput()

	return l.token[0]
}

// lex creates a new scanner for the input string.
func newReader(name string, input io.RuneScanner) *reader {
	l := &reader{
		name:  name,
		input: input,
		state: lexItem,
		items: make(chan item, 2), // Two items of buffering is sufficient for all state functions
	}
	return l
}

// state functions

// lexItem ...
func lexItem(l *reader) stateFn {
	switch r := l.next(); {
	case r == eof:
		l.emit(itemEOF)
		return nil
	// case r == '§':
	// 	if l.peek() == ' ' {
	// 		l.next()
	// 	}
	// 	l.emit(itemSection)
	// 	return lexItem
	case r == '"':
		return lexQuote
	case r == '+' || r == '-':
		rr := l.peek()
		if isPunctuation(rr) {
			l.emit(itemWord)
			return lexItem
		} else if '0' <= rr && rr <= '9' {
			return lexNumber
		} else {
			l.emit(itemPunctuation)
			return lexItem
		}
	case r == '+' || r == '-' || ('0' <= r && r <= '9'):
		l.backup()
		return lexNumber
	case isPunctuation(r):
		l.backup()
		return lexPunctuation
	default:
		l.backup()
		return lexWord
	}
	return nil
}

// lexWord scans an alphanumeric
func lexWord(l *reader) stateFn {
top:
	switch r := l.next(); {
	case isPunctuation(r):
		l.backup()
		l.emit(itemWord)
	default:
		goto top
	}
	return lexItem
}

func lexPunctuation(l *reader) stateFn {
	switch l.next() {
	case '\n':
		l.emit(itemNewline)
	case '(':
		l.emit(itemOpenParenthesis)
	case ')':
		l.emit(itemCloseParenthesis)
	case '{':
		l.emit(itemOpenCurlyBrace)
	case '}':
		l.emit(itemCloseCurlyBrace)
	case '\'':
		l.emit(itemQuote)
	case ' ':
		l.emit(itemSpace)
	case '.':
		l.emit(itemPeriod)
	case '\f':
		l.emit(itemPageBreak)
	case '§':
		l.emit(itemSection)
	case ',', ':', ';', '!', '-':
		l.emit(itemPunctuation)
	default:
		l.emit(itemPunctuation)
		//panic("???")
	}
	return lexItem
}

// lexNumber scans a number: decimal, octal, hex, float, or imaginary.  This
// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
// and "089" - but when it's wrong the input is invalid and the parser (via
// strconv) will notice.
func lexNumber(l *reader) stateFn {
	if !l.scanNumber() {
		return l.errorf("bad number syntax: %q", l.current.String())
	}
	l.emit(itemNumber)
	return lexPunctuation
}

func (l *reader) scanNumber() bool {
	// Optional leading sign.
	l.accept("+-")
	// Is it hex?
	digits := "0123456789"
	if l.accept("0") && l.accept("xX") {
		digits = "0123456789abcdefABCDEF"
	}
	l.acceptRun(digits)
	if l.accept(".") {
		l.acceptRun(digits)
	}
	if l.accept("/") {
		l.acceptRun(digits)
	}
	if l.accept("eE") {
		l.accept("+-")
		l.acceptRun("0123456789")
	}
	// Is it imaginary?
	l.accept("i")
	// Next thing mustn't be alphanumeric.
	if isAlphaNumeric(l.peek()) {
		l.next()
		return false
	}
	return true
}

// lexQuote scans a quoted string.
func lexQuote(l *reader) stateFn {
Loop:
	for {
		switch l.next() {
		case '\\':
			if r := l.next(); r != eof && r != '\n' {
				break
			}
			fallthrough
		case eof, '\n':
			return l.errorf("unterminated quoted string")
		case '"':
			break Loop
		}
	}
	l.emit(itemString)
	return lexPunctuation
}

// isPunctuation reports whether r is a punctuation character.
func isPunctuation(r rune) bool {
	switch r {
	case ' ', '\t', '\n', '\r', '§', '\f', '(', ')', '{', '}', '\'', '-', eof:
		return true
	case '.', '!', ',', ':':
		return true
	}
	return false
}

// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
func isAlphaNumeric(r rune) bool {
	return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
}

func (reader *reader) read() (term Term) {
	switch token := reader.nextItem(); token.typ {
	case itemString:
		term = String(strings.Trim(token.val, string("\"")))
	case itemNumber:
		num := big.NewRat(0, 1)
		num.SetString(token.val)
		term = &Number{num}
	case itemOpenParenthesis:
		part := &partDescription{ignore: 1<<itemSpace | 1<<itemCloseParenthesis, end: 1 << itemCloseParenthesis}
		term = Tuple(reader.readPart(part))
	case itemOpenCurlyBrace:
		part := &partDescription{ignore: 1<<itemSpace | 1<<itemCloseCurlyBrace, end: 1 << itemCloseCurlyBrace}
		term = Application(reader.readPart(part))
	case itemEOF:
		reader.backupItem()
		term = nil
	case itemError:
		term = Error(token.val)
	default:
		term = Symbol(token.val)
	}
	return
}

func Read(in io.RuneScanner) (result Term) {
	defer func() {
		switch x := recover().(type) {
		case Term:
			result = x
		case interface{}:
			result = Error(fmt.Sprintf("%v", x))
		}
	}()
	reader := newReader("", in)
	result = reader.read()
	return
}

type partDescription struct {
	ignore, start, end uint64
	sub                *partDescription
}

func (reader *reader) readPart(part *partDescription) (result Part) {
	first := true
	for {
		token := reader.peekItem()
		if token.typ == itemEOF {
			break
		}
		if first == false {
			if 1<<token.typ&part.start != 0 {
				break
			}
		} else {
			first = false
		}
		if 1<<token.typ&part.ignore != 0 {
			reader.nextItem()
		} else {
			var t Term
			if part.sub == nil || 1<<token.typ&part.end != 0 {
				t = reader.read()
			} else {
				t = reader.readPart(part.sub)
			}
			result = append(result, t)
		}
		if 1<<token.typ&part.end != 0 {
			break
		}
	}
	return
}

func ReadDocument(in io.RuneScanner) Part {
	line := &partDescription{end: 1 << itemNewline}
	part := &partDescription{end: 1 << itemNewline, sub: line}
	section := &partDescription{start: 1 << itemSection, sub: part}
	document := &partDescription{sub: section}
	return newReader("", in).readPart(document)
}
<commit_msg>added ReadSentence<commit_after>package hu

import (
	"bytes"
	"fmt"
	"io"
	"math/big"
	"strings"
	"unicode"
)

// The lexer bits inspired by those in the Go standard library.
//
// Copyright 2011 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.

// item represents a token or text string returned from the scanner.
type item struct {
	typ itemType
	val string
}

func (i item) String() string {
	switch {
	case i.typ == itemEOF:
		return "EOF"
	case i.typ == itemError:
		return i.val
	case len(i.val) > 10:
		return fmt.Sprintf("%.10q...", i.val)
	}
	return fmt.Sprintf("%q", i.val)
}

// itemType identifies the type of lex items.
type itemType uint

const (
	itemError itemType = iota // error occurred; value is text of error
	itemEOF
	itemNewline     //
	itemWord        // alphanumeric word
	itemNumber      // simple number, including imaginary
	itemPunctuation //
	itemString      // quoted string (includes quotes)
	itemOpenParenthesis
	itemCloseParenthesis
	itemOpenCurlyBrace
	itemCloseCurlyBrace
	itemQuote
	itemSpace
	itemPeriod
	itemPageBreak
	itemSection
)

// Make the types prettyprint.
var itemName = map[itemType]string{
	itemError:            "error",
	itemEOF:              "EOF",
	itemNewline:          "newline",
	itemWord:             "word",
	itemPunctuation:      "punctuation",
	itemNumber:           "number",
	itemString:           "string",
	itemOpenParenthesis:  "(",
	itemCloseParenthesis: ")",
	itemOpenCurlyBrace:   "{",
	itemCloseCurlyBrace:  "}",
	itemQuote:            "'",
	itemSpace:            "space",
	itemPeriod:           "period",
	itemPageBreak:        "page break",
	itemSection:          "§",
}

func (i itemType) String() string {
	s := itemName[i]
	if s == "" {
		return fmt.Sprintf("item%d", int(i))
	}
	return s
}

const eof = rune(-1)

// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*reader) stateFn

// reader holds the state of the scanner.
type reader struct {
	name      string         // the name of the input; used only for error reports.
	input     io.RuneScanner // the string being scanned.
	current   bytes.Buffer
	state     stateFn   // the next lexing function to enter
	width     int       // width of last rune read from input.
	items     chan item // channel of scanned items.
	token     [2]item   // two-token lookahead for parser.
	peekCount int
}

// next returns the next rune in the input.
func (l *reader) next() (rune rune) {
	rune, size, err := l.input.ReadRune()
	if err == nil {
		l.width, _ = l.current.WriteRune(rune)
		if size != l.width {
			fmt.Println("size: ", size, "width: ", l.width)
		}
		return rune
	} else {
		l.width = 0
		return eof
	}
	panic("")
}

// peek returns but does not consume the next rune in the input.
func (l *reader) peek() rune {
	rune := l.next()
	l.backup()
	return rune
}

// backup steps back one rune. Can only be called once per call of next.
func (l *reader) backup() {
	if l.width > 0 {
		err := l.input.UnreadRune()
		if err != nil {
			fmt.Println("err: ", err)
		}
		l.current.Truncate(l.current.Len() - l.width)
	}
}

// emit passes an item back to the client.
func (l *reader) emit(t itemType) {
	l.items <- item{t, l.current.String()}
	l.current.Reset()
	l.width = 0
}

// accept consumes the next rune if it's from the valid set.
func (l *reader) accept(valid string) bool {
	if strings.IndexRune(valid, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

// acceptRun consumes a run of runes from the valid set.
func (l *reader) acceptRun(valid string) {
	for strings.IndexRune(valid, l.next()) >= 0 {
	}
	l.backup()
}

// error returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.run.
func (l *reader) errorf(format string, args ...interface{}) stateFn {
	l.items <- item{itemError, fmt.Sprintf(format, args...)}
	return nil
}

// nextItem returns the next item from the input.
func (l *reader) nextItemFromInput() item {
	for {
		select {
		case item := <-l.items:
			return item
		default:
			l.state = l.state(l)
		}
	}
	panic("not reached")
}

// next returns the next item taking into account peek
func (l *reader) nextItem() item {
	if l.peekCount > 0 {
		l.peekCount--
	} else {
		l.token[0] = l.nextItemFromInput()
	}
	return l.token[l.peekCount]
}

// backup backs the input stream up one token.
func (l *reader) backupItem() {
	l.peekCount++
}

// peek returns but does not consume the next token.
func (l *reader) peekItem() item {
	if l.peekCount > 0 {
		return l.token[l.peekCount-1]
	}
	l.peekCount = 1
	l.token[0] = l.nextItemFromInput()

	return l.token[0]
}

// lex creates a new scanner for the input string.
func newReader(name string, input io.RuneScanner) *reader {
	l := &reader{
		name:  name,
		input: input,
		state: lexItem,
		items: make(chan item, 2), // Two items of buffering is sufficient for all state functions
	}
	return l
}

// state functions

// lexItem ...
func lexItem(l *reader) stateFn {
	switch r := l.next(); {
	case r == eof:
		l.emit(itemEOF)
		return nil
	// case r == '§':
	// 	if l.peek() == ' ' {
	// 		l.next()
	// 	}
	// 	l.emit(itemSection)
	// 	return lexItem
	case r == '"':
		return lexQuote
	case r == '+' || r == '-':
		rr := l.peek()
		if isPunctuation(rr) {
			l.emit(itemWord)
			return lexItem
		} else if '0' <= rr && rr <= '9' {
			return lexNumber
		} else {
			l.emit(itemPunctuation)
			return lexItem
		}
	case r == '+' || r == '-' || ('0' <= r && r <= '9'):
		l.backup()
		return lexNumber
	case isPunctuation(r):
		l.backup()
		return lexPunctuation
	default:
		l.backup()
		return lexWord
	}
	return nil
}

// lexWord scans an alphanumeric
func lexWord(l *reader) stateFn {
top:
	switch r := l.next(); {
	case isPunctuation(r):
		l.backup()
		l.emit(itemWord)
	default:
		goto top
	}
	return lexItem
}

func lexPunctuation(l *reader) stateFn {
	switch l.next() {
	case '\n':
		l.emit(itemNewline)
	case '(':
		l.emit(itemOpenParenthesis)
	case ')':
		l.emit(itemCloseParenthesis)
	case '{':
		l.emit(itemOpenCurlyBrace)
	case '}':
		l.emit(itemCloseCurlyBrace)
	case '\'':
		l.emit(itemQuote)
	case ' ':
		l.emit(itemSpace)
	case '.':
		l.emit(itemPeriod)
	case '\f':
		l.emit(itemPageBreak)
	case '§':
		l.emit(itemSection)
	case ',', ':', ';', '!', '-':
		l.emit(itemPunctuation)
	default:
		l.emit(itemPunctuation)
		//panic("???")
	}
	return lexItem
}

// lexNumber scans a number: decimal, octal, hex, float, or imaginary.  This
// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
// and "089" - but when it's wrong the input is invalid and the parser (via
// strconv) will notice.
func lexNumber(l *reader) stateFn {
	if !l.scanNumber() {
		return l.errorf("bad number syntax: %q", l.current.String())
	}
	l.emit(itemNumber)
	return lexPunctuation
}

func (l *reader) scanNumber() bool {
	// Optional leading sign.
	l.accept("+-")
	// Is it hex?
	digits := "0123456789"
	if l.accept("0") && l.accept("xX") {
		digits = "0123456789abcdefABCDEF"
	}
	l.acceptRun(digits)
	if l.accept(".") {
		l.acceptRun(digits)
	}
	if l.accept("/") {
		l.acceptRun(digits)
	}
	if l.accept("eE") {
		l.accept("+-")
		l.acceptRun("0123456789")
	}
	// Is it imaginary?
	l.accept("i")
	// Next thing mustn't be alphanumeric.
	if isAlphaNumeric(l.peek()) {
		l.next()
		return false
	}
	return true
}

// lexQuote scans a quoted string.
func lexQuote(l *reader) stateFn {
Loop:
	for {
		switch l.next() {
		case '\\':
			if r := l.next(); r != eof && r != '\n' {
				break
			}
			fallthrough
		case eof, '\n':
			return l.errorf("unterminated quoted string")
		case '"':
			break Loop
		}
	}
	l.emit(itemString)
	return lexPunctuation
}

// isPunctuation reports whether r is a punctuation character.
func isPunctuation(r rune) bool {
	switch r {
	case ' ', '\t', '\n', '\r', '§', '\f', '(', ')', '{', '}', '\'', '-', eof:
		return true
	case '.', '!', ',', ':':
		return true
	}
	return false
}

// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
func isAlphaNumeric(r rune) bool {
	return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
}

func (reader *reader) read() (term Term) {
	switch token := reader.nextItem(); token.typ {
	case itemString:
		term = String(strings.Trim(token.val, string("\"")))
	case itemNumber:
		num := big.NewRat(0, 1)
		num.SetString(token.val)
		term = &Number{num}
	case itemOpenParenthesis:
		part := &partDescription{ignore: 1<<itemSpace | 1<<itemCloseParenthesis, end: 1 << itemCloseParenthesis}
		term = Tuple(reader.readPart(part))
	case itemOpenCurlyBrace:
		part := &partDescription{ignore: 1<<itemSpace | 1<<itemCloseCurlyBrace, end: 1 << itemCloseCurlyBrace}
		term = Application(reader.readPart(part))
	case itemEOF:
		reader.backupItem()
		term = nil
	case itemError:
		term = Error(token.val)
	default:
		term = Symbol(token.val)
	}
	return
}

func Read(in io.RuneScanner) (result Term) {
	defer func() {
		switch x := recover().(type) {
		case Term:
			result = x
		case interface{}:
			result = Error(fmt.Sprintf("%v", x))
		}
	}()
	reader := newReader("", in)
	result = reader.read()
	return
}

type partDescription struct {
	ignore, start, end uint64
	sub                *partDescription
}

func (reader *reader) readPart(part *partDescription) (result Part) {
	first := true
	for {
		token := reader.peekItem()
		if token.typ == itemEOF {
			break
		}
		if first == false {
			if 1<<token.typ&part.start != 0 {
				break
			}
		} else {
			first = false
		}
		if 1<<token.typ&part.ignore != 0 {
			reader.nextItem()
		} else {
			var t Term
			if part.sub == nil || 1<<token.typ&part.end != 0 {
				t = reader.read()
			} else {
				t = reader.readPart(part.sub)
			}
			result = append(result, t)
		}
		if 1<<token.typ&part.end != 0 {
			break
		}
	}
	return
}

func ReadDocument(in io.RuneScanner) Part {
	line := &partDescription{end: 1 << itemNewline}
	part := &partDescription{end: 1 << itemNewline, sub: line}
	section := &partDescription{start: 1 << itemSection, sub: part}
	document := &partDescription{sub: section}
	return newReader("", in).readPart(document)
}

func ReadSentence(in io.RuneScanner) []Term {
	line := &partDescription{ignore: 1<<itemPeriod | 1<<itemSpace | 1<<itemNewline, end: 1 << itemPeriod}
	return newReader("", in).readPart(line)
}
<|endoftext|>


<commit_before>package gerrittest

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"

	"github.com/opalmer/gerrittest/internal"
	log "github.com/sirupsen/logrus"
)

var (
	// GitCommand is the command used to run git commands.
	GitCommand = "git"

	// DefaultGitCommands contains a mapping of git commands
	// to their arguments. This is used by Repository for running
	// git commands.
	DefaultGitCommands = map[string][]string{
		"status":              {"status", "--porcelain"},
		"config":              {"config", "--local"},
		"add":                 {"add", "--force"},
		"remote-add":          {"remote", "add"},
		"get-remote-url":      {"remote", "get-url"},
		"commit":              {"commit", "--allow-empty", "--message"},
		"push":                {"push", "--porcelain"},
		"last-commit-message": {"log", "-n", "1", "--format=medium"},
		"amend":               {"commit", "--amend", "--no-edit", "--allow-empty"},
	}

	// ErrRemoteDoesNotExist is returned by GetRemoteURL if the requested
	// remote does not appear to exist.
	ErrRemoteDoesNotExist = errors.New("requested remote does not exist")

	//ErrFailedToLocateChange is returned by functions, such as Push(), that
	// expect to find a change number in the output from git.
	ErrFailedToLocateChange = errors.New("failed to locate ChangeID")

	// ErrNoCommits is returned by ChangeID if there are not any commits
	// to the repository yet.
	ErrNoCommits = errors.New("no commits")

	// RegexChangeID is used to match the Change-Id for a commit.
	RegexChangeID = regexp.MustCompile(`(?m)^\s+Change-Id: (I[a-f0-9]{40}).*$`)
)

// Repository is used to store information about an interact
// with a git repository. In the end, this is a thin wrapper
// around GitConfig commands.
type Repository struct {
	SSHCommand string
	Root       string
	Username   string
}

// setEnvironment sets up the environment for the given command.
func (r *Repository) setEnvironment(cmd *exec.Cmd) error {
	// We'll need the user to set $HOME otherwise some git commands won't work.
	usr, err := user.Current()
	if err != nil {
		return err
	}
	cmd.Env = append(cmd.Env, fmt.Sprintf("HOME=%s", usr.HomeDir))

	// Set environment variables to ensure the proper ssh command is run. Not
	// all versions of git support core.sshCommand from the config.
	for _, key := range []string{"GIT_SSH_COMMAND", "GIT_SSH"} {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, r.SSHCommand))
	}
	return nil
}

func (r *Repository) run(cmd *exec.Cmd) (string, string, error) {
	cwd, err := os.Getwd()
	logger := log.WithFields(log.Fields{
		"phase": "run",
		"cmd":   strings.Join(cmd.Args, " "),
		"wd":    cwd,
	})
	if err != nil {
		return "", "", err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return "", "", err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return "", "", err
	}
	if err := cmd.Start(); err != nil {
		logger.WithError(err).Error()
		return "", "", err
	}
	bytesOut, err := ioutil.ReadAll(stdout)
	if err != nil {
		return "", "", err
	}
	bytesErr, err := ioutil.ReadAll(stderr)
	if err != nil {
		return string(bytesOut), "", err
	}
	err = cmd.Wait()
	sOut := string(bytesOut)
	sErr := string(bytesErr)
	code := 0
	if exiterr, ok := err.(*exec.ExitError); ok {
		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			code = status.ExitStatus()
		}
	}
	logger.WithFields(log.Fields{
		"stdout": sOut,
		"stderr": sErr,
		"code":   code,
	}).Debug()
	return string(bytesOut), string(bytesErr), err
}

// Git runs git with the provided arguments. This also ensures the proper
// working path and environment are set before calling git.
func (r *Repository) Git(args []string) (string, string, error) {
	workdir, err := os.Getwd()
	if err != nil {
		return "", "", err
	}

	// Change directories to the directory of the repository. Technically
	// there's a -C flag but not all versions of git have this flag and not
	// all subcommands respect it the same way.
	defer os.Chdir(workdir) // nolint: errcheck
	if err := os.Chdir(r.Root); err != nil {
		return "", "", err
	}

	cmd := exec.Command(GitCommand, args...)
	if err := r.setEnvironment(cmd); err != nil {
		return "", "", err
	}
	return r.run(cmd)
}

// Status returns the current status of the repository.
func (r *Repository) Status() (string, error) {
	stdout, _, err := r.Git(DefaultGitCommands["status"])
	return stdout, err
}

// Add is similar to Add() except it allows content to be created in
// addition to be added to the repo.
func (r *Repository) Add(path string, mode os.FileMode, content []byte) error {
	absolute := filepath.Join(r.Root, path)
	if err := os.MkdirAll(filepath.Dir(absolute), 0700); err != nil {
		return err
	}

	if err := ioutil.WriteFile(absolute, content, mode); err != nil {
		return err
	}

	_, _, err := r.Git(append(DefaultGitCommands["add"], path))
	return err
}

// Remove removes the requested content from the repository.
func (r *Repository) Remove(path string) error {
	absolute := filepath.Join(r.Root, path)
	stat, err := os.Stat(absolute)
	if os.IsNotExist(err) {
		return nil
	}
	args := []string{"rm"}
	if stat.IsDir() {
		args = append(args, "-r")
	}
	args = append(args, path)
	_, _, err = r.Git(args)
	return err
}

// Commit will add a new commit to the repository with the
// given message.
func (r *Repository) Commit(message string) error {
	_, _, err := r.Git(append(DefaultGitCommands["commit"], message))
	return err
}

// Push will push changes to the given remote and reference. `ref`
// will default to 'HEAD:refs/for/master' if not provided.
func (r *Repository) Push(ref string) error {
	if ref == "" {
		ref = "HEAD:refs/for/master"
	}

	_, _, err := r.Git(append(DefaultGitCommands["push"], "origin", ref))
	return err
}

// GetRemoteURL will return the url for the given remote name. If the requested
// remote does not exist ErrRemoteDoesNotExist will be returned.
func (r *Repository) GetRemoteURL(name string) (string, error) {
	stdout, stderr, err := r.Git(append(DefaultGitCommands["get-remote-url"], name))
	if err != nil && strings.Contains(stderr, "No such remote") {
		return "", ErrRemoteDoesNotExist
	}
	return strings.TrimSpace(stdout), nil
}

// AddRemote will add a remote with the given name so long as it does not
// already exist.
func (r *Repository) AddRemote(name string, uri string) error {
	if _, err := r.GetRemoteURL(name); err == nil {
		return nil
	}
	_, _, err := r.Git(append(DefaultGitCommands["remote-add"], name, uri))
	return err
}

// AddOriginFromContainer adds a new remote based on the provided container.
func (r *Repository) AddOriginFromContainer(container *Container, project string) error {
	return r.AddRemote("origin", fmt.Sprintf(
		"ssh://%s@%s:%d/%s", r.Username,
		container.SSH.Address, container.SSH.Public, project))
}

// ChangeID returns a string representing the change id of the last
// commit.
func (r *Repository) ChangeID() (string, error) {
	stdout, stderr, err := r.Git(DefaultGitCommands["last-commit-message"])
	if strings.Contains(stderr, "does not have any commits yet") {
		return "", ErrNoCommits
	}

	if err != nil {
		return "", err
	}

	matches := RegexChangeID.FindAllStringSubmatch(stdout, -1)
	if len(matches) > 0 && len(matches[0]) > 1 {
		return matches[0][1], nil
	}
	return "", ErrFailedToLocateChange
}

// Amend amends the current commit.
func (r *Repository) Amend() error {
	_, stderr, err := r.Git(DefaultGitCommands["amend"])
	if strings.Contains(stderr, "You have nothing to amend") {
		err = ErrNoCommits
	}
	return err
}

// Destroy will remove the entire repository from disk, useful for temporary
// repositories. This cannot be reversed.
func (r *Repository) Destroy() error {
	return os.RemoveAll(r.Root)
}

// NewRepository constructs and returns a *Repository struct. It will also
// ensure the repository is properly setup before returning.
func NewRepository(config *Config) (*Repository, error) {
	root, err := ioutil.TempDir("", fmt.Sprintf("%s-", ProjectName))
	if err != nil {
		return nil, err
	}

	repo := &Repository{
		Username:   config.GitConfig["user.name"],
		SSHCommand: config.GitConfig["core.sshCommand"],
		Root:       root,
	}

	if _, _, err := repo.Git([]string{"init", "--quiet"}); err != nil {
		repo.Destroy() // nolint: errcheck
		return nil, err
	}

	if err := ioutil.WriteFile(
		filepath.Join(repo.Root, ".git", "hooks", "commit-msg"),
		internal.MustAsset("internal/commit-msg"), 0700); err != nil {
		repo.Destroy() // nolint: errcheck
		return nil, err
	}

	for key, value := range config.GitConfig {
		if _, _, err := repo.Git(append(DefaultGitCommands["config"], key, value)); err != nil {
			return nil, err
		}
	}

	return repo, nil
}
<commit_msg>AddRemote bugfix<commit_after>package gerrittest

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"

	"github.com/opalmer/gerrittest/internal"
	log "github.com/sirupsen/logrus"
)

var (
	// GitCommand is the command used to run git commands.
	GitCommand = "git"

	// DefaultGitCommands contains a mapping of git commands
	// to their arguments. This is used by Repository for running
	// git commands.
	DefaultGitCommands = map[string][]string{
		"status":              {"status", "--porcelain"},
		"config":              {"config", "--local"},
		"add":                 {"add", "--force"},
		"remote-add":          {"remote", "add"},
		"get-remote-url":      {"remote", "get-url"},
		"commit":              {"commit", "--allow-empty", "--message"},
		"push":                {"push", "--porcelain"},
		"last-commit-message": {"log", "-n", "1", "--format=medium"},
		"amend":               {"commit", "--amend", "--no-edit", "--allow-empty"},
	}

	// ErrRemoteDoesNotExist is returned by GetRemote if the requested
	// remote does not appear to exist.
	ErrRemoteDoesNotExist = errors.New("requested remote does not exist")

	//ErrFailedToLocateChange is returned by functions, such as Push(), that
	// expect to find a change number in the output from git.
	ErrFailedToLocateChange = errors.New("failed to locate ChangeID")

	// ErrRemoteDiffers is returned by any function that adds a remote
	// where the named remote already exists but with a different url.
	ErrRemoteDiffers = errors.New(
		"the requested remote exists but with a different url")

	// ErrNoCommits is returned by ChangeID if there are not any commits
	// to the repository yet.
	ErrNoCommits = errors.New("no commits")

	// RegexChangeID is used to match the Change-Id for a commit.
	RegexChangeID = regexp.MustCompile(`(?m)^\s+Change-Id: (I[a-f0-9]{40}).*$`)
)

// Repository is used to store information about an interact
// with a git repository. In the end, this is a thin wrapper
// around GitConfig commands.
type Repository struct {
	log        *log.Entry
	SSHCommand string
	Root       string
	Username   string
}

// setEnvironment sets up the environment for the given command.
func (r *Repository) setEnvironment(cmd *exec.Cmd) error {
	// We'll need the user to set $HOME otherwise some git commands won't work.
	usr, err := user.Current()
	if err != nil {
		return err
	}
	cmd.Env = append(cmd.Env, fmt.Sprintf("HOME=%s", usr.HomeDir))

	// Set environment variables to ensure the proper ssh command is run. Not
	// all versions of git support core.sshCommand from the config.
	for _, key := range []string{"GIT_SSH_COMMAND", "GIT_SSH"} {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, r.SSHCommand))
	}
	return nil
}

func (r *Repository) run(cmd *exec.Cmd) (string, string, error) {
	cwd, err := os.Getwd()
	logger := r.log.WithFields(log.Fields{
		"phase": "run",
		"cmd":   strings.Join(cmd.Args, " "),
		"wd":    cwd,
	})
	if err != nil {
		return "", "", err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return "", "", err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return "", "", err
	}
	if err := cmd.Start(); err != nil {
		logger.WithError(err).Error()
		return "", "", err
	}
	bytesOut, err := ioutil.ReadAll(stdout)
	if err != nil {
		return "", "", err
	}
	bytesErr, err := ioutil.ReadAll(stderr)
	if err != nil {
		return string(bytesOut), "", err
	}
	err = cmd.Wait()
	sOut := string(bytesOut)
	sErr := string(bytesErr)
	code := 0
	if exiterr, ok := err.(*exec.ExitError); ok {
		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			code = status.ExitStatus()
		}
	}
	logger.WithFields(log.Fields{
		"stdout": sOut,
		"stderr": sErr,
		"code":   code,
	}).Debug()
	return string(bytesOut), string(bytesErr), err
}

// Git runs git with the provided arguments. This also ensures the proper
// working path and environment are set before calling git.
func (r *Repository) Git(args []string) (string, string, error) {
	workdir, err := os.Getwd()
	if err != nil {
		return "", "", err
	}

	// Change directories to the directory of the repository. Technically
	// there's a -C flag but not all versions of git have this flag and not
	// all subcommands respect it the same way.
	defer os.Chdir(workdir) // nolint: errcheck
	if err := os.Chdir(r.Root); err != nil {
		return "", "", err
	}

	cmd := exec.Command(GitCommand, args...)
	if err := r.setEnvironment(cmd); err != nil {
		return "", "", err
	}
	return r.run(cmd)
}

// Status returns the current status of the repository.
func (r *Repository) Status() (string, error) {
	stdout, _, err := r.Git(DefaultGitCommands["status"])
	return stdout, err
}

// Add is similar to Add() except it allows content to be created in
// addition to be added to the repo.
func (r *Repository) Add(path string, mode os.FileMode, content []byte) error {
	absolute := filepath.Join(r.Root, path)
	if err := os.MkdirAll(filepath.Dir(absolute), 0700); err != nil {
		return err
	}

	if err := ioutil.WriteFile(absolute, content, mode); err != nil {
		return err
	}

	_, _, err := r.Git(append(DefaultGitCommands["add"], path))
	return err
}

// Remove removes the requested content from the repository.
func (r *Repository) Remove(path string) error {
	absolute := filepath.Join(r.Root, path)
	stat, err := os.Stat(absolute)
	if os.IsNotExist(err) {
		return nil
	}
	args := []string{"rm"}
	if stat.IsDir() {
		args = append(args, "-r")
	}
	args = append(args, path)
	_, _, err = r.Git(args)
	return err
}

// Commit will add a new commit to the repository with the
// given message.
func (r *Repository) Commit(message string) error {
	_, _, err := r.Git(append(DefaultGitCommands["commit"], message))
	return err
}

// Push will push changes to the given remote and reference. `ref`
// will default to 'HEAD:refs/for/master' if not provided.
func (r *Repository) Push(ref string) error {
	if ref == "" {
		ref = "HEAD:refs/for/master"
	}

	_, _, err := r.Git(append(DefaultGitCommands["push"], "origin", ref))
	return err
}

// GetRemote will return the url for the given remote name. If the requested
// remote does not exist ErrRemoteDoesNotExist will be returned.
func (r *Repository) GetRemote(name string) (string, error) {
	stdout, stderr, err := r.Git(append(DefaultGitCommands["get-remote-url"], name))
	if err != nil && strings.Contains(stderr, "No such remote") {
		return "", ErrRemoteDoesNotExist
	}
	return strings.TrimSpace(stdout), nil
}

// AddRemote will add a remote with the given name so long as it does not
// already exist.
func (r *Repository) AddRemote(name string, uri string) error {
	logger := r.log.WithFields(log.Fields{
		"phase": "add-remote",
		"name":  name,
		"uri":   uri,
	})
	logger.Debug()
	remote, err := r.GetRemote(name)
	if err == ErrRemoteDoesNotExist {
		_, _, err := r.Git(append(DefaultGitCommands["remote-add"], name, uri))
		return err
	}
	if err == nil && remote != uri {
		logger.WithField("existing-uri", remote).Warn()
		return ErrRemoteDiffers
	}
	return err
}

// AddOriginFromContainer adds a new remote based on the provided container.
func (r *Repository) AddOriginFromContainer(container *Container, project string) error {
	return r.AddRemote("origin", fmt.Sprintf(
		"ssh://%s@%s:%d/%s", r.Username,
		container.SSH.Address, container.SSH.Public, project))
}

// ChangeID returns a string representing the change id of the last
// commit.
func (r *Repository) ChangeID() (string, error) {
	stdout, stderr, err := r.Git(DefaultGitCommands["last-commit-message"])
	if strings.Contains(stderr, "does not have any commits yet") {
		return "", ErrNoCommits
	}

	if err != nil {
		return "", err
	}

	matches := RegexChangeID.FindAllStringSubmatch(stdout, -1)
	if len(matches) > 0 && len(matches[0]) > 1 {
		return matches[0][1], nil
	}
	return "", ErrFailedToLocateChange
}

// Amend amends the current commit.
func (r *Repository) Amend() error {
	_, stderr, err := r.Git(DefaultGitCommands["amend"])
	if strings.Contains(stderr, "You have nothing to amend") {
		err = ErrNoCommits
	}
	return err
}

// Destroy will remove the entire repository from disk, useful for temporary
// repositories. This cannot be reversed.
func (r *Repository) Destroy() error {
	return os.RemoveAll(r.Root)
}

// NewRepository constructs and returns a *Repository struct. It will also
// ensure the repository is properly setup before returning.
func NewRepository(config *Config) (*Repository, error) {
	root, err := ioutil.TempDir("", fmt.Sprintf("%s-", ProjectName))
	if err != nil {
		return nil, err
	}

	repo := &Repository{
		log:        log.WithField("cmp", "repo"),
		Username:   config.GitConfig["user.name"],
		SSHCommand: config.GitConfig["core.sshCommand"],
		Root:       root,
	}

	if _, _, err := repo.Git([]string{"init", "--quiet"}); err != nil {
		repo.Destroy() // nolint: errcheck
		return nil, err
	}

	if err := ioutil.WriteFile(
		filepath.Join(repo.Root, ".git", "hooks", "commit-msg"),
		internal.MustAsset("internal/commit-msg"), 0700); err != nil {
		repo.Destroy() // nolint: errcheck
		return nil, err
	}

	for key, value := range config.GitConfig {
		if _, _, err := repo.Git(append(DefaultGitCommands["config"], key, value)); err != nil {
			return nil, err
		}
	}

	return repo, nil
}
<|endoftext|>


<commit_before><commit_msg>Race fixed in test<commit_after><|endoftext|>


<commit_before>// Copyright 2016 The etcd-operator Authors
//
// 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 version

var (
	Version = "0.6.0"
	GitSHA  = "Not provided (use ./build instead of go build)"
)
<commit_msg>version: bump to 0.6.0+git<commit_after>// Copyright 2016 The etcd-operator Authors
//
// 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 version

var (
	Version = "0.6.0+git"
	GitSHA  = "Not provided (use ./build instead of go build)"
)
<|endoftext|>


<commit_before>package version

type Version struct {
	GitCommit string
	Version string
}

var VersionInfo = Version{
	GitCommit: gitCommit,
	Version: version,
}

const gitCommit = "unknown"
const version = "0.1"
<commit_msg>Change to git sha<commit_after>package version

type Version struct {
	GitSHA string
	Version string
}

var VersionInfo = Version{
	GitSHA: gitSha,
	Version: version,
}

const gitSha = "unknown"
const version = "0.1"
<|endoftext|>


<commit_before>package version

// Version is the version of the build.
const Version = "0.1.24"
<commit_msg>bump to v0.1.25-dev<commit_after>package version

// Version is the version of the build.
const Version = "0.1.25-dev"
<|endoftext|>


<commit_before>package version

const (
	Version = "0.1.5+git"
)
<commit_msg>version: bump to v0.1.6<commit_after>package version

const (
	Version = "0.1.6"
)
<|endoftext|>


<commit_before>// Copyright (c) 2016, 2017 Evgeny Badin

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package version

// Version represents the application version using SemVer
const Version string = "0.2.2"
<commit_msg>Version bump<commit_after>// Copyright (c) 2016, 2017 Evgeny Badin

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package version

// Version represents the application version using SemVer
const Version string = "0.2.3"
<|endoftext|>