<commit_before>package knx

import (
	"context"
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/vapourismo/knx-go/knx/cemi"
	"github.com/vapourismo/knx-go/knx/proto"
)

// TunnelConfig allows you to configure the client's behavior.
type TunnelConfig struct {
	// ResendInterval is how long to wait for a response, until the request is resend. A interval
	// <= 0 can't be used. The default value will be used instead.
	ResendInterval time.Duration

	// HeartbeatDelay specifies the time which has to elapse without any incoming communication,
	// until a heartbeat is triggered. A delay <= 0 will result in the use of a default value.
	HeartbeatDelay time.Duration

	// ResponseTimeout specifies how long to wait for a response. A timeout <= 0 will not be
	// accepted. Instead, the default value will be used.
	ResponseTimeout time.Duration
}

// Default configuration elements
var (
	defaultResendInterval  = 500 * time.Millisecond
	defaultHeartbeatDelay  = 10 * time.Second
	defaultResponseTimeout = 10 * time.Second

	DefaultClientConfig = TunnelConfig{
		defaultResendInterval,
		defaultHeartbeatDelay,
		defaultResponseTimeout,
	}
)

// checkClientConfig makes sure that the configuration is actually usable.
func checkClientConfig(config TunnelConfig) TunnelConfig {
	if config.ResendInterval <= 0 {
		config.ResendInterval = defaultResendInterval
	}

	if config.HeartbeatDelay <= 0 {
		config.HeartbeatDelay = defaultHeartbeatDelay
	}

	if config.ResponseTimeout <= 0 {
		config.ResponseTimeout = defaultResponseTimeout
	}

	return config
}

// tunnelConn is a handle for a tunnel connection.
type tunnelConn struct {
	sock      Socket
	config    TunnelConfig
	channel   uint8
	control   proto.HostInfo
	seqMu     *sync.Mutex
	seqNumber uint8
	ack       chan *proto.TunnelRes
	inbound   chan cemi.CEMI
}

// requestConn repeatedly sends a connection request through the socket until the provided context gets
// canceled, or a response is received. A response that renders the gateway as busy will not stop
// requestConn.
func (conn *tunnelConn) requestConn(ctx context.Context) (err error) {
	req := &proto.ConnReq{}

	// Send the initial request.
	err = conn.sock.Send(req)
	if err != nil {
		return
	}

	// Create a resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	// Cycle until a request gets a response.
	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Resend timer triggered.
		case <-ticker.C:
			err = conn.sock.Send(req)
			if err != nil {
				return
			}

		// A message has been received or the channel has been closed.
		case msg, open := <-conn.sock.Inbound():
			if !open {
				return errors.New("Socket's inbound channel has been closed")
			}

			// We're only interested in connection responses.
			if res, ok := msg.(*proto.ConnRes); ok {
				switch res.Status {
				// Conection has been established.
				case proto.ConnResOk:
					conn.channel = res.Channel
					conn.control = res.Control
					return nil

				// The gateway is busy, but we don't stop yet.
				case proto.ConnResBusy:
					continue

				// Connection request has been denied.
				default:
					return res.Status
				}
			}
		}
	}
}

// requestConnState periodically sends a connection state request to the gateway until it has
// received a response or the context is done.
func (conn *tunnelConn) requestConnState(
	ctx context.Context,
	heartbeat <-chan proto.ConnState,
) (proto.ConnState, error) {
	req := &proto.ConnStateReq{Channel: conn.channel, Status: 0, Control: proto.HostInfo{}}

	// Send first connection state request
	err := conn.sock.Send(req)
	if err != nil {
		return proto.ConnStateInactive, err
	}

	// Start the resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return proto.ConnStateInactive, ctx.Err()

		// Resend timer fired.
		case <-ticker.C:
			err := conn.sock.Send(req)
			if err != nil {
				return proto.ConnStateInactive, err
			}

		// Received a connection state response.
		case res, open := <-heartbeat:
			if !open {
				return proto.ConnStateInactive, errors.New("Connection server has terminated")
			}

			return res, nil
		}
	}
}

// requestDisc sends a disconnect request to the gateway.
func (conn *tunnelConn) requestDisc() error {
	return conn.sock.Send(&proto.DiscReq{
		Channel: conn.channel,
		Status:  0,
		Control: conn.control,
	})
}

// requestTunnel sends a tunnel request to the gateway and waits for an appropriate acknowledgement.
func (conn *tunnelConn) requestTunnel(
	ctx context.Context,
	data cemi.CEMI,
) error {
	// Sequence numbers cannot be reused, therefore we must protect against that.
	conn.seqMu.Lock()
	defer conn.seqMu.Unlock()

	req := &proto.TunnelReq{
		Channel:   conn.channel,
		SeqNumber: conn.seqNumber,
		Payload:   data,
	}

	// Send initial request.
	err := conn.sock.Send(req)
	if err != nil {
		return err
	}

	// Start the resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Resend timer fired.
		case <-ticker.C:
			err := conn.sock.Send(req)
			if err != nil {
				return err
			}

		// Received a tunnel response.
		case res, open := <-conn.ack:
			if !open {
				return errors.New("Connection server has terminated")
			}

			// Ignore mismatching sequence numbers.
			if res.SeqNumber != conn.seqNumber {
				continue
			}

			// Gateway has received the request, therefore we can increase on our side.
			conn.seqNumber++

			// Check if the response confirms the tunnel request.
			if res.Status == 0 {
				return nil
			}

			return fmt.Errorf("Tunnel request has been rejected with status %#x", res.Status)
		}
	}
}

// performHeartbeat uses requestState to determine if the gateway is still alive.
func (conn *tunnelConn) performHeartbeat(
	ctx context.Context,
	heartbeat <-chan proto.ConnState,
	timeout chan<- struct{},
) {
	// Setup a child context which will time out with the given heartbeat timeout.
	childCtx, cancel := context.WithTimeout(ctx, conn.config.ResponseTimeout)
	defer cancel()

	// Request the connction state.
	state, err := conn.requestConnState(childCtx, heartbeat)
	if err != nil || state != proto.ConnStateNormal {
		if err != nil {
			log(conn, "conn", "Error while requesting connection state: %v", err)
		} else {
			log(conn, "conn", "Bad connection state: %v", state)
		}

		// Write to timeout as an indication that the heartbeat has failed.
		select {
		case <-ctx.Done():
		case timeout <- struct{}{}:
		}
	}
}

// handleDiscReq validates the request.
func (conn *tunnelConn) handleDiscReq(
	ctx context.Context,
	req *proto.DiscReq,
) error {
	// Validate the request channel.
	if req.Channel != conn.channel {
		return errors.New("Invalid communication channel in disconnect request")
	}

	// We don't need to check if this errors or not. It doesn't matter.
	conn.sock.Send(&proto.DiscRes{Channel: req.Channel, Status: 0})

	return nil
}

// handleDiscRes validates the response.
func (conn *tunnelConn) handleDiscRes(
	ctx context.Context,
	res *proto.DiscRes,
) error {
	// Validate the response channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in disconnect response")
	}

	return nil
}

// handleTunnelReq validates the request, pushes the data to the client and acknowledges the
// request for the gateway.
func (conn *tunnelConn) handleTunnelReq(
	ctx context.Context,
	req *proto.TunnelReq,
	seqNumber *uint8,
) error {
	// Validate the request channel.
	if req.Channel != conn.channel {
		return errors.New("Invalid communication channel in tunnel request")
	}

	// Is the sequence number what we expected?
	if req.SeqNumber == *seqNumber {
		*seqNumber++

		// Send tunnel data to the client.
		go func() {
			select {
			case <-ctx.Done():
			case conn.inbound <- req.Payload:
			}
		}()
	}

	// Send the acknowledgement.
	return conn.sock.Send(&proto.TunnelRes{
		Channel:   conn.channel,
		SeqNumber: req.SeqNumber,
		Status:    0,
	})
}

// handleTunnelRes validates the response and relays it to a sender that is awaiting an
// acknowledgement.
func (conn *tunnelConn) handleTunnelRes(
	ctx context.Context,
	res *proto.TunnelRes,
) error {
	// Validate the request channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in connection state response")
	}

	// Send to client.
	go func() {
		select {
		case <-ctx.Done():
		case <-time.After(conn.config.ResendInterval):
		case conn.ack <- res:
		}
	}()

	return nil
}

// handleConnStateRes validates the response and sends it to the heartbeat routine, if
// there is a waiting one.
func (conn *tunnelConn) handleConnStateRes(
	ctx context.Context,
	res *proto.ConnStateRes,
	heartbeat chan<- proto.ConnState,
) error {
	// Validate the request channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in connection state response")
	}

	// Send connection state to the heartbeat goroutine.
	go func() {
		select {
		case <-ctx.Done():
		case <-time.After(conn.config.ResendInterval):
		case heartbeat <- res.Status:
		}
	}()

	return nil
}

// serve processes incoming packets. It will return with nil when a disconnect request or
// response has been received.
func (conn *tunnelConn) serve(
	ctx context.Context,
) error {
	defer close(conn.ack)
	defer close(conn.inbound)

	heartbeat := make(chan proto.ConnState)
	defer close(heartbeat)

	timeout := make(chan struct{})

	var seqNumber uint8

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Heartbeat worker signals a result.
		case <-timeout:
			return errors.New("Heartbeat did not succeed")

		// There were no incoming packets for some time.
		case <-time.After(conn.config.HeartbeatDelay):
			go conn.performHeartbeat(ctx, heartbeat, timeout)

		// A message has been received or the channel is closed.
		case msg, open := <-conn.sock.Inbound():
			if !open {
				return errors.New("Socket's inbound channel is closed")
			}

			// Determine what to do with the message.
			switch msg := msg.(type) {
			case *proto.DiscReq:
				err := conn.handleDiscReq(ctx, msg)
				if err == nil {
					return nil
				}

				log(conn, "conn", "Error while handling disconnect request %v: %v", msg, err)

			case *proto.DiscRes:
				err := conn.handleDiscRes(ctx, msg)
				if err == nil {
					return nil
				}

				log(conn, "conn", "Error while handling disconnect response %v: %v", msg, err)

			case *proto.TunnelReq:
				err := conn.handleTunnelReq(ctx, msg, &seqNumber)
				if err != nil {
					log(conn, "conn", "Error while handling tunnel request %v: %v", msg, err)
				}

			case *proto.TunnelRes:
				err := conn.handleTunnelRes(ctx, msg)
				if err != nil {
					log(conn, "conn", "Error while handling tunnel response %v: %v", msg, err)
				}

			case *proto.ConnStateRes:
				err := conn.handleConnStateRes(ctx, msg, heartbeat)
				if err != nil {
					log(
						conn, "conn",
						"Error while handling connection state response: %v", err,
					)
				}
			}
		}
	}
}

// Tunnel represents the client endpoint in a connection with a gateway.
type Tunnel struct {
	tunnelConn

	ctx    context.Context
	cancel context.CancelFunc
}

// Connect establishes a connection with a gateway. You can pass a zero initialized ClientConfig;
// the function will take care of filling in the default values.
func Connect(gatewayAddr string, config TunnelConfig) (*Tunnel, error) {
	// Create socket which will be used for communication.
	sock, err := NewTunnelSocket(gatewayAddr)
	if err != nil {
		return nil, err
	}

	// Prepare a context for the inbound server.
	ctx, cancel := context.WithCancel(context.Background())

	// Initialize the Client structure.
	client := &Tunnel{
		tunnelConn: tunnelConn{
			sock:    sock,
			config:  checkClientConfig(config),
			seqMu:   &sync.Mutex{},
			ack:     make(chan *proto.TunnelRes),
			inbound: make(chan cemi.CEMI),
		},
		ctx:    ctx,
		cancel: cancel,
	}

	// Prepare a context, so that the connection request cannot run forever.
	connectCtx, cancelConnect := context.WithTimeout(ctx, client.config.ResponseTimeout)
	defer cancelConnect()

	// Connect to the gateway.
	err = client.requestConn(connectCtx)
	if err != nil {
		sock.Close()
		return nil, err
	}

	go client.serve(client.ctx)

	return client, nil
}

// Close will terminate the connection.
func (client *Tunnel) Close() {
	client.requestDisc()
	client.cancel()
	client.sock.Close()
}

// Inbound retrieves the channel which transmits incoming data.
func (client *Tunnel) Inbound() <-chan cemi.CEMI {
	return client.inbound
}

// Send relays a tunnel request to the gateway with the given contents.
func (client *Tunnel) Send(data cemi.CEMI) error {
	// Prepare a context, so that we won't wait forever for a tunnel response.
	ctx, cancel := context.WithTimeout(client.ctx, client.config.ResponseTimeout)
	defer cancel()

	// Send the tunnel reqest.
	err := client.requestTunnel(ctx, data)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Add reconnect feature to tunnel connection<commit_after>package knx

import (
	"context"
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/vapourismo/knx-go/knx/cemi"
	"github.com/vapourismo/knx-go/knx/proto"
)

// TunnelConfig allows you to configure the client's behavior.
type TunnelConfig struct {
	// ResendInterval is how long to wait for a response, until the request is resend. A interval
	// <= 0 can't be used. The default value will be used instead.
	ResendInterval time.Duration

	// HeartbeatDelay specifies the time which has to elapse without any incoming communication,
	// until a heartbeat is triggered. A delay <= 0 will result in the use of a default value.
	HeartbeatDelay time.Duration

	// ResponseTimeout specifies how long to wait for a response. A timeout <= 0 will not be
	// accepted. Instead, the default value will be used.
	ResponseTimeout time.Duration
}

// Default configuration elements
var (
	defaultResendInterval  = 500 * time.Millisecond
	defaultHeartbeatDelay  = 10 * time.Second
	defaultResponseTimeout = 10 * time.Second

	DefaultClientConfig = TunnelConfig{
		defaultResendInterval,
		defaultHeartbeatDelay,
		defaultResponseTimeout,
	}
)

// checkClientConfig makes sure that the configuration is actually usable.
func checkClientConfig(config TunnelConfig) TunnelConfig {
	if config.ResendInterval <= 0 {
		config.ResendInterval = defaultResendInterval
	}

	if config.HeartbeatDelay <= 0 {
		config.HeartbeatDelay = defaultHeartbeatDelay
	}

	if config.ResponseTimeout <= 0 {
		config.ResponseTimeout = defaultResponseTimeout
	}

	return config
}

// tunnelConn is a handle for a tunnel connection.
type tunnelConn struct {
	sock      Socket
	config    TunnelConfig
	channel   uint8
	control   proto.HostInfo
	seqMu     *sync.Mutex
	seqNumber uint8
	ack       chan *proto.TunnelRes
	inbound   chan cemi.CEMI
}

// requestConn repeatedly sends a connection request through the socket until the provided context gets
// canceled, or a response is received. A response that renders the gateway as busy will not stop
// requestConn.
func (conn *tunnelConn) requestConn(ctx context.Context) (err error) {
	req := &proto.ConnReq{}

	// Send the initial request.
	err = conn.sock.Send(req)
	if err != nil {
		return
	}

	// Create a resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	// Cycle until a request gets a response.
	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Resend timer triggered.
		case <-ticker.C:
			err = conn.sock.Send(req)
			if err != nil {
				return
			}

		// A message has been received or the channel has been closed.
		case msg, open := <-conn.sock.Inbound():
			if !open {
				return errors.New("Socket's inbound channel has been closed")
			}

			// We're only interested in connection responses.
			if res, ok := msg.(*proto.ConnRes); ok {
				switch res.Status {
				// Conection has been established.
				case proto.ConnResOk:
					conn.channel = res.Channel
					conn.control = res.Control
					return nil

				// The gateway is busy, but we don't stop yet.
				case proto.ConnResBusy:
					continue

				// Connection request has been denied.
				default:
					return res.Status
				}
			}
		}
	}
}

// requestConnState periodically sends a connection state request to the gateway until it has
// received a response or the context is done.
func (conn *tunnelConn) requestConnState(
	ctx context.Context,
	heartbeat <-chan proto.ConnState,
) (proto.ConnState, error) {
	req := &proto.ConnStateReq{Channel: conn.channel, Status: 0, Control: proto.HostInfo{}}

	// Send first connection state request
	err := conn.sock.Send(req)
	if err != nil {
		return proto.ConnStateInactive, err
	}

	// Start the resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return proto.ConnStateInactive, ctx.Err()

		// Resend timer fired.
		case <-ticker.C:
			err := conn.sock.Send(req)
			if err != nil {
				return proto.ConnStateInactive, err
			}

		// Received a connection state response.
		case res, open := <-heartbeat:
			if !open {
				return proto.ConnStateInactive, errors.New("Connection server has terminated")
			}

			return res, nil
		}
	}
}

// requestDisc sends a disconnect request to the gateway.
func (conn *tunnelConn) requestDisc() error {
	return conn.sock.Send(&proto.DiscReq{
		Channel: conn.channel,
		Status:  0,
		Control: conn.control,
	})
}

// requestTunnel sends a tunnel request to the gateway and waits for an appropriate acknowledgement.
func (conn *tunnelConn) requestTunnel(
	ctx context.Context,
	data cemi.CEMI,
) error {
	// Sequence numbers cannot be reused, therefore we must protect against that.
	conn.seqMu.Lock()
	defer conn.seqMu.Unlock()

	req := &proto.TunnelReq{
		Channel:   conn.channel,
		SeqNumber: conn.seqNumber,
		Payload:   data,
	}

	// Send initial request.
	err := conn.sock.Send(req)
	if err != nil {
		return err
	}

	// Start the resend timer.
	ticker := time.NewTicker(conn.config.ResendInterval)
	defer ticker.Stop()

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Resend timer fired.
		case <-ticker.C:
			err := conn.sock.Send(req)
			if err != nil {
				return err
			}

		// Received a tunnel response.
		case res, open := <-conn.ack:
			if !open {
				return errors.New("Connection server has terminated")
			}

			// Ignore mismatching sequence numbers.
			if res.SeqNumber != conn.seqNumber {
				continue
			}

			// Gateway has received the request, therefore we can increase on our side.
			conn.seqNumber++

			// Check if the response confirms the tunnel request.
			if res.Status == 0 {
				return nil
			}

			return fmt.Errorf("Tunnel request has been rejected with status %#x", res.Status)
		}
	}
}

// performHeartbeat uses requestState to determine if the gateway is still alive.
func (conn *tunnelConn) performHeartbeat(
	ctx context.Context,
	heartbeat <-chan proto.ConnState,
	timeout chan<- struct{},
) {
	// Setup a child context which will time out with the given heartbeat timeout.
	childCtx, cancel := context.WithTimeout(ctx, conn.config.ResponseTimeout)
	defer cancel()

	// Request the connction state.
	state, err := conn.requestConnState(childCtx, heartbeat)
	if err != nil || state != proto.ConnStateNormal {
		if err != nil {
			log(conn, "conn", "Error while requesting connection state: %v", err)
		} else {
			log(conn, "conn", "Bad connection state: %v", state)
		}

		// Write to timeout as an indication that the heartbeat has failed.
		select {
		case <-ctx.Done():
		case timeout <- struct{}{}:
		}
	}
}

// handleDiscReq validates the request.
func (conn *tunnelConn) handleDiscReq(
	ctx context.Context,
	req *proto.DiscReq,
) error {
	// Validate the request channel.
	if req.Channel != conn.channel {
		return errors.New("Invalid communication channel in disconnect request")
	}

	// We don't need to check if this errors or not. It doesn't matter.
	conn.sock.Send(&proto.DiscRes{Channel: req.Channel, Status: 0})

	return nil
}

// handleDiscRes validates the response.
func (conn *tunnelConn) handleDiscRes(
	ctx context.Context,
	res *proto.DiscRes,
) error {
	// Validate the response channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in disconnect response")
	}

	return nil
}

// handleTunnelReq validates the request, pushes the data to the client and acknowledges the
// request for the gateway.
func (conn *tunnelConn) handleTunnelReq(
	ctx context.Context,
	req *proto.TunnelReq,
	seqNumber *uint8,
) error {
	// Validate the request channel.
	if req.Channel != conn.channel {
		return errors.New("Invalid communication channel in tunnel request")
	}

	// Is the sequence number what we expected?
	if req.SeqNumber == *seqNumber {
		*seqNumber++

		// Send tunnel data to the client.
		go func() {
			select {
			case <-ctx.Done():
			case conn.inbound <- req.Payload:
			}
		}()
	}

	// Send the acknowledgement.
	return conn.sock.Send(&proto.TunnelRes{
		Channel:   conn.channel,
		SeqNumber: req.SeqNumber,
		Status:    0,
	})
}

// handleTunnelRes validates the response and relays it to a sender that is awaiting an
// acknowledgement.
func (conn *tunnelConn) handleTunnelRes(
	ctx context.Context,
	res *proto.TunnelRes,
) error {
	// Validate the request channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in connection state response")
	}

	// Send to client.
	go func() {
		select {
		case <-ctx.Done():
		case <-time.After(conn.config.ResendInterval):
		case conn.ack <- res:
		}
	}()

	return nil
}

// handleConnStateRes validates the response and sends it to the heartbeat routine, if
// there is a waiting one.
func (conn *tunnelConn) handleConnStateRes(
	ctx context.Context,
	res *proto.ConnStateRes,
	heartbeat chan<- proto.ConnState,
) error {
	// Validate the request channel.
	if res.Channel != conn.channel {
		return errors.New("Invalid communication channel in connection state response")
	}

	// Send connection state to the heartbeat goroutine.
	go func() {
		select {
		case <-ctx.Done():
		case <-time.After(conn.config.ResendInterval):
		case heartbeat <- res.Status:
		}
	}()

	return nil
}

var (
	errHeartbeatFailed = errors.New("Heartbeat did not succeed")
	errInboundClosed   = errors.New("Socket's inbound channel is closed")
	errDisconnected    = errors.New("Gateway terminated the connection")
)

// process processes incoming packets.
func (conn *tunnelConn) process(ctx context.Context) error {
	heartbeat := make(chan proto.ConnState)
	defer close(heartbeat)

	timeout := make(chan struct{})

	var seqNumber uint8

	for {
		select {
		// Termination has been requested.
		case <-ctx.Done():
			return ctx.Err()

		// Heartbeat worker signals a result.
		case <-timeout:
			return errHeartbeatFailed

		// There were no incoming packets for some time.
		case <-time.After(conn.config.HeartbeatDelay):
			go conn.performHeartbeat(ctx, heartbeat, timeout)

		// A message has been received or the channel is closed.
		case msg, open := <-conn.sock.Inbound():
			if !open {
				return errInboundClosed
			}

			// Determine what to do with the message.
			switch msg := msg.(type) {
			case *proto.DiscReq:
				err := conn.handleDiscReq(ctx, msg)
				if err == nil {
					return errDisconnected
				}

				log(conn, "conn", "Error while handling disconnect request %v: %v", msg, err)

			case *proto.DiscRes:
				err := conn.handleDiscRes(ctx, msg)
				if err == nil {
					return nil
				}

				log(conn, "conn", "Error while handling disconnect response %v: %v", msg, err)

			case *proto.TunnelReq:
				err := conn.handleTunnelReq(ctx, msg, &seqNumber)
				if err != nil {
					log(conn, "conn", "Error while handling tunnel request %v: %v", msg, err)
				}

			case *proto.TunnelRes:
				err := conn.handleTunnelRes(ctx, msg)
				if err != nil {
					log(conn, "conn", "Error while handling tunnel response %v: %v", msg, err)
				}

			case *proto.ConnStateRes:
				err := conn.handleConnStateRes(ctx, msg, heartbeat)
				if err != nil {
					log(
						conn, "conn",
						"Error while handling connection state response: %v", err,
					)
				}
			}
		}
	}
}

// serve serves the tunnel connection. It can sustain certain failures. This function will try to
// reconnect in case of a heartbeat failure or disconnect.
func (conn *tunnelConn) serve(ctx context.Context) (err error) {
	defer close(conn.ack)
	defer close(conn.inbound)

	for {
		err = conn.process(ctx)
		log(conn, "conn", "Server terminated with error: %v", err)

		// Check if we can try again.
		if err == errDisconnected || err == errHeartbeatFailed {
			log(conn, "conn", "Attempting reconnect")

			reconnCtx, cancelReconn := context.WithTimeout(ctx, conn.config.ResponseTimeout)
			reconnErr := conn.requestConn(reconnCtx)
			cancelReconn()

			if reconnErr == nil {
				log(conn, "conn", "Reconnect succeeded")
				continue
			}

			log(conn, "conn", "Reconnect failed: %v", reconnErr)
		}

		return
	}
}

// Tunnel represents the client endpoint in a connection with a gateway.
type Tunnel struct {
	tunnelConn

	ctx    context.Context
	cancel context.CancelFunc
}

// Connect establishes a connection with a gateway. You can pass a zero initialized ClientConfig;
// the function will take care of filling in the default values.
func Connect(gatewayAddr string, config TunnelConfig) (*Tunnel, error) {
	// Create socket which will be used for communication.
	sock, err := NewTunnelSocket(gatewayAddr)
	if err != nil {
		return nil, err
	}

	// Prepare a context for the inbound server.
	ctx, cancel := context.WithCancel(context.Background())

	// Initialize the Client structure.
	client := &Tunnel{
		tunnelConn: tunnelConn{
			sock:    sock,
			config:  checkClientConfig(config),
			seqMu:   &sync.Mutex{},
			ack:     make(chan *proto.TunnelRes),
			inbound: make(chan cemi.CEMI),
		},
		ctx:    ctx,
		cancel: cancel,
	}

	// Prepare a context, so that the connection request cannot run forever.
	connectCtx, cancelConnect := context.WithTimeout(ctx, client.config.ResponseTimeout)
	defer cancelConnect()

	// Connect to the gateway.
	err = client.requestConn(connectCtx)
	if err != nil {
		sock.Close()
		return nil, err
	}

	go client.serve(client.ctx)

	return client, nil
}

// Close will terminate the connection.
func (client *Tunnel) Close() {
	client.requestDisc()
	client.cancel()
	client.sock.Close()
}

// Inbound retrieves the channel which transmits incoming data.
func (client *Tunnel) Inbound() <-chan cemi.CEMI {
	return client.inbound
}

// Send relays a tunnel request to the gateway with the given contents.
func (client *Tunnel) Send(data cemi.CEMI) error {
	// Prepare a context, so that we won't wait forever for a tunnel response.
	ctx, cancel := context.WithTimeout(client.ctx, client.config.ResponseTimeout)
	defer cancel()

	// Send the tunnel reqest.
	err := client.requestTunnel(ctx, data)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2017 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package layers

import (
	"encoding/binary"
	"errors"
	"fmt"
	"net"

	"github.com/google/gopacket"
)

type STPSwitchID struct {
	Priority STPPriority // Bridge priority
	SysID    uint16      // VLAN ID
	HwAddr   net.HardwareAddr
}

type STPPriority uint16

// Potential values for STPSwitchID.Priority.
const (
	STPPriorityMax  STPPriority = 32768
	STPPriorityHigh STPPriority = 16384
	STPPriorityLow  STPPriority = 8192
	STPPriorityMin  STPPriority = 4046
)

// STP decode spanning tree protocol packets to transport BPDU (bridge protocol data unit) message.
type STP struct {
	BaseLayer
	ProtocolID        uint16
	Version           uint8
	Type              uint8
	TC, TCA           bool // TC: Topologie change ; TCA: Topologie change ack
	RouteID, BridgeID STPSwitchID
	Cost              uint32
	PortID            uint16
	MessageAge        uint16
	MaxAge            uint16
	HelloTime         uint16
	FDelay            uint16
}

// LayerType returns gopacket.LayerTypeSTP.
func (s *STP) LayerType() gopacket.LayerType { return LayerTypeSTP }

// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (s *STP) CanDecode() gopacket.LayerClass {
	return LayerTypeSTP
}

// DecodeFromBytes decodes the given bytes into this layer.
func (stp *STP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
	stpLength := 35
	if len(data) < stpLength {
		df.SetTruncated()
		return fmt.Errorf("STP length %d too short", len(data))
	}

	stp.ProtocolID = binary.BigEndian.Uint16(data[:2])
	stp.Version = uint8(data[2])
	stp.Type = uint8(data[3])
	stp.TC = data[4]&0x01 != 0
	stp.TCA = data[4]&0x80 != 0
	stp.RouteID.Priority = STPPriority(binary.BigEndian.Uint16(data[5:7]) & 0xf000)
	stp.RouteID.SysID = binary.BigEndian.Uint16(data[5:7]) & 0x0fff
	stp.RouteID.HwAddr = net.HardwareAddr(data[7:13])
	stp.Cost = binary.BigEndian.Uint32(data[13:17])
	stp.BridgeID.Priority = STPPriority(binary.BigEndian.Uint16(data[17:19]) & 0xf000)
	stp.BridgeID.SysID = binary.BigEndian.Uint16(data[17:19]) & 0x0fff
	stp.BridgeID.HwAddr = net.HardwareAddr(data[19:25])
	stp.PortID = binary.BigEndian.Uint16(data[25:27])
	stp.MessageAge = binary.BigEndian.Uint16(data[27:29])
	stp.MaxAge = binary.BigEndian.Uint16(data[29:31])
	stp.HelloTime = binary.BigEndian.Uint16(data[31:33])
	stp.FDelay = binary.BigEndian.Uint16(data[33:35])
	stp.Contents = data[:stpLength]
	stp.Payload = data[stpLength:]

	return nil
}

// NextLayerType returns the layer type contained by this DecodingLayer.
func (stp *STP) NextLayerType() gopacket.LayerType {
	return gopacket.LayerTypePayload
}

// Check if the priority value is correct.
func checkPriority(prio STPPriority) (uint16, error) {
	switch prio {
	case STPPriorityMax:
		return uint16(prio), nil
	case STPPriorityHigh:
		return uint16(prio), nil
	case STPPriorityLow:
		return uint16(prio), nil
	case STPPriorityMin:
		return uint16(prio), nil
	default:
		return uint16(prio), errors.New("Invalid Priority value must be one of the following:\n32768\n16384\n8192\n4096\n")
	}
}

// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (s *STP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
	var flags uint8 = 0x00
	bytes, err := b.PrependBytes(35)
	if err != nil {
		return err
	}
	binary.BigEndian.PutUint16(bytes, s.ProtocolID)
	bytes[2] = s.Version
	bytes[3] = s.Type
	if s.TC {
		flags |= 0x01
	}
	if s.TCA {
		flags |= 0x80
	}
	bytes[4] = flags

	prioRoot, err := checkPriority(s.RouteID.Priority)
	if err != nil {
		panic(err)
	}
	if s.RouteID.SysID >= 4096 {
		panic("Invalid VlanID value ..!")
	}
	binary.BigEndian.PutUint16(bytes[5:7], prioRoot|s.RouteID.SysID)
	copy(bytes[7:13], s.RouteID.HwAddr)

	binary.BigEndian.PutUint32(bytes[13:17], s.Cost)

	prioBridge, err := checkPriority(s.BridgeID.Priority)
	if err != nil {
		panic(err)
	}
	if s.BridgeID.SysID >= 4096 {
		panic("Invalid VlanID value ..!")
	}
	binary.BigEndian.PutUint16(bytes[17:19], prioBridge|s.BridgeID.SysID)
	copy(bytes[19:25], s.BridgeID.HwAddr)

	binary.BigEndian.PutUint16(bytes[25:27], s.PortID)
	binary.BigEndian.PutUint16(bytes[27:29], s.MessageAge)
	binary.BigEndian.PutUint16(bytes[29:31], s.MaxAge)
	binary.BigEndian.PutUint16(bytes[31:33], s.HelloTime)
	binary.BigEndian.PutUint16(bytes[33:35], s.FDelay)

	return nil
}

func decodeSTP(data []byte, p gopacket.PacketBuilder) error {
	stp := &STP{}
	return decodingLayerDecoder(stp, data, p)
}
<commit_msg>fix the Bridge priority bugs<commit_after>// Copyright 2017 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package layers

import (
	"encoding/binary"
	"errors"
	"fmt"
	"net"

	"github.com/google/gopacket"
)

type STPSwitchID struct {
	Priority uint16 // Bridge priority
	SysID    uint16 // VLAN ID
	HwAddr   net.HardwareAddr
}

// STP decode spanning tree protocol packets to transport BPDU (bridge protocol data unit) message.
type STP struct {
	BaseLayer
	ProtocolID        uint16
	Version           uint8
	Type              uint8
	TC, TCA           bool // TC: Topologie change ; TCA: Topologie change ack
	RouteID, BridgeID STPSwitchID
	Cost              uint32
	PortID            uint16
	MessageAge        uint16
	MaxAge            uint16
	HelloTime         uint16
	FDelay            uint16
}

// LayerType returns gopacket.LayerTypeSTP.
func (s *STP) LayerType() gopacket.LayerType { return LayerTypeSTP }

// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (s *STP) CanDecode() gopacket.LayerClass {
	return LayerTypeSTP
}

// DecodeFromBytes decodes the given bytes into this layer.
func (stp *STP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
	stpLength := 35
	if len(data) < stpLength {
		df.SetTruncated()
		return fmt.Errorf("STP length %d too short", len(data))
	}

	stp.ProtocolID = binary.BigEndian.Uint16(data[:2])
	stp.Version = uint8(data[2])
	stp.Type = uint8(data[3])
	stp.TC = data[4]&0x01 != 0
	stp.TCA = data[4]&0x80 != 0
	stp.RouteID.Priority = binary.BigEndian.Uint16(data[5:7]) & 0xf000
	stp.RouteID.SysID = binary.BigEndian.Uint16(data[5:7]) & 0x0fff
	stp.RouteID.HwAddr = net.HardwareAddr(data[7:13])
	stp.Cost = binary.BigEndian.Uint32(data[13:17])
	stp.BridgeID.Priority = binary.BigEndian.Uint16(data[17:19]) & 0xf000
	stp.BridgeID.SysID = binary.BigEndian.Uint16(data[17:19]) & 0x0fff
	stp.BridgeID.HwAddr = net.HardwareAddr(data[19:25])
	stp.PortID = binary.BigEndian.Uint16(data[25:27])
	stp.MessageAge = binary.BigEndian.Uint16(data[27:29])
	stp.MaxAge = binary.BigEndian.Uint16(data[29:31])
	stp.HelloTime = binary.BigEndian.Uint16(data[31:33])
	stp.FDelay = binary.BigEndian.Uint16(data[33:35])
	stp.Contents = data[:stpLength]
	stp.Payload = data[stpLength:]

	return nil
}

// NextLayerType returns the layer type contained by this DecodingLayer.
func (stp *STP) NextLayerType() gopacket.LayerType {
	return gopacket.LayerTypePayload
}

// Check if the priority value is correct.
func checkPriority(prio uint16) (uint16, error) {
	if prio == 0 {
		return prio, errors.New("Invalid Priority value must be in the rage <4096-61440> with an increment of 4096")
	}
	if prio%4096 == 0 {
		return prio, nil
	} else {
		return prio, errors.New("Invalid Priority value must be in the rage <4096-61440> with an increment of 4096")
	}
}

// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (s *STP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
	var flags uint8 = 0x00
	bytes, err := b.PrependBytes(35)
	if err != nil {
		return err
	}
	binary.BigEndian.PutUint16(bytes, s.ProtocolID)
	bytes[2] = s.Version
	bytes[3] = s.Type
	if s.TC {
		flags |= 0x01
	}
	if s.TCA {
		flags |= 0x80
	}
	bytes[4] = flags

	prioRoot, err := checkPriority(s.RouteID.Priority)
	if err != nil {
		panic(err)
	}
	if s.RouteID.SysID >= 4096 {
		panic("Invalid VlanID value ..!")
	}
	binary.BigEndian.PutUint16(bytes[5:7], prioRoot|s.RouteID.SysID)
	copy(bytes[7:13], s.RouteID.HwAddr)

	binary.BigEndian.PutUint32(bytes[13:17], s.Cost)

	prioBridge, err := checkPriority(s.BridgeID.Priority)
	if err != nil {
		panic(err)
	}
	if s.BridgeID.SysID >= 4096 {
		panic("Invalid VlanID value ..!")
	}
	binary.BigEndian.PutUint16(bytes[17:19], prioBridge|s.BridgeID.SysID)
	copy(bytes[19:25], s.BridgeID.HwAddr)

	binary.BigEndian.PutUint16(bytes[25:27], s.PortID)
	binary.BigEndian.PutUint16(bytes[27:29], s.MessageAge)
	binary.BigEndian.PutUint16(bytes[29:31], s.MaxAge)
	binary.BigEndian.PutUint16(bytes[31:33], s.HelloTime)
	binary.BigEndian.PutUint16(bytes[33:35], s.FDelay)

	return nil
}

func decodeSTP(data []byte, p gopacket.PacketBuilder) error {
	stp := &STP{}
	return decodingLayerDecoder(stp, data, p)
}
<|endoftext|>


<commit_before>package mark

import (
	fmt "github.com/k0kubun/pp"
	"strings"
	"testing"
)

var tokenNames = map[itemType]string{
	-1: "itemEOF",
	0:  "itemError",
	1:  "itemNewLine",
	2:  "itemHTML",
	3:  "itemText",
	4:  "itemLineBreak",
	5:  "itemHeading",
	6:  "itemLHeading",
	7:  "itemBlockQuote",
	8:  "itemList",
	9:  "itemCodeBlock",
	10: "itemGfmCodeBlock",
	11: "itemHr",
	12: "itemTable",
	13: "itemLpTable",
	14: "itemLink",
	15: "itemAutoLink",
	16: "itemGfmLink",
	17: "itemStrong",
	18: "itemItalic",
	19: "itemStrike",
	20: "itemCode",
	21: "itemImage",
	22: "itemBr",
	23: "itemPipe",
	24: "itemIndent",
}

func printRound(i int) {
	sep := strings.Repeat("#", 15)
	fmt.Printf("\n\n%s Round %d %s\n\n", sep, i, sep)
}

func lTestBasic(t *testing.T) {
	l := lex("1", `
asdasdsa

Id  |	Name  |  Age
----|---------|-----
 1  | Ariel   |  26
 2  | Erez	  |  29

asdas
`)

	// for item := range l.items {
	// 	fmt.Printf(tokenNames[item.typ] + " ---> '" + item.val + "'" + "\n")
	// }
	tr := &Tree{lex: l}
	tr.parse()
	// fmt.Println(tr.Nodes)
	tr.render()
	fmt.Printf(tr.output)
}

func TestList(t *testing.T) {
	printRound(1)
	// Test round 1
	// TODO(Ariel): BUG!!!!
	src := `
- a
   b
  c

  d
  e
`
	l := lex("1", src)
	// fmt.Printf("Source:\n" + src + "\n")
	// for item := range l.items {
	// 	fmt.Printf(tokenNames[item.typ]+" ---> "+item.val+", length: %s\n", len(item.val))
	// }
	tr := &Tree{lex: l, links: map[string]*DefLinkNode{}}
	tr.parse()
	// fmt.Println(tr.Nodes)
	tr.render()
	fmt.Printf(tr.output + "\n")
}
<commit_msg>test(lexer): start lexing test<commit_after>package mark

import (
	"fmt"
	"testing"
)

var itemName = map[itemType]string{
	eof:              "EOF",
	itemError:        "Error",
	itemNewLine:      "NewLine",
	itemHTML:         "HTML",
	itemHeading:      "Heading",
	itemLHeading:     "LHeading",
	itemBlockQuote:   "BlockQuote",
	itemList:         "List",
	itemListItem:     "ListItem",
	itemLooseItem:    "LooseItem",
	itemCodeBlock:    "CodeBlock",
	itemGfmCodeBlock: "GfmCodeBlock",
	itemHr:           "Hr",
	itemTable:        "Table",
	itemLpTable:      "LpTable",
	itemText:         "Text",
	itemLink:         "Link",
	itemDefLink:      "DefLink",
	itemRefLink:      "RefLink",
	itemAutoLink:     "AutoLink",
	itemGfmLink:      "GfmLink",
	itemStrong:       "Strong",
	itemItalic:       "Italic",
	itemStrike:       "Strike",
	itemCode:         "Code",
	itemImage:        "Image",
	itemRefImage:     "RefImage",
	itemBr:           "Br",
	itemPipe:         "Pipe",
	itemIndent:       "Indent",
}

func (i itemType) String() string {
	s := itemName[i]
	if s == "" {
		return fmt.Sprintf("item%d", int(i))
	}
	return s
}

type lexTest struct {
	name  string
	input string
	items []item
}

var lexTests = []lexTest{
	{"empty", "", []item{
		{eof, 0, ""},
	}},
	{"heading", "# Hello", []item{
		{itemHeading, 0, "# Hello"},
		{eof, 0, ""},
	}},
}

// collect gathers the emitted items into a slice.
func collect(t *lexTest) (items []item) {
	l := lex(t.input)
	for {
		item := l.nextItem()
		items = append(items, item)
		if item.typ == eof || item.typ == itemError {
			break
		}
	}
	return
}

func equal(i1, i2 []item, checkPos bool) bool {
	if len(i1) != len(i2) {
		return false
	}
	for k := range i1 {
		if i1[k].typ != i2[k].typ {
			return false
		}
		if i1[k].val != i2[k].val {
			return false
		}
		if checkPos && i1[k].pos != i2[k].pos {
			return false
		}
	}
	return true
}

func TestLex(t *testing.T) {
	for _, test := range lexTests {
		items := collect(&test)
		if !equal(items, test.items, false) {
			t.Errorf("%s: got\n\t%+v\nexpected\n\t%+v", test.name, items, test.items)
		}
	}
}
<|endoftext|>


<commit_before>package lfs

import (
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/github/git-lfs/git"
)

type Configuration struct {
	CurrentRemote         string
	gitConfig             map[string]string
	remotes               []string
	httpClient            *http.Client
	redirectingHttpClient *http.Client
	isTracingHttp         bool
	loading               sync.Mutex
}

type Endpoint struct {
	Url            string
	SshUserAndHost string
	SshPath        string
}

var (
	Config        = NewConfig()
	httpPrefixRe  = regexp.MustCompile("\\Ahttps?://")
	defaultRemote = "origin"
)

func NewConfig() *Configuration {
	c := &Configuration{
		CurrentRemote: defaultRemote,
		isTracingHttp: len(os.Getenv("GIT_CURL_VERBOSE")) > 0,
	}
	return c
}

func (c *Configuration) Endpoint() Endpoint {
	if url, ok := c.GitConfig("lfs.url"); ok {
		return Endpoint{Url: url}
	}

	if len(c.CurrentRemote) > 0 && c.CurrentRemote != defaultRemote {
		if endpoint := c.RemoteEndpoint(c.CurrentRemote); len(endpoint.Url) > 0 {
			return endpoint
		}
	}

	return c.RemoteEndpoint(defaultRemote)
}

func (c *Configuration) ConcurrentTransfers() int {
	uploads := 3

	if v, ok := c.GitConfig("lfs.concurrenttransfers"); ok {
		n, err := strconv.Atoi(v)
		if err == nil && n > 0 {
			uploads = n
		}
	}

	return uploads
}

func (c *Configuration) BatchTransfer() bool {
	if v, ok := c.GitConfig("lfs.batch"); ok {
		if v == "true" || v == "" {
			return true
		}

		// Any numeric value except 0 is considered true
		if n, err := strconv.Atoi(v); err == nil && n != 0 {
			return true
		}
	}
	return false
}

func (c *Configuration) RemoteEndpoint(remote string) Endpoint {
	if len(remote) == 0 {
		remote = defaultRemote
	}

	if url, ok := c.GitConfig("remote." + remote + ".lfsurl"); ok {
		return Endpoint{Url: url}
	}

	if url, ok := c.GitConfig("remote." + remote + ".url"); ok {
		return remoteEndpointFromUrl(url)
	}

	return Endpoint{}
}

func remoteEndpointFromUrl(url string) Endpoint {
	e := Endpoint{Url: url}

	if !httpPrefixRe.MatchString(url) {
		pieces := strings.SplitN(url, ":", 2)
		hostPieces := strings.SplitN(pieces[0], "@", 2)
		if len(hostPieces) < 2 {
			e.Url = "<unknown>"
			return e
		}

		e.SshUserAndHost = pieces[0]
		e.SshPath = pieces[1]
		e.Url = fmt.Sprintf("https://%s/%s", hostPieces[1], pieces[1])
	}

	if path.Ext(url) == ".git" {
		e.Url += "/info/lfs"
	} else {
		e.Url += ".git/info/lfs"
	}

	return e
}

func (c *Configuration) Remotes() []string {
	c.loadGitConfig()
	return c.remotes
}

func (c *Configuration) GitConfig(key string) (string, bool) {
	c.loadGitConfig()
	value, ok := c.gitConfig[strings.ToLower(key)]
	return value, ok
}

func (c *Configuration) SetConfig(key, value string) {
	c.loadGitConfig()
	c.gitConfig[key] = value
}

func (c *Configuration) ObjectUrl(oid string) (*url.URL, error) {
	return ObjectUrl(c.Endpoint(), oid)
}

func ObjectUrl(endpoint Endpoint, oid string) (*url.URL, error) {
	u, err := url.Parse(endpoint.Url)
	if err != nil {
		return nil, err
	}

	u.Path = path.Join(u.Path, "objects")
	if len(oid) > 0 {
		u.Path = path.Join(u.Path, oid)
	}
	return u, nil
}

func (c *Configuration) loadGitConfig() {
	c.loading.Lock()
	defer c.loading.Unlock()

	if c.gitConfig != nil {
		return
	}

	uniqRemotes := make(map[string]bool)

	c.gitConfig = make(map[string]string)

	var output string
	listOutput, err := git.Config.List()
	if err != nil {
		panic(fmt.Errorf("Error listing git config: %s", err))
	}

	configFile := filepath.Join(LocalWorkingDir, ".gitconfig")
	fileOutput, err := git.Config.ListFromFile(configFile)
	if err != nil {
		panic(fmt.Errorf("Error listing git config from file: %s", err))
	}

	output = fileOutput + "\n" + listOutput

	lines := strings.Split(output, "\n")
	for _, line := range lines {
		pieces := strings.SplitN(line, "=", 2)
		if len(pieces) < 2 {
			continue
		}
		key := strings.ToLower(pieces[0])
		c.gitConfig[key] = pieces[1]

		keyParts := strings.Split(key, ".")
		if len(keyParts) > 1 && keyParts[0] == "remote" {
			remote := keyParts[1]
			uniqRemotes[remote] = remote == "origin"
		}
	}

	c.remotes = make([]string, 0, len(uniqRemotes))
	for remote, isOrigin := range uniqRemotes {
		if isOrigin {
			continue
		}
		c.remotes = append(c.remotes, remote)
	}
}
<commit_msg>Followed convention of grouping mutex with guarded stuff<commit_after>package lfs

import (
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/github/git-lfs/git"
)

type Configuration struct {
	CurrentRemote         string
	httpClient            *http.Client
	redirectingHttpClient *http.Client
	isTracingHttp         bool

	loading   sync.Mutex // guards initialization of gitConfig and remotes
	gitConfig map[string]string
	remotes   []string
}

type Endpoint struct {
	Url            string
	SshUserAndHost string
	SshPath        string
}

var (
	Config        = NewConfig()
	httpPrefixRe  = regexp.MustCompile("\\Ahttps?://")
	defaultRemote = "origin"
)

func NewConfig() *Configuration {
	c := &Configuration{
		CurrentRemote: defaultRemote,
		isTracingHttp: len(os.Getenv("GIT_CURL_VERBOSE")) > 0,
	}
	return c
}

func (c *Configuration) Endpoint() Endpoint {
	if url, ok := c.GitConfig("lfs.url"); ok {
		return Endpoint{Url: url}
	}

	if len(c.CurrentRemote) > 0 && c.CurrentRemote != defaultRemote {
		if endpoint := c.RemoteEndpoint(c.CurrentRemote); len(endpoint.Url) > 0 {
			return endpoint
		}
	}

	return c.RemoteEndpoint(defaultRemote)
}

func (c *Configuration) ConcurrentTransfers() int {
	uploads := 3

	if v, ok := c.GitConfig("lfs.concurrenttransfers"); ok {
		n, err := strconv.Atoi(v)
		if err == nil && n > 0 {
			uploads = n
		}
	}

	return uploads
}

func (c *Configuration) BatchTransfer() bool {
	if v, ok := c.GitConfig("lfs.batch"); ok {
		if v == "true" || v == "" {
			return true
		}

		// Any numeric value except 0 is considered true
		if n, err := strconv.Atoi(v); err == nil && n != 0 {
			return true
		}
	}
	return false
}

func (c *Configuration) RemoteEndpoint(remote string) Endpoint {
	if len(remote) == 0 {
		remote = defaultRemote
	}

	if url, ok := c.GitConfig("remote." + remote + ".lfsurl"); ok {
		return Endpoint{Url: url}
	}

	if url, ok := c.GitConfig("remote." + remote + ".url"); ok {
		return remoteEndpointFromUrl(url)
	}

	return Endpoint{}
}

func remoteEndpointFromUrl(url string) Endpoint {
	e := Endpoint{Url: url}

	if !httpPrefixRe.MatchString(url) {
		pieces := strings.SplitN(url, ":", 2)
		hostPieces := strings.SplitN(pieces[0], "@", 2)
		if len(hostPieces) < 2 {
			e.Url = "<unknown>"
			return e
		}

		e.SshUserAndHost = pieces[0]
		e.SshPath = pieces[1]
		e.Url = fmt.Sprintf("https://%s/%s", hostPieces[1], pieces[1])
	}

	if path.Ext(url) == ".git" {
		e.Url += "/info/lfs"
	} else {
		e.Url += ".git/info/lfs"
	}

	return e
}

func (c *Configuration) Remotes() []string {
	c.loadGitConfig()
	return c.remotes
}

func (c *Configuration) GitConfig(key string) (string, bool) {
	c.loadGitConfig()
	value, ok := c.gitConfig[strings.ToLower(key)]
	return value, ok
}

func (c *Configuration) SetConfig(key, value string) {
	c.loadGitConfig()
	c.gitConfig[key] = value
}

func (c *Configuration) ObjectUrl(oid string) (*url.URL, error) {
	return ObjectUrl(c.Endpoint(), oid)
}

func ObjectUrl(endpoint Endpoint, oid string) (*url.URL, error) {
	u, err := url.Parse(endpoint.Url)
	if err != nil {
		return nil, err
	}

	u.Path = path.Join(u.Path, "objects")
	if len(oid) > 0 {
		u.Path = path.Join(u.Path, oid)
	}
	return u, nil
}

func (c *Configuration) loadGitConfig() {
	c.loading.Lock()
	defer c.loading.Unlock()

	if c.gitConfig != nil {
		return
	}

	uniqRemotes := make(map[string]bool)

	c.gitConfig = make(map[string]string)

	var output string
	listOutput, err := git.Config.List()
	if err != nil {
		panic(fmt.Errorf("Error listing git config: %s", err))
	}

	configFile := filepath.Join(LocalWorkingDir, ".gitconfig")
	fileOutput, err := git.Config.ListFromFile(configFile)
	if err != nil {
		panic(fmt.Errorf("Error listing git config from file: %s", err))
	}

	output = fileOutput + "\n" + listOutput

	lines := strings.Split(output, "\n")
	for _, line := range lines {
		pieces := strings.SplitN(line, "=", 2)
		if len(pieces) < 2 {
			continue
		}
		key := strings.ToLower(pieces[0])
		c.gitConfig[key] = pieces[1]

		keyParts := strings.Split(key, ".")
		if len(keyParts) > 1 && keyParts[0] == "remote" {
			remote := keyParts[1]
			uniqRemotes[remote] = remote == "origin"
		}
	}

	c.remotes = make([]string, 0, len(uniqRemotes))
	for remote, isOrigin := range uniqRemotes {
		if isOrigin {
			continue
		}
		c.remotes = append(c.remotes, remote)
	}
}
<|endoftext|>


<commit_before>package lfs

import (
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"

	"github.com/github/git-lfs/git"
	"github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx"
)

var (
	Config        = NewConfig()
	defaultRemote = "origin"
)

// FetchPruneConfig collects together the config options that control fetching and pruning
type FetchPruneConfig struct {
	// The number of days prior to current date for which (local) refs other than HEAD
	// will be fetched with --recent (default 7, 0 = only fetch HEAD)
	FetchRecentRefsDays int
	// Makes the FetchRecentRefsDays option apply to all remote refs as well (default false)
	FetchRecentRefsIncludeRemotes bool
	// number of days prior to latest commit on a ref that we'll fetch previous
	// LFS changes too (default 3, 0 = only fetch at ref)
	FetchRecentCommitsDays int
	// Whether to always fetch recent even without --recent
	FetchRecentAlways bool
	// Number of days added to FetchRecent*; data outside combined window will be
	// deleted when prune is run. (default 3)
	PruneOffsetDays int
}

type Configuration struct {
	CurrentRemote         string
	httpClient            *HttpClient
	redirectingHttpClient *http.Client
	envVars               map[string]string
	isTracingHttp         bool
	isLoggingStats        bool

	loading           sync.Mutex // guards initialization of gitConfig and remotes
	gitConfig         map[string]string
	remotes           []string
	extensions        map[string]Extension
	fetchIncludePaths []string
	fetchExcludePaths []string
	fetchPruneConfig  *FetchPruneConfig
}

func NewConfig() *Configuration {
	c := &Configuration{
		CurrentRemote: defaultRemote,
		envVars:       make(map[string]string),
	}
	c.isTracingHttp = c.GetenvBool("GIT_CURL_VERBOSE", false)
	c.isLoggingStats = c.GetenvBool("GIT_LOG_STATS", false)
	return c
}

func (c *Configuration) Getenv(key string) string {
	if i, ok := c.envVars[key]; ok {
		return i
	}

	v := os.Getenv(key)
	c.envVars[key] = v
	return v
}

func (c *Configuration) Setenv(key, value string) error {
	// Check see if we have this in our cache, if so update it
	if _, ok := c.envVars[key]; ok {
		c.envVars[key] = value
	}

	// Now set in process
	return os.Setenv(key, value)
}

// GetenvBool parses a boolean environment variable and returns the result as a bool.
// If the environment variable is unset, empty, or if the parsing fails,
// the value of def (default) is returned instead.
func (c *Configuration) GetenvBool(key string, def bool) bool {
	s := c.Getenv(key)
	if len(s) == 0 {
		return def
	}

	b, err := strconv.ParseBool(s)
	if err != nil {
		return def
	}
	return b
}

func (c *Configuration) Endpoint() Endpoint {
	if url, ok := c.GitConfig("lfs.url"); ok {
		return NewEndpoint(url)
	}

	if len(c.CurrentRemote) > 0 && c.CurrentRemote != defaultRemote {
		if endpoint := c.RemoteEndpoint(c.CurrentRemote); len(endpoint.Url) > 0 {
			return endpoint
		}
	}

	return c.RemoteEndpoint(defaultRemote)
}

func (c *Configuration) ConcurrentTransfers() int {
	uploads := 3

	if v, ok := c.GitConfig("lfs.concurrenttransfers"); ok {
		n, err := strconv.Atoi(v)
		if err == nil && n > 0 {
			uploads = n
		}
	}

	return uploads
}

func (c *Configuration) BatchTransfer() bool {
	if v, ok := c.GitConfig("lfs.batch"); ok {
		if v == "true" || v == "" {
			return true
		}

		// Any numeric value except 0 is considered true
		if n, err := strconv.Atoi(v); err == nil && n != 0 {
			return true
		}
	}
	return false
}

// PrivateAccess will retrieve the access value and return true if
// the value is set to private. When a repo is marked as having private
// access, the http requests for the batch api will fetch the credentials
// before running, otherwise the request will run without credentials.
func (c *Configuration) PrivateAccess() bool {
	key := fmt.Sprintf("lfs.%s.access", c.Endpoint().Url)
	if v, ok := c.GitConfig(key); ok {
		if strings.ToLower(v) == "private" {
			return true
		}
	}
	return false
}

// SetPrivateAccess will set the private access flag in .git/config.
func (c *Configuration) SetPrivateAccess() {
	tracerx.Printf("setting repository access to private")
	key := fmt.Sprintf("lfs.%s.access", c.Endpoint().Url)
	configFile := filepath.Join(LocalGitDir, "config")
	git.Config.SetLocal(configFile, key, "private")

	// Modify the config cache because it's checked again in this process
	// without being reloaded.
	c.loading.Lock()
	c.gitConfig[key] = "private"
	c.loading.Unlock()
}

func (c *Configuration) FetchIncludePaths() []string {
	c.loadGitConfig()
	return c.fetchIncludePaths
}
func (c *Configuration) FetchExcludePaths() []string {
	c.loadGitConfig()
	return c.fetchExcludePaths
}

func (c *Configuration) RemoteEndpoint(remote string) Endpoint {
	if len(remote) == 0 {
		remote = defaultRemote
	}
	if url, ok := c.GitConfig("remote." + remote + ".lfsurl"); ok {
		return NewEndpoint(url)
	}

	if url, ok := c.GitConfig("remote." + remote + ".url"); ok {
		return NewEndpointFromCloneURL(url)
	}

	return Endpoint{}
}

func (c *Configuration) Remotes() []string {
	c.loadGitConfig()
	return c.remotes
}

func (c *Configuration) Extensions() map[string]Extension {
	c.loadGitConfig()
	return c.extensions
}

func (c *Configuration) GitConfig(key string) (string, bool) {
	c.loadGitConfig()
	value, ok := c.gitConfig[strings.ToLower(key)]
	return value, ok
}

func (c *Configuration) SetConfig(key, value string) {
	c.loadGitConfig()
	c.gitConfig[key] = value
}

func (c *Configuration) ObjectUrl(oid string) (*url.URL, error) {
	return ObjectUrl(c.Endpoint(), oid)
}

func (c *Configuration) FetchPruneConfig() *FetchPruneConfig {
	if c.fetchPruneConfig == nil {
		c.fetchPruneConfig = &FetchPruneConfig{
			FetchRecentRefsDays:           7,
			FetchRecentRefsIncludeRemotes: false,
			FetchRecentCommitsDays:        3,
			PruneOffsetDays:               3,
		}
		if v, ok := c.GitConfig("lfs.fetchrecentrefsdays"); ok {
			n, err := strconv.Atoi(v)
			if err == nil && n > 0 {
				c.fetchPruneConfig.FetchRecentRefsDays = n
			}
		}
		if v, ok := c.GitConfig("lfs.fetchrecentrefsincluderemotes"); ok {

			if v == "true" || v == "" {
				c.fetchPruneConfig.FetchRecentRefsIncludeRemotes = true
			}

			// Any numeric value except 0 is considered true
			if n, err := strconv.Atoi(v); err == nil && n != 0 {
				c.fetchPruneConfig.FetchRecentRefsIncludeRemotes = true
			}
		}
		if v, ok := c.GitConfig("lfs.fetchrecentcommitsdays"); ok {
			n, err := strconv.Atoi(v)
			if err == nil && n > 0 {
				c.fetchPruneConfig.FetchRecentCommitsDays = n
			}
		}
		if v, ok := c.GitConfig("lfs.fetchrecentalways"); ok {
			if v == "true" || v == "" {
				c.fetchPruneConfig.FetchRecentAlways = true
			}
		}
		if v, ok := c.GitConfig("lfs.pruneoffsetdays"); ok {
			n, err := strconv.Atoi(v)
			if err == nil && n > 0 {
				c.fetchPruneConfig.PruneOffsetDays = n
			}
		}

	}
	return c.fetchPruneConfig
}

func (c *Configuration) loadGitConfig() {
	c.loading.Lock()
	defer c.loading.Unlock()

	if c.gitConfig != nil {
		return
	}

	uniqRemotes := make(map[string]bool)

	c.gitConfig = make(map[string]string)
	c.extensions = make(map[string]Extension)

	var output string
	listOutput, err := git.Config.List()
	if err != nil {
		panic(fmt.Errorf("Error listing git config: %s", err))
	}

	configFile := filepath.Join(LocalWorkingDir, ".gitconfig")
	fileOutput, err := git.Config.ListFromFile(configFile)
	if err != nil {
		panic(fmt.Errorf("Error listing git config from file: %s", err))
	}

	localConfig := filepath.Join(LocalGitDir, "config")
	localOutput, err := git.Config.ListFromFile(localConfig)
	if err != nil {
		panic(fmt.Errorf("Error listing git config from file %s", err))
	}

	output = fileOutput + "\n" + listOutput + "\n" + localOutput

	lines := strings.Split(output, "\n")
	for _, line := range lines {
		pieces := strings.SplitN(line, "=", 2)
		if len(pieces) < 2 {
			continue
		}
		key := strings.ToLower(pieces[0])
		value := pieces[1]
		c.gitConfig[key] = value

		keyParts := strings.Split(key, ".")
		if len(keyParts) > 1 && keyParts[0] == "remote" {
			remote := keyParts[1]
			uniqRemotes[remote] = remote == "origin"
		} else if len(keyParts) == 4 && keyParts[0] == "lfs" && keyParts[1] == "extension" {
			name := keyParts[2]
			ext := c.extensions[name]
			switch keyParts[3] {
			case "clean":
				ext.Clean = value
			case "smudge":
				ext.Smudge = value
			case "priority":
				p, err := strconv.Atoi(value)
				if err == nil && p >= 0 {
					ext.Priority = p
				}
			}
			ext.Name = name
			c.extensions[name] = ext
		} else if len(keyParts) == 2 && keyParts[0] == "lfs" && keyParts[1] == "fetchinclude" {
			for _, inc := range strings.Split(value, ",") {
				inc = strings.TrimSpace(inc)
				c.fetchIncludePaths = append(c.fetchIncludePaths, inc)
			}
		} else if len(keyParts) == 2 && keyParts[0] == "lfs" && keyParts[1] == "fetchexclude" {
			for _, ex := range strings.Split(value, ",") {
				ex = strings.TrimSpace(ex)
				c.fetchExcludePaths = append(c.fetchExcludePaths, ex)
			}
		}
	}

	c.remotes = make([]string, 0, len(uniqRemotes))
	for remote, isOrigin := range uniqRemotes {
		if isOrigin {
			continue
		}
		c.remotes = append(c.remotes, remote)
	}
}
<commit_msg>Rename lfs.fetchrecentrefsincluderemotes to lfs.fetchrecentremoterefs<commit_after>package lfs

import (
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"

	"github.com/github/git-lfs/git"
	"github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx"
)

var (
	Config        = NewConfig()
	defaultRemote = "origin"
)

// FetchPruneConfig collects together the config options that control fetching and pruning
type FetchPruneConfig struct {
	// The number of days prior to current date for which (local) refs other than HEAD
	// will be fetched with --recent (default 7, 0 = only fetch HEAD)
	FetchRecentRefsDays int
	// Makes the FetchRecentRefsDays option apply to all remote refs as well (default false)
	FetchRecentRefsIncludeRemotes bool
	// number of days prior to latest commit on a ref that we'll fetch previous
	// LFS changes too (default 3, 0 = only fetch at ref)
	FetchRecentCommitsDays int
	// Whether to always fetch recent even without --recent
	FetchRecentAlways bool
	// Number of days added to FetchRecent*; data outside combined window will be
	// deleted when prune is run. (default 3)
	PruneOffsetDays int
}

type Configuration struct {
	CurrentRemote         string
	httpClient            *HttpClient
	redirectingHttpClient *http.Client
	envVars               map[string]string
	isTracingHttp         bool
	isLoggingStats        bool

	loading           sync.Mutex // guards initialization of gitConfig and remotes
	gitConfig         map[string]string
	remotes           []string
	extensions        map[string]Extension
	fetchIncludePaths []string
	fetchExcludePaths []string
	fetchPruneConfig  *FetchPruneConfig
}

func NewConfig() *Configuration {
	c := &Configuration{
		CurrentRemote: defaultRemote,
		envVars:       make(map[string]string),
	}
	c.isTracingHttp = c.GetenvBool("GIT_CURL_VERBOSE", false)
	c.isLoggingStats = c.GetenvBool("GIT_LOG_STATS", false)
	return c
}

func (c *Configuration) Getenv(key string) string {
	if i, ok := c.envVars[key]; ok {
		return i
	}

	v := os.Getenv(key)
	c.envVars[key] = v
	return v
}

func (c *Configuration) Setenv(key, value string) error {
	// Check see if we have this in our cache, if so update it
	if _, ok := c.envVars[key]; ok {
		c.envVars[key] = value
	}

	// Now set in process
	return os.Setenv(key, value)
}

// GetenvBool parses a boolean environment variable and returns the result as a bool.
// If the environment variable is unset, empty, or if the parsing fails,
// the value of def (default) is returned instead.
func (c *Configuration) GetenvBool(key string, def bool) bool {
	s := c.Getenv(key)
	if len(s) == 0 {
		return def
	}

	b, err := strconv.ParseBool(s)
	if err != nil {
		return def
	}
	return b
}

func (c *Configuration) Endpoint() Endpoint {
	if url, ok := c.GitConfig("lfs.url"); ok {
		return NewEndpoint(url)
	}

	if len(c.CurrentRemote) > 0 && c.CurrentRemote != defaultRemote {
		if endpoint := c.RemoteEndpoint(c.CurrentRemote); len(endpoint.Url) > 0 {
			return endpoint
		}
	}

	return c.RemoteEndpoint(defaultRemote)
}

func (c *Configuration) ConcurrentTransfers() int {
	uploads := 3

	if v, ok := c.GitConfig("lfs.concurrenttransfers"); ok {
		n, err := strconv.Atoi(v)
		if err == nil && n > 0 {
			uploads = n
		}
	}

	return uploads
}

func (c *Configuration) BatchTransfer() bool {
	if v, ok := c.GitConfig("lfs.batch"); ok {
		if v == "true" || v == "" {
			return true
		}

		// Any numeric value except 0 is considered true
		if n, err := strconv.Atoi(v); err == nil && n != 0 {
			return true
		}
	}
	return false
}

// PrivateAccess will retrieve the access value and return true if
// the value is set to private. When a repo is marked as having private
// access, the http requests for the batch api will fetch the credentials
// before running, otherwise the request will run without credentials.
func (c *Configuration) PrivateAccess() bool {
	key := fmt.Sprintf("lfs.%s.access", c.Endpoint().Url)
	if v, ok := c.GitConfig(key); ok {
		if strings.ToLower(v) == "private" {
			return true
		}
	}
	return false
}

// SetPrivateAccess will set the private access flag in .git/config.
func (c *Configuration) SetPrivateAccess() {
	tracerx.Printf("setting repository access to private")
	key := fmt.Sprintf("lfs.%s.access", c.Endpoint().Url)
	configFile := filepath.Join(LocalGitDir, "config")
	git.Config.SetLocal(configFile, key, "private")

	// Modify the config cache because it's checked again in this process
	// without being reloaded.
	c.loading.Lock()
	c.gitConfig[key] = "private"
	c.loading.Unlock()
}

func (c *Configuration) FetchIncludePaths() []string {
	c.loadGitConfig()
	return c.fetchIncludePaths
}
func (c *Configuration) FetchExcludePaths() []string {
	c.loadGitConfig()
	return c.fetchExcludePaths
}

func (c *Configuration) RemoteEndpoint(remote string) Endpoint {
	if len(remote) == 0 {
		remote = defaultRemote
	}
	if url, ok := c.GitConfig("remote." + remote + ".lfsurl"); ok {
		return NewEndpoint(url)
	}

	if url, ok := c.GitConfig("remote." + remote + ".url"); ok {
		return NewEndpointFromCloneURL(url)
	}

	return Endpoint{}
}

func (c *Configuration) Remotes() []string {
	c.loadGitConfig()
	return c.remotes
}

func (c *Configuration) Extensions() map[string]Extension {
	c.loadGitConfig()
	return c.extensions
}

func (c *Configuration) GitConfig(key string) (string, bool) {
	c.loadGitConfig()
	value, ok := c.gitConfig[strings.ToLower(key)]
	return value, ok
}

func (c *Configuration) SetConfig(key, value string) {
	c.loadGitConfig()
	c.gitConfig[key] = value
}

func (c *Configuration) ObjectUrl(oid string) (*url.URL, error) {
	return ObjectUrl(c.Endpoint(), oid)
}

func (c *Configuration) FetchPruneConfig() *FetchPruneConfig {
	if c.fetchPruneConfig == nil {
		c.fetchPruneConfig = &FetchPruneConfig{
			FetchRecentRefsDays:           7,
			FetchRecentRefsIncludeRemotes: false,
			FetchRecentCommitsDays:        3,
			PruneOffsetDays:               3,
		}
		if v, ok := c.GitConfig("lfs.fetchrecentrefsdays"); ok {
			n, err := strconv.Atoi(v)
			if err == nil && n > 0 {
				c.fetchPruneConfig.FetchRecentRefsDays = n
			}
		}
		if v, ok := c.GitConfig("lfs.fetchrecentremoterefs"); ok {

			if v == "true" || v == "" {
				c.fetchPruneConfig.FetchRecentRefsIncludeRemotes = true
			}

			// Any numeric value except 0 is considered true
			if n, err := strconv.Atoi(v); err == nil && n != 0 {
				c.fetchPruneConfig.FetchRecentRefsIncludeRemotes = true
			}
		}
		if v, ok := c.GitConfig("lfs.fetchrecentcommitsdays"); ok {
			n, err := strconv.Atoi(v)
			if err == nil && n > 0 {
				c.fetchPruneConfig.FetchRecentCommitsDays = n
			}
		}
		if v, ok := c.GitConfig("lfs.fetchrecentalways"); ok {
			if v == "true" || v == "" {
				c.fetchPruneConfig.FetchRecentAlways = true
			}
		}
		if v, ok := c.GitConfig("lfs.pruneoffsetdays"); ok {
			n, err := strconv.Atoi(v)
			if err == nil && n > 0 {
				c.fetchPruneConfig.PruneOffsetDays = n
			}
		}

	}
	return c.fetchPruneConfig
}

func (c *Configuration) loadGitConfig() {
	c.loading.Lock()
	defer c.loading.Unlock()

	if c.gitConfig != nil {
		return
	}

	uniqRemotes := make(map[string]bool)

	c.gitConfig = make(map[string]string)
	c.extensions = make(map[string]Extension)

	var output string
	listOutput, err := git.Config.List()
	if err != nil {
		panic(fmt.Errorf("Error listing git config: %s", err))
	}

	configFile := filepath.Join(LocalWorkingDir, ".gitconfig")
	fileOutput, err := git.Config.ListFromFile(configFile)
	if err != nil {
		panic(fmt.Errorf("Error listing git config from file: %s", err))
	}

	localConfig := filepath.Join(LocalGitDir, "config")
	localOutput, err := git.Config.ListFromFile(localConfig)
	if err != nil {
		panic(fmt.Errorf("Error listing git config from file %s", err))
	}

	output = fileOutput + "\n" + listOutput + "\n" + localOutput

	lines := strings.Split(output, "\n")
	for _, line := range lines {
		pieces := strings.SplitN(line, "=", 2)
		if len(pieces) < 2 {
			continue
		}
		key := strings.ToLower(pieces[0])
		value := pieces[1]
		c.gitConfig[key] = value

		keyParts := strings.Split(key, ".")
		if len(keyParts) > 1 && keyParts[0] == "remote" {
			remote := keyParts[1]
			uniqRemotes[remote] = remote == "origin"
		} else if len(keyParts) == 4 && keyParts[0] == "lfs" && keyParts[1] == "extension" {
			name := keyParts[2]
			ext := c.extensions[name]
			switch keyParts[3] {
			case "clean":
				ext.Clean = value
			case "smudge":
				ext.Smudge = value
			case "priority":
				p, err := strconv.Atoi(value)
				if err == nil && p >= 0 {
					ext.Priority = p
				}
			}
			ext.Name = name
			c.extensions[name] = ext
		} else if len(keyParts) == 2 && keyParts[0] == "lfs" && keyParts[1] == "fetchinclude" {
			for _, inc := range strings.Split(value, ",") {
				inc = strings.TrimSpace(inc)
				c.fetchIncludePaths = append(c.fetchIncludePaths, inc)
			}
		} else if len(keyParts) == 2 && keyParts[0] == "lfs" && keyParts[1] == "fetchexclude" {
			for _, ex := range strings.Split(value, ",") {
				ex = strings.TrimSpace(ex)
				c.fetchExcludePaths = append(c.fetchExcludePaths, ex)
			}
		}
	}

	c.remotes = make([]string, 0, len(uniqRemotes))
	for remote, isOrigin := range uniqRemotes {
		if isOrigin {
			continue
		}
		c.remotes = append(c.remotes, remote)
	}
}
<|endoftext|>


<commit_before>package commands

import (
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/git-lfs/git-lfs/lfs"
	"github.com/git-lfs/git-lfs/localstorage"
	"github.com/git-lfs/git-lfs/subprocess"

	"github.com/git-lfs/git-lfs/git"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/spf13/cobra"
)

var (
	cloneFlags git.CloneFlags

	cloneSkipRepoInstall bool
)

func cloneCommand(cmd *cobra.Command, args []string) {
	requireGitVersion()

	// We pass all args to git clone
	err := git.CloneWithoutFilters(cloneFlags, args)
	if err != nil {
		Exit("Error(s) during clone:\n%v", err)
	}

	// now execute pull (need to be inside dir)
	cwd, err := tools.Getwd()
	if err != nil {
		Exit("Unable to derive current working dir: %v", err)
	}

	// Either the last argument was a relative or local dir, or we have to
	// derive it from the clone URL
	clonedir, err := filepath.Abs(args[len(args)-1])
	if err != nil || !tools.DirExists(clonedir) {
		// Derive from clone URL instead
		base := path.Base(args[len(args)-1])
		if strings.HasSuffix(base, ".git") {
			base = base[:len(base)-4]
		}
		clonedir, _ = filepath.Abs(base)
		if !tools.DirExists(clonedir) {
			Exit("Unable to find clone dir at %q", clonedir)
		}
	}

	err = os.Chdir(clonedir)
	if err != nil {
		Exit("Unable to change directory to clone dir %q: %v", clonedir, err)
	}

	// Make sure we pop back to dir we started in at the end
	defer os.Chdir(cwd)

	// Also need to derive dirs now
	localstorage.ResolveDirs()
	requireInRepo()

	// Now just call pull with default args
	// Support --origin option to clone
	var remote string
	if len(cloneFlags.Origin) > 0 {
		remote = cloneFlags.Origin
	} else {
		remote = "origin"
	}

	includeArg, excludeArg := getIncludeExcludeArgs(cmd)
	filter := buildFilepathFilter(cfg, includeArg, excludeArg)
	if cloneFlags.NoCheckout || cloneFlags.Bare {
		// If --no-checkout or --bare then we shouldn't check out, just fetch instead
		cfg.CurrentRemote = remote
		fetchRef("HEAD", filter)
	} else {
		pull(remote, filter)
		err := postCloneSubmodules(args)
		if err != nil {
			Exit("Error performing 'git lfs pull' for submodules: %v", err)
		}
	}

	if !cloneSkipRepoInstall {
		// If --skip-repo wasn't given, install repo-level hooks while
		// we're still in the checkout directory.

		if err := lfs.InstallHooks(false); err != nil {
			ExitWithError(err)
		}
	}
}

func postCloneSubmodules(args []string) error {
	// In git 2.9+ the filter option will have been passed through to submodules
	// So we need to lfs pull inside each
	if !git.Config.IsGitVersionAtLeast("2.9.0") {
		// In earlier versions submodules would have used smudge filter
		return nil
	}
	// Also we only do this if --recursive or --recurse-submodules was provided
	if !cloneFlags.Recursive && !cloneFlags.RecurseSubmodules {
		return nil
	}

	// Use `git submodule foreach --recursive` to cascade into nested submodules
	// Also good to call a new instance of git-lfs rather than do things
	// inside this instance, since that way we get a clean env in that subrepo
	cmd := subprocess.ExecCommand("git", "submodule", "foreach", "--recursive",
		"git lfs pull")
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

func init() {
	RegisterCommand("clone", cloneCommand, func(cmd *cobra.Command) {
		cmd.PreRun = nil

		// Mirror all git clone flags
		cmd.Flags().StringVarP(&cloneFlags.TemplateDirectory, "template", "", "", "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Local, "local", "l", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Shared, "shared", "s", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.NoHardlinks, "no-hardlinks", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Quiet, "quiet", "q", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.NoCheckout, "no-checkout", "n", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Progress, "progress", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Bare, "bare", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Mirror, "mirror", "", false, "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.Origin, "origin", "o", "", "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.Branch, "branch", "b", "", "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.Upload, "upload-pack", "u", "", "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.Reference, "reference", "", "", "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.ReferenceIfAble, "reference-if-able", "", "", "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Dissociate, "dissociate", "", false, "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.SeparateGit, "separate-git-dir", "", "", "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.Depth, "depth", "", "", "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Recursive, "recursive", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.RecurseSubmodules, "recurse-submodules", "", false, "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.Config, "config", "c", "", "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.SingleBranch, "single-branch", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.NoSingleBranch, "no-single-branch", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Verbose, "verbose", "v", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Ipv4, "ipv4", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Ipv6, "ipv6", "", false, "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.ShallowSince, "shallow-since", "", "", "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.ShallowExclude, "shallow-exclude", "", "", "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.ShallowSubmodules, "shallow-submodules", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.NoShallowSubmodules, "no-shallow-submodules", "", false, "See 'git clone --help'")
		cmd.Flags().Int64VarP(&cloneFlags.Jobs, "jobs", "j", -1, "See 'git clone --help'")

		cmd.Flags().StringVarP(&includeArg, "include", "I", "", "Include a list of paths")
		cmd.Flags().StringVarP(&excludeArg, "exclude", "X", "", "Exclude a list of paths")

		cmd.Flags().BoolVar(&cloneSkipRepoInstall, "skip-repo", false, "Skip LFS repo setup")
	})
}
<commit_msg>commands: mark 'git lfs clone' as deprecated<commit_after>package commands

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/git-lfs/git-lfs/lfs"
	"github.com/git-lfs/git-lfs/localstorage"
	"github.com/git-lfs/git-lfs/subprocess"

	"github.com/git-lfs/git-lfs/git"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/spf13/cobra"
)

var (
	cloneFlags git.CloneFlags

	cloneSkipRepoInstall bool

	// cloneIsDeprecated marks whether or not the clone command is
	// deprecated. It is false until Git v2.15.0 is released, including the
	// 'delay' capability.
	cloneIsDeprecated = false
)

func cloneCommand(cmd *cobra.Command, args []string) {
	requireGitVersion()

	if cloneIsDeprecated {
		msg := []string{
			"WARNING: 'git-lfs(1) clone' is deprecated and will not be updated",
			"          with new flags from 'git clone'",
			"",
			"'git-clone(1)' has been updated in upstream Git to have comparable",
			"speeds to 'git-lfs(1) clone'.",
		}

		fmt.Fprintln(os.Stderr, strings.Join(msg, "\n")+"\n")
	}

	// We pass all args to git clone
	err := git.CloneWithoutFilters(cloneFlags, args)
	if err != nil {
		Exit("Error(s) during clone:\n%v", err)
	}

	// now execute pull (need to be inside dir)
	cwd, err := tools.Getwd()
	if err != nil {
		Exit("Unable to derive current working dir: %v", err)
	}

	// Either the last argument was a relative or local dir, or we have to
	// derive it from the clone URL
	clonedir, err := filepath.Abs(args[len(args)-1])
	if err != nil || !tools.DirExists(clonedir) {
		// Derive from clone URL instead
		base := path.Base(args[len(args)-1])
		if strings.HasSuffix(base, ".git") {
			base = base[:len(base)-4]
		}
		clonedir, _ = filepath.Abs(base)
		if !tools.DirExists(clonedir) {
			Exit("Unable to find clone dir at %q", clonedir)
		}
	}

	err = os.Chdir(clonedir)
	if err != nil {
		Exit("Unable to change directory to clone dir %q: %v", clonedir, err)
	}

	// Make sure we pop back to dir we started in at the end
	defer os.Chdir(cwd)

	// Also need to derive dirs now
	localstorage.ResolveDirs()
	requireInRepo()

	// Now just call pull with default args
	// Support --origin option to clone
	var remote string
	if len(cloneFlags.Origin) > 0 {
		remote = cloneFlags.Origin
	} else {
		remote = "origin"
	}

	includeArg, excludeArg := getIncludeExcludeArgs(cmd)
	filter := buildFilepathFilter(cfg, includeArg, excludeArg)
	if cloneFlags.NoCheckout || cloneFlags.Bare {
		// If --no-checkout or --bare then we shouldn't check out, just fetch instead
		cfg.CurrentRemote = remote
		fetchRef("HEAD", filter)
	} else {
		pull(remote, filter)
		err := postCloneSubmodules(args)
		if err != nil {
			Exit("Error performing 'git lfs pull' for submodules: %v", err)
		}
	}

	if !cloneSkipRepoInstall {
		// If --skip-repo wasn't given, install repo-level hooks while
		// we're still in the checkout directory.

		if err := lfs.InstallHooks(false); err != nil {
			ExitWithError(err)
		}
	}
}

func postCloneSubmodules(args []string) error {
	// In git 2.9+ the filter option will have been passed through to submodules
	// So we need to lfs pull inside each
	if !git.Config.IsGitVersionAtLeast("2.9.0") {
		// In earlier versions submodules would have used smudge filter
		return nil
	}
	// Also we only do this if --recursive or --recurse-submodules was provided
	if !cloneFlags.Recursive && !cloneFlags.RecurseSubmodules {
		return nil
	}

	// Use `git submodule foreach --recursive` to cascade into nested submodules
	// Also good to call a new instance of git-lfs rather than do things
	// inside this instance, since that way we get a clean env in that subrepo
	cmd := subprocess.ExecCommand("git", "submodule", "foreach", "--recursive",
		"git lfs pull")
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

func init() {
	RegisterCommand("clone", cloneCommand, func(cmd *cobra.Command) {
		cmd.PreRun = nil

		// Mirror all git clone flags
		cmd.Flags().StringVarP(&cloneFlags.TemplateDirectory, "template", "", "", "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Local, "local", "l", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Shared, "shared", "s", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.NoHardlinks, "no-hardlinks", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Quiet, "quiet", "q", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.NoCheckout, "no-checkout", "n", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Progress, "progress", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Bare, "bare", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Mirror, "mirror", "", false, "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.Origin, "origin", "o", "", "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.Branch, "branch", "b", "", "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.Upload, "upload-pack", "u", "", "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.Reference, "reference", "", "", "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.ReferenceIfAble, "reference-if-able", "", "", "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Dissociate, "dissociate", "", false, "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.SeparateGit, "separate-git-dir", "", "", "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.Depth, "depth", "", "", "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Recursive, "recursive", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.RecurseSubmodules, "recurse-submodules", "", false, "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.Config, "config", "c", "", "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.SingleBranch, "single-branch", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.NoSingleBranch, "no-single-branch", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Verbose, "verbose", "v", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Ipv4, "ipv4", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.Ipv6, "ipv6", "", false, "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.ShallowSince, "shallow-since", "", "", "See 'git clone --help'")
		cmd.Flags().StringVarP(&cloneFlags.ShallowExclude, "shallow-exclude", "", "", "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.ShallowSubmodules, "shallow-submodules", "", false, "See 'git clone --help'")
		cmd.Flags().BoolVarP(&cloneFlags.NoShallowSubmodules, "no-shallow-submodules", "", false, "See 'git clone --help'")
		cmd.Flags().Int64VarP(&cloneFlags.Jobs, "jobs", "j", -1, "See 'git clone --help'")

		cmd.Flags().StringVarP(&includeArg, "include", "I", "", "Include a list of paths")
		cmd.Flags().StringVarP(&excludeArg, "exclude", "X", "", "Exclude a list of paths")

		cmd.Flags().BoolVar(&cloneSkipRepoInstall, "skip-repo", false, "Skip LFS repo setup")
	})
}
<|endoftext|>


<commit_before>package commands

import (
	"encoding/json"
	"os"

	"github.com/spf13/cobra"
)

var (
	locksCmdFlags = new(locksFlags)
)

func locksCommand(cmd *cobra.Command, args []string) {
	filters, err := locksCmdFlags.Filters()
	if err != nil {
		Exit("Error building filters: %v", err)
	}

	lockClient := newLockClient(lockRemote)
	defer lockClient.Close()

	locks, err := lockClient.SearchLocks(filters, locksCmdFlags.Limit, locksCmdFlags.Local)
	// Print any we got before exiting

	if locksCmdFlags.JSON {
		if err := json.NewEncoder(os.Stdout).Encode(locks); err != nil {
			Error(err.Error())
		}
		return
	}

	for _, lock := range locks {
		Print("%s\t%s", lock.Path, lock.Owner)
	}

	if err != nil {
		Exit("Error while retrieving locks: %v", err)
	}
}

// locksFlags wraps up and holds all of the flags that can be given to the
// `git lfs locks` command.
type locksFlags struct {
	// Path is an optional filter parameter to filter against the lock's
	// path
	Path string
	// Id is an optional filter parameter used to filtere against the lock's
	// ID.
	Id string
	// limit is an optional request parameter sent to the server used to
	// limit the
	Limit int
	// local limits the scope of lock reporting to the locally cached record
	// of locks for the current user & doesn't query the server
	Local bool
	// JSON is an optional parameter to output data in json format.
	JSON bool
}

// Filters produces a filter based on locksFlags instance.
func (l *locksFlags) Filters() (map[string]string, error) {
	filters := make(map[string]string)

	if l.Path != "" {
		path, err := lockPath(l.Path)
		if err != nil {
			return nil, err
		}

		filters["path"] = path
	}
	if l.Id != "" {
		filters["id"] = l.Id
	}

	return filters, nil
}

func init() {
	RegisterCommand("locks", locksCommand, func(cmd *cobra.Command) {
		cmd.Flags().StringVarP(&lockRemote, "remote", "r", cfg.CurrentRemote, lockRemoteHelp)
		cmd.Flags().StringVarP(&locksCmdFlags.Path, "path", "p", "", "filter locks results matching a particular path")
		cmd.Flags().StringVarP(&locksCmdFlags.Id, "id", "i", "", "filter locks results matching a particular ID")
		cmd.Flags().IntVarP(&locksCmdFlags.Limit, "limit", "l", 0, "optional limit for number of results to return")
		cmd.Flags().BoolVarP(&locksCmdFlags.Local, "local", "", false, "only list cached local record of own locks")
		cmd.Flags().BoolVarP(&locksCmdFlags.JSON, "json", "", false, "print output in json")
	})
}
<commit_msg>line up 'locks' output columns<commit_after>package commands

import (
	"encoding/json"
	"os"
	"sort"
	"strings"

	"github.com/git-lfs/git-lfs/locking"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/spf13/cobra"
)

var (
	locksCmdFlags = new(locksFlags)
)

func locksCommand(cmd *cobra.Command, args []string) {
	filters, err := locksCmdFlags.Filters()
	if err != nil {
		Exit("Error building filters: %v", err)
	}

	lockClient := newLockClient(lockRemote)
	defer lockClient.Close()

	locks, err := lockClient.SearchLocks(filters, locksCmdFlags.Limit, locksCmdFlags.Local)
	// Print any we got before exiting

	if locksCmdFlags.JSON {
		if err := json.NewEncoder(os.Stdout).Encode(locks); err != nil {
			Error(err.Error())
		}
		return
	}

	var maxlen int
	lockPaths := make([]string, 0, len(locks))
	locksByPath := make(map[string]locking.Lock)
	for _, lock := range locks {
		lockPaths = append(lockPaths, lock.Path)
		locksByPath[lock.Path] = lock
		maxlen = tools.MaxInt(maxlen, len(lock.Path))
	}

	sort.Strings(lockPaths)
	for _, lockPath := range lockPaths {
		lock := locksByPath[lockPath]
		padding := tools.MaxInt(maxlen-len(lock.Path), 0)
		Print("%s%s\t%s", lock.Path, strings.Repeat(" ", padding), lock.Owner)
	}

	if err != nil {
		Exit("Error while retrieving locks: %v", err)
	}
}

// locksFlags wraps up and holds all of the flags that can be given to the
// `git lfs locks` command.
type locksFlags struct {
	// Path is an optional filter parameter to filter against the lock's
	// path
	Path string
	// Id is an optional filter parameter used to filtere against the lock's
	// ID.
	Id string
	// limit is an optional request parameter sent to the server used to
	// limit the
	Limit int
	// local limits the scope of lock reporting to the locally cached record
	// of locks for the current user & doesn't query the server
	Local bool
	// JSON is an optional parameter to output data in json format.
	JSON bool
}

// Filters produces a filter based on locksFlags instance.
func (l *locksFlags) Filters() (map[string]string, error) {
	filters := make(map[string]string)

	if l.Path != "" {
		path, err := lockPath(l.Path)
		if err != nil {
			return nil, err
		}

		filters["path"] = path
	}
	if l.Id != "" {
		filters["id"] = l.Id
	}

	return filters, nil
}

func init() {
	RegisterCommand("locks", locksCommand, func(cmd *cobra.Command) {
		cmd.Flags().StringVarP(&lockRemote, "remote", "r", cfg.CurrentRemote, lockRemoteHelp)
		cmd.Flags().StringVarP(&locksCmdFlags.Path, "path", "p", "", "filter locks results matching a particular path")
		cmd.Flags().StringVarP(&locksCmdFlags.Id, "id", "i", "", "filter locks results matching a particular ID")
		cmd.Flags().IntVarP(&locksCmdFlags.Limit, "limit", "l", 0, "optional limit for number of results to return")
		cmd.Flags().BoolVarP(&locksCmdFlags.Local, "local", "", false, "only list cached local record of own locks")
		cmd.Flags().BoolVarP(&locksCmdFlags.JSON, "json", "", false, "print output in json")
	})
}
<|endoftext|>


<commit_before>package google

import (
	"github.com/0x263b/Porygon2"
	"github.com/0x263b/Porygon2/web"
)

func google(command *bot.Cmd, matches []string) (msg string, err error) {
	return "This command is deprecated. https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=deprecated", nil
}

func init() {
	bot.RegisterCommand(
		"^g(?:oogle)? (.+)$",
		google)
}
<commit_msg>cleaning up<commit_after>package google

import (
	"github.com/0x263b/Porygon2"
)

func google(command *bot.Cmd, matches []string) (msg string, err error) {
	return "This command is deprecated. https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=deprecated", nil
}

func init() {
	bot.RegisterCommand(
		"^g(?:oogle)? (.+)$",
		google)
}
<|endoftext|>


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

// This file was auto-generated by the vanadium vdl tool.
// Source: chat.vdl

package vdl

import (
	// VDL system imports
	"v.io/v23"
	"v.io/v23/context"
	"v.io/v23/rpc"
)

// ChatClientMethods is the client interface
// containing Chat methods.
type ChatClientMethods interface {
	// SendMessage sends a message to a user.
	SendMessage(ctx *context.T, text string, opts ...rpc.CallOpt) error
}

// ChatClientStub adds universal methods to ChatClientMethods.
type ChatClientStub interface {
	ChatClientMethods
	rpc.UniversalServiceMethods
}

// ChatClient returns a client stub for Chat.
func ChatClient(name string, opts ...rpc.BindOpt) ChatClientStub {
	var client rpc.Client
	for _, opt := range opts {
		if clientOpt, ok := opt.(rpc.Client); ok {
			client = clientOpt
		}
	}
	return implChatClientStub{name, client}
}

type implChatClientStub struct {
	name   string
	client rpc.Client
}

func (c implChatClientStub) c(ctx *context.T) rpc.Client {
	if c.client != nil {
		return c.client
	}
	return v23.GetClient(ctx)
}

func (c implChatClientStub) SendMessage(ctx *context.T, i0 string, opts ...rpc.CallOpt) (err error) {
	var call rpc.ClientCall
	if call, err = c.c(ctx).StartCall(ctx, c.name, "SendMessage", []interface{}{i0}, opts...); err != nil {
		return
	}
	err = call.Finish()
	return
}

// ChatServerMethods is the interface a server writer
// implements for Chat.
type ChatServerMethods interface {
	// SendMessage sends a message to a user.
	SendMessage(call rpc.ServerCall, text string) error
}

// ChatServerStubMethods is the server interface containing
// Chat methods, as expected by rpc.Server.
// There is no difference between this interface and ChatServerMethods
// since there are no streaming methods.
type ChatServerStubMethods ChatServerMethods

// ChatServerStub adds universal methods to ChatServerStubMethods.
type ChatServerStub interface {
	ChatServerStubMethods
	// Describe the Chat interfaces.
	Describe__() []rpc.InterfaceDesc
}

// ChatServer returns a server stub for Chat.
// It converts an implementation of ChatServerMethods into
// an object that may be used by rpc.Server.
func ChatServer(impl ChatServerMethods) ChatServerStub {
	stub := implChatServerStub{
		impl: impl,
	}
	// Initialize GlobState; always check the stub itself first, to handle the
	// case where the user has the Glob method defined in their VDL source.
	if gs := rpc.NewGlobState(stub); gs != nil {
		stub.gs = gs
	} else if gs := rpc.NewGlobState(impl); gs != nil {
		stub.gs = gs
	}
	return stub
}

type implChatServerStub struct {
	impl ChatServerMethods
	gs   *rpc.GlobState
}

func (s implChatServerStub) SendMessage(call rpc.ServerCall, i0 string) error {
	return s.impl.SendMessage(call, i0)
}

func (s implChatServerStub) Globber() *rpc.GlobState {
	return s.gs
}

func (s implChatServerStub) Describe__() []rpc.InterfaceDesc {
	return []rpc.InterfaceDesc{ChatDesc}
}

// ChatDesc describes the Chat interface.
var ChatDesc rpc.InterfaceDesc = descChat

// descChat hides the desc to keep godoc clean.
var descChat = rpc.InterfaceDesc{
	Name:    "Chat",
	PkgPath: "chat/vdl",
	Methods: []rpc.MethodDesc{
		{
			Name: "SendMessage",
			Doc:  "// SendMessage sends a message to a user.",
			InArgs: []rpc.ArgDesc{
				{"text", ``}, // string
			},
		},
	},
}
<commit_msg>vdl: Counterpart of https://vanadium-review.googlesource.com/9136<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file was auto-generated by the vanadium vdl tool.
// Source: chat.vdl

package vdl

import (
	// VDL system imports
	"v.io/v23"
	"v.io/v23/context"
	"v.io/v23/rpc"
)

// ChatClientMethods is the client interface
// containing Chat methods.
type ChatClientMethods interface {
	// SendMessage sends a message to a user.
	SendMessage(ctx *context.T, text string, opts ...rpc.CallOpt) error
}

// ChatClientStub adds universal methods to ChatClientMethods.
type ChatClientStub interface {
	ChatClientMethods
	rpc.UniversalServiceMethods
}

// ChatClient returns a client stub for Chat.
func ChatClient(name string) ChatClientStub {
	return implChatClientStub{name}
}

type implChatClientStub struct {
	name string
}

func (c implChatClientStub) SendMessage(ctx *context.T, i0 string, opts ...rpc.CallOpt) (err error) {
	var call rpc.ClientCall
	if call, err = v23.GetClient(ctx).StartCall(ctx, c.name, "SendMessage", []interface{}{i0}, opts...); err != nil {
		return
	}
	err = call.Finish()
	return
}

// ChatServerMethods is the interface a server writer
// implements for Chat.
type ChatServerMethods interface {
	// SendMessage sends a message to a user.
	SendMessage(call rpc.ServerCall, text string) error
}

// ChatServerStubMethods is the server interface containing
// Chat methods, as expected by rpc.Server.
// There is no difference between this interface and ChatServerMethods
// since there are no streaming methods.
type ChatServerStubMethods ChatServerMethods

// ChatServerStub adds universal methods to ChatServerStubMethods.
type ChatServerStub interface {
	ChatServerStubMethods
	// Describe the Chat interfaces.
	Describe__() []rpc.InterfaceDesc
}

// ChatServer returns a server stub for Chat.
// It converts an implementation of ChatServerMethods into
// an object that may be used by rpc.Server.
func ChatServer(impl ChatServerMethods) ChatServerStub {
	stub := implChatServerStub{
		impl: impl,
	}
	// Initialize GlobState; always check the stub itself first, to handle the
	// case where the user has the Glob method defined in their VDL source.
	if gs := rpc.NewGlobState(stub); gs != nil {
		stub.gs = gs
	} else if gs := rpc.NewGlobState(impl); gs != nil {
		stub.gs = gs
	}
	return stub
}

type implChatServerStub struct {
	impl ChatServerMethods
	gs   *rpc.GlobState
}

func (s implChatServerStub) SendMessage(call rpc.ServerCall, i0 string) error {
	return s.impl.SendMessage(call, i0)
}

func (s implChatServerStub) Globber() *rpc.GlobState {
	return s.gs
}

func (s implChatServerStub) Describe__() []rpc.InterfaceDesc {
	return []rpc.InterfaceDesc{ChatDesc}
}

// ChatDesc describes the Chat interface.
var ChatDesc rpc.InterfaceDesc = descChat

// descChat hides the desc to keep godoc clean.
var descChat = rpc.InterfaceDesc{
	Name:    "Chat",
	PkgPath: "chat/vdl",
	Methods: []rpc.MethodDesc{
		{
			Name: "SendMessage",
			Doc:  "// SendMessage sends a message to a user.",
			InArgs: []rpc.ArgDesc{
				{"text", ``}, // string
			},
		},
	},
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"path/filepath"
)

// constant for directory name
const (
	// name of directory contains build result
	BuildDirName = "blog"
	// name of directory contains user post files
	PostsDirName = "posts"
	// name of post file
	PostFileName = "post.md"
	// name of directory contains template files
	LayoutsDirName = "layouts"
)

func main() {

	dir, _ := os.Getwd()
	cmd := getSubCommand()
	switch cmd {
	case "new":
		NewBlogCreator(dir).Create()
	case "build":
		NewBlogBuilder(filepath.Join(dir, BuildDirName)).Build("")
	case "post":
		NewPostCreator(filepath.Join(dir, PostsDirName)).Create("test")
	case "serve":
		RunFileServer(filepath.Join(dir, BuildDirName))
	default:
		log.Fatalf("unknown command %s", cmd)
	}
}

func getSubCommand() string {
	cmd := ""
	if len(os.Args) > 1 {
		cmd = os.Args[1]
	}
	return cmd
}
<commit_msg>main should now insert parameter properly<commit_after>package main

import (
	"flag"
	"log"
	"os"
	"path/filepath"
	"strings"
)

// constant for directory name
const (
	// name of directory contains build result
	BuildDirName = "blog"
	// name of directory contains user post files
	PostsDirName = "posts"
	// name of post file
	PostFileName = "post.md"
	// name of directory contains template files
	LayoutsDirName = "layouts"
)

func main() {

	dir, _ := os.Getwd()
	cmd := getSubCommand()
	switch cmd {
	case "new":
		NewBlogCreator(dir).Create()
	case "build":
		NewBlogBuilder(dir).Build(filepath.Join(dir, BuildDirName))
	case "post":
		title := parseCreatePostTitle()
		NewPostCreator(filepath.Join(dir, PostsDirName)).Create(title)
	case "serve":
		RunFileServer(filepath.Join(dir, BuildDirName))
	default:
		log.Fatalf("unknown command %s", cmd)
	}
}

func getSubCommand() string {
	cmd := ""
	if len(os.Args) > 1 {
		cmd = os.Args[1]
	}
	return cmd
}

func parseCreatePostTitle() string {
	var title = ""
	flagSet := flag.NewFlagSet("post", flag.ExitOnError)
	flagSet.StringVar(&title, "title", "", "post title")
	flagSet.Parse(os.Args[2:])

	title = strings.TrimSpace(title)
	if title == "" {
		log.Fatalf("post title is required")
	}
	return title
}
<|endoftext|>