<commit_before>package pipeline

import (
	"fmt"
	"io"

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

var DefaultConfig = docker.Config{
	AttachStdin:  true,
	AttachStdout: true,
	AttachStderr: true,
	OpenStdin:    true,
	StdinOnce:    true,
}

func startContainer(opts docker.CreateContainerOptions) (string, error) {
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return "", err
	}
	container, err := client.CreateContainer(opts)
	if err != nil {
		return "", err
	}
	err = client.StartContainer(container.ID, opts.HostConfig)
	if err != nil {
		return "", err
	}

	return container.ID, nil
}

func stopContainer(id string) error {
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return err
	}
	return client.StopContainer(id, 5)
}

func pullImage(image string) error {
	repository, tag := docker.ParseRepositoryTag(image)
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return err
	}
	opts := docker.PullImageOptions{Repository: repository, Tag: "latest"}
	if tag != "" {
		opts.Tag = tag
	}
	return client.PullImage(opts, docker.AuthConfiguration{})
}

func pipeToStdin(id string, in io.Reader) error {
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return err
	}
	return client.AttachToContainer(docker.AttachToContainerOptions{
		Container:   id,
		InputStream: in,
		Stdin:       true,
		Stream:      true,
	})
}

func containerLogs(id string, out io.Writer) error {
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return err
	}
	return client.AttachToContainer(docker.AttachToContainerOptions{
		Container:    id,
		OutputStream: out,
		ErrorStream:  out,
		Stdout:       true,
		Stderr:       true,
		Logs:         true,
	})
}

func waitContainer(id string) (int, error) {
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return 0, err
	}

	return client.WaitContainer(id)
}

func isImageLocal(image string) (bool, error) {
	repository, tag := docker.ParseRepositoryTag(image)
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return false, err
	}
	images, err := client.ListImages(docker.ListImagesOptions{All: true, Digests: false})
	if err != nil {
		return false, err
	}
	expectedRepoTag := fmt.Sprintf("%s:%s", repository, tag)
	for _, image := range images {
		for _, repoTag := range image.RepoTags {
			if repoTag == expectedRepoTag {
				return true, nil
			}
		}
	}
	return false, nil
}
<commit_msg>If no tag is found, assume latest<commit_after>package pipeline

import (
	"fmt"
	"io"

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

var DefaultConfig = docker.Config{
	AttachStdin:  true,
	AttachStdout: true,
	AttachStderr: true,
	OpenStdin:    true,
	StdinOnce:    true,
}

func startContainer(opts docker.CreateContainerOptions) (string, error) {
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return "", err
	}
	container, err := client.CreateContainer(opts)
	if err != nil {
		return "", err
	}
	err = client.StartContainer(container.ID, opts.HostConfig)
	if err != nil {
		return "", err
	}

	return container.ID, nil
}

func stopContainer(id string) error {
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return err
	}
	return client.StopContainer(id, 5)
}

func pullImage(image string) error {
	repository, tag := docker.ParseRepositoryTag(image)
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return err
	}
	opts := docker.PullImageOptions{Repository: repository, Tag: "latest"}
	if tag != "" {
		opts.Tag = tag
	}
	return client.PullImage(opts, docker.AuthConfiguration{})
}

func pipeToStdin(id string, in io.Reader) error {
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return err
	}
	return client.AttachToContainer(docker.AttachToContainerOptions{
		Container:   id,
		InputStream: in,
		Stdin:       true,
		Stream:      true,
	})
}

func containerLogs(id string, out io.Writer) error {
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return err
	}
	return client.AttachToContainer(docker.AttachToContainerOptions{
		Container:    id,
		OutputStream: out,
		ErrorStream:  out,
		Stdout:       true,
		Stderr:       true,
		Logs:         true,
	})
}

func waitContainer(id string) (int, error) {
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return 0, err
	}

	return client.WaitContainer(id)
}

func isImageLocal(image string) (bool, error) {
	repository, tag := docker.ParseRepositoryTag(image)
	if tag == "" {
		tag = "latest"
	}
	client, err := docker.NewClientFromEnv()
	if err != nil {
		return false, err
	}
	images, err := client.ListImages(docker.ListImagesOptions{All: true, Digests: false})
	if err != nil {
		return false, err
	}
	expectedRepoTag := fmt.Sprintf("%s:%s", repository, tag)
	for _, image := range images {
		for _, repoTag := range image.RepoTags {
			if repoTag == expectedRepoTag {
				return true, nil
			}
		}
	}
	return false, nil
}
<|endoftext|>


<commit_before>// 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.

// Package build provides tools for building Go packages.
package build

import (
	"bytes"
	"exec"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
)

// Build produces a build Script for the given package.
func Build(tree *Tree, pkg string, info *DirInfo) (*Script, os.Error) {
	s := &Script{}
	b := &build{
		script: s,
		path:   filepath.Join(tree.SrcDir(), pkg),
	}
	b.obj = b.abs("_obj") + string(filepath.Separator)

	b.goarch = runtime.GOARCH
	if g := os.Getenv("GOARCH"); g != "" {
		b.goarch = g
	}
	var err os.Error
	b.arch, err = ArchChar(b.goarch)
	if err != nil {
		return nil, err
	}

	// .go files to be built with gc
	gofiles := b.abss(info.GoFiles...)
	s.addInput(gofiles...)

	var ofiles []string // object files to be linked or packed

	// make build directory
	b.mkdir(b.obj)
	s.addIntermediate(b.obj)

	// cgo
	if len(info.CgoFiles) > 0 {
		cgoFiles := b.abss(info.CgoFiles...)
		s.addInput(cgoFiles...)
		outGo, outObj := b.cgo(cgoFiles)
		gofiles = append(gofiles, outGo...)
		ofiles = append(ofiles, outObj...)
		s.addIntermediate(outGo...)
		s.addIntermediate(outObj...)
	}

	// compile
	if len(gofiles) > 0 {
		ofile := b.obj + "_go_." + b.arch
		b.gc(ofile, gofiles...)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	// assemble
	for _, sfile := range info.SFiles {
		ofile := b.obj + sfile[:len(sfile)-1] + b.arch
		sfile = b.abs(sfile)
		s.addInput(sfile)
		b.asm(ofile, sfile)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	if len(ofiles) == 0 {
		return nil, os.NewError("make: no object files to build")
	}

	// choose target file
	var targ string
	if info.IsCommand() {
		// use the last part of the import path as binary name
		_, bin := filepath.Split(pkg)
		if runtime.GOOS == "windows" {
			bin += ".exe"
		}
		targ = filepath.Join(tree.BinDir(), bin)
	} else {
		targ = filepath.Join(tree.PkgDir(), pkg+".a")
	}

	// make target directory
	targDir, _ := filepath.Split(targ)
	b.mkdir(targDir)

	// link binary or pack object
	if info.IsCommand() {
		b.ld(targ, ofiles...)
	} else {
		b.gopack(targ, ofiles...)
	}
	s.Output = append(s.Output, targ)

	return b.script, nil
}

// A Script describes the build process for a Go package.
// The Input, Intermediate, and Output fields are lists of absolute paths.
type Script struct {
	Cmd          []*Cmd
	Input        []string
	Intermediate []string
	Output       []string
}

func (s *Script) addInput(file ...string) {
	s.Input = append(s.Input, file...)
}

func (s *Script) addIntermediate(file ...string) {
	s.Intermediate = append(s.Intermediate, file...)
}

// Run runs the Script's Cmds in order.
func (s *Script) Run() os.Error {
	for _, c := range s.Cmd {
		if err := c.Run(); err != nil {
			return err
		}
	}
	return nil
}

// Stale returns true if the build's inputs are newer than its outputs.
func (s *Script) Stale() bool {
	var latest int64
	// get latest mtime of outputs
	for _, file := range s.Output {
		fi, err := os.Stat(file)
		if err != nil {
			// any error reading output files means stale
			return true
		}
		if m := fi.Mtime_ns; m > latest {
			latest = m
		}
	}
	for _, file := range s.Input {
		fi, err := os.Stat(file)
		if err != nil || fi.Mtime_ns > latest {
			// any error reading input files means stale
			// (attempt to rebuild to figure out why)
			return true
		}
	}
	return false
}

// Clean removes the Script's Intermediate files.
// It tries to remove every file and returns the first error it encounters.
func (s *Script) Clean() (err os.Error) {
	// Reverse order so that directories get removed after the files they contain.
	for i := len(s.Intermediate) - 1; i >= 0; i-- {
		if e := os.Remove(s.Intermediate[i]); err == nil {
			err = e
		}
	}
	return
}

// Clean removes the Script's Intermediate and Output files.
// It tries to remove every file and returns the first error it encounters.
func (s *Script) Nuke() (err os.Error) {
	// Reverse order so that directories get removed after the files they contain.
	for i := len(s.Output) - 1; i >= 0; i-- {
		if e := os.Remove(s.Output[i]); err == nil {
			err = e
		}
	}
	if e := s.Clean(); err == nil {
		err = e
	}
	return
}

// A Cmd describes an individual build command.
type Cmd struct {
	Args   []string // command-line
	Stdout string   // write standard output to this file, "" is passthrough
	Dir    string   // working directory
	Env    []string // environment
	Input  []string // file paths (dependencies)
	Output []string // file paths
}

func (c *Cmd) String() string {
	return strings.Join(c.Args, " ")
}

// Run executes the Cmd.
func (c *Cmd) Run() os.Error {
	out := new(bytes.Buffer)
	cmd := exec.Command(c.Args[0], c.Args[1:]...)
	cmd.Dir = c.Dir
	cmd.Env = c.Env
	cmd.Stdout = out
	cmd.Stderr = out
	if c.Stdout != "" {
		f, err := os.Create(c.Stdout)
		if err != nil {
			return err
		}
		defer f.Close()
		cmd.Stdout = f
	}
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("command %q: %v\n%v", c, err, out)
	}
	return nil
}

// ArchChar returns the architecture character for the given goarch.
// For example, ArchChar("amd64") returns "6".
func ArchChar(goarch string) (string, os.Error) {
	switch goarch {
	case "386":
		return "8", nil
	case "amd64":
		return "6", nil
	case "arm":
		return "5", nil
	}
	return "", os.NewError("unsupported GOARCH " + goarch)
}

type build struct {
	script *Script
	path   string
	obj    string
	goarch string
	arch   string
}

func (b *build) abs(file string) string {
	if filepath.IsAbs(file) {
		return file
	}
	return filepath.Join(b.path, file)
}

func (b *build) abss(file ...string) []string {
	s := make([]string, len(file))
	for i, f := range file {
		s[i] = b.abs(f)
	}
	return s
}

func (b *build) add(c Cmd) {
	b.script.Cmd = append(b.script.Cmd, &c)
}

func (b *build) mkdir(name string) {
	b.add(Cmd{
		Args:   []string{"mkdir", "-p", name},
		Output: []string{name},
	})
}

func (b *build) gc(ofile string, gofiles ...string) {
	gc := b.arch + "g"
	args := append([]string{gc, "-o", ofile}, gcImportArgs...)
	args = append(args, gofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  gofiles,
		Output: []string{ofile},
	})
}

func (b *build) asm(ofile string, sfile string) {
	asm := b.arch + "a"
	b.add(Cmd{
		Args:   []string{asm, "-o", ofile, sfile},
		Input:  []string{sfile},
		Output: []string{ofile},
	})
}

func (b *build) ld(targ string, ofiles ...string) {
	ld := b.arch + "l"
	args := append([]string{ld, "-o", targ}, ldImportArgs...)
	args = append(args, ofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) gopack(targ string, ofiles ...string) {
	b.add(Cmd{
		Args:   append([]string{"gopack", "grc", targ}, ofiles...),
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) cc(ofile string, cfiles ...string) {
	cc := b.arch + "c"
	dir := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
	inc := filepath.Join(runtime.GOROOT(), "pkg", dir)
	args := []string{cc, "-FVw", "-I", inc, "-o", ofile}
	b.add(Cmd{
		Args:   append(args, cfiles...),
		Input:  cfiles,
		Output: []string{ofile},
	})
}

func (b *build) gccCompile(ofile, cfile string) {
	b.add(Cmd{
		Args:   b.gccArgs("-o", ofile, "-c", cfile),
		Input:  []string{cfile},
		Output: []string{ofile},
	})
}

func (b *build) gccLink(ofile string, ofiles ...string) {
	b.add(Cmd{
		Args:   append(b.gccArgs("-o", ofile), ofiles...),
		Input:  ofiles,
		Output: []string{ofile},
	})
}

func (b *build) gccArgs(args ...string) []string {
	// TODO(adg): HOST_CC
	a := []string{"gcc", "-I", b.path, "-g", "-fPIC", "-O2"}
	switch b.arch {
	case "8":
		a = append(a, "-m32")
	case "6":
		a = append(a, "-m64")
	}
	return append(a, args...)
}

var cgoRe = regexp.MustCompile("[/\\:]")

func (b *build) cgo(cgofiles []string) (outGo, outObj []string) {
	// cgo
	// TODO(adg): CGOPKGPATH
	// TODO(adg): CGO_FLAGS
	gofiles := []string{b.obj + "_cgo_gotypes.go"}
	cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"}
	for _, fn := range cgofiles {
		f := b.obj + cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
		gofiles = append(gofiles, f+"cgo1.go")
		cfiles = append(cfiles, f+"cgo2.c")
	}
	defunC := b.obj + "_cgo_defun.c"
	output := append([]string{defunC}, cfiles...)
	output = append(output, gofiles...)
	b.add(Cmd{
		Args:   append([]string{"cgo", "--"}, cgofiles...),
		Dir:    b.path,
		Env:    append(os.Environ(), "GOARCH="+b.goarch),
		Input:  cgofiles,
		Output: output,
	})
	outGo = append(outGo, gofiles...)
	exportH := filepath.Join(b.path, "_cgo_export.h")
	b.script.addIntermediate(defunC, exportH, b.obj+"_cgo_flags")
	b.script.addIntermediate(cfiles...)

	// cc _cgo_defun.c
	defunObj := b.obj + "_cgo_defun." + b.arch
	b.cc(defunObj, defunC)
	outObj = append(outObj, defunObj)

	// gcc
	linkobj := make([]string, 0, len(cfiles))
	for _, cfile := range cfiles {
		ofile := cfile[:len(cfile)-1] + "o"
		b.gccCompile(ofile, cfile)
		linkobj = append(linkobj, ofile)
		if !strings.HasSuffix(ofile, "_cgo_main.o") {
			outObj = append(outObj, ofile)
		} else {
			b.script.addIntermediate(ofile)
		}
	}
	dynObj := b.obj + "_cgo_.o"
	b.gccLink(dynObj, linkobj...)
	b.script.addIntermediate(dynObj)

	// cgo -dynimport
	importC := b.obj + "_cgo_import.c"
	b.add(Cmd{
		Args:   []string{"cgo", "-dynimport", dynObj},
		Stdout: importC,
		Input:  []string{dynObj},
		Output: []string{importC},
	})
	b.script.addIntermediate(importC)

	// cc _cgo_import.ARCH
	importObj := b.obj + "_cgo_import." + b.arch
	b.cc(importObj, importC)
	outObj = append(outObj, importObj)

	return
}
<commit_msg>go/build: use back quotes for regular expression<commit_after>// 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.

// Package build provides tools for building Go packages.
package build

import (
	"bytes"
	"exec"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
)

// Build produces a build Script for the given package.
func Build(tree *Tree, pkg string, info *DirInfo) (*Script, os.Error) {
	s := &Script{}
	b := &build{
		script: s,
		path:   filepath.Join(tree.SrcDir(), pkg),
	}
	b.obj = b.abs("_obj") + string(filepath.Separator)

	b.goarch = runtime.GOARCH
	if g := os.Getenv("GOARCH"); g != "" {
		b.goarch = g
	}
	var err os.Error
	b.arch, err = ArchChar(b.goarch)
	if err != nil {
		return nil, err
	}

	// .go files to be built with gc
	gofiles := b.abss(info.GoFiles...)
	s.addInput(gofiles...)

	var ofiles []string // object files to be linked or packed

	// make build directory
	b.mkdir(b.obj)
	s.addIntermediate(b.obj)

	// cgo
	if len(info.CgoFiles) > 0 {
		cgoFiles := b.abss(info.CgoFiles...)
		s.addInput(cgoFiles...)
		outGo, outObj := b.cgo(cgoFiles)
		gofiles = append(gofiles, outGo...)
		ofiles = append(ofiles, outObj...)
		s.addIntermediate(outGo...)
		s.addIntermediate(outObj...)
	}

	// compile
	if len(gofiles) > 0 {
		ofile := b.obj + "_go_." + b.arch
		b.gc(ofile, gofiles...)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	// assemble
	for _, sfile := range info.SFiles {
		ofile := b.obj + sfile[:len(sfile)-1] + b.arch
		sfile = b.abs(sfile)
		s.addInput(sfile)
		b.asm(ofile, sfile)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	if len(ofiles) == 0 {
		return nil, os.NewError("make: no object files to build")
	}

	// choose target file
	var targ string
	if info.IsCommand() {
		// use the last part of the import path as binary name
		_, bin := filepath.Split(pkg)
		if runtime.GOOS == "windows" {
			bin += ".exe"
		}
		targ = filepath.Join(tree.BinDir(), bin)
	} else {
		targ = filepath.Join(tree.PkgDir(), pkg+".a")
	}

	// make target directory
	targDir, _ := filepath.Split(targ)
	b.mkdir(targDir)

	// link binary or pack object
	if info.IsCommand() {
		b.ld(targ, ofiles...)
	} else {
		b.gopack(targ, ofiles...)
	}
	s.Output = append(s.Output, targ)

	return b.script, nil
}

// A Script describes the build process for a Go package.
// The Input, Intermediate, and Output fields are lists of absolute paths.
type Script struct {
	Cmd          []*Cmd
	Input        []string
	Intermediate []string
	Output       []string
}

func (s *Script) addInput(file ...string) {
	s.Input = append(s.Input, file...)
}

func (s *Script) addIntermediate(file ...string) {
	s.Intermediate = append(s.Intermediate, file...)
}

// Run runs the Script's Cmds in order.
func (s *Script) Run() os.Error {
	for _, c := range s.Cmd {
		if err := c.Run(); err != nil {
			return err
		}
	}
	return nil
}

// Stale returns true if the build's inputs are newer than its outputs.
func (s *Script) Stale() bool {
	var latest int64
	// get latest mtime of outputs
	for _, file := range s.Output {
		fi, err := os.Stat(file)
		if err != nil {
			// any error reading output files means stale
			return true
		}
		if m := fi.Mtime_ns; m > latest {
			latest = m
		}
	}
	for _, file := range s.Input {
		fi, err := os.Stat(file)
		if err != nil || fi.Mtime_ns > latest {
			// any error reading input files means stale
			// (attempt to rebuild to figure out why)
			return true
		}
	}
	return false
}

// Clean removes the Script's Intermediate files.
// It tries to remove every file and returns the first error it encounters.
func (s *Script) Clean() (err os.Error) {
	// Reverse order so that directories get removed after the files they contain.
	for i := len(s.Intermediate) - 1; i >= 0; i-- {
		if e := os.Remove(s.Intermediate[i]); err == nil {
			err = e
		}
	}
	return
}

// Clean removes the Script's Intermediate and Output files.
// It tries to remove every file and returns the first error it encounters.
func (s *Script) Nuke() (err os.Error) {
	// Reverse order so that directories get removed after the files they contain.
	for i := len(s.Output) - 1; i >= 0; i-- {
		if e := os.Remove(s.Output[i]); err == nil {
			err = e
		}
	}
	if e := s.Clean(); err == nil {
		err = e
	}
	return
}

// A Cmd describes an individual build command.
type Cmd struct {
	Args   []string // command-line
	Stdout string   // write standard output to this file, "" is passthrough
	Dir    string   // working directory
	Env    []string // environment
	Input  []string // file paths (dependencies)
	Output []string // file paths
}

func (c *Cmd) String() string {
	return strings.Join(c.Args, " ")
}

// Run executes the Cmd.
func (c *Cmd) Run() os.Error {
	out := new(bytes.Buffer)
	cmd := exec.Command(c.Args[0], c.Args[1:]...)
	cmd.Dir = c.Dir
	cmd.Env = c.Env
	cmd.Stdout = out
	cmd.Stderr = out
	if c.Stdout != "" {
		f, err := os.Create(c.Stdout)
		if err != nil {
			return err
		}
		defer f.Close()
		cmd.Stdout = f
	}
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("command %q: %v\n%v", c, err, out)
	}
	return nil
}

// ArchChar returns the architecture character for the given goarch.
// For example, ArchChar("amd64") returns "6".
func ArchChar(goarch string) (string, os.Error) {
	switch goarch {
	case "386":
		return "8", nil
	case "amd64":
		return "6", nil
	case "arm":
		return "5", nil
	}
	return "", os.NewError("unsupported GOARCH " + goarch)
}

type build struct {
	script *Script
	path   string
	obj    string
	goarch string
	arch   string
}

func (b *build) abs(file string) string {
	if filepath.IsAbs(file) {
		return file
	}
	return filepath.Join(b.path, file)
}

func (b *build) abss(file ...string) []string {
	s := make([]string, len(file))
	for i, f := range file {
		s[i] = b.abs(f)
	}
	return s
}

func (b *build) add(c Cmd) {
	b.script.Cmd = append(b.script.Cmd, &c)
}

func (b *build) mkdir(name string) {
	b.add(Cmd{
		Args:   []string{"mkdir", "-p", name},
		Output: []string{name},
	})
}

func (b *build) gc(ofile string, gofiles ...string) {
	gc := b.arch + "g"
	args := append([]string{gc, "-o", ofile}, gcImportArgs...)
	args = append(args, gofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  gofiles,
		Output: []string{ofile},
	})
}

func (b *build) asm(ofile string, sfile string) {
	asm := b.arch + "a"
	b.add(Cmd{
		Args:   []string{asm, "-o", ofile, sfile},
		Input:  []string{sfile},
		Output: []string{ofile},
	})
}

func (b *build) ld(targ string, ofiles ...string) {
	ld := b.arch + "l"
	args := append([]string{ld, "-o", targ}, ldImportArgs...)
	args = append(args, ofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) gopack(targ string, ofiles ...string) {
	b.add(Cmd{
		Args:   append([]string{"gopack", "grc", targ}, ofiles...),
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) cc(ofile string, cfiles ...string) {
	cc := b.arch + "c"
	dir := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
	inc := filepath.Join(runtime.GOROOT(), "pkg", dir)
	args := []string{cc, "-FVw", "-I", inc, "-o", ofile}
	b.add(Cmd{
		Args:   append(args, cfiles...),
		Input:  cfiles,
		Output: []string{ofile},
	})
}

func (b *build) gccCompile(ofile, cfile string) {
	b.add(Cmd{
		Args:   b.gccArgs("-o", ofile, "-c", cfile),
		Input:  []string{cfile},
		Output: []string{ofile},
	})
}

func (b *build) gccLink(ofile string, ofiles ...string) {
	b.add(Cmd{
		Args:   append(b.gccArgs("-o", ofile), ofiles...),
		Input:  ofiles,
		Output: []string{ofile},
	})
}

func (b *build) gccArgs(args ...string) []string {
	// TODO(adg): HOST_CC
	a := []string{"gcc", "-I", b.path, "-g", "-fPIC", "-O2"}
	switch b.arch {
	case "8":
		a = append(a, "-m32")
	case "6":
		a = append(a, "-m64")
	}
	return append(a, args...)
}

var cgoRe = regexp.MustCompile(`[/\\:]`)

func (b *build) cgo(cgofiles []string) (outGo, outObj []string) {
	// cgo
	// TODO(adg): CGOPKGPATH
	// TODO(adg): CGO_FLAGS
	gofiles := []string{b.obj + "_cgo_gotypes.go"}
	cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"}
	for _, fn := range cgofiles {
		f := b.obj + cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
		gofiles = append(gofiles, f+"cgo1.go")
		cfiles = append(cfiles, f+"cgo2.c")
	}
	defunC := b.obj + "_cgo_defun.c"
	output := append([]string{defunC}, cfiles...)
	output = append(output, gofiles...)
	b.add(Cmd{
		Args:   append([]string{"cgo", "--"}, cgofiles...),
		Dir:    b.path,
		Env:    append(os.Environ(), "GOARCH="+b.goarch),
		Input:  cgofiles,
		Output: output,
	})
	outGo = append(outGo, gofiles...)
	exportH := filepath.Join(b.path, "_cgo_export.h")
	b.script.addIntermediate(defunC, exportH, b.obj+"_cgo_flags")
	b.script.addIntermediate(cfiles...)

	// cc _cgo_defun.c
	defunObj := b.obj + "_cgo_defun." + b.arch
	b.cc(defunObj, defunC)
	outObj = append(outObj, defunObj)

	// gcc
	linkobj := make([]string, 0, len(cfiles))
	for _, cfile := range cfiles {
		ofile := cfile[:len(cfile)-1] + "o"
		b.gccCompile(ofile, cfile)
		linkobj = append(linkobj, ofile)
		if !strings.HasSuffix(ofile, "_cgo_main.o") {
			outObj = append(outObj, ofile)
		} else {
			b.script.addIntermediate(ofile)
		}
	}
	dynObj := b.obj + "_cgo_.o"
	b.gccLink(dynObj, linkobj...)
	b.script.addIntermediate(dynObj)

	// cgo -dynimport
	importC := b.obj + "_cgo_import.c"
	b.add(Cmd{
		Args:   []string{"cgo", "-dynimport", dynObj},
		Stdout: importC,
		Input:  []string{dynObj},
		Output: []string{importC},
	})
	b.script.addIntermediate(importC)

	// cc _cgo_import.ARCH
	importObj := b.obj + "_cgo_import." + b.arch
	b.cc(importObj, importC)
	outObj = append(outObj, importObj)

	return
}
<|endoftext|>


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

// Godoc comment extraction and comment -> HTML formatting.

package doc

import (
	"go/ast";
	"io";
	"once";
	"regexp";
	"strings";
	"template";	// for htmlEscape
)

// Comment extraction

var (
	comment_markers *regexp.Regexp;
	trailing_whitespace *regexp.Regexp;
	comment_junk *regexp.Regexp;
)

func makeRex(s string) *regexp.Regexp {
	re, err := regexp.Compile(s);
	if err != nil {
		panic("MakeRegexp ", s, " ", err.String());
	}
	return re;
}

// TODO(rsc): Cannot use var initialization for regexps,
// because Regexp constructor needs threads.
func setupRegexps() {
	comment_markers = makeRex("^/[/*] ?");
	trailing_whitespace = makeRex("[ \t\r]+$");
	comment_junk = makeRex("^[ \t]*(/\\*|\\*/)[ \t]*$");
}

// CommentText returns the text of comment,
// with the comment markers - //, /*, and */ - removed.
func CommentText(comment *ast.CommentGroup) string {
	if comment == nil {
		return "";
	}
	comments := make([]string, len(comment.List));
	for i, c := range comment.List {
		comments[i] = string(c.Text);
	}

	once.Do(setupRegexps);
	lines := make([]string, 0, 20);
	for _, c := range comments {
		// split on newlines
		cl := strings.Split(c, "\n", 0);

		// walk lines, stripping comment markers
		w := 0;
		for _, l := range cl {
			// remove /* and */ lines
			if comment_junk.MatchString(l) {
				continue;
			}

			// strip trailing white space
			m := trailing_whitespace.ExecuteString(l);
			if len(m) > 0 {
				l = l[0 : m[1]];
			}

			// strip leading comment markers
			m = comment_markers.ExecuteString(l);
			if len(m) > 0 {
				l = l[m[1] : len(l)];
			}

			// throw away leading blank lines
			if w == 0 && l == "" {
				continue;
			}

			cl[w] = l;
			w++;
		}

		// throw away trailing blank lines
		for w > 0 && cl[w-1] == "" {
			w--;
		}
		cl = cl[0 : w];

		// add this comment to total list
		// TODO: maybe separate with a single blank line
		// if there is already a comment and len(cl) > 0?
		for _, l := range cl {
			n := len(lines);
			if n+1 >= cap(lines) {
				newlines := make([]string, n, 2*cap(lines));
				for k := range newlines {
					newlines[k] = lines[k];
				}
				lines = newlines;
			}
			lines = lines[0 : n+1];
			lines[n] = l;
		}
	}

	// add final "" entry to get trailing newline.
	// loop always leaves room for one more.
	n := len(lines);
	lines = lines[0 : n+1];

	return strings.Join(lines, "\n");
}

// Split bytes into lines.
func split(text []byte) [][]byte {
	// count lines
	n := 0;
	last := 0;
	for i, c := range text {
		if c == '\n' {
			last = i+1;
			n++;
		}
	}
	if last < len(text) {
		n++;
	}

	// split
	out := make([][]byte, n);
	last = 0;
	n = 0;
	for i, c := range text {
		if c == '\n' {
			out[n] = text[last : i+1];
			last = i+1;
			n++;
		}
	}
	if last < len(text) {
		out[n] = text[last : len(text)];
	}

	return out;
}

var (
	ldquo = strings.Bytes("“");
	rdquo = strings.Bytes("”");
)

// Escape comment text for HTML.
// Also, turn `` into “ and '' into ”.
func commentEscape(w io.Writer, s []byte) {
	last := 0;
	for i := 0; i < len(s)-1; i++ {
		if s[i] == s[i+1] && (s[i] == '`' || s[i] == '\'') {
			template.HtmlEscape(w, s[last : i]);
			last = i+2;
			switch s[i] {
			case '`':
				w.Write(ldquo);
			case '\'':
				w.Write(rdquo);
			}
			i++;	// loop will add one more
		}
	}
	template.HtmlEscape(w, s[last : len(s)]);
}

var (
	html_p = strings.Bytes("
\n");
	html_endp = strings.Bytes("
\n");
	html_pre = strings.Bytes("
");
	html_endpre = strings.Bytes("
\n");
)

func indentLen(s []byte) int {
	i := 0;
	for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
		i++;
	}
	return i;
}

func isBlank(s []byte) bool {
	return len(s) == 0 || (len(s) == 1 && s[0] == '\n')
}

func commonPrefix(a, b []byte) []byte {
	i := 0;
	for i < len(a) && i < len(b) && a[i] == b[i] {
		i++;
	}
	return a[0 : i];
}

func unindent(block [][]byte) {
	if len(block) == 0 {
		return;
	}

	// compute maximum common white prefix
	prefix := block[0][0 : indentLen(block[0])];
	for _, line := range block {
		if !isBlank(line) {
			prefix = commonPrefix(prefix, line[0 : indentLen(line)]);
		}
	}
	n := len(prefix);

	// remove
	for i, line := range block {
		if !isBlank(line) {
			block[i] = line[n : len(line)];
		}
	}
}

// Convert comment text to formatted HTML.
// The comment was prepared by DocReader,
// so it is known not to have leading, trailing blank lines
// nor to have trailing spaces at the end of lines.
// The comment markers have already been removed.
//
// Turn each run of multiple \n into

// Turn each run of indented lines into
 without indent.
//
// TODO(rsc): I'd like to pass in an array of variable names []string
// and then italicize those strings when they appear as words.
func ToHtml(w io.Writer, s []byte) {
	inpara := false;

	/* TODO(rsc): 6g cant generate code for these
	close := func() {
		if inpara {
			w.Write(html_endp);
			inpara = false;
		}
	};
	open := func() {
		if !inpara {
			w.Write(html_p);
			inpara = true;
		}
	};
	*/

	lines := split(s);
	unindent(lines);
	for i := 0; i < len(lines);  {
		line := lines[i];
		if isBlank(line) {
			// close paragraph
			if inpara {
				w.Write(html_endp);
				inpara = false;
			}
			i++;
			continue;
		}
		if indentLen(line) > 0 {
			// close paragraph
			if inpara {
				w.Write(html_endp);
				inpara = false;
			}

			// count indented or blank lines
			j := i+1;
			for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) {
				j++;
			}
			// but not trailing blank lines
			for j > i && isBlank(lines[j-1]) {
				j--;
			}
			block := lines[i : j];
			i = j;

			unindent(block);

			// put those lines in a pre block.
			// they don't get the nice text formatting,
			// just html escaping
			w.Write(html_pre);
			for _, line := range block {
				template.HtmlEscape(w, line);
			}
			w.Write(html_endpre);
			continue;
		}
		// open paragraph
		if !inpara {
			w.Write(html_p);
			inpara = true;
		}
		commentEscape(w, lines[i]);
		i++;
	}
	if inpara {
		w.Write(html_endp);
		inpara = false;
	}
}

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

// Godoc comment extraction and comment -> HTML formatting.

package doc

import (
	"go/ast";
	"io";
	"once";
	"regexp";
	"strings";
	"template";	// for htmlEscape
)

// Comment extraction

var (
	comment_markers *regexp.Regexp;
	trailing_whitespace *regexp.Regexp;
	comment_junk *regexp.Regexp;
)

func makeRex(s string) *regexp.Regexp {
	re, err := regexp.Compile(s);
	if err != nil {
		panic("MakeRegexp ", s, " ", err.String());
	}
	return re;
}

// TODO(rsc): Cannot use var initialization for regexps,
// because Regexp constructor needs threads.
func setupRegexps() {
	comment_markers = makeRex("^/[/*] ?");
	trailing_whitespace = makeRex("[ \t\r]+$");
	comment_junk = makeRex("^[ \t]*(/\\*|\\*/)[ \t]*$");
}

// CommentText returns the text of comment,
// with the comment markers - //, /*, and */ - removed.
func CommentText(comment *ast.CommentGroup) string {
	if comment == nil {
		return "";
	}
	comments := make([]string, len(comment.List));
	for i, c := range comment.List {
		comments[i] = string(c.Text);
	}

	once.Do(setupRegexps);
	lines := make([]string, 0, 20);
	for _, c := range comments {
		// split on newlines
		cl := strings.Split(c, "\n", 0);

		// walk lines, stripping comment markers
		w := 0;
		for _, l := range cl {
			// remove /* and */ lines
			if comment_junk.MatchString(l) {
				continue;
			}

			// strip trailing white space
			m := trailing_whitespace.ExecuteString(l);
			if len(m) > 0 {
				l = l[0 : m[1]];
			}

			// strip leading comment markers
			m = comment_markers.ExecuteString(l);
			if len(m) > 0 {
				l = l[m[1] : len(l)];
			}

			cl[w] = l;
			w++;
		}
		cl = cl[0:w];

		// Add this comment to total list.
		for _, l := range cl {
			n := len(lines);
			if n+1 >= cap(lines) {
				newlines := make([]string, n, 2*cap(lines));
				for k := range newlines {
					newlines[k] = lines[k];
				}
				lines = newlines;
			}
			lines = lines[0 : n+1];
			lines[n] = l;
		}
	}

	// Remove leading blank lines; convert runs of
	// interior blank lines to a single blank line.
	n := 0;
	for _, line := range lines {
		if line != "" || n > 0 && lines[n-1] != "" {
			lines[n] = line;
			n++;
		}
	}
	lines = lines[0 : n];

	// Add final "" entry to get trailing newline from Join.
	// The original loop always leaves room for one more.
	if n > 0 && lines[n-1] != "" {
		lines = lines[0 : n+1];
		lines[n] = "";
	}

	return strings.Join(lines, "\n");
}

// Split bytes into lines.
func split(text []byte) [][]byte {
	// count lines
	n := 0;
	last := 0;
	for i, c := range text {
		if c == '\n' {
			last = i+1;
			n++;
		}
	}
	if last < len(text) {
		n++;
	}

	// split
	out := make([][]byte, n);
	last = 0;
	n = 0;
	for i, c := range text {
		if c == '\n' {
			out[n] = text[last : i+1];
			last = i+1;
			n++;
		}
	}
	if last < len(text) {
		out[n] = text[last : len(text)];
	}

	return out;
}

var (
	ldquo = strings.Bytes("“");
	rdquo = strings.Bytes("”");
)

// Escape comment text for HTML.
// Also, turn `` into “ and '' into ”.
func commentEscape(w io.Writer, s []byte) {
	last := 0;
	for i := 0; i < len(s)-1; i++ {
		if s[i] == s[i+1] && (s[i] == '`' || s[i] == '\'') {
			template.HtmlEscape(w, s[last : i]);
			last = i+2;
			switch s[i] {
			case '`':
				w.Write(ldquo);
			case '\'':
				w.Write(rdquo);
			}
			i++;	// loop will add one more
		}
	}
	template.HtmlEscape(w, s[last : len(s)]);
}

var (
	html_p = strings.Bytes("
\n");
	html_endp = strings.Bytes("
\n");
	html_pre = strings.Bytes("
");
	html_endpre = strings.Bytes("
\n");
)

func indentLen(s []byte) int {
	i := 0;
	for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
		i++;
	}
	return i;
}

func isBlank(s []byte) bool {
	return len(s) == 0 || (len(s) == 1 && s[0] == '\n')
}

func commonPrefix(a, b []byte) []byte {
	i := 0;
	for i < len(a) && i < len(b) && a[i] == b[i] {
		i++;
	}
	return a[0 : i];
}

func unindent(block [][]byte) {
	if len(block) == 0 {
		return;
	}

	// compute maximum common white prefix
	prefix := block[0][0 : indentLen(block[0])];
	for _, line := range block {
		if !isBlank(line) {
			prefix = commonPrefix(prefix, line[0 : indentLen(line)]);
		}
	}
	n := len(prefix);

	// remove
	for i, line := range block {
		if !isBlank(line) {
			block[i] = line[n : len(line)];
		}
	}
}

// Convert comment text to formatted HTML.
// The comment was prepared by DocReader,
// so it is known not to have leading, trailing blank lines
// nor to have trailing spaces at the end of lines.
// The comment markers have already been removed.
//
// Turn each run of multiple \n into

// Turn each run of indented lines into
 without indent.
//
// TODO(rsc): I'd like to pass in an array of variable names []string
// and then italicize those strings when they appear as words.
func ToHtml(w io.Writer, s []byte) {
	inpara := false;

	/* TODO(rsc): 6g cant generate code for these
	close := func() {
		if inpara {
			w.Write(html_endp);
			inpara = false;
		}
	};
	open := func() {
		if !inpara {
			w.Write(html_p);
			inpara = true;
		}
	};
	*/

	lines := split(s);
	unindent(lines);
	for i := 0; i < len(lines);  {
		line := lines[i];
		if isBlank(line) {
			// close paragraph
			if inpara {
				w.Write(html_endp);
				inpara = false;
			}
			i++;
			continue;
		}
		if indentLen(line) > 0 {
			// close paragraph
			if inpara {
				w.Write(html_endp);
				inpara = false;
			}

			// count indented or blank lines
			j := i+1;
			for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) {
				j++;
			}
			// but not trailing blank lines
			for j > i && isBlank(lines[j-1]) {
				j--;
			}
			block := lines[i : j];
			i = j;

			unindent(block);

			// put those lines in a pre block.
			// they don't get the nice text formatting,
			// just html escaping
			w.Write(html_pre);
			for _, line := range block {
				template.HtmlEscape(w, line);
			}
			w.Write(html_endpre);
			continue;
		}
		// open paragraph
		if !inpara {
			w.Write(html_p);
			inpara = true;
		}
		commentEscape(w, lines[i]);
		i++;
	}
	if inpara {
		w.Write(html_endp);
		inpara = false;
	}
}

<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/gorilla/websocket"
	"github.com/hacsoc/slacksoc/api"
)

const (
	TOKEN_VAR = "SLACKSOC_TOKEN"
	NO_TOKEN_ERROR = "You must have the SLACKSOC_TOKEN variable to run the" +
					 " slacksoc bot"
)

type Bot struct {
	Token string
	Channels map[string]string
	WebSocketURL string
}

func NewBot(token string) *Bot {
	return &Bot{Token: token}
}

func (bot *Bot) Call(method string, data url.Values) (*http.Response, error) {
	data.Set("token", bot.Token)
	return api.Call(method, data)
}

func (bot *Bot) Start() error {
	payload, err := httpToJSON(bot.Call("rtm.start", url.Values{}))
	if err != nil {
		return err
	}
	ok, present := payload["ok"].(bool)
	if !present || ok != true {
		return &RTMStartError{"could not connect to RTM API"}
	}
	bot.GetChannelInfo()
	websocketURL, _ := payload["url"].(string)
	bot.WebSocketURL = websocketURL
	return nil
}

func (bot *Bot) Loop() error {
	dialer := websocket.Dialer{}
	conn, _, err := dialer.Dial(bot.WebSocketURL, http.Header{})
	if err != nil {
		return err
	}
	for {
		messageType, bytes, err := conn.ReadMessage()
		if err != nil {
			// NextReader returns an error if the connection is closed
			conn.Close()
			return nil
		}
		if messageType == websocket.BinaryMessage {
			continue // ignore binary messages
		}
		var message map[string]interface{}
		if err = json.Unmarshal(bytes, &message); err != nil {
			continue
		}
		fmt.Println("", message)
		if _, ok := message["type"]; !ok {
			continue
		}
		switch message["type"].(string) {
		case "message":
			bot.ReceiveMessage(conn, message)
		default:
			continue
		}
	}
}

func (bot *Bot) ReceiveMessage(conn *websocket.Conn, message map[string]interface{}) {
	subtype, _ := message["subtype"]
	hiddenSubtype, ok := message["hidden"]
	hidden := ok && hiddenSubtype.(bool)
	reply := bot.ConstructReply(message, subtype, hidden)
	if reply != nil {
		conn.WriteJSON(reply)
	}
}

func (bot *Bot) ConstructReply(message map[string]interface{}, subtype interface{}, hidden bool) interface{} {
	if subtype != nil {
		switch subtype.(string) {
		case "bot_message":
			// don't reply to other bots
			return nil
		case "channel_join":
			return bot.SetRealNameFields(message)
		default:
			return nil
		}
	} else {
		text := message["text"].(string)
		if strings.Contains(text, "hi slacksoc") {
			return Mention(message["user"].(string), message["channel"].(string), "hi ", "")
		} else if text == "slacksoc: pm me" {
			return bot.DirectMessage(message["user"].(string), "hi")
		}
		return nil
	}
}

func (bot *Bot) SetRealNameFields(message map[string]interface{}) interface{} {
	channel := message["channel"].(string)
	if channel != bot.Channels["general"] {
		return nil
	}
	return bot.DirectMessage(message["user"].(string), "Please set your real name fields")
}

func (bot *Bot) DirectMessage(user, text string) interface{} {
	dm, err := bot.OpenDirectMessage(user)
	if err != nil {
		return nil
	}
	return Message(text, dm)
}

func (bot *Bot) OpenDirectMessage(user string) (string, error) {
	resp, err := bot.Call("im.open", url.Values{"user": []string{user}})
	payload, err := httpToJSON(resp, err)
	if err != nil {
		return "", err
	}
	success := payload["ok"].(bool)
	if !success {
		fmt.Println(payload)
		return "", nil // need an actual error here
	}
	channel := payload["channel"].(map[string]interface{})
	return channel["id"].(string), nil
}

func Mention(nick, channel, beforeNick, afterNick string) interface{} {
	text := beforeNick
	nick = "<@" + nick + ">"
	text += nick
	if text == nick {
		text += ": "
	}
	text += afterNick
	return Message(text, channel)
}

func Message(text, channel string) interface{} {
	return map[string]string{
		"id": time.Now().Format("010206150405"),
		"type": "message",
		"channel": channel,
		"text": text,
	}
}

func main() {
	token := os.Getenv(TOKEN_VAR)
	if token == "" {
		fmt.Println(NO_TOKEN_ERROR)
		os.Exit(1)
	}

	bot := NewBot(token)
	fmt.Println("Starting bot")
	if err := bot.Start(); err != nil {
		fmt.Println(err)
	}
	fmt.Println("Looping")
	if err := bot.Loop(); err != nil {
		fmt.Println(err)
	}
}
<commit_msg>add a helpful link. requires more manually control over the api calls<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/gorilla/websocket"
	"github.com/hacsoc/slacksoc/api"
)

const (
	TOKEN_VAR = "SLACKSOC_TOKEN"
	NO_TOKEN_ERROR = "You must have the SLACKSOC_TOKEN variable to run the" +
					 " slacksoc bot"
)

type Bot struct {
	Token string
	Channels map[string]string
	WebSocketURL string
}

func NewBot(token string) *Bot {
	return &Bot{Token: token}
}

func (bot *Bot) Call(method string, data url.Values) (*http.Response, error) {
	data.Set("token", bot.Token)
	return api.Call(method, data)
}

func (bot *Bot) Start() error {
	payload, err := httpToJSON(bot.Call("rtm.start", url.Values{}))
	if err != nil {
		return err
	}
	ok, present := payload["ok"].(bool)
	if !present || ok != true {
		return &RTMStartError{"could not connect to RTM API"}
	}
	bot.GetChannelInfo()
	websocketURL, _ := payload["url"].(string)
	bot.WebSocketURL = websocketURL
	return nil
}

func (bot *Bot) Loop() error {
	dialer := websocket.Dialer{}
	conn, _, err := dialer.Dial(bot.WebSocketURL, http.Header{})
	if err != nil {
		return err
	}
	for {
		messageType, bytes, err := conn.ReadMessage()
		if err != nil {
			// NextReader returns an error if the connection is closed
			conn.Close()
			return nil
		}
		if messageType == websocket.BinaryMessage {
			continue // ignore binary messages
		}
		var message map[string]interface{}
		if err = json.Unmarshal(bytes, &message); err != nil {
			continue
		}
		fmt.Println("", message)
		if _, ok := message["type"]; !ok {
			continue
		}
		switch message["type"].(string) {
		case "message":
			bot.ReceiveMessage(conn, message)
		default:
			continue
		}
	}
}

func (bot *Bot) ReceiveMessage(conn *websocket.Conn, message map[string]interface{}) {
	subtype, _ := message["subtype"]
	hiddenSubtype, ok := message["hidden"]
	hidden := ok && hiddenSubtype.(bool)
	reply := bot.ConstructReply(message, subtype, hidden)
	if reply != nil {
		conn.WriteJSON(reply)
	}
}

func (bot *Bot) ConstructReply(message map[string]interface{}, subtype interface{}, hidden bool) interface{} {
	if subtype != nil {
		switch subtype.(string) {
		case "bot_message":
			// don't reply to other bots
			return nil
		case "channel_join":
			return bot.SetRealNameFields(message)
		default:
			return nil
		}
	} else {
		text := message["text"].(string)
		if strings.Contains(text, "hi slacksoc") {
			return Mention(message["user"].(string), message["channel"].(string), "hi ", "")
		} else if text == "slacksoc: pm me" {
			return bot.DirectMessage(message["user"].(string), "hi")
		}
		return nil
	}
}

func (bot *Bot) SetRealNameFields(message map[string]interface{}) interface{} {
	channel := message["channel"].(string)
	if channel != bot.Channels["general"] {
		return nil
	}
	userID := message["user"].(string)
	dmChan := make(chan string)
	userChan := make(chan interface{})
	go func() {
		dm, _ := bot.OpenDirectMessage(userID)
		dmChan <- dm
	}()
	go func() {
		resp, err := bot.Call("users.info", url.Values{"user": []string{userID}})
		payload, err := httpToJSON(resp, err)
		userChan <- payload
	}()
	payload := (<- userChan).(map[string]interface{})
	success := payload["ok"].(bool)
	if !success {
		fmt.Println(payload)
		return nil
	}
	user := payload["user"].(map[string]interface{})
	nick := user["name"].(string)
	text := "Please set your real name fields. https://hacsoc.slack.com/team/%s."
	text += " Then click \"Edit\"."
	text = fmt.Sprintf(text, nick)
	dm := <- dmChan
	return Message(text, dm)
}

func (bot *Bot) DirectMessage(user, text string) interface{} {
	dm, err := bot.OpenDirectMessage(user)
	if err != nil {
		return nil
	}
	return Message(text, dm)
}

func (bot *Bot) OpenDirectMessage(user string) (string, error) {
	resp, err := bot.Call("im.open", url.Values{"user": []string{user}})
	payload, err := httpToJSON(resp, err)
	if err != nil {
		return "", err
	}
	success := payload["ok"].(bool)
	if !success {
		fmt.Println(payload)
		return "", nil // need an actual error here
	}
	channel := payload["channel"].(map[string]interface{})
	return channel["id"].(string), nil
}

func Mention(nick, channel, beforeNick, afterNick string) interface{} {
	text := beforeNick
	nick = "<@" + nick + ">"
	text += nick
	if text == nick {
		text += ": "
	}
	text += afterNick
	return Message(text, channel)
}

func Message(text, channel string) interface{} {
	return map[string]string{
		"id": time.Now().Format("010206150405"),
		"type": "message",
		"channel": channel,
		"text": text,
	}
}

func main() {
	token := os.Getenv(TOKEN_VAR)
	if token == "" {
		fmt.Println(NO_TOKEN_ERROR)
		os.Exit(1)
	}

	bot := NewBot(token)
	fmt.Println("Starting bot")
	if err := bot.Start(); err != nil {
		fmt.Println(err)
	}
	fmt.Println("Looping")
	if err := bot.Loop(); err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>


<commit_before>package miniporte

import (
	"log"
	"os"
	"strings"

	irc "github.com/fluffle/goirc/client"
	link "github.com/oz/miniporte/link"
)

type Bot struct {
	Chans  []string
	Config *irc.Config
	Client *irc.Conn
}

func New() *Bot {
	cfg := irc.NewConfig(getEnvOr("IRC_NICK", "miniporte"))
	cfg.SSL = true
	cfg.Me.Name = getEnvOr("IRC_NAME", "Mini-Porte")
	cfg.Me.Ident = getEnvOr("IRC_IDENT", "MiniPorteIRCBot")
	cfg.Server = getEnvOr("IRC_SERVER", "irc.freenode.net:7000")
	cfg.NewNick = func(n string) string { return n + "_" }

	return &Bot{
		Chans:  strings.Split(getEnvOr("IRC_CHANS", "#af83-bots"), ","),
		Config: cfg,
		Client: irc.Client(cfg),
	}
}

func (b *Bot) OnMessage(msg *irc.Line) {
	// Ignore non-public messages
	if !msg.Public() {
		return
	}

	if url := link.Find(msg.Text()); url != "" {
		tags := link.Tags(msg.Text())
		if len(tags) == 0 {
			tags = []string{"private"}
		}
		tags = append(tags, msg.Nick, msg.Target())
		go func() {
			if err := link.Save(url, tags); err != nil {
				if !link.IncludesPrivate(tags) {
					b.Client.Privmsg(msg.Target(), "Oops! "+err.Error())
				}
				return
			}
			if !link.IncludesPrivate(tags) {
				b.Client.Privmsg(msg.Target(), "Saved!")
			}
		}()
	}
}

func (b *Bot) JoinChannels() {
	log.Println("Joining channels", b.Chans)
	for _, c := range b.Chans {
		b.Client.Join(c)
	}
}

func (b *Bot) Run() {
	ctl := make(chan string)

	// Connected
	b.Client.HandleFunc("connected", func(conn *irc.Conn, line *irc.Line) {
		log.Println("Connected!")
		b.JoinChannels()
	})

	// Disconnected
	b.Client.HandleFunc("disconnected",
		func(conn *irc.Conn, line *irc.Line) {
			log.Println("Disconnected")
			ctl <- "disconnected"
		})

	// PRIVMSG
	b.Client.HandleFunc("PRIVMSG", func(conn *irc.Conn, line *irc.Line) {
		b.OnMessage(line)
	})

	// Connection loop
	for {
		log.Println("Connecting to IRC...")
		if err := b.Client.Connect(); err != nil {
			log.Printf("Connection error: %s\n", err)
		}

		for cmd := range ctl {
			if cmd == "quit" {
				b.Client.Quit("Bye...")
				log.Println("Quitting...")
				return
			}
		}
	}

}

// Retrieve the environment variable "name", or a default value.
func getEnvOr(name, defaultValue string) (out string) {
	out = os.Getenv(name)
	if out == "" {
		out = defaultValue
	}
	return
}
<commit_msg>Handle disconnects correctly<commit_after>package miniporte

import (
	"log"
	"os"
	"strings"

	irc "github.com/fluffle/goirc/client"
	link "github.com/oz/miniporte/link"
)

type Bot struct {
	Chans  []string
	Config *irc.Config
	Client *irc.Conn
	Ctl    (chan string)
}

func New() *Bot {
	cfg := irc.NewConfig(getEnvOr("IRC_NICK", "miniporte"))
	cfg.SSL = true
	cfg.Me.Name = getEnvOr("IRC_NAME", "Mini-Porte")
	cfg.Me.Ident = getEnvOr("IRC_IDENT", "MiniPorteIRCBot")
	cfg.Server = getEnvOr("IRC_SERVER", "irc.freenode.net:7000")
	cfg.NewNick = func(n string) string { return n + "_" }

	return &Bot{
		Chans:  strings.Split(getEnvOr("IRC_CHANS", "#af83-bots"), ","),
		Config: cfg,
		Client: irc.Client(cfg),
		Ctl:    make(chan string),
	}
}

func (b *Bot) OnMessage(msg *irc.Line) {
	// Ignore non-public messages
	if !msg.Public() {
		return
	}

	if url := link.Find(msg.Text()); url != "" {
		tags := link.Tags(msg.Text())
		if len(tags) == 0 {
			tags = []string{"private"}
		}
		tags = append(tags, msg.Nick, msg.Target())
		go func() {
			if err := link.Save(url, tags); err != nil {
				if !link.IncludesPrivate(tags) {
					b.Client.Privmsg(msg.Target(), "Oops! "+err.Error())
				}
				return
			}
			if !link.IncludesPrivate(tags) {
				b.Client.Privmsg(msg.Target(), "Saved!")
			}
		}()
	}
}

func (b *Bot) JoinChannels() {
	log.Println("Joining channels", b.Chans)
	for _, c := range b.Chans {
		b.Client.Join(c)
	}
}

func (b *Bot) Run() {
	// Connected
	b.Client.HandleFunc("connected", func(conn *irc.Conn, line *irc.Line) {
		log.Println("Connected!")
		b.JoinChannels()
	})

	// Disconnected
	b.Client.HandleFunc("disconnected", func(conn *irc.Conn, line *irc.Line) {
		log.Println("Disconnected")
		b.Ctl <- "disconnected"
	})

	// PRIVMSG
	b.Client.HandleFunc("PRIVMSG", func(conn *irc.Conn, line *irc.Line) {
		b.OnMessage(line)
	})

	// Connection loop
	for {
		log.Println("Connecting to IRC...")
		if err := b.Client.Connect(); err != nil {
			log.Printf("Connection error: %s\n", err)
		}

		for cmd := range b.Ctl {
			switch cmd {
			case "quit":
				b.Client.Quit("Bye...")
				log.Println("Quitting...")
				return
			case "disconnected":
				log.Println("Trying to reconnect after", cmd)
				break
			default:
				log.Println("Ignoring command", cmd)
			}
		}
	}
}

// Retrieve the environment variable "name", or a default value.
func getEnvOr(name, defaultValue string) (out string) {
	out = os.Getenv(name)
	if out == "" {
		out = defaultValue
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/url"
	"os"

	"github.com/ajm188/slack"
)

const (
	tokenVar     = "SLACKSOC_TOKEN"
	noTokenError = "You must have the SLACKSOC_TOKEN variable to run the" +
		" slacksoc bot"
	version = "0.2.1"
)

func setRealNameFields(bot *slack.Bot, event map[string]interface{}) (*slack.Message, slack.Status) {
	channel := event["channel"].(string)
	if channel != bot.Channels["general"] {
		return nil, slack.Continue
	}
	userID := event["user"].(string)
	dmChan := make(chan string)
	userChan := make(chan interface{})
	go func() {
		dm, _ := bot.OpenDirectMessage(userID)
		dmChan <- dm
	}()
	go func() {
		payload, _ := bot.Call("users.info", url.Values{"user": []string{userID}})
		userChan <- payload
	}()
	payload := (<-userChan).(map[string]interface{})
	success := payload["ok"].(bool)
	if !success {
		fmt.Println(payload)
		return nil, slack.Continue
	}
	user := payload["user"].(map[string]interface{})
	nick := user["name"].(string)
	text := "Please set your real name fields. https://hacsoc.slack.com/team/%s."
	text += " Then click \"Edit\"."
	text = fmt.Sprintf(text, nick)
	dm := <-dmChan
	return slack.NewMessage(text, dm), slack.Continue
}

func sendDM(bot *slack.Bot, event map[string]interface{}) (*slack.Message, slack.Status) {
	user := event["user"].(string)
	return bot.DirectMessage(user, "hi"), slack.Continue
}

func main() {
	token := os.Getenv(tokenVar)
	if token == "" {
		fmt.Println(noTokenError)
		os.Exit(1)
	}

	bot := slack.NewBot(token)
	bot.Respond("hi", slack.Respond("hi there!"))
	bot.Respond("pm me", sendDM)
	bot.Respond("((what's)|(tell me) your)? ?version??",
		slack.Respond(fmt.Sprintf("My version is %s. My lib version is %s", version, slack.Version)))
	bot.Listen("gentoo", slack.React("funroll-loops"))
	bot.Listen("slacksoc", slack.React("raisedeyebrow"))
	bot.OnEventWithSubtype("message", "channel_join", setRealNameFields)
	fmt.Println("Starting bot")
	if err := bot.Start(); err != nil {
		fmt.Println(err)
	}
}
<commit_msg>Not the first word<commit_after>package main

import (
	"fmt"
	"net/url"
	"os"

	"github.com/ajm188/slack"
)

const (
	tokenVar     = "SLACKSOC_TOKEN"
	noTokenError = "You must have the SLACKSOC_TOKEN variable to run the" +
		" slacksoc bot"
	version = "0.2.1"
)

func setRealNameFields(bot *slack.Bot, event map[string]interface{}) (*slack.Message, slack.Status) {
	channel := event["channel"].(string)
	if channel != bot.Channels["general"] {
		return nil, slack.Continue
	}
	userID := event["user"].(string)
	dmChan := make(chan string)
	userChan := make(chan interface{})
	go func() {
		dm, _ := bot.OpenDirectMessage(userID)
		dmChan <- dm
	}()
	go func() {
		payload, _ := bot.Call("users.info", url.Values{"user": []string{userID}})
		userChan <- payload
	}()
	payload := (<-userChan).(map[string]interface{})
	success := payload["ok"].(bool)
	if !success {
		fmt.Println(payload)
		return nil, slack.Continue
	}
	user := payload["user"].(map[string]interface{})
	nick := user["name"].(string)
	text := "Please set your real name fields. https://hacsoc.slack.com/team/%s."
	text += " Then click \"Edit\"."
	text = fmt.Sprintf(text, nick)
	dm := <-dmChan
	return slack.NewMessage(text, dm), slack.Continue
}

func sendDM(bot *slack.Bot, event map[string]interface{}) (*slack.Message, slack.Status) {
	user := event["user"].(string)
	return bot.DirectMessage(user, "hi"), slack.Continue
}

func main() {
	token := os.Getenv(tokenVar)
	if token == "" {
		fmt.Println(noTokenError)
		os.Exit(1)
	}

	bot := slack.NewBot(token)
	bot.Respond("hi", slack.Respond("hi there!"))
	bot.Respond("pm me", sendDM)
	bot.Respond("((what's)|(tell me) your)? ?version??",
		slack.Respond(fmt.Sprintf("My version is %s. My lib version is %s", version, slack.Version)))
	bot.Listen("gentoo", slack.React("funroll-loops"))
	bot.Listen("\\A.slacksoc", slack.React("raisedeyebrow"))
	bot.OnEventWithSubtype("message", "channel_join", setRealNameFields)
	fmt.Println("Starting bot")
	if err := bot.Start(); err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>


<commit_before>// Package mph is a Go implementation of the compress, hash and displace (CHD)
// minimal perfect hash algorithm.
//
// See http://csourceforge.net/papers/esa09.pdf for details.
//
// To create and serialize a hash table:
//
//		b := mph.Builder()
// 		for k, v := range data {
// 			b.Add(k, v)
// 		}
// 		h, _ := b.Build()
// 		w, _ := os.Create("data.idx")
// 		b, _ := h.Write(w)
//
// To read from the hash table:
//
//		r, _ := os.Open("data.idx")
//		h, _ := h.Read(r)
//
//		v := h.Get([]byte("some key"))
//		if v == nil {
//		    // Key not found
//		}
//
// MMAP is also indirectly supported, by deserializing from a byte
// slice and slicing the keys and values.
//
// See https://github.com/alecthomas/mph for source.
package mph

import (
	"bytes"
	"encoding/binary"
	"io"
	"io/ioutil"
)

// CHD hash table lookup.
type CHD struct {
	// Random hash function table.
	r []uint64
	// Array of indices into hash function table r. We assume there aren't
	// more than 2^16 hash functions O_o
	indices []uint16
	// Final table of values.
	keys   [][]byte
	values [][]byte
}

func hasher(data []byte) uint64 {
	var hash uint64 = 14695981039346656037
	for _, c := range data {
		hash ^= uint64(c)
		hash *= 1099511628211
	}
	return hash
}

// Read a serialized CHD.
func Read(r io.Reader) (*CHD, error) {
	b, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	return Mmap(b)
}

// Mmap creates a new CHD aliasing the CHD structure over an existing byte region (typically mmapped).
func Mmap(b []byte) (*CHD, error) {
	c := &CHD{}

	bi := &sliceReader{b: b}

	// Read vector of hash functions.
	rl := bi.ReadInt()
	c.r = bi.ReadUint64Array(rl)

	// Read hash function indices.
	il := bi.ReadInt()
	c.indices = bi.ReadUint16Array(il)

	el := bi.ReadInt()

	c.keys = make([][]byte, el)
	c.values = make([][]byte, el)

	for i := uint64(0); i < el; i++ {
		kl := bi.ReadInt()
		vl := bi.ReadInt()
		c.keys[i] = bi.Read(kl)
		c.values[i] = bi.Read(vl)
	}

	return c, nil
}

// Get an entry from the hash table.
func (c *CHD) Get(key []byte) []byte {
	r0 := c.r[0]
	h := hasher(key) ^ r0
	i := h % uint64(len(c.indices))
	ri := c.indices[i]
	// This can occur if there were unassigned slots in the hash table.
	if ri >= uint16(len(c.r)) {
		return nil
	}
	r := c.r[ri]
	ti := (h ^ r) % uint64(len(c.keys))
	// fmt.Printf("r[0]=%d, h=%d, i=%d, ri=%d, r=%d, ti=%d\n", c.r[0], h, i, ri, r, ti)
	k := c.keys[ti]
	if bytes.Compare(k, key) != 0 {
		return nil
	}
	v := c.values[ti]
	return v
}

func (c *CHD) Len() int {
	return len(c.keys)
}

// Iterate over entries in the hash table.
func (c *CHD) Iterate() *Iterator {
	if len(c.keys) == 0 {
		return nil
	}
	return &Iterator{c: c}
}

// Serialize the CHD. The serialized form is conducive to mmapped access. See
// the Mmap function for details.
func (c *CHD) Write(w io.Writer) error {
	write := func(nd ...interface{}) error {
		for _, d := range nd {
			if err := binary.Write(w, binary.LittleEndian, d); err != nil {
				return err
			}
		}
		return nil
	}

	data := []interface{}{
		uint32(len(c.r)), c.r,
		uint32(len(c.indices)), c.indices,
		uint32(len(c.keys)),
	}

	if err := write(data...); err != nil {
		return err
	}

	for i := range c.keys {
		k, v := c.keys[i], c.values[i]
		if err := write(uint32(len(k)), uint32(len(v))); err != nil {
			return err
		}
		if _, err := w.Write(k); err != nil {
			return err
		}
		if _, err := w.Write(v); err != nil {
			return err
		}
	}
	return nil
}

type Iterator struct {
	i int
	c *CHD
}

func (c *Iterator) Get() (key []byte, value []byte) {
	return c.c.keys[c.i], c.c.values[c.i]
}

func (c *Iterator) Next() *Iterator {
	c.i++
	if c.i >= len(c.c.keys) {
		return nil
	}
	return c
}
<commit_msg>Fix typo in URL of esa09.pdf paper<commit_after>// Package mph is a Go implementation of the compress, hash and displace (CHD)
// minimal perfect hash algorithm.
//
// See http://cmph.sourceforge.net/papers/esa09.pdf for details.
//
// To create and serialize a hash table:
//
//		b := mph.Builder()
// 		for k, v := range data {
// 			b.Add(k, v)
// 		}
// 		h, _ := b.Build()
// 		w, _ := os.Create("data.idx")
// 		b, _ := h.Write(w)
//
// To read from the hash table:
//
//		r, _ := os.Open("data.idx")
//		h, _ := h.Read(r)
//
//		v := h.Get([]byte("some key"))
//		if v == nil {
//		    // Key not found
//		}
//
// MMAP is also indirectly supported, by deserializing from a byte
// slice and slicing the keys and values.
//
// See https://github.com/alecthomas/mph for source.
package mph

import (
	"bytes"
	"encoding/binary"
	"io"
	"io/ioutil"
)

// CHD hash table lookup.
type CHD struct {
	// Random hash function table.
	r []uint64
	// Array of indices into hash function table r. We assume there aren't
	// more than 2^16 hash functions O_o
	indices []uint16
	// Final table of values.
	keys   [][]byte
	values [][]byte
}

func hasher(data []byte) uint64 {
	var hash uint64 = 14695981039346656037
	for _, c := range data {
		hash ^= uint64(c)
		hash *= 1099511628211
	}
	return hash
}

// Read a serialized CHD.
func Read(r io.Reader) (*CHD, error) {
	b, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	return Mmap(b)
}

// Mmap creates a new CHD aliasing the CHD structure over an existing byte region (typically mmapped).
func Mmap(b []byte) (*CHD, error) {
	c := &CHD{}

	bi := &sliceReader{b: b}

	// Read vector of hash functions.
	rl := bi.ReadInt()
	c.r = bi.ReadUint64Array(rl)

	// Read hash function indices.
	il := bi.ReadInt()
	c.indices = bi.ReadUint16Array(il)

	el := bi.ReadInt()

	c.keys = make([][]byte, el)
	c.values = make([][]byte, el)

	for i := uint64(0); i < el; i++ {
		kl := bi.ReadInt()
		vl := bi.ReadInt()
		c.keys[i] = bi.Read(kl)
		c.values[i] = bi.Read(vl)
	}

	return c, nil
}

// Get an entry from the hash table.
func (c *CHD) Get(key []byte) []byte {
	r0 := c.r[0]
	h := hasher(key) ^ r0
	i := h % uint64(len(c.indices))
	ri := c.indices[i]
	// This can occur if there were unassigned slots in the hash table.
	if ri >= uint16(len(c.r)) {
		return nil
	}
	r := c.r[ri]
	ti := (h ^ r) % uint64(len(c.keys))
	// fmt.Printf("r[0]=%d, h=%d, i=%d, ri=%d, r=%d, ti=%d\n", c.r[0], h, i, ri, r, ti)
	k := c.keys[ti]
	if bytes.Compare(k, key) != 0 {
		return nil
	}
	v := c.values[ti]
	return v
}

func (c *CHD) Len() int {
	return len(c.keys)
}

// Iterate over entries in the hash table.
func (c *CHD) Iterate() *Iterator {
	if len(c.keys) == 0 {
		return nil
	}
	return &Iterator{c: c}
}

// Serialize the CHD. The serialized form is conducive to mmapped access. See
// the Mmap function for details.
func (c *CHD) Write(w io.Writer) error {
	write := func(nd ...interface{}) error {
		for _, d := range nd {
			if err := binary.Write(w, binary.LittleEndian, d); err != nil {
				return err
			}
		}
		return nil
	}

	data := []interface{}{
		uint32(len(c.r)), c.r,
		uint32(len(c.indices)), c.indices,
		uint32(len(c.keys)),
	}

	if err := write(data...); err != nil {
		return err
	}

	for i := range c.keys {
		k, v := c.keys[i], c.values[i]
		if err := write(uint32(len(k)), uint32(len(v))); err != nil {
			return err
		}
		if _, err := w.Write(k); err != nil {
			return err
		}
		if _, err := w.Write(v); err != nil {
			return err
		}
	}
	return nil
}

type Iterator struct {
	i int
	c *CHD
}

func (c *Iterator) Get() (key []byte, value []byte) {
	return c.c.keys[c.i], c.c.values[c.i]
}

func (c *Iterator) Next() *Iterator {
	c.i++
	if c.i >= len(c.c.keys) {
		return nil
	}
	return c
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"text/template"

	"github.com/k0kubun/pp"
)

const helpTemplate = `Execute commands via docker-compose

Usage:
    {{ .Name }} COMMAND [args...]
    {{ .Name }} COMMAND -h|--help
    {{ .Name }} [options]

Options:
    -h, --help     Show this
    -v, --version  Show {{ .Name }} version
        --debug    Debug context and configuration

Commands:
{{- range $name, $sub := .Substitution }}
    {{ printf $.NameFormat $name }}{{ if ne $sub.Summary "" }} # {{ $sub.Summary }}{{ end }}
{{- end }}
`

// CLI is an object holding states
type CLI struct {
	*Context
	Config         *Config
	Args           []string
	RunInContainer bool

	Stdin  io.Reader
	Stdout io.Writer
	Stderr io.Writer
}

// NewCLI creates a new CLI instance
func NewCLI(ctx *Context, cfg *Config, args []string) *CLI {
	return &CLI{
		Context:        ctx,
		Config:         cfg,
		Args:           args[1:],
		RunInContainer: true,

		Stdin:  os.Stdin,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}
}

// Run executes commands
func (c *CLI) Run() error {
	c.setup()
	c.substituteCommand()

	switch c.Args[0] {
	case "-h", "--help", ".help":
		return c.ExecHelp()
	case "-v", "--version":
		return c.ExecVersion()
	case "--debug":
		return c.ExecDebug()
	case ".sub-help":
		return c.ExecSubHelp()
	}

	if c.RunInContainer {
		return c.run()
	}

	return c.exec(c.Args[0], c.Args[1:]...)
}

func (c *CLI) setup() {
	os.Setenv("COMPOSE_PROJECT_NAME", c.Config.ProjectName)
	os.Setenv("DOCKER_HOST_IP", c.IP)
}

func (c *CLI) substituteCommand() {
	if len(c.Args) == 0 {
		c.Args = []string{".help"}
		return
	}

	if s, ok := c.Substitution[c.Args[0]]; ok {
		c.Args[0] = s.Command
		c.RunInContainer = s.RunInContainer

		if s.HelpFile != "" && len(c.Args) > 1 {
			switch c.Args[1] {
			case "-h", "--help":
				c.Args = []string{".sub-help", s.HelpFile}
			}
		}
	}
}

func (c *CLI) exec(name string, args ...string) error {
	cmd := exec.Command(name, args...)
	if name == "docker-compose" {
		cmd.Dir = c.BaseDir
	} else {
		cmd.Dir = c.RootDir
	}
	cmd.Stdin = c.Stdin
	cmd.Stdout = c.Stdout
	cmd.Stderr = c.Stderr
	return cmd.Run()
}

func (c *CLI) run() error {
	if err := c.exec("docker-compose", "up", "-d", "--remove-orphans"); err != nil {
		return err
	}

	args := append([]string{
		"exec",
		c.Config.MainService,
	}, c.Args...)

	return c.exec("docker-compose", args...)
}

// ExecVersion prints version info
func (c *CLI) ExecVersion() error {
	fmt.Fprintf(c.Stdout, "%s (revision %s)\n", Version, Revision)
	return nil
}

// ExecDebug prints internal state objects
func (c *CLI) ExecDebug() error {
	pp.Fprintln(c.Stdout, c.Context)
	pp.Fprintln(c.Stdout, c.Config)
	return nil
}

// ExecHelp shows help contents
func (c *CLI) ExecHelp() error {
	maxNameLen := 0
	for name := range c.Substitution {
		if l := len(name); l > maxNameLen {
			maxNameLen = l
		}
	}

	for _, s := range c.Substitution {
		if s.HelpFile == "" {
			continue
		}
		s.Summary, _ = loadHelpFile(s.HelpFile)
	}

	tmpl := template.Must(template.New("help").Parse(helpTemplate))
	return tmpl.Execute(c.Stderr, map[string]interface{}{
		"Substitution": c.Substitution,
		"NameFormat":   fmt.Sprintf("%%-%ds", maxNameLen+1),
		"Name":         "rid",
	})
}

// ExecSubHelp shows help contents for a custom sub-command
func (c *CLI) ExecSubHelp() error {
	_, description := loadHelpFile(c.Args[1])
	fmt.Fprint(c.Stderr, description)
	return nil
}
<commit_msg>Rename<commit_after>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"text/template"

	"github.com/k0kubun/pp"
)

const helpTemplate = `Execute commands via docker-compose

Usage:
    {{ .Name }} COMMAND [args...]
    {{ .Name }} COMMAND -h|--help
    {{ .Name }} [options]

Options:
    -h, --help     Show this
    -v, --version  Show {{ .Name }} version
        --debug    Debug context and configuration

Commands:
{{- range $name, $sub := .Substitution }}
    {{ printf $.NameFormat $name }}{{ if ne $sub.Summary "" }} # {{ $sub.Summary }}{{ end }}
{{- end }}
`

// CLI is an object holding states
type CLI struct {
	*Context
	Config         *Config
	Args           []string
	RunInContainer bool

	Stdin  io.Reader
	Stdout io.Writer
	Stderr io.Writer
}

// NewCLI creates a new CLI instance
func NewCLI(ctx *Context, cfg *Config, args []string) *CLI {
	return &CLI{
		Context:        ctx,
		Config:         cfg,
		Args:           args[1:],
		RunInContainer: true,

		Stdin:  os.Stdin,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}
}

// Run executes commands
func (c *CLI) Run() error {
	c.setup()
	c.substituteCommand()

	switch c.Args[0] {
	case "-h", "--help", ".help":
		return c.ExecHelp()
	case "-v", "--version":
		return c.ExecVersion()
	case "--debug":
		return c.ExecDebug()
	case ".sub-help":
		return c.ExecSubHelp()
	}

	if c.RunInContainer {
		return c.runInContainer(c.Args[0], c.Args[1:]...)
	}

	return c.run(c.Args[0], c.Args[1:]...)
}

func (c *CLI) setup() {
	os.Setenv("COMPOSE_PROJECT_NAME", c.Config.ProjectName)
	os.Setenv("DOCKER_HOST_IP", c.IP)
}

func (c *CLI) substituteCommand() {
	if len(c.Args) == 0 {
		c.Args = []string{".help"}
		return
	}

	if s, ok := c.Substitution[c.Args[0]]; ok {
		c.Args[0] = s.Command
		c.RunInContainer = s.RunInContainer

		if s.HelpFile != "" && len(c.Args) > 1 {
			switch c.Args[1] {
			case "-h", "--help":
				c.Args = []string{".sub-help", s.HelpFile}
			}
		}
	}
}

func (c *CLI) run(name string, args ...string) error {
	cmd := exec.Command(name, args...)
	if name == "docker-compose" {
		cmd.Dir = c.BaseDir
	} else {
		cmd.Dir = c.RootDir
	}
	cmd.Stdin = c.Stdin
	cmd.Stdout = c.Stdout
	cmd.Stderr = c.Stderr
	return cmd.Run()
}

func (c *CLI) runInContainer(name string, args ...string) error {
	if err := c.run("docker-compose", "up", "-d", "--remove-orphans"); err != nil {
		return err
	}

	args = append([]string{
		"exec",
		c.Config.MainService,
		name,
	}, args...)

	return c.run("docker-compose", args...)
}

// ExecVersion prints version info
func (c *CLI) ExecVersion() error {
	fmt.Fprintf(c.Stdout, "%s (revision %s)\n", Version, Revision)
	return nil
}

// ExecDebug prints internal state objects
func (c *CLI) ExecDebug() error {
	pp.Fprintln(c.Stdout, c.Context)
	pp.Fprintln(c.Stdout, c.Config)
	return nil
}

// ExecHelp shows help contents
func (c *CLI) ExecHelp() error {
	maxNameLen := 0
	for name := range c.Substitution {
		if l := len(name); l > maxNameLen {
			maxNameLen = l
		}
	}

	for _, s := range c.Substitution {
		if s.HelpFile == "" {
			continue
		}
		s.Summary, _ = loadHelpFile(s.HelpFile)
	}

	tmpl := template.Must(template.New("help").Parse(helpTemplate))
	return tmpl.Execute(c.Stderr, map[string]interface{}{
		"Substitution": c.Substitution,
		"NameFormat":   fmt.Sprintf("%%-%ds", maxNameLen+1),
		"Name":         "rid",
	})
}

// ExecSubHelp shows help contents for a custom sub-command
func (c *CLI) ExecSubHelp() error {
	_, description := loadHelpFile(c.Args[1])
	fmt.Fprint(c.Stderr, description)
	return nil
}
<|endoftext|>


<commit_before>package cli

type App struct {
	Name        string
	Description string
	Commands    []Command
}

type Command struct {
	Name        string
	Description string
	Action      Action
}

type Action func(name string)

func (a App) Run(command string) {
	for _, c := range a.Commands {
		if c.Name == command {
			c.Action(command)
		}
	}
}
<commit_msg>Experimenting with some names<commit_after>package cli

type App struct {
	Name     string
	Summary  string
	Action   Action
	Commands []Command
}

type Command struct {
	Name        string
	Shortname   string
	Summary     string
	Description string
	Action      Action
}

type Action func(name string)

func (a App) Run(command string) {
	for _, c := range a.Commands {
		if c.Name == command {
			c.Action(command)
		}
	}
}
<|endoftext|>


<commit_before>// +build go1.2

// Copyright 2013 com 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 com is an open source project for commonly used functions for the Go programming language.
package com

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"

	"github.com/admpub/log"
)

// ElapsedMemory 内存占用
func ElapsedMemory() (ret string) {
	memStat := new(runtime.MemStats)
	runtime.ReadMemStats(memStat)
	ret = FormatByte(memStat.Alloc, 3)
	return
}

// ExecCmdDirBytes executes system command in given directory
// and return stdout, stderr in bytes type, along with possible error.
func ExecCmdDirBytes(dir, cmdName string, args ...string) ([]byte, []byte, error) {
	bufOut := new(bytes.Buffer)
	bufErr := new(bytes.Buffer)

	cmd := exec.Command(cmdName, args...)
	cmd.Dir = dir
	cmd.Stdout = bufOut
	cmd.Stderr = bufErr

	err := cmd.Run()
	return bufOut.Bytes(), bufErr.Bytes(), err
}

// ExecCmdBytes executes system command
// and return stdout, stderr in bytes type, along with possible error.
func ExecCmdBytes(cmdName string, args ...string) ([]byte, []byte, error) {
	return ExecCmdDirBytes("", cmdName, args...)
}

// ExecCmdDir executes system command in given directory
// and return stdout, stderr in string type, along with possible error.
func ExecCmdDir(dir, cmdName string, args ...string) (string, string, error) {
	bufOut, bufErr, err := ExecCmdDirBytes(dir, cmdName, args...)
	return string(bufOut), string(bufErr), err
}

// ExecCmd executes system command
// and return stdout, stderr in string type, along with possible error.
func ExecCmd(cmdName string, args ...string) (string, string, error) {
	return ExecCmdDir("", cmdName, args...)
}

// WritePidFile writes the process ID to the file at PidFile.
// It does nothing if PidFile is not set.
func WritePidFile(pidFile string) error {
	if pidFile == "" {
		return nil
	}
	pid := []byte(strconv.Itoa(os.Getpid()) + "\n")
	return ioutil.WriteFile(pidFile, pid, 0644)
}

var (
	space  = rune(' ')
	quote  = rune('"')
	slash  = rune('\\')
	envOS  = regexp.MustCompile(`\{\$[a-zA-Z0-9_]+\}`)
	envWin = regexp.MustCompile(`\{%[a-zA-Z0-9_]+%\}`)
)

func ParseArgs(command string) (params []string) {
	item := []rune{}
	hasQuote := false
	hasSlash := false
	maxIndex := len(command) - 1
	//tower.exe -c tower.yaml -p "eee\"ddd" -t aaaa
	for k, v := range command {
		if !hasQuote {
			if v == space {
				params = append(params, string(item))
				item = []rune{}
				continue
			}
			if v == quote {
				hasQuote = true
				continue
			}
		} else {
			if !hasSlash && v == quote {
				hasQuote = false
				continue
			}
			if !hasSlash && v == slash && k+1 <= maxIndex && command[k+1] == '"' {
				hasSlash = true
				continue
			}
			hasSlash = false
		}
		item = append(item, v)
	}
	if len(item) > 0 {
		params = append(params, string(item))
	}
	for k, v := range params {
		v = envWin.ReplaceAllStringFunc(v, getWinEnv)
		params[k] = envOS.ReplaceAllStringFunc(v, getEnv)
	}
	//fmt.Printf("---> %#v\n", params)
	//params = []string{}
	return
}

func getWinEnv(s string) string {
	s = strings.TrimPrefix(s, `{%`)
	s = strings.TrimSuffix(s, `%}`)
	return os.Getenv(s)
}

func getEnv(s string) string {
	s = strings.TrimPrefix(s, `{$`)
	s = strings.TrimSuffix(s, `}`)
	return os.Getenv(s)
}

type CmdResultCapturer struct {
	Do func([]byte) error
}

func (this CmdResultCapturer) Write(p []byte) (n int, err error) {
	err = this.Do(p)
	n = len(p)
	return
}

func (this CmdResultCapturer) WriteString(p string) (n int, err error) {
	err = this.Do([]byte(p))
	n = len(p)
	return
}

func CreateCmdStr(command string, recvResult func([]byte) error) *exec.Cmd {
	out := CmdResultCapturer{Do: recvResult}
	return CreateCmdStrWithWriter(command, out)
}

func CreateCmd(params []string, recvResult func([]byte) error) *exec.Cmd {
	out := CmdResultCapturer{Do: recvResult}
	return CreateCmdWithWriter(params, out)
}

func CreateCmdStrWithWriter(command string, writer ...io.Writer) *exec.Cmd {
	params := ParseArgs(command)
	return CreateCmdWithWriter(params, writer...)
}

func CreateCmdWithWriter(params []string, writer ...io.Writer) *exec.Cmd {
	var cmd *exec.Cmd
	length := len(params)
	if length == 0 || len(params[0]) == 0 {
		return cmd
	}
	if length > 1 {
		cmd = exec.Command(params[0], params[1:]...)
	} else {
		cmd = exec.Command(params[0])
	}
	var wOut, wErr io.Writer
	length = len(writer)
	if length > 0 && writer[0] != nil {
		wOut = writer[0]
		if length > 1 {
			wErr = writer[1]
		} else {
			wErr = wOut
		}
	} else {
		wOut = os.Stdout
		wErr = os.Stderr
	}
	cmd.Stdout = wOut
	cmd.Stderr = wErr
	return cmd
}

func RunCmdStr(command string, recvResult func([]byte) error) *exec.Cmd {
	out := CmdResultCapturer{Do: recvResult}
	return RunCmdStrWithWriter(command, out)
}

func RunCmd(params []string, recvResult func([]byte) error) *exec.Cmd {
	out := CmdResultCapturer{Do: recvResult}
	return RunCmdWithWriter(params, out)
}

func RunCmdStrWithWriter(command string, writer ...io.Writer) *exec.Cmd {
	params := ParseArgs(command)
	return RunCmdWithWriter(params, writer...)
}

func RunCmdWithWriter(params []string, writer ...io.Writer) *exec.Cmd {
	cmd := CreateCmdWithWriter(params, writer...)

	go func() {
		err := cmd.Run()
		if err != nil {
			cmd.Stderr.Write([]byte(err.Error()))
		}
	}()

	return cmd
}

func CloseProcessFromPidFile(pidFile string) (err error) {
	if pidFile == `` {
		return
	}
	b, err := ioutil.ReadFile(pidFile)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return
	}
	pid, err := strconv.Atoi(strings.TrimSpace(string(b)))
	if err != nil {
		log.Error(err.Error())
		return nil
	}
	return CloseProcessFromPid(pid)
}

func CloseProcessFromPid(pid int) (err error) {
	procs, err := os.FindProcess(pid)
	if err == nil {
		return procs.Kill()
	}
	return
}
<commit_msg>update<commit_after>// +build go1.2

// Copyright 2013 com 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 com is an open source project for commonly used functions for the Go programming language.
package com

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"

	"github.com/admpub/log"
)

// ElapsedMemory 内存占用
func ElapsedMemory() (ret string) {
	memStat := new(runtime.MemStats)
	runtime.ReadMemStats(memStat)
	ret = FormatByte(memStat.Alloc, 3)
	return
}

// ExecCmdDirBytes executes system command in given directory
// and return stdout, stderr in bytes type, along with possible error.
func ExecCmdDirBytes(dir, cmdName string, args ...string) ([]byte, []byte, error) {
	bufOut := new(bytes.Buffer)
	bufErr := new(bytes.Buffer)

	cmd := exec.Command(cmdName, args...)
	cmd.Dir = dir
	cmd.Stdout = bufOut
	cmd.Stderr = bufErr

	err := cmd.Run()
	return bufOut.Bytes(), bufErr.Bytes(), err
}

// ExecCmdBytes executes system command
// and return stdout, stderr in bytes type, along with possible error.
func ExecCmdBytes(cmdName string, args ...string) ([]byte, []byte, error) {
	return ExecCmdDirBytes("", cmdName, args...)
}

// ExecCmdDir executes system command in given directory
// and return stdout, stderr in string type, along with possible error.
func ExecCmdDir(dir, cmdName string, args ...string) (string, string, error) {
	bufOut, bufErr, err := ExecCmdDirBytes(dir, cmdName, args...)
	return string(bufOut), string(bufErr), err
}

// ExecCmd executes system command
// and return stdout, stderr in string type, along with possible error.
func ExecCmd(cmdName string, args ...string) (string, string, error) {
	return ExecCmdDir("", cmdName, args...)
}

// WritePidFile writes the process ID to the file at PidFile.
// It does nothing if PidFile is not set.
func WritePidFile(pidFile string) error {
	if pidFile == "" {
		return nil
	}
	pid := []byte(strconv.Itoa(os.Getpid()) + "\n")
	return ioutil.WriteFile(pidFile, pid, 0644)
}

var (
	space  = rune(' ')
	quote  = rune('"')
	slash  = rune('\\')
	envOS  = regexp.MustCompile(`\{\$[a-zA-Z0-9_]+\}`)
	envWin = regexp.MustCompile(`\{%[a-zA-Z0-9_]+%\}`)
)

func ParseArgs(command string) (params []string) {
	item := []rune{}
	hasQuote := false
	hasSlash := false
	maxIndex := len(command) - 1
	//tower.exe -c tower.yaml -p "eee\"ddd" -t aaaa
	for k, v := range command {
		if !hasQuote {
			if v == space {
				params = append(params, string(item))
				item = []rune{}
				continue
			}
			if v == quote {
				hasQuote = true
				continue
			}
		} else {
			if !hasSlash && v == quote {
				hasQuote = false
				continue
			}
			if !hasSlash && v == slash && k+1 <= maxIndex && command[k+1] == '"' {
				hasSlash = true
				continue
			}
			hasSlash = false
		}
		item = append(item, v)
	}
	if len(item) > 0 {
		params = append(params, string(item))
	}
	for k, v := range params {
		v = envWin.ReplaceAllStringFunc(v, getWinEnv)
		params[k] = envOS.ReplaceAllStringFunc(v, getEnv)
	}
	//fmt.Printf("---> %#v\n", params)
	//params = []string{}
	return
}

func getWinEnv(s string) string {
	s = strings.TrimPrefix(s, `{%`)
	s = strings.TrimSuffix(s, `%}`)
	return os.Getenv(s)
}

func getEnv(s string) string {
	s = strings.TrimPrefix(s, `{$`)
	s = strings.TrimSuffix(s, `}`)
	return os.Getenv(s)
}

type CmdResultCapturer struct {
	Do func([]byte) error
}

func (this CmdResultCapturer) Write(p []byte) (n int, err error) {
	err = this.Do(p)
	n = len(p)
	return
}

func (this CmdResultCapturer) WriteString(p string) (n int, err error) {
	err = this.Do([]byte(p))
	n = len(p)
	return
}

func CreateCmdStr(command string, recvResult func([]byte) error) *exec.Cmd {
	out := CmdResultCapturer{Do: recvResult}
	return CreateCmdStrWithWriter(command, out)
}

func CreateCmd(params []string, recvResult func([]byte) error) *exec.Cmd {
	out := CmdResultCapturer{Do: recvResult}
	return CreateCmdWithWriter(params, out)
}

func CreateCmdStrWithWriter(command string, writer ...io.Writer) *exec.Cmd {
	params := ParseArgs(command)
	return CreateCmdWithWriter(params, writer...)
}

func CreateCmdWithWriter(params []string, writer ...io.Writer) *exec.Cmd {
	var cmd *exec.Cmd
	length := len(params)
	if length == 0 || len(params[0]) == 0 {
		return cmd
	}
	if length > 1 {
		cmd = exec.Command(params[0], params[1:]...)
	} else {
		cmd = exec.Command(params[0])
	}
	var wOut, wErr io.Writer
	length = len(writer)
	if length > 0 && writer[0] != nil {
		wOut = writer[0]
		if length > 1 {
			wErr = writer[1]
		} else {
			wErr = wOut
		}
	} else {
		wOut = os.Stdout
		wErr = os.Stderr
	}
	cmd.Stdout = wOut
	cmd.Stderr = wErr
	return cmd
}

func RunCmdStr(command string, recvResult func([]byte) error) *exec.Cmd {
	out := CmdResultCapturer{Do: recvResult}
	return RunCmdStrWithWriter(command, out)
}

func RunCmd(params []string, recvResult func([]byte) error) *exec.Cmd {
	out := CmdResultCapturer{Do: recvResult}
	return RunCmdWithWriter(params, out)
}

func RunCmdStrWithWriter(command string, writer ...io.Writer) *exec.Cmd {
	params := ParseArgs(command)
	return RunCmdWithWriter(params, writer...)
}

func RunCmdWithWriter(params []string, writer ...io.Writer) *exec.Cmd {
	cmd := CreateCmdWithWriter(params, writer...)

	go func() {
		err := cmd.Run()
		if err != nil {
			cmd.Stderr.Write([]byte(err.Error()))
		}
	}()

	return cmd
}

func CloseProcessFromPidFile(pidFile string) (err error) {
	if pidFile == `` {
		return
	}
	b, err := ioutil.ReadFile(pidFile)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return
	}
	pid, err := strconv.Atoi(strings.TrimSpace(string(b)))
	if err != nil {
		log.Error(err.Error())
		return nil
	}
	return CloseProcessFromPid(pid)
}

func CloseProcessFromPid(pid int) (err error) {
	if pid <= 0 {
		return nil
	}
	procs, err := os.FindProcess(pid)
	if err == nil {
		return procs.Kill()
	}
	return
}
<|endoftext|>


<commit_before>package main

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

var initInput sync.Once
var input *bufio.Reader

func prompt() (string, error) {
	initInput.Do(func (){input = bufio.NewReader(os.Stdin)})

	fmt.Print("masc> ")
	line, _, err := input.ReadLine()
	return string(line), err
}

func isWhite(r rune) bool {
	return r == ' ' || r == '\t'
}

func isQuot(r rune) bool { return r == '"' }

func tokenize(line string) (toks []string) {
	// states
	const (
		READY = iota
		INTOK
		INQUOT
	)

	var tmp string
	state := READY
	for _, c := range line {
		switch state {
		case READY:
			if isWhite(c) { continue }
			tmp = string(c)
			state = INTOK
		case INTOK:
			if isWhite(c) {
				toks = append(toks, tmp)
				state = READY
				continue
			}
			tmp += string(c)
		case INQUOT:
		default:
			panic("Invalid state")
		}
	}
	if len(tmp) > 0 {
		toks = append(toks, tmp)
	}
	return
}

var actions map[string]func([]string)

func init() {
	actions = map[string]func([]string){
		"mail": func(toks []string) {
			panic("NOT YET IMPLEMENTED")
		},
		"m": alias("mail"),
	}
}

func alias(cmd string) func([]string) {
	return actions[cmd]
}

func UIMain() {
	// Prompt loop
	line, err := prompt()
	for err == nil {
		// tokenize the line
		toks := tokenize(line)
		if len(toks) == 0 { goto nothing }
		if toks[0] == "exit" { goto exit }
		{
			action, ok := actions[toks[0]]
			if !ok {
				fmt.Println("?")
				goto nothing
			}
			func () {
				defer func() {
					err := recover()
					if err != nil {
						fmt.Printf("error: %s\n", err)
					}
				}()
				action(toks[1:])
			}()
		}
nothing:
		line, err = prompt()
	}
	if err != nil && err != io.EOF {
		panic(err)
	}
exit:
	fmt.Println("Bye!")
}
<commit_msg>Stub help screen<commit_after>package main

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

var initInput sync.Once
var input *bufio.Reader

func prompt() (string, error) {
	initInput.Do(func (){input = bufio.NewReader(os.Stdin)})

	fmt.Print("masc> ")
	line, _, err := input.ReadLine()
	return string(line), err
}

func isWhite(r rune) bool {
	return r == ' ' || r == '\t'
}

func isQuot(r rune) bool { return r == '"' }

func tokenize(line string) (toks []string) {
	// states
	const (
		READY = iota
		INTOK
		INQUOT
	)

	var tmp string
	state := READY
	for _, c := range line {
		switch state {
		case READY:
			if isWhite(c) { continue }
			tmp = string(c)
			state = INTOK
		case INTOK:
			if isWhite(c) {
				toks = append(toks, tmp)
				state = READY
				continue
			}
			tmp += string(c)
		case INQUOT:
		default:
			panic("Invalid state")
		}
	}
	if len(tmp) > 0 {
		toks = append(toks, tmp)
	}
	return
}

var actions map[string]func([]string)

func init() {
	actions = map[string]func([]string){
		"help": func(toks []string) {
			fmt.Println(`Commands:
`)
		},
		"h": alias("help"),
		"?": alias("help"),
		"mail": func(toks []string) {
			panic("NOT YET IMPLEMENTED")
		},
		"m": alias("mail"),
	}
}

func alias(cmd string) func([]string) {
	return actions[cmd]
}

func UIMain() {
	// Prompt loop
	line, err := prompt()
	for err == nil {
		// tokenize the line
		toks := tokenize(line)
		if len(toks) == 0 { goto nothing }
		if toks[0] == "exit" { goto exit }
		{
			action, ok := actions[toks[0]]
			if !ok {
				fmt.Println("?")
				goto nothing
			}
			func () {
				defer func() {
					err := recover()
					if err != nil {
						fmt.Printf("error: %s\n", err)
					}
				}()
				action(toks[1:])
			}()
		}
nothing:
		line, err = prompt()
	}
	if err != nil && err != io.EOF {
		panic(err)
	}
exit:
	fmt.Println("Bye!")
}
<|endoftext|>


<commit_before>// +build go1.2

// Copyright 2013 com 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 com is an open source project for commonly used functions for the Go programming language.
package com

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"

	"github.com/lunny/log"
)

//内存占用
func ElapsedMemory() (ret string) {
	memStat := new(runtime.MemStats)
	runtime.ReadMemStats(memStat)
	ret = FormatByte(memStat.Alloc, 3)
	return
}

// ExecCmdDirBytes executes system command in given directory
// and return stdout, stderr in bytes type, along with possible error.
func ExecCmdDirBytes(dir, cmdName string, args ...string) ([]byte, []byte, error) {
	bufOut := new(bytes.Buffer)
	bufErr := new(bytes.Buffer)

	cmd := exec.Command(cmdName, args...)
	cmd.Dir = dir
	cmd.Stdout = bufOut
	cmd.Stderr = bufErr

	err := cmd.Run()
	return bufOut.Bytes(), bufErr.Bytes(), err
}

// ExecCmdBytes executes system command
// and return stdout, stderr in bytes type, along with possible error.
func ExecCmdBytes(cmdName string, args ...string) ([]byte, []byte, error) {
	return ExecCmdDirBytes("", cmdName, args...)
}

// ExecCmdDir executes system command in given directory
// and return stdout, stderr in string type, along with possible error.
func ExecCmdDir(dir, cmdName string, args ...string) (string, string, error) {
	bufOut, bufErr, err := ExecCmdDirBytes(dir, cmdName, args...)
	return string(bufOut), string(bufErr), err
}

// ExecCmd executes system command
// and return stdout, stderr in string type, along with possible error.
func ExecCmd(cmdName string, args ...string) (string, string, error) {
	return ExecCmdDir("", cmdName, args...)
}

// WritePidFile writes the process ID to the file at PidFile.
// It does nothing if PidFile is not set.
func WritePidFile(pidFile string) error {
	if pidFile == "" {
		return nil
	}
	pid := []byte(strconv.Itoa(os.Getpid()) + "\n")
	return ioutil.WriteFile(pidFile, pid, 0644)
}

var (
	space  = rune(' ')
	quote  = rune('"')
	slash  = rune('\\')
	envOS  = regexp.MustCompile(`\{\$[a-zA-Z0-9_]+\}`)
	envWin = regexp.MustCompile(`\{%[a-zA-Z0-9_]+%\}`)
)

func ParseArgs(command string) (params []string) {
	item := []rune{}
	hasQuote := false
	hasSlash := false
	maxIndex := len(command) - 1
	//tower.exe -c tower.yaml -p "eee\"ddd" -t aaaa
	for k, v := range command {
		if !hasQuote {
			if v == space {
				params = append(params, string(item))
				item = []rune{}
				continue
			}
			if v == quote {
				hasQuote = true
				continue
			}
		} else {
			if !hasSlash && v == quote {
				hasQuote = false
				continue
			}
			if !hasSlash && v == slash && k+1 <= maxIndex && command[k+1] == '"' {
				hasSlash = true
				continue
			}
			hasSlash = false
		}
		item = append(item, v)
	}
	if len(item) > 0 {
		params = append(params, string(item))
	}
	for k, v := range params {
		v = envWin.ReplaceAllStringFunc(v, getWinEnv)
		params[k] = envOS.ReplaceAllStringFunc(v, getEnv)
	}
	//fmt.Printf("---> %#v\n", params)
	//params = []string{}
	return
}

func getWinEnv(s string) string {
	s = strings.TrimPrefix(s, `{%`)
	s = strings.TrimSuffix(s, `%}`)
	return os.Getenv(s)
}

func getEnv(s string) string {
	s = strings.TrimPrefix(s, `{$`)
	s = strings.TrimSuffix(s, `}`)
	return os.Getenv(s)
}

type CmdResultCapturer struct {
	Do func([]byte) error
}

func (this CmdResultCapturer) Write(p []byte) (n int, err error) {
	err = this.Do(p)
	n = len(p)
	return
}

func (this CmdResultCapturer) WriteString(p string) (n int, err error) {
	err = this.Do([]byte(p))
	n = len(p)
	return
}

func RunCmdStr(command string, recvResult func([]byte) error) *exec.Cmd {
	out := CmdResultCapturer{Do: recvResult}
	return RunCmdStrWithWriter(command, out)
}

func RunCmd(params []string, recvResult func([]byte) error) *exec.Cmd {
	out := CmdResultCapturer{Do: recvResult}
	return RunCmdWithWriter(params, out)
}

func RunCmdStrWithWriter(command string, writer ...io.Writer) *exec.Cmd {
	params := ParseArgs(command)
	return RunCmdWithWriter(params, writer...)
}

func RunCmdWithWriter(params []string, writer ...io.Writer) *exec.Cmd {
	var cmd *exec.Cmd
	length := len(params)
	if length == 0 || len(params[0]) == 0 {
		return cmd
	}
	if length > 1 {
		cmd = exec.Command(params[0], params[1:]...)
	} else {
		cmd = exec.Command(params[0])
	}
	var wOut, wErr io.Writer
	length = len(writer)
	if length > 0 && writer[0] != nil {
		wOut = writer[0]
		if length > 1 {
			wErr = writer[1]
		} else {
			wErr = wOut
		}
	} else {
		wOut = os.Stdout
		wErr = os.Stdout
	}
	cmd.Stdout = wOut
	cmd.Stderr = wErr

	go func() {
		err := cmd.Run()
		if err != nil {
			wErr.Write([]byte(err.Error()))
		}
	}()

	return cmd
}

func CloseProcessFromPidFile(pidFile string) (err error) {
	if pidFile == `` {
		return
	}
	b, err := ioutil.ReadFile(pidFile)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return
	}
	pid, err := strconv.Atoi(strings.TrimSpace(string(b)))
	if err != nil {
		log.Error(err.Error())
		return nil
	}
	return CloseProcessFromPid(pid)
}

func CloseProcessFromPid(pid int) (err error) {
	procs, err := os.FindProcess(pid)
	if err == nil {
		return procs.Kill()
	}
	return
}
<commit_msg>improved<commit_after>// +build go1.2

// Copyright 2013 com 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 com is an open source project for commonly used functions for the Go programming language.
package com

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"

	"github.com/lunny/log"
)

//内存占用
func ElapsedMemory() (ret string) {
	memStat := new(runtime.MemStats)
	runtime.ReadMemStats(memStat)
	ret = FormatByte(memStat.Alloc, 3)
	return
}

// ExecCmdDirBytes executes system command in given directory
// and return stdout, stderr in bytes type, along with possible error.
func ExecCmdDirBytes(dir, cmdName string, args ...string) ([]byte, []byte, error) {
	bufOut := new(bytes.Buffer)
	bufErr := new(bytes.Buffer)

	cmd := exec.Command(cmdName, args...)
	cmd.Dir = dir
	cmd.Stdout = bufOut
	cmd.Stderr = bufErr

	err := cmd.Run()
	return bufOut.Bytes(), bufErr.Bytes(), err
}

// ExecCmdBytes executes system command
// and return stdout, stderr in bytes type, along with possible error.
func ExecCmdBytes(cmdName string, args ...string) ([]byte, []byte, error) {
	return ExecCmdDirBytes("", cmdName, args...)
}

// ExecCmdDir executes system command in given directory
// and return stdout, stderr in string type, along with possible error.
func ExecCmdDir(dir, cmdName string, args ...string) (string, string, error) {
	bufOut, bufErr, err := ExecCmdDirBytes(dir, cmdName, args...)
	return string(bufOut), string(bufErr), err
}

// ExecCmd executes system command
// and return stdout, stderr in string type, along with possible error.
func ExecCmd(cmdName string, args ...string) (string, string, error) {
	return ExecCmdDir("", cmdName, args...)
}

// WritePidFile writes the process ID to the file at PidFile.
// It does nothing if PidFile is not set.
func WritePidFile(pidFile string) error {
	if pidFile == "" {
		return nil
	}
	pid := []byte(strconv.Itoa(os.Getpid()) + "\n")
	return ioutil.WriteFile(pidFile, pid, 0644)
}

var (
	space  = rune(' ')
	quote  = rune('"')
	slash  = rune('\\')
	envOS  = regexp.MustCompile(`\{\$[a-zA-Z0-9_]+\}`)
	envWin = regexp.MustCompile(`\{%[a-zA-Z0-9_]+%\}`)
)

func ParseArgs(command string) (params []string) {
	item := []rune{}
	hasQuote := false
	hasSlash := false
	maxIndex := len(command) - 1
	//tower.exe -c tower.yaml -p "eee\"ddd" -t aaaa
	for k, v := range command {
		if !hasQuote {
			if v == space {
				params = append(params, string(item))
				item = []rune{}
				continue
			}
			if v == quote {
				hasQuote = true
				continue
			}
		} else {
			if !hasSlash && v == quote {
				hasQuote = false
				continue
			}
			if !hasSlash && v == slash && k+1 <= maxIndex && command[k+1] == '"' {
				hasSlash = true
				continue
			}
			hasSlash = false
		}
		item = append(item, v)
	}
	if len(item) > 0 {
		params = append(params, string(item))
	}
	for k, v := range params {
		v = envWin.ReplaceAllStringFunc(v, getWinEnv)
		params[k] = envOS.ReplaceAllStringFunc(v, getEnv)
	}
	//fmt.Printf("---> %#v\n", params)
	//params = []string{}
	return
}

func getWinEnv(s string) string {
	s = strings.TrimPrefix(s, `{%`)
	s = strings.TrimSuffix(s, `%}`)
	return os.Getenv(s)
}

func getEnv(s string) string {
	s = strings.TrimPrefix(s, `{$`)
	s = strings.TrimSuffix(s, `}`)
	return os.Getenv(s)
}

type CmdResultCapturer struct {
	Do func([]byte) error
}

func (this CmdResultCapturer) Write(p []byte) (n int, err error) {
	err = this.Do(p)
	n = len(p)
	return
}

func (this CmdResultCapturer) WriteString(p string) (n int, err error) {
	err = this.Do([]byte(p))
	n = len(p)
	return
}

func RunCmdStr(command string, recvResult func([]byte) error) *exec.Cmd {
	out := CmdResultCapturer{Do: recvResult}
	return RunCmdStrWithWriter(command, out)
}

func RunCmd(params []string, recvResult func([]byte) error) *exec.Cmd {
	out := CmdResultCapturer{Do: recvResult}
	return RunCmdWithWriter(params, out)
}

func RunCmdStrWithWriter(command string, writer ...io.Writer) *exec.Cmd {
	params := ParseArgs(command)
	return RunCmdWithWriter(params, writer...)
}

func RunCmdWithWriter(params []string, writer ...io.Writer) *exec.Cmd {
	var cmd *exec.Cmd
	length := len(params)
	if length == 0 || len(params[0]) == 0 {
		return cmd
	}
	if length > 1 {
		cmd = exec.Command(params[0], params[1:]...)
	} else {
		cmd = exec.Command(params[0])
	}
	var wOut, wErr io.Writer
	length = len(writer)
	if length > 0 && writer[0] != nil {
		wOut = writer[0]
		if length > 1 {
			wErr = writer[1]
		} else {
			wErr = wOut
		}
	} else {
		wOut = os.Stdout
		wErr = os.Stderr
	}
	cmd.Stdout = wOut
	cmd.Stderr = wErr

	go func() {
		err := cmd.Run()
		if err != nil {
			cmd.Stderr.Write([]byte(err.Error()))
		}
	}()

	return cmd
}

func CloseProcessFromPidFile(pidFile string) (err error) {
	if pidFile == `` {
		return
	}
	b, err := ioutil.ReadFile(pidFile)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return
	}
	pid, err := strconv.Atoi(strings.TrimSpace(string(b)))
	if err != nil {
		log.Error(err.Error())
		return nil
	}
	return CloseProcessFromPid(pid)
}

func CloseProcessFromPid(pid int) (err error) {
	procs, err := os.FindProcess(pid)
	if err == nil {
		return procs.Kill()
	}
	return
}
<|endoftext|>