<commit_before>package services

import (
	"fmt"
	"strings"

	"github.com/racker/perigee"
	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"
)

// ListOptsBuilder allows extensions to add additional parameters to the
// List request.
type ListOptsBuilder interface {
	ToCDNServiceListQuery() (string, error)
}

// ListOpts allows the filtering and sorting of paginated collections through
// the API. Marker and Limit are used for pagination.
type ListOpts struct {
	Marker string `q:"marker"`
	Limit  int    `q:"limit"`
}

// ToCDNServiceListQuery formats a ListOpts into a query string.
func (opts ListOpts) ToCDNServiceListQuery() (string, error) {
	q, err := gophercloud.BuildQueryString(opts)
	if err != nil {
		return "", err
	}
	return q.String(), nil
}

// List returns a Pager which allows you to iterate over a collection of
// CDN services. It accepts a ListOpts struct, which allows for pagination via
// marker and limit.
func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
	url := listURL(c)
	if opts != nil {
		query, err := opts.ToCDNServiceListQuery()
		if err != nil {
			return pagination.Pager{Err: err}
		}
		url += query
	}

	createPage := func(r pagination.PageResult) pagination.Page {
		p := ServicePage{pagination.MarkerPageBase{PageResult: r}}
		p.MarkerPageBase.Owner = p
		return p
	}

	pager := pagination.NewPager(c, url, createPage)
	return pager
}

// CreateOptsBuilder is the interface options structs have to satisfy in order
// to be used in the main Create operation in this package. Since many
// extensions decorate or modify the common logic, it is useful for them to
// satisfy a basic interface in order for them to be used.
type CreateOptsBuilder interface {
	ToCDNServiceCreateMap() (map[string]interface{}, error)
}

// CreateOpts is the common options struct used in this package's Create
// operation.
type CreateOpts struct {
	// REQUIRED. Specifies the name of the service. The minimum length for name is
	// 3. The maximum length is 256.
	Name string
	// REQUIRED. Specifies a list of domains used by users to access their website.
	Domains []Domain
	// REQUIRED. Specifies a list of origin domains or IP addresses where the
	// original assets are stored.
	Origins []Origin
	// REQUIRED. Specifies the CDN provider flavor ID to use. For a list of
	// flavors, see the operation to list the available flavors. The minimum
	// length for flavor_id is 1. The maximum length is 256.
	FlavorID string
	// OPTIONAL. Specifies the TTL rules for the assets under this service. Supports wildcards for fine-grained control.
	Caching []CacheRule
	// OPTIONAL. Specifies the restrictions that define who can access assets (content from the CDN cache).
	Restrictions []Restriction
}

// ToCDNServiceCreateMap casts a CreateOpts struct to a map.
func (opts CreateOpts) ToCDNServiceCreateMap() (map[string]interface{}, error) {
	s := make(map[string]interface{})

	if opts.Name == "" {
		return nil, no("Name")
	}
	s["name"] = opts.Name

	if opts.Domains == nil {
		return nil, no("Domains")
	}
	for _, domain := range opts.Domains {
		if domain.Domain == "" {
			return nil, no("Domains[].Domain")
		}
	}
	s["domains"] = opts.Domains

	if opts.Origins == nil {
		return nil, no("Origins")
	}
	for _, origin := range opts.Origins {
		if origin.Origin == "" {
			return nil, no("Origins[].Origin")
		}
		if origin.Rules == nil && len(opts.Origins) > 1 {
			return nil, no("Origins[].Rules")
		}
		for _, rule := range origin.Rules {
			if rule.Name == "" {
				return nil, no("Origins[].Rules[].Name")
			}
			if rule.RequestURL == "" {
				return nil, no("Origins[].Rules[].RequestURL")
			}
		}
	}
	s["origins"] = opts.Origins

	if opts.FlavorID == "" {
		return nil, no("FlavorID")
	}
	s["flavor_id"] = opts.FlavorID

	if opts.Caching != nil {
		for _, cache := range opts.Caching {
			if cache.Name == "" {
				return nil, no("Caching[].Name")
			}
			if cache.Rules != nil {
				for _, rule := range cache.Rules {
					if rule.Name == "" {
						return nil, no("Caching[].Rules[].Name")
					}
					if rule.RequestURL == "" {
						return nil, no("Caching[].Rules[].RequestURL")
					}
				}
			}
		}
		s["caching"] = opts.Caching
	}

	if opts.Restrictions != nil {
		for _, restriction := range opts.Restrictions {
			if restriction.Name == "" {
				return nil, no("Restrictions[].Name")
			}
			if restriction.Rules != nil {
				for _, rule := range restriction.Rules {
					if rule.Name == "" {
						return nil, no("Restrictions[].Rules[].Name")
					}
				}
			}
		}
		s["restrictions"] = opts.Restrictions
	}

	return s, nil
}

// Create accepts a CreateOpts struct and creates a new CDN service using the
// values provided.
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
	var res CreateResult

	reqBody, err := opts.ToCDNServiceCreateMap()
	if err != nil {
		res.Err = err
		return res
	}

	// Send request to API
	resp, err := perigee.Request("POST", createURL(c), perigee.Options{
		MoreHeaders: c.AuthenticatedHeaders(),
		ReqBody:     &reqBody,
		OkCodes:     []int{202},
	})
	res.Header = resp.HttpResponse.Header
	res.Err = err
	return res
}

// Get retrieves a specific service based on its URL or its unique ID. For
// example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and
// "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
// are valid options for idOrURL.
func Get(c *gophercloud.ServiceClient, idOrURL string) GetResult {
	var url string
	if strings.Contains(idOrURL, "/") {
		url = idOrURL
	} else {
		url = getURL(c, idOrURL)
	}

	var res GetResult
	_, res.Err = perigee.Request("GET", url, perigee.Options{
		MoreHeaders: c.AuthenticatedHeaders(),
		Results:     &res.Body,
		OkCodes:     []int{200},
	})
	return res
}

// UpdateOptsBuilder is the interface options structs have to satisfy in order
// to be used in the main Update operation in this package. Since many
// extensions decorate or modify the common logic, it is useful for them to
// satisfy a basic interface in order for them to be used.
type UpdateOptsBuilder interface {
	ToCDNServiceUpdateMap() ([]map[string]interface{}, error)
}

// Op represents an update operation.
type Op string

var (
	// Add is a constant used for performing a "add" operation when updating.
	Add Op = "add"
	// Remove is a constant used for performing a "remove" operation when updating.
	Remove Op = "remove"
	// Replace is a constant used for performing a "replace" operation when updating.
	Replace Op = "replace"
)

// UpdateOpts represents the attributes used when updating an existing CDN service.
type UpdateOpts []UpdateOpt

// UpdateOpt represents a single update to an existing service. Multiple updates
// to a service can be submitted at the same time. See UpdateOpts.
type UpdateOpt struct {
	// Specifies the update operation to perform.
	Op Op `json:"op"`
	// Specifies the JSON Pointer location within the service's JSON representation
	// of the service parameter being added, replaced or removed.
	Path string `json:"path"`
	// Specifies the actual value to be added or replaced. It is not required for
	// the remove operation.
	Value map[string]interface{} `json:"value,omitempty"`
}

// ToCDNServiceUpdateMap casts an UpdateOpts struct to a map.
func (opts UpdateOpts) ToCDNServiceUpdateMap() ([]map[string]interface{}, error) {
	s := make([]map[string]interface{}, len(opts))

	for i, opt := range opts {
		if opt.Op != Add && opt.Op != Remove && opt.Op != Replace {
			return nil, fmt.Errorf("Invalid Op: %v", opt.Op)
		}
		if opt.Op == "" {
			return nil, no("Op")
		}
		if opt.Path == "" {
			return nil, no("Path")
		}
		if opt.Op != Remove && opt.Value == nil {
			return nil, no("Value")
		}
		s[i] = map[string]interface{}{
			"op":    opt.Op,
			"path":  opt.Path,
			"value": opt.Value,
		}
	}

	return s, nil
}

// Update accepts a UpdateOpts struct and updates an existing CDN service using
// the values provided. idOrURL can be either the service's URL or its ID. For
// example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and
// "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
// are valid options for idOrURL.
func Update(c *gophercloud.ServiceClient, idOrURL string, opts UpdateOptsBuilder) UpdateResult {
	var url string
	if strings.Contains(idOrURL, "/") {
		url = idOrURL
	} else {
		url = updateURL(c, idOrURL)
	}

	var res UpdateResult
	reqBody, err := opts.ToCDNServiceUpdateMap()
	if err != nil {
		res.Err = err
		return res
	}

	resp, err := perigee.Request("PATCH", url, perigee.Options{
		MoreHeaders: c.AuthenticatedHeaders(),
		ReqBody:     &reqBody,
		OkCodes:     []int{202},
	})
	res.Header = resp.HttpResponse.Header
	res.Err = err
	return res
}

// Delete accepts a service's ID or its URL and deletes the CDN service
// associated with it. For example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and
// "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
// are valid options for idOrURL.
func Delete(c *gophercloud.ServiceClient, idOrURL string) DeleteResult {
	var url string
	if strings.Contains(idOrURL, "/") {
		url = idOrURL
	} else {
		url = deleteURL(c, idOrURL)
	}

	var res DeleteResult
	_, res.Err = perigee.Request("DELETE", url, perigee.Options{
		MoreHeaders: c.AuthenticatedHeaders(),
		OkCodes:     []int{202},
	})
	return res
}
<commit_msg>Move the Path type over and doc it.<commit_after>package services

import (
	"fmt"
	"strings"

	"github.com/racker/perigee"
	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"
)

// ListOptsBuilder allows extensions to add additional parameters to the
// List request.
type ListOptsBuilder interface {
	ToCDNServiceListQuery() (string, error)
}

// ListOpts allows the filtering and sorting of paginated collections through
// the API. Marker and Limit are used for pagination.
type ListOpts struct {
	Marker string `q:"marker"`
	Limit  int    `q:"limit"`
}

// ToCDNServiceListQuery formats a ListOpts into a query string.
func (opts ListOpts) ToCDNServiceListQuery() (string, error) {
	q, err := gophercloud.BuildQueryString(opts)
	if err != nil {
		return "", err
	}
	return q.String(), nil
}

// List returns a Pager which allows you to iterate over a collection of
// CDN services. It accepts a ListOpts struct, which allows for pagination via
// marker and limit.
func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
	url := listURL(c)
	if opts != nil {
		query, err := opts.ToCDNServiceListQuery()
		if err != nil {
			return pagination.Pager{Err: err}
		}
		url += query
	}

	createPage := func(r pagination.PageResult) pagination.Page {
		p := ServicePage{pagination.MarkerPageBase{PageResult: r}}
		p.MarkerPageBase.Owner = p
		return p
	}

	pager := pagination.NewPager(c, url, createPage)
	return pager
}

// CreateOptsBuilder is the interface options structs have to satisfy in order
// to be used in the main Create operation in this package. Since many
// extensions decorate or modify the common logic, it is useful for them to
// satisfy a basic interface in order for them to be used.
type CreateOptsBuilder interface {
	ToCDNServiceCreateMap() (map[string]interface{}, error)
}

// CreateOpts is the common options struct used in this package's Create
// operation.
type CreateOpts struct {
	// REQUIRED. Specifies the name of the service. The minimum length for name is
	// 3. The maximum length is 256.
	Name string
	// REQUIRED. Specifies a list of domains used by users to access their website.
	Domains []Domain
	// REQUIRED. Specifies a list of origin domains or IP addresses where the
	// original assets are stored.
	Origins []Origin
	// REQUIRED. Specifies the CDN provider flavor ID to use. For a list of
	// flavors, see the operation to list the available flavors. The minimum
	// length for flavor_id is 1. The maximum length is 256.
	FlavorID string
	// OPTIONAL. Specifies the TTL rules for the assets under this service. Supports wildcards for fine-grained control.
	Caching []CacheRule
	// OPTIONAL. Specifies the restrictions that define who can access assets (content from the CDN cache).
	Restrictions []Restriction
}

// ToCDNServiceCreateMap casts a CreateOpts struct to a map.
func (opts CreateOpts) ToCDNServiceCreateMap() (map[string]interface{}, error) {
	s := make(map[string]interface{})

	if opts.Name == "" {
		return nil, no("Name")
	}
	s["name"] = opts.Name

	if opts.Domains == nil {
		return nil, no("Domains")
	}
	for _, domain := range opts.Domains {
		if domain.Domain == "" {
			return nil, no("Domains[].Domain")
		}
	}
	s["domains"] = opts.Domains

	if opts.Origins == nil {
		return nil, no("Origins")
	}
	for _, origin := range opts.Origins {
		if origin.Origin == "" {
			return nil, no("Origins[].Origin")
		}
		if origin.Rules == nil && len(opts.Origins) > 1 {
			return nil, no("Origins[].Rules")
		}
		for _, rule := range origin.Rules {
			if rule.Name == "" {
				return nil, no("Origins[].Rules[].Name")
			}
			if rule.RequestURL == "" {
				return nil, no("Origins[].Rules[].RequestURL")
			}
		}
	}
	s["origins"] = opts.Origins

	if opts.FlavorID == "" {
		return nil, no("FlavorID")
	}
	s["flavor_id"] = opts.FlavorID

	if opts.Caching != nil {
		for _, cache := range opts.Caching {
			if cache.Name == "" {
				return nil, no("Caching[].Name")
			}
			if cache.Rules != nil {
				for _, rule := range cache.Rules {
					if rule.Name == "" {
						return nil, no("Caching[].Rules[].Name")
					}
					if rule.RequestURL == "" {
						return nil, no("Caching[].Rules[].RequestURL")
					}
				}
			}
		}
		s["caching"] = opts.Caching
	}

	if opts.Restrictions != nil {
		for _, restriction := range opts.Restrictions {
			if restriction.Name == "" {
				return nil, no("Restrictions[].Name")
			}
			if restriction.Rules != nil {
				for _, rule := range restriction.Rules {
					if rule.Name == "" {
						return nil, no("Restrictions[].Rules[].Name")
					}
				}
			}
		}
		s["restrictions"] = opts.Restrictions
	}

	return s, nil
}

// Create accepts a CreateOpts struct and creates a new CDN service using the
// values provided.
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
	var res CreateResult

	reqBody, err := opts.ToCDNServiceCreateMap()
	if err != nil {
		res.Err = err
		return res
	}

	// Send request to API
	resp, err := perigee.Request("POST", createURL(c), perigee.Options{
		MoreHeaders: c.AuthenticatedHeaders(),
		ReqBody:     &reqBody,
		OkCodes:     []int{202},
	})
	res.Header = resp.HttpResponse.Header
	res.Err = err
	return res
}

// Get retrieves a specific service based on its URL or its unique ID. For
// example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and
// "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
// are valid options for idOrURL.
func Get(c *gophercloud.ServiceClient, idOrURL string) GetResult {
	var url string
	if strings.Contains(idOrURL, "/") {
		url = idOrURL
	} else {
		url = getURL(c, idOrURL)
	}

	var res GetResult
	_, res.Err = perigee.Request("GET", url, perigee.Options{
		MoreHeaders: c.AuthenticatedHeaders(),
		Results:     &res.Body,
		OkCodes:     []int{200},
	})
	return res
}

// UpdateOptsBuilder is the interface options structs have to satisfy in order
// to be used in the main Update operation in this package. Since many
// extensions decorate or modify the common logic, it is useful for them to
// satisfy a basic interface in order for them to be used.
type UpdateOptsBuilder interface {
	ToCDNServiceUpdateMap() ([]map[string]interface{}, error)
}

// Op represents an update operation.
type Op string

var (
	// Add is a constant used for performing a "add" operation when updating.
	Add Op = "add"
	// Remove is a constant used for performing a "remove" operation when updating.
	Remove Op = "remove"
	// Replace is a constant used for performing a "replace" operation when updating.
	Replace Op = "replace"
)

// Path is a JSON pointer location that indicates which service parameter is being added, replaced,
// or removed.
type Path struct {
	baseElement string
}

func (p Path) renderDash() string {
	return fmt.Sprintf("/%s/-", p.baseElement)
}

func (p Path) renderIndex(index int64) string {
	return fmt.Sprintf("/%s/%d", p.baseElement, index)
}

var (
	// PathDomains indicates that an update operation is to be performed on a Domain.
	PathDomains = Path{baseElement: "domains"}

	// PathOrigins indicates that an update operation is to be performed on an Origin.
	PathOrigins = Path{baseElement: "origins"}

	// PathCaching indicates that an update operation is to be performed on a CacheRule.
	PathCaching = Path{baseElement: "caching"}
)

// UpdateOpts represents the attributes used when updating an existing CDN service.
type UpdateOpts []UpdateOpt

// UpdateOpt represents a single update to an existing service. Multiple updates
// to a service can be submitted at the same time. See UpdateOpts.
type UpdateOpt struct {
	// Specifies the update operation to perform.
	Op Op `json:"op"`
	// Specifies the JSON Pointer location within the service's JSON representation
	// of the service parameter being added, replaced or removed.
	Path string `json:"path"`
	// Specifies the actual value to be added or replaced. It is not required for
	// the remove operation.
	Value map[string]interface{} `json:"value,omitempty"`
}

// ToCDNServiceUpdateMap casts an UpdateOpts struct to a map.
func (opts UpdateOpts) ToCDNServiceUpdateMap() ([]map[string]interface{}, error) {
	s := make([]map[string]interface{}, len(opts))

	for i, opt := range opts {
		if opt.Op != Add && opt.Op != Remove && opt.Op != Replace {
			return nil, fmt.Errorf("Invalid Op: %v", opt.Op)
		}
		if opt.Op == "" {
			return nil, no("Op")
		}
		if opt.Path == "" {
			return nil, no("Path")
		}
		if opt.Op != Remove && opt.Value == nil {
			return nil, no("Value")
		}
		s[i] = map[string]interface{}{
			"op":    opt.Op,
			"path":  opt.Path,
			"value": opt.Value,
		}
	}

	return s, nil
}

// Update accepts a UpdateOpts struct and updates an existing CDN service using
// the values provided. idOrURL can be either the service's URL or its ID. For
// example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and
// "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
// are valid options for idOrURL.
func Update(c *gophercloud.ServiceClient, idOrURL string, opts UpdateOptsBuilder) UpdateResult {
	var url string
	if strings.Contains(idOrURL, "/") {
		url = idOrURL
	} else {
		url = updateURL(c, idOrURL)
	}

	var res UpdateResult
	reqBody, err := opts.ToCDNServiceUpdateMap()
	if err != nil {
		res.Err = err
		return res
	}

	resp, err := perigee.Request("PATCH", url, perigee.Options{
		MoreHeaders: c.AuthenticatedHeaders(),
		ReqBody:     &reqBody,
		OkCodes:     []int{202},
	})
	res.Header = resp.HttpResponse.Header
	res.Err = err
	return res
}

// Delete accepts a service's ID or its URL and deletes the CDN service
// associated with it. For example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and
// "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
// are valid options for idOrURL.
func Delete(c *gophercloud.ServiceClient, idOrURL string) DeleteResult {
	var url string
	if strings.Contains(idOrURL, "/") {
		url = idOrURL
	} else {
		url = deleteURL(c, idOrURL)
	}

	var res DeleteResult
	_, res.Err = perigee.Request("DELETE", url, perigee.Options{
		MoreHeaders: c.AuthenticatedHeaders(),
		OkCodes:     []int{202},
	})
	return res
}
<|endoftext|>


<commit_before>// Copyright 2013 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.

// +build darwin dragonfly freebsd linux netbsd openbsd windows solaris

package poll

import (
	"errors"
	"sync"
	"syscall"
	"time"
)

// runtimeNano returns the current value of the runtime clock in nanoseconds.
func runtimeNano() int64

func runtime_pollServerInit()
func runtime_pollOpen(fd uintptr) (uintptr, int)
func runtime_pollClose(ctx uintptr)
func runtime_pollWait(ctx uintptr, mode int) int
func runtime_pollWaitCanceled(ctx uintptr, mode int) int
func runtime_pollReset(ctx uintptr, mode int) int
func runtime_pollSetDeadline(ctx uintptr, d int64, mode int)
func runtime_pollUnblock(ctx uintptr)

type pollDesc struct {
	runtimeCtx uintptr
}

var serverInit sync.Once

func (pd *pollDesc) init(fd *FD) error {
	serverInit.Do(runtime_pollServerInit)
	ctx, errno := runtime_pollOpen(uintptr(fd.Sysfd))
	if errno != 0 {
		if ctx != 0 {
			runtime_pollUnblock(ctx)
			runtime_pollClose(ctx)
		}
		return syscall.Errno(errno)
	}
	pd.runtimeCtx = ctx
	return nil
}

func (pd *pollDesc) close() {
	if pd.runtimeCtx == 0 {
		return
	}
	runtime_pollClose(pd.runtimeCtx)
	pd.runtimeCtx = 0
}

// Evict evicts fd from the pending list, unblocking any I/O running on fd.
func (pd *pollDesc) evict() {
	if pd.runtimeCtx == 0 {
		return
	}
	runtime_pollUnblock(pd.runtimeCtx)
}

func (pd *pollDesc) prepare(mode int) error {
	if pd.runtimeCtx == 0 {
		return nil
	}
	res := runtime_pollReset(pd.runtimeCtx, mode)
	return convertErr(res)
}

func (pd *pollDesc) prepareRead() error {
	return pd.prepare('r')
}

func (pd *pollDesc) prepareWrite() error {
	return pd.prepare('w')
}

func (pd *pollDesc) wait(mode int) error {
	if pd.runtimeCtx == 0 {
		return errors.New("waiting for unsupported file type")
	}
	res := runtime_pollWait(pd.runtimeCtx, mode)
	return convertErr(res)
}

func (pd *pollDesc) waitRead() error {
	return pd.wait('r')
}

func (pd *pollDesc) waitWrite() error {
	return pd.wait('w')
}

func (pd *pollDesc) waitCanceled(mode int) {
	if pd.runtimeCtx == 0 {
		return
	}
	runtime_pollWaitCanceled(pd.runtimeCtx, mode)
}

func (pd *pollDesc) waitCanceledRead() {
	pd.waitCanceled('r')
}

func (pd *pollDesc) waitCanceledWrite() {
	pd.waitCanceled('w')
}

func convertErr(res int) error {
	switch res {
	case 0:
		return nil
	case 1:
		return ErrClosing
	case 2:
		return ErrTimeout
	}
	println("unreachable: ", res)
	panic("unreachable")
}

func (fd *FD) SetDeadline(t time.Time) error {
	return setDeadlineImpl(fd, t, 'r'+'w')
}

func (fd *FD) SetReadDeadline(t time.Time) error {
	return setDeadlineImpl(fd, t, 'r')
}

func (fd *FD) SetWriteDeadline(t time.Time) error {
	return setDeadlineImpl(fd, t, 'w')
}

func setDeadlineImpl(fd *FD, t time.Time, mode int) error {
	diff := int64(time.Until(t))
	d := runtimeNano() + diff
	if d <= 0 && diff > 0 {
		// If the user has a deadline in the future, but the delay calculation
		// overflows, then set the deadline to the maximum possible value.
		d = 1<<63 - 1
	}
	if t.IsZero() {
		d = 0
	}
	if err := fd.incref(); err != nil {
		return err
	}
	if fd.pd.runtimeCtx == 0 {
		return errors.New("file type does not support deadlines")
	}
	runtime_pollSetDeadline(fd.pd.runtimeCtx, d, mode)
	fd.decref()
	return nil
}
<commit_msg>internal/poll: remove unused poll.pollDesc methods<commit_after>// Copyright 2013 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.

// +build darwin dragonfly freebsd linux netbsd openbsd windows solaris

package poll

import (
	"errors"
	"sync"
	"syscall"
	"time"
)

// runtimeNano returns the current value of the runtime clock in nanoseconds.
func runtimeNano() int64

func runtime_pollServerInit()
func runtime_pollOpen(fd uintptr) (uintptr, int)
func runtime_pollClose(ctx uintptr)
func runtime_pollWait(ctx uintptr, mode int) int
func runtime_pollWaitCanceled(ctx uintptr, mode int) int
func runtime_pollReset(ctx uintptr, mode int) int
func runtime_pollSetDeadline(ctx uintptr, d int64, mode int)
func runtime_pollUnblock(ctx uintptr)

type pollDesc struct {
	runtimeCtx uintptr
}

var serverInit sync.Once

func (pd *pollDesc) init(fd *FD) error {
	serverInit.Do(runtime_pollServerInit)
	ctx, errno := runtime_pollOpen(uintptr(fd.Sysfd))
	if errno != 0 {
		if ctx != 0 {
			runtime_pollUnblock(ctx)
			runtime_pollClose(ctx)
		}
		return syscall.Errno(errno)
	}
	pd.runtimeCtx = ctx
	return nil
}

func (pd *pollDesc) close() {
	if pd.runtimeCtx == 0 {
		return
	}
	runtime_pollClose(pd.runtimeCtx)
	pd.runtimeCtx = 0
}

// Evict evicts fd from the pending list, unblocking any I/O running on fd.
func (pd *pollDesc) evict() {
	if pd.runtimeCtx == 0 {
		return
	}
	runtime_pollUnblock(pd.runtimeCtx)
}

func (pd *pollDesc) prepare(mode int) error {
	if pd.runtimeCtx == 0 {
		return nil
	}
	res := runtime_pollReset(pd.runtimeCtx, mode)
	return convertErr(res)
}

func (pd *pollDesc) prepareRead() error {
	return pd.prepare('r')
}

func (pd *pollDesc) prepareWrite() error {
	return pd.prepare('w')
}

func (pd *pollDesc) wait(mode int) error {
	if pd.runtimeCtx == 0 {
		return errors.New("waiting for unsupported file type")
	}
	res := runtime_pollWait(pd.runtimeCtx, mode)
	return convertErr(res)
}

func (pd *pollDesc) waitRead() error {
	return pd.wait('r')
}

func (pd *pollDesc) waitWrite() error {
	return pd.wait('w')
}

func (pd *pollDesc) waitCanceled(mode int) {
	if pd.runtimeCtx == 0 {
		return
	}
	runtime_pollWaitCanceled(pd.runtimeCtx, mode)
}

func convertErr(res int) error {
	switch res {
	case 0:
		return nil
	case 1:
		return ErrClosing
	case 2:
		return ErrTimeout
	}
	println("unreachable: ", res)
	panic("unreachable")
}

func (fd *FD) SetDeadline(t time.Time) error {
	return setDeadlineImpl(fd, t, 'r'+'w')
}

func (fd *FD) SetReadDeadline(t time.Time) error {
	return setDeadlineImpl(fd, t, 'r')
}

func (fd *FD) SetWriteDeadline(t time.Time) error {
	return setDeadlineImpl(fd, t, 'w')
}

func setDeadlineImpl(fd *FD, t time.Time, mode int) error {
	diff := int64(time.Until(t))
	d := runtimeNano() + diff
	if d <= 0 && diff > 0 {
		// If the user has a deadline in the future, but the delay calculation
		// overflows, then set the deadline to the maximum possible value.
		d = 1<<63 - 1
	}
	if t.IsZero() {
		d = 0
	}
	if err := fd.incref(); err != nil {
		return err
	}
	if fd.pd.runtimeCtx == 0 {
		return errors.New("file type does not support deadlines")
	}
	runtime_pollSetDeadline(fd.pd.runtimeCtx, d, mode)
	fd.decref()
	return nil
}
<|endoftext|>


<commit_before>/* Go (cgo) interface to libgeoip */
package geoip

/*
#cgo CFLAGS: -I/opt/local/include -I/usr/local/include -I/usr/include
#cgo LDFLAGS: -lGeoIP -L/opt/local/lib -L/usr/local/lib -L/usr/lib
#include <stdio.h>
#include <errno.h>
#include <GeoIP.h>
#include <GeoIPCity.h>

//typedef GeoIP* GeoIP_pnt
*/
import "C"

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"sync"
	"unsafe"
)

type GeoIP struct {
	db *C.GeoIP
	mu sync.Mutex
}

func (gi *GeoIP) free() {
	if gi == nil {
		return
	}
	if gi.db == nil {
		gi = nil
		return
	}
	C.GeoIP_delete(gi.db)
	gi = nil
	return
}

// Opens a GeoIP database by filename, all formats supported by libgeoip are
// supported though there are only functions to access some of the databases in this API.
// The database is opened in MEMORY_CACHE mode, if you need to optimize for memory
// instead of performance you should change this.
// If you don't pass a filename, it will try opening the database from
// a list of common paths.
func Open(files ...string) (*GeoIP, error) {
	if len(files) == 0 {
		files = []string{
			"/usr/share/GeoIP/GeoIP.dat",       // Linux default
			"/usr/share/local/GeoIP/GeoIP.dat", // source install?
			"/usr/local/share/GeoIP/GeoIP.dat", // FreeBSD
			"/opt/local/share/GeoIP/GeoIP.dat", // MacPorts
			"/usr/share/GeoIP/GeoIP.dat",       // ArchLinux
		}
	}

	g := &GeoIP{}
	runtime.SetFinalizer(g, (*GeoIP).free)

	var err error

	for _, file := range files {

		// libgeoip prints errors if it can't open the file, so check first
		if _, err := os.Stat(file); err != nil {
			if os.IsExist(err) {
				log.Println(err)
			}
			continue
		}

		cbase := C.CString(file)
		defer C.free(unsafe.Pointer(cbase))

		g.db, err = C.GeoIP_open(cbase, C.GEOIP_MEMORY_CACHE)
		if g.db != nil && err != nil {
			break
		}
	}
	if err != nil {
		return nil, fmt.Errorf("Error opening GeoIP database (%s): %s", files, err)
	}

	if g.db == nil {
		return nil, fmt.Errorf("Didn't open GeoIP database (%s)", files)
	}

	C.GeoIP_set_charset(g.db, C.GEOIP_CHARSET_UTF8)
	return g, nil
}

// SetCustomDirectory sets the default location for the GeoIP .dat files used when
// calling OpenType()
func SetCustomDirectory(dir string) {
	cdir := C.CString(dir)
	// GeoIP doesn't copy the string, so don't free it when we're done here.
	// defer C.free(unsafe.Pointer(cdir))
	C.GeoIP_setup_custom_directory(cdir)
}

// OpenType opens a specified GeoIP database type in the default location. Constants
// are defined for each database type (for example GEOIP_COUNTRY_EDITION).
func OpenType(dbType int) (*GeoIP, error) {
	g := &GeoIP{}
	runtime.SetFinalizer(g, (*GeoIP).free)

	var err error

	g.db, err = C.GeoIP_open_type(C.int(dbType), C.GEOIP_MEMORY_CACHE)
	if err != nil {
		return nil, fmt.Errorf("Error opening GeoIP database (%d): %s", dbType, err)
	}

	if g.db == nil {
		return nil, fmt.Errorf("Didn't open GeoIP database (%d)", dbType)
	}

	C.GeoIP_set_charset(g.db, C.GEOIP_CHARSET_UTF8)

	return g, nil
}

// Takes an IPv4 address string and returns the organization name for that IP.
// Requires the GeoIP organization database.
func (gi *GeoIP) GetOrg(ip string) string {
	name, _ := gi.GetName(ip)
	return name
}

// Works on the ASN, Netspeed, Organization and probably other
// databases, takes and IP string and returns a "name" and the
// netmask.
func (gi *GeoIP) GetName(ip string) (name string, netmask int) {
	if gi.db == nil {
		return
	}

	gi.mu.Lock()
	defer gi.mu.Unlock()

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	cname := C.GeoIP_name_by_addr(gi.db, cip)

	if cname != nil {
		name = C.GoString(cname)
		defer C.free(unsafe.Pointer(cname))
		netmask = int(C.GeoIP_last_netmask(gi.db))
		return
	}
	return
}

type GeoIPRecord struct {
	CountryCode  string
	CountryCode3 string
	CountryName  string
	Region       string
	City         string
	PostalCode   string
	Latitude     float32
	Longitude    float32
	// DMACode       int
	AreaCode      int
	CharSet       int
	ContinentCode string
}

// Returns the "City Record" for an IP address. Requires the GeoCity(Lite)
// database - http://www.maxmind.com/en/city
func (gi *GeoIP) GetRecord(ip string) *GeoIPRecord {
	if gi.db == nil {
		return nil
	}

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	record := C.GeoIP_record_by_addr(gi.db, cip)
	if record == nil {
		return nil
	}
	// defer C.free(unsafe.Pointer(record))
	defer C.GeoIPRecord_delete(record)
	rec := new(GeoIPRecord)
	rec.CountryCode = C.GoString(record.country_code)
	rec.CountryCode3 = C.GoString(record.country_code3)
	rec.CountryName = C.GoString(record.country_name)
	rec.Region = C.GoString(record.region)
	rec.City = C.GoString(record.city)
	rec.PostalCode = C.GoString(record.postal_code)
	rec.Latitude = float32(record.latitude)
	rec.Longitude = float32(record.longitude)
	rec.AreaCode = int(record.area_code)
	rec.CharSet = int(record.charset)
	rec.ContinentCode = C.GoString(record.continent_code)

	return rec
}

// Returns the country code and region code for an IP address. Requires
// the GeoIP Region database.
func (gi *GeoIP) GetRegion(ip string) (string, string) {
	if gi.db == nil {
		return "", ""
	}

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))

	// Even though we don't query the netmask here, this routine clobbers
	// our global netmask and so still needs to be enclosed in the mutex
	gi.mu.Lock()
	region := C.GeoIP_region_by_addr(gi.db, cip)
	gi.mu.Unlock()

	if region == nil {
		return "", ""
	}

	countryCode := C.GoString(&region.country_code[0])
	regionCode := C.GoString(&region.region[0])
	defer C.free(unsafe.Pointer(region))

	return countryCode, regionCode
}

// Returns the region name given a country code and region code
func GetRegionName(countryCode, regionCode string) string {

	cc := C.CString(countryCode)
	defer C.free(unsafe.Pointer(cc))

	rc := C.CString(regionCode)
	defer C.free(unsafe.Pointer(rc))

	region := C.GeoIP_region_name_by_code(cc, rc)
	if region == nil {
		return ""
	}

	// it's a static string constant, don't free this
	regionName := C.GoString(region)

	return regionName
}

// Same as GetName() but for IPv6 addresses.
func (gi *GeoIP) GetNameV6(ip string) (name string, netmask int) {
	if gi.db == nil {
		return
	}

	gi.mu.Lock()
	defer gi.mu.Unlock()

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	cname := C.GeoIP_name_by_addr_v6(gi.db, cip)

	if cname != nil {
		name = C.GoString(cname)
		defer C.free(unsafe.Pointer(cname))
		netmask = int(C.GeoIP_last_netmask(gi.db))
		return
	}
	return
}

// Takes an IPv4 address string and returns the country code for that IP
// and the netmask for that IP range.
func (gi *GeoIP) GetCountry(ip string) (cc string, netmask int) {
	if gi.db == nil {
		return
	}

	gi.mu.Lock() // Lock to make sure we get the right result from GeoIP_last_netmask
	defer gi.mu.Unlock()

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	ccountry := C.GeoIP_country_code_by_addr(gi.db, cip)

	if ccountry != nil {
		cc = C.GoString(ccountry)
		netmask = int(C.GeoIP_last_netmask(gi.db))
		return
	}
	return
}

// GetCountry_v6 works the same as GetCountry except for IPv6 addresses, be sure to
// load a database with IPv6 data to get any results.
func (gi *GeoIP) GetCountry_v6(ip string) (cc string, netmask int) {
	if gi.db == nil {
		return
	}

	gi.mu.Lock()
	defer gi.mu.Unlock()

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	ccountry := C.GeoIP_country_code_by_addr_v6(gi.db, cip)
	if ccountry != nil {
		cc = C.GoString(ccountry)
		netmask = int(C.GeoIP_last_netmask(gi.db))
		return
	}
	return
}
<commit_msg>Expand comment for the mutex<commit_after>/* Go (cgo) interface to libgeoip */
package geoip

/*
#cgo CFLAGS: -I/opt/local/include -I/usr/local/include -I/usr/include
#cgo LDFLAGS: -lGeoIP -L/opt/local/lib -L/usr/local/lib -L/usr/lib
#include <stdio.h>
#include <errno.h>
#include <GeoIP.h>
#include <GeoIPCity.h>

//typedef GeoIP* GeoIP_pnt
*/
import "C"

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"sync"
	"unsafe"
)

type GeoIP struct {
	db *C.GeoIP

	// We don't use GeoIP's thread-safe API calls, which means there is a
	// single global netmask variable that gets clobbered in the main
	// lookup routine.  Any calls which have _GeoIP_seek_record_gl need to
	// be wrapped in this mutex.

	mu sync.Mutex
}

func (gi *GeoIP) free() {
	if gi == nil {
		return
	}
	if gi.db == nil {
		gi = nil
		return
	}
	C.GeoIP_delete(gi.db)
	gi = nil
	return
}

// Opens a GeoIP database by filename, all formats supported by libgeoip are
// supported though there are only functions to access some of the databases in this API.
// The database is opened in MEMORY_CACHE mode, if you need to optimize for memory
// instead of performance you should change this.
// If you don't pass a filename, it will try opening the database from
// a list of common paths.
func Open(files ...string) (*GeoIP, error) {
	if len(files) == 0 {
		files = []string{
			"/usr/share/GeoIP/GeoIP.dat",       // Linux default
			"/usr/share/local/GeoIP/GeoIP.dat", // source install?
			"/usr/local/share/GeoIP/GeoIP.dat", // FreeBSD
			"/opt/local/share/GeoIP/GeoIP.dat", // MacPorts
			"/usr/share/GeoIP/GeoIP.dat",       // ArchLinux
		}
	}

	g := &GeoIP{}
	runtime.SetFinalizer(g, (*GeoIP).free)

	var err error

	for _, file := range files {

		// libgeoip prints errors if it can't open the file, so check first
		if _, err := os.Stat(file); err != nil {
			if os.IsExist(err) {
				log.Println(err)
			}
			continue
		}

		cbase := C.CString(file)
		defer C.free(unsafe.Pointer(cbase))

		g.db, err = C.GeoIP_open(cbase, C.GEOIP_MEMORY_CACHE)
		if g.db != nil && err != nil {
			break
		}
	}
	if err != nil {
		return nil, fmt.Errorf("Error opening GeoIP database (%s): %s", files, err)
	}

	if g.db == nil {
		return nil, fmt.Errorf("Didn't open GeoIP database (%s)", files)
	}

	C.GeoIP_set_charset(g.db, C.GEOIP_CHARSET_UTF8)
	return g, nil
}

// SetCustomDirectory sets the default location for the GeoIP .dat files used when
// calling OpenType()
func SetCustomDirectory(dir string) {
	cdir := C.CString(dir)
	// GeoIP doesn't copy the string, so don't free it when we're done here.
	// defer C.free(unsafe.Pointer(cdir))
	C.GeoIP_setup_custom_directory(cdir)
}

// OpenType opens a specified GeoIP database type in the default location. Constants
// are defined for each database type (for example GEOIP_COUNTRY_EDITION).
func OpenType(dbType int) (*GeoIP, error) {
	g := &GeoIP{}
	runtime.SetFinalizer(g, (*GeoIP).free)

	var err error

	g.db, err = C.GeoIP_open_type(C.int(dbType), C.GEOIP_MEMORY_CACHE)
	if err != nil {
		return nil, fmt.Errorf("Error opening GeoIP database (%d): %s", dbType, err)
	}

	if g.db == nil {
		return nil, fmt.Errorf("Didn't open GeoIP database (%d)", dbType)
	}

	C.GeoIP_set_charset(g.db, C.GEOIP_CHARSET_UTF8)

	return g, nil
}

// Takes an IPv4 address string and returns the organization name for that IP.
// Requires the GeoIP organization database.
func (gi *GeoIP) GetOrg(ip string) string {
	name, _ := gi.GetName(ip)
	return name
}

// Works on the ASN, Netspeed, Organization and probably other
// databases, takes and IP string and returns a "name" and the
// netmask.
func (gi *GeoIP) GetName(ip string) (name string, netmask int) {
	if gi.db == nil {
		return
	}

	gi.mu.Lock()
	defer gi.mu.Unlock()

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	cname := C.GeoIP_name_by_addr(gi.db, cip)

	if cname != nil {
		name = C.GoString(cname)
		defer C.free(unsafe.Pointer(cname))
		netmask = int(C.GeoIP_last_netmask(gi.db))
		return
	}
	return
}

type GeoIPRecord struct {
	CountryCode  string
	CountryCode3 string
	CountryName  string
	Region       string
	City         string
	PostalCode   string
	Latitude     float32
	Longitude    float32
	// DMACode       int
	AreaCode      int
	CharSet       int
	ContinentCode string
}

// Returns the "City Record" for an IP address. Requires the GeoCity(Lite)
// database - http://www.maxmind.com/en/city
func (gi *GeoIP) GetRecord(ip string) *GeoIPRecord {
	if gi.db == nil {
		return nil
	}

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	record := C.GeoIP_record_by_addr(gi.db, cip)
	if record == nil {
		return nil
	}
	// defer C.free(unsafe.Pointer(record))
	defer C.GeoIPRecord_delete(record)
	rec := new(GeoIPRecord)
	rec.CountryCode = C.GoString(record.country_code)
	rec.CountryCode3 = C.GoString(record.country_code3)
	rec.CountryName = C.GoString(record.country_name)
	rec.Region = C.GoString(record.region)
	rec.City = C.GoString(record.city)
	rec.PostalCode = C.GoString(record.postal_code)
	rec.Latitude = float32(record.latitude)
	rec.Longitude = float32(record.longitude)
	rec.AreaCode = int(record.area_code)
	rec.CharSet = int(record.charset)
	rec.ContinentCode = C.GoString(record.continent_code)

	return rec
}

// Returns the country code and region code for an IP address. Requires
// the GeoIP Region database.
func (gi *GeoIP) GetRegion(ip string) (string, string) {
	if gi.db == nil {
		return "", ""
	}

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))

	gi.mu.Lock()
	region := C.GeoIP_region_by_addr(gi.db, cip)
	gi.mu.Unlock()

	if region == nil {
		return "", ""
	}

	countryCode := C.GoString(&region.country_code[0])
	regionCode := C.GoString(&region.region[0])
	defer C.free(unsafe.Pointer(region))

	return countryCode, regionCode
}

// Returns the region name given a country code and region code
func GetRegionName(countryCode, regionCode string) string {

	cc := C.CString(countryCode)
	defer C.free(unsafe.Pointer(cc))

	rc := C.CString(regionCode)
	defer C.free(unsafe.Pointer(rc))

	region := C.GeoIP_region_name_by_code(cc, rc)
	if region == nil {
		return ""
	}

	// it's a static string constant, don't free this
	regionName := C.GoString(region)

	return regionName
}

// Same as GetName() but for IPv6 addresses.
func (gi *GeoIP) GetNameV6(ip string) (name string, netmask int) {
	if gi.db == nil {
		return
	}

	gi.mu.Lock()
	defer gi.mu.Unlock()

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	cname := C.GeoIP_name_by_addr_v6(gi.db, cip)

	if cname != nil {
		name = C.GoString(cname)
		defer C.free(unsafe.Pointer(cname))
		netmask = int(C.GeoIP_last_netmask(gi.db))
		return
	}
	return
}

// Takes an IPv4 address string and returns the country code for that IP
// and the netmask for that IP range.
func (gi *GeoIP) GetCountry(ip string) (cc string, netmask int) {
	if gi.db == nil {
		return
	}

	gi.mu.Lock()
	defer gi.mu.Unlock()

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	ccountry := C.GeoIP_country_code_by_addr(gi.db, cip)

	if ccountry != nil {
		cc = C.GoString(ccountry)
		netmask = int(C.GeoIP_last_netmask(gi.db))
		return
	}
	return
}

// GetCountry_v6 works the same as GetCountry except for IPv6 addresses, be sure to
// load a database with IPv6 data to get any results.
func (gi *GeoIP) GetCountry_v6(ip string) (cc string, netmask int) {
	if gi.db == nil {
		return
	}

	gi.mu.Lock()
	defer gi.mu.Unlock()

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	ccountry := C.GeoIP_country_code_by_addr_v6(gi.db, cip)
	if ccountry != nil {
		cc = C.GoString(ccountry)
		netmask = int(C.GeoIP_last_netmask(gi.db))
		return
	}
	return
}
<|endoftext|>


<commit_before>package classfile

import "log"

// Constant pool tags
const (
    CONSTANT_Class              =  7
    CONSTANT_Fieldref           =  9
    CONSTANT_Methodref          = 10
    CONSTANT_InterfaceMethodref = 11
    CONSTANT_String             =  8
    CONSTANT_Integer            =  3
    CONSTANT_Float              =  4
    CONSTANT_Long               =  5
    CONSTANT_Double             =  6
    CONSTANT_NameAndType        = 12
    CONSTANT_Utf8               =  1
    CONSTANT_MethodHandle       = 15
    CONSTANT_MethodType         = 16
    CONSTANT_InvokeDynamic      = 18
)

/*
cp_info {
    u1 tag;
    u1 info[];
}
*/
type ConstantInfo interface {
    readInfo(reader *ClassReader)
}

// todo ugly code
func newConstantInfo(tag uint8, cp *ConstantPool) (ConstantInfo) {
    switch tag {
    case CONSTANT_Integer:              return &ConstantIntegerInfo{}
    case CONSTANT_Float:                return &ConstantFloatInfo{}
    case CONSTANT_Long:                 return &ConstantLongInfo{}
    case CONSTANT_Double:               return &ConstantDoubleInfo{}
    case CONSTANT_Utf8:                 return &ConstantUtf8Info{}
    case CONSTANT_String:               return &ConstantStringInfo{cp:cp}
    case CONSTANT_Class:                return &ConstantClassInfo{cp:cp}
    case CONSTANT_Fieldref:             c := &ConstantFieldrefInfo{};           c.cp = cp; return c
    case CONSTANT_Methodref:            c := &ConstantMethodrefInfo{};          c.cp = cp; return c
    case CONSTANT_InterfaceMethodref:   c := &ConstantInterfaceMethodrefInfo{}; c.cp = cp; return c
    case CONSTANT_NameAndType:          return &ConstantNameAndTypeInfo{}
    case CONSTANT_MethodType:           return &ConstantMethodTypeInfo{}
    case CONSTANT_MethodHandle:         return &ConstantMethodHandleInfo{}
    case CONSTANT_InvokeDynamic:        return &ConstantInvokeDynamicInfo{}
    default: // todo
        log.Panicf("Invalid constant pool tag: %v", tag)
        return nil
    }
}
<commit_msg>code refactor<commit_after>package classfile

import "jvmgo/util"

// Constant pool tags
const (
    CONSTANT_Class              =  7
    CONSTANT_Fieldref           =  9
    CONSTANT_Methodref          = 10
    CONSTANT_InterfaceMethodref = 11
    CONSTANT_String             =  8
    CONSTANT_Integer            =  3
    CONSTANT_Float              =  4
    CONSTANT_Long               =  5
    CONSTANT_Double             =  6
    CONSTANT_NameAndType        = 12
    CONSTANT_Utf8               =  1
    CONSTANT_MethodHandle       = 15
    CONSTANT_MethodType         = 16
    CONSTANT_InvokeDynamic      = 18
)

/*
cp_info {
    u1 tag;
    u1 info[];
}
*/
type ConstantInfo interface {
    readInfo(reader *ClassReader)
}

// todo ugly code
func newConstantInfo(tag uint8, cp *ConstantPool) (ConstantInfo) {
    switch tag {
    case CONSTANT_Integer:              return &ConstantIntegerInfo{}
    case CONSTANT_Float:                return &ConstantFloatInfo{}
    case CONSTANT_Long:                 return &ConstantLongInfo{}
    case CONSTANT_Double:               return &ConstantDoubleInfo{}
    case CONSTANT_Utf8:                 return &ConstantUtf8Info{}
    case CONSTANT_String:               return &ConstantStringInfo{cp:cp}
    case CONSTANT_Class:                return &ConstantClassInfo{cp:cp}
    case CONSTANT_Fieldref:             c := &ConstantFieldrefInfo{};           c.cp = cp; return c
    case CONSTANT_Methodref:            c := &ConstantMethodrefInfo{};          c.cp = cp; return c
    case CONSTANT_InterfaceMethodref:   c := &ConstantInterfaceMethodrefInfo{}; c.cp = cp; return c
    case CONSTANT_NameAndType:          return &ConstantNameAndTypeInfo{}
    case CONSTANT_MethodType:           return &ConstantMethodTypeInfo{}
    case CONSTANT_MethodHandle:         return &ConstantMethodHandleInfo{}
    case CONSTANT_InvokeDynamic:        return &ConstantInvokeDynamicInfo{}
    default: // todo
        util.Panicf("BAD constant pool tag: %v", tag)
        return nil
    }
}
<|endoftext|>


<commit_before>package lang

import (
    "time"
    "unsafe"
    . "jvmgo/any"
    "jvmgo/jvm/rtda"
    rtc "jvmgo/jvm/rtda/class"
)

func init() {
    _system(arraycopy,          "arraycopy",            "(Ljava/lang/Object;ILjava/lang/Object;II)V")
    _system(currentTimeMillis,  "currentTimeMillis",    "()J")
    _system(identityHashCode,   "identityHashCode",     "(Ljava/lang/Object;)I")
    _system(nanoTime,           "nanoTime",             "()J")
}

func _system(method Any, name, desc string) {
    rtc.RegisterNativeMethod("java/lang/System", name, desc, method)
}

// public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
// (Ljava/lang/Object;ILjava/lang/Object;II)V
func arraycopy(frame *rtda.Frame) {
    stack := frame.OperandStack()
    length := stack.PopInt()
    destPos := stack.PopInt()
    dest := stack.PopRef()
    srcPos := stack.PopInt()
    src := stack.PopRef()

    // NullPointerException
    if src == nil || dest == nil {
        panic("NPE") // todo
    }
    // ArrayStoreException
    if !rtc.HaveSameArrayType(src, dest) {
        panic("ArrayStoreException")
    }
    // IndexOutOfBoundsException
    if srcPos < 0 || destPos < 0 || length < 0 ||
            srcPos + length > rtc.ArrayLength(src) ||
            destPos + length > rtc.ArrayLength(dest) {

        panic("IndexOutOfBoundsException") // todo
    }

    rtc.ArrayCopy(src, dest, srcPos, destPos, length)
}

// public static native long currentTimeMillis();
// ()J
func currentTimeMillis(frame *rtda.Frame) {
    stack := frame.OperandStack()
    millis := time.Now().UnixNano() / 1000
    stack.PushLong(millis)
}

// public static native int identityHashCode(Object x);
// (Ljava/lang/Object;)I
func identityHashCode(frame *rtda.Frame) {
    // todo
    stack := frame.OperandStack()
    ref := stack.PopRef()
    hashCode := int32(uintptr(unsafe.Pointer(ref)))
    stack.PushInt(hashCode)
}

// private static native Properties initProperties(Properties props);
// (Ljava/util/Properties;)Ljava/util/Properties;

// public static native long nanoTime();
// ()J
func nanoTime(frame *rtda.Frame) {
    stack := frame.OperandStack()
    nanoTime := time.Now().UnixNano()
    stack.PushLong(nanoTime)
}
<commit_msg>native: java.lang.System.initProperties()<commit_after>package lang

import (
    "time"
    "unsafe"
    . "jvmgo/any"
    "jvmgo/jvm/rtda"
    rtc "jvmgo/jvm/rtda/class"
)

func init() {
    _system(arraycopy,          "arraycopy",            "(Ljava/lang/Object;ILjava/lang/Object;II)V")
    _system(currentTimeMillis,  "currentTimeMillis",    "()J")
    _system(identityHashCode,   "identityHashCode",     "(Ljava/lang/Object;)I")
    _system(initProperties,     "initProperties",       "(Ljava/util/Properties;)Ljava/util/Properties;")
    _system(nanoTime,           "nanoTime",             "()J")
}

func _system(method Any, name, desc string) {
    rtc.RegisterNativeMethod("java/lang/System", name, desc, method)
}

// public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
// (Ljava/lang/Object;ILjava/lang/Object;II)V
func arraycopy(frame *rtda.Frame) {
    stack := frame.OperandStack()
    length := stack.PopInt()
    destPos := stack.PopInt()
    dest := stack.PopRef()
    srcPos := stack.PopInt()
    src := stack.PopRef()

    // NullPointerException
    if src == nil || dest == nil {
        panic("NPE") // todo
    }
    // ArrayStoreException
    if !rtc.HaveSameArrayType(src, dest) {
        panic("ArrayStoreException")
    }
    // IndexOutOfBoundsException
    if srcPos < 0 || destPos < 0 || length < 0 ||
            srcPos + length > rtc.ArrayLength(src) ||
            destPos + length > rtc.ArrayLength(dest) {

        panic("IndexOutOfBoundsException") // todo
    }

    rtc.ArrayCopy(src, dest, srcPos, destPos, length)
}

// public static native long currentTimeMillis();
// ()J
func currentTimeMillis(frame *rtda.Frame) {
    stack := frame.OperandStack()
    millis := time.Now().UnixNano() / 1000
    stack.PushLong(millis)
}

// public static native int identityHashCode(Object x);
// (Ljava/lang/Object;)I
func identityHashCode(frame *rtda.Frame) {
    // todo
    stack := frame.OperandStack()
    ref := stack.PopRef()
    hashCode := int32(uintptr(unsafe.Pointer(ref)))
    stack.PushInt(hashCode)
}

// private static native Properties initProperties(Properties props);
// (Ljava/util/Properties;)Ljava/util/Properties;
func initProperties(frame *rtda.Frame) {
    stack := frame.OperandStack()
    props := stack.PopRef()
    stack.PushRef(props)
    // todo
}

// public static native long nanoTime();
// ()J
func nanoTime(frame *rtda.Frame) {
    stack := frame.OperandStack()
    nanoTime := time.Now().UnixNano()
    stack.PushLong(nanoTime)
}
<|endoftext|>


<commit_before>package tenus

import (
	"fmt"
	"net"
	"os"
	"syscall"

	"github.com/docker/libcontainer/netlink"
	"github.com/docker/libcontainer/system"
)

// VethOptions allows you to specify options for veth link.
type VethOptions struct {
	// Veth pair's peer interface name
	PeerName string
	// TX queue length
	TxQueueLen int
}

// Vether embeds Linker interface and adds few more functions mostly to handle peer link interface
type Vether interface {
	// Linker interface
	Linker
	// PeerNetInterface returns peer network interface
	PeerNetInterface() *net.Interface
	// SetPeerLinkUp sets peer link up - which also brings up the other peer in VethPair
	SetPeerLinkUp() error
	// DeletePeerLink deletes peer link - this also deletes the other peer in VethPair
	DeletePeerLink() error
	// SetPeerLinkIp configures peer link's IP address
	SetPeerLinkIp(net.IP, *net.IPNet) error
	// SetPeerLinkNsToDocker sends peer link into Docker
	SetPeerLinkNsToDocker(string, string) error
	// SetPeerLinkNsPid sends peer link into container specified by PID
	SetPeerLinkNsPid(int) error
	// SetPeerLinkNsFd sends peer link into container specified by path
	SetPeerLinkNsFd(string) error
	// SetPeerLinkNetInNs configures peer link's IP network in network namespace specified by PID
	SetPeerLinkNetInNs(int, net.IP, *net.IPNet, *net.IP) error
}

// VethPair is a Link. Veth links are created in pairs called peers.
type VethPair struct {
	Link
	// Peer network interface
	peerIfc *net.Interface
}

// NewVethPair creates a pair of veth network links.
//
// It is equivalent of running:
// 		ip link add name veth${RANDOM STRING} type veth peer name veth${RANDOM STRING}.
// NewVethPair returns Vether which is initialized to a pointer of type VethPair if the
// veth link was successfully created on Linux host. Newly created pair of veth links
// are assigned random names starting with "veth".
// NewVethPair returns error if the veth pair could not be created.
func NewVethPair() (Vether, error) {
	ifcName := makeNetInterfaceName("veth")
	peerName := makeNetInterfaceName("veth")

	if err := netlink.NetworkCreateVethPair(ifcName, peerName, 0); err != nil {
		return nil, err
	}

	newIfc, err := net.InterfaceByName(ifcName)
	if err != nil {
		return nil, fmt.Errorf("Could not find the new interface: %s", err)
	}

	peerIfc, err := net.InterfaceByName(peerName)
	if err != nil {
		return nil, fmt.Errorf("Could not find the new interface: %s", err)
	}

	return &VethPair{
		Link: Link{
			ifc: newIfc,
		},
		peerIfc: peerIfc,
	}, nil
}

// NewVethPairWithOptions creates a pair of veth network links.
//
// It is equivalent of running:
// 		ip link add name ${first device name} type veth peer name ${second device name}
// NewVethPairWithOptions returns Vether which is initialized to a pointer of type VethPair if the
// veth link was successfully created on the Linux host. It accepts VethOptions which allow you to set
// peer interface name. It returns error if the veth pair could not be created.
func NewVethPairWithOptions(ifcName string, opts VethOptions) (Vether, error) {
	peerName := opts.PeerName
	txQLen := opts.TxQueueLen

	if ok, err := NetInterfaceNameValid(ifcName); !ok {
		return nil, err
	}

	if _, err := net.InterfaceByName(ifcName); err == nil {
		return nil, fmt.Errorf("Interface name %s already assigned on the host", ifcName)
	}

	if peerName != "" {
		if ok, err := NetInterfaceNameValid(peerName); !ok {
			return nil, err
		}

		if _, err := net.InterfaceByName(peerName); err == nil {
			return nil, fmt.Errorf("Interface name %s already assigned on the host", peerName)
		}
	} else {
		peerName = makeNetInterfaceName("veth")
	}

	if err := netlink.NetworkCreateVethPair(ifcName, peerName, txQLen); err != nil {
		return nil, err
	}

	newIfc, err := net.InterfaceByName(ifcName)
	if err != nil {
		return nil, fmt.Errorf("Could not find the new interface: %s", err)
	}

	peerIfc, err := net.InterfaceByName(peerName)
	if err != nil {
		return nil, fmt.Errorf("Could not find the new interface: %s", err)
	}

	return &VethPair{
		Link: Link{
			ifc: newIfc,
		},
		peerIfc: peerIfc,
	}, nil
}

// NetInterface returns veth link's primary network interface
func (veth *VethPair) NetInterface() *net.Interface {
	return veth.ifc
}

// NetInterface returns veth link's peer network interface
func (veth *VethPair) PeerNetInterface() *net.Interface {
	return veth.peerIfc
}

// SetPeerLinkUp sets peer link up
func (veth *VethPair) SetPeerLinkUp() error {
	return netlink.NetworkLinkUp(veth.peerIfc)
}

// DeletePeerLink deletes peer link. It also deletes the other peer interface in VethPair
func (veth *VethPair) DeletePeerLink() error {
	return netlink.NetworkLinkDel(veth.peerIfc.Name)
}

// SetPeerLinkIp configures peer link's IP address
func (veth *VethPair) SetPeerLinkIp(ip net.IP, nw *net.IPNet) error {
	return netlink.NetworkLinkAddIp(veth.peerIfc, ip, nw)
}

// SetPeerLinkNsToDocker sends peer link into Docker
func (veth *VethPair) SetPeerLinkNsToDocker(name string, dockerHost string) error {
	pid, err := DockerPidByName(name, dockerHost)
	if err != nil {
		return fmt.Errorf("Failed to find docker %s :  %s", name, err)
	}

	return netlink.NetworkSetNsPid(veth.peerIfc, pid)
}

// SetPeerLinkNsPid sends peer link into container specified by PID
func (veth *VethPair) SetPeerLinkNsPid(nspid int) error {
	return netlink.NetworkSetNsPid(veth.peerIfc, nspid)
}

// SetPeerLinkNsFd sends peer link into container specified by path
func (veth *VethPair) SetPeerLinkNsFd(nspath string) error {
	fd, err := syscall.Open(nspath, syscall.O_RDONLY, 0)
	if err != nil {
		return fmt.Errorf("Could not attach to Network namespace: %s", err)
	}

	return netlink.NetworkSetNsFd(veth.peerIfc, fd)
}

// SetPeerLinkNetInNs configures peer link's IP network in network namespace specified by PID
func (veth *VethPair) SetPeerLinkNetInNs(nspid int, ip net.IP, network *net.IPNet, gw *net.IP) error {
	origNs, _ := NetNsHandle(os.Getpid())
	defer syscall.Close(int(origNs))
	defer system.Setns(origNs, syscall.CLONE_NEWNET)

	if err := SetNetNsToPid(nspid); err != nil {
		return fmt.Errorf("Setting network namespace failed: %s", err)
	}

	if err := netlink.NetworkLinkAddIp(veth.peerIfc, ip, network); err != nil {
		return fmt.Errorf("Unable to set IP: %s in pid: %d network namespace", ip.String(), nspid)
	}

	if err := netlink.NetworkLinkUp(veth.peerIfc); err != nil {
		return fmt.Errorf("Unable to bring %s interface UP: %s", veth.peerIfc.Name, nspid)
	}

	if gw != nil {
		if err := netlink.AddDefaultGw(gw.String(), veth.peerIfc.Name); err != nil {
			return fmt.Errorf("Unable to set Default gateway: %s in pid: %d network namespace", gw.String(), nspid)
		}
	}

	return nil
}
<commit_msg>TX len validation.<commit_after>package tenus

import (
	"fmt"
	"net"
	"os"
	"syscall"

	"github.com/docker/libcontainer/netlink"
	"github.com/docker/libcontainer/system"
)

// VethOptions allows you to specify options for veth link.
type VethOptions struct {
	// Veth pair's peer interface name
	PeerName string
	// TX queue length
	TxQueueLen int
}

// Vether embeds Linker interface and adds few more functions mostly to handle peer link interface
type Vether interface {
	// Linker interface
	Linker
	// PeerNetInterface returns peer network interface
	PeerNetInterface() *net.Interface
	// SetPeerLinkUp sets peer link up - which also brings up the other peer in VethPair
	SetPeerLinkUp() error
	// DeletePeerLink deletes peer link - this also deletes the other peer in VethPair
	DeletePeerLink() error
	// SetPeerLinkIp configures peer link's IP address
	SetPeerLinkIp(net.IP, *net.IPNet) error
	// SetPeerLinkNsToDocker sends peer link into Docker
	SetPeerLinkNsToDocker(string, string) error
	// SetPeerLinkNsPid sends peer link into container specified by PID
	SetPeerLinkNsPid(int) error
	// SetPeerLinkNsFd sends peer link into container specified by path
	SetPeerLinkNsFd(string) error
	// SetPeerLinkNetInNs configures peer link's IP network in network namespace specified by PID
	SetPeerLinkNetInNs(int, net.IP, *net.IPNet, *net.IP) error
}

// VethPair is a Link. Veth links are created in pairs called peers.
type VethPair struct {
	Link
	// Peer network interface
	peerIfc *net.Interface
}

// NewVethPair creates a pair of veth network links.
//
// It is equivalent of running:
// 		ip link add name veth${RANDOM STRING} type veth peer name veth${RANDOM STRING}.
// NewVethPair returns Vether which is initialized to a pointer of type VethPair if the
// veth link was successfully created on Linux host. Newly created pair of veth links
// are assigned random names starting with "veth".
// NewVethPair returns error if the veth pair could not be created.
func NewVethPair() (Vether, error) {
	ifcName := makeNetInterfaceName("veth")
	peerName := makeNetInterfaceName("veth")

	if err := netlink.NetworkCreateVethPair(ifcName, peerName, 0); err != nil {
		return nil, err
	}

	newIfc, err := net.InterfaceByName(ifcName)
	if err != nil {
		return nil, fmt.Errorf("Could not find the new interface: %s", err)
	}

	peerIfc, err := net.InterfaceByName(peerName)
	if err != nil {
		return nil, fmt.Errorf("Could not find the new interface: %s", err)
	}

	return &VethPair{
		Link: Link{
			ifc: newIfc,
		},
		peerIfc: peerIfc,
	}, nil
}

// NewVethPairWithOptions creates a pair of veth network links.
//
// It is equivalent of running:
// 		ip link add name ${first device name} type veth peer name ${second device name}
// NewVethPairWithOptions returns Vether which is initialized to a pointer of type VethPair if the
// veth link was successfully created on the Linux host. It accepts VethOptions which allow you to set
// peer interface name. It returns error if the veth pair could not be created.
func NewVethPairWithOptions(ifcName string, opts VethOptions) (Vether, error) {
	peerName := opts.PeerName
	txQLen := opts.TxQueueLen

	if ok, err := NetInterfaceNameValid(ifcName); !ok {
		return nil, err
	}

	if _, err := net.InterfaceByName(ifcName); err == nil {
		return nil, fmt.Errorf("Interface name %s already assigned on the host", ifcName)
	}

	if peerName != "" {
		if ok, err := NetInterfaceNameValid(peerName); !ok {
			return nil, err
		}

		if _, err := net.InterfaceByName(peerName); err == nil {
			return nil, fmt.Errorf("Interface name %s already assigned on the host", peerName)
		}
	} else {
		peerName = makeNetInterfaceName("veth")
	}

	if txQLen < 0 {
		return nil, fmt.Errorf("TX queue length must be a positive integer: %d", txQLen)
	}

	if err := netlink.NetworkCreateVethPair(ifcName, peerName, txQLen); err != nil {
		return nil, err
	}

	newIfc, err := net.InterfaceByName(ifcName)
	if err != nil {
		return nil, fmt.Errorf("Could not find the new interface: %s", err)
	}

	peerIfc, err := net.InterfaceByName(peerName)
	if err != nil {
		return nil, fmt.Errorf("Could not find the new interface: %s", err)
	}

	return &VethPair{
		Link: Link{
			ifc: newIfc,
		},
		peerIfc: peerIfc,
	}, nil
}

// NetInterface returns veth link's primary network interface
func (veth *VethPair) NetInterface() *net.Interface {
	return veth.ifc
}

// NetInterface returns veth link's peer network interface
func (veth *VethPair) PeerNetInterface() *net.Interface {
	return veth.peerIfc
}

// SetPeerLinkUp sets peer link up
func (veth *VethPair) SetPeerLinkUp() error {
	return netlink.NetworkLinkUp(veth.peerIfc)
}

// DeletePeerLink deletes peer link. It also deletes the other peer interface in VethPair
func (veth *VethPair) DeletePeerLink() error {
	return netlink.NetworkLinkDel(veth.peerIfc.Name)
}

// SetPeerLinkIp configures peer link's IP address
func (veth *VethPair) SetPeerLinkIp(ip net.IP, nw *net.IPNet) error {
	return netlink.NetworkLinkAddIp(veth.peerIfc, ip, nw)
}

// SetPeerLinkNsToDocker sends peer link into Docker
func (veth *VethPair) SetPeerLinkNsToDocker(name string, dockerHost string) error {
	pid, err := DockerPidByName(name, dockerHost)
	if err != nil {
		return fmt.Errorf("Failed to find docker %s :  %s", name, err)
	}

	return netlink.NetworkSetNsPid(veth.peerIfc, pid)
}

// SetPeerLinkNsPid sends peer link into container specified by PID
func (veth *VethPair) SetPeerLinkNsPid(nspid int) error {
	return netlink.NetworkSetNsPid(veth.peerIfc, nspid)
}

// SetPeerLinkNsFd sends peer link into container specified by path
func (veth *VethPair) SetPeerLinkNsFd(nspath string) error {
	fd, err := syscall.Open(nspath, syscall.O_RDONLY, 0)
	if err != nil {
		return fmt.Errorf("Could not attach to Network namespace: %s", err)
	}

	return netlink.NetworkSetNsFd(veth.peerIfc, fd)
}

// SetPeerLinkNetInNs configures peer link's IP network in network namespace specified by PID
func (veth *VethPair) SetPeerLinkNetInNs(nspid int, ip net.IP, network *net.IPNet, gw *net.IP) error {
	origNs, _ := NetNsHandle(os.Getpid())
	defer syscall.Close(int(origNs))
	defer system.Setns(origNs, syscall.CLONE_NEWNET)

	if err := SetNetNsToPid(nspid); err != nil {
		return fmt.Errorf("Setting network namespace failed: %s", err)
	}

	if err := netlink.NetworkLinkAddIp(veth.peerIfc, ip, network); err != nil {
		return fmt.Errorf("Unable to set IP: %s in pid: %d network namespace", ip.String(), nspid)
	}

	if err := netlink.NetworkLinkUp(veth.peerIfc); err != nil {
		return fmt.Errorf("Unable to bring %s interface UP: %s", veth.peerIfc.Name, nspid)
	}

	if gw != nil {
		if err := netlink.AddDefaultGw(gw.String(), veth.peerIfc.Name); err != nil {
			return fmt.Errorf("Unable to set Default gateway: %s in pid: %d network namespace", gw.String(), nspid)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/user"
	"path"

	"github.com/efarrer/gmash/auth"
	"github.com/efarrer/gmash/ip"
	"github.com/efarrer/gmash/sshd"

	"golang.org/x/crypto/ssh"
)

func main() {
	// Get the user's home directory
	usr, err := user.Current()
	if err != nil {
		log.Fatalf("Unable to get user's home directory (%s)\n", err)
	}
	gmashDir := path.Join(usr.HomeDir, ".gmash")

	// Create the gmash dir
	err = os.MkdirAll(gmashDir, 0700)
	if err != nil {
		log.Fatalf("Unable to create %s (%s)\n", gmashDir, err)
	}

	// Generate a random user password for this session
	masterPassword, err := auth.GeneratePassword(10)
	if err != nil {
		log.Fatalf("Unable to generate password (%s)", err)
	}

	// Construct the ssh configuration with password authentication
	sshConf := ssh.ServerConfig{
		PasswordCallback: auth.CreatePasswordCallback(masterPassword),
	}
	shellConf := sshd.DefaultShellConf(
		"/bin/bash",
		func(err error) { fmt.Printf("%s\n", err) },
	)

	// Generate server ssh keys
	signer, err := auth.TryLoadKeys(path.Join(gmashDir, "key"))
	if err != nil {
		log.Fatal(err)
	}
	sshConf.AddHostKey(signer)

	pubIP, err := ip.LinuxPublicIP()
	if err != nil {
		log.Fatalf("%s\n", err)
	}

	listener, err := sshd.SSHServer(pubIP+":", &sshConf, shellConf)
	if err != nil {
		log.Fatalf("%s\n", err)
	}
	defer func() { _ = listener.Close() }()

	fpMD5, fpSHA256 := auth.GetFingerPrint(signer)
	fmt.Printf("Started server with RSA key: %s\n", fpMD5)
	fmt.Printf("Started server with RSA key: %s\n", fpSHA256)
	fmt.Println("")
	fmt.Printf("To connect type:\n")
	fmt.Printf("ssh -o UserKnownHostsFile=/dev/null %s -p %d\n", pubIP, listener.Addr().(*net.TCPAddr).Port)
	fmt.Printf("password %s\n", masterPassword)

	select {}
}
<commit_msg>Add support for ngrok.<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"os/user"
	"path"

	"github.com/efarrer/gmash/auth"
	"github.com/efarrer/gmash/ip"
	"github.com/efarrer/gmash/ngrok"
	"github.com/efarrer/gmash/sshd"

	"golang.org/x/crypto/ssh"
)

func main() {
	var local = flag.Bool("local", false, "Whether to only allow connections over the local network")
	var global = flag.Bool("global", false, "Whether to allow connections from anywhere")

	flag.Parse()

	if !*local && !*global {
		log.Fatal("You must specify either the -local or -global arguments")
	}

	// Get the user's home directory
	usr, err := user.Current()
	if err != nil {
		log.Fatalf("Unable to get user's home directory (%s)\n", err)
	}
	gmashDir := path.Join(usr.HomeDir, ".gmash")

	// Create the gmash dir
	err = os.MkdirAll(gmashDir, 0700)
	if err != nil {
		log.Fatalf("Unable to create %s (%s)\n", gmashDir, err)
	}

	// Generate a random user password for this session
	masterPassword, err := auth.GeneratePassword(10)
	if err != nil {
		log.Fatalf("Unable to generate password (%s)", err)
	}

	// Construct the ssh configuration with password authentication
	sshConf := ssh.ServerConfig{
		PasswordCallback: auth.CreatePasswordCallback(masterPassword),
	}
	shellConf := sshd.DefaultShellConf(
		"/bin/bash",
		func(err error) { fmt.Printf("%s\n", err) },
	)

	// Generate server ssh keys
	signer, err := auth.TryLoadKeys(path.Join(gmashDir, "key"))
	if err != nil {
		log.Fatal(err)
	}
	sshConf.AddHostKey(signer)

	pubIP, err := ip.LinuxPublicIP()
	if err != nil {
		log.Fatalf("%s\n", err)
	}

	if *global {
		pubIP = "0.0.0.0"
	}

	listener, err := sshd.SSHServer(pubIP+":", &sshConf, shellConf)
	if err != nil {
		log.Fatalf("%s\n", err)
	}
	defer func() { _ = listener.Close() }()

	ctx, cancel := context.WithCancel(context.Background())

	port := listener.Addr().(*net.TCPAddr).Port
	if *global {
		resp := ngrok.Execute(ctx, port)
		if resp.Err != nil {
			return
		}
		pubIP = resp.Value.Host
		port = resp.Value.Port
	}

	fpMD5, fpSHA256 := auth.GetFingerPrint(signer)
	fmt.Printf("Started server with RSA key: %s\n", fpMD5)
	fmt.Printf("Started server with RSA key: %s\n", fpSHA256)
	fmt.Println("")
	fmt.Printf("To connect type:\n")
	fmt.Printf("ssh -o UserKnownHostsFile=/dev/null %s -p %d\n", pubIP, port)
	fmt.Printf("password %s\n", masterPassword)

	signalCh := make(chan os.Signal, 1)
	signal.Notify(signalCh, os.Interrupt)
	select {
	case <-signalCh:
		cancel()
		fmt.Printf("Bubye\n")
	case <-ctx.Done():
	}
}
<|endoftext|>


<commit_before>package gobls

import (
	"bufio"
	"bytes"
	"io"
)

type Scanner interface {
	Scan() (string, error)
}

type scanner struct {
	br *bufio.Reader
}

func NewScanner(r io.Reader) Scanner {
	return &scanner{br: bufio.NewReader(r)}
}

func (s *scanner) Scan() (string, error) {
	line, isPrefix, err := s.br.ReadLine()
	if err != nil {
		return string(line), err
	}
	if !isPrefix {
		return string(line), nil
	}
	// here's a long line
	buf := bytes.NewBuffer(line)
	for {
		line, isPrefix, rerr := s.br.ReadLine()
		_, werr := buf.Write(line)
		if rerr != nil {
			return buf.String(), rerr
		}
		if werr != nil {
			return buf.String(), werr
		}
		if !isPrefix {
			return buf.String(), nil
		}
	}
}
<commit_msg>returns byte slices rather than strings<commit_after>package gobls

import (
	"bufio"
	"bytes"
	"io"
)

type Scanner interface {
	Scan() ([]byte, error)
}

type scanner struct {
	br *bufio.Reader
}

func NewScanner(r io.Reader) Scanner {
	return &scanner{br: bufio.NewReader(r)}
}

func (s *scanner) Scan() ([]byte, error) {
	line, isPrefix, err := s.br.ReadLine()
	if err != nil {
		return line, err
	}
	if !isPrefix {
		return line, nil
	}
	// here's a long line
	buf := bytes.NewBuffer(line)
	for {
		line, isPrefix, rerr := s.br.ReadLine()
		_, werr := buf.Write(line)
		if rerr != nil {
			return buf.Bytes(), rerr
		}
		if werr != nil {
			return buf.Bytes(), werr
		}
		if !isPrefix {
			return buf.Bytes(), nil
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"time"
)

var Char struct {
	class, name, race, acct string
	lvl                     int
	seen                    time.Time
}

func main() {
	f, err := os.OpenFile("bot.log", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0640)
	defer f.Close()
	if err != nil {
		log.Fatal(err)
	}
	log.SetOutput(f)

	// for who.go WhoChar(char, lvl, class, race, acct)
	var char = flag.String("char", "",
		"Character name for update or import. Ex: Rynshana")
	var lvl = flag.Int("lvl", 0,
		"Character level for update or import. Ex: 50")
	var class = flag.String("class", "",
		"Character class for initial import. Ex: \"Cleric\"")
	var race = flag.String("race", "",
		"Character race for initial import. Ex: \"Moon Elf\"")
	var acct = flag.String("acct", "",
		"Character account for initial import. Ex: Krimic")
	// for who.go WhoBatch(ppl)
	var who = flag.String("who", "",
		"Batched who output. "+
			"Ex: \"[10 Ctr] Rarac  (Orc)|[ 2 War] Xatus  (Troll)\"")
	// for identify.go Identify(filename)
	var file = flag.String("import", "",
		"Parse file for identify stats, import to DB. Ex: newstats.txt")
	// for time.go Uptime(curup)
	var time = flag.String("time", "",
		"Parse uptime for boot tracking. Ex: 58:10:26")
	// for tell.go ReplyTo(char, tell)
	var tell = flag.String("tell", "",
		"Tell with command and maybe operant. Ex: \"stat a longsword\"")
	// run database backup, restore, and parsing
	var backup = flag.Bool("bak", false,
		"Backup the toril.db database.")
	var restore = flag.String("res", "",
		"Restore the toril.db database from backup file.")
	var short_stats = flag.Bool("s", false,
		"Run ShortStats() creation for item DB.")
	var long_stats = flag.Bool("l", false,
		"Run LongStats() creation for item DB.")

	flag.Parse()

	// only run one command at a time
	switch {
	case *char != "" && 50 >= *lvl && *lvl > 0 &&
		*class != "" && *race != "" && *acct != "":
		WhoChar(*char, *lvl, *class, *race, *acct)
	case *file != "":
		Identify(*file)
	case *time != "":
		Uptime(*time)
	case *char != "" && *tell != "":
		ReplyTo(*char, *tell)
	case *who != "":
		WhoBatch(*who)
	case *backup:
		cmd := exec.Command("sh", "-c",
			"echo '.dump' | sqlite3 toril.db | "+
				"gzip -c >toril.db.`date +\"%Y-%m-%d\"`.gz")
		err := cmd.Run()
		if err != nil {
			log.Fatal(err)
		}
	case *restore != "":
		cmd := exec.Command("sh", "-c", "zcat "+*restore+" | sqlite3 toril.db")
		err := cmd.Run()
		if err != nil {
			log.Fatal(err)
		}
	case *short_stats:
		ShortStats()
	case *long_stats:
		LongStats()
	}
}
<commit_msg>Reordered switch cases based on usage<commit_after>package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"time"
)

var Char struct {
	class, name, race, acct string
	lvl                     int
	seen                    time.Time
}

func main() {
	f, err := os.OpenFile("bot.log", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0640)
	defer f.Close()
	if err != nil {
		log.Fatal(err)
	}
	log.SetOutput(f)

	// for who.go WhoChar(char, lvl, class, race, acct)
	var char = flag.String("char", "",
		"Character name for update or import. Ex: Rynshana")
	var lvl = flag.Int("lvl", 0,
		"Character level for update or import. Ex: 50")
	var class = flag.String("class", "",
		"Character class for initial import. Ex: \"Cleric\"")
	var race = flag.String("race", "",
		"Character race for initial import. Ex: \"Moon Elf\"")
	var acct = flag.String("acct", "",
		"Character account for initial import. Ex: Krimic")
	// for who.go WhoBatch(ppl)
	var who = flag.String("who", "",
		"Batched who output. "+
			"Ex: \"[10 Ctr] Rarac  (Orc)|[ 2 War] Xatus  (Troll)\"")
	// for identify.go Identify(filename)
	var file = flag.String("import", "",
		"Parse file for identify stats, import to DB. Ex: newstats.txt")
	// for time.go Uptime(curup)
	var time = flag.String("time", "",
		"Parse uptime for boot tracking. Ex: 58:10:26")
	// for tell.go ReplyTo(char, tell)
	var tell = flag.String("tell", "",
		"Tell with command and maybe operant. Ex: \"stat a longsword\"")
	// run database backup, restore, and parsing
	var backup = flag.Bool("bak", false,
		"Backup the toril.db database.")
	var restore = flag.String("res", "",
		"Restore the toril.db database from backup file.")
	var short_stats = flag.Bool("s", false,
		"Run ShortStats() creation for item DB.")
	var long_stats = flag.Bool("l", false,
		"Run LongStats() creation for item DB.")

	flag.Parse()

	// only run one command at a time
	switch {
	case *time != "":
		Uptime(*time)
	case *who != "":
		WhoBatch(*who)
	case *char != "" && *tell != "":
		ReplyTo(*char, *tell)
	case *char != "" && 50 >= *lvl && *lvl > 0 &&
		*class != "" && *race != "" && *acct != "":
		WhoChar(*char, *lvl, *class, *race, *acct)
	case *short_stats:
		ShortStats()
	case *long_stats:
		LongStats()
	case *file != "":
		Identify(*file)
	case *backup:
		cmd := exec.Command("sh", "-c",
			"echo '.dump' | sqlite3 toril.db | "+
				"gzip -c >toril.db.`date +\"%Y-%m-%d\"`.gz")
		err := cmd.Run()
		if err != nil {
			log.Fatal(err)
		}
	case *restore != "":
		cmd := exec.Command("sh", "-c", "zcat "+*restore+" | sqlite3 toril.db")
		err := cmd.Run()
		if err != nil {
			log.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>package godbg

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"regexp"
	"runtime"
	"strings"
)

// http://stackoverflow.com/a/23554672/6309 https://vividcortex.com/blog/2013/12/03/go-idiom-package-and-object/
// you design a type with methods as usual, and then you also place matching functions at the package level itself.
// These functions simply delegate to a default instance of the type that’s a private package-level variable, created in an init() function.

// Pdbg allows to print debug message with indent and function name added
type Pdbg struct {
	bout     *bytes.Buffer
	berr     *bytes.Buffer
	sout     *bufio.Writer
	serr     *bufio.Writer
	breaks   []string
	excludes []string
	skips    []string
	global   bool
}

// Out returns a writer for normal messages.
// By default, os.StdOut
func Out() io.Writer {
	return pdbg.Out()
}

// Out returns a writer for normal messages for a given pdbg instance.
// By default, os.StdOut
func (pdbg *Pdbg) Out() io.Writer {
	if pdbg.sout == nil {
		return os.Stdout
	}
	return pdbg.sout
}

// Err returns a writer for error messages.
// By default, os.StdErr
func Err() io.Writer {
	return pdbg.Err()
}

// Err returns a writer for error messages for a given pdbg instance.
// By default, os.StdErr
func (pdbg *Pdbg) Err() io.Writer {
	if pdbg.serr == nil {
		return os.Stderr
	}
	return pdbg.serr
}

// global pdbg used for printing
var pdbg *Pdbg

func init() {
	pdbg = NewPdbg()
	pdbg.global = true
}

// Option set an option for a Pdbg
// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
type Option func(*Pdbg)

// SetBuffers is an option for replacing stdout and stderr by
// bytes buffers (in a bufio.Writer).
// If apdbg is nil, set for the global pdbg instance
func SetBuffers(apdbg *Pdbg) {
	if apdbg == nil {
		apdbg = pdbg
	}
	// http://stackoverflow.com/questions/10473800/in-go-how-do-i-capture-stdout-of-a-function-into-a-string
	apdbg.bout = bytes.NewBuffer(nil)
	apdbg.sout = bufio.NewWriter(apdbg.bout)
	apdbg.berr = bytes.NewBuffer(nil)
	apdbg.serr = bufio.NewWriter(apdbg.berr)
	if apdbg.global {
		os.Stdout = Out()
		os.Stderr = Err()
	}
}

// SetExcludes set excludes on a pdbg (nil for global pdbg)
func (pdbg *Pdbg) SetExcludes(excludes []string) {
	pdbg.excludes = excludes
}

// OptExcludes is an option to set excludes at the creation of a pdbg
func OptExcludes(excludes []string) Option {
	return func(apdbg *Pdbg) {
		apdbg.SetExcludes(excludes)
	}
}

// SetSkips set skips on a pdbg (nil for global pdbg)
func (pdbg *Pdbg) SetSkips(skips []string) {
	sk := []string{"/godbg.go'"}
	sk = append(sk, skips...)
	pdbg.skips = sk
}

// OptSkips is an option to set excludes at the creation of a pdbg
func OptSkips(skips []string) Option {
	return func(apdbg *Pdbg) {
		apdbg.SetSkips(skips)
	}
}

// NewPdbg creates a PDbg instance, with options
func NewPdbg(options ...Option) *Pdbg {
	newpdbg := &Pdbg{}
	for _, option := range options {
		option(newpdbg)
	}
	newpdbg.breaks = append(newpdbg.breaks, "smartystreets")
	//newpdbg.breaks = append(newpdbg.breaks, "(*Pdbg).Pdbgf")
	newpdbg.skips = append(newpdbg.skips, "/godbg.go'")
	return newpdbg
}

// ResetIOs reset the out and err buffer of global pdbg instance
func ResetIOs() {
	pdbg.ResetIOs()
}

// ResetIOs reset the out and err buffer
// (unless they were the default stdout and stderr,
// in which case it does nothing)
func (pdbg *Pdbg) ResetIOs() {
	if pdbg.sout != nil {
		pdbg.bout = bytes.NewBuffer(nil)
		pdbg.sout.Reset(pdbg.bout)
		pdbg.berr = bytes.NewBuffer(nil)
		pdbg.serr.Reset(pdbg.berr)
	}
}

// OutString returns the string for out messages for the global pdbg instance.
// It flushes the out buffer.
// If out is set to os.Stdout, returns an empty string
func OutString() string {
	return pdbg.OutString()
}

// OutString returns the string for out messages for a given pdbg instance.
// It flushes the out buffer.
// If out is set to os.Stdout, returns an empty string
func (pdbg *Pdbg) OutString() string {
	if pdbg.sout == nil {
		return ""
	}
	pdbg.sout.Flush()
	return pdbg.bout.String()
}

// ErrString returns the string for error messages for the global pdbg instance.
// It flushes the err buffer.
// If err is set to os.StdErr, returns an empty string
func ErrString() string {
	return pdbg.ErrString()
}

// ErrString returns the string for error messages for a given pdbg instance.
// It flushes the err buffer.
// If err is set to os.StdErr, returns an empty string
func (pdbg *Pdbg) ErrString() string {
	if pdbg.serr == nil {
		return ""
	}
	pdbg.serr.Flush()
	return pdbg.berr.String()
}

func (pdbg *Pdbg) pdbgExcluded(dbg string) bool {
	for _, e := range pdbg.excludes {
		if strings.Contains(dbg, e) {
			// fmt.Printf("EXCLUDE over '%v' including '%v'\n", dbg, e) // DBG
			return true
		}
	}
	return false
}

func (pdbg *Pdbg) pdbgBreak(dbg string) bool {
	for _, b := range pdbg.breaks {
		if strings.Contains(dbg, b) {
			// fmt.Printf("BREAK over '%v' including '%v'\n", dbg, b) // DBG
			return true
		}
	}
	return false
}

func (pdbg *Pdbg) pdbgSkip(dbg string) (bool, int) {
	depthToAdd := 0
	for i, s := range pdbg.skips {
		if strings.Contains(dbg, s) {
			if i > 0 {
				depthToAdd = 1
			}
			// fmt.Printf("SKIP over '%v' including '%v'\n", dbg, s) // DBG
			return true, depthToAdd
		}
	}
	return false, depthToAdd
}

// Pdbgf uses global Pdbg variable for printing strings, with indent and function name
func Pdbgf(format string, args ...interface{}) string {
	return pdbg.Pdbgf(format, args...)
}

type caller func(skip int) (pc uintptr, file string, line int, ok bool)

var mycaller = runtime.Caller

// Pdbgf uses custom Pdbg variable for printing strings, with indent and function name
func (pdbg *Pdbg) Pdbgf(format string, args ...interface{}) string {
	msg := fmt.Sprintf(format+"\n", args...)
	msg = strings.TrimSpace(msg)

	pmsg := ""
	depth := 0
	nbskip := 0
	nbInitialSkips := 0
	first := true
	addOneForSkip := 0
	// fmt.Printf("~~~~~~~~~~~~~~~~~~~~~~\n") // DBG
	for ok := true; ok; {
		pc, file, line, ok := mycaller(depth)
		if !ok {
			break
		}
		fname := runtime.FuncForPC(pc).Name()
		fline := fmt.Sprintf("Name of function: '%v': '%+x' (line %v): file '%v'\n", fname, fname, line, file)
		// fmt.Println(fline) // DBG
		if pdbg.pdbgExcluded(fline) {
			depth = depth + 1
			if first {
				return ""
			}
			continue
		}
		if pdbg.pdbgBreak(fline) {
			break
		}
		if isSkipped, depthToAdd := pdbg.pdbgSkip(fline); isSkipped {
			depth = depth + 1
			nbskip = nbskip + 1
			addOneForSkip = addOneForSkip + depthToAdd
			continue
		}
		fnamerx1 := regexp.MustCompile(`.*\.func[^a-zA-Z0-9]`)
		fname = fnamerx1.ReplaceAllString(fname, "func.")
		fnamerx2 := regexp.MustCompile(`.*/`)
		fname = fnamerx2.ReplaceAllString(fname, "")
		if !strings.HasPrefix(fname, "func.") {
			fnamerx3 := regexp.MustCompile(`^.*?\.`)
			// fmt.Printf("fname before: '%v'", fname)
			fname = fnamerx3.ReplaceAllString(fname, "")
			// fmt.Printf(" => fname after: '%v'\n", fname)
			fnamerx4 := regexp.MustCompile(`[\(\)]`)
			fname = fnamerx4.ReplaceAllString(fname, "")
		}
		dbg := fname + ":" + fmt.Sprintf("%d", line)
		if first {
			// fmt.Printf(" => nbskip '%v'; addOneForSkip '%v'\n", nbskip, addOneForSkip) // DBG
			nbInitialSkips = nbskip - addOneForSkip
			pmsg = "[" + dbg + "]"
		} else {
			pmsg = pmsg + " (" + dbg + ")"
		}
		first = false
		depth = depth + 1
	}
	finalDepth := depth
	depth = finalDepth - nbInitialSkips - 1

	spaces := ""
	if depth >= 0 {
		spaces = strings.Repeat(" ", depth*2)
	}
	// fmt.Printf("spaces '%s', finalDepth '%d', depth '%d', nbInitialSkips '%d', addOneForSkip='%d'\n", spaces, finalDepth, depth, nbInitialSkips, addOneForSkip) // DBG
	res := pmsg
	if pmsg != "" {
		pmsg = spaces + pmsg + "\n"
	}
	msg = pmsg + spaces + "  " + msg + "\n"
	// fmt.Printf("==> MSG '%v'\n", msg) // DBG
	fmt.Fprint(pdbg.Err(), fmt.Sprint(msg))
	return res
}

var r = regexp.MustCompile(`:\d+[\)\]]`)
var r2 = regexp.MustCompile(`func\.\d+[\)\]]`)

// ShouldEqualNL is a custom goconvey assertion to ignore differences
// with func id and lines: `[globalPdbgExcludeTest:16] (func.019:167)` would
// be equal to [globalPdbgExcludeTest] (func)
// (see https://github.com/smartystreets/goconvey/wiki/Custom-Assertions)
func ShouldEqualNL(actual interface{}, expected ...interface{}) string {
	a := actual.(string)
	e := expected[0].(string)
	a = r.ReplaceAllStringFunc(a, func(s string) string { return s[len(s)-1:] })
	e = r.ReplaceAllStringFunc(e, func(s string) string { return s[len(s)-1:] })
	a = r2.ReplaceAllStringFunc(a, func(s string) string { return "func" + s[len(s)-1:] })
	e = r2.ReplaceAllStringFunc(e, func(s string) string { return "func" + s[len(s)-1:] })
	if a == e {
		return ""
	}
	return fmt.Sprintf("Extected: '%s'\nActual:   '%s'\n(Should be equal even with different lines and function ids)", e, a)
}
<commit_msg>Start implementing replacing os.Std: bad idea<commit_after>package godbg

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"regexp"
	"runtime"
	"strings"
)

// http://stackoverflow.com/a/23554672/6309 https://vividcortex.com/blog/2013/12/03/go-idiom-package-and-object/
// you design a type with methods as usual, and then you also place matching functions at the package level itself.
// These functions simply delegate to a default instance of the type that’s a private package-level variable, created in an init() function.

// Pdbg allows to print debug message with indent and function name added
type Pdbg struct {
	bout     *bytes.Buffer
	berr     *bytes.Buffer
	sout     *bufio.Writer
	serr     *bufio.Writer
	breaks   []string
	excludes []string
	skips    []string
	std      *std
}

type std struct {
	osstdout *os.File
	osstderr *os.File
	sow      *os.File
	sor      *os.File
	sew      *os.File
	ser      *os.File
}

// Out returns a writer for normal messages.
// By default, os.StdOut
func Out() io.Writer {
	return pdbg.Out()
}

// Out returns a writer for normal messages for a given pdbg instance.
// By default, os.StdOut
func (pdbg *Pdbg) Out() io.Writer {
	if pdbg.sout == nil {
		return os.Stdout
	}
	return pdbg.sout
}

// Err returns a writer for error messages.
// By default, os.StdErr
func Err() io.Writer {
	return pdbg.Err()
}

// Err returns a writer for error messages for a given pdbg instance.
// By default, os.StdErr
func (pdbg *Pdbg) Err() io.Writer {
	if pdbg.serr == nil {
		return os.Stderr
	}
	return pdbg.serr
}

// global pdbg used for printing
var pdbg *Pdbg

func init() {
	pdbg = NewPdbg()
	pdbg.std = &std{osstdout: os.Stdout, osstderr: os.Stderr}
}

// Option set an option for a Pdbg
// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
type Option func(*Pdbg)

// SetBuffers is an option for replacing stdout and stderr by
// bytes buffers (in a bufio.Writer).
// If apdbg is nil, set for the global pdbg instance
func SetBuffers(apdbg *Pdbg) {
	if apdbg == nil {
		apdbg = pdbg
	}
	// http://stackoverflow.com/questions/10473800/in-go-how-do-i-capture-stdout-of-a-function-into-a-string
	apdbg.bout = bytes.NewBuffer(nil)
	apdbg.sout = bufio.NewWriter(apdbg.bout)
	apdbg.berr = bytes.NewBuffer(nil)
	apdbg.serr = bufio.NewWriter(apdbg.berr)
	if apdbg.std != nil {
		r, w, err := os.Pipe()
		if err != nil {
			panic(err)
		}
		apdbg.std.sor = r
		apdbg.std.sow = w
		apdbg.sout = bufio.NewWriter(apdbg.std.sow)
		r, w, err = os.Pipe()
		if err != nil {
			panic(err)
		}
		apdbg.std.ser = r
		apdbg.std.sew = w
		apdbg.serr = bufio.NewWriter(apdbg.std.sew)

		os.Stdout = apdbg.std.sow
		os.Stderr = apdbg.std.sew
	}
}

// SetExcludes set excludes on a pdbg (nil for global pdbg)
func (pdbg *Pdbg) SetExcludes(excludes []string) {
	pdbg.excludes = excludes
}

// OptExcludes is an option to set excludes at the creation of a pdbg
func OptExcludes(excludes []string) Option {
	return func(apdbg *Pdbg) {
		apdbg.SetExcludes(excludes)
	}
}

// SetSkips set skips on a pdbg (nil for global pdbg)
func (pdbg *Pdbg) SetSkips(skips []string) {
	sk := []string{"/godbg.go'"}
	sk = append(sk, skips...)
	pdbg.skips = sk
}

// OptSkips is an option to set excludes at the creation of a pdbg
func OptSkips(skips []string) Option {
	return func(apdbg *Pdbg) {
		apdbg.SetSkips(skips)
	}
}

// NewPdbg creates a PDbg instance, with options
func NewPdbg(options ...Option) *Pdbg {
	newpdbg := &Pdbg{}
	for _, option := range options {
		option(newpdbg)
	}
	newpdbg.breaks = append(newpdbg.breaks, "smartystreets")
	//newpdbg.breaks = append(newpdbg.breaks, "(*Pdbg).Pdbgf")
	newpdbg.skips = append(newpdbg.skips, "/godbg.go'")
	return newpdbg
}

// ResetIOs reset the out and err buffer of global pdbg instance
func ResetIOs() {
	pdbg.ResetIOs()
}

// ResetIOs reset the out and err buffer
// (unless they were the default stdout and stderr,
// in which case it does nothing)
func (pdbg *Pdbg) ResetIOs() {
	if pdbg.sout != nil {
		pdbg.bout = bytes.NewBuffer(nil)
		pdbg.sout.Reset(pdbg.bout)
		pdbg.berr = bytes.NewBuffer(nil)
		pdbg.serr.Reset(pdbg.berr)
	}
}

// OutString returns the string for out messages for the global pdbg instance.
// It flushes the out buffer.
// If out is set to os.Stdout, returns an empty string
func OutString() string {
	return pdbg.OutString()
}

// OutString returns the string for out messages for a given pdbg instance.
// It flushes the out buffer.
// If out is set to os.Stdout, returns an empty string
func (pdbg *Pdbg) OutString() string {
	if pdbg.sout == nil {
		return ""
	}
	pdbg.sout.Flush()
	return pdbg.bout.String()
}

// ErrString returns the string for error messages for the global pdbg instance.
// It flushes the err buffer.
// If err is set to os.StdErr, returns an empty string
func ErrString() string {
	return pdbg.ErrString()
}

// ErrString returns the string for error messages for a given pdbg instance.
// It flushes the err buffer.
// If err is set to os.StdErr, returns an empty string
func (pdbg *Pdbg) ErrString() string {
	if pdbg.serr == nil {
		return ""
	}
	pdbg.serr.Flush()
	return pdbg.berr.String()
}

func (pdbg *Pdbg) pdbgExcluded(dbg string) bool {
	for _, e := range pdbg.excludes {
		if strings.Contains(dbg, e) {
			// fmt.Printf("EXCLUDE over '%v' including '%v'\n", dbg, e) // DBG
			return true
		}
	}
	return false
}

func (pdbg *Pdbg) pdbgBreak(dbg string) bool {
	for _, b := range pdbg.breaks {
		if strings.Contains(dbg, b) {
			// fmt.Printf("BREAK over '%v' including '%v'\n", dbg, b) // DBG
			return true
		}
	}
	return false
}

func (pdbg *Pdbg) pdbgSkip(dbg string) (bool, int) {
	depthToAdd := 0
	for i, s := range pdbg.skips {
		if strings.Contains(dbg, s) {
			if i > 0 {
				depthToAdd = 1
			}
			// fmt.Printf("SKIP over '%v' including '%v'\n", dbg, s) // DBG
			return true, depthToAdd
		}
	}
	return false, depthToAdd
}

// Pdbgf uses global Pdbg variable for printing strings, with indent and function name
func Pdbgf(format string, args ...interface{}) string {
	return pdbg.Pdbgf(format, args...)
}

type caller func(skip int) (pc uintptr, file string, line int, ok bool)

var mycaller = runtime.Caller

// Pdbgf uses custom Pdbg variable for printing strings, with indent and function name
func (pdbg *Pdbg) Pdbgf(format string, args ...interface{}) string {
	msg := fmt.Sprintf(format+"\n", args...)
	msg = strings.TrimSpace(msg)

	pmsg := ""
	depth := 0
	nbskip := 0
	nbInitialSkips := 0
	first := true
	addOneForSkip := 0
	// fmt.Printf("~~~~~~~~~~~~~~~~~~~~~~\n") // DBG
	for ok := true; ok; {
		pc, file, line, ok := mycaller(depth)
		if !ok {
			break
		}
		fname := runtime.FuncForPC(pc).Name()
		fline := fmt.Sprintf("Name of function: '%v': '%+x' (line %v): file '%v'\n", fname, fname, line, file)
		// fmt.Println(fline) // DBG
		if pdbg.pdbgExcluded(fline) {
			depth = depth + 1
			if first {
				return ""
			}
			continue
		}
		if pdbg.pdbgBreak(fline) {
			break
		}
		if isSkipped, depthToAdd := pdbg.pdbgSkip(fline); isSkipped {
			depth = depth + 1
			nbskip = nbskip + 1
			addOneForSkip = addOneForSkip + depthToAdd
			continue
		}
		fnamerx1 := regexp.MustCompile(`.*\.func[^a-zA-Z0-9]`)
		fname = fnamerx1.ReplaceAllString(fname, "func.")
		fnamerx2 := regexp.MustCompile(`.*/`)
		fname = fnamerx2.ReplaceAllString(fname, "")
		if !strings.HasPrefix(fname, "func.") {
			fnamerx3 := regexp.MustCompile(`^.*?\.`)
			// fmt.Printf("fname before: '%v'", fname)
			fname = fnamerx3.ReplaceAllString(fname, "")
			// fmt.Printf(" => fname after: '%v'\n", fname)
			fnamerx4 := regexp.MustCompile(`[\(\)]`)
			fname = fnamerx4.ReplaceAllString(fname, "")
		}
		dbg := fname + ":" + fmt.Sprintf("%d", line)
		if first {
			// fmt.Printf(" => nbskip '%v'; addOneForSkip '%v'\n", nbskip, addOneForSkip) // DBG
			nbInitialSkips = nbskip - addOneForSkip
			pmsg = "[" + dbg + "]"
		} else {
			pmsg = pmsg + " (" + dbg + ")"
		}
		first = false
		depth = depth + 1
	}
	finalDepth := depth
	depth = finalDepth - nbInitialSkips - 1

	spaces := ""
	if depth >= 0 {
		spaces = strings.Repeat(" ", depth*2)
	}
	// fmt.Printf("spaces '%s', finalDepth '%d', depth '%d', nbInitialSkips '%d', addOneForSkip='%d'\n", spaces, finalDepth, depth, nbInitialSkips, addOneForSkip) // DBG
	res := pmsg
	if pmsg != "" {
		pmsg = spaces + pmsg + "\n"
	}
	msg = pmsg + spaces + "  " + msg + "\n"
	// fmt.Printf("==> MSG '%v'\n", msg) // DBG
	fmt.Fprint(pdbg.Err(), fmt.Sprint(msg))
	return res
}

var r = regexp.MustCompile(`:\d+[\)\]]`)
var r2 = regexp.MustCompile(`func\.\d+[\)\]]`)

// ShouldEqualNL is a custom goconvey assertion to ignore differences
// with func id and lines: `[globalPdbgExcludeTest:16] (func.019:167)` would
// be equal to [globalPdbgExcludeTest] (func)
// (see https://github.com/smartystreets/goconvey/wiki/Custom-Assertions)
func ShouldEqualNL(actual interface{}, expected ...interface{}) string {
	a := actual.(string)
	e := expected[0].(string)
	a = r.ReplaceAllStringFunc(a, func(s string) string { return s[len(s)-1:] })
	e = r.ReplaceAllStringFunc(e, func(s string) string { return s[len(s)-1:] })
	a = r2.ReplaceAllStringFunc(a, func(s string) string { return "func" + s[len(s)-1:] })
	e = r2.ReplaceAllStringFunc(e, func(s string) string { return "func" + s[len(s)-1:] })
	if a == e {
		return ""
	}
	return fmt.Sprintf("Extected: '%s'\nActual:   '%s'\n(Should be equal even with different lines and function ids)", e, a)
}
<|endoftext|>