<commit_before>package command

import (
	"flag"
	"fmt"
	"os"
	"path"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/command/agent"
	"github.com/mitchellh/cli"
)

const (
	// lockKillGracePeriod is how long we allow a child between
	// a SIGTERM and a SIGKILL. This is to let the child cleanup
	// any necessary state. We have to balance this with the risk
	// of a split-brain where multiple children may be acting as if
	// they hold a lock. This value is currently based on the default
	// lock-delay value of 15 seconds. This only affects locks and not
	// semaphores.
	lockKillGracePeriod = 5 * time.Second
)

// LockCommand is a Command implementation that is used to setup
// a "lock" which manages lock acquisition and invokes a sub-process
type LockCommand struct {
	ShutdownCh <-chan struct{}
	Ui         cli.Ui

	child     *os.Process
	childLock sync.Mutex
	verbose   bool
}

func (c *LockCommand) Help() string {
	helpText := `
Usage: consul lock [options] prefix child...

  Acquires a lock or semaphore at a given path, and invokes a child
  process when successful. The child process can assume the lock is
  held while it executes. If the lock is lost or communication is
  disrupted the child process will be sent a SIGTERM signal and given
  time to gracefully exit. After the grace period expires the process
  will be hard terminated.
  For Consul agents on Windows, the child process is always hard
  terminated with a SIGKILL, since Windows has no POSIX compatible
  notion for SIGTERM.

  When -n=1, only a single lock holder or leader exists providing
  mutual exclusion. Setting a higher value switches to a semaphore
  allowing multiple holders to coordinate.

  The prefix provided must have write privileges.

Options:

  -http-addr=127.0.0.1:8500  HTTP address of the Consul agent.
  -n=1                       Maximum number of allowed lock holders. If this
                             value is one, it operates as a lock, otherwise
                             a semaphore is used.
  -name=""                   Optional name to associate with lock session.
  -token=""                  ACL token to use. Defaults to that of agent.
  -verbose                   Enables verbose output
`
	return strings.TrimSpace(helpText)
}

func (c *LockCommand) Run(args []string) int {
	var childDone chan struct{}
	var name, token string
	var limit int
	cmdFlags := flag.NewFlagSet("watch", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
	cmdFlags.IntVar(&limit, "n", 1, "")
	cmdFlags.StringVar(&name, "name", "", "")
	cmdFlags.StringVar(&token, "token", "", "")
	cmdFlags.BoolVar(&c.verbose, "verbose", false, "")
	httpAddr := HTTPAddrFlag(cmdFlags)
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	// Check the limit
	if limit <= 0 {
		c.Ui.Error(fmt.Sprintf("Lock holder limit must be positive"))
		return 1
	}

	// Verify the prefix and child are provided
	extra := cmdFlags.Args()
	if len(extra) < 2 {
		c.Ui.Error("Key prefix and child command must be specified")
		c.Ui.Error("")
		c.Ui.Error(c.Help())
		return 1
	}
	prefix := extra[0]
	prefix = strings.TrimPrefix(prefix, "/")
	script := strings.Join(extra[1:], " ")

	// Calculate a session name if none provided
	if name == "" {
		name = fmt.Sprintf("Consul lock for '%s' at '%s'", script, prefix)
	}

	// Create and test the HTTP client
	conf := api.DefaultConfig()
	conf.Address = *httpAddr
	conf.Token = token
	client, err := api.NewClient(conf)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
		return 1
	}
	_, err = client.Agent().NodeName()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error querying Consul agent: %s", err))
		return 1
	}

	// Setup the lock or semaphore
	var lu *LockUnlock
	if limit == 1 {
		lu, err = c.setupLock(client, prefix, name)
	} else {
		lu, err = c.setupSemaphore(client, limit, prefix, name)
	}
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Lock setup failed: %s", err))
		return 1
	}

	// Attempt the acquisition
	if c.verbose {
		c.Ui.Info("Attempting lock acquisition")
	}
	lockCh, err := lu.lockFn(c.ShutdownCh)
	if lockCh == nil {
		if err == nil {
			c.Ui.Error("Shutdown triggered during lock acquisition")
		} else {
			c.Ui.Error(fmt.Sprintf("Lock acquisition failed: %s", err))
		}
		return 1
	}

	// Check if we were shutdown but managed to still acquire the lock
	select {
	case <-c.ShutdownCh:
		c.Ui.Error("Shutdown triggered during lock acquisition")
		goto RELEASE
	default:
	}

	// Start the child process
	childDone = make(chan struct{})
	go func() {
		if err := c.startChild(script, childDone); err != nil {
			c.Ui.Error(fmt.Sprintf("%s", err))
		}
	}()

	// Monitor for shutdown, child termination, or lock loss
	select {
	case <-c.ShutdownCh:
		if c.verbose {
			c.Ui.Info("Shutdown triggered, killing child")
		}
	case <-lockCh:
		if c.verbose {
			c.Ui.Info("Lock lost, killing child")
		}
	case <-childDone:
		if c.verbose {
			c.Ui.Info("Child terminated, releasing lock")
		}
		goto RELEASE
	}

	// Kill the child
	if err := c.killChild(childDone); err != nil {
		c.Ui.Error(fmt.Sprintf("%s", err))
	}

RELEASE:
	// Release the lock before termination
	if err := lu.unlockFn(); err != nil {
		c.Ui.Error(fmt.Sprintf("Lock release failed: %s", err))
		return 1
	}

	// Cleanup the lock if no longer in use
	if err := lu.cleanupFn(); err != nil {
		if err != lu.inUseErr {
			c.Ui.Error(fmt.Sprintf("Lock cleanup failed: %s", err))
			return 1
		} else if c.verbose {
			c.Ui.Info("Cleanup aborted, lock in use")
		}
	} else if c.verbose {
		c.Ui.Info("Cleanup succeeded")
	}
	return 0
}

// setupLock is used to setup a new Lock given the API client,
// the key prefix to operate on, and an optional session name.
func (c *LockCommand) setupLock(client *api.Client, prefix, name string) (*LockUnlock, error) {
	// Use the DefaultSemaphoreKey extention, this way if a lock and
	// semaphore are both used at the same prefix, we will get a conflict
	// which we can report to the user.
	key := path.Join(prefix, api.DefaultSemaphoreKey)
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Setting up lock at path: %s", key))
	}
	opts := api.LockOptions{
		Key:         key,
		SessionName: name,
	}
	l, err := client.LockOpts(&opts)
	if err != nil {
		return nil, err
	}
	lu := &LockUnlock{
		lockFn:    l.Lock,
		unlockFn:  l.Unlock,
		cleanupFn: l.Destroy,
		inUseErr:  api.ErrLockInUse,
	}
	return lu, nil
}

// setupSemaphore is used to setup a new Semaphore given the
// API client, key prefix, session name, and slot holder limit.
func (c *LockCommand) setupSemaphore(client *api.Client, limit int, prefix, name string) (*LockUnlock, error) {
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Setting up semaphore (limit %d) at prefix: %s", limit, prefix))
	}
	opts := api.SemaphoreOptions{
		Prefix:      prefix,
		Limit:       limit,
		SessionName: name,
	}
	s, err := client.SemaphoreOpts(&opts)
	if err != nil {
		return nil, err
	}
	lu := &LockUnlock{
		lockFn:    s.Acquire,
		unlockFn:  s.Release,
		cleanupFn: s.Destroy,
		inUseErr:  api.ErrSemaphoreInUse,
	}
	return lu, nil
}

// startChild is a long running routine used to start and
// wait for the child process to exit.
func (c *LockCommand) startChild(script string, doneCh chan struct{}) error {
	defer close(doneCh)
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Starting handler '%s'", script))
	}
	// Create the command
	cmd, err := agent.ExecScript(script)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error executing handler: %s", err))
		return err
	}

	// Setup the command streams
	cmd.Env = append(os.Environ(),
		"CONSUL_LOCK_HELD=true",
	)
	cmd.Stdin = nil
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// Start the child process
	c.childLock.Lock()
	if err := cmd.Start(); err != nil {
		c.Ui.Error(fmt.Sprintf("Error starting handler: %s", err))
		c.childLock.Unlock()
		return err
	}

	// Setup the child info
	c.child = cmd.Process
	c.childLock.Unlock()

	// Wait for the child process
	if err := cmd.Wait(); err != nil {
		c.Ui.Error(fmt.Sprintf("Error running handler: %s", err))
		return err
	}
	return nil
}

// killChild is used to forcefully kill the child, first using SIGTERM
// to allow for a graceful cleanup and then using SIGKILL for a hard
// termination.
// On Windows, the child is always hard terminated with a SIGKILL, even
// on the first attempt.
func (c *LockCommand) killChild(childDone chan struct{}) error {
	// Get the child process
	c.childLock.Lock()
	child := c.child
	c.childLock.Unlock()

	// If there is no child process (failed to start), we can quit early
	if child == nil {
		if c.verbose {
			c.Ui.Info("No child process to kill")
		}
		return nil
	}

	// Attempt termination first
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Terminating child pid %d", child.Pid))
	}
	if err := signalPid(child.Pid, syscall.SIGTERM); err != nil {
		return fmt.Errorf("Failed to terminate %d: %v", child.Pid, err)
	}

	// Wait for termination, or until a timeout
	select {
	case <-childDone:
		if c.verbose {
			c.Ui.Info("Child terminated")
		}
		return nil
	case <-time.After(lockKillGracePeriod):
		if c.verbose {
			c.Ui.Info(fmt.Sprintf("Child did not exit after grace period of %v",
				lockKillGracePeriod))
		}
	}

	// Send a final SIGKILL
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Killing child pid %d", child.Pid))
	}
	if err := signalPid(child.Pid, syscall.SIGKILL); err != nil {
		return fmt.Errorf("Failed to kill %d: %v", child.Pid, err)
	}
	return nil
}

func (c *LockCommand) Synopsis() string {
	return "Execute a command holding a lock"
}

// LockUnlock is used to abstract over the differences between
// a lock and a semaphore.
type LockUnlock struct {
	lockFn    func(<-chan struct{}) (<-chan struct{}, error)
	unlockFn  func() error
	cleanupFn func() error
	inUseErr  error
}
<commit_msg>command/lock: Pass stdin to child process when -pass-stdin passed.<commit_after>package command

import (
	"flag"
	"fmt"
	"os"
	"path"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/command/agent"
	"github.com/mitchellh/cli"
)

const (
	// lockKillGracePeriod is how long we allow a child between
	// a SIGTERM and a SIGKILL. This is to let the child cleanup
	// any necessary state. We have to balance this with the risk
	// of a split-brain where multiple children may be acting as if
	// they hold a lock. This value is currently based on the default
	// lock-delay value of 15 seconds. This only affects locks and not
	// semaphores.
	lockKillGracePeriod = 5 * time.Second
)

// LockCommand is a Command implementation that is used to setup
// a "lock" which manages lock acquisition and invokes a sub-process
type LockCommand struct {
	ShutdownCh <-chan struct{}
	Ui         cli.Ui

	child     *os.Process
	childLock sync.Mutex
	verbose   bool
}

func (c *LockCommand) Help() string {
	helpText := `
Usage: consul lock [options] prefix child...

  Acquires a lock or semaphore at a given path, and invokes a child
  process when successful. The child process can assume the lock is
  held while it executes. If the lock is lost or communication is
  disrupted the child process will be sent a SIGTERM signal and given
  time to gracefully exit. After the grace period expires the process
  will be hard terminated.
  For Consul agents on Windows, the child process is always hard
  terminated with a SIGKILL, since Windows has no POSIX compatible
  notion for SIGTERM.

  When -n=1, only a single lock holder or leader exists providing
  mutual exclusion. Setting a higher value switches to a semaphore
  allowing multiple holders to coordinate.

  The prefix provided must have write privileges.

Options:

  -http-addr=127.0.0.1:8500  HTTP address of the Consul agent.
  -n=1                       Maximum number of allowed lock holders. If this
                             value is one, it operates as a lock, otherwise
                             a semaphore is used.
  -name=""                   Optional name to associate with lock session.
  -token=""                  ACL token to use. Defaults to that of agent.
  -pass-stdin                Pass stdin to child process.
  -verbose                   Enables verbose output
`
	return strings.TrimSpace(helpText)
}

func (c *LockCommand) Run(args []string) int {
	var childDone chan struct{}
	var name, token string
	var limit int
	var passStdin bool
	cmdFlags := flag.NewFlagSet("watch", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
	cmdFlags.IntVar(&limit, "n", 1, "")
	cmdFlags.StringVar(&name, "name", "", "")
	cmdFlags.StringVar(&token, "token", "", "")
	cmdFlags.BoolVar(&passStdin, "pass-stdin", false, "")
	cmdFlags.BoolVar(&c.verbose, "verbose", false, "")
	httpAddr := HTTPAddrFlag(cmdFlags)
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	// Check the limit
	if limit <= 0 {
		c.Ui.Error(fmt.Sprintf("Lock holder limit must be positive"))
		return 1
	}

	// Verify the prefix and child are provided
	extra := cmdFlags.Args()
	if len(extra) < 2 {
		c.Ui.Error("Key prefix and child command must be specified")
		c.Ui.Error("")
		c.Ui.Error(c.Help())
		return 1
	}
	prefix := extra[0]
	prefix = strings.TrimPrefix(prefix, "/")
	script := strings.Join(extra[1:], " ")

	// Calculate a session name if none provided
	if name == "" {
		name = fmt.Sprintf("Consul lock for '%s' at '%s'", script, prefix)
	}

	// Create and test the HTTP client
	conf := api.DefaultConfig()
	conf.Address = *httpAddr
	conf.Token = token
	client, err := api.NewClient(conf)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
		return 1
	}
	_, err = client.Agent().NodeName()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error querying Consul agent: %s", err))
		return 1
	}

	// Setup the lock or semaphore
	var lu *LockUnlock
	if limit == 1 {
		lu, err = c.setupLock(client, prefix, name)
	} else {
		lu, err = c.setupSemaphore(client, limit, prefix, name)
	}
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Lock setup failed: %s", err))
		return 1
	}

	// Attempt the acquisition
	if c.verbose {
		c.Ui.Info("Attempting lock acquisition")
	}
	lockCh, err := lu.lockFn(c.ShutdownCh)
	if lockCh == nil {
		if err == nil {
			c.Ui.Error("Shutdown triggered during lock acquisition")
		} else {
			c.Ui.Error(fmt.Sprintf("Lock acquisition failed: %s", err))
		}
		return 1
	}

	// Check if we were shutdown but managed to still acquire the lock
	select {
	case <-c.ShutdownCh:
		c.Ui.Error("Shutdown triggered during lock acquisition")
		goto RELEASE
	default:
	}

	// Start the child process
	childDone = make(chan struct{})
	go func() {
		if err := c.startChild(script, childDone, passStdin); err != nil {
			c.Ui.Error(fmt.Sprintf("%s", err))
		}
	}()

	// Monitor for shutdown, child termination, or lock loss
	select {
	case <-c.ShutdownCh:
		if c.verbose {
			c.Ui.Info("Shutdown triggered, killing child")
		}
	case <-lockCh:
		if c.verbose {
			c.Ui.Info("Lock lost, killing child")
		}
	case <-childDone:
		if c.verbose {
			c.Ui.Info("Child terminated, releasing lock")
		}
		goto RELEASE
	}

	// Kill the child
	if err := c.killChild(childDone); err != nil {
		c.Ui.Error(fmt.Sprintf("%s", err))
	}

RELEASE:
	// Release the lock before termination
	if err := lu.unlockFn(); err != nil {
		c.Ui.Error(fmt.Sprintf("Lock release failed: %s", err))
		return 1
	}

	// Cleanup the lock if no longer in use
	if err := lu.cleanupFn(); err != nil {
		if err != lu.inUseErr {
			c.Ui.Error(fmt.Sprintf("Lock cleanup failed: %s", err))
			return 1
		} else if c.verbose {
			c.Ui.Info("Cleanup aborted, lock in use")
		}
	} else if c.verbose {
		c.Ui.Info("Cleanup succeeded")
	}
	return 0
}

// setupLock is used to setup a new Lock given the API client,
// the key prefix to operate on, and an optional session name.
func (c *LockCommand) setupLock(client *api.Client, prefix, name string) (*LockUnlock, error) {
	// Use the DefaultSemaphoreKey extention, this way if a lock and
	// semaphore are both used at the same prefix, we will get a conflict
	// which we can report to the user.
	key := path.Join(prefix, api.DefaultSemaphoreKey)
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Setting up lock at path: %s", key))
	}
	opts := api.LockOptions{
		Key:         key,
		SessionName: name,
	}
	l, err := client.LockOpts(&opts)
	if err != nil {
		return nil, err
	}
	lu := &LockUnlock{
		lockFn:    l.Lock,
		unlockFn:  l.Unlock,
		cleanupFn: l.Destroy,
		inUseErr:  api.ErrLockInUse,
	}
	return lu, nil
}

// setupSemaphore is used to setup a new Semaphore given the
// API client, key prefix, session name, and slot holder limit.
func (c *LockCommand) setupSemaphore(client *api.Client, limit int, prefix, name string) (*LockUnlock, error) {
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Setting up semaphore (limit %d) at prefix: %s", limit, prefix))
	}
	opts := api.SemaphoreOptions{
		Prefix:      prefix,
		Limit:       limit,
		SessionName: name,
	}
	s, err := client.SemaphoreOpts(&opts)
	if err != nil {
		return nil, err
	}
	lu := &LockUnlock{
		lockFn:    s.Acquire,
		unlockFn:  s.Release,
		cleanupFn: s.Destroy,
		inUseErr:  api.ErrSemaphoreInUse,
	}
	return lu, nil
}

// startChild is a long running routine used to start and
// wait for the child process to exit.
func (c *LockCommand) startChild(script string, doneCh chan struct{}, passStdin bool) error {
	defer close(doneCh)
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Starting handler '%s'", script))
	}
	// Create the command
	cmd, err := agent.ExecScript(script)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error executing handler: %s", err))
		return err
	}

	// Setup the command streams
	cmd.Env = append(os.Environ(),
		"CONSUL_LOCK_HELD=true",
	)
	if passStdin {
		if c.verbose {
			c.Ui.Info("Stdin passed to handler process")
		}
		cmd.Stdin = os.Stdin
	} else {
		cmd.Stdin = nil
	}
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// Start the child process
	c.childLock.Lock()
	if err := cmd.Start(); err != nil {
		c.Ui.Error(fmt.Sprintf("Error starting handler: %s", err))
		c.childLock.Unlock()
		return err
	}

	// Setup the child info
	c.child = cmd.Process
	c.childLock.Unlock()

	// Wait for the child process
	if err := cmd.Wait(); err != nil {
		c.Ui.Error(fmt.Sprintf("Error running handler: %s", err))
		return err
	}
	return nil
}

// killChild is used to forcefully kill the child, first using SIGTERM
// to allow for a graceful cleanup and then using SIGKILL for a hard
// termination.
// On Windows, the child is always hard terminated with a SIGKILL, even
// on the first attempt.
func (c *LockCommand) killChild(childDone chan struct{}) error {
	// Get the child process
	c.childLock.Lock()
	child := c.child
	c.childLock.Unlock()

	// If there is no child process (failed to start), we can quit early
	if child == nil {
		if c.verbose {
			c.Ui.Info("No child process to kill")
		}
		return nil
	}

	// Attempt termination first
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Terminating child pid %d", child.Pid))
	}
	if err := signalPid(child.Pid, syscall.SIGTERM); err != nil {
		return fmt.Errorf("Failed to terminate %d: %v", child.Pid, err)
	}

	// Wait for termination, or until a timeout
	select {
	case <-childDone:
		if c.verbose {
			c.Ui.Info("Child terminated")
		}
		return nil
	case <-time.After(lockKillGracePeriod):
		if c.verbose {
			c.Ui.Info(fmt.Sprintf("Child did not exit after grace period of %v",
				lockKillGracePeriod))
		}
	}

	// Send a final SIGKILL
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Killing child pid %d", child.Pid))
	}
	if err := signalPid(child.Pid, syscall.SIGKILL); err != nil {
		return fmt.Errorf("Failed to kill %d: %v", child.Pid, err)
	}
	return nil
}

func (c *LockCommand) Synopsis() string {
	return "Execute a command holding a lock"
}

// LockUnlock is used to abstract over the differences between
// a lock and a semaphore.
type LockUnlock struct {
	lockFn    func(<-chan struct{}) (<-chan struct{}, error)
	unlockFn  func() error
	cleanupFn func() error
	inUseErr  error
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"os"
	"testing"
	"time"
)

import . "launchpad.net/gocheck"

var travis = flag.Bool("travis", false, "Enable it if the tests runs in TravisCI")

// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { TestingT(t) }

type CommandSuite struct{}

var _ = Suite(&CommandSuite{})

func (self *CommandSuite) TestBasic(c *C) {
	cmd := NewCommand("./tests/test -exit=0 -time=1 -max=100000")
	cmd.Run()
	cmd.Wait()

	response := cmd.GetResponse()

	c.Assert(response.Failed, Equals, false)
	c.Assert(response.ExitCode, Equals, 0)
	c.Assert(response.Stdout, HasLen, 588895)
	c.Assert(response.Stderr, HasLen, 0)
	c.Assert(response.Pid, Not(Equals), 0)
	c.Assert(int(response.RealTime/time.Second), Equals, 1)
	c.Assert(int(response.UserTime), Not(Equals), 0)
	c.Assert(int(response.SysTime), Not(Equals), 0)
	c.Assert(int(response.Rusage.Utime.Usec), Not(Equals), 0)
}

func (self *CommandSuite) TestBasicWithTimeout(c *C) {
	cmd := NewCommand("./tests/test -exit=0 -time=2")
	cmd.SetTimeout(1 * time.Second)
	cmd.Run()
	cmd.Wait()

	response := cmd.GetResponse()

	c.Assert(response.Failed, Equals, true)
	c.Assert(response.ExitCode, Equals, -1)
	c.Assert(int(response.RealTime/time.Second), Equals, 1)
	c.Assert(int(response.UserTime), Not(Equals), 0)
}

func (self *CommandSuite) TestKill(c *C) {
	cmd := NewCommand("./tests/test -exit=0 -time=2")
	cmd.Run()

	go func() {
		time.Sleep(1 * time.Second)
		cmd.Kill()
	}()

	cmd.Wait()

	response := cmd.GetResponse()

	c.Assert(response.Failed, Equals, true)
	c.Assert(response.ExitCode, Equals, -1)
	c.Assert(int(response.RealTime/time.Second), Equals, 1)
	c.Assert(int(response.UserTime), Not(Equals), 0)
}

func (self *CommandSuite) TestSetUser(c *C) {
	if *travis {
		c.Skip("Running at TravisCI")
	}

	cmd := NewCommand("./tests/test -exit=0 -time=1")
	cmd.SetUser("daemon")
	if err := cmd.Run(); err != nil {
		fmt.Println(err)
		c.Fail()
		return
	}

	cmd.Wait()

	response := cmd.GetResponse()

	c.Assert(response.Failed, Equals, false)
	c.Assert(response.ExitCode, Equals, 0)
}

func (self *CommandSuite) TestSetWorkingDir(c *C) {
	cmd := NewCommand("./test -exit=0 -wd")

	cwd, _ := os.Getwd()
	wd := cwd + "/tests"

	cmd.SetWorkingDir(wd)
	cmd.Run()
	cmd.Wait()

	response := cmd.GetResponse()

	c.Assert(response.Failed, Equals, false)
	c.Assert(response.ExitCode, Equals, 0)
	c.Assert(string(response.Stdout), Equals, wd+"\n")
}

func (self *CommandSuite) TestSetEnvironment(c *C) {
	cmd := NewCommand("./tests/test -exit=0 -env")
	cmd.SetEnvironment([]string{"FOO=bar"})
	cmd.Run()
	cmd.Wait()

	response := cmd.GetResponse()

	c.Assert(response.Failed, Equals, false)
	c.Assert(response.ExitCode, Equals, 0)
	c.Assert(string(response.Stdout), Equals, "FOO=bar\n")
}
<commit_msg>fix tests<commit_after>package command

import (
	"flag"
	"fmt"
	"os"
	"testing"
	"time"
)

import . "launchpad.net/gocheck"

var travis = flag.Bool("travis", false, "Enable it if the tests runs in TravisCI")

// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { TestingT(t) }

type CommandSuite struct{}

var _ = Suite(&CommandSuite{})

func (self *CommandSuite) TestBasic(c *C) {
	cmd := NewCommand("./tests/test -exit=0 -time=1 -max=100000")
	cmd.Run()
	cmd.Wait()

	response := cmd.GetResponse()

	c.Assert(response.Failed, Equals, false)
	c.Assert(response.ExitCode, Equals, 0)
	c.Assert(response.Stdout, HasLen, 588895)
	c.Assert(response.Stderr, HasLen, 0)
	c.Assert(response.Pid, Not(Equals), 0)
	c.Assert(int(response.RealTime/time.Second), Equals, 1)
	c.Assert(int(response.UserTime), Not(Equals), 0)
	c.Assert(int(response.SysTime), Not(Equals), 0)
	c.Assert(int(response.Rusage.Utime.Usec), Not(Equals), 0)
}

func (self *CommandSuite) TestBasicWithTimeout(c *C) {
	cmd := NewCommand("./tests/test -exit=0 -time=2")
	cmd.SetTimeout(1 * time.Second)
	cmd.Run()
	cmd.Wait()

	response := cmd.GetResponse()

	c.Assert(response.Failed, Equals, true)
	c.Assert(response.ExitCode, Equals, -1)
	c.Assert(int(response.RealTime/time.Second), Equals, 1)
}

func (self *CommandSuite) TestKill(c *C) {
	cmd := NewCommand("./tests/test -exit=0 -time=2")
	cmd.Run()

	go func() {
		time.Sleep(1 * time.Second)
		cmd.Kill()
	}()

	cmd.Wait()

	response := cmd.GetResponse()

	c.Assert(response.Failed, Equals, true)
	c.Assert(response.ExitCode, Equals, -1)
	c.Assert(int(response.RealTime/time.Second), Equals, 1)
}

func (self *CommandSuite) TestSetUser(c *C) {
	if *travis {
		c.Skip("Running at TravisCI")
	}

	cmd := NewCommand("./tests/test -exit=0 -time=1")
	cmd.SetUser("daemon")
	if err := cmd.Run(); err != nil {
		fmt.Println(err)
		c.Fail()
		return
	}

	cmd.Wait()

	response := cmd.GetResponse()

	c.Assert(response.Failed, Equals, false)
	c.Assert(response.ExitCode, Equals, 0)
}

func (self *CommandSuite) TestSetWorkingDir(c *C) {
	cmd := NewCommand("./test -exit=0 -wd")

	cwd, _ := os.Getwd()
	wd := cwd + "/tests"

	cmd.SetWorkingDir(wd)
	cmd.Run()
	cmd.Wait()

	response := cmd.GetResponse()

	c.Assert(response.Failed, Equals, false)
	c.Assert(response.ExitCode, Equals, 0)
	c.Assert(string(response.Stdout), Equals, wd+"\n")
}

func (self *CommandSuite) TestSetEnvironment(c *C) {
	cmd := NewCommand("./tests/test -exit=0 -env")
	cmd.SetEnvironment([]string{"FOO=bar"})
	cmd.Run()
	cmd.Wait()

	response := cmd.GetResponse()

	c.Assert(response.Failed, Equals, false)
	c.Assert(response.ExitCode, Equals, 0)
	c.Assert(string(response.Stdout), Equals, "FOO=bar\n")
}
<|endoftext|>


<commit_before>package commands

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/zetamatta/go-box"

	"github.com/zetamatta/nyagos/readline"
	"github.com/zetamatta/nyagos/shell"
)

func cmd_box(ctx context.Context, cmd *shell.Cmd) (int, error) {
	data, err := ioutil.ReadAll(cmd.Stdin)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		return 1, err
	}
	list := strings.Split(string(data), "\n")
	if len(list) == 0 {
		return 1, nil
	}
	for i := 0; i < len(list); i++ {
		list[i] = strings.TrimSpace(list[i])
	}
	result := box.Choice(
		list,
		readline.Console)
	fmt.Fprintln(cmd.Stdout, result)
	return 0, nil
}
<commit_msg>Fix: `box` did not print linefeed on lastline after enter typed.<commit_after>package commands

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/zetamatta/go-box"

	"github.com/zetamatta/nyagos/readline"
	"github.com/zetamatta/nyagos/shell"
)

func cmd_box(ctx context.Context, cmd *shell.Cmd) (int, error) {
	data, err := ioutil.ReadAll(cmd.Stdin)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		return 1, err
	}
	list := strings.Split(string(data), "\n")
	if len(list) == 0 {
		return 1, nil
	}
	for i := 0; i < len(list); i++ {
		list[i] = strings.TrimSpace(list[i])
	}
	result := box.Choice(
		list,
		readline.Console)
	fmt.Fprintln(readline.Console)
	fmt.Fprintln(cmd.Stdout, result)
	return 0, nil
}
<|endoftext|>


<commit_before>package common

import (
	"net"
)

const (
	// Cilium constants

	// PluginPath is the docker plugins directory where docker plugin is present.
	PluginPath = "/run/docker/plugins/"
	// DriverSock is the cilium socket for the communication between docker and cilium.
	DriverSock = PluginPath + "cilium.sock"
	// CiliumPath is the path where cilium operational files are running.
	CiliumPath = "/var/run/cilium/"
	// CiliumSock is the cilium socket for the communication between the daemon and cilium client.
	CiliumSock = CiliumPath + "cilium.sock"
	// DefaultContainerMAC represents a dummy MAC address for the containers.
	DefaultContainerMAC = "AA:BB:CC:DD:EE:FF"
	// BPFMap is the file that contains the BPF Map for the host.
	BPFMap     = "/sys/fs/bpf/tc/globals/cilium_lxc"
	BPFMapRoot = "/sys/fs/bpf"
	// PolicyMapPath is the base path for the cilium policy for each local container.
	PolicyMapPath = "/sys/fs/bpf/tc/globals/cilium_policy_"
	// RFC3339Milli is the RFC3339 with milliseconds for the default timestamp format
	// log files.
	RFC3339Milli = "2006-01-02T15:04:05.000Z07:00"

	// Consul dedicated constants

	// OperationalPath is the base path to store the operational details in consul.
	OperationalPath = "cilium-net/operational"
	// LastFreeIDKeyPath is the path where the Last free UUID is stored in consul.
	LastFreeIDKeyPath = OperationalPath + "/LastUUID"
	// LabelsKeyPath is the base path where labels are stored in consul.
	LabelsKeyPath = OperationalPath + "/SHA256SUMLabels/"
	// IDKeyPath is the base path where the IDs are stored in consul.
	IDKeyPath = OperationalPath + "/ID/"
	// MaxSetOfLabels is maximum number of set of labels that can be stored in consul.
	MaxSetOfLabels = 0xFFFF
	// FirstFreeID is the first ID for which the labels should be assigned.
	FirstFreeID = 256
	// SecCtxFromHost represents reserved security context IDs reserved for special
	// purposes.
	SecCtxFromHost = 1

	// Networking dedicated constants

	// DefaultIPv6Prefix is the default IPv6 address assigned to the cilium interface.
	DefaultIPv6Prefix = "beef::"
	// DefaultIPv4Prefix is the IPv6 prefix used to map IPv4 addresses.
	DefaultIPv4Prefix = "dead::"
	// DefaultIPv4Range is the CIDR used for 6to4 communications.
	DefaultIPv4Range = `10.%d.0.0/16`
	// DefaultIPv4Mask is the default mask for the CIDR used for 6to4 communications.
	DefaultIPv4Mask = 16

	// Miscellaneous dedicated constants

	// GlobalLabelPrefix is the default root path for the policy.
	GlobalLabelPrefix = "io.cilium"
	// CiliumLabelSource is the default label source for the labels read from containers.
	CiliumLabelSource = "cilium"
	// K8sLabelSource is the default label source for the labels read from kubernetes.
	K8sLabelSource = "k8s"
	// Label source for reserved types
	ReservedLabelSource = "reserved"
	// EndpointsPerHost is the maximum number of endpoints allowed per host. It should
	// represent the same number of IPv6 addresses supported on each node.
	EndpointsPerHost = 0xFFFF

	// Endpoint prefixes

	// CiliumPrefix is used to distinguish cilium IDs between different ID types.
	CiliumPrefix = "cilium://"
	// DockerPrefix is used to distinguish docker IDs between different ID types.
	DockerPrefix = "docker://"
)

var (
	// Default addressing schema
	//
	// cluster:		    beef:beef:beef:beef::/64
	// loadbalancer:            beef:beef:beef:beef:<lb>::/80
	// node:		    beef:beef:beef:beef:<lb>:<node>:<node>:/112
	// lxc:			    beef:beef:beef:beef:<lb>:<node>:<node>:<lxc>/128

	// ClusterIPv6Mask represents the CIDR Mask for the cilium cluster.
	ClusterIPv6Mask = net.CIDRMask(64, 128)
	// LoadBalancerIPv6Mask represents the CIDR Mask for the cilium load balancer.
	LoadBalancerIPv6Mask = net.CIDRMask(80, 128)
	// NodeIPv6Mask represents the CIDR Mask for the cilium node.
	NodeIPv6Mask = net.CIDRMask(112, 128)
	// ContainerIPv6Mask represents the CIDR Mask for the cilium endpoint/container.
	ContainerIPv6Mask = net.CIDRMask(128, 128)
)
<commit_msg>Added specific BPF options in const.go<commit_after>package common

import (
	"net"
)

const (
	// Cilium constants

	// PluginPath is the docker plugins directory where docker plugin is present.
	PluginPath = "/run/docker/plugins/"
	// DriverSock is the cilium socket for the communication between docker and cilium.
	DriverSock = PluginPath + "cilium.sock"
	// CiliumPath is the path where cilium operational files are running.
	CiliumPath = "/var/run/cilium/"
	// CiliumSock is the cilium socket for the communication between the daemon and cilium client.
	CiliumSock = CiliumPath + "cilium.sock"
	// DefaultContainerMAC represents a dummy MAC address for the containers.
	DefaultContainerMAC = "AA:BB:CC:DD:EE:FF"
	// BPFMap is the file that contains the BPF Map for the host.
	BPFMap     = "/sys/fs/bpf/tc/globals/cilium_lxc"
	BPFMapRoot = "/sys/fs/bpf"
	// PolicyMapPath is the base path for the cilium policy for each local container.
	PolicyMapPath = "/sys/fs/bpf/tc/globals/cilium_policy_"
	// RFC3339Milli is the RFC3339 with milliseconds for the default timestamp format
	// log files.
	RFC3339Milli = "2006-01-02T15:04:05.000Z07:00"
	// DisablePolicyEnforcement represent the C constant to disable policy
	// enforcement.
	DisablePolicyEnforcement = "DISABLE_POLICY_ENFORCEMENT"
	// EnableNAT46 represent the C constant to enable nat46 mode.
	EnableNAT46 = "ENABLE_NAT46"

	// Consul dedicated constants

	// OperationalPath is the base path to store the operational details in consul.
	OperationalPath = "cilium-net/operational"
	// LastFreeIDKeyPath is the path where the Last free UUID is stored in consul.
	LastFreeIDKeyPath = OperationalPath + "/LastUUID"
	// LabelsKeyPath is the base path where labels are stored in consul.
	LabelsKeyPath = OperationalPath + "/SHA256SUMLabels/"
	// IDKeyPath is the base path where the IDs are stored in consul.
	IDKeyPath = OperationalPath + "/ID/"
	// MaxSetOfLabels is maximum number of set of labels that can be stored in consul.
	MaxSetOfLabels = 0xFFFF
	// FirstFreeID is the first ID for which the labels should be assigned.
	FirstFreeID = 256
	// SecCtxFromHost represents reserved security context IDs reserved for special
	// purposes.
	SecCtxFromHost = 1

	// Networking dedicated constants

	// DefaultIPv6Prefix is the default IPv6 address assigned to the cilium interface.
	DefaultIPv6Prefix = "beef::"
	// DefaultIPv4Prefix is the IPv6 prefix used to map IPv4 addresses.
	DefaultIPv4Prefix = "dead::"
	// DefaultIPv4Range is the CIDR used for 6to4 communications.
	DefaultIPv4Range = `10.%d.0.0/16`
	// DefaultIPv4Mask is the default mask for the CIDR used for 6to4 communications.
	DefaultIPv4Mask = 16

	// Miscellaneous dedicated constants

	// GlobalLabelPrefix is the default root path for the policy.
	GlobalLabelPrefix = "io.cilium"
	// CiliumLabelSource is the default label source for the labels read from containers.
	CiliumLabelSource = "cilium"
	// K8sLabelSource is the default label source for the labels read from kubernetes.
	K8sLabelSource = "k8s"
	// Label source for reserved types
	ReservedLabelSource = "reserved"
	// EndpointsPerHost is the maximum number of endpoints allowed per host. It should
	// represent the same number of IPv6 addresses supported on each node.
	EndpointsPerHost = 0xFFFF

	// Endpoint prefixes

	// CiliumPrefix is used to distinguish cilium IDs between different ID types.
	CiliumPrefix = "cilium://"
	// DockerPrefix is used to distinguish docker IDs between different ID types.
	DockerPrefix = "docker://"
)

var (
	// Default addressing schema
	//
	// cluster:		    beef:beef:beef:beef::/64
	// loadbalancer:            beef:beef:beef:beef:<lb>::/80
	// node:		    beef:beef:beef:beef:<lb>:<node>:<node>:/112
	// lxc:			    beef:beef:beef:beef:<lb>:<node>:<node>:<lxc>/128

	// ClusterIPv6Mask represents the CIDR Mask for the cilium cluster.
	ClusterIPv6Mask = net.CIDRMask(64, 128)
	// LoadBalancerIPv6Mask represents the CIDR Mask for the cilium load balancer.
	LoadBalancerIPv6Mask = net.CIDRMask(80, 128)
	// NodeIPv6Mask represents the CIDR Mask for the cilium node.
	NodeIPv6Mask = net.CIDRMask(112, 128)
	// ContainerIPv6Mask represents the CIDR Mask for the cilium endpoint/container.
	ContainerIPv6Mask = net.CIDRMask(128, 128)
)
<|endoftext|>


<commit_before>package common

import "reflect"

//PageParam 分页参数
type PageParam struct {
	//页数,从1开始
	Page int `json:"page"`
	//每页的条数,>0
	PageSize int `json:"page_size"`
}

//Limit 根据maxPage和maxPageSize限制Page和PageSize
func (p *PageParam) Limit(maxPage, maxPageSize int) {
	if p.Page <= 0 {
		p.Page = 1
	}
	if p.PageSize <= 0 {
		p.PageSize = 10
	}
	if maxPage > 0 && p.Page > maxPage {
		p.Page = maxPage
	}
	if maxPageSize > 0 && p.PageSize > maxPageSize {
		p.PageSize = maxPageSize
	}
}

//StartIndex 返回从0开始的起始索引
func (p *PageParam) StartIndex() int {
	return (p.Page - 1) * p.PageSize
}

//EndIndex 返回从0开始的截止索引
func (p *PageParam) EndIndex() int {
	return p.StartIndex() + p.PageSize - 1
}

// PageResultItemsSetter 结果设置
type PageResultItemsSetter func(sum int, index int, elem interface{})

// ResultSet is the result set with total and items
type ResultSet interface {
	SetTotal(total int64)
	SetData(data interface{}, itemsSetter PageResultItemsSetter)
	GetItemsSetter() PageResultItemsSetter
	CalTotalPage()
}

//PageResult 分页结果
type PageResult struct {
	PageParam
	Total     int64       `json:"total"`
	TotalPage int64       `json:"totalPage"`
	Items     interface{} `json:"items"`
}

// SetTotal implements ResultSet.SetTotal
func (p *PageResult) SetTotal(total int64) {
	p.Total = total
}

// SetData implements ResultSet.SetData
func (p *PageResult) SetData(data interface{}, itemsSetter PageResultItemsSetter) {
	p.Items = data

	if itemsSetter == nil {
		return
	}

	sliceVal, _, _ := ExtractRefTuple(data)
	if sliceVal.Kind() != reflect.Slice {
		return
	}

	itemsLen := sliceVal.Len()

	for i := 0; i < itemsLen; i++ {
		itemsSetter(itemsLen, i, sliceVal.Index(i).Interface())
	}
}

// CalTotalPage 计算总页数
func (p *PageResult) CalTotalPage() {
	if p.PageSize > 0 {
		if p.Total%int64(p.PageSize) == 0 {
			p.TotalPage = p.Total / int64(p.PageSize)
		} else {
			p.TotalPage = p.Total/int64(p.PageSize) + 1
		}
	}
}

//Query 基本的查询参数
type Query struct {
	PageParam
	//ID
	ID int64 `json:"id"`
}
<commit_msg>add cursor param<commit_after>package common

import "reflect"

//PageParam 分页参数
type PageParam struct {
	//页数,从1开始
	Page int `json:"page"`
	//每页的条数,>0
	PageSize int `json:"page_size"`
	//游标
	Cursor int64 `json:"cursor"`
}

//Limit 根据maxPage和maxPageSize限制Page和PageSize
func (p *PageParam) Limit(maxPage, maxPageSize int) {
	if p.Page <= 0 {
		p.Page = 1
	}
	if p.PageSize <= 0 {
		p.PageSize = 10
	}
	if maxPage > 0 && p.Page > maxPage {
		p.Page = maxPage
	}
	if maxPageSize > 0 && p.PageSize > maxPageSize {
		p.PageSize = maxPageSize
	}
}

//StartIndex 返回从0开始的起始索引
func (p *PageParam) StartIndex() int {
	return (p.Page - 1) * p.PageSize
}

//EndIndex 返回从0开始的截止索引
func (p *PageParam) EndIndex() int {
	return p.StartIndex() + p.PageSize - 1
}

// PageResultItemsSetter 结果设置
type PageResultItemsSetter func(sum int, index int, elem interface{})

// ResultSet is the result set with total and items
type ResultSet interface {
	SetTotal(total int64)
	SetData(data interface{}, itemsSetter PageResultItemsSetter)
	GetItemsSetter() PageResultItemsSetter
	CalTotalPage()
}

//PageResult 分页结果
type PageResult struct {
	PageParam
	Total     int64       `json:"total"`
	TotalPage int64       `json:"totalPage"`
	Items     interface{} `json:"items"`
}

// SetTotal implements ResultSet.SetTotal
func (p *PageResult) SetTotal(total int64) {
	p.Total = total
}

// SetData implements ResultSet.SetData
func (p *PageResult) SetData(data interface{}, itemsSetter PageResultItemsSetter) {
	p.Items = data

	if itemsSetter == nil {
		return
	}

	sliceVal, _, _ := ExtractRefTuple(data)
	if sliceVal.Kind() != reflect.Slice {
		return
	}

	itemsLen := sliceVal.Len()

	for i := 0; i < itemsLen; i++ {
		itemsSetter(itemsLen, i, sliceVal.Index(i).Interface())
	}
}

// CalTotalPage 计算总页数
func (p *PageResult) CalTotalPage() {
	if p.PageSize > 0 {
		if p.Total%int64(p.PageSize) == 0 {
			p.TotalPage = p.Total / int64(p.PageSize)
		} else {
			p.TotalPage = p.Total/int64(p.PageSize) + 1
		}
	}
}

//Query 基本的查询参数
type Query struct {
	PageParam
	//ID
	ID int64 `json:"id"`
}
<|endoftext|>


<commit_before>package common

import (
	"net/http"
	"net/url"
	"strings"

	"github.com/axgle/mahonia"
	"github.com/saintfish/chardet"
)

// RequestURL return the search result
func RequestURL(url string) (*http.Response, error) {
	client := &http.Client{}
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("User-Agent", GetUserAgent())
	response, err := client.Do(req)
	return response, err
}

// QuickestURL return the quickest response
func QuickestURL(index int, url string) int {
	// start := time.Now()
	_, err := http.Get(url)
	if err != nil {
		return -1
	}
	// timeUsed := strconv.FormatFloat(time.Since(start).Seconds(), 'f', 6, 64)
	// fmt.Println(url, timeUsed)
	return index
}

// DetectBody gbk convert to utf-8
func DetectBody(body []byte) string {
	var bodyString string
	detector := chardet.NewTextDetector()
	result, err := detector.DetectBest(body)
	if err != nil {
		return string(body)
	}
	if strings.Contains(strings.ToLower(result.Charset), "utf") {
		bodyString = string(body)
	} else {
		bodyString = mahonia.NewDecoder("gbk").ConvertString(string(body))
	}
	return bodyString
}

// StringInSlice search for an element in a golang slice
func StringInSlice(domain string, list []string) bool {
	for _, eachDomain := range list {
		if domain == eachDomain {
			return true
		}
	}
	return false
}

//ReturnDomain parse url and return the domain
func ReturnDomain(currentURL string) string {
	urlParse, _ := url.Parse(currentURL)
	domain := urlParse.Host
	return domain
}
<commit_msg>一个空格引起的更新<commit_after>package common

import (
	"net/http"
	"net/url"
	"strings"

	"github.com/axgle/mahonia"
	"github.com/saintfish/chardet"
)

// RequestURL return the search result
func RequestURL(url string) (*http.Response, error) {
	client := &http.Client{}
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("User-Agent", GetUserAgent())
	response, err := client.Do(req)
	return response, err
}

// QuickestURL return the quickest response
func QuickestURL(index int, url string) int {
	// start := time.Now()
	_, err := http.Get(url)
	if err != nil {
		return -1
	}
	// timeUsed := strconv.FormatFloat(time.Since(start).Seconds(), 'f', 6, 64)
	// fmt.Println(url, timeUsed)
	return index
}

// DetectBody gbk convert to utf-8
func DetectBody(body []byte) string {
	var bodyString string
	detector := chardet.NewTextDetector()
	result, err := detector.DetectBest(body)
	if err != nil {
		return string(body)
	}
	if strings.Contains(strings.ToLower(result.Charset), "utf") {
		bodyString = string(body)
	} else {
		bodyString = mahonia.NewDecoder("gbk").ConvertString(string(body))
	}
	return bodyString
}

// StringInSlice search for an element in a golang slice
func StringInSlice(domain string, list []string) bool {
	for _, eachDomain := range list {
		if domain == eachDomain {
			return true
		}
	}
	return false
}

// ReturnDomain parse url and return the domain
func ReturnDomain(currentURL string) string {
	urlParse, _ := url.Parse(currentURL)
	domain := urlParse.Host
	return domain
}
<|endoftext|>


<commit_before>package utee

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/x509"
	"encoding/hex"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/smtp"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"
)

var (
	PlainMd5  = Md5Str("")
	PlainSha1 = Sha1Str("")
)

func init() {
	rand.Seed(time.Now().UTC().UnixNano())
}

func Md5Str(salt string) func(string) string {
	return func(s string) string {
		h := md5.New()
		io.WriteString(h, s)
		io.WriteString(h, salt)
		return hex.EncodeToString(h.Sum(nil))
	}
}

func Sha1Str(salt string) func(string) string {
	return func(s string) string {
		h := sha1.New()
		io.WriteString(h, s)
		io.WriteString(h, salt)
		return hex.EncodeToString(h.Sum(nil))
	}
}

func Sha256Str(salt string) func(string) string {
	return func(s string) string {
		h := sha256.New()
		io.WriteString(h, s)
		io.WriteString(h, salt)
		return hex.EncodeToString(h.Sum(nil))
	}
}

func HmacSha256(s string, key string) string {
	h := hmac.New(sha256.New, []byte(key))
	io.WriteString(h, s)
	return hex.EncodeToString(h.Sum(nil))
}

func Chk(err error) {
	if err != nil {
		panic(err)
	}
}

func Log(err error, prefix ...string) {
	if err == nil {
		return
	}

	s := ""
	if len(prefix) > 0 {
		s = prefix[0]
	}
	log.Println(s, err)
}

// Truncate truncate string
func Truncate(s string, n int) string {
	if n <= 0 {
		return ""
	}

	length := utf8.RuneCountInString(s)
	if length <= n {
		return s
	}

	l := []rune{}
	for _, r := range s {
		l = append(l, r)
	}

	l = l[:(length - n)]
	return string(l)
}

func Tick(t ...time.Time) int64 {
	if len(t) == 0 {
		return time.Now().UnixNano() / 1e6
	} else {
		return t[0].UnixNano() / 1e6
	}
}

func TickSec() int64 {
	return time.Now().Unix()
}

func TickHour() int64 {
	return time.Now().Unix() / 3600 * 3600
}

func Millis(fmt string, timeStr string) (int64, error) {
	data, err := time.Parse(fmt, timeStr)
	if err != nil {
		return 0, err
	}
	return data.UnixNano() / 1e6, nil
}

func Md5(b []byte) []byte {
	h := md5.New()
	h.Write(b)
	return h.Sum(nil)
}

func DeleteMap(m map[string]interface{}, ks ...string) {
	for _, v := range ks {
		delete(m, v)
	}
}

func IsPemExpire(b []byte) (bool, error) {
	block, _ := pem.Decode(b)
	if block == nil {
		return false, errors.New("failed to parse certificate PEM")
	}
	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return false, err
	}
	return cert.NotAfter.Before(time.Now()), nil
}

func Shuffle(src []string) []string {
	dest := make([]string, len(src))
	perm := rand.Perm(len(src))
	for i, v := range perm {
		dest[v] = src[i]
	}
	return dest
}

func SendMail(user, password, host, to, subject, body, mailtype string) error {
	hp := strings.Split(host, ":")
	auth := smtp.PlainAuth("", user, password, hp[0])
	var content_type string
	if mailtype == "html" {
		content_type = "Content-Type: text/" + mailtype + "; charset=UTF-8"
	} else {
		content_type = "Content-Type: text/plain" + "; charset=UTF-8"
	}

	msg := []byte("To: " + to + "\r\nFrom: " + user + "<" + user + ">\r\nSubject: " + subject + "\r\n" + content_type + "\r\n\r\n" + body)
	send_to := strings.Split(to, ";")
	err := smtp.SendMail(host, auth, user, send_to, msg)
	return err
}

func ParseAddr(s string) (string, int, error) {
	a := strings.Split(s, ":")
	if len(a) != 2 {
		return "", 0, fmt.Errorf("bad url %s", s)
	}
	port, err := strconv.Atoi(a[1])
	return a[0], port, err
}

// split a into several parts, no more than n
func SplitSlice(a []string, n int) [][]string {
	if len(a) < n || n == 1 {
		return [][]string{a}
	}

	result := make([][]string, n)
	for i, s := range a {
		idx := i % n
		result[idx] = append(result[idx], s)
	}
	return result
}

func IntToInf(src []int) []interface{} {
	result := []interface{}{}
	for _, v := range src {
		result = append(result, v)
	}
	return result
}

func StrToInf(src []string) []interface{} {
	result := []interface{}{}
	for _, v := range src {
		result = append(result, v)
	}
	return result
}

func PrintJson(any ...interface{}) {
	for _, obj := range any {
		b, err := json.Marshal(obj)
		fmt.Println(err, string(b))
	}
}
<commit_msg>v1.1.1, revert uniq function<commit_after>package utee

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/x509"
	"encoding/hex"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/smtp"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"
)

var (
	PlainMd5  = Md5Str("")
	PlainSha1 = Sha1Str("")
)

func init() {
	rand.Seed(time.Now().UTC().UnixNano())
}

func Md5Str(salt string) func(string) string {
	return func(s string) string {
		h := md5.New()
		io.WriteString(h, s)
		io.WriteString(h, salt)
		return hex.EncodeToString(h.Sum(nil))
	}
}

func Sha1Str(salt string) func(string) string {
	return func(s string) string {
		h := sha1.New()
		io.WriteString(h, s)
		io.WriteString(h, salt)
		return hex.EncodeToString(h.Sum(nil))
	}
}

func Sha256Str(salt string) func(string) string {
	return func(s string) string {
		h := sha256.New()
		io.WriteString(h, s)
		io.WriteString(h, salt)
		return hex.EncodeToString(h.Sum(nil))
	}
}

func HmacSha256(s string, key string) string {
	h := hmac.New(sha256.New, []byte(key))
	io.WriteString(h, s)
	return hex.EncodeToString(h.Sum(nil))
}

func Chk(err error) {
	if err != nil {
		panic(err)
	}
}

func Log(err error, prefix ...string) {
	if err == nil {
		return
	}

	s := ""
	if len(prefix) > 0 {
		s = prefix[0]
	}
	log.Println(s, err)
}

// Truncate truncate string
func Truncate(s string, n int) string {
	if n <= 0 {
		return ""
	}

	length := utf8.RuneCountInString(s)
	if length <= n {
		return s
	}

	l := []rune{}
	for _, r := range s {
		l = append(l, r)
	}

	l = l[:(length - n)]
	return string(l)
}

func Tick(t ...time.Time) int64 {
	if len(t) == 0 {
		return time.Now().UnixNano() / 1e6
	} else {
		return t[0].UnixNano() / 1e6
	}
}

func TickSec() int64 {
	return time.Now().Unix()
}

func TickHour() int64 {
	return time.Now().Unix() / 3600 * 3600
}

func Millis(fmt string, timeStr string) (int64, error) {
	data, err := time.Parse(fmt, timeStr)
	if err != nil {
		return 0, err
	}
	return data.UnixNano() / 1e6, nil
}

func Md5(b []byte) []byte {
	h := md5.New()
	h.Write(b)
	return h.Sum(nil)
}

func DeleteMap(m map[string]interface{}, ks ...string) {
	for _, v := range ks {
		delete(m, v)
	}
}

func IsPemExpire(b []byte) (bool, error) {
	block, _ := pem.Decode(b)
	if block == nil {
		return false, errors.New("failed to parse certificate PEM")
	}
	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return false, err
	}
	return cert.NotAfter.Before(time.Now()), nil
}

func Shuffle(src []string) []string {
	dest := make([]string, len(src))
	perm := rand.Perm(len(src))
	for i, v := range perm {
		dest[v] = src[i]
	}
	return dest
}

func SendMail(user, password, host, to, subject, body, mailtype string) error {
	hp := strings.Split(host, ":")
	auth := smtp.PlainAuth("", user, password, hp[0])
	var content_type string
	if mailtype == "html" {
		content_type = "Content-Type: text/" + mailtype + "; charset=UTF-8"
	} else {
		content_type = "Content-Type: text/plain" + "; charset=UTF-8"
	}

	msg := []byte("To: " + to + "\r\nFrom: " + user + "<" + user + ">\r\nSubject: " + subject + "\r\n" + content_type + "\r\n\r\n" + body)
	send_to := strings.Split(to, ";")
	err := smtp.SendMail(host, auth, user, send_to, msg)
	return err
}

func ParseAddr(s string) (string, int, error) {
	a := strings.Split(s, ":")
	if len(a) != 2 {
		return "", 0, fmt.Errorf("bad url %s", s)
	}
	port, err := strconv.Atoi(a[1])
	return a[0], port, err
}

func Unique(data []interface{}) []interface{} {
	m := map[interface{}]interface{}{}

	for _, d := range data {
		m[d] = "0"
	}

	l := []interface{}{}
	for key := range m {
		l = append(l, key)
	}
	return l
}

func UniqueInt(data []int) []int {
	m := map[int]string{}

	for _, d := range data {
		m[d] = "0"
	}

	l := []int{}
	for key := range m {
		l = append(l, key)
	}
	return l
}

func UniqueStr(data []string) []string {
	m := map[string]string{}

	for _, d := range data {
		m[d] = "0"
	}

	l := []string{}
	for key := range m {
		l = append(l, key)
	}
	return l
}

// split a into several parts, no more than n
func SplitSlice(a []string, n int) [][]string {
	if len(a) < n || n == 1 {
		return [][]string{a}
	}

	result := make([][]string, n)
	for i, s := range a {
		idx := i % n
		result[idx] = append(result[idx], s)
	}
	return result
}

func IntToInf(src []int) []interface{} {
	result := []interface{}{}
	for _, v := range src {
		result = append(result, v)
	}
	return result
}

func StrToInf(src []string) []interface{} {
	result := []interface{}{}
	for _, v := range src {
		result = append(result, v)
	}
	return result
}

func PrintJson(any ...interface{}) {
	for _, obj := range any {
		b, err := json.Marshal(obj)
		fmt.Println(err, string(b))
	}
}
<|endoftext|>


<commit_before>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package main

import (
	"fmt"
	"log"
	"regexp"
	"strconv"
	"time"
)

type opcode int

const (
	match     opcode = iota // Match a regular expression against input, and set the match register.
	cmp                     // Compare two values on the stack and set the match register.
	jnm                     // Jump if no match.
	jm                      // Jump if match.
	inc                     // Increment a variable value
	strptime                // Parse into the timestamp register
	timestamp               // Return value of timestamp register
	ret                     // Return, end program successfully
	push                    // Push operand onto stack
	capref                  // Push capture group reference at operand onto stack
	str                     // Push string constant at operand onto stack
	set                     // Set a variable value
	add                     // Add top values on stack and push to stack
	sub                     // Subtract tpo value from second top value on stack, and push to stack.
	mload                   // Load metric at operand onto top of stack.
	dload                   // Pop operand keys and metric off stack and load datum at metric[key] onto stack.
)

var opNames = map[opcode]string{
	match:     "match",
	cmp:       "cmp",
	jnm:       "jnm",
	jm:        "jm",
	inc:       "inc",
	strptime:  "strptime",
	timestamp: "timestamp",
	ret:       "ret",
	push:      "push",
	capref:    "capref",
	str:       "str",
	set:       "set",
	add:       "add",
	sub:       "sub",
	mload:     "mload",
	dload:     "dload",
}

var builtin = map[string]opcode{
	"strptime":  strptime,
	"timestamp": timestamp,
}

type instr struct {
	op   opcode
	opnd int
}

// func (i instr) String() string {
// 	return fmt.Sprintf("%s %d", opNames[i.op], i.opnd)
// }

type thread struct {
	pc      int              // Program counter.
	match   bool             // Match register.
	matches map[int][]string // Match result variables.
	time    time.Time        // Time register.
	stack   []interface{}    // Data stack.
}

type vm struct {
	name string
	prog []instr

	re  []*regexp.Regexp // Regular expression constants
	str []string         // String constants

	ts_mem map[string]time.Time // memo of time string parse results

	t thread // Current thread of execution
}

// Push a value onto the stack
func (t *thread) Push(value interface{}) {
	t.stack = append(t.stack, value)
}

// Pop a value off the stack
func (t *thread) Pop() (value interface{}) {
	last := len(t.stack) - 1
	value = t.stack[last]
	t.stack = t.stack[:last]
	return
}

// Log a runtime error and terminate the program
func (v *vm) errorf(format string, args ...interface{}) bool {
	log.Printf("Runtime error: "+format+"\n", args...)
	v.t.match = false
	return true
}

func (t *thread) PopInt() (int64, error) {
	val := t.Pop()
	switch n := val.(type) {
	case int64:
		return n, nil
	case int:
		return int64(n), nil
	case string:
		r, err := strconv.ParseInt(n, 10, 64)
		if err != nil {
			return 0, fmt.Errorf("conversion of %q to numeric failed: %s", val, err)
		}
		return r, nil
	case time.Time:
		return n.Unix(), nil
	case *Datum:
		return n.Value, nil
	}
	return 0, fmt.Errorf("Unexpected numeric type %T %q", val, val)
}

// Execute acts on the current instruction, and returns a boolean indicating
// if the current thread should terminate.
func (v *vm) execute(t *thread, i instr, input string) bool {
	switch i.op {
	case match:
		// match regex and store success
		// Store the results in the operandth element of the stack,
		// where i.opnd == the matched re index
		t.matches[i.opnd] = v.re[i.opnd].FindStringSubmatch(input)
		t.match = t.matches[i.opnd] != nil
	case cmp:
		// Compare two elements on the stack.
		// Set the match register based on the truthiness of the comparison.
		// Operand contains the expected result.
		b, err := t.PopInt()
		if err != nil {
			return v.errorf("%s", err)
		}
		a, err := t.PopInt()
		if err != nil {
			return v.errorf("%s", err)
		}

		switch i.opnd {
		case -1:
			t.match = a < b
		case 0:
			t.match = a == b
		case 1:
			t.match = a > b
		}
	case jnm:
		if !t.match {
			t.pc = i.opnd
			// Don't fall to end of loop or pc gets incremented.
			return false
		}
	case jm:
		if t.match {
			t.pc = i.opnd
			// Don't fall to end of loop or pc gets incremented.
			return false
		}
	case inc:
		// increment a counter
		var delta int64 = 1
		// If opnd is nonzero, the delta is on the stack.
		if i.opnd > 0 {
			var err error
			delta, err = t.PopInt()
			if err != nil {
				return v.errorf("%s", err)
			}
		}
		switch d := t.Pop().(type) {
		case Incrementable:
			d.IncBy(delta, t.time)
		case int:
			m := metrics[v.name][d]
			m.IncBy(delta, t.time)
		default:
			return v.errorf("Unexpected type to increment: %T %q", d, d)
		}

	case set:
		// Set a gauge
		value, err := t.PopInt()
		if err != nil {
			return v.errorf("%s", err)
		}

		switch d := t.Pop().(type) {
		case Settable:
			d.Set(value, t.time)
		case int:
			m := metrics[v.name][d]
			m.Set(value, t.time)
		default:
			return v.errorf("Unexpected type to set: %T %q", d, d)
		}

	case strptime:
		// Parse a time string into the time register
		layout := t.Pop().(string)

		var ts string
		switch s := t.Pop().(type) {
		case string:
			ts = s

		case int: /* capref */
			// First find the match storage index on the stack
			re := t.Pop().(int)
			// Store the result from the re'th index at the s'th index
			ts = t.matches[re][s]
		}
		if tm, ok := v.ts_mem[ts]; !ok {
			tm, err := time.Parse(layout, ts)
			if err != nil {
				return v.errorf("time.Parse(%s, %s) failed: %s", layout, ts, err)
			}
			v.ts_mem[ts] = tm
			t.time = tm
		} else {
			t.time = tm
		}

	case timestamp:
		// Put the time register onto the stack
		t.Push(t.time)

	case capref:
		// Put a capture group reference onto the stack.
		// First find the match storage index on the stack,
		re := t.Pop().(int)
		// Push the result from the re'th match at operandth index
		t.Push(t.matches[re][i.opnd])

	case str:
		// Put a string constant onto the stack
		t.Push(v.str[i.opnd])

	case ret:
		// Exit the virtual machine.
		t.match = true
		return true

	case push:
		// Push a value onto the stack
		t.Push(i.opnd)

	case add:
		// Add two values at TOS, and push result onto stack
		b, err := t.PopInt()
		if err != nil {
			v.errorf("%s", err)
		}
		a, err := t.PopInt()
		if err != nil {
			v.errorf("%s", err)
		}
		t.Push(a + b)

	case sub:
		// Subtract two values at TOS, push result onto stack
		b, err := t.PopInt()
		if err != nil {
			v.errorf("%s", err)
		}
		a, err := t.PopInt()
		if err != nil {
			v.errorf("%s", err)
		}
		t.Push(a - b)

	case mload:
		// Load a metric at operand onto stack
		t.Push(metrics[v.name][i.opnd])

	case dload:
		// Load a datum from metric at TOS onto stack
		m := t.Pop().(*Metric)
		var keys []string
		for a := 0; a < i.opnd; a++ {
			keys = append(keys, t.Pop().(string))
		}
		h := key_hash(keys)
		if _, ok := m.Values[h]; !ok {
			m.Values[h] = &Datum{}
		}
		t.Push(m.Values[h])

	default:
		return v.errorf("illegal instruction: %q", i.op)
	}
	t.pc++
	return false
}

// Run fetches and executes each instruction in the program on the input string
// until termination. It returns a boolean indicating a successful action was taken.
func (v *vm) Run(input string) bool {
	t := v.t
	t.stack = make([]interface{}, 0)
	t.matches = make(map[int][]string, 0)
	for {
		if t.pc >= len(v.prog) {
			return t.match
		}
		i := v.prog[t.pc]
		terminate := v.execute(&t, i, input)
		if terminate {
			// t.match indicates that an action was taken after a match.
			return t.match
		}
	}
	panic("not reached")
}

func newVm(name string, re []*regexp.Regexp, str []string, prog []instr) *vm {
	return &vm{
		name:   name,
		re:     re,
		str:    str,
		prog:   prog,
		ts_mem: make(map[string]time.Time, 0),
	}
}
<commit_msg>Add comment and remove return code of vm execution.<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package main

import (
	"fmt"
	"log"
	"regexp"
	"strconv"
	"time"
)

type opcode int

const (
	match     opcode = iota // Match a regular expression against input, and set the match register.
	cmp                     // Compare two values on the stack and set the match register.
	jnm                     // Jump if no match.
	jm                      // Jump if match.
	inc                     // Increment a variable value
	strptime                // Parse into the timestamp register
	timestamp               // Return value of timestamp register
	ret                     // Return, end program successfully
	push                    // Push operand onto stack
	capref                  // Push capture group reference at operand onto stack
	str                     // Push string constant at operand onto stack
	set                     // Set a variable value
	add                     // Add top values on stack and push to stack
	sub                     // Subtract tpo value from second top value on stack, and push to stack.
	mload                   // Load metric at operand onto top of stack.
	dload                   // Pop operand keys and metric off stack and load datum at metric[key] onto stack.
)

var opNames = map[opcode]string{
	match:     "match",
	cmp:       "cmp",
	jnm:       "jnm",
	jm:        "jm",
	inc:       "inc",
	strptime:  "strptime",
	timestamp: "timestamp",
	ret:       "ret",
	push:      "push",
	capref:    "capref",
	str:       "str",
	set:       "set",
	add:       "add",
	sub:       "sub",
	mload:     "mload",
	dload:     "dload",
}

var builtin = map[string]opcode{
	"strptime":  strptime,
	"timestamp": timestamp,
}

type instr struct {
	op   opcode
	opnd int
}

// func (i instr) String() string {
// 	return fmt.Sprintf("%s %d", opNames[i.op], i.opnd)
// }

type thread struct {
	pc      int              // Program counter.
	match   bool             // Match register.
	matches map[int][]string // Match result variables.
	time    time.Time        // Time register.
	stack   []interface{}    // Data stack.
}

type vm struct {
	name string
	prog []instr

	re  []*regexp.Regexp // Regular expression constants
	str []string         // String constants

	ts_mem map[string]time.Time // memo of time string parse results

	t thread // Current thread of execution
}

// Push a value onto the stack
func (t *thread) Push(value interface{}) {
	t.stack = append(t.stack, value)
}

// Pop a value off the stack
func (t *thread) Pop() (value interface{}) {
	last := len(t.stack) - 1
	value = t.stack[last]
	t.stack = t.stack[:last]
	return
}

// Log a runtime error and terminate the program
func (v *vm) errorf(format string, args ...interface{}) bool {
	log.Printf("Runtime error: "+format+"\n", args...)
	v.t.match = false
	return true
}

func (t *thread) PopInt() (int64, error) {
	val := t.Pop()
	switch n := val.(type) {
	case int64:
		return n, nil
	case int:
		return int64(n), nil
	case string:
		r, err := strconv.ParseInt(n, 10, 64)
		if err != nil {
			return 0, fmt.Errorf("conversion of %q to numeric failed: %s", val, err)
		}
		return r, nil
	case time.Time:
		return n.Unix(), nil
	case *Datum:
		return n.Value, nil
	}
	return 0, fmt.Errorf("Unexpected numeric type %T %q", val, val)
}

// Execute acts on the current instruction, and returns a boolean indicating
// if the current thread should terminate.
func (v *vm) execute(t *thread, i instr, input string) bool {
	switch i.op {
	case match:
		// match regex and store success
		// Store the results in the operandth element of the stack,
		// where i.opnd == the matched re index
		t.matches[i.opnd] = v.re[i.opnd].FindStringSubmatch(input)
		t.match = t.matches[i.opnd] != nil
	case cmp:
		// Compare two elements on the stack.
		// Set the match register based on the truthiness of the comparison.
		// Operand contains the expected result.
		b, err := t.PopInt()
		if err != nil {
			return v.errorf("%s", err)
		}
		a, err := t.PopInt()
		if err != nil {
			return v.errorf("%s", err)
		}

		switch i.opnd {
		case -1:
			t.match = a < b
		case 0:
			t.match = a == b
		case 1:
			t.match = a > b
		}
	case jnm:
		if !t.match {
			t.pc = i.opnd
			// Don't fall to end of loop or pc gets incremented.
			return false
		}
	case jm:
		if t.match {
			t.pc = i.opnd
			// Don't fall to end of loop or pc gets incremented.
			return false
		}
	case inc:
		// increment a counter
		var delta int64 = 1
		// If opnd is nonzero, the delta is on the stack.
		if i.opnd > 0 {
			var err error
			delta, err = t.PopInt()
			if err != nil {
				return v.errorf("%s", err)
			}
		}
		switch d := t.Pop().(type) {
		case Incrementable:
			d.IncBy(delta, t.time)
		case int:
			m := metrics[v.name][d]
			m.IncBy(delta, t.time)
		default:
			return v.errorf("Unexpected type to increment: %T %q", d, d)
		}

	case set:
		// Set a gauge
		value, err := t.PopInt()
		if err != nil {
			return v.errorf("%s", err)
		}

		switch d := t.Pop().(type) {
		case Settable:
			d.Set(value, t.time)
		case int:
			m := metrics[v.name][d]
			m.Set(value, t.time)
		default:
			return v.errorf("Unexpected type to set: %T %q", d, d)
		}

	case strptime:
		// Parse a time string into the time register
		layout := t.Pop().(string)

		var ts string
		switch s := t.Pop().(type) {
		case string:
			ts = s

		case int: /* capref */
			// First find the match storage index on the stack
			re := t.Pop().(int)
			// Store the result from the re'th index at the s'th index
			ts = t.matches[re][s]
		}
		if tm, ok := v.ts_mem[ts]; !ok {
			tm, err := time.Parse(layout, ts)
			if err != nil {
				return v.errorf("time.Parse(%s, %s) failed: %s", layout, ts, err)
			}
			v.ts_mem[ts] = tm
			t.time = tm
		} else {
			t.time = tm
		}

	case timestamp:
		// Put the time register onto the stack
		t.Push(t.time)

	case capref:
		// Put a capture group reference onto the stack.
		// First find the match storage index on the stack,
		re := t.Pop().(int)
		// Push the result from the re'th match at operandth index
		t.Push(t.matches[re][i.opnd])

	case str:
		// Put a string constant onto the stack
		t.Push(v.str[i.opnd])

	case ret:
		// Exit the virtual machine.
		t.match = true
		return true

	case push:
		// Push a value onto the stack
		t.Push(i.opnd)

	case add:
		// Add two values at TOS, and push result onto stack
		b, err := t.PopInt()
		if err != nil {
			v.errorf("%s", err)
		}
		a, err := t.PopInt()
		if err != nil {
			v.errorf("%s", err)
		}
		t.Push(a + b)

	case sub:
		// Subtract two values at TOS, push result onto stack
		b, err := t.PopInt()
		if err != nil {
			v.errorf("%s", err)
		}
		a, err := t.PopInt()
		if err != nil {
			v.errorf("%s", err)
		}
		t.Push(a - b)

	case mload:
		// Load a metric at operand onto stack
		t.Push(metrics[v.name][i.opnd])

	case dload:
		// Load a datum from metric at TOS onto stack
		m := t.Pop().(*Metric)
		var keys []string
		for a := 0; a < i.opnd; a++ {
			keys = append(keys, t.Pop().(string))
		}
		h := key_hash(keys)
		if _, ok := m.Values[h]; !ok {
			m.Values[h] = &Datum{}
		}
		t.Push(m.Values[h])

	default:
		return v.errorf("illegal instruction: %q", i.op)
	}
	t.pc++
	return false
}

// Run fetches and executes each instruction in the program on the input string
// until termination. It returns a boolean indicating a successful action was taken.
func (v *vm) Run(input string) {
	t := v.t
	t.stack = make([]interface{}, 0)
	t.matches = make(map[int][]string, 0)
	for {
		if t.pc >= len(v.prog) {
			return
		}
		i := v.prog[t.pc]
		terminate := v.execute(&t, i, input)
		if terminate {
			return
		}
	}
	panic("not reached")
}

func newVm(name string, re []*regexp.Regexp, str []string, prog []instr) *vm {
	return &vm{
		name:   name,
		re:     re,
		str:    str,
		prog:   prog,
		ts_mem: make(map[string]time.Time, 0),
	}
}
<|endoftext|>


<commit_before>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package main

import (
	"fmt"
	"log"
	"regexp"
	"strconv"
	"time"
)

type opcode int

const (
	match     opcode = iota // Match a regular expression against input
	jnm                     // Jump if no match
	inc                     // Increment a variable value
	strptime                // Parse into the timestamp register
	timestamp               // Return value of timestamp register
	ret                     // Return, end program successfully
	push                    // Push operand onto stack
	capref                  // Push capture group reference at operand onto stack
	str                     // Push string constant at operand onto stack
	set                     // Set a variable value
	add                     // Add top values on stack and push to stack
	sub                     // Subtract tpo value from second top value on stack, and push to stack.
	mload                   // Load metric at operand onto top of stack.
	dload                   // Pop operand keys and metric off stack and load datum at metric[key] onto stack.
)

var opNames = map[opcode]string{
	match:     "match",
	jnm:       "jnm",
	inc:       "inc",
	strptime:  "strptime",
	timestamp: "timestamp",
	ret:       "ret",
	push:      "push",
	capref:    "capref",
	str:       "str",
	set:       "set",
	add:       "add",
	sub:       "sub",
	mload:     "mload",
	dload:     "dload",
}

var builtin = map[string]opcode{
	"strptime":  strptime,
	"timestamp": timestamp,
}

type instr struct {
	op   opcode
	opnd int
}

func (i instr) String() string {
	return fmt.Sprintf("%s %d", opNames[i.op], i.opnd)
}

type thread struct {
	pc      int
	reg     int
	matches map[int][]string
	time    time.Time
	stack   []interface{}
}

type vm struct {
	name string
	prog []instr

	// const regexps
	re []*regexp.Regexp
	// const strings
	str []string

	// data segment
	symtab *scope

	t thread
}

func (t *thread) Push(value interface{}) {
	t.stack = append(t.stack, value)
}

func (t *thread) Pop() (value interface{}) {
	last := len(t.stack) - 1
	value = t.stack[last]
	t.stack = t.stack[:last]
	return
}

func (v *vm) errorf(format string, args ...interface{}) bool {
	log.Printf("Runtime error: "+format+"\n", args...)
	v.t.reg = 0
	return true
}

// Execute acts on the current instruction, and returns a boolean indicating
// if the current thread should terminate.
func (v *vm) execute(t *thread, i instr, input string) bool {
	switch i.op {
	case match:
		// match regex and store success
		t.matches[i.opnd] = v.re[i.opnd].FindStringSubmatch(input)
		if t.matches[i.opnd] != nil {
			t.reg = 1
		} else {
			t.reg = 0
		}
	case jnm:
		if t.reg == 0 {
			t.pc = i.opnd
			return false
		}
	case inc:
		// increment a counter
		var delta int64 = 1
		// If opnd is nonzero, the delta is on the stack.
		if i.opnd > 0 {
			val := t.Pop()
			// Don't know what type it is on the stack though.
			switch n := val.(type) {
			case int:
				delta = int64(n)
			case int64:
				delta = n
			case string:
				var err error
				delta, err = strconv.ParseInt(n, 10, 64)
				if err != nil {
					return v.errorf("conversion of %q to numeric failed: %s", val, err)
				}
			default:
				return v.errorf("Unexpected type %T %q", val, val)
			}
		}
		switch d := t.Pop().(type) {
		case Incrementable:
			d.IncBy(delta, t.time)
		case int:
			m := metrics[d]
			m.IncBy(delta, t.time)
		default:
			return v.errorf("Unexpected type to increment: %T %q", d, d)
		}

	case set:
		// Set a gauge
		var value int64
		val := t.Pop()
		// Don't know what type it is on the stack though.
		switch n := val.(type) {
		case int:
			value = int64(n)
		case int64:
			value = n
		case string:
			var err error
			value, err = strconv.ParseInt(n, 10, 64)
			if err != nil {
				return v.errorf("conversion of %q to numeric failed: %s", val, err)
			}
		case time.Time:
			value = n.Unix()
		default:
			return v.errorf("Unexpected type %T %q\n", val, val)
		}
		switch d := t.Pop().(type) {
		case Settable:
			d.Set(value, t.time)
		case int:
			m := metrics[d]
			m.Set(value, t.time)
		default:
			return v.errorf("Unexpected type to set: %T %q", d, d)
		}

	case strptime:
		layout := t.Pop().(string)

		var ts string
		switch s := t.Pop().(type) {
		case string:
			ts = s

		case int: /* capref */
			re := t.Pop().(int)
			ts = t.matches[re][s]
		}

		tm, err := time.Parse(layout, ts)
		if err != nil {
			return v.errorf("time.Parse(%s, %s) failed: %s", layout, ts, err)
		}
		t.time = tm

	case timestamp:
		t.Push(t.time)

	case capref:
		// Get the regex number from the stack
		re := t.Pop().(int)
		t.Push(t.matches[re][i.opnd])

	case str:
		t.Push(v.str[i.opnd])

	case ret:
		return true

	case push:
		t.Push(i.opnd)

	case add:
		var a, b int64
		switch d := t.Pop().(type) {
		case *Datum:
			a = d.Value
		case int64:
			a = d
		case int:
			a = int64(d)
		case time.Time:
			a = d.Unix()
		default:
			return v.errorf("Unexpected type for add %T %q\n", d, d)
		}
		switch d := t.Pop().(type) {
		case *Datum:
			b = d.Value
		case int64:
			b = d
		case int:
			b = int64(d)
		case time.Time:
			b = d.Unix()
		default:
			return v.errorf("Unexpected type for add %T %q\n", d, d)
		}
		t.Push(a + b)

	case sub:
		var a, b int64
		switch d := t.Pop().(type) {
		case *Datum:
			a = d.Value
		case int64:
			a = d
		case int:
			a = int64(d)
		case time.Time:
			a = d.Unix()
		default:
			return v.errorf("Unexpected type for sub %T %q\n", d, d)
		}
		switch d := t.Pop().(type) {
		case *Datum:
			b = d.Value
		case int64:
			b = d
		case int:
			b = int64(d)
		case time.Time:
			b = d.Unix()
		default:
			return v.errorf("Unexpected type for sub %T %q\n", d, d)
		}
		t.Push(b - a)

	case mload:
		t.Push(metrics[i.opnd])

	case dload:
		m := t.Pop().(*Metric)
		var keys []string
		for a := 0; a < i.opnd; a++ {
			keys = append(keys, t.Pop().(string))
		}
		h := key_hash(keys)
		if _, ok := m.Values[h]; !ok {
			m.Values[h] = &Datum{}
		}
		t.Push(m.Values[h])

	default:
		return v.errorf("illegal instruction: %q", i.op)
	}
	t.pc++
	return false
}

// Run fetches and executes each instruction in the program on the input string
// until termination. It returns a boolean indicating a successful match.
func (v *vm) Run(input string) bool {
	t := v.t
	t.stack = make([]interface{}, 0)
	t.matches = make(map[int][]string, 0)
	for {
		if t.pc >= len(v.prog) {
			return false
		}
		i := v.prog[t.pc]
		terminate := v.execute(&t, i, input)
		if terminate {
			// t.reg contains the result of the last match.
			return t.reg == 1
		}
	}
	panic("not reached")
}
<commit_msg>Update comments for match and capref instructions.<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package main

import (
	"fmt"
	"log"
	"regexp"
	"strconv"
	"time"
)

type opcode int

const (
	match     opcode = iota // Match a regular expression against input
	jnm                     // Jump if no match
	inc                     // Increment a variable value
	strptime                // Parse into the timestamp register
	timestamp               // Return value of timestamp register
	ret                     // Return, end program successfully
	push                    // Push operand onto stack
	capref                  // Push capture group reference at operand onto stack
	str                     // Push string constant at operand onto stack
	set                     // Set a variable value
	add                     // Add top values on stack and push to stack
	sub                     // Subtract tpo value from second top value on stack, and push to stack.
	mload                   // Load metric at operand onto top of stack.
	dload                   // Pop operand keys and metric off stack and load datum at metric[key] onto stack.
)

var opNames = map[opcode]string{
	match:     "match",
	jnm:       "jnm",
	inc:       "inc",
	strptime:  "strptime",
	timestamp: "timestamp",
	ret:       "ret",
	push:      "push",
	capref:    "capref",
	str:       "str",
	set:       "set",
	add:       "add",
	sub:       "sub",
	mload:     "mload",
	dload:     "dload",
}

var builtin = map[string]opcode{
	"strptime":  strptime,
	"timestamp": timestamp,
}

type instr struct {
	op   opcode
	opnd int
}

func (i instr) String() string {
	return fmt.Sprintf("%s %d", opNames[i.op], i.opnd)
}

type thread struct {
	pc      int
	reg     int
	matches map[int][]string
	time    time.Time
	stack   []interface{}
}

type vm struct {
	name string
	prog []instr

	// const regexps
	re []*regexp.Regexp
	// const strings
	str []string

	// data segment
	symtab *scope

	t thread
}

func (t *thread) Push(value interface{}) {
	t.stack = append(t.stack, value)
}

func (t *thread) Pop() (value interface{}) {
	last := len(t.stack) - 1
	value = t.stack[last]
	t.stack = t.stack[:last]
	return
}

func (v *vm) errorf(format string, args ...interface{}) bool {
	log.Printf("Runtime error: "+format+"\n", args...)
	v.t.reg = 0
	return true
}

// Execute acts on the current instruction, and returns a boolean indicating
// if the current thread should terminate.
func (v *vm) execute(t *thread, i instr, input string) bool {
	switch i.op {
	case match:
		// match regex and store success
		// Store the results in the operandth element of the stack,
		// where i.opnd == the matched re index
		t.matches[i.opnd] = v.re[i.opnd].FindStringSubmatch(input)
		if t.matches[i.opnd] != nil {
			t.reg = 1
		} else {
			t.reg = 0
		}
	case jnm:
		if t.reg == 0 {
			t.pc = i.opnd
			return false
		}
	case inc:
		// increment a counter
		var delta int64 = 1
		// If opnd is nonzero, the delta is on the stack.
		if i.opnd > 0 {
			val := t.Pop()
			// Don't know what type it is on the stack though.
			switch n := val.(type) {
			case int:
				delta = int64(n)
			case int64:
				delta = n
			case string:
				var err error
				delta, err = strconv.ParseInt(n, 10, 64)
				if err != nil {
					return v.errorf("conversion of %q to numeric failed: %s", val, err)
				}
			default:
				return v.errorf("Unexpected type %T %q", val, val)
			}
		}
		switch d := t.Pop().(type) {
		case Incrementable:
			d.IncBy(delta, t.time)
		case int:
			m := metrics[d]
			m.IncBy(delta, t.time)
		default:
			return v.errorf("Unexpected type to increment: %T %q", d, d)
		}

	case set:
		// Set a gauge
		var value int64
		val := t.Pop()
		// Don't know what type it is on the stack though.
		switch n := val.(type) {
		case int:
			value = int64(n)
		case int64:
			value = n
		case string:
			var err error
			value, err = strconv.ParseInt(n, 10, 64)
			if err != nil {
				return v.errorf("conversion of %q to numeric failed: %s", val, err)
			}
		case time.Time:
			value = n.Unix()
		default:
			return v.errorf("Unexpected type %T %q\n", val, val)
		}
		switch d := t.Pop().(type) {
		case Settable:
			d.Set(value, t.time)
		case int:
			m := metrics[d]
			m.Set(value, t.time)
		default:
			return v.errorf("Unexpected type to set: %T %q", d, d)
		}

	case strptime:
		layout := t.Pop().(string)

		var ts string
		switch s := t.Pop().(type) {
		case string:
			ts = s

		case int: /* capref */
			// First find the match storage index on the stack
			re := t.Pop().(int)
			// Store the result from the re'th index at the s'th index
			ts = t.matches[re][s]
		}

		tm, err := time.Parse(layout, ts)
		if err != nil {
			return v.errorf("time.Parse(%s, %s) failed: %s", layout, ts, err)
		}
		t.time = tm

	case timestamp:
		t.Push(t.time)

	case capref:
		// Find the match storage index on the stack,
		re := t.Pop().(int)
		// Push the result from the re'th match at operandth index
		t.Push(t.matches[re][i.opnd])

	case str:
		t.Push(v.str[i.opnd])

	case ret:
		return true

	case push:
		t.Push(i.opnd)

	case add:
		var a, b int64
		switch d := t.Pop().(type) {
		case *Datum:
			a = d.Value
		case int64:
			a = d
		case int:
			a = int64(d)
		case time.Time:
			a = d.Unix()
		default:
			return v.errorf("Unexpected type for add %T %q\n", d, d)
		}
		switch d := t.Pop().(type) {
		case *Datum:
			b = d.Value
		case int64:
			b = d
		case int:
			b = int64(d)
		case time.Time:
			b = d.Unix()
		default:
			return v.errorf("Unexpected type for add %T %q\n", d, d)
		}
		t.Push(a + b)

	case sub:
		var a, b int64
		switch d := t.Pop().(type) {
		case *Datum:
			a = d.Value
		case int64:
			a = d
		case int:
			a = int64(d)
		case time.Time:
			a = d.Unix()
		default:
			return v.errorf("Unexpected type for sub %T %q\n", d, d)
		}
		switch d := t.Pop().(type) {
		case *Datum:
			b = d.Value
		case int64:
			b = d
		case int:
			b = int64(d)
		case time.Time:
			b = d.Unix()
		default:
			return v.errorf("Unexpected type for sub %T %q\n", d, d)
		}
		t.Push(b - a)

	case mload:
		t.Push(metrics[i.opnd])

	case dload:
		m := t.Pop().(*Metric)
		var keys []string
		for a := 0; a < i.opnd; a++ {
			keys = append(keys, t.Pop().(string))
		}
		h := key_hash(keys)
		if _, ok := m.Values[h]; !ok {
			m.Values[h] = &Datum{}
		}
		t.Push(m.Values[h])

	default:
		return v.errorf("illegal instruction: %q", i.op)
	}
	t.pc++
	return false
}

// Run fetches and executes each instruction in the program on the input string
// until termination. It returns a boolean indicating a successful match.
func (v *vm) Run(input string) bool {
	t := v.t
	t.stack = make([]interface{}, 0)
	t.matches = make(map[int][]string, 0)
	for {
		if t.pc >= len(v.prog) {
			return false
		}
		i := v.prog[t.pc]
		terminate := v.execute(&t, i, input)
		if terminate {
			// t.reg contains the result of the last match.
			return t.reg == 1
		}
	}
	panic("not reached")
}
<|endoftext|>


<commit_before>// +build !windows

package graphdriver

import (
	"fmt"
	"os"
	"syscall"

	"github.com/containers/storage/pkg/idtools"
)

func platformLChown(path string, info os.FileInfo, toHost, toContainer *idtools.IDMappings) error {
	sysinfo := info.Sys()
	if st, ok := sysinfo.(*syscall.Stat_t); ok {
		// Map an on-disk UID/GID pair from host to container
		// using the first map, then back to the host using the
		// second map.  Skip that first step if they're 0, to
		// compensate for cases where a parent layer should
		// have had a mapped value, but didn't.
		uid, gid := int(st.Uid), int(st.Gid)
		if toContainer != nil {
			pair := idtools.IDPair{
				UID: uid,
				GID: gid,
			}
			mappedUid, mappedGid, err := toContainer.ToContainer(pair)
			if err != nil {
				if (uid != 0) || (gid != 0) {
					return fmt.Errorf("error mapping host ID pair %#v for %q to container: %v", pair, path, err)
				}
				mappedUid, mappedGid = uid, gid
			}
			uid, gid = mappedUid, mappedGid
		}
		if toHost != nil {
			pair := idtools.IDPair{
				UID: uid,
				GID: gid,
			}
			mappedPair, err := toHost.ToHost(pair)
			if err != nil {
				return fmt.Errorf("error mapping container ID pair %#v for %q to host: %v", pair, path, err)
			}
			uid, gid = mappedPair.UID, mappedPair.GID
		}
		if uid != int(st.Uid) || gid != int(st.Gid) {
			stat, err := os.Lstat(path)
			if err != nil {
				return fmt.Errorf("%s: lstat(%q): %v", os.Args[0], path, err)
			}
			// Make the change.
			if err := syscall.Lchown(path, uid, gid); err != nil {
				return fmt.Errorf("%s: chown(%q): %v", os.Args[0], path, err)
			}
			// Restore the SUID and SGID bits if they were originally set.
			if (stat.Mode()&os.ModeSymlink == 0) && stat.Mode()&(os.ModeSetuid|os.ModeSetgid) != 0 {
				if err := os.Chmod(path, stat.Mode()); err != nil {
					return fmt.Errorf("%s: chmod(%q): %v", os.Args[0], path, err)
				}
			}
		}
	}
	return nil
}
<commit_msg>Need to preserve the Xattr security.capabilty when chowning.<commit_after>// +build !windows

package graphdriver

import (
	"fmt"
	"os"
	"syscall"

	"github.com/containers/storage/pkg/idtools"
	"github.com/containers/storage/pkg/system"
)

func platformLChown(path string, info os.FileInfo, toHost, toContainer *idtools.IDMappings) error {
	sysinfo := info.Sys()
	if st, ok := sysinfo.(*syscall.Stat_t); ok {
		// Map an on-disk UID/GID pair from host to container
		// using the first map, then back to the host using the
		// second map.  Skip that first step if they're 0, to
		// compensate for cases where a parent layer should
		// have had a mapped value, but didn't.
		uid, gid := int(st.Uid), int(st.Gid)
		if toContainer != nil {
			pair := idtools.IDPair{
				UID: uid,
				GID: gid,
			}
			mappedUid, mappedGid, err := toContainer.ToContainer(pair)
			if err != nil {
				if (uid != 0) || (gid != 0) {
					return fmt.Errorf("error mapping host ID pair %#v for %q to container: %v", pair, path, err)
				}
				mappedUid, mappedGid = uid, gid
			}
			uid, gid = mappedUid, mappedGid
		}
		if toHost != nil {
			pair := idtools.IDPair{
				UID: uid,
				GID: gid,
			}
			mappedPair, err := toHost.ToHost(pair)
			if err != nil {
				return fmt.Errorf("error mapping container ID pair %#v for %q to host: %v", pair, path, err)
			}
			uid, gid = mappedPair.UID, mappedPair.GID
		}
		if uid != int(st.Uid) || gid != int(st.Gid) {
			stat, err := os.Lstat(path)
			if err != nil {
				return fmt.Errorf("%s: lstat(%q): %v", os.Args[0], path, err)
			}
			cap, err := system.Lgetxattr(path, "security.capability")
			if err != nil && err != system.ErrNotSupportedPlatform {
				return fmt.Errorf("%s: Lgetxattr(%q): %v", os.Args[0], path, err)
			}

			// Make the change.
			if err := syscall.Lchown(path, uid, gid); err != nil {
				return fmt.Errorf("%s: chown(%q): %v", os.Args[0], path, err)
			}
			// Restore the SUID and SGID bits if they were originally set.
			if (stat.Mode()&os.ModeSymlink == 0) && stat.Mode()&(os.ModeSetuid|os.ModeSetgid) != 0 {
				if err := os.Chmod(path, stat.Mode()); err != nil {
					return fmt.Errorf("%s: chmod(%q): %v", os.Args[0], path, err)
				}
			}
			if cap != nil {
				if err := system.Lsetxattr(path, "security.capability", cap, 0); err != nil {
					return fmt.Errorf("%s: Lsetxattr(%q): %v", os.Args[0], path, err)
				}
			}

		}
	}
	return nil
}
<|endoftext|>


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

package main

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"log"
	"os"
	"strings"
)

func transpile(out io.Writer, in io.Reader, debug io.Writer) error {
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "sketch.go", in, 0)
	if err != nil {
		return fmt.Errorf("failed to parse file: %v", err)
	}

	if debug != nil {
		ast.Fprint(debug, fset, f, nil)
	}

	for _, d := range f.Decls {
		if err := handleDecl(out, d); err != nil {
			return fmt.Errorf("error handling decl %v: %v", err)
		}
	}
	return nil
}

func handleDecl(out io.Writer, d ast.Decl) error {
	switch decl := d.(type) {
	case *ast.GenDecl:
		return handleGenDecl(out, decl)
	case *ast.FuncDecl:
		return handleFuncDecl(out, decl)
	default:
		return fmt.Errorf("unsupported decl: %v", d)
	}
}

func handleGenDecl(out io.Writer, gd *ast.GenDecl) error {
	for _, s := range gd.Specs {
		vs, ok := s.(*ast.ValueSpec)
		if !ok {
			return fmt.Errorf("unsupported spec: %v", s)
		}
		if len(vs.Names) > 1 {
			return fmt.Errorf("unsupported # of value names: %v", vs.Names)
		}
		decl := []string{}
		if vs.Names[0].Obj.Kind == ast.Con {
			decl = append(decl, "const")
		}
		if len(vs.Values) > 1 {
			return fmt.Errorf("unsupported # of values: %v", vs.Names)
		}
		l, ok := vs.Values[0].(*ast.BasicLit)
		if !ok {
			return fmt.Errorf("unsupported value: %v", vs.Values[0])
		}
		if l.Kind != token.INT {
			return fmt.Errorf("unsupported literal kind: %v", l.Kind)
		}
		decl = append(decl, "int", vs.Names[0].Name, "=", l.Value)
		fmt.Fprintf(out, "%s;\n", strings.Join(decl, " "))
	}
	return nil
}

func handleFuncDecl(out io.Writer, fd *ast.FuncDecl) error {
	if fd.Type.Results != nil {
		return fmt.Errorf("unsupported return type: %v", fd.Type.Results)
	}
	if fd.Type.Params.List != nil {
		return fmt.Errorf("unsupported param type: %v", fd.Type.Params)
	}
	fmt.Fprintf(out, "void %s() {\n", fd.Name)
	if err := handleBlockStmt(out, fd.Body); err != nil {
		return fmt.Errorf("error handling block statement for %q: %v", fd.Name, err)
	}
	fmt.Fprintln(out, "}")
	return nil
}

func handleBlockStmt(out io.Writer, bs *ast.BlockStmt) error {
	for _, s := range bs.List {
		switch st := s.(type) {
		case *ast.ExprStmt:
			c, ok := st.X.(*ast.CallExpr)
			if !ok {
				return fmt.Errorf("unsupported expr: %v", st.X)
			}
			fmt.Fprintf(out, "  ")
			if err := handleCallExpr(out, c); err != nil {
				return err

			}
		case *ast.AssignStmt:
			if len(st.Lhs) > 1 {
				return fmt.Errorf("unsupported # of lhs exprs: %v", st.Lhs)

			}
			if err := handleExpr(out, st.Lhs[0]); err != nil {
				return fmt.Errorf("error handling left expr: %v", st.Lhs[0])
			}
			fmt.Fprintf(out, "=")
			if len(st.Rhs) > 1 {
				return fmt.Errorf("unsupported # of rhs exprs: %v", st.Rhs)

			}
			if err := handleExpr(out, st.Rhs[0]); err != nil {
				return fmt.Errorf("error handling right expr: %v", st.Rhs[0])
			}
		case *ast.IfStmt:
			fmt.Fprintf(out, "if (")
			if err := handleExpr(out, st.Cond); err != nil {
				return fmt.Errorf("error handling if block conditionx: %v", err)
			}
			fmt.Fprint(out, ") {\n")
			if err := handleBlockStmt(out, st.Body); err != nil {
				return fmt.Errorf("error handling if block statements: %v", err)
			}
			fmt.Fprintf(out, "}")
			if st.Else != nil {
				bs, ok := st.Else.(*ast.BlockStmt)
				if !ok {
					return fmt.Errorf("unsupported statement: %v", st.Else)
				}
				fmt.Fprintf(out, " else {\n")
				if err := handleBlockStmt(out, bs); err != nil {
					return fmt.Errorf("error handling else block statements: %v", err)
				}
				fmt.Fprintf(out, "}")
			}
			fmt.Fprintln(out)
		default:
			return fmt.Errorf("unsupported statement: %v", s)

		}
	}
	return nil
}

func handleCallExpr(out io.Writer, c *ast.CallExpr) error {
	funcName, ok := c.Fun.(*ast.Ident)
	if !ok {
		return fmt.Errorf("unsupported func expr: %v", c.Fun)
	}
	args := []string{}
	for _, a := range c.Args {
		var buf bytes.Buffer
		if err := handleExpr(&buf, a); err != nil {
			return fmt.Errorf("error handling func arg expr %v: %v", a, err)
		}
		args = append(args, buf.String())
	}
	fmt.Fprintf(out, "%s(%s);\n", funcName, strings.Join(args, ", "))
	return nil
}

func handleBinaryExpr(out io.Writer, be *ast.BinaryExpr) error {
	if err := handleExpr(out, be.X); err != nil {
		return fmt.Errorf("error handling left part %v of binary expr: %v", be.X, err)
	}
	fmt.Fprint(out, be.Op)
	if err := handleExpr(out, be.Y); err != nil {
		return fmt.Errorf("error handling right part %v of binary expr: %v", be.Y, err)
	}
	return nil
}

func handleIdent(out io.Writer, ident *ast.Ident) error {
	fmt.Fprintf(out, ident.Name)
	return nil
}

func handleBasicLit(out io.Writer, lit *ast.BasicLit) error {
	fmt.Fprintf(out, lit.Value)
	return nil
}

func handleExpr(out io.Writer, e ast.Expr) error {
	switch expr := e.(type) {
	case *ast.CallExpr:
		return handleCallExpr(out, expr)
	case *ast.BinaryExpr:
		return handleBinaryExpr(out, expr)
	case *ast.Ident:
		return handleIdent(out, expr)
	case *ast.BasicLit:
		return handleBasicLit(out, expr)
	default:
		return fmt.Errorf("unsupported expr: %v", e)
	}
}

func main() {
	if err := transpile(os.Stdout, os.Stdin, os.Stderr); err != nil {
		log.Fatalf("failed to transpile: %v", err)
	}
}
<commit_msg>fix ident<commit_after>//
// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"log"
	"os"
	"strings"
)

func transpile(out io.Writer, in io.Reader, debug io.Writer) error {
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "sketch.go", in, 0)
	if err != nil {
		return fmt.Errorf("failed to parse file: %v", err)
	}

	if debug != nil {
		ast.Fprint(debug, fset, f, nil)
	}

	for _, d := range f.Decls {
		if err := handleDecl(out, d); err != nil {
			return fmt.Errorf("error handling decl %v: %v", d, err)
		}
	}
	return nil
}

func handleDecl(out io.Writer, d ast.Decl) error {
	switch decl := d.(type) {
	case *ast.GenDecl:
		return handleGenDecl(out, decl)
	case *ast.FuncDecl:
		return handleFuncDecl(out, decl)
	default:
		return fmt.Errorf("unsupported decl: %v", d)
	}
}

func handleGenDecl(out io.Writer, gd *ast.GenDecl) error {
	for _, s := range gd.Specs {
		vs, ok := s.(*ast.ValueSpec)
		if !ok {
			return fmt.Errorf("unsupported spec: %v", s)
		}
		if len(vs.Names) > 1 {
			return fmt.Errorf("unsupported # of value names: %v", vs.Names)
		}
		decl := []string{}
		if vs.Names[0].Obj.Kind == ast.Con {
			decl = append(decl, "const")
		}
		if len(vs.Values) > 1 {
			return fmt.Errorf("unsupported # of values: %v", vs.Names)
		}
		l, ok := vs.Values[0].(*ast.BasicLit)
		if !ok {
			return fmt.Errorf("unsupported value: %v", vs.Values[0])
		}
		if l.Kind != token.INT {
			return fmt.Errorf("unsupported literal kind: %v", l.Kind)
		}
		decl = append(decl, "int", vs.Names[0].Name, "=", l.Value)
		fmt.Fprintf(out, "%s;\n", strings.Join(decl, " "))
	}
	return nil
}

func handleFuncDecl(out io.Writer, fd *ast.FuncDecl) error {
	if fd.Type.Results != nil {
		return fmt.Errorf("unsupported return type: %v", fd.Type.Results)
	}
	if fd.Type.Params.List != nil {
		return fmt.Errorf("unsupported param type: %v", fd.Type.Params)
	}
	fmt.Fprintf(out, "void %s() {\n", fd.Name)
	if err := handleBlockStmt(out, fd.Body); err != nil {
		return fmt.Errorf("error handling block statement for %q: %v", fd.Name, err)
	}
	fmt.Fprintln(out, "}")
	return nil
}

func handleBlockStmt(out io.Writer, bs *ast.BlockStmt) error {
	for _, s := range bs.List {
		fmt.Fprintf(out, "  ")
		switch st := s.(type) {
		case *ast.ExprStmt:
			c, ok := st.X.(*ast.CallExpr)
			if !ok {
				return fmt.Errorf("unsupported expr: %v", st.X)
			}
			if err := handleCallExpr(out, c); err != nil {
				return err

			}
		case *ast.AssignStmt:
			if len(st.Lhs) > 1 {
				return fmt.Errorf("unsupported # of lhs exprs: %v", st.Lhs)

			}
			if err := handleExpr(out, st.Lhs[0]); err != nil {
				return fmt.Errorf("error handling left expr: %v", st.Lhs[0])
			}
			fmt.Fprintf(out, "=")
			if len(st.Rhs) > 1 {
				return fmt.Errorf("unsupported # of rhs exprs: %v", st.Rhs)

			}
			if err := handleExpr(out, st.Rhs[0]); err != nil {
				return fmt.Errorf("error handling right expr: %v", st.Rhs[0])
			}
		case *ast.IfStmt:
			fmt.Fprintf(out, "if (")
			if err := handleExpr(out, st.Cond); err != nil {
				return fmt.Errorf("error handling if block conditionx: %v", err)
			}
			fmt.Fprint(out, ") {\n")
			if err := handleBlockStmt(out, st.Body); err != nil {
				return fmt.Errorf("error handling if block statements: %v", err)
			}
			fmt.Fprintf(out, "}")
			if st.Else != nil {
				bs, ok := st.Else.(*ast.BlockStmt)
				if !ok {
					return fmt.Errorf("unsupported statement: %v", st.Else)
				}
				fmt.Fprintf(out, " else {\n")
				if err := handleBlockStmt(out, bs); err != nil {
					return fmt.Errorf("error handling else block statements: %v", err)
				}
				fmt.Fprintf(out, "}")
			}
			fmt.Fprintln(out)
		default:
			return fmt.Errorf("unsupported statement: %v", s)

		}
	}
	return nil
}

func handleCallExpr(out io.Writer, c *ast.CallExpr) error {
	funcName, ok := c.Fun.(*ast.Ident)
	if !ok {
		return fmt.Errorf("unsupported func expr: %v", c.Fun)
	}
	args := []string{}
	for _, a := range c.Args {
		var buf bytes.Buffer
		if err := handleExpr(&buf, a); err != nil {
			return fmt.Errorf("error handling func arg expr %v: %v", a, err)
		}
		args = append(args, buf.String())
	}
	fmt.Fprintf(out, "%s(%s);\n", funcName, strings.Join(args, ", "))
	return nil
}

func handleBinaryExpr(out io.Writer, be *ast.BinaryExpr) error {
	if err := handleExpr(out, be.X); err != nil {
		return fmt.Errorf("error handling left part %v of binary expr: %v", be.X, err)
	}
	fmt.Fprint(out, be.Op)
	if err := handleExpr(out, be.Y); err != nil {
		return fmt.Errorf("error handling right part %v of binary expr: %v", be.Y, err)
	}
	return nil
}

func handleIdent(out io.Writer, ident *ast.Ident) error {
	fmt.Fprintf(out, ident.Name)
	return nil
}

func handleBasicLit(out io.Writer, lit *ast.BasicLit) error {
	fmt.Fprintf(out, lit.Value)
	return nil
}

func handleExpr(out io.Writer, e ast.Expr) error {
	switch expr := e.(type) {
	case *ast.CallExpr:
		return handleCallExpr(out, expr)
	case *ast.BinaryExpr:
		return handleBinaryExpr(out, expr)
	case *ast.Ident:
		return handleIdent(out, expr)
	case *ast.BasicLit:
		return handleBasicLit(out, expr)
	default:
		return fmt.Errorf("unsupported expr: %v", e)
	}
}

func main() {
	if err := transpile(os.Stdout, os.Stdin, os.Stderr); err != nil {
		log.Fatalf("failed to transpile: %v", err)
	}
}
<|endoftext|>