<commit_before>package trac

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Client struct {
	server     string // https://user:passwd@trac.example.com/login/jsonrpc
	httpClient *http.Client

	Search *Search
	System *System
	Ticket *Ticket
	Wiki   *Wiki
}

type Request struct {
	Method string   `json:"method"`
	Params []string `json:"params"`
}

type Response struct {
	Error  RPCError        `json:"error,omitempty"`
	Id     string          `json:"id,omitempty"`
	Result json.RawMessage `json:"result,omitempty"`
}

type RPCError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Name    string `json:"name"`
}

func (r *RPCError) Error() string {
	return fmt.Sprintf("%v(%d): %v", r.Name, r.Code, r.Message)
}

func NewClient(server string) *Client {
	c := &Client{
		server: server,
	}
	if c.httpClient == nil {
		c.httpClient = http.DefaultClient
	}

	// RPC exported functions
	c.Search = &Search{client: c}
	c.System = &System{client: c}
	c.Ticket = &Ticket{client: c}
	c.Wiki = &Wiki{client: c}
	return c
}

// Query sends a Request and returns a Response.
// Response.Result is unmarshaled by Client.Do
func (c *Client) Query(function string, params ...string) (Response, error) {
	var response = Response{}
	query := Request{function, params}
	body, err := json.Marshal(query)
	if err != nil {
		return response, err
	}
	res, err := http.Post(c.server, "application/json", bytes.NewReader(body))
	if err != nil {
		return response, err
	}
	defer res.Body.Close()

	resp, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return response, err
	}

	if err := json.Unmarshal(resp, &response); err != nil {
		return response, err
	}
	if response.Error.Code != 0 {
		return response, &response.Error
	}
	return response, nil
}

// Do wraps Client.Query to unmarshal Response.Result in the value pointed to
// by v
func (c *Client) Do(function string, v interface{}, params ...string) (interface{}, error) {
	r, err := c.Query(function, params...)
	if err != nil {
		return nil, err
	}

	if err := json.Unmarshal(r.Result, &v); err != nil {
		return nil, err
	}
	return v, nil
}

// All returns a slice of names. To be used for endpoints which returns lists
// of names. E.g. components, milestones, priorities.
func (c *Client) All(function string) ([]string, error) {
	var r []string
	_, err := c.Do(function, &r)
	return r, err
}
<commit_msg>Allow any type in call params<commit_after>package trac

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

// Client handles communications with Trac JSONRPC.
type Client struct {
	server     string // https://user:passwd@trac.example.com/login/jsonrpc
	httpClient *http.Client

	// RPC functions
	Search *Search
	System *System
	Ticket *Ticket
	Wiki   *Wiki
}

// Request is send to Trac JSONRPC via a HTTP POST request.
type Request struct {
	Method string        `json:"method"`
	Params []interface{} `json:"params"`
}

// Response represents a response returned by Trac JSONRPC.
type Response struct {
	Error  RPCError        `json:"error,omitempty"`
	ID     string          `json:"id,omitempty"`
	Result json.RawMessage `json:"result,omitempty"`
}

// RPCError is the RPC error returned within the response.
type RPCError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Name    string `json:"name"`
}

func (r *RPCError) Error() string {
	return fmt.Sprintf("%v(%d): %v", r.Name, r.Code, r.Message)
}

// NewClient returns a new Trac JSONRPC client.
func NewClient(server string) *Client {
	c := &Client{
		server: server,
	}
	if c.httpClient == nil {
		c.httpClient = http.DefaultClient
	}

	// RPC exported functions
	c.Search = &Search{client: c}
	c.System = &System{client: c}
	c.Ticket = &Ticket{client: c}
	c.Wiki = &Wiki{client: c}
	return c
}

// Query sends a Request and returns a Response.
// Response.Result is unmarshaled by Client.Do
func (c *Client) Query(function string, params ...interface{}) (Response, error) {
	var response = Response{}
	query := Request{function, params}
	body, err := json.Marshal(query)
	if err != nil {
		return response, err
	}

	fmt.Printf("%v\n", string(body))

	res, err := http.Post(c.server, "application/json", bytes.NewReader(body))
	if err != nil {
		return response, err
	}
	defer res.Body.Close()

	resp, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return response, err
	}

	if err := json.Unmarshal(resp, &response); err != nil {
		return response, err
	}
	if response.Error.Code != 0 {
		return response, &response.Error
	}
	return response, nil
}

// Do wraps Client.Query to unmarshal Response.Result in the value pointed to
// by v
func (c *Client) Do(function string, v interface{}, params ...interface{}) (interface{}, error) {
	r, err := c.Query(function, params...)
	if err != nil {
		return nil, err
	}

	if err := json.Unmarshal(r.Result, &v); err != nil {
		return nil, err
	}
	return v, nil
}

// All returns a slice of names. To be used for endpoints which returns lists
// of names. E.g. components, milestones, priorities.
func (c *Client) All(function string) ([]string, error) {
	var r []string
	_, err := c.Do(function, &r)
	return r, err
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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 util

import (
	"archive/tar"
	"bufio"
	"compress/gzip"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/blang/semver"
	"github.com/sirupsen/logrus"

	"k8s.io/release/pkg/command"
)

const (
	TagPrefix = "v"
)

// PackagesAvailable takes a slice of packages and determines if they are installed
// on the host OS. Replaces common::check_packages.
func PackagesAvailable(packages ...string) (bool, error) {
	hostOS, osErr := getOS()
	if osErr != nil {
		return false, osErr
	}

	var pkgMgr string
	missingPkgs := []string{}

	ok := true
	switch hostOS {
	case "Ubuntu", "Debian", "LinuxMint":
		pkgMgr = "apt"

		for _, pkg := range packages {
			checkCmd := command.New(
				"dpkg",
				"-l",
				pkg,
			)

			logrus.Infof("Checking %s", pkg)
			checkCmdStatus, checkCmdErr := checkCmd.RunSilent()
			if checkCmdErr != nil {
				return false, checkCmdErr
			}

			if !checkCmdStatus.Success() {
				logrus.Infof("Adding %s to missing packages", pkg)
				missingPkgs = append(missingPkgs, pkg)
				ok = false
			}
		}
	case "Fedora":
		pkgMgr = "dnf"

		for _, pkg := range packages {
			checkCmd := command.New(
				"rpm",
				"--quiet",
				"-q",
				pkg,
			)

			checkCmdStatus, checkCmdErr := checkCmd.RunSilent()
			if checkCmdErr != nil {
				return false, checkCmdErr
			}

			if !checkCmdStatus.Success() {
				missingPkgs = append(missingPkgs, pkg)
				ok = false
			}
		}
	}

	installInstructionsPrefix := fmt.Sprintf("Install using '%s install '", pkgMgr)

	if len(missingPkgs) > 0 {
		missingPkgsString := strings.Join(missingPkgs, ",")

		logrus.Warnf("The following packages are not installed: %s", missingPkgsString)

		for _, pkg := range missingPkgs {
			installInstructions := fmt.Sprintf("%s%s", installInstructionsPrefix, pkg)

			logrus.Infof("Install %s with: %s", pkg, installInstructions)
		}
	}

	return ok, nil
}

func getOS() (string, error) {
	get := command.New("lsb_release", "-si")
	getStream, getErr := get.RunSilentSuccessOutput()
	if getErr != nil {
		return "", getErr
	}

	osOutput := getStream.OutputTrimNL()
	logrus.Infof("Host OS is %s", osOutput)

	return osOutput, nil
}

/*
#############################################################################
# Simple yes/no prompt
#
# @optparam default -n(default)/-y/-e (default to n, y or make (e)xplicit)
# @param message
common::askyorn () {
  local yorn
  local def=n
  local msg="y/N"

  case $1 in
  -y) # yes default
      def="y" msg="Y/n"
      shift
      ;;
  -e) # Explicit
      def="" msg="y/n"
      shift
      ;;
  -n) shift
      ;;
  esac

  while [[ $yorn != [yYnN] ]]; do
    logecho -n "$*? ($msg): "
    read yorn
    : ${yorn:=$def}
  done

  # Final test to set return code
  [[ $yorn == [yY] ]]
}
*/

func Ask(question, expectedResponse string, retries int) (answer string, success bool, err error) {
	attempts := 1

	if retries < 0 {
		fmt.Printf("Retries was set to a number less than zero (%d). Please specify a positive number of retries or zero, if you want to ask unconditionally.", retries)
	}

	for attempts <= retries {
		scanner := bufio.NewScanner(os.Stdin)
		fmt.Printf("%s (%d/%d) ", question, attempts, retries)

		scanner.Scan()
		answer = scanner.Text()

		if answer == expectedResponse {
			return answer, true, nil
		}

		fmt.Printf("Expected '%s', but got '%s'", expectedResponse, answer)

		attempts++
	}

	return answer, false, errors.New("expected response was not input. Retries exceeded")
}

// FakeGOPATH creates a temp directory, links the base directory into it and
// sets the GOPATH environment variable to it.
func FakeGOPATH(srcDir string) (string, error) {
	logrus.Debug("Linking repository into temp dir")
	baseDir, err := ioutil.TempDir("", "ff-")
	if err != nil {
		return "", err
	}

	logrus.Infof("New working directory is %q", baseDir)

	os.Setenv("GOPATH", baseDir)
	logrus.Debugf("GOPATH: %s", os.Getenv("GOPATH"))

	gitRoot := fmt.Sprintf("%s/src/k8s.io", baseDir)
	if err := os.MkdirAll(gitRoot, os.FileMode(0755)); err != nil {
		return "", err
	}
	gitRoot = filepath.Join(gitRoot, "kubernetes")

	// link the repo into the working directory
	logrus.Debugf("Creating symlink from %q to %q", srcDir, gitRoot)
	if err := os.Symlink(srcDir, gitRoot); err != nil {
		return "", err
	}

	logrus.Infof("Changing working directory to %s", gitRoot)
	if err := os.Chdir(gitRoot); err != nil {
		return "", err
	}

	return gitRoot, nil
}

// ReadFileFromGzippedTar opens a tarball and reads contents of a file inside.
func ReadFileFromGzippedTar(tarPath, filePath string) (io.Reader, error) {
	file, err := os.Open(tarPath)
	if err != nil {
		return nil, err
	}

	archive, err := gzip.NewReader(file)
	if err != nil {
		return nil, err
	}
	tr := tar.NewReader(archive)

	for {
		h, err := tr.Next()
		if err == io.EOF {
			break // End of archive
		}

		if h.Name == filePath {
			return tr, nil
		}
	}

	return nil, errors.New("unable to find file in tarball")
}

// MoreRecent determines if file at path a was modified more recently than file
// at path b. If one file does not exist, the other will be treated as most
// recent. If both files do not exist or an error occurs, an error is returned.
func MoreRecent(a, b string) (bool, error) {
	fileA, errA := os.Stat(a)
	if errA != nil && !os.IsNotExist(errA) {
		return false, errA
	}

	fileB, errB := os.Stat(b)
	if errB != nil && !os.IsNotExist(errB) {
		return false, errB
	}

	switch {
	case os.IsNotExist(errA) && os.IsNotExist(errB):
		return false, errors.New("neither file exists")
	case os.IsNotExist(errA):
		return false, nil
	case os.IsNotExist(errB):
		return true, nil
	}

	return (fileA.ModTime().Unix() >= fileB.ModTime().Unix()), nil
}

func AddTagPrefix(tag string) string {
	return TagPrefix + tag
}

func TrimTagPrefix(tag string) string {
	return strings.TrimPrefix(tag, TagPrefix)
}

func TagStringToSemver(tag string) (semver.Version, error) {
	return semver.Make(TrimTagPrefix(tag))
}

func SemverToTagString(tag semver.Version) string {
	return AddTagPrefix(tag.String())
}
<commit_msg>pkg/util: Clean up log messages for PackagesAvailable() and getOS()<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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 util

import (
	"archive/tar"
	"bufio"
	"compress/gzip"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/blang/semver"
	"github.com/sirupsen/logrus"

	"k8s.io/release/pkg/command"
)

const (
	TagPrefix = "v"
)

// PackagesAvailable takes a slice of packages and determines if they are installed
// on the host OS. Replaces common::check_packages.
func PackagesAvailable(packages ...string) (bool, error) {
	hostOS, osErr := getOS()
	if osErr != nil {
		return false, osErr
	}

	var pkgMgr string
	missingPkgs := []string{}

	ok := true
	switch hostOS {
	case "Ubuntu", "Debian", "LinuxMint":
		pkgMgr = "apt"
		logrus.Infof("Assuming %s as the host OS package manager", pkgMgr)

		for _, pkg := range packages {
			checkCmd := command.New(
				"dpkg",
				"-l",
				pkg,
			)

			logrus.Infof("Checking if %s has been installed via %s...", pkg, pkgMgr)
			checkCmdStatus, checkCmdErr := checkCmd.RunSilent()
			if checkCmdErr != nil {
				return false, checkCmdErr
			}

			if !checkCmdStatus.Success() {
				logrus.Infof("Adding %s to missing packages", pkg)
				missingPkgs = append(missingPkgs, pkg)
				ok = false
			}
		}
	case "Fedora":
		pkgMgr = "dnf"
		logrus.Infof("Assuming %s as the host OS package manager", pkgMgr)

		for _, pkg := range packages {
			checkCmd := command.New(
				"rpm",
				"--quiet",
				"-q",
				pkg,
			)

			logrus.Infof("Checking if %s has been installed via %s...", pkg, pkgMgr)
			checkCmdStatus, checkCmdErr := checkCmd.RunSilent()
			if checkCmdErr != nil {
				return false, checkCmdErr
			}

			if !checkCmdStatus.Success() {
				missingPkgs = append(missingPkgs, pkg)
				ok = false
			}
		}
	}

	installInstructionsPrefix := fmt.Sprintf("sudo %s install ", pkgMgr)

	if len(missingPkgs) > 0 {
		missingPkgsString := strings.Join(missingPkgs, ",")

		logrus.Warnf("The following packages are not installed via %s: %s", pkgMgr, missingPkgsString)

		for _, pkg := range missingPkgs {
			installInstructions := fmt.Sprintf("'%s%s'", installInstructionsPrefix, pkg)

			logrus.Infof("Install %s with: %s", pkg, installInstructions)
		}
	}

	return ok, nil
}

func getOS() (string, error) {
	logrus.Info("Checking host OS...")

	get := command.New("lsb_release", "-si")
	getStream, getErr := get.RunSilentSuccessOutput()
	if getErr != nil {
		return "", getErr
	}

	osOutput := getStream.OutputTrimNL()
	logrus.Infof("Host OS is %s", osOutput)

	return osOutput, nil
}

/*
#############################################################################
# Simple yes/no prompt
#
# @optparam default -n(default)/-y/-e (default to n, y or make (e)xplicit)
# @param message
common::askyorn () {
  local yorn
  local def=n
  local msg="y/N"

  case $1 in
  -y) # yes default
      def="y" msg="Y/n"
      shift
      ;;
  -e) # Explicit
      def="" msg="y/n"
      shift
      ;;
  -n) shift
      ;;
  esac

  while [[ $yorn != [yYnN] ]]; do
    logecho -n "$*? ($msg): "
    read yorn
    : ${yorn:=$def}
  done

  # Final test to set return code
  [[ $yorn == [yY] ]]
}
*/

func Ask(question, expectedResponse string, retries int) (answer string, success bool, err error) {
	attempts := 1

	if retries < 0 {
		fmt.Printf("Retries was set to a number less than zero (%d). Please specify a positive number of retries or zero, if you want to ask unconditionally.", retries)
	}

	for attempts <= retries {
		scanner := bufio.NewScanner(os.Stdin)
		fmt.Printf("%s (%d/%d) ", question, attempts, retries)

		scanner.Scan()
		answer = scanner.Text()

		if answer == expectedResponse {
			return answer, true, nil
		}

		fmt.Printf("Expected '%s', but got '%s'", expectedResponse, answer)

		attempts++
	}

	return answer, false, errors.New("expected response was not input. Retries exceeded")
}

// FakeGOPATH creates a temp directory, links the base directory into it and
// sets the GOPATH environment variable to it.
func FakeGOPATH(srcDir string) (string, error) {
	logrus.Debug("Linking repository into temp dir")
	baseDir, err := ioutil.TempDir("", "ff-")
	if err != nil {
		return "", err
	}

	logrus.Infof("New working directory is %q", baseDir)

	os.Setenv("GOPATH", baseDir)
	logrus.Debugf("GOPATH: %s", os.Getenv("GOPATH"))

	gitRoot := fmt.Sprintf("%s/src/k8s.io", baseDir)
	if err := os.MkdirAll(gitRoot, os.FileMode(0755)); err != nil {
		return "", err
	}
	gitRoot = filepath.Join(gitRoot, "kubernetes")

	// link the repo into the working directory
	logrus.Debugf("Creating symlink from %q to %q", srcDir, gitRoot)
	if err := os.Symlink(srcDir, gitRoot); err != nil {
		return "", err
	}

	logrus.Infof("Changing working directory to %s", gitRoot)
	if err := os.Chdir(gitRoot); err != nil {
		return "", err
	}

	return gitRoot, nil
}

// ReadFileFromGzippedTar opens a tarball and reads contents of a file inside.
func ReadFileFromGzippedTar(tarPath, filePath string) (io.Reader, error) {
	file, err := os.Open(tarPath)
	if err != nil {
		return nil, err
	}

	archive, err := gzip.NewReader(file)
	if err != nil {
		return nil, err
	}
	tr := tar.NewReader(archive)

	for {
		h, err := tr.Next()
		if err == io.EOF {
			break // End of archive
		}

		if h.Name == filePath {
			return tr, nil
		}
	}

	return nil, errors.New("unable to find file in tarball")
}

// MoreRecent determines if file at path a was modified more recently than file
// at path b. If one file does not exist, the other will be treated as most
// recent. If both files do not exist or an error occurs, an error is returned.
func MoreRecent(a, b string) (bool, error) {
	fileA, errA := os.Stat(a)
	if errA != nil && !os.IsNotExist(errA) {
		return false, errA
	}

	fileB, errB := os.Stat(b)
	if errB != nil && !os.IsNotExist(errB) {
		return false, errB
	}

	switch {
	case os.IsNotExist(errA) && os.IsNotExist(errB):
		return false, errors.New("neither file exists")
	case os.IsNotExist(errA):
		return false, nil
	case os.IsNotExist(errB):
		return true, nil
	}

	return (fileA.ModTime().Unix() >= fileB.ModTime().Unix()), nil
}

func AddTagPrefix(tag string) string {
	return TagPrefix + tag
}

func TrimTagPrefix(tag string) string {
	return strings.TrimPrefix(tag, TagPrefix)
}

func TagStringToSemver(tag string) (semver.Version, error) {
	return semver.Make(TrimTagPrefix(tag))
}

func SemverToTagString(tag semver.Version) string {
	return AddTagPrefix(tag.String())
}
<|endoftext|>


<commit_before>// Name: pkgfile.go
// Desc:
//   The types used in this module to represent package files.
package pkgfile

const (
	MoveFileType        string = "MoveFile"
	RunScriptType       string = "RunScript"
	DownloadPackageType string = "DownloadPackage"
)

type PkgOperation interface {
	GetType() string
}

// A type to represent moving a file from one place to another.
type MoveFile struct {
	Src string
	Dst string
}

func (mf MoveFile) GetType() string {
	return MoveFileType
}

// A type to represent running a script at a given location (if it has run
// permissions.)
type RunScript struct {
	Src string
}

func (rs RunScript) GetType() string {
	return RunScriptType
}

// A type to represent downloading another package from GitHub.
type DownloadPackage struct {
	Url string
}

func (dp DownloadPackage) GetType() string {
	return DownloadPackageType
}

// The type that represents a whole package file.
type PkgFile []PkgOperation
<commit_msg>Changed the name from DownloadPackage to InstallPackage.<commit_after>// Name: pkgfile.go
// Desc:
//   The types used in this module to represent package files.
package pkgfile

const (
	MoveFileType       string = "MoveFile"
	RunScriptType      string = "RunScript"
	InstallPackageType string = "InstallPackage"
)

type PkgOperation interface {
	GetType() string
}

// A type to represent moving a file from one place to another.
type MoveFile struct {
	Src string
	Dst string
}

func (mf MoveFile) GetType() string {
	return MoveFileType
}

// A type to represent running a script at a given location (if it has run
// permissions.)
type RunScript struct {
	Src string
}

func (rs RunScript) GetType() string {
	return RunScriptType
}

// A type to represent downloading another package from GitHub.
type InstallPackage struct {
	Url string
}

func (dp InstallPackage) GetType() string {
	return InstallPackageType
}

// The type that represents a whole package file.
type PkgFile []PkgOperation
<|endoftext|>


<commit_before>package piccolo

import (
	"time"
)

type Piccolo struct {
	Jobs []*Job
	add  chan *Job
}

type Job struct {
	Job    func()
	Ticker int
}

type byTime []*Job

func (s byTime) Len() int {
	return len(s)
}
func (s byTime) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s byTime) Less(i, j int) bool {

}
<commit_msg>update timer.go<commit_after>package piccolo

import (
	"time"
)

type Piccolo struct {
	Jobs []*Job
	add  chan *Job
}

type Job struct {
	Job    func()
	Ticker int
}

type byTime []*Job

func (s byTime) Len() int {
	return len(s)
}
func (s byTime) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s byTime) Less(i, j int) bool {
  if s[i]
}
<|endoftext|>


<commit_before>package ethchain

import (
	"bytes"
	"github.com/ethereum/eth-go/ethutil"
	"github.com/ethereum/eth-go/ethwire"
	"log"
	"math"
	"math/big"
)

type BlockChain struct {
	Ethereum EthManager
	// The famous, the fabulous Mister GENESIIIIIIS (block)
	genesisBlock *Block
	// Last known total difficulty
	TD *big.Int

	LastBlockNumber uint64

	CurrentBlock  *Block
	LastBlockHash []byte
}

func NewBlockChain(ethereum EthManager) *BlockChain {
	bc := &BlockChain{}
	bc.genesisBlock = NewBlockFromBytes(ethutil.Encode(Genesis))
	bc.Ethereum = ethereum

	bc.setLastBlock()

	return bc
}

func (bc *BlockChain) Genesis() *Block {
	return bc.genesisBlock
}

func (bc *BlockChain) NewBlock(coinbase []byte, txs []*Transaction) *Block {
	var root interface{}
	var lastBlockTime int64
	hash := ZeroHash256

	if bc.CurrentBlock != nil {
		root = bc.CurrentBlock.state.trie.Root
		hash = bc.LastBlockHash
		lastBlockTime = bc.CurrentBlock.Time
	}

	block := CreateBlock(
		root,
		hash,
		coinbase,
		ethutil.BigPow(2, 32),
		nil,
		"",
		txs)

	if bc.CurrentBlock != nil {
		var mul *big.Int
		if block.Time < lastBlockTime+42 {
			mul = big.NewInt(1)
		} else {
			mul = big.NewInt(-1)
		}

		diff := new(big.Int)
		diff.Add(diff, bc.CurrentBlock.Difficulty)
		diff.Div(diff, big.NewInt(1024))
		diff.Mul(diff, mul)
		diff.Add(diff, bc.CurrentBlock.Difficulty)
		block.Difficulty = diff
	}

	return block
}

func (bc *BlockChain) HasBlock(hash []byte) bool {
	data, _ := ethutil.Config.Db.Get(hash)
	return len(data) != 0
}

// TODO: At one point we might want to save a block by prevHash in the db to optimise this...
func (bc *BlockChain) HasBlockWithPrevHash(hash []byte) bool {
	block := bc.CurrentBlock

	for ; block != nil; block = bc.GetBlock(block.PrevHash) {
		if bytes.Compare(hash, block.PrevHash) == 0 {
			return true
		}
	}
	return false
}

func (bc *BlockChain) CalculateBlockTD(block *Block) *big.Int {
	blockDiff := new(big.Int)

	for _, uncle := range block.Uncles {
		blockDiff = blockDiff.Add(blockDiff, uncle.Difficulty)
	}
	blockDiff = blockDiff.Add(blockDiff, block.Difficulty)

	return blockDiff
}
func (bc *BlockChain) FindCanonicalChainFromMsg(msg *ethwire.Msg, commonBlockHash []byte) bool {
	var blocks []*Block
	for i := 0; i < (msg.Data.Len() - 1); i++ {
		block := NewBlockFromRlpValue(msg.Data.Get(i))
		blocks = append(blocks, block)
	}
	return bc.FindCanonicalChain(blocks, commonBlockHash)
}

// Is tasked by finding the CanonicalChain and resetting the chain if we are not the Conical one
// Return true if we are the using the canonical chain false if not
func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte) bool {
	// 1. Calculate TD of the current chain
	// 2. Calculate TD of the new chain
	// Reset state to the correct one

	chainDifficulty := new(big.Int)

	// Calculate the entire chain until the block we both have
	// Start with the newest block we got, all the way back to the common block we both know
	for _, block := range blocks {
		if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
			log.Println("[CHAIN] We have found the common parent block, breaking")
			break
		}
		chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block))
	}

	log.Println("[CHAIN] Incoming chain difficulty:", chainDifficulty)

	curChainDifficulty := new(big.Int)
	block := bc.CurrentBlock
	for i := 0; block != nil; block = bc.GetBlock(block.PrevHash) {
		i++
		if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
			log.Println("[CHAIN] We have found the common parent block, breaking")
			break
		}
		anOtherBlock := bc.GetBlock(block.PrevHash)
		if anOtherBlock == nil {
			// We do not want to count the genesis block for difficulty since that's not being sent
			log.Println("[CHAIN] At genesis block, breaking")
			break
		}
		curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block))
	}

	log.Println("[CHAIN] Current chain difficulty:", curChainDifficulty)
	if chainDifficulty.Cmp(curChainDifficulty) == 1 {
		log.Printf("[CHAIN] The incoming Chain beat our asses, resetting to block: %x", commonBlockHash)
		bc.ResetTillBlockHash(commonBlockHash)
		return false
	} else {
		log.Println("[CHAIN] Our chain showed the incoming chain who is boss. Ignoring.")
		return true
	}
}
func (bc *BlockChain) ResetTillBlockHash(hash []byte) error {
	lastBlock := bc.CurrentBlock
	var returnTo *Block
	// Reset to Genesis if that's all the origin there is.
	if bytes.Compare(hash, bc.genesisBlock.Hash()) == 0 {
		returnTo = bc.genesisBlock
		bc.CurrentBlock = bc.genesisBlock
		bc.LastBlockHash = bc.genesisBlock.Hash()
		bc.LastBlockNumber = 1
	} else {
		// TODO: Somehow this doesn't really give the right numbers, double check.
		// TODO: Change logs into debug lines
		returnTo = bc.GetBlock(hash)
		bc.CurrentBlock = returnTo
		bc.LastBlockHash = returnTo.Hash()
		info := bc.BlockInfo(returnTo)
		bc.LastBlockNumber = info.Number
	}

	// XXX Why are we resetting? This is the block chain, it has nothing to do with states
	//bc.Ethereum.StateManager().PrepareDefault(returnTo)

	// Manually reset the last sync block
	err := ethutil.Config.Db.Delete(lastBlock.Hash())
	if err != nil {
		return err
	}

	var block *Block
	for ; block != nil; block = bc.GetBlock(block.PrevHash) {
		if bytes.Compare(block.Hash(), hash) == 0 {
			log.Println("[CHAIN] We have arrived at the the common parent block, breaking")
			break
		}
		err = ethutil.Config.Db.Delete(block.Hash())
		if err != nil {
			return err
		}
	}
	log.Println("[CHAIN] Split chain deleted and reverted to common parent block.")
	return nil
}

func (bc *BlockChain) GenesisBlock() *Block {
	return bc.genesisBlock
}

// Get chain return blocks from hash up to max in RLP format
func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
	var chain []interface{}
	// Get the current hash to start with
	currentHash := bc.CurrentBlock.Hash()
	// Get the last number on the block chain
	lastNumber := bc.BlockInfo(bc.CurrentBlock).Number
	// Get the parents number
	parentNumber := bc.BlockInfoByHash(hash).Number
	// Get the min amount. We might not have max amount of blocks
	count := uint64(math.Min(float64(lastNumber-parentNumber), float64(max)))
	startNumber := parentNumber + count

	num := lastNumber
	for ; num > startNumber; currentHash = bc.GetBlock(currentHash).PrevHash {
		num--
	}
	for i := uint64(0); bytes.Compare(currentHash, hash) != 0 && num >= parentNumber && i < count; i++ {
		// Get the block of the chain
		block := bc.GetBlock(currentHash)
		currentHash = block.PrevHash

		chain = append(chain, block.Value().Val)

		num--
	}

	return chain
}

func (bc *BlockChain) GetChain(hash []byte, amount int) []*Block {
	genHash := bc.genesisBlock.Hash()

	block := bc.GetBlock(hash)
	var blocks []*Block

	for i := 0; i < amount && block != nil; block = bc.GetBlock(block.PrevHash) {
		blocks = append([]*Block{block}, blocks...)

		if bytes.Compare(genHash, block.Hash()) == 0 {
			break
		}
		i++
	}

	return blocks
}

func AddTestNetFunds(block *Block) {
	for _, addr := range []string{
		"8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin
		"e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey
		"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
		"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex
		"2ef47100e0787b915105fd5e3f4ff6752079d5cb", // Maran
	} {
		codedAddr := ethutil.FromHex(addr)
		account := block.state.GetAccount(codedAddr)
		account.Amount = ethutil.BigPow(2, 200)
		block.state.UpdateStateObject(account)
	}
	log.Printf("%x\n", block.RlpEncode())
}

func (bc *BlockChain) setLastBlock() {
	data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
	if len(data) != 0 {
		block := NewBlockFromBytes(data)
		info := bc.BlockInfo(block)
		bc.CurrentBlock = block
		bc.LastBlockHash = block.Hash()
		bc.LastBlockNumber = info.Number

		ethutil.Config.Log.Infof("[CHAIN] Last known block height #%d\n", bc.LastBlockNumber)
	} else {
		AddTestNetFunds(bc.genesisBlock)

		bc.genesisBlock.state.trie.Sync()
		// Prepare the genesis block
		bc.Add(bc.genesisBlock)

		//log.Printf("root %x\n", bm.bc.genesisBlock.State().Root)
		//bm.bc.genesisBlock.PrintHash()
	}

	// Set the last know difficulty (might be 0x0 as initial value, Genesis)
	bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())

	ethutil.Config.Log.Infof("Last block: %x\n", bc.CurrentBlock.Hash())
}

func (bc *BlockChain) SetTotalDifficulty(td *big.Int) {
	ethutil.Config.Db.Put([]byte("LastKnownTotalDifficulty"), td.Bytes())
	bc.TD = td
}

// Add a block to the chain and record addition information
func (bc *BlockChain) Add(block *Block) {
	bc.writeBlockInfo(block)
	// Prepare the genesis block

	bc.CurrentBlock = block
	bc.LastBlockHash = block.Hash()

	encodedBlock := block.RlpEncode()
	ethutil.Config.Db.Put(block.Hash(), encodedBlock)
	ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
}

func (bc *BlockChain) GetBlock(hash []byte) *Block {
	data, _ := ethutil.Config.Db.Get(hash)
	if len(data) == 0 {
		return nil
	}

	return NewBlockFromBytes(data)
}

func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo {
	bi := BlockInfo{}
	data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...))
	bi.RlpDecode(data)

	return bi
}

func (bc *BlockChain) BlockInfo(block *Block) BlockInfo {
	bi := BlockInfo{}
	data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
	bi.RlpDecode(data)

	return bi
}

// Unexported method for writing extra non-essential block info to the db
func (bc *BlockChain) writeBlockInfo(block *Block) {
	bc.LastBlockNumber++
	bi := BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash}

	// For now we use the block hash with the words "info" appended as key
	ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
}

func (bc *BlockChain) Stop() {
	if bc.CurrentBlock != nil {
		log.Println("[CHAIN] Stopped")
	}
}
<commit_msg>added roman<commit_after>package ethchain

import (
	"bytes"
	"github.com/ethereum/eth-go/ethutil"
	"github.com/ethereum/eth-go/ethwire"
	"log"
	"math"
	"math/big"
)

type BlockChain struct {
	Ethereum EthManager
	// The famous, the fabulous Mister GENESIIIIIIS (block)
	genesisBlock *Block
	// Last known total difficulty
	TD *big.Int

	LastBlockNumber uint64

	CurrentBlock  *Block
	LastBlockHash []byte
}

func NewBlockChain(ethereum EthManager) *BlockChain {
	bc := &BlockChain{}
	bc.genesisBlock = NewBlockFromBytes(ethutil.Encode(Genesis))
	bc.Ethereum = ethereum

	bc.setLastBlock()

	return bc
}

func (bc *BlockChain) Genesis() *Block {
	return bc.genesisBlock
}

func (bc *BlockChain) NewBlock(coinbase []byte, txs []*Transaction) *Block {
	var root interface{}
	var lastBlockTime int64
	hash := ZeroHash256

	if bc.CurrentBlock != nil {
		root = bc.CurrentBlock.state.trie.Root
		hash = bc.LastBlockHash
		lastBlockTime = bc.CurrentBlock.Time
	}

	block := CreateBlock(
		root,
		hash,
		coinbase,
		ethutil.BigPow(2, 32),
		nil,
		"",
		txs)

	if bc.CurrentBlock != nil {
		var mul *big.Int
		if block.Time < lastBlockTime+42 {
			mul = big.NewInt(1)
		} else {
			mul = big.NewInt(-1)
		}

		diff := new(big.Int)
		diff.Add(diff, bc.CurrentBlock.Difficulty)
		diff.Div(diff, big.NewInt(1024))
		diff.Mul(diff, mul)
		diff.Add(diff, bc.CurrentBlock.Difficulty)
		block.Difficulty = diff
	}

	return block
}

func (bc *BlockChain) HasBlock(hash []byte) bool {
	data, _ := ethutil.Config.Db.Get(hash)
	return len(data) != 0
}

// TODO: At one point we might want to save a block by prevHash in the db to optimise this...
func (bc *BlockChain) HasBlockWithPrevHash(hash []byte) bool {
	block := bc.CurrentBlock

	for ; block != nil; block = bc.GetBlock(block.PrevHash) {
		if bytes.Compare(hash, block.PrevHash) == 0 {
			return true
		}
	}
	return false
}

func (bc *BlockChain) CalculateBlockTD(block *Block) *big.Int {
	blockDiff := new(big.Int)

	for _, uncle := range block.Uncles {
		blockDiff = blockDiff.Add(blockDiff, uncle.Difficulty)
	}
	blockDiff = blockDiff.Add(blockDiff, block.Difficulty)

	return blockDiff
}
func (bc *BlockChain) FindCanonicalChainFromMsg(msg *ethwire.Msg, commonBlockHash []byte) bool {
	var blocks []*Block
	for i := 0; i < (msg.Data.Len() - 1); i++ {
		block := NewBlockFromRlpValue(msg.Data.Get(i))
		blocks = append(blocks, block)
	}
	return bc.FindCanonicalChain(blocks, commonBlockHash)
}

// Is tasked by finding the CanonicalChain and resetting the chain if we are not the Conical one
// Return true if we are the using the canonical chain false if not
func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte) bool {
	// 1. Calculate TD of the current chain
	// 2. Calculate TD of the new chain
	// Reset state to the correct one

	chainDifficulty := new(big.Int)

	// Calculate the entire chain until the block we both have
	// Start with the newest block we got, all the way back to the common block we both know
	for _, block := range blocks {
		if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
			log.Println("[CHAIN] We have found the common parent block, breaking")
			break
		}
		chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block))
	}

	log.Println("[CHAIN] Incoming chain difficulty:", chainDifficulty)

	curChainDifficulty := new(big.Int)
	block := bc.CurrentBlock
	for i := 0; block != nil; block = bc.GetBlock(block.PrevHash) {
		i++
		if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
			log.Println("[CHAIN] We have found the common parent block, breaking")
			break
		}
		anOtherBlock := bc.GetBlock(block.PrevHash)
		if anOtherBlock == nil {
			// We do not want to count the genesis block for difficulty since that's not being sent
			log.Println("[CHAIN] At genesis block, breaking")
			break
		}
		curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block))
	}

	log.Println("[CHAIN] Current chain difficulty:", curChainDifficulty)
	if chainDifficulty.Cmp(curChainDifficulty) == 1 {
		log.Printf("[CHAIN] The incoming Chain beat our asses, resetting to block: %x", commonBlockHash)
		bc.ResetTillBlockHash(commonBlockHash)
		return false
	} else {
		log.Println("[CHAIN] Our chain showed the incoming chain who is boss. Ignoring.")
		return true
	}
}
func (bc *BlockChain) ResetTillBlockHash(hash []byte) error {
	lastBlock := bc.CurrentBlock
	var returnTo *Block
	// Reset to Genesis if that's all the origin there is.
	if bytes.Compare(hash, bc.genesisBlock.Hash()) == 0 {
		returnTo = bc.genesisBlock
		bc.CurrentBlock = bc.genesisBlock
		bc.LastBlockHash = bc.genesisBlock.Hash()
		bc.LastBlockNumber = 1
	} else {
		// TODO: Somehow this doesn't really give the right numbers, double check.
		// TODO: Change logs into debug lines
		returnTo = bc.GetBlock(hash)
		bc.CurrentBlock = returnTo
		bc.LastBlockHash = returnTo.Hash()
		info := bc.BlockInfo(returnTo)
		bc.LastBlockNumber = info.Number
	}

	// XXX Why are we resetting? This is the block chain, it has nothing to do with states
	//bc.Ethereum.StateManager().PrepareDefault(returnTo)

	// Manually reset the last sync block
	err := ethutil.Config.Db.Delete(lastBlock.Hash())
	if err != nil {
		return err
	}

	var block *Block
	for ; block != nil; block = bc.GetBlock(block.PrevHash) {
		if bytes.Compare(block.Hash(), hash) == 0 {
			log.Println("[CHAIN] We have arrived at the the common parent block, breaking")
			break
		}
		err = ethutil.Config.Db.Delete(block.Hash())
		if err != nil {
			return err
		}
	}
	log.Println("[CHAIN] Split chain deleted and reverted to common parent block.")
	return nil
}

func (bc *BlockChain) GenesisBlock() *Block {
	return bc.genesisBlock
}

// Get chain return blocks from hash up to max in RLP format
func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
	var chain []interface{}
	// Get the current hash to start with
	currentHash := bc.CurrentBlock.Hash()
	// Get the last number on the block chain
	lastNumber := bc.BlockInfo(bc.CurrentBlock).Number
	// Get the parents number
	parentNumber := bc.BlockInfoByHash(hash).Number
	// Get the min amount. We might not have max amount of blocks
	count := uint64(math.Min(float64(lastNumber-parentNumber), float64(max)))
	startNumber := parentNumber + count

	num := lastNumber
	for ; num > startNumber; currentHash = bc.GetBlock(currentHash).PrevHash {
		num--
	}
	for i := uint64(0); bytes.Compare(currentHash, hash) != 0 && num >= parentNumber && i < count; i++ {
		// Get the block of the chain
		block := bc.GetBlock(currentHash)
		currentHash = block.PrevHash

		chain = append(chain, block.Value().Val)

		num--
	}

	return chain
}

func (bc *BlockChain) GetChain(hash []byte, amount int) []*Block {
	genHash := bc.genesisBlock.Hash()

	block := bc.GetBlock(hash)
	var blocks []*Block

	for i := 0; i < amount && block != nil; block = bc.GetBlock(block.PrevHash) {
		blocks = append([]*Block{block}, blocks...)

		if bytes.Compare(genHash, block.Hash()) == 0 {
			break
		}
		i++
	}

	return blocks
}

func AddTestNetFunds(block *Block) {
	for _, addr := range []string{
		"8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin
		"e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey
		"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
		"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex
		"2ef47100e0787b915105fd5e3f4ff6752079d5cb", // Maran
		"cd2a3d9f938e13cd947ec05abc7fe734df8dd826", // Roman
	} {
		codedAddr := ethutil.FromHex(addr)
		account := block.state.GetAccount(codedAddr)
		account.Amount = ethutil.BigPow(2, 200)
		block.state.UpdateStateObject(account)
	}
	log.Printf("%x\n", block.RlpEncode())
}

func (bc *BlockChain) setLastBlock() {
	data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
	if len(data) != 0 {
		block := NewBlockFromBytes(data)
		info := bc.BlockInfo(block)
		bc.CurrentBlock = block
		bc.LastBlockHash = block.Hash()
		bc.LastBlockNumber = info.Number

		ethutil.Config.Log.Infof("[CHAIN] Last known block height #%d\n", bc.LastBlockNumber)
	} else {
		AddTestNetFunds(bc.genesisBlock)

		bc.genesisBlock.state.trie.Sync()
		// Prepare the genesis block
		bc.Add(bc.genesisBlock)

		//log.Printf("root %x\n", bm.bc.genesisBlock.State().Root)
		//bm.bc.genesisBlock.PrintHash()
	}

	// Set the last know difficulty (might be 0x0 as initial value, Genesis)
	bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())

	ethutil.Config.Log.Infof("Last block: %x\n", bc.CurrentBlock.Hash())
}

func (bc *BlockChain) SetTotalDifficulty(td *big.Int) {
	ethutil.Config.Db.Put([]byte("LastKnownTotalDifficulty"), td.Bytes())
	bc.TD = td
}

// Add a block to the chain and record addition information
func (bc *BlockChain) Add(block *Block) {
	bc.writeBlockInfo(block)
	// Prepare the genesis block

	bc.CurrentBlock = block
	bc.LastBlockHash = block.Hash()

	encodedBlock := block.RlpEncode()
	ethutil.Config.Db.Put(block.Hash(), encodedBlock)
	ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
}

func (bc *BlockChain) GetBlock(hash []byte) *Block {
	data, _ := ethutil.Config.Db.Get(hash)
	if len(data) == 0 {
		return nil
	}

	return NewBlockFromBytes(data)
}

func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo {
	bi := BlockInfo{}
	data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...))
	bi.RlpDecode(data)

	return bi
}

func (bc *BlockChain) BlockInfo(block *Block) BlockInfo {
	bi := BlockInfo{}
	data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
	bi.RlpDecode(data)

	return bi
}

// Unexported method for writing extra non-essential block info to the db
func (bc *BlockChain) writeBlockInfo(block *Block) {
	bc.LastBlockNumber++
	bi := BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash}

	// For now we use the block hash with the words "info" appended as key
	ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
}

func (bc *BlockChain) Stop() {
	if bc.CurrentBlock != nil {
		log.Println("[CHAIN] Stopped")
	}
}
<|endoftext|>


<commit_before>package eval

// Builtin special forms.

import "github.com/elves/elvish/parse"

type exitusOp func(*Evaluator) Exitus
type builtinSpecialCompile func(*Compiler, *parse.FormNode) exitusOp

type builtinSpecial struct {
	compile     builtinSpecialCompile
	streamTypes [2]StreamType
}

var builtinSpecials map[string]builtinSpecial

func init() {
	// Needed to avoid initialization loop
	builtinSpecials = map[string]builtinSpecial{
		"var": builtinSpecial{compileVar, [2]StreamType{}},
		"set": builtinSpecial{compileSet, [2]StreamType{}},
		"del": builtinSpecial{compileDel, [2]StreamType{}},

		"fn": builtinSpecial{compileFn, [2]StreamType{}},

		"static-typeof": builtinSpecial{
			compileStaticTypeof, [2]StreamType{0, fdStream}},
	}
}

func mayAssign(tvar, tval Type) bool {
	if isAny(tval) || isAny(tvar) {
		return true
	}
	// XXX(xiaq) This is not how you check the equality of two interfaces. But
	// it happens to work when all the Type instances we have are empty
	// structs.
	return tval == tvar
}

func checkSetType(cp *Compiler, names []string, values []*parse.CompoundNode, vop valuesOp, p parse.Pos) {
	if !vop.tr.mayCountTo(len(names)) {
		cp.errorf(p, "number of variables doesn't match that of values")
	}
	_, more := vop.tr.count()
	if more {
		// TODO Try to check soundness to some extent
		return
	}
	for i, name := range names {
		tval := vop.tr[i].t
		tvar := cp.ResolveVar(splitQualifiedName(name))
		if !mayAssign(tvar, tval) {
			cp.errorf(values[i].Pos, "type mismatch: assigning %#v value to %#v variable", tval, tvar)
		}
	}
}

// ensure that a CompoundNode contains exactly one PrimaryNode.
func ensurePrimary(cp *Compiler, cn *parse.CompoundNode, msg string) *parse.PrimaryNode {
	if len(cn.Nodes) != 1 || cn.Nodes[0].Right != nil {
		cp.errorf(cn.Pos, msg)
	}
	return cn.Nodes[0].Left
}

// ensureVariableOrStringPrimary ensures that a CompoundNode contains exactly
// one PrimaryNode of type VariablePrimary or StringPrimary.
func ensureVariableOrStringPrimary(cp *Compiler, cn *parse.CompoundNode, msg string) (*parse.PrimaryNode, string) {
	pn := ensurePrimary(cp, cn, msg)
	switch pn.Typ {
	case parse.VariablePrimary, parse.StringPrimary:
		return pn, pn.Node.(*parse.StringNode).Text
	default:
		cp.errorf(cn.Pos, msg)
		return nil, ""
	}
}

// ensureVariablePrimary ensures that a CompoundNode contains exactly one
// PrimaryNode of type VariablePrimary.
func ensureVariablePrimary(cp *Compiler, cn *parse.CompoundNode, msg string) (*parse.PrimaryNode, string) {
	pn, text := ensureVariableOrStringPrimary(cp, cn, msg)
	if pn.Typ != parse.VariablePrimary {
		cp.errorf(pn.Pos, msg)
	}
	return pn, text
}

// ensureStartWithVariabl ensures the first compound of the form is a
// VariablePrimary. This is merely for better error messages; No actual
// processing is done.
func ensureStartWithVariable(cp *Compiler, fn *parse.FormNode, form string) {
	if len(fn.Args.Nodes) == 0 {
		cp.errorf(fn.Pos, "expect variable after %s", form)
	}
	ensureVariablePrimary(cp, fn.Args.Nodes[0], "expect variable")
}

// VarForm    = 'var' { VarGroup } [ '=' Compound ]
// VarGroup   = { VariablePrimary } [ StringPrimary ]
//
// Variables in the same VarGroup has the type specified by the StringPrimary.
// Only in the last VarGroup the StringPrimary may be omitted, in which case it
// defaults to "any". For instance,
//
// var $u $v Type1 $x $y Type2 $z = a b c d e
//
// gives $u and $v type Type1, $x $y type Type2 and $z type Any and
// assigns them the values a, b, c, d, e respectively.
func compileVar(cp *Compiler, fn *parse.FormNode) exitusOp {
	var (
		names  []string
		types  []Type
		values []*parse.CompoundNode
	)

	ensureStartWithVariable(cp, fn, "var")

	for i, cn := range fn.Args.Nodes {
		expect := "expect variable, type or equal sign"
		pn, text := ensureVariableOrStringPrimary(cp, cn, expect)
		if pn.Typ == parse.VariablePrimary {
			names = append(names, text)
		} else {
			if text == "=" {
				values = fn.Args.Nodes[i+1:]
				break
			} else {
				if t, ok := typenames[text]; !ok {
					cp.errorf(pn.Pos, "%v is not a valid type name", text)
				} else {
					if len(names) == len(types) {
						cp.errorf(pn.Pos, "duplicate type")
					}
					for i := len(types); i < len(names); i++ {
						types = append(types, t)
					}
				}
			}
		}
	}

	for i := len(types); i < len(names); i++ {
		types = append(types, AnyType{})
	}

	for i, name := range names {
		cp.pushVar(name, types[i])
	}

	var vop valuesOp
	if values != nil {
		vop = cp.compileCompounds(values)
		checkSetType(cp, names, values, vop, fn.Pos)
	}
	return func(ev *Evaluator) Exitus {
		for i, name := range names {
			ev.local[name] = newInternalVariable(types[i].Default(), types[i])
		}
		if vop.f != nil {
			return doSet(ev, names, vop.f(ev))
		}
		return success
	}
}

// SetForm = 'set' { VariablePrimary } '=' { Compound }
func compileSet(cp *Compiler, fn *parse.FormNode) exitusOp {
	var (
		names  []string
		values []*parse.CompoundNode
	)

	ensureStartWithVariable(cp, fn, "set")

	for i, cn := range fn.Args.Nodes {
		expect := "expect variable or equal sign"
		pn, text := ensureVariableOrStringPrimary(cp, cn, expect)
		if pn.Typ == parse.VariablePrimary {
			ns, name := splitQualifiedName(text)
			cp.mustResolveVar(ns, name, cn.Pos)
			names = append(names, text)
		} else {
			if text != "=" {
				cp.errorf(pn.Pos, expect)
			}
			values = fn.Args.Nodes[i+1:]
			break
		}
	}

	var vop valuesOp
	vop = cp.compileCompounds(values)
	checkSetType(cp, names, values, vop, fn.Pos)

	return func(ev *Evaluator) Exitus {
		return doSet(ev, names, vop.f(ev))
	}
}

var (
	arityMismatch Exitus = newFailure("arity mismatch")
	typeMismatch  Exitus = newFailure("type mismatch")
)

func doSet(ev *Evaluator, names []string, values []Value) Exitus {
	// TODO Support assignment of mismatched arity in some restricted way -
	// "optional" and "rest" arguments and the like
	if len(names) != len(values) {
		return arityMismatch
	}

	for i, name := range names {
		// TODO Prevent overriding builtin variables e.g. $pid $env
		variable := ev.ResolveVar(splitQualifiedName(name))
		tvar := variable.StaticType()
		tval := values[i].Type()
		if !mayAssign(tvar, tval) {
			return typeMismatch
		}
		variable.Set(values[i])
	}

	return success
}

// DelForm = 'del' { VariablePrimary }
func compileDel(cp *Compiler, fn *parse.FormNode) exitusOp {
	// Do conventional compiling of all compound expressions, including
	// ensuring that variables can be resolved
	var names []string
	for _, cn := range fn.Args.Nodes {
		_, qname := ensureVariablePrimary(cp, cn, "expect variable")
		ns, name := splitQualifiedName(qname)
		if ns != "" && ns != "local" {
			cp.errorf(cn.Pos, "can only delete a variable on local scope")
		}
		if cp.resolveVarOnThisScope(name) == nil {
			cp.errorf(cn.Pos, "variable $%s not found on current local scope", name)
		}

		cp.popVar(name)
		names = append(names, name)
	}
	return func(ev *Evaluator) Exitus {
		for _, name := range names {
			delete(ev.local, name)
		}
		return success
	}
}

// FnForm = 'fn' StringPrimary { VariablePrimary } ClosurePrimary
//
// fn defines a function. This isn't strictly needed, since user-defined
// functions are just variables. The following two lines should be exactly
// equivalent:
//
// fn f $a $b { put (* $a $b) (/ $a *b) }
// var $fn-f = { |$a $b| put (* $a $b) (/ $a $b) }
func compileFn(cp *Compiler, fn *parse.FormNode) exitusOp {
	if len(fn.Args.Nodes) == 0 {
		cp.errorf(fn.Pos, "expect function name after fn")
	}
	pn, fnName := ensureVariableOrStringPrimary(cp, fn.Args.Nodes[0], "expect string literal")
	varName := "fn-" + fnName
	if cp.resolveVarOnThisScope(varName) != nil {
		cp.errorf(pn.Pos, "redefinition of function %s", fnName)
	}

	var closureNode *parse.ClosureNode
	var argNames []*parse.CompoundNode

	for i, cn := range fn.Args.Nodes[1:] {
		expect := "expect variable or closure"
		pn := ensurePrimary(cp, cn, expect)
		switch pn.Typ {
		case parse.ClosurePrimary:
			if i+2 != len(fn.Args.Nodes) {
				cp.errorf(fn.Args.Nodes[i+2].Pos, "garbage after closure literal")
			}
			closureNode = pn.Node.(*parse.ClosureNode)
			break
		case parse.VariablePrimary:
			argNames = append(argNames, cn)
		default:
			cp.errorf(pn.Pos, expect)
		}
	}

	if len(argNames) > 0 {
		closureNode = &parse.ClosureNode{
			closureNode.Pos,
			&parse.SpacedNode{argNames[0].Pos, argNames},
			closureNode.Chunk,
		}
	}

	op := cp.compileClosure(closureNode)

	cp.pushVar(varName, ClosureType{})

	return func(ev *Evaluator) Exitus {
		ev.local[varName] = newInternalVariable(op.f(ev)[0], ClosureType{})
		return success
	}
}

func compileStaticTypeof(cp *Compiler, fn *parse.FormNode) exitusOp {
	// Do conventional compiling of all compounds, only keeping the static type
	// information
	var trs []typeRun
	for _, cn := range fn.Args.Nodes {
		trs = append(trs, cp.compileCompound(cn).tr)
	}
	return func(ev *Evaluator) Exitus {
		out := ev.ports[1].ch
		for _, tr := range trs {
			out <- NewString(tr.String())
		}
		return success
	}
}
<commit_msg>Teach "set" builtin to check for existence of variable at runtime.<commit_after>package eval

// Builtin special forms.

import (
	"fmt"

	"github.com/elves/elvish/parse"
)

type exitusOp func(*Evaluator) Exitus
type builtinSpecialCompile func(*Compiler, *parse.FormNode) exitusOp

type builtinSpecial struct {
	compile     builtinSpecialCompile
	streamTypes [2]StreamType
}

var builtinSpecials map[string]builtinSpecial

func init() {
	// Needed to avoid initialization loop
	builtinSpecials = map[string]builtinSpecial{
		"var": builtinSpecial{compileVar, [2]StreamType{}},
		"set": builtinSpecial{compileSet, [2]StreamType{}},
		"del": builtinSpecial{compileDel, [2]StreamType{}},

		"fn": builtinSpecial{compileFn, [2]StreamType{}},

		"static-typeof": builtinSpecial{
			compileStaticTypeof, [2]StreamType{0, fdStream}},
	}
}

func mayAssign(tvar, tval Type) bool {
	if isAny(tval) || isAny(tvar) {
		return true
	}
	// XXX(xiaq) This is not how you check the equality of two interfaces. But
	// it happens to work when all the Type instances we have are empty
	// structs.
	return tval == tvar
}

func checkSetType(cp *Compiler, names []string, values []*parse.CompoundNode, vop valuesOp, p parse.Pos) {
	if !vop.tr.mayCountTo(len(names)) {
		cp.errorf(p, "number of variables doesn't match that of values")
	}
	_, more := vop.tr.count()
	if more {
		// TODO Try to check soundness to some extent
		return
	}
	for i, name := range names {
		tval := vop.tr[i].t
		tvar := cp.ResolveVar(splitQualifiedName(name))
		if !mayAssign(tvar, tval) {
			cp.errorf(values[i].Pos, "type mismatch: assigning %#v value to %#v variable", tval, tvar)
		}
	}
}

// ensure that a CompoundNode contains exactly one PrimaryNode.
func ensurePrimary(cp *Compiler, cn *parse.CompoundNode, msg string) *parse.PrimaryNode {
	if len(cn.Nodes) != 1 || cn.Nodes[0].Right != nil {
		cp.errorf(cn.Pos, msg)
	}
	return cn.Nodes[0].Left
}

// ensureVariableOrStringPrimary ensures that a CompoundNode contains exactly
// one PrimaryNode of type VariablePrimary or StringPrimary.
func ensureVariableOrStringPrimary(cp *Compiler, cn *parse.CompoundNode, msg string) (*parse.PrimaryNode, string) {
	pn := ensurePrimary(cp, cn, msg)
	switch pn.Typ {
	case parse.VariablePrimary, parse.StringPrimary:
		return pn, pn.Node.(*parse.StringNode).Text
	default:
		cp.errorf(cn.Pos, msg)
		return nil, ""
	}
}

// ensureVariablePrimary ensures that a CompoundNode contains exactly one
// PrimaryNode of type VariablePrimary.
func ensureVariablePrimary(cp *Compiler, cn *parse.CompoundNode, msg string) (*parse.PrimaryNode, string) {
	pn, text := ensureVariableOrStringPrimary(cp, cn, msg)
	if pn.Typ != parse.VariablePrimary {
		cp.errorf(pn.Pos, msg)
	}
	return pn, text
}

// ensureStartWithVariabl ensures the first compound of the form is a
// VariablePrimary. This is merely for better error messages; No actual
// processing is done.
func ensureStartWithVariable(cp *Compiler, fn *parse.FormNode, form string) {
	if len(fn.Args.Nodes) == 0 {
		cp.errorf(fn.Pos, "expect variable after %s", form)
	}
	ensureVariablePrimary(cp, fn.Args.Nodes[0], "expect variable")
}

// VarForm    = 'var' { VarGroup } [ '=' Compound ]
// VarGroup   = { VariablePrimary } [ StringPrimary ]
//
// Variables in the same VarGroup has the type specified by the StringPrimary.
// Only in the last VarGroup the StringPrimary may be omitted, in which case it
// defaults to "any". For instance,
//
// var $u $v Type1 $x $y Type2 $z = a b c d e
//
// gives $u and $v type Type1, $x $y type Type2 and $z type Any and
// assigns them the values a, b, c, d, e respectively.
func compileVar(cp *Compiler, fn *parse.FormNode) exitusOp {
	var (
		names  []string
		types  []Type
		values []*parse.CompoundNode
	)

	ensureStartWithVariable(cp, fn, "var")

	for i, cn := range fn.Args.Nodes {
		expect := "expect variable, type or equal sign"
		pn, text := ensureVariableOrStringPrimary(cp, cn, expect)
		if pn.Typ == parse.VariablePrimary {
			names = append(names, text)
		} else {
			if text == "=" {
				values = fn.Args.Nodes[i+1:]
				break
			} else {
				if t, ok := typenames[text]; !ok {
					cp.errorf(pn.Pos, "%v is not a valid type name", text)
				} else {
					if len(names) == len(types) {
						cp.errorf(pn.Pos, "duplicate type")
					}
					for i := len(types); i < len(names); i++ {
						types = append(types, t)
					}
				}
			}
		}
	}

	for i := len(types); i < len(names); i++ {
		types = append(types, AnyType{})
	}

	for i, name := range names {
		cp.pushVar(name, types[i])
	}

	var vop valuesOp
	if values != nil {
		vop = cp.compileCompounds(values)
		checkSetType(cp, names, values, vop, fn.Pos)
	}
	return func(ev *Evaluator) Exitus {
		for i, name := range names {
			ev.local[name] = newInternalVariable(types[i].Default(), types[i])
		}
		if vop.f != nil {
			return doSet(ev, names, vop.f(ev))
		}
		return success
	}
}

// SetForm = 'set' { VariablePrimary } '=' { Compound }
func compileSet(cp *Compiler, fn *parse.FormNode) exitusOp {
	var (
		names  []string
		values []*parse.CompoundNode
	)

	ensureStartWithVariable(cp, fn, "set")

	for i, cn := range fn.Args.Nodes {
		expect := "expect variable or equal sign"
		pn, text := ensureVariableOrStringPrimary(cp, cn, expect)
		if pn.Typ == parse.VariablePrimary {
			ns, name := splitQualifiedName(text)
			cp.mustResolveVar(ns, name, cn.Pos)
			names = append(names, text)
		} else {
			if text != "=" {
				cp.errorf(pn.Pos, expect)
			}
			values = fn.Args.Nodes[i+1:]
			break
		}
	}

	var vop valuesOp
	vop = cp.compileCompounds(values)
	checkSetType(cp, names, values, vop, fn.Pos)

	return func(ev *Evaluator) Exitus {
		return doSet(ev, names, vop.f(ev))
	}
}

var (
	arityMismatch Exitus = newFailure("arity mismatch")
	typeMismatch  Exitus = newFailure("type mismatch")
)

func doSet(ev *Evaluator, names []string, values []Value) Exitus {
	// TODO Support assignment of mismatched arity in some restricted way -
	// "optional" and "rest" arguments and the like
	if len(names) != len(values) {
		return arityMismatch
	}

	for i, name := range names {
		// TODO Prevent overriding builtin variables e.g. $pid $env
		variable := ev.ResolveVar(splitQualifiedName(name))
		if variable == nil {
			return newFailure(fmt.Sprintf("variable $%s not found; the compiler has a bug", name))
		}
		tvar := variable.StaticType()
		tval := values[i].Type()
		if !mayAssign(tvar, tval) {
			return typeMismatch
		}
		variable.Set(values[i])
	}

	return success
}

// DelForm = 'del' { VariablePrimary }
func compileDel(cp *Compiler, fn *parse.FormNode) exitusOp {
	// Do conventional compiling of all compound expressions, including
	// ensuring that variables can be resolved
	var names []string
	for _, cn := range fn.Args.Nodes {
		_, qname := ensureVariablePrimary(cp, cn, "expect variable")
		ns, name := splitQualifiedName(qname)
		if ns != "" && ns != "local" {
			cp.errorf(cn.Pos, "can only delete a variable on local scope")
		}
		if cp.resolveVarOnThisScope(name) == nil {
			cp.errorf(cn.Pos, "variable $%s not found on current local scope", name)
		}

		cp.popVar(name)
		names = append(names, name)
	}
	return func(ev *Evaluator) Exitus {
		for _, name := range names {
			delete(ev.local, name)
		}
		return success
	}
}

// FnForm = 'fn' StringPrimary { VariablePrimary } ClosurePrimary
//
// fn defines a function. This isn't strictly needed, since user-defined
// functions are just variables. The following two lines should be exactly
// equivalent:
//
// fn f $a $b { put (* $a $b) (/ $a *b) }
// var $fn-f = { |$a $b| put (* $a $b) (/ $a $b) }
func compileFn(cp *Compiler, fn *parse.FormNode) exitusOp {
	if len(fn.Args.Nodes) == 0 {
		cp.errorf(fn.Pos, "expect function name after fn")
	}
	pn, fnName := ensureVariableOrStringPrimary(cp, fn.Args.Nodes[0], "expect string literal")
	varName := "fn-" + fnName
	if cp.resolveVarOnThisScope(varName) != nil {
		cp.errorf(pn.Pos, "redefinition of function %s", fnName)
	}

	var closureNode *parse.ClosureNode
	var argNames []*parse.CompoundNode

	for i, cn := range fn.Args.Nodes[1:] {
		expect := "expect variable or closure"
		pn := ensurePrimary(cp, cn, expect)
		switch pn.Typ {
		case parse.ClosurePrimary:
			if i+2 != len(fn.Args.Nodes) {
				cp.errorf(fn.Args.Nodes[i+2].Pos, "garbage after closure literal")
			}
			closureNode = pn.Node.(*parse.ClosureNode)
			break
		case parse.VariablePrimary:
			argNames = append(argNames, cn)
		default:
			cp.errorf(pn.Pos, expect)
		}
	}

	if len(argNames) > 0 {
		closureNode = &parse.ClosureNode{
			closureNode.Pos,
			&parse.SpacedNode{argNames[0].Pos, argNames},
			closureNode.Chunk,
		}
	}

	op := cp.compileClosure(closureNode)

	cp.pushVar(varName, ClosureType{})

	return func(ev *Evaluator) Exitus {
		ev.local[varName] = newInternalVariable(op.f(ev)[0], ClosureType{})
		return success
	}
}

func compileStaticTypeof(cp *Compiler, fn *parse.FormNode) exitusOp {
	// Do conventional compiling of all compounds, only keeping the static type
	// information
	var trs []typeRun
	for _, cn := range fn.Args.Nodes {
		trs = append(trs, cp.compileCompound(cn).tr)
	}
	return func(ev *Evaluator) Exitus {
		out := ev.ports[1].ch
		for _, tr := range trs {
			out <- NewString(tr.String())
		}
		return success
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 The Neugram Authors. All rights reserved.
// See the LICENSE file for rights to use this source code.

package shell

import (
	"fmt"
	"os/user"
	"path/filepath"
	"strings"
	"unicode"
)

func expansion(argv1 []string, params Params) ([]string, error) {
	var err error
	var argv2 []string
	for _, expander := range expanders {
		for _, arg := range argv1 {
			argv2, err = expander(argv2, arg, params)
			if err != nil {
				return nil, err
			}
		}
		argv1 = argv2
		argv2 = nil
	}

	return argv1, nil
}

var expanders = []func([]string, string, Params) ([]string, error){
	braceExpand,
	tildeExpand,
	paramExpand,
	pathsExpand,
}

// brace expansion (for example: "c{d,e}" becomes "cd ce")
func braceExpand(src []string, arg string, _ Params) (res []string, err error) {
	res = src
	i1 := indexUnquoted(arg, '{')
	if i1 == -1 {
		return append(res, arg), nil
	}
	i2 := indexUnquoted(arg[i1:], '}')
	if i2 == -1 {
		return append(res, arg), nil
	} else {
		prefix, suffix := arg[:i1], arg[i1+i2+1:]
		arg = arg[i1+1 : i1+i2]
		for len(arg) > 0 {
			c := indexUnquoted(arg, ',')
			if c == -1 {
				res, _ = braceExpand(res, prefix+arg+suffix, nil)
				break
			}
			res, _ = braceExpand(res, prefix+arg[:c]+suffix, nil)
			arg = arg[c+1:]
		}
	}
	return res, nil
}

// tilde expansion (important: cd ~, cd ~/foo, less so: cd ~user1)
func tildeExpand(src []string, arg string, params Params) (res []string, err error) {
	res = src
	if !strings.HasPrefix(arg, "~") {
		return append(res, arg), nil
	}
	name := arg[1:]
	for i, r := range name {
		if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
			name = name[:i]
			break
		}
	}
	var u *user.User
	if len(name) == 0 {
		u, err = user.Current()
	} else {
		u, err = user.Lookup(name)
	}
	if err != nil {
		if _, ok := err.(user.UnknownUserError); ok {
			return append(res, arg), nil
		}
		return nil, fmt.Errorf("expanding %s: %v", arg, err)
	}
	return append(src, u.HomeDir+arg[1+len(name):]), nil
}

// param expansion ($x, $PATH, ${x}, long tail of questionable sh features)
func paramExpand(src []string, arg string, params Params) (res []string, err error) {
	// TODO
	return append(src, arg), nil
}

// paths expansion (*, ?, [)
func pathsExpand(src []string, arg string, params Params) (res []string, err error) {
	res = src
	if !strings.ContainsAny(arg, "*?[") {
		return append(res, arg), nil
	}
	// TODO to support interior quoting (like ab"*".c) this will need a rewrite.
	matches, err := filepath.Glob(arg)
	if err != nil {
		return nil, err
	}
	return append(res, matches...), nil
}

// indexUnquoted returns the index of the first unquoted Unicode code
// point r, or -1. A code point r is quoted if it is directly preceded
// by a '\' or enclosed in "" or ''.
func indexUnquoted(s string, r rune) int {
	prevSlash := false
	inBlock := rune(-1)
	for i, v := range s {
		if inBlock != -1 {
			if v == inBlock {
				inBlock = -1
			}
			continue
		}

		if !prevSlash {
			switch v {
			case r:
				return i
			case '\'', '"':
				inBlock = v
			}
		}

		prevSlash = v == '\\'
	}

	return -1
}
<commit_msg>shell: param expansion<commit_after>// Copyright 2015 The Neugram Authors. All rights reserved.
// See the LICENSE file for rights to use this source code.

package shell

import (
	"fmt"
	"os/user"
	"path/filepath"
	"strings"
	"unicode"
)

func expansion(argv1 []string, params Params) ([]string, error) {
	var err error
	var argv2 []string
	for _, expander := range expanders {
		for _, arg := range argv1 {
			argv2, err = expander(argv2, arg, params)
			if err != nil {
				return nil, err
			}
		}
		argv1 = argv2
		argv2 = nil
	}

	return argv1, nil
}

var expanders = []func([]string, string, Params) ([]string, error){
	braceExpand,
	tildeExpand,
	paramExpand,
	pathsExpand,
}

// brace expansion (for example: "c{d,e}" becomes "cd ce")
func braceExpand(src []string, arg string, _ Params) (res []string, err error) {
	res = src
	i1 := indexUnquoted(arg, '{')
	if i1 == -1 {
		return append(res, arg), nil
	}
	i2 := indexUnquoted(arg[i1:], '}')
	if i2 == -1 {
		return append(res, arg), nil
	} else {
		prefix, suffix := arg[:i1], arg[i1+i2+1:]
		arg = arg[i1+1 : i1+i2]
		for len(arg) > 0 {
			c := indexUnquoted(arg, ',')
			if c == -1 {
				res, _ = braceExpand(res, prefix+arg+suffix, nil)
				break
			}
			res, _ = braceExpand(res, prefix+arg[:c]+suffix, nil)
			arg = arg[c+1:]
		}
	}
	return res, nil
}

// tilde expansion (important: cd ~, cd ~/foo, less so: cd ~user1)
func tildeExpand(src []string, arg string, params Params) (res []string, err error) {
	res = src
	if !strings.HasPrefix(arg, "~") {
		return append(res, arg), nil
	}
	name := arg[1:]
	for i, r := range name {
		if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
			name = name[:i]
			break
		}
	}
	var u *user.User
	if len(name) == 0 {
		u, err = user.Current()
	} else {
		u, err = user.Lookup(name)
	}
	if err != nil {
		if _, ok := err.(user.UnknownUserError); ok {
			return append(res, arg), nil
		}
		return nil, fmt.Errorf("expanding %s: %v", arg, err)
	}
	return append(src, u.HomeDir+arg[1+len(name):]), nil
}

// param expansion ($x, $PATH, ${x}, long tail of questionable sh features)
// TODO also expand env
func paramExpand(src []string, arg string, params Params) (res []string, err error) {
	res = src
	for {
		i1 := indexParam(arg)
		if i1 == -1 {
			break
		}
		var r rune
		i2 := -1
		for i2, r = range arg[i1+1:] {
			if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
				i2--
				break
			}
		}
		if i2 == -1 {
			return nil, fmt.Errorf("invalid $ parameter: %q", arg)
		}
		end := i1 + 1 + i2 + 1
		name := arg[i1+1 : end]
		val := params.Get(name)
		arg = arg[:i1] + val + arg[end:]
	}
	return append(res, arg), nil
}

// paths expansion (*, ?, [)
func pathsExpand(src []string, arg string, params Params) (res []string, err error) {
	res = src
	if !strings.ContainsAny(arg, "*?[") {
		return append(res, arg), nil
	}
	// TODO to support interior quoting (like ab"*".c) this will need a rewrite.
	matches, err := filepath.Glob(arg)
	if err != nil {
		return nil, err
	}
	return append(res, matches...), nil
}

// indexUnquoted returns the index of the first unquoted Unicode code
// point r, or -1. A code point r is quoted if it is directly preceded
// by a '\' or enclosed in "" or ''.
func indexUnquoted(s string, r rune) int {
	prevSlash := false
	inBlock := rune(-1)
	for i, v := range s {
		if inBlock != -1 {
			if v == inBlock {
				inBlock = -1
			}
			continue
		}

		if !prevSlash {
			switch v {
			case r:
				return i
			case '\'', '"':
				inBlock = v
			}
		}

		prevSlash = v == '\\'
	}

	return -1
}

// indexParam returns the index of the first $ not quoted with '' or \, or -1.
func indexParam(s string) int {
	prevSlash := false
	inQuote := false
	for i, v := range s {
		if inQuote {
			if v == '\'' {
				inQuote = false
			}
			continue
		}

		if !prevSlash {
			switch v {
			case '$':
				return i
			case '\'':
				inQuote = true
			}
		}

		prevSlash = v == '\\'
	}

	return -1
}
<|endoftext|>


<commit_before>package zoom_test

import (
	"encoding/json"
	"log"
	"os"

	"github.com/himalayan-institute/zoom-lib-golang"
)

// ExampleWebinar contains examples for the /webinar endpoints
func ExampleWebinar() {
	var (
		apiKey          = os.Getenv("ZOOM_API_KEY")
		apiSecret       = os.Getenv("ZOOM_API_SECRET")
		email           = os.Getenv("ZOOM_EXAMPLE_EMAIL")
		registrantEmail = os.Getenv("ZOOM_EXAMPLE_REGISTRANT_EMAIL")
	)

	zoom.APIKey = apiKey
	zoom.APISecret = apiSecret
	zoom.Debug = true

	user, err := zoom.GetUserByEmail(zoom.GetUserByEmailOptions{
		Email: email,
	})
	if err != nil {
		log.Fatalf("got error listing users: %+v\n", err)
	}

	fifty := int(50)
	webinars, err := zoom.ListWebinars(zoom.ListWebinarsOptions{
		HostID:   user.ID,
		PageSize: &fifty,
	})
	if err != nil {
		log.Fatalf("got error listing webinars: %+v\n", err)
	}

	log.Printf("Got open webinars: %+v\n", webinars)

	webinars, err = zoom.ListRegistrationWebinars(zoom.ListWebinarsOptions{
		HostID:   user.ID,
		PageSize: &fifty,
	})
	if err != nil {
		log.Fatalf("got error listing webinars: %+v\n", err)
	}

	log.Printf("Got registration webinars: %+v\n", webinars)

	webinar, err := zoom.GetWebinarInfo(zoom.GetWebinarInfoOptions{
		HostID: user.ID,
		ID:     webinars.Webinars[0].ID,
	})

	if err != nil {
		log.Fatalf("got error getting single webinar: %+v\n", err)
	}

	log.Printf("Got single webinars: %+v\n", webinar)

	log.Printf("created at: %s\n", webinar.CreatedAt)
	log.Printf("first occurence start: %s\n", webinar.Occurrences[0].StartTime)

	customQs := []zoom.CustomQuestion{
		{
			Title: "asdf foo bar",
			Value: "example custom question answer",
		},
	}

	b, err := json.Marshal(customQs)
	if err != nil {
		log.Fatalf("error marshaling custom Qs to JSON: %s\n", err)
	}

	registrantInfo := zoom.RegisterForWebinarOptions{
		ID:              webinar.ID,
		Email:           registrantEmail,
		FirstName:       "Foo",
		LastName:        "Bar",
		CustomQuestions: string(b),
	}

	registrant, err := zoom.RegisterForWebinar(registrantInfo)
	if err != nil {
		log.Fatalf("got error registering a user for webinar %d: %+v\n", webinar.ID, err)
	}

	log.Printf("Got registrant: %+v\n", registrant)

	getRegistrationOpts := zoom.GetWebinarRegistrationInfoOptions{
		WebinarID: webinar.ID,
		HostID:    user.ID,
	}

	registrationInfo, err := zoom.GetWebinarRegistrationInfo(getRegistrationOpts)
	if err != nil {
		log.Fatalf("got error getting registration info for webinar %d: %+v\n", webinar.ID, err)
	}

	log.Printf("Got registration information: %+v\n", registrationInfo)
}
<commit_msg>Fix misspelling of occurrence<commit_after>package zoom_test

import (
	"encoding/json"
	"log"
	"os"

	"github.com/himalayan-institute/zoom-lib-golang"
)

// ExampleWebinar contains examples for the /webinar endpoints
func ExampleWebinar() {
	var (
		apiKey          = os.Getenv("ZOOM_API_KEY")
		apiSecret       = os.Getenv("ZOOM_API_SECRET")
		email           = os.Getenv("ZOOM_EXAMPLE_EMAIL")
		registrantEmail = os.Getenv("ZOOM_EXAMPLE_REGISTRANT_EMAIL")
	)

	zoom.APIKey = apiKey
	zoom.APISecret = apiSecret
	zoom.Debug = true

	user, err := zoom.GetUserByEmail(zoom.GetUserByEmailOptions{
		Email: email,
	})
	if err != nil {
		log.Fatalf("got error listing users: %+v\n", err)
	}

	fifty := int(50)
	webinars, err := zoom.ListWebinars(zoom.ListWebinarsOptions{
		HostID:   user.ID,
		PageSize: &fifty,
	})
	if err != nil {
		log.Fatalf("got error listing webinars: %+v\n", err)
	}

	log.Printf("Got open webinars: %+v\n", webinars)

	webinars, err = zoom.ListRegistrationWebinars(zoom.ListWebinarsOptions{
		HostID:   user.ID,
		PageSize: &fifty,
	})
	if err != nil {
		log.Fatalf("got error listing webinars: %+v\n", err)
	}

	log.Printf("Got registration webinars: %+v\n", webinars)

	webinar, err := zoom.GetWebinarInfo(zoom.GetWebinarInfoOptions{
		HostID: user.ID,
		ID:     webinars.Webinars[0].ID,
	})

	if err != nil {
		log.Fatalf("got error getting single webinar: %+v\n", err)
	}

	log.Printf("Got single webinars: %+v\n", webinar)

	log.Printf("created at: %s\n", webinar.CreatedAt)
	log.Printf("first occurrence start: %s\n", webinar.Occurrences[0].StartTime)

	customQs := []zoom.CustomQuestion{
		{
			Title: "asdf foo bar",
			Value: "example custom question answer",
		},
	}

	b, err := json.Marshal(customQs)
	if err != nil {
		log.Fatalf("error marshaling custom Qs to JSON: %s\n", err)
	}

	registrantInfo := zoom.RegisterForWebinarOptions{
		ID:              webinar.ID,
		Email:           registrantEmail,
		FirstName:       "Foo",
		LastName:        "Bar",
		CustomQuestions: string(b),
	}

	registrant, err := zoom.RegisterForWebinar(registrantInfo)
	if err != nil {
		log.Fatalf("got error registering a user for webinar %d: %+v\n", webinar.ID, err)
	}

	log.Printf("Got registrant: %+v\n", registrant)

	getRegistrationOpts := zoom.GetWebinarRegistrationInfoOptions{
		WebinarID: webinar.ID,
		HostID:    user.ID,
	}

	registrationInfo, err := zoom.GetWebinarRegistrationInfo(getRegistrationOpts)
	if err != nil {
		log.Fatalf("got error getting registration info for webinar %d: %+v\n", webinar.ID, err)
	}

	log.Printf("Got registration information: %+v\n", registrationInfo)
}
<|endoftext|>


<commit_before>package docker_loader

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	dClient "github.com/docker/docker/client"
	"github.com/karimra/gnmic/collector"
	"github.com/karimra/gnmic/loaders"
	"github.com/mitchellh/mapstructure"
)

const (
	loggingPrefix = "[docker_loader] "
	watchInterval = 30 * time.Second
	loaderName    = "docker"
)

func init() {
	loaders.Register(loaderName, func() loaders.TargetLoader {
		return &dockerLoader{
			cfg:         new(cfg),
			wg:          new(sync.WaitGroup),
			lastTargets: make(map[string]*collector.TargetConfig),
			logger:      log.New(ioutil.Discard, loggingPrefix, log.LstdFlags|log.Lmicroseconds),
		}
	})
}

type dockerLoader struct {
	cfg         *cfg
	client      *dClient.Client
	wg          *sync.WaitGroup
	lastTargets map[string]*collector.TargetConfig
	logger      *log.Logger
	fl          []*targetFilterComp
}

type targetFilterComp struct {
	fl   []filters.Args
	nt   filters.Args
	port string
	cfg  map[string]interface{}
}

type cfg struct {
	Address  string          `json:"address,omitempty" mapstructure:"address,omitempty"`
	Interval time.Duration   `json:"interval,omitempty" mapstructure:"interval,omitempty"`
	Timeout  time.Duration   `json:"timeout,omitempty" mapstructure:"timeout,omitempty"`
	Filters  []*targetFilter `json:"filters,omitempty" mapstructure:"filters,omitempty"`
	Debug    bool            `json:"debug,omitempty" mapstructure:"debug,omitempty"`
}

type targetFilter struct {
	Containers []map[string]string    `json:"containers,omitempty" mapstructure:"containers,omitempty"`
	Network    map[string]string      `json:"network,omitempty" mapstructure:"network,omitempty"`
	Port       string                 `json:"port,omitempty" mapstructure:"port,omitempty"`
	Config     map[string]interface{} `json:"config,omitempty" mapstructure:"config,omitempty"`
}

func (d *dockerLoader) Init(ctx context.Context, cfg map[string]interface{}, logger *log.Logger) error {
	err := loaders.DecodeConfig(cfg, d.cfg)
	if err != nil {
		return err
	}
	if d.cfg.Interval <= 0 {
		d.cfg.Interval = watchInterval
	}
	if d.cfg.Timeout <= 0 || d.cfg.Timeout >= d.cfg.Interval {
		d.cfg.Timeout = d.cfg.Interval / 2
	}
	if len(d.cfg.Filters) == 0 {
		d.cfg.Filters = []*targetFilter{
			{
				Containers: []map[string]string{
					{"status": "running"},
				},
			},
		}
	}
	d.fl = make([]*targetFilterComp, 0, len(d.cfg.Filters))
	for _, fm := range d.cfg.Filters {
		// network filter
		nflt := filters.NewArgs()
		for k, v := range fm.Network {
			nflt.Add(k, v)
		}
		// container filters
		cflt := make([]filters.Args, 0, len(fm.Containers))
		for _, sfm := range fm.Containers {
			flt := filters.NewArgs(filters.KeyValuePair{
				Key:   "status",
				Value: "running",
			})
			for k, v := range sfm {
				flt.Add(k, v)
			}
			cflt = append(cflt, flt)
		}
		// target filters
		d.fl = append(d.fl, &targetFilterComp{
			fl:   cflt,
			nt:   nflt,
			port: fm.Port,
			cfg:  fm.Config,
		})
	}

	if logger != nil {
		d.logger.SetOutput(logger.Writer())
		d.logger.SetFlags(logger.Flags())
	}

	d.client, err = d.createDockerClient()
	if err != nil {
		return err
	}
	ping, err := d.client.Ping(ctx)
	if err != nil {
		return err
	}

	d.logger.Printf("docker daemon: %+v", ping)
	d.logger.Printf("initialized loader type %q: %s", loaderName, d)
	return nil
}

func (d *dockerLoader) createDockerClient() (*dClient.Client, error) {
	var opts []dClient.Opt
	if d.cfg.Address == "" {
		opts = []dClient.Opt{
			dClient.FromEnv,
			dClient.WithTimeout(d.cfg.Timeout),
		}
	} else {
		opts = []dClient.Opt{
			dClient.WithAPIVersionNegotiation(),
			dClient.WithHost(d.cfg.Address),
			dClient.WithTimeout(d.cfg.Timeout),
		}
	}
	return dClient.NewClientWithOpts(opts...)
}

func (d *dockerLoader) Start(ctx context.Context) chan *loaders.TargetOperation {
	opChan := make(chan *loaders.TargetOperation)
	go func() {
		defer close(opChan)
		for {
			select {
			case <-ctx.Done():
				return
			default:
				d.logger.Printf("querying %q targets", loaderName)
				readTargets, err := d.getTargets(ctx)
				if err != nil {
					d.logger.Printf("failed to read targets from docker daemon: %v", err)
					time.Sleep(d.cfg.Interval)
					continue
				}
				if d.cfg.Debug {
					d.logger.Printf("docker loader discovered %d target(s)", len(readTargets))
				}
				select {
				case <-ctx.Done():
					return
				case opChan <- d.diff(readTargets):
					time.Sleep(d.cfg.Interval)
				}
			}
		}
	}()
	return opChan
}

func (d *dockerLoader) getTargets(ctx context.Context) (map[string]*collector.TargetConfig, error) {
	d.wg = new(sync.WaitGroup)
	d.wg.Add(len(d.fl))
	readTargets := make(map[string]*collector.TargetConfig)
	m := new(sync.Mutex)
	errChan := make(chan error, len(d.fl))
	for _, targetFilter := range d.fl {
		go func(fl *targetFilterComp) {
			defer d.wg.Done()
			// get networks
			nrs, err := d.client.NetworkList(ctx, types.NetworkListOptions{
				Filters: fl.nt,
			})
			if err != nil {
				errChan <- fmt.Errorf("failed getting networks list using filter %+v: %v", fl.nt, err)
				return
			}
			// get containers for each defined filter
			for _, cfl := range fl.fl {
				conts, err := d.client.ContainerList(ctx, types.ContainerListOptions{
					Filters: cfl,
				})
				if err != nil {
					errChan <- fmt.Errorf("failed getting containers list using filter %+v: %v", cfl, err)
					return
				}
				for _, cont := range conts {
					d.logger.Printf("container %q, names: %v, labels: %v", cont.ID, cont.Names, cont.Labels)
					tc := new(collector.TargetConfig)
					if fl.cfg != nil {
						err = mapstructure.Decode(fl.cfg, tc)
						if err != nil {
							d.logger.Printf("failed to decode config map: %v", err)
						}
						if d.cfg.Debug {
							d.logger.Printf("target config before adding name and address: %v", tc)
						}
					}
					// set target name
					tc.Name = cont.ID
					if len(cont.Names) > 0 {
						tc.Name = strings.TrimLeft(cont.Names[0], "/")
					}
					if d.cfg.Debug {
						d.logger.Printf("filter %v returned container %v", cfl, tc.Name)
					}
					switch strings.ToLower(cont.HostConfig.NetworkMode) {
					case "host":
						if d.cfg.Address == "" || strings.HasPrefix(d.cfg.Address, "unix://") {
							tc.Address = "localhost"
						} else {
							tc.Address, _, err = net.SplitHostPort(d.cfg.Address)
							if err != nil {
								errChan <- err
								continue
							}
						}
						if fl.port != "" {
							if !strings.Contains(fl.port, "=") {
								tc.Address = fmt.Sprintf("%s:%s", tc.Address, fl.port)
							} else {
								portLabel := strings.Replace(fl.port, "label=", "", 1)
								if p, ok := cont.Labels[portLabel]; ok {
									tc.Address = fmt.Sprintf("%s:%s", tc.Address, p)
								}
							}
						}
					default:
						if strings.HasPrefix(d.cfg.Address, "unix:///") {
							for _, nr := range nrs {
								if n, ok := cont.NetworkSettings.Networks[nr.Name]; ok {
									if n.IPAddress != "" {
										tc.Address = n.IPAddress
										break
									}
									tc.Address = n.GlobalIPv6Address
									break
								}
							}
							if tc.Address == "" {
								d.logger.Printf("%q no address found", tc.Name)
								continue
							}
							if fl.port != "" {
								if !strings.Contains(fl.port, "=") {
									tc.Address = fmt.Sprintf("%s:%s", tc.Address, fl.port)
								} else {
									portLabel := strings.Replace(fl.port, "label=", "", 1)
									if p, ok := cont.Labels[portLabel]; ok {
										tc.Address = fmt.Sprintf("%s:%s", tc.Address, p)
									}
								}
							}
						} else {
							// get port from config/label
							port := getPortNumber(cont.Labels, fl.port)
							// check if port is exposed, find the public port and build the target address
							for _, p := range cont.Ports {
								// the container private port matches the port from the docker label
								if p.PrivatePort == port && p.Type == "tcp" {
									ipAddr := p.IP
									if ipAddr == "0.0.0.0" || ipAddr == "::" {
										if d.cfg.Address == "" {
											// if docker daemon is empty use localhost as target address
											ipAddr = "localhost"
										} else {
											// derive target address from daemon address if not empty
											u, err := url.Parse(d.cfg.Address)
											if err != nil {
												d.logger.Printf("failed to parse docker daemon address")
												continue
											}
											ipAddr, _, _ = net.SplitHostPort(u.Host)
										}

									}
									tc.Address = fmt.Sprintf("%s:%d", ipAddr, p.PublicPort)
								}
							}
							// if an address was not found using the exposed ports
							// select the bridge address, and use the port from label if not zero
							if tc.Address == "" {
								for _, nr := range nrs {
									if n, ok := cont.NetworkSettings.Networks[nr.Name]; ok {
										if n.IPAddress != "" {
											tc.Address = n.IPAddress
											break
										}
										tc.Address = n.GlobalIPv6Address
										break
									}
								}
								if tc.Address == "" {
									d.logger.Printf("%q no address found", tc.Name)
									continue
								}
								if port != 0 {
									tc.Address = fmt.Sprintf("%s:%d", tc.Address, port)
								}
							}
						}
					}
					//
					if d.cfg.Debug {
						d.logger.Printf("discovered target config %s with filter: %v", tc, cfl)
					}
					m.Lock()
					readTargets[tc.Name] = tc
					m.Unlock()
				}
			}
		}(targetFilter)
	}
	var errors = make([]error, 0)
	go func() {
		for err := range errChan {
			errors = append(errors, err)
		}
	}()

	d.wg.Wait()
	close(errChan)
	if len(errors) > 0 {
		for _, err := range errors {
			d.logger.Printf("%v", err)
		}
		return nil, fmt.Errorf("there was %d error(s)", len(errors))
	}
	return readTargets, nil
}

func (d *dockerLoader) diff(m map[string]*collector.TargetConfig) *loaders.TargetOperation {
	result := loaders.Diff(d.lastTargets, m)
	for _, t := range result.Add {
		if _, ok := d.lastTargets[t.Name]; !ok {
			d.lastTargets[t.Name] = t
		}
	}
	for _, n := range result.Del {
		delete(d.lastTargets, n)
	}
	if d.cfg.Debug {
		b, err := json.MarshalIndent(result, "", "  ")
		if err != nil {
			d.logger.Printf("discovery diff result: %v", result)
		} else {
			d.logger.Printf("discovery diff result:\n%s", string(b))
		}
	}
	return result
}

func (d *dockerLoader) String() string {
	b, err := json.Marshal(d.cfg)
	if err != nil {
		return fmt.Sprintf("%+v", d.cfg)
	}
	return string(b)
}

/// helpers

func getPortNumber(labels map[string]string, p string) uint16 {
	var port uint16
	if p != "" {
		if !strings.Contains(p, "=") {
			p, _ := strconv.Atoi(p)
			port = uint16(p)
		} else {
			s := labels[strings.Replace(p, "label=", "", 1)]
			p, _ := strconv.Atoi(s)
			port = uint16(p)
		}
	}
	return port
}
<commit_msg>cleanup<commit_after>package docker_loader

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	dClient "github.com/docker/docker/client"
	"github.com/karimra/gnmic/collector"
	"github.com/karimra/gnmic/loaders"
	"github.com/mitchellh/mapstructure"
)

const (
	loggingPrefix = "[docker_loader] "
	watchInterval = 30 * time.Second
	loaderName    = "docker"
)

func init() {
	loaders.Register(loaderName, func() loaders.TargetLoader {
		return &dockerLoader{
			cfg:         new(cfg),
			wg:          new(sync.WaitGroup),
			lastTargets: make(map[string]*collector.TargetConfig),
			logger:      log.New(ioutil.Discard, loggingPrefix, log.LstdFlags|log.Lmicroseconds),
		}
	})
}

type dockerLoader struct {
	cfg         *cfg
	client      *dClient.Client
	wg          *sync.WaitGroup
	lastTargets map[string]*collector.TargetConfig
	logger      *log.Logger
	fl          []*targetFilterComp
}

type targetFilterComp struct {
	fl   []filters.Args
	nt   filters.Args
	port string
	cfg  map[string]interface{}
}

type cfg struct {
	Address  string          `json:"address,omitempty" mapstructure:"address,omitempty"`
	Interval time.Duration   `json:"interval,omitempty" mapstructure:"interval,omitempty"`
	Timeout  time.Duration   `json:"timeout,omitempty" mapstructure:"timeout,omitempty"`
	Filters  []*targetFilter `json:"filters,omitempty" mapstructure:"filters,omitempty"`
	Debug    bool            `json:"debug,omitempty" mapstructure:"debug,omitempty"`
}

type targetFilter struct {
	Containers []map[string]string    `json:"containers,omitempty" mapstructure:"containers,omitempty"`
	Network    map[string]string      `json:"network,omitempty" mapstructure:"network,omitempty"`
	Port       string                 `json:"port,omitempty" mapstructure:"port,omitempty"`
	Config     map[string]interface{} `json:"config,omitempty" mapstructure:"config,omitempty"`
}

func (d *dockerLoader) Init(ctx context.Context, cfg map[string]interface{}, logger *log.Logger) error {
	err := loaders.DecodeConfig(cfg, d.cfg)
	if err != nil {
		return err
	}
	d.setDefaults()

	d.fl = make([]*targetFilterComp, 0, len(d.cfg.Filters))
	for _, fm := range d.cfg.Filters {
		// network filter
		nflt := filters.NewArgs()
		for k, v := range fm.Network {
			nflt.Add(k, v)
		}
		// container filters
		cflt := make([]filters.Args, 0, len(fm.Containers))
		for _, sfm := range fm.Containers {
			flt := filters.NewArgs(filters.KeyValuePair{
				Key:   "status",
				Value: "running",
			})
			for k, v := range sfm {
				flt.Add(k, v)
			}
			cflt = append(cflt, flt)
		}
		// target filters
		d.fl = append(d.fl, &targetFilterComp{
			fl:   cflt,
			nt:   nflt,
			port: fm.Port,
			cfg:  fm.Config,
		})
	}

	if logger != nil {
		d.logger.SetOutput(logger.Writer())
		d.logger.SetFlags(logger.Flags())
	}

	d.client, err = d.createDockerClient()
	if err != nil {
		return err
	}

	ping, err := d.client.Ping(ctx)
	if err != nil {
		return err
	}

	d.logger.Printf("connected to docker daemon: %+v", ping)
	d.logger.Printf("initialized loader type %q: %s", loaderName, d)
	return nil
}

func (d *dockerLoader) setDefaults() {
	if d.cfg.Interval <= 0 {
		d.cfg.Interval = watchInterval
	}
	if d.cfg.Timeout <= 0 || d.cfg.Timeout >= d.cfg.Interval {
		d.cfg.Timeout = d.cfg.Interval / 2
	}
	if len(d.cfg.Filters) == 0 {
		d.cfg.Filters = []*targetFilter{
			{
				Containers: []map[string]string{
					{"status": "running"},
				},
			},
		}
	}
}

func (d *dockerLoader) createDockerClient() (*dClient.Client, error) {
	var opts []dClient.Opt
	if d.cfg.Address == "" {
		opts = []dClient.Opt{
			dClient.FromEnv,
			dClient.WithTimeout(d.cfg.Timeout),
		}
	} else {
		opts = []dClient.Opt{
			dClient.WithAPIVersionNegotiation(),
			dClient.WithHost(d.cfg.Address),
			dClient.WithTimeout(d.cfg.Timeout),
		}
	}
	return dClient.NewClientWithOpts(opts...)
}

func (d *dockerLoader) Start(ctx context.Context) chan *loaders.TargetOperation {
	opChan := make(chan *loaders.TargetOperation)
	go func() {
		defer close(opChan)
		for {
			select {
			case <-ctx.Done():
				return
			default:
				d.logger.Printf("querying %q targets", loaderName)
				readTargets, err := d.getTargets(ctx)
				if err != nil {
					d.logger.Printf("failed to read targets from docker daemon: %v", err)
					time.Sleep(d.cfg.Interval)
					continue
				}
				if d.cfg.Debug {
					d.logger.Printf("docker loader discovered %d target(s)", len(readTargets))
				}
				select {
				case <-ctx.Done():
					return
				case opChan <- d.diff(readTargets):
					time.Sleep(d.cfg.Interval)
				}
			}
		}
	}()
	return opChan
}

func (d *dockerLoader) getTargets(ctx context.Context) (map[string]*collector.TargetConfig, error) {
	d.wg = new(sync.WaitGroup)
	d.wg.Add(len(d.fl))
	readTargets := make(map[string]*collector.TargetConfig)
	m := new(sync.Mutex)
	errChan := make(chan error, len(d.fl))
	for _, targetFilter := range d.fl {
		go func(fl *targetFilterComp) {
			defer d.wg.Done()
			// get networks
			nrs, err := d.client.NetworkList(ctx, types.NetworkListOptions{
				Filters: fl.nt,
			})
			if err != nil {
				errChan <- fmt.Errorf("failed getting networks list using filter %+v: %v", fl.nt, err)
				return
			}
			// get containers for each defined filter
			for _, cfl := range fl.fl {
				conts, err := d.client.ContainerList(ctx, types.ContainerListOptions{
					Filters: cfl,
				})
				if err != nil {
					errChan <- fmt.Errorf("failed getting containers list using filter %+v: %v", cfl, err)
					continue
				}
				for _, cont := range conts {
					d.logger.Printf("building target from container %q, names: %v, labels: %v", cont.ID, cont.Names, cont.Labels)
					tc := new(collector.TargetConfig)
					if fl.cfg != nil {
						err = mapstructure.Decode(fl.cfg, tc)
						if err != nil {
							d.logger.Printf("failed to decode config map: %v", err)
						}
					}
					// set target name
					tc.Name = cont.ID
					if len(cont.Names) > 0 {
						tc.Name = strings.TrimLeft(cont.Names[0], "/")
					}
					// discover target address and port
					switch strings.ToLower(cont.HostConfig.NetworkMode) {
					case "host":
						if d.cfg.Address == "" || strings.HasPrefix(d.cfg.Address, "unix://") {
							tc.Address = "localhost"
						} else {
							tc.Address, _, err = net.SplitHostPort(d.cfg.Address)
							if err != nil {
								errChan <- err
								continue
							}
						}
						if fl.port != "" {
							if !strings.Contains(fl.port, "=") {
								tc.Address = fmt.Sprintf("%s:%s", tc.Address, fl.port)
							} else {
								portLabel := strings.Replace(fl.port, "label=", "", 1)
								if p, ok := cont.Labels[portLabel]; ok {
									tc.Address = fmt.Sprintf("%s:%s", tc.Address, p)
								}
							}
						}
					default:
						if strings.HasPrefix(d.cfg.Address, "unix:///") {
							for _, nr := range nrs {
								if n, ok := cont.NetworkSettings.Networks[nr.Name]; ok {
									if n.IPAddress != "" {
										tc.Address = n.IPAddress
										break
									}
									tc.Address = n.GlobalIPv6Address
									break
								}
							}
							if tc.Address == "" {
								d.logger.Printf("%q no address found", tc.Name)
								continue
							}
							if fl.port != "" {
								if !strings.Contains(fl.port, "=") {
									tc.Address = fmt.Sprintf("%s:%s", tc.Address, fl.port)
								} else {
									portLabel := strings.Replace(fl.port, "label=", "", 1)
									if p, ok := cont.Labels[portLabel]; ok {
										tc.Address = fmt.Sprintf("%s:%s", tc.Address, p)
									}
								}
							}
						} else {
							// get port from config/label
							port := getPortNumber(cont.Labels, fl.port)
							// check if port is exposed, find the public port and build the target address
							for _, p := range cont.Ports {
								// the container private port matches the port from the docker label
								if p.PrivatePort == port && p.Type == "tcp" {
									ipAddr := p.IP
									if ipAddr == "0.0.0.0" || ipAddr == "::" {
										if d.cfg.Address == "" {
											// if docker daemon is empty use localhost as target address
											ipAddr = "localhost"
										} else {
											// derive target address from daemon address if not empty
											u, err := url.Parse(d.cfg.Address)
											if err != nil {
												d.logger.Printf("failed to parse docker daemon address")
												continue
											}
											ipAddr, _, _ = net.SplitHostPort(u.Host)
										}

									}
									tc.Address = fmt.Sprintf("%s:%d", ipAddr, p.PublicPort)
								}
							}
							// if an address was not found using the exposed ports
							// select the bridge address, and use the port from label if not zero
							if tc.Address == "" {
								for _, nr := range nrs {
									if n, ok := cont.NetworkSettings.Networks[nr.Name]; ok {
										if n.IPAddress != "" {
											tc.Address = n.IPAddress
											break
										}
										tc.Address = n.GlobalIPv6Address
										break
									}
								}
								if tc.Address == "" {
									d.logger.Printf("%q no address found", tc.Name)
									continue
								}
								if port != 0 {
									tc.Address = fmt.Sprintf("%s:%d", tc.Address, port)
								}
							}
						}
					}
					//
					if d.cfg.Debug {
						d.logger.Printf("discovered target config %s with filter: %v", tc, cfl)
					}
					m.Lock()
					readTargets[tc.Name] = tc
					m.Unlock()
				}
			}
		}(targetFilter)
	}
	var errors = make([]error, 0)
	go func() {
		for err := range errChan {
			errors = append(errors, err)
		}
	}()

	d.wg.Wait()
	close(errChan)
	if len(errors) > 0 {
		for _, err := range errors {
			d.logger.Printf("%v", err)
		}
		return nil, fmt.Errorf("there was %d error(s)", len(errors))
	}
	return readTargets, nil
}

func (d *dockerLoader) diff(m map[string]*collector.TargetConfig) *loaders.TargetOperation {
	result := loaders.Diff(d.lastTargets, m)
	for _, t := range result.Add {
		if _, ok := d.lastTargets[t.Name]; !ok {
			d.lastTargets[t.Name] = t
		}
	}
	for _, n := range result.Del {
		delete(d.lastTargets, n)
	}
	if d.cfg.Debug {
		b, err := json.MarshalIndent(result, "", "  ")
		if err != nil {
			d.logger.Printf("discovery diff result: %v", result)
		} else {
			d.logger.Printf("discovery diff result:\n%s", string(b))
		}
	}
	return result
}

func (d *dockerLoader) String() string {
	b, err := json.Marshal(d.cfg)
	if err != nil {
		return fmt.Sprintf("%+v", d.cfg)
	}
	return string(b)
}

/// helpers

func getPortNumber(labels map[string]string, p string) uint16 {
	var port uint16
	if p != "" {
		if !strings.Contains(p, "=") {
			p, _ := strconv.Atoi(p)
			port = uint16(p)
		} else {
			s := labels[strings.Replace(p, "label=", "", 1)]
			p, _ := strconv.Atoi(s)
			port = uint16(p)
		}
	}
	return port
}
<|endoftext|>


<commit_before>package game

import (
	"github.com/hajimehoshi/go.ebiten/graphics"
	"github.com/hajimehoshi/go.ebiten/graphics/matrix"
	"image"
	"image/color"
	"math/rand"
	"os"
	"time"
)

type Sprite struct {
	width  int
	height int
	ch     chan bool
	x      int
	y      int
	vx     int
	vy     int
}

func NewSprite(screenWidth, screenHeight, width, height int) *Sprite {
	maxX := screenWidth - width
	maxY := screenHeight - height
	sprite := &Sprite{
		width:  width,
		height: height,
		ch:     make(chan bool),
		x:      rand.Intn(maxX),
		y:      rand.Intn(maxY),
		vx:     rand.Intn(2)*2 - 1,
		vy:     rand.Intn(2)*2 - 1,
	}
	go sprite.update(screenWidth, screenHeight)
	return sprite
}

func (sprite *Sprite) update(screenWidth, screenHeight int) {
	maxX := screenWidth - sprite.width
	maxY := screenHeight - sprite.height
	for {
		<-sprite.ch
		sprite.x += sprite.vx
		sprite.y += sprite.vy
		if sprite.x < 0 || maxX <= sprite.x {
			sprite.vx = -sprite.vx
		}
		if sprite.y < 0 || maxY <= sprite.y {
			sprite.vy = -sprite.vy
		}
		sprite.ch <- true
	}
}

func (sprite *Sprite) Update() {
	sprite.ch <- true
	<-sprite.ch
}

type Sprites struct {
	ebitenTexture graphics.Texture
	sprites       []*Sprite
	angle         int
}

func NewSprites() *Sprites {
	return &Sprites{}
}

func (game *Sprites) ScreenWidth() int {
	return 256
}

func (game *Sprites) ScreenHeight() int {
	return 240
}

func (game *Sprites) Init(tf graphics.TextureFactory) {
	file, err := os.Open("ebiten.png")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	img, _, err := image.Decode(file)
	if err != nil {
		panic(err)
	}
	game.ebitenTexture = tf.NewTextureFromImage(img)
	game.sprites = []*Sprite{}
	for i := 0; i < 1000; i++ {
		sprite := NewSprite(
			game.ScreenWidth(),
			game.ScreenHeight(),
			game.ebitenTexture.Width,
			game.ebitenTexture.Height)
		game.sprites = append(game.sprites, sprite)
	}
}

func (game *Sprites) Update() {
	for _, sprite := range game.sprites {
		sprite.Update()
	}
	game.angle++
}

func (game *Sprites) Draw(g graphics.GraphicsContext, offscreen graphics.Texture) {
	g.Fill(&color.RGBA{R: 128, G: 128, B: 255, A: 255})

	// Draw the sprites
	locations := make([]graphics.TextureLocation, 0, len(game.sprites))
	texture := game.ebitenTexture
	for _, sprite := range game.sprites {
		location := graphics.TextureLocation{
			LocationX: sprite.x,
			LocationY: sprite.y,
			Source: graphics.Rect{
				0, 0, texture.Width, texture.Height,
			},
		}
		locations = append(locations, location)
	}
	geometryMatrix := matrix.IdentityGeometry()
	g.DrawTextureParts(texture.ID, locations,
		geometryMatrix, matrix.IdentityColor())
}

func init() {
	rand.Seed(time.Now().UnixNano())
}
<commit_msg>Remove an unneeded variable<commit_after>package game

import (
	"github.com/hajimehoshi/go.ebiten/graphics"
	"github.com/hajimehoshi/go.ebiten/graphics/matrix"
	"image"
	"image/color"
	"math/rand"
	"os"
	"time"
)

type Sprite struct {
	width  int
	height int
	ch     chan bool
	x      int
	y      int
	vx     int
	vy     int
}

func NewSprite(screenWidth, screenHeight, width, height int) *Sprite {
	maxX := screenWidth - width
	maxY := screenHeight - height
	sprite := &Sprite{
		width:  width,
		height: height,
		ch:     make(chan bool),
		x:      rand.Intn(maxX),
		y:      rand.Intn(maxY),
		vx:     rand.Intn(2)*2 - 1,
		vy:     rand.Intn(2)*2 - 1,
	}
	go sprite.update(screenWidth, screenHeight)
	return sprite
}

func (sprite *Sprite) update(screenWidth, screenHeight int) {
	maxX := screenWidth - sprite.width
	maxY := screenHeight - sprite.height
	for {
		<-sprite.ch
		sprite.x += sprite.vx
		sprite.y += sprite.vy
		if sprite.x < 0 || maxX <= sprite.x {
			sprite.vx = -sprite.vx
		}
		if sprite.y < 0 || maxY <= sprite.y {
			sprite.vy = -sprite.vy
		}
		sprite.ch <- true
	}
}

func (sprite *Sprite) Update() {
	sprite.ch <- true
	<-sprite.ch
}

type Sprites struct {
	ebitenTexture graphics.Texture
	sprites       []*Sprite
}

func NewSprites() *Sprites {
	return &Sprites{}
}

func (game *Sprites) ScreenWidth() int {
	return 256
}

func (game *Sprites) ScreenHeight() int {
	return 240
}

func (game *Sprites) Init(tf graphics.TextureFactory) {
	file, err := os.Open("ebiten.png")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	img, _, err := image.Decode(file)
	if err != nil {
		panic(err)
	}
	game.ebitenTexture = tf.NewTextureFromImage(img)
	game.sprites = []*Sprite{}
	for i := 0; i < 1000; i++ {
		sprite := NewSprite(
			game.ScreenWidth(),
			game.ScreenHeight(),
			game.ebitenTexture.Width,
			game.ebitenTexture.Height)
		game.sprites = append(game.sprites, sprite)
	}
}

func (game *Sprites) Update() {
	for _, sprite := range game.sprites {
		sprite.Update()
	}
}

func (game *Sprites) Draw(g graphics.GraphicsContext, offscreen graphics.Texture) {
	g.Fill(&color.RGBA{R: 128, G: 128, B: 255, A: 255})

	// Draw the sprites
	locations := make([]graphics.TextureLocation, 0, len(game.sprites))
	texture := game.ebitenTexture
	for _, sprite := range game.sprites {
		location := graphics.TextureLocation{
			LocationX: sprite.x,
			LocationY: sprite.y,
			Source: graphics.Rect{
				0, 0, texture.Width, texture.Height,
			},
		}
		locations = append(locations, location)
	}
	geometryMatrix := matrix.IdentityGeometry()
	g.DrawTextureParts(texture.ID, locations,
		geometryMatrix, matrix.IdentityColor())
}

func init() {
	rand.Seed(time.Now().UnixNano())
}
<|endoftext|>