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

package imap

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

// ErrAborted is returned by Command.Result when the command execution is
// interrupted prior to receiving a completion response from the server. This is
// usually caused by a break in the connection.
var ErrAborted = errors.New("imap: command aborted")

// abort is a sentinel value assigned to Command.result to indicate the absence
// of a valid command completion response.
var abort = new(Response)

// Command represents a single command sent to the server.
type Command struct {
	// FIFO queue for command data. These are the responses that were accepted
	// by this command's filter. New responses are appended to the end as they
	// are received.
	Data []*Response

	// Client that created this Command instance.
	client *Client

	// Command execution parameters copied from Client.CommandConfig.
	config CommandConfig

	// Command tag assigned by the Client.
	tag string

	// UID flag for FETCH, STORE, COPY, and SEARCH commands.
	uid bool

	// Command name without the UID prefix.
	name string

	// Sequence numbers or UIDs of messages affected by this command. This is
	// used to filter FETCH responses.
	seqset *SeqSet

	// Raw command text without CRLFs or literal strings.
	raw string

	// Command completion response. This is set to abort if the command is not
	// in progress, but a valid completion response was not received.
	result *Response
}

// newCommand initializes and returns a new Command instance. Nil is returned if
// the specified name does not appear in c.CommandConfig.
func newCommand(c *Client, name string) *Command {
	config := c.CommandConfig[name]
	if config == nil {
		return nil
	}
	cmd := &Command{client: c, config: *config, name: name, raw: name}
	if len(name) > 4 && name[:4] == "UID " {
		cmd.uid = true
		cmd.name = name[4:]
	}
	return cmd
}

// Client returns the Client instance that created this command.
func (cmd *Command) Client() *Client {
	return cmd.client
}

// Tag returns the command tag assigned by the Client.
func (cmd *Command) Tag() string {
	return cmd.tag
}

// UID returns true if the command is using UIDs instead of message sequence
// numbers.
func (cmd *Command) UID() bool {
	return cmd.uid
}

// Name returns the command name. If full == true, the UID prefix is included
// for UID commands.
func (cmd *Command) Name(full bool) string {
	if full && cmd.uid {
		return "UID " + cmd.name
	}
	return cmd.name
}

// InProgress returns true until the command completion result is available. No
// new responses will be appended to cmd.Data after this method returns false.
func (cmd *Command) InProgress() bool {
	return cmd.result == nil
}

// Result returns the command completion result. The call blocks until the
// command is no longer in progress. If expect != 0, an error is returned if the
// completion status is other than expected. ErrAborted is returned if the
// command execution was interrupted prior to receiving a completion response.
func (cmd *Command) Result(expect RespStatus) (rsp *Response, err error) {
	for cmd.result == nil {
		if err = cmd.client.Recv(block); err != nil {
			return
		}
	}
	if rsp = cmd.result; rsp == abort {
		rsp, err = nil, ErrAborted
	} else if expect != 0 && rsp.Status&expect == 0 {
		err = ResponseError{rsp, "unexpected completion status"}
	}
	return
}

// String returns the raw command text without CRLFs or literal data.
func (cmd *Command) String() string {
	return cmd.raw
}

// rawCommand contains the raw text and literals about to be sent to the server.
type rawCommand struct {
	*bytes.Buffer // Command text, including all required CRLFs

	literals []Literal // Literal strings
	nonsync  bool      // Support for non-synchronizing literals (RFC 2088)
	binary   bool      // Support for binary literals (RFC 3516)
}

// build returns a rawCommand struct constructed from the command parameters.
func (cmd *Command) build(tag string, fields []Field) (*rawCommand, error) {
	raw := &rawCommand{
		Buffer:  bytes.NewBuffer(make([]byte, 0, 128)),
		nonsync: cmd.client.Caps["LITERAL+"],
		binary:  cmd.client.Caps["BINARY"],
	}
	raw.WriteString(tag)
	raw.WriteByte(' ')
	if cmd.uid {
		raw.WriteString("UID ")
	}
	raw.WriteString(cmd.name)
	err := raw.WriteFields(fields, true)
	buf := raw.Bytes()
	raw.Write(crlf)

	if len(fields) > 0 {
		cmd.seqset, _ = fields[0].(*SeqSet)
	}
	if len(raw.literals) > 0 {
		buf = bytes.Replace(buf, crlf, nil, -1)
	}
	cmd.tag = tag
	cmd.raw = string(buf)
	return raw, err
}

// WriteFields writes command fields to the raw buffer using the appropriate
// format for each field type.
func (raw *rawCommand) WriteFields(fields []Field, SP bool) error {
	for _, f := range fields {
		if SP {
			raw.WriteByte(' ')
		} else {
			SP = true
		}
		switch v := f.(type) {
		case string:
			raw.WriteString(v)
		case int, int8, int16, int32, int64:
			raw.WriteString(strconv.FormatInt(intValue(f), 10))
		case uint, uint8, uint16, uint32, uint64:
			raw.WriteString(strconv.FormatUint(uintValue(f), 10))
		case time.Time:
			raw.WriteString(v.Format(DATETIME))
		case []Field:
			raw.WriteByte('(')
			if err := raw.WriteFields(v, false); err != nil {
				return err
			}
			raw.WriteByte(')')
		case []byte:
			raw.Write(v)
		case Literal:
			info := v.Info()
			if info.Bin {
				if !raw.binary {
					return NotAvailableError("BINARY")
				}
				raw.WriteByte('~')
			}
			raw.WriteByte('{')
			raw.WriteString(strconv.FormatUint(uint64(info.Len), 10))
			if raw.nonsync {
				raw.WriteByte('+')
			}
			raw.WriteString("}\r\n")
			raw.literals = append(raw.literals, v)
		case fmt.Stringer:
			raw.WriteString(v.String())
		case nil:
			raw.WriteString("NIL")
		default:
			return fmt.Errorf("imap: invalid command field %#v", v)
		}
	}
	return nil
}

// ReadLine returns the next line from the raw buffer, panicking if a complete
// line is not found. The CRLF ending is stripped. The line remains valid until
// the next read or write call.
func (raw *rawCommand) ReadLine() []byte {
	b := raw.Bytes()
	n := bytes.IndexByte(b, '\n') + 1
	if n < 2 || b[n-2] != '\r' {
		panic("imap: corrupt command text buffer") // Should never happen...
	}
	return raw.Next(n)[:n-2]
}

// ResponseFilter defines the signature of functions that determine response
// ownership. The function returns true if rsp belongs to cmd. A nil filter
// rejects all responses. A response that is rejected by all active filters is
// considered to be unilateral server data.
type ResponseFilter func(cmd *Command, rsp *Response) bool

// NameFilter accepts the response if rsp.Label matches the command name.
func NameFilter(cmd *Command, rsp *Response) bool {
	return rsp.Label == cmd.name
}

// ByeFilter accepts the response if rsp.Status is BYE.
func ByeFilter(_ *Command, rsp *Response) bool {
	return rsp.Status == BYE
}

// FetchFilter accepts FETCH and STORE command responses by matching message
// sequence numbers or UIDs, depending on the command type. UID matches are more
// exact because there is no risk of mistaking unilateral server data (e.g. an
// unsolicited flags update) for command data.
func FetchFilter(cmd *Command, rsp *Response) bool {
	msg := rsp.MessageInfo()
	if msg == nil {
		return false // Not a FETCH response
	} else if cmd.seqset == nil {
		return true // Accept all FETCH responses if SeqSet wasn't used
	}
	set := *cmd.seqset

	// Check message sequence number or UID against the set
	if cmd.uid {
		if msg.UID == 0 {
			return false // UID data item must be included for UID commands
		} else if set.Contains(msg.UID) {
			return true
		}
	} else if set.Contains(msg.Seq) {
		return true
	}

	// Try matching against "*"
	return set.Dynamic() && msg.Seq == cmd.client.Mailbox.Messages
}

// LabelFilter returns a new filter configured to accept responses with the
// specified labels.
func LabelFilter(labels ...string) ResponseFilter {
	accept := make(map[string]bool, len(labels))
	for _, v := range labels {
		accept[v] = true
	}
	return func(_ *Command, rsp *Response) bool {
		return accept[rsp.Label]
	}
}

// SelectFilter accepts SELECT and EXAMINE command responses.
var SelectFilter = LabelFilter(
	"FLAGS", "EXISTS", "RECENT",
	"UNSEEN", "PERMANENTFLAGS", "UIDNEXT", "UIDVALIDITY",
	"UIDNOTSTICKY",
)

// CommandConfig specifies command execution parameters.
type CommandConfig struct {
	States    ConnState      // Mask of states in which this command may be issued
	Filter    ResponseFilter // Filter for identifying command responses
	Exclusive bool           // Exclusive Client access flag
}

// defaultCommands returns the default command configuration map used to
// initialize Client.CommandConfig.
func defaultCommands() map[string]*CommandConfig {
	const (
		all   = Login | Auth | Selected | Logout
		login = Login
		auth  = Auth | Selected
		sel   = Selected
	)
	return map[string]*CommandConfig{
		// RFC 3501 (6.1. Client Commands - Any State)
		"CAPABILITY": &CommandConfig{States: all, Filter: NameFilter},
		"NOOP":       &CommandConfig{States: all},
		"LOGOUT":     &CommandConfig{States: all, Filter: ByeFilter},

		// RFC 3501 (6.2. Client Commands - Not Authenticated State)
		"STARTTLS":     &CommandConfig{States: login, Exclusive: true},
		"AUTHENTICATE": &CommandConfig{States: login, Exclusive: true},
		"LOGIN":        &CommandConfig{States: login, Exclusive: true},

		// RFC 3501 (6.3. Client Commands - Authenticated State)
		"SELECT":      &CommandConfig{States: auth, Filter: SelectFilter, Exclusive: true},
		"EXAMINE":     &CommandConfig{States: auth, Filter: SelectFilter, Exclusive: true},
		"CREATE":      &CommandConfig{States: auth},
		"DELETE":      &CommandConfig{States: auth},
		"RENAME":      &CommandConfig{States: auth},
		"SUBSCRIBE":   &CommandConfig{States: auth},
		"UNSUBSCRIBE": &CommandConfig{States: auth},
		"LIST":        &CommandConfig{States: auth, Filter: NameFilter},
		"LSUB":        &CommandConfig{States: auth, Filter: NameFilter},
		"STATUS":      &CommandConfig{States: auth, Filter: NameFilter},
		"APPEND":      &CommandConfig{States: auth},

		// RFC 3501 (6.4. Client Commands - Selected State)
		"CHECK":      &CommandConfig{States: sel},
		"CLOSE":      &CommandConfig{States: sel, Exclusive: true},
		"EXPUNGE":    &CommandConfig{States: sel, Filter: NameFilter},
		"SEARCH":     &CommandConfig{States: sel, Filter: NameFilter},
		"FETCH":      &CommandConfig{States: sel, Filter: FetchFilter},
		"STORE":      &CommandConfig{States: sel, Filter: FetchFilter},
		"COPY":       &CommandConfig{States: sel},
		"UID SEARCH": &CommandConfig{States: sel, Filter: NameFilter},
		"UID FETCH":  &CommandConfig{States: sel, Filter: FetchFilter},
		"UID STORE":  &CommandConfig{States: sel, Filter: FetchFilter},
		"UID COPY":   &CommandConfig{States: sel},

		// RFC 2087
		"SETQUOTA":     &CommandConfig{States: auth, Filter: LabelFilter("QUOTA")},
		"GETQUOTA":     &CommandConfig{States: auth, Filter: LabelFilter("QUOTA")},
		"GETQUOTAROOT": &CommandConfig{States: auth, Filter: LabelFilter("QUOTA", "QUOTAROOT")},

		// RFC 2177
		"IDLE": &CommandConfig{States: auth, Exclusive: true},

		// RFC 2971
		"ID": &CommandConfig{States: all, Filter: NameFilter},

		// RFC 3691
		"UNSELECT": &CommandConfig{States: sel, Exclusive: true},

		// RFC 4315
		"UID EXPUNGE": &CommandConfig{States: sel, Filter: NameFilter},

		// RFC 4978
		"COMPRESS": &CommandConfig{States: auth, Exclusive: true},

		// RFC 5161
		"ENABLE": &CommandConfig{States: all, Filter: LabelFilter("ENABLED")},
	}
}
<commit_msg>Use a single instance of the command set for all clients<commit_after>// Copyright 2013 The Go-IMAP Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package imap

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

// ErrAborted is returned by Command.Result when the command execution is
// interrupted prior to receiving a completion response from the server. This is
// usually caused by a break in the connection.
var ErrAborted = errors.New("imap: command aborted")

// abort is a sentinel value assigned to Command.result to indicate the absence
// of a valid command completion response.
var abort = new(Response)

// Command represents a single command sent to the server.
type Command struct {
	// FIFO queue for command data. These are the responses that were accepted
	// by this command's filter. New responses are appended to the end as they
	// are received.
	Data []*Response

	// Client that created this Command instance.
	client *Client

	// Command execution parameters copied from Client.CommandConfig.
	config CommandConfig

	// Command tag assigned by the Client.
	tag string

	// UID flag for FETCH, STORE, COPY, and SEARCH commands.
	uid bool

	// Command name without the UID prefix.
	name string

	// Sequence numbers or UIDs of messages affected by this command. This is
	// used to filter FETCH responses.
	seqset *SeqSet

	// Raw command text without CRLFs or literal strings.
	raw string

	// Command completion response. This is set to abort if the command is not
	// in progress, but a valid completion response was not received.
	result *Response
}

// newCommand initializes and returns a new Command instance. Nil is returned if
// the specified name does not appear in c.CommandConfig.
func newCommand(c *Client, name string) *Command {
	config := c.CommandConfig[name]
	if config == nil {
		return nil
	}
	cmd := &Command{client: c, config: *config, name: name, raw: name}
	if len(name) > 4 && name[:4] == "UID " {
		cmd.uid = true
		cmd.name = name[4:]
	}
	return cmd
}

// Client returns the Client instance that created this command.
func (cmd *Command) Client() *Client {
	return cmd.client
}

// Tag returns the command tag assigned by the Client.
func (cmd *Command) Tag() string {
	return cmd.tag
}

// UID returns true if the command is using UIDs instead of message sequence
// numbers.
func (cmd *Command) UID() bool {
	return cmd.uid
}

// Name returns the command name. If full == true, the UID prefix is included
// for UID commands.
func (cmd *Command) Name(full bool) string {
	if full && cmd.uid {
		return "UID " + cmd.name
	}
	return cmd.name
}

// InProgress returns true until the command completion result is available. No
// new responses will be appended to cmd.Data after this method returns false.
func (cmd *Command) InProgress() bool {
	return cmd.result == nil
}

// Result returns the command completion result. The call blocks until the
// command is no longer in progress. If expect != 0, an error is returned if the
// completion status is other than expected. ErrAborted is returned if the
// command execution was interrupted prior to receiving a completion response.
func (cmd *Command) Result(expect RespStatus) (rsp *Response, err error) {
	for cmd.result == nil {
		if err = cmd.client.Recv(block); err != nil {
			return
		}
	}
	if rsp = cmd.result; rsp == abort {
		rsp, err = nil, ErrAborted
	} else if expect != 0 && rsp.Status&expect == 0 {
		err = ResponseError{rsp, "unexpected completion status"}
	}
	return
}

// String returns the raw command text without CRLFs or literal data.
func (cmd *Command) String() string {
	return cmd.raw
}

// rawCommand contains the raw text and literals about to be sent to the server.
type rawCommand struct {
	*bytes.Buffer // Command text, including all required CRLFs

	literals []Literal // Literal strings
	nonsync  bool      // Support for non-synchronizing literals (RFC 2088)
	binary   bool      // Support for binary literals (RFC 3516)
}

// build returns a rawCommand struct constructed from the command parameters.
func (cmd *Command) build(tag string, fields []Field) (*rawCommand, error) {
	raw := &rawCommand{
		Buffer:  bytes.NewBuffer(make([]byte, 0, 128)),
		nonsync: cmd.client.Caps["LITERAL+"],
		binary:  cmd.client.Caps["BINARY"],
	}
	raw.WriteString(tag)
	raw.WriteByte(' ')
	if cmd.uid {
		raw.WriteString("UID ")
	}
	raw.WriteString(cmd.name)
	err := raw.WriteFields(fields, true)
	buf := raw.Bytes()
	raw.Write(crlf)

	if len(fields) > 0 {
		cmd.seqset, _ = fields[0].(*SeqSet)
	}
	if len(raw.literals) > 0 {
		buf = bytes.Replace(buf, crlf, nil, -1)
	}
	cmd.tag = tag
	cmd.raw = string(buf)
	return raw, err
}

// WriteFields writes command fields to the raw buffer using the appropriate
// format for each field type.
func (raw *rawCommand) WriteFields(fields []Field, SP bool) error {
	for _, f := range fields {
		if SP {
			raw.WriteByte(' ')
		} else {
			SP = true
		}
		switch v := f.(type) {
		case string:
			raw.WriteString(v)
		case int, int8, int16, int32, int64:
			raw.WriteString(strconv.FormatInt(intValue(f), 10))
		case uint, uint8, uint16, uint32, uint64:
			raw.WriteString(strconv.FormatUint(uintValue(f), 10))
		case time.Time:
			raw.WriteString(v.Format(DATETIME))
		case []Field:
			raw.WriteByte('(')
			if err := raw.WriteFields(v, false); err != nil {
				return err
			}
			raw.WriteByte(')')
		case []byte:
			raw.Write(v)
		case Literal:
			info := v.Info()
			if info.Bin {
				if !raw.binary {
					return NotAvailableError("BINARY")
				}
				raw.WriteByte('~')
			}
			raw.WriteByte('{')
			raw.WriteString(strconv.FormatUint(uint64(info.Len), 10))
			if raw.nonsync {
				raw.WriteByte('+')
			}
			raw.WriteString("}\r\n")
			raw.literals = append(raw.literals, v)
		case fmt.Stringer:
			raw.WriteString(v.String())
		case nil:
			raw.WriteString("NIL")
		default:
			return fmt.Errorf("imap: invalid command field %#v", v)
		}
	}
	return nil
}

// ReadLine returns the next line from the raw buffer, panicking if a complete
// line is not found. The CRLF ending is stripped. The line remains valid until
// the next read or write call.
func (raw *rawCommand) ReadLine() []byte {
	b := raw.Bytes()
	n := bytes.IndexByte(b, '\n') + 1
	if n < 2 || b[n-2] != '\r' {
		panic("imap: corrupt command text buffer") // Should never happen...
	}
	return raw.Next(n)[:n-2]
}

// ResponseFilter defines the signature of functions that determine response
// ownership. The function returns true if rsp belongs to cmd. A nil filter
// rejects all responses. A response that is rejected by all active filters is
// considered to be unilateral server data.
type ResponseFilter func(cmd *Command, rsp *Response) bool

// NameFilter accepts the response if rsp.Label matches the command name.
func NameFilter(cmd *Command, rsp *Response) bool {
	return rsp.Label == cmd.name
}

// ByeFilter accepts the response if rsp.Status is BYE.
func ByeFilter(_ *Command, rsp *Response) bool {
	return rsp.Status == BYE
}

// FetchFilter accepts FETCH and STORE command responses by matching message
// sequence numbers or UIDs, depending on the command type. UID matches are more
// exact because there is no risk of mistaking unilateral server data (e.g. an
// unsolicited flags update) for command data.
func FetchFilter(cmd *Command, rsp *Response) bool {
	msg := rsp.MessageInfo()
	if msg == nil {
		return false // Not a FETCH response
	} else if cmd.seqset == nil {
		return true // Accept all FETCH responses if SeqSet wasn't used
	}
	set := *cmd.seqset

	// Check message sequence number or UID against the set
	if cmd.uid {
		if msg.UID == 0 {
			return false // UID data item must be included for UID commands
		} else if set.Contains(msg.UID) {
			return true
		}
	} else if set.Contains(msg.Seq) {
		return true
	}

	// Try matching against "*"
	return set.Dynamic() && msg.Seq == cmd.client.Mailbox.Messages
}

// LabelFilter returns a new filter configured to accept responses with the
// specified labels.
func LabelFilter(labels ...string) ResponseFilter {
	accept := make(map[string]bool, len(labels))
	for _, v := range labels {
		accept[v] = true
	}
	return func(_ *Command, rsp *Response) bool {
		return accept[rsp.Label]
	}
}

// SelectFilter accepts SELECT and EXAMINE command responses.
var SelectFilter = LabelFilter(
	"FLAGS", "EXISTS", "RECENT",
	"UNSEEN", "PERMANENTFLAGS", "UIDNEXT", "UIDVALIDITY",
	"UIDNOTSTICKY",
)

// CommandConfig specifies command execution parameters.
type CommandConfig struct {
	States    ConnState      // Mask of states in which this command may be issued
	Filter    ResponseFilter // Filter for identifying command responses
	Exclusive bool           // Exclusive Client access flag
}

const (
	all   = Login | Auth | Selected | Logout
	login = Login
	auth  = Auth | Selected
	sel   = Selected
)

var defaultCommandSet = map[string]*CommandConfig{
	// RFC 3501 (6.1. Client Commands - Any State)
	"CAPABILITY": &CommandConfig{States: all, Filter: NameFilter},
	"NOOP":       &CommandConfig{States: all},
	"LOGOUT":     &CommandConfig{States: all, Filter: ByeFilter},

	// RFC 3501 (6.2. Client Commands - Not Authenticated State)
	"STARTTLS":     &CommandConfig{States: login, Exclusive: true},
	"AUTHENTICATE": &CommandConfig{States: login, Exclusive: true},
	"LOGIN":        &CommandConfig{States: login, Exclusive: true},

	// RFC 3501 (6.3. Client Commands - Authenticated State)
	"SELECT":      &CommandConfig{States: auth, Filter: SelectFilter, Exclusive: true},
	"EXAMINE":     &CommandConfig{States: auth, Filter: SelectFilter, Exclusive: true},
	"CREATE":      &CommandConfig{States: auth},
	"DELETE":      &CommandConfig{States: auth},
	"RENAME":      &CommandConfig{States: auth},
	"SUBSCRIBE":   &CommandConfig{States: auth},
	"UNSUBSCRIBE": &CommandConfig{States: auth},
	"LIST":        &CommandConfig{States: auth, Filter: NameFilter},
	"LSUB":        &CommandConfig{States: auth, Filter: NameFilter},
	"STATUS":      &CommandConfig{States: auth, Filter: NameFilter},
	"APPEND":      &CommandConfig{States: auth},

	// RFC 3501 (6.4. Client Commands - Selected State)
	"CHECK":      &CommandConfig{States: sel},
	"CLOSE":      &CommandConfig{States: sel, Exclusive: true},
	"EXPUNGE":    &CommandConfig{States: sel, Filter: NameFilter},
	"SEARCH":     &CommandConfig{States: sel, Filter: NameFilter},
	"FETCH":      &CommandConfig{States: sel, Filter: FetchFilter},
	"STORE":      &CommandConfig{States: sel, Filter: FetchFilter},
	"COPY":       &CommandConfig{States: sel},
	"UID SEARCH": &CommandConfig{States: sel, Filter: NameFilter},
	"UID FETCH":  &CommandConfig{States: sel, Filter: FetchFilter},
	"UID STORE":  &CommandConfig{States: sel, Filter: FetchFilter},
	"UID COPY":   &CommandConfig{States: sel},

	// RFC 2087
	"SETQUOTA":     &CommandConfig{States: auth, Filter: LabelFilter("QUOTA")},
	"GETQUOTA":     &CommandConfig{States: auth, Filter: LabelFilter("QUOTA")},
	"GETQUOTAROOT": &CommandConfig{States: auth, Filter: LabelFilter("QUOTA", "QUOTAROOT")},

	// RFC 2177
	"IDLE": &CommandConfig{States: auth, Exclusive: true},

	// RFC 2971
	"ID": &CommandConfig{States: all, Filter: NameFilter},

	// RFC 3691
	"UNSELECT": &CommandConfig{States: sel, Exclusive: true},

	// RFC 4315
	"UID EXPUNGE": &CommandConfig{States: sel, Filter: NameFilter},

	// RFC 4978
	"COMPRESS": &CommandConfig{States: auth, Exclusive: true},

	// RFC 5161
	"ENABLE": &CommandConfig{States: all, Filter: LabelFilter("ENABLED")},
}

// defaultCommands returns the default command configuration map used to
// initialize Client.CommandConfig.
func defaultCommands() map[string]*CommandConfig {
	return defaultCommandSet
}
<|endoftext|>


<commit_before>package importer

import (
	"bufio"
	"fmt"
	"go/ast" // for build.Default
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

var knownOS = map[string]bool{
	"android":   true,
	"darwin":    true,
	"dragonfly": true,
	"freebsd":   true,
	"linux":     true,
	"nacl":      true,
	"netbsd":    true,
	"openbsd":   true,
	"plan9":     true,
	"solaris":   true,
	"windows":   true,
}

var knownArch = map[string]bool{
	"386":      true,
	"amd64":    true,
	"amd64p32": true,
	"arm":      true,
	"arm64":    true,
	"ppc64":    true,
	"ppc64le":  true,
	"mips":     true,
	"mipsle":   true,
	"mips64":   true,
	"mips64le": true,
}

// goodOSArchFile returns false if the name contains a $GOOS or $GOARCH
// suffix which does not match the current system.
// The recognized name formats are:
//
//     name_$(GOOS).*
//     name_$(GOARCH).*
//     name_$(GOOS)_$(GOARCH).*
//     name_$(GOOS)_test.*
//     name_$(GOARCH)_test.*
//     name_$(GOOS)_$(GOARCH)_test.*
//
// An exception: if GOOS=android, then files with GOOS=linux are also matched.
func goodOSArchFile(goos, goarch, name string, allTags map[string]bool) bool {
	// Before Go 1.4, a file called "linux.go" would be equivalent to having a
	// build tag "linux" in that file. For Go 1.4 and beyond, we require this
	// auto-tagging to apply only to files with a non-empty prefix, so
	// "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
	// systems, such as android, to arrive without breaking existing code with
	// innocuous source code in "android.go". The easiest fix: cut everything
	// in the name before the initial _.
	i := strings.Index(name, "_")
	if i < 0 {
		return true
	}
	name = name[i:] // ignore everything before first _

	// strip extension
	if dot := strings.Index(name, "."); dot != -1 {
		name = name[:dot]
	}

	l := strings.Split(name, "_")
	if n := len(l); n > 0 && l[n-1] == "test" {
		l = l[:n-1]
	}
	n := len(l)
	switch {
	case n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]]:
		if allTags != nil {
			allTags[l[n-2]] = true
			allTags[l[n-1]] = true
		}
		if l[n-1] != goarch {
			return false
		}
		if goos == "android" && l[n-2] == "linux" {
			return true
		}
		return l[n-2] == goos
	case n >= 1 && knownOS[l[n-1]]:
		if allTags != nil {
			allTags[l[n-1]] = true
		}
		if goos == "android" && l[n-1] == "linux" {
			return true
		}
		return l[n-1] == goos
	case n >= 1 && knownArch[l[n-1]]:
		if allTags != nil {
			allTags[l[n-1]] = true
		}
		return l[n-1] == goarch
	default:
		return true
	}
}

func loadPackage(p *Package) error {
	var Sfiles []string // files with ".S" (capital S)
	var firstFile string
	imported := make(map[string][]token.Position)
	testImported := make(map[string][]token.Position)
	xTestImported := make(map[string][]token.Position)
	allTags := make(map[string]bool)
	fset := token.NewFileSet()
	err := filepath.Walk(p.Dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() && path != p.Dir {
			return filepath.SkipDir
		}

		name := filepath.Base(path)

		filename := filepath.Join(p.Dir, name)
		match, data, err := p.matchFile(path, allTags)
		if err != nil {
			return err
		}
		ext := filepath.Ext(name)
		if !match {
			if ext == ".go" {
				p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
			}
			return nil
		}

		// Going to save the file.  For non-Go files, can stop here.
		switch ext {
		case ".c":
			p.CFiles = append(p.CFiles, name)
			return nil
		case ".cc", ".cpp", ".cxx":
			p.CXXFiles = append(p.CXXFiles, name)
			return nil
		case ".m":
			p.MFiles = append(p.MFiles, name)
			return nil
		case ".h", ".hh", ".hpp", ".hxx":
			p.HFiles = append(p.HFiles, name)
			return nil
		case ".s":
			p.SFiles = append(p.SFiles, name)
			return nil
		case ".S":
			Sfiles = append(Sfiles, name)
			return nil
		case ".swig":
			p.SwigFiles = append(p.SwigFiles, name)
			return nil
		case ".swigcxx":
			p.SwigCXXFiles = append(p.SwigCXXFiles, name)
			return nil
		case ".syso":
			// binary objects to add to package archive
			// Likely of the form foo_windows.syso, but
			// the name was vetted above with goodOSArchFile.
			p.SysoFiles = append(p.SysoFiles, name)
			return nil
		}

		pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments)
		if err != nil {
			return err
		}

		pkg := pf.Name.Name
		if pkg == "documentation" {
			p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
			return nil
		}

		isTest := strings.HasSuffix(name, "_test.go")
		isXTest := false
		if isTest && strings.HasSuffix(pkg, "_test") {
			isXTest = true
			pkg = pkg[:len(pkg)-len("_test")]
		}

		if p.Name == "" {
			p.Name = pkg
			firstFile = name
		} else if pkg != p.Name {
			return &MultiplePackageError{
				Dir:      p.Dir,
				Packages: []string{p.Name, pkg},
				Files:    []string{firstFile, name},
			}
		}
		// Record imports and information about cgo.
		isCgo := false
		for _, decl := range pf.Decls {
			d, ok := decl.(*ast.GenDecl)
			if !ok {
				continue
			}
			for _, dspec := range d.Specs {
				spec, ok := dspec.(*ast.ImportSpec)
				if !ok {
					continue
				}
				quoted := spec.Path.Value
				path, err := strconv.Unquote(quoted)
				if err != nil {
					return fmt.Errorf("%s: parser returned invalid quoted string: <%s>", filename, quoted)
				}
				if isXTest {
					xTestImported[path] = append(xTestImported[path], fset.Position(spec.Pos()))
				} else if isTest {
					testImported[path] = append(testImported[path], fset.Position(spec.Pos()))
				} else {
					imported[path] = append(imported[path], fset.Position(spec.Pos()))
				}
				if path == "C" {
					if isTest {
						return fmt.Errorf("use of cgo in test %s not supported", filename)
					}
					cg := spec.Doc
					if cg == nil && len(d.Specs) == 1 {
						cg = d.Doc
					}
					if cg != nil {
						if err := saveCgo(p, filename, cg); err != nil {
							return err
						}
					}
					isCgo = true
				}
			}
		}
		switch {
		case isCgo:
			allTags["cgo"] = true
			if p.importer.(*Importer).CgoEnabled {
				p.CgoFiles = append(p.CgoFiles, name)
			} else {
				p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
			}
		case isXTest:
			p.XTestGoFiles = append(p.XTestGoFiles, name)
		case isTest:
			p.TestGoFiles = append(p.TestGoFiles, name)
		default:
			p.GoFiles = append(p.GoFiles, name)
		}
		return nil
	})
	if err != nil {
		return err
	}
	if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
		return &NoGoError{p.Dir}
	}

	for tag := range allTags {
		p.AllTags = append(p.AllTags, tag)
	}
	sort.Strings(p.AllTags)

	p.Imports, p.ImportPos = cleanImports(imported)
	p.TestImports, p.TestImportPos = cleanImports(testImported)
	p.XTestImports, p.XTestImportPos = cleanImports(xTestImported)

	// add the .S files only if we are using cgo
	// (which means gcc will compile them).
	// The standard assemblers expect .s files.
	if len(p.CgoFiles) > 0 {
		p.SFiles = append(p.SFiles, Sfiles...)
		sort.Strings(p.SFiles)
	}

	return nil
}

// saveCgo saves the information from the #cgo lines in the import "C" comment.
// These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives
// that affect the way cgo's C code is built.
func saveCgo(di *Package, filename string, cg *ast.CommentGroup) error {
	r := strings.NewReader(cg.Text())
	sc := bufio.NewScanner(r)
	for sc.Scan() {
		line := sc.Text()

		// Line is
		//	#cgo [GOOS/GOARCH...] LDFLAGS: stuff
		//
		line = strings.TrimSpace(line)
		if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') {
			continue
		}

		// Split at colon.
		line = strings.TrimSpace(line[4:])
		i := strings.Index(line, ":")
		if i < 0 {
			return fmt.Errorf("%s: invalid #cgo line: %s", filename, sc.Text())
		}
		line, argstr := line[:i], line[i+1:]

		// Parse GOOS/GOARCH stuff.
		f := strings.Fields(line)
		if len(f) < 1 {
			return fmt.Errorf("%s: invalid #cgo line: %s", filename, sc.Text())
		}

		cond, verb := f[:len(f)-1], f[len(f)-1]
		if len(cond) > 0 {
			ok := false
			for _, c := range cond {
				if di.match(c, nil) {
					ok = true
					break
				}
			}
			if !ok {
				continue
			}
		}

		args, err := splitQuoted(argstr)
		if err != nil {
			return fmt.Errorf("%s: invalid #cgo line: %s", filename, sc.Text())
		}
		for i, arg := range args {
			arg, ok := expandSrcDir(arg, di.Dir)
			if !ok {
				return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
			}
			args[i] = arg
		}

		switch verb {
		case "CFLAGS":
			di.CgoCFLAGS = append(di.CgoCFLAGS, args...)
		case "CPPFLAGS":
			di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...)
		case "CXXFLAGS":
			di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...)
		case "LDFLAGS":
			di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
		case "pkg-config":
			di.CgoPkgConfig = append(di.CgoPkgConfig, args...)
		default:
			return fmt.Errorf("%s: invalid #cgo verb: %s", filename, sc.Text())
		}
	}
	return sc.Err()
}

func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) {
	if len(m) == 0 {
		return nil, nil
	}
	all := make([]string, 0, len(m))
	for path := range m {
		all = append(all, path)
	}
	sort.Strings(all)
	return all, m
}
<commit_msg>importer: simplify loadPackage<commit_after>package importer

import (
	"bufio"
	"fmt"
	"go/ast" // for build.Default
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

var knownOS = map[string]bool{
	"android":   true,
	"darwin":    true,
	"dragonfly": true,
	"freebsd":   true,
	"linux":     true,
	"nacl":      true,
	"netbsd":    true,
	"openbsd":   true,
	"plan9":     true,
	"solaris":   true,
	"windows":   true,
}

var knownArch = map[string]bool{
	"386":      true,
	"amd64":    true,
	"amd64p32": true,
	"arm":      true,
	"arm64":    true,
	"ppc64":    true,
	"ppc64le":  true,
	"mips":     true,
	"mipsle":   true,
	"mips64":   true,
	"mips64le": true,
}

// goodOSArchFile returns false if the name contains a $GOOS or $GOARCH
// suffix which does not match the current system.
// The recognized name formats are:
//
//     name_$(GOOS).*
//     name_$(GOARCH).*
//     name_$(GOOS)_$(GOARCH).*
//     name_$(GOOS)_test.*
//     name_$(GOARCH)_test.*
//     name_$(GOOS)_$(GOARCH)_test.*
//
// An exception: if GOOS=android, then files with GOOS=linux are also matched.
func goodOSArchFile(goos, goarch, name string, allTags map[string]bool) bool {
	// Before Go 1.4, a file called "linux.go" would be equivalent to having a
	// build tag "linux" in that file. For Go 1.4 and beyond, we require this
	// auto-tagging to apply only to files with a non-empty prefix, so
	// "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
	// systems, such as android, to arrive without breaking existing code with
	// innocuous source code in "android.go". The easiest fix: cut everything
	// in the name before the initial _.
	i := strings.Index(name, "_")
	if i < 0 {
		return true
	}
	name = name[i:] // ignore everything before first _

	// strip extension
	if dot := strings.Index(name, "."); dot != -1 {
		name = name[:dot]
	}

	l := strings.Split(name, "_")
	if n := len(l); n > 0 && l[n-1] == "test" {
		l = l[:n-1]
	}
	n := len(l)
	switch {
	case n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]]:
		if allTags != nil {
			allTags[l[n-2]] = true
			allTags[l[n-1]] = true
		}
		if l[n-1] != goarch {
			return false
		}
		if goos == "android" && l[n-2] == "linux" {
			return true
		}
		return l[n-2] == goos
	case n >= 1 && knownOS[l[n-1]]:
		if allTags != nil {
			allTags[l[n-1]] = true
		}
		if goos == "android" && l[n-1] == "linux" {
			return true
		}
		return l[n-1] == goos
	case n >= 1 && knownArch[l[n-1]]:
		if allTags != nil {
			allTags[l[n-1]] = true
		}
		return l[n-1] == goarch
	default:
		return true
	}
}

type byName []os.FileInfo

func (x byName) Len() int           { return len(x) }
func (x byName) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
func (x byName) Less(i, j int) bool { return x[i].Name() < x[j].Name() }

func loadPackage(p *Package) error {
	dir, err := os.Open(p.Dir)
	if err != nil {
		return fmt.Errorf("loadPackage: unable open directory: %v", err)
	}
	defer dir.Close()

	dents, err := dir.Readdir(-1)
	if err != nil {
		return fmt.Errorf("loadPackage: unable read directory: %v", err)
	}

	var Sfiles []string // files with ".S" (capital S)
	var firstFile string
	imported := make(map[string][]token.Position)
	testImported := make(map[string][]token.Position)
	xTestImported := make(map[string][]token.Position)
	allTags := make(map[string]bool)
	fset := token.NewFileSet()

	// cmd/gb expects file names to be sorted ... this seems artificial
	sort.Sort(byName(dents))
	for _, fi := range dents {
		if fi.IsDir() {
			continue
		}

		name := fi.Name()
		path := filepath.Join(p.Dir, name)
		match, data, err := p.matchFile(path, allTags)
		if err != nil {
			return err
		}
		ext := filepath.Ext(name)
		if !match {
			if ext == ".go" {
				p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
			}
			continue
		}

		switch ext {
		case ".c":
			p.CFiles = append(p.CFiles, name)
		case ".cc", ".cpp", ".cxx":
			p.CXXFiles = append(p.CXXFiles, name)
		case ".m":
			p.MFiles = append(p.MFiles, name)
		case ".h", ".hh", ".hpp", ".hxx":
			p.HFiles = append(p.HFiles, name)
		case ".s":
			p.SFiles = append(p.SFiles, name)
		case ".S":
			Sfiles = append(Sfiles, name)
		case ".swig":
			p.SwigFiles = append(p.SwigFiles, name)
		case ".swigcxx":
			p.SwigCXXFiles = append(p.SwigCXXFiles, name)
		case ".syso":
			// binary objects to add to package archive
			// Likely of the form foo_windows.syso, but
			// the name was vetted above with goodOSArchFile.
			p.SysoFiles = append(p.SysoFiles, name)
		default:
			pf, err := parser.ParseFile(fset, path, data, parser.ImportsOnly|parser.ParseComments)
			if err != nil {
				return err
			}

			pkg := pf.Name.Name
			if pkg == "documentation" {
				p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
				continue
			}

			isTest := strings.HasSuffix(name, "_test.go")
			isXTest := false
			if isTest && strings.HasSuffix(pkg, "_test") {
				isXTest = true
				pkg = pkg[:len(pkg)-len("_test")]
			}

			if p.Name == "" {
				p.Name = pkg
				firstFile = name
			} else if pkg != p.Name {
				return &MultiplePackageError{
					Dir:      p.Dir,
					Packages: []string{p.Name, pkg},
					Files:    []string{firstFile, name},
				}
			}
			// Record imports and information about cgo.
			isCgo := false
			for _, decl := range pf.Decls {
				d, ok := decl.(*ast.GenDecl)
				if !ok {
					continue
				}
				for _, dspec := range d.Specs {
					spec, ok := dspec.(*ast.ImportSpec)
					if !ok {
						continue
					}
					quoted := spec.Path.Value
					path, err := strconv.Unquote(quoted)
					if err != nil {
						return fmt.Errorf("%s: parser returned invalid quoted string: <%s>", path, quoted)
					}
					if isXTest {
						xTestImported[path] = append(xTestImported[path], fset.Position(spec.Pos()))
					} else if isTest {
						testImported[path] = append(testImported[path], fset.Position(spec.Pos()))
					} else {
						imported[path] = append(imported[path], fset.Position(spec.Pos()))
					}
					if path == "C" {
						if isTest {
							return fmt.Errorf("use of cgo in test %s not supported", path)
						}
						cg := spec.Doc
						if cg == nil && len(d.Specs) == 1 {
							cg = d.Doc
						}
						if cg != nil {
							if err := saveCgo(p, path, cg); err != nil {
								return err
							}
						}
						isCgo = true
					}
				}
			}
			switch {
			case isCgo:
				allTags["cgo"] = true
				if p.importer.(*Importer).CgoEnabled {
					p.CgoFiles = append(p.CgoFiles, name)
				} else {
					p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
				}
			case isXTest:
				p.XTestGoFiles = append(p.XTestGoFiles, name)
			case isTest:
				p.TestGoFiles = append(p.TestGoFiles, name)
			default:
				p.GoFiles = append(p.GoFiles, name)
			}
		}
	}
	if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
		return &NoGoError{p.Dir}
	}

	for tag := range allTags {
		p.AllTags = append(p.AllTags, tag)
	}
	sort.Strings(p.AllTags)

	p.Imports, p.ImportPos = cleanImports(imported)
	p.TestImports, p.TestImportPos = cleanImports(testImported)
	p.XTestImports, p.XTestImportPos = cleanImports(xTestImported)

	// add the .S files only if we are using cgo
	// (which means gcc will compile them).
	// The standard assemblers expect .s files.
	if len(p.CgoFiles) > 0 {
		p.SFiles = append(p.SFiles, Sfiles...)
		sort.Strings(p.SFiles)
	}
	return nil
}

// saveCgo saves the information from the #cgo lines in the import "C" comment.
// These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives
// that affect the way cgo's C code is built.
func saveCgo(di *Package, filename string, cg *ast.CommentGroup) error {
	r := strings.NewReader(cg.Text())
	sc := bufio.NewScanner(r)
	for sc.Scan() {
		line := sc.Text()

		// Line is
		//	#cgo [GOOS/GOARCH...] LDFLAGS: stuff
		//
		line = strings.TrimSpace(line)
		if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') {
			continue
		}

		// Split at colon.
		line = strings.TrimSpace(line[4:])
		i := strings.Index(line, ":")
		if i < 0 {
			return fmt.Errorf("%s: invalid #cgo line: %s", filename, sc.Text())
		}
		line, argstr := line[:i], line[i+1:]

		// Parse GOOS/GOARCH stuff.
		f := strings.Fields(line)
		if len(f) < 1 {
			return fmt.Errorf("%s: invalid #cgo line: %s", filename, sc.Text())
		}

		cond, verb := f[:len(f)-1], f[len(f)-1]
		if len(cond) > 0 {
			ok := false
			for _, c := range cond {
				if di.match(c, nil) {
					ok = true
					break
				}
			}
			if !ok {
				continue
			}
		}

		args, err := splitQuoted(argstr)
		if err != nil {
			return fmt.Errorf("%s: invalid #cgo line: %s", filename, sc.Text())
		}
		for i, arg := range args {
			arg, ok := expandSrcDir(arg, di.Dir)
			if !ok {
				return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
			}
			args[i] = arg
		}

		switch verb {
		case "CFLAGS":
			di.CgoCFLAGS = append(di.CgoCFLAGS, args...)
		case "CPPFLAGS":
			di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...)
		case "CXXFLAGS":
			di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...)
		case "LDFLAGS":
			di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
		case "pkg-config":
			di.CgoPkgConfig = append(di.CgoPkgConfig, args...)
		default:
			return fmt.Errorf("%s: invalid #cgo verb: %s", filename, sc.Text())
		}
	}
	return sc.Err()
}

func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) {
	if len(m) == 0 {
		return nil, nil
	}
	all := make([]string, 0, len(m))
	for path := range m {
		all = append(all, path)
	}
	sort.Strings(all)
	return all, m
}
<|endoftext|>


<commit_before>package srv

import (
	"log"
	"net"
)

const Version = "0.7.3"

var sing struct {
	Dao			*dao
	Rao			*rao
	UssnMgr		*ussnMgr
	BookMgr		*bookMgr
	TssnMgr		*tssnMgr
}

func Serve(port string) {
	sing.Dao = newDao()
	sing.Rao = newRao()
	sing.UssnMgr = newUssnMgr()
	sing.BookMgr = newBookMgr()
	sing.TssnMgr = newTssnMgr()
	defer sing.Dao.Close()
	go sing.UssnMgr.Loop()
	go sing.BookMgr.Loop()
	go sing.TssnMgr.Loop()

	ln, err := net.Listen("tcp", ":" + port)
	if err != nil {
		log.Fatalln(err)
	} else {
		log.Println("mjpancake-server", Version, "listen", port)
	}

	for {
		conn, err := ln.Accept()

		if err != nil {
			log.Println("E accept", err)
		} else {
			go loopUssn(conn)
		}
	}
}

<commit_msg>bump to 0.7.4<commit_after>package srv

import (
	"log"
	"net"
)

const Version = "0.7.4"

var sing struct {
	Dao			*dao
	Rao			*rao
	UssnMgr		*ussnMgr
	BookMgr		*bookMgr
	TssnMgr		*tssnMgr
}

func Serve(port string) {
	sing.Dao = newDao()
	sing.Rao = newRao()
	sing.UssnMgr = newUssnMgr()
	sing.BookMgr = newBookMgr()
	sing.TssnMgr = newTssnMgr()
	defer sing.Dao.Close()
	go sing.UssnMgr.Loop()
	go sing.BookMgr.Loop()
	go sing.TssnMgr.Loop()

	ln, err := net.Listen("tcp", ":" + port)
	if err != nil {
		log.Fatalln(err)
	} else {
		log.Println("mjpancake-server", Version, "listen", port)
	}

	for {
		conn, err := ln.Accept()

		if err != nil {
			log.Println("E accept", err)
		} else {
			go loopUssn(conn)
		}
	}
}

<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package client

import (
	"fmt"
	"io"
	"strconv"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
)

type CmdConfigGet struct {
	key    string
	writer io.Writer
}

type CmdConfigSet struct {
	key    string
	value  string
	writer io.Writer
}

type CmdConfigReset struct {
	writer io.Writer
}

func (v *CmdConfigGet) ParseArgv(ctx *cli.Context) error {
	if len(ctx.Args()) < 1 {
		return fmt.Errorf("Not enough arguments.")
	}
	v.key = ctx.Args()[0]
	if v.writer == nil {
		v.writer = GlobUI.OutputWriter()
	}
	return nil
}

func (v *CmdConfigSet) ParseArgv(ctx *cli.Context) error {
	if len(ctx.Args()) < 1 {
		return fmt.Errorf("Not enough arguments.")
	}
	v.key = ctx.Args()[0]
	if len(ctx.Args()) > 1 {
		v.value = ctx.Args()[1]
	}
	if v.writer == nil {
		v.writer = GlobUI.OutputWriter()
	}
	return nil
}

func (v *CmdConfigReset) ParseArgv(ctx *cli.Context) error {
	if v.writer == nil {
		v.writer = GlobUI.OutputWriter()
	}
	return nil
}

func (v *CmdConfigGet) Run() error {
	cr := G.Env.GetConfig()
	// TODO: print dictionaries?
	if s, isSet := cr.GetStringAtPath(v.key); isSet {
		fmt.Fprintf(v.writer, "%s: %s\n", v.key, s)
	} else if b, isSet := cr.GetBoolAtPath(v.key); isSet {
		fmt.Fprintf(v.writer, "%s: %t\n", v.key, b)
	} else if i, isSet := cr.GetIntAtPath(v.key); isSet {
		fmt.Fprintf(v.writer, "%s: %d\n", v.key, i)
	} else if isSet := cr.GetNullAtPath(v.key); isSet {
		fmt.Fprintf(v.writer, "%s: null\n", v.key)
	}
	return nil
}

func (v *CmdConfigSet) Run() error {
	if v.value != "" {
		cw := G.Env.GetConfigWriter()
		// try to convert the value to an int, and then to a bool
		// if those don't work, use a string
		if val, e := strconv.Atoi(v.value); e == nil {
			cw.SetIntAtPath(v.key, val)
		} else if val, e := strconv.ParseBool(v.value); e == nil {
			// NOTE: this will also convert strings like 't' and 'F' to
			// a bool, which could potentially cause strange errors for
			// e.g. a user named "f"
			cw.SetBoolAtPath(v.key, val)
		} else if v.value == "null" {
			cw.SetNullAtPath(v.key)
		} else {
			cw.SetStringAtPath(v.key, v.value)
		}
		cw.Write()
	} else {
		cw := G.Env.GetConfigWriter()
		cw.DeleteAtPath(v.key)
		cw.Write()
	}
	return nil
}

func (v *CmdConfigReset) Run() error {
	// Clear out file
	cw := G.Env.GetConfigWriter()
	cw.Reset()
	cw.Write()
	return nil
}

func NewCmdConfig(cl *libcmdline.CommandLine) cli.Command {
	return cli.Command{
		Name:         "config",
		Usage:        "Get and set configuration options",
		ArgumentHelp: "[arguments...]",
		Subcommands: []cli.Command{
			NewCmdConfigGet(cl),
			NewCmdConfigSet(cl),
			NewCmdConfigReset(cl),
		},
	}
}

func NewCmdConfigGet(cl *libcmdline.CommandLine) cli.Command {
	return cli.Command{
		Name:         "get",
		Usage:        "Get a config value",
		ArgumentHelp: "<key>",
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdConfigGet{}, "get", c)
		},
	}
}

func NewCmdConfigSet(cl *libcmdline.CommandLine) cli.Command {
	return cli.Command{
		Name:         "set",
		Usage:        "Set a config value",
		ArgumentHelp: "<key> <value>",
		Description:  "Set a config value. Specify an empty value to clear it.",
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdConfigSet{}, "set", c)
		},
	}
}

func NewCmdConfigReset(cl *libcmdline.CommandLine) cli.Command {
	return cli.Command{
		Name:  "reset",
		Usage: "Reset the config",
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdConfigReset{}, "reset", c)
		},
	}
}

func (v *CmdConfigGet) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config: true,
	}
}

func (v *CmdConfigSet) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config: true,
	}
}

func (v *CmdConfigReset) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config: true,
	}
}
<commit_msg>Display only the config file path with info<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package client

import (
	"fmt"
	"io"
	"strconv"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
)

type CmdConfigGet struct {
	key    string
	writer io.Writer
}

type CmdConfigSet struct {
	key    string
	value  string
	writer io.Writer
}

type CmdConfigReset struct {
	writer io.Writer
}

type CmdConfigInfo struct {
	writer io.Writer
}

func (v *CmdConfigGet) ParseArgv(ctx *cli.Context) error {
	if len(ctx.Args()) < 1 {
		return fmt.Errorf("Not enough arguments.")
	}
	v.key = ctx.Args()[0]
	if v.writer == nil {
		v.writer = GlobUI.OutputWriter()
	}
	return nil
}

func (v *CmdConfigSet) ParseArgv(ctx *cli.Context) error {
	if len(ctx.Args()) < 1 {
		return fmt.Errorf("Not enough arguments.")
	}
	v.key = ctx.Args()[0]
	if len(ctx.Args()) > 1 {
		v.value = ctx.Args()[1]
	}
	if v.writer == nil {
		v.writer = GlobUI.OutputWriter()
	}
	return nil
}

func (v *CmdConfigReset) ParseArgv(ctx *cli.Context) error {
	if v.writer == nil {
		v.writer = GlobUI.OutputWriter()
	}
	return nil
}

func (v *CmdConfigInfo) ParseArgv(ctx *cli.Context) error {
	if v.writer == nil {
		v.writer = GlobUI.OutputWriter()
	}
	return nil
}

func (v *CmdConfigGet) Run() error {
	cr := G.Env.GetConfig()
	// TODO: print dictionaries?
	if s, isSet := cr.GetStringAtPath(v.key); isSet {
		fmt.Fprintf(v.writer, "%s: %s\n", v.key, s)
	} else if b, isSet := cr.GetBoolAtPath(v.key); isSet {
		fmt.Fprintf(v.writer, "%s: %t\n", v.key, b)
	} else if i, isSet := cr.GetIntAtPath(v.key); isSet {
		fmt.Fprintf(v.writer, "%s: %d\n", v.key, i)
	} else if isSet := cr.GetNullAtPath(v.key); isSet {
		fmt.Fprintf(v.writer, "%s: null\n", v.key)
	}
	return nil
}

func (v *CmdConfigSet) Run() error {
	if v.value != "" {
		cw := G.Env.GetConfigWriter()
		// try to convert the value to an int, and then to a bool
		// if those don't work, use a string
		if val, e := strconv.Atoi(v.value); e == nil {
			cw.SetIntAtPath(v.key, val)
		} else if val, e := strconv.ParseBool(v.value); e == nil {
			// NOTE: this will also convert strings like 't' and 'F' to
			// a bool, which could potentially cause strange errors for
			// e.g. a user named "f"
			cw.SetBoolAtPath(v.key, val)
		} else if v.value == "null" {
			cw.SetNullAtPath(v.key)
		} else {
			cw.SetStringAtPath(v.key, v.value)
		}
		cw.Write()
	} else {
		cw := G.Env.GetConfigWriter()
		cw.DeleteAtPath(v.key)
		cw.Write()
	}
	return nil
}

func (v *CmdConfigReset) Run() error {
	// Clear out file
	cw := G.Env.GetConfigWriter()
	cw.Reset()
	cw.Write()
	return nil
}

func (v *CmdConfigInfo) Run() error {
	configFile := G.Env.GetConfigFilename()
	fmt.Fprintf(v.writer, "File: %s\n\n", configFile)
	return nil
}

func NewCmdConfig(cl *libcmdline.CommandLine) cli.Command {
	return cli.Command{
		Name:         "config",
		Usage:        "Get and set configuration options",
		ArgumentHelp: "[arguments...]",
		Subcommands: []cli.Command{
			NewCmdConfigGet(cl),
			NewCmdConfigSet(cl),
			NewCmdConfigReset(cl),
			NewCmdConfigInfo(cl),
		},
	}
}

func NewCmdConfigGet(cl *libcmdline.CommandLine) cli.Command {
	return cli.Command{
		Name:         "get",
		Usage:        "Get a config value",
		ArgumentHelp: "<key>",
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdConfigGet{}, "get", c)
		},
	}
}

func NewCmdConfigSet(cl *libcmdline.CommandLine) cli.Command {
	return cli.Command{
		Name:         "set",
		Usage:        "Set a config value",
		ArgumentHelp: "<key> <value>",
		Description:  "Set a config value. Specify an empty value to clear it.",
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdConfigSet{}, "set", c)
		},
	}
}

func NewCmdConfigReset(cl *libcmdline.CommandLine) cli.Command {
	return cli.Command{
		Name:  "reset",
		Usage: "Reset the config",
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdConfigReset{}, "reset", c)
		},
	}
}

func NewCmdConfigInfo(cl *libcmdline.CommandLine) cli.Command {
	return cli.Command{
		Name:  "info",
		Usage: "Show config file path",
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdConfigInfo{}, "info", c)
		},
	}
}

func (v *CmdConfigGet) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config: true,
	}
}

func (v *CmdConfigSet) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config: true,
	}
}

func (v *CmdConfigReset) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config: true,
	}
}

func (v *CmdConfigInfo) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config: true,
	}
}
<|endoftext|>


<commit_before>package database

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/golang/glog"
	_ "github.com/mattn/go-sqlite3"
	"skia.googlesource.com/buildbot.git/go/util"
)

// Config information to create a database connection.
type DatabaseConfig struct {
	MySQLString    string
	SQLiteFilePath string
	MigrationSteps []MigrationStep
}

// Single step to migrated from one database version to the next and back.
type MigrationStep struct {
	MySQLUp    []string
	MySQLDown  []string
	SQLiteUp   []string
	SQLiteDown []string
}

// Database handle to send queries to the underlying database.
type VersionedDB struct {
	// Database intance that is either backed by SQLite or MySQl.
	DB *sql.DB

	// Keeps track if we are connected to MySQL or SQLite
	IsMySQL bool

	// List of migration steps for this database.
	migrationSteps []MigrationStep
}

// Init must be called once before DB is used.
//
// Since it used glog, make sure it is also called after flag.Parse is called.
func NewVersionedDB(conf *DatabaseConfig) *VersionedDB {
	// If there is a connection string then connect to the MySQL server.
	// This is for testing only. In production we get the relevant information
	// from the metadata server.
	var err error
	var isMySQL = true
	var DB *sql.DB = nil

	if conf.MySQLString != "" {
		glog.Infoln("Opening SQL database.")
		if DB, err = sql.Open("mysql", conf.MySQLString); err == nil {
			glog.Infoln("Sending Ping.")
			err = DB.Ping()
		}

		if err != nil {
			glog.Fatalln("Failed to open connection to SQL server:", err)
		}
	} else {
		// Open a local SQLite database instead.
		glog.Infof("Opening local sqlite database at: %s", conf.SQLiteFilePath)
		// Fallback to sqlite for local use.
		DB, err = sql.Open("sqlite3", conf.SQLiteFilePath)
		if err != nil {
			glog.Fatalln("Failed to open:", err)
		}
		isMySQL = false
	}

	result := &VersionedDB{
		DB:             DB,
		IsMySQL:        isMySQL,
		migrationSteps: conf.MigrationSteps,
	}

	// Make sure the migration table exists.
	if err := result.checkVersionTable(); err != nil {
		// We are using panic() instead of Fataln() to be able to trap this
		// in tests and make sure it fails when no version table exists.
		glog.Errorln("Unable to create version table.")
		panic("Attempt to create version table returned: " + err)
	}
	glog.Infoln("Version table OK.")

	// Migrate to the latest version if we are using SQLite, so we don't have
	// to run the *_migratdb command for a local database.
	if !result.IsMySQL {
		result.Migrate(result.MaxDBVersion())
	}

	// Ping the database occasionally to keep the connection fresh.
	go func() {
		c := time.Tick(1 * time.Minute)
		for _ = range c {
			if err := result.DB.Ping(); err != nil {
				glog.Warningln("Database failed to respond:", err)
			}
			glog.Infof("db: Successful ping")
		}
	}()

	return result
}

// Close the underlying database.
func (vdb *VersionedDB) Close() error {
	return vdb.DB.Close()
}

// Migrates the database to the specified target version. Use DBVersion() to
// retrieve the current version of the database.
func (vdb *VersionedDB) Migrate(targetVersion int) error {
	if (targetVersion < 0) || (targetVersion > vdb.MaxDBVersion()) {
		glog.Fatalf("Target db version must be in range: [0 .. %d]", vdb.MaxDBVersion())
	}

	currentVersion, err := vdb.DBVersion()
	if err != nil {
		return err
	}

	if targetVersion == currentVersion {
		return nil
	}

	// start a transaction
	txn, err := vdb.DB.Begin()
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			glog.Errorf("Rolling back commit. Error: %s", err)
			txn.Rollback()
		} else {
			glog.Infoln("Committing changes.")
			txn.Commit()
		}
	}()

	// run through the transactions
	runSteps := vdb.getMigrations(currentVersion, targetVersion)
	if len(runSteps) == 0 {
		glog.Fatalln("Unable to find migration steps.")
	}

	for _, step := range runSteps {
		for _, stmt := range step {
			glog.Infoln("EXECUTING: \n", stmt)
			if _, err = txn.Exec(stmt); err != nil {
				return err
			}
		}
	}

	// update the dbversion table
	if err = vdb.setDBVersion(txn, targetVersion); err != nil {
		return err
	}

	return nil
}

// Returns the current version of the database. It assumes that the
// Migrate function has already been called and the version table has been
// created in the database.
func (vdb *VersionedDB) DBVersion() (int, error) {
	stmt := `
		SELECT version
		FROM sk_db_version
		WHERE id=1`

	var version int
	err := vdb.DB.QueryRow(stmt).Scan(&version)
	return version, err
}

// Returns the highest version currently available.
func (vdb *VersionedDB) MaxDBVersion() int {
	return len(vdb.migrationSteps)
}

// Returns an error if the version table does not exist.
func (vdb *VersionedDB) checkVersionTable() error {
	// Check if the table exists in MySQL or SQLite.
	stmt := "SHOW TABLES LIKE 'sk_db_version'"
	if !vdb.IsMySQL {
		stmt = "SELECT name FROM sqlite_master WHERE type='table' AND name='sk_db_version';"
	}

	var temp string
	err := vdb.DB.QueryRow(stmt).Scan(&temp)
	if err != nil {
		// See if we can create the version table.
		return vdb.ensureVersionTable()
	}

	return nil
}

func (vdb *VersionedDB) setDBVersion(txn *sql.Tx, newDBVersion int) error {
	stmt := `REPLACE INTO sk_db_version (id, version, updated) VALUES(1, ?, ?)`
	_, err := txn.Exec(stmt, newDBVersion, time.Now().Unix())
	return err
}

func (vdb *VersionedDB) ensureVersionTable() error {
	txn, err := vdb.DB.Begin()
	defer func() {
		if err != nil {
			glog.Errorf("Encountered error rolling back: %s", err)
			txn.Rollback()
		} else {
			txn.Commit()
		}
	}()

	if err != nil {
		fmt.Errorf("Unable to start database transaction. %s", err)
	}

	stmt := `CREATE TABLE IF NOT EXISTS sk_db_version (
			id         INTEGER      NOT NULL PRIMARY KEY,
			version    INTEGER      NOT NULL,
			updated    BIGINT       NOT NULL
		)`
	if _, err = txn.Exec(stmt); err != nil {
		return fmt.Errorf("Creating version table failed: %s", err)
	}

	stmt = "SELECT COUNT(*) FROM sk_db_version"
	var count int
	if err = txn.QueryRow(stmt).Scan(&count); err != nil {
		return fmt.Errorf("Unable to read version table: %s", err)
	}

	// In both cases we want the transaction to roll back.
	if count == 0 {
		err = vdb.setDBVersion(txn, 0)
	} else if count > 1 {
		err = fmt.Errorf("Version table contains more than one row.")
	}

	return err
}

// Returns the SQL statements base on whether we are using MySQL and the
// current and target DB version.
// This function assumes that currentVersion != targetVersion.
func (vdb *VersionedDB) getMigrations(currentVersion int, targetVersion int) [][]string {
	inc := util.SignInt(targetVersion - currentVersion)
	idx := currentVersion
	if inc < 0 {
		idx = currentVersion - 1
	}
	delta := util.AbsInt(targetVersion - currentVersion)
	result := make([][]string, 0, delta)

	for i := 0; i < delta; i++ {
		var temp []string
		switch {
		// using mysqlp
		case (inc > 0) && vdb.IsMySQL:
			temp = vdb.migrationSteps[idx].MySQLUp
		case (inc < 0) && vdb.IsMySQL:
			temp = vdb.migrationSteps[idx].MySQLDown
		// using sqlite
		case (inc > 0):
			temp = vdb.migrationSteps[idx].SQLiteUp
		case (inc < 0):
			temp = vdb.migrationSteps[idx].SQLiteDown
		}
		result = append(result, temp)
		idx += inc
	}
	return result
}
<commit_msg>Fix issue from previous CL<commit_after>package database

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/golang/glog"
	_ "github.com/mattn/go-sqlite3"
	"skia.googlesource.com/buildbot.git/go/util"
)

// Config information to create a database connection.
type DatabaseConfig struct {
	MySQLString    string
	SQLiteFilePath string
	MigrationSteps []MigrationStep
}

// Single step to migrated from one database version to the next and back.
type MigrationStep struct {
	MySQLUp    []string
	MySQLDown  []string
	SQLiteUp   []string
	SQLiteDown []string
}

// Database handle to send queries to the underlying database.
type VersionedDB struct {
	// Database intance that is either backed by SQLite or MySQl.
	DB *sql.DB

	// Keeps track if we are connected to MySQL or SQLite
	IsMySQL bool

	// List of migration steps for this database.
	migrationSteps []MigrationStep
}

// Init must be called once before DB is used.
//
// Since it used glog, make sure it is also called after flag.Parse is called.
func NewVersionedDB(conf *DatabaseConfig) *VersionedDB {
	// If there is a connection string then connect to the MySQL server.
	// This is for testing only. In production we get the relevant information
	// from the metadata server.
	var err error
	var isMySQL = true
	var DB *sql.DB = nil

	if conf.MySQLString != "" {
		glog.Infoln("Opening SQL database.")
		if DB, err = sql.Open("mysql", conf.MySQLString); err == nil {
			glog.Infoln("Sending Ping.")
			err = DB.Ping()
		}

		if err != nil {
			glog.Fatalln("Failed to open connection to SQL server:", err)
		}
	} else {
		// Open a local SQLite database instead.
		glog.Infof("Opening local sqlite database at: %s", conf.SQLiteFilePath)
		// Fallback to sqlite for local use.
		DB, err = sql.Open("sqlite3", conf.SQLiteFilePath)
		if err != nil {
			glog.Fatalln("Failed to open:", err)
		}
		isMySQL = false
	}

	result := &VersionedDB{
		DB:             DB,
		IsMySQL:        isMySQL,
		migrationSteps: conf.MigrationSteps,
	}

	// Make sure the migration table exists.
	if err := result.checkVersionTable(); err != nil {
		// We are using panic() instead of Fataln() to be able to trap this
		// in tests and make sure it fails when no version table exists.
		glog.Errorln("Unable to create version table.")
		panic("Attempt to create version table returned: " + err.Error())
	}
	glog.Infoln("Version table OK.")

	// Migrate to the latest version if we are using SQLite, so we don't have
	// to run the *_migratdb command for a local database.
	if !result.IsMySQL {
		result.Migrate(result.MaxDBVersion())
	}

	// Ping the database occasionally to keep the connection fresh.
	go func() {
		c := time.Tick(1 * time.Minute)
		for _ = range c {
			if err := result.DB.Ping(); err != nil {
				glog.Warningln("Database failed to respond:", err)
			}
			glog.Infof("db: Successful ping")
		}
	}()

	return result
}

// Close the underlying database.
func (vdb *VersionedDB) Close() error {
	return vdb.DB.Close()
}

// Migrates the database to the specified target version. Use DBVersion() to
// retrieve the current version of the database.
func (vdb *VersionedDB) Migrate(targetVersion int) error {
	if (targetVersion < 0) || (targetVersion > vdb.MaxDBVersion()) {
		glog.Fatalf("Target db version must be in range: [0 .. %d]", vdb.MaxDBVersion())
	}

	currentVersion, err := vdb.DBVersion()
	if err != nil {
		return err
	}

	if targetVersion == currentVersion {
		return nil
	}

	// start a transaction
	txn, err := vdb.DB.Begin()
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			glog.Errorf("Rolling back commit. Error: %s", err)
			txn.Rollback()
		} else {
			glog.Infoln("Committing changes.")
			txn.Commit()
		}
	}()

	// run through the transactions
	runSteps := vdb.getMigrations(currentVersion, targetVersion)
	if len(runSteps) == 0 {
		glog.Fatalln("Unable to find migration steps.")
	}

	for _, step := range runSteps {
		for _, stmt := range step {
			glog.Infoln("EXECUTING: \n", stmt)
			if _, err = txn.Exec(stmt); err != nil {
				return err
			}
		}
	}

	// update the dbversion table
	if err = vdb.setDBVersion(txn, targetVersion); err != nil {
		return err
	}

	return nil
}

// Returns the current version of the database. It assumes that the
// Migrate function has already been called and the version table has been
// created in the database.
func (vdb *VersionedDB) DBVersion() (int, error) {
	stmt := `
		SELECT version
		FROM sk_db_version
		WHERE id=1`

	var version int
	err := vdb.DB.QueryRow(stmt).Scan(&version)
	return version, err
}

// Returns the highest version currently available.
func (vdb *VersionedDB) MaxDBVersion() int {
	return len(vdb.migrationSteps)
}

// Returns an error if the version table does not exist.
func (vdb *VersionedDB) checkVersionTable() error {
	// Check if the table exists in MySQL or SQLite.
	stmt := "SHOW TABLES LIKE 'sk_db_version'"
	if !vdb.IsMySQL {
		stmt = "SELECT name FROM sqlite_master WHERE type='table' AND name='sk_db_version';"
	}

	var temp string
	err := vdb.DB.QueryRow(stmt).Scan(&temp)
	if err != nil {
		// See if we can create the version table.
		return vdb.ensureVersionTable()
	}

	return nil
}

func (vdb *VersionedDB) setDBVersion(txn *sql.Tx, newDBVersion int) error {
	stmt := `REPLACE INTO sk_db_version (id, version, updated) VALUES(1, ?, ?)`
	_, err := txn.Exec(stmt, newDBVersion, time.Now().Unix())
	return err
}

func (vdb *VersionedDB) ensureVersionTable() error {
	txn, err := vdb.DB.Begin()
	defer func() {
		if err != nil {
			glog.Errorf("Encountered error rolling back: %s", err)
			txn.Rollback()
		} else {
			txn.Commit()
		}
	}()

	if err != nil {
		fmt.Errorf("Unable to start database transaction. %s", err)
	}

	stmt := `CREATE TABLE IF NOT EXISTS sk_db_version (
			id         INTEGER      NOT NULL PRIMARY KEY,
			version    INTEGER      NOT NULL,
			updated    BIGINT       NOT NULL
		)`
	if _, err = txn.Exec(stmt); err != nil {
		return fmt.Errorf("Creating version table failed: %s", err)
	}

	stmt = "SELECT COUNT(*) FROM sk_db_version"
	var count int
	if err = txn.QueryRow(stmt).Scan(&count); err != nil {
		return fmt.Errorf("Unable to read version table: %s", err)
	}

	// In both cases we want the transaction to roll back.
	if count == 0 {
		err = vdb.setDBVersion(txn, 0)
	} else if count > 1 {
		err = fmt.Errorf("Version table contains more than one row.")
	}

	return err
}

// Returns the SQL statements base on whether we are using MySQL and the
// current and target DB version.
// This function assumes that currentVersion != targetVersion.
func (vdb *VersionedDB) getMigrations(currentVersion int, targetVersion int) [][]string {
	inc := util.SignInt(targetVersion - currentVersion)
	idx := currentVersion
	if inc < 0 {
		idx = currentVersion - 1
	}
	delta := util.AbsInt(targetVersion - currentVersion)
	result := make([][]string, 0, delta)

	for i := 0; i < delta; i++ {
		var temp []string
		switch {
		// using mysqlp
		case (inc > 0) && vdb.IsMySQL:
			temp = vdb.migrationSteps[idx].MySQLUp
		case (inc < 0) && vdb.IsMySQL:
			temp = vdb.migrationSteps[idx].MySQLDown
		// using sqlite
		case (inc > 0):
			temp = vdb.migrationSteps[idx].SQLiteUp
		case (inc < 0):
			temp = vdb.migrationSteps[idx].SQLiteDown
		}
		result = append(result, temp)
		idx += inc
	}
	return result
}
<|endoftext|>


<commit_before>package posix

func (k *PosixKernel) Ioctl()         {}
func (k *PosixKernel) RtSigprocmask() {}
func (k *PosixKernel) RtSigaction()   {}
func (k *PosixKernel) Futex()         {}
func (k *PosixKernel) Fcntl()         {}
func (k *PosixKernel) SchedYield()    {}
func (k *PosixKernel) Madvise()       {}
func (k *PosixKernel) Mlock()         {}
func (k *PosixKernel) Munlock()       {}
func (k *PosixKernel) Mlockall()      {}
func (k *PosixKernel) Munlockall()    {}

func (k *PosixKernel) Swapon()  {}
func (k *PosixKernel) Swapoff() {}
<commit_msg>add gettid stub<commit_after>package posix

func (k *PosixKernel) Ioctl()         {}
func (k *PosixKernel) RtSigprocmask() {}
func (k *PosixKernel) RtSigaction()   {}
func (k *PosixKernel) Futex()         {}
func (k *PosixKernel) Fcntl()         {}
func (k *PosixKernel) SchedYield()    {}
func (k *PosixKernel) Madvise()       {}
func (k *PosixKernel) Mlock()         {}
func (k *PosixKernel) Munlock()       {}
func (k *PosixKernel) Mlockall()      {}
func (k *PosixKernel) Munlockall()    {}

func (k *PosixKernel) Swapon()  {}
func (k *PosixKernel) Swapoff() {}

func (k *PosixKernel) Gettid() int { return 0 }
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	// uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
	"github.com/lunixbochs/readline"
	"net"
	"os"

	"github.com/lunixbochs/usercorn/go/models"
)

type Debugger struct {
	instances []models.Usercorn
}

func NewDebugger(first models.Usercorn, extra ...models.Usercorn) *Debugger {
	instances := append([]models.Usercorn{first}, extra...)
	return &Debugger{instances}
}

func (d *Debugger) Listen(addr string) error {
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	go func() {
		for {
			conn, err := ln.Accept()
			if err != nil {
				fmt.Fprintf(os.Stderr, "error in debug accept: %v", err)
				return
			}
			go d.Handle(conn)
		}
	}()
	return nil
}

func (d *Debugger) Handle(c net.Conn) {
	fmt.Fprintf(os.Stderr, "handling conn: %v\n", c)

	stdin, err := c.(*net.TCPConn).File()
	if err != nil {
		fmt.Fprintf(os.Stderr, "error opening 'stdin' for debugger: %v\n", err)
		return
	}
	rl, err := readline.NewEx(&readline.Config{
		Prompt: "> ",
		Stderr: c,
		Stdin:  stdin,
		Stdout: c,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "error opening readline for debugger: %v\n", err)
		return
	}
	for {
		line, err := rl.Readline()
		if err != nil {
			break
		}
		fmt.Println(line)
	}
	stdin.Close()
	c.Close()
}
<commit_msg>make debug connection synchronous<commit_after>package main

import (
	"fmt"
	// uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
	"github.com/lunixbochs/readline"
	"net"
	"os"

	"github.com/lunixbochs/usercorn/go/models"
)

type Debugger struct {
	instances []models.Usercorn
}

func NewDebugger(first models.Usercorn, extra ...models.Usercorn) *Debugger {
	instances := append([]models.Usercorn{first}, extra...)
	return &Debugger{instances}
}

func (d *Debugger) Listen(addr string) error {
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	conn, err := ln.Accept()
	if err != nil {
		return err
	}
	go d.Handle(conn)
	return nil
}

func (d *Debugger) Handle(c net.Conn) {
	fmt.Fprintf(os.Stderr, "Debug connection from %s\n", c.RemoteAddr())

	stdin, err := c.(*net.TCPConn).File()
	if err != nil {
		fmt.Fprintf(os.Stderr, "error opening 'stdin' for debugger: %v\n", err)
		return
	}
	rl, err := readline.NewEx(&readline.Config{
		Prompt: "> ",
		Stderr: c,
		Stdin:  stdin,
		Stdout: c,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "error opening readline for debugger: %v\n", err)
		return
	}
	for {
		line, err := rl.Readline()
		if err != nil {
			break
		}
		fmt.Println(line)
	}
	stdin.Close()
	c.Close()
}
<|endoftext|>


<commit_before>package main

import "bufio"
import "flag"
import "fmt"
import "log"
import "os"

type Request struct {
	ip string
	path string
}

func read_file(fn string) chan *Request {
	file, err := os.Open(fn)
	if (err != nil) {
		log.Fatal(err)
	}
	// TODO check file format

	output := make(chan *Request)
	go func() {
		scanner := bufio.NewScanner(file)
		for scanner.Scan() {
			line := scanner.Text()
			r := new(Request)
			r.ip = "TODO: ip"
			r.path = line
			output <- r
		}
		if (scanner.Err() != nil) {
			log.Fatal(scanner.Err())
		}
		file.Close()
		close(output)
	}()

	return output
}

func read_files(filenames []string) chan *Request {
	output := make(chan *Request)

	go func() {
		for _, filename := range filenames {
			for r := range read_file(filename) {
				output <- r
			}
		}
		close(output)
	}()

	return output
}

func main() {
	flag.Parse()
	positional_args := flag.Args()
	if len(positional_args) < 1 {
		println("Not enough arguments")
		flag.Usage()
		os.Exit(2)
	}
	action := positional_args[0]

	switch action {
	case "listrequests":
		for r := range read_files(positional_args[1:]) {
			fmt.Printf("%s\n", r.path)
		}
	default:
		fmt.Fprintf(os.Stderr, "Unknown action %s\n", action)
		os.Exit(2)
	}
}
<commit_msg>Add xz template<commit_after>package main

import "bufio"
import "flag"
import "fmt"
import "io"
import "log"
import "os"
import "os/exec"

type Request struct {
	ip string
	path string
}

func uncompressXz(r io.Reader) io.ReadCloser {
    rpipe, wpipe := io.Pipe()

    cmd := exec.Command("xz", "--decompress", "--stdout")
    cmd.Stdin = r
    cmd.Stdout = wpipe

    go func() {
        err := cmd.Run()
        wpipe.CloseWithError(err)
    }()

    return rpipe
}

func readFile(fn string) chan *Request {
	file, err := os.Open(fn)
	if (err != nil) {
		log.Fatal(err)
	}
	// TODO check file format

	output := make(chan *Request)
	go func() {
		scanner := bufio.NewScanner(file)
		for scanner.Scan() {
			line := scanner.Text()
			r := new(Request)
			r.ip = "TODO: ip"
			r.path = line
			output <- r
		}
		if (scanner.Err() != nil) {
			log.Fatal(scanner.Err())
		}
		file.Close()
		close(output)
	}()

	return output
}

func readFiles(filenames []string) chan *Request {
	output := make(chan *Request)

	go func() {
		for _, filename := range filenames {
			for r := range readFile(filename) {
				output <- r
			}
		}
		close(output)
	}()

	return output
}

func main() {
	flag.Parse()
	positional_args := flag.Args()
	if len(positional_args) < 1 {
		println("Not enough arguments")
		flag.Usage()
		os.Exit(2)
	}
	action := positional_args[0]

	switch action {
	case "listrequests":
		for r := range readFiles(positional_args[1:]) {
			fmt.Printf("%s\n", r.path)
		}
	default:
		fmt.Fprintf(os.Stderr, "Unknown action %s\n", action)
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>package nv

import (
  "encoding/json"
)

type NV_Event struct {
    ServerID    string
    Timestamp   time.Time
    ARI_Event   string
}

func Init(in chan string, out chan *NV_Event) {
  go func(in chan string, out chan *NV_Event) {
    for instring := range in {
      var e *NV_Event
      json.Unmarshal([]byte(instring), e)
      out <- e
    }
  }(in, out)
}<commit_msg>Need time library<commit_after>package nv

import (
  "encoding/json"
  "time"
)

type NV_Event struct {
    ServerID    string
    Timestamp   time.Time
    ARI_Event   string
}

func Init(in chan string, out chan *NV_Event) {
  go func(in chan string, out chan *NV_Event) {
    for instring := range in {
      var e *NV_Event
      json.Unmarshal([]byte(instring), e)
      out <- e
    }
  }(in, out)
}<|endoftext|>


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

// Based on src/pkg/reflect/deepequal.go from official
// golang's stdlib.

package mergo

import (
	"fmt"
	"reflect"
)

func hasMergeableFields(dst reflect.Value) (exported bool) {
	for i, n := 0, dst.NumField(); i < n; i++ {
		field := dst.Type().Field(i)
		if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
			exported = exported || hasMergeableFields(dst.Field(i))
		} else if isExportedComponent(&field) {
			exported = exported || len(field.PkgPath) == 0
		}
	}
	return
}

func isExportedComponent(field *reflect.StructField) bool {
	pkgPath := field.PkgPath
	if len(pkgPath) > 0 {
		return false
	}
	c := field.Name[0]
	if 'a' <= c && c <= 'z' || c == '_' {
		return false
	}
	return true
}

type Config struct {
	Overwrite                    bool
	AppendSlice                  bool
	TypeCheck                    bool
	Transformers                 Transformers
	overwriteWithEmptyValue      bool
	overwriteSliceWithEmptyValue bool
	sliceDeepCopy                bool
	debug                        bool
}

type Transformers interface {
	Transformer(reflect.Type) func(dst, src reflect.Value) error
}

// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
	overwrite := config.Overwrite
	typeCheck := config.TypeCheck
	overwriteWithEmptySrc := config.overwriteWithEmptyValue
	overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue
	sliceDeepCopy := config.sliceDeepCopy

	if !src.IsValid() {
		return
	}
	if dst.CanAddr() {
		addr := dst.UnsafeAddr()
		h := 17 * addr
		seen := visited[h]
		typ := dst.Type()
		for p := seen; p != nil; p = p.next {
			if p.ptr == addr && p.typ == typ {
				return nil
			}
		}
		// Remember, remember...
		visited[h] = &visit{addr, typ, seen}
	}

	if config.Transformers != nil && !isEmptyValue(dst) {
		if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
			err = fn(dst, src)
			return
		}
	}

	switch dst.Kind() {
	case reflect.Struct:
		if hasMergeableFields(dst) {
			for i, n := 0, dst.NumField(); i < n; i++ {
				if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
					return
				}
			}
		} else {
			if (isReflectNil(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) {
				dst.Set(src)
			}
		}
	case reflect.Map:
		if dst.IsNil() && !src.IsNil() {
			dst.Set(reflect.MakeMap(dst.Type()))
		}

		if src.Kind() != reflect.Map {
			if overwrite {
				dst.Set(src)
			}
			return
		}

		for _, key := range src.MapKeys() {
			srcElement := src.MapIndex(key)
			if !srcElement.IsValid() {
				continue
			}
			dstElement := dst.MapIndex(key)
			switch srcElement.Kind() {
			case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
				if srcElement.IsNil() {
					if overwrite {
						dst.SetMapIndex(key, srcElement)
					}
					continue
				}
				fallthrough
			default:
				if !srcElement.CanInterface() {
					continue
				}
				switch reflect.TypeOf(srcElement.Interface()).Kind() {
				case reflect.Struct:
					fallthrough
				case reflect.Ptr:
					fallthrough
				case reflect.Map:
					srcMapElm := srcElement
					dstMapElm := dstElement
					if srcMapElm.CanInterface() {
						srcMapElm = reflect.ValueOf(srcMapElm.Interface())
						if dstMapElm.IsValid() {
							dstMapElm = reflect.ValueOf(dstMapElm.Interface())
						}
					}
					if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil {
						return
					}
				case reflect.Slice:
					srcSlice := reflect.ValueOf(srcElement.Interface())

					var dstSlice reflect.Value
					if !dstElement.IsValid() || dstElement.IsNil() {
						dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
					} else {
						dstSlice = reflect.ValueOf(dstElement.Interface())
					}

					if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy {
						if typeCheck && srcSlice.Type() != dstSlice.Type() {
							return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
						}
						dstSlice = srcSlice
					} else if config.AppendSlice {
						if srcSlice.Type() != dstSlice.Type() {
							return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
						}
						dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
					} else if sliceDeepCopy {
						i := 0
						for ; i < srcSlice.Len() && i < dstSlice.Len(); i++ {
							srcElement := srcSlice.Index(i)
							dstElement := dstSlice.Index(i)

							if srcElement.CanInterface() {
								srcElement = reflect.ValueOf(srcElement.Interface())
							}
							if dstElement.CanInterface() {
								dstElement = reflect.ValueOf(dstElement.Interface())
							}

							if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
								return
							}
						}

					}
					dst.SetMapIndex(key, dstSlice)
				}
			}
			if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) {
				continue
			}

			if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement)) {
				if dst.IsNil() {
					dst.Set(reflect.MakeMap(dst.Type()))
				}
				dst.SetMapIndex(key, srcElement)
			}
		}
	case reflect.Slice:
		if !dst.CanSet() {
			break
		}
		if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy {
			dst.Set(src)
		} else if config.AppendSlice {
			if src.Type() != dst.Type() {
				return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
			}
			dst.Set(reflect.AppendSlice(dst, src))
		} else if sliceDeepCopy {
			for i := 0; i < src.Len() && i < dst.Len(); i++ {
				srcElement := src.Index(i)
				dstElement := dst.Index(i)
				if srcElement.CanInterface() {
					srcElement = reflect.ValueOf(srcElement.Interface())
				}
				if dstElement.CanInterface() {
					dstElement = reflect.ValueOf(dstElement.Interface())
				}

				if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
					return
				}
			}
		}
	case reflect.Ptr:
		fallthrough
	case reflect.Interface:
		if isReflectNil(src) {
			if overwriteWithEmptySrc && dst.CanSet() && src.Type().AssignableTo(dst.Type()) {
				dst.Set(src)
			}
			break
		}

		if src.Kind() != reflect.Interface {
			if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
				if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
					dst.Set(src)
				}
			} else if src.Kind() == reflect.Ptr {
				if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
					return
				}
			} else if dst.Elem().Type() == src.Type() {
				if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
					return
				}
			} else {
				return ErrDifferentArgumentsTypes
			}
			break
		}

		if dst.IsNil() || overwrite {
			if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
				dst.Set(src)
			}
			break
		}

		if dst.Elem().Kind() == src.Elem().Kind() {
			if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
				return
			}
			break
		}
	default:
		mustSet := (isEmptyValue(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc)
		v := fmt.Sprintf("%v", src)
		if v == "TestIssue106" {
			fmt.Println(mustSet)
			fmt.Println(dst.CanSet())
		}
		if mustSet {
			if dst.CanSet() {
				dst.Set(src)
			} else {
				dst = src
			}
		}
	}

	return
}

// Merge will fill any empty for value type attributes on the dst struct using corresponding
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
// and dst must be a pointer to struct.
// It won't merge unexported (private) fields and will do recursively any exported field.
func Merge(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, opts...)
}

// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
// non-empty src attribute values.
// Deprecated: use Merge(…) with WithOverride
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, append(opts, WithOverride)...)
}

// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
func WithTransformers(transformers Transformers) func(*Config) {
	return func(config *Config) {
		config.Transformers = transformers
	}
}

// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
func WithOverride(config *Config) {
	config.Overwrite = true
}

// WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values.
func WithOverwriteWithEmptyValue(config *Config) {
	config.Overwrite = true
	config.overwriteWithEmptyValue = true
}

// WithOverrideEmptySlice will make merge override empty dst slice with empty src slice.
func WithOverrideEmptySlice(config *Config) {
	config.overwriteSliceWithEmptyValue = true
}

// WithAppendSlice will make merge append slices instead of overwriting it.
func WithAppendSlice(config *Config) {
	config.AppendSlice = true
}

// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
func WithTypeCheck(config *Config) {
	config.TypeCheck = true
}

// WithSliceDeepCopy will merge slice element one by one with Overwrite flag.
func WithSliceDeepCopy(config *Config) {
	config.sliceDeepCopy = true
	config.Overwrite = true
}

func merge(dst, src interface{}, opts ...func(*Config)) error {
	if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
		return ErrNonPointerAgument
	}
	var (
		vDst, vSrc reflect.Value
		err        error
	)

	config := &Config{}

	for _, opt := range opts {
		opt(config)
	}

	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
		return err
	}
	if vDst.Type() != vSrc.Type() {
		return ErrDifferentArgumentsTypes
	}
	return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}

// IsReflectNil is the reflect value provided nil
func isReflectNil(v reflect.Value) bool {
	k := v.Kind()
	switch k {
	case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr:
		// Both interface and slice are nil if first word is 0.
		// Both are always bigger than a word; assume flagIndir.
		return v.IsNil()
	default:
		return false
	}
}
<commit_msg>fixed issue #160 by removing unused old test code<commit_after>// Copyright 2013 Dario Castañé. All rights reserved.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Based on src/pkg/reflect/deepequal.go from official
// golang's stdlib.

package mergo

import (
	"fmt"
	"reflect"
)

func hasMergeableFields(dst reflect.Value) (exported bool) {
	for i, n := 0, dst.NumField(); i < n; i++ {
		field := dst.Type().Field(i)
		if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
			exported = exported || hasMergeableFields(dst.Field(i))
		} else if isExportedComponent(&field) {
			exported = exported || len(field.PkgPath) == 0
		}
	}
	return
}

func isExportedComponent(field *reflect.StructField) bool {
	pkgPath := field.PkgPath
	if len(pkgPath) > 0 {
		return false
	}
	c := field.Name[0]
	if 'a' <= c && c <= 'z' || c == '_' {
		return false
	}
	return true
}

type Config struct {
	Overwrite                    bool
	AppendSlice                  bool
	TypeCheck                    bool
	Transformers                 Transformers
	overwriteWithEmptyValue      bool
	overwriteSliceWithEmptyValue bool
	sliceDeepCopy                bool
	debug                        bool
}

type Transformers interface {
	Transformer(reflect.Type) func(dst, src reflect.Value) error
}

// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
	overwrite := config.Overwrite
	typeCheck := config.TypeCheck
	overwriteWithEmptySrc := config.overwriteWithEmptyValue
	overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue
	sliceDeepCopy := config.sliceDeepCopy

	if !src.IsValid() {
		return
	}
	if dst.CanAddr() {
		addr := dst.UnsafeAddr()
		h := 17 * addr
		seen := visited[h]
		typ := dst.Type()
		for p := seen; p != nil; p = p.next {
			if p.ptr == addr && p.typ == typ {
				return nil
			}
		}
		// Remember, remember...
		visited[h] = &visit{addr, typ, seen}
	}

	if config.Transformers != nil && !isEmptyValue(dst) {
		if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
			err = fn(dst, src)
			return
		}
	}

	switch dst.Kind() {
	case reflect.Struct:
		if hasMergeableFields(dst) {
			for i, n := 0, dst.NumField(); i < n; i++ {
				if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
					return
				}
			}
		} else {
			if (isReflectNil(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) {
				dst.Set(src)
			}
		}
	case reflect.Map:
		if dst.IsNil() && !src.IsNil() {
			dst.Set(reflect.MakeMap(dst.Type()))
		}

		if src.Kind() != reflect.Map {
			if overwrite {
				dst.Set(src)
			}
			return
		}

		for _, key := range src.MapKeys() {
			srcElement := src.MapIndex(key)
			if !srcElement.IsValid() {
				continue
			}
			dstElement := dst.MapIndex(key)
			switch srcElement.Kind() {
			case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
				if srcElement.IsNil() {
					if overwrite {
						dst.SetMapIndex(key, srcElement)
					}
					continue
				}
				fallthrough
			default:
				if !srcElement.CanInterface() {
					continue
				}
				switch reflect.TypeOf(srcElement.Interface()).Kind() {
				case reflect.Struct:
					fallthrough
				case reflect.Ptr:
					fallthrough
				case reflect.Map:
					srcMapElm := srcElement
					dstMapElm := dstElement
					if srcMapElm.CanInterface() {
						srcMapElm = reflect.ValueOf(srcMapElm.Interface())
						if dstMapElm.IsValid() {
							dstMapElm = reflect.ValueOf(dstMapElm.Interface())
						}
					}
					if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil {
						return
					}
				case reflect.Slice:
					srcSlice := reflect.ValueOf(srcElement.Interface())

					var dstSlice reflect.Value
					if !dstElement.IsValid() || dstElement.IsNil() {
						dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
					} else {
						dstSlice = reflect.ValueOf(dstElement.Interface())
					}

					if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy {
						if typeCheck && srcSlice.Type() != dstSlice.Type() {
							return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
						}
						dstSlice = srcSlice
					} else if config.AppendSlice {
						if srcSlice.Type() != dstSlice.Type() {
							return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
						}
						dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
					} else if sliceDeepCopy {
						i := 0
						for ; i < srcSlice.Len() && i < dstSlice.Len(); i++ {
							srcElement := srcSlice.Index(i)
							dstElement := dstSlice.Index(i)

							if srcElement.CanInterface() {
								srcElement = reflect.ValueOf(srcElement.Interface())
							}
							if dstElement.CanInterface() {
								dstElement = reflect.ValueOf(dstElement.Interface())
							}

							if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
								return
							}
						}

					}
					dst.SetMapIndex(key, dstSlice)
				}
			}
			if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) {
				continue
			}

			if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement)) {
				if dst.IsNil() {
					dst.Set(reflect.MakeMap(dst.Type()))
				}
				dst.SetMapIndex(key, srcElement)
			}
		}
	case reflect.Slice:
		if !dst.CanSet() {
			break
		}
		if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy {
			dst.Set(src)
		} else if config.AppendSlice {
			if src.Type() != dst.Type() {
				return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
			}
			dst.Set(reflect.AppendSlice(dst, src))
		} else if sliceDeepCopy {
			for i := 0; i < src.Len() && i < dst.Len(); i++ {
				srcElement := src.Index(i)
				dstElement := dst.Index(i)
				if srcElement.CanInterface() {
					srcElement = reflect.ValueOf(srcElement.Interface())
				}
				if dstElement.CanInterface() {
					dstElement = reflect.ValueOf(dstElement.Interface())
				}

				if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
					return
				}
			}
		}
	case reflect.Ptr:
		fallthrough
	case reflect.Interface:
		if isReflectNil(src) {
			if overwriteWithEmptySrc && dst.CanSet() && src.Type().AssignableTo(dst.Type()) {
				dst.Set(src)
			}
			break
		}

		if src.Kind() != reflect.Interface {
			if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
				if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
					dst.Set(src)
				}
			} else if src.Kind() == reflect.Ptr {
				if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
					return
				}
			} else if dst.Elem().Type() == src.Type() {
				if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
					return
				}
			} else {
				return ErrDifferentArgumentsTypes
			}
			break
		}

		if dst.IsNil() || overwrite {
			if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
				dst.Set(src)
			}
			break
		}

		if dst.Elem().Kind() == src.Elem().Kind() {
			if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
				return
			}
			break
		}
	default:
		mustSet := (isEmptyValue(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc)
		if mustSet {
			if dst.CanSet() {
				dst.Set(src)
			} else {
				dst = src
			}
		}
	}

	return
}

// Merge will fill any empty for value type attributes on the dst struct using corresponding
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
// and dst must be a pointer to struct.
// It won't merge unexported (private) fields and will do recursively any exported field.
func Merge(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, opts...)
}

// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
// non-empty src attribute values.
// Deprecated: use Merge(…) with WithOverride
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, append(opts, WithOverride)...)
}

// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
func WithTransformers(transformers Transformers) func(*Config) {
	return func(config *Config) {
		config.Transformers = transformers
	}
}

// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
func WithOverride(config *Config) {
	config.Overwrite = true
}

// WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values.
func WithOverwriteWithEmptyValue(config *Config) {
	config.Overwrite = true
	config.overwriteWithEmptyValue = true
}

// WithOverrideEmptySlice will make merge override empty dst slice with empty src slice.
func WithOverrideEmptySlice(config *Config) {
	config.overwriteSliceWithEmptyValue = true
}

// WithAppendSlice will make merge append slices instead of overwriting it.
func WithAppendSlice(config *Config) {
	config.AppendSlice = true
}

// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
func WithTypeCheck(config *Config) {
	config.TypeCheck = true
}

// WithSliceDeepCopy will merge slice element one by one with Overwrite flag.
func WithSliceDeepCopy(config *Config) {
	config.sliceDeepCopy = true
	config.Overwrite = true
}

func merge(dst, src interface{}, opts ...func(*Config)) error {
	if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
		return ErrNonPointerAgument
	}
	var (
		vDst, vSrc reflect.Value
		err        error
	)

	config := &Config{}

	for _, opt := range opts {
		opt(config)
	}

	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
		return err
	}
	if vDst.Type() != vSrc.Type() {
		return ErrDifferentArgumentsTypes
	}
	return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}

// IsReflectNil is the reflect value provided nil
func isReflectNil(v reflect.Value) bool {
	k := v.Kind()
	switch k {
	case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr:
		// Both interface and slice are nil if first word is 0.
		// Both are always bigger than a word; assume flagIndir.
		return v.IsNil()
	default:
		return false
	}
}
<|endoftext|>