<commit_before>package crocker

import (
	"fmt"
	"os"
	"io/ioutil"
	. "polydawn.net/gosh/psh"
	"path/filepath"
	"strconv"
	"time"
)

/**
Produces a Dock struct referring to an active docker daemon.
If an existing daemon can be found running, it is used; if not, one is started.
@param dir path to dock dir.  May be relative.
 */
func NewDock(dir string) *Dock {
	dir, err := filepath.Abs(dir)
	if err != nil { panic(err); }
	dock := loadDock(dir)
	if dock == nil {
		dock = createDock(dir)
	}
	return dock
}

/**
Launch a new docker daemon.
You should try loadDock before this.  (Yes, there are inherently race conditions here.)
*/
func createDock(dir string) *Dock {
	dock := &Dock{
		dir: dir,
		isMine: true,
	}
	Sh("mkdir")("-p")(DefaultIO)(dock.Dir())()
	dock.daemon().Start()
	dock.awaitSocket(250 * time.Millisecond)
	return dock
}

/**
Check for what looks like an existing docker daemon setup, and return a Dock if one is found.
We do a basic check if the pidfile and socket are present, and check if pid is stale, and that's it.
No dialing or protocol negotiation is performed at this stage.
*/
func loadDock(dir string) *Dock {
	dock := &Dock{
		dir: dir,
		isMine: false,
	}

	// check pidfile presence.
	pidfileStat, err := os.Stat(dock.GetPidfilePath())
	if os.IsNotExist(err) { return nil; }
	if err != nil { panic(err); }
	if !pidfileStat.Mode().IsRegular() { return nil; }

	// check for process.
	pidfileBlob, err := ioutil.ReadFile(dock.GetPidfilePath())
	if os.IsNotExist(err) { return nil; }
	if err != nil { panic(err); }
	pid, err := strconv.Atoi(string(pidfileBlob))
	if err != nil { panic(err); }
	_, err = os.FindProcess(pid)
	if err != nil { panic(err); }

	// check for socket.
	if dock.awaitSocket(20 * time.Millisecond) != nil { return nil; }

	// alright, looks like a docker daemon.
	return dock
}

/**
Check/wait for existence of docker.sock.
*/
func (dock *Dock) awaitSocket(patience time.Duration) error {
	timeout := time.Now().Add(patience)
	done := false
	for !done {
		done = time.Now().After(timeout)
		sockStat, err := os.Stat(dock.GetSockPath())
		fmt.Printf("wat :: %#v  %#v\n", sockStat, err)
		if os.IsNotExist(err) {
			if !done {
				time.Sleep(10 * time.Millisecond)
			}
			continue
		}
		if err != nil { panic(err); }
		if (sockStat.Mode() & os.ModeSocket) != 0 {
			// success!
			return nil;
		}
	}
	return fmt.Errorf("timeout waiting for docker socket")
}

type Dock struct {
	/**
	 * Absolute path to the base dir for a docker daemon.
	 *
	 * 'docker.sock' and 'docker.pid' are expected to exist immediately inside this path.
	 * The daemon's working dir may also be here.
	 *
	 * The last segment of the path is quite probably a symlink, and should be respected
	 * even if dangling (unless that means making more than one directory on the far
	 * side; if things are that dangling, give up).
	 */
	dir string

	/**
	 * True iff the daemon at this dock location was spawned by us.
	 * Basically used to determine if Slay() should actually fire teh lazors or not.
	 */
	isMine bool
}

func (dock Dock) Dir() string {
	return dock.dir
}

func (dock *Dock) cmd() Shfn {
	return Sh("docker")(DefaultIO)(
		"-H="+fmt.Sprintf("unix://%s", dock.GetSockPath()),
	)
}

func (dock *Dock) Client() Shfn {
	return dock.cmd()
}

func (dock *Dock) GetPidfilePath() string {
	return fmt.Sprintf("%s/%s", dock.Dir(), "docker.pid")
}

func (dock *Dock) GetSockPath() string {
	return fmt.Sprintf("%s/%s", dock.Dir(), "docker.sock")
}

func (dock *Dock) daemon() Shfn {
	return dock.cmd()(
		"-d",
		"-g="+dock.Dir(),
		"-p="+dock.GetPidfilePath(),
	)(Opts{Cwd: dock.Dir()})
}

func (dock *Dock) Slay() {
	if !dock.isMine { return; }
	Sh("bash")("-c")(DefaultIO)("kill `cat \""+dock.GetPidfilePath()+"\"`")()
}
<commit_msg>dial to make sure docker.sock is live when detecting daemons or waiting for a new one.  also error immediately if there's a non-socket in that path.<commit_after>package crocker

import (
	"fmt"
	"os"
	"io/ioutil"
	"net"
	. "polydawn.net/gosh/psh"
	"path/filepath"
	"strconv"
	"time"
)

/**
Produces a Dock struct referring to an active docker daemon.
If an existing daemon can be found running, it is used; if not, one is started.
@param dir path to dock dir.  May be relative.
 */
func NewDock(dir string) *Dock {
	dir, err := filepath.Abs(dir)
	if err != nil { panic(err); }
	dock := loadDock(dir)
	if dock == nil {
		dock = createDock(dir)
	}
	return dock
}

/**
Launch a new docker daemon.
You should try loadDock before this.  (Yes, there are inherently race conditions here.)
*/
func createDock(dir string) *Dock {
	dock := &Dock{
		dir: dir,
		isMine: true,
	}
	Sh("mkdir")("-p")(DefaultIO)(dock.Dir())()
	dock.daemon().Start()
	dock.awaitSocket(250 * time.Millisecond)
	return dock
}

/**
Check for what looks like an existing docker daemon setup, and return a Dock if one is found.
We do a basic check if the pidfile and socket are present, and check if pid is stale, and that's it.
No dialing or protocol negotiation is performed at this stage.
*/
func loadDock(dir string) *Dock {
	dock := &Dock{
		dir: dir,
		isMine: false,
	}

	// check pidfile presence.
	pidfileStat, err := os.Stat(dock.GetPidfilePath())
	if os.IsNotExist(err) { return nil; }
	if err != nil { panic(err); }
	if !pidfileStat.Mode().IsRegular() { return nil; }

	// check for process.
	pidfileBlob, err := ioutil.ReadFile(dock.GetPidfilePath())
	if os.IsNotExist(err) { return nil; }
	if err != nil { panic(err); }
	pid, err := strconv.Atoi(string(pidfileBlob))
	if err != nil { panic(err); }
	_, err = os.FindProcess(pid)
	if err != nil { panic(err); }

	// check for socket.
	if dock.awaitSocket(20 * time.Millisecond) != nil { return nil; }

	// alright, looks like a docker daemon.
	return dock
}

/**
Check/wait for existence of docker.sock.
*/
func (dock *Dock) awaitSocket(patience time.Duration) error {
	timeout := time.Now().Add(patience)
	done := false
	for !done {
		done = time.Now().After(timeout)
		sockStat, err := os.Stat(dock.GetSockPath())
		if os.IsNotExist(err) {
			// continue
		} else if err != nil {
			panic(err);
		} else if (sockStat.Mode() & os.ModeSocket) != 0 {
			// still have to check if it's dialable; docker daemon doesn't even try to remove socket files when it's done.
			dial, err := net.Dial("unix", dock.GetSockPath())
			if err == nil {
				// success!
				dial.Close()
				return nil
			}
		} else {
			// file exists but isn't socket; do not want
			return fmt.Errorf("not a socket in place of docker socket")
		}
		if !done {
			time.Sleep(10 * time.Millisecond)
		}
	}
	return fmt.Errorf("timeout waiting for docker socket")
}

type Dock struct {
	/**
	 * Absolute path to the base dir for a docker daemon.
	 *
	 * 'docker.sock' and 'docker.pid' are expected to exist immediately inside this path.
	 * The daemon's working dir may also be here.
	 *
	 * The last segment of the path is quite probably a symlink, and should be respected
	 * even if dangling (unless that means making more than one directory on the far
	 * side; if things are that dangling, give up).
	 */
	dir string

	/**
	 * True iff the daemon at this dock location was spawned by us.
	 * Basically used to determine if Slay() should actually fire teh lazors or not.
	 */
	isMine bool
}

func (dock Dock) Dir() string {
	return dock.dir
}

func (dock *Dock) cmd() Shfn {
	return Sh("docker")(DefaultIO)(
		"-H="+fmt.Sprintf("unix://%s", dock.GetSockPath()),
	)
}

func (dock *Dock) Client() Shfn {
	return dock.cmd()
}

func (dock *Dock) GetPidfilePath() string {
	return fmt.Sprintf("%s/%s", dock.Dir(), "docker.pid")
}

func (dock *Dock) GetSockPath() string {
	return fmt.Sprintf("%s/%s", dock.Dir(), "docker.sock")
}

func (dock *Dock) daemon() Shfn {
	return dock.cmd()(
		"-d",
		"-g="+dock.Dir(),
		"-p="+dock.GetPidfilePath(),
	)(Opts{Cwd: dock.Dir()})
}

func (dock *Dock) Slay() {
	if !dock.isMine { return; }
	Sh("bash")("-c")(DefaultIO)("kill `cat \""+dock.GetPidfilePath()+"\"`")()
}
<|endoftext|>


<commit_before>package discovery

import (
	"fmt"
	"testing"
)

func TestVersionSet(t *testing.T) {
	tests := []struct {
		ConstraintStr string
		VersionStr    string
		ShouldHave    bool
	}{
		// These test cases are not exhaustive since the underlying go-version
		// library is well-tested. This is mainly here just to exercise our
		// wrapper code, but also used as an opportunity to cover some basic
		// but important cases such as the ~> constraint so that we'll be more
		// likely to catch any accidental breaking behavior changes in the
		// underlying library.
		{
			">=1.0.0",
			"1.0.0",
			true,
		},
		{
			">=1.0.0",
			"0.0.0",
			false,
		},
		{
			">=1.0.0",
			"1.1.0-beta1",
			true,
		},
		{
			"~>1.1.0",
			"1.1.2-beta1",
			true,
		},
		{
			"~>1.1.0",
			"1.2.0",
			false,
		},
	}

	for _, test := range tests {
		t.Run(fmt.Sprintf("%s has %s", test.ConstraintStr, test.VersionStr), func(t *testing.T) {
			accepted, err := ConstraintStr(test.ConstraintStr).Parse()
			if err != nil {
				t.Fatalf("unwanted error parsing constraints string %q: %s", test.ConstraintStr, err)
			}

			version, err := VersionStr(test.VersionStr).Parse()
			if err != nil {
				t.Fatalf("unwanted error parsing version string %q: %s", test.VersionStr, err)
			}

			if got, want := accepted.Allows(version), test.ShouldHave; got != want {
				t.Errorf("Has returned %#v; want %#v", got, want)
			}
		})
	}
}
<commit_msg>update tests to match pre-release constraint rules<commit_after>package discovery

import (
	"fmt"
	"testing"
)

func TestVersionSet(t *testing.T) {
	tests := []struct {
		ConstraintStr string
		VersionStr    string
		ShouldHave    bool
	}{
		// These test cases are not exhaustive since the underlying go-version
		// library is well-tested. This is mainly here just to exercise our
		// wrapper code, but also used as an opportunity to cover some basic
		// but important cases such as the ~> constraint so that we'll be more
		// likely to catch any accidental breaking behavior changes in the
		// underlying library.
		{
			">=1.0.0",
			"1.0.0",
			true,
		},
		{
			">=1.0.0",
			"0.0.0",
			false,
		},
		{
			">=1.0.0",
			"1.1.0-beta1",
			false,
		},
		{
			">=1.0.0",
			"1.1.0",
			true,
		},
		{
			"~>1.1.0-a",
			"1.1.0-beta1",
			true,
		},
		{
			"~>1.1.0",
			"1.1.2",
			true,
		},
		{
			"~>1.1.0",
			"1.2.0",
			false,
		},
	}

	for _, test := range tests {
		t.Run(fmt.Sprintf("%s has %s", test.ConstraintStr, test.VersionStr), func(t *testing.T) {
			accepted, err := ConstraintStr(test.ConstraintStr).Parse()
			if err != nil {
				t.Fatalf("unwanted error parsing constraints string %q: %s", test.ConstraintStr, err)
			}

			version, err := VersionStr(test.VersionStr).Parse()
			if err != nil {
				t.Fatalf("unwanted error parsing version string %q: %s", test.VersionStr, err)
			}

			if got, want := accepted.Allows(version), test.ShouldHave; got != want {
				t.Errorf("Has returned %#v; want %#v", got, want)
			}
		})
	}
}
<|endoftext|>


<commit_before>package interactive

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/gorilla/websocket"
	"github.com/taskcluster/taskcluster-worker/engines"
	"github.com/taskcluster/taskcluster-worker/plugins/interactive/displayconsts"
)

type displayServer struct {
	m        sync.Mutex
	sandbox  engines.Sandbox
	log      *logrus.Entry
	done     chan struct{}
	handlers []*DisplayHandler
}

func newDisplayServer(sandbox engines.Sandbox, log *logrus.Entry) *displayServer {
	return &displayServer{
		log:     log,
		sandbox: sandbox,
		done:    make(chan struct{}),
	}
}

func (s *displayServer) Abort() {
	s.m.Lock()
	defer s.m.Unlock()

	// Ensure the done channel is closed
	select {
	case <-s.done: // can't close twice
	default:
		close(s.done)
	}

	// Abort all existing handlers
	for _, h := range s.handlers {
		h.Abort()
	}
	s.handlers = nil
}

var displayUpgrader = websocket.Upgrader{
	HandshakeTimeout: displayconsts.DisplayHandshakeTimeout,
	ReadBufferSize:   displayconsts.DisplayBufferSize,
	WriteBufferSize:  displayconsts.DisplayBufferSize,
}

func (s *displayServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	select {
	case <-s.done:
		reply(w, http.StatusGone, displayconsts.ErrorMessage{
			Code:    displayconsts.ErrorCodeExecutionTerminated,
			Message: "Task execution has halted, displays are not available anymore.",
		})
		return
	default:
	}

	// If not a websocket upgrade we seek to list the displays
	if !websocket.IsWebSocketUpgrade(r) {
		s.listDisplays(w, r)
		return
	}

	displayName := r.URL.Query().Get("display")
	if displayName == "" {
		reply(w, http.StatusBadRequest, displayconsts.ErrorMessage{
			Code:    displayconsts.ErrorCodeInvalidParameters,
			Message: "Querystring parameter 'display' must by given!",
		})
	}
	display, err := s.sandbox.OpenDisplay(displayName)
	switch err {
	case engines.ErrNoSuchDisplay:
		reply(w, http.StatusNotFound, displayconsts.ErrorMessage{
			Code:    displayconsts.ErrorCodeDisplayNotFound,
			Message: fmt.Sprintf("Display: '%s' couldn't be found", displayName),
		})
		return
	case engines.ErrSandboxTerminated, engines.ErrSandboxAborted:
		reply(w, http.StatusGone, errorMessageExecutionHalted)
		return
	case engines.ErrFeatureNotSupported:
		reply(w, http.StatusBadRequest, errorMessageDisplayNotSupported)
		return
	case nil:
	default:
		//TODO: Send error to sentry
		reply(w, http.StatusInternalServerError, errorMessageInternalError)
		return
	}

	// Upgrade the connection
	ws, err := displayUpgrader.Upgrade(w, r, nil)
	if err != nil {
		display.Close()
		return
	}

	// Lock and ensure that we haven't aborted
	s.m.Lock()
	defer s.m.Unlock()

	select {
	case <-s.done:
		ws.Close()
		display.Close()
		return
	default:
	}

	// Create new handler and add it to the list
	h := NewDisplayHandler(ws, display, s.log.WithField("display", displayName))
	s.handlers = append(s.handlers, h)
}

func (s *displayServer) listDisplays(w http.ResponseWriter, r *http.Request) {
	displays, err := s.sandbox.ListDisplays()
	if err == engines.ErrSandboxTerminated || err == engines.ErrSandboxAborted {
		reply(w, http.StatusGone, errorMessageExecutionHalted)
		return
	}
	if err == engines.ErrFeatureNotSupported {
		reply(w, http.StatusBadRequest, errorMessageDisplayNotSupported)
		return
	}
	if err != nil {
		//TODO: Send error to sentry
		reply(w, http.StatusInternalServerError, errorMessageInternalError)
		return
	}

	// Convert to JSON...
	result := make([]displayconsts.DisplayEntry, len(displays))
	for i, d := range displays {
		result[i].Description = d.Description
		result[i].Display = d.Name
		result[i].Width = d.Width
		result[i].Height = d.Height
	}

	reply(w, http.StatusOK, result)
}

func reply(w http.ResponseWriter, status int, payload interface{}) {
	var data []byte
	if payload != nil {
		var err error
		data, err = json.Marshal(payload)
		if err != nil {
			panic(fmt.Sprintf("Failed to marshal JSON reply, error: %s", err))
		}
	}
	if len(data) > 0 {
		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("Content-Length", strconv.Itoa(len(data)))
	}
	w.WriteHeader(status)
	w.Write(data)
}

var errorMessageDisplayNotSupported = displayconsts.ErrorMessage{
	Code:    displayconsts.ErrorCodeDisplayNotFound,
	Message: "Task execution environment doesn't support display interaction",
}

var errorMessageExecutionHalted = displayconsts.ErrorMessage{
	Code:    displayconsts.ErrorCodeExecutionTerminated,
	Message: "Task execution has halted, displays are not available anymore.",
}

var errorMessageInternalError = displayconsts.ErrorMessage{
	Code:    displayconsts.ErrorCodeInternalError,
	Message: "Worker encountered an internal error",
}
<commit_msg>Addressed comment by @walac<commit_after>package interactive

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/gorilla/websocket"
	"github.com/taskcluster/taskcluster-worker/engines"
	"github.com/taskcluster/taskcluster-worker/plugins/interactive/displayconsts"
)

type displayServer struct {
	m        sync.Mutex
	sandbox  engines.Sandbox
	log      *logrus.Entry
	done     chan struct{}
	handlers []*DisplayHandler
}

func newDisplayServer(sandbox engines.Sandbox, log *logrus.Entry) *displayServer {
	return &displayServer{
		log:     log,
		sandbox: sandbox,
		done:    make(chan struct{}),
	}
}

func (s *displayServer) Abort() {
	s.m.Lock()
	defer s.m.Unlock()

	// Ensure the done channel is closed
	select {
	case <-s.done: // can't close twice
	default:
		close(s.done)
	}

	// Abort all existing handlers
	for _, h := range s.handlers {
		h.Abort()
	}
	s.handlers = nil
}

var displayUpgrader = websocket.Upgrader{
	HandshakeTimeout: displayconsts.DisplayHandshakeTimeout,
	ReadBufferSize:   displayconsts.DisplayBufferSize,
	WriteBufferSize:  displayconsts.DisplayBufferSize,
}

func (s *displayServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	select {
	case <-s.done:
		reply(w, http.StatusGone, displayconsts.ErrorMessage{
			Code:    displayconsts.ErrorCodeExecutionTerminated,
			Message: "Task execution has halted, displays are not available anymore.",
		})
		return
	default:
	}

	// If not a websocket upgrade we seek to list the displays
	if !websocket.IsWebSocketUpgrade(r) {
		s.listDisplays(w, r)
		return
	}

	displayName := r.URL.Query().Get("display")
	if displayName == "" {
		reply(w, http.StatusBadRequest, displayconsts.ErrorMessage{
			Code:    displayconsts.ErrorCodeInvalidParameters,
			Message: "Querystring parameter 'display' must be given!",
		})
	}
	display, err := s.sandbox.OpenDisplay(displayName)
	switch err {
	case engines.ErrNoSuchDisplay:
		reply(w, http.StatusNotFound, displayconsts.ErrorMessage{
			Code:    displayconsts.ErrorCodeDisplayNotFound,
			Message: fmt.Sprintf("Display: '%s' couldn't be found", displayName),
		})
		return
	case engines.ErrSandboxTerminated, engines.ErrSandboxAborted:
		reply(w, http.StatusGone, errorMessageExecutionHalted)
		return
	case engines.ErrFeatureNotSupported:
		reply(w, http.StatusBadRequest, errorMessageDisplayNotSupported)
		return
	case nil:
	default:
		//TODO: Send error to sentry
		reply(w, http.StatusInternalServerError, errorMessageInternalError)
		return
	}

	// Upgrade the connection
	ws, err := displayUpgrader.Upgrade(w, r, nil)
	if err != nil {
		display.Close()
		return
	}

	// Lock and ensure that we haven't aborted
	s.m.Lock()
	defer s.m.Unlock()

	select {
	case <-s.done:
		ws.Close()
		display.Close()
		return
	default:
	}

	// Create new handler and add it to the list
	h := NewDisplayHandler(ws, display, s.log.WithField("display", displayName))
	s.handlers = append(s.handlers, h)
}

func (s *displayServer) listDisplays(w http.ResponseWriter, r *http.Request) {
	displays, err := s.sandbox.ListDisplays()
	if err == engines.ErrSandboxTerminated || err == engines.ErrSandboxAborted {
		reply(w, http.StatusGone, errorMessageExecutionHalted)
		return
	}
	if err == engines.ErrFeatureNotSupported {
		reply(w, http.StatusBadRequest, errorMessageDisplayNotSupported)
		return
	}
	if err != nil {
		//TODO: Send error to sentry
		reply(w, http.StatusInternalServerError, errorMessageInternalError)
		return
	}

	// Convert to JSON...
	result := make([]displayconsts.DisplayEntry, len(displays))
	for i, d := range displays {
		result[i].Description = d.Description
		result[i].Display = d.Name
		result[i].Width = d.Width
		result[i].Height = d.Height
	}

	reply(w, http.StatusOK, result)
}

func reply(w http.ResponseWriter, status int, payload interface{}) {
	var data []byte
	if payload != nil {
		var err error
		data, err = json.Marshal(payload)
		if err != nil {
			panic(fmt.Sprintf("Failed to marshal JSON reply, error: %s", err))
		}
	}
	if len(data) > 0 {
		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("Content-Length", strconv.Itoa(len(data)))
	}
	w.WriteHeader(status)
	w.Write(data)
}

var errorMessageDisplayNotSupported = displayconsts.ErrorMessage{
	Code:    displayconsts.ErrorCodeDisplayNotFound,
	Message: "Task execution environment doesn't support display interaction",
}

var errorMessageExecutionHalted = displayconsts.ErrorMessage{
	Code:    displayconsts.ErrorCodeExecutionTerminated,
	Message: "Task execution has halted, displays are not available anymore.",
}

var errorMessageInternalError = displayconsts.ErrorMessage{
	Code:    displayconsts.ErrorCodeInternalError,
	Message: "Worker encountered an internal error",
}
<|endoftext|>


<commit_before>package opentsdb

import (
	"fmt"
	"net"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/outputs"
)

type OpenTSDB struct {
	Prefix string

	Host string
	Port int

	Debug bool
}

var sanitizedChars = strings.NewReplacer("@", "-", "*", "-", " ", "_",
	`%`, "-", "#", "-", "$", "-")

var sampleConfig = `
  ## prefix for metrics keys
  prefix = "my.specific.prefix."

  ## Telnet Mode ##
  ## DNS name of the OpenTSDB server in telnet mode
  host = "opentsdb.example.com"

  ## Port of the OpenTSDB server in telnet mode
  port = 4242

  ## Debug true - Prints OpenTSDB communication
  debug = false
`

type MetricLine struct {
	Metric    string
	Timestamp int64
	Value     string
	Tags      string
}

func (o *OpenTSDB) Connect() error {
	// Test Connection to OpenTSDB Server
	uri := fmt.Sprintf("%s:%d", o.Host, o.Port)
	tcpAddr, err := net.ResolveTCPAddr("tcp", uri)
	if err != nil {
		return fmt.Errorf("OpenTSDB: TCP address cannot be resolved")
	}
	connection, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return fmt.Errorf("OpenTSDB: Telnet connect fail")
	}
	defer connection.Close()
	return nil
}

func (o *OpenTSDB) Write(metrics []telegraf.Metric) error {
	if len(metrics) == 0 {
		return nil
	}
	now := time.Now()

	// Send Data with telnet / socket communication
	uri := fmt.Sprintf("%s:%d", o.Host, o.Port)
	tcpAddr, _ := net.ResolveTCPAddr("tcp", uri)
	connection, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return fmt.Errorf("OpenTSDB: Telnet connect fail")
	}
	defer connection.Close()

	for _, m := range metrics {
		for _, metric := range buildMetrics(m, now, o.Prefix) {
			messageLine := fmt.Sprintf("put %s %v %s %s\n",
				metric.Metric, metric.Timestamp, metric.Value, metric.Tags)
			if o.Debug {
				fmt.Print(messageLine)
			}
			_, err := connection.Write([]byte(messageLine))
			if err != nil {
				return fmt.Errorf("OpenTSDB: Telnet writing error %s", err.Error())
			}
		}
	}

	return nil
}

func buildTags(mTags map[string]string) []string {
	tags := make([]string, len(mTags))
	index := 0
	for k, v := range mTags {
		tags[index] = sanitizedChars.Replace(fmt.Sprintf("%s=%s", k, v))
		index++
	}
	sort.Strings(tags)
	return tags
}

func buildMetrics(m telegraf.Metric, now time.Time, prefix string) []*MetricLine {
	ret := []*MetricLine{}
	for fieldName, value := range m.Fields() {
		metric := &MetricLine{
			Metric: sanitizedChars.Replace(fmt.Sprintf("%s%s_%s",
				prefix, m.Name(), fieldName)),
			Timestamp: now.Unix(),
		}

		metricValue, buildError := buildValue(value)
		if buildError != nil {
			fmt.Printf("OpenTSDB: %s\n", buildError.Error())
			continue
		}
		metric.Value = metricValue
		tagsSlice := buildTags(m.Tags())
		metric.Tags = fmt.Sprint(strings.Join(tagsSlice, " "))
		ret = append(ret, metric)
	}
	return ret
}

func buildValue(v interface{}) (string, error) {
	var retv string
	switch p := v.(type) {
	case int64:
		retv = IntToString(int64(p))
	case uint64:
		retv = UIntToString(uint64(p))
	case float64:
		retv = FloatToString(float64(p))
	default:
		return retv, fmt.Errorf("unexpected type %T with value %v for OpenTSDB", v, v)
	}
	return retv, nil
}

func IntToString(input_num int64) string {
	return strconv.FormatInt(input_num, 10)
}

func UIntToString(input_num uint64) string {
	return strconv.FormatUint(input_num, 10)
}

func FloatToString(input_num float64) string {
	return strconv.FormatFloat(input_num, 'f', 6, 64)
}

func (o *OpenTSDB) SampleConfig() string {
	return sampleConfig
}

func (o *OpenTSDB) Description() string {
	return "Configuration for OpenTSDB server to send metrics to"
}

func (o *OpenTSDB) Close() error {
	return nil
}

func init() {
	outputs.Add("opentsdb", func() telegraf.Output {
		return &OpenTSDB{}
	})
}
<commit_msg>Replace ":" with "_" in tags. This should make the mysql plugin work with the opentsdb output (it uses a "server" tag like "127.0.0.1:3306") (#1256)<commit_after>package opentsdb

import (
	"fmt"
	"net"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/outputs"
)

type OpenTSDB struct {
	Prefix string

	Host string
	Port int

	Debug bool
}

var sanitizedChars = strings.NewReplacer("@", "-", "*", "-", " ", "_",
	`%`, "-", "#", "-", "$", "-", ":", "_")

var sampleConfig = `
  ## prefix for metrics keys
  prefix = "my.specific.prefix."

  ## Telnet Mode ##
  ## DNS name of the OpenTSDB server in telnet mode
  host = "opentsdb.example.com"

  ## Port of the OpenTSDB server in telnet mode
  port = 4242

  ## Debug true - Prints OpenTSDB communication
  debug = false
`

type MetricLine struct {
	Metric    string
	Timestamp int64
	Value     string
	Tags      string
}

func (o *OpenTSDB) Connect() error {
	// Test Connection to OpenTSDB Server
	uri := fmt.Sprintf("%s:%d", o.Host, o.Port)
	tcpAddr, err := net.ResolveTCPAddr("tcp", uri)
	if err != nil {
		return fmt.Errorf("OpenTSDB: TCP address cannot be resolved")
	}
	connection, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return fmt.Errorf("OpenTSDB: Telnet connect fail")
	}
	defer connection.Close()
	return nil
}

func (o *OpenTSDB) Write(metrics []telegraf.Metric) error {
	if len(metrics) == 0 {
		return nil
	}
	now := time.Now()

	// Send Data with telnet / socket communication
	uri := fmt.Sprintf("%s:%d", o.Host, o.Port)
	tcpAddr, _ := net.ResolveTCPAddr("tcp", uri)
	connection, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return fmt.Errorf("OpenTSDB: Telnet connect fail")
	}
	defer connection.Close()

	for _, m := range metrics {
		for _, metric := range buildMetrics(m, now, o.Prefix) {
			messageLine := fmt.Sprintf("put %s %v %s %s\n",
				metric.Metric, metric.Timestamp, metric.Value, metric.Tags)
			if o.Debug {
				fmt.Print(messageLine)
			}
			_, err := connection.Write([]byte(messageLine))
			if err != nil {
				return fmt.Errorf("OpenTSDB: Telnet writing error %s", err.Error())
			}
		}
	}

	return nil
}

func buildTags(mTags map[string]string) []string {
	tags := make([]string, len(mTags))
	index := 0
	for k, v := range mTags {
		tags[index] = sanitizedChars.Replace(fmt.Sprintf("%s=%s", k, v))
		index++
	}
	sort.Strings(tags)
	return tags
}

func buildMetrics(m telegraf.Metric, now time.Time, prefix string) []*MetricLine {
	ret := []*MetricLine{}
	for fieldName, value := range m.Fields() {
		metric := &MetricLine{
			Metric: sanitizedChars.Replace(fmt.Sprintf("%s%s_%s",
				prefix, m.Name(), fieldName)),
			Timestamp: now.Unix(),
		}

		metricValue, buildError := buildValue(value)
		if buildError != nil {
			fmt.Printf("OpenTSDB: %s\n", buildError.Error())
			continue
		}
		metric.Value = metricValue
		tagsSlice := buildTags(m.Tags())
		metric.Tags = fmt.Sprint(strings.Join(tagsSlice, " "))
		ret = append(ret, metric)
	}
	return ret
}

func buildValue(v interface{}) (string, error) {
	var retv string
	switch p := v.(type) {
	case int64:
		retv = IntToString(int64(p))
	case uint64:
		retv = UIntToString(uint64(p))
	case float64:
		retv = FloatToString(float64(p))
	default:
		return retv, fmt.Errorf("unexpected type %T with value %v for OpenTSDB", v, v)
	}
	return retv, nil
}

func IntToString(input_num int64) string {
	return strconv.FormatInt(input_num, 10)
}

func UIntToString(input_num uint64) string {
	return strconv.FormatUint(input_num, 10)
}

func FloatToString(input_num float64) string {
	return strconv.FormatFloat(input_num, 'f', 6, 64)
}

func (o *OpenTSDB) SampleConfig() string {
	return sampleConfig
}

func (o *OpenTSDB) Description() string {
	return "Configuration for OpenTSDB server to send metrics to"
}

func (o *OpenTSDB) Close() error {
	return nil
}

func init() {
	outputs.Add("opentsdb", func() telegraf.Output {
		return &OpenTSDB{}
	})
}
<|endoftext|>


<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package commands

import (
	. "github.com/limetext/lime/backend"
	. "github.com/quarnster/util/text"
	"reflect"
	"testing"
)

func TestMove(t *testing.T) {
	ed := GetEditor()

	w := ed.NewWindow()
	v := w.NewFile()
	e := v.BeginEdit()
	v.Insert(e, 0, "Hello World!\nTest123123\nAbrakadabra\n")
	v.EndEdit(e)

	type Test struct {
		in      []Region
		by      string
		extend  bool
		forward bool
		exp     []Region
		args    Args
	}

	tests := []Test{
		{
			[]Region{{1, 1}, {3, 3}, {6, 6}},
			"characters",
			false,
			true,
			[]Region{{2, 2}, {4, 4}, {7, 7}},
			nil,
		},
		{
			[]Region{{1, 1}, {3, 3}, {6, 6}},
			"characters",
			false,
			false,
			[]Region{{0, 0}, {2, 2}, {5, 5}},
			nil,
		},
		{
			[]Region{{1, 1}, {3, 3}, {10, 6}},
			"characters",
			false,
			true,
			[]Region{{2, 2}, {4, 4}, {7, 7}},
			nil,
		},
		{
			[]Region{{1, 1}, {3, 3}, {10, 6}},
			"characters",
			false,
			false,
			[]Region{{0, 0}, {2, 2}, {5, 5}},
			nil,
		},
		{
			[]Region{{1, 1}, {3, 3}, {10, 6}},
			"characters",
			true,
			true,
			[]Region{{1, 2}, {3, 4}, {10, 7}},
			nil,
		},
		{
			[]Region{{1, 1}, {3, 3}, {10, 6}},
			"characters",
			true,
			false,
			[]Region{{1, 0}, {3, 2}, {10, 5}},
			nil,
		},
		{
			[]Region{{1, 3}, {3, 5}, {10, 7}},
			"characters",
			true,
			true,
			[]Region{{1, 6}, {10, 8}},
			nil,
		},
		{
			[]Region{{1, 1}},
			"stops",
			true,
			true,
			[]Region{{1, 5}},
			Args{"word_end": true},
		},
		{
			[]Region{{1, 1}},
			"stops",
			false,
			true,
			[]Region{{6, 6}},
			Args{"word_begin": true},
		},
		{
			[]Region{{6, 6}},
			"stops",
			false,
			false,
			[]Region{{0, 0}},
			Args{"word_begin": true},
		},
		{
			[]Region{{34, 34}},
			"lines",
			false,
			false,
			[]Region{{23, 23}},
			nil,
		},
		{
			[]Region{{23, 23}},
			"lines",
			false,
			false,
			[]Region{{10, 10}},
			nil,
		},
		{
			[]Region{{100, 100}},
			"lines",
			false,
			false,
			[]Region{{24, 24}},
			nil,
		},
	}
	for i, test := range tests {
		v.Sel().Clear()
		for _, r := range test.in {
			v.Sel().Add(r)
		}
		args := Args{"by": test.by, "extend": test.extend, "forward": test.forward}
		if test.args != nil {
			for k, v := range test.args {
				args[k] = v
			}
		}
		ed.CommandHandler().RunTextCommand(v, "move", args)
		if sr := v.Sel().Regions(); !reflect.DeepEqual(sr, test.exp) {
			t.Errorf("Test %d failed. Expected %v, but got %v: %+v", i, test.exp, sr, test)
		}
	}

	e = v.BeginEdit()
	v.Insert(e, v.Buffer().Size(), "abc")
	v.EndEdit(e)

	tests = []Test{
		{
			[]Region{{100, 100}},
			"lines",
			false,
			false,
			[]Region{{27, 27}},
			nil,
		},
	}
	for i, test := range tests {
		v.Sel().Clear()
		for _, r := range test.in {
			v.Sel().Add(r)
		}
		args := Args{"by": test.by, "extend": test.extend, "forward": test.forward}
		if test.args != nil {
			for k, v := range test.args {
				args[k] = v
			}
		}

		ed.CommandHandler().RunTextCommand(v, "move", args)
		if sr := v.Sel().Regions(); !reflect.DeepEqual(sr, test.exp) {
			t.Errorf("Test %d failed. Expected %v, but got %v: %+v", i, test.exp, sr, test)
		}
	}
}

func TestMoveTo(t *testing.T) {
	/*
	   Correct behavior of MoveTo:
	       - Moves each cursor directly to the indicated position.
	       - If extend, the selection will be extended in the direction of movement
	*/

	ed := GetEditor()
	w := ed.NewWindow()
	v := w.NewFile()
	e := v.BeginEdit()

	v.Insert(e, 0, "Hello World!\nTest123123\nAbrakadabra\n")
	v.EndEdit(e)

	type MoveToTest struct {
		in     []Region
		to     string
		extend bool
		exp    []Region
	}
	vbufflen := v.Buffer().Size()
	tests := []MoveToTest{
		{
			[]Region{{vbufflen, vbufflen}},
			"bof",
			false,
			[]Region{{0, 0}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}, {vbufflen - 2, vbufflen - 2}},
			"bof",
			false,
			[]Region{{0, 0}},
		},
		{
			[]Region{{vbufflen, vbufflen}, {3, 3}},
			"bof",
			false,
			[]Region{{0, 0}},
		},
		{
			[]Region{{vbufflen, vbufflen}},
			"bof",
			true,
			[]Region{{vbufflen, 0}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}, {vbufflen - 2, vbufflen - 2}},
			"bof",
			true,
			[]Region{{vbufflen - 1, 0}},
		},
		{
			[]Region{{vbufflen, vbufflen}, {3, 3}},
			"bof",
			true,
			[]Region{{vbufflen, 0}},
		},
		{
			[]Region{{0, 0}},
			"eof",
			false,
			[]Region{{vbufflen, vbufflen}},
		},
		{
			[]Region{{0, 0}, {3, 3}},
			"eof",
			false,
			[]Region{{vbufflen, vbufflen}},
		},
		{
			[]Region{{0, 0}, {vbufflen - 4, vbufflen - 4}},
			"eof",
			false,
			[]Region{{vbufflen, vbufflen}},
		},
		{
			[]Region{{0, 0}},
			"eof",
			true,
			[]Region{{0, vbufflen}},
		},
		{
			[]Region{{0, 0}, {3, 3}},
			"eof",
			true,
			[]Region{{0, vbufflen}},
		},
		{
			[]Region{{0, 0}, {vbufflen - 4, vbufflen - 4}},
			"eof",
			true,
			[]Region{{0, vbufflen}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}},
			"bol",
			false,
			[]Region{{24, 24}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}, {vbufflen - 3, vbufflen - 3}},
			"bol",
			false,
			[]Region{{24, 24}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}, {3, 3}},
			"bol",
			false,
			[]Region{{24, 24}, {0, 0}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}},
			"bol",
			true,
			[]Region{{vbufflen - 1, 24}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}, {vbufflen - 3, vbufflen - 3}},
			"bol",
			true,
			[]Region{{vbufflen - 1, 24}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}, {3, 3}},
			"bol",
			true,
			[]Region{{vbufflen - 1, 24}, {3, 0}},
		},
		{
			[]Region{{0, 0}},
			"eol",
			false,
			[]Region{{12, 12}},
		},
		{
			[]Region{{0, 0}, {3, 3}},
			"eol",
			false,
			[]Region{{12, 12}},
		},
		{
			[]Region{{0, 0}, {vbufflen - 4, vbufflen - 4}},
			"eol",
			false,
			[]Region{{12, 12}, {vbufflen - 1, vbufflen - 1}},
		},
		{
			[]Region{{0, 0}},
			"eol",
			true,
			[]Region{{0, 12}},
		},
		{
			[]Region{{0, 0}, {3, 3}},
			"eol",
			true,
			[]Region{{0, 12}},
		},
		{
			[]Region{{0, 0}, {vbufflen - 4, vbufflen - 4}},
			"eol",
			true,
			[]Region{{0, 12}, {vbufflen - 4, vbufflen - 1}},
		},
	}

	for i, test := range tests {
		v.Sel().Clear()
		for _, r := range test.in {
			v.Sel().Add(r)
		}
		args := Args{"to": test.to, "extend": test.extend}
		ed.CommandHandler().RunTextCommand(v, "move_to", args)
		if sr := v.Sel().Regions(); !reflect.DeepEqual(sr, test.exp) {
			t.Errorf("Test %d failed. Expected %v, but got %v: %+v", i, test.exp, sr, test)
		}
	}
}

type scfe struct {
	DummyFrontend
	show Region
}

func (f *scfe) VisibleRegion(v *View) Region {
	s := v.Buffer().Line(v.Buffer().TextPoint(3*3, 1))
	e := v.Buffer().Line(v.Buffer().TextPoint(6*3, 1))
	return Region{s.Begin(), e.End()}
}

func (f *scfe) Show(v *View, r Region) {
	f.show = r
}

func TestScrollLines(t *testing.T) {
	var fe scfe
	ed := GetEditor()
	ed.SetFrontend(&fe)
	ch := ed.CommandHandler()
	w := ed.NewWindow()
	v := w.NewFile()
	e := v.BeginEdit()
	for i := 0; i < 10; i++ {
		v.Insert(e, 0, "Hello World!\nTest123123\nAbrakadabra\n")
	}
	v.EndEdit(e)
	ch.RunTextCommand(v, "scroll_lines", Args{"amount": 0})

	if c := v.Buffer().Line(v.Buffer().TextPoint(3*3, 1)); fe.show.Begin() != c.Begin() {
		t.Errorf("Expected %v, but got %v", c, fe.show)
	}

	ch.RunTextCommand(v, "scroll_lines", Args{"amount": 1})
	if c := v.Buffer().Line(v.Buffer().TextPoint(3*3-1, 1)); fe.show.Begin() != c.Begin() {
		t.Errorf("Expected %v, but got %v", c, fe.show)
	}
	t.Log(fe.VisibleRegion(v), v.Buffer().Line(v.Buffer().TextPoint(6*3+1, 1)))
	ch.RunTextCommand(v, "scroll_lines", Args{"amount": -1})
	if c := v.Buffer().Line(v.Buffer().TextPoint(6*3+1, 1)); fe.show.Begin() != c.Begin() {
		t.Errorf("Expected %v, but got %v", c, fe.show)
	}
}
<commit_msg>Amend some tests so they pass.<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package commands

import (
	. "github.com/limetext/lime/backend"
	. "github.com/quarnster/util/text"
	"reflect"
	"testing"
)

func TestMove(t *testing.T) {
	ed := GetEditor()

	w := ed.NewWindow()
	v := w.NewFile()
	e := v.BeginEdit()
	v.Insert(e, 0, "Hello World!\nTest123123\nAbrakadabra\n")
	v.EndEdit(e)

	type Test struct {
		in      []Region
		by      string
		extend  bool
		forward bool
		exp     []Region
		args    Args
	}

	tests := []Test{
		{
			[]Region{{1, 1}, {3, 3}, {6, 6}},
			"characters",
			false,
			true,
			[]Region{{2, 2}, {4, 4}, {7, 7}},
			nil,
		},
		{
			[]Region{{1, 1}, {3, 3}, {6, 6}},
			"characters",
			false,
			false,
			[]Region{{0, 0}, {2, 2}, {5, 5}},
			nil,
		},
		{
			[]Region{{1, 1}, {3, 3}, {10, 6}},
			"characters",
			false,
			true,
			[]Region{{2, 2}, {4, 4}, {7, 7}},
			nil,
		},
		{
			[]Region{{1, 1}, {3, 3}, {10, 6}},
			"characters",
			false,
			false,
			[]Region{{0, 0}, {2, 2}, {5, 5}},
			nil,
		},
		{
			[]Region{{1, 1}, {3, 3}, {10, 6}},
			"characters",
			true,
			true,
			[]Region{{1, 2}, {3, 4}, {10, 7}},
			nil,
		},
		{
			[]Region{{1, 1}, {3, 3}, {10, 6}},
			"characters",
			true,
			false,
			[]Region{{1, 0}, {3, 2}, {10, 5}},
			nil,
		},
		{
			[]Region{{1, 3}, {3, 5}, {10, 7}},
			"characters",
			true,
			true,
			[]Region{{1, 6}, {10, 8}},
			nil,
		},
		{
			[]Region{{1, 1}},
			"stops",
			true,
			true,
			[]Region{{1, 5}},
			Args{"word_end": true},
		},
		{
			[]Region{{1, 1}},
			"stops",
			false,
			true,
			[]Region{{6, 6}},
			Args{"word_begin": true},
		},
		{
			[]Region{{6, 6}},
			"stops",
			false,
			false,
			[]Region{{0, 0}},
			Args{"word_begin": true},
		},
		{
			[]Region{{34, 34}},
			"lines",
			false,
			false,
			[]Region{{23, 23}},
			nil,
		},
		{
			[]Region{{23, 23}},
			"lines",
			false,
			false,
			[]Region{{10, 10}},
			nil,
		},
		{
			[]Region{{100, 100}},
			"lines",
			false,
			false,
			[]Region{{24, 24}},
			nil,
		},
	}
	for i, test := range tests {
		v.Sel().Clear()
		for _, r := range test.in {
			v.Sel().Add(r)
		}
		args := Args{"by": test.by, "extend": test.extend, "forward": test.forward}
		if test.args != nil {
			for k, v := range test.args {
				args[k] = v
			}
		}
		ed.CommandHandler().RunTextCommand(v, "move", args)
		if sr := v.Sel().Regions(); !reflect.DeepEqual(sr, test.exp) {
			t.Errorf("Test %d failed. Expected %v, but got %v: %+v", i, test.exp, sr, test)
		}
	}

	e = v.BeginEdit()
	v.Insert(e, v.Buffer().Size(), "abc")
	v.EndEdit(e)

	tests = []Test{
		{
			[]Region{{100, 100}},
			"lines",
			false,
			false,
			[]Region{{27, 27}},
			nil,
		},
	}
	for i, test := range tests {
		v.Sel().Clear()
		for _, r := range test.in {
			v.Sel().Add(r)
		}
		args := Args{"by": test.by, "extend": test.extend, "forward": test.forward}
		if test.args != nil {
			for k, v := range test.args {
				args[k] = v
			}
		}

		ed.CommandHandler().RunTextCommand(v, "move", args)
		if sr := v.Sel().Regions(); !reflect.DeepEqual(sr, test.exp) {
			t.Errorf("Test %d failed. Expected %v, but got %v: %+v", i, test.exp, sr, test)
		}
	}
}

func TestMoveTo(t *testing.T) {
	/*
	   Correct behavior of MoveTo:
	       - Moves each cursor directly to the indicated position.
	       - If extend, the selection will be extended in the direction of movement
	*/

	ed := GetEditor()
	w := ed.NewWindow()
	v := w.NewFile()
	e := v.BeginEdit()

	v.Insert(e, 0, "Hello World!\nTest123123\nAbrakadabra\n")
	v.EndEdit(e)

	type MoveToTest struct {
		in     []Region
		to     string
		extend bool
		exp    []Region
	}
	vbufflen := v.Buffer().Size()
	tests := []MoveToTest{
		{
			[]Region{{vbufflen, vbufflen}},
			"bof",
			false,
			[]Region{{0, 0}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}, {vbufflen - 2, vbufflen - 2}},
			"bof",
			false,
			[]Region{{0, 0}},
		},
		{
			[]Region{{vbufflen, vbufflen}, {3, 3}},
			"bof",
			false,
			[]Region{{0, 0}},
		},
		{
			[]Region{{vbufflen, vbufflen}},
			"bof",
			true,
			[]Region{{vbufflen, 0}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}, {vbufflen - 2, vbufflen - 2}},
			"bof",
			true,
			[]Region{{0, vbufflen - 1}},
			// FIXME: Directionality should be preserved.
			// []Region{{vbufflen - 1, 0}},
		},
		{
			[]Region{{vbufflen, vbufflen}, {3, 3}},
			"bof",
			true,
			[]Region{{0, vbufflen}},
			// FIXME: Directionality should be preserved.
			// []Region{{vbufflen, 0}},
		},
		{
			[]Region{{0, 0}},
			"eof",
			false,
			[]Region{{vbufflen, vbufflen}},
		},
		{
			[]Region{{0, 0}, {3, 3}},
			"eof",
			false,
			[]Region{{vbufflen, vbufflen}},
		},
		{
			[]Region{{0, 0}, {vbufflen - 4, vbufflen - 4}},
			"eof",
			false,
			[]Region{{vbufflen, vbufflen}},
		},
		{
			[]Region{{0, 0}},
			"eof",
			true,
			[]Region{{0, vbufflen}},
		},
		{
			[]Region{{0, 0}, {3, 3}},
			"eof",
			true,
			[]Region{{0, vbufflen}},
		},
		{
			[]Region{{0, 0}, {vbufflen - 4, vbufflen - 4}},
			"eof",
			true,
			[]Region{{0, vbufflen}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}},
			"bol",
			false,
			[]Region{{24, 24}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}, {vbufflen - 3, vbufflen - 3}},
			"bol",
			false,
			[]Region{{24, 24}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}, {3, 3}},
			"bol",
			false,
			[]Region{{0, 0}, {24, 24}},
			// FIXME: Region order should be preserved.
			// []Region{{24, 24}, {0, 0}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}},
			"bol",
			true,
			[]Region{{vbufflen - 1, 24}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}, {vbufflen - 3, vbufflen - 3}},
			"bol",
			true,
			[]Region{{24, vbufflen - 1}},
			// FIXME: Directionality should be preserved.
			// []Region{{vbufflen - 1, 24}},
		},
		{
			[]Region{{vbufflen - 1, vbufflen - 1}, {3, 3}},
			"bol",
			true,
			[]Region{{3, 0}, {vbufflen - 1, 24}},
			// FIXME: Region order should be preserved.
			// []Region{{vbufflen - 1, 24}, {3, 0}},
		},
		{
			[]Region{{0, 0}},
			"eol",
			false,
			[]Region{{12, 12}},
		},
		{
			[]Region{{0, 0}, {3, 3}},
			"eol",
			false,
			[]Region{{12, 12}},
		},
		{
			[]Region{{0, 0}, {vbufflen - 4, vbufflen - 4}},
			"eol",
			false,
			[]Region{{12, 12}, {vbufflen - 1, vbufflen - 1}},
		},
		{
			[]Region{{0, 0}},
			"eol",
			true,
			[]Region{{0, 12}},
		},
		{
			[]Region{{0, 0}, {3, 3}},
			"eol",
			true,
			[]Region{{0, 12}},
		},
		{
			[]Region{{0, 0}, {vbufflen - 4, vbufflen - 4}},
			"eol",
			true,
			[]Region{{0, 12}, {vbufflen - 4, vbufflen - 1}},
		},
	}

	for i, test := range tests {
		v.Sel().Clear()
		for _, r := range test.in {
			v.Sel().Add(r)
		}
		args := Args{"to": test.to, "extend": test.extend}
		ed.CommandHandler().RunTextCommand(v, "move_to", args)
		if sr := v.Sel().Regions(); !reflect.DeepEqual(sr, test.exp) {
			t.Errorf("Test %d failed. Expected %v, but got %v: %+v", i, test.exp, sr, test)
		}
	}
}

type scfe struct {
	DummyFrontend
	show Region
}

func (f *scfe) VisibleRegion(v *View) Region {
	s := v.Buffer().Line(v.Buffer().TextPoint(3*3, 1))
	e := v.Buffer().Line(v.Buffer().TextPoint(6*3, 1))
	return Region{s.Begin(), e.End()}
}

func (f *scfe) Show(v *View, r Region) {
	f.show = r
}

func TestScrollLines(t *testing.T) {
	var fe scfe
	ed := GetEditor()
	ed.SetFrontend(&fe)
	ch := ed.CommandHandler()
	w := ed.NewWindow()
	v := w.NewFile()
	e := v.BeginEdit()
	for i := 0; i < 10; i++ {
		v.Insert(e, 0, "Hello World!\nTest123123\nAbrakadabra\n")
	}
	v.EndEdit(e)
	ch.RunTextCommand(v, "scroll_lines", Args{"amount": 0})

	if c := v.Buffer().Line(v.Buffer().TextPoint(3*3, 1)); fe.show.Begin() != c.Begin() {
		t.Errorf("Expected %v, but got %v", c, fe.show)
	}

	ch.RunTextCommand(v, "scroll_lines", Args{"amount": 1})
	if c := v.Buffer().Line(v.Buffer().TextPoint(3*3-1, 1)); fe.show.Begin() != c.Begin() {
		t.Errorf("Expected %v, but got %v", c, fe.show)
	}
	t.Log(fe.VisibleRegion(v), v.Buffer().Line(v.Buffer().TextPoint(6*3+1, 1)))
	ch.RunTextCommand(v, "scroll_lines", Args{"amount": -1})
	if c := v.Buffer().Line(v.Buffer().TextPoint(6*3+1, 1)); fe.show.Begin() != c.Begin() {
		t.Errorf("Expected %v, but got %v", c, fe.show)
	}
}
<|endoftext|>


<commit_before>package reviewdog

import (
	"path/filepath"
	"strings"

	"github.com/haya14busa/reviewdog/diff"
)

type FilteredCheck struct {
	*CheckResult
	InDiff   bool
	LnumDiff int
}

func FilterCheck(results []*CheckResult, diff []*diff.FileDiff, strip int, wd string) []*FilteredCheck {
	checks := make([]*FilteredCheck, 0, len(results))

	addedlines := addedDiffLines(diff, strip)

	for _, result := range results {
		check := &FilteredCheck{CheckResult: result}

		addedline := addedlines.Get(result.Path, result.Lnum)
		if filepath.IsAbs(result.Path) && wd != "" {
			result.Path, _ = filepath.Rel(wd, result.Path)
		}
		result.Path = filepath.Clean(result.Path)
		if addedline != nil {
			check.InDiff = true
			check.LnumDiff = addedline.LnumDiff
		}

		checks = append(checks, check)
	}

	return checks
}

// addedLine represents added line in diff.
type addedLine struct {
	Path     string // path to new file
	Lnum     int    // the line number in the new file
	LnumDiff int    // the line number of the diff (Same as Lnumdiff of diff.Line)
	Content  string // line content
}

// posToAddedLine is a hash table of normalized path to line number to addedLine.
type posToAddedLine map[string]map[int]*addedLine

func (p posToAddedLine) Get(path string, lnum int) *addedLine {
	npath, err := normalizePath(path)
	if err != nil {
		return nil
	}
	ltodiff, ok := p[npath]
	if !ok {
		return nil
	}
	diffline, ok := ltodiff[lnum]
	if !ok {
		return nil
	}
	return diffline
}

// addedDiffLines traverse []*diff.FileDiff and returns posToAddedLine.
func addedDiffLines(filediffs []*diff.FileDiff, strip int) posToAddedLine {
	r := make(posToAddedLine)
	for _, filediff := range filediffs {
		path := filediff.PathNew
		ltodiff := make(map[int]*addedLine)
		if strip > 0 {
			ps := strings.Split(filepath.ToSlash(filediff.PathNew), "/")
			if len(ps) > strip {
				path = filepath.Join(ps[strip:]...)
			}
		}
		np, err := normalizePath(path)
		if err != nil {
			// FIXME(haya14busa): log or return error?
			continue
		}
		path = np

		for _, hunk := range filediff.Hunks {
			for _, line := range hunk.Lines {
				if line.Type == diff.LineAdded {
					ltodiff[line.LnumNew] = &addedLine{
						Path:     path,
						Lnum:     line.LnumNew,
						LnumDiff: line.LnumDiff,
						Content:  line.Content,
					}
				}
			}
		}
		r[path] = ltodiff
	}
	return r
}

func normalizePath(p string) (string, error) {
	if !filepath.IsAbs(p) {
		path, err := filepath.Abs(p)
		if err != nil {
			return "", err
		}
		p = path
	}
	return filepath.ToSlash(p), nil
}
<commit_msg>fix lint warnings<commit_after>package reviewdog

import (
	"path/filepath"
	"strings"

	"github.com/haya14busa/reviewdog/diff"
)

// FilteredCheck represents CheckResult with filtering info.
type FilteredCheck struct {
	*CheckResult
	InDiff   bool
	LnumDiff int
}

// FilterCheck filters check results by diff. It doesn't not drop check which
// is not in diff but set FilteredCheck.InDiff field false.
func FilterCheck(results []*CheckResult, diff []*diff.FileDiff, strip int, wd string) []*FilteredCheck {
	checks := make([]*FilteredCheck, 0, len(results))

	addedlines := addedDiffLines(diff, strip)

	for _, result := range results {
		check := &FilteredCheck{CheckResult: result}

		addedline := addedlines.Get(result.Path, result.Lnum)
		if filepath.IsAbs(result.Path) && wd != "" {
			relPath, err := filepath.Rel(wd, result.Path)
			if err == nil {
				result.Path = relPath
			}
		}
		result.Path = filepath.Clean(result.Path)
		if addedline != nil {
			check.InDiff = true
			check.LnumDiff = addedline.LnumDiff
		}

		checks = append(checks, check)
	}

	return checks
}

// addedLine represents added line in diff.
type addedLine struct {
	Path     string // path to new file
	Lnum     int    // the line number in the new file
	LnumDiff int    // the line number of the diff (Same as Lnumdiff of diff.Line)
	Content  string // line content
}

// posToAddedLine is a hash table of normalized path to line number to addedLine.
type posToAddedLine map[string]map[int]*addedLine

func (p posToAddedLine) Get(path string, lnum int) *addedLine {
	npath, err := normalizePath(path)
	if err != nil {
		return nil
	}
	ltodiff, ok := p[npath]
	if !ok {
		return nil
	}
	diffline, ok := ltodiff[lnum]
	if !ok {
		return nil
	}
	return diffline
}

// addedDiffLines traverse []*diff.FileDiff and returns posToAddedLine.
func addedDiffLines(filediffs []*diff.FileDiff, strip int) posToAddedLine {
	r := make(posToAddedLine)
	for _, filediff := range filediffs {
		path := filediff.PathNew
		ltodiff := make(map[int]*addedLine)
		if strip > 0 {
			ps := strings.Split(filepath.ToSlash(filediff.PathNew), "/")
			if len(ps) > strip {
				path = filepath.Join(ps[strip:]...)
			}
		}
		np, err := normalizePath(path)
		if err != nil {
			// FIXME(haya14busa): log or return error?
			continue
		}
		path = np

		for _, hunk := range filediff.Hunks {
			for _, line := range hunk.Lines {
				if line.Type == diff.LineAdded {
					ltodiff[line.LnumNew] = &addedLine{
						Path:     path,
						Lnum:     line.LnumNew,
						LnumDiff: line.LnumDiff,
						Content:  line.Content,
					}
				}
			}
		}
		r[path] = ltodiff
	}
	return r
}

func normalizePath(p string) (string, error) {
	if !filepath.IsAbs(p) {
		path, err := filepath.Abs(p)
		if err != nil {
			return "", err
		}
		p = path
	}
	return filepath.ToSlash(p), nil
}
<|endoftext|>


<commit_before>package peco

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"os/exec"
	"regexp"
	"strings"
	"unicode"
)

// These are used as keys in the config file
const (
	IgnoreCaseMatch    = "IgnoreCase"
	CaseSensitiveMatch = "CaseSensitive"
	SmartCaseMatch     = "SmartCase"
	RegexpMatch        = "Regexp"
)

var ignoreCaseFlags = []string{"i"}
var defaultFlags = []string{}

type regexpFlags interface {
	flags(string) []string
}
type regexpFlagList []string

func (r regexpFlagList) flags(_ string) []string {
	return []string(r)
}

type regexpFlagFunc func(string) []string

func (r regexpFlagFunc) flags(s string) []string {
	return r(s)
}

func containsUpper(query string) bool {
	for _, c := range query {
		if unicode.IsUpper(c) {
			return true
		}
	}
	return false
}

func regexpFor(q string, flags []string, quotemeta bool) (*regexp.Regexp, error) {
	reTxt := q
	if quotemeta {
		reTxt = regexp.QuoteMeta(q)
	}

	if flags != nil && len(flags) > 0 {
		reTxt = fmt.Sprintf("(?%s)%s", strings.Join(flags, ""), reTxt)
	}

	re, err := regexp.Compile(reTxt)
	if err != nil {
		return nil, err
	}
	return re, nil
}

func queryToRegexps(flags regexpFlags, quotemeta bool, query string) ([]*regexp.Regexp, error) {
	queries := strings.Split(strings.TrimSpace(query), " ")
	regexps := make([]*regexp.Regexp, 0)

	for _, q := range queries {
		re, err := regexpFor(q, flags.flags(query), quotemeta)
		if err != nil {
			return nil, err
		}
		regexps = append(regexps, re)
	}

	return regexps, nil
}

// Filter is responsible for the actual "grep" part of peco
type Filter struct {
	*Ctx
}

// Work is the actual work horse that that does the matching
// in a goroutine of its own. It wraps Matcher.Match().
func (f *Filter) Work(cancel chan struct{}, q HubReq) {
	tracer.Printf("Filter.Work: START\n")
	defer tracer.Printf("Filter.Work: END\n")
	defer q.Done()

	query := q.DataString()
	if query == "" {
		tracer.Printf("Filter.Work: Resetting activingLineBuffer")
		f.ResetActiveLineBuffer()
	} else {
		f.rawLineBuffer.cancelCh = cancel
		f.rawLineBuffer.Replay()

		filter := f.Filter().Clone()
		filter.SetQuery(query)
		tracer.Printf("Running %#v filter using query '%s'", filter, query)

		filter.Accept(f.rawLineBuffer)
		buf := NewRawLineBuffer()
		buf.onEnd = func() { f.SendStatusMsg("") }
		buf.Accept(filter)

		f.SetActiveLineBuffer(buf)
	}

	f.SelectionClear()
}

// Loop keeps watching for incoming queries, and upon receiving
// a query, spawns a goroutine to do the heavy work. It also
// checks for previously running queries, so we can avoid
// running many goroutines doing the grep at the same time
func (f *Filter) Loop() {
	defer f.ReleaseWaitGroup()

	// previous holds a channel that can cancel the previous
	// query. This is used when multiple queries come in succession
	// and the previous query is discarded anyway
	var previous chan struct{}
	for {
		select {
		case <-f.LoopCh():
			return
		case q := <-f.QueryCh():
			if previous != nil {
				// Tell the previous query to stop
				previous <- struct{}{}
			}
			previous = make(chan struct{}, 1)

			f.SendStatusMsg("Running query...")
			go f.Work(previous, q)
		}
	}
}

type QueryFilterer interface {
	Pipeliner
	Cancel()
	Clone() QueryFilterer
	Accept(Pipeliner)
	SetQuery(string)
	String() string
}

type SelectionFilter struct {
	sel *Selection
}

func (sf SelectionFilter) Name() string {
	return "SelectionFilter"
}

type RegexpFilter struct {
	simplePipeline
	compiledQuery []*regexp.Regexp
	flags         regexpFlags
	quotemeta     bool
	query         string
	name          string
	onEnd         func()
}

func NewRegexpFilter() *RegexpFilter {
	return &RegexpFilter{}
}

func (rf RegexpFilter) Clone() QueryFilterer {
	return &RegexpFilter{
		simplePipeline{},
		nil,
		rf.flags,
		rf.quotemeta,
		rf.query,
		rf.name,
		nil,
	}
}

func (rf *RegexpFilter) Accept(p Pipeliner) {
	cancelCh, incomingCh := p.Pipeline()
	rf.cancelCh = cancelCh
	rf.outputCh = make(chan Line)
	go acceptPipeline(cancelCh, incomingCh, rf.outputCh,
		&pipelineCtx{rf.filter, rf.onEnd})
}

var ErrFilterDidNotMatch = errors.New("error: filter did not match against given line")

func (rf *RegexpFilter) filter(l Line) (Line, error) {
	tracer.Printf("RegexpFilter.filter: START")
	defer tracer.Printf("RegexpFilter.filter: END")
	regexps, err := rf.getQueryAsRegexps()
	if err != nil {
		return nil, err
	}
	v := l.DisplayString()
	allMatched := true
	matches := [][]int{}
TryRegexps:
	for _, rx := range regexps {
		tracer.Printf("RegexpFilter.filter: matching '%s' against '%s'", v, rx)
		match := rx.FindAllStringSubmatchIndex(v, -1)
		if match == nil {
			allMatched = false
			break TryRegexps
		}
		matches = append(matches, match...)
	}

	if !allMatched {
		return nil, ErrFilterDidNotMatch
	}

	tracer.Printf("RegexpFilter.filter: line matched pattern\n")
	return NewMatchedLine(l, matches), nil
}

func (rf *RegexpFilter) getQueryAsRegexps() ([]*regexp.Regexp, error) {
	if q := rf.compiledQuery; q != nil {
		return q, nil
	}
	q, err := queryToRegexps(rf.flags, rf.quotemeta, rf.query)
	if err != nil {
		return nil, err
	}

	rf.compiledQuery = q
	return q, nil
}

func (rf *RegexpFilter) SetQuery(q string) {
	rf.query = q
	rf.compiledQuery = nil
}

func (rf RegexpFilter) String() string {
	return rf.name
}

type FilterSet struct {
	filters []QueryFilterer
	current int
}

func (fs *FilterSet) Size() int {
	return len(fs.filters)
}

func (fs *FilterSet) Add(qf QueryFilterer) error {
	fs.filters = append(fs.filters, qf)
	return nil
}

func (fs *FilterSet) Rotate() {
	fs.current++
	if fs.current >= len(fs.filters) {
		fs.current = 0
	}
	tracer.Printf("FilterSet.Rotate: now filter in effect is %s", fs.filters[fs.current])
}

var ErrFilterNotFound = errors.New("specified filter was not found")

func (fs *FilterSet) SetCurrentByName(name string) error {
	for i, f := range fs.filters {
		if f.String() == name {
			fs.current = i
			return nil
		}
	}
	return ErrFilterNotFound
}

func (fs *FilterSet) GetCurrent() QueryFilterer {
	return fs.filters[fs.current]
}

func NewIgnoreCaseFilter() *RegexpFilter {
	return &RegexpFilter{
		flags:     regexpFlagList(ignoreCaseFlags),
		quotemeta: true,
		name:      "IgnoreCase",
	}
}

func NewCaseSensitiveFilter() *RegexpFilter {
	return &RegexpFilter{
		flags:     regexpFlagList(defaultFlags),
		quotemeta: true,
		name:      "CaseSensitive",
	}
}

// SmartCaseFilter turns ON the ignore-case flag in the regexp
// if the query contains a upper-case character
func NewSmartCaseFilter() *RegexpFilter {
	return &RegexpFilter{
		flags: regexpFlagFunc(func(q string) []string {
			if containsUpper(q) {
				return defaultFlags
			}
			return []string{"i"}
		}),
		quotemeta: true,
		name:      "SmartCase",
	}
}

type ExternalCmdFilter struct {
	simplePipeline
	enableSep       bool
	cmd             string
	args            []string
	name            string
	query           string
	thresholdBufsiz int
}

func NewExternalCmdFilter(name, cmd string, args []string, threshold int, enableSep bool) *ExternalCmdFilter {
	tracer.Printf("name = %s, cmd = %s, args = %#v", name, cmd, args)
	return &ExternalCmdFilter{
		simplePipeline:  simplePipeline{},
		enableSep:       enableSep,
		cmd:             cmd,
		args:            args,
		name:            name,
		thresholdBufsiz: threshold,
	}
}

func (ecf ExternalCmdFilter) Clone() QueryFilterer {
	return &ExternalCmdFilter{
		simplePipeline:  simplePipeline{},
		enableSep:       ecf.enableSep,
		cmd:             ecf.cmd,
		args:            ecf.args,
		name:            ecf.name,
		thresholdBufsiz: ecf.thresholdBufsiz,
	}
}

func (ecf *ExternalCmdFilter) Verify() error {
	if ecf.cmd == "" {
		return fmt.Errorf("no executable specified for custom matcher '%s'", ecf.name)
	}

	if _, err := exec.LookPath(ecf.cmd); err != nil {
		return err
	}
	return nil
}

func (ecf *ExternalCmdFilter) Accept(p Pipeliner) {
	cancelCh, incomingCh := p.Pipeline()
	outputCh := make(chan Line)
	ecf.cancelCh = cancelCh
	ecf.outputCh = outputCh

	go func() {
		defer close(outputCh)

		defer tracer.Printf("ExternalCmdFilter.Accept: DONE")

		// for every N lines, execute the external command
		buf := []Line{}
		for l := range incomingCh {
			buf = append(buf, l)
			if len(buf) < ecf.thresholdBufsiz {
				continue
			}

			ecf.launchExternalCmd(buf, cancelCh, outputCh)
			buf = []Line{} // drain
		}

		if len(buf) > 0 {
			ecf.launchExternalCmd(buf, cancelCh, outputCh)
		}
	}()
}

func (ecf *ExternalCmdFilter) SetQuery(q string) {
	ecf.query = q
}

func (ecf ExternalCmdFilter) String() string {
	return ecf.name
}

func (ecf *ExternalCmdFilter) launchExternalCmd(buf []Line, cancelCh chan struct{}, outputCh chan Line) {
	defer func() { recover() }() // ignore errors

	tracer.Printf("ExternalCmdFilter.launchExternalCmd: START")
	defer tracer.Printf("ExternalCmdFilter.launchExternalCmd: END")

	tracer.Printf("buf = %v", buf)

	args := append([]string{ecf.query}, ecf.args...)
	cmd := exec.Command(ecf.cmd, args...)

	inbuf := &bytes.Buffer{}
	for _, l := range buf {
		inbuf.WriteString(l.DisplayString() + "\n")
	}

	cmd.Stdin = inbuf
	r, err := cmd.StdoutPipe()
	if err != nil {
		return
	}

	tracer.Printf("cmd = %#v", cmd)
	err = cmd.Start()
	if err != nil {
		return
	}

	go cmd.Wait()

	cmdCh := make(chan Line)
	go func(cmdCh chan Line, rdr *bufio.Reader) {
		defer tracer.Printf("Done reader")
		defer func() { recover() }()
		defer close(cmdCh)
		for {
			tracer.Printf("ReadLine")
			b, _, err := rdr.ReadLine()
			if len(b) > 0 {
				// TODO: need to redo the spec for custom matchers
				// This is the ONLY location where we need to actually
				// RECREATE a RawLine, and thus the only place where
				// ctx.enableSep is required.
				cmdCh <- NewMatchedLine(NewRawLine(string(b), ecf.enableSep), nil)
			}
			if err != nil {
				break
			}
		}
	}(cmdCh, bufio.NewReader(r))

	defer func() {
		if p := cmd.Process; p != nil {
			p.Kill()
		}
	}()

	defer tracer.Printf("Done waiting for cancel or line")

	for {
		select {
		case <-cancelCh:
			return
		case l, ok := <-cmdCh:
			if l == nil || !ok {
				return
			}
			tracer.Printf("Custom: l = %s", l.DisplayString())
			outputCh <- l
		}
	}
}
<commit_msg>Re-instate the missing order/dedupe matches bit<commit_after>package peco

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"os/exec"
	"regexp"
	"sort"
	"strings"
	"unicode"
)

// These are used as keys in the config file
const (
	IgnoreCaseMatch    = "IgnoreCase"
	CaseSensitiveMatch = "CaseSensitive"
	SmartCaseMatch     = "SmartCase"
	RegexpMatch        = "Regexp"
)

var ignoreCaseFlags = []string{"i"}
var defaultFlags = []string{}

type regexpFlags interface {
	flags(string) []string
}
type regexpFlagList []string

func (r regexpFlagList) flags(_ string) []string {
	return []string(r)
}

type regexpFlagFunc func(string) []string

func (r regexpFlagFunc) flags(s string) []string {
	return r(s)
}

func containsUpper(query string) bool {
	for _, c := range query {
		if unicode.IsUpper(c) {
			return true
		}
	}
	return false
}

func regexpFor(q string, flags []string, quotemeta bool) (*regexp.Regexp, error) {
	reTxt := q
	if quotemeta {
		reTxt = regexp.QuoteMeta(q)
	}

	if flags != nil && len(flags) > 0 {
		reTxt = fmt.Sprintf("(?%s)%s", strings.Join(flags, ""), reTxt)
	}

	re, err := regexp.Compile(reTxt)
	if err != nil {
		return nil, err
	}
	return re, nil
}

func queryToRegexps(flags regexpFlags, quotemeta bool, query string) ([]*regexp.Regexp, error) {
	queries := strings.Split(strings.TrimSpace(query), " ")
	regexps := make([]*regexp.Regexp, 0)

	for _, q := range queries {
		re, err := regexpFor(q, flags.flags(query), quotemeta)
		if err != nil {
			return nil, err
		}
		regexps = append(regexps, re)
	}

	return regexps, nil
}

// sort related stuff
type byMatchStart [][]int

func (m byMatchStart) Len() int {
	return len(m)
}

func (m byMatchStart) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

func (m byMatchStart) Less(i, j int) bool {
	if m[i][0] < m[j][0] {
		return true
	}

	if m[i][0] == m[j][0] {
		return m[i][1]-m[i][0] < m[i][1]-m[i][0]
	}

	return false
}
func matchContains(a []int, b []int) bool {
	return a[0] <= b[0] && a[1] >= b[1]
}

func matchOverlaps(a []int, b []int) bool {
	return a[0] <= b[0] && a[1] >= b[0] ||
		a[0] <= b[1] && a[1] >= b[1]
}

func mergeMatches(a []int, b []int) []int {
	ret := make([]int, 2)

	// Note: In practice this should never happen
	// because we're sorting by N[0] before calling
	// this routine, but for completeness' sake...
	if a[0] < b[0] {
		ret[0] = a[0]
	} else {
		ret[0] = b[0]
	}

	if a[1] < b[1] {
		ret[1] = b[1]
	} else {
		ret[1] = a[1]
	}
	return ret
}

// Filter is responsible for the actual "grep" part of peco
type Filter struct {
	*Ctx
}

// Work is the actual work horse that that does the matching
// in a goroutine of its own. It wraps Matcher.Match().
func (f *Filter) Work(cancel chan struct{}, q HubReq) {
	tracer.Printf("Filter.Work: START\n")
	defer tracer.Printf("Filter.Work: END\n")
	defer q.Done()

	query := q.DataString()
	if query == "" {
		tracer.Printf("Filter.Work: Resetting activingLineBuffer")
		f.ResetActiveLineBuffer()
	} else {
		f.rawLineBuffer.cancelCh = cancel
		f.rawLineBuffer.Replay()

		filter := f.Filter().Clone()
		filter.SetQuery(query)
		tracer.Printf("Running %#v filter using query '%s'", filter, query)

		filter.Accept(f.rawLineBuffer)
		buf := NewRawLineBuffer()
		buf.onEnd = func() { f.SendStatusMsg("") }
		buf.Accept(filter)

		f.SetActiveLineBuffer(buf)
	}

	f.SelectionClear()
}

// Loop keeps watching for incoming queries, and upon receiving
// a query, spawns a goroutine to do the heavy work. It also
// checks for previously running queries, so we can avoid
// running many goroutines doing the grep at the same time
func (f *Filter) Loop() {
	defer f.ReleaseWaitGroup()

	// previous holds a channel that can cancel the previous
	// query. This is used when multiple queries come in succession
	// and the previous query is discarded anyway
	var previous chan struct{}
	for {
		select {
		case <-f.LoopCh():
			return
		case q := <-f.QueryCh():
			if previous != nil {
				// Tell the previous query to stop
				previous <- struct{}{}
			}
			previous = make(chan struct{}, 1)

			f.SendStatusMsg("Running query...")
			go f.Work(previous, q)
		}
	}
}

type QueryFilterer interface {
	Pipeliner
	Cancel()
	Clone() QueryFilterer
	Accept(Pipeliner)
	SetQuery(string)
	String() string
}

type SelectionFilter struct {
	sel *Selection
}

func (sf SelectionFilter) Name() string {
	return "SelectionFilter"
}

type RegexpFilter struct {
	simplePipeline
	compiledQuery []*regexp.Regexp
	flags         regexpFlags
	quotemeta     bool
	query         string
	name          string
	onEnd         func()
}

func NewRegexpFilter() *RegexpFilter {
	return &RegexpFilter{}
}

func (rf RegexpFilter) Clone() QueryFilterer {
	return &RegexpFilter{
		simplePipeline{},
		nil,
		rf.flags,
		rf.quotemeta,
		rf.query,
		rf.name,
		nil,
	}
}

func (rf *RegexpFilter) Accept(p Pipeliner) {
	cancelCh, incomingCh := p.Pipeline()
	rf.cancelCh = cancelCh
	rf.outputCh = make(chan Line)
	go acceptPipeline(cancelCh, incomingCh, rf.outputCh,
		&pipelineCtx{rf.filter, rf.onEnd})
}

var ErrFilterDidNotMatch = errors.New("error: filter did not match against given line")

func (rf *RegexpFilter) filter(l Line) (Line, error) {
	tracer.Printf("RegexpFilter.filter: START")
	defer tracer.Printf("RegexpFilter.filter: END")
	regexps, err := rf.getQueryAsRegexps()
	if err != nil {
		return nil, err
	}
	v := l.DisplayString()
	allMatched := true
	matches := [][]int{}
TryRegexps:
	for _, rx := range regexps {
		tracer.Printf("RegexpFilter.filter: matching '%s' against '%s'", v, rx)
		match := rx.FindAllStringSubmatchIndex(v, -1)
		if match == nil {
			allMatched = false
			break TryRegexps
		}
		matches = append(matches, match...)
	}

	if !allMatched {
		return nil, ErrFilterDidNotMatch
	}

	tracer.Printf("RegexpFilter.filter: line matched pattern\n")
	sort.Sort(byMatchStart(matches))

	// We need to "dedupe" the results. For example, if we matched the
	// same region twice, we don't want that to be drawn

	deduped := make([][]int, 0, len(matches))

	for i, m := range matches {
		// Always push the first one
		if i == 0 {
			deduped = append(deduped, m)
			continue
		}

		prev := deduped[len(deduped)-1]
		switch {
		case matchContains(prev, m):
			// If the previous match contains this one, then
			// don't do anything
			continue
		case matchOverlaps(prev, m):
			// If the previous match overlaps with this one,
			// merge the results and make it a bigger one
			deduped[len(deduped)-1] = mergeMatches(prev, m)
		default:
			deduped = append(deduped, m)
		}
	}
	return NewMatchedLine(l, matches), nil
}

func (rf *RegexpFilter) getQueryAsRegexps() ([]*regexp.Regexp, error) {
	if q := rf.compiledQuery; q != nil {
		return q, nil
	}
	q, err := queryToRegexps(rf.flags, rf.quotemeta, rf.query)
	if err != nil {
		return nil, err
	}

	rf.compiledQuery = q
	return q, nil
}

func (rf *RegexpFilter) SetQuery(q string) {
	rf.query = q
	rf.compiledQuery = nil
}

func (rf RegexpFilter) String() string {
	return rf.name
}

type FilterSet struct {
	filters []QueryFilterer
	current int
}

func (fs *FilterSet) Size() int {
	return len(fs.filters)
}

func (fs *FilterSet) Add(qf QueryFilterer) error {
	fs.filters = append(fs.filters, qf)
	return nil
}

func (fs *FilterSet) Rotate() {
	fs.current++
	if fs.current >= len(fs.filters) {
		fs.current = 0
	}
	tracer.Printf("FilterSet.Rotate: now filter in effect is %s", fs.filters[fs.current])
}

var ErrFilterNotFound = errors.New("specified filter was not found")

func (fs *FilterSet) SetCurrentByName(name string) error {
	for i, f := range fs.filters {
		if f.String() == name {
			fs.current = i
			return nil
		}
	}
	return ErrFilterNotFound
}

func (fs *FilterSet) GetCurrent() QueryFilterer {
	return fs.filters[fs.current]
}

func NewIgnoreCaseFilter() *RegexpFilter {
	return &RegexpFilter{
		flags:     regexpFlagList(ignoreCaseFlags),
		quotemeta: true,
		name:      "IgnoreCase",
	}
}

func NewCaseSensitiveFilter() *RegexpFilter {
	return &RegexpFilter{
		flags:     regexpFlagList(defaultFlags),
		quotemeta: true,
		name:      "CaseSensitive",
	}
}

// SmartCaseFilter turns ON the ignore-case flag in the regexp
// if the query contains a upper-case character
func NewSmartCaseFilter() *RegexpFilter {
	return &RegexpFilter{
		flags: regexpFlagFunc(func(q string) []string {
			if containsUpper(q) {
				return defaultFlags
			}
			return []string{"i"}
		}),
		quotemeta: true,
		name:      "SmartCase",
	}
}

type ExternalCmdFilter struct {
	simplePipeline
	enableSep       bool
	cmd             string
	args            []string
	name            string
	query           string
	thresholdBufsiz int
}

func NewExternalCmdFilter(name, cmd string, args []string, threshold int, enableSep bool) *ExternalCmdFilter {
	tracer.Printf("name = %s, cmd = %s, args = %#v", name, cmd, args)
	return &ExternalCmdFilter{
		simplePipeline:  simplePipeline{},
		enableSep:       enableSep,
		cmd:             cmd,
		args:            args,
		name:            name,
		thresholdBufsiz: threshold,
	}
}

func (ecf ExternalCmdFilter) Clone() QueryFilterer {
	return &ExternalCmdFilter{
		simplePipeline:  simplePipeline{},
		enableSep:       ecf.enableSep,
		cmd:             ecf.cmd,
		args:            ecf.args,
		name:            ecf.name,
		thresholdBufsiz: ecf.thresholdBufsiz,
	}
}

func (ecf *ExternalCmdFilter) Verify() error {
	if ecf.cmd == "" {
		return fmt.Errorf("no executable specified for custom matcher '%s'", ecf.name)
	}

	if _, err := exec.LookPath(ecf.cmd); err != nil {
		return err
	}
	return nil
}

func (ecf *ExternalCmdFilter) Accept(p Pipeliner) {
	cancelCh, incomingCh := p.Pipeline()
	outputCh := make(chan Line)
	ecf.cancelCh = cancelCh
	ecf.outputCh = outputCh

	go func() {
		defer close(outputCh)

		defer tracer.Printf("ExternalCmdFilter.Accept: DONE")

		// for every N lines, execute the external command
		buf := []Line{}
		for l := range incomingCh {
			buf = append(buf, l)
			if len(buf) < ecf.thresholdBufsiz {
				continue
			}

			ecf.launchExternalCmd(buf, cancelCh, outputCh)
			buf = []Line{} // drain
		}

		if len(buf) > 0 {
			ecf.launchExternalCmd(buf, cancelCh, outputCh)
		}
	}()
}

func (ecf *ExternalCmdFilter) SetQuery(q string) {
	ecf.query = q
}

func (ecf ExternalCmdFilter) String() string {
	return ecf.name
}

func (ecf *ExternalCmdFilter) launchExternalCmd(buf []Line, cancelCh chan struct{}, outputCh chan Line) {
	defer func() { recover() }() // ignore errors

	tracer.Printf("ExternalCmdFilter.launchExternalCmd: START")
	defer tracer.Printf("ExternalCmdFilter.launchExternalCmd: END")

	tracer.Printf("buf = %v", buf)

	args := append([]string{ecf.query}, ecf.args...)
	cmd := exec.Command(ecf.cmd, args...)

	inbuf := &bytes.Buffer{}
	for _, l := range buf {
		inbuf.WriteString(l.DisplayString() + "\n")
	}

	cmd.Stdin = inbuf
	r, err := cmd.StdoutPipe()
	if err != nil {
		return
	}

	tracer.Printf("cmd = %#v", cmd)
	err = cmd.Start()
	if err != nil {
		return
	}

	go cmd.Wait()

	cmdCh := make(chan Line)
	go func(cmdCh chan Line, rdr *bufio.Reader) {
		defer tracer.Printf("Done reader")
		defer func() { recover() }()
		defer close(cmdCh)
		for {
			tracer.Printf("ReadLine")
			b, _, err := rdr.ReadLine()
			if len(b) > 0 {
				// TODO: need to redo the spec for custom matchers
				// This is the ONLY location where we need to actually
				// RECREATE a RawLine, and thus the only place where
				// ctx.enableSep is required.
				cmdCh <- NewMatchedLine(NewRawLine(string(b), ecf.enableSep), nil)
			}
			if err != nil {
				break
			}
		}
	}(cmdCh, bufio.NewReader(r))

	defer func() {
		if p := cmd.Process; p != nil {
			p.Kill()
		}
	}()

	defer tracer.Printf("Done waiting for cancel or line")

	for {
		select {
		case <-cancelCh:
			return
		case l, ok := <-cmdCh:
			if l == nil || !ok {
				return
			}
			tracer.Printf("Custom: l = %s", l.DisplayString())
			outputCh <- l
		}
	}
}
<|endoftext|>


<commit_before>package funcs

import (
	"github.com/gaochao1/sw"
	"github.com/gaochao1/swcollector/g"
	"github.com/open-falcon/common/model"
	"log"
	"strconv"
	"time"
)

type SwPing struct {
	Ip   string
	Ping float64
}

func PingMetrics() (L []*model.MetricValue) {

	chs := make([]chan SwPing, len(AliveIp))
	for i, ip := range AliveIp {
		if ip != "" {
			chs[i] = make(chan SwPing)
			go pingMetrics(ip, chs[i])
		}
	}

	for _, ch := range chs {
		swPing := <-ch
		log.Println("===", swPing)
		L = append(L, GaugeValueIp(time.Now().Unix(), swPing.Ip, "switch.Ping", swPing.Ping))
	}

	return L
}

func pingMetrics(ip string, ch chan SwPing) {
	var swPing SwPing

	ping, err := sw.PingStatSummary(ip, g.Config().Switch.PingRetry, g.Config().Switch.PingTimeout)
	if err != nil {
		log.Println(err)
	}

	swPing.Ip = ip
	swPing.Ping, _ = strconv.ParseFloat(ping["avg"], 64)
	ch <- swPing

	return
}
<commit_msg>ping update<commit_after>package funcs

import (
	"github.com/gaochao1/sw"
	"github.com/gaochao1/swcollector/g"
	"github.com/open-falcon/common/model"
	"log"
	"time"
)

type SwPing struct {
	Ip   string
	Ping float64
}

func PingMetrics() (L []*model.MetricValue) {

	chs := make([]chan SwPing, len(AliveIp))
	for i, ip := range AliveIp {
		if ip != "" {
			chs[i] = make(chan SwPing)
			go pingMetrics(ip, chs[i])
		}
	}

	for _, ch := range chs {
		swPing := <-ch
		L = append(L, GaugeValueIp(time.Now().Unix(), swPing.Ip, "switch.Ping", swPing.Ping))
	}

	return L
}

func pingMetrics(ip string, ch chan SwPing) {
	var swPing SwPing

	rtt, err := sw.PingRtt(ip, g.Config().Switch.PingTimeout)
	if err != nil {
		log.Println(ip, err)
		swPing.Ping = 0
	}

	swPing.Ip = ip
	swPing.Ping = rtt
	ch <- swPing

	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"

	"code.google.com/p/goauth2/oauth"

	flag "github.com/docker/docker/pkg/mflag"
	"github.com/google/go-github/github"
)

type Config struct {
	BuildFlags []string `json:"build_flags,omitempty"`
	AuthToken  string   `json:"auth_token"`
}

// initialize flags
var (
	// Config needs to be initted in init()
	ConfigPath  *string
	BuildFlags  = flag.String([]string{"b", "-buildflags"}, "", "Pass build flags to gox")
	ForceCreate = flag.Bool([]string{"f", "-force"}, false, "Replace the asset if it already exists")
	AssetName   = flag.String([]string{"n", "-name"}, "", "Set the name for the release asset")
	Prerelease  = flag.Bool([]string{"p", "-prerelease"}, false, "With -create, create as a dev release")
	ReleaseName = flag.String([]string{"r", "-release"}, "", "Create the release if it does not exist and set the release name")
	Version     = flag.Bool([]string{"v", "-version"}, false, "Print the name and version")
	GoxPath     string
)

func LoadConfig(ConfigPath *string) Config {
	var ret Config
	ConfigFile, err := ioutil.ReadFile(*ConfigPath)
	err = json.Unmarshal(ConfigFile, &ret)
	if err != nil {
		log.Fatalln(err)
	}
	return ret
}

func init() {
	CurrentUser, _ := user.Current()
	DefaultConfigPath := fmt.Sprintf("%s/.config/ghrelease/config.json", CurrentUser.HomeDir)
	ConfigPath = flag.String([]string{"c", "-config"}, DefaultConfigPath, "Set ghrelease config file")

	flag.Parse()

	GoPath := os.Getenv("GOPATH")
	GoxPath = fmt.Sprintf("%s/bin/gox", GoPath)
}

func main() {
	GHRConfig := LoadConfig(ConfigPath)

	EnvAuthToken := os.Getenv("GHRELEASE_AUTH_TOKEN")
	if EnvAuthToken != "" {
		GHRConfig.AuthToken = EnvAuthToken
	}
	if GHRConfig.AuthToken == "" {
		log.Println("auth_token is required to upload release assets to GitHub")
	}

	transport := &oauth.Transport{
		Token: &oauth.Token{AccessToken: GHRConfig.AuthToken},
	}
	client := github.NewClient(transport.Client())
}
<commit_msg>use path.Join()<commit_after>package main

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"

	"code.google.com/p/goauth2/oauth"

	flag "github.com/docker/docker/pkg/mflag"
	"github.com/google/go-github/github"
)

type Config struct {
	BuildFlags []string `json:"build_flags,omitempty"`
	AuthToken  string   `json:"auth_token"`
}

// initialize flags
var (
	// Config needs to be initted in init()
	ConfigPath  *string
	BuildFlags  = flag.String([]string{"b", "-buildflags"}, "", "Pass build flags to gox")
	ForceCreate = flag.Bool([]string{"f", "-force"}, false, "Replace the asset if it already exists")
	AssetName   = flag.String([]string{"n", "-name"}, "", "Set the name for the release asset")
	Prerelease  = flag.Bool([]string{"p", "-prerelease"}, false, "With -create, create as a dev release")
	ReleaseName = flag.String([]string{"r", "-release"}, "", "Create the release if it does not exist and set the release name")
	Version     = flag.Bool([]string{"v", "-version"}, false, "Print the name and version")
	GoxPath     string
)

func LoadConfig(ConfigPath *string) Config {
	var ret Config
	ConfigFile, err := ioutil.ReadFile(*ConfigPath)
	err = json.Unmarshal(ConfigFile, &ret)
	if err != nil {
		log.Fatalln(err)
	}
	return ret
}

func init() {
	CurrentUser, _ := user.Current()
	DefaultConfigPath := path.Join(CurrentUser.HomeDir, ".config/ghrelease/config.json")
	ConfigPath = flag.String([]string{"c", "-config"}, DefaultConfigPath, "Set ghrelease config file")

	flag.Parse()

	GoPath := os.Getenv("GOPATH")
	GoxPath = path.Join(GoPath, "bin/gox")
}

func main() {
	GHRConfig := LoadConfig(ConfigPath)

	EnvAuthToken := os.Getenv("GHRELEASE_AUTH_TOKEN")
	if EnvAuthToken != "" {
		GHRConfig.AuthToken = EnvAuthToken
	}
	if GHRConfig.AuthToken == "" {
		log.Println("auth_token is required to upload release assets to GitHub")
	}

	transport := &oauth.Transport{
		Token: &oauth.Token{AccessToken: GHRConfig.AuthToken},
	}
	client := github.NewClient(transport.Client())
}
<|endoftext|>


<commit_before>package main

import (
	flag "github.com/docker/docker/pkg/mflag"
)

var (
	AuthFile    = flag.String([]string{"a", "-authfile"}, "", "Set the auth file for GitHub credentials")
	CreateAsset = flag.Bool([]string{"c", "-create"}, false, "Create release if it does not exist")
	BuildFlags  = flag.String([]string{"b", "-buildflags"}, "", "Pass build flags to gox")
	ForceCreate = flag.Bool([]string{"f", "-force"}, false, "Replace the asset if it already exists")
	AssetName   = flag.String([]string{"n", "-name"}, "", "Set the name for the release asset")
	Prerelease  = flag.Bool([]string{"p", "-prerelease"}, false, "With -create, create as a dev release")
	ReleaseName = flag.String([]string{"r", "-release"}, "", "Set the release name")
	Version     = flag.Bool([]string{"v", "-version"}, false, "Print the name and version")
)

func main() {
}
<commit_msg>parse flags<commit_after>package main

import flag "github.com/docker/docker/pkg/mflag"

// initialize flags
var (
	AuthFile    = flag.String([]string{"a", "-authfile"}, "", "Set the auth file for GitHub credentials")
	CreateAsset = flag.Bool([]string{"c", "-create"}, false, "Create release if it does not exist")
	BuildFlags  = flag.String([]string{"b", "-buildflags"}, "", "Pass build flags to gox")
	ForceCreate = flag.Bool([]string{"f", "-force"}, false, "Replace the asset if it already exists")
	AssetName   = flag.String([]string{"n", "-name"}, "", "Set the name for the release asset")
	Prerelease  = flag.Bool([]string{"p", "-prerelease"}, false, "With -create, create as a dev release")
	ReleaseName = flag.String([]string{"r", "-release"}, "", "Set the release name")
	Version     = flag.Bool([]string{"v", "-version"}, false, "Print the name and version")
)

func init() {
	flag.Parse()
}

func main() {
}
<|endoftext|>


<commit_before>// Copyright 2013 Chris McGee <sirnewton_01@yahoo.ca>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gdblib

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"
)

type cmdDescr struct {
	cmd            string
	response       chan cmdResultRecord
	forceInterrupt bool
}

type cmdResultRecord struct {
	id         int64
	indication string
	result     string
}

type AsyncResultRecord struct {
	Indication string
	Result     map[string]interface{}
}

type GDB struct {
	// Channel of gdb console lines
	Console chan string
	// Channel of target process lines
	Target chan string
	// Channel of internal GDB log lines
	InternalLog chan string
	// Channel of async result records
	AsyncResults chan AsyncResultRecord

	gdbCmd *exec.Cmd

	// Inferior process (if running)
	inferiorLock    sync.Mutex
	inferiorProcess *os.Process
	inferiorPid     string
	inferiorRunning bool

	// Internal channel to send a command to the gdb interpreter
	input chan cmdDescr
	// Internal channel to send result records to callers waiting for a response
	result chan cmdResultRecord

	// Registry of command descriptors for synchronous commands
	cmdRegistry map[int64]cmdDescr
	nextId      int64
}

func convertCString(cstr string) string {
	str := cstr

	if str[0] == '"' && str[len(str)-1] == '"' {
		str = str[1 : len(str)-1]
	}
	str = strings.Replace(str, `\"`, `"`, -1)
	str = strings.Replace(str, `\n`, "\n", -1)

	return str
}

// NewGDB creates a new gdb debugging session. Provide the full OS path
//  to the program to debug. The source root directory is optional in
//  order to resolve the source file references.
func NewGDB(program string, srcRoot string) (*GDB, error) {
	gdb := &GDB{}

	gdb.gdbCmd = exec.Command("gdb", program, "--interpreter", "mi2")
	if srcRoot != "" {
		gdb.gdbCmd.Dir = srcRoot
	}

	// Perform any os-specific customizations on the command before launching it
	fixCmd(gdb.gdbCmd)

	gdb.Console = make(chan string)
	gdb.Target = make(chan string)
	gdb.InternalLog = make(chan string)
	gdb.AsyncResults = make(chan AsyncResultRecord)

	gdb.input = make(chan cmdDescr)
	gdb.result = make(chan cmdResultRecord)
	gdb.cmdRegistry = make(map[int64]cmdDescr)
	gdb.nextId = 0

	wg := sync.WaitGroup{}
	wg.Add(3)

	wg2 := sync.WaitGroup{}
	wg2.Add(1)

	writer := func() {
		inPipe, err := gdb.gdbCmd.StdinPipe()

		wg2.Done()

		if err != nil {
			return
		}

		wg.Done()

		// Add a default "main" breakpoint (works in C and Go) to force execution to pause
		//  waiting for user to add breakpoints, etc.
		inPipe.Write([]byte("-break-insert main\n"))

		for {
			select {
			case newInput := <-gdb.input:
				// Interrupt the process so that we can send the command
				gdb.inferiorLock.Lock()
				interrupted := false
				if newInput.forceInterrupt && gdb.inferiorProcess != nil && gdb.inferiorRunning {
					interrupted = true
					interruptInferior(gdb.inferiorProcess, gdb.inferiorPid)
				}
				gdb.inferiorLock.Unlock()

				if newInput.response != nil {
					gdb.nextId++
					id := gdb.nextId
					gdb.cmdRegistry[id] = newInput

					inPipe.Write([]byte(strconv.FormatInt(id, 10) + newInput.cmd + "\n"))
				} else {
					inPipe.Write([]byte(newInput.cmd + "\n"))
				}

				// If it is an empty command then it is because the client is requesting
				//  plain interrupt without continuing.
				if interrupted && newInput.cmd != "" {
					inPipe.Write([]byte("-exec-continue\n"))
				}
			case resultRecord := <-gdb.result:
				descriptor := gdb.cmdRegistry[resultRecord.id]

				if descriptor.cmd != "" {
					descriptor.response <- resultRecord
				}
			}
		}
	}

	reader := func() {
		wg2.Wait()
		outPipe, err := gdb.gdbCmd.StdoutPipe()

		if err != nil {
			return
		}

		wg.Done()

		reader := bufio.NewReader(outPipe)
		resultRecordRegex := regexp.MustCompile(`^(\d*)\^(\S+?)(,(.*))?$`)
		asyncRecordRegex := regexp.MustCompile(`^([*=])(\S+?),(.*)$`)

		for {
			line, err := reader.ReadString('\n')
			if err != nil {
				fmt.Printf("ERROR: %v\n", err.Error())
				break
			}
			line = strings.Replace(line, "\r", "", -1)
			line = strings.Replace(line, "\n", "", -1)

			if len(line) == 0 {
				continue
			}

			// TODO unescape the quotes, newlines, etc.
			// stream outputs
			if line[0] == '~' {
				line = convertCString(line[1:])
				gdb.Console <- line
			} else if line[0] == '@' {
				line = convertCString(line[1:])
				gdb.Target <- line
			} else if line[0] == '&' {
				line = convertCString(line[1:])
				gdb.InternalLog <- line + "\n"
				// result record
			} else if matches := resultRecordRegex.FindStringSubmatch(line); matches != nil {
				commandId := matches[1]
				resultIndication := matches[2]
				result := ""
				if len(matches) > 4 {
					result = matches[4]
				}

				if commandId != "" {
					id, err := strconv.ParseInt(commandId, 10, 64)

					if err == nil {
						resultRecord := cmdResultRecord{id: id, indication: resultIndication, result: result}
						gdb.result <- resultRecord
					}

					// TODO handle the parse error case
				}
				//				else {
				//					fmt.Printf("[RESULT RECORD] ID:%v %v %v\n", commandId, resultIndication, result)
				//				}
				// async record
				//				fmt.Printf("[ASYNC RESULT RECORD] %v %v\n", resultIndication, result)
			} else if matches := asyncRecordRegex.FindStringSubmatch(line); matches != nil {
				// recordType := matches[1]
				resultIndication := matches[2]
				result := matches[3]

				resultNode, _ := createObjectNode("{" + result + "}")
				resultObj := make(map[string]interface{})
				jsonStr := resultNode.toJSON()
				err := json.Unmarshal([]byte(jsonStr), &resultObj)

				if err == nil {
					resultRecord := AsyncResultRecord{Indication: resultIndication, Result: resultObj}

					gdb.inferiorLock.Lock()
					if resultIndication == "thread-group-started" {
						pidStr, ok := resultObj["pid"].(string)

						if ok {
							pid, err := strconv.ParseInt(pidStr, 10, 32)
							if err == nil {
								gdb.inferiorProcess, err = os.FindProcess(int(pid))
								gdb.inferiorPid = pidStr
							}
						}
					} else if resultIndication == "thread-group-exited" {
						gdb.inferiorProcess = nil
					} else if resultIndication == "running" {
						gdb.inferiorRunning = true
					} else if resultIndication == "stopped" {
						gdb.inferiorRunning = false
					}
					gdb.inferiorLock.Unlock()

					gdb.AsyncResults <- resultRecord
				} else {
					fmt.Printf("[ORIGINAL] %v\n", result)
					fmt.Printf("[JSON] %v\n", jsonStr)
					fmt.Printf("Error unmarshalling JSON for async result record: %v %v\n", err.Error(), resultNode.toJSON())
				}
				// TODO handle the parse error case
				//				fmt.Printf("[ASYNC RESULT RECORD] %v %v\n", resultIndication, result)
			} else if line == "(gdb) " {
				// This is the gdb prompt. We can just throw it out
			} else {
				//fmt.Printf("%v\n", line)
				gdb.Target <- line + "\n"
			}
		}
	}

	// Handle standard error as if it comes from the target
	errReader := func() {
		wg2.Wait()
		errPipe, err := gdb.gdbCmd.StderrPipe()

		if err != nil {
			return
		}

		wg.Done()

		reader := bufio.NewReader(errPipe)

		for {
			line, err := reader.ReadString('\n')
			if err != nil {
				fmt.Printf("ERROR: %v\n", err.Error())
				break
			}

			gdb.Target <- "[stderr] " + line
		}
	}

	go reader()
	go errReader()
	go writer()

	wg.Wait()

	err := gdb.gdbCmd.Start()
	if err != nil {
		return nil, err
	}

	return gdb, nil
}

func (gdb *GDB) Wait() error {
	return gdb.gdbCmd.Wait()
}

func parseResult(result cmdResultRecord, resultObj interface{}) error {
	if result.indication == "error" {
		msg := strings.Replace(result.result, `msg="`, "", 1)
		msg = msg[:len(msg)-1]

		return errors.New(msg)
	}

	if resultObj != nil {
		//		fmt.Printf("[ORIGINAL] %v\n", result.result)

		gdbNode, _ := createObjectNode("{" + result.result + "}")
		jsonStr := gdbNode.toJSON()

		//		fmt.Printf("[JSON DUMP] %v\n", jsonStr)

		err := json.Unmarshal([]byte(jsonStr), &resultObj)
		if err != nil {
			fmt.Printf("[ORIGINAL] %v\n", result.result)
			fmt.Printf("[JSON DUMP] %v\n", jsonStr)
			return err
		}
	}

	return nil
}

func (gdb *GDB) GdbExit() {
	descriptor := cmdDescr{forceInterrupt: true}
	descriptor.cmd = "-gdb-exit"
	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor
	<-descriptor.response
}

func (gdb *GDB) GdbSet(name, value string) error {
	descriptor := cmdDescr{}
	descriptor.cmd = fmt.Sprintf("-gdb-set %s %s", name, value)
	descriptor.response = make(chan cmdResultRecord)

	gdb.input <- descriptor
	rsp := <-descriptor.response

	return parseResult(rsp, nil)
}

func (gdb *GDB) GdbShow(name string) (string, error) {
	descriptor := cmdDescr{}
	descriptor.cmd = fmt.Sprintf("-gdb-show %s", name)
	descriptor.response = make(chan cmdResultRecord)

	gdb.input <- descriptor
	result := <-descriptor.response

	resultMap := make(map[string]string)
	err := parseResult(result, &resultMap)

	return resultMap["value"], err
}
<commit_msg>option to lauch as sudo<commit_after>// Copyright 2013 Chris McGee <sirnewton_01@yahoo.ca>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gdblib

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"
)

type cmdDescr struct {
	cmd            string
	response       chan cmdResultRecord
	forceInterrupt bool
}

type cmdResultRecord struct {
	id         int64
	indication string
	result     string
}

type AsyncResultRecord struct {
	Indication string
	Result     map[string]interface{}
}

type GDB struct {
	// Channel of gdb console lines
	Console chan string
	// Channel of target process lines
	Target chan string
	// Channel of internal GDB log lines
	InternalLog chan string
	// Channel of async result records
	AsyncResults chan AsyncResultRecord

	gdbCmd *exec.Cmd

	// Inferior process (if running)
	inferiorLock    sync.Mutex
	inferiorProcess *os.Process
	inferiorPid     string
	inferiorRunning bool

	// Internal channel to send a command to the gdb interpreter
	input chan cmdDescr
	// Internal channel to send result records to callers waiting for a response
	result chan cmdResultRecord

	// Registry of command descriptors for synchronous commands
	cmdRegistry map[int64]cmdDescr
	nextId      int64
}

func convertCString(cstr string) string {
	str := cstr

	if str[0] == '"' && str[len(str)-1] == '"' {
		str = str[1 : len(str)-1]
	}
	str = strings.Replace(str, `\"`, `"`, -1)
	str = strings.Replace(str, `\n`, "\n", -1)

	return str
}

// NewGDB creates a new gdb debugging session. Provide the full OS path
//  to the program to debug. The source root directory is optional in
//  order to resolve the source file references.
func NewGDB(program string, srcRoot string, sudo bool) (*GDB, error) {
	gdb := &GDB{}

   sudoCmd := ""
   if sudo {
      sudoCmd = "sudo "
   }

   gdb.gdbCmd = exec.Command(sudoCmd + "gdb", program, "--interpreter", "mi2")
	if srcRoot != "" {
		gdb.gdbCmd.Dir = srcRoot
	}

	// Perform any os-specific customizations on the command before launching it
	fixCmd(gdb.gdbCmd)

	gdb.Console = make(chan string)
	gdb.Target = make(chan string)
	gdb.InternalLog = make(chan string)
	gdb.AsyncResults = make(chan AsyncResultRecord)

	gdb.input = make(chan cmdDescr)
	gdb.result = make(chan cmdResultRecord)
	gdb.cmdRegistry = make(map[int64]cmdDescr)
	gdb.nextId = 0

	wg := sync.WaitGroup{}
	wg.Add(3)

	wg2 := sync.WaitGroup{}
	wg2.Add(1)

	writer := func() {
		inPipe, err := gdb.gdbCmd.StdinPipe()

		wg2.Done()

		if err != nil {
			return
		}

		wg.Done()

		// Add a default "main" breakpoint (works in C and Go) to force execution to pause
		//  waiting for user to add breakpoints, etc.
		inPipe.Write([]byte("-break-insert main\n"))

		for {
			select {
			case newInput := <-gdb.input:
				// Interrupt the process so that we can send the command
				gdb.inferiorLock.Lock()
				interrupted := false
				if newInput.forceInterrupt && gdb.inferiorProcess != nil && gdb.inferiorRunning {
					interrupted = true
					interruptInferior(gdb.inferiorProcess, gdb.inferiorPid)
				}
				gdb.inferiorLock.Unlock()

				if newInput.response != nil {
					gdb.nextId++
					id := gdb.nextId
					gdb.cmdRegistry[id] = newInput

					inPipe.Write([]byte(strconv.FormatInt(id, 10) + newInput.cmd + "\n"))
				} else {
					inPipe.Write([]byte(newInput.cmd + "\n"))
				}

				// If it is an empty command then it is because the client is requesting
				//  plain interrupt without continuing.
				if interrupted && newInput.cmd != "" {
					inPipe.Write([]byte("-exec-continue\n"))
				}
			case resultRecord := <-gdb.result:
				descriptor := gdb.cmdRegistry[resultRecord.id]

				if descriptor.cmd != "" {
					descriptor.response <- resultRecord
				}
			}
		}
	}

	reader := func() {
		wg2.Wait()
		outPipe, err := gdb.gdbCmd.StdoutPipe()

		if err != nil {
			return
		}

		wg.Done()

		reader := bufio.NewReader(outPipe)
		resultRecordRegex := regexp.MustCompile(`^(\d*)\^(\S+?)(,(.*))?$`)
		asyncRecordRegex := regexp.MustCompile(`^([*=])(\S+?),(.*)$`)

		for {
			line, err := reader.ReadString('\n')
			if err != nil {
				fmt.Printf("ERROR: %v\n", err.Error())
				break
			}
			line = strings.Replace(line, "\r", "", -1)
			line = strings.Replace(line, "\n", "", -1)

			if len(line) == 0 {
				continue
			}

			// TODO unescape the quotes, newlines, etc.
			// stream outputs
			if line[0] == '~' {
				line = convertCString(line[1:])
				gdb.Console <- line
			} else if line[0] == '@' {
				line = convertCString(line[1:])
				gdb.Target <- line
			} else if line[0] == '&' {
				line = convertCString(line[1:])
				gdb.InternalLog <- line + "\n"
				// result record
			} else if matches := resultRecordRegex.FindStringSubmatch(line); matches != nil {
				commandId := matches[1]
				resultIndication := matches[2]
				result := ""
				if len(matches) > 4 {
					result = matches[4]
				}

				if commandId != "" {
					id, err := strconv.ParseInt(commandId, 10, 64)

					if err == nil {
						resultRecord := cmdResultRecord{id: id, indication: resultIndication, result: result}
						gdb.result <- resultRecord
					}

					// TODO handle the parse error case
				}
				//				else {
				//					fmt.Printf("[RESULT RECORD] ID:%v %v %v\n", commandId, resultIndication, result)
				//				}
				// async record
				//				fmt.Printf("[ASYNC RESULT RECORD] %v %v\n", resultIndication, result)
			} else if matches := asyncRecordRegex.FindStringSubmatch(line); matches != nil {
				// recordType := matches[1]
				resultIndication := matches[2]
				result := matches[3]

				resultNode, _ := createObjectNode("{" + result + "}")
				resultObj := make(map[string]interface{})
				jsonStr := resultNode.toJSON()
				err := json.Unmarshal([]byte(jsonStr), &resultObj)

				if err == nil {
					resultRecord := AsyncResultRecord{Indication: resultIndication, Result: resultObj}

					gdb.inferiorLock.Lock()
					if resultIndication == "thread-group-started" {
						pidStr, ok := resultObj["pid"].(string)

						if ok {
							pid, err := strconv.ParseInt(pidStr, 10, 32)
							if err == nil {
								gdb.inferiorProcess, err = os.FindProcess(int(pid))
								gdb.inferiorPid = pidStr
							}
						}
					} else if resultIndication == "thread-group-exited" {
						gdb.inferiorProcess = nil
					} else if resultIndication == "running" {
						gdb.inferiorRunning = true
					} else if resultIndication == "stopped" {
						gdb.inferiorRunning = false
					}
					gdb.inferiorLock.Unlock()

					gdb.AsyncResults <- resultRecord
				} else {
					fmt.Printf("[ORIGINAL] %v\n", result)
					fmt.Printf("[JSON] %v\n", jsonStr)
					fmt.Printf("Error unmarshalling JSON for async result record: %v %v\n", err.Error(), resultNode.toJSON())
				}
				// TODO handle the parse error case
				//				fmt.Printf("[ASYNC RESULT RECORD] %v %v\n", resultIndication, result)
			} else if line == "(gdb) " {
				// This is the gdb prompt. We can just throw it out
			} else {
				//fmt.Printf("%v\n", line)
				gdb.Target <- line + "\n"
			}
		}
	}

	// Handle standard error as if it comes from the target
	errReader := func() {
		wg2.Wait()
		errPipe, err := gdb.gdbCmd.StderrPipe()

		if err != nil {
			return
		}

		wg.Done()

		reader := bufio.NewReader(errPipe)

		for {
			line, err := reader.ReadString('\n')
			if err != nil {
				fmt.Printf("ERROR: %v\n", err.Error())
				break
			}

			gdb.Target <- "[stderr] " + line
		}
	}

	go reader()
	go errReader()
	go writer()

	wg.Wait()

	err := gdb.gdbCmd.Start()
	if err != nil {
		return nil, err
	}

	return gdb, nil
}

func (gdb *GDB) Wait() error {
	return gdb.gdbCmd.Wait()
}

func parseResult(result cmdResultRecord, resultObj interface{}) error {
	if result.indication == "error" {
		msg := strings.Replace(result.result, `msg="`, "", 1)
		msg = msg[:len(msg)-1]

		return errors.New(msg)
	}

	if resultObj != nil {
		//		fmt.Printf("[ORIGINAL] %v\n", result.result)

		gdbNode, _ := createObjectNode("{" + result.result + "}")
		jsonStr := gdbNode.toJSON()

		//		fmt.Printf("[JSON DUMP] %v\n", jsonStr)

		err := json.Unmarshal([]byte(jsonStr), &resultObj)
		if err != nil {
			fmt.Printf("[ORIGINAL] %v\n", result.result)
			fmt.Printf("[JSON DUMP] %v\n", jsonStr)
			return err
		}
	}

	return nil
}

func (gdb *GDB) GdbExit() {
	descriptor := cmdDescr{forceInterrupt: true}
	descriptor.cmd = "-gdb-exit"
	descriptor.response = make(chan cmdResultRecord)
	gdb.input <- descriptor
	<-descriptor.response
}

func (gdb *GDB) GdbSet(name, value string) error {
	descriptor := cmdDescr{}
	descriptor.cmd = fmt.Sprintf("-gdb-set %s %s", name, value)
	descriptor.response = make(chan cmdResultRecord)

	gdb.input <- descriptor
	rsp := <-descriptor.response

	return parseResult(rsp, nil)
}

func (gdb *GDB) GdbShow(name string) (string, error) {
	descriptor := cmdDescr{}
	descriptor.cmd = fmt.Sprintf("-gdb-show %s", name)
	descriptor.response = make(chan cmdResultRecord)

	gdb.input <- descriptor
	result := <-descriptor.response

	resultMap := make(map[string]string)
	err := parseResult(result, &resultMap)

	return resultMap["value"], err
}
<|endoftext|>