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

// +build ignore

package main

import (
	"archive/zip"
	"bufio"
	"bytes"
	"exp/locale/collate"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"unicode"
)

// This regression test runs tests for the test files in CollationTest.zip
// (taken from http://www.unicode.org/Public/UCA/<unicode.Version>/).
//
// The test files have the following form:
// # header
// 0009 0021;	# ('\u0009') <CHARACTER TABULATION>	[| | | 0201 025E]
// 0009 003F;	# ('\u0009') <CHARACTER TABULATION>	[| | | 0201 0263]
// 000A 0021;	# ('\u000A') <LINE FEED (LF)>	[| | | 0202 025E]
// 000A 003F;	# ('\u000A') <LINE FEED (LF)>	[| | | 0202 0263]
//
// The part before the semicolon is the hex representation of a sequence
// of runes. After the hash mark is a comment. The strings
// represented by rune sequence are in the file in sorted order, as
// defined by the DUCET.

var url = flag.String("url",
	"http://www.unicode.org/Public/UCA/"+unicode.Version+"/CollationTest.zip",
	"URL of Unicode collation tests zip file")
var localFiles = flag.Bool("local",
	false,
	"data files have been copied to the current directory; for debugging only")

type Test struct {
	name    string
	str     []string
	comment []string
}

var versionRe = regexp.MustCompile(`# UCA Version: (.*)\n?$`)
var testRe = regexp.MustCompile(`^([\dA-F ]+);.*# (.*)\n?$`)

func Error(e error) {
	if e != nil {
		log.Fatal(e)
	}
}

func loadTestData() []Test {
	if *localFiles {
		pwd, _ := os.Getwd()
		*url = "file://" + path.Join(pwd, path.Base(*url))
	}
	t := &http.Transport{}
	t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
	c := &http.Client{Transport: t}
	resp, err := c.Get(*url)
	Error(err)
	if resp.StatusCode != 200 {
		log.Fatalf(`bad GET status for "%s": %s`, *url, resp.Status)
	}
	f := resp.Body
	buffer, err := ioutil.ReadAll(f)
	f.Close()
	Error(err)
	archive, err := zip.NewReader(bytes.NewReader(buffer), int64(len(buffer)))
	Error(err)
	tests := []Test{}
	for _, f := range archive.File {
		// Skip the short versions, which are simply duplicates of the long versions.
		if strings.Contains(f.Name, "SHORT") || f.FileInfo().IsDir() {
			continue
		}
		ff, err := f.Open()
		Error(err)
		defer ff.Close()
		input := bufio.NewReader(ff)
		test := Test{name: path.Base(f.Name)}
		for {
			line, err := input.ReadString('\n')
			if err != nil {
				if err == io.EOF {
					break
				}
				log.Fatal(err)
			}
			if len(line) <= 1 || line[0] == '#' {
				if m := versionRe.FindStringSubmatch(line); m != nil {
					if m[1] != unicode.Version {
						log.Printf("warning:%s: version is %s; want %s", f.Name, m[1], unicode.Version)
					}
				}
				continue
			}
			m := testRe.FindStringSubmatch(line)
			if m == nil || len(m) < 3 {
				log.Fatalf(`Failed to parse: "%s" result: %#v`, line, m)
			}
			str := ""
			for _, split := range strings.Split(m[1], " ") {
				r, err := strconv.ParseUint(split, 16, 64)
				Error(err)
				str += string(rune(r))
			}
			test.str = append(test.str, str)
			test.comment = append(test.comment, m[2])
		}
		tests = append(tests, test)
	}
	return tests
}

var errorCount int

func fail(t Test, pattern string, args ...interface{}) {
	format := fmt.Sprintf("error:%s:%s", t.name, pattern)
	log.Printf(format, args...)
	errorCount++
	if errorCount > 30 {
		log.Fatal("too many errors")
	}
}

func runes(b []byte) []rune {
	return []rune(string(b))
}

func doTest(t Test) {
	c := collate.Root
	c.Strength = collate.Tertiary
	b := &collate.Buffer{}
	if strings.Contains(t.name, "NON_IGNOR") {
		c.Alternate = collate.AltNonIgnorable
	}

	prev := []byte(t.str[0])
	for i := 1; i < len(t.str); i++ {
		s := []byte(t.str[i])
		ka := c.Key(b, prev)
		kb := c.Key(b, s)
		if r := bytes.Compare(ka, kb); r == 1 {
			fail(t, "%d: Key(%.4X) < Key(%.4X) (%X < %X) == %d; want -1 or 0", i, runes(prev), runes(s), ka, kb, r)
			prev = s
			continue
		}
		if r := c.Compare(b, prev, s); r == 1 {
			fail(t, "%d: Compare(%.4X, %.4X) == %d; want -1 or 0", i, runes(prev), runes(s), r)
		}
		if r := c.Compare(b, s, prev); r == -1 {
			fail(t, "%d: Compare(%.4X, %.4X) == %d; want 1 or 0", i, runes(s), runes(prev), r)
		}
		prev = s
	}
}

func main() {
	flag.Parse()
	for _, test := range loadTestData() {
		doTest(test)
	}
	if errorCount == 0 {
		fmt.Println("PASS")
	}
}
<commit_msg>exp/locale/collate: let regtest generate its own collation table. The main table will need to get a slightly different collation table as the one used by regtest, as the regtest is based on the standard UCA DUCET, while the locale-specific tables are all based on a CLDR root table. This change allows changing the table without affecting the regression test.<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

package main

import (
	"archive/zip"
	"bufio"
	"bytes"
	"exp/locale/collate"
	"exp/locale/collate/build"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"unicode"
)

// This regression test runs tests for the test files in CollationTest.zip
// (taken from http://www.unicode.org/Public/UCA/<unicode.Version>/).
//
// The test files have the following form:
// # header
// 0009 0021;	# ('\u0009') <CHARACTER TABULATION>	[| | | 0201 025E]
// 0009 003F;	# ('\u0009') <CHARACTER TABULATION>	[| | | 0201 0263]
// 000A 0021;	# ('\u000A') <LINE FEED (LF)>	[| | | 0202 025E]
// 000A 003F;	# ('\u000A') <LINE FEED (LF)>	[| | | 0202 0263]
//
// The part before the semicolon is the hex representation of a sequence
// of runes. After the hash mark is a comment. The strings
// represented by rune sequence are in the file in sorted order, as
// defined by the DUCET.

var testdata = flag.String("testdata",
	"http://www.unicode.org/Public/UCA/"+unicode.Version+"/CollationTest.zip",
	"URL of Unicode collation tests zip file")
var ducet = flag.String("ducet",
	"http://unicode.org/Public/UCA/"+unicode.Version+"/allkeys.txt",
	"URL of the Default Unicode Collation Element Table (DUCET).")
var localFiles = flag.Bool("local",
	false,
	"data files have been copied to the current directory; for debugging only")

type Test struct {
	name    string
	str     []string
	comment []string
}

var versionRe = regexp.MustCompile(`# UCA Version: (.*)\n?$`)
var testRe = regexp.MustCompile(`^([\dA-F ]+);.*# (.*)\n?$`)

func Error(e error) {
	if e != nil {
		log.Fatal(e)
	}
}

// openReader opens the url or file given by url and returns it as an io.ReadCloser
// or nil on error.
func openReader(url string) io.ReadCloser {
	if *localFiles {
		pwd, _ := os.Getwd()
		url = "file://" + path.Join(pwd, path.Base(url))
	}
	t := &http.Transport{}
	t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
	c := &http.Client{Transport: t}
	resp, err := c.Get(url)
	Error(err)
	if resp.StatusCode != 200 {
		Error(fmt.Errorf(`bad GET status for "%s": %s`, url, resp.Status))
	}
	return resp.Body
}

// parseUCA parses a Default Unicode Collation Element Table of the format
// specified in http://www.unicode.org/reports/tr10/#File_Format.
// It returns the variable top.
func parseUCA(builder *build.Builder) {
	r := openReader(*ducet)
	defer r.Close()
	input := bufio.NewReader(r)
	colelem := regexp.MustCompile(`\[([.*])([0-9A-F.]+)\]`)
	for i := 1; true; i++ {
		l, prefix, err := input.ReadLine()
		if err == io.EOF {
			break
		}
		Error(err)
		line := string(l)
		if prefix {
			log.Fatalf("%d: buffer overflow", i)
		}
		if len(line) == 0 || line[0] == '#' {
			continue
		}
		if line[0] == '@' {
			if strings.HasPrefix(line[1:], "version ") {
				if v := strings.Split(line[1:], " ")[1]; v != unicode.Version {
					log.Fatalf("incompatible version %s; want %s", v, unicode.Version)
				}
			}
		} else {
			// parse entries
			part := strings.Split(line, " ; ")
			if len(part) != 2 {
				log.Fatalf("%d: production rule without ';': %v", i, line)
			}
			lhs := []rune{}
			for _, v := range strings.Split(part[0], " ") {
				if v != "" {
					lhs = append(lhs, rune(convHex(i, v)))
				}
			}
			vars := []int{}
			rhs := [][]int{}
			for i, m := range colelem.FindAllStringSubmatch(part[1], -1) {
				if m[1] == "*" {
					vars = append(vars, i)
				}
				elem := []int{}
				for _, h := range strings.Split(m[2], ".") {
					elem = append(elem, convHex(i, h))
				}
				rhs = append(rhs, elem)
			}
			builder.Add(lhs, rhs, vars)
		}
	}
}

func convHex(line int, s string) int {
	r, e := strconv.ParseInt(s, 16, 32)
	if e != nil {
		log.Fatalf("%d: %v", line, e)
	}
	return int(r)
}

func loadTestData() []Test {
	f := openReader(*testdata)
	buffer, err := ioutil.ReadAll(f)
	f.Close()
	Error(err)
	archive, err := zip.NewReader(bytes.NewReader(buffer), int64(len(buffer)))
	Error(err)
	tests := []Test{}
	for _, f := range archive.File {
		// Skip the short versions, which are simply duplicates of the long versions.
		if strings.Contains(f.Name, "SHORT") || f.FileInfo().IsDir() {
			continue
		}
		ff, err := f.Open()
		Error(err)
		defer ff.Close()
		input := bufio.NewReader(ff)
		test := Test{name: path.Base(f.Name)}
		for {
			line, err := input.ReadString('\n')
			if err != nil {
				if err == io.EOF {
					break
				}
				log.Fatal(err)
			}
			if len(line) <= 1 || line[0] == '#' {
				if m := versionRe.FindStringSubmatch(line); m != nil {
					if m[1] != unicode.Version {
						log.Printf("warning:%s: version is %s; want %s", f.Name, m[1], unicode.Version)
					}
				}
				continue
			}
			m := testRe.FindStringSubmatch(line)
			if m == nil || len(m) < 3 {
				log.Fatalf(`Failed to parse: "%s" result: %#v`, line, m)
			}
			str := ""
			for _, split := range strings.Split(m[1], " ") {
				r, err := strconv.ParseUint(split, 16, 64)
				Error(err)
				str += string(rune(r))
			}
			test.str = append(test.str, str)
			test.comment = append(test.comment, m[2])
		}
		tests = append(tests, test)
	}
	return tests
}

var errorCount int

func fail(t Test, pattern string, args ...interface{}) {
	format := fmt.Sprintf("error:%s:%s", t.name, pattern)
	log.Printf(format, args...)
	errorCount++
	if errorCount > 30 {
		log.Fatal("too many errors")
	}
}

func runes(b []byte) []rune {
	return []rune(string(b))
}

func doTest(t Test) {
	bld := build.NewBuilder()
	parseUCA(bld)
	c, err := bld.Build()
	Error(err)
	c.Strength = collate.Tertiary
	c.Alternate = collate.AltShifted
	b := &collate.Buffer{}
	if strings.Contains(t.name, "NON_IGNOR") {
		c.Alternate = collate.AltNonIgnorable
	}

	prev := []byte(t.str[0])
	for i := 1; i < len(t.str); i++ {
		s := []byte(t.str[i])
		ka := c.Key(b, prev)
		kb := c.Key(b, s)
		if r := bytes.Compare(ka, kb); r == 1 {
			fail(t, "%d: Key(%.4X) < Key(%.4X) (%X < %X) == %d; want -1 or 0", i, runes(prev), runes(s), ka, kb, r)
			prev = s
			continue
		}
		if r := c.Compare(b, prev, s); r == 1 {
			fail(t, "%d: Compare(%.4X, %.4X) == %d; want -1 or 0", i, runes(prev), runes(s), r)
		}
		if r := c.Compare(b, s, prev); r == -1 {
			fail(t, "%d: Compare(%.4X, %.4X) == %d; want 1 or 0", i, runes(s), runes(prev), r)
		}
		prev = s
	}
}

func main() {
	flag.Parse()
	for _, test := range loadTestData() {
		doTest(test)
	}
	if errorCount == 0 {
		fmt.Println("PASS")
	}
}
<|endoftext|>


<commit_before>package mqstore

import (
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"os/exec"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/TeaMeow/KitSvc/shared/mqutil"
	nsq "github.com/bitly/go-nsq"
	"github.com/parnurzeal/gorequest"
)

var (
	AllConnected = false
	SentTotal    = 0
	RecvTotal    = 0
	QueueTotal   = 0
)

type mqstore struct {
	*nsq.Producer
	config      *nsq.Config
	isConnected bool
	queue       []message
}

type message struct {
	topic string
	body  []byte
}

func NewProducer(url, producer, prodHTTP string, lookupds []string, m *mqutil.Engine, deployed <-chan bool) *mqstore {
	// Ping the Event Store to make sure it's alive.
	if err := pingMQ(prodHTTP); err != nil {
		logrus.Fatalln(err)
	}

	config := nsq.NewConfig()
	prod, err := nsq.NewProducer(producer, config)
	//prod.SetLogger(nil, nsq.LogLevelError)
	if err != nil {
		logrus.Errorln(err)
		logrus.Fatalln("Error occurred while creating the NSQ producer.")
	}

	ms := &mqstore{
		Producer:    prod,
		config:      config,
		isConnected: true,
	}

	go ms.capture(url, prodHTTP, lookupds, m, deployed)

	go ms.push(prodHTTP)

	return ms
}

// TODO: PING LOOKUPD

func pingMQ(addr string) error {
	for i := 0; i < 30; i++ {
		_, err := http.Get("http://" + addr)
		if err == nil {
			return nil
		}
		logrus.Infof("Cannot connect to NSQ producer, retry in 3 second.")
		time.Sleep(time.Second * 3)
	}

	return errors.New("Cannot connect to NSQ producer.")
}

// sendToRouter sends the received event data to self router.
func sendToRouter(method string, url string, json []byte) {
	// Send the request via the HTTP client.
	resp, _, err := gorequest.
		New().
		CustomMethod(method, url).
		Send(string(json)).
		End()
	if err != nil {
		logrus.Errorln(err)
		// not fatal, TOO PANIC!
		logrus.Fatalln("Error occurred while sending the event to self router.")
	}
	if resp.StatusCode != 200 {
		logrus.Infoln("The event has been recevied by the router, but the status code wasn't 200.")
	}
}

func createTopic(httpProducer, topic string) {
	cmd := exec.Command("curl", "-X", "POST", fmt.Sprintf("http://%s/topic/create?topic=%s", httpProducer, topic))
	cmd.Start()
	cmd.Wait()
}

type logger struct {
}

func (l *logger) Output(calldepth int, s string) error {
	logger := logrus.StandardLogger()
	typ := s[0:3]
	switch typ {
	case "DBG":
		logger.Debug(s[9:len(s)])
	case "INF":
		logger.Info(s[9:len(s)])
	case "WRN":
		logger.Warn(s[9:len(s)])
	case "ERR":
		logger.Error(s[9:len(s)])
	}

	return nil
}

func (mq *mqstore) capture(url string, prodHTTP string, lookupds []string, m *mqutil.Engine, deployed <-chan bool) {
	// Continue if the router was ready.
	<-deployed

	for _, v := range m.Listeners {
		c, err := nsq.NewConsumer(v.Topic, v.Channel, nsq.NewConfig())
		l := &logger{}
		c.SetLogger(l, nsq.LogLevelDebug)
		if err != nil {
			logrus.Errorln(err)
			logrus.Fatalf("Cannot create the NSQ `%s` consumer. (channel: %s)", v.Topic, v.Channel)
		}
		//
		createTopic(prodHTTP, v.Topic)
		//
		c.AddHandler(nsq.HandlerFunc(func(msg *nsq.Message) error {
			//
			RecvTotal++
			//
			sendToRouter(v.Method, url+v.Path, msg.Body)

			return nil
		}))

		if err := c.ConnectToNSQLookupds(lookupds); err != nil {
			logrus.Errorln(err)
			logrus.Fatalln("Cannot connect to the NSQ lookupds.")
		}
	}
}

func (mq *mqstore) push(prodHTTP string) {
	for {
		// Check the queue every second.
		<-time.After(time.Second * 1)

		// Ping the Event Store to see if it's back online or not.
		if !mq.isConnected {
			if err := pingMQ(prodHTTP); err == nil {
				mq.isConnected = true
				AllConnected = true

				logrus.Infof("NSQ Producer is back online, there are %d unsent messages that will begin to send.", len(mq.queue))
			}
			continue
		}

		// Skip if there's nothing in the queue.
		if len(mq.queue) == 0 {
			continue
		}

		// A downward loop for the queue.
		for i := len(mq.queue) - 1; i >= 0; i-- {
			m := mq.queue[i]
			//
			<-time.After(time.Millisecond * 2)
			// Append the event in the stream.
			err := mq.Publish(m.topic, m.body)
			if err != nil {
				continue
			}
			//
			QueueTotal--
			// Remove the event from the queue since it has been sent.
			mq.queue = append(mq.queue[:i], mq.queue[i+1:]...)
		}
	}
}

func (mq *mqstore) send(topic string, data interface{}) {
	body, err := json.Marshal(data)
	if err != nil {
		//return err
	}
	//
	SentTotal++

	//
	if err := mq.Publish(topic, body); err != nil {
		switch t := err.(type) {
		case *net.OpError:
			// Mayne connect refuse
			if t.Op == "dial" {
				mq.isConnected = false
				AllConnected = false
				mq.queue = append([]message{message{topic, body}}, mq.queue...)
				QueueTotal++

				logrus.Warningf("The `%s` message will be sent when the NSQ Producer is back online. (queue length: %d)", topic, len(mq.queue))
			}
		}
	}
}
<commit_msg>cleaned mqstore<commit_after>package mqstore

import (
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"os/exec"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/TeaMeow/KitSvc/shared/mqutil"
	nsq "github.com/bitly/go-nsq"
	"github.com/parnurzeal/gorequest"
)

var (
	// AllConnected returns true when the message queue were all connected.
	AllConnected = false
	// SentTotal returns the total of the sent message.
	SentTotal = 0
	// RecvTotal returns the total of the received message.
	RecvTotal = 0
	// QueueTotal returns the total of the message are still in the queue.
	QueueTotal = 0
)

type mqstore struct {
	*nsq.Producer
	config      *nsq.Config
	isConnected bool
	queue       []message
}

// message represents a message.
type message struct {
	topic string
	body  []byte
}

// NewProducer creates a new NSQ producer, and start to capturing the incoming messages.
func NewProducer(url, producer, prodHTTP string, lookupds []string, m *mqutil.Engine, deployed <-chan bool) *mqstore {
	// Ping the Event Store to make sure it's alive.
	if err := pingMQ(prodHTTP); err != nil {
		logrus.Fatalln(err)
	}

	config := nsq.NewConfig()
	prod, err := nsq.NewProducer(producer, config)
	//prod.SetLogger(nil, nsq.LogLevelError)
	if err != nil {
		logrus.Errorln(err)
		logrus.Fatalln("Error occurred while creating the NSQ producer.")
	}

	ms := &mqstore{
		Producer:    prod,
		config:      config,
		isConnected: true,
	}

	// Capturing the messages when the router was ready in the goroutine.
	go ms.capture(url, prodHTTP, lookupds, m, deployed)
	// Pushing the messages which are in the local queue to the remote message queue.
	go ms.push(prodHTTP)

	return ms
}

// TODO: PING LOOKUPD

// pingMQ pings the NSQ with backoff to ensure
// a connection can be established before we proceed with the
// message queue setup and migration.
func pingMQ(addr string) error {
	for i := 0; i < 30; i++ {
		_, err := http.Get("http://" + addr)
		if err == nil {
			return nil
		}
		logrus.Infof("Cannot connect to NSQ producer, retry in 3 second.")
		time.Sleep(time.Second * 3)
	}

	return errors.New("Cannot connect to NSQ producer.")
}

// sendToRouter sends the received event data to self router.
func sendToRouter(method string, url string, json []byte) {
	// Send the request via the HTTP client.
	resp, _, err := gorequest.
		New().
		CustomMethod(method, url).
		Send(string(json)).
		End()
	if err != nil {
		logrus.Errorln(err)
		// not fatal, TOO PANIC!
		logrus.Fatalln("Error occurred while sending the event to self router.")
	}
	if resp.StatusCode != 200 {
		logrus.Infoln("The event has been recevied by the router, but the status code wasn't 200.")
	}
}

// createTopic creates the new topic in the remote message queue,
// so we can subscribe to it.
func createTopic(httpProducer, topic string) {
	cmd := exec.Command("curl", "-X", "POST", fmt.Sprintf("http://%s/topic/create?topic=%s", httpProducer, topic))
	cmd.Start()
	cmd.Wait()
}

type logger struct {
}

func (l *logger) Output(calldepth int, s string) error {
	logger := logrus.StandardLogger()
	typ := s[0:3]
	switch typ {
	case "DBG":
		logger.Debug(s[9:len(s)])
	case "INF":
		logger.Info(s[9:len(s)])
	case "WRN":
		logger.Warn(s[9:len(s)])
	case "ERR":
		logger.Error(s[9:len(s)])
	}

	return nil
}

// capture the incoming events.
func (mq *mqstore) capture(url string, prodHTTP string, lookupds []string, m *mqutil.Engine, deployed <-chan bool) {
	// Continue if the router was ready.
	<-deployed
	// Each of the topic listener.
	for _, v := range m.Listeners {
		c, err := nsq.NewConsumer(v.Topic, v.Channel, nsq.NewConfig())
		l := &logger{}
		c.SetLogger(l, nsq.LogLevelDebug)
		if err != nil {
			logrus.Errorln(err)
			logrus.Fatalf("Cannot create the NSQ `%s` consumer. (channel: %s)", v.Topic, v.Channel)
		}

		// Create the topic to make sure it does exist before we subscribe to it.
		createTopic(prodHTTP, v.Topic)
		// Add the topic handler.
		c.AddHandler(nsq.HandlerFunc(func(msg *nsq.Message) error {
			RecvTotal++
			// Send the received message to the self router,
			// so we can process it with Gin.
			sendToRouter(v.Method, url+v.Path, msg.Body)
			return nil
		}))

		// Connect to the NSQLookupds instead of a single NSQ node.
		if err := c.ConnectToNSQLookupds(lookupds); err != nil {
			logrus.Errorln(err)
			logrus.Fatalln("Cannot connect to the NSQ lookupds.")
		}
	}
}

// push the message which are in the queue to the remote message queue.
func (mq *mqstore) push(prodHTTP string) {
	for {
		<-time.After(time.Millisecond * 10)

		// Ping the NSQ Producer to see if it's back online or not.
		if !mq.isConnected {
			if err := pingMQ(prodHTTP); err == nil {
				mq.isConnected = true
				AllConnected = true

				logrus.Infof("NSQ Producer is back online, there are %d unsent messages that will begin to send.", len(mq.queue))
			}
			continue
		}

		// Skip if there's nothing in the queue.
		if len(mq.queue) == 0 {
			continue
		}

		// A downward loop for the queue.
		for i := len(mq.queue) - 1; i >= 0; i-- {
			m := mq.queue[i]
			// Wait a little bit for another event.
			<-time.After(time.Millisecond * 2)
			// Append the message in the topic.
			err := mq.Publish(m.topic, m.body)
			if err != nil {
				continue
			}
			QueueTotal--
			// Remove the message from the queue since it has been sent.
			mq.queue = append(mq.queue[:i], mq.queue[i+1:]...)
		}
	}
}

// send the event to the specified stream.
func (mq *mqstore) send(topic string, data interface{}) {
	body, err := json.Marshal(data)
	if err != nil {
		//return err
	}
	// Counter.
	SentTotal++

	// Send the message to the remote message queue.
	if err := mq.Publish(topic, body); err != nil {
		switch t := err.(type) {
		case *net.OpError:
			// Push the message to the local queue if connecting refused.
			if t.Op == "dial" {
				// Mark the connection as lost.
				mq.isConnected = false
				AllConnected = false
				// Push the message to local queue.
				mq.queue = append([]message{message{topic, body}}, mq.queue...)
				// Counter.
				QueueTotal++
				logrus.Warningf("The `%s` message will be sent when the NSQ Producer is back online. (queue length: %d)", topic, len(mq.queue))
			}
		}
	}
}
<|endoftext|>


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

package jose

import (
	"bytes"
	"testing"
)

func TestBase64URLEncode(t *testing.T) {
	// Test arrays with various sizes
	if base64URLEncode([]byte{}) != "" {
		t.Error("failed to encode empty array")
	}

	if base64URLEncode([]byte{0}) != "AA" {
		t.Error("failed to encode [0x00]")
	}

	if base64URLEncode([]byte{0, 1}) != "AAE" {
		t.Error("failed to encode [0x00, 0x01]")
	}

	if base64URLEncode([]byte{0, 1, 2}) != "AAEC" {
		t.Error("failed to encode [0x00, 0x01, 0x02]")
	}

	if base64URLEncode([]byte{0, 1, 2, 3}) != "AAECAw" {
		t.Error("failed to encode [0x00, 0x01, 0x02, 0x03]")
	}
}

func TestBase64URLDecode(t *testing.T) {
	// Test arrays with various sizes
	val, err := base64URLDecode("")
	if err != nil || !bytes.Equal(val, []byte{}) {
		t.Error("failed to decode empty array")
	}

	val, err = base64URLDecode("AA")
	if err != nil || !bytes.Equal(val, []byte{0}) {
		t.Error("failed to decode [0x00]")
	}

	val, err = base64URLDecode("AAE")
	if err != nil || !bytes.Equal(val, []byte{0, 1}) {
		t.Error("failed to decode [0x00, 0x01]")
	}

	val, err = base64URLDecode("AAEC")
	if err != nil || !bytes.Equal(val, []byte{0, 1, 2}) {
		t.Error("failed to decode [0x00, 0x01, 0x02]")
	}

	val, err = base64URLDecode("AAECAw")
	if err != nil || !bytes.Equal(val, []byte{0, 1, 2, 3}) {
		t.Error("failed to decode [0x00, 0x01, 0x02, 0x03]")
	}
}

func TestDeflateRoundtrip(t *testing.T) {
	original := []byte("Lorem ipsum dolor sit amet")

	compressed, err := deflate(original)
	if err != nil {
		panic(err)
	}

	output, err := inflate(compressed)
	if err != nil {
		panic(err)
	}

	if bytes.Compare(output, original) != 0 {
		t.Error("Input and output do not match")
	}
}

func TestInvalidCompression(t *testing.T) {
	_, err := compress("XYZ", []byte{})
	if err == nil {
		t.Error("should not accept invalid algorithm")
	}

	_, err = decompress("XYZ", []byte{})
	if err == nil {
		t.Error("should not accept invalid algorithm")
	}

	_, err = decompress(DEFLATE, []byte{1, 2, 3, 4})
	if err == nil {
		t.Error("should not accept invalid data")
	}
}

func TestByteBufferTrim(t *testing.T) {
	buf := newBufferFromInt(1)
	if !bytes.Equal(buf.data, []byte{1}) {
		t.Error("Byte buffer for integer '1' should contain [0x01]")
	}

	buf = newBufferFromInt(65537)
	if !bytes.Equal(buf.data, []byte{1, 0, 1}) {
		t.Error("Byte buffer for integer '65537' should contain [0x01, 0x00, 0x01]")
	}
}

func TestFixedSizeBuffer(t *testing.T) {
	data0 := []byte{}
	data1 := []byte{1}
	data2 := []byte{1, 2}
	data3 := []byte{1, 2, 3}
	data4 := []byte{1, 2, 3, 4}

	buf0 := newFixedSizeBuffer(data0, 4)
	buf1 := newFixedSizeBuffer(data1, 4)
	buf2 := newFixedSizeBuffer(data2, 4)
	buf3 := newFixedSizeBuffer(data3, 4)
	buf4 := newFixedSizeBuffer(data4, 4)

	if !bytes.Equal(buf0.data, []byte{0, 0, 0, 0}) {
		t.Error("Invalid padded buffer for buf0")
	}
	if !bytes.Equal(buf1.data, []byte{0, 0, 0, 1}) {
		t.Error("Invalid padded buffer for buf1")
	}
	if !bytes.Equal(buf2.data, []byte{0, 0, 1, 2}) {
		t.Error("Invalid padded buffer for buf2")
	}
	if !bytes.Equal(buf3.data, []byte{0, 1, 2, 3}) {
		t.Error("Invalid padded buffer for buf3")
	}
	if !bytes.Equal(buf4.data, []byte{1, 2, 3, 4}) {
		t.Error("Invalid padded buffer for buf4")
	}
}
<commit_msg>Add test to ensure we never serialize nil<commit_after>/*-
 * Copyright 2014 Square Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jose

import (
	"bytes"
	"strings"
	"testing"
)

func TestBase64URLEncode(t *testing.T) {
	// Test arrays with various sizes
	if base64URLEncode([]byte{}) != "" {
		t.Error("failed to encode empty array")
	}

	if base64URLEncode([]byte{0}) != "AA" {
		t.Error("failed to encode [0x00]")
	}

	if base64URLEncode([]byte{0, 1}) != "AAE" {
		t.Error("failed to encode [0x00, 0x01]")
	}

	if base64URLEncode([]byte{0, 1, 2}) != "AAEC" {
		t.Error("failed to encode [0x00, 0x01, 0x02]")
	}

	if base64URLEncode([]byte{0, 1, 2, 3}) != "AAECAw" {
		t.Error("failed to encode [0x00, 0x01, 0x02, 0x03]")
	}
}

func TestBase64URLDecode(t *testing.T) {
	// Test arrays with various sizes
	val, err := base64URLDecode("")
	if err != nil || !bytes.Equal(val, []byte{}) {
		t.Error("failed to decode empty array")
	}

	val, err = base64URLDecode("AA")
	if err != nil || !bytes.Equal(val, []byte{0}) {
		t.Error("failed to decode [0x00]")
	}

	val, err = base64URLDecode("AAE")
	if err != nil || !bytes.Equal(val, []byte{0, 1}) {
		t.Error("failed to decode [0x00, 0x01]")
	}

	val, err = base64URLDecode("AAEC")
	if err != nil || !bytes.Equal(val, []byte{0, 1, 2}) {
		t.Error("failed to decode [0x00, 0x01, 0x02]")
	}

	val, err = base64URLDecode("AAECAw")
	if err != nil || !bytes.Equal(val, []byte{0, 1, 2, 3}) {
		t.Error("failed to decode [0x00, 0x01, 0x02, 0x03]")
	}
}

func TestDeflateRoundtrip(t *testing.T) {
	original := []byte("Lorem ipsum dolor sit amet")

	compressed, err := deflate(original)
	if err != nil {
		panic(err)
	}

	output, err := inflate(compressed)
	if err != nil {
		panic(err)
	}

	if bytes.Compare(output, original) != 0 {
		t.Error("Input and output do not match")
	}
}

func TestInvalidCompression(t *testing.T) {
	_, err := compress("XYZ", []byte{})
	if err == nil {
		t.Error("should not accept invalid algorithm")
	}

	_, err = decompress("XYZ", []byte{})
	if err == nil {
		t.Error("should not accept invalid algorithm")
	}

	_, err = decompress(DEFLATE, []byte{1, 2, 3, 4})
	if err == nil {
		t.Error("should not accept invalid data")
	}
}

func TestByteBufferTrim(t *testing.T) {
	buf := newBufferFromInt(1)
	if !bytes.Equal(buf.data, []byte{1}) {
		t.Error("Byte buffer for integer '1' should contain [0x01]")
	}

	buf = newBufferFromInt(65537)
	if !bytes.Equal(buf.data, []byte{1, 0, 1}) {
		t.Error("Byte buffer for integer '65537' should contain [0x01, 0x00, 0x01]")
	}
}

func TestFixedSizeBuffer(t *testing.T) {
	data0 := []byte{}
	data1 := []byte{1}
	data2 := []byte{1, 2}
	data3 := []byte{1, 2, 3}
	data4 := []byte{1, 2, 3, 4}

	buf0 := newFixedSizeBuffer(data0, 4)
	buf1 := newFixedSizeBuffer(data1, 4)
	buf2 := newFixedSizeBuffer(data2, 4)
	buf3 := newFixedSizeBuffer(data3, 4)
	buf4 := newFixedSizeBuffer(data4, 4)

	if !bytes.Equal(buf0.data, []byte{0, 0, 0, 0}) {
		t.Error("Invalid padded buffer for buf0")
	}
	if !bytes.Equal(buf1.data, []byte{0, 0, 0, 1}) {
		t.Error("Invalid padded buffer for buf1")
	}
	if !bytes.Equal(buf2.data, []byte{0, 0, 1, 2}) {
		t.Error("Invalid padded buffer for buf2")
	}
	if !bytes.Equal(buf3.data, []byte{0, 1, 2, 3}) {
		t.Error("Invalid padded buffer for buf3")
	}
	if !bytes.Equal(buf4.data, []byte{1, 2, 3, 4}) {
		t.Error("Invalid padded buffer for buf4")
	}
}

func TestSerializeJSONRejectsNil(t *testing.T) {
	defer func() {
		r := recover()
		if r == nil || !strings.Contains(r.(string), "nil pointer") {
			t.Error("serialize function should not accept nil pointer")
		}
	}()

	mustSerializeJSON(nil)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"io"
	"log"
	"os/exec"
	"strings"
)

type Runner struct {
	printer     PrintResetter
	current     *exec.Cmd
	template    string
	placeholder string
	buf			*bytes.Buffer
}

func (r *Runner) runWithInput(input []byte) {
	cmd := r.cmdWithInput(string(input))

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	ch := r.readCmdStdout(stdout)

	err = cmd.Start()
	if err != nil {
		log.Fatal(err)
	}

	r.buf = new(bytes.Buffer)
	r.current = cmd

	for str := range ch {
		r.printer.Print(str)
		r.buf.WriteString(str)
	}
	cmd.Wait()
	r.printer.Reset()
}

func (r *Runner) cmdWithInput(input string) *exec.Cmd {
	line := strings.Replace(r.template, r.placeholder, input, -1)
	splitted := strings.Split(line, " ")

	return exec.Command(splitted[0], splitted[1:len(splitted)]...)
}

func (r *Runner) readCmdStdout(stdout io.ReadCloser) <-chan string {
	ch := make(chan string)
	cmdreader := bufio.NewReader(stdout)

	go func() {
		for {
			line, err := cmdreader.ReadBytes('\n')
			if err != nil || err == io.EOF {
				break
			}
			ch <- string(line)
		}
		close(ch)
	}()

	return ch
}

func (r *Runner) writeCmdStdout(out io.Writer) (n int64, err error) {
	return io.Copy(out, r.buf)
}

func (r *Runner) killCurrent() {
	if r.current != nil {
		r.current.Process.Kill()
		r.current.Wait()

		r.current = nil
	}

	r.printer.Reset()
}
<commit_msg>go fmt<commit_after>package main

import (
	"bufio"
	"bytes"
	"io"
	"log"
	"os/exec"
	"strings"
)

type Runner struct {
	printer     PrintResetter
	current     *exec.Cmd
	template    string
	placeholder string
	buf         *bytes.Buffer
}

func (r *Runner) runWithInput(input []byte) {
	cmd := r.cmdWithInput(string(input))

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	ch := r.readCmdStdout(stdout)

	err = cmd.Start()
	if err != nil {
		log.Fatal(err)
	}

	r.buf = new(bytes.Buffer)
	r.current = cmd

	for str := range ch {
		r.printer.Print(str)
		r.buf.WriteString(str)
	}
	cmd.Wait()
	r.printer.Reset()
}

func (r *Runner) cmdWithInput(input string) *exec.Cmd {
	line := strings.Replace(r.template, r.placeholder, input, -1)
	splitted := strings.Split(line, " ")

	return exec.Command(splitted[0], splitted[1:len(splitted)]...)
}

func (r *Runner) readCmdStdout(stdout io.ReadCloser) <-chan string {
	ch := make(chan string)
	cmdreader := bufio.NewReader(stdout)

	go func() {
		for {
			line, err := cmdreader.ReadBytes('\n')
			if err != nil || err == io.EOF {
				break
			}
			ch <- string(line)
		}
		close(ch)
	}()

	return ch
}

func (r *Runner) writeCmdStdout(out io.Writer) (n int64, err error) {
	return io.Copy(out, r.buf)
}

func (r *Runner) killCurrent() {
	if r.current != nil {
		r.current.Process.Kill()
		r.current.Wait()

		r.current = nil
	}

	r.printer.Reset()
}
<|endoftext|>


<commit_before><commit_msg>support init and return error<commit_after><|endoftext|>


<commit_before><commit_msg>echo.v2 migration<commit_after><|endoftext|>


<commit_before><commit_msg>rm whitespace from tab writer<commit_after><|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/binary"
	"encoding/pem"
	"errors"
	"fmt"
	"math/big"
	"os"

	"golang.org/x/crypto/ssh"
)

func parseString(in []byte) (out, rest []byte, ok bool) {
	if len(in) < 4 {
		return
	}
	length := binary.BigEndian.Uint32(in)
	if uint32(len(in)) < 4+length {
		return
	}
	out = in[4 : 4+length]
	rest = in[4+length:]
	ok = true
	return
}

// parseRSA parses an RSA key according to RFC 4253, section 6.6.
func parseRSA(in []byte) (out *rsa.PublicKey, rest []byte, err error) {
	var w struct {
		E    *big.Int
		N    *big.Int
		Rest []byte `ssh:"rest"`
	}
	if err := ssh.Unmarshal(in, &w); err != nil {
		return nil, nil, err
	}

	if w.E.BitLen() > 24 {
		return nil, nil, errors.New("ssh: exponent too large")
	}
	e := w.E.Int64()
	if e < 3 || e&1 == 0 {
		return nil, nil, errors.New("ssh: incorrect exponent")
	}

	key := &rsa.PublicKey{
		E: int(e),
		N: w.N,
	}
	return key, w.Rest, nil
}

// ParsePublicKey parses an SSH public key formatted for use in
// the SSH wire protocol according to RFC 4253, section 6.6.
func ParsePublicKey(in []byte) (out *rsa.PublicKey, err error) {
	algo, in, ok := parseString(in)
	if !ok {
		return nil, errors.New("ssh: short read")
	}
	if string(algo) != "ssh-rsa" {
		return nil, fmt.Errorf("ssh: unsupported key type %q", algo)
	}
	var rest []byte
	out, rest, err = parseRSA(in)
	if len(rest) > 0 {
		return nil, errors.New("ssh: trailing junk in public key")
	}

	return out, err
}

// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
// (see sshd(8) manual page) once the options and key type fields have been
// removed.
func parseAuthorizedKey(in []byte) (out *rsa.PublicKey, comment string, err error) {
	in = bytes.TrimSpace(in)

	i := bytes.IndexAny(in, " \t")
	if i == -1 {
		i = len(in)
	}
	base64Key := in[:i]

	key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key)))
	n, err := base64.StdEncoding.Decode(key, base64Key)
	if err != nil {
		return nil, "", err
	}
	key = key[:n]
	out, err = ParsePublicKey(key)
	if err != nil {
		return nil, "", err
	}
	comment = string(bytes.TrimSpace(in[i:]))
	return out, comment, nil
}

// ParseAuthorizedKeys parses a public key from an authorized_keys
// file used in OpenSSH according to the sshd(8) manual page.
func ParseAuthorizedKey(in []byte) (out *rsa.PublicKey, comment string, options []string, rest []byte, err error) {
	for len(in) > 0 {
		end := bytes.IndexByte(in, '\n')
		if end != -1 {
			rest = in[end+1:]
			in = in[:end]
		} else {
			rest = nil
		}

		end = bytes.IndexByte(in, '\r')
		if end != -1 {
			in = in[:end]
		}

		in = bytes.TrimSpace(in)
		if len(in) == 0 || in[0] == '#' {
			in = rest
			continue
		}

		i := bytes.IndexAny(in, " \t")
		if i == -1 {
			in = rest
			continue
		}

		if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
			return out, comment, options, rest, nil
		}

		// No key type recognised. Maybe there's an options field at
		// the beginning.
		var b byte
		inQuote := false
		var candidateOptions []string
		optionStart := 0
		for i, b = range in {
			isEnd := !inQuote && (b == ' ' || b == '\t')
			if (b == ',' && !inQuote) || isEnd {
				if i-optionStart > 0 {
					candidateOptions = append(candidateOptions, string(in[optionStart:i]))
				}
				optionStart = i + 1
			}
			if isEnd {
				break
			}
			if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) {
				inQuote = !inQuote
			}
		}
		for i < len(in) && (in[i] == ' ' || in[i] == '\t') {
			i++
		}
		if i == len(in) {
			// Invalid line: unmatched quote
			in = rest
			continue
		}

		in = in[i:]
		i = bytes.IndexAny(in, " \t")
		if i == -1 {
			in = rest
			continue
		}

		if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
			options = candidateOptions
			return out, comment, options, rest, nil
		}

		in = rest
		continue
	}

	return nil, "", nil, nil, errors.New("ssh: no key found")
}

// ParsePrivateKey returns a private key from a PEM encoded private key. It
// supports RSA (PKCS#1)
func ParsePrivateKey(pemBytes []byte) (key *rsa.PrivateKey, err error) {
	block, _ := pem.Decode(pemBytes)
	if block == nil {
		return nil, errors.New("ssh: no key found")
	}

	switch block.Type {
	case "RSA PRIVATE KEY":
		return x509.ParsePKCS1PrivateKey(block.Bytes)
	default:
		return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
	}
}

func openPubKey(path string) (out []*rsa.PublicKey, err error) {
	file, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		fmt.Println(scanner.Text()) // Println will add back the final '\n'
		key, _, _, _, err := ParseAuthorizedKey(scanner.Bytes())
		if err != nil {
			fmt.Printf("Could not read key")
			continue
		}
		out = append(out, key)

	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
	}

	return out, err

}

func openPrivKey(path string) (key *rsa.PrivateKey, err error) {
	file, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	fileInfo, err := file.Stat()
	if err != nil {
		panic(err)
	}
	buf := make([]byte, fileInfo.Size())
	_, err = file.Read(buf)
	if err != nil {
		panic(err)
	}

	return ParsePrivateKey(buf)

}
<commit_msg>whoopse<commit_after>package main

import (
	"bufio"
	"bytes"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/binary"
	"encoding/pem"
	"errors"
	"fmt"
	"math/big"
	"os"

	"golang.org/x/crypto/ssh"
)

func parseString(in []byte) (out, rest []byte, ok bool) {
	if len(in) < 4 {
		return
	}
	length := binary.BigEndian.Uint32(in)
	if uint32(len(in)) < 4+length {
		return
	}
	out = in[4 : 4+length]
	rest = in[4+length:]
	ok = true
	return
}

// parseRSA parses an RSA key according to RFC 4253, section 6.6.
func parseRSA(in []byte) (out *rsa.PublicKey, rest []byte, err error) {
	var w struct {
		E    *big.Int
		N    *big.Int
		Rest []byte `ssh:"rest"`
	}
	if err := ssh.Unmarshal(in, &w); err != nil {
		return nil, nil, err
	}

	if w.E.BitLen() > 24 {
		return nil, nil, errors.New("ssh: exponent too large")
	}
	e := w.E.Int64()
	if e < 3 || e&1 == 0 {
		return nil, nil, errors.New("ssh: incorrect exponent")
	}

	key := &rsa.PublicKey{
		E: int(e),
		N: w.N,
	}
	return key, w.Rest, nil
}

// ParsePublicKey parses an SSH public key formatted for use in
// the SSH wire protocol according to RFC 4253, section 6.6.
func ParsePublicKey(in []byte) (out *rsa.PublicKey, err error) {
	algo, in, ok := parseString(in)
	if !ok {
		return nil, errors.New("ssh: short read")
	}
	if string(algo) != "ssh-rsa" {
		return nil, fmt.Errorf("ssh: unsupported key type %q", algo)
	}
	var rest []byte
	out, rest, err = parseRSA(in)
	if len(rest) > 0 {
		return nil, errors.New("ssh: trailing junk in public key")
	}

	return out, err
}

// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
// (see sshd(8) manual page) once the options and key type fields have been
// removed.
func parseAuthorizedKey(in []byte) (out *rsa.PublicKey, comment string, err error) {
	in = bytes.TrimSpace(in)

	i := bytes.IndexAny(in, " \t")
	if i == -1 {
		i = len(in)
	}
	base64Key := in[:i]

	key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key)))
	n, err := base64.StdEncoding.Decode(key, base64Key)
	if err != nil {
		return nil, "", err
	}
	key = key[:n]
	out, err = ParsePublicKey(key)
	if err != nil {
		return nil, "", err
	}
	comment = string(bytes.TrimSpace(in[i:]))
	return out, comment, nil
}

// ParseAuthorizedKeys parses a public key from an authorized_keys
// file used in OpenSSH according to the sshd(8) manual page.
func ParseAuthorizedKey(in []byte) (out *rsa.PublicKey, comment string, options []string, rest []byte, err error) {
	for len(in) > 0 {
		end := bytes.IndexByte(in, '\n')
		if end != -1 {
			rest = in[end+1:]
			in = in[:end]
		} else {
			rest = nil
		}

		end = bytes.IndexByte(in, '\r')
		if end != -1 {
			in = in[:end]
		}

		in = bytes.TrimSpace(in)
		if len(in) == 0 || in[0] == '#' {
			in = rest
			continue
		}

		i := bytes.IndexAny(in, " \t")
		if i == -1 {
			in = rest
			continue
		}

		if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
			return out, comment, options, rest, nil
		}

		// No key type recognised. Maybe there's an options field at
		// the beginning.
		var b byte
		inQuote := false
		var candidateOptions []string
		optionStart := 0
		for i, b = range in {
			isEnd := !inQuote && (b == ' ' || b == '\t')
			if (b == ',' && !inQuote) || isEnd {
				if i-optionStart > 0 {
					candidateOptions = append(candidateOptions, string(in[optionStart:i]))
				}
				optionStart = i + 1
			}
			if isEnd {
				break
			}
			if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) {
				inQuote = !inQuote
			}
		}
		for i < len(in) && (in[i] == ' ' || in[i] == '\t') {
			i++
		}
		if i == len(in) {
			// Invalid line: unmatched quote
			in = rest
			continue
		}

		in = in[i:]
		i = bytes.IndexAny(in, " \t")
		if i == -1 {
			in = rest
			continue
		}

		if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
			options = candidateOptions
			return out, comment, options, rest, nil
		}

		in = rest
		continue
	}

	return nil, "", nil, nil, errors.New("ssh: no key found")
}

// ParsePrivateKey returns a private key from a PEM encoded private key. It
// supports RSA (PKCS#1)
func ParsePrivateKey(pemBytes []byte) (key *rsa.PrivateKey, err error) {
	block, _ := pem.Decode(pemBytes)
	if block == nil {
		return nil, errors.New("ssh: no key found")
	}

	switch block.Type {
	case "RSA PRIVATE KEY":
		return x509.ParsePKCS1PrivateKey(block.Bytes)
	default:
		return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
	}
}

func openPubKey(path string) (out []*rsa.PublicKey, err error) {
	file, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		key, _, _, _, err := ParseAuthorizedKey(scanner.Bytes())
		if err != nil {
			fmt.Printf("Could not read key")
			continue
		}
		out = append(out, key)

	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
	}

	return out, err

}

func openPrivKey(path string) (key *rsa.PrivateKey, err error) {
	file, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	fileInfo, err := file.Stat()
	if err != nil {
		panic(err)
	}
	buf := make([]byte, fileInfo.Size())
	_, err = file.Read(buf)
	if err != nil {
		panic(err)
	}

	return ParsePrivateKey(buf)

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"time"
)

func status(w http.ResponseWriter, r *http.Request) {
	storage.Lock()
	defer storage.Unlock()

	log.Printf("serving status to %s", r.RemoteAddr)

	now := time.Now()

	for _, md := range storage.Files {
		if md.Expire.After(now) {
			fmt.Fprintf(w, "%s %s %s\n", md.Hash, md.Expire.Sub(now), md.Filename)
		}
	}
}
<commit_msg>restrict status output to sender address<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"time"
)

func status(w http.ResponseWriter, r *http.Request) {
	storage.Lock()
	defer storage.Unlock()

	log.Printf("serving status to %s", r.RemoteAddr)

	now := time.Now()

	from, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		log.Printf("split: %v", err)
		w.Header().Set("Content-Type", "text/html")
		return
	}

	for _, md := range storage.Files {
		if md.From == from && md.Expire.After(now) {
			rem := md.Expire.Sub(now)
			fmt.Fprintf(w, "%s %v %s\n", md.Hash, rem-(rem%time.Minute), md.Filename)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/jpillora/backoff"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/v1"
)

type LogEvent struct {
	Pod       *v1.Pod
	Container *v1.Container
	Timestamp *time.Time
	Message   string
}

type LogEventFunc func(LogEvent)

func NewContainerTailer(
	clientset *kubernetes.Clientset,
	pod v1.Pod,
	container v1.Container,
	eventFunc LogEventFunc) *ContainerTailer {
	return &ContainerTailer{
		clientset: clientset,
		pod:       pod,
		container: container,
		eventFunc: eventFunc,
	}
}

type ContainerTailer struct {
	clientset *kubernetes.Clientset
	pod       v1.Pod
	container v1.Container
	stop      bool
	eventFunc LogEventFunc
}

func (ct *ContainerTailer) Stop() {
	ct.stop = true
}

func (ct *ContainerTailer) Run() error {
	for !ct.stop {
		stream, err := ct.getStream()
		if err != nil {
			return err
		}
		if stream == nil {
			break
		}

		sc := bufio.NewScanner(stream)
		for sc.Scan() {
			ct.receiveLine(sc.Text())
		}
		_ = stream.Close()

		if err := sc.Err(); err != nil {
			return err
		}
	}
	return nil
}

func (ct *ContainerTailer) receiveLine(s string) {
	parts := strings.SplitN(s, " ", 2)

	var timestamp *time.Time
	if t, err := time.Parse(time.RFC3339Nano, parts[0]); err == nil {
		timestamp = &t
	}

	ct.eventFunc(LogEvent{
		Pod:       &ct.pod,
		Container: &ct.container,
		Timestamp: timestamp,
		Message:   parts[1],
	})
}

func (ct *ContainerTailer) getStream() (io.ReadCloser, error) {
	sinceSeconds := int64(1)

	boff := &backoff.Backoff{}
	for {
		stream, err := ct.clientset.Core().Pods(ct.pod.Namespace).GetLogs(ct.pod.Name, &v1.PodLogOptions{
			Container:    ct.container.Name,
			Follow:       true,
			Timestamps:   true,
			SinceSeconds: &sinceSeconds,
		}).Stream()
		if err != nil {
			if status, ok := err.(errors.APIStatus); ok {
				// This will happen if the pod isn't ready for log-reading yet
				switch status.Status().Code {
				case http.StatusBadRequest:
					time.Sleep(boff.Duration())
					continue
				case http.StatusNotFound:
					return nil, nil
				}
			}
			return nil, err
		}
		return stream, nil
	}
}
<commit_msg>Fix issue caused by bufio.Scanner not supporting long lines.<commit_after>package main

import (
	"bufio"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/jpillora/backoff"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/v1"
)

type LogEvent struct {
	Pod       *v1.Pod
	Container *v1.Container
	Timestamp *time.Time
	Message   string
}

type LogEventFunc func(LogEvent)

func NewContainerTailer(
	clientset *kubernetes.Clientset,
	pod v1.Pod,
	container v1.Container,
	eventFunc LogEventFunc) *ContainerTailer {
	return &ContainerTailer{
		clientset: clientset,
		pod:       pod,
		container: container,
		eventFunc: eventFunc,
	}
}

type ContainerTailer struct {
	clientset *kubernetes.Clientset
	pod       v1.Pod
	container v1.Container
	stop      bool
	eventFunc LogEventFunc
}

func (ct *ContainerTailer) Stop() {
	ct.stop = true
}

func (ct *ContainerTailer) Run() error {
	for !ct.stop {
		stream, err := ct.getStream()
		if err != nil {
			return err
		}
		if stream == nil {
			break
		}
		if err := ct.runStream(stream); err != nil {
			return err
		}
	}
	return nil
}

func (ct *ContainerTailer) runStream(stream io.ReadCloser) error {
	defer func() {
		_ = stream.Close()
	}()

	r := bufio.NewReader(stream)
	for {
		line, err := r.ReadString('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		ct.receiveLine(line)
	}
	return nil
}

func (ct *ContainerTailer) receiveLine(s string) {
	if len(s) > 0 && s[len(s)-1] == '\n' {
		s = s[0 : len(s)-1]
	}
	for len(s) > 0 && s[len(s)-1] == '\r' {
		s = s[0 : len(s)-1]
	}

	parts := strings.SplitN(s, " ", 2)

	var timestamp *time.Time
	if t, err := time.Parse(time.RFC3339Nano, parts[0]); err == nil {
		timestamp = &t
	}

	ct.eventFunc(LogEvent{
		Pod:       &ct.pod,
		Container: &ct.container,
		Timestamp: timestamp,
		Message:   parts[1],
	})
}

func (ct *ContainerTailer) getStream() (io.ReadCloser, error) {
	sinceSeconds := int64(1)

	boff := &backoff.Backoff{}
	for {
		stream, err := ct.clientset.Core().Pods(ct.pod.Namespace).GetLogs(ct.pod.Name, &v1.PodLogOptions{
			Container:    ct.container.Name,
			Follow:       true,
			Timestamps:   true,
			SinceSeconds: &sinceSeconds,
		}).Stream()
		if err != nil {
			if status, ok := err.(errors.APIStatus); ok {
				// This will happen if the pod isn't ready for log-reading yet
				switch status.Status().Code {
				case http.StatusBadRequest:
					time.Sleep(boff.Duration())
					continue
				case http.StatusNotFound:
					return nil, nil
				}
			}
			return nil, err
		}
		return stream, nil
	}
}
<|endoftext|>


<commit_before>// Package taptun provides an interface to the user level network
// TAP / TUN device.
//
// https://www.kernel.org/doc/Documentation/networking/tuntap.txt
package taptun

import (
	"io"
	"os"
	"syscall"
)

// OpenTun creates a tunN interface and returns a *Tun device connected to
// the tun interface.
func OpenTun() (*Tun, error) {
	name, f, err := openTun()
	return &Tun{
		ReadWriteCloser: f,
		name:            name,
	}, err
}

// Tun represents a TUN Virtual Point-to-Point network device.
type Tun struct {
	io.ReadWriteCloser
	name string
}

func (t *Tun) String() string {
	return t.name
}

// OpenTap creates a tapN interface and returns a *Tap device connected to
// the t pinterface.
func OpenTap() (*Tap, error) {
	name, f, err := openTap()
	return &Tap{
		ReadWriteCloser: f,
		name:            name,
	}, err
}

// Tap represents a TAP Virtual Ethernet network device.
type Tap struct {
	io.ReadWriteCloser
	name string
}

func (t *Tap) String() string {
	return t.name
}

func openTun() (string, *os.File, error) {
	return createInterface(syscall.IFF_TUN | syscall.IFF_NO_PI)
}

func openTap() (string, *os.File, error) {
	return createInterface(syscall.IFF_TAP | syscall.IFF_NO_PI)
}
<commit_msg>Added ReadPacket<commit_after>// Package taptun provides an interface to the user level network
// TAP / TUN device.
//
// https://www.kernel.org/doc/Documentation/networking/tuntap.txt
package taptun

import (
	"fmt"
	"io"
	"os"
	"syscall"
)

// OpenTun creates a tunN interface and returns a *Tun device connected to
// the tun interface.
func OpenTun() (*Tun, error) {
	name, f, err := openTun()
	return &Tun{
		ReadWriteCloser: f,
		name:            name,
	}, err
}

// Tun represents a TUN Virtual Point-to-Point network device.
type Tun struct {
	io.ReadWriteCloser
	name string
}

func (t *Tun) String() string {
	return t.name
}

// OpenTap creates a tapN interface and returns a *Tap device connected to
// the t pinterface.
func OpenTap() (*Tap, error) {
	name, f, err := openTap()
	return &Tap{
		ReadWriteCloser: f,
		name:            name,
	}, err
}

// Tap represents a TAP Virtual Ethernet network device.
type Tap struct {
	io.ReadWriteCloser
	name string
}

func (t *Tap) String() string {
	return t.name
}

func openTun() (string, *os.File, error) {
	return createInterface(syscall.IFF_TUN | syscall.IFF_NO_PI)
}

func openTap() (string, *os.File, error) {
	return createInterface(syscall.IFF_TAP | syscall.IFF_NO_PI)
}

// ErrTruncated indicates the buffer supplied to ReadFrame was insufficient
// to hold the ingress frame.
type ErrTruncated struct {
	length int
}

func (e ErrTruncated) Error() string {
	return fmt.Sprintf("supplied buffer was not large enough, frame truncated at %v", e.length)
}

// ReadFrame reads a single frame from the tap device.
// The buffer supplied must be large enough to hold the whole frame including a 4 byte header returned by the kernel.
// If the buffer is not large enough to hold the entire frame and error of type ErrTruncated will be returned.
func ReadFrame(tap *Tap, buf []byte) ([]byte, error) {
	n, err := tap.Read(buf)
	return buf[:n], err
}
<|endoftext|>


<commit_before><commit_msg>Fixed a package name changed<commit_after><|endoftext|>


<commit_before>package coretest

import (
	"fmt"
	"strings"
	"testing"
)

const (
	targetAddress = "http://127.0.0.1:4001/v2/keys/message"
	helloStr = "Hello world"
	newHelloStr = "Hello etcd"
	keyNotFound = "Key not found"
)

// TestEtcdUpdateValue tests to update value of a key.
// The test coverage includes setting, getting, updating, deleting.
func TestEtcdUpdateValue(t *testing.T) {
	stdout, stderr, err := Run("curl", "-L", targetAddress, "-XPUT", "-d", fmt.Sprintf("value=\"%s\"", helloStr))
	if err != nil {
		t.Fatalf("curl set failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}
	if !strings.Contains(stdout, helloStr) {
		t.Fatalf("Failed getting value %v\nstdout: %v", helloStr, stdout)
	}

	stdout, stderr, err = Run("curl", "-L", targetAddress, "-XPUT", "-d", fmt.Sprintf("value=\"%s\"", newHelloStr))
	if err != nil {
		t.Fatalf("curl update failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	stdout, stderr, err = Run("curl", "-L", targetAddress)
	if err != nil {
		t.Fatalf("curl get failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}
	if !strings.Contains(stdout, newHelloStr) {
		t.Fatalf("Failed getting value %v\nstdout: %v", newHelloStr, stdout)
	}

	stdout, stderr, err = Run("curl", "-L", targetAddress, "-XDELETE")
	if err != nil {
		t.Fatalf("curl delete failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	stdout, stderr, err = Run("curl", "-L", targetAddress)
	if err != nil {
		t.Fatalf("curl get failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}
	if !strings.Contains(stdout, keyNotFound) {
		t.Fatalf("Failed getting value %v\nstdout: %v", keyNotFound, stdout)
	}
}
<commit_msg>fix(etcd_test): use a random key everytime<commit_after>package coretest

import (
	"encoding/hex"
	"fmt"
	"crypto/rand"
	"strings"
	"testing"
)

const (
	targetAddress = "http://127.0.0.1:4001/v2/keys/"
	helloStr      = "Hello world"
	newHelloStr   = "Hello etcd"
	keyNotFound   = "Key not found"
)

// generateKey generate's a 16 byte random string.
func generateKey() string {
	b := make([]byte, 16)
	_, err := rand.Read(b)
	if err != nil {
		return ""
	}

	return hex.EncodeToString(b)
}

// TestEtcdUpdateValue tests to update value of a key.
// The test coverage includes setting, getting, updating, deleting.
func TestEtcdUpdateValue(t *testing.T) {
	// Use a random key name so members of a cluster don't step on each other.
	target := targetAddress + generateKey()

	stdout, stderr, err := Run("curl", "-L", target, "-XPUT", "-d", fmt.Sprintf("value=\"%s\"", helloStr))
	if err != nil {
		t.Fatalf("curl set failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}
	if !strings.Contains(stdout, helloStr) {
		t.Fatalf("Failed getting value %v\nstdout: %v", helloStr, stdout)
	}

	stdout, stderr, err = Run("curl", "-L", target, "-XPUT", "-d", fmt.Sprintf("value=\"%s\"", newHelloStr))
	if err != nil {
		t.Fatalf("curl update failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	stdout, stderr, err = Run("curl", "-L", target)
	if err != nil {
		t.Fatalf("curl get failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}
	if !strings.Contains(stdout, newHelloStr) {
		t.Fatalf("Failed getting value %v\nstdout: %v", newHelloStr, stdout)
	}

	stdout, stderr, err = Run("curl", "-L", target, "-XDELETE")
	if err != nil {
		t.Fatalf("curl delete failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}

	stdout, stderr, err = Run("curl", "-L", target)
	if err != nil {
		t.Fatalf("curl get failed with error: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}
	if !strings.Contains(stdout, keyNotFound) {
		t.Fatalf("Failed getting value %v\nstdout: %v", keyNotFound, stdout)
	}
}
<|endoftext|>


<commit_before>// Package eval handles evaluation of nodes and consists the runtime of the
// shell.
package eval

//go:generate ./gen-embedded-modules

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"unicode/utf8"

	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/store"
	"github.com/elves/elvish/sys"
	"github.com/elves/elvish/util"
)

var logger = util.GetLogger("[eval] ")

// FnPrefix is the prefix for the variable names of functions. Defining a
// function "foo" is equivalent to setting a variable named FnPrefix + "foo".
const FnPrefix = "&"

// Namespace is a map from name to variables.
type Namespace map[string]Variable

// Evaler is used to evaluate elvish sources. It maintains runtime context
// shared among all evalCtx instances.
type Evaler struct {
	Global  Namespace
	Modules map[string]Namespace
	Store   *store.Store
	Editor  Editor
	DataDir string
	intCh   chan struct{}
}

// EvalCtx maintains an Evaler along with its runtime context. After creation
// an EvalCtx is seldom modified, and new instances are created when needed.
type EvalCtx struct {
	*Evaler
	name, srcName, src string

	local, up   Namespace
	ports       []*Port
	positionals []Value

	begin, end int
	traceback  *util.SourceContext

	background bool
}

// NewEvaler creates a new Evaler.
func NewEvaler(st *store.Store, dataDir string) *Evaler {
	return &Evaler{Namespace{}, map[string]Namespace{}, st, nil, dataDir, nil}
}

func (ev *Evaler) searchPaths() []string {
	return builtinNamespace["paths"].(*EnvPathList).get()
}

const (
	outChanSize    = 32
	outChanLeader  = "▶ "
	falseIndicator = "✗"
	initIndent     = NoPretty
)

// NewTopEvalCtx creates a top-level evalCtx.
func NewTopEvalCtx(ev *Evaler, name, text string, ports []*Port) *EvalCtx {
	return &EvalCtx{
		ev, "top",
		name, text,
		ev.Global, Namespace{},
		ports, nil,
		0, len(text), nil, false,
	}
}

// fork returns a modified copy of ec. The ports are forked, and the name is
// changed to the given value. Other fields are copied shallowly.
func (ec *EvalCtx) fork(name string) *EvalCtx {
	newPorts := make([]*Port, len(ec.ports))
	for i, p := range ec.ports {
		newPorts[i] = p.Fork()
	}
	return &EvalCtx{
		ec.Evaler, name,
		ec.srcName, ec.src,
		ec.local, ec.up,
		newPorts, ec.positionals,
		ec.begin, ec.end, ec.traceback, ec.background,
	}
}

// port returns ec.ports[i] or nil if i is out of range. This makes it possible
// to treat ec.ports as if it has an infinite tail of nil's.
func (ec *EvalCtx) port(i int) *Port {
	if i >= len(ec.ports) {
		return nil
	}
	return ec.ports[i]
}

// growPorts makes the size of ec.ports at least n, adding nil's if necessary.
func (ec *EvalCtx) growPorts(n int) {
	if len(ec.ports) >= n {
		return
	}
	ports := ec.ports
	ec.ports = make([]*Port, n)
	copy(ec.ports, ports)
}

func makeScope(s Namespace) scope {
	sc := scope{}
	for name := range s {
		sc[name] = true
	}
	return sc
}

// eval evaluates a chunk node n. The supplied name and text are used in
// diagnostic messages.
func (ev *Evaler) eval(op Op, ports []*Port, name, text string) error {
	ec := NewTopEvalCtx(ev, name, text, ports)
	return ec.PEval(op)
}

func (ec *EvalCtx) Interrupts() <-chan struct{} {
	return ec.intCh
}

// Eval sets up the Evaler and evaluates a chunk. The supplied name and text are
// used in diagnostic messages.
func (ev *Evaler) Eval(op Op, name, text string) error {
	inCh := make(chan Value)
	close(inCh)

	outCh := make(chan Value, outChanSize)
	outDone := make(chan struct{})
	go func() {
		for v := range outCh {
			fmt.Printf("%s%s\n", outChanLeader, v.Repr(initIndent))
		}
		close(outDone)
	}()

	ports := []*Port{
		{File: os.Stdin, Chan: inCh},
		{File: os.Stdout, Chan: outCh},
		{File: os.Stderr, Chan: BlackholeChan},
	}

	// signal.Ignore(syscall.SIGTTIN)

	// Ignore TTOU.
	// When a subprocess in its own process group puts itself in the foreground,
	// the elvish will be in the background. In that case, elvish will move
	// itself back to the foreground by calling tcsetpgrp. However, whenever a
	// background process calls tcsetpgrp (or otherwise attempts to modify the
	// terminal configuration), TTOU will be sent, whose default handler is to
	// stop the process. When the process lives in an orphaned process group
	// (most likely for elvish), the call will outright fail. Therefore, for
	// elvish to be able to move itself back to the foreground, we need to
	// ignore TTOU.
	signal.Ignore(syscall.SIGTTOU)
	stopSigGoroutine := make(chan struct{})
	sigGoRoutineDone := make(chan struct{})
	// Set up intCh.
	ev.intCh = make(chan struct{})
	sigCh := make(chan os.Signal)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGQUIT)
	go func() {
		closedIntCh := false
	loop:
		for {
			select {
			case <-sigCh:
				if !closedIntCh {
					close(ev.intCh)
					closedIntCh = true
				}
			case <-stopSigGoroutine:
				break loop
			}
		}
		ev.intCh = nil
		signal.Stop(sigCh)
		close(sigGoRoutineDone)
	}()

	err := ev.eval(op, ports, name, text)
	close(outCh)
	<-outDone
	close(stopSigGoroutine)
	<-sigGoRoutineDone

	// Put myself in foreground, in case some command has put me in background.
	// XXX Should probably use fd of /dev/tty instead of 0.
	if sys.IsATTY(0) {
		err := sys.Tcsetpgrp(0, syscall.Getpgrp())
		if err != nil {
			fmt.Println("failed to put myself in foreground:", err)
		}
	}

	// Un-ignore TTOU.
	signal.Ignore(syscall.SIGTTOU)

	return err
}

func summarize(text string) string {
	// TODO Make a proper summary.
	if len(text) < 32 {
		return text
	}
	var b bytes.Buffer
	for i, r := range text {
		if i+len(string(r)) >= 32 {
			break
		}
		b.WriteRune(r)
	}
	return b.String()
}

// Compile compiles elvish code in the global scope. If the error is not nil, it
// always has type CompilationError.
func (ev *Evaler) Compile(n *parse.Chunk, name, text string) (Op, error) {
	return compile(makeScope(ev.Global), n, name, text)
}

// PEval evaluates an op in a protected environment so that calls to errorf are
// wrapped in an Error.
func (ec *EvalCtx) PEval(op Op) (err error) {
	defer catch(&err, ec)
	op.Exec(ec)
	return nil
}

func (ec *EvalCtx) PCall(f Callable, args []Value, opts map[string]Value) (err error) {
	defer catch(&err, ec)
	f.Call(ec, args, opts)
	return nil
}

func (ec *EvalCtx) PCaptureOutput(f Callable, args []Value, opts map[string]Value) (vs []Value, err error) {
	// XXX There is no source.
	return pcaptureOutput(ec, Op{
		func(newec *EvalCtx) { f.Call(newec, args, opts) }, -1, -1})
}

func catch(perr *error, ec *EvalCtx) {
	// NOTE: We have to duplicate instead of calling util.Catch here, since
	// recover can only catch a panic when called directly from a deferred
	// function.
	r := recover()
	if r == nil {
		return
	}
	if exc, ok := r.(util.Thrown); ok {
		err := exc.Error
		if _, ok := err.(*Exception); !ok {
			err = ec.makeException(err)
		}
		*perr = err
	} else if r != nil {
		panic(r)
	}
}

// makeException turns an error into an Exception by adding traceback.
func (ec *EvalCtx) makeException(e error) *Exception {
	return &Exception{e, ec.addTraceback()}
}

func (ec *EvalCtx) addTraceback() *util.SourceContext {
	return &util.SourceContext{
		Name: ec.srcName, Source: ec.src,
		Begin: ec.begin, End: ec.end, Next: ec.traceback,
	}
}

// errorpf stops the ec.eval immediately by panicking with a diagnostic message.
// The panic is supposed to be caught by ec.eval.
func (ec *EvalCtx) errorpf(begin, end int, format string, args ...interface{}) {
	ec.begin, ec.end = begin, end
	throwf(format, args...)
}

// SourceText evaluates a chunk of elvish source.
func (ev *Evaler) SourceText(name, src string) error {
	n, err := parse.Parse(name, src)
	if err != nil {
		return err
	}
	op, err := ev.Compile(n, name, src)
	if err != nil {
		return err
	}
	return ev.Eval(op, name, src)
}

func readFileUTF8(fname string) (string, error) {
	bytes, err := ioutil.ReadFile(fname)
	if err != nil {
		return "", err
	}
	if !utf8.Valid(bytes) {
		return "", fmt.Errorf("%s: source is not valid UTF-8", fname)
	}
	return string(bytes), nil
}

// Source evaluates the content of a file.
func (ev *Evaler) Source(fname string) error {
	src, err := readFileUTF8(fname)
	if err != nil {
		return err
	}
	return ev.SourceText(fname, src)
}

// Builtin returns the builtin namespace.
func Builtin() Namespace {
	return builtinNamespace
}

// ErrStoreUnconnected is thrown by ResolveVar when a shared: variable needs to
// be resolved but the store is not connected.
var ErrStoreUnconnected = errors.New("store unconnected")

// ResolveVar resolves a variable. When the variable cannot be found, nil is
// returned.
func (ec *EvalCtx) ResolveVar(ns, name string) Variable {
	switch ns {
	case "local":
		return ec.getLocal(name)
	case "up":
		return ec.up[name]
	case "builtin":
		return builtinNamespace[name]
	case "":
		if v := ec.getLocal(name); v != nil {
			return v
		}
		if v, ok := ec.up[name]; ok {
			return v
		}
		return builtinNamespace[name]
	case "e":
		if strings.HasPrefix(name, FnPrefix) {
			return NewRoVariable(ExternalCmd{name[len(FnPrefix):]})
		}
	case "E":
		return envVariable{name}
	case "shared":
		if ec.Store == nil {
			throw(ErrStoreUnconnected)
		}
		return sharedVariable{ec.Store, name}
	default:
		if ns, ok := ec.Modules[ns]; ok {
			return ns[name]
		}
	}
	return nil
}

// getLocal finds the named local variable.
func (ec *EvalCtx) getLocal(name string) Variable {
	i, err := strconv.Atoi(name)
	if err == nil {
		// Logger.Println("positional variable", i)
		// Logger.Printf("EvalCtx=%p, args=%v", ec, ec.positionals)
		if i < 0 {
			i += len(ec.positionals)
		}
		if i < 0 || i >= len(ec.positionals) {
			// Logger.Print("out of range")
			return nil
		}
		// Logger.Print("found")
		return NewRoVariable(ec.positionals[i])
	}
	return ec.local[name]
}

var ErrMoreThanOneRest = errors.New("more than one @ lvalue")

// IterateInputs calls the passed function for each input element.
func (ec *EvalCtx) IterateInputs(f func(Value)) {
	var w sync.WaitGroup
	inputs := make(chan Value)

	w.Add(2)
	go func() {
		linesToChan(ec.ports[0].File, inputs)
		w.Done()
	}()
	go func() {
		for v := range ec.ports[0].Chan {
			inputs <- v
		}
		w.Done()
	}()
	go func() {
		w.Wait()
		close(inputs)
	}()

	for v := range inputs {
		f(v)
	}
}

func linesToChan(r io.Reader, ch chan<- Value) {
	filein := bufio.NewReader(r)
	for {
		line, err := filein.ReadString('\n')
		if line != "" {
			ch <- String(strings.TrimSuffix(line, "\n"))
		}
		if err != nil {
			if err != io.EOF {
				logger.Println("error on reading:", err)
			}
			break
		}
	}
}

// OutputChan returns a channel onto which output can be written.
func (ec *EvalCtx) OutputChan() chan<- Value {
	return ec.ports[1].Chan
}
<commit_msg>eval: Un-ignore signal correctly.<commit_after>// Package eval handles evaluation of nodes and consists the runtime of the
// shell.
package eval

//go:generate ./gen-embedded-modules

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"unicode/utf8"

	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/store"
	"github.com/elves/elvish/sys"
	"github.com/elves/elvish/util"
)

var logger = util.GetLogger("[eval] ")

// FnPrefix is the prefix for the variable names of functions. Defining a
// function "foo" is equivalent to setting a variable named FnPrefix + "foo".
const FnPrefix = "&"

// Namespace is a map from name to variables.
type Namespace map[string]Variable

// Evaler is used to evaluate elvish sources. It maintains runtime context
// shared among all evalCtx instances.
type Evaler struct {
	Global  Namespace
	Modules map[string]Namespace
	Store   *store.Store
	Editor  Editor
	DataDir string
	intCh   chan struct{}
}

// EvalCtx maintains an Evaler along with its runtime context. After creation
// an EvalCtx is seldom modified, and new instances are created when needed.
type EvalCtx struct {
	*Evaler
	name, srcName, src string

	local, up   Namespace
	ports       []*Port
	positionals []Value

	begin, end int
	traceback  *util.SourceContext

	background bool
}

// NewEvaler creates a new Evaler.
func NewEvaler(st *store.Store, dataDir string) *Evaler {
	return &Evaler{Namespace{}, map[string]Namespace{}, st, nil, dataDir, nil}
}

func (ev *Evaler) searchPaths() []string {
	return builtinNamespace["paths"].(*EnvPathList).get()
}

const (
	outChanSize    = 32
	outChanLeader  = "▶ "
	falseIndicator = "✗"
	initIndent     = NoPretty
)

// NewTopEvalCtx creates a top-level evalCtx.
func NewTopEvalCtx(ev *Evaler, name, text string, ports []*Port) *EvalCtx {
	return &EvalCtx{
		ev, "top",
		name, text,
		ev.Global, Namespace{},
		ports, nil,
		0, len(text), nil, false,
	}
}

// fork returns a modified copy of ec. The ports are forked, and the name is
// changed to the given value. Other fields are copied shallowly.
func (ec *EvalCtx) fork(name string) *EvalCtx {
	newPorts := make([]*Port, len(ec.ports))
	for i, p := range ec.ports {
		newPorts[i] = p.Fork()
	}
	return &EvalCtx{
		ec.Evaler, name,
		ec.srcName, ec.src,
		ec.local, ec.up,
		newPorts, ec.positionals,
		ec.begin, ec.end, ec.traceback, ec.background,
	}
}

// port returns ec.ports[i] or nil if i is out of range. This makes it possible
// to treat ec.ports as if it has an infinite tail of nil's.
func (ec *EvalCtx) port(i int) *Port {
	if i >= len(ec.ports) {
		return nil
	}
	return ec.ports[i]
}

// growPorts makes the size of ec.ports at least n, adding nil's if necessary.
func (ec *EvalCtx) growPorts(n int) {
	if len(ec.ports) >= n {
		return
	}
	ports := ec.ports
	ec.ports = make([]*Port, n)
	copy(ec.ports, ports)
}

func makeScope(s Namespace) scope {
	sc := scope{}
	for name := range s {
		sc[name] = true
	}
	return sc
}

// eval evaluates a chunk node n. The supplied name and text are used in
// diagnostic messages.
func (ev *Evaler) eval(op Op, ports []*Port, name, text string) error {
	ec := NewTopEvalCtx(ev, name, text, ports)
	return ec.PEval(op)
}

func (ec *EvalCtx) Interrupts() <-chan struct{} {
	return ec.intCh
}

// Eval sets up the Evaler and evaluates a chunk. The supplied name and text are
// used in diagnostic messages.
func (ev *Evaler) Eval(op Op, name, text string) error {
	inCh := make(chan Value)
	close(inCh)

	outCh := make(chan Value, outChanSize)
	outDone := make(chan struct{})
	go func() {
		for v := range outCh {
			fmt.Printf("%s%s\n", outChanLeader, v.Repr(initIndent))
		}
		close(outDone)
	}()

	ports := []*Port{
		{File: os.Stdin, Chan: inCh},
		{File: os.Stdout, Chan: outCh},
		{File: os.Stderr, Chan: BlackholeChan},
	}

	// signal.Ignore(syscall.SIGTTIN)

	// Ignore TTOU.
	// When a subprocess in its own process group puts itself in the foreground,
	// the elvish will be in the background. In that case, elvish will move
	// itself back to the foreground by calling tcsetpgrp. However, whenever a
	// background process calls tcsetpgrp (or otherwise attempts to modify the
	// terminal configuration), TTOU will be sent, whose default handler is to
	// stop the process. When the process lives in an orphaned process group
	// (most likely for elvish), the call will outright fail. Therefore, for
	// elvish to be able to move itself back to the foreground, we need to
	// ignore TTOU.
	signal.Ignore(syscall.SIGTTOU)
	stopSigGoroutine := make(chan struct{})
	sigGoRoutineDone := make(chan struct{})
	// Set up intCh.
	ev.intCh = make(chan struct{})
	sigCh := make(chan os.Signal)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGQUIT)
	go func() {
		closedIntCh := false
	loop:
		for {
			select {
			case <-sigCh:
				if !closedIntCh {
					close(ev.intCh)
					closedIntCh = true
				}
			case <-stopSigGoroutine:
				break loop
			}
		}
		ev.intCh = nil
		signal.Stop(sigCh)
		close(sigGoRoutineDone)
	}()

	err := ev.eval(op, ports, name, text)
	close(outCh)
	<-outDone
	close(stopSigGoroutine)
	<-sigGoRoutineDone

	// Put myself in foreground, in case some command has put me in background.
	// XXX Should probably use fd of /dev/tty instead of 0.
	if sys.IsATTY(0) {
		err := sys.Tcsetpgrp(0, syscall.Getpgrp())
		if err != nil {
			fmt.Println("failed to put myself in foreground:", err)
		}
	}

	// Un-ignore TTOU.
	signal.Reset(syscall.SIGTTOU)

	return err
}

func summarize(text string) string {
	// TODO Make a proper summary.
	if len(text) < 32 {
		return text
	}
	var b bytes.Buffer
	for i, r := range text {
		if i+len(string(r)) >= 32 {
			break
		}
		b.WriteRune(r)
	}
	return b.String()
}

// Compile compiles elvish code in the global scope. If the error is not nil, it
// always has type CompilationError.
func (ev *Evaler) Compile(n *parse.Chunk, name, text string) (Op, error) {
	return compile(makeScope(ev.Global), n, name, text)
}

// PEval evaluates an op in a protected environment so that calls to errorf are
// wrapped in an Error.
func (ec *EvalCtx) PEval(op Op) (err error) {
	defer catch(&err, ec)
	op.Exec(ec)
	return nil
}

func (ec *EvalCtx) PCall(f Callable, args []Value, opts map[string]Value) (err error) {
	defer catch(&err, ec)
	f.Call(ec, args, opts)
	return nil
}

func (ec *EvalCtx) PCaptureOutput(f Callable, args []Value, opts map[string]Value) (vs []Value, err error) {
	// XXX There is no source.
	return pcaptureOutput(ec, Op{
		func(newec *EvalCtx) { f.Call(newec, args, opts) }, -1, -1})
}

func catch(perr *error, ec *EvalCtx) {
	// NOTE: We have to duplicate instead of calling util.Catch here, since
	// recover can only catch a panic when called directly from a deferred
	// function.
	r := recover()
	if r == nil {
		return
	}
	if exc, ok := r.(util.Thrown); ok {
		err := exc.Error
		if _, ok := err.(*Exception); !ok {
			err = ec.makeException(err)
		}
		*perr = err
	} else if r != nil {
		panic(r)
	}
}

// makeException turns an error into an Exception by adding traceback.
func (ec *EvalCtx) makeException(e error) *Exception {
	return &Exception{e, ec.addTraceback()}
}

func (ec *EvalCtx) addTraceback() *util.SourceContext {
	return &util.SourceContext{
		Name: ec.srcName, Source: ec.src,
		Begin: ec.begin, End: ec.end, Next: ec.traceback,
	}
}

// errorpf stops the ec.eval immediately by panicking with a diagnostic message.
// The panic is supposed to be caught by ec.eval.
func (ec *EvalCtx) errorpf(begin, end int, format string, args ...interface{}) {
	ec.begin, ec.end = begin, end
	throwf(format, args...)
}

// SourceText evaluates a chunk of elvish source.
func (ev *Evaler) SourceText(name, src string) error {
	n, err := parse.Parse(name, src)
	if err != nil {
		return err
	}
	op, err := ev.Compile(n, name, src)
	if err != nil {
		return err
	}
	return ev.Eval(op, name, src)
}

func readFileUTF8(fname string) (string, error) {
	bytes, err := ioutil.ReadFile(fname)
	if err != nil {
		return "", err
	}
	if !utf8.Valid(bytes) {
		return "", fmt.Errorf("%s: source is not valid UTF-8", fname)
	}
	return string(bytes), nil
}

// Source evaluates the content of a file.
func (ev *Evaler) Source(fname string) error {
	src, err := readFileUTF8(fname)
	if err != nil {
		return err
	}
	return ev.SourceText(fname, src)
}

// Builtin returns the builtin namespace.
func Builtin() Namespace {
	return builtinNamespace
}

// ErrStoreUnconnected is thrown by ResolveVar when a shared: variable needs to
// be resolved but the store is not connected.
var ErrStoreUnconnected = errors.New("store unconnected")

// ResolveVar resolves a variable. When the variable cannot be found, nil is
// returned.
func (ec *EvalCtx) ResolveVar(ns, name string) Variable {
	switch ns {
	case "local":
		return ec.getLocal(name)
	case "up":
		return ec.up[name]
	case "builtin":
		return builtinNamespace[name]
	case "":
		if v := ec.getLocal(name); v != nil {
			return v
		}
		if v, ok := ec.up[name]; ok {
			return v
		}
		return builtinNamespace[name]
	case "e":
		if strings.HasPrefix(name, FnPrefix) {
			return NewRoVariable(ExternalCmd{name[len(FnPrefix):]})
		}
	case "E":
		return envVariable{name}
	case "shared":
		if ec.Store == nil {
			throw(ErrStoreUnconnected)
		}
		return sharedVariable{ec.Store, name}
	default:
		if ns, ok := ec.Modules[ns]; ok {
			return ns[name]
		}
	}
	return nil
}

// getLocal finds the named local variable.
func (ec *EvalCtx) getLocal(name string) Variable {
	i, err := strconv.Atoi(name)
	if err == nil {
		// Logger.Println("positional variable", i)
		// Logger.Printf("EvalCtx=%p, args=%v", ec, ec.positionals)
		if i < 0 {
			i += len(ec.positionals)
		}
		if i < 0 || i >= len(ec.positionals) {
			// Logger.Print("out of range")
			return nil
		}
		// Logger.Print("found")
		return NewRoVariable(ec.positionals[i])
	}
	return ec.local[name]
}

var ErrMoreThanOneRest = errors.New("more than one @ lvalue")

// IterateInputs calls the passed function for each input element.
func (ec *EvalCtx) IterateInputs(f func(Value)) {
	var w sync.WaitGroup
	inputs := make(chan Value)

	w.Add(2)
	go func() {
		linesToChan(ec.ports[0].File, inputs)
		w.Done()
	}()
	go func() {
		for v := range ec.ports[0].Chan {
			inputs <- v
		}
		w.Done()
	}()
	go func() {
		w.Wait()
		close(inputs)
	}()

	for v := range inputs {
		f(v)
	}
}

func linesToChan(r io.Reader, ch chan<- Value) {
	filein := bufio.NewReader(r)
	for {
		line, err := filein.ReadString('\n')
		if line != "" {
			ch <- String(strings.TrimSuffix(line, "\n"))
		}
		if err != nil {
			if err != io.EOF {
				logger.Println("error on reading:", err)
			}
			break
		}
	}
}

// OutputChan returns a channel onto which output can be written.
func (ec *EvalCtx) OutputChan() chan<- Value {
	return ec.ports[1].Chan
}
<|endoftext|>


<commit_before>package main

import "testing"

func TestInsufficientMaterial(t *testing.T) {
	for _, fen := range []string{
		"K6k/8/8/8/8/8/8/8 w - - 0 1",
		"KN5k/8/8/8/8/8/8/8 w - - 0 1",
		"KB5k/8/8/8/8/8/8/8 w - - 0 1",
		"KB1b3k/8/8/8/8/8/8/8 w - - 0 1",
		"K6k/8/8/8/8/8/8/1B1B1B1B w - - 0 1",
		"K1b1b1bk/8/8/8/8/8/8/1B1B1B1B w - - 0 1",
	} {
		pos, err := ParseFEN(fen)
		if err != nil {
			panic(err)
		}
		if got := Eval(pos); got != 0 {
			t.Errorf("Insufficient Material: Eval(%v)==%v, want 0", fen, got)
		}
	}
}

func BenchmarkEval(b *testing.B) {
	pos, err := ParseFEN("r1b2rkB/1pp1ppbp/2n3p1/8/PpP3nP/8/3KPP2/1N3BNR w - - 0 13")
	if err != nil {
		b.Fatal(err)
	}
	for i := 0; i < b.N; i++ {
		Eval(pos)
	}
}
<commit_msg>add insufficient material tests<commit_after>package main

import "testing"

func TestIsInsufficient(t *testing.T) {
	for _, test := range []struct {
		fen  string
		want bool
	}{
		{"K6k/8/8/8/8/8/8/8 w - - 0 1", true},
		{"KN5k/8/8/8/8/8/8/8 w - - 0 1", true},
		{"KB5k/8/8/8/8/8/8/8 w - - 0 1", true},
		{"KB1b3k/8/8/8/8/8/8/8 w - - 0 1", true},
		{"K6k/8/8/8/8/8/8/1B1B1B1B w - - 0 1", true},
		{"K1b1b1bk/8/8/8/8/8/8/1B1B1B1B w - - 0 1", true},
		{"KN4nk/8/8/8/8/8/8/8 w - - 0 1", false},
		{"KNN4k/8/8/8/8/8/8/8 w - - 0 1", false},
		{"KNB4k/8/8/8/8/8/8/8 w - - 0 1", false},
		{"KNb4k/8/8/8/8/8/8/8 w - - 0 1", false},
		{"K6k/P7/8/8/8/8/8/8 w - - 0 1", false},
		{"K6k/R7/8/8/8/8/8/8 w - - 0 1", false},
		{"K6k/Q7/8/8/8/8/8/8 w - - 0 1", false},
		{InitialPositionFEN, false},
	} {
		pos, err := ParseFEN(test.fen)
		if err != nil {
			t.Fatal(err)
		}
		npawns := PopCount(pos.b[White][Pawn] | pos.b[Black][Pawn])
		nknights := PopCount(pos.b[White][Knight] | pos.b[Black][Knight])
		nbishops := PopCount(pos.b[White][Bishop] | pos.b[Black][Bishop])
		nrooks := PopCount(pos.b[White][Rook] | pos.b[Black][Rook])
		nqueens := PopCount(pos.b[White][Queen] | pos.b[Black][Queen])

		if got := IsInsufficient(pos, npawns, nknights, nbishops, nrooks, nqueens); got != test.want {
			t.Errorf("IsInsufficient(%v): got %v, want %v", test.fen, got, test.want)
		}
	}
}

func BenchmarkEval(b *testing.B) {
	pos, err := ParseFEN("r1b2rkB/1pp1ppbp/2n3p1/8/PpP3nP/8/3KPP2/1N3BNR w - - 0 13")
	if err != nil {
		b.Fatal(err)
	}
	for i := 0; i < b.N; i++ {
		Eval(pos)
	}
}
<|endoftext|>


<commit_before>package base

import (
	"bytes"
	"fmt"
	"regexp"
	"time"
)

type ReadCallback struct {
	Callback           func(*Driver, string) error
	Contains           string
	containsBytes      []byte
	ContainsRe         string
	containsReCompiled *regexp.Regexp
	CaseInsensitive    bool
	MultiLine          bool
	ResetOutput        bool
	Complete           bool
	Name               string
}

func (r *ReadCallback) contains() []byte {
	if len(r.containsBytes) == 0 {
		r.containsBytes = []byte(r.Contains)
	}

	return r.containsBytes
}

func (r *ReadCallback) containsRe() *regexp.Regexp {
	if r.containsReCompiled == nil {
		flags := ""

		if r.CaseInsensitive && r.MultiLine {
			flags = "(?im)"
		} else if r.CaseInsensitive {
			flags = "(?i)"
		} else if r.MultiLine {
			flags = "(?m)"
		}

		r.containsReCompiled = regexp.MustCompile(fmt.Sprintf(`%s%s`, flags, r.contains()))
	}

	return r.containsReCompiled
}

func (d *Driver) ReadWithCallbacks( //nolint:gocognit
	callbacks []*ReadCallback,
	input string,
	output []byte,
	sleep time.Duration,
) error {
	if input != "" {
		err := d.Channel.WriteAndReturn([]byte(input), false)
		if err != nil {
			return err
		}

		return d.ReadWithCallbacks(callbacks, "", nil, sleep)
	}

	for {
		newOutput, err := d.Channel.Read()
		if err != nil {
			return err
		}

		output = append(output, newOutput...)

		for _, callback := range callbacks {
			o := output
			if callback.CaseInsensitive {
				o = bytes.ToLower(output)
			}

			if (callback.Contains != "" && bytes.Contains(o, callback.contains())) ||
				(callback.ContainsRe != "" && callback.containsRe().Match(o)) {
				err = callback.Callback(d, string(output))
				if err != nil {
					return err
				}

				if callback.Complete {
					return nil
				}

				if callback.ResetOutput {
					output = []byte{}
				}

				return d.ReadWithCallbacks(callbacks, "", output, sleep)
			}
		}

		time.Sleep(sleep)
	}
}
<commit_msg>more playing w/ read callback thing<commit_after>package base

import (
	"bytes"
	"errors"
	"fmt"
	"regexp"
	"time"
)

var ErrCallbackAlreadyTriggered = errors.New("callback set to 'OnlyOnce', but already triggered")

type ReadCallback struct {
	Callback           func(*Driver, string) error
	Contains           string
	containsBytes      []byte
	ContainsRe         string
	containsReCompiled *regexp.Regexp
	CaseInsensitive    bool
	MultiLine          bool
	// ResetOutput bool indicating if the output should be reset or not after callback execution.
	ResetOutput bool
	// OnlyOnce bool indicating if this callback should be executed only one time.
	OnlyOnce  bool
	triggered bool
	Complete  bool
	Name      string
}

func (r *ReadCallback) contains() []byte {
	if len(r.containsBytes) == 0 {
		r.containsBytes = []byte(r.Contains)
	}

	return r.containsBytes
}

func (r *ReadCallback) containsRe() *regexp.Regexp {
	if r.containsReCompiled == nil {
		flags := ""

		if r.CaseInsensitive && r.MultiLine {
			flags = "(?im)"
		} else if r.CaseInsensitive {
			flags = "(?i)"
		} else if r.MultiLine {
			flags = "(?m)"
		}

		r.containsReCompiled = regexp.MustCompile(fmt.Sprintf(`%s%s`, flags, r.ContainsRe))
	}

	return r.containsReCompiled
}

func (d *Driver) executeCallback(
	i int,
	callbacks []*ReadCallback,
	output []byte,
	readDelay time.Duration) error {
	callback := callbacks[i]

	if callback.OnlyOnce {
		if callback.triggered {
			return ErrCallbackAlreadyTriggered
		}

		callback.triggered = true
	}

	err := callback.Callback(d, string(output))
	if err != nil {
		return err
	}

	if callback.Complete {
		return nil
	}

	if callback.ResetOutput {
		output = []byte{}
	}

	return d.readWithCallbacks(callbacks, output, readDelay)
}

func (d *Driver) readWithCallbacks(
	callbacks []*ReadCallback,
	output []byte,
	readDelay time.Duration,
) error {
	for {
		newOutput, err := d.Channel.Read()
		if err != nil {
			return err
		}

		output = append(output, newOutput...)

		for i, callback := range callbacks {
			o := output
			if callback.CaseInsensitive {
				o = bytes.ToLower(output)
			}

			if (callback.Contains != "" && bytes.Contains(o, callback.contains())) ||
				(callback.ContainsRe != "" && callback.containsRe().Match(o)) {
				return d.executeCallback(i, callbacks, output, readDelay)
			}
		}

		time.Sleep(readDelay)
	}
}

func (d *Driver) ReadWithCallbacks(
	callbacks []*ReadCallback,
	input string,
	readDelay time.Duration,
) error {
	if input != "" {
		err := d.Channel.WriteAndReturn([]byte(input), false)
		if err != nil {
			return err
		}
	}

	return d.readWithCallbacks(callbacks, []byte{}, readDelay)
}
<|endoftext|>