<commit_before>package utils

import (
	"bytes"
	"encoding/hex"
	"testing"
)

func hexDump(data []byte) string {
	if data == nil {
		return "nil"
	}

	if len(data) > 8 {
		return hex.EncodeToString(data[:8]) + "..."
	}

	return hex.EncodeToString(data)
}

var intSerializationData = []struct {
	value      uint64
	serialized []byte
}{
	{0x00, []byte{0x00}},
	{0x01, []byte{0x01}},
	{0x02, []byte{0x02}},
	{0x7F, []byte{0x7F}},
	{0x80, []byte{0x80, 0x01}},
	{0x81, []byte{0x81, 0x01}},
	{0x100, []byte{0x80, 0x02}},
	{0xFFFFFFFFFFFFFFFF, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01}},
}

func TestIntSerialization(t *testing.T) {

	for _, tst := range intSerializationData {

		var b bytes.Buffer

		err := SerializeInt(tst.value, &b)

		if err != nil {
			t.Fatalf("Error while serializing int: %v", err)
			continue
		}

		s := b.Bytes()
		if !bytes.Equal(s, tst.serialized) {
			t.Fatalf(
				"Serialization of value %v failed, data not equal (%v vs %v)",
				tst.value,
				hexDump(s),
				hexDump(tst.serialized))
			continue
		}

		// Try to serialize back
		d, err := DeserializeInt(&b)
		if err != nil {
			t.Fatalf("Error while deserializing int back: %v", err)
			continue
		}

		if d != tst.value {
			t.Fatalf("Incorrectly deserialized the value back, was: %v, got %v", tst.value, d)
			continue
		}

	}

}

var intDeserializationData = []struct {
	serialized []byte
	value      uint64
	bogus      bool
}{
	{[]byte{0x00}, 0x00, false},
	{[]byte{0x01}, 0x01, false},
	{[]byte{0x02}, 0x02, false},
	{[]byte{0x7F}, 0x7F, false},
	{[]byte{0x80, 0x01}, 0x80, false},
	{[]byte{0x81, 0x01}, 0x81, false},
	{[]byte{0x80, 0x02}, 0x100, false},
	{[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01}, 0xFFFFFFFFFFFFFFFF, false},

	{[]byte{0x80, 0x00}, 0x00, true},
	{[]byte{0x81, 0x00}, 0x01, true},
	{[]byte{0x80, 0x82, 0x00}, 0x100, true},
	{[]byte{0x80}, 0x00, true},
	{[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}, 0, true},
	{[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02}, 0, true},
	{[]byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02}, 0, true},
}

func TestIntDeserialization(t *testing.T) {

	for _, tst := range intDeserializationData {

		v, err := DeserializeInt(bytes.NewBuffer(tst.serialized))

		if tst.bogus {
			if err == nil {
				t.Errorf("Dserialization of bogus data did not produce an error")
			}
			continue
		}

		if err != nil {
			t.Errorf("Could not deserialize data, expected value: %v, got error: %v", tst.value, err)
			continue
		}

		if v != tst.value {
			t.Errorf("Mismatch of deserialized value: %v, should be: %v", v, tst.value)
		}
	}
}

var bufferSerializationData = []struct {
	value      []byte
	serialized []byte
	err        error
	maxLength  uint64
}{
	{[]byte{}, []byte{0}, nil, 0},
	{[]byte{0}, []byte{1, 0}, nil, 1},
	{[]byte{0}, nil, ErrBufferToLarge, 0},
	{[]byte{7, 13}, []byte{2, 7, 13}, nil, 2},
	{[]byte{7, 13}, nil, ErrBufferToLarge, 1},
	{[]byte{7, 13}, nil, ErrBufferToLarge, 0},
}

func TestBufferSerialization(t *testing.T) {

	for _, tst := range bufferSerializationData {

		var b bytes.Buffer

		err := SerializeBuffer(tst.value, &b, tst.maxLength)

		if tst.err != nil {

			if err == nil {
				t.Errorf("Was able to serialize buffer but expected error")
				continue
			}

			continue
		}

		if err != nil {
			t.Errorf("Could not serialize buffer: %v", err)
			continue
		}

		if !bytes.Equal(b.Bytes(), tst.serialized) {
			t.Errorf("Serialized buffer value is incorrect, expected: %v, is: %v", hexDump(tst.serialized), hexDump(b.Bytes()))
			continue
		}

		v2, err := DeserializeBuffer(&b, tst.maxLength)
		if err != nil {
			t.Errorf("Unexpected deserialization error: %v", err)
			continue
		}

		if !bytes.Equal(v2, tst.value) {
			t.Errorf("Buffer value after deserialization is invalid, expected: %v, is: %v", hexDump(tst.value), v2)
			continue
		}
	}
}

var bufferDeserializationData = []struct {
	serialized []byte
	value      []byte
	err        error
	maxLength  uint64
}{
	{[]byte{0}, []byte{}, nil, 0},
	{[]byte{1, 0}, []byte{0}, nil, 1},
	{[]byte{1, 0}, nil, ErrBufferToLarge, 0},
	{[]byte{2, 7, 13}, []byte{7, 13}, nil, 2},
	{[]byte{2, 7, 13}, nil, ErrBufferToLarge, 1},
	{[]byte{2, 7, 13}, nil, ErrBufferToLarge, 0},
}

func TestBufferDeserialization(t *testing.T) {

	for _, tst := range bufferDeserializationData {

		v, err := DeserializeBuffer(bytes.NewBuffer(tst.serialized), tst.maxLength)

		if tst.err != nil {

			if err == nil {
				t.Errorf("Was able to serialize buffer but expected error")
				continue
			}

			continue
		}

		if err != nil {
			t.Errorf("Could not deserialize buffer: %v", err)
			continue
		}

		if !bytes.Equal(v, tst.value) {
			t.Errorf("Buffer value after deserialization is invalid, expected: %v, is: %v", hexDump(tst.value), v)
			continue
		}

	}

}
<commit_msg>Add string serialization tests<commit_after>package utils

import (
	"bytes"
	"encoding/hex"
	"testing"
)

func hexDump(data []byte) string {
	if data == nil {
		return "nil"
	}

	if len(data) > 8 {
		return hex.EncodeToString(data[:8]) + "..."
	}

	return hex.EncodeToString(data)
}

var intSerializationData = []struct {
	value      uint64
	serialized []byte
}{
	{0x00, []byte{0x00}},
	{0x01, []byte{0x01}},
	{0x02, []byte{0x02}},
	{0x7F, []byte{0x7F}},
	{0x80, []byte{0x80, 0x01}},
	{0x81, []byte{0x81, 0x01}},
	{0x100, []byte{0x80, 0x02}},
	{0xFFFFFFFFFFFFFFFF, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01}},
}

func TestIntSerialization(t *testing.T) {

	for _, tst := range intSerializationData {

		var b bytes.Buffer

		err := SerializeInt(tst.value, &b)

		if err != nil {
			t.Fatalf("Error while serializing int: %v", err)
			continue
		}

		s := b.Bytes()
		if !bytes.Equal(s, tst.serialized) {
			t.Fatalf(
				"Serialization of value %v failed, data not equal (%v vs %v)",
				tst.value,
				hexDump(s),
				hexDump(tst.serialized))
			continue
		}

		// Try to serialize back
		d, err := DeserializeInt(&b)
		if err != nil {
			t.Fatalf("Error while deserializing int back: %v", err)
			continue
		}

		if d != tst.value {
			t.Fatalf("Incorrectly deserialized the value back, was: %v, got %v", tst.value, d)
			continue
		}

	}

}

var intDeserializationData = []struct {
	serialized []byte
	value      uint64
	bogus      bool
}{
	{[]byte{0x00}, 0x00, false},
	{[]byte{0x01}, 0x01, false},
	{[]byte{0x02}, 0x02, false},
	{[]byte{0x7F}, 0x7F, false},
	{[]byte{0x80, 0x01}, 0x80, false},
	{[]byte{0x81, 0x01}, 0x81, false},
	{[]byte{0x80, 0x02}, 0x100, false},
	{[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01}, 0xFFFFFFFFFFFFFFFF, false},

	{[]byte{0x80, 0x00}, 0x00, true},
	{[]byte{0x81, 0x00}, 0x01, true},
	{[]byte{0x80, 0x82, 0x00}, 0x100, true},
	{[]byte{0x80}, 0x00, true},
	{[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}, 0, true},
	{[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02}, 0, true},
	{[]byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02}, 0, true},
}

func TestIntDeserialization(t *testing.T) {

	for _, tst := range intDeserializationData {

		v, err := DeserializeInt(bytes.NewBuffer(tst.serialized))

		if tst.bogus {
			if err == nil {
				t.Errorf("Dserialization of bogus data did not produce an error")
			}
			continue
		}

		if err != nil {
			t.Errorf("Could not deserialize data, expected value: %v, got error: %v", tst.value, err)
			continue
		}

		if v != tst.value {
			t.Errorf("Mismatch of deserialized value: %v, should be: %v", v, tst.value)
		}
	}
}

var bufferSerializationData = []struct {
	value      []byte
	serialized []byte
	err        error
	maxLength  uint64
}{
	{[]byte{}, []byte{0}, nil, 0},
	{[]byte{0}, []byte{1, 0}, nil, 1},
	{[]byte{0}, nil, ErrBufferToLarge, 0},
	{[]byte{7, 13}, []byte{2, 7, 13}, nil, 2},
	{[]byte{7, 13}, nil, ErrBufferToLarge, 1},
	{[]byte{7, 13}, nil, ErrBufferToLarge, 0},
}

func TestBufferSerialization(t *testing.T) {

	for _, tst := range bufferSerializationData {

		var b bytes.Buffer

		err := SerializeBuffer(tst.value, &b, tst.maxLength)

		if tst.err != nil {

			if err == nil {
				t.Errorf("Was able to serialize buffer but expected error")
				continue
			}

			continue
		}

		if err != nil {
			t.Errorf("Could not serialize buffer: %v", err)
			continue
		}

		if !bytes.Equal(b.Bytes(), tst.serialized) {
			t.Errorf("Serialized buffer value is incorrect, expected: %v, is: %v", hexDump(tst.serialized), hexDump(b.Bytes()))
			continue
		}

		v2, err := DeserializeBuffer(&b, tst.maxLength)
		if err != nil {
			t.Errorf("Unexpected deserialization error: %v", err)
			continue
		}

		if !bytes.Equal(v2, tst.value) {
			t.Errorf("Buffer value after deserialization is invalid, expected: %v, is: %v", hexDump(tst.value), v2)
			continue
		}
	}
}

var bufferDeserializationData = []struct {
	serialized []byte
	value      []byte
	err        error
	maxLength  uint64
}{
	{[]byte{0}, []byte{}, nil, 0},
	{[]byte{1, 0}, []byte{0}, nil, 1},
	{[]byte{1, 0}, nil, ErrBufferToLarge, 0},
	{[]byte{2, 7, 13}, []byte{7, 13}, nil, 2},
	{[]byte{2, 7, 13}, nil, ErrBufferToLarge, 1},
	{[]byte{2, 7, 13}, nil, ErrBufferToLarge, 0},
}

func TestBufferDeserialization(t *testing.T) {

	for _, tst := range bufferDeserializationData {

		v, err := DeserializeBuffer(bytes.NewBuffer(tst.serialized), tst.maxLength)

		if tst.err != nil {

			if err == nil {
				t.Errorf("Was able to serialize buffer but expected error")
				continue
			}

			continue
		}

		if err != nil {
			t.Errorf("Could not deserialize buffer: %v", err)
			continue
		}

		if !bytes.Equal(v, tst.value) {
			t.Errorf("Buffer value after deserialization is invalid, expected: %v, is: %v", hexDump(tst.value), hexDump(v))
			continue
		}

	}

}

var stringSerializationData = []struct {
	value      string
	serialized []byte
	err        error
	maxLength  uint64
}{
	{"", []byte{0}, nil, 0},
	{"a", []byte{1, 'a'}, nil, 1},
	{"a", nil, ErrBufferToLarge, 0},
	{"abc", []byte{3, 'a', 'b', 'c'}, nil, 3},
	{"abc", nil, ErrBufferToLarge, 2},
	{"abc", nil, ErrBufferToLarge, 1},
	{"abc", nil, ErrBufferToLarge, 0},
	{"\u2318", []byte{3, 0xe2, 0x8c, 0x98}, nil, 3},
}

func TestStringSerialization(t *testing.T) {

	for _, tst := range stringSerializationData {

		var b bytes.Buffer

		err := SerializeString(tst.value, &b, tst.maxLength)

		if tst.err != nil {

			if err == nil {
				t.Errorf("Was able to serialize string but expected error")
				continue
			}

			continue
		}

		if err != nil {
			t.Errorf("Could not serialize string: %v", err)
			continue
		}

		if !bytes.Equal(b.Bytes(), tst.serialized) {
			t.Errorf("Serialized string value is incorrect, expected: %v, is: %v", hexDump(tst.serialized), hexDump(b.Bytes()))
			continue
		}

		v2, err := DeserializeString(&b, tst.maxLength)
		if err != nil {
			t.Errorf("Unexpected deserialization error: %v", err)
			continue
		}

		if v2 != tst.value {
			t.Errorf("String value after deserialization is invalid, expected: %v, is: %v", tst.value, v2)
			continue
		}
	}
}

var stringDeserializationData = []struct {
	serialized []byte
	value      string
	err        error
	maxLength  uint64
}{
	{[]byte{0}, "", nil, 0},
	{[]byte{1, 'a'}, "a", nil, 1},
	{[]byte{1, 'a'}, "", ErrBufferToLarge, 0},
	{[]byte{2, 'a', 'b'}, "ab", nil, 2},
	{[]byte{2, 'a', 'b'}, "", ErrBufferToLarge, 1},
	{[]byte{2, 'a', 'b'}, "", ErrBufferToLarge, 0},
	{[]byte{2, 0x80, 'a'}, "", ErrStringNotUTF8, 2},
}

func TestStrigDeserialization(t *testing.T) {

	for _, tst := range stringDeserializationData {

		v, err := DeserializeString(bytes.NewBuffer(tst.serialized), tst.maxLength)

		if tst.err != nil {

			if err == nil {
				t.Errorf("Was able to serialize string but expected error")
				continue
			}

			continue
		}

		if err != nil {
			t.Errorf("Could not deserialize string: %v", err)
			continue
		}

		if v != tst.value {
			t.Errorf("String value after deserialization is invalid, expected: %v, is: %v", tst.value, v)
			continue
		}

	}

}
<|endoftext|>


<commit_before>package physical

import (
	"crypto/tls"
	"crypto/x509"
	"database/sql"
	"fmt"
	"io/ioutil"
	"net/url"
	"sort"
	"strings"
	"time"

	log "github.com/mgutz/logxi/v1"

	"github.com/armon/go-metrics"
	mysql "github.com/go-sql-driver/mysql"
)

// Unreserved tls key
// Reserved values are "true", "false", "skip-verify"
const mysqlTLSKey = "default"

// MySQLBackend is a physical backend that stores data
// within MySQL database.
type MySQLBackend struct {
	dbTable    string
	client     *sql.DB
	statements map[string]*sql.Stmt
	logger     log.Logger
}

// newMySQLBackend constructs a MySQL backend using the given API client and
// server address and credential for accessing mysql database.
func newMySQLBackend(conf map[string]string, logger log.Logger) (Backend, error) {
	// Get the MySQL credentials to perform read/write operations.
	username, ok := conf["username"]
	if !ok || username == "" {
		return nil, fmt.Errorf("missing username")
	}
	password, ok := conf["password"]
	if !ok || username == "" {
		return nil, fmt.Errorf("missing password")
	}

	// Get or set MySQL server address. Defaults to localhost and default port(3306)
	address, ok := conf["address"]
	if !ok {
		address = "127.0.0.1:3306"
	}

	// Get the MySQL database and table details.
	database, ok := conf["database"]
	if !ok {
		database = "vault"
	}
	table, ok := conf["table"]
	if !ok {
		table = "vault"
	}
	dbTable := database + "." + table

	dsnParams := url.Values{}
	tlsCaFile, ok := conf["tls_ca_file"]
	if ok {
		if err := setupMySQLTLSConfig(tlsCaFile); err != nil {
			return nil, fmt.Errorf("failed register TLS config: %v", err)
		}

		dsnParams.Add("tls", mysqlTLSKey)
	}

	// Create MySQL handle for the database.
	dsn := username + ":" + password + "@tcp(" + address + ")/?" + dsnParams.Encode()
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to mysql: %v", err)
	}

	// Create the required database if it doesn't exists.
	if _, err := db.Exec("CREATE DATABASE IF NOT EXISTS " + database); err != nil {
		return nil, fmt.Errorf("failed to create mysql database: %v", err)
	}

	// Create the required table if it doesn't exists.
	create_query := "CREATE TABLE IF NOT EXISTS " + dbTable +
		" (vault_key varbinary(512), vault_value mediumblob, PRIMARY KEY (vault_key))"
	if _, err := db.Exec(create_query); err != nil {
		return nil, fmt.Errorf("failed to create mysql table: %v", err)
	}

	// Setup the backend.
	m := &MySQLBackend{
		dbTable:    dbTable,
		client:     db,
		statements: make(map[string]*sql.Stmt),
		logger:     logger,
	}

	// Prepare all the statements required
	statements := map[string]string{
		"put": "INSERT INTO " + dbTable +
			" VALUES( ?, ? ) ON DUPLICATE KEY UPDATE vault_value=VALUES(vault_value)",
		"get":    "SELECT vault_value FROM " + dbTable + " WHERE vault_key = ?",
		"delete": "DELETE FROM " + dbTable + " WHERE vault_key = ?",
		"list":   "SELECT vault_key FROM " + dbTable + " WHERE vault_key LIKE ?",
	}
	for name, query := range statements {
		if err := m.prepare(name, query); err != nil {
			return nil, err
		}
	}
	return m, nil
}

// prepare is a helper to prepare a query for future execution
func (m *MySQLBackend) prepare(name, query string) error {
	stmt, err := m.client.Prepare(query)
	if err != nil {
		return fmt.Errorf("failed to prepare '%s': %v", name, err)
	}
	m.statements[name] = stmt
	return nil
}

// Put is used to insert or update an entry.
func (m *MySQLBackend) Put(entry *Entry) error {
	defer metrics.MeasureSince([]string{"mysql", "put"}, time.Now())

	_, err := m.statements["put"].Exec(entry.Key, entry.Value)
	if err != nil {
		return err
	}
	return nil
}

// Get is used to fetch and entry.
func (m *MySQLBackend) Get(key string) (*Entry, error) {
	defer metrics.MeasureSince([]string{"mysql", "get"}, time.Now())

	var result []byte
	err := m.statements["get"].QueryRow(key).Scan(&result)
	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	ent := &Entry{
		Key:   key,
		Value: result,
	}
	return ent, nil
}

// Delete is used to permanently delete an entry
func (m *MySQLBackend) Delete(key string) error {
	defer metrics.MeasureSince([]string{"mysql", "delete"}, time.Now())

	_, err := m.statements["delete"].Exec(key)
	if err != nil {
		return err
	}
	return nil
}

// List is used to list all the keys under a given
// prefix, up to the next prefix.
func (m *MySQLBackend) List(prefix string) ([]string, error) {
	defer metrics.MeasureSince([]string{"mysql", "list"}, time.Now())

	// Add the % wildcard to the prefix to do the prefix search
	likePrefix := prefix + "%"
	rows, err := m.statements["list"].Query(likePrefix)

	var keys []string
	for rows.Next() {
		var key string
		err = rows.Scan(&key)
		if err != nil {
			return nil, fmt.Errorf("failed to scan rows: %v", err)
		}

		key = strings.TrimPrefix(key, prefix)
		if i := strings.Index(key, "/"); i == -1 {
			// Add objects only from the current 'folder'
			keys = append(keys, key)
		} else if i != -1 {
			// Add truncated 'folder' paths
			keys = appendIfMissing(keys, string(key[:i+1]))
		}
	}

	sort.Strings(keys)
	return keys, nil
}

// Establish a TLS connection with a given CA certificate
// Register a tsl.Config associted with the same key as the dns param from sql.Open
// foo:bar@tcp(127.0.0.1:3306)/dbname?tls=default
func setupMySQLTLSConfig(tlsCaFile string) error {
	rootCertPool := x509.NewCertPool()

	pem, err := ioutil.ReadFile(tlsCaFile)
	if err != nil {
		return err
	}

	if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
		return err
	}

	err = mysql.RegisterTLSConfig(mysqlTLSKey, &tls.Config{
		RootCAs: rootCertPool,
	})
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>check for failure on that mysql query (#2105)<commit_after>package physical

import (
	"crypto/tls"
	"crypto/x509"
	"database/sql"
	"fmt"
	"io/ioutil"
	"net/url"
	"sort"
	"strings"
	"time"

	log "github.com/mgutz/logxi/v1"

	"github.com/armon/go-metrics"
	mysql "github.com/go-sql-driver/mysql"
)

// Unreserved tls key
// Reserved values are "true", "false", "skip-verify"
const mysqlTLSKey = "default"

// MySQLBackend is a physical backend that stores data
// within MySQL database.
type MySQLBackend struct {
	dbTable    string
	client     *sql.DB
	statements map[string]*sql.Stmt
	logger     log.Logger
}

// newMySQLBackend constructs a MySQL backend using the given API client and
// server address and credential for accessing mysql database.
func newMySQLBackend(conf map[string]string, logger log.Logger) (Backend, error) {
	// Get the MySQL credentials to perform read/write operations.
	username, ok := conf["username"]
	if !ok || username == "" {
		return nil, fmt.Errorf("missing username")
	}
	password, ok := conf["password"]
	if !ok || username == "" {
		return nil, fmt.Errorf("missing password")
	}

	// Get or set MySQL server address. Defaults to localhost and default port(3306)
	address, ok := conf["address"]
	if !ok {
		address = "127.0.0.1:3306"
	}

	// Get the MySQL database and table details.
	database, ok := conf["database"]
	if !ok {
		database = "vault"
	}
	table, ok := conf["table"]
	if !ok {
		table = "vault"
	}
	dbTable := database + "." + table

	dsnParams := url.Values{}
	tlsCaFile, ok := conf["tls_ca_file"]
	if ok {
		if err := setupMySQLTLSConfig(tlsCaFile); err != nil {
			return nil, fmt.Errorf("failed register TLS config: %v", err)
		}

		dsnParams.Add("tls", mysqlTLSKey)
	}

	// Create MySQL handle for the database.
	dsn := username + ":" + password + "@tcp(" + address + ")/?" + dsnParams.Encode()
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to mysql: %v", err)
	}

	// Create the required database if it doesn't exists.
	if _, err := db.Exec("CREATE DATABASE IF NOT EXISTS " + database); err != nil {
		return nil, fmt.Errorf("failed to create mysql database: %v", err)
	}

	// Create the required table if it doesn't exists.
	create_query := "CREATE TABLE IF NOT EXISTS " + dbTable +
		" (vault_key varbinary(512), vault_value mediumblob, PRIMARY KEY (vault_key))"
	if _, err := db.Exec(create_query); err != nil {
		return nil, fmt.Errorf("failed to create mysql table: %v", err)
	}

	// Setup the backend.
	m := &MySQLBackend{
		dbTable:    dbTable,
		client:     db,
		statements: make(map[string]*sql.Stmt),
		logger:     logger,
	}

	// Prepare all the statements required
	statements := map[string]string{
		"put": "INSERT INTO " + dbTable +
			" VALUES( ?, ? ) ON DUPLICATE KEY UPDATE vault_value=VALUES(vault_value)",
		"get":    "SELECT vault_value FROM " + dbTable + " WHERE vault_key = ?",
		"delete": "DELETE FROM " + dbTable + " WHERE vault_key = ?",
		"list":   "SELECT vault_key FROM " + dbTable + " WHERE vault_key LIKE ?",
	}
	for name, query := range statements {
		if err := m.prepare(name, query); err != nil {
			return nil, err
		}
	}
	return m, nil
}

// prepare is a helper to prepare a query for future execution
func (m *MySQLBackend) prepare(name, query string) error {
	stmt, err := m.client.Prepare(query)
	if err != nil {
		return fmt.Errorf("failed to prepare '%s': %v", name, err)
	}
	m.statements[name] = stmt
	return nil
}

// Put is used to insert or update an entry.
func (m *MySQLBackend) Put(entry *Entry) error {
	defer metrics.MeasureSince([]string{"mysql", "put"}, time.Now())

	_, err := m.statements["put"].Exec(entry.Key, entry.Value)
	if err != nil {
		return err
	}
	return nil
}

// Get is used to fetch and entry.
func (m *MySQLBackend) Get(key string) (*Entry, error) {
	defer metrics.MeasureSince([]string{"mysql", "get"}, time.Now())

	var result []byte
	err := m.statements["get"].QueryRow(key).Scan(&result)
	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	ent := &Entry{
		Key:   key,
		Value: result,
	}
	return ent, nil
}

// Delete is used to permanently delete an entry
func (m *MySQLBackend) Delete(key string) error {
	defer metrics.MeasureSince([]string{"mysql", "delete"}, time.Now())

	_, err := m.statements["delete"].Exec(key)
	if err != nil {
		return err
	}
	return nil
}

// List is used to list all the keys under a given
// prefix, up to the next prefix.
func (m *MySQLBackend) List(prefix string) ([]string, error) {
	defer metrics.MeasureSince([]string{"mysql", "list"}, time.Now())

	// Add the % wildcard to the prefix to do the prefix search
	likePrefix := prefix + "%"
	rows, err := m.statements["list"].Query(likePrefix)
	if err != nil {
		return nil, fmt.Errorf("failed to execute statement: %v", err)
	}

	var keys []string
	for rows.Next() {
		var key string
		err = rows.Scan(&key)
		if err != nil {
			return nil, fmt.Errorf("failed to scan rows: %v", err)
		}

		key = strings.TrimPrefix(key, prefix)
		if i := strings.Index(key, "/"); i == -1 {
			// Add objects only from the current 'folder'
			keys = append(keys, key)
		} else if i != -1 {
			// Add truncated 'folder' paths
			keys = appendIfMissing(keys, string(key[:i+1]))
		}
	}

	sort.Strings(keys)
	return keys, nil
}

// Establish a TLS connection with a given CA certificate
// Register a tsl.Config associted with the same key as the dns param from sql.Open
// foo:bar@tcp(127.0.0.1:3306)/dbname?tls=default
func setupMySQLTLSConfig(tlsCaFile string) error {
	rootCertPool := x509.NewCertPool()

	pem, err := ioutil.ReadFile(tlsCaFile)
	if err != nil {
		return err
	}

	if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
		return err
	}

	err = mysql.RegisterTLSConfig(mysqlTLSKey, &tls.Config{
		RootCAs: rootCertPool,
	})
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>/*
 Copyright 2021 The CloudEvents Authors
 SPDX-License-Identifier: Apache-2.0
*/

package http

import (
	"fmt"
	"net"
	nethttp "net/http"
	"net/url"
	"strings"
	"time"
)

// Option is the function signature required to be considered an http.Option.
type Option func(*Protocol) error

// WithTarget sets the outbound recipient of cloudevents when using an HTTP
// request.
func WithTarget(targetUrl string) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http target option can not set nil protocol")
		}
		targetUrl = strings.TrimSpace(targetUrl)
		if targetUrl != "" {
			var err error
			var target *url.URL
			target, err = url.Parse(targetUrl)
			if err != nil {
				return fmt.Errorf("http target option failed to parse target url: %s", err.Error())
			}

			p.Target = target

			if p.RequestTemplate == nil {
				p.RequestTemplate = &nethttp.Request{
					Method: nethttp.MethodPost,
				}
			}
			p.RequestTemplate.URL = target

			return nil
		}
		return fmt.Errorf("http target option was empty string")
	}
}

// WithHeader sets an additional default outbound header for all cloudevents
// when using an HTTP request.
func WithHeader(key, value string) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http header option can not set nil protocol")
		}
		key = strings.TrimSpace(key)
		if key != "" {
			if p.RequestTemplate == nil {
				p.RequestTemplate = &nethttp.Request{
					Method: nethttp.MethodPost,
				}
			}
			if p.RequestTemplate.Header == nil {
				p.RequestTemplate.Header = nethttp.Header{}
			}
			p.RequestTemplate.Header.Add(key, value)
			return nil
		}
		return fmt.Errorf("http header option was empty string")
	}
}

// WithShutdownTimeout sets the shutdown timeout when the http server is being shutdown.
func WithShutdownTimeout(timeout time.Duration) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http shutdown timeout option can not set nil protocol")
		}
		p.ShutdownTimeout = timeout
		return nil
	}
}

func checkListen(p *Protocol, prefix string) error {
	switch {
	case p.listener.Load() != nil:
		return fmt.Errorf("error setting %v: listener already set", prefix)
	}
	return nil
}

// WithPort sets the listening port for StartReceiver.
// Only one of WithListener or WithPort is allowed.
func WithPort(port int) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http port option can not set nil protocol")
		}
		if port < 0 || port > 65535 {
			return fmt.Errorf("http port option was given an invalid port: %d", port)
		}
		if err := checkListen(p, "http port option"); err != nil {
			return err
		}
		p.Port = port
		return nil
	}
}

// WithListener sets the listener for StartReceiver.
// Only one of WithListener or WithPort is allowed.
func WithListener(l net.Listener) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http listener option can not set nil protocol")
		}
		if err := checkListen(p, "http listener"); err != nil {
			return err
		}
		p.listener.Store(l)
		return nil
	}
}

// WithPath sets the path to receive cloudevents on for HTTP transports.
func WithPath(path string) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http path option can not set nil protocol")
		}
		path = strings.TrimSpace(path)
		if len(path) == 0 {
			return fmt.Errorf("http path option was given an invalid path: %q", path)
		}
		p.Path = path
		return nil
	}
}

// WithMethod sets the HTTP verb (GET, POST, PUT, etc.) to use
// when using an HTTP request.
func WithMethod(method string) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http method option can not set nil protocol")
		}
		method = strings.TrimSpace(method)
		if method != "" {
			if p.RequestTemplate == nil {
				p.RequestTemplate = &nethttp.Request{}
			}
			p.RequestTemplate.Method = method
			return nil
		}
		return fmt.Errorf("http method option was empty string")
	}
}

//
// Middleware is a function that takes an existing http.Handler and wraps it in middleware,
// returning the wrapped http.Handler.
type Middleware func(next nethttp.Handler) nethttp.Handler

// WithMiddleware adds an HTTP middleware to the transport. It may be specified multiple times.
// Middleware is applied to everything before it. For example
// `NewClient(WithMiddleware(foo), WithMiddleware(bar))` would result in `bar(foo(original))`.
func WithMiddleware(middleware Middleware) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http middleware option can not set nil protocol")
		}
		p.middleware = append(p.middleware, middleware)
		return nil
	}
}

// WithRoundTripper sets the HTTP RoundTripper.
func WithRoundTripper(roundTripper nethttp.RoundTripper) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http round tripper option can not set nil protocol")
		}
		p.roundTripper = roundTripper
		return nil
	}
}

// WithRoundTripperDecorator decorates the default HTTP RoundTripper chosen.
func WithRoundTripperDecorator(decorator func(roundTripper nethttp.RoundTripper) nethttp.RoundTripper) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http round tripper option can not set nil protocol")
		}
		if p.roundTripper == nil {
			if p.Client == nil {
				p.roundTripper = nethttp.DefaultTransport
			} else {
				p.roundTripper = p.Client.Transport
			}
		}
		p.roundTripper = decorator(p.roundTripper)
		return nil
	}
}

// WithClient sets the protocol client
func WithClient(client nethttp.Client) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("client option can not set nil protocol")
		}
		p.Client = &client
		return nil
	}
}

// WithGetHandlerFunc sets the http GET handler func
func WithGetHandlerFunc(fn nethttp.HandlerFunc) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http GET handler func can not set nil protocol")
		}
		p.GetHandlerFn = fn
		return nil
	}
}

// WithOptionsHandlerFunc sets the http OPTIONS handler func
func WithOptionsHandlerFunc(fn nethttp.HandlerFunc) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http OPTIONS handler func can not set nil protocol")
		}
		p.OptionsHandlerFn = fn
		return nil
	}
}

// WithDefaultOptionsHandlerFunc sets the options handler to be the built in handler and configures the options.
// methods: the supported methods reported to OPTIONS caller.
// rate: the rate limit reported to OPTIONS caller.
// origins: the prefix of the accepted origins, or "*".
// callback: preform the callback to ACK the OPTIONS request.
func WithDefaultOptionsHandlerFunc(methods []string, rate int, origins []string, callback bool) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http OPTIONS handler func can not set nil protocol")
		}
		p.OptionsHandlerFn = p.DeleteHandlerFn
		p.WebhookConfig = &WebhookConfig{
			AllowedMethods:  methods,
			AllowedRate:     &rate,
			AllowedOrigins:  origins,
			AutoACKCallback: callback,
		}
		return nil
	}
}

// IsRetriable is a custom function that can be used to override the
// default retriable status codes.
type IsRetriable func(statusCode int) bool

// WithIsRetriableFunc sets the function that gets called to determine if an
// error should be retried. If not set, the defaultIsRetriableFunc is used.
func WithIsRetriableFunc(isRetriable IsRetriable) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("isRetriable handler func can not set nil protocol")
		}
		if isRetriable == nil {
			return fmt.Errorf("isRetriable handler can not be nil")
		}
		p.isRetriableFunc = isRetriable
		return nil
	}
}

func WithRateLimiter(rl RateLimiter) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http OPTIONS handler func can not set nil protocol")
		}
		p.limiter = rl
		return nil
	}
}
<commit_msg>Adding proper abuse protection handler (#743)<commit_after>/*
 Copyright 2021 The CloudEvents Authors
 SPDX-License-Identifier: Apache-2.0
*/

package http

import (
	"fmt"
	"net"
	nethttp "net/http"
	"net/url"
	"strings"
	"time"
)

// Option is the function signature required to be considered an http.Option.
type Option func(*Protocol) error

// WithTarget sets the outbound recipient of cloudevents when using an HTTP
// request.
func WithTarget(targetUrl string) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http target option can not set nil protocol")
		}
		targetUrl = strings.TrimSpace(targetUrl)
		if targetUrl != "" {
			var err error
			var target *url.URL
			target, err = url.Parse(targetUrl)
			if err != nil {
				return fmt.Errorf("http target option failed to parse target url: %s", err.Error())
			}

			p.Target = target

			if p.RequestTemplate == nil {
				p.RequestTemplate = &nethttp.Request{
					Method: nethttp.MethodPost,
				}
			}
			p.RequestTemplate.URL = target

			return nil
		}
		return fmt.Errorf("http target option was empty string")
	}
}

// WithHeader sets an additional default outbound header for all cloudevents
// when using an HTTP request.
func WithHeader(key, value string) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http header option can not set nil protocol")
		}
		key = strings.TrimSpace(key)
		if key != "" {
			if p.RequestTemplate == nil {
				p.RequestTemplate = &nethttp.Request{
					Method: nethttp.MethodPost,
				}
			}
			if p.RequestTemplate.Header == nil {
				p.RequestTemplate.Header = nethttp.Header{}
			}
			p.RequestTemplate.Header.Add(key, value)
			return nil
		}
		return fmt.Errorf("http header option was empty string")
	}
}

// WithShutdownTimeout sets the shutdown timeout when the http server is being shutdown.
func WithShutdownTimeout(timeout time.Duration) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http shutdown timeout option can not set nil protocol")
		}
		p.ShutdownTimeout = timeout
		return nil
	}
}

func checkListen(p *Protocol, prefix string) error {
	switch {
	case p.listener.Load() != nil:
		return fmt.Errorf("error setting %v: listener already set", prefix)
	}
	return nil
}

// WithPort sets the listening port for StartReceiver.
// Only one of WithListener or WithPort is allowed.
func WithPort(port int) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http port option can not set nil protocol")
		}
		if port < 0 || port > 65535 {
			return fmt.Errorf("http port option was given an invalid port: %d", port)
		}
		if err := checkListen(p, "http port option"); err != nil {
			return err
		}
		p.Port = port
		return nil
	}
}

// WithListener sets the listener for StartReceiver.
// Only one of WithListener or WithPort is allowed.
func WithListener(l net.Listener) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http listener option can not set nil protocol")
		}
		if err := checkListen(p, "http listener"); err != nil {
			return err
		}
		p.listener.Store(l)
		return nil
	}
}

// WithPath sets the path to receive cloudevents on for HTTP transports.
func WithPath(path string) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http path option can not set nil protocol")
		}
		path = strings.TrimSpace(path)
		if len(path) == 0 {
			return fmt.Errorf("http path option was given an invalid path: %q", path)
		}
		p.Path = path
		return nil
	}
}

// WithMethod sets the HTTP verb (GET, POST, PUT, etc.) to use
// when using an HTTP request.
func WithMethod(method string) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http method option can not set nil protocol")
		}
		method = strings.TrimSpace(method)
		if method != "" {
			if p.RequestTemplate == nil {
				p.RequestTemplate = &nethttp.Request{}
			}
			p.RequestTemplate.Method = method
			return nil
		}
		return fmt.Errorf("http method option was empty string")
	}
}

//
// Middleware is a function that takes an existing http.Handler and wraps it in middleware,
// returning the wrapped http.Handler.
type Middleware func(next nethttp.Handler) nethttp.Handler

// WithMiddleware adds an HTTP middleware to the transport. It may be specified multiple times.
// Middleware is applied to everything before it. For example
// `NewClient(WithMiddleware(foo), WithMiddleware(bar))` would result in `bar(foo(original))`.
func WithMiddleware(middleware Middleware) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http middleware option can not set nil protocol")
		}
		p.middleware = append(p.middleware, middleware)
		return nil
	}
}

// WithRoundTripper sets the HTTP RoundTripper.
func WithRoundTripper(roundTripper nethttp.RoundTripper) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http round tripper option can not set nil protocol")
		}
		p.roundTripper = roundTripper
		return nil
	}
}

// WithRoundTripperDecorator decorates the default HTTP RoundTripper chosen.
func WithRoundTripperDecorator(decorator func(roundTripper nethttp.RoundTripper) nethttp.RoundTripper) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http round tripper option can not set nil protocol")
		}
		if p.roundTripper == nil {
			if p.Client == nil {
				p.roundTripper = nethttp.DefaultTransport
			} else {
				p.roundTripper = p.Client.Transport
			}
		}
		p.roundTripper = decorator(p.roundTripper)
		return nil
	}
}

// WithClient sets the protocol client
func WithClient(client nethttp.Client) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("client option can not set nil protocol")
		}
		p.Client = &client
		return nil
	}
}

// WithGetHandlerFunc sets the http GET handler func
func WithGetHandlerFunc(fn nethttp.HandlerFunc) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http GET handler func can not set nil protocol")
		}
		p.GetHandlerFn = fn
		return nil
	}
}

// WithOptionsHandlerFunc sets the http OPTIONS handler func
func WithOptionsHandlerFunc(fn nethttp.HandlerFunc) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http OPTIONS handler func can not set nil protocol")
		}
		p.OptionsHandlerFn = fn
		return nil
	}
}

// WithDefaultOptionsHandlerFunc sets the options handler to be the built in handler and configures the options.
// methods: the supported methods reported to OPTIONS caller.
// rate: the rate limit reported to OPTIONS caller.
// origins: the prefix of the accepted origins, or "*".
// callback: preform the callback to ACK the OPTIONS request.
func WithDefaultOptionsHandlerFunc(methods []string, rate int, origins []string, callback bool) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http OPTIONS handler func can not set nil protocol")
		}
		p.OptionsHandlerFn = p.OptionsHandler
		p.WebhookConfig = &WebhookConfig{
			AllowedMethods:  methods,
			AllowedRate:     &rate,
			AllowedOrigins:  origins,
			AutoACKCallback: callback,
		}
		return nil
	}
}

// IsRetriable is a custom function that can be used to override the
// default retriable status codes.
type IsRetriable func(statusCode int) bool

// WithIsRetriableFunc sets the function that gets called to determine if an
// error should be retried. If not set, the defaultIsRetriableFunc is used.
func WithIsRetriableFunc(isRetriable IsRetriable) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("isRetriable handler func can not set nil protocol")
		}
		if isRetriable == nil {
			return fmt.Errorf("isRetriable handler can not be nil")
		}
		p.isRetriableFunc = isRetriable
		return nil
	}
}

func WithRateLimiter(rl RateLimiter) Option {
	return func(p *Protocol) error {
		if p == nil {
			return fmt.Errorf("http OPTIONS handler func can not set nil protocol")
		}
		p.limiter = rl
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"code.google.com/p/go.tools/astutil"
)

func fixImports(f *ast.File) error {
	// refs are a set of possible package references currently unsatisified by imports.
	// first key: either base package (e.g. "fmt") or renamed package
	// second key: referenced package symbol (e.g. "Println")
	refs := make(map[string]map[string]bool)

	// decls are the current package imports. key is base package or renamed package.
	decls := make(map[string]*ast.ImportSpec)

	// collect potential uses of packages.
	var visitor visitFn
	visitor = visitFn(func(node ast.Node) ast.Visitor {
		if node == nil {
			return visitor
		}
		switch v := node.(type) {
		case *ast.ImportSpec:
			if v.Name != nil {
				decls[v.Name.Name] = v
			} else {
				local := path.Base(strings.Trim(v.Path.Value, `\"`))
				decls[local] = v
			}
		case *ast.SelectorExpr:
			xident, ok := v.X.(*ast.Ident)
			if !ok {
				break
			}
			if xident.Obj != nil {
				// if the parser can resolve it, it's not a package ref
				break
			}
			pkgName := xident.Name
			if refs[pkgName] == nil {
				refs[pkgName] = make(map[string]bool)
			}
			if decls[pkgName] == nil {
				refs[pkgName][v.Sel.Name] = true
			}
		}
		return visitor
	})
	ast.Walk(visitor, f)

	// Search for imports matching potential package references.
	searches := 0
	type result struct {
		ipath string
		err error
	}
	results := make(chan result)
	for pkgName, symbols := range refs {
		if len(symbols) == 0 {
			continue // skip over packages already imported
		}
		go func(pkgName string, symbols map[string]bool) {
			ipath, err := findImport(pkgName, symbols)
			results <- result{ ipath, err }
		}(pkgName, symbols)
		searches++
	}
	for i := 0; i < searches; i++ {
		result := <-results
		if result.err != nil {
			return result.err
		}
		if result.ipath != "" {
			astutil.AddImport(f, result.ipath)
		}
	}

	// Nil out any unused ImportSpecs, to be removed in following passes
	unusedImport := map[string]bool{}
	for pkg, is := range decls {
		if refs[pkg] == nil && pkg != "_" && pkg != "." {
			unusedImport[strings.Trim(is.Path.Value, `"`)] = true
		}
	}
	for ipath := range unusedImport {
		astutil.DeleteImport(f, ipath)
	}

	return nil
}

type pkg struct {
	importpath string // full pkg import path, e.g. "net/http"
	dir        string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
}

var pkgIndexOnce sync.Once

var pkgIndex struct {
	sync.Mutex
	m map[string][]pkg // shortname => []pkg, e.g "http" => "net/http"
}

func loadPkgIndex() {
	pkgIndex.Lock()
	pkgIndex.m = make(map[string][]pkg)
	pkgIndex.Unlock()

	var wg sync.WaitGroup
	for _, path := range build.Default.SrcDirs() {
		f, err := os.Open(path)
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		children, err := f.Readdir(-1)
		f.Close()
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		for _, child := range children {
			if child.IsDir() {
				wg.Add(1)
				go func(path, name string) {
					defer wg.Done()
					loadPkg(&wg, path, name)
				}(path, child.Name())
			}
		}
	}
	wg.Wait()
}

var fset = token.NewFileSet()

func loadPkg(wg *sync.WaitGroup, root, importpath string) {
	shortName := path.Base(importpath)

	dir := filepath.Join(root, importpath)
	pkgIndex.Lock()
	pkgIndex.m[shortName] = append(pkgIndex.m[shortName], pkg{
		importpath: importpath,
		dir:        dir,
	})
	pkgIndex.Unlock()

	pkgDir, err := os.Open(dir)
	if err != nil {
		return
	}
	children, err := pkgDir.Readdir(-1)
	pkgDir.Close()
	if err != nil {
		return
	}
	for _, child := range children {
		if child.IsDir() {
			wg.Add(1)
			go func(root, name string) {
				defer wg.Done()
				loadPkg(wg, root, name)
			}(root, filepath.Join(importpath, child.Name()))
		}
	}
}

// loadExports returns a list exports for a package.
func loadExports(dir string) map[string]bool {
	exports := make(map[string]bool)
	buildPkg, err := build.ImportDir(dir, 0)
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not import %q: %v", dir, err)
		return nil
	}
	for _, file := range buildPkg.GoFiles {
		f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
		if err != nil {
			fmt.Fprintf(os.Stderr, "could not parse %q: %v", file, err)
			continue
		}
		for name := range f.Scope.Objects {
			if ast.IsExported(name) {
				exports[name] = true
			}
		}
	}
	return exports
}

// findImport searches for a package with the given symbols.
// If no package is found, findImport returns "".
// Declared as a variable rather than a function so goimports can be easily
// extended by adding a file with an init function.
var findImport = func(pkgName string, symbols map[string]bool) (string, error) {
	pkgIndexOnce.Do(loadPkgIndex)

	// Collect exports for packages with matching names.
	var wg sync.WaitGroup
	var pkgsMu sync.Mutex // guards pkgs
	// full importpath => exported symbol => True
	// e.g. "net/http" => "Client" => True
	pkgs := make(map[string]map[string]bool)
	pkgIndex.Lock()
	for _, pkg := range pkgIndex.m[pkgName] {
		wg.Add(1)
		go func(importpath, dir string) {
			defer wg.Done()
			exports := loadExports(dir)
			if exports != nil {
				pkgsMu.Lock()
				pkgs[importpath] = exports
				pkgsMu.Unlock()
			}
		}(pkg.importpath, pkg.dir)
	}
	pkgIndex.Unlock()
	wg.Wait()

	// Filter out packages missing required exported symbols.
	for symbol := range symbols {
		for importpath, exports := range pkgs {
			if !exports[symbol] {
				delete(pkgs, importpath)
			}
		}
	}
	for importpath := range pkgs {
		return importpath, nil
	}
	return "", nil
}

type visitFn func(node ast.Node) ast.Visitor

func (fn visitFn) Visit(node ast.Node) ast.Visitor {
	return fn(node)
}
<commit_msg>gofmt<commit_after>package main

import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"code.google.com/p/go.tools/astutil"
)

func fixImports(f *ast.File) error {
	// refs are a set of possible package references currently unsatisified by imports.
	// first key: either base package (e.g. "fmt") or renamed package
	// second key: referenced package symbol (e.g. "Println")
	refs := make(map[string]map[string]bool)

	// decls are the current package imports. key is base package or renamed package.
	decls := make(map[string]*ast.ImportSpec)

	// collect potential uses of packages.
	var visitor visitFn
	visitor = visitFn(func(node ast.Node) ast.Visitor {
		if node == nil {
			return visitor
		}
		switch v := node.(type) {
		case *ast.ImportSpec:
			if v.Name != nil {
				decls[v.Name.Name] = v
			} else {
				local := path.Base(strings.Trim(v.Path.Value, `\"`))
				decls[local] = v
			}
		case *ast.SelectorExpr:
			xident, ok := v.X.(*ast.Ident)
			if !ok {
				break
			}
			if xident.Obj != nil {
				// if the parser can resolve it, it's not a package ref
				break
			}
			pkgName := xident.Name
			if refs[pkgName] == nil {
				refs[pkgName] = make(map[string]bool)
			}
			if decls[pkgName] == nil {
				refs[pkgName][v.Sel.Name] = true
			}
		}
		return visitor
	})
	ast.Walk(visitor, f)

	// Search for imports matching potential package references.
	searches := 0
	type result struct {
		ipath string
		err   error
	}
	results := make(chan result)
	for pkgName, symbols := range refs {
		if len(symbols) == 0 {
			continue // skip over packages already imported
		}
		go func(pkgName string, symbols map[string]bool) {
			ipath, err := findImport(pkgName, symbols)
			results <- result{ipath, err}
		}(pkgName, symbols)
		searches++
	}
	for i := 0; i < searches; i++ {
		result := <-results
		if result.err != nil {
			return result.err
		}
		if result.ipath != "" {
			astutil.AddImport(f, result.ipath)
		}
	}

	// Nil out any unused ImportSpecs, to be removed in following passes
	unusedImport := map[string]bool{}
	for pkg, is := range decls {
		if refs[pkg] == nil && pkg != "_" && pkg != "." {
			unusedImport[strings.Trim(is.Path.Value, `"`)] = true
		}
	}
	for ipath := range unusedImport {
		astutil.DeleteImport(f, ipath)
	}

	return nil
}

type pkg struct {
	importpath string // full pkg import path, e.g. "net/http"
	dir        string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
}

var pkgIndexOnce sync.Once

var pkgIndex struct {
	sync.Mutex
	m map[string][]pkg // shortname => []pkg, e.g "http" => "net/http"
}

func loadPkgIndex() {
	pkgIndex.Lock()
	pkgIndex.m = make(map[string][]pkg)
	pkgIndex.Unlock()

	var wg sync.WaitGroup
	for _, path := range build.Default.SrcDirs() {
		f, err := os.Open(path)
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		children, err := f.Readdir(-1)
		f.Close()
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		for _, child := range children {
			if child.IsDir() {
				wg.Add(1)
				go func(path, name string) {
					defer wg.Done()
					loadPkg(&wg, path, name)
				}(path, child.Name())
			}
		}
	}
	wg.Wait()
}

var fset = token.NewFileSet()

func loadPkg(wg *sync.WaitGroup, root, importpath string) {
	shortName := path.Base(importpath)

	dir := filepath.Join(root, importpath)
	pkgIndex.Lock()
	pkgIndex.m[shortName] = append(pkgIndex.m[shortName], pkg{
		importpath: importpath,
		dir:        dir,
	})
	pkgIndex.Unlock()

	pkgDir, err := os.Open(dir)
	if err != nil {
		return
	}
	children, err := pkgDir.Readdir(-1)
	pkgDir.Close()
	if err != nil {
		return
	}
	for _, child := range children {
		if child.IsDir() {
			wg.Add(1)
			go func(root, name string) {
				defer wg.Done()
				loadPkg(wg, root, name)
			}(root, filepath.Join(importpath, child.Name()))
		}
	}
}

// loadExports returns a list exports for a package.
func loadExports(dir string) map[string]bool {
	exports := make(map[string]bool)
	buildPkg, err := build.ImportDir(dir, 0)
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not import %q: %v", dir, err)
		return nil
	}
	for _, file := range buildPkg.GoFiles {
		f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
		if err != nil {
			fmt.Fprintf(os.Stderr, "could not parse %q: %v", file, err)
			continue
		}
		for name := range f.Scope.Objects {
			if ast.IsExported(name) {
				exports[name] = true
			}
		}
	}
	return exports
}

// findImport searches for a package with the given symbols.
// If no package is found, findImport returns "".
// Declared as a variable rather than a function so goimports can be easily
// extended by adding a file with an init function.
var findImport = func(pkgName string, symbols map[string]bool) (string, error) {
	pkgIndexOnce.Do(loadPkgIndex)

	// Collect exports for packages with matching names.
	var wg sync.WaitGroup
	var pkgsMu sync.Mutex // guards pkgs
	// full importpath => exported symbol => True
	// e.g. "net/http" => "Client" => True
	pkgs := make(map[string]map[string]bool)
	pkgIndex.Lock()
	for _, pkg := range pkgIndex.m[pkgName] {
		wg.Add(1)
		go func(importpath, dir string) {
			defer wg.Done()
			exports := loadExports(dir)
			if exports != nil {
				pkgsMu.Lock()
				pkgs[importpath] = exports
				pkgsMu.Unlock()
			}
		}(pkg.importpath, pkg.dir)
	}
	pkgIndex.Unlock()
	wg.Wait()

	// Filter out packages missing required exported symbols.
	for symbol := range symbols {
		for importpath, exports := range pkgs {
			if !exports[symbol] {
				delete(pkgs, importpath)
			}
		}
	}
	for importpath := range pkgs {
		return importpath, nil
	}
	return "", nil
}

type visitFn func(node ast.Node) ast.Visitor

func (fn visitFn) Visit(node ast.Node) ast.Visitor {
	return fn(node)
}
<|endoftext|>


<commit_before>// Package gofsm is a library for building finite-state machines (automata).
//
// Why yet another FSM library for go?
//
// gofsm is aimed squarely at home automation - human visible, configured and
// friendly. The configuration format is yaml, and easy to read/write by hand.
//
// gofsm is used in the gohome automation project:
// http://github.com/barnybug/gohome
package gofsm

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"strings"
	"time"

	"gopkg.in/yaml.v2"
)

type Automata struct {
	Automaton map[string]*Automaton
	Actions   chan Action
	Changes   chan Change
}

type State struct {
	Name     string
	Steps    []Step
	Entering Actions
	Leaving  Actions
}

type Step struct {
	When    string
	Actions Actions
	Next    string
}

type Actions []string

type Transition struct {
	When    string
	Actions Actions
}

type Automaton struct {
	Start  string
	States map[string]struct {
		Entering Actions
		Leaving  Actions
	}
	Transitions map[string][]Transition
	Name        string
	State       *State
	Since       time.Time
	actions     chan Action
	changes     chan Change
	sm          map[string]*State
}

type Action struct {
	Name    string
	Trigger interface{}
	Change  Change
}

type Change struct {
	Automaton string
	Old       string
	New       string
	Since     time.Time
	Duration  time.Duration
	Trigger   interface{}
}

type Event interface {
	Match(s string) bool
}

func (self Action) String() string {
	return self.Name
}

func (self *Automata) Process(event Event) {
	for _, aut := range self.Automaton {
		aut.Process(event)
	}
}

func (self *Automata) String() string {
	var out string
	now := time.Now()
	for k, aut := range self.Automaton {
		if out != "" {
			out += "\n"
		}
		du := now.Sub(aut.Since)
		out += fmt.Sprintf("%s: %s for %s", k, aut.State.Name, du)
	}
	return out
}

func (self *Automaton) ChangeState(next string, event Event) {
	self.changeState(next, event, Actions{})
}

func (self *Automaton) changeState(next string, event Event, actions Actions) {
	now := time.Now()
	var change Change
	// is a state change happening
	stateChanged := (self.State.Name != next)

	if stateChanged {
		duration := now.Sub(self.Since)
		change = Change{Automaton: self.Name, Old: self.State.Name, New: next, Duration: duration, Since: self.Since, Trigger: event}

		// emit leaving actions
		for _, action := range self.State.Leaving {
			self.actions <- Action{action, event, change}
		}
	}

	// emit transition actions
	for _, action := range actions {
		self.actions <- Action{action, event, change}
	}

	// change state
	if stateChanged {
		self.State = self.sm[next]
		self.Since = now
		self.changes <- change

		// emit entering actions
		for _, action := range self.State.Entering {
			self.actions <- Action{action, event, change}
		}
	}
}

func (self *Automaton) Process(event Event) {
	for _, t := range self.State.Steps {
		if event.Match(t.When) {
			self.changeState(t.Next, event, t.Actions)
		}
	}
}

func (self *Automaton) load() error {
	if self.Start == "" {
		return errors.New("missing Start entry")
	}
	if len(self.States) == 0 {
		return errors.New("missing States entries")
	}
	if len(self.Transitions) == 0 {
		return errors.New("missing Transitions entries")
	}

	sm := map[string]*State{}

	ambigMap := map[string]map[string]bool{}

	var allStates []string
	for name, val := range self.States {
		state := State{Name: name}
		state.Entering = val.Entering
		state.Leaving = val.Leaving
		sm[name] = &state

		allStates = append(allStates, name)
		ambigMap[name] = map[string]bool{}
	}
	self.sm = sm

	var ok bool
	if self.State, ok = sm[self.Start]; !ok {
		return errors.New("starting State invalid")
	}
	self.Since = time.Now()

	type StringPair struct {
		_1 string
		_2 string
	}

	for name, trans := range self.Transitions {
		var pairs []StringPair
		lr := strings.SplitN(name, "->", 2)
		if len(lr) == 2 {
			// from->to
			var froms, tos []string
			if lr[0] == "*" {
				froms = allStates
			} else {
				froms = strings.Split(lr[0], ",")
			}
			if lr[1] == "*" {
				tos = allStates
			} else {
				tos = strings.Split(lr[1], ",")
			}
			for _, f := range froms {
				for _, t := range tos {
					pairs = append(pairs, StringPair{f, t})
				}
			}
		} else {
			// from1,from2 = from1->from1, from2->from2
			var froms []string
			if lr[0] == "*" {
				froms = allStates
			} else {
				froms = strings.Split(lr[0], ",")
			}
			for _, f := range froms {
				pairs = append(pairs, StringPair{f, f})
			}
		}

		for _, pair := range pairs {
			from, to := pair._1, pair._2
			var sfrom *State
			if sfrom, ok = self.sm[from]; !ok {
				return errors.New(fmt.Sprintf("State: %s not found", from))
			}
			if _, ok := self.sm[to]; !ok {
				return errors.New(fmt.Sprintf("State: %s not found", from))
			}

			for _, v := range trans {
				// check this state hasn't this exact condition already
				if ambigMap[from][v.When] {
					return errors.New(fmt.Sprintf("State: %s condition: %s is ambiguous", from, v.When))
				}
				ambigMap[from][v.When] = true
				t := Step{v.When, v.Actions, to}
				sfrom.Steps = append(sfrom.Steps, t)
			}
		}
	}
	return nil
}

type AutomataState map[string]AutomatonState

type AutomatonState struct {
	State string
	Since time.Time
}

func (self *Automata) Persist() AutomataState {
	ret := AutomataState{}
	for k, aut := range self.Automaton {
		ret[k] = AutomatonState{aut.State.Name, aut.Since}
	}
	return ret
}

func (self *Automata) Restore(s AutomataState) {
	for k, as := range s {
		if aut, ok := self.Automaton[k]; ok {
			if state, ok := aut.sm[as.State]; ok {
				aut.State = state
				aut.Since = as.Since
			} else {
				log.Println("Invalid restored state:", as.State)
			}
		}
	}
}

func LoadFile(filename string) (*Automata, error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return Load(data)
}

func Load(str []byte) (*Automata, error) {
	var aut Automata = Automata{Actions: make(chan Action, 32), Changes: make(chan Change, 32)}
	err := yaml.Unmarshal(str, &aut.Automaton)
	for k, a := range aut.Automaton {
		err := a.load()
		if err != nil {
			return nil, errors.New(fmt.Sprintf("%s: %s", k, err.Error()))
		}
		a.Name = k
		a.actions = aut.Actions
		a.changes = aut.Changes
	}

	return &aut, err
}
<commit_msg>Only perform one transition per call to Process<commit_after>// Package gofsm is a library for building finite-state machines (automata).
//
// Why yet another FSM library for go?
//
// gofsm is aimed squarely at home automation - human visible, configured and
// friendly. The configuration format is yaml, and easy to read/write by hand.
//
// gofsm is used in the gohome automation project:
// http://github.com/barnybug/gohome
package gofsm

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"strings"
	"time"

	"gopkg.in/yaml.v2"
)

type Automata struct {
	Automaton map[string]*Automaton
	Actions   chan Action
	Changes   chan Change
}

type State struct {
	Name     string
	Steps    []Step
	Entering Actions
	Leaving  Actions
}

type Step struct {
	When    string
	Actions Actions
	Next    string
}

type Actions []string

type Transition struct {
	When    string
	Actions Actions
}

type Automaton struct {
	Start  string
	States map[string]struct {
		Entering Actions
		Leaving  Actions
	}
	Transitions map[string][]Transition
	Name        string
	State       *State
	Since       time.Time
	actions     chan Action
	changes     chan Change
	sm          map[string]*State
}

type Action struct {
	Name    string
	Trigger interface{}
	Change  Change
}

type Change struct {
	Automaton string
	Old       string
	New       string
	Since     time.Time
	Duration  time.Duration
	Trigger   interface{}
}

type Event interface {
	Match(s string) bool
}

func (self Action) String() string {
	return self.Name
}

func (self *Automata) Process(event Event) {
	for _, aut := range self.Automaton {
		aut.Process(event)
	}
}

func (self *Automata) String() string {
	var out string
	now := time.Now()
	for k, aut := range self.Automaton {
		if out != "" {
			out += "\n"
		}
		du := now.Sub(aut.Since)
		out += fmt.Sprintf("%s: %s for %s", k, aut.State.Name, du)
	}
	return out
}

func (self *Automaton) ChangeState(next string, event Event) {
	self.changeState(next, event, Actions{})
}

func (self *Automaton) changeState(next string, event Event, actions Actions) {
	now := time.Now()
	var change Change
	// is a state change happening
	stateChanged := (self.State.Name != next)

	if stateChanged {
		duration := now.Sub(self.Since)
		change = Change{Automaton: self.Name, Old: self.State.Name, New: next, Duration: duration, Since: self.Since, Trigger: event}

		// emit leaving actions
		for _, action := range self.State.Leaving {
			self.actions <- Action{action, event, change}
		}
	}

	// emit transition actions
	for _, action := range actions {
		self.actions <- Action{action, event, change}
	}

	// change state
	if stateChanged {
		self.State = self.sm[next]
		self.Since = now
		self.changes <- change

		// emit entering actions
		for _, action := range self.State.Entering {
			self.actions <- Action{action, event, change}
		}
	}
}

func (self *Automaton) Process(event Event) {
	for _, t := range self.State.Steps {
		if event.Match(t.When) {
			self.changeState(t.Next, event, t.Actions)
			break
		}
	}
}

func (self *Automaton) load() error {
	if self.Start == "" {
		return errors.New("missing Start entry")
	}
	if len(self.States) == 0 {
		return errors.New("missing States entries")
	}
	if len(self.Transitions) == 0 {
		return errors.New("missing Transitions entries")
	}

	sm := map[string]*State{}

	ambigMap := map[string]map[string]bool{}

	var allStates []string
	for name, val := range self.States {
		state := State{Name: name}
		state.Entering = val.Entering
		state.Leaving = val.Leaving
		sm[name] = &state

		allStates = append(allStates, name)
		ambigMap[name] = map[string]bool{}
	}
	self.sm = sm

	var ok bool
	if self.State, ok = sm[self.Start]; !ok {
		return errors.New("starting State invalid")
	}
	self.Since = time.Now()

	type StringPair struct {
		_1 string
		_2 string
	}

	for name, trans := range self.Transitions {
		var pairs []StringPair
		lr := strings.SplitN(name, "->", 2)
		if len(lr) == 2 {
			// from->to
			var froms, tos []string
			if lr[0] == "*" {
				froms = allStates
			} else {
				froms = strings.Split(lr[0], ",")
			}
			if lr[1] == "*" {
				tos = allStates
			} else {
				tos = strings.Split(lr[1], ",")
			}
			for _, f := range froms {
				for _, t := range tos {
					pairs = append(pairs, StringPair{f, t})
				}
			}
		} else {
			// from1,from2 = from1->from1, from2->from2
			var froms []string
			if lr[0] == "*" {
				froms = allStates
			} else {
				froms = strings.Split(lr[0], ",")
			}
			for _, f := range froms {
				pairs = append(pairs, StringPair{f, f})
			}
		}

		for _, pair := range pairs {
			from, to := pair._1, pair._2
			var sfrom *State
			if sfrom, ok = self.sm[from]; !ok {
				return errors.New(fmt.Sprintf("State: %s not found", from))
			}
			if _, ok := self.sm[to]; !ok {
				return errors.New(fmt.Sprintf("State: %s not found", from))
			}

			for _, v := range trans {
				// check this state hasn't this exact condition already
				if ambigMap[from][v.When] {
					return errors.New(fmt.Sprintf("State: %s condition: %s is ambiguous", from, v.When))
				}
				ambigMap[from][v.When] = true
				t := Step{v.When, v.Actions, to}
				sfrom.Steps = append(sfrom.Steps, t)
			}
		}
	}
	return nil
}

type AutomataState map[string]AutomatonState

type AutomatonState struct {
	State string
	Since time.Time
}

func (self *Automata) Persist() AutomataState {
	ret := AutomataState{}
	for k, aut := range self.Automaton {
		ret[k] = AutomatonState{aut.State.Name, aut.Since}
	}
	return ret
}

func (self *Automata) Restore(s AutomataState) {
	for k, as := range s {
		if aut, ok := self.Automaton[k]; ok {
			if state, ok := aut.sm[as.State]; ok {
				aut.State = state
				aut.Since = as.Since
			} else {
				log.Println("Invalid restored state:", as.State)
			}
		}
	}
}

func LoadFile(filename string) (*Automata, error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return Load(data)
}

func Load(str []byte) (*Automata, error) {
	var aut Automata = Automata{Actions: make(chan Action, 32), Changes: make(chan Change, 32)}
	err := yaml.Unmarshal(str, &aut.Automaton)
	for k, a := range aut.Automaton {
		err := a.load()
		if err != nil {
			return nil, errors.New(fmt.Sprintf("%s: %s", k, err.Error()))
		}
		a.Name = k
		a.actions = aut.Actions
		a.changes = aut.Changes
	}

	return &aut, err
}
<|endoftext|>


<commit_before>package tag

import (
	"bufio"
	"bytes"
	"fmt"
	"image"
	"image/color"
	"log"
	"os"
	"strings"
//	"time"

	"github.com/as/edit"
	"github.com/as/event"
	"github.com/as/frame"
	"github.com/as/frame/font"
	"github.com/as/frame/win"
	"github.com/as/text"
	"github.com/as/text/action"
	"github.com/as/text/find"
	"github.com/as/text/kbd"
	mus "github.com/as/text/mouse"
	//"github.com/as/worm"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/mouse"
)

var db = win.Db
var un = win.Un
var trace = win.Trace

func p(e mouse.Event) image.Point {
	return image.Pt(int(e.X), int(e.Y))

}

type doter interface {
	Dot() (int64, int64)
}

func whatsdot(d doter) string {
	q0, q1 := d.Dot()
	return fmt.Sprintf("Dot: [%d:%d]", q0, q1)
}

// Put
var (
	Buttonsdown = 0
	noselect    bool
	lastclickpt image.Point
)

type Tag struct {
	sp image.Point
	*win.Win
	Body       *win.Win
	Scrolling  bool
	scrolldy   int
	dirty      bool
	r0, r1     int64
	red, green frame.Color
	escR image.Rectangle
//	Log        worm.Logger	// TODO
	offset int64
}

func (t *Tag) Dirty() bool {
	return t.dirty || t.Win.Dirty() || (t.Body != nil && t.Body.Dirty())
}

func (t *Tag) Mark() {
	t.dirty = true
}

func (t *Tag) Loc() image.Rectangle {
	r := t.Win.Loc()
	if t.Body != nil {
		r.Max.Y += t.Body.Loc().Dy()
	}
	return r
}

// TagSize returns the size of a tag given the font
func TagSize(ft *font.Font) int {
	return ft.Dy() + ft.Dy()/2
}

// TagPad returns the padding for the tag given the window's padding
// always returns an x-aligned point
func TagPad(wpad image.Point) image.Point {
	return image.Pt(wpad.X, 3)
}

// Put
func NewTag(src screen.Screen, wind screen.Window, ft *font.Font, sp, size, pad image.Point, cols frame.Color) *Tag {

	// Make the main tag
	tagY := TagSize(ft)

	// Make tag
	wtag := win.New(src, ft, wind,
		sp,
		image.Pt(size.X, tagY),
		TagPad(pad), cols,
	)

	sp = sp.Add(image.Pt(0, tagY))
	size = size.Sub(image.Pt(0, tagY))
	if size.Y < tagY {
		return &Tag{sp: sp, Win: wtag, Body: nil}
	}
	// Make window
	cols.Back = Yellow
	w := win.New(src, ft, wind,
		sp,
		size,
		pad, frame.A,
	)
//	lg := worm.NewCoalescer(worm.NewLogger(), time.Second*3)
//	w.Editor = text.NewHistory(w.Editor, lg)
	acol := frame.A
	Green := image.NewUniform(color.RGBA{0x99, 0xDD, 0x99, 192})
	acol.Hi.Back = Green
	green := acol

	Red := image.NewUniform(color.RGBA{0xDD, 0x99, 0x99, 192})
	acol.Hi.Back = Red
	red := acol

	return &Tag{sp: sp, Win: wtag, Body: w,
		 // Log: lg,
		red: red, green: green}
}

func (t *Tag) Move(pt image.Point) {
	t.Win.Move(pt)
	if t.Body == nil {
		return
	}
	pt.Y += t.Win.Loc().Dy()
	t.Body.Move(pt)
}

func (t *Tag) Resize(pt image.Point) {
	dy := TagSize(t.Win.Font)
	if pt.X < dy || pt.Y < dy {
		println("ignore daft size request:", pt.String())
		return
	}
	t.Win.Resize(image.Pt(pt.X, dy))
	pt.Y -= dy
	if t.Body != nil {
		t.Body.Resize(pt)
	}
}

func mustCompile(prog string) *edit.Command {
	p, err := edit.Compile(prog)
	if err != nil {
		log.Printf("tag.go:/mustCompile/: failed to compile %q\n", prog)
		return nil
	}
	return p
}

func (t *Tag) Get(name string) {
	name, addr := action.SplitPath(name)
	w := t.Body
	wtag := t.Win
	wtag.Delete(0, wtag.Len())
	wtag.InsertString(name+"\tPut Del [Edit , ]", 0)
	wtag.Refresh()
	if w == nil {
		return
	}
	s := readfile(name)
	fmt.Printf("files size is %d\n", len(s))
	w.Insert(s, 0)
	if addr != "" {
		w.Send(mustCompile("#0"))
		w.Send(mustCompile(addr))
	}
}

type GetEvent struct {
	Path  string
	Addr  string
	IsDir bool
}

func (t *Tag) FileName() string {
	if t == nil || t.Win == nil {
		return ""
	}
	name, err := bufio.NewReader(bytes.NewReader(t.Win.Bytes())).ReadString('\t')
	if err != nil {
		return ""
	}
	return strings.TrimSpace(name)
}

func (t *Tag) Put() (err error) {
	name := t.FileName()
	if name == "" {
		return fmt.Errorf("no file")
	}
	writefile(name, t.Body.Bytes())
	return nil
}
func pt(e mouse.Event) image.Point{
	return image.Pt(int(e.X), int(e.Y))
}
func (t *Tag) Mouse(act text.Editor, e interface{}) {
	win := act.(*win.Win)
	if act := win; true {
		org := act.Origin()
		switch e := e.(type) {
		case mus.SnarfEvent:
			snarf(act)
		case mus.InsertEvent:
			paste(act)
		case mus.MarkEvent:
			if e.Button != 1 {
				t.r0, t.r1 = act.Dot()
			}
			q0 := org + act.IndexOf(p(e.Event))
			q1 := q0
			act.Sq = q0
			if e.Button == 1 && e.Double {
				q0, q1 = find.FreeExpand(act, q0)
				t.escR = image.Rect(-3,-3,3,3).Add(pt(e.Event))
				println(q0,q1)
				println("double click")
			}
			act.Select(q0, q1)
		case mus.SweepEvent:
			if t.escR != image.ZR {
				if pt(e.Event).In(t.escR){
					break
				}
				t.escR = image.ZR
				act.Select(act.Sq, act.Sq)
			}
			q0, q1 := act.Dot()
			//r0 := org+act.IndexOf(p(e.Event))
			sweeper := text.Sweeper(act)
			if act == t.Win {
				sweeper = mus.NewNopScroller(act)
			}
			act.Sq, q0, q1 = mus.Sweep(sweeper, e, 15, act.Sq, q0, q1, act)
			if e.Button == 1 {
				act.Select(q0, q1)
			} else {
				act.Select(q0, q1)
			}
		case mus.SelectEvent:
			q0, q1 := act.Dot()
			println(q0,q1)
			if e.Button == 1 {
				act.Select(q0, q1)
				break
			}
			if e.Button == 2 || e.Button == 3 {
				q0, q1 := act.Dot()
				if q0 == q1 && text.Region3(q0, t.r0-1, t.r1) == 0 {
					// just use the existing selection and look
					q0, q1 = t.r0, t.r1
					act.Select(q0, q1)
				}
				if q0 == q1 {
					q0, q1 = find.ExpandFile(act.Bytes(), q0)
				}

				from := text.Editor(act)
				if from == t.Win {
					from = t
				}
				if e.Button == 3 {
					act.Select(q0, q1)
					act.SendFirst(event.Look{
						Rec: event.Rec{
							Q0: q0,
							Q1: q1,
							P:  act.Bytes()[q0:q1],
						},
						From:     from,
						To:       []event.Editor{t.Body},
						FromFile: t.FileName(),
					})
				} else {
					act.SendFirst(event.Cmd{
						Rec: event.Rec{
							Q0: q0, Q1: q1,
							P: act.Bytes()[q0:q1],
						},
						From:     from,
						To:       []event.Editor{t.Body},
						FromFile: t.FileName(),
					})
				}
			}
		}
	}
}

// Put
func (t *Tag) Handle(act text.Editor, e interface{}) {
	switch e := e.(type) {
	case mus.MarkEvent, mus.SweepEvent, mus.SelectEvent, mus.SnarfEvent, mus.InsertEvent:
		t.Mouse(act, e)
	case string:
		if e == "Redo" {
			//			act.Redo()
		} else if e == "Undo" {
		/*
			ev, err := t.Log.ReadAt(t.Log.Len()-1-t.offset)
			t.offset++
			if err != nil{
				t.SendFirst(err)
				return
			}
			ev2 := event.Invert(ev)
			switch ev2 := ev2.(type){
			case *event.Insert:
			t.Send(fmt.Errorf("INsert %#v\n", ev))
				act.Insert(ev2.P, ev2.Q0)
			case *event.Delete:
				q0,q1 := ev2.Q0, ev2.Q1
				if q0 > q1{
					q0,q1=q1,q0
				}
				if q0 != q1{
					q1--
				}
			t.Send(fmt.Errorf("Delete %#v\n", ev))
				act.Delete(q0,q1)
			}
			t.Send(fmt.Errorf("%#v\n", ev))
		*/
			//			act.Undo()
		} else if e == "Put" {
			t.Put()
		} else if e == "Get" {
			t.Get(t.FileName())
		}
		t.Mark()
	case *edit.Command:
		fmt.Printf("command %#v\n", e)
		if e == nil {
			break
		}
		fn := e.Func()
		if fn != nil {
			fn(t.Body) // Always execute on body for now
		}
		t.Mark()
	case key.Event:
		if e.Direction == 2 {
			break
		}
		ntab := int64(-1)
		if e.Rune == '\n' || e.Rune == '\r' && act == t.Body {
			q0, q1 := act.Dot()
			if q0 == q1 {
				p := act.Bytes()
				l0, _ := find.Findlinerev(p, q0, 0)
				ntab = find.Accept(p, l0, []byte{'\t'})
				ntab -= l0 + 1
			}
		}
		kbd.SendClient(act, e)
		e.Rune = '\t'
		for ntab >= 0 {
			kbd.SendClient(act, e)
			ntab--
		}
	}
	t.dirty = true
}

func (t *Tag) Upload(wind screen.Window) {
	if t.Body != nil && t.Body.Dirty() {
		t.Body.Upload()
	}
	if t.Win.Dirty() {
		t.Win.Upload()
	}
}

func (t *Tag) Refresh() {
	if t.Body != nil {
		t.Body.Refresh()
	}
	if t.Win.Dirty() {
		t.Win.Refresh()
	}
}

func isdir(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		if err == os.ErrNotExist {
			return false
		}
		fmt.Println(err)
		return false
	}
	return fi.IsDir()
}
func isfile(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}
<commit_msg>fixes as/a#8<commit_after>package tag

import (
	"bufio"
	"bytes"
	"fmt"
	"image"
	"image/color"
	"log"
	"os"
	"strings"
//	"time"

	"github.com/as/edit"
	"github.com/as/event"
	"github.com/as/frame"
	"github.com/as/frame/font"
	"github.com/as/frame/win"
	"github.com/as/text"
	"github.com/as/text/action"
	"github.com/as/text/find"
	"github.com/as/text/kbd"
	mus "github.com/as/text/mouse"
	//"github.com/as/worm"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/mouse"
)

var db = win.Db
var un = win.Un
var trace = win.Trace

func p(e mouse.Event) image.Point {
	return image.Pt(int(e.X), int(e.Y))

}

type doter interface {
	Dot() (int64, int64)
}

func whatsdot(d doter) string {
	q0, q1 := d.Dot()
	return fmt.Sprintf("Dot: [%d:%d]", q0, q1)
}

// Put
var (
	Buttonsdown = 0
	noselect    bool
	lastclickpt image.Point
)

type Tag struct {
	sp image.Point
	*win.Win
	Body       *win.Win
	Scrolling  bool
	scrolldy   int
	dirty      bool
	r0, r1     int64
	red, green frame.Color
	escR image.Rectangle
//	Log        worm.Logger	// TODO
	offset int64
}

func (t *Tag) Dirty() bool {
	return t.dirty || t.Win.Dirty() || (t.Body != nil && t.Body.Dirty())
}

func (t *Tag) Mark() {
	t.dirty = true
}

func (t *Tag) Loc() image.Rectangle {
	r := t.Win.Loc()
	if t.Body != nil {
		r.Max.Y += t.Body.Loc().Dy()
	}
	return r
}

// TagSize returns the size of a tag given the font
func TagSize(ft *font.Font) int {
	return ft.Dy() + ft.Dy()/2
}

// TagPad returns the padding for the tag given the window's padding
// always returns an x-aligned point
func TagPad(wpad image.Point) image.Point {
	return image.Pt(wpad.X, 3)
}

// Put
func NewTag(src screen.Screen, wind screen.Window, ft *font.Font, sp, size, pad image.Point, cols frame.Color) *Tag {

	// Make the main tag
	tagY := TagSize(ft)

	// Make tag
	wtag := win.New(src, ft, wind,
		sp,
		image.Pt(size.X, tagY),
		TagPad(pad), cols,
	)

	sp = sp.Add(image.Pt(0, tagY))
	size = size.Sub(image.Pt(0, tagY))
	if size.Y < tagY {
		return &Tag{sp: sp, Win: wtag, Body: nil}
	}
	// Make window
	cols.Back = Yellow
	w := win.New(src, ft, wind,
		sp,
		size,
		pad, frame.A,
	)
//	lg := worm.NewCoalescer(worm.NewLogger(), time.Second*3)
//	w.Editor = text.NewHistory(w.Editor, lg)
	acol := frame.A
	Green := image.NewUniform(color.RGBA{0x99, 0xDD, 0x99, 192})
	acol.Hi.Back = Green
	green := acol

	Red := image.NewUniform(color.RGBA{0xDD, 0x99, 0x99, 192})
	acol.Hi.Back = Red
	red := acol

	return &Tag{sp: sp, Win: wtag, Body: w,
		 // Log: lg,
		red: red, green: green}
}

func (t *Tag) Move(pt image.Point) {
	t.Win.Move(pt)
	if t.Body == nil {
		return
	}
	pt.Y += t.Win.Loc().Dy()
	t.Body.Move(pt)
}

func (t *Tag) Resize(pt image.Point) {
	dy := TagSize(t.Win.Font)
	if pt.X < dy || pt.Y < dy {
		println("ignore daft size request:", pt.String())
		return
	}
	t.Win.Resize(image.Pt(pt.X, dy))
	pt.Y -= dy
	if t.Body != nil {
		t.Body.Resize(pt)
	}
}

func mustCompile(prog string) *edit.Command {
	p, err := edit.Compile(prog)
	if err != nil {
		log.Printf("tag.go:/mustCompile/: failed to compile %q\n", prog)
		return nil
	}
	return p
}

func (t *Tag) Get(name string) {
	w := t.Body
	if w == nil{
		w.SendFirst(fmt.Errorf("tag: no body to get %q\n", name))
		return
	}
	name, addr := action.SplitPath(name)
	wtag := t.Win
	p := wtag.Bytes()
	maint := find.Find(p, 0, []byte{'|'})
	if maint == -1 {
		maint = int64(len(p))
	}
	wtag.Delete(0, maint+1)
	wtag.InsertString(name+"\tPut Del |", 0)
	wtag.Refresh()
	s := readfile(name)
	fmt.Printf("files size is %d\n", len(s))
	w.Delete(0, w.Len())
	w.Insert(s, 0)
	w.Select(0,0)
	if addr != "" {
		w.SendFirst(mustCompile("#0"))
		w.SendFirst(mustCompile(addr))
	}
}

type GetEvent struct {
	Path  string
	Addr  string
	IsDir bool
}

func (t *Tag) FileName() string {
	if t == nil || t.Win == nil {
		return ""
	}
	name, err := bufio.NewReader(bytes.NewReader(t.Win.Bytes())).ReadString('\t')
	if err != nil {
		return ""
	}
	return strings.TrimSpace(name)
}

func (t *Tag) Put() (err error) {
	name := t.FileName()
	if name == "" {
		return fmt.Errorf("no file")
	}
	writefile(name, t.Body.Bytes())
	return nil
}
func pt(e mouse.Event) image.Point{
	return image.Pt(int(e.X), int(e.Y))
}
func (t *Tag) Mouse(act text.Editor, e interface{}) {
	win := act.(*win.Win)
	if act := win; true {
		org := act.Origin()
		switch e := e.(type) {
		case mus.SnarfEvent:
			snarf(act)
		case mus.InsertEvent:
			paste(act)
		case mus.MarkEvent:
			if e.Button != 1 {
				t.r0, t.r1 = act.Dot()
			}
			q0 := org + act.IndexOf(p(e.Event))
			q1 := q0
			act.Sq = q0
			if e.Button == 1 && e.Double {
				q0, q1 = find.FreeExpand(act, q0)
				t.escR = image.Rect(-3,-3,3,3).Add(pt(e.Event))
				println(q0,q1)
				println("double click")
			}
			act.Select(q0, q1)
		case mus.SweepEvent:
			if t.escR != image.ZR {
				if pt(e.Event).In(t.escR){
					break
				}
				t.escR = image.ZR
				act.Select(act.Sq, act.Sq)
			}
			q0, q1 := act.Dot()
			//r0 := org+act.IndexOf(p(e.Event))
			sweeper := text.Sweeper(act)
			if act == t.Win {
				sweeper = mus.NewNopScroller(act)
			}
			act.Sq, q0, q1 = mus.Sweep(sweeper, e, 15, act.Sq, q0, q1, act)
			if e.Button == 1 {
				act.Select(q0, q1)
			} else {
				act.Select(q0, q1)
			}
		case mus.SelectEvent:
			q0, q1 := act.Dot()
			println(q0,q1)
			if e.Button == 1 {
				act.Select(q0, q1)
				break
			}
			if e.Button == 2 || e.Button == 3 {
				q0, q1 := act.Dot()
				if q0 == q1 && text.Region3(q0, t.r0-1, t.r1) == 0 {
					// just use the existing selection and look
					q0, q1 = t.r0, t.r1
					act.Select(q0, q1)
				}
				if q0 == q1 {
					q0, q1 = find.ExpandFile(act.Bytes(), q0)
				}

				from := text.Editor(act)
				if from == t.Win {
					from = t
				}
				if e.Button == 3 {
					act.Select(q0, q1)
					act.SendFirst(event.Look{
						Rec: event.Rec{
							Q0: q0,
							Q1: q1,
							P:  act.Bytes()[q0:q1],
						},
						From:     from,
						To:       []event.Editor{t.Body},
						FromFile: t.FileName(),
					})
				} else {
					act.SendFirst(event.Cmd{
						Rec: event.Rec{
							Q0: q0, Q1: q1,
							P: act.Bytes()[q0:q1],
						},
						From:     from,
						To:       []event.Editor{t.Body},
						FromFile: t.FileName(),
					})
				}
			}
		}
	}
}

// Put
func (t *Tag) Handle(act text.Editor, e interface{}) {
	switch e := e.(type) {
	case mus.MarkEvent, mus.SweepEvent, mus.SelectEvent, mus.SnarfEvent, mus.InsertEvent:
		t.Mouse(act, e)
	case string:
		if e == "Redo" {
			//			act.Redo()
		} else if e == "Undo" {
		/*
			ev, err := t.Log.ReadAt(t.Log.Len()-1-t.offset)
			t.offset++
			if err != nil{
				t.SendFirst(err)
				return
			}
			ev2 := event.Invert(ev)
			switch ev2 := ev2.(type){
			case *event.Insert:
			t.Send(fmt.Errorf("INsert %#v\n", ev))
				act.Insert(ev2.P, ev2.Q0)
			case *event.Delete:
				q0,q1 := ev2.Q0, ev2.Q1
				if q0 > q1{
					q0,q1=q1,q0
				}
				if q0 != q1{
					q1--
				}
			t.Send(fmt.Errorf("Delete %#v\n", ev))
				act.Delete(q0,q1)
			}
			t.Send(fmt.Errorf("%#v\n", ev))
		*/
			//			act.Undo()
		} else if e == "Put" {
			t.Put()
		} else if e == "Get" {
			t.Get(t.FileName())
		}
		t.Mark()
	case *edit.Command:
		fmt.Printf("command %#v\n", e)
		if e == nil {
			break
		}
		fn := e.Func()
		if fn != nil {
			fn(t.Body) // Always execute on body for now
		}
		t.Mark()
	case key.Event:
		if e.Direction == 2 {
			break
		}
		ntab := int64(-1)
		if (e.Rune == '\n' || e.Rune == '\r') && act == t.Body {
			q0, q1 := act.Dot()
			if q0 == q1 {
				p := act.Bytes()
				l0, _ := find.Findlinerev(p, q0, 0)
				ntab = find.Accept(p, l0, []byte{'\t'})
				ntab -= l0 + 1
			}
		}
		kbd.SendClient(act, e)
		for ntab >= 0 {
			e.Rune = '\t'
			kbd.SendClient(act, e)
			ntab--
		}
	}
	t.dirty = true
}

func (t *Tag) Upload(wind screen.Window) {
	if t.Body != nil && t.Body.Dirty() {
		t.Body.Upload()
	}
	if t.Win.Dirty() {
		t.Win.Upload()
	}
}

func (t *Tag) Refresh() {
	if t.Body != nil {
		t.Body.Refresh()
	}
	if t.Win.Dirty() {
		t.Win.Refresh()
	}
}

func isdir(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		if err == os.ErrNotExist {
			return false
		}
		fmt.Println(err)
		return false
	}
	return fi.IsDir()
}
func isfile(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/signal"
	"strings"
	"unicode/utf8"
)

const (
	VERSION                   = "0.0.1"
	defaultPlaceholder        = "{{}}"
	keyBackspace              = 8
	keyDelete                 = 127
	keyEndOfTransmission      = 4
	keyLineFeed               = 10
	keyCarriageReturn         = 13
	keyEndOfTransmissionBlock = 23
	keyEscape		  = 27
)

var placeholder string

var usage = `fzz allows you to run a command interactively.

Usage:

	fzz command

The command MUST include the placeholder '{{}}'.

Arguments:

	-v		Print version and exit
`

func printUsage() {
	fmt.Printf(usage)
}

func isPipe(f *os.File) bool {
	s, err := f.Stat()
	if err != nil {
		return false
	}

	return s.Mode()&os.ModeNamedPipe != 0
}

func containsPlaceholder(s []string, ph string) bool {
	for _, v := range s {
		if strings.Contains(v, ph) {
			return true
		}
	}
	return false
}

func validPlaceholder(p string) bool {
	return len(p)%2 == 0
}

func removeLastWord(s []byte) []byte {
	fields := bytes.Fields(s)
	if len(fields) > 0 {
		r := bytes.Join(fields[:len(fields)-1], []byte{' '})
		if len(r) > 1 {
			r = append(r, ' ')
		}
		return r
	}
	return []byte{}
}

func main() {
	flVersion := flag.Bool("v", false, "Print fzz version and quit")
	flag.Usage = printUsage
	flag.Parse()

	if *flVersion {
		fmt.Printf("fzz %s\n", VERSION)
		os.Exit(0)
	}

	if len(flag.Args()) < 2 {
		fmt.Fprintf(os.Stderr, usage)
		os.Exit(1)
	}

	if placeholder = os.Getenv("FZZ_PLACEHOLDER"); placeholder == "" {
		placeholder = defaultPlaceholder
	}

	if !validPlaceholder(placeholder) {
		fmt.Fprintln(os.Stderr, "Placeholder is not valid, needs even number of characters")
		os.Exit(1)
	}

	if !containsPlaceholder(flag.Args(), placeholder) {
		fmt.Fprintln(os.Stderr, "No placeholder in arguments")
		os.Exit(1)
	}

	tty, err := NewTTY()
	if err != nil {
		log.Fatal(err)
	}
	defer tty.resetState()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		tty.resetState()
		os.Exit(1)
	}()
	tty.setSttyState("cbreak", "-echo")

	printer := NewPrinter(tty, tty.cols, tty.rows-1) // prompt is one row
	runner := &Runner{
		printer:     printer,
		template:    flag.Args(),
		placeholder: placeholder,
	}

	if isPipe(os.Stdin) {
		runner.stdinbuf = new(bytes.Buffer)
		io.Copy(runner.stdinbuf, os.Stdin)
	}

	input := make([]byte, 0)
	b := make([]byte, 1)

	for {
		tty.resetScreen()
		tty.printPrompt(input[:len(input)])

		if len(input) > 0 {
			runner.killCurrent()

			cmdInput := make([]byte, len(input))
			copy(cmdInput, input)

			go func() {
				runner.runWithInput(cmdInput)
				tty.cursorAfterPrompt(utf8.RuneCount(input))
			}()
		}

		tty.Read(b)
		switch b[0] {
		case keyBackspace, keyDelete:
			if len(input) > 1 {
				r, rsize := utf8.DecodeLastRune(input)
				if r == utf8.RuneError {
					input = input[:len(input)-1]
				} else {
					input = input[:len(input)-rsize]
				}
			} else if len(input) == 1 {
				input = nil
			}
		case keyEndOfTransmission, keyLineFeed, keyCarriageReturn:
			tty.resetScreen()
			runner.writeCmdStdout(os.Stdout)
			return
		case keyEscape:
			tty.resetScreen()
			return
		case keyEndOfTransmissionBlock:
			input = removeLastWord(input)
		default:
			// TODO: Default is wrong here. Only append printable characters to
			// input
			input = append(input, b...)
		}
	}
}
<commit_msg>Avoid reading incomplete utf8 sequences<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/signal"
	"strings"
	"unicode/utf8"
)

const (
	VERSION                   = "0.0.1"
	defaultPlaceholder        = "{{}}"
	keyBackspace              = 8
	keyDelete                 = 127
	keyEndOfTransmission      = 4
	keyLineFeed               = 10
	keyCarriageReturn         = 13
	keyEndOfTransmissionBlock = 23
	keyEscape		  = 27
)

var placeholder string

var usage = `fzz allows you to run a command interactively.

Usage:

	fzz command

The command MUST include the placeholder '{{}}'.

Arguments:

	-v		Print version and exit
`

func printUsage() {
	fmt.Printf(usage)
}

func isPipe(f *os.File) bool {
	s, err := f.Stat()
	if err != nil {
		return false
	}

	return s.Mode()&os.ModeNamedPipe != 0
}

func containsPlaceholder(s []string, ph string) bool {
	for _, v := range s {
		if strings.Contains(v, ph) {
			return true
		}
	}
	return false
}

func validPlaceholder(p string) bool {
	return len(p)%2 == 0
}

func removeLastWord(s []byte) []byte {
	fields := bytes.Fields(s)
	if len(fields) > 0 {
		r := bytes.Join(fields[:len(fields)-1], []byte{' '})
		if len(r) > 1 {
			r = append(r, ' ')
		}
		return r
	}
	return []byte{}
}

func main() {
	flVersion := flag.Bool("v", false, "Print fzz version and quit")
	flag.Usage = printUsage
	flag.Parse()

	if *flVersion {
		fmt.Printf("fzz %s\n", VERSION)
		os.Exit(0)
	}

	if len(flag.Args()) < 2 {
		fmt.Fprintf(os.Stderr, usage)
		os.Exit(1)
	}

	if placeholder = os.Getenv("FZZ_PLACEHOLDER"); placeholder == "" {
		placeholder = defaultPlaceholder
	}

	if !validPlaceholder(placeholder) {
		fmt.Fprintln(os.Stderr, "Placeholder is not valid, needs even number of characters")
		os.Exit(1)
	}

	if !containsPlaceholder(flag.Args(), placeholder) {
		fmt.Fprintln(os.Stderr, "No placeholder in arguments")
		os.Exit(1)
	}

	tty, err := NewTTY()
	if err != nil {
		log.Fatal(err)
	}
	defer tty.resetState()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		<-c
		tty.resetState()
		os.Exit(1)
	}()
	tty.setSttyState("cbreak", "-echo")

	printer := NewPrinter(tty, tty.cols, tty.rows-1) // prompt is one row
	runner := &Runner{
		printer:     printer,
		template:    flag.Args(),
		placeholder: placeholder,
	}

	if isPipe(os.Stdin) {
		runner.stdinbuf = new(bytes.Buffer)
		io.Copy(runner.stdinbuf, os.Stdin)
	}

	input := make([]byte, 0)
	rs := bufio.NewScanner(tty)
	rs.Split(bufio.ScanRunes)

	for {
		tty.resetScreen()
		tty.printPrompt(input[:len(input)])

		if len(input) > 0 {
			runner.killCurrent()

			cmdInput := make([]byte, len(input))
			copy(cmdInput, input)

			go func() {
				runner.runWithInput(cmdInput)
				tty.cursorAfterPrompt(utf8.RuneCount(input))
			}()
		}

		if !rs.Scan() {
			tty.resetScreen()
			log.Fatal(rs.Err())
		}
		b := rs.Bytes()

		switch b[0] {
		case keyBackspace, keyDelete:
			if len(input) > 1 {
				r, rsize := utf8.DecodeLastRune(input)
				if r == utf8.RuneError {
					input = input[:len(input)-1]
				} else {
					input = input[:len(input)-rsize]
				}
			} else if len(input) == 1 {
				input = nil
			}
		case keyEndOfTransmission, keyLineFeed, keyCarriageReturn:
			tty.resetScreen()
			runner.writeCmdStdout(os.Stdout)
			return
		case keyEscape:
			tty.resetScreen()
			return
		case keyEndOfTransmissionBlock:
			input = removeLastWord(input)
		default:
			// TODO: Default is wrong here. Only append printable characters to
			// input
			input = append(input, b...)
		}
	}
}
<|endoftext|>


<commit_before>package g

import (
	"log"
	"runtime"
)

// changelog:
// 0.0.1: init project
// 0.0.4: bugfix: set replicas before add node
// 0.0.8: change receiver, mv proc cron to proc pkg, add readme, add gitversion, add config reload, add trace tools
// 0.0.9: fix bugs of conn pool(use transfer's private conn pool, named & minimum)
// 0.0.10: use more efficient proc & sema, rm conn_pool status log
// 0.0.11: fix bug: all graphs' traffic delined when one graph broken down, modify retry interval
// 0.0.14: support sending multi copies to graph node, align ts for judge, add filter
// 0.1.4: 添加influxdb存储支持；用于流量采集系统，修改程序名称以区分；修改程序启功方式支持supervisor。
// 0.1.5: 修改项目名称
// 0.1.7：删除 judge 和 graph 部分
// 0.1.9: 添加 mallard pfc统计
// 0.2.0: 添加优雅重启支持

const (
	VERSION      = "0.2.0"
	GAUGE        = "GAUGE"
	COUNTER      = "COUNTER"
	DERIVE       = "DERIVE"
	DEFAULT_STEP = 60
	MIN_STEP     = 30
)

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}
<commit_msg>更新版本号<commit_after>package g

import (
	"log"
	"runtime"
)

// changelog:
// 0.0.1: init project
// 0.0.4: bugfix: set replicas before add node
// 0.0.8: change receiver, mv proc cron to proc pkg, add readme, add gitversion, add config reload, add trace tools
// 0.0.9: fix bugs of conn pool(use transfer's private conn pool, named & minimum)
// 0.0.10: use more efficient proc & sema, rm conn_pool status log
// 0.0.11: fix bug: all graphs' traffic delined when one graph broken down, modify retry interval
// 0.0.14: support sending multi copies to graph node, align ts for judge, add filter
// 0.1.4: 添加influxdb存储支持；用于流量采集系统，修改程序名称以区分；修改程序启功方式支持supervisor。
// 0.1.5: 修改项目名称
// 0.1.7：删除 judge 和 graph 部分
// 0.1.9: 添加 mallard pfc统计
// 0.2.0: 添加优雅重启支持
// 1.0.0: 修改打包方式

const (
	VERSION      = "1.0.0"
	GAUGE        = "GAUGE"
	COUNTER      = "COUNTER"
	DERIVE       = "DERIVE"
	DEFAULT_STEP = 60
	MIN_STEP     = 30
)

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}
<|endoftext|>