<commit_before>// Copyright (c) 2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

// TODO: consistent error wrapping

package dcrd

import (
	"context"
	"encoding/hex"
	"encoding/json"
	"strings"

	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/dcrutil/v2"
	"github.com/decred/dcrd/gcs"
	"github.com/decred/dcrd/wire"
	"github.com/decred/dcrwallet/errors"
	"github.com/jrick/bitset"
	"github.com/jrick/wsrpc/v2"
	"golang.org/x/sync/errgroup"
)

// Caller provides a client interface to perform JSON-RPC remote procedure calls.
type Caller interface {
	// Call performs the remote procedure call defined by method and
	// waits for a response or a broken client connection.
	// Args provides positional parameters for the call.
	// Res must be a pointer to a struct, slice, or map type to unmarshal
	// a result (if any), or nil if no result is needed.
	Call(ctx context.Context, method string, res interface{}, args ...interface{}) error
}

// RPC provides methods for calling dcrd JSON-RPCs without exposing the details
// of JSON encoding.
type RPC struct {
	Caller
}

// New creates a new RPC client instance from a caller.
func New(caller Caller) *RPC {
	return &RPC{caller}
}

func hashSliceToStrings(hashes []*chainhash.Hash) []string {
	s := make([]string, len(hashes))
	for i, h := range hashes {
		s[i] = h.String()
	}
	return s
}

func addrSliceToStrings(addrs []dcrutil.Address) []string {
	s := make([]string, len(addrs))
	for i, a := range addrs {
		s[i] = a.String()
	}
	return s
}

// exists serves as a common entry point for all exists* RPCs which take a
// single JSON parameter (usually an array) and return a hex-encoded bitset.
func exists(ctx context.Context, r *RPC, method string, res *bitset.Bytes, param json.RawMessage) error {
	var bitsetHex string
	err := r.Call(ctx, method, &bitsetHex, param)
	if err != nil {
		errors.E(errors.Op(method), err)
	}
	decoded, err := hex.DecodeString(bitsetHex)
	if err != nil {
		return errors.E(errors.Op(method), errors.Encoding, err)
	}
	*res = decoded
	return nil
}

// ExistsLiveTicket returns whether a ticket identified by its hash is currently
// live and not immature.
func (r *RPC) ExistsLiveTicket(ctx context.Context, ticket *chainhash.Hash) (bool, error) {
	const op errors.Op = "dcrd.ExistsLiveTicket"
	var exists bool
	err := r.Call(ctx, "existsliveticket", &exists, ticket.String())
	if err != nil {
		return false, errors.E(op, err)
	}
	return exists, err
}

// ExistsLiveExpiredTickets returns bitsets identifying whether each ticket
// is currently live or expired.
func (r *RPC) ExistsLiveExpiredTickets(ctx context.Context, tickets []*chainhash.Hash) (live, expired bitset.Bytes, err error) {
	const op errors.Op = "dcrd.ExistsLiveExpiredTickets"
	// Reuse the single json.RawMessage for both calls
	ticketArray, _ := json.Marshal(hashSliceToStrings(tickets))
	errs := make(chan error, 2)
	go func() { errs <- exists(ctx, r, "existslivetickets", &live, ticketArray) }()
	go func() { errs <- exists(ctx, r, "existsexpiredtickets", &expired, ticketArray) }()
	for i := 0; i < cap(errs); i++ {
		if e := <-errs; err == nil && e != nil {
			// Must only return after all exists calls are
			// known to be completed to avoid a data race on the
			// return values.  Set the final error to return, and
			// only return after all errors have been read.
			err = errors.E(op, e)
		}
	}
	return
}

// ExistsExpiredMissedTickets returns bitsets identifying whether each ticket
// is currently expired or missed.
func (r *RPC) ExistsExpiredMissedTickets(ctx context.Context, tickets []*chainhash.Hash) (expired, missed bitset.Bytes, err error) {
	const op errors.Op = "dcrd.ExistsExpiredMissedTickets"
	ticketArray, _ := json.Marshal(hashSliceToStrings(tickets))
	errs := make(chan error, 2)
	go func() { errs <- exists(ctx, r, "existsexpiredtickets", &expired, ticketArray) }()
	go func() { errs <- exists(ctx, r, "existsmissedtickets", &missed, ticketArray) }()
	for i := 0; i < cap(errs); i++ {
		if e := <-errs; err == nil && e != nil {
			err = errors.E(op, e)
		}
	}
	return
}

// UsedAddresses returns a bitset identifying whether each address has been
// publically used on the blockchain.  This feature requires the optional dcrd
// existsaddress index to be enabled.
func (r *RPC) UsedAddresses(ctx context.Context, addrs []dcrutil.Address) (bitset.Bytes, error) {
	const op errors.Op = "dcrd.UsedAddresses"
	addrArray, _ := json.Marshal(addrSliceToStrings(addrs))
	var bits bitset.Bytes
	err := exists(ctx, r, "existsaddresses", &bits, addrArray)
	if err != nil {
		return nil, errors.E(op, err)
	}
	return bits, nil
}

// ExistsLiveTickets returns a bitset identifying whether each ticket is
// currently live.
func (r *RPC) ExistsLiveTickets(ctx context.Context, tickets []*chainhash.Hash) (bitset.Bytes, error) {
	const op errors.Op = "dcrd.ExistsLiveTickets"
	ticketArray, _ := json.Marshal(hashSliceToStrings(tickets))
	var bits bitset.Bytes
	err := exists(ctx, r, "existslivetickets", &bits, ticketArray)
	if err != nil {
		return nil, errors.E(op, err)
	}
	return bits, nil
}

// MempoolCount returns the count of a particular kind of transaction in mempool.
// Kind may be one of:
//   "all"
//   "regular"
//   "tickets"
//   "votes"
//   "revocations"
func (r *RPC) MempoolCount(ctx context.Context, kind string) (int, error) {
	const op errors.Op = "dcrd.MempoolCount"
	// This is rather inefficient, as only the count is needed, not all
	// matching hashes.
	var hashStrings []string
	err := r.Call(ctx, "getrawmempool", &hashStrings, false, kind)
	if err != nil {
		return 0, errors.E(op, err)
	}
	return len(hashStrings), nil
}

// PublishTransaction submits the transaction to dcrd mempool for acceptance.
// If accepted, the transaction is published to other peers.
// The transaction may not be an orphan.
func (r *RPC) PublishTransaction(ctx context.Context, tx *wire.MsgTx) error {
	const op errors.Op = "dcrd.PublishTransaction"
	var b strings.Builder
	b.Grow(tx.SerializeSize() * 2)
	err := tx.Serialize(hex.NewEncoder(&b))
	if err != nil {
		return errors.E(op, errors.Encoding, err)
	}
	err = r.Call(ctx, "sendrawtransaction", nil, b.String())
	if err != nil {
		switch e := err.(type) {
		case *wsrpc.Error:
			// Duplicate txs are not considered an error
			if e.Code == codeDuplicateTx {
				return nil
			}
		}
		return errors.E(op, err)
	}
	return nil
}

// PublishTransactions submits each transaction to dcrd mempool for acceptance.
// If accepted, the transaction is published to other peers.
// Transactions are sent in order and later transactions may spend outputs of
// previous transactions.
// No transaction may be an orphan.
func (r *RPC) PublishTransactions(ctx context.Context, txs ...*wire.MsgTx) error {
	const op errors.Op = "dcrd.PublishTransactions"

	// sendrawtransaction does not allow orphans, so we can not concurrently
	// send transactions.  All transaction sends are attempted, and the
	// first non-nil error is returned.
	var firstErr error
	for _, tx := range txs {
		err := r.PublishTransaction(ctx, tx)
		if err != nil && firstErr == nil {
			firstErr = err
		}
	}
	if firstErr != nil {
		return errors.E(op, firstErr)
	}
	return nil
}

// Blocks returns the blocks for each block hash.
func (r *RPC) Blocks(ctx context.Context, blockHashes []*chainhash.Hash) ([]*wire.MsgBlock, error) {
	const op errors.Op = "dcrd.Blocks"

	blocks := make([]*wire.MsgBlock, len(blockHashes))
	var g errgroup.Group
	for i := range blockHashes {
		i := i
		g.Go(func() error {
			blocks[i] = new(wire.MsgBlock)
			return r.Call(ctx, "getblock", unhex(blocks[i]), blockHashes[i].String(), false)
		})
	}
	err := g.Wait()
	if err != nil {
		return nil, errors.E(op, err)
	}
	return blocks, nil
}

// CFilter returns the committed filter for a block.
func (r *RPC) CFilter(ctx context.Context, blockHash *chainhash.Hash) (*gcs.Filter, error) {
	const opf = "dcrd.CFilter(%v)"

	var f cfilter
	err := r.Call(ctx, "getcfilter", unhex(&f), blockHash.String(), "regular")
	if err != nil {
		op := errors.Opf(opf, blockHash)
		return nil, errors.E(op, err)
	}
	return f.Filter, nil
}

// CFilters returns committed filters for blocks.
func (r *RPC) CFilters(ctx context.Context, blockHashes []*chainhash.Hash) ([]*gcs.Filter, error) {
	const opf = "dcrd.CFilters(%v)"

	// TODO: this is spammy and would be better implemented with a single RPC.
	filters := make([]*gcs.Filter, len(blockHashes))
	var g errgroup.Group
	for i := range blockHashes {
		i := i
		g.Go(func() error {
			var f cfilter
			err := r.Call(ctx, "getcfilter", unhex(&f), blockHashes[i].String(), "regular")
			filters[i] = f.Filter
			if err != nil {
				op := errors.Opf(opf, blockHashes[i])
				err = errors.E(op, err)
			}
			return err
		})
	}
	err := g.Wait()
	if err != nil {
		return nil, err
	}
	return filters, nil
}

// Headers returns the block headers starting at the fork point between the
// client and the dcrd server identified by the client's block locators.
func (r *RPC) Headers(ctx context.Context, blockLocators []*chainhash.Hash, hashStop *chainhash.Hash) ([]*wire.BlockHeader, error) {
	const op errors.Op = "dcrd.Headers"

	res := &struct {
		Headers *headers `json:"headers"`
	}{
		Headers: new(headers),
	}
	err := r.Call(ctx, "getheaders", res, &hashes{blockLocators}, hashStop.String())
	if err != nil {
		return nil, errors.E(op, err)
	}
	return res.Headers.Headers, nil
}

// LoadTxFilter loads or reloads the precise server-side transaction filter used
// for relevant transaction notifications and rescans.
// Addresses and outpoints are added to an existing filter if reload is false.
func (r *RPC) LoadTxFilter(ctx context.Context, reload bool, addrs []dcrutil.Address, outpoints []wire.OutPoint) error {
	const op errors.Op = "dcrd.LoadTxFilter"

	type outpoint struct {
		Hash  string `json:"hash"`
		Index uint32 `json:"index"`
		Tree  int8   `json:"tree"`
	}
	outpointArray := make([]*outpoint, len(outpoints))
	for i, o := range outpoints {
		outpointArray[i] = &outpoint{
			Hash:  o.Hash.String(),
			Index: o.Index,
			Tree:  o.Tree,
		}
	}

	err := r.Call(ctx, "loadtxfilter", nil, reload, addrSliceToStrings(addrs), outpointArray)
	if err != nil {
		return errors.E(op, err)
	}
	return nil
}

// Rescan rescans the specified blocks in order, using the loaded transaction
// filter to determine which transactions are possibly relevant to the client.
// The save function is called for the discovered transactions from each block.
func (r *RPC) Rescan(ctx context.Context, blocks []chainhash.Hash, save func(block *chainhash.Hash, txs []*wire.MsgTx) error) error {
	const op errors.Op = "dcrd.Rescan"

	var res struct {
		DiscoveredData []struct {
			Hash         string   `json:"hash"`
			Transactions []string `json:"transactions"`
		} `json:"discovereddata"`
	}
	err := r.Call(ctx, "rescan", &res, &hashesContiguous{blocks})
	if err != nil {
		return errors.E(op, err)
	}
	for _, d := range res.DiscoveredData {
		blockHash, err := chainhash.NewHashFromStr(d.Hash)
		if err != nil {
			return errors.E(op, errors.Encoding, err)
		}
		txs := make([]*wire.MsgTx, 0, len(d.Transactions))
		for _, txHex := range d.Transactions {
			tx := new(wire.MsgTx)
			err := tx.Deserialize(hex.NewDecoder(strings.NewReader(txHex)))
			if err != nil {
				return errors.E(op, errors.Encoding, err)
			}
			txs = append(txs, tx)
		}
		err = save(blockHash, txs)
		if err != nil {
			return err
		}
	}
	return nil
}

// StakeDifficulty returns the stake difficulty (AKA ticket price) of the next
// block.
func (r *RPC) StakeDifficulty(ctx context.Context) (dcrutil.Amount, error) {
	const op errors.Op = "dcrd.StakeDifficulty"

	var res struct {
		Sdiff float64 `json:"nextstakedifficulty"`
	}
	err := r.Call(ctx, "getstakedifficulty", &res)
	if err != nil {
		return 0, errors.E(op, err)
	}
	sdiff, err := dcrutil.NewAmount(res.Sdiff)
	if err != nil {
		return 0, errors.E(op, err)
	}
	return sdiff, nil
}
<commit_msg>rpc/client: Add missing return after error check<commit_after>// Copyright (c) 2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

// TODO: consistent error wrapping

package dcrd

import (
	"context"
	"encoding/hex"
	"encoding/json"
	"strings"

	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/dcrutil/v2"
	"github.com/decred/dcrd/gcs"
	"github.com/decred/dcrd/wire"
	"github.com/decred/dcrwallet/errors"
	"github.com/jrick/bitset"
	"github.com/jrick/wsrpc/v2"
	"golang.org/x/sync/errgroup"
)

// Caller provides a client interface to perform JSON-RPC remote procedure calls.
type Caller interface {
	// Call performs the remote procedure call defined by method and
	// waits for a response or a broken client connection.
	// Args provides positional parameters for the call.
	// Res must be a pointer to a struct, slice, or map type to unmarshal
	// a result (if any), or nil if no result is needed.
	Call(ctx context.Context, method string, res interface{}, args ...interface{}) error
}

// RPC provides methods for calling dcrd JSON-RPCs without exposing the details
// of JSON encoding.
type RPC struct {
	Caller
}

// New creates a new RPC client instance from a caller.
func New(caller Caller) *RPC {
	return &RPC{caller}
}

func hashSliceToStrings(hashes []*chainhash.Hash) []string {
	s := make([]string, len(hashes))
	for i, h := range hashes {
		s[i] = h.String()
	}
	return s
}

func addrSliceToStrings(addrs []dcrutil.Address) []string {
	s := make([]string, len(addrs))
	for i, a := range addrs {
		s[i] = a.String()
	}
	return s
}

// exists serves as a common entry point for all exists* RPCs which take a
// single JSON parameter (usually an array) and return a hex-encoded bitset.
func exists(ctx context.Context, r *RPC, method string, res *bitset.Bytes, param json.RawMessage) error {
	var bitsetHex string
	err := r.Call(ctx, method, &bitsetHex, param)
	if err != nil {
		return errors.E(errors.Op(method), err)
	}
	decoded, err := hex.DecodeString(bitsetHex)
	if err != nil {
		return errors.E(errors.Op(method), errors.Encoding, err)
	}
	*res = decoded
	return nil
}

// ExistsLiveTicket returns whether a ticket identified by its hash is currently
// live and not immature.
func (r *RPC) ExistsLiveTicket(ctx context.Context, ticket *chainhash.Hash) (bool, error) {
	const op errors.Op = "dcrd.ExistsLiveTicket"
	var exists bool
	err := r.Call(ctx, "existsliveticket", &exists, ticket.String())
	if err != nil {
		return false, errors.E(op, err)
	}
	return exists, err
}

// ExistsLiveExpiredTickets returns bitsets identifying whether each ticket
// is currently live or expired.
func (r *RPC) ExistsLiveExpiredTickets(ctx context.Context, tickets []*chainhash.Hash) (live, expired bitset.Bytes, err error) {
	const op errors.Op = "dcrd.ExistsLiveExpiredTickets"
	// Reuse the single json.RawMessage for both calls
	ticketArray, _ := json.Marshal(hashSliceToStrings(tickets))
	errs := make(chan error, 2)
	go func() { errs <- exists(ctx, r, "existslivetickets", &live, ticketArray) }()
	go func() { errs <- exists(ctx, r, "existsexpiredtickets", &expired, ticketArray) }()
	for i := 0; i < cap(errs); i++ {
		if e := <-errs; err == nil && e != nil {
			// Must only return after all exists calls are
			// known to be completed to avoid a data race on the
			// return values.  Set the final error to return, and
			// only return after all errors have been read.
			err = errors.E(op, e)
		}
	}
	return
}

// ExistsExpiredMissedTickets returns bitsets identifying whether each ticket
// is currently expired or missed.
func (r *RPC) ExistsExpiredMissedTickets(ctx context.Context, tickets []*chainhash.Hash) (expired, missed bitset.Bytes, err error) {
	const op errors.Op = "dcrd.ExistsExpiredMissedTickets"
	ticketArray, _ := json.Marshal(hashSliceToStrings(tickets))
	errs := make(chan error, 2)
	go func() { errs <- exists(ctx, r, "existsexpiredtickets", &expired, ticketArray) }()
	go func() { errs <- exists(ctx, r, "existsmissedtickets", &missed, ticketArray) }()
	for i := 0; i < cap(errs); i++ {
		if e := <-errs; err == nil && e != nil {
			err = errors.E(op, e)
		}
	}
	return
}

// UsedAddresses returns a bitset identifying whether each address has been
// publically used on the blockchain.  This feature requires the optional dcrd
// existsaddress index to be enabled.
func (r *RPC) UsedAddresses(ctx context.Context, addrs []dcrutil.Address) (bitset.Bytes, error) {
	const op errors.Op = "dcrd.UsedAddresses"
	addrArray, _ := json.Marshal(addrSliceToStrings(addrs))
	var bits bitset.Bytes
	err := exists(ctx, r, "existsaddresses", &bits, addrArray)
	if err != nil {
		return nil, errors.E(op, err)
	}
	return bits, nil
}

// ExistsLiveTickets returns a bitset identifying whether each ticket is
// currently live.
func (r *RPC) ExistsLiveTickets(ctx context.Context, tickets []*chainhash.Hash) (bitset.Bytes, error) {
	const op errors.Op = "dcrd.ExistsLiveTickets"
	ticketArray, _ := json.Marshal(hashSliceToStrings(tickets))
	var bits bitset.Bytes
	err := exists(ctx, r, "existslivetickets", &bits, ticketArray)
	if err != nil {
		return nil, errors.E(op, err)
	}
	return bits, nil
}

// MempoolCount returns the count of a particular kind of transaction in mempool.
// Kind may be one of:
//   "all"
//   "regular"
//   "tickets"
//   "votes"
//   "revocations"
func (r *RPC) MempoolCount(ctx context.Context, kind string) (int, error) {
	const op errors.Op = "dcrd.MempoolCount"
	// This is rather inefficient, as only the count is needed, not all
	// matching hashes.
	var hashStrings []string
	err := r.Call(ctx, "getrawmempool", &hashStrings, false, kind)
	if err != nil {
		return 0, errors.E(op, err)
	}
	return len(hashStrings), nil
}

// PublishTransaction submits the transaction to dcrd mempool for acceptance.
// If accepted, the transaction is published to other peers.
// The transaction may not be an orphan.
func (r *RPC) PublishTransaction(ctx context.Context, tx *wire.MsgTx) error {
	const op errors.Op = "dcrd.PublishTransaction"
	var b strings.Builder
	b.Grow(tx.SerializeSize() * 2)
	err := tx.Serialize(hex.NewEncoder(&b))
	if err != nil {
		return errors.E(op, errors.Encoding, err)
	}
	err = r.Call(ctx, "sendrawtransaction", nil, b.String())
	if err != nil {
		switch e := err.(type) {
		case *wsrpc.Error:
			// Duplicate txs are not considered an error
			if e.Code == codeDuplicateTx {
				return nil
			}
		}
		return errors.E(op, err)
	}
	return nil
}

// PublishTransactions submits each transaction to dcrd mempool for acceptance.
// If accepted, the transaction is published to other peers.
// Transactions are sent in order and later transactions may spend outputs of
// previous transactions.
// No transaction may be an orphan.
func (r *RPC) PublishTransactions(ctx context.Context, txs ...*wire.MsgTx) error {
	const op errors.Op = "dcrd.PublishTransactions"

	// sendrawtransaction does not allow orphans, so we can not concurrently
	// send transactions.  All transaction sends are attempted, and the
	// first non-nil error is returned.
	var firstErr error
	for _, tx := range txs {
		err := r.PublishTransaction(ctx, tx)
		if err != nil && firstErr == nil {
			firstErr = err
		}
	}
	if firstErr != nil {
		return errors.E(op, firstErr)
	}
	return nil
}

// Blocks returns the blocks for each block hash.
func (r *RPC) Blocks(ctx context.Context, blockHashes []*chainhash.Hash) ([]*wire.MsgBlock, error) {
	const op errors.Op = "dcrd.Blocks"

	blocks := make([]*wire.MsgBlock, len(blockHashes))
	var g errgroup.Group
	for i := range blockHashes {
		i := i
		g.Go(func() error {
			blocks[i] = new(wire.MsgBlock)
			return r.Call(ctx, "getblock", unhex(blocks[i]), blockHashes[i].String(), false)
		})
	}
	err := g.Wait()
	if err != nil {
		return nil, errors.E(op, err)
	}
	return blocks, nil
}

// CFilter returns the committed filter for a block.
func (r *RPC) CFilter(ctx context.Context, blockHash *chainhash.Hash) (*gcs.Filter, error) {
	const opf = "dcrd.CFilter(%v)"

	var f cfilter
	err := r.Call(ctx, "getcfilter", unhex(&f), blockHash.String(), "regular")
	if err != nil {
		op := errors.Opf(opf, blockHash)
		return nil, errors.E(op, err)
	}
	return f.Filter, nil
}

// CFilters returns committed filters for blocks.
func (r *RPC) CFilters(ctx context.Context, blockHashes []*chainhash.Hash) ([]*gcs.Filter, error) {
	const opf = "dcrd.CFilters(%v)"

	// TODO: this is spammy and would be better implemented with a single RPC.
	filters := make([]*gcs.Filter, len(blockHashes))
	var g errgroup.Group
	for i := range blockHashes {
		i := i
		g.Go(func() error {
			var f cfilter
			err := r.Call(ctx, "getcfilter", unhex(&f), blockHashes[i].String(), "regular")
			filters[i] = f.Filter
			if err != nil {
				op := errors.Opf(opf, blockHashes[i])
				err = errors.E(op, err)
			}
			return err
		})
	}
	err := g.Wait()
	if err != nil {
		return nil, err
	}
	return filters, nil
}

// Headers returns the block headers starting at the fork point between the
// client and the dcrd server identified by the client's block locators.
func (r *RPC) Headers(ctx context.Context, blockLocators []*chainhash.Hash, hashStop *chainhash.Hash) ([]*wire.BlockHeader, error) {
	const op errors.Op = "dcrd.Headers"

	res := &struct {
		Headers *headers `json:"headers"`
	}{
		Headers: new(headers),
	}
	err := r.Call(ctx, "getheaders", res, &hashes{blockLocators}, hashStop.String())
	if err != nil {
		return nil, errors.E(op, err)
	}
	return res.Headers.Headers, nil
}

// LoadTxFilter loads or reloads the precise server-side transaction filter used
// for relevant transaction notifications and rescans.
// Addresses and outpoints are added to an existing filter if reload is false.
func (r *RPC) LoadTxFilter(ctx context.Context, reload bool, addrs []dcrutil.Address, outpoints []wire.OutPoint) error {
	const op errors.Op = "dcrd.LoadTxFilter"

	type outpoint struct {
		Hash  string `json:"hash"`
		Index uint32 `json:"index"`
		Tree  int8   `json:"tree"`
	}
	outpointArray := make([]*outpoint, len(outpoints))
	for i, o := range outpoints {
		outpointArray[i] = &outpoint{
			Hash:  o.Hash.String(),
			Index: o.Index,
			Tree:  o.Tree,
		}
	}

	err := r.Call(ctx, "loadtxfilter", nil, reload, addrSliceToStrings(addrs), outpointArray)
	if err != nil {
		return errors.E(op, err)
	}
	return nil
}

// Rescan rescans the specified blocks in order, using the loaded transaction
// filter to determine which transactions are possibly relevant to the client.
// The save function is called for the discovered transactions from each block.
func (r *RPC) Rescan(ctx context.Context, blocks []chainhash.Hash, save func(block *chainhash.Hash, txs []*wire.MsgTx) error) error {
	const op errors.Op = "dcrd.Rescan"

	var res struct {
		DiscoveredData []struct {
			Hash         string   `json:"hash"`
			Transactions []string `json:"transactions"`
		} `json:"discovereddata"`
	}
	err := r.Call(ctx, "rescan", &res, &hashesContiguous{blocks})
	if err != nil {
		return errors.E(op, err)
	}
	for _, d := range res.DiscoveredData {
		blockHash, err := chainhash.NewHashFromStr(d.Hash)
		if err != nil {
			return errors.E(op, errors.Encoding, err)
		}
		txs := make([]*wire.MsgTx, 0, len(d.Transactions))
		for _, txHex := range d.Transactions {
			tx := new(wire.MsgTx)
			err := tx.Deserialize(hex.NewDecoder(strings.NewReader(txHex)))
			if err != nil {
				return errors.E(op, errors.Encoding, err)
			}
			txs = append(txs, tx)
		}
		err = save(blockHash, txs)
		if err != nil {
			return err
		}
	}
	return nil
}

// StakeDifficulty returns the stake difficulty (AKA ticket price) of the next
// block.
func (r *RPC) StakeDifficulty(ctx context.Context) (dcrutil.Amount, error) {
	const op errors.Op = "dcrd.StakeDifficulty"

	var res struct {
		Sdiff float64 `json:"nextstakedifficulty"`
	}
	err := r.Call(ctx, "getstakedifficulty", &res)
	if err != nil {
		return 0, errors.E(op, err)
	}
	sdiff, err := dcrutil.NewAmount(res.Sdiff)
	if err != nil {
		return 0, errors.E(op, err)
	}
	return sdiff, nil
}
<|endoftext|>


<commit_before>package environment

import (
	"database/sql"
	"encoding/base64"
	"encoding/json"

	"github.com/ovh/cds/engine/api/database"
	"github.com/ovh/cds/engine/api/secret"
	"github.com/ovh/cds/sdk"
)

// CreateAudit Create environment variable audit for the given project
func CreateAudit(db database.QueryExecuter, key string, env *sdk.Environment, u *sdk.User) error {

	vars, err := GetAllVariable(db, key, env.Name, WithEncryptPassword())
	if err != nil {
		return err
	}

	for i := range vars {
		v := &vars[i]
		if sdk.NeedPlaceholder(v.Type) {
			v.Value = base64.StdEncoding.EncodeToString([]byte(v.Value))
		}
	}

	data, err := json.Marshal(vars)
	if err != nil {
		return err
	}

	query := `
		INSERT INTO environment_variable_audit (versionned, environment_id, data, author)
		VALUES (NOW(), $1, $2, $3)
	`
	_, err = db.Exec(query, env.ID, string(data), u.Username)
	return err
}

// GetAudit retrieve the current environment variable audit
func GetAudit(db database.Querier, auditID int64) ([]sdk.Variable, error) {
	query := `
		SELECT environment_variable_audit.data
		FROM environment_variable_audit
		WHERE environment_variable_audit.id = $1
	`
	var data string
	err := db.QueryRow(query, auditID).Scan(&data)
	if err != nil {
		return nil, err
	}
	var vars []sdk.Variable
	err = json.Unmarshal([]byte(data), &vars)
	for i := range vars {
		v := &vars[i]
		if sdk.NeedPlaceholder(v.Type) {
			decode, err := base64.StdEncoding.DecodeString(v.Value)
			if err != nil {
				return nil, err
			}
			v.Value = string(decode)
		}
	}
	return vars, err
}

// GetEnvironmentAudit Get environment audit for the given project
func GetEnvironmentAudit(db database.Querier, key, envName string) ([]sdk.VariableAudit, error) {
	audits := []sdk.VariableAudit{}
	query := `
		SELECT environment_variable_audit.id, environment_variable_audit.versionned, environment_variable_audit.data, environment_variable_audit.author
		FROM environment_variable_audit
		JOIN environment ON environment.id = environment_variable_audit.environment_id
		JOIN project ON project.id = environment.project_id
		WHERE project.projectkey = $1 AND environment.name = $2
		ORDER BY environment_variable_audit.versionned DESC
	`
	rows, err := db.Query(query, key, envName)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		var audit sdk.VariableAudit
		var data string
		err := rows.Scan(&audit.ID, &audit.Versionned, &data, &audit.Author)
		if err != nil {
			return nil, err
		}
		var vars []sdk.Variable
		err = json.Unmarshal([]byte(data), &vars)
		if err != nil {
			return nil, err
		}
		for i := range vars {
			v := &vars[i]
			if sdk.NeedPlaceholder(v.Type) {
				v.Value = sdk.PasswordPlaceholder
			}
		}
		audit.Variables = vars
		audits = append(audits, audit)
	}
	return audits, nil
}

// GetAllVariableNameByProject Get all variable from all environment
func GetAllVariableNameByProject(db database.Querier, key string) ([]string, error) {
	nameArray := []string{}
	query := `
		SELECT distinct(environment_variable.name)
		FROM environment_variable
		JOIN environment on environment.id = environment_variable.environment_id
		JOIN project on project.id = environment.project_id
		WHERE project.projectkey=$1`
	rows, err := db.Query(query, key)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var name string
		err = rows.Scan(&name)
		if err != nil {
			return nil, err
		}
		nameArray = append(nameArray, name)
	}
	return nameArray, nil
}

type structarg struct {
	clearsecret   bool
	encryptsecret bool
}

// GetAllVariableFuncArg defines the base type for functional argument of GetAllVariable
type GetAllVariableFuncArg func(args *structarg)

// WithClearPassword is a function argument to GetAllVariable
func WithClearPassword() GetAllVariableFuncArg {
	return func(args *structarg) {
		args.clearsecret = true
	}
}

// WithEncryptPassword is a function argument to GetAllVariable
func WithEncryptPassword() GetAllVariableFuncArg {
	return func(args *structarg) {
		args.encryptsecret = true
	}
}

// GetAllVariable Get all variable for the given environment
func GetAllVariable(db database.Querier, key, envName string, args ...GetAllVariableFuncArg) ([]sdk.Variable, error) {
	c := structarg{}
	for _, f := range args {
		f(&c)
	}

	variables := []sdk.Variable{}
	query := `SELECT environment_variable.id, environment_variable.name, environment_variable.value,
						environment_variable.cipher_value, environment_variable.type
	          FROM environment_variable
	          JOIN environment ON environment.id = environment_variable.environment_id
	          JOIN project ON project.id = environment.project_id
	          WHERE environment.name = $1 AND project.projectKey = $2
	          ORDER BY name`
	rows, err := db.Query(query, envName, key)
	if err != nil {
		return variables, err
	}
	defer rows.Close()
	for rows.Next() {
		var v sdk.Variable
		var typeVar string
		var clearVal sql.NullString
		var cipherVal []byte
		err = rows.Scan(&v.ID, &v.Name, &clearVal, &cipherVal, &typeVar)
		if err != nil {
			return nil, err
		}
		v.Type = sdk.VariableTypeFromString(typeVar)

		if c.encryptsecret && sdk.NeedPlaceholder(v.Type) {
			v.Value = string(cipherVal)
		} else {
			v.Value, err = secret.DecryptS(v.Type, clearVal, cipherVal, c.clearsecret)
			if err != nil {
				return nil, err
			}
		}
		variables = append(variables, v)
	}
	return variables, err
}

// GetAllVariableByID Get all variable for the given environment
func GetAllVariableByID(db database.Querier, environmentID int64, args ...GetAllVariableFuncArg) ([]sdk.Variable, error) {
	c := structarg{}
	for _, f := range args {
		f(&c)
	}
	variables := []sdk.Variable{}
	query := `SELECT environment_variable.id, environment_variable.name, environment_variable.value,
						environment_variable.cipher_value, environment_variable.type
	          FROM environment_variable
	          WHERE environment_variable.environment_id = $1
	          ORDER BY name`
	rows, err := db.Query(query, environmentID)
	if err != nil {
		return variables, err
	}
	defer rows.Close()
	for rows.Next() {
		var v sdk.Variable
		var typeVar string
		var clearVal sql.NullString
		var cipherVal []byte
		err = rows.Scan(&v.ID, &v.Name, &clearVal, &cipherVal, &typeVar)
		if err != nil {
			return nil, err
		}
		v.Type = sdk.VariableTypeFromString(typeVar)
		v.Value, err = secret.DecryptS(v.Type, clearVal, cipherVal, c.clearsecret)
		if err != nil {
			return nil, err
		}

		variables = append(variables, v)
	}
	return variables, err
}

// InsertVariable Insert a new variable in the given environment
func InsertVariable(db database.QueryExecuter, environmentID int64, variable *sdk.Variable) error {
	query := `INSERT INTO environment_variable(environment_id, name, value, cipher_value, type)
		  VALUES($1, $2, $3, $4, $5) RETURNING id`

	clear, cipher, err := secret.EncryptS(variable.Type, variable.Value)
	if err != nil {
		return err
	}

	err = db.QueryRow(query, environmentID, variable.Name, clear, cipher, string(variable.Type)).Scan(&variable.ID)
	if err != nil {
		return err
	}

	query = `
		UPDATE environment
		SET last_modified = current_timestamp
		WHERE id=$1
	`
	_, err = db.Exec(query, environmentID)
	return err
}

// UpdateVariable Update a variable in the given environment
func UpdateVariable(db database.Executer, envID int64, variable sdk.Variable) error {
	// If we are updating a batch of variables, some of them might be secrets, we don't want to crush the value
	if sdk.NeedPlaceholder(variable.Type) && variable.Value == sdk.PasswordPlaceholder {
		return nil
	}

	clear, cipher, err := secret.EncryptS(variable.Type, variable.Value)
	if err != nil {
		return err
	}

	query := `UPDATE environment_variable
	          SET value=$1, cipher_value=$2, type=$3
	          FROM environment
	          WHERE environment.id = $4 AND environment_variable.name = $5`
	result, err := db.Exec(query, clear, cipher, string(variable.Type), envID, variable.Name)
	if err != nil {
		return err
	}
	rowAffected, err := result.RowsAffected()
	if err != nil {
		return err
	}
	if rowAffected == 0 {
		return sdk.ErrNoVariable
	}

	query = `
		UPDATE environment
		SET last_modified = current_timestamp
		WHERE id=$1`
	_, err = db.Exec(query, envID)
	return err
}

// DeleteVariable Delete a variable from the given pipeline
func DeleteVariable(db database.Executer, envID int64, variableName string) error {
	query := `DELETE FROM environment_variable
	          USING environment
	          WHERE environment.id = $1 AND environment_variable.name = $2`
	result, err := db.Exec(query, envID, variableName)
	rowAffected, err := result.RowsAffected()
	if err != nil {
		return err
	}
	if rowAffected == 0 {
		return sdk.ErrNoVariable
	}

	query = `
		UPDATE environment
		SET last_modified = current_timestamp
		WHERE id = $1`
	_, err = db.Exec(query, envID)
	return err
}

// DeleteAllVariable Delete all variables from the given pipeline
func DeleteAllVariable(db database.Executer, environmentID int64) error {
	query := `DELETE FROM environment_variable
	          WHERE environment_variable.environment_id = $1`
	_, err := db.Exec(query, environmentID)
	if err != nil {
		return err
	}

	query = `
		UPDATE environment
		SET last_modified = current_timestamp
		WHERE id=$1`
	_, err = db.Exec(query, environmentID)
	return err
}
<commit_msg>fix (api): fix sql request for env variable update<commit_after>package environment

import (
	"database/sql"
	"encoding/base64"
	"encoding/json"

	"github.com/ovh/cds/engine/api/database"
	"github.com/ovh/cds/engine/api/secret"
	"github.com/ovh/cds/sdk"
)

// CreateAudit Create environment variable audit for the given project
func CreateAudit(db database.QueryExecuter, key string, env *sdk.Environment, u *sdk.User) error {

	vars, err := GetAllVariable(db, key, env.Name, WithEncryptPassword())
	if err != nil {
		return err
	}

	for i := range vars {
		v := &vars[i]
		if sdk.NeedPlaceholder(v.Type) {
			v.Value = base64.StdEncoding.EncodeToString([]byte(v.Value))
		}
	}

	data, err := json.Marshal(vars)
	if err != nil {
		return err
	}

	query := `
		INSERT INTO environment_variable_audit (versionned, environment_id, data, author)
		VALUES (NOW(), $1, $2, $3)
	`
	_, err = db.Exec(query, env.ID, string(data), u.Username)
	return err
}

// GetAudit retrieve the current environment variable audit
func GetAudit(db database.Querier, auditID int64) ([]sdk.Variable, error) {
	query := `
		SELECT environment_variable_audit.data
		FROM environment_variable_audit
		WHERE environment_variable_audit.id = $1
	`
	var data string
	err := db.QueryRow(query, auditID).Scan(&data)
	if err != nil {
		return nil, err
	}
	var vars []sdk.Variable
	err = json.Unmarshal([]byte(data), &vars)
	for i := range vars {
		v := &vars[i]
		if sdk.NeedPlaceholder(v.Type) {
			decode, err := base64.StdEncoding.DecodeString(v.Value)
			if err != nil {
				return nil, err
			}
			v.Value = string(decode)
		}
	}
	return vars, err
}

// GetEnvironmentAudit Get environment audit for the given project
func GetEnvironmentAudit(db database.Querier, key, envName string) ([]sdk.VariableAudit, error) {
	audits := []sdk.VariableAudit{}
	query := `
		SELECT environment_variable_audit.id, environment_variable_audit.versionned, environment_variable_audit.data, environment_variable_audit.author
		FROM environment_variable_audit
		JOIN environment ON environment.id = environment_variable_audit.environment_id
		JOIN project ON project.id = environment.project_id
		WHERE project.projectkey = $1 AND environment.name = $2
		ORDER BY environment_variable_audit.versionned DESC
	`
	rows, err := db.Query(query, key, envName)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		var audit sdk.VariableAudit
		var data string
		err := rows.Scan(&audit.ID, &audit.Versionned, &data, &audit.Author)
		if err != nil {
			return nil, err
		}
		var vars []sdk.Variable
		err = json.Unmarshal([]byte(data), &vars)
		if err != nil {
			return nil, err
		}
		for i := range vars {
			v := &vars[i]
			if sdk.NeedPlaceholder(v.Type) {
				v.Value = sdk.PasswordPlaceholder
			}
		}
		audit.Variables = vars
		audits = append(audits, audit)
	}
	return audits, nil
}

// GetAllVariableNameByProject Get all variable from all environment
func GetAllVariableNameByProject(db database.Querier, key string) ([]string, error) {
	nameArray := []string{}
	query := `
		SELECT distinct(environment_variable.name)
		FROM environment_variable
		JOIN environment on environment.id = environment_variable.environment_id
		JOIN project on project.id = environment.project_id
		WHERE project.projectkey=$1`
	rows, err := db.Query(query, key)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var name string
		err = rows.Scan(&name)
		if err != nil {
			return nil, err
		}
		nameArray = append(nameArray, name)
	}
	return nameArray, nil
}

type structarg struct {
	clearsecret   bool
	encryptsecret bool
}

// GetAllVariableFuncArg defines the base type for functional argument of GetAllVariable
type GetAllVariableFuncArg func(args *structarg)

// WithClearPassword is a function argument to GetAllVariable
func WithClearPassword() GetAllVariableFuncArg {
	return func(args *structarg) {
		args.clearsecret = true
	}
}

// WithEncryptPassword is a function argument to GetAllVariable
func WithEncryptPassword() GetAllVariableFuncArg {
	return func(args *structarg) {
		args.encryptsecret = true
	}
}

// GetAllVariable Get all variable for the given environment
func GetAllVariable(db database.Querier, key, envName string, args ...GetAllVariableFuncArg) ([]sdk.Variable, error) {
	c := structarg{}
	for _, f := range args {
		f(&c)
	}

	variables := []sdk.Variable{}
	query := `SELECT environment_variable.id, environment_variable.name, environment_variable.value,
						environment_variable.cipher_value, environment_variable.type
	          FROM environment_variable
	          JOIN environment ON environment.id = environment_variable.environment_id
	          JOIN project ON project.id = environment.project_id
	          WHERE environment.name = $1 AND project.projectKey = $2
	          ORDER BY name`
	rows, err := db.Query(query, envName, key)
	if err != nil {
		return variables, err
	}
	defer rows.Close()
	for rows.Next() {
		var v sdk.Variable
		var typeVar string
		var clearVal sql.NullString
		var cipherVal []byte
		err = rows.Scan(&v.ID, &v.Name, &clearVal, &cipherVal, &typeVar)
		if err != nil {
			return nil, err
		}
		v.Type = sdk.VariableTypeFromString(typeVar)

		if c.encryptsecret && sdk.NeedPlaceholder(v.Type) {
			v.Value = string(cipherVal)
		} else {
			v.Value, err = secret.DecryptS(v.Type, clearVal, cipherVal, c.clearsecret)
			if err != nil {
				return nil, err
			}
		}
		variables = append(variables, v)
	}
	return variables, err
}

// GetAllVariableByID Get all variable for the given environment
func GetAllVariableByID(db database.Querier, environmentID int64, args ...GetAllVariableFuncArg) ([]sdk.Variable, error) {
	c := structarg{}
	for _, f := range args {
		f(&c)
	}
	variables := []sdk.Variable{}
	query := `SELECT environment_variable.id, environment_variable.name, environment_variable.value,
						environment_variable.cipher_value, environment_variable.type
	          FROM environment_variable
	          WHERE environment_variable.environment_id = $1
	          ORDER BY name`
	rows, err := db.Query(query, environmentID)
	if err != nil {
		return variables, err
	}
	defer rows.Close()
	for rows.Next() {
		var v sdk.Variable
		var typeVar string
		var clearVal sql.NullString
		var cipherVal []byte
		err = rows.Scan(&v.ID, &v.Name, &clearVal, &cipherVal, &typeVar)
		if err != nil {
			return nil, err
		}
		v.Type = sdk.VariableTypeFromString(typeVar)
		v.Value, err = secret.DecryptS(v.Type, clearVal, cipherVal, c.clearsecret)
		if err != nil {
			return nil, err
		}

		variables = append(variables, v)
	}
	return variables, err
}

// InsertVariable Insert a new variable in the given environment
func InsertVariable(db database.QueryExecuter, environmentID int64, variable *sdk.Variable) error {
	query := `INSERT INTO environment_variable(environment_id, name, value, cipher_value, type)
		  VALUES($1, $2, $3, $4, $5) RETURNING id`

	clear, cipher, err := secret.EncryptS(variable.Type, variable.Value)
	if err != nil {
		return err
	}

	err = db.QueryRow(query, environmentID, variable.Name, clear, cipher, string(variable.Type)).Scan(&variable.ID)
	if err != nil {
		return err
	}

	query = `
		UPDATE environment
		SET last_modified = current_timestamp
		WHERE id=$1
	`
	_, err = db.Exec(query, environmentID)
	return err
}

// UpdateVariable Update a variable in the given environment
func UpdateVariable(db database.Executer, envID int64, variable sdk.Variable) error {
	// If we are updating a batch of variables, some of them might be secrets, we don't want to crush the value
	if sdk.NeedPlaceholder(variable.Type) && variable.Value == sdk.PasswordPlaceholder {
		return nil
	}

	clear, cipher, err := secret.EncryptS(variable.Type, variable.Value)
	if err != nil {
		return err
	}

	query := `UPDATE environment_variable
	          SET value=$1, cipher_value=$2, type=$3
	          WHERE environment_id = $4 AND environment_variable.name = $5`
	result, err := db.Exec(query, clear, cipher, string(variable.Type), envID, variable.Name)
	if err != nil {
		return err
	}
	rowAffected, err := result.RowsAffected()
	if err != nil {
		return err
	}
	if rowAffected == 0 {
		return sdk.ErrNoVariable
	}

	query = `
		UPDATE environment
		SET last_modified = current_timestamp
		WHERE id=$1`
	_, err = db.Exec(query, envID)
	return err
}

// DeleteVariable Delete a variable from the given pipeline
func DeleteVariable(db database.Executer, envID int64, variableName string) error {
	query := `DELETE FROM environment_variable
	          USING environment
	          WHERE environment.id = $1 AND environment_variable.name = $2`
	result, err := db.Exec(query, envID, variableName)
	rowAffected, err := result.RowsAffected()
	if err != nil {
		return err
	}
	if rowAffected == 0 {
		return sdk.ErrNoVariable
	}

	query = `
		UPDATE environment
		SET last_modified = current_timestamp
		WHERE id = $1`
	_, err = db.Exec(query, envID)
	return err
}

// DeleteAllVariable Delete all variables from the given pipeline
func DeleteAllVariable(db database.Executer, environmentID int64) error {
	query := `DELETE FROM environment_variable
	          WHERE environment_variable.environment_id = $1`
	_, err := db.Exec(query, environmentID)
	if err != nil {
		return err
	}

	query = `
		UPDATE environment
		SET last_modified = current_timestamp
		WHERE id=$1`
	_, err = db.Exec(query, environmentID)
	return err
}
<|endoftext|>


<commit_before>package bundled

const epmElv = `# Verbosity configuration
debug-mode = $false

# Configuration for common domains
-default-domain-config = [
  &"github.com"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
  &"bitbucket.org"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
  &"gitlab.com"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
]

# Internal configuration
-data-dir = ~/.elvish
-lib-dir = $-data-dir/lib

# Runtime state - records get copied from -default-domain-config
# or read from epm-domain.cfg files as needed
-domain-config = [&]

# General utility functions

fn -debug [text]{
  if $debug-mode {
    print (edit:styled '=> ' blue)
    echo $text
  }
}

fn -info [text]{
  print (edit:styled '=> ' green)
  echo $text
}

fn -warn [text]{
  print (edit:styled '=> ' yellow)
  echo $text
}

fn -error [text]{
  print (edit:styled '=> ' red)
  echo $text
}

fn dest [pkg]{
  put $-lib-dir/$pkg
}

fn is-installed [pkg]{
  put ?(test -e (dest $pkg))
}

fn -package-domain [pkg]{
  splits / $pkg | take 1
}

fn -package-without-domain [pkg]{
  joins / [(splits / $pkg | drop 1)]
}

# Known method handlers. Each entry is indexed by method name (the
# value of the "method" key in the domain configs), and must contain
# two keys: install and upgrade, each one must be a closure that
# received two arguments: package name and the domain config entry
-method-handler = [
  &git= [
    &src= [pkg dom-cfg]{
      put $dom-cfg[protocol]"://"$pkg
    }

    &install= [pkg dom-cfg]{
      dest = (dest $pkg)
      -info "Installing "$pkg
      mkdir -p $dest
      git clone ($-method-handler[git][src] $pkg $dom-cfg) $dest
    }

    &upgrade= [pkg dom-cfg]{
      dest = (dest $pkg)
      -info "Updating "$pkg
      git -C $dest pull
    }
  ]

  &rsync= [
    &src= [pkg dom-cfg]{
      put $dom-cfg[location]/(-package-without-domain $pkg)/
    }

    &install= [pkg dom-cfg]{
      dest = (dest $pkg)
      pkgd = (-package-without-domain $pkg)
      -info "Installing "$pkg
      rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
    }

    &upgrade= [pkg dom-cfg]{
      dest = (dest $pkg)
      pkgd = (-package-without-domain $pkg)
      if (not (is-installed $pkg)) {
        -error "Package "$pkg" is not installed."
        return
      }
      -info "Updating "$pkg
      rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
    }
  ]
]

# Return the filename of the domain config file for the given domain
# (regardless of whether it exists)
fn -domain-config-file [dom]{
  put $-lib-dir/$dom/epm-domain.cfg
}

# Return the filename of the metadata file for the given package
# (regardless of whether it exists)
fn -package-metadata-file [pkg]{
  put (dest $pkg)/metadata.json
}

# Read the domain config file for a given domain. If the file does not
# exist but we have a built-in definition, then we create the file
# with the default.
fn -read-domain-config [dom]{
  cfgfile = (-domain-config-file $dom)
  # Only read config if it hasn't been loaded already
  if (not (has-key $-domain-config $dom)) {
    if ?(test -f $cfgfile) {
      # If the config file exists, read it...
      -domain-config[$dom] = (cat $cfgfile | from-json)
      -debug "Read domain config for "$dom": "(to-string $-domain-config[$dom])
    } else {
      # ...otherwise check if we have a default config for the domain, and save it
      if (has-key $-default-domain-config $dom) {
        -domain-config[$dom] = $-default-domain-config[$dom]
        -debug "No existing config for "$dom", using the default: "(to-string $-domain-config[$dom])
        mkdir -p (dirname $cfgfile)
        put $-domain-config[$dom] | to-json > $cfgfile
      } else {
        fail "No existing config for "$dom" and no default available. Please create config file "(tilde-abbr $cfgfile)" by hand"
      }
    }
  }
}

# Return the method by which a package is installed
fn -package-method [pkg]{
  dom = (-package-domain $pkg)
  -read-domain-config $dom
  if (has-key $-domain-config $dom) {
    cfg = $-domain-config[$dom]
    put $cfg[method]
  } else {
    put $false
  }
}

# Invoke package operations defined in $-method-handler above
fn -package-op [pkg what]{
  dom = (-package-domain $pkg)
  -read-domain-config $dom
  if (has-key $-domain-config $dom) {
    cfg = $-domain-config[$dom]
    method = $cfg[method]
    if (has-key $-method-handler $method) {
      if (has-key $-method-handler[$method] $what) {
        $-method-handler[$method][$what] $pkg $cfg
      } else {
        fail "No handler for '"$what"' defined in method '"$method"'"
      }
    } else {
      fail "No handler defined for method '"$method"', specified in in config file "(-domain-config-file $dom)
    }
  }
}

# Read and parse the package metadata, if it exists
fn metadata [pkg]{
  res = [&]
  mdata = (-package-metadata-file $pkg)
  if (and (is-installed $pkg) ?(test -f $mdata)) {
    res = (cat $mdata | from-json)
  }
  put $res
}

# Print out information about a package
fn query [pkg]{
  data = (metadata $pkg)
  echo (edit:styled "Package "$pkg cyan)
  echo (edit:styled "Source:" blue) (-package-method $pkg) (-package-op $pkg src)
  if (is-installed $pkg) {
    echo (edit:styled "Installed at "(dest $pkg) green)
  } else {
    echo (edit:styled "Not installed" red)
  }
  keys $data | each [key]{
    echo (edit:styled $key":" blue) $data[$key]
  }
}

# Uninstall a single package by removing its directory
fn -uninstall-package [pkg]{
  if (not (is-installed $pkg)) {
    -error "Package "$pkg" is not installed."
    return
  }
  dest = (dest $pkg)
  -info "Removing package "$pkg
  rm -rf $dest
}

######################################################################
# Main user-facing functions

# List installed packages
fn installed {
  e:ls $-lib-dir | each [dom]{
    if ?(test -f (-domain-config-file $dom)) {
      -read-domain-config $dom
      lvl = $-domain-config[$dom][levels]
      find $-lib-dir/$dom -type d -depth $lvl | each [pkg]{
        replaces $-lib-dir/ "" $pkg
      }
    }
  }
}

# Install and upgrade are method-specific, so we call the
# corresponding functions using -package-op
fn install [&silent-if-installed=$false @pkgs]{
  for pkg $pkgs {
    if (is-installed $pkg) {
      if (not $silent-if-installed) {
        -info "Package "$pkg" is already installed."
      }
    } else {
      -package-op $pkg install
    }
  }
}

fn upgrade [@pkgs]{
  if (eq $pkgs []) {
    pkgs = [(installed)]
    -info 'Upgrading all installed packages'
  }
  for pkg $pkgs {
    if (not (is-installed $pkg)) {
      -error "Package "$pkg" is not installed."
    } else {
      -package-op $pkg upgrade
    }
  }
}

# Uninstall is the same for everyone, just remove the directory
fn uninstall [@pkgs]{
  if (eq $pkgs []) {
    fail 'Must specify at least one package.'
    return
  }
  for pkg $pkgs {
    -uninstall-package $pkg
  }
}`
<commit_msg>Addressed feedback<commit_after>package bundled

const epmElv = `# Verbosity configuration
debug-mode = $false

# Configuration for common domains
-default-domain-config = [
  &"github.com"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
  &"bitbucket.org"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
  &"gitlab.com"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
]

# Internal configuration
-data-dir = ~/.elvish
-lib-dir = $-data-dir/lib

# General utility functions

fn -debug [text]{
  if $debug-mode {
    print (edit:styled '=> ' blue)
    echo $text
  }
}

fn -info [text]{
  print (edit:styled '=> ' green)
  echo $text
}

fn -warn [text]{
  print (edit:styled '=> ' yellow)
  echo $text
}

fn -error [text]{
  print (edit:styled '=> ' red)
  echo $text
}

fn dest [pkg]{
  put $-lib-dir/$pkg
}

fn is-installed [pkg]{
  put ?(test -e (dest $pkg))
}

fn -package-domain [pkg]{
  splits &max=2 / $pkg | take 1
}

fn -package-without-domain [pkg]{
  splits &max=2 / $pkg | drop 1 | joins ''
}

# Known method handlers. Each entry is indexed by method name (the
# value of the "method" key in the domain configs), and must contain
# two keys: install and upgrade, each one must be a closure that
# received two arguments: package name and the domain config entry
#
# - Method 'git' requires the key 'protocol' in the domain config,
#   which has to be `http` or `https`
# - Method 'rsync' requires the key 'location' in the domain config,
#   which has to contain the directory where the domain files are
#   stored. It can be any source location understood by the rsync
#   command.
-method-handler = [
  &git= [
    &src= [pkg dom-cfg]{
      put $dom-cfg[protocol]"://"$pkg
    }

    &install= [pkg dom-cfg]{
      dest = (dest $pkg)
      -info "Installing "$pkg
      mkdir -p $dest
      git clone ($-method-handler[git][src] $pkg $dom-cfg) $dest
    }

    &upgrade= [pkg dom-cfg]{
      dest = (dest $pkg)
      -info "Updating "$pkg
      git -C $dest pull
    }
  ]

  &rsync= [
    &src= [pkg dom-cfg]{
      put $dom-cfg[location]/(-package-without-domain $pkg)/
    }

    &install= [pkg dom-cfg]{
      dest = (dest $pkg)
      pkgd = (-package-without-domain $pkg)
      -info "Installing "$pkg
      rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
    }

    &upgrade= [pkg dom-cfg]{
      dest = (dest $pkg)
      pkgd = (-package-without-domain $pkg)
      if (not (is-installed $pkg)) {
        -error "Package "$pkg" is not installed."
        return
      }
      -info "Updating "$pkg
      rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
    }
  ]
]

# Return the filename of the domain config file for the given domain
# (regardless of whether it exists)
fn -domain-config-file [dom]{
  put $-lib-dir/$dom/epm-domain.cfg
}

# Return the filename of the metadata file for the given package
# (regardless of whether it exists)
fn -package-metadata-file [pkg]{
  put (dest $pkg)/metadata.json
}

fn -write-domain-config [dom]{
  cfgfile = (-domain-config-file $dom)
  mkdir -p (dirname $cfgfile)
  if (has-key $-default-domain-config $dom) {
    put $-default-domain-config[$dom] | to-json > $cfgfile
  } else {
    -error "No default config exists for domain "$dom"."
  }
}

# Returns the domain config file for a given domain. If the file does not
# exist but we have a built-in definition, then we return the
# default. Otherwise we return $false, so the result can always be
# checked with `if`.
fn -domain-config [dom]{
  cfgfile = (-domain-config-file $dom)
  cfg = $false
  if ?(test -f $cfgfile) {
    # If the config file exists, read it...
    cfg = (cat $cfgfile | from-json)
    -debug "Read domain config for "$dom": "(to-string $cfg)
  } else {
    # ...otherwise check if we have a default config for the domain, and save it
    if (has-key $-default-domain-config $dom) {
      cfg = $-default-domain-config[$dom]
      -debug "No existing config for "$dom", using the default: "(to-string $cfg)
    } else {
      -debug "No existing config for "$dom" and no default available."
    }
  }
  put $cfg
}

# Return the method by which a package is installed
fn -package-method [pkg]{
  dom = (-package-domain $pkg)
  cfg = (-domain-config $dom)
  if $cfg {
    put $cfg[method]
  } else {
    put $false
  }
}

# Invoke package operations defined in $-method-handler above
fn -package-op [pkg what]{
  dom = (-package-domain $pkg)
  cfg = (-domain-config $dom)
  if $cfg {
    method = $cfg[method]
    if (has-key $-method-handler $method) {
      if (has-key $-method-handler[$method] $what) {
        $-method-handler[$method][$what] $pkg $cfg
      } else {
        fail "No handler for '"$what"' defined in method '"$method"'"
      }
    } else {
      fail "No handler defined for method '"$method"', specified in in config file "(-domain-config-file $dom)
    }
  } else {
    -error "No config for domain '"$dom"'."
  }
}

# Read and parse the package metadata, if it exists
fn metadata [pkg]{
  res = [&]
  mdata = (-package-metadata-file $pkg)
  if (and (is-installed $pkg) ?(test -f $mdata)) {
    res = (cat $mdata | from-json)
  }
  put $res
}

# Print out information about a package
fn query [pkg]{
  data = (metadata $pkg)
  echo (edit:styled "Package "$pkg cyan)
  echo (edit:styled "Source:" blue) (-package-method $pkg) (-package-op $pkg src)
  if (is-installed $pkg) {
    echo (edit:styled "Installed at "(dest $pkg) green)
  } else {
    echo (edit:styled "Not installed" red)
  }
  keys $data | each [key]{
    echo (edit:styled $key":" blue) $data[$key]
  }
}

# Uninstall a single package by removing its directory
fn -uninstall-package [pkg]{
  if (not (is-installed $pkg)) {
    -error "Package "$pkg" is not installed."
    return
  }
  dest = (dest $pkg)
  -info "Removing package "$pkg
  rm -rf $dest
}

######################################################################
# Main user-facing functions

# List installed packages
fn installed {
  e:ls $-lib-dir | each [dom]{
    cfg = (-domain-config $dom)
    # Only list domains for which we know the config, so that the user
    # can have his own non-package directories under ~/.elvish/lib
    # without conflicts.
    if $cfg {
      lvl = $cfg[levels]
      find $-lib-dir/$dom -type d -depth $lvl | each [pkg]{
        replaces $-lib-dir/ "" $pkg
      }
    }
  }
}

# Install and upgrade are method-specific, so we call the
# corresponding functions using -package-op
fn install [&silent-if-installed=$false @pkgs]{
  for pkg $pkgs {
    if (is-installed $pkg) {
      if (not $silent-if-installed) {
        -info "Package "$pkg" is already installed."
      }
    } else {
      -package-op $pkg install
    }
  }
}

fn upgrade [@pkgs]{
  if (eq $pkgs []) {
    pkgs = [(installed)]
    -info 'Upgrading all installed packages'
  }
  for pkg $pkgs {
    if (not (is-installed $pkg)) {
      -error "Package "$pkg" is not installed."
    } else {
      -package-op $pkg upgrade
    }
  }
}

# Uninstall is the same for everyone, just remove the directory
fn uninstall [@pkgs]{
  if (eq $pkgs []) {
    -error 'Must specify at least one package.'
    return
  }
  for pkg $pkgs {
    -uninstall-package $pkg
  }
}`
<|endoftext|>


<commit_before>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zanzibar

import (
	"net/http"
)

// Header defines methods on ServerHeaders
type Header interface {
	Get(key string) (string, bool)
	Add(key string, value string)
	Set(key string, value string)
	Keys() []string
}

// ServerHTTPHeader wrapper to implement ServerHeaderInterface
// on http.Header
type ServerHTTPHeader http.Header

// TODO: Add a TChannel Implementation

// Get retrieves the first string stored on a given header. Bool
// return value is used to distinguish between the presence of a
// header with golang's zerovalue string and the absence of the string.
func (zh ServerHTTPHeader) Get(
	key string,
) (string, bool) {
	// TODO: Canonicalize strings before lookup.
	// Use textproto.CanonicalMIMEHeaderKey
	h := zh[key]
	if len(h) > 0 {
		return h[0], true
	}
	return "", false
}

// GetOrEmptyStr retrieves the first string stored on a given header or
// the empty string (golang's zero vlaue for string types)
func (zh ServerHTTPHeader) GetOrEmptyStr(
	key string,
) string {
	value, ok := zh.Get(key)
	if ok {
		return value
	}
	return ""
}

// Add appends a value for a given header.
func (zh ServerHTTPHeader) Add(
	key string, value string,
) {
	// TODO: Canonicalize strings before inserting
	// Use textproto.CanonicalMIMEHeaderKey
	zh[key] = append(zh[key], value)
}

// Set sets a value for a given header, overwriting all previous values.
func (zh ServerHTTPHeader) Set(
	key string, value string,
) {
	// TODO: Canonicalize strings before inserting
	// Use textproto.CanonicalMIMEHeaderKey
	h := make([]string, 1)
	h[0] = value
	zh[key] = h
}

// Keys returns a slice of header keys.
func (zh ServerHTTPHeader) Keys() []string {
	keys := make([]string, len(zh))
	i := 0
	for k := range zh {
		keys[i] = k
		i++
	}
	return keys
}
<commit_msg>Function signatures to one line<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zanzibar

import (
	"net/http"
)

// Header defines methods on ServerHeaders
type Header interface {
	Get(key string) (string, bool)
	Add(key string, value string)
	Set(key string, value string)
	Keys() []string
}

// ServerHTTPHeader wrapper to implement ServerHeaderInterface
// on http.Header
type ServerHTTPHeader http.Header

// TODO: Add a TChannel Implementation

// Get retrieves the first string stored on a given header. Bool
// return value is used to distinguish between the presence of a
// header with golang's zerovalue string and the absence of the string.
func (zh ServerHTTPHeader) Get(key string) (string, bool) {
	// TODO: Canonicalize strings before lookup.
	// Use textproto.CanonicalMIMEHeaderKey
	h := zh[key]
	if len(h) > 0 {
		return h[0], true
	}
	return "", false
}

// GetOrEmptyStr retrieves the first string stored on a given header or
// the empty string (golang's zero vlaue for string types)
func (zh ServerHTTPHeader) GetOrEmptyStr(key string) string {
	value, ok := zh.Get(key)
	if ok {
		return value
	}
	return ""
}

// Add appends a value for a given header.
func (zh ServerHTTPHeader) Add(key string, value string) {
	// TODO: Canonicalize strings before inserting
	// Use textproto.CanonicalMIMEHeaderKey
	zh[key] = append(zh[key], value)
}

// Set sets a value for a given header, overwriting all previous values.
func (zh ServerHTTPHeader) Set(key string, value string) {
	// TODO: Canonicalize strings before inserting
	// Use textproto.CanonicalMIMEHeaderKey
	h := make([]string, 1)
	h[0] = value
	zh[key] = h
}

// Keys returns a slice of header keys.
func (zh ServerHTTPHeader) Keys() []string {
	keys := make([]string, len(zh))
	i := 0
	for k := range zh {
		keys[i] = k
		i++
	}
	return keys
}
<|endoftext|>


<commit_before>package request

import (
	"errors"
	"fmt"
	"github.com/golang/protobuf/proto"
	"io/ioutil"
	"memmi/pbuf"
	"net/http"
)

type ProtoIoImpl struct {
}

func (io *ProtoIoImpl) WriteProtoResponse(w http.ResponseWriter, message proto.Message) error {
	data, err := proto.Marshal(message)
	if err != nil {
		return err
	}
	_, wErr := w.Write(data)
	return wErr
}

func (io *ProtoIoImpl) ReadNextCardRequest(r *http.Request) (pbuf.NextCardRequest, error) {
	var retNextCardRequest pbuf.NextCardRequest
	data, readErr := ioutil.ReadAll(r.Body)
	if len(data) == 0 {
		return retNextCardRequest, errors.New("Cannot read from empty request body.")
	}
	if readErr != nil {
		return retNextCardRequest, readErr
	}
	marshalError := proto.Unmarshal(data, &retNextCardRequest)
	return retNextCardRequest, marshalError
}

func (io *ProtoIoImpl) ReadCardScoreReport(r *http.Request) (pbuf.CardScoreReport, error) {
	var retReport pbuf.CardScoreReport
	data, readErr := ioutil.ReadAll(r.Body)
	if len(data) == 0 {
		return retReport, errors.New("Cannot read from empty request body.")
	}
	if readErr != nil {
		return retReport, readErr
	}
	marshalError := proto.Unmarshal(data, &retReport)
	return retReport, marshalError
}

func (io *ProtoIoImpl) ReadReportAndNext(r *http.Request) (pbuf.ReportAndNext, error) {
	var retReportNext pbuf.ReportAndNext
	data, readErr := ioutil.ReadAll(r.Body)
	if len(data) == 0 {
		return retReportNext, errors.New("Cannot read from empty request body.")
	}
	if readErr != nil {
		return retReportNext, readErr
	}
	marshalError := proto.Unmarshal(data, &retReportNext)
	return retReportNext, marshalError
}

func (io *ProtoIoImpl) ReadCardSetRequest(r *http.Request) (pbuf.CardSetRequest, error) {
	var retCardSetRequest pbuf.CardSetRequest
	data, readErr := ioutil.ReadAll(r.Body)
	if len(data) == 0 {
		return retCardSetRequest, errors.New("Cannot read from empty request body.")
	}
	if readErr != nil {
		return retCardSetRequest, readErr
	}
	marshalError := proto.Unmarshal(data, &retCardSetRequest)
	return retCardSetRequest, marshalError
}

func (io *ProtoIoImpl) ReadCardRequest(r *http.Request) (pbuf.CardRequest, error) {
	var retCardRequest pbuf.CardRequest
	data, readErr := ioutil.ReadAll(r.Body)
	if len(data) == 0 {
		return retCardRequest, errors.New("Cannot read from empty request body.")
	}
	if readErr != nil {
		return retCardRequest, readErr
	}
	marshalError := proto.Unmarshal(data, &retCardRequest)
	fmt.Println(marshalError)
	return retCardRequest, marshalError
}
<commit_msg>Removed a print line<commit_after>package request

import (
	"errors"
	"github.com/golang/protobuf/proto"
	"io/ioutil"
	"memmi/pbuf"
	"net/http"
)

type ProtoIoImpl struct {
}

func (io *ProtoIoImpl) WriteProtoResponse(w http.ResponseWriter, message proto.Message) error {
	data, err := proto.Marshal(message)
	if err != nil {
		return err
	}
	_, wErr := w.Write(data)
	return wErr
}

func (io *ProtoIoImpl) ReadNextCardRequest(r *http.Request) (pbuf.NextCardRequest, error) {
	var retNextCardRequest pbuf.NextCardRequest
	data, readErr := ioutil.ReadAll(r.Body)
	if len(data) == 0 {
		return retNextCardRequest, errors.New("Cannot read from empty request body.")
	}
	if readErr != nil {
		return retNextCardRequest, readErr
	}
	marshalError := proto.Unmarshal(data, &retNextCardRequest)
	return retNextCardRequest, marshalError
}

func (io *ProtoIoImpl) ReadCardScoreReport(r *http.Request) (pbuf.CardScoreReport, error) {
	var retReport pbuf.CardScoreReport
	data, readErr := ioutil.ReadAll(r.Body)
	if len(data) == 0 {
		return retReport, errors.New("Cannot read from empty request body.")
	}
	if readErr != nil {
		return retReport, readErr
	}
	marshalError := proto.Unmarshal(data, &retReport)
	return retReport, marshalError
}

func (io *ProtoIoImpl) ReadReportAndNext(r *http.Request) (pbuf.ReportAndNext, error) {
	var retReportNext pbuf.ReportAndNext
	data, readErr := ioutil.ReadAll(r.Body)
	if len(data) == 0 {
		return retReportNext, errors.New("Cannot read from empty request body.")
	}
	if readErr != nil {
		return retReportNext, readErr
	}
	marshalError := proto.Unmarshal(data, &retReportNext)
	return retReportNext, marshalError
}

func (io *ProtoIoImpl) ReadCardSetRequest(r *http.Request) (pbuf.CardSetRequest, error) {
	var retCardSetRequest pbuf.CardSetRequest
	data, readErr := ioutil.ReadAll(r.Body)
	if len(data) == 0 {
		return retCardSetRequest, errors.New("Cannot read from empty request body.")
	}
	if readErr != nil {
		return retCardSetRequest, readErr
	}
	marshalError := proto.Unmarshal(data, &retCardSetRequest)
	return retCardSetRequest, marshalError
}

func (io *ProtoIoImpl) ReadCardRequest(r *http.Request) (pbuf.CardRequest, error) {
	var retCardRequest pbuf.CardRequest
	data, readErr := ioutil.ReadAll(r.Body)
	if len(data) == 0 {
		return retCardRequest, errors.New("Cannot read from empty request body.")
	}
	if readErr != nil {
		return retCardRequest, readErr
	}
	marshalError := proto.Unmarshal(data, &retCardRequest)
	return retCardRequest, marshalError
}
<|endoftext|>


<commit_before>package sslip

import "golang.org/x/net/dns/dnsmessage"

func NameToA (name string) dnsmessage.AResource {
	return dnsmessage.AResource{}
}
<commit_msg>Remove deprecated `sslip.go`, was renamed to `xip.go`<commit_after><|endoftext|>


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

package highlight

import (
	"bufio"
	"bytes"
	"fmt"
	gohtml "html"
	"path/filepath"
	"strings"
	"sync"

	"code.gitea.io/gitea/modules/analyze"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"
	"github.com/alecthomas/chroma"
	"github.com/alecthomas/chroma/formatters/html"
	"github.com/alecthomas/chroma/lexers"
	"github.com/alecthomas/chroma/styles"
	lru "github.com/hashicorp/golang-lru"
)

// don't index files larger than this many bytes for performance purposes
const sizeLimit = 1000000

var (
	// For custom user mapping
	highlightMapping = map[string]string{}

	once sync.Once

	cache *lru.ARCCache
)

// NewContext loads custom highlight map from local config
func NewContext() {
	once.Do(func() {
		keys := setting.Cfg.Section("highlight.mapping").Keys()
		for i := range keys {
			highlightMapping[keys[i].Name()] = keys[i].Value()
		}

		// The size 512 is simply a conservative rule of thumb
		c, err := lru.NewARC(512)
		if err != nil {
			panic(fmt.Sprintf("failed to initialize LRU cache for highlighter: %s", err))
		}
		cache = c
	})
}

// Code returns a HTML version of code string with chroma syntax highlighting classes
func Code(fileName, code string) string {
	NewContext()

	// diff view newline will be passed as empty, change to literal \n so it can be copied
	// preserve literal newline in blame view
	if code == "" || code == "\n" {
		return "\n"
	}

	if len(code) > sizeLimit {
		return code
	}
	formatter := html.New(html.WithClasses(true),
		html.WithLineNumbers(false),
		html.PreventSurroundingPre(true),
	)
	if formatter == nil {
		log.Error("Couldn't create chroma formatter")
		return code
	}

	htmlbuf := bytes.Buffer{}
	htmlw := bufio.NewWriter(&htmlbuf)

	var lexer chroma.Lexer
	if val, ok := highlightMapping[filepath.Ext(fileName)]; ok {
		//use mapped value to find lexer
		lexer = lexers.Get(val)
	}

	if lexer == nil {
		if l, ok := cache.Get(fileName); ok {
			lexer = l.(chroma.Lexer)
		}
	}

	if lexer == nil {
		lexer = lexers.Match(fileName)
		if lexer == nil {
			lexer = lexers.Fallback
		}
		cache.Add(fileName, lexer)
	}

	iterator, err := lexer.Tokenise(nil, string(code))
	if err != nil {
		log.Error("Can't tokenize code: %v", err)
		return code
	}
	// style not used for live site but need to pass something
	err = formatter.Format(htmlw, styles.GitHub, iterator)
	if err != nil {
		log.Error("Can't format code: %v", err)
		return code
	}

	htmlw.Flush()
	// Chroma will add newlines for certain lexers in order to highlight them properly
	// Once highlighted, strip them here so they don't cause copy/paste trouble in HTML output
	return strings.TrimSuffix(htmlbuf.String(), "\n")
}

// File returns map with line lumbers and HTML version of code with chroma syntax highlighting classes
func File(numLines int, fileName string, code []byte) map[int]string {
	NewContext()

	if len(code) > sizeLimit {
		return plainText(string(code), numLines)
	}
	formatter := html.New(html.WithClasses(true),
		html.WithLineNumbers(false),
		html.PreventSurroundingPre(true),
	)

	if formatter == nil {
		log.Error("Couldn't create chroma formatter")
		return plainText(string(code), numLines)
	}

	htmlbuf := bytes.Buffer{}
	htmlw := bufio.NewWriter(&htmlbuf)

	var lexer chroma.Lexer
	if val, ok := highlightMapping[filepath.Ext(fileName)]; ok {
		lexer = lexers.Get(val)
	}

	if lexer == nil {
		language := analyze.GetCodeLanguage(fileName, code)

		lexer = lexers.Get(language)
		if lexer == nil {
			lexer = lexers.Match(fileName)
			if lexer == nil {
				lexer = lexers.Fallback
			}
		}
	}

	iterator, err := lexer.Tokenise(nil, string(code))
	if err != nil {
		log.Error("Can't tokenize code: %v", err)
		return plainText(string(code), numLines)
	}

	err = formatter.Format(htmlw, styles.GitHub, iterator)
	if err != nil {
		log.Error("Can't format code: %v", err)
		return plainText(string(code), numLines)
	}

	htmlw.Flush()
	m := make(map[int]string, numLines)
	for k, v := range strings.SplitN(htmlbuf.String(), "\n", numLines) {
		line := k + 1
		content := string(v)
		//need to keep lines that are only \n so copy/paste works properly in browser
		if content == "" {
			content = "\n"
		}
		m[line] = content
	}
	return m
}

// return unhiglighted map
func plainText(code string, numLines int) map[int]string {
	m := make(map[int]string, numLines)
	for k, v := range strings.SplitN(string(code), "\n", numLines) {
		line := k + 1
		content := string(v)
		//need to keep lines that are only \n so copy/paste works properly in browser
		if content == "" {
			content = "\n"
		}
		m[line] = gohtml.EscapeString(content)
	}
	return m
}
<commit_msg>Replace ARCCache with TwoQueueCache to avoid patent issue (#16240)<commit_after>// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package highlight

import (
	"bufio"
	"bytes"
	"fmt"
	gohtml "html"
	"path/filepath"
	"strings"
	"sync"

	"code.gitea.io/gitea/modules/analyze"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"
	"github.com/alecthomas/chroma"
	"github.com/alecthomas/chroma/formatters/html"
	"github.com/alecthomas/chroma/lexers"
	"github.com/alecthomas/chroma/styles"
	lru "github.com/hashicorp/golang-lru"
)

// don't index files larger than this many bytes for performance purposes
const sizeLimit = 1000000

var (
	// For custom user mapping
	highlightMapping = map[string]string{}

	once sync.Once

	cache *lru.TwoQueueCache
)

// NewContext loads custom highlight map from local config
func NewContext() {
	once.Do(func() {
		keys := setting.Cfg.Section("highlight.mapping").Keys()
		for i := range keys {
			highlightMapping[keys[i].Name()] = keys[i].Value()
		}

		// The size 512 is simply a conservative rule of thumb
		c, err := lru.New2Q(512)
		if err != nil {
			panic(fmt.Sprintf("failed to initialize LRU cache for highlighter: %s", err))
		}
		cache = c
	})
}

// Code returns a HTML version of code string with chroma syntax highlighting classes
func Code(fileName, code string) string {
	NewContext()

	// diff view newline will be passed as empty, change to literal \n so it can be copied
	// preserve literal newline in blame view
	if code == "" || code == "\n" {
		return "\n"
	}

	if len(code) > sizeLimit {
		return code
	}
	formatter := html.New(html.WithClasses(true),
		html.WithLineNumbers(false),
		html.PreventSurroundingPre(true),
	)
	if formatter == nil {
		log.Error("Couldn't create chroma formatter")
		return code
	}

	htmlbuf := bytes.Buffer{}
	htmlw := bufio.NewWriter(&htmlbuf)

	var lexer chroma.Lexer
	if val, ok := highlightMapping[filepath.Ext(fileName)]; ok {
		//use mapped value to find lexer
		lexer = lexers.Get(val)
	}

	if lexer == nil {
		if l, ok := cache.Get(fileName); ok {
			lexer = l.(chroma.Lexer)
		}
	}

	if lexer == nil {
		lexer = lexers.Match(fileName)
		if lexer == nil {
			lexer = lexers.Fallback
		}
		cache.Add(fileName, lexer)
	}

	iterator, err := lexer.Tokenise(nil, string(code))
	if err != nil {
		log.Error("Can't tokenize code: %v", err)
		return code
	}
	// style not used for live site but need to pass something
	err = formatter.Format(htmlw, styles.GitHub, iterator)
	if err != nil {
		log.Error("Can't format code: %v", err)
		return code
	}

	htmlw.Flush()
	// Chroma will add newlines for certain lexers in order to highlight them properly
	// Once highlighted, strip them here so they don't cause copy/paste trouble in HTML output
	return strings.TrimSuffix(htmlbuf.String(), "\n")
}

// File returns map with line lumbers and HTML version of code with chroma syntax highlighting classes
func File(numLines int, fileName string, code []byte) map[int]string {
	NewContext()

	if len(code) > sizeLimit {
		return plainText(string(code), numLines)
	}
	formatter := html.New(html.WithClasses(true),
		html.WithLineNumbers(false),
		html.PreventSurroundingPre(true),
	)

	if formatter == nil {
		log.Error("Couldn't create chroma formatter")
		return plainText(string(code), numLines)
	}

	htmlbuf := bytes.Buffer{}
	htmlw := bufio.NewWriter(&htmlbuf)

	var lexer chroma.Lexer
	if val, ok := highlightMapping[filepath.Ext(fileName)]; ok {
		lexer = lexers.Get(val)
	}

	if lexer == nil {
		language := analyze.GetCodeLanguage(fileName, code)

		lexer = lexers.Get(language)
		if lexer == nil {
			lexer = lexers.Match(fileName)
			if lexer == nil {
				lexer = lexers.Fallback
			}
		}
	}

	iterator, err := lexer.Tokenise(nil, string(code))
	if err != nil {
		log.Error("Can't tokenize code: %v", err)
		return plainText(string(code), numLines)
	}

	err = formatter.Format(htmlw, styles.GitHub, iterator)
	if err != nil {
		log.Error("Can't format code: %v", err)
		return plainText(string(code), numLines)
	}

	htmlw.Flush()
	m := make(map[int]string, numLines)
	for k, v := range strings.SplitN(htmlbuf.String(), "\n", numLines) {
		line := k + 1
		content := string(v)
		//need to keep lines that are only \n so copy/paste works properly in browser
		if content == "" {
			content = "\n"
		}
		m[line] = content
	}
	return m
}

// return unhiglighted map
func plainText(code string, numLines int) map[int]string {
	m := make(map[int]string, numLines)
	for k, v := range strings.SplitN(string(code), "\n", numLines) {
		line := k + 1
		content := string(v)
		//need to keep lines that are only \n so copy/paste works properly in browser
		if content == "" {
			content = "\n"
		}
		m[line] = gohtml.EscapeString(content)
	}
	return m
}
<|endoftext|>


<commit_before>package popularpost

import (
	"fmt"
	"socialapi/config"
	"socialapi/models"
	"time"

	"github.com/jinzhu/now"
	"github.com/koding/logging"
	"github.com/koding/redis"
	"github.com/streadway/amqp"
)

var (
	PopularPostKeyName = "popularpost"
)

type Controller struct {
	log   logging.Logger
	redis *redis.RedisSession
}

func (t *Controller) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	if delivery.Redelivered {
		t.log.Error("Redelivered message gave error again, putting to maintenance queue", err)
		delivery.Ack(false)
		return true
	}

	t.log.Error("an error occured putting message back to queue", err)
	delivery.Nack(false, true)
	return false
}

func New(log logging.Logger, redis *redis.RedisSession) *Controller {
	return &Controller{
		log:   log,
		redis: redis,
	}
}

func (f *Controller) InteractionSaved(i *models.Interaction) error {
	return f.handleInteraction(1, i)
}

func (f *Controller) InteractionDeleted(i *models.Interaction) error {
	return f.handleInteraction(-1, i)
}

func (f *Controller) handleInteraction(incrementCount int, i *models.Interaction) error {
	cm, err := models.ChannelMessageById(i.MessageId)
	if err != nil {
		return err
	}

	c, err := models.ChannelById(cm.InitialChannelId)
	if err != nil {
		return err
	}

	if notEligibleForPopularPost(c, cm) {
		f.log.Error(fmt.Sprintf("Not eligible Interaction Id:%d", i.Id))
		return nil
	}

	if createdMoreThan7DaysAgo(cm.CreatedAt) {
		f.log.Debug(fmt.Sprintf("Post created more than 7 days ago: %v, %v", i.Id, i.CreatedAt))
		return nil
	}

	keyname := &KeyName{
		GroupName: c.GroupName, ChannelName: c.Name,
		Time: cm.CreatedAt,
	}

	err = f.saveToDailyBucket(keyname, incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	err = f.saveToSevenDayBucket(keyname, incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	return nil
}

func (f *Controller) saveToDailyBucket(k *KeyName, inc int, id int64) error {
	_, err := f.redis.SortedSetIncrBy(k.Today(), inc, id)
	return err
}

func (f *Controller) saveToSevenDayBucket(k *KeyName, inc int, id int64) error {
	key := k.Weekly()

	exists := f.redis.Exists(key)
	if exists {
		_, err := f.redis.SortedSetIncrBy(key, inc, id)
		return err
	}

	err := f.createSevenDayCombinedBucket(k)
	if err != nil {
		return err
	}

	return nil
}

func (f *Controller) createSevenDayCombinedBucket(k *KeyName) error {
	keys, weights := []interface{}{}, []interface{}{}

	from := getStartOfDay(k.Time)
	aggregate := "SUM"

	for i := 0; i <= 6; i++ {
		currentDate := getXDaysAgo(from, i)
		keys = append(keys, k.Before(currentDate))

		// add by 1 to prevent divide by 0 errors
		weight := float64(i + 1)
		weights = append(weights, float64(1/weight))
	}

	_, err := f.redis.SortedSetsUnion(k.Weekly(), keys, weights, aggregate)

	return err
}

func PopularPostKey(groupName, channelName string, current time.Time) string {
	name := KeyName{
		GroupName: groupName, ChannelName: channelName,
		Time: current.UTC(),
	}

	return name.Weekly()
}

//----------------------------------------------------------
// KeyName
//----------------------------------------------------------

type KeyName struct {
	GroupName, ChannelName string
	Time                   time.Time
}

func (k *KeyName) Today() string {
	return k.do(getStartOfDay(k.Time))
}

func (k *KeyName) Before(t time.Time) string {
	return k.do(t)
}

func (k *KeyName) Weekly() string {
	current := getStartOfDay(k.Time.UTC())
	sevenDaysAgo := getXDaysAgo(current, 7).UTC().Unix()

	return fmt.Sprintf("%s-%d", k.do(current), sevenDaysAgo)
}

func (k *KeyName) do(t time.Time) string {
	return fmt.Sprintf("%s:%s:%s:%s:%d",
		config.MustGet().Environment, k.GroupName, PopularPostKeyName,
		k.ChannelName, t.UTC().Unix(),
	)
}

//----------------------------------------------------------
// helpers
//----------------------------------------------------------

func notEligibleForPopularPost(c *models.Channel, cm *models.ChannelMessage) bool {
	if c.MetaBits.Is(models.Troll) {
		return true
	}

	if c.PrivacyConstant != models.Channel_PRIVACY_PUBLIC {
		return true
	}

	if cm.MetaBits.Is(models.Troll) {
		return true
	}

	if cm.TypeConstant != models.ChannelMessage_TYPE_POST {
		return true
	}

	return false
}

//----------------------------------------------------------
// Time helpers
//----------------------------------------------------------

func createdMoreThan7DaysAgo(createdAt time.Time) bool {
	delta := time.Now().Sub(createdAt)
	return delta.Hours()/24 > 7
}

func getStartOfDay(t time.Time) time.Time {
	return now.New(t).BeginningOfDay()
}

func getXDaysAgo(t time.Time, days int) time.Time {
	daysAgo := -time.Hour * 24 * time.Duration(days)
	return t.Add(daysAgo)
}

//----------------------------------------------------------
func (t *Controller) CreateKeyAtStartOfDay(groupName, channelName string) {
	endOfDay := now.EndOfDay().UTC()
	difference := time.Now().UTC().Sub(endOfDay)

	<-time.After(difference)

	keyname := &KeyName{
		GroupName: groupName, ChannelName: channelName,
		Time: time.Now().UTC(),
	}

	t.createSevenDayCombinedBucket(keyname)
}
<commit_msg>Social: minor naming change<commit_after>package popularpost

import (
	"fmt"
	"socialapi/config"
	"socialapi/models"
	"time"

	"github.com/jinzhu/now"
	"github.com/koding/logging"
	"github.com/koding/redis"
	"github.com/streadway/amqp"
)

var (
	PopularPostKeyName = "popularpost"
)

type Controller struct {
	log   logging.Logger
	redis *redis.RedisSession
}

func (t *Controller) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	if delivery.Redelivered {
		t.log.Error("Redelivered message gave error again, putting to maintenance queue", err)
		delivery.Ack(false)
		return true
	}

	t.log.Error("an error occured putting message back to queue", err)
	delivery.Nack(false, true)
	return false
}

func New(log logging.Logger, redis *redis.RedisSession) *Controller {
	return &Controller{
		log:   log,
		redis: redis,
	}
}

func (f *Controller) InteractionSaved(i *models.Interaction) error {
	return f.handleInteraction(1, i)
}

func (f *Controller) InteractionDeleted(i *models.Interaction) error {
	return f.handleInteraction(-1, i)
}

func (f *Controller) handleInteraction(incrementCount int, i *models.Interaction) error {
	cm, err := models.ChannelMessageById(i.MessageId)
	if err != nil {
		return err
	}

	c, err := models.ChannelById(cm.InitialChannelId)
	if err != nil {
		return err
	}

	if notEligibleForPopularPost(c, cm) {
		f.log.Error(fmt.Sprintf("Not eligible Interaction Id:%d", i.Id))
		return nil
	}

	if createdMoreThan7DaysAgo(cm.CreatedAt) {
		f.log.Debug(fmt.Sprintf("Post created more than 7 days ago: %v, %v", i.Id, i.CreatedAt))
		return nil
	}

	keyname := &KeyName{
		GroupName: c.GroupName, ChannelName: c.Name,
		Time: cm.CreatedAt,
	}

	err = f.saveToDailyBucket(keyname, incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	err = f.saveToSevenDayBucket(keyname, incrementCount, i.MessageId)
	if err != nil {
		return err
	}

	return nil
}

func (f *Controller) saveToDailyBucket(k *KeyName, inc int, id int64) error {
	_, err := f.redis.SortedSetIncrBy(k.Today(), inc, id)
	return err
}

func (f *Controller) saveToSevenDayBucket(k *KeyName, inc int, id int64) error {
	key := k.Weekly()

	exists := f.redis.Exists(key)
	if exists {
		_, err := f.redis.SortedSetIncrBy(key, inc, id)
		return err
	}

	err := f.createSevenDayBucket(k)
	if err != nil {
		return err
	}

	return nil
}

func (f *Controller) createSevenDayBucket(k *KeyName) error {
	keys, weights := []interface{}{}, []interface{}{}

	from := getStartOfDay(k.Time)
	aggregate := "SUM"

	for i := 0; i <= 6; i++ {
		currentDate := getXDaysAgo(from, i)
		keys = append(keys, k.Before(currentDate))

		// add by 1 to prevent divide by 0 errors
		weight := float64(i + 1)
		weights = append(weights, float64(1/weight))
	}

	_, err := f.redis.SortedSetsUnion(k.Weekly(), keys, weights, aggregate)

	return err
}

func PopularPostKey(groupName, channelName string, current time.Time) string {
	name := KeyName{
		GroupName: groupName, ChannelName: channelName,
		Time: current.UTC(),
	}

	return name.Weekly()
}

//----------------------------------------------------------
// KeyName
//----------------------------------------------------------

type KeyName struct {
	GroupName, ChannelName string
	Time                   time.Time
}

func (k *KeyName) Today() string {
	return k.do(getStartOfDay(k.Time))
}

func (k *KeyName) Before(t time.Time) string {
	return k.do(t)
}

func (k *KeyName) Weekly() string {
	current := getStartOfDay(k.Time.UTC())
	sevenDaysAgo := getXDaysAgo(current, 7).UTC().Unix()

	return fmt.Sprintf("%s-%d", k.do(current), sevenDaysAgo)
}

func (k *KeyName) do(t time.Time) string {
	return fmt.Sprintf("%s:%s:%s:%s:%d",
		config.MustGet().Environment, k.GroupName, PopularPostKeyName,
		k.ChannelName, t.UTC().Unix(),
	)
}

//----------------------------------------------------------
// helpers
//----------------------------------------------------------

func notEligibleForPopularPost(c *models.Channel, cm *models.ChannelMessage) bool {
	if c.MetaBits.Is(models.Troll) {
		return true
	}

	if c.PrivacyConstant != models.Channel_PRIVACY_PUBLIC {
		return true
	}

	if cm.MetaBits.Is(models.Troll) {
		return true
	}

	if cm.TypeConstant != models.ChannelMessage_TYPE_POST {
		return true
	}

	return false
}

//----------------------------------------------------------
// Time helpers
//----------------------------------------------------------

func createdMoreThan7DaysAgo(createdAt time.Time) bool {
	delta := time.Now().Sub(createdAt)
	return delta.Hours()/24 > 7
}

func getStartOfDay(t time.Time) time.Time {
	return now.New(t).BeginningOfDay()
}

func getXDaysAgo(t time.Time, days int) time.Time {
	daysAgo := -time.Hour * 24 * time.Duration(days)
	return t.Add(daysAgo)
}

//----------------------------------------------------------
func (t *Controller) CreateKeyAtStartOfDay(groupName, channelName string) {
	endOfDay := now.EndOfDay().UTC()
	difference := time.Now().UTC().Sub(endOfDay)

	<-time.After(difference)

	keyname := &KeyName{
		GroupName: groupName, ChannelName: channelName,
		Time: time.Now().UTC(),
	}

	t.createSevenDayBucket(keyname)
}
<|endoftext|>


<commit_before>package mstree

import (
	"io"
	"regexp"
	"strings"
	"sync"
)

type node struct {
	Children map[string]*node
	Lock     *sync.Mutex
}

func newNode() *node {
	return &node{make(map[string]*node), new(sync.Mutex)}
}

func (n *node) insert(tokens []string, inserted *bool) {
	if len(tokens) == 0 {
		return
	}
	n.Lock.Lock()
	first, tail := tokens[0], tokens[1:]
	child, ok := n.Children[first]
	if !ok {
		*inserted = true
		child = newNode()
		n.Children[first] = child
	}
	n.Lock.Unlock()
	child.insert(tail, inserted)
}

func (n *node) TraverseDump(prefix string, writer io.Writer) {
	if len(n.Children) == 0 {
		io.WriteString(writer, prefix+"\n")
	} else {
		for k, node := range n.Children {
			var nPref string
			if prefix == "" {
				nPref = k
			} else {
				nPref = prefix + "." + k
			}
			node.TraverseDump(nPref, writer)
		}
	}
}

func (n *node) search(pattern string) map[string]*node {
	if pattern == "*" {
		return n.Children
	}

	results := make(map[string]*node)

	wcIndex := strings.Index(pattern, "*")
	qIndex := strings.Index(pattern, "?")
	obIndex := strings.Index(pattern, "[")
	cbIndex := strings.Index(pattern, "]")

	if wcIndex == -1 && qIndex == -1 && obIndex == -1 && cbIndex == -1 {
		if node, ok := n.Children[pattern]; ok {
			results[pattern] = node
		}
		return results
	}

	if cbIndex == -1 && obIndex == -1 {
		if qIndex == -1 {
			// Only *
			lwcIndex := strings.LastIndex(pattern, "*")

			if wcIndex != lwcIndex || (wcIndex != 0 && wcIndex != len(pattern)-1) {
				// more than one wildcard or one wildcard in the middle
				re, err := regexp.Compile(strings.Replace(pattern, "*", ".*", -1))
				if err != nil {
					return results
				}
				for k, node := range n.Children {
					if re.MatchString(k) {
						results[k] = node
					}
				}
				return results
			}

			if wcIndex == len(pattern)-1 {
				// wildcard at the end
				partial := pattern[:len(pattern)-1]
				for k, node := range n.Children {
					if strings.HasPrefix(k, partial) {
						results[k] = node
					}
				}
			} else {
				// wildcard at the begining
				partial := pattern[1:]
				for k, node := range n.Children {
					if strings.HasSuffix(k, partial) {
						results[k] = node
					}
				}
			}
		} else if wcIndex == -1 {
			// Only ?
			lqIndex := strings.LastIndex(pattern, "?")
			if qIndex != lqIndex || (qIndex != 0 && qIndex != len(pattern)-1) {
				// more than one ? or one ? in the middle
				re, err := regexp.Compile(strings.Replace(pattern, "?", ".?", -1))
				if err != nil {
					return results
				}
				for k, node := range n.Children {
					if re.MatchString(k) {
						results[k] = node
					}
				}
				return results
			}

			if qIndex == len(pattern)-1 {
				// ? at the end
				partial := pattern[:len(pattern)-1]
				for k, node := range n.Children {
					if k[:len(k)-1] == partial {
						results[k] = node
					}
				}
			} else {
				// ? at the begining
				partial := pattern[1:]
				for k, node := range n.Children {
					if k[1:] == partial {
						results[k] = node
					}
				}
			}

		} else {
			// * and ? presents
			rePattern := strings.Replace(strings.Replace(pattern, "*", ".*", -1), "?", ".?", -1)
			re, err := regexp.Compile(rePattern)
			if err != nil {
				return results
			}
			for k, node := range n.Children {
				if re.MatchString(k) {
					results[k] = node
				}
			}
		}
	} else {
		rePattern := strings.Replace(strings.Replace(pattern, "*", ".*", -1), "?", ".?", -1)
		re := regexp.MustCompile(rePattern)
		for k, node := range n.Children {
			if re.MatchString(k) {
				results[k] = node
			}
		}
	}

	return results
}
<commit_msg>non-strict regexp match avoided<commit_after>package mstree

import (
	"io"
	"regexp"
	"strings"
	"sync"
)

type node struct {
	Children map[string]*node
	Lock     *sync.Mutex
}

func newNode() *node {
	return &node{make(map[string]*node), new(sync.Mutex)}
}

func (n *node) insert(tokens []string, inserted *bool) {
	if len(tokens) == 0 {
		return
	}
	n.Lock.Lock()
	first, tail := tokens[0], tokens[1:]
	child, ok := n.Children[first]
	if !ok {
		*inserted = true
		child = newNode()
		n.Children[first] = child
	}
	n.Lock.Unlock()
	child.insert(tail, inserted)
}

func (n *node) TraverseDump(prefix string, writer io.Writer) {
	if len(n.Children) == 0 {
		io.WriteString(writer, prefix+"\n")
	} else {
		for k, node := range n.Children {
			var nPref string
			if prefix == "" {
				nPref = k
			} else {
				nPref = prefix + "." + k
			}
			node.TraverseDump(nPref, writer)
		}
	}
}

func (n *node) search(pattern string) map[string]*node {
	if pattern == "*" {
		return n.Children
	}

	results := make(map[string]*node)

	wcIndex := strings.Index(pattern, "*")
	qIndex := strings.Index(pattern, "?")
	obIndex := strings.Index(pattern, "[")
	cbIndex := strings.Index(pattern, "]")

	if wcIndex == -1 && qIndex == -1 && obIndex == -1 && cbIndex == -1 {
		if node, ok := n.Children[pattern]; ok {
			results[pattern] = node
		}
		return results
	}

	if cbIndex == -1 && obIndex == -1 {
		if qIndex == -1 {
			// Only *
			lwcIndex := strings.LastIndex(pattern, "*")

			if wcIndex != lwcIndex || (wcIndex != 0 && wcIndex != len(pattern)-1) {
				// more than one wildcard or one wildcard in the middle
				rePattern := "^" + strings.Replace(pattern, "*", ".*", -1) + "$"
				re, err := regexp.Compile(rePattern)
				if err != nil {
					return results
				}
				for k, node := range n.Children {
					if re.MatchString(k) {
						results[k] = node
					}
				}
				return results
			}

			if wcIndex == len(pattern)-1 {
				// wildcard at the end
				partial := pattern[:len(pattern)-1]
				for k, node := range n.Children {
					if strings.HasPrefix(k, partial) {
						results[k] = node
					}
				}
			} else {
				// wildcard at the begining
				partial := pattern[1:]
				for k, node := range n.Children {
					if strings.HasSuffix(k, partial) {
						results[k] = node
					}
				}
			}
		} else if wcIndex == -1 {
			// Only ?
			lqIndex := strings.LastIndex(pattern, "?")
			if qIndex != lqIndex || (qIndex != 0 && qIndex != len(pattern)-1) {
				// more than one ? or one ? in the middle
				rePattern := "^" + strings.Replace(pattern, "?", ".?", -1) + "$"
				re, err := regexp.Compile(rePattern)
				if err != nil {
					return results
				}
				for k, node := range n.Children {
					if re.MatchString(k) {
						results[k] = node
					}
				}
				return results
			}

			if qIndex == len(pattern)-1 {
				// ? at the end
				partial := pattern[:len(pattern)-1]
				for k, node := range n.Children {
					if k[:len(k)-1] == partial {
						results[k] = node
					}
				}
			} else {
				// ? at the begining
				partial := pattern[1:]
				for k, node := range n.Children {
					if k[1:] == partial {
						results[k] = node
					}
				}
			}

		} else {
			// * and ? presents
			rePattern := "^" + strings.Replace(strings.Replace(pattern, "*", ".*", -1), "?", ".?", -1) + "$"
			re, err := regexp.Compile(rePattern)
			if err != nil {
				return results
			}
			for k, node := range n.Children {
				if re.MatchString(k) {
					results[k] = node
				}
			}
		}
	} else {
		rePattern := "^" + strings.Replace(strings.Replace(pattern, "*", ".*", -1), "?", ".?", -1) + "$"
		re, err := regexp.Compile(rePattern)
		if err != nil {
			return results
		}
		for k, node := range n.Children {
			if re.MatchString(k) {
				results[k] = node
			}
		}
	}

	return results
}
<|endoftext|>


<commit_before>// goncurses - ncurses library for Go.
// Copyright 2011 Rob Thornton. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/* This simple example mirrors the "hello world" TLDP ncurses howto */

package main

import (
	"code.google.com/p/goncurses"
	"log"
)

func main() {
	// Initialize goncurses. It's essential End() is called to ensure the
	// terminal isn't altered after the program ends
	stdscr, err := goncurses.Init()
	if err != nil {
		log.Fatal("init", err)
	}
	defer goncurses.End()

	stdscr.Print("Hello, World!!!")
	stdscr.MovePrint(3, 0, "Press any key to continue")
	stdscr.Refresh()
	stdscr.GetChar()
}
<commit_msg>Update hello example with better documentation and minor tweaks<commit_after>// goncurses - ncurses library for Go.
// Copyright 2011 Rob Thornton. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/* The classic "Hello, World!" program in Goncurses! */

package main

import (
	"code.google.com/p/goncurses"
	"log"
)

func main() {
	// Initialize goncurses. It's essential End() is called to ensure the
	// terminal isn't altered after the program ends
	stdscr, err := goncurses.Init()
	if err != nil {
		log.Fatal("init", err)
	}
	defer goncurses.End()

	// (Go)ncurses draws by cursor position and uses reverse cartisian
	// coordinate system (y,x). Initially, the cursor is positioned at the
	// coordinates 0,0 so the first call to Print will output the text at the
	// top, left position of the screen since stdscr is a window which
	// represents the terminal's screen size.
	stdscr.Print("Hello, World!")
	stdscr.MovePrint(3, 0, "Press any key to continue")

	// Refresh() flushes output to the screen. Internally, it is the same as
	// calling NoutRefresh() on the window followed by a call to Update()
	stdscr.Refresh()

	// GetChar will block execution until an input event, like typing a
	// character on your keyboard, is received
	stdscr.GetChar()
}
<|endoftext|>