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

package proxy

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/GoogleCloudPlatform/cloudsql-proxy/logging"
)

const (
	DefaultRefreshCfgThrottle = time.Minute
	keepAlivePeriod           = time.Minute
)

// errNotCached is returned when the instance was not found in the Client's
// cache. It is an internal detail and is not actually ever returned to the
// user.
var errNotCached = errors.New("instance was not found in cache")

// Conn represents a connection from a client to a specific instance.
type Conn struct {
	Instance string
	Conn     net.Conn
}

// CertSource is how a Client obtains various certificates required for operation.
type CertSource interface {
	// Local returns a certificate that can be used to authenticate with the
	// provided instance.
	Local(instance string) (tls.Certificate, error)
	// Remote returns the instance's CA certificate, address, and name.
	Remote(instance string) (cert *x509.Certificate, addr, name string, err error)
}

// Client is a type to handle connecting to a Server. All fields are required
// unless otherwise specified.
type Client struct {
	// Port designates which remote port should be used when connecting to
	// instances. This value is defined by the server-side code, but for now it
	// should always be 3307.
	Port int
	// Required; specifies how certificates are obtained.
	Certs CertSource
	// Optionally tracks connections through this client. If nil, connections
	// are not tracked and will not be closed before method Run exits.
	Conns *ConnSet
	// Dialer should return a new connection to the provided address. It is
	// called on each new connection to an instance. net.Dial will be used if
	// left nil.
	Dialer func(net, addr string) (net.Conn, error)

	// RefreshCfgThrottle is the amount of time to wait between configuration
	// refreshes. If not set, it defaults to 1 minute.
	//
	// This is to prevent quota exhaustion in the case of client-side
	// malfunction.
	RefreshCfgThrottle time.Duration

	// The cfgCache holds the most recent connection configuration keyed by
	// instance. Relevant functions are refreshCfg and cachedCfg. It is
	// protected by cfgL.
	cfgCache map[string]cacheEntry
	cfgL     sync.RWMutex

	// MaxConnections is the maximum number of connections to establish
	// before refusing new connections. 0 means no limit.
	MaxConnections uint64

	// ConnectionsCounter is used to enforce the optional maxConnections limit
	ConnectionsCounter  uint64
	ConnectionsCounterL sync.RWMutex
}

type cacheEntry struct {
	lastRefreshed time.Time
	// If err is not nil, the addr and cfg are not valid.
	err  error
	addr string
	cfg  *tls.Config
}

// Run causes the client to start waiting for new connections to connSrc and
// proxy them to the destination instance. It blocks until connSrc is closed.
func (c *Client) Run(connSrc <-chan Conn) {
	for conn := range connSrc {
		go c.handleConn(conn)
	}

	if err := c.Conns.Close(); err != nil {
		logging.Errorf("closing client had error: %v", err)
	}
}

func (c *Client) handleConn(conn Conn) {
	// Track connections count only if a maximum connections limit is set to avoid useless overhead
	if c.MaxConnections > 0 {
		active := atomic.AddUint64(&c.ConnectionsCounter, 1)

		// Deferred decrement of ConnectionsCounter upon connection closing
		defer atomic.AddUint64(&c.ConnectionsCounter, ^uint64(0))

		if active > c.MaxConnections {
			logging.Errorf("too many open connections (max %d)", c.MaxConnections)
			conn.Conn.Close()
			return
		}
	}

	server, err := c.Dial(conn.Instance)
	if err != nil {
		logging.Errorf("couldn't connect to %q: %v", conn.Instance, err)
		conn.Conn.Close()
		return
	}

	if false {
		// Log the connection's traffic via the debug connection if we're in a
		// verbose mode. Note that this is the unencrypted traffic stream.
		conn.Conn = dbgConn{conn.Conn}
	}

	c.Conns.Add(conn.Instance, conn.Conn)
	copyThenClose(server, conn.Conn, conn.Instance, "local connection on "+conn.Conn.LocalAddr().String())

	if err := c.Conns.Remove(conn.Instance, conn.Conn); err != nil {
		logging.Errorf("%s", err)
	}
}

// refreshCfg uses the CertSource inside the Client to find the instance's
// address as well as construct a new tls.Config to connect to the instance. It
// caches the result.
func (c *Client) refreshCfg(instance string) (addr string, cfg *tls.Config, err error) {
	c.cfgL.Lock()
	defer c.cfgL.Unlock()

	throttle := c.RefreshCfgThrottle
	if throttle == 0 {
		throttle = DefaultRefreshCfgThrottle
	}

	if old := c.cfgCache[instance]; time.Since(old.lastRefreshed) < throttle {
		logging.Errorf("Throttling refreshCfg(%s): it was only called %v ago", instance, time.Since(old.lastRefreshed))
		// Refresh was called too recently, just reuse the result.
		return old.addr, old.cfg, old.err
	}

	if c.cfgCache == nil {
		c.cfgCache = make(map[string]cacheEntry)
	}

	defer func() {
		c.cfgCache[instance] = cacheEntry{
			lastRefreshed: time.Now(),

			err:  err,
			addr: addr,
			cfg:  cfg,
		}
	}()

	mycert, err := c.Certs.Local(instance)
	if err != nil {
		return "", nil, err
	}

	scert, addr, name, err := c.Certs.Remote(instance)
	if err != nil {
		return "", nil, err
	}
	certs := x509.NewCertPool()
	certs.AddCert(scert)

	cfg = &tls.Config{
		ServerName:   name,
		Certificates: []tls.Certificate{mycert},
		RootCAs:      certs,
	}
	return fmt.Sprintf("%s:%d", addr, c.Port), cfg, nil
}

func (c *Client) cachedCfg(instance string) (string, *tls.Config) {
	c.cfgL.RLock()
	ret, ok := c.cfgCache[instance]
	c.cfgL.RUnlock()

	// Don't waste time returning an expired/invalid cert.
	if !ok || ret.err != nil || time.Now().After(ret.cfg.Certificates[0].Leaf.NotAfter) {
		return "", nil
	}
	return ret.addr, ret.cfg
}

// Dial uses the configuration stored in the client to connect to an instance.
// If this func returns a nil error the connection is correctly authenticated
// to connect to the instance.
func (c *Client) Dial(instance string) (net.Conn, error) {
	if addr, cfg := c.cachedCfg(instance); cfg != nil {
		ret, err := c.tryConnect(addr, cfg)
		if err == nil {
			return ret, err
		}
	}

	addr, cfg, err := c.refreshCfg(instance)
	if err != nil {
		return nil, err
	}
	return c.tryConnect(addr, cfg)
}

func (c *Client) tryConnect(addr string, cfg *tls.Config) (net.Conn, error) {
	d := c.Dialer
	if d == nil {
		d = net.Dial
	}
	conn, err := d("tcp", addr)
	if err != nil {
		return nil, err
	}
	type setKeepAliver interface {
		SetKeepAlive(keepalive bool) error
		SetKeepAlivePeriod(d time.Duration) error
	}

	if s, ok := conn.(setKeepAliver); ok {
		if err := s.SetKeepAlive(true); err != nil {
			logging.Verbosef("Couldn't set KeepAlive to true: %v", err)
		} else if err := s.SetKeepAlivePeriod(keepAlivePeriod); err != nil {
			logging.Verbosef("Couldn't set KeepAlivePeriod to %v", keepAlivePeriod)
		}
	} else {
		logging.Verbosef("KeepAlive not supported: long-running tcp connections may be killed by the OS.")
	}

	ret := tls.Client(conn, cfg)
	if err := ret.Handshake(); err != nil {
		ret.Close()
		return nil, err
	}
	return ret, nil
}

// NewConnSrc returns a chan which can be used to receive connections
// on the passed Listener. All requests sent to the returned chan will have the
// instance name provided here. The chan will be closed if the Listener returns
// an error.
func NewConnSrc(instance string, l net.Listener) <-chan Conn {
	ch := make(chan Conn)
	go func() {
		for {
			c, err := l.Accept()
			if err != nil {
				logging.Errorf("listener (%#v) had error: %v", l, err)
				l.Close()
				close(ch)
				return
			}
			ch <- Conn{instance, c}
		}
	}()
	return ch
}
<commit_msg>Remove unused mutex<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package proxy

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/GoogleCloudPlatform/cloudsql-proxy/logging"
)

const (
	DefaultRefreshCfgThrottle = time.Minute
	keepAlivePeriod           = time.Minute
)

// errNotCached is returned when the instance was not found in the Client's
// cache. It is an internal detail and is not actually ever returned to the
// user.
var errNotCached = errors.New("instance was not found in cache")

// Conn represents a connection from a client to a specific instance.
type Conn struct {
	Instance string
	Conn     net.Conn
}

// CertSource is how a Client obtains various certificates required for operation.
type CertSource interface {
	// Local returns a certificate that can be used to authenticate with the
	// provided instance.
	Local(instance string) (tls.Certificate, error)
	// Remote returns the instance's CA certificate, address, and name.
	Remote(instance string) (cert *x509.Certificate, addr, name string, err error)
}

// Client is a type to handle connecting to a Server. All fields are required
// unless otherwise specified.
type Client struct {
	// Port designates which remote port should be used when connecting to
	// instances. This value is defined by the server-side code, but for now it
	// should always be 3307.
	Port int
	// Required; specifies how certificates are obtained.
	Certs CertSource
	// Optionally tracks connections through this client. If nil, connections
	// are not tracked and will not be closed before method Run exits.
	Conns *ConnSet
	// Dialer should return a new connection to the provided address. It is
	// called on each new connection to an instance. net.Dial will be used if
	// left nil.
	Dialer func(net, addr string) (net.Conn, error)

	// RefreshCfgThrottle is the amount of time to wait between configuration
	// refreshes. If not set, it defaults to 1 minute.
	//
	// This is to prevent quota exhaustion in the case of client-side
	// malfunction.
	RefreshCfgThrottle time.Duration

	// The cfgCache holds the most recent connection configuration keyed by
	// instance. Relevant functions are refreshCfg and cachedCfg. It is
	// protected by cfgL.
	cfgCache map[string]cacheEntry
	cfgL     sync.RWMutex

	// MaxConnections is the maximum number of connections to establish
	// before refusing new connections. 0 means no limit.
	MaxConnections uint64

	// ConnectionsCounter is used to enforce the optional maxConnections limit
	ConnectionsCounter  uint64
}

type cacheEntry struct {
	lastRefreshed time.Time
	// If err is not nil, the addr and cfg are not valid.
	err  error
	addr string
	cfg  *tls.Config
}

// Run causes the client to start waiting for new connections to connSrc and
// proxy them to the destination instance. It blocks until connSrc is closed.
func (c *Client) Run(connSrc <-chan Conn) {
	for conn := range connSrc {
		go c.handleConn(conn)
	}

	if err := c.Conns.Close(); err != nil {
		logging.Errorf("closing client had error: %v", err)
	}
}

func (c *Client) handleConn(conn Conn) {
	// Track connections count only if a maximum connections limit is set to avoid useless overhead
	if c.MaxConnections > 0 {
		active := atomic.AddUint64(&c.ConnectionsCounter, 1)

		// Deferred decrement of ConnectionsCounter upon connection closing
		defer atomic.AddUint64(&c.ConnectionsCounter, ^uint64(0))

		if active > c.MaxConnections {
			logging.Errorf("too many open connections (max %d)", c.MaxConnections)
			conn.Conn.Close()
			return
		}
	}

	server, err := c.Dial(conn.Instance)
	if err != nil {
		logging.Errorf("couldn't connect to %q: %v", conn.Instance, err)
		conn.Conn.Close()
		return
	}

	if false {
		// Log the connection's traffic via the debug connection if we're in a
		// verbose mode. Note that this is the unencrypted traffic stream.
		conn.Conn = dbgConn{conn.Conn}
	}

	c.Conns.Add(conn.Instance, conn.Conn)
	copyThenClose(server, conn.Conn, conn.Instance, "local connection on "+conn.Conn.LocalAddr().String())

	if err := c.Conns.Remove(conn.Instance, conn.Conn); err != nil {
		logging.Errorf("%s", err)
	}
}

// refreshCfg uses the CertSource inside the Client to find the instance's
// address as well as construct a new tls.Config to connect to the instance. It
// caches the result.
func (c *Client) refreshCfg(instance string) (addr string, cfg *tls.Config, err error) {
	c.cfgL.Lock()
	defer c.cfgL.Unlock()

	throttle := c.RefreshCfgThrottle
	if throttle == 0 {
		throttle = DefaultRefreshCfgThrottle
	}

	if old := c.cfgCache[instance]; time.Since(old.lastRefreshed) < throttle {
		logging.Errorf("Throttling refreshCfg(%s): it was only called %v ago", instance, time.Since(old.lastRefreshed))
		// Refresh was called too recently, just reuse the result.
		return old.addr, old.cfg, old.err
	}

	if c.cfgCache == nil {
		c.cfgCache = make(map[string]cacheEntry)
	}

	defer func() {
		c.cfgCache[instance] = cacheEntry{
			lastRefreshed: time.Now(),

			err:  err,
			addr: addr,
			cfg:  cfg,
		}
	}()

	mycert, err := c.Certs.Local(instance)
	if err != nil {
		return "", nil, err
	}

	scert, addr, name, err := c.Certs.Remote(instance)
	if err != nil {
		return "", nil, err
	}
	certs := x509.NewCertPool()
	certs.AddCert(scert)

	cfg = &tls.Config{
		ServerName:   name,
		Certificates: []tls.Certificate{mycert},
		RootCAs:      certs,
	}
	return fmt.Sprintf("%s:%d", addr, c.Port), cfg, nil
}

func (c *Client) cachedCfg(instance string) (string, *tls.Config) {
	c.cfgL.RLock()
	ret, ok := c.cfgCache[instance]
	c.cfgL.RUnlock()

	// Don't waste time returning an expired/invalid cert.
	if !ok || ret.err != nil || time.Now().After(ret.cfg.Certificates[0].Leaf.NotAfter) {
		return "", nil
	}
	return ret.addr, ret.cfg
}

// Dial uses the configuration stored in the client to connect to an instance.
// If this func returns a nil error the connection is correctly authenticated
// to connect to the instance.
func (c *Client) Dial(instance string) (net.Conn, error) {
	if addr, cfg := c.cachedCfg(instance); cfg != nil {
		ret, err := c.tryConnect(addr, cfg)
		if err == nil {
			return ret, err
		}
	}

	addr, cfg, err := c.refreshCfg(instance)
	if err != nil {
		return nil, err
	}
	return c.tryConnect(addr, cfg)
}

func (c *Client) tryConnect(addr string, cfg *tls.Config) (net.Conn, error) {
	d := c.Dialer
	if d == nil {
		d = net.Dial
	}
	conn, err := d("tcp", addr)
	if err != nil {
		return nil, err
	}
	type setKeepAliver interface {
		SetKeepAlive(keepalive bool) error
		SetKeepAlivePeriod(d time.Duration) error
	}

	if s, ok := conn.(setKeepAliver); ok {
		if err := s.SetKeepAlive(true); err != nil {
			logging.Verbosef("Couldn't set KeepAlive to true: %v", err)
		} else if err := s.SetKeepAlivePeriod(keepAlivePeriod); err != nil {
			logging.Verbosef("Couldn't set KeepAlivePeriod to %v", keepAlivePeriod)
		}
	} else {
		logging.Verbosef("KeepAlive not supported: long-running tcp connections may be killed by the OS.")
	}

	ret := tls.Client(conn, cfg)
	if err := ret.Handshake(); err != nil {
		ret.Close()
		return nil, err
	}
	return ret, nil
}

// NewConnSrc returns a chan which can be used to receive connections
// on the passed Listener. All requests sent to the returned chan will have the
// instance name provided here. The chan will be closed if the Listener returns
// an error.
func NewConnSrc(instance string, l net.Listener) <-chan Conn {
	ch := make(chan Conn)
	go func() {
		for {
			c, err := l.Accept()
			if err != nil {
				logging.Errorf("listener (%#v) had error: %v", l, err)
				l.Close()
				close(ch)
				return
			}
			ch <- Conn{instance, c}
		}
	}()
	return ch
}
<|endoftext|>


<commit_before>package HarborAPItest

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"testing"
        "github.com/vmware/harbor/tests/apitests/apilib"
)

func TestSearch(t *testing.T) {
        fmt.Println("Test for Search (SearchGet) API\n")
	assert := assert.New(t)

	apiTest := HarborAPI.NewHarborAPI()
	var resault HarborAPI.Search
        resault, err := apiTest.SearchGet("library")
	//fmt.Printf("%+v\n", resault)
	if err != nil {
		t.Error("Error while search project or repository", err.Error())
		t.Log(err)
	} else {
		assert.Equal(resault.Projects[0].ProjectId, int32(1), "Project id should be equal")
		assert.Equal(resault.Projects[0].ProjectName, "library", "Project name should be library")
		assert.Equal(resault.Projects[0].Public, int32(1), "Project public status should be 1 (true)")
		//t.Log(resault)
	}
	//if resault.Response.StatusCode != 200 {
	//	t.Log(resault.Response)
	//}

}
<commit_msg>Update hbapisearch_test.go<commit_after>package HarborAPItest

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"testing"
        "github.com/vmware/harbor/tests/apitests/apilib"
)

func TestSearch(t *testing.T) {
        fmt.Println("Test for Search (SearchGet) API")
	assert := assert.New(t)

	apiTest := HarborAPI.NewHarborAPI()
	var resault HarborAPI.Search
        resault, err := apiTest.SearchGet("library")
	//fmt.Printf("%+v\n", resault)
	if err != nil {
		t.Error("Error while search project or repository", err.Error())
		t.Log(err)
	} else {
		assert.Equal(resault.Projects[0].ProjectId, int32(1), "Project id should be equal")
		assert.Equal(resault.Projects[0].ProjectName, "library", "Project name should be library")
		assert.Equal(resault.Projects[0].Public, int32(1), "Project public status should be 1 (true)")
		//t.Log(resault)
	}
	//if resault.Response.StatusCode != 200 {
	//	t.Log(resault.Response)
	//}

}
<|endoftext|>


<commit_before>package impl

import (
	"flag"
	"fmt"
	"github.com/apple/foundationdb/bindings/go/src/fdb"
	"github.com/apple/foundationdb/bindings/go/src/fdb/directory"
	"github.com/apple/foundationdb/bindings/go/src/fdb/subspace"
	"github.com/apple/foundationdb/bindings/go/src/fdb/tuple"
	"hash/crc32"
	"strings"
	"vitessedata/plugin"
)

type fdbctxt struct {
	db          fdb.Database
	dir         directory.DirectorySubspace
	subs        [256]subspace.Subspace
	clusterFile string
	nhk         int
}

func opendb(path []string) *fdbctxt {
	var ctxt fdbctxt

	cf := flag.String("clusterfile", "", "fdb cluster file.")
	nhk := flag.Int("nh", 1, "number of hash bucket column")
	flag.Parse()

	ctxt.clusterFile = *cf
	ctxt.nhk = *nhk

	fdb.MustAPIVersion(510)

	if ctxt.clusterFile != "" {
		// For now, fdb only support one database "DB"
		ctxt.db = fdb.MustOpen(ctxt.clusterFile, []byte("DB"))
	} else {
		ctxt.db = fdb.MustOpenDefault()
	}

	var err error
	ctxt.dir, err = directory.CreateOrOpen(ctxt.db, path, nil)
	if err != nil {
		panic(err)
	}

	for i := 0; i < 256; i++ {
		ctxt.subs[i] = ctxt.dir.Sub([]byte{byte(i)})
	}
	return &ctxt
}

func buildTuple(vs []interface{}) tuple.Tuple {
	tup := make([]tuple.TupleElement, len(vs))
	for idx, v := range vs {
		tup[idx] = v.(tuple.TupleElement)
	}
	return tup
}

func (ctxt *fdbctxt) buildKey(t tuple.Tuple) (fdb.Key, byte) {
	nhk := ctxt.nhk
	if nhk == 0 {
		nhk = len(t)
	}

	kb := t[:nhk].Pack()
	bkt := byte(crc32.ChecksumIEEE(kb))
	key := ctxt.subs[bkt].Pack(t)

	plugin.DbgLog("Build key: %v -> %v, at bkt %d.", t, key, bkt)
	return key, bkt
}

func (ctxt *fdbctxt) buildBktKey(bkt byte, t tuple.Tuple) fdb.Key {
	if t == nil || len(t) == 0 {
		return ctxt.subs[bkt].FDBKey()
	}
	return ctxt.subs[bkt].Pack(t)
}

func (ctxt *fdbctxt) parseKeyValue(bkt byte, kv fdb.KeyValue) (tuple.Tuple, tuple.Tuple, error) {
	kt, err := ctxt.subs[bkt].Unpack(kv.Key)
	if err != nil {
		return nil, nil, err
	}

	vt, err := tuple.Unpack(kv.Value)
	if err != nil {
		return nil, nil, err
	}

	return kt, vt, nil
}

func (ctxt *fdbctxt) buildRange(bkt byte, ta, tz tuple.Tuple) fdb.KeyRange {
	ka := ctxt.buildBktKey(bkt, ta)
	kra, _ := fdb.PrefixRange(ka)
	kz := ctxt.buildBktKey(bkt, tz)
	krz, _ := fdb.PrefixRange(kz)
	return fdb.KeyRange{kra.Begin, krz.End}
}

func (ctxt *fdbctxt) ins(tr fdb.Transaction, kt, vt tuple.Tuple) {
	k, _ := ctxt.buildKey(kt)
	v := vt.Pack()
	tr.Set(k, v)
}

func (ctxt *fdbctxt) del(tr fdb.Transaction, kt tuple.Tuple) {
	k, _ := ctxt.buildKey(kt)
	tr.Clear(k)
}

func (ctxt *fdbctxt) get(tr fdb.Transaction, kt tuple.Tuple) (tuple.Tuple, error) {
	k, _ := ctxt.buildKey(kt)
	ba := tr.Get(k).MustGet()
	return tuple.Unpack(ba)
}

func decodeReqPath(path string) ([]string, []string, []string, error) {
	// path from request should be format mountpoint/dir/dir/key1,key2:val1,val2,val3
	// remove mount point, then return path splited by "/"
	idx := strings.Index(path[1:], "/")
	strs := strings.Split(path[idx+1:], "/")

	if len(strs) < 2 {
		return nil, nil, nil, fmt.Errorf("FDB path %v is not a valid format.", path)
	}

	dirpath := strs[:len(strs)-1]
	kvstrs := strings.Split(strs[len(strs)-1], ":")
	if len(kvstrs) != 2 {
		return nil, nil, nil, fmt.Errorf("FDB path %v is not a valid format.", path)
	}
	return dirpath, strings.Split(kvstrs[0], ","), strings.Split(kvstrs[1], ","), nil
}
<commit_msg>Reduce log verbosity.<commit_after>package impl

import (
	"flag"
	"fmt"
	"github.com/apple/foundationdb/bindings/go/src/fdb"
	"github.com/apple/foundationdb/bindings/go/src/fdb/directory"
	"github.com/apple/foundationdb/bindings/go/src/fdb/subspace"
	"github.com/apple/foundationdb/bindings/go/src/fdb/tuple"
	"hash/crc32"
	"strings"
	"vitessedata/plugin"
)

type fdbctxt struct {
	db          fdb.Database
	dir         directory.DirectorySubspace
	subs        [256]subspace.Subspace
	clusterFile string
	nhk         int
}

func opendb(path []string) *fdbctxt {
	var ctxt fdbctxt

	cf := flag.String("clusterfile", "", "fdb cluster file.")
	nhk := flag.Int("nh", 1, "number of hash bucket column")
	flag.Parse()
	ctxt.clusterFile = *cf
	ctxt.nhk = *nhk
	plugin.DbgLog("Opening database with cf %s, nhk %d.", *cf, *nhk)

	fdb.MustAPIVersion(510)

	if ctxt.clusterFile != "" {
		// For now, fdb only support one database "DB"
		ctxt.db = fdb.MustOpen(ctxt.clusterFile, []byte("DB"))
	} else {
		ctxt.db = fdb.MustOpenDefault()
	}

	var err error
	ctxt.dir, err = directory.CreateOrOpen(ctxt.db, path, nil)
	if err != nil {
		panic(err)
	}

	for i := 0; i < 256; i++ {
		ctxt.subs[i] = ctxt.dir.Sub([]byte{byte(i)})
	}
	return &ctxt
}

func buildTuple(vs []interface{}) tuple.Tuple {
	tup := make([]tuple.TupleElement, len(vs))
	for idx, v := range vs {
		tup[idx] = v.(tuple.TupleElement)
	}
	return tup
}

func (ctxt *fdbctxt) buildKey(t tuple.Tuple) (fdb.Key, byte) {
	nhk := ctxt.nhk
	if nhk == 0 {
		nhk = len(t)
	}

	kb := t[:nhk].Pack()
	bkt := byte(crc32.ChecksumIEEE(kb))
	key := ctxt.subs[bkt].Pack(t)

	// plugin.DbgLog("Build key: %v -> %v, at bkt %d.", t, key, bkt)
	return key, bkt
}

func (ctxt *fdbctxt) buildBktKey(bkt byte, t tuple.Tuple) fdb.Key {
	if t == nil || len(t) == 0 {
		return ctxt.subs[bkt].FDBKey()
	}
	return ctxt.subs[bkt].Pack(t)
}

func (ctxt *fdbctxt) parseKeyValue(bkt byte, kv fdb.KeyValue) (tuple.Tuple, tuple.Tuple, error) {
	kt, err := ctxt.subs[bkt].Unpack(kv.Key)
	if err != nil {
		return nil, nil, err
	}

	vt, err := tuple.Unpack(kv.Value)
	if err != nil {
		return nil, nil, err
	}

	return kt, vt, nil
}

func (ctxt *fdbctxt) buildRange(bkt byte, ta, tz tuple.Tuple) fdb.KeyRange {
	ka := ctxt.buildBktKey(bkt, ta)
	kra, _ := fdb.PrefixRange(ka)
	kz := ctxt.buildBktKey(bkt, tz)
	krz, _ := fdb.PrefixRange(kz)
	return fdb.KeyRange{kra.Begin, krz.End}
}

func (ctxt *fdbctxt) ins(tr fdb.Transaction, kt, vt tuple.Tuple) {
	k, _ := ctxt.buildKey(kt)
	v := vt.Pack()
	tr.Set(k, v)
}

func (ctxt *fdbctxt) del(tr fdb.Transaction, kt tuple.Tuple) {
	k, _ := ctxt.buildKey(kt)
	tr.Clear(k)
}

func (ctxt *fdbctxt) get(tr fdb.Transaction, kt tuple.Tuple) (tuple.Tuple, error) {
	k, _ := ctxt.buildKey(kt)
	ba := tr.Get(k).MustGet()
	return tuple.Unpack(ba)
}

func decodeReqPath(path string) ([]string, []string, []string, error) {
	// path from request should be format mountpoint/dir/dir/key1,key2:val1,val2,val3
	// remove mount point, then return path splited by "/"
	idx := strings.Index(path[1:], "/")
	strs := strings.Split(path[idx+1:], "/")

	if len(strs) < 2 {
		return nil, nil, nil, fmt.Errorf("FDB path %v is not a valid format.", path)
	}

	dirpath := strs[:len(strs)-1]
	kvstrs := strings.Split(strs[len(strs)-1], ":")
	if len(kvstrs) != 2 {
		return nil, nil, nil, fmt.Errorf("FDB path %v is not a valid format.", path)
	}
	return dirpath, strings.Split(kvstrs[0], ","), strings.Split(kvstrs[1], ","), nil
}
<|endoftext|>


<commit_before>package defaults

import (
	"net/http"

	"github.com/volatiletech/authboss"
)

const (
	// RedirectFormValueName is the name of the form field
	// in the http request that will be used when redirecting
	RedirectFormValueName = "redir"
)

// Responder helps respond to http requests
type Responder struct {
	Renderer authboss.Renderer
}

// NewResponder constructor
func NewResponder(renderer authboss.Renderer) *Responder {
	return &Responder{Renderer: renderer}
}

// Respond to an HTTP request. It's main job is to merge data that comes in from
// various middlewares via the context with the data sent by the controller and render that.
func (r *Responder) Respond(w http.ResponseWriter, req *http.Request, code int, page string, data authboss.HTMLData) error {
	ctxData := req.Context().Value(authboss.CTXKeyData)
	if ctxData != nil {
		data.Merge(ctxData.(authboss.HTMLData))
	}

	rendered, mime, err := r.Renderer.Render(req.Context(), page, data)
	if err != nil {
		return err
	}

	w.Header().Set("Content-Type", mime)
	w.WriteHeader(code)

	_, err = w.Write(rendered)
	return err
}

func isAPIRequest(r *http.Request) bool {
	return r.Header.Get("Content-Type") == "application/json"
}

// Redirector for http requests
type Redirector struct {
	Renderer authboss.Renderer

	// FormValueName for the redirection
	FormValueName string
}

// NewRedirector constructor
func NewRedirector(renderer authboss.Renderer, formValueName string) *Redirector {
	return &Redirector{FormValueName: formValueName, Renderer: renderer}
}

// Redirect the client elsewhere. If it's an API request it will simply render
// a JSON response with information that should help a client to decide what
// to do.
func (r *Redirector) Redirect(w http.ResponseWriter, req *http.Request, ro authboss.RedirectOptions) error {
	var redirectFunction = r.redirectNonAPI
	if isAPIRequest(req) {
		redirectFunction = r.redirectAPI
	}

	return redirectFunction(w, req, ro)
}

func (r Redirector) redirectAPI(w http.ResponseWriter, req *http.Request, ro authboss.RedirectOptions) error {
	path := ro.RedirectPath
	redir := req.FormValue(r.FormValueName)
	if len(redir) != 0 && ro.FollowRedirParam {
		path = redir
	}

	var status, message string
	if len(ro.Success) != 0 {
		status = "success"
		message = ro.Success
	}
	if len(ro.Failure) != 0 {
		status = "failure"
		message = ro.Failure
	}

	data := authboss.HTMLData{
		"location": path,
	}

	if len(status) != 0 {
		data["status"] = status
		data["message"] = message
	}

	body, mime, err := r.Renderer.Render(req.Context(), "redirect", data)
	if err != nil {
		return err
	}

	if len(body) != 0 {
		w.Header().Set("Content-Type", mime)
	}

	if ro.Code != 0 {
		w.WriteHeader(ro.Code)
	}
	_, err = w.Write(body)
	return err
}

func (r Redirector) redirectNonAPI(w http.ResponseWriter, req *http.Request, ro authboss.RedirectOptions) error {
	path := ro.RedirectPath
	redir := req.FormValue(r.FormValueName)
	if len(redir) != 0 && ro.FollowRedirParam {
		path = redir
	}

	if len(ro.Success) != 0 {
		authboss.PutSession(w, authboss.FlashSuccessKey, ro.Success)
	}
	if len(ro.Failure) != 0 {
		authboss.PutSession(w, authboss.FlashErrorKey, ro.Failure)
	}

	http.Redirect(w, req, path, http.StatusFound)
	return nil
}
<commit_msg>Fix nil bug in responder<commit_after>package defaults

import (
	"net/http"

	"github.com/volatiletech/authboss"
)

const (
	// RedirectFormValueName is the name of the form field
	// in the http request that will be used when redirecting
	RedirectFormValueName = "redir"
)

// Responder helps respond to http requests
type Responder struct {
	Renderer authboss.Renderer
}

// NewResponder constructor
func NewResponder(renderer authboss.Renderer) *Responder {
	return &Responder{Renderer: renderer}
}

// Respond to an HTTP request. It's main job is to merge data that comes in from
// various middlewares via the context with the data sent by the controller and render that.
func (r *Responder) Respond(w http.ResponseWriter, req *http.Request, code int, page string, data authboss.HTMLData) error {
	ctxData := req.Context().Value(authboss.CTXKeyData)
	if ctxData != nil {
		if data == nil {
			data = authboss.HTMLData{}
		}
		data.Merge(ctxData.(authboss.HTMLData))
	}

	rendered, mime, err := r.Renderer.Render(req.Context(), page, data)
	if err != nil {
		return err
	}

	w.Header().Set("Content-Type", mime)
	w.WriteHeader(code)

	_, err = w.Write(rendered)
	return err
}

func isAPIRequest(r *http.Request) bool {
	return r.Header.Get("Content-Type") == "application/json"
}

// Redirector for http requests
type Redirector struct {
	Renderer authboss.Renderer

	// FormValueName for the redirection
	FormValueName string
}

// NewRedirector constructor
func NewRedirector(renderer authboss.Renderer, formValueName string) *Redirector {
	return &Redirector{FormValueName: formValueName, Renderer: renderer}
}

// Redirect the client elsewhere. If it's an API request it will simply render
// a JSON response with information that should help a client to decide what
// to do.
func (r *Redirector) Redirect(w http.ResponseWriter, req *http.Request, ro authboss.RedirectOptions) error {
	var redirectFunction = r.redirectNonAPI
	if isAPIRequest(req) {
		redirectFunction = r.redirectAPI
	}

	return redirectFunction(w, req, ro)
}

func (r Redirector) redirectAPI(w http.ResponseWriter, req *http.Request, ro authboss.RedirectOptions) error {
	path := ro.RedirectPath
	redir := req.FormValue(r.FormValueName)
	if len(redir) != 0 && ro.FollowRedirParam {
		path = redir
	}

	var status, message string
	if len(ro.Success) != 0 {
		status = "success"
		message = ro.Success
	}
	if len(ro.Failure) != 0 {
		status = "failure"
		message = ro.Failure
	}

	data := authboss.HTMLData{
		"location": path,
	}

	if len(status) != 0 {
		data["status"] = status
		data["message"] = message
	}

	body, mime, err := r.Renderer.Render(req.Context(), "redirect", data)
	if err != nil {
		return err
	}

	if len(body) != 0 {
		w.Header().Set("Content-Type", mime)
	}

	if ro.Code != 0 {
		w.WriteHeader(ro.Code)
	}
	_, err = w.Write(body)
	return err
}

func (r Redirector) redirectNonAPI(w http.ResponseWriter, req *http.Request, ro authboss.RedirectOptions) error {
	path := ro.RedirectPath
	redir := req.FormValue(r.FormValueName)
	if len(redir) != 0 && ro.FollowRedirParam {
		path = redir
	}

	if len(ro.Success) != 0 {
		authboss.PutSession(w, authboss.FlashSuccessKey, ro.Success)
	}
	if len(ro.Failure) != 0 {
		authboss.PutSession(w, authboss.FlashErrorKey, ro.Failure)
	}

	http.Redirect(w, req, path, http.StatusFound)
	return nil
}
<|endoftext|>


<commit_before>package phi_test

import (
	libphi "github.com/fate-lovely/phi"
	"github.com/stretchr/testify/require"
	"github.com/ulule/limiter/v3"
	"github.com/ulule/limiter/v3/drivers/middleware/phi"
	"github.com/ulule/limiter/v3/drivers/store/memory"
	"github.com/valyala/fasthttp"
	"github.com/valyala/fasthttp/fasthttputil"
	"net"
	"strconv"
	"sync"
	"sync/atomic"
	"testing"
)

func TestFasthttpMiddleware(t *testing.T) {
	is := require.New(t)
	req := fasthttp.AcquireRequest()
	req.Header.SetHost("localhost:8080")
	req.Header.SetRequestURI("/")

	store := memory.NewStore()
	is.NotZero(store)

	rate, err := limiter.NewRateFromFormatted("10-M")
	is.NoError(err)
	is.NotZero(rate)

	middleware := phi.NewMiddleware(limiter.New(store, rate))

	router := libphi.NewRouter()
	router.Use(middleware)
	router.Get("/", func(ctx *fasthttp.RequestCtx) {
		ctx.SetStatusCode(fasthttp.StatusOK)
		ctx.SetBodyString("hello")
	})

	success := int64(10)
	clients := int64(100)

	//
	// Sequential
	//

	for i := int64(1); i <= clients; i++ {
		resp := fasthttp.AcquireResponse()
		err := serve(router.ServeFastHTTP, req, resp)
		is.Nil(err)

		if i <= success {
			is.Equal(resp.StatusCode(), fasthttp.StatusOK)
		} else {
			is.Equal(resp.StatusCode(), fasthttp.StatusTooManyRequests)
		}
	}

	//
	// Concurrent
	//

	store = memory.NewStore()
	is.NotZero(store)

	middleware = phi.NewMiddleware(limiter.New(store, rate))

	router = libphi.NewRouter()
	router.Use(middleware)
	router.Get("/", func(ctx *fasthttp.RequestCtx) {
		ctx.SetStatusCode(fasthttp.StatusOK)
		ctx.SetBodyString("hello")
	})

	wg := &sync.WaitGroup{}
	counter := int64(0)

	for i := int64(1); i <= clients; i++ {
		wg.Add(1)

		go func() {
			resp := fasthttp.AcquireResponse()
			err := serve(router.ServeFastHTTP, req, resp)
			is.Nil(err)

			if resp.StatusCode() == fasthttp.StatusOK {
				atomic.AddInt64(&counter, 1)
			}

			wg.Done()
		}()
	}

	wg.Wait()
	is.Equal(success, atomic.LoadInt64(&counter))

	//
	// Custom KeyGetter
	//

	store = memory.NewStore()
	is.NotZero(store)

	j := 0
	KeyGetter := func(ctx *fasthttp.RequestCtx) string {
		j++
		return strconv.Itoa(j)
	}
	middleware = phi.NewMiddleware(limiter.New(store, rate), phi.WithKeyGetter(KeyGetter))

	is.NotZero(middleware)

	router = libphi.NewRouter()
	router.Use(middleware)
	router.Get("/", func(ctx *fasthttp.RequestCtx) {
		ctx.SetStatusCode(fasthttp.StatusOK)
		ctx.SetBodyString("hello")
	})

	for i := int64(1); i <= clients; i++ {
		resp := fasthttp.AcquireResponse()
		err := serve(router.ServeFastHTTP, req, resp)
		is.Nil(err)
		is.Equal(fasthttp.StatusOK, resp.StatusCode(), strconv.Itoa(int(i)))
	}
}

func serve(handler fasthttp.RequestHandler, req *fasthttp.Request, res *fasthttp.Response) error {
	ln := fasthttputil.NewInmemoryListener()
	defer ln.Close()

	go func() {
		err := fasthttp.Serve(ln, handler)
		if err != nil {
			panic(err)
		}
	}()

	client := fasthttp.Client{
		Dial: func(addr string) (net.Conn, error) {
			return ln.Dial()
		},
	}

	return client.Do(req, res)
}
<commit_msg>Fix request host on phi middleware test<commit_after>package phi_test

import (
	libphi "github.com/fate-lovely/phi"
	"github.com/stretchr/testify/require"
	"github.com/ulule/limiter/v3"
	"github.com/ulule/limiter/v3/drivers/middleware/phi"
	"github.com/ulule/limiter/v3/drivers/store/memory"
	"github.com/valyala/fasthttp"
	"github.com/valyala/fasthttp/fasthttputil"
	"net"
	"strconv"
	"sync"
	"sync/atomic"
	"testing"
)

func TestFasthttpMiddleware(t *testing.T) {
	is := require.New(t)

	store := memory.NewStore()
	is.NotZero(store)

	rate, err := limiter.NewRateFromFormatted("10-M")
	is.NoError(err)
	is.NotZero(rate)

	middleware := phi.NewMiddleware(limiter.New(store, rate))

	router := libphi.NewRouter()
	router.Use(middleware)
	router.Get("/", func(ctx *fasthttp.RequestCtx) {
		ctx.SetStatusCode(fasthttp.StatusOK)
		ctx.SetBodyString("hello")
	})

	success := int64(10)
	clients := int64(100)

	//
	// Sequential
	//

	for i := int64(1); i <= clients; i++ {
		resp := fasthttp.AcquireResponse()
		req := fasthttp.AcquireRequest()
		req.Header.SetHost("localhost:8081")
		req.Header.SetRequestURI("/")
		err := serve(router.ServeFastHTTP, req, resp)
		is.Nil(err)

		if i <= success {
			is.Equal(resp.StatusCode(), fasthttp.StatusOK)
		} else {
			is.Equal(resp.StatusCode(), fasthttp.StatusTooManyRequests)
		}
	}

	//
	// Concurrent
	//

	store = memory.NewStore()
	is.NotZero(store)

	middleware = phi.NewMiddleware(limiter.New(store, rate))

	router = libphi.NewRouter()
	router.Use(middleware)
	router.Get("/", func(ctx *fasthttp.RequestCtx) {
		ctx.SetStatusCode(fasthttp.StatusOK)
		ctx.SetBodyString("hello")
	})

	wg := &sync.WaitGroup{}
	counter := int64(0)

	for i := int64(1); i <= clients; i++ {
		wg.Add(1)

		go func() {
			resp := fasthttp.AcquireResponse()
			req := fasthttp.AcquireRequest()
			req.Header.SetHost("localhost:8081")
			req.Header.SetRequestURI("/")
			err := serve(router.ServeFastHTTP, req, resp)
			is.Nil(err)

			if resp.StatusCode() == fasthttp.StatusOK {
				atomic.AddInt64(&counter, 1)
			}

			wg.Done()
		}()
	}

	wg.Wait()
	is.Equal(success, atomic.LoadInt64(&counter))

	//
	// Custom KeyGetter
	//

	store = memory.NewStore()
	is.NotZero(store)

	j := 0
	KeyGetter := func(ctx *fasthttp.RequestCtx) string {
		j++
		return strconv.Itoa(j)
	}
	middleware = phi.NewMiddleware(limiter.New(store, rate), phi.WithKeyGetter(KeyGetter))

	is.NotZero(middleware)

	router = libphi.NewRouter()
	router.Use(middleware)
	router.Get("/", func(ctx *fasthttp.RequestCtx) {
		ctx.SetStatusCode(fasthttp.StatusOK)
		ctx.SetBodyString("hello")
	})

	for i := int64(1); i <= clients; i++ {
		resp := fasthttp.AcquireResponse()
		req := fasthttp.AcquireRequest()
		req.Header.SetHost("localhost:8081")
		req.Header.SetRequestURI("/")
		err := serve(router.ServeFastHTTP, req, resp)
		is.Nil(err)
		is.Equal(fasthttp.StatusOK, resp.StatusCode(), strconv.Itoa(int(i)))
	}
}

func serve(handler fasthttp.RequestHandler, req *fasthttp.Request, res *fasthttp.Response) error {
	ln := fasthttputil.NewInmemoryListener()
	defer ln.Close()

	go func() {
		err := fasthttp.Serve(ln, handler)
		if err != nil {
			panic(err)
		}
	}()

	client := fasthttp.Client{
		Dial: func(addr string) (net.Conn, error) {
			return ln.Dial()
		},
	}

	return client.Do(req, res)
}
<|endoftext|>


<commit_before>package router

import (
	wt "github.com/weaveworks/weave/testing"
	"testing"
	"time"
)

// TODO test gossip unicast and broadcast; atm we only test topology
// gossip, which does not employ unicast or broadcast.

type mockChannelConnection struct {
	RemoteConnection
	dest *Router
}

// Construct a "passive" Router, i.e. without any goroutines, except
// for Routes and GossipSenders.
func NewTestRouter(name PeerName) *Router {
	router := NewRouter(RouterConfig{Name: name})
	// need to create a dummy channel otherwise tests hang on nil
	// channels when the Router invoked ConnectionMaker.Refresh
	router.ConnectionMaker.actionChan = make(chan ConnectionMakerAction, ChannelSize)
	router.Routes.Start()
	return router
}

func (conn *mockChannelConnection) SendProtocolMsg(protocolMsg ProtocolMsg) {
	if err := conn.dest.handleGossip(protocolMsg.tag, protocolMsg.msg); err != nil {
		panic(err)
	}
	conn.dest.sendPendingGossip()
}

func (router *Router) AddTestChannelConnection(r *Router) {
	fromName := router.Ourself.Peer.Name
	toName := r.Ourself.Peer.Name

	fromPeer := NewPeer(fromName, "", router.Ourself.Peer.UID, 0)
	toPeer := NewPeer(toName, "", r.Ourself.Peer.UID, 0)

	r.Peers.FetchWithDefault(fromPeer)    // Has side-effect of incrementing refcount
	router.Peers.FetchWithDefault(toPeer) //

	conn := &mockChannelConnection{RemoteConnection{router.Ourself.Peer, toPeer, "", false, true}, r}
	router.Ourself.handleAddConnection(conn)
	router.Ourself.handleConnectionEstablished(conn)
	router.sendPendingGossip()
}

func (router *Router) DeleteTestChannelConnection(r *Router) {
	fromName := router.Ourself.Peer.Name
	toName := r.Ourself.Peer.Name

	fromPeer, _ := r.Peers.Fetch(fromName)
	toPeer, _ := router.Peers.Fetch(toName)

	fromPeer.DecrementLocalRefCount()
	toPeer.DecrementLocalRefCount()

	conn, _ := router.Ourself.ConnectionTo(toName)
	router.Ourself.handleDeleteConnection(conn)
	router.sendPendingGossip()
}

func TestGossipTopology(t *testing.T) {
	wt.RunWithTimeout(t, 1*time.Second, func() {
		implTestGossipTopology(t)
	})
}

// Create a Peer representing the receiver router, with connections to
// the routers supplied as arguments, carrying across all UID and
// version information.
func (router *Router) tp(routers ...*Router) *Peer {
	peer := NewPeer(router.Ourself.Peer.Name, "", router.Ourself.Peer.UID, 0)
	connections := make(map[PeerName]Connection)
	for _, r := range routers {
		p := NewPeer(r.Ourself.Peer.Name, "", r.Ourself.Peer.UID, r.Ourself.Peer.version)
		connections[r.Ourself.Peer.Name] = newMockConnection(peer, p)
	}
	peer.SetVersionAndConnections(router.Ourself.Peer.version, connections)
	return peer
}

// Check that the topology of router matches the peers and all of their connections
func checkTopology(t *testing.T, router *Router, wantedPeers ...*Peer) {
	checkTopologyPeers(t, true, router.Peers.allPeers(), wantedPeers...)
}

func implTestGossipTopology(t *testing.T) {
	// Create some peers that will talk to each other
	peer1Name, _ := PeerNameFromString("01:00:00:01:00:00")
	peer2Name, _ := PeerNameFromString("02:00:00:02:00:00")
	peer3Name, _ := PeerNameFromString("03:00:00:03:00:00")
	r1 := NewTestRouter(peer1Name)
	r2 := NewTestRouter(peer2Name)
	r3 := NewTestRouter(peer3Name)

	// Check state when they have no connections
	checkTopology(t, r1, r1.tp())
	checkTopology(t, r2, r2.tp())

	// Now try adding some connections
	r1.AddTestChannelConnection(r2)
	checkTopology(t, r1, r1.tp(r2), r2.tp())
	checkTopology(t, r2, r1.tp(r2), r2.tp())
	r2.AddTestChannelConnection(r1)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1))

	// Currently, the connection from 2 to 3 is one-way only
	r2.AddTestChannelConnection(r3)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1, r3), r3.tp())
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1, r3), r3.tp())
	// When r2 gossiped to r3, 1 was unreachable from r3 so it got removed from the
	// list of peers, but remains referenced in the connection from 1 to 3.
	checkTopology(t, r3, r2.tp(r1, r3), r3.tp())

	// Add a connection from 3 to 1 and now r1 is reachable.
	r3.AddTestChannelConnection(r1)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1, r3), r3.tp(r1))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1, r3), r3.tp(r1))
	checkTopology(t, r3, r1.tp(), r2.tp(r1, r3), r3.tp(r1))

	r1.AddTestChannelConnection(r3)
	checkTopology(t, r1, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1))
	checkTopology(t, r2, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1))
	checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1))

	// Drop the connection from 2 to 3
	r2.DeleteTestChannelConnection(r3)
	checkTopology(t, r1, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))
	checkTopology(t, r2, r1.tp(r2, r3), r2.tp(r1))
	checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))

	// Drop the connection from 1 to 3
	r1.DeleteTestChannelConnection(r3)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1))

	checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1))
	// r3 still thinks r1 has a connection to it
	checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))

	// On a timer, r3 will gossip to r1
	r3.SendAllGossip()
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1))
}
<commit_msg>fix bug<commit_after>package router

import (
	wt "github.com/weaveworks/weave/testing"
	"testing"
	"time"
)

// TODO test gossip unicast and broadcast; atm we only test topology
// gossip, which does not employ unicast or broadcast.

type mockChannelConnection struct {
	RemoteConnection
	dest *Router
}

// Construct a "passive" Router, i.e. without any goroutines, except
// for Routes and GossipSenders.
func NewTestRouter(name PeerName) *Router {
	router := NewRouter(RouterConfig{}, name, "")
	// need to create a dummy channel otherwise tests hang on nil
	// channels when the Router invoked ConnectionMaker.Refresh
	router.ConnectionMaker.actionChan = make(chan ConnectionMakerAction, ChannelSize)
	router.Routes.Start()
	return router
}

func (conn *mockChannelConnection) SendProtocolMsg(protocolMsg ProtocolMsg) {
	if err := conn.dest.handleGossip(protocolMsg.tag, protocolMsg.msg); err != nil {
		panic(err)
	}
	conn.dest.sendPendingGossip()
}

func (router *Router) AddTestChannelConnection(r *Router) {
	fromName := router.Ourself.Peer.Name
	toName := r.Ourself.Peer.Name

	fromPeer := NewPeer(fromName, "", router.Ourself.Peer.UID, 0)
	toPeer := NewPeer(toName, "", r.Ourself.Peer.UID, 0)

	r.Peers.FetchWithDefault(fromPeer)    // Has side-effect of incrementing refcount
	router.Peers.FetchWithDefault(toPeer) //

	conn := &mockChannelConnection{RemoteConnection{router.Ourself.Peer, toPeer, "", false, true}, r}
	router.Ourself.handleAddConnection(conn)
	router.Ourself.handleConnectionEstablished(conn)
	router.sendPendingGossip()
}

func (router *Router) DeleteTestChannelConnection(r *Router) {
	fromName := router.Ourself.Peer.Name
	toName := r.Ourself.Peer.Name

	fromPeer, _ := r.Peers.Fetch(fromName)
	toPeer, _ := router.Peers.Fetch(toName)

	fromPeer.DecrementLocalRefCount()
	toPeer.DecrementLocalRefCount()

	conn, _ := router.Ourself.ConnectionTo(toName)
	router.Ourself.handleDeleteConnection(conn)
	router.sendPendingGossip()
}

func TestGossipTopology(t *testing.T) {
	wt.RunWithTimeout(t, 1*time.Second, func() {
		implTestGossipTopology(t)
	})
}

// Create a Peer representing the receiver router, with connections to
// the routers supplied as arguments, carrying across all UID and
// version information.
func (router *Router) tp(routers ...*Router) *Peer {
	peer := NewPeer(router.Ourself.Peer.Name, "", router.Ourself.Peer.UID, 0)
	connections := make(map[PeerName]Connection)
	for _, r := range routers {
		p := NewPeer(r.Ourself.Peer.Name, "", r.Ourself.Peer.UID, r.Ourself.Peer.version)
		connections[r.Ourself.Peer.Name] = newMockConnection(peer, p)
	}
	peer.SetVersionAndConnections(router.Ourself.Peer.version, connections)
	return peer
}

// Check that the topology of router matches the peers and all of their connections
func checkTopology(t *testing.T, router *Router, wantedPeers ...*Peer) {
	checkTopologyPeers(t, true, router.Peers.allPeers(), wantedPeers...)
}

func implTestGossipTopology(t *testing.T) {
	// Create some peers that will talk to each other
	peer1Name, _ := PeerNameFromString("01:00:00:01:00:00")
	peer2Name, _ := PeerNameFromString("02:00:00:02:00:00")
	peer3Name, _ := PeerNameFromString("03:00:00:03:00:00")
	r1 := NewTestRouter(peer1Name)
	r2 := NewTestRouter(peer2Name)
	r3 := NewTestRouter(peer3Name)

	// Check state when they have no connections
	checkTopology(t, r1, r1.tp())
	checkTopology(t, r2, r2.tp())

	// Now try adding some connections
	r1.AddTestChannelConnection(r2)
	checkTopology(t, r1, r1.tp(r2), r2.tp())
	checkTopology(t, r2, r1.tp(r2), r2.tp())
	r2.AddTestChannelConnection(r1)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1))

	// Currently, the connection from 2 to 3 is one-way only
	r2.AddTestChannelConnection(r3)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1, r3), r3.tp())
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1, r3), r3.tp())
	// When r2 gossiped to r3, 1 was unreachable from r3 so it got removed from the
	// list of peers, but remains referenced in the connection from 1 to 3.
	checkTopology(t, r3, r2.tp(r1, r3), r3.tp())

	// Add a connection from 3 to 1 and now r1 is reachable.
	r3.AddTestChannelConnection(r1)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1, r3), r3.tp(r1))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1, r3), r3.tp(r1))
	checkTopology(t, r3, r1.tp(), r2.tp(r1, r3), r3.tp(r1))

	r1.AddTestChannelConnection(r3)
	checkTopology(t, r1, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1))
	checkTopology(t, r2, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1))
	checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1))

	// Drop the connection from 2 to 3
	r2.DeleteTestChannelConnection(r3)
	checkTopology(t, r1, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))
	checkTopology(t, r2, r1.tp(r2, r3), r2.tp(r1))
	checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))

	// Drop the connection from 1 to 3
	r1.DeleteTestChannelConnection(r3)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1))

	checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1))
	// r3 still thinks r1 has a connection to it
	checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))

	// On a timer, r3 will gossip to r1
	r3.SendAllGossip()
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1))
}
<|endoftext|>


<commit_before>// Copyright 2022 ASoulDocs. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package store

import (
	"bytes"
	"net/url"
	"os"
	"path"

	goldmarktoc "github.com/abhinav/goldmark-toc"
	"github.com/pkg/errors"
	"github.com/yuin/goldmark"
	emoji "github.com/yuin/goldmark-emoji"
	highlighting "github.com/yuin/goldmark-highlighting"
	goldmarkmeta "github.com/yuin/goldmark-meta"
	"github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/extension"
	"github.com/yuin/goldmark/parser"
	goldmarkhtml "github.com/yuin/goldmark/renderer/html"
	"github.com/yuin/goldmark/text"
)

func convertFile(pathPrefix, file string) (content []byte, meta map[string]interface{}, headings goldmarktoc.Items, err error) {
	body, err := os.ReadFile(file)
	if err != nil {
		return nil, nil, nil, errors.Wrap(err, "read")
	}

	md := goldmark.New(
		goldmark.WithParserOptions(
			parser.WithAutoHeadingID(),
		),
		goldmark.WithRendererOptions(
			goldmarkhtml.WithHardWraps(),
			goldmarkhtml.WithXHTML(),
			goldmarkhtml.WithUnsafe(),
		),
		goldmark.WithExtensions(
			extension.GFM,
			goldmarkmeta.Meta,
			emoji.Emoji,
			highlighting.NewHighlighting(
				highlighting.WithStyle("base16-snazzy"),
				highlighting.WithGuessLanguage(true),
			),
			extension.NewFootnote(),
		),
	)

	ctx := parser.NewContext()
	doc := md.Parser().Parse(text.NewReader(body), parser.WithContext(ctx))

	// Headings
	tree, err := goldmarktoc.Inspect(doc, body)
	if err != nil {
		return nil, nil, nil, errors.Wrap(err, "inspect headings")
	}
	headings = tree.Items
	if len(headings) > 0 {
		headings = headings[0].Items
	}

	// Links
	err = inspectLinks(pathPrefix, doc)
	if err != nil {
		return nil, nil, nil, errors.Wrap(err, "inspect links")
	}

	var buf bytes.Buffer
	err = md.Renderer().Render(&buf, body, doc)
	if err != nil {
		return nil, nil, nil, errors.Wrap(err, "render")
	}

	return buf.Bytes(), goldmarkmeta.Get(ctx), headings, nil
}

func inspectLinks(pathPrefix string, doc ast.Node) error {
	return ast.Walk(doc, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
		if !entering {
			return ast.WalkContinue, nil
		}

		link, ok := n.(*ast.Link)
		if !ok {
			return ast.WalkContinue, nil
		}

		dest, err := url.Parse(string(link.Destination))
		if err != nil {
			return ast.WalkContinue, nil
		}

		if dest.Scheme == "http" || dest.Scheme == "https" {
			// TODO: external links adds an SVG
			return ast.WalkSkipChildren, nil
		} else if dest.Scheme != "" {
			return ast.WalkContinue, nil
		}

		if bytes.HasPrefix(link.Destination, []byte("#")) {
			return ast.WalkContinue, nil
		}

		// Example: README.md => /docs/introduction
		if bytes.EqualFold(link.Destination, []byte(readme+".md")) {
			link.Destination = []byte(pathPrefix)
			return ast.WalkSkipChildren, nil
		}

		// Example: "installation.md" => "installation"
		link.Destination = bytes.TrimSuffix(link.Destination, []byte(".md"))

		// Example: "../howto/README" => "../howto/"
		link.Destination = bytes.TrimSuffix(link.Destination, []byte(readme))

		// Example: ("/docs", "../howto/") => "/docs/howto"
		link.Destination = []byte(path.Join(pathPrefix, string(link.Destination)))
		return ast.WalkSkipChildren, nil
	})
}
<commit_msg>markdown: support anchors within links<commit_after>// Copyright 2022 ASoulDocs. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package store

import (
	"bytes"
	"net/url"
	"os"
	"path"

	goldmarktoc "github.com/abhinav/goldmark-toc"
	"github.com/pkg/errors"
	"github.com/yuin/goldmark"
	emoji "github.com/yuin/goldmark-emoji"
	highlighting "github.com/yuin/goldmark-highlighting"
	goldmarkmeta "github.com/yuin/goldmark-meta"
	"github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/extension"
	"github.com/yuin/goldmark/parser"
	goldmarkhtml "github.com/yuin/goldmark/renderer/html"
	"github.com/yuin/goldmark/text"
)

func convertFile(pathPrefix, file string) (content []byte, meta map[string]interface{}, headings goldmarktoc.Items, err error) {
	body, err := os.ReadFile(file)
	if err != nil {
		return nil, nil, nil, errors.Wrap(err, "read")
	}

	md := goldmark.New(
		goldmark.WithParserOptions(
			parser.WithAutoHeadingID(),
		),
		goldmark.WithRendererOptions(
			goldmarkhtml.WithHardWraps(),
			goldmarkhtml.WithXHTML(),
			goldmarkhtml.WithUnsafe(),
		),
		goldmark.WithExtensions(
			extension.GFM,
			goldmarkmeta.Meta,
			emoji.Emoji,
			highlighting.NewHighlighting(
				highlighting.WithStyle("base16-snazzy"),
				highlighting.WithGuessLanguage(true),
			),
			extension.NewFootnote(),
		),
	)

	ctx := parser.NewContext()
	doc := md.Parser().Parse(text.NewReader(body), parser.WithContext(ctx))

	// Headings
	tree, err := goldmarktoc.Inspect(doc, body)
	if err != nil {
		return nil, nil, nil, errors.Wrap(err, "inspect headings")
	}
	headings = tree.Items
	if len(headings) > 0 {
		headings = headings[0].Items
	}

	// Links
	err = inspectLinks(pathPrefix, doc)
	if err != nil {
		return nil, nil, nil, errors.Wrap(err, "inspect links")
	}

	var buf bytes.Buffer
	err = md.Renderer().Render(&buf, body, doc)
	if err != nil {
		return nil, nil, nil, errors.Wrap(err, "render")
	}

	return buf.Bytes(), goldmarkmeta.Get(ctx), headings, nil
}

func inspectLinks(pathPrefix string, doc ast.Node) error {
	return ast.Walk(doc, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
		if !entering {
			return ast.WalkContinue, nil
		}

		link, ok := n.(*ast.Link)
		if !ok {
			return ast.WalkContinue, nil
		}

		dest, err := url.Parse(string(link.Destination))
		if err != nil {
			return ast.WalkContinue, nil
		}

		if dest.Scheme == "http" || dest.Scheme == "https" {
			// TODO: external links adds an SVG
			return ast.WalkSkipChildren, nil
		} else if dest.Scheme != "" {
			return ast.WalkContinue, nil
		}

		var anchor []byte
		if i := bytes.IndexByte(link.Destination, '#'); i > -1 {
			if i == 0 {
				return ast.WalkContinue, nil
			}

			anchor = link.Destination[i:]
			link.Destination = link.Destination[:i]
		}

		// Example: README.md => /docs/introduction
		if bytes.EqualFold(link.Destination, []byte(readme+".md")) {
			link.Destination = append([]byte(pathPrefix), anchor...)
			return ast.WalkSkipChildren, nil
		}

		// Example: "installation.md" => "installation"
		link.Destination = bytes.TrimSuffix(link.Destination, []byte(".md"))

		// Example: "../howto/README" => "../howto/"
		link.Destination = bytes.TrimSuffix(link.Destination, []byte(readme))

		// Example: ("/docs", "../howto/") => "/docs/howto"
		link.Destination = []byte(path.Join(pathPrefix, string(link.Destination)))

		link.Destination = append(link.Destination, anchor...)
		return ast.WalkSkipChildren, nil
	})
}
<|endoftext|>


<commit_before>package interpreter

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"sync"
	"syscall"

	"github.com/zetamatta/go-findfile"

	"../dos"
)

const FLAG_AMP2NEWCONSOLE = false

var WildCardExpansionAlways = false

var dbg = false

type CommandNotFound struct {
	Name string
	Err  error
}

// from "TDM-GCC-64/x86_64-w64-mingw32/include/winbase.h"
const (
	CREATE_NEW_CONSOLE       = 0x10
	CREATE_NEW_PROCESS_GROUP = 0x200
)

func (this CommandNotFound) Stringer() string {
	return fmt.Sprintf("'%s' is not recognized as an internal or external command,\noperable program or batch file", this.Name)
}

func (this CommandNotFound) Error() string {
	return this.Stringer()
}

type ErrorLevel int

const (
	NOERROR  ErrorLevel = 0
	THROUGH  ErrorLevel = -1
	SHUTDOWN ErrorLevel = -2
)

func (this ErrorLevel) HasValue() bool {
	return this >= NOERROR
}

func (this ErrorLevel) HasError() bool {
	return this > NOERROR
}

func (this ErrorLevel) String() string {
	switch this {
	case THROUGH:
		return "THROUGH"
	case SHUTDOWN:
		return "SHUTDOWN"
	default:
		return fmt.Sprintf("%d", this)
	}
}

type Interpreter struct {
	exec.Cmd
	Stdio        [3]*os.File
	HookCount    int
	Tag          interface{}
	PipeSeq      [2]uint
	IsBackGround bool
	RawArgs      []string

	OnClone func(*Interpreter) error
	Closers []io.Closer
}

func (this *Interpreter) closeAtEnd() {
	if this.Closers != nil {
		for _, c := range this.Closers {
			c.Close()
		}
		this.Closers = nil
	}
}

func (this *Interpreter) Close() {
	this.closeAtEnd()
}

func New() *Interpreter {
	this := Interpreter{
		Stdio: [3]*os.File{os.Stdin, os.Stdout, os.Stderr},
	}
	this.Stdin = os.Stdin
	this.Stdout = os.Stdout
	this.Stderr = os.Stderr
	this.PipeSeq[0] = pipeSeq
	this.PipeSeq[1] = 0
	this.Tag = nil
	return &this
}

func (this *Interpreter) SetStdin(f *os.File) {
	this.Stdio[0] = f
	this.Stdin = f
}
func (this *Interpreter) SetStdout(f *os.File) {
	this.Stdio[1] = f
	this.Stdout = f
}
func (this *Interpreter) SetStderr(f *os.File) {
	this.Stdio[2] = f
	this.Stderr = f
}

func (this *Interpreter) Clone() (*Interpreter, error) {
	rv := new(Interpreter)
	rv.Stdio[0] = this.Stdio[0]
	rv.Stdio[1] = this.Stdio[1]
	rv.Stdio[2] = this.Stdio[2]
	rv.Stdin = this.Stdin
	rv.Stdout = this.Stdout
	rv.Stderr = this.Stderr
	rv.HookCount = this.HookCount
	rv.Tag = this.Tag
	rv.PipeSeq = rv.PipeSeq
	rv.Closers = nil
	rv.OnClone = this.OnClone
	if this.OnClone != nil {
		if err := this.OnClone(rv); err != nil {
			return nil, err
		}
	}
	return rv, nil
}

type ArgsHookT func(it *Interpreter, args []string) ([]string, error)

var argsHook = func(it *Interpreter, args []string) ([]string, error) {
	return args, nil
}

func SetArgsHook(argsHook_ ArgsHookT) (rv ArgsHookT) {
	rv, argsHook = argsHook, argsHook_
	return
}

type HookT func(*Interpreter) (ErrorLevel, error)

var hook = func(*Interpreter) (ErrorLevel, error) {
	return THROUGH, nil
}

func SetHook(hook_ HookT) (rv HookT) {
	rv, hook = hook, hook_
	return
}

var OnCommandNotFound = func(this *Interpreter, err error) error {
	err = &CommandNotFound{this.Args[0], err}
	return err
}

var ErrorLevelStr string

func nvl(a *os.File, b *os.File) *os.File {
	if a != nil {
		return a
	} else {
		return b
	}
}

func (this *Interpreter) spawnvp_noerrmsg() (ErrorLevel, error) {
	// command is empty.
	if len(this.Args) <= 0 {
		return NOERROR, nil
	}
	if dbg {
		print("spawnvp_noerrmsg('", this.Args[0], "')\n")
	}

	// aliases and lua-commands
	if errorlevel, err := hook(this); errorlevel != THROUGH || err != nil {
		return errorlevel, err
	}

	// command not found hook
	var err error
	this.Path, err = exec.LookPath(this.Args[0])
	if err != nil {
		return ErrorLevel(255), OnCommandNotFound(this, err)
	}

	if WildCardExpansionAlways {
		this.Args = findfile.Globs(this.Args)
	}

	// executable-file
	if FLAG_AMP2NEWCONSOLE {
		if this.SysProcAttr != nil && (this.SysProcAttr.CreationFlags&CREATE_NEW_CONSOLE) != 0 {
			err = this.Start()
			return ErrorLevel(0), err
		}
	}
	err = this.Run()

	errorlevel, errorlevelOk := dos.GetErrorLevel(&this.Cmd)
	if errorlevelOk {
		return ErrorLevel(errorlevel), err
	} else {
		return ErrorLevel(255), err
	}
}

func (this *Interpreter) Spawnvp() (ErrorLevel, error) {
	errorlevel, err := this.spawnvp_noerrmsg()
	if err != nil {
		fmt.Fprintln(this.Stderr, err.Error())
	}
	return errorlevel, err
}

type result_t struct {
	NextValue ErrorLevel
	Error     error
}

var pipeSeq uint = 0

func (this *Interpreter) Interpret(text string) (errorlevel ErrorLevel, err error) {
	if dbg {
		print("Interpret('", text, "')\n")
	}
	if this == nil {
		return ErrorLevel(255), errors.New("Fatal Error: Interpret: instance is nil")
	}
	errorlevel = NOERROR
	err = nil

	statements, statementsErr := Parse(text)
	if statementsErr != nil {
		if dbg {
			print("Parse Error:", statementsErr.Error(), "\n")
		}
		return NOERROR, statementsErr
	}
	if argsHook != nil {
		if dbg {
			print("call argsHook\n")
		}
		for _, pipeline := range statements {
			for _, state := range pipeline {
				state.Args, err = argsHook(this, state.Args)
				if err != nil {
					return ErrorLevel(255), err
				}
			}
		}
		if dbg {
			print("done argsHook\n")
		}
	}
	for _, pipeline := range statements {
		for i, state := range pipeline {
			if state.Term == "|" && (i+1 >= len(pipeline) || len(pipeline[i+1].Args) <= 0) {
				return ErrorLevel(255), errors.New("The syntax of the command is incorrect.")
			}
		}
	}

	for _, pipeline := range statements {

		var pipeIn *os.File = nil
		pipeSeq++
		isBackGround := this.IsBackGround
		for _, state := range pipeline {
			if state.Term == "&" {
				isBackGround = true
				break
			}
		}
		var wg sync.WaitGroup
		for i, state := range pipeline {
			if dbg {
				print(i, ": pipeline loop(", state.Args[0], ")\n")
			}
			cmd := new(Interpreter)
			cmd.PipeSeq[0] = pipeSeq
			cmd.PipeSeq[1] = uint(1 + i)
			cmd.IsBackGround = isBackGround
			cmd.Tag = this.Tag
			cmd.HookCount = this.HookCount
			cmd.SetStdin(nvl(this.Stdio[0], os.Stdin))
			cmd.SetStdout(nvl(this.Stdio[1], os.Stdout))
			cmd.SetStderr(nvl(this.Stdio[2], os.Stderr))
			cmd.OnClone = this.OnClone
			if this.OnClone != nil {
				if err := this.OnClone(cmd); err != nil {
					return ErrorLevel(255), err
				}
			}

			var err error = nil

			if pipeIn != nil {
				cmd.SetStdin(pipeIn)
				cmd.Closers = append(cmd.Closers, pipeIn)
				pipeIn = nil
			}

			if state.Term[0] == '|' {
				var pipeOut *os.File
				pipeIn, pipeOut, err = os.Pipe()
				cmd.SetStdout(pipeOut)
				if state.Term == "|&" {
					cmd.SetStderr(pipeOut)
				}
				cmd.Closers = append(cmd.Closers, pipeOut)
			}

			for _, red := range state.Redirect {
				var fd *os.File
				fd, err = red.OpenOn(cmd)
				if err != nil {
					return NOERROR, err
				}
				defer fd.Close()
			}

			cmd.Args = state.Args
			cmd.RawArgs = state.RawArgs
			if i > 0 {
				cmd.IsBackGround = true
			}
			if i == len(pipeline)-1 && state.Term != "&" {
				errorlevel, err = cmd.Spawnvp()
				cmd.closeAtEnd()
				ErrorLevelStr = errorlevel.String()
				cmd.Close()
			} else {
				if !isBackGround {
					wg.Add(1)
				}
				go func(cmd1 *Interpreter) {
					if isBackGround {
						if FLAG_AMP2NEWCONSOLE {
							if len(pipeline) == 1 {
								cmd1.SysProcAttr = &syscall.SysProcAttr{
									CreationFlags: CREATE_NEW_CONSOLE |
										CREATE_NEW_PROCESS_GROUP,
								}
							}
						}
					} else {
						defer wg.Done()
					}
					cmd1.Spawnvp()
					cmd1.closeAtEnd()
					cmd1.Close()
				}(cmd)
			}
		}
		if !isBackGround {
			wg.Wait()
			if len(pipeline) > 0 {
				switch pipeline[len(pipeline)-1].Term {
				case "&&":
					if errorlevel != 0 {
						return errorlevel, nil
					}
				case "||":
					if errorlevel == 0 {
						return errorlevel, nil
					}
				}
			}
		}
	}
	return
}
<commit_msg>Removed Interpreter.closeAtEnd() & codes move into .Close()<commit_after>package interpreter

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"sync"
	"syscall"

	"github.com/zetamatta/go-findfile"

	"../dos"
)

const FLAG_AMP2NEWCONSOLE = false

var WildCardExpansionAlways = false

var dbg = false

type CommandNotFound struct {
	Name string
	Err  error
}

// from "TDM-GCC-64/x86_64-w64-mingw32/include/winbase.h"
const (
	CREATE_NEW_CONSOLE       = 0x10
	CREATE_NEW_PROCESS_GROUP = 0x200
)

func (this CommandNotFound) Stringer() string {
	return fmt.Sprintf("'%s' is not recognized as an internal or external command,\noperable program or batch file", this.Name)
}

func (this CommandNotFound) Error() string {
	return this.Stringer()
}

type ErrorLevel int

const (
	NOERROR  ErrorLevel = 0
	THROUGH  ErrorLevel = -1
	SHUTDOWN ErrorLevel = -2
)

func (this ErrorLevel) HasValue() bool {
	return this >= NOERROR
}

func (this ErrorLevel) HasError() bool {
	return this > NOERROR
}

func (this ErrorLevel) String() string {
	switch this {
	case THROUGH:
		return "THROUGH"
	case SHUTDOWN:
		return "SHUTDOWN"
	default:
		return fmt.Sprintf("%d", this)
	}
}

type Interpreter struct {
	exec.Cmd
	Stdio        [3]*os.File
	HookCount    int
	Tag          interface{}
	PipeSeq      [2]uint
	IsBackGround bool
	RawArgs      []string

	OnClone func(*Interpreter) error
	Closers []io.Closer
}

func (this *Interpreter) Close() {
	if this.Closers != nil {
		for _, c := range this.Closers {
			c.Close()
		}
		this.Closers = nil
	}
}

func New() *Interpreter {
	this := Interpreter{
		Stdio: [3]*os.File{os.Stdin, os.Stdout, os.Stderr},
	}
	this.Stdin = os.Stdin
	this.Stdout = os.Stdout
	this.Stderr = os.Stderr
	this.PipeSeq[0] = pipeSeq
	this.PipeSeq[1] = 0
	this.Tag = nil
	return &this
}

func (this *Interpreter) SetStdin(f *os.File) {
	this.Stdio[0] = f
	this.Stdin = f
}
func (this *Interpreter) SetStdout(f *os.File) {
	this.Stdio[1] = f
	this.Stdout = f
}
func (this *Interpreter) SetStderr(f *os.File) {
	this.Stdio[2] = f
	this.Stderr = f
}

func (this *Interpreter) Clone() (*Interpreter, error) {
	rv := new(Interpreter)
	rv.Stdio[0] = this.Stdio[0]
	rv.Stdio[1] = this.Stdio[1]
	rv.Stdio[2] = this.Stdio[2]
	rv.Stdin = this.Stdin
	rv.Stdout = this.Stdout
	rv.Stderr = this.Stderr
	rv.HookCount = this.HookCount
	rv.Tag = this.Tag
	rv.PipeSeq = rv.PipeSeq
	rv.Closers = nil
	rv.OnClone = this.OnClone
	if this.OnClone != nil {
		if err := this.OnClone(rv); err != nil {
			return nil, err
		}
	}
	return rv, nil
}

type ArgsHookT func(it *Interpreter, args []string) ([]string, error)

var argsHook = func(it *Interpreter, args []string) ([]string, error) {
	return args, nil
}

func SetArgsHook(argsHook_ ArgsHookT) (rv ArgsHookT) {
	rv, argsHook = argsHook, argsHook_
	return
}

type HookT func(*Interpreter) (ErrorLevel, error)

var hook = func(*Interpreter) (ErrorLevel, error) {
	return THROUGH, nil
}

func SetHook(hook_ HookT) (rv HookT) {
	rv, hook = hook, hook_
	return
}

var OnCommandNotFound = func(this *Interpreter, err error) error {
	err = &CommandNotFound{this.Args[0], err}
	return err
}

var ErrorLevelStr string

func nvl(a *os.File, b *os.File) *os.File {
	if a != nil {
		return a
	} else {
		return b
	}
}

func (this *Interpreter) spawnvp_noerrmsg() (ErrorLevel, error) {
	// command is empty.
	if len(this.Args) <= 0 {
		return NOERROR, nil
	}
	if dbg {
		print("spawnvp_noerrmsg('", this.Args[0], "')\n")
	}

	// aliases and lua-commands
	if errorlevel, err := hook(this); errorlevel != THROUGH || err != nil {
		return errorlevel, err
	}

	// command not found hook
	var err error
	this.Path, err = exec.LookPath(this.Args[0])
	if err != nil {
		return ErrorLevel(255), OnCommandNotFound(this, err)
	}

	if WildCardExpansionAlways {
		this.Args = findfile.Globs(this.Args)
	}

	// executable-file
	if FLAG_AMP2NEWCONSOLE {
		if this.SysProcAttr != nil && (this.SysProcAttr.CreationFlags&CREATE_NEW_CONSOLE) != 0 {
			err = this.Start()
			return ErrorLevel(0), err
		}
	}
	err = this.Run()

	errorlevel, errorlevelOk := dos.GetErrorLevel(&this.Cmd)
	if errorlevelOk {
		return ErrorLevel(errorlevel), err
	} else {
		return ErrorLevel(255), err
	}
}

func (this *Interpreter) Spawnvp() (ErrorLevel, error) {
	errorlevel, err := this.spawnvp_noerrmsg()
	if err != nil {
		fmt.Fprintln(this.Stderr, err.Error())
	}
	return errorlevel, err
}

type result_t struct {
	NextValue ErrorLevel
	Error     error
}

var pipeSeq uint = 0

func (this *Interpreter) Interpret(text string) (errorlevel ErrorLevel, err error) {
	if dbg {
		print("Interpret('", text, "')\n")
	}
	if this == nil {
		return ErrorLevel(255), errors.New("Fatal Error: Interpret: instance is nil")
	}
	errorlevel = NOERROR
	err = nil

	statements, statementsErr := Parse(text)
	if statementsErr != nil {
		if dbg {
			print("Parse Error:", statementsErr.Error(), "\n")
		}
		return NOERROR, statementsErr
	}
	if argsHook != nil {
		if dbg {
			print("call argsHook\n")
		}
		for _, pipeline := range statements {
			for _, state := range pipeline {
				state.Args, err = argsHook(this, state.Args)
				if err != nil {
					return ErrorLevel(255), err
				}
			}
		}
		if dbg {
			print("done argsHook\n")
		}
	}
	for _, pipeline := range statements {
		for i, state := range pipeline {
			if state.Term == "|" && (i+1 >= len(pipeline) || len(pipeline[i+1].Args) <= 0) {
				return ErrorLevel(255), errors.New("The syntax of the command is incorrect.")
			}
		}
	}

	for _, pipeline := range statements {

		var pipeIn *os.File = nil
		pipeSeq++
		isBackGround := this.IsBackGround
		for _, state := range pipeline {
			if state.Term == "&" {
				isBackGround = true
				break
			}
		}
		var wg sync.WaitGroup
		for i, state := range pipeline {
			if dbg {
				print(i, ": pipeline loop(", state.Args[0], ")\n")
			}
			cmd := new(Interpreter)
			cmd.PipeSeq[0] = pipeSeq
			cmd.PipeSeq[1] = uint(1 + i)
			cmd.IsBackGround = isBackGround
			cmd.Tag = this.Tag
			cmd.HookCount = this.HookCount
			cmd.SetStdin(nvl(this.Stdio[0], os.Stdin))
			cmd.SetStdout(nvl(this.Stdio[1], os.Stdout))
			cmd.SetStderr(nvl(this.Stdio[2], os.Stderr))
			cmd.OnClone = this.OnClone
			if this.OnClone != nil {
				if err := this.OnClone(cmd); err != nil {
					return ErrorLevel(255), err
				}
			}

			var err error = nil

			if pipeIn != nil {
				cmd.SetStdin(pipeIn)
				cmd.Closers = append(cmd.Closers, pipeIn)
				pipeIn = nil
			}

			if state.Term[0] == '|' {
				var pipeOut *os.File
				pipeIn, pipeOut, err = os.Pipe()
				cmd.SetStdout(pipeOut)
				if state.Term == "|&" {
					cmd.SetStderr(pipeOut)
				}
				cmd.Closers = append(cmd.Closers, pipeOut)
			}

			for _, red := range state.Redirect {
				var fd *os.File
				fd, err = red.OpenOn(cmd)
				if err != nil {
					return NOERROR, err
				}
				defer fd.Close()
			}

			cmd.Args = state.Args
			cmd.RawArgs = state.RawArgs
			if i > 0 {
				cmd.IsBackGround = true
			}
			if i == len(pipeline)-1 && state.Term != "&" {
				errorlevel, err = cmd.Spawnvp()
				ErrorLevelStr = errorlevel.String()
				cmd.Close()
			} else {
				if !isBackGround {
					wg.Add(1)
				}
				go func(cmd1 *Interpreter) {
					if isBackGround {
						if FLAG_AMP2NEWCONSOLE {
							if len(pipeline) == 1 {
								cmd1.SysProcAttr = &syscall.SysProcAttr{
									CreationFlags: CREATE_NEW_CONSOLE |
										CREATE_NEW_PROCESS_GROUP,
								}
							}
						}
					} else {
						defer wg.Done()
					}
					cmd1.Spawnvp()
					cmd1.Close()
				}(cmd)
			}
		}
		if !isBackGround {
			wg.Wait()
			if len(pipeline) > 0 {
				switch pipeline[len(pipeline)-1].Term {
				case "&&":
					if errorlevel != 0 {
						return errorlevel, nil
					}
				case "||":
					if errorlevel == 0 {
						return errorlevel, nil
					}
				}
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>package ircclient

// Handles basic IRC protocol messages (like PING)

import (
	"log"
	"time"
)

type basicProtocol struct {
	ic       *IRCClient
	timer    *time.Timer
	lastping int64
	done     chan bool
}

func (bp *basicProtocol) Register(cl *IRCClient) {
	bp.ic = cl
	bp.done = make(chan bool)
	// Send a PING message every few minutes to detect locked-up
	// server connection
	go func() {
		for {
			sleep := time.NewTimer(120e9)
			select {
			case _ = <-bp.done:
				return
			case _ = <-sleep.C:
			}
			if bp.lastping != 0 {
				continue
			}
			bp.lastping = time.Seconds()
			bp.ic.conn.Output <- "PING :client"
			bp.timer = time.NewTimer(60e9) // TODO
			go func() {
				select {
				case _ = <-bp.timer.C:
					bp.ic.Disconnect("(Client) timer expired")
				}
			}()
		}
	}()
}
func (bp *basicProtocol) String() string {
	return "basic"
}
func (bp *basicProtocol) ProcessLine(msg *IRCMessage) {
	switch msg.Command {
	case "PING":
		if len(msg.Args) != 1 {
			log.Printf("WARNING: Invalid PING received")
		}
		bp.ic.conn.Output <- "PONG :" + msg.Args[0]
	case "PONG":
		bp.lastping = 0
		bp.timer.Stop()
	}
}
func (bp *basicProtocol) Unregister() {
	bp.done <- true
}

func (bp *basicProtocol) Info() string {
	return "basic irc protocol (e.g. PING), implemented as plugin."
}

func (bp *basicProtocol) ProcessCommand(cmd *IRCCommand) {
	// TODO
}
<commit_msg>ircclient/basicprotocol: add Usage<commit_after>package ircclient

// Handles basic IRC protocol messages (like PING)

import (
	"log"
	"time"
)

type basicProtocol struct {
	ic       *IRCClient
	timer    *time.Timer
	lastping int64
	done     chan bool
}

func (bp *basicProtocol) Register(cl *IRCClient) {
	bp.ic = cl
	bp.done = make(chan bool)
	// Send a PING message every few minutes to detect locked-up
	// server connection
	go func() {
		for {
			sleep := time.NewTimer(120e9)
			select {
			case _ = <-bp.done:
				return
			case _ = <-sleep.C:
			}
			if bp.lastping != 0 {
				continue
			}
			bp.lastping = time.Seconds()
			bp.ic.conn.Output <- "PING :client"
			bp.timer = time.NewTimer(60e9) // TODO
			go func() {
				select {
				case _ = <-bp.timer.C:
					bp.ic.Disconnect("(Client) timer expired")
				}
			}()
		}
	}()
}

func (bp *basicProtocol) String() string {
	return "basic"
}

func (bp *basicProtocol) Usage(cmd string) string {
	// stub, no commands here
}

func (bp *basicProtocol) ProcessLine(msg *IRCMessage) {
	switch msg.Command {
	case "PING":
		if len(msg.Args) != 1 {
			log.Printf("WARNING: Invalid PING received")
		}
		bp.ic.conn.Output <- "PONG :" + msg.Args[0]
	case "PONG":
		bp.lastping = 0
		bp.timer.Stop()
	}
}
func (bp *basicProtocol) Unregister() {
	bp.done <- true
}

func (bp *basicProtocol) Info() string {
	return "basic irc protocol (e.g. PING), implemented as plugin."
}

func (bp *basicProtocol) ProcessCommand(cmd *IRCCommand) {
	// TODO
}
<|endoftext|>


<commit_before>package redis_test

type CleanupFunc func()
type CleanupFuncs []CleanupFunc

func (fns CleanupFuncs) Cleanup() {
	for i := len(fns) - 1; i >= 0; i-- {
		fns[i]()
	}
}

func (fns *CleanupFuncs) Register(fn CleanupFunc) {
	(*fns) = append(*fns, fn)
}
<commit_msg>remove unused file<commit_after><|endoftext|>


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

// This file defines a refactoring to rename variables, functions, methods,
// structs, interfaces, and packages.

package refactoring

import (
	"go/ast"
	"regexp"
	"runtime"
	//"fmt"
	"go/parser"
	"go/token"
	"io/ioutil"
	"path/filepath"
	"strings"

	"code.google.com/p/go.tools/go/types"
	"golang-refactoring.org/go-doctor/analysis/names"
	"golang-refactoring.org/go-doctor/filesystem"
	"golang-refactoring.org/go-doctor/text"
)

// A Rename refactoring is used to rename identifiers in Go programs.
type Rename struct {
	refactoringBase
	newName   string
	signature *types.Signature
}

func (r *Rename) Description() *Description {
	return &Description{
		Name:      "Rename",
		Synopsis:  "Changes the name of an identifier",
		Usage:     "<new_name>",
		Multifile: true,
		Params: []Parameter{Parameter{
			Label:        "New Name:",
			Prompt:       "What to rename this identifier to.",
			DefaultValue: "",
		}},
		Quality: Testing,
	}
}

func (r *Rename) Run(config *Config) *Result {
	r.refactoringBase.Run(config)
	if !validateArgs(config, r.Description(), r.Log) {
		return &r.Result
	}
	r.Log.ChangeInitialErrorsToWarnings()
	if r.Log.ContainsErrors() {
		return &r.Result
	}

	r.newName = config.Args[0].(string)
	if !r.isIdentifierValid(r.newName) {
		r.Log.Errorf("The new name %s is not a valid Go identifier", r.newName)
		return &r.Result
	}

	if r.selectedNode == nil {
		r.Log.Error("Please select an identifier to rename.")
		r.Log.AssociatePos(r.selectionStart, r.selectionEnd)
		return &r.Result
	}

	if r.newName == "" {
		r.Log.Error("newName cannot be empty")
		return &r.Result
	}

	switch ident := r.selectedNode.(type) {
	case *ast.Ident:
		if ast.IsExported(ident.Name) && !ast.IsExported(r.newName) {
			r.Log.Error("newName cannot be non Exportable if selected identifier name is Exportable")
			return &r.Result
		}
		if ident.Name == "main" && r.pkgInfo(r.fileContaining(ident)).Pkg.Name() == "main" {
			r.Log.Error("cannot rename main function inside main package ,it eliminates the program entry 							point")
			r.Log.AssociateNode(ident)
			return &r.Result
		}

		r.rename(ident)
		r.updateLog(config, false)
	case *ast.BasicLit:
		for pkg, _ := range r.program.AllPackages {
			if pkg.Name() == strings.Replace(ident.Value, "\"", "", 2) {
				search := names.NewSearchEngine(r.program)
				searchResult := search.PackageRename(pkg.Name())
				r.addOccurrences(searchResult)
				r.addFileSystemChanges(searchResult, pkg.Name())
			}
		}
		r.updateLog(config, false)
	default:
		r.Log.Error("Please select an identifier to rename.")
		r.Log.AssociatePos(r.selectionStart, r.selectionEnd)
	}
	return &r.Result
}

func (r *Rename) isIdentifierValid(newName string) bool {
	matched, err := regexp.MatchString("^\\p{L}[\\p{L}\\p{N}]*$", newName)
	if matched && err == nil {
		keyword, err := regexp.MatchString("^(break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go|goto|if|import|interface|map|package|range|return|select|struct|switch|type|var)$", newName)
		return !keyword && err == nil
	}
	return false
}

func (r *Rename) rename(ident *ast.Ident) {
	if !r.identExists(ident) {
		search := names.NewSearchEngine(r.program)
		searchResult, err := search.FindOccurrences(ident)
		if err != nil {
			r.Log.Error(err)
			return
		}

		r.addOccurrences(searchResult)
		if search.IsPackageName(ident) {
			r.addFileSystemChanges(searchResult, ident.Name)
		}
		//TODO: r.checkForErrors()
		return
	}

}

//IdentifierExists checks if there already exists an Identifier with the newName,with in the scope of the oldname.
func (r *Rename) identExists(ident *ast.Ident) bool {

	obj := r.pkgInfo(r.fileContaining(ident)).ObjectOf(ident)
	search := names.NewSearchEngine(r.program)

	if obj == nil && !search.IsPackageName(ident) {

		r.Log.Error("unable to find declaration of selected identifier")
		r.Log.AssociateNode(ident)
		return true
	}

	if search.IsPackageName(ident) {
		return false
	}
	identscope := obj.Parent()

	if names.IsMethod(obj) {
		objfound, _, pointerindirections := types.LookupFieldOrMethod(names.MethodReceiver(obj).Type(), obj.Pkg(), r.newName)
		if names.IsMethod(objfound) && pointerindirections {
			r.Log.Error("newname already exists in scope,please select other value for the newname")
			r.Log.AssociateNode(ident)
			return true
		} else {
			return false
		}
	}

	if identscope.LookupParent(r.newName) != nil {
		r.Log.Error("newname already exists in scope,please select other value for the newname")
		r.Log.AssociateNode(ident)
		return true
	}

	return false
}

//addOccurrences adds all the Occurences to the editset
func (r *Rename) addOccurrences(allOccurrences map[string][]text.Extent) {
	for filename, occurrences := range allOccurrences {
		if isInGoRoot(filename) {
			r.Log.Warnf("Occurrences in %s will not be renamed",
				filename)
		} else {
			for _, occurrence := range occurrences {
				if r.Edits[filename] == nil {
					r.Edits[filename] = text.NewEditSet()
				}
				r.Edits[filename].Add(occurrence, r.newName)
			}
		}
	}
}

func isInGoRoot(absPath string) bool {
	goRoot := runtime.GOROOT()
	if !strings.HasSuffix(goRoot, string(filepath.Separator)) {
		goRoot += string(filepath.Separator)
	}
	return strings.HasPrefix(absPath, goRoot)
}

func (r *Rename) addFileSystemChanges(allOccurrences map[string][]text.Extent, identName string) {
	for filename, _ := range allOccurrences {

		if filepath.Base(filepath.Dir(filename)) == identName && allFilesinDirectoryhaveSamePkg(filepath.Dir(filename), identName) {
			chg := &filesystem.Rename{filepath.Dir(filename), r.newName}
			r.FSChanges = append(r.FSChanges, chg)
		}
	}
}

func allFilesinDirectoryhaveSamePkg(directorypath string, identName string) bool {

	var renamefile bool = false
	fileInfos, _ := ioutil.ReadDir(directorypath)

	for _, file := range fileInfos {
		if strings.HasSuffix(file.Name(), ".go") {
			fset := token.NewFileSet()
			f, err := parser.ParseFile(fset, filepath.Join(directorypath, file.Name()), nil, 0)
			if err != nil {
				panic(err)
			}
			if f.Name.Name == identName {
				renamefile = true
			}
		}
	}

	return renamefile
}
<commit_msg>Updates for latest go.tools, see below<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file defines a refactoring to rename variables, functions, methods,
// structs, interfaces, and packages.

package refactoring

import (
	"go/ast"
	"regexp"
	"runtime"
	//"fmt"
	"go/parser"
	"go/token"
	"io/ioutil"
	"path/filepath"
	"strings"

	"code.google.com/p/go.tools/go/types"
	"golang-refactoring.org/go-doctor/analysis/names"
	"golang-refactoring.org/go-doctor/filesystem"
	"golang-refactoring.org/go-doctor/text"
)

// A Rename refactoring is used to rename identifiers in Go programs.
type Rename struct {
	refactoringBase
	newName   string
	signature *types.Signature
}

func (r *Rename) Description() *Description {
	return &Description{
		Name:      "Rename",
		Synopsis:  "Changes the name of an identifier",
		Usage:     "<new_name>",
		Multifile: true,
		Params: []Parameter{Parameter{
			Label:        "New Name:",
			Prompt:       "What to rename this identifier to.",
			DefaultValue: "",
		}},
		Quality: Testing,
	}
}

func (r *Rename) Run(config *Config) *Result {
	r.refactoringBase.Run(config)
	if !validateArgs(config, r.Description(), r.Log) {
		return &r.Result
	}
	r.Log.ChangeInitialErrorsToWarnings()
	if r.Log.ContainsErrors() {
		return &r.Result
	}

	r.newName = config.Args[0].(string)
	if !r.isIdentifierValid(r.newName) {
		r.Log.Errorf("The new name %s is not a valid Go identifier", r.newName)
		return &r.Result
	}

	if r.selectedNode == nil {
		r.Log.Error("Please select an identifier to rename.")
		r.Log.AssociatePos(r.selectionStart, r.selectionEnd)
		return &r.Result
	}

	if r.newName == "" {
		r.Log.Error("newName cannot be empty")
		return &r.Result
	}

	switch ident := r.selectedNode.(type) {
	case *ast.Ident:
		if ast.IsExported(ident.Name) && !ast.IsExported(r.newName) {
			r.Log.Error("newName cannot be non Exportable if selected identifier name is Exportable")
			return &r.Result
		}
		if ident.Name == "main" && r.pkgInfo(r.fileContaining(ident)).Pkg.Name() == "main" {
			r.Log.Error("cannot rename main function inside main package ,it eliminates the program entry 							point")
			r.Log.AssociateNode(ident)
			return &r.Result
		}

		r.rename(ident)
		r.updateLog(config, false)
	case *ast.BasicLit:
		for pkg, _ := range r.program.AllPackages {
			if pkg.Name() == strings.Replace(ident.Value, "\"", "", 2) {
				search := names.NewSearchEngine(r.program)
				searchResult := search.PackageRename(pkg.Name())
				r.addOccurrences(searchResult)
				r.addFileSystemChanges(searchResult, pkg.Name())
			}
		}
		r.updateLog(config, false)
	default:
		r.Log.Error("Please select an identifier to rename.")
		r.Log.AssociatePos(r.selectionStart, r.selectionEnd)
	}
	return &r.Result
}

func (r *Rename) isIdentifierValid(newName string) bool {
	matched, err := regexp.MatchString("^\\p{L}[\\p{L}\\p{N}]*$", newName)
	if matched && err == nil {
		keyword, err := regexp.MatchString("^(break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go|goto|if|import|interface|map|package|range|return|select|struct|switch|type|var)$", newName)
		return !keyword && err == nil
	}
	return false
}

func (r *Rename) rename(ident *ast.Ident) {
	if !r.identExists(ident) {
		search := names.NewSearchEngine(r.program)
		searchResult, err := search.FindOccurrences(ident)
		if err != nil {
			r.Log.Error(err)
			return
		}

		r.addOccurrences(searchResult)
		if search.IsPackageName(ident) {
			r.addFileSystemChanges(searchResult, ident.Name)
		}
		//TODO: r.checkForErrors()
		return
	}

}

//IdentifierExists checks if there already exists an Identifier with the newName,with in the scope of the oldname.
func (r *Rename) identExists(ident *ast.Ident) bool {
	obj := r.pkgInfo(r.fileContaining(ident)).ObjectOf(ident)
	search := names.NewSearchEngine(r.program)

	if obj == nil && !search.IsPackageName(ident) {

		r.Log.Error("unable to find declaration of selected identifier")
		r.Log.AssociateNode(ident)
		return true
	}

	if search.IsPackageName(ident) {
		return false
	}
	identscope := obj.Parent()

	if names.IsMethod(obj) {
		objfound, _, pointerindirections := types.LookupFieldOrMethod(names.MethodReceiver(obj).Type(), true, obj.Pkg(), r.newName)
		if names.IsMethod(objfound) && pointerindirections {
			r.Log.Error("newname already exists in scope,please select other value for the newname")
			r.Log.AssociateNode(ident)
			return true
		} else {
			return false
		}
	}

	if identscope.LookupParent(r.newName) != nil {
		r.Log.Error("newname already exists in scope,please select other value for the newname")
		r.Log.AssociateNode(ident)
		return true
	}

	return false
}

//addOccurrences adds all the Occurences to the editset
func (r *Rename) addOccurrences(allOccurrences map[string][]text.Extent) {
	for filename, occurrences := range allOccurrences {
		if isInGoRoot(filename) {
			r.Log.Warnf("Occurrences in %s will not be renamed",
				filename)
		} else {
			for _, occurrence := range occurrences {
				if r.Edits[filename] == nil {
					r.Edits[filename] = text.NewEditSet()
				}
				r.Edits[filename].Add(occurrence, r.newName)
			}
		}
	}
}

func isInGoRoot(absPath string) bool {
	goRoot := runtime.GOROOT()
	if !strings.HasSuffix(goRoot, string(filepath.Separator)) {
		goRoot += string(filepath.Separator)
	}
	return strings.HasPrefix(absPath, goRoot)
}

func (r *Rename) addFileSystemChanges(allOccurrences map[string][]text.Extent, identName string) {
	for filename, _ := range allOccurrences {

		if filepath.Base(filepath.Dir(filename)) == identName && allFilesinDirectoryhaveSamePkg(filepath.Dir(filename), identName) {
			chg := &filesystem.Rename{filepath.Dir(filename), r.newName}
			r.FSChanges = append(r.FSChanges, chg)
		}
	}
}

func allFilesinDirectoryhaveSamePkg(directorypath string, identName string) bool {

	var renamefile bool = false
	fileInfos, _ := ioutil.ReadDir(directorypath)

	for _, file := range fileInfos {
		if strings.HasSuffix(file.Name(), ".go") {
			fset := token.NewFileSet()
			f, err := parser.ParseFile(fset, filepath.Join(directorypath, file.Name()), nil, 0)
			if err != nil {
				panic(err)
			}
			if f.Name.Name == identName {
				renamefile = true
			}
		}
	}

	return renamefile
}
<|endoftext|>