<commit_before>package dnsmasq

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/storage/filesystem"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/subprocess"
	"github.com/lxc/lxd/shared/version"
)

const staticAllocationDeviceSeparator = "."

// DHCPAllocation represents an IP allocation from dnsmasq.
type DHCPAllocation struct {
	IP             net.IP
	StaticFileName string
	MAC            net.HardwareAddr
}

// ConfigMutex used to coordinate access to the dnsmasq config files.
var ConfigMutex sync.Mutex

// UpdateStaticEntry writes a single dhcp-host line for a network/instance combination.
func UpdateStaticEntry(network string, projectName string, instanceName string, deviceName string, netConfig map[string]string, hwaddr string, ipv4Address string, ipv6Address string) error {
	hwaddr = strings.ToLower(hwaddr)
	line := hwaddr

	// Generate the dhcp-host line
	if ipv4Address != "" {
		line += fmt.Sprintf(",%s", ipv4Address)
	}

	if ipv6Address != "" {
		line += fmt.Sprintf(",[%s]", ipv6Address)
	}

	if netConfig["dns.mode"] == "" || netConfig["dns.mode"] == "managed" {
		line += fmt.Sprintf(",%s", project.DNS(projectName, instanceName))
	}

	if line == hwaddr {
		return nil
	}

	deviceStaticFileName := StaticAllocationFileName(projectName, instanceName, deviceName)
	err := ioutil.WriteFile(shared.VarPath("networks", network, "dnsmasq.hosts", deviceStaticFileName), []byte(line+"\n"), 0644)
	if err != nil {
		return err
	}

	return nil
}

// RemoveStaticEntry removes a single dhcp-host line for a network/instance combination.
func RemoveStaticEntry(network string, projectName string, instanceName string, deviceName string) error {
	deviceStaticFileName := StaticAllocationFileName(projectName, instanceName, deviceName)
	err := os.Remove(shared.VarPath("networks", network, "dnsmasq.hosts", deviceStaticFileName))
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	return nil
}

// Kill kills dnsmasq for a particular network (or optionally reloads it).
func Kill(name string, reload bool) error {
	pidPath := shared.VarPath("networks", name, "dnsmasq.pid")

	// If the pid file doesn't exist, there is no process to kill.
	if !shared.PathExists(pidPath) {
		return nil
	}

	// Import saved subprocess details
	p, err := subprocess.ImportProcess(pidPath)
	if err != nil {
		return fmt.Errorf("Could not read pid file: %s", err)
	}

	if reload {
		err = p.Reload()
		if err != nil && err != subprocess.ErrNotRunning {
			return fmt.Errorf("Could not reload dnsmasq: %s", err)
		}

		return nil
	}

	err = p.Stop()
	if err != nil && err != subprocess.ErrNotRunning {
		return fmt.Errorf("Unable to kill dnsmasq: %s", err)
	}

	time.Sleep(100 * time.Millisecond) // Give OS time to release sockets.

	return nil
}

// GetVersion returns the version of dnsmasq.
func GetVersion() (*version.DottedVersion, error) {
	output, err := shared.RunCommandCLocale("dnsmasq", "--version")
	if err != nil {
		return nil, fmt.Errorf("Failed to check dnsmasq version: %v", err)
	}

	lines := strings.Split(string(output), " ")
	return version.Parse(lines[2])
}

// DHCPStaticAllocation retrieves the dnsmasq statically allocated MAC and IPs for an instance device static file.
// Returns MAC, IPv4 and IPv6 DHCPAllocation structs respectively.
func DHCPStaticAllocation(network string, deviceStaticFileName string) (net.HardwareAddr, DHCPAllocation, DHCPAllocation, error) {
	var IPv4, IPv6 DHCPAllocation
	var mac net.HardwareAddr

	file, err := os.Open(shared.VarPath("networks", network, "dnsmasq.hosts", deviceStaticFileName))
	if err != nil {
		return nil, IPv4, IPv6, err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		fields := strings.SplitN(scanner.Text(), ",", -1)
		for _, field := range fields {
			// Check if field is IPv4 or IPv6 address.
			if strings.Count(field, ".") == 3 {
				IP := net.ParseIP(field)
				if IP.To4() == nil {
					return nil, IPv4, IPv6, fmt.Errorf("Error parsing IP address %q", field)
				}
				IPv4 = DHCPAllocation{StaticFileName: deviceStaticFileName, IP: IP.To4(), MAC: mac}

			} else if strings.HasPrefix(field, "[") && strings.HasSuffix(field, "]") {
				IP := net.ParseIP(field[1 : len(field)-1])
				if IP == nil {
					return nil, IPv4, IPv6, fmt.Errorf("Error parsing IP address %q", field)
				}
				IPv6 = DHCPAllocation{StaticFileName: deviceStaticFileName, IP: IP, MAC: mac}
			} else if strings.Count(field, ":") == 5 {
				// This field is expected to come first, so that mac variable can be used with
				// populating the DHCPAllocation structs too.
				mac, err = net.ParseMAC(field)
				if err != nil {
					return nil, IPv4, IPv6, fmt.Errorf("Error parsing MAC address %q", field)
				}
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return nil, IPv4, IPv6, err
	}

	return mac, IPv4, IPv6, nil
}

// DHCPAllAllocations returns a map of IPs currently allocated (statically and dynamically)
// in dnsmasq for a specific network. The returned map is keyed by a 16 byte array representing
// the net.IP format. The value of each map item is a DHCPAllocation struct containing at least
// whether the allocation was static or dynamic and optionally instance name or MAC address.
// MAC addresses are only included for dynamic IPv4 allocations (where name is not reliable).
// Static allocations are not overridden by dynamic allocations, allowing for instance name to be
// included for static IPv6 allocations. IPv6 addresses that are dynamically assigned cannot be
// reliably linked to instances using either name or MAC because dnsmasq does not record the MAC
// address for these records, and the recorded host name can be set by the instance if the dns.mode
// for the network is set to "dynamic" and so cannot be trusted, so in this case we do not return
// any identifying info.
func DHCPAllAllocations(network string) (map[[4]byte]DHCPAllocation, map[[16]byte]DHCPAllocation, error) {
	IPv4s := make(map[[4]byte]DHCPAllocation)
	IPv6s := make(map[[16]byte]DHCPAllocation)

	// First read all statically allocated IPs.
	files, err := ioutil.ReadDir(shared.VarPath("networks", network, "dnsmasq.hosts"))
	if err != nil && os.IsNotExist(err) {
		return nil, nil, err
	}

	for _, entry := range files {
		_, IPv4, IPv6, err := DHCPStaticAllocation(network, "", "", "", entry.Name())
		if err != nil {
			return nil, nil, err
		}

		if IPv4.IP != nil {
			var IPKey [4]byte
			copy(IPKey[:], IPv4.IP.To4())
			IPv4s[IPKey] = IPv4
		}

		if IPv6.IP != nil {
			var IPKey [16]byte
			copy(IPKey[:], IPv6.IP.To16())
			IPv6s[IPKey] = IPv6
		}
	}

	// Next read all dynamic allocated IPs.
	file, err := os.Open(shared.VarPath("networks", network, "dnsmasq.leases"))
	if err != nil {
		return nil, nil, err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		fields := strings.Fields(scanner.Text())
		if len(fields) == 5 {
			IP := net.ParseIP(fields[2])
			if IP == nil {
				return nil, nil, fmt.Errorf("Error parsing IP address: %v", fields[2])
			}

			// Handle IPv6 addresses.
			if IP.To4() == nil {
				var IPKey [16]byte
				copy(IPKey[:], IP.To16())

				// Don't replace IPs from static config as more reliable.
				if IPv6s[IPKey].Name != "" {
					continue
				}

				IPv6s[IPKey] = DHCPAllocation{
					Static: false,
					IP:     IP.To16(),
				}
			} else {
				// MAC only available in IPv4 leases.
				MAC, err := net.ParseMAC(fields[1])
				if err != nil {
					return nil, nil, err
				}

				var IPKey [4]byte
				copy(IPKey[:], IP.To4())

				// Don't replace IPs from static config as more reliable.
				if IPv4s[IPKey].Name != "" {
					continue
				}

				IPv4s[IPKey] = DHCPAllocation{
					MAC:    MAC,
					Static: false,
					IP:     IP.To4(),
				}
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return nil, nil, err
	}

	return IPv4s, IPv6s, nil
}

// StaticAllocationFileName returns the file name to use for a dnsmasq instance device static allocation.
func StaticAllocationFileName(projectName string, instanceName string, deviceName string) string {
	escapedDeviceName := filesystem.PathNameEncode(deviceName)

	return strings.Join([]string{project.Instance(projectName, instanceName), escapedDeviceName}, staticAllocationDeviceSeparator)
}
<commit_msg>lxd/dnsmasq: Update DHCPAllAllocations to use StaticFileName field<commit_after>package dnsmasq

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/storage/filesystem"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/subprocess"
	"github.com/lxc/lxd/shared/version"
)

const staticAllocationDeviceSeparator = "."

// DHCPAllocation represents an IP allocation from dnsmasq.
type DHCPAllocation struct {
	IP             net.IP
	StaticFileName string
	MAC            net.HardwareAddr
}

// ConfigMutex used to coordinate access to the dnsmasq config files.
var ConfigMutex sync.Mutex

// UpdateStaticEntry writes a single dhcp-host line for a network/instance combination.
func UpdateStaticEntry(network string, projectName string, instanceName string, deviceName string, netConfig map[string]string, hwaddr string, ipv4Address string, ipv6Address string) error {
	hwaddr = strings.ToLower(hwaddr)
	line := hwaddr

	// Generate the dhcp-host line
	if ipv4Address != "" {
		line += fmt.Sprintf(",%s", ipv4Address)
	}

	if ipv6Address != "" {
		line += fmt.Sprintf(",[%s]", ipv6Address)
	}

	if netConfig["dns.mode"] == "" || netConfig["dns.mode"] == "managed" {
		line += fmt.Sprintf(",%s", project.DNS(projectName, instanceName))
	}

	if line == hwaddr {
		return nil
	}

	deviceStaticFileName := StaticAllocationFileName(projectName, instanceName, deviceName)
	err := ioutil.WriteFile(shared.VarPath("networks", network, "dnsmasq.hosts", deviceStaticFileName), []byte(line+"\n"), 0644)
	if err != nil {
		return err
	}

	return nil
}

// RemoveStaticEntry removes a single dhcp-host line for a network/instance combination.
func RemoveStaticEntry(network string, projectName string, instanceName string, deviceName string) error {
	deviceStaticFileName := StaticAllocationFileName(projectName, instanceName, deviceName)
	err := os.Remove(shared.VarPath("networks", network, "dnsmasq.hosts", deviceStaticFileName))
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	return nil
}

// Kill kills dnsmasq for a particular network (or optionally reloads it).
func Kill(name string, reload bool) error {
	pidPath := shared.VarPath("networks", name, "dnsmasq.pid")

	// If the pid file doesn't exist, there is no process to kill.
	if !shared.PathExists(pidPath) {
		return nil
	}

	// Import saved subprocess details
	p, err := subprocess.ImportProcess(pidPath)
	if err != nil {
		return fmt.Errorf("Could not read pid file: %s", err)
	}

	if reload {
		err = p.Reload()
		if err != nil && err != subprocess.ErrNotRunning {
			return fmt.Errorf("Could not reload dnsmasq: %s", err)
		}

		return nil
	}

	err = p.Stop()
	if err != nil && err != subprocess.ErrNotRunning {
		return fmt.Errorf("Unable to kill dnsmasq: %s", err)
	}

	time.Sleep(100 * time.Millisecond) // Give OS time to release sockets.

	return nil
}

// GetVersion returns the version of dnsmasq.
func GetVersion() (*version.DottedVersion, error) {
	output, err := shared.RunCommandCLocale("dnsmasq", "--version")
	if err != nil {
		return nil, fmt.Errorf("Failed to check dnsmasq version: %v", err)
	}

	lines := strings.Split(string(output), " ")
	return version.Parse(lines[2])
}

// DHCPStaticAllocation retrieves the dnsmasq statically allocated MAC and IPs for an instance device static file.
// Returns MAC, IPv4 and IPv6 DHCPAllocation structs respectively.
func DHCPStaticAllocation(network string, deviceStaticFileName string) (net.HardwareAddr, DHCPAllocation, DHCPAllocation, error) {
	var IPv4, IPv6 DHCPAllocation
	var mac net.HardwareAddr

	file, err := os.Open(shared.VarPath("networks", network, "dnsmasq.hosts", deviceStaticFileName))
	if err != nil {
		return nil, IPv4, IPv6, err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		fields := strings.SplitN(scanner.Text(), ",", -1)
		for _, field := range fields {
			// Check if field is IPv4 or IPv6 address.
			if strings.Count(field, ".") == 3 {
				IP := net.ParseIP(field)
				if IP.To4() == nil {
					return nil, IPv4, IPv6, fmt.Errorf("Error parsing IP address %q", field)
				}
				IPv4 = DHCPAllocation{StaticFileName: deviceStaticFileName, IP: IP.To4(), MAC: mac}

			} else if strings.HasPrefix(field, "[") && strings.HasSuffix(field, "]") {
				IP := net.ParseIP(field[1 : len(field)-1])
				if IP == nil {
					return nil, IPv4, IPv6, fmt.Errorf("Error parsing IP address %q", field)
				}
				IPv6 = DHCPAllocation{StaticFileName: deviceStaticFileName, IP: IP, MAC: mac}
			} else if strings.Count(field, ":") == 5 {
				// This field is expected to come first, so that mac variable can be used with
				// populating the DHCPAllocation structs too.
				mac, err = net.ParseMAC(field)
				if err != nil {
					return nil, IPv4, IPv6, fmt.Errorf("Error parsing MAC address %q", field)
				}
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return nil, IPv4, IPv6, err
	}

	return mac, IPv4, IPv6, nil
}

// DHCPAllAllocations returns a map of IPs currently allocated (statically and dynamically)
// in dnsmasq for a specific network. The returned map is keyed by a 16 byte array representing
// the net.IP format. The value of each map item is a DHCPAllocation struct containing at least
// whether the allocation was static or dynamic and optionally instance name or MAC address.
// MAC addresses are only included for dynamic IPv4 allocations (where name is not reliable).
// Static allocations are not overridden by dynamic allocations, allowing for instance name to be
// included for static IPv6 allocations. IPv6 addresses that are dynamically assigned cannot be
// reliably linked to instances using either name or MAC because dnsmasq does not record the MAC
// address for these records, and the recorded host name can be set by the instance if the dns.mode
// for the network is set to "dynamic" and so cannot be trusted, so in this case we do not return
// any identifying info.
func DHCPAllAllocations(network string) (map[[4]byte]DHCPAllocation, map[[16]byte]DHCPAllocation, error) {
	IPv4s := make(map[[4]byte]DHCPAllocation)
	IPv6s := make(map[[16]byte]DHCPAllocation)

	// First read all statically allocated IPs.
	files, err := ioutil.ReadDir(shared.VarPath("networks", network, "dnsmasq.hosts"))
	if err != nil && os.IsNotExist(err) {
		return nil, nil, err
	}

	for _, entry := range files {
		_, IPv4, IPv6, err := DHCPStaticAllocation(network, "", "", "", entry.Name())
		if err != nil {
			return nil, nil, err
		}

		if IPv4.IP != nil {
			var IPKey [4]byte
			copy(IPKey[:], IPv4.IP.To4())
			IPv4s[IPKey] = IPv4
		}

		if IPv6.IP != nil {
			var IPKey [16]byte
			copy(IPKey[:], IPv6.IP.To16())
			IPv6s[IPKey] = IPv6
		}
	}

	// Next read all dynamic allocated IPs.
	file, err := os.Open(shared.VarPath("networks", network, "dnsmasq.leases"))
	if err != nil {
		return nil, nil, err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		fields := strings.Fields(scanner.Text())
		if len(fields) == 5 {
			IP := net.ParseIP(fields[2])
			if IP == nil {
				return nil, nil, fmt.Errorf("Error parsing IP address: %v", fields[2])
			}

			// Handle IPv6 addresses.
			if IP.To4() == nil {
				var IPKey [16]byte
				copy(IPKey[:], IP.To16())

				// Don't replace IPs from static config as more reliable.
				if IPv6s[IPKey].StaticFileName != "" {
					continue
				}

				IPv6s[IPKey] = DHCPAllocation{
					IP: IP.To16(),
				}
			} else {
				// MAC only available in IPv4 leases.
				MAC, err := net.ParseMAC(fields[1])
				if err != nil {
					return nil, nil, err
				}

				var IPKey [4]byte
				copy(IPKey[:], IP.To4())

				// Don't replace IPs from static config as more reliable.
				if IPv4s[IPKey].StaticFileName != "" {
					continue
				}

				IPv4s[IPKey] = DHCPAllocation{
					MAC: MAC,
					IP:  IP.To4(),
				}
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return nil, nil, err
	}

	return IPv4s, IPv6s, nil
}

// StaticAllocationFileName returns the file name to use for a dnsmasq instance device static allocation.
func StaticAllocationFileName(projectName string, instanceName string, deviceName string) string {
	escapedDeviceName := filesystem.PathNameEncode(deviceName)

	return strings.Join([]string{project.Instance(projectName, instanceName), escapedDeviceName}, staticAllocationDeviceSeparator)
}
<|endoftext|>


<commit_before>// Package errcheck is the library used to implement the errcheck command-line tool.
//
// Note: The API of this package has not been finalized and may change at any point.
package errcheck

import (
	"bytes"
	"code.google.com/p/go.tools/go/exact"
	"code.google.com/p/go.tools/go/types"
	"errors"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"honnef.co/go/importer"
	"io/ioutil"
	"os"
	"regexp"
)

var (
	// ErrNoGoFiles is returned when CheckPackage is run on a package with no Go source files
	ErrNoGoFiles = errors.New("package contains no go source files")
)

// UncheckedErrors is returned from the CheckPackage function if the package contains
// any unchecked errors.
type UncheckedErrors struct {
	// Errors is a list of all the unchecked errors in the package.
	// Printing an error reports its position within the file and the contents of the line.
	Errors []error
}

func (e UncheckedErrors) Error() string {
	return fmt.Sprint(len(e.Errors), "unchecked errors")
}

func CheckPackage(pkgPath string, ignore map[string]*regexp.Regexp, blank bool) error {
	pkg, err := newPackage(pkgPath)
	if err != nil {
		return err
	}

	return checkPackage(pkg, ignore, blank)
}

// package_ represents a single Go package
type package_ struct {
	path     string
	fset     *token.FileSet
	astFiles []*ast.File
	files    map[string]file
}

// newPackage creates a package_ from the Go files in path
func newPackage(path string) (package_, error) {
	p := package_{path: path, fset: token.NewFileSet()}
	pkg, err := findPackage(path)
	if err != nil {
		return p, fmt.Errorf("could not find package: %s", err)
	}
	fileNames := getFiles(pkg)

	if len(fileNames) == 0 {
		return p, ErrNoGoFiles
	}

	p.astFiles = make([]*ast.File, len(fileNames))
	p.files = make(map[string]file, len(fileNames))

	for i, fileName := range fileNames {
		f, err := parseFile(p.fset, fileName)
		if err != nil {
			return p, fmt.Errorf("could not parse %s: %s", fileName, err)
		}
		p.files[fileName] = f
		p.astFiles[i] = f.ast
	}

	return p, nil
}

// typedPackage is like package_ but with type information
type typedPackage struct {
	package_
	callTypes map[*ast.CallExpr]types.Type
	identObjs map[*ast.Ident]types.Object
}

// typeCheck creates a typedPackage from a package_
func typeCheck(p package_) (typedPackage, error) {
	tp := typedPackage{
		package_:  p,
		callTypes: make(map[*ast.CallExpr]types.Type),
		identObjs: make(map[*ast.Ident]types.Object),
	}

	exprFn := func(x ast.Expr, typ types.Type, val exact.Value) {
		call, ok := x.(*ast.CallExpr)
		if !ok {
			return
		}
		tp.callTypes[call] = typ
	}
	identFn := func(id *ast.Ident, obj types.Object) {
		tp.identObjs[id] = obj
	}
	context := types.Context{
		Expr:   exprFn,
		Ident:  identFn,
		Import: importer.NewImporter().Import,
	}

	_, err := context.Check(p.path, p.fset, p.astFiles...)
	return tp, err
}

// file represents a single Go source file
type file struct {
	fset  *token.FileSet
	name  string
	ast   *ast.File
	lines [][]byte
}

func parseFile(fset *token.FileSet, fileName string) (f file, err error) {
	rd, err := os.Open(fileName)
	if err != nil {
		return f, err
	}
	defer rd.Close()

	data, err := ioutil.ReadAll(rd)
	if err != nil {
		return f, err
	}

	astFile, err := parser.ParseFile(fset, fileName, bytes.NewReader(data), parser.ParseComments)
	if err != nil {
		return f, fmt.Errorf("could not parse: %s", err)
	}

	lines := bytes.Split(data, []byte("\n"))
	f = file{fset: fset, name: fileName, ast: astFile, lines: lines}
	return f, nil
}

// checker implements the errcheck algorithm
type checker struct {
	pkg    typedPackage
	ignore map[string]*regexp.Regexp
	blank  bool

	errors []error
}

type uncheckedError struct {
	pos  token.Position
	line []byte
}

func (e uncheckedError) Error() string {
	return fmt.Sprintf("%s\t%s", e.pos, e.line)
}

func (c *checker) ignoreCall(call *ast.CallExpr) bool {
	// Try to get an identifier.
	// Currently only supports simple expressions:
	//     1. f()
	//     2. x.y.f()
	var id *ast.Ident
	switch exp := call.Fun.(type) {
	case (*ast.Ident):
		id = exp
	case (*ast.SelectorExpr):
		id = exp.Sel
	default:
		// eg: *ast.SliceExpr, *ast.IndexExpr
	}

	if id == nil {
		return false
	}

	// If we got an identifier for the function, see if it is ignored

	if re, ok := c.ignore[""]; ok && re.MatchString(id.Name) {
		return true
	}

	if obj := c.pkg.identObjs[id]; obj != nil {
		if pkg := obj.Pkg(); pkg != nil {
			if re, ok := c.ignore[pkg.Path()]; ok {
				return re.MatchString(id.Name)
			}
		}
	}

	return false
}

// errorsByArg returns a slice s such that
// len(s) == number of return types of call
// s[i] == true iff return type at position i from left is an error type
func (c *checker) errorsByArg(call *ast.CallExpr) []bool {
	switch t := c.pkg.callTypes[call].(type) {
	case *types.Named:
		// Single return
		return []bool{isErrorType(t.Obj())}
	case *types.Tuple:
		// Multiple returns
		s := make([]bool, t.Len())
		for i := 0; i < t.Len(); i++ {
			nt, ok := t.At(i).Type().(*types.Named)
			s[i] = ok && isErrorType(nt.Obj())
		}
		return s
	}
	return nil
}

func (c *checker) callReturnsError(call *ast.CallExpr) bool {
	for _, isError := range c.errorsByArg(call) {
		if isError {
			return true
		}
	}
	return false
}

func (c *checker) addErrorAtPosition(position token.Pos) {
	pos := c.pkg.fset.Position(position)
	line := bytes.TrimSpace(c.pkg.files[pos.Filename].lines[pos.Line-1])
	c.errors = append(c.errors, uncheckedError{pos, line})
}

func (c *checker) Visit(node ast.Node) ast.Visitor {
	switch stmt := node.(type) {
	case *ast.ExprStmt:
		if call, ok := stmt.X.(*ast.CallExpr); ok {
			if !c.ignoreCall(call) && c.callReturnsError(call) {
				c.addErrorAtPosition(call.Lparen)
			}
		}
	case *ast.AssignStmt:
		if !c.blank {
			break
		}
		if len(stmt.Rhs) == 1 {
			// single value on rhs; check against lhs identifiers
			if call, ok := stmt.Rhs[0].(*ast.CallExpr); ok {
				if c.ignoreCall(call) {
					break
				}
				isError := c.errorsByArg(call)
				for i := 0; i < len(stmt.Lhs); i++ {
					if id, ok := stmt.Lhs[i].(*ast.Ident); ok {
						if id.Name == "_" && isError[i] {
							c.addErrorAtPosition(id.NamePos)
						}
					}
				}
			}
		} else {
			// multiple value on rhs; in this case a call can't return
			// multiple values. Assume len(stmt.Lhs) == len(stmt.Rhs)
			for i := 0; i < len(stmt.Lhs); i++ {
				if id, ok := stmt.Lhs[i].(*ast.Ident); ok {
					if call, ok := stmt.Rhs[i].(*ast.CallExpr); ok {
						if c.ignoreCall(call) {
							continue
						}
						if id.Name == "_" && c.callReturnsError(call) {
							c.addErrorAtPosition(id.NamePos)
						}
					}
				}
			}
		}
	default:
	}
	return c
}

func checkPackage(pkg package_, ignore map[string]*regexp.Regexp, blank bool) error {
	tp, err := typeCheck(pkg)
	if err != nil {
		return fmt.Errorf("could not type check: %s", err)
	}

	visitor := &checker{tp, ignore, blank, []error{}}
	for _, astFile := range pkg.astFiles {
		ast.Walk(visitor, astFile)
	}

	if len(visitor.errors) > 0 {
		return UncheckedErrors{visitor.errors}
	}
	return nil
}

type obj interface {
	Pkg() *types.Package
	Name() string
}

func isErrorType(v obj) bool {
	return v.Pkg() == nil && v.Name() == "error"
}
<commit_msg>Use go.tools/importer to parse and import packages a la go build<commit_after>// Package errcheck is the library used to implement the errcheck command-line tool.
//
// Note: The API of this package has not been finalized and may change at any point.
package errcheck

import (
	"bytes"
	"code.google.com/p/go.tools/go/types"
	"code.google.com/p/go.tools/importer"
	"errors"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"regexp"
)

var (
	// ErrNoGoFiles is returned when CheckPackage is run on a package with no Go source files
	ErrNoGoFiles = errors.New("package contains no go source files")
)

// UncheckedErrors is returned from the CheckPackage function if the package contains
// any unchecked errors.
type UncheckedErrors struct {
	// Errors is a list of all the unchecked errors in the package.
	// Printing an error reports its position within the file and the contents of the line.
	Errors []error
}

func (e UncheckedErrors) Error() string {
	return fmt.Sprint(len(e.Errors), "unchecked errors")
}

func CheckPackage(pkgPath string, ignore map[string]*regexp.Regexp, blank bool) error {
	pkg, err := newPackage(pkgPath)
	if err != nil {
		return err
	}

	return checkPackage(pkg, ignore, blank)
}

// package_ represents a single Go package
type package_ struct {
	path     string
	fset     *token.FileSet
	astFiles []*ast.File
	files    map[string]file
}

// newPackage creates a package_ from the Go files in path
func newPackage(path string) (package_, error) {
	p := package_{path: path, fset: token.NewFileSet()}
	pkg, err := findPackage(path)
	if err != nil {
		return p, fmt.Errorf("could not find package: %s", err)
	}
	fileNames := getFiles(pkg)

	if len(fileNames) == 0 {
		return p, ErrNoGoFiles
	}

	p.astFiles = make([]*ast.File, len(fileNames))
	p.files = make(map[string]file, len(fileNames))

	for i, fileName := range fileNames {
		f, err := parseFile(p.fset, fileName)
		if err != nil {
			return p, fmt.Errorf("could not parse %s: %s", fileName, err)
		}
		p.files[fileName] = f
		p.astFiles[i] = f.ast
	}

	return p, nil
}

// typedPackage is like package_ but with type information
type typedPackage struct {
	package_
	info *importer.PackageInfo
}

// typeCheck creates a typedPackage from a package_
func typeCheck(p package_) (typedPackage, error) {
	context := types.Context{}

	loader := importer.MakeGoBuildLoader(nil)
	importerContext := &importer.Context{
		TypeChecker: context,
		Loader:      loader,
	}
	importer := importer.New(importerContext)
	info, err := importer.LoadPackage(p.path)
	return typedPackage{
		package_: p,
		info:     info,
	}, err
}

// file represents a single Go source file
type file struct {
	fset  *token.FileSet
	name  string
	ast   *ast.File
	lines [][]byte
}

func parseFile(fset *token.FileSet, fileName string) (f file, err error) {
	rd, err := os.Open(fileName)
	if err != nil {
		return f, err
	}
	defer rd.Close()

	data, err := ioutil.ReadAll(rd)
	if err != nil {
		return f, err
	}

	astFile, err := parser.ParseFile(fset, fileName, bytes.NewReader(data), parser.ParseComments)
	if err != nil {
		return f, fmt.Errorf("could not parse: %s", err)
	}

	lines := bytes.Split(data, []byte("\n"))
	f = file{fset: fset, name: fileName, ast: astFile, lines: lines}
	return f, nil
}

// checker implements the errcheck algorithm
type checker struct {
	pkg    typedPackage
	ignore map[string]*regexp.Regexp
	blank  bool

	errors []error
}

type uncheckedError struct {
	pos  token.Position
	line []byte
}

func (e uncheckedError) Error() string {
	return fmt.Sprintf("%s\t%s", e.pos, e.line)
}

func (c *checker) ignoreCall(call *ast.CallExpr) bool {
	// Try to get an identifier.
	// Currently only supports simple expressions:
	//     1. f()
	//     2. x.y.f()
	var id *ast.Ident
	switch exp := call.Fun.(type) {
	case (*ast.Ident):
		id = exp
	case (*ast.SelectorExpr):
		id = exp.Sel
	default:
		// eg: *ast.SliceExpr, *ast.IndexExpr
	}

	if id == nil {
		return false
	}

	// If we got an identifier for the function, see if it is ignored

	if re, ok := c.ignore[""]; ok && re.MatchString(id.Name) {
		return true
	}

	if obj := c.pkg.info.ObjectOf(id); obj != nil {
		// if obj := c.pkg.identObjs[id]; obj != nil {
		if pkg := obj.Pkg(); pkg != nil {
			if re, ok := c.ignore[pkg.Path()]; ok {
				return re.MatchString(id.Name)
			}
		}
	}

	return false
}

// errorsByArg returns a slice s such that
// len(s) == number of return types of call
// s[i] == true iff return type at position i from left is an error type
func (c *checker) errorsByArg(call *ast.CallExpr) []bool {
	switch t := c.pkg.info.TypeOf(call).(type) {
	// switch t := c.pkg.callTypes[call].(type) {
	case *types.Named:
		// Single return
		return []bool{isErrorType(t.Obj())}
	case *types.Tuple:
		// Multiple returns
		s := make([]bool, t.Len())
		for i := 0; i < t.Len(); i++ {
			nt, ok := t.At(i).Type().(*types.Named)
			s[i] = ok && isErrorType(nt.Obj())
		}
		return s
	}
	return nil
}

func (c *checker) callReturnsError(call *ast.CallExpr) bool {
	for _, isError := range c.errorsByArg(call) {
		if isError {
			return true
		}
	}
	return false
}

func (c *checker) addErrorAtPosition(position token.Pos) {
	pos := c.pkg.fset.Position(position)
	line := bytes.TrimSpace(c.pkg.files[pos.Filename].lines[pos.Line-1])
	c.errors = append(c.errors, uncheckedError{pos, line})
}

func (c *checker) Visit(node ast.Node) ast.Visitor {
	switch stmt := node.(type) {
	case *ast.ExprStmt:
		if call, ok := stmt.X.(*ast.CallExpr); ok {
			if !c.ignoreCall(call) && c.callReturnsError(call) {
				c.addErrorAtPosition(call.Lparen)
			}
		}
	case *ast.AssignStmt:
		if !c.blank {
			break
		}
		if len(stmt.Rhs) == 1 {
			// single value on rhs; check against lhs identifiers
			if call, ok := stmt.Rhs[0].(*ast.CallExpr); ok {
				if c.ignoreCall(call) {
					break
				}
				isError := c.errorsByArg(call)
				for i := 0; i < len(stmt.Lhs); i++ {
					if id, ok := stmt.Lhs[i].(*ast.Ident); ok {
						if id.Name == "_" && isError[i] {
							c.addErrorAtPosition(id.NamePos)
						}
					}
				}
			}
		} else {
			// multiple value on rhs; in this case a call can't return
			// multiple values. Assume len(stmt.Lhs) == len(stmt.Rhs)
			for i := 0; i < len(stmt.Lhs); i++ {
				if id, ok := stmt.Lhs[i].(*ast.Ident); ok {
					if call, ok := stmt.Rhs[i].(*ast.CallExpr); ok {
						if c.ignoreCall(call) {
							continue
						}
						if id.Name == "_" && c.callReturnsError(call) {
							c.addErrorAtPosition(id.NamePos)
						}
					}
				}
			}
		}
	default:
	}
	return c
}

func checkPackage(pkg package_, ignore map[string]*regexp.Regexp, blank bool) error {
	tp, err := typeCheck(pkg)
	if err != nil {
		return fmt.Errorf("could not type check: %s", err)
	}

	visitor := &checker{tp, ignore, blank, []error{}}
	for _, astFile := range tp.info.Files {
		ast.Walk(visitor, astFile)
	}

	if len(visitor.errors) > 0 {
		return UncheckedErrors{visitor.errors}
	}
	return nil
}

type obj interface {
	Pkg() *types.Package
	Name() string
}

func isErrorType(v obj) bool {
	return v.Pkg() == nil && v.Name() == "error"
}
<|endoftext|>


<commit_before>package couchcache

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"image"
	"image/png"
	"sync"

	"app"
	"gopnik"
	"plugins"

	"github.com/op/go-logging"
	json "github.com/orofarne/strict-json"
)

var log = logging.MustGetLogger("global")

type u8Color [3]uint8

func (col u8Color) RGBA() (r, g, b, a uint32) {
	r = uint32(col[0])
	r |= r << 8
	g = uint32(col[1])
	g |= g << 8
	b = uint32(col[2])
	b |= b << 8
	a = 0xffff
	return
}

var colorBlack = u8Color{0, 0, 0}

type kvstoreCachePluginConf struct {
	Backend             app.PluginConfig
	UseMultilevel       bool
	UseSecondLevelCache bool
	Prefix              string
}

type KVStorePlugin struct {
	config    kvstoreCachePluginConf
	store     gopnik.KVStore
	cache2L   map[u8Color][]byte
	cache2LMu sync.RWMutex
}

func (self *KVStorePlugin) Configure(cfg json.RawMessage) error {
	err := json.Unmarshal(cfg, &self.config)
	if err != nil {
		return err
	}

	plug, err := plugins.DefaultPluginStore.Create(
		self.config.Backend.Plugin, self.config.Backend.PluginConfig)
	if err != nil {
		return err
	}
	var ok bool
	self.store, ok = plug.(gopnik.KVStore)
	if !ok {
		return fmt.Errorf("Invalid KV plugin")
	}

	if self.config.UseSecondLevelCache {
		self.cache2L = make(map[u8Color][]byte)
	}

	return nil
}

func (self *KVStorePlugin) key(coord gopnik.TileCoord, level int) string {
	if self.config.Prefix == "" {
		return fmt.Sprintf("%v:%v:%v:%v:%v",
			level, coord.Size, coord.Zoom, coord.X, coord.Y)
	} else {
		return fmt.Sprintf("%v:%v:%v:%v:%v:%v",
			self.config.Prefix, level, coord.Size, coord.Zoom, coord.X, coord.Y)
	}
}

func (self *KVStorePlugin) parseSecondLevel(metacoord, coord gopnik.TileCoord, data []byte) ([]byte, error) {
	var col u8Color
	colorSize := uint64(binary.Size(col))
	index := (coord.Y-metacoord.Y)*metacoord.Size + (coord.X - metacoord.X)
	offset := colorSize * index
	buf := bytes.NewReader(data)
	_, err := buf.Seek(int64(offset), 0)
	if err != nil {
		return nil, fmt.Errorf("Invalid second level cache: %v", err)
	}
	err = binary.Read(buf, binary.BigEndian, &col)
	if err != nil {
		return nil, fmt.Errorf("Invalid second level cache (invalid color): %v", err)
	}
	if col[0] == colorBlack[0] && col[1] == colorBlack[1] && col[2] == colorBlack[2] {
		// Empty!
		return nil, nil
	}

	// Check cache
	if self.config.UseSecondLevelCache {
		self.cache2LMu.RLock()
		img := self.cache2L[col]
		self.cache2LMu.RUnlock()
		if img != nil {
			return img, nil
		}
	}

	// Generate image
	bounds := image.Rect(0, 0, 256, 256)
	img := image.NewRGBA(bounds)
	for x := bounds.Min.X; x < bounds.Max.X; x++ {
		for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
			img.Set(x, y, col)
		}
	}
	outbuf := bytes.NewBuffer(nil)
	err = png.Encode(outbuf, img)
	if err != nil {
		return nil, err
	}
	imgData := outbuf.Bytes()

	// Save image to cache
	if self.config.UseSecondLevelCache {
		self.cache2LMu.Lock()
		self.cache2L[col] = imgData
		self.cache2LMu.Unlock()
	}

	return imgData, nil
}

func (self *KVStorePlugin) Get(coord gopnik.TileCoord) ([]byte, error) {
	// Request from kvstore
	N := 1
	if self.config.UseMultilevel {
		N = 2
	}

	for i := 1; i <= N; i++ {
		var k_coord gopnik.TileCoord
		switch i {
		case 2:
			k_coord = app.App.Metatiler().TileToMetatile(&coord)
		default:
			k_coord = coord
		}
		key := self.key(k_coord, i)
		log.Debug("Request tile by key '%v' from kvstore...", key)
		data, err := self.store.Get(key)
		if data == nil {
			//Key not found
			continue
		}
		if err != nil {
			return nil, err
		}
		log.Debug("Key '%v' [level=%v] found: %v bytes", key, i, len(data))
		if data != nil {
			switch i {
			case 2:
				return self.parseSecondLevel(k_coord, coord, data)
			default:
				return data, err
			}
		}
	}
	return nil, nil
}

func (self *KVStorePlugin) setData(coord gopnik.TileCoord, data []byte, level int) error {
	tileKey := self.key(coord, level)
	return self.store.Set(tileKey, data)
}

func (self *KVStorePlugin) setSecondLevelData(coord gopnik.TileCoord, tiles []gopnik.Tile) error {
	buf := bytes.NewBuffer(nil)
	for _, elem := range tiles {
		if elem.SingleColor != nil {
			r, g, b, _ := elem.SingleColor.RGBA()
			col := u8Color{uint8(r), uint8(g), uint8(b)}
			err := binary.Write(buf, binary.BigEndian, &col)
			if err != nil {
				return err
			}
		} else {
			err := binary.Write(buf, binary.BigEndian, colorBlack)
			if err != nil {
				return err
			}
		}
	}
	return self.setData(coord, buf.Bytes(), 2)
}

func (self *KVStorePlugin) Set(coord gopnik.TileCoord, tiles []gopnik.Tile) error {
	var err error

	c := coord
	c.Size = 1

	for c.Y < coord.Y+coord.Size {
		c.X = coord.X
		for c.X < coord.X+coord.Size {
			index := int((c.Y-coord.Y)*coord.Size + (c.X - coord.X))
			if !self.config.UseMultilevel || tiles[index].SingleColor == nil {
				if err = self.setData(c, tiles[index].Image, 1); err != nil {
					return err
				}
			}
			c.X++
		}
		c.Y++
	}

	// Save secondLevel
	for _, elem := range tiles {
		if elem.SingleColor != nil {
			err = self.setSecondLevelData(coord, tiles)
			if err != nil {
				return err
			}
			break
		}
	}

	return nil
}

type KVStorePluginFactory struct {
}

func (cpf *KVStorePluginFactory) Name() string {
	return "KVStorePlugin"
}

func (cpf *KVStorePluginFactory) New(cfg json.RawMessage) (interface{}, error) {
	var res = new(KVStorePlugin)
	err := res.Configure(cfg)
	if err != nil {
		return nil, err
	}
	return res, nil
}

func init() {
	plugins.DefaultPluginStore.AddPlugin(new(KVStorePluginFactory))
}
<commit_msg>gopnikcopy modified, deleting second level added<commit_after>package couchcache

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"image"
	"image/png"
	"sync"

	"app"
	"gopnik"
	"plugins"

	"github.com/op/go-logging"
	json "github.com/orofarne/strict-json"
)

var log = logging.MustGetLogger("global")

type u8Color [3]uint8

func (col u8Color) RGBA() (r, g, b, a uint32) {
	r = uint32(col[0])
	r |= r << 8
	g = uint32(col[1])
	g |= g << 8
	b = uint32(col[2])
	b |= b << 8
	a = 0xffff
	return
}

var colorBlack = u8Color{0, 0, 0}

type kvstoreCachePluginConf struct {
	Backend             app.PluginConfig
	UseMultilevel       bool
	UseSecondLevelCache bool
	Prefix              string
}

type KVStorePlugin struct {
	config    kvstoreCachePluginConf
	store     gopnik.KVStore
	cache2L   map[u8Color][]byte
	cache2LMu sync.RWMutex
}

func (self *KVStorePlugin) Configure(cfg json.RawMessage) error {
	err := json.Unmarshal(cfg, &self.config)
	if err != nil {
		return err
	}

	plug, err := plugins.DefaultPluginStore.Create(
		self.config.Backend.Plugin, self.config.Backend.PluginConfig)
	if err != nil {
		return err
	}
	var ok bool
	self.store, ok = plug.(gopnik.KVStore)
	if !ok {
		return fmt.Errorf("Invalid KV plugin")
	}

	if self.config.UseSecondLevelCache {
		self.cache2L = make(map[u8Color][]byte)
	}

	return nil
}

func (self *KVStorePlugin) key(coord gopnik.TileCoord, level int) string {
	if self.config.Prefix == "" {
		return fmt.Sprintf("%v:%v:%v:%v:%v",
			level, coord.Size, coord.Zoom, coord.X, coord.Y)
	} else {
		return fmt.Sprintf("%v:%v:%v:%v:%v:%v",
			self.config.Prefix, level, coord.Size, coord.Zoom, coord.X, coord.Y)
	}
}

func (self *KVStorePlugin) parseSecondLevel(metacoord, coord gopnik.TileCoord, data []byte) ([]byte, error) {
	var col u8Color
	colorSize := uint64(binary.Size(col))
	index := (coord.Y-metacoord.Y)*metacoord.Size + (coord.X - metacoord.X)
	offset := colorSize * index
	buf := bytes.NewReader(data)
	_, err := buf.Seek(int64(offset), 0)
	if err != nil {
		return nil, fmt.Errorf("Invalid second level cache: %v", err)
	}
	err = binary.Read(buf, binary.BigEndian, &col)
	if err != nil {
		return nil, fmt.Errorf("Invalid second level cache (invalid color): %v", err)
	}
	if col[0] == colorBlack[0] && col[1] == colorBlack[1] && col[2] == colorBlack[2] {
		// Empty!
		return nil, nil
	}

	// Check cache
	if self.config.UseSecondLevelCache {
		self.cache2LMu.RLock()
		img := self.cache2L[col]
		self.cache2LMu.RUnlock()
		if img != nil {
			return img, nil
		}
	}

	// Generate image
	bounds := image.Rect(0, 0, 256, 256)
	img := image.NewRGBA(bounds)
	for x := bounds.Min.X; x < bounds.Max.X; x++ {
		for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
			img.Set(x, y, col)
		}
	}
	outbuf := bytes.NewBuffer(nil)
	err = png.Encode(outbuf, img)
	if err != nil {
		return nil, err
	}
	imgData := outbuf.Bytes()

	// Save image to cache
	if self.config.UseSecondLevelCache {
		self.cache2LMu.Lock()
		self.cache2L[col] = imgData
		self.cache2LMu.Unlock()
	}

	return imgData, nil
}

func (self *KVStorePlugin) Get(coord gopnik.TileCoord) ([]byte, error) {
	// Request from kvstore
	N := 1
	if self.config.UseMultilevel {
		N = 2
	}

	for i := 1; i <= N; i++ {
		var k_coord gopnik.TileCoord
		switch i {
		case 2:
			k_coord = app.App.Metatiler().TileToMetatile(&coord)
		default:
			k_coord = coord
		}
		key := self.key(k_coord, i)
		log.Debug("Request tile by key '%v' from kvstore...", key)
		data, err := self.store.Get(key)
		if data == nil {
			//Key not found
			continue
		}
		if err != nil {
			return nil, err
		}
		log.Debug("Key '%v' [level=%v] found: %v bytes", key, i, len(data))
		if data != nil {
			switch i {
			case 2:
				return self.parseSecondLevel(k_coord, coord, data)
			default:
				return data, err
			}
		}
	}
	return nil, nil
}

func (self *KVStorePlugin) setData(coord gopnik.TileCoord, data []byte, level int) error {
	tileKey := self.key(coord, level)
	return self.store.Set(tileKey, data)
}

func (self *KVStorePlugin) setSecondLevelData(coord gopnik.TileCoord, tiles []gopnik.Tile) error {
	buf := bytes.NewBuffer(nil)
	for i, elem := range tiles {
		if elem.SingleColor != nil {
			//trying to remove old tile on first level
			coordToDelete := gopnik.TileCoord{
				X: coord.X + uint64(i)%coord.Size,
				Y: coord.Y + uint64(i)/coord.Size,
				Zoom: coord.Zoom,
				Size: 1,
				Tags: coord.Tags,
			}
			self.Delete(coordToDelete, 1)

			r, g, b, _ := elem.SingleColor.RGBA()
			col := u8Color{uint8(r), uint8(g), uint8(b)}
			err := binary.Write(buf, binary.BigEndian, &col)
			if err != nil {
				return err
			}
		} else {
			err := binary.Write(buf, binary.BigEndian, colorBlack)
			if err != nil {
				return err
			}
		}
	}
	return self.setData(coord, buf.Bytes(), 2)
}

func (self *KVStorePlugin) Set(coord gopnik.TileCoord, tiles []gopnik.Tile) error {
	var err error
	c := coord
	c.Size = 1

	for c.Y < coord.Y+coord.Size {
		c.X = coord.X
		for c.X < coord.X+coord.Size {
			index := int((c.Y-coord.Y)*coord.Size + (c.X - coord.X))
			if !self.config.UseMultilevel || tiles[index].SingleColor == nil {
				if err = self.setData(c, tiles[index].Image, 1); err != nil {
					return err
				}
			}
			c.X++
		}
		c.Y++
	}

	// Save secondLevel
	for _, elem := range tiles {
		if elem.SingleColor != nil {
			err = self.setSecondLevelData(coord, tiles)
			if err != nil {
				return err
			}
			break
		}
	}

	return nil
}

type KVStorePluginFactory struct {
}

func (self *KVStorePlugin) Delete(coord gopnik.TileCoord, level int) error {
	tileKey := self.key(coord, level)
	log.Debug("Trying to delete second level tile with key: %v ...", tileKey)
	err := self.store.Delete(tileKey)
	if err != nil {
		log.Debug("Error while deleting tile: %v", err)
		return err
	}
	log.Debug("Tile with key %v was deleted", tileKey)
	return nil
}

func (cpf *KVStorePluginFactory) Name() string {
	return "KVStorePlugin"
}

func (cpf *KVStorePluginFactory) New(cfg json.RawMessage) (interface{}, error) {
	var res = new(KVStorePlugin)
	err := res.Configure(cfg)
	if err != nil {
		return nil, err
	}
	return res, nil
}

func init() {
	plugins.DefaultPluginStore.AddPlugin(new(KVStorePluginFactory))
}
<|endoftext|>


<commit_before>package tries_contact_src

import "fmt"

type Node struct {
	Character string
	Children []*Node
	CountTraversed int
}

func (node *Node) AddEntry(word string){

	char := string(word[0])

	node.CountTraversed++

	isChild, newChild := isChild(char, node)

	if ! isChild {

		newChild.Character = char

		node.Children = append(node.Children, newChild)
	}

	if len(word) > 1 {

		newChild.AddEntry(word[1:])

	} else {

		var endChild = new(Node)

		endChild.Character = "*"

		newChild.Children = append(newChild.Children, endChild)
	}

}

func isChild(char string, node *Node) (result bool, next *Node){

	for _, child := range node.Children{

		if child.Character == char {

			return true, child
		}

	}

	var newChild = new(Node)

	return false, newChild
}

func (node *Node) FindPartial(word string) {

	char := string(word[0])

	isChild, newChild := isChild(char,node)

	if isChild {

		if len(word) > 1 {

			newChild.FindPartial(word[1:])

		} else {

			fmt.Println(newChild.CountTraversed)
		}

	}

}
<commit_msg>tries-contact fixed print<commit_after>package tries_contact_src

import "fmt"

type Node struct {
	Character string
	Children []*Node
	CountTraversed int
}

func (node *Node) AddEntry(word string){

	char := string(word[0])

	node.CountTraversed++

	isChild, newChild := isChild(char, node)

	if ! isChild {

		newChild.Character = char

		node.Children = append(node.Children, newChild)
	}

	if len(word) > 1 {

		newChild.AddEntry(word[1:])

	} else {

		var endChild = new(Node)

		endChild.Character = "*"

		newChild.Children = append(newChild.Children, endChild)
	}

}

func isChild(char string, node *Node) (result bool, next *Node){

	for _, child := range node.Children{

		if child.Character == char {

			return true, child
		}

	}

	var newChild = new(Node)

	return false, newChild
}

func (node *Node) FindPartial(word string) {

	char := string(word[0])

	isChild, newChild := isChild(char,node)

	if isChild {

		if len(word) > 1 {

			newChild.FindPartial(word[1:])

		} else {

			fmt.Println(newChild.CountTraversed)
		}

	} 	else {

		fmt.Println(0)
	}

}
<|endoftext|>


<commit_before>package backend

import (
	"fmt"
	"io"
	"path"
	"sort"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
)

type S3Backend struct {
	bucket string
	path   string
	svc    *s3.S3
}

func NewS3Backend(bucket string, s3path string, svc *s3.S3) *S3Backend {
	return &S3Backend{
		bucket: bucket,
		path:   strings.TrimPrefix(path.Clean(s3path), "/"),
		svc:    svc,
	}
}

func (s *S3Backend) ListDBs() ([]string, error) {
	return s.listDirs(s.path, "")
}

func (s *S3Backend) ListVersions(db, after string, checkForSuccess bool) ([]string, error) {
	versions, err := s.listDirs(path.Join(s.path, db), after)
	if err != nil {
		return nil, err
	}

	if checkForSuccess {
		var filtered []string
		for _, version := range versions {
			successFile := path.Join(s.path, db, version, "_SUCCESS")
			exists := s.exists(successFile)

			if exists {
				filtered = append(filtered, version)
			}
		}

		versions = filtered
	}

	return versions, nil
}

func (s *S3Backend) listDirs(dir, after string) ([]string, error) {
	// This code assumes you're using S3 like a filesystem, with directories
	// separated by /'s. It also ignores the trailing slash on a prefix (for the
	// purposes of sorting lexicographically), to be consistent with other
	// backends.
	var res []string

	for {
		params := &s3.ListObjectsInput{
			Bucket:    aws.String(s.bucket),
			Delimiter: aws.String("/"),
			Marker:    aws.String(after),
			MaxKeys:   aws.Int64(1000),
			Prefix:    aws.String(dir + "/"),
		}
		resp, err := s.svc.ListObjects(params)

		if err != nil {
			return nil, s.s3error(err)
		} else if resp.CommonPrefixes == nil {
			break
		}

		for _, p := range resp.CommonPrefixes {
			prefix := strings.TrimSuffix(*p.Prefix, "/")

			// List the prefix, to make sure it's a "directory"
			isDir := false
			params := &s3.ListObjectsInput{
				Bucket:    aws.String(s.bucket),
				Delimiter: aws.String(""),
				Marker:    aws.String(after),
				MaxKeys:   aws.Int64(3),
				Prefix:    aws.String(prefix),
			}
			resp, err := s.svc.ListObjects(params)
			if err != nil {
				return nil, err
			}

			for _, key := range resp.Contents {
				if strings.TrimSpace(path.Base(*key.Key)) != "" {
					isDir = true
					break
				}
			}

			if isDir {
				res = append(res, path.Base(prefix))
			}
		}

		if !*resp.IsTruncated || len(resp.CommonPrefixes) == 0 {
			break
		} else {
			after = resp.CommonPrefixes[len(resp.CommonPrefixes)-1].String()
		}
	}

	sort.Strings(res)
	return res, nil
}

func (s *S3Backend) ListFiles(db, version string) ([]string, error) {
	versionPrefix := path.Join(s.path, db, version)

	// We use a set here because S3 sometimes returns duplicate keys.
	res := make(map[string]bool)

	params := &s3.ListObjectsInput{
		Bucket:    aws.String(s.bucket),
		Delimiter: aws.String(""),
		MaxKeys:   aws.Int64(1000),
		Prefix:    aws.String(versionPrefix),
	}

	err := s.svc.ListObjectsPages(params, func(page *s3.ListObjectsOutput, isLastPage bool) bool {
		for _, key := range page.Contents {
			name := path.Base(*key.Key)
			// S3 sometimes has keys that are the same as the "directory"
			if strings.TrimSpace(name) != "" && !strings.HasPrefix(name, "_") && !strings.HasPrefix(name, ".") {
				res[name] = true
			}
		}

		return true
	})

	if err != nil {
		return nil, s.s3error(err)
	}

	sorted := make([]string, 0, len(res))
	for name := range res {
		sorted = append(sorted, name)
	}

	sort.Strings(sorted)
	return sorted, nil
}

func (s *S3Backend) Open(db, version, file string) (io.ReadCloser, error) {
	src := path.Join(s.path, db, version, file)
	params := &s3.GetObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(src),
	}
	resp, err := s.svc.GetObject(params)

	if err != nil {
		return nil, fmt.Errorf("error opening S3 path %s: %s", s.path, err)
	}

	return resp.Body, nil
}

func (s *S3Backend) DisplayPath(parts ...string) string {
	allParts := append([]string{s.path}, parts...)
	return s.displayURL(allParts...)
}

func (s *S3Backend) displayURL(parts ...string) string {
	key := strings.TrimPrefix(path.Join(parts...), "/")
	return fmt.Sprintf("s3://%s/%s", s.bucket, key)
}

func (s *S3Backend) exists(key string) bool {
	params := &s3.GetObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(key),
	}
	_, err := s.svc.GetObject(params)

	if err != nil {
		return false
	}

	return true
}

func (s *S3Backend) s3error(err error) error {
	return fmt.Errorf("unexpected S3 error on bucket %s: %s", s.bucket, err)
}
<commit_msg>Tell the body to close on exists call.<commit_after>package backend

import (
	"fmt"
	"io"
	"path"
	"sort"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
)

type S3Backend struct {
	bucket string
	path   string
	svc    *s3.S3
}

func NewS3Backend(bucket string, s3path string, svc *s3.S3) *S3Backend {
	return &S3Backend{
		bucket: bucket,
		path:   strings.TrimPrefix(path.Clean(s3path), "/"),
		svc:    svc,
	}
}

func (s *S3Backend) ListDBs() ([]string, error) {
	return s.listDirs(s.path, "")
}

func (s *S3Backend) ListVersions(db, after string, checkForSuccess bool) ([]string, error) {
	versions, err := s.listDirs(path.Join(s.path, db), after)
	if err != nil {
		return nil, err
	}

	if checkForSuccess {
		var filtered []string
		for _, version := range versions {
			successFile := path.Join(s.path, db, version, "_SUCCESS")
			exists := s.exists(successFile)

			if exists {
				filtered = append(filtered, version)
			}
		}

		versions = filtered
	}

	return versions, nil
}

func (s *S3Backend) listDirs(dir, after string) ([]string, error) {
	// This code assumes you're using S3 like a filesystem, with directories
	// separated by /'s. It also ignores the trailing slash on a prefix (for the
	// purposes of sorting lexicographically), to be consistent with other
	// backends.
	var res []string

	for {
		params := &s3.ListObjectsInput{
			Bucket:    aws.String(s.bucket),
			Delimiter: aws.String("/"),
			Marker:    aws.String(after),
			MaxKeys:   aws.Int64(1000),
			Prefix:    aws.String(dir + "/"),
		}
		resp, err := s.svc.ListObjects(params)

		if err != nil {
			return nil, s.s3error(err)
		} else if resp.CommonPrefixes == nil {
			break
		}

		for _, p := range resp.CommonPrefixes {
			prefix := strings.TrimSuffix(*p.Prefix, "/")

			// List the prefix, to make sure it's a "directory"
			isDir := false
			params := &s3.ListObjectsInput{
				Bucket:    aws.String(s.bucket),
				Delimiter: aws.String(""),
				Marker:    aws.String(after),
				MaxKeys:   aws.Int64(3),
				Prefix:    aws.String(prefix),
			}
			resp, err := s.svc.ListObjects(params)
			if err != nil {
				return nil, err
			}

			for _, key := range resp.Contents {
				if strings.TrimSpace(path.Base(*key.Key)) != "" {
					isDir = true
					break
				}
			}

			if isDir {
				res = append(res, path.Base(prefix))
			}
		}

		if !*resp.IsTruncated || len(resp.CommonPrefixes) == 0 {
			break
		} else {
			after = resp.CommonPrefixes[len(resp.CommonPrefixes)-1].String()
		}
	}

	sort.Strings(res)
	return res, nil
}

func (s *S3Backend) ListFiles(db, version string) ([]string, error) {
	versionPrefix := path.Join(s.path, db, version)

	// We use a set here because S3 sometimes returns duplicate keys.
	res := make(map[string]bool)

	params := &s3.ListObjectsInput{
		Bucket:    aws.String(s.bucket),
		Delimiter: aws.String(""),
		MaxKeys:   aws.Int64(1000),
		Prefix:    aws.String(versionPrefix),
	}

	err := s.svc.ListObjectsPages(params, func(page *s3.ListObjectsOutput, isLastPage bool) bool {
		for _, key := range page.Contents {
			name := path.Base(*key.Key)
			// S3 sometimes has keys that are the same as the "directory"
			if strings.TrimSpace(name) != "" && !strings.HasPrefix(name, "_") && !strings.HasPrefix(name, ".") {
				res[name] = true
			}
		}

		return true
	})

	if err != nil {
		return nil, s.s3error(err)
	}

	sorted := make([]string, 0, len(res))
	for name := range res {
		sorted = append(sorted, name)
	}

	sort.Strings(sorted)
	return sorted, nil
}

func (s *S3Backend) Open(db, version, file string) (io.ReadCloser, error) {
	src := path.Join(s.path, db, version, file)
	params := &s3.GetObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(src),
	}
	resp, err := s.svc.GetObject(params)

	if err != nil {
		return nil, fmt.Errorf("error opening S3 path %s: %s", s.path, err)
	}

	return resp.Body, nil
}

func (s *S3Backend) DisplayPath(parts ...string) string {
	allParts := append([]string{s.path}, parts...)
	return s.displayURL(allParts...)
}

func (s *S3Backend) displayURL(parts ...string) string {
	key := strings.TrimPrefix(path.Join(parts...), "/")
	return fmt.Sprintf("s3://%s/%s", s.bucket, key)
}

func (s *S3Backend) exists(key string) bool {
	params := &s3.GetObjectInput{
		Bucket: aws.String(s.bucket),
		Key:    aws.String(key),
	}
	body, err := s.svc.GetObject(params)
	if err != nil {
		return false
	}
	defer body.Body.Close()

	return true
}

func (s *S3Backend) s3error(err error) error {
	return fmt.Errorf("unexpected S3 error on bucket %s: %s", s.bucket, err)
}
<|endoftext|>


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

package ui

import (
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"

	"github.com/bogem/nehm/track"
)

// TracksMenu gets tracks from GetTracks function, show these tracks in menu
// and returns selected.
//
// TracksMenu finishes when user pushes 'd' button.
type TracksMenu struct {
	GetTracks func(offset uint) ([]track.Track, error)
	Limit     uint
	Offset    uint

	selected          []track.Track
	selectionFinished bool
}

// Show gets tracks from GetTracks function, show these tracks,
// adds selected to TracksMenu.selected and returns them.
func (tm TracksMenu) Show() []track.Track {
	Println("Getting information about tracks")
	tracks, err := tm.GetTracks(tm.Offset)
	if err != nil {
		handleError(err)
		Term("", nil)
	}
	oldOffset := tm.Offset

	if len(tracks) == 0 {
		Term("there are not tracks to show", nil)
	}

	for !tm.selectionFinished {
		if oldOffset != tm.Offset {
			oldOffset = tm.Offset
			tracks, err = tm.GetTracks(tm.Offset)
			if err != nil {
				handleError(err)
				if tm.Offset >= tm.Limit {
					Println("Downloading previous page")
					Sleep() // pause the goroutine so user can read the errors
					tm.Offset -= tm.Limit
					continue
				} else {
					Term("", nil)
				}
			}
		}

		trackItems := tm.formTrackItems(tracks)
		clearScreen()
		tm.showMenu(trackItems)
	}
	return tm.selected
}

func handleError(err error) {
	switch {
	case strings.Contains(err.Error(), "403"):
		Error("you're not allowed to see these tracks", nil)
	case strings.Contains(err.Error(), "404"):
		Error("there are no tracks", nil)
	default:
		Error("", err)
	}
}

var trackItems []MenuItem

func (tm *TracksMenu) formTrackItems(tracks []track.Track) []MenuItem {
	if trackItems == nil {
		trackItems = make([]MenuItem, 0, tm.Limit)
	}
	trackItems = trackItems[:0]

	for i, t := range tracks {
		desc := t.Fullname() + " (" + t.Duration() + ")"

		var trackItem MenuItem
		if contains(tm.selected, t) {
			trackItem = MenuItem{
				Index: GreenString("A"),
				Desc:  desc,
			}
		} else {
			t := t // https://golang.org/doc/faq#closures_and_goroutines
			trackItem = MenuItem{
				Index: strconv.Itoa(i + 1),
				Desc:  desc,
				Run:   func() { tm.selected = append(tm.selected, t) },
			}
		}
		trackItems = append(trackItems, trackItem)
	}
	return trackItems
}

func contains(s []track.Track, t track.Track) bool {
	for _, v := range s {
		if v.ID() == t.ID() {
			return true
		}
	}
	return false
}

func clearScreen() {
	var cmd *exec.Cmd
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cls")
	} else {
		cmd = exec.Command("clear")
	}
	cmd.Stdout = os.Stdout
	cmd.Run()
}

var (
	controlItems []MenuItem
	menu         Menu
)

func (tm *TracksMenu) showMenu(trackItems []MenuItem) {
	if controlItems == nil {
		controlItems = tm.controlItems()
	}
	menu.Clear()
	menu.AddItems(trackItems...)
	menu.AddNewline()
	menu.AddItems(controlItems...)
	menu.Show()
}

func (tm *TracksMenu) controlItems() []MenuItem {
	return []MenuItem{
		MenuItem{
			Index: "d",
			Desc:  GreenString("Download tracks"),
			Run:   func() { tm.selectionFinished = true },
		},

		MenuItem{
			Index: "n",
			Desc:  "Next page",
			Run:   func() { tm.Offset += tm.Limit },
		},

		MenuItem{
			Index: "p",
			Desc:  "Prev page",
			Run: func() {
				if tm.Offset >= tm.Limit {
					tm.Offset -= tm.Limit
				} else {
					tm.Offset = 0
				}
			},
		},
	}
}
<commit_msg>Fix typo in track menu<commit_after>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package ui

import (
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"

	"github.com/bogem/nehm/track"
)

// TracksMenu gets tracks from GetTracks function, show these tracks in menu
// and returns selected.
//
// TracksMenu finishes when user pushes 'd' button.
type TracksMenu struct {
	GetTracks func(offset uint) ([]track.Track, error)
	Limit     uint
	Offset    uint

	selected          []track.Track
	selectionFinished bool
}

// Show gets tracks from GetTracks function, show these tracks,
// adds selected to TracksMenu.selected and returns them.
func (tm TracksMenu) Show() []track.Track {
	Println("Getting information about tracks")
	tracks, err := tm.GetTracks(tm.Offset)
	if err != nil {
		handleError(err)
		Term("", nil)
	}
	oldOffset := tm.Offset

	if len(tracks) == 0 {
		Term("there are no tracks to show", nil)
	}

	for !tm.selectionFinished {
		if oldOffset != tm.Offset {
			oldOffset = tm.Offset
			tracks, err = tm.GetTracks(tm.Offset)
			if err != nil {
				handleError(err)
				if tm.Offset >= tm.Limit {
					Println("Downloading previous page")
					Sleep() // pause the goroutine so user can read the errors
					tm.Offset -= tm.Limit
					continue
				} else {
					Term("", nil)
				}
			}
		}

		trackItems := tm.formTrackItems(tracks)
		clearScreen()
		tm.showMenu(trackItems)
	}
	return tm.selected
}

func handleError(err error) {
	switch {
	case strings.Contains(err.Error(), "403"):
		Error("you're not allowed to see these tracks", nil)
	case strings.Contains(err.Error(), "404"):
		Error("there are no tracks", nil)
	default:
		Error("", err)
	}
}

var trackItems []MenuItem

func (tm *TracksMenu) formTrackItems(tracks []track.Track) []MenuItem {
	if trackItems == nil {
		trackItems = make([]MenuItem, 0, tm.Limit)
	}
	trackItems = trackItems[:0]

	for i, t := range tracks {
		desc := t.Fullname() + " (" + t.Duration() + ")"

		var trackItem MenuItem
		if contains(tm.selected, t) {
			trackItem = MenuItem{
				Index: GreenString("A"),
				Desc:  desc,
			}
		} else {
			t := t // https://golang.org/doc/faq#closures_and_goroutines
			trackItem = MenuItem{
				Index: strconv.Itoa(i + 1),
				Desc:  desc,
				Run:   func() { tm.selected = append(tm.selected, t) },
			}
		}
		trackItems = append(trackItems, trackItem)
	}
	return trackItems
}

func contains(s []track.Track, t track.Track) bool {
	for _, v := range s {
		if v.ID() == t.ID() {
			return true
		}
	}
	return false
}

func clearScreen() {
	var cmd *exec.Cmd
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cls")
	} else {
		cmd = exec.Command("clear")
	}
	cmd.Stdout = os.Stdout
	cmd.Run()
}

var (
	controlItems []MenuItem
	menu         Menu
)

func (tm *TracksMenu) showMenu(trackItems []MenuItem) {
	if controlItems == nil {
		controlItems = tm.controlItems()
	}
	menu.Clear()
	menu.AddItems(trackItems...)
	menu.AddNewline()
	menu.AddItems(controlItems...)
	menu.Show()
}

func (tm *TracksMenu) controlItems() []MenuItem {
	return []MenuItem{
		MenuItem{
			Index: "d",
			Desc:  GreenString("Download tracks"),
			Run:   func() { tm.selectionFinished = true },
		},

		MenuItem{
			Index: "n",
			Desc:  "Next page",
			Run:   func() { tm.Offset += tm.Limit },
		},

		MenuItem{
			Index: "p",
			Desc:  "Prev page",
			Run: func() {
				if tm.Offset >= tm.Limit {
					tm.Offset -= tm.Limit
				} else {
					tm.Offset = 0
				}
			},
		},
	}
}
<|endoftext|>


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

package updater

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/keybase/go-updater/util"
)

// Version is the updater version
const Version = "0.3.1"

// Updater knows how to find and apply updates
type Updater struct {
	source       UpdateSource
	config       Config
	log          Log
	guiBusyCount int
}

// UpdateSource defines where the updater can find updates
type UpdateSource interface {
	// Description is a short description about the update source
	Description() string
	// FindUpdate finds an update given options
	FindUpdate(options UpdateOptions) (*Update, error)
}

// Context defines options, UI and hooks for the updater.
// This is where you can define custom behavior specific to your apps.
type Context interface {
	GetUpdateUI() UpdateUI
	UpdateOptions() UpdateOptions
	Verify(update Update) error
	BeforeUpdatePrompt(update Update, options UpdateOptions) error
	BeforeApply(update Update) error
	Apply(update Update, options UpdateOptions, tmpDir string) error
	AfterApply(update Update) error
	ReportError(err error, update *Update, options UpdateOptions)
	ReportAction(action UpdateAction, update *Update, options UpdateOptions)
	ReportSuccess(update *Update, options UpdateOptions)
	AfterUpdateCheck(update *Update)
	GetAppStatePath() string
	IsCheckCommand() bool
	DeepClean()
}

// Config defines configuration for the Updater
type Config interface {
	GetUpdateAuto() (bool, bool)
	SetUpdateAuto(b bool) error
	GetUpdateAutoOverride() bool
	SetUpdateAutoOverride(bool) error
	GetInstallID() string
	SetInstallID(installID string) error
	IsLastUpdateCheckTimeRecent(d time.Duration) bool
	SetLastUpdateCheckTime()
	SetLastAppliedVersion(string) error
	GetLastAppliedVersion() string
}

// Log is the logging interface for this package
type Log interface {
	Debug(...interface{})
	Info(...interface{})
	Debugf(s string, args ...interface{})
	Infof(s string, args ...interface{})
	Warningf(s string, args ...interface{})
	Errorf(s string, args ...interface{})
}

// NewUpdater constructs an Updater
func NewUpdater(source UpdateSource, config Config, log Log) *Updater {
	return &Updater{
		source: source,
		config: config,
		log:    log,
	}
}

// Update checks, downloads and performs an update
func (u *Updater) Update(ctx Context) (*Update, error) {
	options := ctx.UpdateOptions()
	update, err := u.update(ctx, options)
	report(ctx, err, update, options)
	return update, err
}

// update returns the update received, and an error if the update was not
// performed. The error with be of type Error. The error may be due to the user
// (or system) canceling an update, in which case error.IsCancel() will be true.
func (u *Updater) update(ctx Context, options UpdateOptions) (*Update, error) {
	update, err := u.checkForUpdate(ctx, options)
	if err != nil {
		return nil, findErr(err)
	}
	if update == nil || !update.NeedUpdate {
		// No update available
		return nil, nil
	}
	u.log.Infof("Got update with version: %s", update.Version)

	err = ctx.BeforeUpdatePrompt(*update, options)
	if err != nil {
		return update, err
	}

	// Prompt for update
	updateAction, err := u.promptForUpdateAction(ctx, *update, options)
	if err != nil {
		return update, promptErr(err)
	}
	switch updateAction {
	case UpdateActionApply:
		ctx.ReportAction(UpdateActionApply, update, options)
	case UpdateActionAuto:
		ctx.ReportAction(UpdateActionAuto, update, options)
	case UpdateActionSnooze:
		ctx.ReportAction(UpdateActionSnooze, update, options)
		return update, CancelErr(fmt.Errorf("Snoozed update"))
	case UpdateActionCancel:
		ctx.ReportAction(UpdateActionCancel, update, options)
		return update, CancelErr(fmt.Errorf("Canceled"))
	case UpdateActionError:
		return update, promptErr(fmt.Errorf("Unknown prompt error"))
	case UpdateActionContinue:
		// Continue
	case UpdateActionUIBusy:
		// Return nil so that AfterUpdateCheck won't exit the service
		return nil, guiBusyErr(fmt.Errorf("User active, retrying later"))
	}

	// Linux updates don't have assets so it's ok to prompt for update above before
	// we check for nil asset.
	if update.Asset == nil || update.Asset.URL == "" {
		u.log.Info("No update asset to apply")
		return update, nil
	}

	tmpDir := u.tempDir()
	defer u.Cleanup(tmpDir)
	if err := u.downloadAsset(update.Asset, tmpDir, options); err != nil {
		return update, downloadErr(err)
	}

	u.log.Infof("Verify asset: %s", update.Asset.LocalPath)
	if err := ctx.Verify(*update); err != nil {
		return update, verifyErr(err)
	}

	if err := u.apply(ctx, *update, options, tmpDir); err != nil {
		return update, err
	}

	return update, nil
}

func (u *Updater) apply(ctx Context, update Update, options UpdateOptions, tmpDir string) error {
	u.log.Info("Before apply")
	if err := ctx.BeforeApply(update); err != nil {
		return applyErr(err)
	}

	u.log.Info("Applying update")
	if err := ctx.Apply(update, options, tmpDir); err != nil {
		u.log.Info("Apply error: %v", err)
		return applyErr(err)
	}

	u.log.Info("After apply")
	if err := ctx.AfterApply(update); err != nil {
		return applyErr(err)
	}

	return nil
}

// downloadAsset will download the update to a temporary path (if not cached),
// check the digest, and set the LocalPath property on the asset.
func (u *Updater) downloadAsset(asset *Asset, tmpDir string, options UpdateOptions) error {
	if asset == nil {
		return fmt.Errorf("No asset to download")
	}
	downloadOptions := util.DownloadURLOptions{
		Digest:        asset.Digest,
		RequireDigest: true,
		UseETag:       true,
		Log:           u.log,
	}

	downloadPath := filepath.Join(tmpDir, asset.Name)
	// If asset had a file extension, lets add it back on
	if err := util.DownloadURL(asset.URL, downloadPath, downloadOptions); err != nil {
		return err
	}

	asset.LocalPath = downloadPath
	return nil
}

// checkForUpdate checks a update source (like a remote API) for an update.
// It may set an InstallID, if the server tells us to.
func (u *Updater) checkForUpdate(ctx Context, options UpdateOptions) (*Update, error) {
	u.log.Infof("Checking for update, current version is %s", options.Version)
	u.log.Infof("Using updater source: %s", u.source.Description())
	u.log.Debugf("Using options: %#v", options)

	update, findErr := u.source.FindUpdate(options)
	if findErr != nil {
		return nil, findErr
	}
	if update == nil {
		return nil, nil
	}

	// Save InstallID if we received one
	if update.InstallID != "" && u.config.GetInstallID() != update.InstallID {
		u.log.Debugf("Saving install ID: %s", update.InstallID)
		if err := u.config.SetInstallID(update.InstallID); err != nil {
			u.log.Warningf("Error saving install ID: %s", err)
			ctx.ReportError(configErr(fmt.Errorf("Error saving install ID: %s", err)), update, options)
		}
	}

	return update, nil
}

// promptForUpdateAction prompts the user for permission to apply an update
func (u *Updater) promptForUpdateAction(ctx Context, update Update, options UpdateOptions) (UpdateAction, error) {
	u.log.Debug("Prompt for update")

	auto, autoSet := u.config.GetUpdateAuto()
	autoOverride := u.config.GetUpdateAutoOverride()
	u.log.Debugf("Auto update: %s (set=%s autoOverride=%s)", strconv.FormatBool(auto), strconv.FormatBool(autoSet), strconv.FormatBool(autoOverride))
	if auto && !autoOverride {
		if !ctx.IsCheckCommand() {
			// If there's an error getting active status, we'll just update
			isActive, err := u.checkUserActive(ctx)
			if err == nil && isActive {
				return UpdateActionUIBusy, nil
			}
			u.guiBusyCount = 0
		}
		return UpdateActionAuto, nil
	}

	updateUI := ctx.GetUpdateUI()

	// If auto update never set, default to true
	autoUpdate := auto || !autoSet
	promptOptions := UpdatePromptOptions{AutoUpdate: autoUpdate}
	updatePromptResponse, err := updateUI.UpdatePrompt(update, options, promptOptions)
	if err != nil {
		return UpdateActionError, err
	}
	if updatePromptResponse == nil {
		return UpdateActionError, fmt.Errorf("No response")
	}

	if updatePromptResponse.Action != UpdateActionContinue {
		u.log.Debugf("Update prompt response: %#v", updatePromptResponse)
		if err := u.config.SetUpdateAuto(updatePromptResponse.AutoUpdate); err != nil {
			u.log.Warningf("Error setting auto preference: %s", err)
			ctx.ReportError(configErr(fmt.Errorf("Error setting auto preference: %s", err)), &update, options)
		}
	}

	return updatePromptResponse.Action, nil
}

type guiAppState struct {
	IsUserActive bool `json:"isUserActive"`
}

func (u *Updater) checkUserActive(ctx Context) (bool, error) {

	if u.guiBusyCount >= 3 {
		u.log.Warningf("Waited for GUI %d times - ignoring busy", u.guiBusyCount)
		return false, nil
	}

	// Read app-state.json, written by the GUI
	rawState, err := util.ReadFile(ctx.GetAppStatePath())
	if err != nil {
		u.log.Warningf("Error reading GUI state - proceeding", err)
		return false, err
	}

	guistate := guiAppState{}
	err = json.Unmarshal(rawState, &guistate)
	if err != nil {
		u.log.Warningf("Error parsing GUI state - proceeding", err)
		return false, err
	}
	if guistate.IsUserActive {
		u.guiBusyCount++
		u.log.Infof("GUI busy on attempt %d", u.guiBusyCount)
	}

	return guistate.IsUserActive, nil
}

func report(ctx Context, err error, update *Update, options UpdateOptions) {
	if err != nil {
		// Don't report cancels or GUI busy
		switch e := err.(type) {
		case Error:
			if e.IsCancel() || e.IsGUIBusy() {
				return
			}
		}
		ctx.ReportError(err, update, options)
	} else if update != nil {
		ctx.ReportSuccess(update, options)
	}
}

// tempDir, if specified, will contain files that were replaced during an update
// and will be removed after an update. The temp dir should already exist.
func (u *Updater) tempDir() string {
	tmpDir := util.TempPath("", "KeybaseUpdater.")
	if err := util.MakeDirs(tmpDir, 0700, u.log); err != nil {
		u.log.Warningf("Error trying to create temp dir: %s", err)
		return ""
	}
	return tmpDir
}

// Cleanup removes temporary files
func (u *Updater) Cleanup(tmpDir string) {
	if tmpDir != "" {
		u.log.Debugf("Remove temporary directory: %q", tmpDir)
		if err := os.RemoveAll(tmpDir); err != nil {
			u.log.Warningf("Error removing temporary directory %q: %s", tmpDir, err)
		}
	}
}
<commit_msg>Revert "Fix a bug that kept updates from happening as long as GUI was busy" (#169)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package updater

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/keybase/go-updater/util"
)

// Version is the updater version
const Version = "0.3.2"

// Updater knows how to find and apply updates
type Updater struct {
	source       UpdateSource
	config       Config
	log          Log
	guiBusyCount int
}

// UpdateSource defines where the updater can find updates
type UpdateSource interface {
	// Description is a short description about the update source
	Description() string
	// FindUpdate finds an update given options
	FindUpdate(options UpdateOptions) (*Update, error)
}

// Context defines options, UI and hooks for the updater.
// This is where you can define custom behavior specific to your apps.
type Context interface {
	GetUpdateUI() UpdateUI
	UpdateOptions() UpdateOptions
	Verify(update Update) error
	BeforeUpdatePrompt(update Update, options UpdateOptions) error
	BeforeApply(update Update) error
	Apply(update Update, options UpdateOptions, tmpDir string) error
	AfterApply(update Update) error
	ReportError(err error, update *Update, options UpdateOptions)
	ReportAction(action UpdateAction, update *Update, options UpdateOptions)
	ReportSuccess(update *Update, options UpdateOptions)
	AfterUpdateCheck(update *Update)
	GetAppStatePath() string
	IsCheckCommand() bool
	DeepClean()
}

// Config defines configuration for the Updater
type Config interface {
	GetUpdateAuto() (bool, bool)
	SetUpdateAuto(b bool) error
	GetUpdateAutoOverride() bool
	SetUpdateAutoOverride(bool) error
	GetInstallID() string
	SetInstallID(installID string) error
	IsLastUpdateCheckTimeRecent(d time.Duration) bool
	SetLastUpdateCheckTime()
	SetLastAppliedVersion(string) error
	GetLastAppliedVersion() string
}

// Log is the logging interface for this package
type Log interface {
	Debug(...interface{})
	Info(...interface{})
	Debugf(s string, args ...interface{})
	Infof(s string, args ...interface{})
	Warningf(s string, args ...interface{})
	Errorf(s string, args ...interface{})
}

// NewUpdater constructs an Updater
func NewUpdater(source UpdateSource, config Config, log Log) *Updater {
	return &Updater{
		source: source,
		config: config,
		log:    log,
	}
}

// Update checks, downloads and performs an update
func (u *Updater) Update(ctx Context) (*Update, error) {
	options := ctx.UpdateOptions()
	update, err := u.update(ctx, options)
	report(ctx, err, update, options)
	return update, err
}

// update returns the update received, and an error if the update was not
// performed. The error with be of type Error. The error may be due to the user
// (or system) canceling an update, in which case error.IsCancel() will be true.
func (u *Updater) update(ctx Context, options UpdateOptions) (*Update, error) {
	update, err := u.checkForUpdate(ctx, options)
	if err != nil {
		return nil, findErr(err)
	}
	if update == nil || !update.NeedUpdate {
		// No update available
		return nil, nil
	}
	u.log.Infof("Got update with version: %s", update.Version)

	err = ctx.BeforeUpdatePrompt(*update, options)
	if err != nil {
		return update, err
	}

	// Prompt for update
	updateAction, err := u.promptForUpdateAction(ctx, *update, options)
	if err != nil {
		return update, promptErr(err)
	}
	switch updateAction {
	case UpdateActionApply:
		ctx.ReportAction(UpdateActionApply, update, options)
	case UpdateActionAuto:
		ctx.ReportAction(UpdateActionAuto, update, options)
	case UpdateActionSnooze:
		ctx.ReportAction(UpdateActionSnooze, update, options)
		return update, CancelErr(fmt.Errorf("Snoozed update"))
	case UpdateActionCancel:
		ctx.ReportAction(UpdateActionCancel, update, options)
		return update, CancelErr(fmt.Errorf("Canceled"))
	case UpdateActionError:
		return update, promptErr(fmt.Errorf("Unknown prompt error"))
	case UpdateActionContinue:
		// Continue
	case UpdateActionUIBusy:
		return update, guiBusyErr(fmt.Errorf("User active, retrying later"))
	}

	// Linux updates don't have assets so it's ok to prompt for update above before
	// we check for nil asset.
	if update.Asset == nil || update.Asset.URL == "" {
		u.log.Info("No update asset to apply")
		return update, nil
	}

	tmpDir := u.tempDir()
	defer u.Cleanup(tmpDir)
	if err := u.downloadAsset(update.Asset, tmpDir, options); err != nil {
		return update, downloadErr(err)
	}

	u.log.Infof("Verify asset: %s", update.Asset.LocalPath)
	if err := ctx.Verify(*update); err != nil {
		return update, verifyErr(err)
	}

	if err := u.apply(ctx, *update, options, tmpDir); err != nil {
		return update, err
	}

	return update, nil
}

func (u *Updater) apply(ctx Context, update Update, options UpdateOptions, tmpDir string) error {
	u.log.Info("Before apply")
	if err := ctx.BeforeApply(update); err != nil {
		return applyErr(err)
	}

	u.log.Info("Applying update")
	if err := ctx.Apply(update, options, tmpDir); err != nil {
		u.log.Info("Apply error: %v", err)
		return applyErr(err)
	}

	u.log.Info("After apply")
	if err := ctx.AfterApply(update); err != nil {
		return applyErr(err)
	}

	return nil
}

// downloadAsset will download the update to a temporary path (if not cached),
// check the digest, and set the LocalPath property on the asset.
func (u *Updater) downloadAsset(asset *Asset, tmpDir string, options UpdateOptions) error {
	if asset == nil {
		return fmt.Errorf("No asset to download")
	}
	downloadOptions := util.DownloadURLOptions{
		Digest:        asset.Digest,
		RequireDigest: true,
		UseETag:       true,
		Log:           u.log,
	}

	downloadPath := filepath.Join(tmpDir, asset.Name)
	// If asset had a file extension, lets add it back on
	if err := util.DownloadURL(asset.URL, downloadPath, downloadOptions); err != nil {
		return err
	}

	asset.LocalPath = downloadPath
	return nil
}

// checkForUpdate checks a update source (like a remote API) for an update.
// It may set an InstallID, if the server tells us to.
func (u *Updater) checkForUpdate(ctx Context, options UpdateOptions) (*Update, error) {
	u.log.Infof("Checking for update, current version is %s", options.Version)
	u.log.Infof("Using updater source: %s", u.source.Description())
	u.log.Debugf("Using options: %#v", options)

	update, findErr := u.source.FindUpdate(options)
	if findErr != nil {
		return nil, findErr
	}
	if update == nil {
		return nil, nil
	}

	// Save InstallID if we received one
	if update.InstallID != "" && u.config.GetInstallID() != update.InstallID {
		u.log.Debugf("Saving install ID: %s", update.InstallID)
		if err := u.config.SetInstallID(update.InstallID); err != nil {
			u.log.Warningf("Error saving install ID: %s", err)
			ctx.ReportError(configErr(fmt.Errorf("Error saving install ID: %s", err)), update, options)
		}
	}

	return update, nil
}

// promptForUpdateAction prompts the user for permission to apply an update
func (u *Updater) promptForUpdateAction(ctx Context, update Update, options UpdateOptions) (UpdateAction, error) {
	u.log.Debug("Prompt for update")

	auto, autoSet := u.config.GetUpdateAuto()
	autoOverride := u.config.GetUpdateAutoOverride()
	u.log.Debugf("Auto update: %s (set=%s autoOverride=%s)", strconv.FormatBool(auto), strconv.FormatBool(autoSet), strconv.FormatBool(autoOverride))
	if auto && !autoOverride {
		if !ctx.IsCheckCommand() {
			// If there's an error getting active status, we'll just update
			isActive, err := u.checkUserActive(ctx)
			if err == nil && isActive {
				return UpdateActionUIBusy, nil
			}
			u.guiBusyCount = 0
		}
		return UpdateActionAuto, nil
	}

	updateUI := ctx.GetUpdateUI()

	// If auto update never set, default to true
	autoUpdate := auto || !autoSet
	promptOptions := UpdatePromptOptions{AutoUpdate: autoUpdate}
	updatePromptResponse, err := updateUI.UpdatePrompt(update, options, promptOptions)
	if err != nil {
		return UpdateActionError, err
	}
	if updatePromptResponse == nil {
		return UpdateActionError, fmt.Errorf("No response")
	}

	if updatePromptResponse.Action != UpdateActionContinue {
		u.log.Debugf("Update prompt response: %#v", updatePromptResponse)
		if err := u.config.SetUpdateAuto(updatePromptResponse.AutoUpdate); err != nil {
			u.log.Warningf("Error setting auto preference: %s", err)
			ctx.ReportError(configErr(fmt.Errorf("Error setting auto preference: %s", err)), &update, options)
		}
	}

	return updatePromptResponse.Action, nil
}

type guiAppState struct {
	IsUserActive bool `json:"isUserActive"`
}

func (u *Updater) checkUserActive(ctx Context) (bool, error) {

	if u.guiBusyCount >= 3 {
		u.log.Warningf("Waited for GUI %d times - ignoring busy", u.guiBusyCount)
		return false, nil
	}

	// Read app-state.json, written by the GUI
	rawState, err := util.ReadFile(ctx.GetAppStatePath())
	if err != nil {
		u.log.Warningf("Error reading GUI state - proceeding", err)
		return false, err
	}

	guistate := guiAppState{}
	err = json.Unmarshal(rawState, &guistate)
	if err != nil {
		u.log.Warningf("Error parsing GUI state - proceeding", err)
		return false, err
	}
	if guistate.IsUserActive {
		u.guiBusyCount++
	}

	return guistate.IsUserActive, nil
}

func report(ctx Context, err error, update *Update, options UpdateOptions) {
	if err != nil {
		// Don't report cancels or GUI busy
		switch e := err.(type) {
		case Error:
			if e.IsCancel() || e.IsGUIBusy() {
				return
			}
		}
		ctx.ReportError(err, update, options)
	} else if update != nil {
		ctx.ReportSuccess(update, options)
	}
}

// tempDir, if specified, will contain files that were replaced during an update
// and will be removed after an update. The temp dir should already exist.
func (u *Updater) tempDir() string {
	tmpDir := util.TempPath("", "KeybaseUpdater.")
	if err := util.MakeDirs(tmpDir, 0700, u.log); err != nil {
		u.log.Warningf("Error trying to create temp dir: %s", err)
		return ""
	}
	return tmpDir
}

// Cleanup removes temporary files
func (u *Updater) Cleanup(tmpDir string) {
	if tmpDir != "" {
		u.log.Debugf("Remove temporary directory: %q", tmpDir)
		if err := os.RemoveAll(tmpDir); err != nil {
			u.log.Warningf("Error removing temporary directory %q: %s", tmpDir, err)
		}
	}
}
<|endoftext|>


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

// Package usb provides a wrapper around libusb-1.0.
//
// Note that this package was deprecated in favor of github.com/google/gousb.
// Please use the new package when starting new projects.
package usb

type Context struct {
	ctx  *libusbContext
	done chan struct{}
}

func (c *Context) Debug(level int) {
	libusb.setDebug(c.ctx, level)
}

func NewContext() *Context {
	c, err := libusb.init()
	if err != nil {
		panic(err)
	}
	ctx := &Context{
		ctx:  c,
		done: make(chan struct{}),
	}
	go libusb.handleEvents(ctx.ctx, ctx.done)
	return ctx
}

// ListDevices calls each with each enumerated device.
// If the function returns true, the device is opened and a Device is returned if the operation succeeds.
// Every Device returned (whether an error is also returned or not) must be closed.
// If there are any errors enumerating the devices,
// the final one is returned along with any successfully opened devices.
func (c *Context) ListDevices(each func(desc *Descriptor) bool) ([]*Device, error) {
	list, err := libusb.getDevices(c.ctx)
	if err != nil {
		return nil, err
	}

	var reterr error
	var ret []*Device
	for _, dev := range list {
		desc, err := libusb.getDeviceDesc(dev)
		if err != nil {
			libusb.dereference(dev)
			reterr = err
			continue
		}

		if each(desc) {
			handle, err := libusb.open(dev)
			if err != nil {
				reterr = err
				continue
			}
			ret = append(ret, newDevice(handle, desc))
		} else {
			libusb.dereference(dev)
		}
	}
	return ret, reterr
}

// OpenDeviceWithVidPid opens Device from specific VendorId and ProductId.
// If none is found, it returns nil and nil error. If there are multiple devices
// with the same VID/PID, it will return one of them, picked arbitrarily.
// If there were any errors during device list traversal, it is possible
// it will return a non-nil device and non-nil error. A Device.Close() must
// be called to release the device if the returned device wasn't nil.
func (c *Context) OpenDeviceWithVidPid(vid, pid int) (*Device, error) {
	var found bool
	devs, err := c.ListDevices(func(desc *Descriptor) bool {
		if found {
			return false
		}
		if desc.Vendor == ID(vid) && desc.Product == ID(pid) {
			found = true
			return true
		}
		return false
	})
	if len(devs) == 0 {
		return nil, err
	}
	return devs[0], nil
}

func (c *Context) Close() error {
	c.done <- struct{}{}
	if c.ctx != nil {
		libusb.exit(c.ctx)
	}
	c.ctx = nil
	return nil
}
<commit_msg>Add a properly formatted deprecation comment, triggering lint warnings.<commit_after>// Copyright 2013 Google Inc.  All rights reserved.
// Copyright 2016 the gousb Authors.  All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package usb provides a wrapper around libusb-1.0.
//
// Deprecated: this package was deprecated in favor of github.com/google/gousb.
// Please use the new package when starting new projects.
package usb

type Context struct {
	ctx  *libusbContext
	done chan struct{}
}

func (c *Context) Debug(level int) {
	libusb.setDebug(c.ctx, level)
}

func NewContext() *Context {
	c, err := libusb.init()
	if err != nil {
		panic(err)
	}
	ctx := &Context{
		ctx:  c,
		done: make(chan struct{}),
	}
	go libusb.handleEvents(ctx.ctx, ctx.done)
	return ctx
}

// ListDevices calls each with each enumerated device.
// If the function returns true, the device is opened and a Device is returned if the operation succeeds.
// Every Device returned (whether an error is also returned or not) must be closed.
// If there are any errors enumerating the devices,
// the final one is returned along with any successfully opened devices.
func (c *Context) ListDevices(each func(desc *Descriptor) bool) ([]*Device, error) {
	list, err := libusb.getDevices(c.ctx)
	if err != nil {
		return nil, err
	}

	var reterr error
	var ret []*Device
	for _, dev := range list {
		desc, err := libusb.getDeviceDesc(dev)
		if err != nil {
			libusb.dereference(dev)
			reterr = err
			continue
		}

		if each(desc) {
			handle, err := libusb.open(dev)
			if err != nil {
				reterr = err
				continue
			}
			ret = append(ret, newDevice(handle, desc))
		} else {
			libusb.dereference(dev)
		}
	}
	return ret, reterr
}

// OpenDeviceWithVidPid opens Device from specific VendorId and ProductId.
// If none is found, it returns nil and nil error. If there are multiple devices
// with the same VID/PID, it will return one of them, picked arbitrarily.
// If there were any errors during device list traversal, it is possible
// it will return a non-nil device and non-nil error. A Device.Close() must
// be called to release the device if the returned device wasn't nil.
func (c *Context) OpenDeviceWithVidPid(vid, pid int) (*Device, error) {
	var found bool
	devs, err := c.ListDevices(func(desc *Descriptor) bool {
		if found {
			return false
		}
		if desc.Vendor == ID(vid) && desc.Product == ID(pid) {
			found = true
			return true
		}
		return false
	})
	if len(devs) == 0 {
		return nil, err
	}
	return devs[0], nil
}

func (c *Context) Close() error {
	c.done <- struct{}{}
	if c.ctx != nil {
		libusb.exit(c.ctx)
	}
	c.ctx = nil
	return nil
}
<|endoftext|>


<commit_before>package netlink

import "fmt"

type Filter interface {
	Attrs() *FilterAttrs
	Type() string
}

// FilterAttrs represents a netlink filter. A filter is associated with a link,
// has a handle and a parent. The root filter of a device should have a
// parent == HANDLE_ROOT.
type FilterAttrs struct {
	LinkIndex int
	Handle    uint32
	Parent    uint32
	Priority  uint16 // lower is higher priority
	Protocol  uint16 // syscall.ETH_P_*
}

func (q FilterAttrs) String() string {
	return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Priority: %d, Protocol: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Priority, q.Protocol)
}

type TcAct int32

const (
	TC_ACT_UNSPEC     TcAct = -1
	TC_ACT_OK         TcAct = 0
	TC_ACT_RECLASSIFY TcAct = 1
	TC_ACT_SHOT       TcAct = 2
	TC_ACT_PIPE       TcAct = 3
	TC_ACT_STOLEN     TcAct = 4
	TC_ACT_QUEUED     TcAct = 5
	TC_ACT_REPEAT     TcAct = 6
	TC_ACT_REDIRECT   TcAct = 7
	TC_ACT_JUMP       TcAct = 0x10000000
)

func (a TcAct) String() string {
	switch a {
	case TC_ACT_UNSPEC:
		return "unspec"
	case TC_ACT_OK:
		return "ok"
	case TC_ACT_RECLASSIFY:
		return "reclassify"
	case TC_ACT_SHOT:
		return "shot"
	case TC_ACT_PIPE:
		return "pipe"
	case TC_ACT_STOLEN:
		return "stolen"
	case TC_ACT_QUEUED:
		return "queued"
	case TC_ACT_REPEAT:
		return "repeat"
	case TC_ACT_REDIRECT:
		return "redirect"
	case TC_ACT_JUMP:
		return "jump"
	}
	return fmt.Sprintf("0x%x", a)
}

type TcPolAct int32

const (
	TC_POLICE_UNSPEC     TcPolAct = TcPolAct(TC_ACT_UNSPEC)
	TC_POLICE_OK         TcPolAct = TcPolAct(TC_ACT_OK)
	TC_POLICE_RECLASSIFY TcPolAct = TcPolAct(TC_ACT_RECLASSIFY)
	TC_POLICE_SHOT       TcPolAct = TcPolAct(TC_ACT_SHOT)
	TC_POLICE_PIPE       TcPolAct = TcPolAct(TC_ACT_PIPE)
)

func (a TcPolAct) String() string {
	switch a {
	case TC_POLICE_UNSPEC:
		return "unspec"
	case TC_POLICE_OK:
		return "ok"
	case TC_POLICE_RECLASSIFY:
		return "reclassify"
	case TC_POLICE_SHOT:
		return "shot"
	case TC_POLICE_PIPE:
		return "pipe"
	}
	return fmt.Sprintf("0x%x", a)
}

type ActionAttrs struct {
	Index   int
	Capab   int
	Action  TcAct
	Refcnt  int
	Bindcnt int
}

func (q ActionAttrs) String() string {
	return fmt.Sprintf("{Index: %d, Capab: %x, Action: %s, Refcnt: %d, Bindcnt: %d}", q.Index, q.Capab, q.Action.String(), q.Refcnt, q.Bindcnt)
}

// Action represents an action in any supported filter.
type Action interface {
	Attrs() *ActionAttrs
	Type() string
}

type GenericAction struct {
	ActionAttrs
}

func (action *GenericAction) Type() string {
	return "generic"
}

func (action *GenericAction) Attrs() *ActionAttrs {
	return &action.ActionAttrs
}

type BpfAction struct {
	ActionAttrs
	Fd   int
	Name string
}

func (action *BpfAction) Type() string {
	return "bpf"
}

func (action *BpfAction) Attrs() *ActionAttrs {
	return &action.ActionAttrs
}

type MirredAct uint8

func (a MirredAct) String() string {
	switch a {
	case TCA_EGRESS_REDIR:
		return "egress redir"
	case TCA_EGRESS_MIRROR:
		return "egress mirror"
	case TCA_INGRESS_REDIR:
		return "ingress redir"
	case TCA_INGRESS_MIRROR:
		return "ingress mirror"
	}
	return "unknown"
}

const (
	TCA_EGRESS_REDIR   MirredAct = 1 /* packet redirect to EGRESS*/
	TCA_EGRESS_MIRROR  MirredAct = 2 /* mirror packet to EGRESS */
	TCA_INGRESS_REDIR  MirredAct = 3 /* packet redirect to INGRESS*/
	TCA_INGRESS_MIRROR MirredAct = 4 /* mirror packet to INGRESS */
)

type MirredAction struct {
	ActionAttrs
	MirredAction MirredAct
	Ifindex      int
}

func (action *MirredAction) Type() string {
	return "mirred"
}

func (action *MirredAction) Attrs() *ActionAttrs {
	return &action.ActionAttrs
}

func NewMirredAction(redirIndex int) *MirredAction {
	return &MirredAction{
		ActionAttrs: ActionAttrs{
			Action: TC_ACT_STOLEN,
		},
		MirredAction: TCA_EGRESS_REDIR,
		Ifindex:      redirIndex,
	}
}

// U32 filters on many packet related properties
type U32 struct {
	FilterAttrs
	ClassId    uint32
	RedirIndex int
	Actions    []Action
}

func (filter *U32) Attrs() *FilterAttrs {
	return &filter.FilterAttrs
}

func (filter *U32) Type() string {
	return "u32"
}

type FilterFwAttrs struct {
	ClassId   uint32
	InDev     string
	Mask      uint32
	Index     uint32
	Buffer    uint32
	Mtu       uint32
	Mpu       uint16
	Rate      uint32
	AvRate    uint32
	PeakRate  uint32
	Action    TcPolAct
	Overhead  uint16
	LinkLayer int
}

type BpfFilter struct {
	FilterAttrs
	ClassId      uint32
	Fd           int
	Name         string
	DirectAction bool
}

func (filter *BpfFilter) Type() string {
	return "bpf"
}

func (filter *BpfFilter) Attrs() *FilterAttrs {
	return &filter.FilterAttrs
}

// GenericFilter filters represent types that are not currently understood
// by this netlink library.
type GenericFilter struct {
	FilterAttrs
	FilterType string
}

func (filter *GenericFilter) Attrs() *FilterAttrs {
	return &filter.FilterAttrs
}

func (filter *GenericFilter) Type() string {
	return filter.FilterType
}
<commit_msg>filter: fix recursive string call (#162)<commit_after>package netlink

import "fmt"

type Filter interface {
	Attrs() *FilterAttrs
	Type() string
}

// FilterAttrs represents a netlink filter. A filter is associated with a link,
// has a handle and a parent. The root filter of a device should have a
// parent == HANDLE_ROOT.
type FilterAttrs struct {
	LinkIndex int
	Handle    uint32
	Parent    uint32
	Priority  uint16 // lower is higher priority
	Protocol  uint16 // syscall.ETH_P_*
}

func (q FilterAttrs) String() string {
	return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Priority: %d, Protocol: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Priority, q.Protocol)
}

type TcAct int32

const (
	TC_ACT_UNSPEC     TcAct = -1
	TC_ACT_OK         TcAct = 0
	TC_ACT_RECLASSIFY TcAct = 1
	TC_ACT_SHOT       TcAct = 2
	TC_ACT_PIPE       TcAct = 3
	TC_ACT_STOLEN     TcAct = 4
	TC_ACT_QUEUED     TcAct = 5
	TC_ACT_REPEAT     TcAct = 6
	TC_ACT_REDIRECT   TcAct = 7
	TC_ACT_JUMP       TcAct = 0x10000000
)

func (a TcAct) String() string {
	switch a {
	case TC_ACT_UNSPEC:
		return "unspec"
	case TC_ACT_OK:
		return "ok"
	case TC_ACT_RECLASSIFY:
		return "reclassify"
	case TC_ACT_SHOT:
		return "shot"
	case TC_ACT_PIPE:
		return "pipe"
	case TC_ACT_STOLEN:
		return "stolen"
	case TC_ACT_QUEUED:
		return "queued"
	case TC_ACT_REPEAT:
		return "repeat"
	case TC_ACT_REDIRECT:
		return "redirect"
	case TC_ACT_JUMP:
		return "jump"
	}
	return fmt.Sprintf("0x%x", int32(a))
}

type TcPolAct int32

const (
	TC_POLICE_UNSPEC     TcPolAct = TcPolAct(TC_ACT_UNSPEC)
	TC_POLICE_OK         TcPolAct = TcPolAct(TC_ACT_OK)
	TC_POLICE_RECLASSIFY TcPolAct = TcPolAct(TC_ACT_RECLASSIFY)
	TC_POLICE_SHOT       TcPolAct = TcPolAct(TC_ACT_SHOT)
	TC_POLICE_PIPE       TcPolAct = TcPolAct(TC_ACT_PIPE)
)

func (a TcPolAct) String() string {
	switch a {
	case TC_POLICE_UNSPEC:
		return "unspec"
	case TC_POLICE_OK:
		return "ok"
	case TC_POLICE_RECLASSIFY:
		return "reclassify"
	case TC_POLICE_SHOT:
		return "shot"
	case TC_POLICE_PIPE:
		return "pipe"
	}
	return fmt.Sprintf("0x%x", int32(a))
}

type ActionAttrs struct {
	Index   int
	Capab   int
	Action  TcAct
	Refcnt  int
	Bindcnt int
}

func (q ActionAttrs) String() string {
	return fmt.Sprintf("{Index: %d, Capab: %x, Action: %s, Refcnt: %d, Bindcnt: %d}", q.Index, q.Capab, q.Action.String(), q.Refcnt, q.Bindcnt)
}

// Action represents an action in any supported filter.
type Action interface {
	Attrs() *ActionAttrs
	Type() string
}

type GenericAction struct {
	ActionAttrs
}

func (action *GenericAction) Type() string {
	return "generic"
}

func (action *GenericAction) Attrs() *ActionAttrs {
	return &action.ActionAttrs
}

type BpfAction struct {
	ActionAttrs
	Fd   int
	Name string
}

func (action *BpfAction) Type() string {
	return "bpf"
}

func (action *BpfAction) Attrs() *ActionAttrs {
	return &action.ActionAttrs
}

type MirredAct uint8

func (a MirredAct) String() string {
	switch a {
	case TCA_EGRESS_REDIR:
		return "egress redir"
	case TCA_EGRESS_MIRROR:
		return "egress mirror"
	case TCA_INGRESS_REDIR:
		return "ingress redir"
	case TCA_INGRESS_MIRROR:
		return "ingress mirror"
	}
	return "unknown"
}

const (
	TCA_EGRESS_REDIR   MirredAct = 1 /* packet redirect to EGRESS*/
	TCA_EGRESS_MIRROR  MirredAct = 2 /* mirror packet to EGRESS */
	TCA_INGRESS_REDIR  MirredAct = 3 /* packet redirect to INGRESS*/
	TCA_INGRESS_MIRROR MirredAct = 4 /* mirror packet to INGRESS */
)

type MirredAction struct {
	ActionAttrs
	MirredAction MirredAct
	Ifindex      int
}

func (action *MirredAction) Type() string {
	return "mirred"
}

func (action *MirredAction) Attrs() *ActionAttrs {
	return &action.ActionAttrs
}

func NewMirredAction(redirIndex int) *MirredAction {
	return &MirredAction{
		ActionAttrs: ActionAttrs{
			Action: TC_ACT_STOLEN,
		},
		MirredAction: TCA_EGRESS_REDIR,
		Ifindex:      redirIndex,
	}
}

// U32 filters on many packet related properties
type U32 struct {
	FilterAttrs
	ClassId    uint32
	RedirIndex int
	Actions    []Action
}

func (filter *U32) Attrs() *FilterAttrs {
	return &filter.FilterAttrs
}

func (filter *U32) Type() string {
	return "u32"
}

type FilterFwAttrs struct {
	ClassId   uint32
	InDev     string
	Mask      uint32
	Index     uint32
	Buffer    uint32
	Mtu       uint32
	Mpu       uint16
	Rate      uint32
	AvRate    uint32
	PeakRate  uint32
	Action    TcPolAct
	Overhead  uint16
	LinkLayer int
}

type BpfFilter struct {
	FilterAttrs
	ClassId      uint32
	Fd           int
	Name         string
	DirectAction bool
}

func (filter *BpfFilter) Type() string {
	return "bpf"
}

func (filter *BpfFilter) Attrs() *FilterAttrs {
	return &filter.FilterAttrs
}

// GenericFilter filters represent types that are not currently understood
// by this netlink library.
type GenericFilter struct {
	FilterAttrs
	FilterType string
}

func (filter *GenericFilter) Attrs() *FilterAttrs {
	return &filter.FilterAttrs
}

func (filter *GenericFilter) Type() string {
	return filter.FilterType
}
<|endoftext|>