<commit_before>/*
Copyright (c) 2013, Richard Johnson
Copyright (c) 2014, Kilian Gilonne
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package safebrowsing

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"os"
	//	"runtime/debug"
	"strconv"
	"strings"
	"time"
)

type HostHash string
type LookupHash string

type SafeBrowsing struct {
	DataDir string

	Key             string
	Client          string
	AppVersion      string
	ProtocolVersion string

	UpdateDelay int
	LastUpdated time.Time

	Lists   map[string]*SafeBrowsingList
	Cache   map[HostHash]*FullHashCache
	request func(string, string, bool) (*http.Response, error)

	Logger logger
}

var SupportedLists map[string]bool = map[string]bool{
	"goog-malware-shavar":  true,
	"googpub-phish-shavar": true,
}

var Logger logger = new(DefaultLogger)
var Client string = "api"
var AppVersion string = "1.5.2"
var ProtocolVersion string = "3.0"
var OfflineMode bool = false
var Transport *http.Transport = &http.Transport{}

func NewSafeBrowsing(apiKey string, dataDirectory string) (sb *SafeBrowsing, err error) {
	sb = &SafeBrowsing{
		Key:             apiKey,
		Client:          Client,
		AppVersion:      AppVersion,
		ProtocolVersion: ProtocolVersion,
		DataDir:         dataDirectory,
		Lists:           make(map[string]*SafeBrowsingList),
		Cache:           make(map[HostHash]*FullHashCache),
		request:         request,
		Logger:          Logger,
	}

	// if the dataDirectory does not currently exist, have a go at creating it:
	err = os.MkdirAll(dataDirectory, os.ModeDir|0700)
	if err != nil {
		sb.Logger.Error(
			"Directory \"%s\" does not exist, and I was unable to create it!",
			dataDirectory)
	}

	// if we are in offline mode we want to just load up the lists we
	// currently have and work with that
	if OfflineMode {
		for listName, _ := range SupportedLists {
			fileName := sb.DataDir + "/" + listName + ".dat"
			tmpList := newSafeBrowsingList(listName, fileName)
			tmpList.Logger = sb.Logger
			err := tmpList.load(nil)
			if err != nil {
				sb.Logger.Warn("Error loading list %s: %s", listName, err)
				continue
			}
			sb.Lists[listName] = tmpList
		}
		//		debug.FreeOSMemory()
		return sb, nil
	}

	// normal mode, contact the server for updates, etc.
	err = sb.UpdateProcess()

	return sb, err
}

func (sb *SafeBrowsing) UpdateProcess() (err error) {

	sb.Logger.Info("Requesting list of lists from server...")
	err = sb.requestSafeBrowsingLists()
	if err != nil {
		return err
	}
	err = sb.loadExistingData()
	if err != nil {
		return err
	}
	err, status := sb.update()
	if (err != nil) && (status != 503) {
		return err
	} else if status == 503 {
		sb.Logger.Warn("GSB service temporarily unavailable")
	}

	go sb.reloadLoop()
	return nil
}

func (sb *SafeBrowsing) requestSafeBrowsingLists() (err error) {
	//	defer debug.FreeOSMemory()

	url := fmt.Sprintf(
		"https://safebrowsing.google.com/safebrowsing/list?"+
			"client=%s&apikey=%s&appver=%s&pver=%s",
		sb.Client, sb.Key, sb.AppVersion, sb.ProtocolVersion)

	listresp, err := sb.request(url, "", true)
	if err != nil {
		return err
	}
	if listresp.StatusCode == 503 {
		sb.requestSafeBrowsingLists()
	} else if listresp.StatusCode != 200 {
		return fmt.Errorf("Unexpected server response code: %d", listresp.StatusCode)
	}
	return sb.processSafeBrowsingLists(listresp.Body)
}

func (sb *SafeBrowsing) processSafeBrowsingLists(body io.Reader) (err error) {
	buf := bytes.Buffer{}
	if _, err = buf.ReadFrom(body); err != nil {
		return fmt.Errorf("Unable to read list data: %s", err)
	}
	for _, listName := range strings.Split(strings.TrimSpace(buf.String()), "\n") {
		if _, exists := SupportedLists[listName]; !exists {
			continue
		}
		fileName := sb.DataDir + "/" + listName + ".dat"
		tmpList := newSafeBrowsingList(listName, fileName)
		tmpList.Logger = sb.Logger
		sb.Lists[listName] = tmpList
	}
	return nil
}

func (sb *SafeBrowsing) loadExistingData() error {

	sb.Logger.Info("Loading existing data....")
	for _, sbl := range sb.Lists {
		err := sbl.load(nil)
		if err != nil {
			return fmt.Errorf("Error loading list from %s: %s", sb.DataDir, err)
		}
		//		debug.FreeOSMemory()
	}
	return nil
}

func (sb *SafeBrowsing) update() (err error, status int) {

	sb.Logger.Info("Requesting updates...")
	if err, status = sb.requestRedirectList(); err != nil {
		return fmt.Errorf("Unable to retrieve updates: %s", err.Error()), status
	}

	for listName, list := range sb.Lists {
		if err = list.loadDataFromRedirectLists(); err != nil {
			return fmt.Errorf("Unable to process updates for %s: %s", listName, err.Error()), status
		}
	}

	// update the last updated time
	sb.LastUpdated = time.Now()
	return nil, status
}

func (sb *SafeBrowsing) requestRedirectList() (err error, status int) {
	//	defer debug.FreeOSMemory()

	url := fmt.Sprintf(
		"https://safebrowsing.google.com/safebrowsing/downloads?"+
			"client=%s&apikey=%s&appver=%s&pver=%s",
		sb.Client, sb.Key, sb.AppVersion, sb.ProtocolVersion)

	listsStr := ""
	for list, sbl := range sb.Lists {
		listsStr += string(list) + ";"
		addChunkRange := sbl.ChunkRanges[CHUNK_TYPE_ADD]
		if addChunkRange != "" {
			listsStr += "a:" + addChunkRange + ":"
		}
		subChunkRange := sbl.ChunkRanges[CHUNK_TYPE_SUB]
		if subChunkRange != "" {
			listsStr += "s:" + subChunkRange
		}
		listsStr += "\n"
	}
	redirects, err := sb.request(url, listsStr, true)
	if err != nil {
		return err, 0
	}
	defer redirects.Body.Close()
	if redirects.StatusCode != 200 {
		tmp := &bytes.Buffer{}
		tmp.ReadFrom(redirects.Body)
		return fmt.Errorf("Unexpected server response code: %d\n%s", redirects.StatusCode, tmp), redirects.StatusCode
	}
	if err = sb.processRedirectList(redirects.Body); err != nil {
		return err, redirects.StatusCode
	}
	return nil, redirects.StatusCode
}

func (sb *SafeBrowsing) processRedirectList(buf io.Reader) error {
	//	defer debug.FreeOSMemory()

	scanner := bufio.NewScanner(buf)
	//initialize temporary var
	var currentListName string
	var RedirectList []string = nil
	currentDeletes := make(map[ChunkData_ChunkType]map[ChunkNum]bool)
	currentDeletes[CHUNK_TYPE_ADD] = make(map[ChunkNum]bool)
	currentDeletes[CHUNK_TYPE_SUB] = make(map[ChunkNum]bool)

	for scanner.Scan() {
		line := scanner.Text()
		bits := strings.SplitN(line, ":", 2)
		switch bits[0] {
		case "n":
			updateDelayStr := bits[1]
			updateDelay, err := strconv.Atoi(updateDelayStr)
			if err != nil {
				return fmt.Errorf("Unable to parse timeout: %s", err)
			}
			sb.UpdateDelay = updateDelay
		case "r":
			// we need to reset full!
			sb.reset()
			// the docs say to request again, so we do that...
			err, _ := sb.requestRedirectList()
			return err
		case "i":
			if RedirectList != nil {
				// save to DataRedirects
				sb.Lists[currentListName].DataRedirects = RedirectList
				sb.Lists[currentListName].DeleteChunks = currentDeletes
			}
			// reinitialize temporary var
			RedirectList = make([]string, 0)
			currentDeletes = make(map[ChunkData_ChunkType]map[ChunkNum]bool)
			currentDeletes[CHUNK_TYPE_ADD] = make(map[ChunkNum]bool, 0)
			currentDeletes[CHUNK_TYPE_SUB] = make(map[ChunkNum]bool, 0)
			currentListName = bits[1]
		case "u":
			RedirectList = append(RedirectList, "https://"+bits[1])
		case "ad":
			addDeletes, err := parseChunkRange(bits[1])
			if err != nil {
				return fmt.Errorf("Error parsing delete add chunks range: %s", err)
			}
			currentDeletes[CHUNK_TYPE_ADD] = addDeletes
		case "sd":
			subDeletes, err := parseChunkRange(bits[1])
			if err != nil {
				return fmt.Errorf("Error parsing delete sub chunks range: %s", err)
			}
			currentDeletes[CHUNK_TYPE_SUB] = subDeletes
		default:
			continue
		}
		//		debug.FreeOSMemory()
	}

	// add the final list
	sb.Lists[currentListName].DataRedirects = RedirectList
	sb.Lists[currentListName].DeleteChunks = currentDeletes
	if err := scanner.Err(); err != nil && err != io.EOF {
		return fmt.Errorf("Unable to parse list response: %s", err)
	}
	return nil
}

func (sb *SafeBrowsing) reset() {

	for _, sbl := range sb.Lists {
		sbl.Lookup = NewTrie()
		sbl.FullHashes = NewTrie()
		sbl.FullHashRequested = NewTrie()
		sbl.DataRedirects = make([]string, 0)
		sbl.DeleteChunks = make(map[ChunkData_ChunkType]map[ChunkNum]bool)
		sbl.DeleteChunks[CHUNK_TYPE_ADD] = make(map[ChunkNum]bool, 0)
		sbl.DeleteChunks[CHUNK_TYPE_SUB] = make(map[ChunkNum]bool, 0)
		// kill off the chunks
		sbl.ChunkRanges = map[ChunkData_ChunkType]string{
			CHUNK_TYPE_ADD: "",
			CHUNK_TYPE_SUB: "",
		}
		// delete any files we have loaded for this map
		if sbl.FileName != "" {
			os.Remove(sbl.FileName)
		}
		//		debug.FreeOSMemory()
	}
}

func (sb *SafeBrowsing) reloadLoop() {

	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	randomFloat := r.Float64()
	for {
		// wait the update delay
		duration := time.Duration(sb.UpdateDelay) * time.Second
		sb.Logger.Info("Next update in %d seconds", sb.UpdateDelay)
		time.Sleep(duration)
		err, status := sb.update()
		for x := 0; status == 503; x++ {
			// first we wait 1 min, than some time between 30-60 mins
			// doubling until we stop at 480 mins or succeed
			mins := (30 * (randomFloat + 1) * float64(x)) + 1
			if mins > 480 {
				mins = 480
			}
			sb.Logger.Warn(
				"Update failed, in back-off mode (waiting %d mins): %s",
				mins,
				err,
			)
			time.Sleep(time.Duration(mins) * time.Minute)
			err, status = sb.update()
		}
		//		debug.FreeOSMemory()
	}
}
<commit_msg>fix v3 key parameter name<commit_after>/*
Copyright (c) 2013, Richard Johnson
Copyright (c) 2014, Kilian Gilonne
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package safebrowsing

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"os"
	//	"runtime/debug"
	"strconv"
	"strings"
	"time"
)

type HostHash string
type LookupHash string

type SafeBrowsing struct {
	DataDir string

	Key             string
	Client          string
	AppVersion      string
	ProtocolVersion string

	UpdateDelay int
	LastUpdated time.Time

	Lists   map[string]*SafeBrowsingList
	Cache   map[HostHash]*FullHashCache
	request func(string, string, bool) (*http.Response, error)

	Logger logger
}

var SupportedLists map[string]bool = map[string]bool{
	"goog-malware-shavar":  true,
	"googpub-phish-shavar": true,
}

var Logger logger = new(DefaultLogger)
var Client string = "api"
var AppVersion string = "1.5.2"
var ProtocolVersion string = "3.0"
var OfflineMode bool = false
var Transport *http.Transport = &http.Transport{}

func NewSafeBrowsing(apiKey string, dataDirectory string) (sb *SafeBrowsing, err error) {
	sb = &SafeBrowsing{
		Key:             apiKey,
		Client:          Client,
		AppVersion:      AppVersion,
		ProtocolVersion: ProtocolVersion,
		DataDir:         dataDirectory,
		Lists:           make(map[string]*SafeBrowsingList),
		Cache:           make(map[HostHash]*FullHashCache),
		request:         request,
		Logger:          Logger,
	}

	// if the dataDirectory does not currently exist, have a go at creating it:
	err = os.MkdirAll(dataDirectory, os.ModeDir|0700)
	if err != nil {
		sb.Logger.Error(
			"Directory \"%s\" does not exist, and I was unable to create it!",
			dataDirectory)
	}

	// if we are in offline mode we want to just load up the lists we
	// currently have and work with that
	if OfflineMode {
		for listName, _ := range SupportedLists {
			fileName := sb.DataDir + "/" + listName + ".dat"
			tmpList := newSafeBrowsingList(listName, fileName)
			tmpList.Logger = sb.Logger
			err := tmpList.load(nil)
			if err != nil {
				sb.Logger.Warn("Error loading list %s: %s", listName, err)
				continue
			}
			sb.Lists[listName] = tmpList
		}
		//		debug.FreeOSMemory()
		return sb, nil
	}

	// normal mode, contact the server for updates, etc.
	err = sb.UpdateProcess()

	return sb, err
}

func (sb *SafeBrowsing) UpdateProcess() (err error) {

	sb.Logger.Info("Requesting list of lists from server...")
	err = sb.requestSafeBrowsingLists()
	if err != nil {
		return err
	}
	err = sb.loadExistingData()
	if err != nil {
		return err
	}
	err, status := sb.update()
	if (err != nil) && (status != 503) {
		return err
	} else if status == 503 {
		sb.Logger.Warn("GSB service temporarily unavailable")
	}

	go sb.reloadLoop()
	return nil
}

func (sb *SafeBrowsing) requestSafeBrowsingLists() (err error) {
	//	defer debug.FreeOSMemory()

	url := fmt.Sprintf(
		"https://safebrowsing.google.com/safebrowsing/list?"+
			"client=%s&%s=%s&appver=%s&pver=%s",
		sb.Client, sb.keyParam(), sb.Key, sb.AppVersion, sb.ProtocolVersion)

	listresp, err := sb.request(url, "", true)
	if err != nil {
		return err
	}
	if listresp.StatusCode == 503 {
		sb.requestSafeBrowsingLists()
	} else if listresp.StatusCode != 200 {
		return fmt.Errorf("Unexpected server response code: %d", listresp.StatusCode)
	}
	return sb.processSafeBrowsingLists(listresp.Body)
}

func (sb *SafeBrowsing) processSafeBrowsingLists(body io.Reader) (err error) {
	buf := bytes.Buffer{}
	if _, err = buf.ReadFrom(body); err != nil {
		return fmt.Errorf("Unable to read list data: %s", err)
	}
	for _, listName := range strings.Split(strings.TrimSpace(buf.String()), "\n") {
		if _, exists := SupportedLists[listName]; !exists {
			continue
		}
		fileName := sb.DataDir + "/" + listName + ".dat"
		tmpList := newSafeBrowsingList(listName, fileName)
		tmpList.Logger = sb.Logger
		sb.Lists[listName] = tmpList
	}
	return nil
}

func (sb *SafeBrowsing) loadExistingData() error {

	sb.Logger.Info("Loading existing data....")
	for _, sbl := range sb.Lists {
		err := sbl.load(nil)
		if err != nil {
			return fmt.Errorf("Error loading list from %s: %s", sb.DataDir, err)
		}
		//		debug.FreeOSMemory()
	}
	return nil
}

func (sb *SafeBrowsing) update() (err error, status int) {

	sb.Logger.Info("Requesting updates...")
	if err, status = sb.requestRedirectList(); err != nil {
		return fmt.Errorf("Unable to retrieve updates: %s", err.Error()), status
	}

	for listName, list := range sb.Lists {
		if err = list.loadDataFromRedirectLists(); err != nil {
			return fmt.Errorf("Unable to process updates for %s: %s", listName, err.Error()), status
		}
	}

	// update the last updated time
	sb.LastUpdated = time.Now()
	return nil, status
}

func (sb *SafeBrowsing) requestRedirectList() (err error, status int) {
	//	defer debug.FreeOSMemory()

	url := fmt.Sprintf(
		"https://safebrowsing.google.com/safebrowsing/downloads?"+
			"client=%s&%s=%s&appver=%s&pver=%s",
		sb.Client, sb.keyParam(), sb.Key, sb.AppVersion, sb.ProtocolVersion)

	listsStr := ""
	for list, sbl := range sb.Lists {
		listsStr += string(list) + ";"
		addChunkRange := sbl.ChunkRanges[CHUNK_TYPE_ADD]
		if addChunkRange != "" {
			listsStr += "a:" + addChunkRange + ":"
		}
		subChunkRange := sbl.ChunkRanges[CHUNK_TYPE_SUB]
		if subChunkRange != "" {
			listsStr += "s:" + subChunkRange
		}
		listsStr += "\n"
	}
	redirects, err := sb.request(url, listsStr, true)
	if err != nil {
		return err, 0
	}
	defer redirects.Body.Close()
	if redirects.StatusCode != 200 {
		tmp := &bytes.Buffer{}
		tmp.ReadFrom(redirects.Body)
		return fmt.Errorf("Unexpected server response code: %d\n%s", redirects.StatusCode, tmp), redirects.StatusCode
	}
	if err = sb.processRedirectList(redirects.Body); err != nil {
		return err, redirects.StatusCode
	}
	return nil, redirects.StatusCode
}

func (sb *SafeBrowsing) processRedirectList(buf io.Reader) error {
	//	defer debug.FreeOSMemory()

	scanner := bufio.NewScanner(buf)
	//initialize temporary var
	var currentListName string
	var RedirectList []string = nil
	currentDeletes := make(map[ChunkData_ChunkType]map[ChunkNum]bool)
	currentDeletes[CHUNK_TYPE_ADD] = make(map[ChunkNum]bool)
	currentDeletes[CHUNK_TYPE_SUB] = make(map[ChunkNum]bool)

	for scanner.Scan() {
		line := scanner.Text()
		bits := strings.SplitN(line, ":", 2)
		switch bits[0] {
		case "n":
			updateDelayStr := bits[1]
			updateDelay, err := strconv.Atoi(updateDelayStr)
			if err != nil {
				return fmt.Errorf("Unable to parse timeout: %s", err)
			}
			sb.UpdateDelay = updateDelay
		case "r":
			// we need to reset full!
			sb.reset()
			// the docs say to request again, so we do that...
			err, _ := sb.requestRedirectList()
			return err
		case "i":
			if RedirectList != nil {
				// save to DataRedirects
				sb.Lists[currentListName].DataRedirects = RedirectList
				sb.Lists[currentListName].DeleteChunks = currentDeletes
			}
			// reinitialize temporary var
			RedirectList = make([]string, 0)
			currentDeletes = make(map[ChunkData_ChunkType]map[ChunkNum]bool)
			currentDeletes[CHUNK_TYPE_ADD] = make(map[ChunkNum]bool, 0)
			currentDeletes[CHUNK_TYPE_SUB] = make(map[ChunkNum]bool, 0)
			currentListName = bits[1]
		case "u":
			RedirectList = append(RedirectList, "https://"+bits[1])
		case "ad":
			addDeletes, err := parseChunkRange(bits[1])
			if err != nil {
				return fmt.Errorf("Error parsing delete add chunks range: %s", err)
			}
			currentDeletes[CHUNK_TYPE_ADD] = addDeletes
		case "sd":
			subDeletes, err := parseChunkRange(bits[1])
			if err != nil {
				return fmt.Errorf("Error parsing delete sub chunks range: %s", err)
			}
			currentDeletes[CHUNK_TYPE_SUB] = subDeletes
		default:
			continue
		}
		//		debug.FreeOSMemory()
	}

	// add the final list
	sb.Lists[currentListName].DataRedirects = RedirectList
	sb.Lists[currentListName].DeleteChunks = currentDeletes
	if err := scanner.Err(); err != nil && err != io.EOF {
		return fmt.Errorf("Unable to parse list response: %s", err)
	}
	return nil
}

func (sb *SafeBrowsing) reset() {

	for _, sbl := range sb.Lists {
		sbl.Lookup = NewTrie()
		sbl.FullHashes = NewTrie()
		sbl.FullHashRequested = NewTrie()
		sbl.DataRedirects = make([]string, 0)
		sbl.DeleteChunks = make(map[ChunkData_ChunkType]map[ChunkNum]bool)
		sbl.DeleteChunks[CHUNK_TYPE_ADD] = make(map[ChunkNum]bool, 0)
		sbl.DeleteChunks[CHUNK_TYPE_SUB] = make(map[ChunkNum]bool, 0)
		// kill off the chunks
		sbl.ChunkRanges = map[ChunkData_ChunkType]string{
			CHUNK_TYPE_ADD: "",
			CHUNK_TYPE_SUB: "",
		}
		// delete any files we have loaded for this map
		if sbl.FileName != "" {
			os.Remove(sbl.FileName)
		}
		//		debug.FreeOSMemory()
	}
}

func (sb *SafeBrowsing) reloadLoop() {

	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	randomFloat := r.Float64()
	for {
		// wait the update delay
		duration := time.Duration(sb.UpdateDelay) * time.Second
		sb.Logger.Info("Next update in %d seconds", sb.UpdateDelay)
		time.Sleep(duration)
		err, status := sb.update()
		for x := 0; status == 503; x++ {
			// first we wait 1 min, than some time between 30-60 mins
			// doubling until we stop at 480 mins or succeed
			mins := (30 * (randomFloat + 1) * float64(x)) + 1
			if mins > 480 {
				mins = 480
			}
			sb.Logger.Warn(
				"Update failed, in back-off mode (waiting %d mins): %s",
				mins,
				err,
			)
			time.Sleep(time.Duration(mins) * time.Minute)
			err, status = sb.update()
		}
		//		debug.FreeOSMemory()
	}
}

const v3KeyPrefix = "AIza"

func (sb *SafeBrowsing) keyParam() string {
	if strings.HasPrefix(sb.Key, v3KeyPrefix) {
		return "key"
	}
	return "apikey"
}
<|endoftext|>


<commit_before>package boardgame

import (
	"math"
	"math/rand"
	"strconv"
)

//StatePolicy defines a sanitization policy for a State object. In particular,
//it defines a policy for the Game state, and a single, fixed policy for all
//Player states. Each string returns the policy for the property with that
//name in that sub-state object. Properties with no corresponding policy are
//effectively PolicyNoOp for all groups.
type StatePolicy struct {
	Game   map[string]GroupPolicy
	Player map[string]GroupPolicy
}

//Policies apply to Groups of players. Groups with numbers 0 or above are
//defined in State.GroupMembership. There are two special groups: Self and
//Other.
const (
	//GroupSelf applies if the player the state is being prepared for is the
	//current PlayerState being transformed.
	GroupSelf = -1
	//GroupOther applies if the player the state is being prepared for is NOT
	//the current PlayerState being transformed.
	GroupOther = -2
	//GroupAll matches all players. It's useful for setting a restrictive
	//policy by default, that then some sub-groups relax by applying a less
	//restrictive policy.
	GroupAll = -3
)

//A group Santization policy represents all of the various policies that apply
//depending on whether the player we're preparing the state for is a member of
//the given group. To calculate the effective policy, we first collect all
//Policies that apply to the given player, based on their group membership,
//and then applied the *least* restrictive one.
type GroupPolicy map[int]Policy

//A sanitization policy reflects how to tranform a given State property when
//presenting to someone outside of the target group.
type Policy int

const (
	//Non sanitized. For non-group properties (e.g. strings, ints, bools), any
	//policy other than PolicyVisible or PolicyRandom is effectively
	//PolicyHidden.
	PolicyVisible Policy = iota
	//For groups (e.g. stacks, int slices), return a group that has the same
	//length. For all else, it's effectively PolicyHidden. In practice, stacks
	//will be set so that their NumComponents() is the same, but every
	//component that exists returns the GenericComponent.
	PolicyLen

	//For groups, PolicyNonEmpty will allow it to be observed that the stack's
	//NumComponents is either Empty (0 components) or non-empty (1
	//components). So for GrowableStacks, it will either have no components or
	//1 component. And for SizedStack, either all of the slots will be empty,
	//or the first slot will be non-empty. In all cases, the Component
	//present, if there is one, will be the deck's GenericComponent.
	PolicyNonEmpty

	//PolicyHidden returns effectively the zero value for the type. For
	//stacks, the deck it is, and the Size (for SizedStack) is set, but
	//nothing else is.
	PolicyHidden

	//PolicyRandom sets the property to a random but legal value to obscure
	//it. Not practically useful often, but is used in ComputedProperties to
	//ensure that people do not take an accidental dependency on a property
	//they didn't explicitly list in their dependencies.
	PolicyRandom

	//TODO: implement the other policies.
)

//statePlayerIndex is the index of the PlayerState that we're working on (-1
//for Game). preparingForPlayerIndex is the index that we're preparing the
//overall santiized state for, as provied to
//GameManager.SanitizedStateForPlayer()
func sanitizeStateObj(readSetter PropertyReadSetter, policy map[string]GroupPolicy, statePlayerIndex int, preparingForPlayerIndex int, defaultPolicy Policy) {

	for propName, propType := range readSetter.Props() {
		prop, err := readSetter.Prop(propName)

		if err != nil {
			//TODO: shouldn't we return an error or something?
			continue
		}
		readSetter.SetProp(propName, sanitizeProperty(prop, propType, policy[propName], statePlayerIndex, preparingForPlayerIndex, defaultPolicy))
	}

}

func sanitizeProperty(prop interface{}, propType PropertyType, policyGroup GroupPolicy, statePlayerIndex int, preparingForPlayerIndex int, defaultPolicy Policy) interface{} {

	//We're going to collect all of the policies that apply.
	var applicablePolicies []Policy

	for group, policy := range policyGroup {
		policyApplies := false
		switch group {
		case GroupSelf:
			policyApplies = (statePlayerIndex == preparingForPlayerIndex)
		case GroupOther:
			policyApplies = (statePlayerIndex != preparingForPlayerIndex)
		case GroupAll:
			policyApplies = true
		default:
			//In the future we'll interrogate whether the given group index is
			//in the specified property at this point.
			panic("Unsupported policy group")
		}
		if policyApplies {
			applicablePolicies = append(applicablePolicies, policy)
		}
	}

	//Now calculate the LEAST restrictive of the policies that apply.
	effectivePolicy := PolicyVisible
	if len(applicablePolicies) > 0 {
		effectivePolicy = Policy(math.MaxInt64)
		for _, policy := range applicablePolicies {
			if policy < effectivePolicy {
				effectivePolicy = policy
			}
		}
	} else {
		effectivePolicy = defaultPolicy
	}

	return applyPolicy(effectivePolicy, prop, propType)
}

func randomBool() bool {
	r := rand.Intn(2)
	if r == 0 {
		return false
	}
	return true
}

func randomInt() int {
	return rand.Int()
}

func randomGrowableStack(stack *GrowableStack) *GrowableStack {
	result := stack.Copy()

	indexes := make([]int, rand.Intn(16))

	for i, _ := range indexes {
		indexes[i] = emptyIndexSentinel
	}

	result.indexes = indexes

	return result
}

func randomSizedStack(stack *SizedStack) *SizedStack {
	result := stack.Copy()

	indexes := make([]int, len(result.indexes))

	for i, _ := range indexes {
		if randomBool() {
			indexes[i] = emptyIndexSentinel
		} else {
			indexes[i] = genericComponentSentinel
		}
	}

	result.indexes = indexes

	return result
}

func applyPolicy(policy Policy, input interface{}, propType PropertyType) interface{} {
	if policy == PolicyVisible {
		return input
	}

	if policy == PolicyRandom {
		switch propType {
		case TypeBool:
			return randomBool()
		case TypeInt:
			return randomInt()
		case TypeString:
			//Note: unlike the other random*() functions, this is defined in
			//game for the purposes of creating an ID. That's sufficient for
			//this use.
			return randomString(16)
		case TypeGrowableStack:
			return randomGrowableStack(input.(*GrowableStack))
		case TypeSizedStack:
			return randomSizedStack(input.(*SizedStack))
		default:
			panic("Unknown property type for policy random")
		}
	}

	//Go through the propTypes where everythign that's not PolicyVisible is
	//effectively PolicyHidden.

	switch propType {
	case TypeBool:
		return false
	case TypeInt:
		return 0
	case TypeString:
		return ""
	}

	//Now we're left with len-properties.

	stack := input.(Stack)

	stack.applySanitizationPolicy(policy)

	return input

}

func (g *GrowableStack) applySanitizationPolicy(policy Policy) {

	if policy == PolicyVisible {
		return
	}

	if policy == PolicyLen {

		indexes := make([]int, len(g.indexes))

		for i := 0; i < len(indexes); i++ {
			indexes[i] = genericComponentSentinel
		}

		g.indexes = indexes
		return
	}

	if policy == PolicyHidden {
		g.indexes = make([]int, 0)
		return
	}

	if policy == PolicyNonEmpty {
		if g.NumComponents() == 0 {
			g.indexes = make([]int, 0)
		} else {
			g.indexes = []int{genericComponentSentinel}
		}

		return
	}

	panic("Unknown sanitization policy" + strconv.Itoa(int(policy)))

}

func (s *SizedStack) applySanitizationPolicy(policy Policy) {

	if policy == PolicyVisible {
		return
	}

	if policy == PolicyLen {

		indexes := make([]int, len(s.indexes))

		for i := 0; i < len(indexes); i++ {
			if s.indexes[i] == emptyIndexSentinel {
				indexes[i] = emptyIndexSentinel
			} else {
				indexes[i] = genericComponentSentinel
			}
		}

		s.indexes = indexes

		return
	}

	if policy == PolicyHidden || policy == PolicyNonEmpty {

		hasComponents := s.NumComponents() > 0

		indexes := make([]int, len(s.indexes))
		for i := 0; i < len(indexes); i++ {
			indexes[i] = -1
		}
		s.indexes = indexes

		if policy == PolicyNonEmpty && hasComponents {
			s.indexes[0] = genericComponentSentinel
		}

		return
	}

	panic("Unknown sanitization policy" + strconv.Itoa(int(policy)))
}
<commit_msg>Popped out notion of SubStatePolicy for SanitizationPolicy, since we use it a fair bit (and are about to use it even more). Part of #41.<commit_after>package boardgame

import (
	"math"
	"math/rand"
	"strconv"
)

//StatePolicy defines a sanitization policy for a State object. In particular,
//it defines a policy for the Game state, and a single, fixed policy for all
//Player states. Each string returns the policy for the property with that
//name in that sub-state object. Properties with no corresponding policy are
//effectively PolicyNoOp for all groups.
type StatePolicy struct {
	Game   SubStatePolicy
	Player SubStatePolicy
}

//Policies apply to Groups of players. Groups with numbers 0 or above are
//defined in State.GroupMembership. There are two special groups: Self and
//Other.
const (
	//GroupSelf applies if the player the state is being prepared for is the
	//current PlayerState being transformed.
	GroupSelf = -1
	//GroupOther applies if the player the state is being prepared for is NOT
	//the current PlayerState being transformed.
	GroupOther = -2
	//GroupAll matches all players. It's useful for setting a restrictive
	//policy by default, that then some sub-groups relax by applying a less
	//restrictive policy.
	GroupAll = -3
)

//SubStatePolicy is a sanitization policy for a sub-part of a State, for
//example a Game or Player.
type SubStatePolicy map[string]GroupPolicy

//A group Santization policy represents all of the various policies that apply
//depending on whether the player we're preparing the state for is a member of
//the given group. To calculate the effective policy, we first collect all
//Policies that apply to the given player, based on their group membership,
//and then applied the *least* restrictive one.
type GroupPolicy map[int]Policy

//A sanitization policy reflects how to tranform a given State property when
//presenting to someone outside of the target group.
type Policy int

const (
	//Non sanitized. For non-group properties (e.g. strings, ints, bools), any
	//policy other than PolicyVisible or PolicyRandom is effectively
	//PolicyHidden.
	PolicyVisible Policy = iota
	//For groups (e.g. stacks, int slices), return a group that has the same
	//length. For all else, it's effectively PolicyHidden. In practice, stacks
	//will be set so that their NumComponents() is the same, but every
	//component that exists returns the GenericComponent.
	PolicyLen

	//For groups, PolicyNonEmpty will allow it to be observed that the stack's
	//NumComponents is either Empty (0 components) or non-empty (1
	//components). So for GrowableStacks, it will either have no components or
	//1 component. And for SizedStack, either all of the slots will be empty,
	//or the first slot will be non-empty. In all cases, the Component
	//present, if there is one, will be the deck's GenericComponent.
	PolicyNonEmpty

	//PolicyHidden returns effectively the zero value for the type. For
	//stacks, the deck it is, and the Size (for SizedStack) is set, but
	//nothing else is.
	PolicyHidden

	//PolicyRandom sets the property to a random but legal value to obscure
	//it. Not practically useful often, but is used in ComputedProperties to
	//ensure that people do not take an accidental dependency on a property
	//they didn't explicitly list in their dependencies.
	PolicyRandom

	//TODO: implement the other policies.
)

//statePlayerIndex is the index of the PlayerState that we're working on (-1
//for Game). preparingForPlayerIndex is the index that we're preparing the
//overall santiized state for, as provied to
//GameManager.SanitizedStateForPlayer()
func sanitizeStateObj(readSetter PropertyReadSetter, policy SubStatePolicy, statePlayerIndex int, preparingForPlayerIndex int, defaultPolicy Policy) {

	for propName, propType := range readSetter.Props() {
		prop, err := readSetter.Prop(propName)

		if err != nil {
			//TODO: shouldn't we return an error or something?
			continue
		}
		readSetter.SetProp(propName, sanitizeProperty(prop, propType, policy[propName], statePlayerIndex, preparingForPlayerIndex, defaultPolicy))
	}

}

func sanitizeProperty(prop interface{}, propType PropertyType, policyGroup GroupPolicy, statePlayerIndex int, preparingForPlayerIndex int, defaultPolicy Policy) interface{} {

	//We're going to collect all of the policies that apply.
	var applicablePolicies []Policy

	for group, policy := range policyGroup {
		policyApplies := false
		switch group {
		case GroupSelf:
			policyApplies = (statePlayerIndex == preparingForPlayerIndex)
		case GroupOther:
			policyApplies = (statePlayerIndex != preparingForPlayerIndex)
		case GroupAll:
			policyApplies = true
		default:
			//In the future we'll interrogate whether the given group index is
			//in the specified property at this point.
			panic("Unsupported policy group")
		}
		if policyApplies {
			applicablePolicies = append(applicablePolicies, policy)
		}
	}

	//Now calculate the LEAST restrictive of the policies that apply.
	effectivePolicy := PolicyVisible
	if len(applicablePolicies) > 0 {
		effectivePolicy = Policy(math.MaxInt64)
		for _, policy := range applicablePolicies {
			if policy < effectivePolicy {
				effectivePolicy = policy
			}
		}
	} else {
		effectivePolicy = defaultPolicy
	}

	return applyPolicy(effectivePolicy, prop, propType)
}

func randomBool() bool {
	r := rand.Intn(2)
	if r == 0 {
		return false
	}
	return true
}

func randomInt() int {
	return rand.Int()
}

func randomGrowableStack(stack *GrowableStack) *GrowableStack {
	result := stack.Copy()

	indexes := make([]int, rand.Intn(16))

	for i, _ := range indexes {
		indexes[i] = emptyIndexSentinel
	}

	result.indexes = indexes

	return result
}

func randomSizedStack(stack *SizedStack) *SizedStack {
	result := stack.Copy()

	indexes := make([]int, len(result.indexes))

	for i, _ := range indexes {
		if randomBool() {
			indexes[i] = emptyIndexSentinel
		} else {
			indexes[i] = genericComponentSentinel
		}
	}

	result.indexes = indexes

	return result
}

func applyPolicy(policy Policy, input interface{}, propType PropertyType) interface{} {
	if policy == PolicyVisible {
		return input
	}

	if policy == PolicyRandom {
		switch propType {
		case TypeBool:
			return randomBool()
		case TypeInt:
			return randomInt()
		case TypeString:
			//Note: unlike the other random*() functions, this is defined in
			//game for the purposes of creating an ID. That's sufficient for
			//this use.
			return randomString(16)
		case TypeGrowableStack:
			return randomGrowableStack(input.(*GrowableStack))
		case TypeSizedStack:
			return randomSizedStack(input.(*SizedStack))
		default:
			panic("Unknown property type for policy random")
		}
	}

	//Go through the propTypes where everythign that's not PolicyVisible is
	//effectively PolicyHidden.

	switch propType {
	case TypeBool:
		return false
	case TypeInt:
		return 0
	case TypeString:
		return ""
	}

	//Now we're left with len-properties.

	stack := input.(Stack)

	stack.applySanitizationPolicy(policy)

	return input

}

func (g *GrowableStack) applySanitizationPolicy(policy Policy) {

	if policy == PolicyVisible {
		return
	}

	if policy == PolicyLen {

		indexes := make([]int, len(g.indexes))

		for i := 0; i < len(indexes); i++ {
			indexes[i] = genericComponentSentinel
		}

		g.indexes = indexes
		return
	}

	if policy == PolicyHidden {
		g.indexes = make([]int, 0)
		return
	}

	if policy == PolicyNonEmpty {
		if g.NumComponents() == 0 {
			g.indexes = make([]int, 0)
		} else {
			g.indexes = []int{genericComponentSentinel}
		}

		return
	}

	panic("Unknown sanitization policy" + strconv.Itoa(int(policy)))

}

func (s *SizedStack) applySanitizationPolicy(policy Policy) {

	if policy == PolicyVisible {
		return
	}

	if policy == PolicyLen {

		indexes := make([]int, len(s.indexes))

		for i := 0; i < len(indexes); i++ {
			if s.indexes[i] == emptyIndexSentinel {
				indexes[i] = emptyIndexSentinel
			} else {
				indexes[i] = genericComponentSentinel
			}
		}

		s.indexes = indexes

		return
	}

	if policy == PolicyHidden || policy == PolicyNonEmpty {

		hasComponents := s.NumComponents() > 0

		indexes := make([]int, len(s.indexes))
		for i := 0; i < len(indexes); i++ {
			indexes[i] = -1
		}
		s.indexes = indexes

		if policy == PolicyNonEmpty && hasComponents {
			s.indexes[0] = genericComponentSentinel
		}

		return
	}

	panic("Unknown sanitization policy" + strconv.Itoa(int(policy)))
}
<|endoftext|>


<commit_before>// main.go
//
// Main source file for Scarlet.
//
package main

func main() {
	println("do something...")
	return
}<commit_msg>Look at your code, now back to me, now back to your code. What do you see? Your code now has a mainloop.<commit_after>// main.go
//
// Main source file for Scarlet.
//
package main

import (
	"os"
	"os/signal"
	"syscall"
)

var (
	systemSignals = make(chan os.Signal)
)

func main() {
	println("starting scarlet")
	startSignalListener()
	return
}

func startSignalListener() {
	signal.Notify(systemSignals, syscall.SIGINT, syscall.SIGKILL, syscall.SIGHUP)
	for {
		sig := <-systemSignals
		switch sig {
		case syscall.SIGINT:
			println("caught SIGINT; exiting")
			os.Exit(0)
		case syscall.SIGKILL:
			println("caught SIGKILL; exiting")
			os.Exit(0)
		case syscall.SIGHUP:
			println("caught SIHUP; reloading...")
			// call a function to reload the configuration, and
			// re-initialize any connections (if necessary)
		}
	}
}
<|endoftext|>


<commit_before>package io

import (
	"go-bots/ev3"
	"go-bots/scooba/config"
	"go-bots/scooba/logic"
	"time"
)

var devs *ev3.Devices
var data chan<- logic.Data
var commands <-chan logic.Commands

var ml, mr, mfl, mfr *ev3.Attribute
var irL, irFL, irFR, irR *ev3.Attribute

var ledRR, ledRG, ledLR, ledLG *ev3.Attribute

var start time.Time

// StartTime gets the time when the bot started
func StartTime() time.Time {
	return start
}

// Init initializes the io module
func Init(d chan<- logic.Data, s time.Time) {
	devs = ev3.Scan(&ev3.OutPortModes{
		OutA: ev3.OutPortModeDcMotor,
		OutB: ev3.OutPortModeAuto,
		OutC: ev3.OutPortModeAuto,
		OutD: ev3.OutPortModeDcMotor,
	})
	data = d
	start = s

	// Ir Left
	ev3.CheckDriver(devs.In2, ev3.DriverIr, ev3.In1)
	// Ir FrontLeft
	ev3.CheckDriver(devs.In4, ev3.DriverIr, ev3.In2)
	// Ir FrontRight
	ev3.CheckDriver(devs.In1, ev3.DriverIr, ev3.In3)
	// Ir Right
	ev3.CheckDriver(devs.In3, ev3.DriverIr, ev3.In4)

	// A front Left
	ev3.CheckDriver(devs.OutA, ev3.DriverRcxMotor, ev3.OutA)
	frontRight := devs.OutA
	// B left direct
	ev3.CheckDriver(devs.OutB, ev3.DriverTachoMotorLarge, ev3.OutB)
	// C right direct
	ev3.CheckDriver(devs.OutC, ev3.DriverTachoMotorLarge, ev3.OutC)
	// D front Right
	ev3.CheckDriver(devs.OutD, ev3.DriverRcxMotor, ev3.OutD)
	frontLeft := devs.OutD

	ev3.SetMode(devs.In1, ev3.IrModeProx)
	ev3.SetMode(devs.In2, ev3.IrModeProx)
	ev3.SetMode(devs.In3, ev3.IrModeProx)
	ev3.SetMode(devs.In4, ev3.IrModeProx)

	ev3.RunCommand(devs.OutA, ev3.CmdStop)
	ev3.RunCommand(devs.OutB, ev3.CmdReset)
	ev3.RunCommand(devs.OutC, ev3.CmdReset)
	ev3.RunCommand(devs.OutD, ev3.CmdStop)

	irL = ev3.OpenByteR(devs.In2, ev3.BinData)
	irFL = ev3.OpenByteR(devs.In4, ev3.BinData)
	irFR = ev3.OpenByteR(devs.In1, ev3.BinData)
	irR = ev3.OpenByteR(devs.In3, ev3.BinData)
	// A front Left
	mfl = ev3.OpenTextW(devs.OutA, ev3.DutyCycleSp)
	// B left direct
	ml = ev3.OpenTextW(devs.OutB, ev3.DutyCycleSp)
	// C right direct
	mr = ev3.OpenTextW(devs.OutC, ev3.DutyCycleSp)
	// D front Right
	mfr = ev3.OpenTextW(devs.OutD, ev3.DutyCycleSp)

	ledLG = ev3.OpenTextW(devs.LedLeftGreen, ev3.Brightness)
	ledLR = ev3.OpenTextW(devs.LedLeftRed, ev3.Brightness)
	ledRG = ev3.OpenTextW(devs.LedRightGreen, ev3.Brightness)
	ledRR = ev3.OpenTextW(devs.LedRightRed, ev3.Brightness)
	ledLG.Value = 0
	ledLR.Value = 0
	ledRG.Value = 0
	ledRR.Value = 0
	ledLG.Sync()
	ledLR.Sync()
	ledRG.Sync()
	ledRR.Sync()

	// Wheels
	mr.Value = 0
	ml.Value = 0
	mr.Sync()
	ml.Sync()
	ev3.RunCommand(devs.OutC, ev3.CmdReset)
	ev3.RunCommand(devs.OutD, ev3.CmdReset)
	ev3.RunCommand(devs.OutC, ev3.CmdRunDirect)
	ev3.RunCommand(devs.OutD, ev3.CmdRunDirect)

	// front Left
	mfl.Value = 0
	mfl.Sync()
	ev3.RunCommand(frontLeft, ev3.CmdStop)
	ev3.RunCommand(frontLeft, ev3.CmdRunDirect)

	// front Right
	mfr.Value = 0
	mfr.Sync()
	ev3.RunCommand(frontRight, ev3.CmdStop)
	ev3.RunCommand(frontRight, ev3.CmdRunDirect)
}

var speedL, speedR int
var lastMillis, currentMillis int

// ProcessCommand process the commands
func ProcessCommand(c *logic.Commands) {
	currentMillis = c.Millis
	lastMillis = currentMillis

	mlValue := speedL / 100
	mrValue := speedR / 100
	if mlValue > 100 {
		mlValue = 100
	}
	if mlValue < -100 {
		mlValue = -100
	}
	if mrValue > 100 {
		mrValue = 100
	}
	if mrValue < -100 {
		mrValue = -100
	}
	ml.Value = mlValue
	mr.Value = mrValue
	ml.Sync()
	mr.Sync()

	ledLG.Value = c.LedLeftGreen
	ledLR.Value = c.LedLeftRed
	ledRG.Value = c.LedRightGreen
	ledRR.Value = c.LedRightRed
	ledLG.Sync()
	ledLR.Sync()
	ledRG.Sync()
	ledRR.Sync()

	mfl.Value = -config.FrontWheelsSpeed
	mfr.Value = config.FrontWheelsSpeed

	mfl.Sync()
	mfr.Sync()
}

// Loop contains the io loop
func Loop() {
	for {
		now := time.Now()
		millis := ev3.TimespanAsMillis(start, now)

		irL.Sync()
		irFL.Sync()
		irFR.Sync()
		irR.Sync()

		// fmt.Fprintln(os.Stderr, "DATA", irL.Value, irFL.Value, irFR.Value, irR.Value)

		data <- logic.Data{
			Start:             start,
			Millis:            millis,
			IrValueLeft:       irL.Value,
			IrValueFrontLeft:  irFL.Value,
			IrValueFrontRight: irFR.Value,
			IrValueRight:      irR.Value,
		}
	}
}

// Close terminates and cleans up the io module
func Close() {
	defer ev3.RunCommand(devs.OutA, ev3.CmdStop)
	defer ev3.RunCommand(devs.OutB, ev3.CmdReset)
	defer ev3.RunCommand(devs.OutC, ev3.CmdReset)
	defer ev3.RunCommand(devs.OutD, ev3.CmdStop)

	defer ev3.RunCommand(devs.OutB, ev3.CmdStop)
	defer ev3.RunCommand(devs.OutC, ev3.CmdStop)

	ledLG.Value = 0
	ledLR.Value = 0
	ledRG.Value = 0
	ledRR.Value = 0
	ledLG.Sync()
	ledLR.Sync()
	ledRG.Sync()
	ledRR.Sync()

	// TODO: close all files
	// mfl, mlr, ml, mr
	// irL, irFL, irFR, irR
}
<commit_msg>Fixed IR ports.<commit_after>package io

import (
	"go-bots/ev3"
	"go-bots/scooba/config"
	"go-bots/scooba/logic"
	"time"
)

var devs *ev3.Devices
var data chan<- logic.Data
var commands <-chan logic.Commands

var ml, mr, mfl, mfr *ev3.Attribute
var irL, irFL, irFR, irR *ev3.Attribute

var ledRR, ledRG, ledLR, ledLG *ev3.Attribute

var start time.Time

// StartTime gets the time when the bot started
func StartTime() time.Time {
	return start
}

// Init initializes the io module
func Init(d chan<- logic.Data, s time.Time) {
	devs = ev3.Scan(&ev3.OutPortModes{
		OutA: ev3.OutPortModeDcMotor,
		OutB: ev3.OutPortModeAuto,
		OutC: ev3.OutPortModeAuto,
		OutD: ev3.OutPortModeDcMotor,
	})
	data = d
	start = s

	// Ir Left
	ev3.CheckDriver(devs.In2, ev3.DriverIr, ev3.In2)
	// Ir FrontLeft
	ev3.CheckDriver(devs.In4, ev3.DriverIr, ev3.In4)
	// Ir FrontRight
	ev3.CheckDriver(devs.In1, ev3.DriverIr, ev3.In1)
	// Ir Right
	ev3.CheckDriver(devs.In3, ev3.DriverIr, ev3.In3)

	// A front Left
	ev3.CheckDriver(devs.OutA, ev3.DriverRcxMotor, ev3.OutA)
	frontRight := devs.OutA
	// B left direct
	ev3.CheckDriver(devs.OutB, ev3.DriverTachoMotorLarge, ev3.OutB)
	// C right direct
	ev3.CheckDriver(devs.OutC, ev3.DriverTachoMotorLarge, ev3.OutC)
	// D front Right
	ev3.CheckDriver(devs.OutD, ev3.DriverRcxMotor, ev3.OutD)
	frontLeft := devs.OutD

	ev3.SetMode(devs.In1, ev3.IrModeProx)
	ev3.SetMode(devs.In2, ev3.IrModeProx)
	ev3.SetMode(devs.In3, ev3.IrModeProx)
	ev3.SetMode(devs.In4, ev3.IrModeProx)

	ev3.RunCommand(devs.OutA, ev3.CmdStop)
	ev3.RunCommand(devs.OutB, ev3.CmdReset)
	ev3.RunCommand(devs.OutC, ev3.CmdReset)
	ev3.RunCommand(devs.OutD, ev3.CmdStop)

	irL = ev3.OpenByteR(devs.In2, ev3.BinData)
	irFL = ev3.OpenByteR(devs.In4, ev3.BinData)
	irFR = ev3.OpenByteR(devs.In1, ev3.BinData)
	irR = ev3.OpenByteR(devs.In3, ev3.BinData)
	// A front Left
	mfl = ev3.OpenTextW(devs.OutA, ev3.DutyCycleSp)
	// B left direct
	ml = ev3.OpenTextW(devs.OutB, ev3.DutyCycleSp)
	// C right direct
	mr = ev3.OpenTextW(devs.OutC, ev3.DutyCycleSp)
	// D front Right
	mfr = ev3.OpenTextW(devs.OutD, ev3.DutyCycleSp)

	ledLG = ev3.OpenTextW(devs.LedLeftGreen, ev3.Brightness)
	ledLR = ev3.OpenTextW(devs.LedLeftRed, ev3.Brightness)
	ledRG = ev3.OpenTextW(devs.LedRightGreen, ev3.Brightness)
	ledRR = ev3.OpenTextW(devs.LedRightRed, ev3.Brightness)
	ledLG.Value = 0
	ledLR.Value = 0
	ledRG.Value = 0
	ledRR.Value = 0
	ledLG.Sync()
	ledLR.Sync()
	ledRG.Sync()
	ledRR.Sync()

	// Wheels
	mr.Value = 0
	ml.Value = 0
	mr.Sync()
	ml.Sync()
	ev3.RunCommand(devs.OutC, ev3.CmdReset)
	ev3.RunCommand(devs.OutD, ev3.CmdReset)
	ev3.RunCommand(devs.OutC, ev3.CmdRunDirect)
	ev3.RunCommand(devs.OutD, ev3.CmdRunDirect)

	// front Left
	mfl.Value = 0
	mfl.Sync()
	ev3.RunCommand(frontLeft, ev3.CmdStop)
	ev3.RunCommand(frontLeft, ev3.CmdRunDirect)

	// front Right
	mfr.Value = 0
	mfr.Sync()
	ev3.RunCommand(frontRight, ev3.CmdStop)
	ev3.RunCommand(frontRight, ev3.CmdRunDirect)
}

var speedL, speedR int
var lastMillis, currentMillis int

// ProcessCommand process the commands
func ProcessCommand(c *logic.Commands) {
	currentMillis = c.Millis
	lastMillis = currentMillis

	mlValue := speedL / 100
	mrValue := speedR / 100
	if mlValue > 100 {
		mlValue = 100
	}
	if mlValue < -100 {
		mlValue = -100
	}
	if mrValue > 100 {
		mrValue = 100
	}
	if mrValue < -100 {
		mrValue = -100
	}
	ml.Value = mlValue
	mr.Value = mrValue
	ml.Sync()
	mr.Sync()

	ledLG.Value = c.LedLeftGreen
	ledLR.Value = c.LedLeftRed
	ledRG.Value = c.LedRightGreen
	ledRR.Value = c.LedRightRed
	ledLG.Sync()
	ledLR.Sync()
	ledRG.Sync()
	ledRR.Sync()

	mfl.Value = -config.FrontWheelsSpeed
	mfr.Value = config.FrontWheelsSpeed

	mfl.Sync()
	mfr.Sync()
}

// Loop contains the io loop
func Loop() {
	for {
		now := time.Now()
		millis := ev3.TimespanAsMillis(start, now)

		irL.Sync()
		irFL.Sync()
		irFR.Sync()
		irR.Sync()

		// fmt.Fprintln(os.Stderr, "DATA", irL.Value, irFL.Value, irFR.Value, irR.Value)

		data <- logic.Data{
			Start:             start,
			Millis:            millis,
			IrValueLeft:       irL.Value,
			IrValueFrontLeft:  irFL.Value,
			IrValueFrontRight: irFR.Value,
			IrValueRight:      irR.Value,
		}
	}
}

// Close terminates and cleans up the io module
func Close() {
	defer ev3.RunCommand(devs.OutA, ev3.CmdStop)
	defer ev3.RunCommand(devs.OutB, ev3.CmdReset)
	defer ev3.RunCommand(devs.OutC, ev3.CmdReset)
	defer ev3.RunCommand(devs.OutD, ev3.CmdStop)

	defer ev3.RunCommand(devs.OutB, ev3.CmdStop)
	defer ev3.RunCommand(devs.OutC, ev3.CmdStop)

	ledLG.Value = 0
	ledLR.Value = 0
	ledRG.Value = 0
	ledRR.Value = 0
	ledLG.Sync()
	ledLR.Sync()
	ledRG.Sync()
	ledRR.Sync()

	// TODO: close all files
	// mfl, mlr, ml, mr
	// irL, irFL, irFR, irR
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/github/git-media/gitmedia"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	BuildOS   = flag.String("os", "", "OS to target: darwin, freebsd, linux, windows")
	BuildArch = flag.String("arch", "", "Arch to target: 386, amd64")
	BuildAll  = flag.Bool("all", false, "Builds all architectures")
	ShowHelp  = flag.Bool("help", false, "Shows help")
)

func main() {
	cmd, err := exec.Command("script/fmt").Output()
	if err != nil {
		panic(err)
	}

	if len(cmd) > 0 {
		fmt.Println(string(cmd))
	}

	flag.Parse()
	if *ShowHelp {
		fmt.Println("usage: script/bootstrap [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	if *BuildAll {
		for _, buildos := range []string{"darwin", "freebsd", "linux", "windows"} {
			for _, buildarch := range []string{"386", "amd64"} {
				build(buildos, buildarch)
			}
		}
	} else {
		build(*BuildOS, *BuildArch)
	}
}

func build(buildos, buildarch string) {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := "git-media-" + gitmedia.Version
	dir := "bin"

	if addenv {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
		dir = filepath.Join(dir, "releases", buildos+"-"+buildarch, name)
	}

	filepath.Walk("cmd", func(path string, info os.FileInfo, err error) error {
		if !strings.HasSuffix(path, ".go") {
			return nil
		}

		cmd := filepath.Base(path)
		cmd = cmd[0 : len(cmd)-3]
		return buildCommand(path, dir, buildos, buildarch)
	})

	if addenv {
		err := os.MkdirAll(dir, 0755)
		if err != nil {
			fmt.Println("Error setting up installer:\n", err.Error())
			return
		}

		err = setupInstaller(buildos, buildarch, dir)
		if err != nil {
			fmt.Println("Error setting up installer:\n", err.Error())
		}
	}
}

func buildCommand(path, dir, buildos, buildarch string) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := filepath.Base(path)
	name = name[0 : len(name)-3]

	bin := filepath.Join(dir, name)

	if buildos == "windows" {
		bin = bin + ".exe"
	}

	cmd := exec.Command("go", "build", "-o", bin, path)
	if addenv {
		cmd.Env = []string{
			"GOOS=" + buildos,
			"GOARCH=" + buildarch,
			"GOPATH=" + os.Getenv("GOPATH"),
			"GOROOT=" + os.Getenv("GOROOT"),
		}
	}

	output, err := cmd.CombinedOutput()
	if len(output) > 0 {
		fmt.Println(string(output))
	}
	return err
}

func setupInstaller(buildos, buildarch, dir string) error {
	if buildos == "windows" {
		return winInstaller(buildos, buildarch, dir)
	} else {
		return unixInstaller(buildos, buildarch, dir)
	}
}

func unixInstaller(buildos, buildarch, dir string) error {
	fullInstallPath := filepath.Join(dir, "install.sh")
	cmd := exec.Command("cp", "script/install.sh.example", fullInstallPath)
	if err := logAndRun(cmd); err != nil {
		return err
	}

	if err := os.Chmod(fullInstallPath, 0755); err != nil {
		return err
	}

	name := zipName(buildos, buildarch) + ".tar.gz"
	cmd = exec.Command("tar", "czf", "../"+name, filepath.Base(dir))
	cmd.Dir = filepath.Dir(dir)
	return logAndRun(cmd)
}

func winInstaller(buildos, buildarch, dir string) error {
	cmd := exec.Command("cp", "script/install.bat.example", filepath.Join(dir, "install.bat"))
	if err := logAndRun(cmd); err != nil {
		return err
	}

	installerPath := filepath.Dir(filepath.Dir(dir))

	name := zipName(buildos, buildarch) + ".zip"
	full := filepath.Join(installerPath, name)
	matches, err := filepath.Glob(dir + "/*")
	if err != nil {
		return err
	}

	args := make([]string, len(matches)+2)
	args[0] = "-j" // junk the zip paths
	args[1] = full
	copy(args[2:], matches)

	cmd = exec.Command("zip", args...)
	return logAndRun(cmd)
}

func logAndRun(cmd *exec.Cmd) error {
	fmt.Printf(" - %s\n", strings.Join(cmd.Args, " "))
	if len(cmd.Dir) > 0 {
		fmt.Printf("   - in %s\n", cmd.Dir)
	}

	output, err := cmd.CombinedOutput()
	fmt.Println(string(output))
	return err
}

func zipName(os, arch string) string {
	return fmt.Sprintf("git-media-%s-%s-%s", os, arch, gitmedia.Version)
}
<commit_msg>ンンンンン ンンンン<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/github/git-media/gitmedia"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	BuildOS    = flag.String("os", "", "OS to target: darwin, freebsd, linux, windows")
	BuildArch  = flag.String("arch", "", "Arch to target: 386, amd64")
	BuildAll   = flag.Bool("all", false, "Builds all architectures")
	ShowHelp   = flag.Bool("help", false, "Shows help")
	matrixKeys = map[string]string{
		"darwin":  "Mac",
		"freebsd": "FreeBSD",
		"linux":   "Linux",
		"windows": "Windows",
		"amd64":   "AMD64",
	}
)

type Release struct {
	Label    string
	Filename string
}

func main() {
	cmd, err := exec.Command("script/fmt").Output()
	if err != nil {
		panic(err)
	}

	if len(cmd) > 0 {
		fmt.Println(string(cmd))
	}

	flag.Parse()
	if *ShowHelp {
		fmt.Println("usage: script/bootstrap [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	buildMatrix := make(map[string]Release)

	errored := false

	if *BuildAll {
		for _, buildos := range []string{"darwin", "freebsd", "linux", "windows"} {
			for _, buildarch := range []string{"386", "amd64"} {
				if err := build(buildos, buildarch, buildMatrix); err != nil {
					errored = true
				}
			}
		}
	} else {
		build(*BuildOS, *BuildArch, buildMatrix)
		errored = true // skip build matrix stuff
	}

	if !errored {
		by, err := json.Marshal(buildMatrix)
		if err != nil {
			fmt.Println("Error encoding build matrix to json:", err)
			return
		}

		file, err := os.Create("bin/releases/build_matrix.json")
		if err != nil {
			fmt.Println("Error creating build_matrix.json:", err)
			return
		}

		written, err := file.Write(by)
		file.Close()

		if err != nil {
			fmt.Println("Error writing build_matrix.json", err)
			return
		}

		if jsonSize := len(by); written != jsonSize {
			fmt.Printf("Expected to write %d bytes, actually wrote %d.\n", jsonSize, written)
		}
	}
}

func build(buildos, buildarch string, buildMatrix map[string]Release) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := "git-media-" + gitmedia.Version
	dir := "bin"

	if addenv {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
		dir = filepath.Join(dir, "releases", buildos+"-"+buildarch, name)
	}

	filepath.Walk("cmd", func(path string, info os.FileInfo, err error) error {
		if !strings.HasSuffix(path, ".go") {
			return nil
		}

		cmd := filepath.Base(path)
		cmd = cmd[0 : len(cmd)-3]
		return buildCommand(path, dir, buildos, buildarch)
	})

	if addenv {
		err := os.MkdirAll(dir, 0755)
		if err != nil {
			fmt.Println("Error setting up installer:\n", err.Error())
			return err
		}

		err = setupInstaller(buildos, buildarch, dir, buildMatrix)
		if err != nil {
			fmt.Println("Error setting up installer:\n", err.Error())
			return err
		}
	}

	return nil
}

func buildCommand(path, dir, buildos, buildarch string) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := filepath.Base(path)
	name = name[0 : len(name)-3]

	bin := filepath.Join(dir, name)

	if buildos == "windows" {
		bin = bin + ".exe"
	}

	cmd := exec.Command("go", "build", "-o", bin, path)
	if addenv {
		cmd.Env = []string{
			"GOOS=" + buildos,
			"GOARCH=" + buildarch,
			"GOPATH=" + os.Getenv("GOPATH"),
			"GOROOT=" + os.Getenv("GOROOT"),
		}
	}

	output, err := cmd.CombinedOutput()
	if len(output) > 0 {
		fmt.Println(string(output))
	}
	return err
}

func setupInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	if buildos == "windows" {
		return winInstaller(buildos, buildarch, dir, buildMatrix)
	} else {
		return unixInstaller(buildos, buildarch, dir, buildMatrix)
	}
}

func unixInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	fullInstallPath := filepath.Join(dir, "install.sh")
	cmd := exec.Command("cp", "script/install.sh.example", fullInstallPath)
	if err := logAndRun(cmd); err != nil {
		return err
	}

	if err := os.Chmod(fullInstallPath, 0755); err != nil {
		return err
	}

	name := zipName(buildos, buildarch) + ".tar.gz"

	addToMatrix(buildMatrix, buildos, buildarch, name)

	cmd = exec.Command("tar", "czf", "../"+name, filepath.Base(dir))
	cmd.Dir = filepath.Dir(dir)
	return logAndRun(cmd)
}

func addToMatrix(buildMatrix map[string]Release, buildos, buildarch, name string) {
	buildMatrix[fmt.Sprintf("%s-%s", buildos, buildarch)] = Release{
		Label:    releaseLabel(buildos, buildarch),
		Filename: name,
	}
}

func winInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	cmd := exec.Command("cp", "script/install.bat.example", filepath.Join(dir, "install.bat"))
	if err := logAndRun(cmd); err != nil {
		return err
	}

	installerPath := filepath.Dir(filepath.Dir(dir))

	name := zipName(buildos, buildarch) + ".zip"
	full := filepath.Join(installerPath, name)
	matches, err := filepath.Glob(dir + "/*")
	if err != nil {
		return err
	}

	addToMatrix(buildMatrix, buildos, buildarch, name)

	args := make([]string, len(matches)+2)
	args[0] = "-j" // junk the zip paths
	args[1] = full
	copy(args[2:], matches)

	cmd = exec.Command("zip", args...)
	return logAndRun(cmd)
}

func logAndRun(cmd *exec.Cmd) error {
	fmt.Printf(" - %s\n", strings.Join(cmd.Args, " "))
	if len(cmd.Dir) > 0 {
		fmt.Printf("   - in %s\n", cmd.Dir)
	}

	output, err := cmd.CombinedOutput()
	fmt.Println(string(output))
	return err
}

func zipName(os, arch string) string {
	return fmt.Sprintf("git-media-%s-%s-%s", os, arch, gitmedia.Version)
}

func releaseLabel(buildos, buildarch string) string {
	return fmt.Sprintf("%s %s", key(buildos), key(buildarch))
}

func key(k string) string {
	if s, ok := matrixKeys[k]; ok {
		return s
	}
	return k
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	BuildOS   = flag.String("os", "", "OS to target: darwin, freebsd, linux, windows")
	BuildArch = flag.String("arch", "", "Arch to target: 386, amd64")
	BuildAll  = flag.Bool("all", false, "Builds all architectures")
	ShowHelp  = flag.Bool("help", false, "Shows help")
)

func main() {
	cmd, err := exec.Command("script/fmt").Output()
	if err != nil {
		panic(err)
	}

	if len(cmd) > 0 {
		fmt.Println(string(cmd))
	}

	flag.Parse()
	if *ShowHelp {
		fmt.Println("usage: script/build [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	if *BuildAll {
		for _, buildos := range []string{"darwin", "freebsd", "linux", "windows"} {
			for _, buildarch := range []string{"386", "amd64"} {
				build(buildos, buildarch)
			}
		}
	} else {
		build(*BuildOS, *BuildArch)
	}
}

func build(buildos, buildarch string) {
	if len(buildos) > 0 && len(buildarch) > 0 {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
	}

	filepath.Walk("cmd", func(path string, info os.FileInfo, err error) error {
		if !strings.HasSuffix(path, ".go") {
			return nil
		}

		return buildCommand(path, buildos, buildarch)
	})
}

func buildCommand(path, buildos, buildarch string) error {
	base := filepath.Base(path)
	base = base[0 : len(base)-3]
	dir := "bin"
	addenv := len(buildos) > 0 && len(buildarch) > 0

	if addenv {
		dir = filepath.Join(dir, buildos+"-"+buildarch)
	}

	bin := filepath.Join(dir, base)

	cmd := exec.Command("go", "build", "-o", bin, path)
	var out bytes.Buffer
	cmd.Stderr = &out
	if addenv {
		cmd.Env = []string{"GOOS=" + buildos, "GOARCH=" + buildarch, "GOPATH=" + os.Getenv("GOPATH")}
	}

	if err := cmd.Run(); err != nil {
		fmt.Println(out.String())
		return err
	}

	return nil
}
<commit_msg>ラララララ ラー ララララー<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	BuildOS   = flag.String("os", "", "OS to target: darwin, freebsd, linux, windows")
	BuildArch = flag.String("arch", "", "Arch to target: 386, amd64")
	BuildAll  = flag.Bool("all", false, "Builds all architectures")
	ShowHelp  = flag.Bool("help", false, "Shows help")
)

func main() {
	cmd, err := exec.Command("script/fmt").Output()
	if err != nil {
		panic(err)
	}

	if len(cmd) > 0 {
		fmt.Println(string(cmd))
	}

	flag.Parse()
	if *ShowHelp {
		fmt.Println("usage: script/build [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	if *BuildAll {
		for _, buildos := range []string{"darwin", "freebsd", "linux", "windows"} {
			for _, buildarch := range []string{"386", "amd64"} {
				build(buildos, buildarch)
			}
		}
	} else {
		build(*BuildOS, *BuildArch)
	}
}

func build(buildos, buildarch string) {
	if len(buildos) > 0 && len(buildarch) > 0 {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
	}

	filepath.Walk("cmd", func(path string, info os.FileInfo, err error) error {
		if !strings.HasSuffix(path, ".go") {
			return nil
		}

		return buildCommand(path, buildos, buildarch)
	})
}

func buildCommand(path, buildos, buildarch string) error {
	base := filepath.Base(path)
	base = base[0 : len(base)-3]
	dir := "bin"
	addenv := len(buildos) > 0 && len(buildarch) > 0

	if addenv {
		dir = filepath.Join(dir, buildos+"-"+buildarch)
	}

	bin := filepath.Join(dir, base)

	cmd := exec.Command("go", "build", "-o", bin, path)
	var out bytes.Buffer
	cmd.Stderr = &out
	if addenv {
		cmd.Env = []string{
			"GOOS=" + buildos,
			"GOARCH=" + buildarch,
			"GOPATH=" + os.Getenv("GOPATH"),
		}
	}

	if err := cmd.Run(); err != nil {
		fmt.Println(out.String())
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package flow

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
	"sync"
	"time"

	"encoding/json"

	"github.com/codelingo/lingo/app/util"
	"github.com/common-nighthawk/go-figure"
	"github.com/golang/protobuf/proto"
	"github.com/juju/errors"
	"github.com/urfave/cli"
)

type DecoratedResult struct {

	// Ctx are the flags and args parsed from the decorated fact
	Ctx *cli.Context

	// Payload is the struct returned form the flow server for the matched
	// fact.
	Payload proto.Message
}

type flowRunner struct {
	cliCtx           *cli.Context
	cliApp           *CLIApp
	decoratorApp     *DecoratorApp
	decoratedResultc chan *DecoratedResult
	errc             chan error
}

type CLIApp struct {
	cli.App
	Request func(*cli.Context) (chan proto.Message, <-chan *UserVar, chan error, func(), error)

	// Help data
	Tagline string
}

type DecoratorApp struct {
	cli.App
	ConfirmDecorated func(*cli.Context, proto.Message) (bool, error)
	SetUserVar       func(*UserVar)

	// Help info
	DecoratorUsage   string
	DecoratorExample string
}

func NewFlow(cliApp *CLIApp, decoratorApp *DecoratorApp) *flowRunner {

	// setBaseApp overrides Action with the help action. We don't want this
	// and either want nil or the action that was already set on
	// CLIApp.Action.
	action := cliApp.Action
	setBaseApp(cliApp)
	cliApp.Action = action

	fRunner := &flowRunner{
		cliApp:       cliApp,
		decoratorApp: decoratorApp,
		errc:         make(chan error),
	}
	if fRunner.decoratorApp != nil {
		fRunner.decoratorApp.Setup()
	}
	fRunner.setHelp()

	if fRunner.cliApp.Action == nil {

		// If Action is set, decoratedResultc is not used, nor is it closed.
		// Only set it if we're calling the internal flowRunner.action.
		fRunner.decoratedResultc = make(chan *DecoratedResult)
		fRunner.cliApp.Action = fRunner.action
	}
	return fRunner
}

func (f *flowRunner) setHelp() {
	cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
		defer func() {

			// decoratedResultc is nil if the Flow is using it's own action.
			if f.decoratedResultc != nil {
				close(f.decoratedResultc)
			}
		}()
		figure.NewFigure("codelingo", "larry3d", false).Print()
		printHelp(w, CLIAPPHELPTMP, f.cliApp)
		if f.decoratorApp != nil {
			printHelp(w, DECAPPHELPTMP, f.decoratorApp)
		}
		printHelp(w, INFOTMP, f.cliApp)
	}

	f.cliApp.Commands = append(f.cliApp.Commands, cli.Command{
		Name: "_genJSONHelp",
		Action: func(ctx *cli.Context) {

			defer func() {
				// decoratedResultc is nil if the Flow is using it's own action.
				if f.decoratedResultc != nil {
					close(f.decoratedResultc)
				}
			}()

			var cliFlags, decFlags []string
			for _, flag := range f.cliApp.Flags {
				cliFlags = append(cliFlags, flag.String())
			}
			if f.decoratorApp != nil {
				for _, flag := range f.decoratorApp.Flags {
					decFlags = append(decFlags, flag.String())
				}
			}

			helpData := helpData{
				Name:         f.cliApp.Name,
				Tagline:      f.cliApp.Tagline,
				Description:  f.cliApp.Usage,
				LastCompiled: f.cliApp.Compiled.String(),
				Version:      f.cliApp.Version,

				Options: decFlags,
			}

			if f.decoratorApp != nil {
				helpData.Decorator = decHelp{
					Description: f.decoratorApp.Usage,
					// Example: f.decoratorApp.Example,
					Options: decFlags,
				}
			}

			b, err := json.Marshal(helpData)
			if err != nil {
				fmt.Println(err)
				return
			}

			outPath := ctx.Args()[0]
			if err := ioutil.WriteFile(outPath, b, 0644); err != nil {
				fmt.Println(err)
				return
			}
			fmt.Println("Success! JSON Help written to:", outPath)

		},
	})

}

func setBaseApp(cliApp *CLIApp) {

	app := CLIApp{
		App:     *cli.NewApp(),
		Request: cliApp.Request,
	}

	// base settings
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "lingo-file",
			Usage: "the Tenet `FILE` to run rewrite over. If the flag is not set, codelingo.yaml files are read from the branch being rewritten.",
			//	Destination: &language,
		},
	}
	app.Compiled = time.Now()
	app.EnableBashCompletion = true

	// user settings
	app.Name = cliApp.Name
	app.Tagline = cliApp.Tagline
	app.Usage = cliApp.Usage
	app.Flags = append(app.Flags, cliApp.Flags...)
	app.Action = cliApp.Action
	app.Version = cliApp.Version

	*cliApp = app
}

// TODO(waigani) incorrect usage func

func (f *flowRunner) Run() (chan *DecoratedResult, chan error) {

	go func() {
		defer close(f.errc)
		if err := f.cliApp.Run(os.Args); err != nil {
			f.errc <- err
		}
	}()

	return f.decoratedResultc, f.errc
}

func (f *flowRunner) action(ctx *cli.Context) {
	if err := f.command(ctx); err != nil {
		util.Logger.Debugw(errors.ErrorStack(err))
		util.FatalOSErr(err)
	}

}

func (f *flowRunner) command(ctx *cli.Context) (err error) {
	wg := sync.WaitGroup{}
	wg.Add(1)
	defer func() {
		wg.Wait()
		close(f.decoratedResultc)
	}()
	defer wg.Done()

	resultc, userVarC, errc, cancel, err := f.RunCLI()
	if err != nil {
		return errors.Trace(err)
	}

	// If user is manually confirming results, set a long timeout.
	timeout := time.After(time.Hour * 1)
l:
	for {
		select {
		case err, ok := <-errc:
			if !ok {
				errc = nil
				break
			}

			util.Logger.Debugf("Result error: %s", errors.ErrorStack(err))
			return errors.Trace(err)
		case v, ok := <-userVarC:
			if !ok {
				userVarC = nil
				break
			}

			f.SetUserVar(v)
		case result, ok := <-resultc:
			if !ok {
				resultc = nil
				break
			}

			// TODO(waigani) this is brittle and expects the result struct to have a
			// DecoratorOptions field. We need to refactor the Flow server to return a
			// tuple of [<decorator>, <result>].
			decorator := reflect.Indirect(reflect.ValueOf(result)).FieldByName("DecoratorOptions").String()

			var keep bool
			if ctx.Bool("keep-all") {
				keep = true
			} else {
				keep, err = f.ConfirmDecorated(decorator, result)
				if err != nil {
					cancel()
					return errors.Trace(err)
				}
			}

			if keep {
				wg.Add(1)
				go func(string, proto.Message) {
					defer wg.Done()
					ctx, err := NewCtx(&f.decoratorApp.App, strings.Split(decorator, " ")[1:]...)
					if err != nil {
						cancel()
						f.errc <- err
						util.Logger.Fatalf("error getting decorated context: %q", err)
						return
					}
					util.Logger.Debug("sending result to Flow...")
					f.decoratedResultc <- &DecoratedResult{
						Ctx:     ctx,
						Payload: result,
					}
					util.Logger.Debug("...result sent to Flow")

				}(decorator, result)

			}

		case <-timeout:
			cancel()
			return errors.New("timed out waiting for issue")
		}
		if resultc == nil && errc == nil && userVarC == nil {
			break l
		}
	}

	return
}

func (f *flowRunner) CliCtx() (*cli.Context, error) {
	if f.cliCtx == nil {
		ctx, err := NewCtx(&f.cliApp.App, os.Args[1:]...)
		if err != nil {
			return nil, errors.Trace(err)
		}
		f.cliCtx = ctx
	}
	return f.cliCtx, nil
}

func (f *flowRunner) RunCLI() (chan proto.Message, <-chan *UserVar, chan error, func(), error) {
	ctx, err := f.CliCtx()
	if err != nil {
		return nil, nil, nil, nil, errors.Trace(err)
	}
	if ctx.Bool("debug") {
		err := util.SetDebugLogger()
		if err != nil {
			return nil, nil, nil, nil, errors.Trace(err)
		}
	}

	return f.cliApp.Request(ctx)
}

func (f *flowRunner) ConfirmDecorated(decorator string, payload proto.Message) (bool, error) {
	ctx, err := NewCtx(&f.decoratorApp.App, strings.Split(decorator, " ")...)
	if err != nil {
		return false, errors.Trace(err)
	}

	return f.decoratorApp.ConfirmDecorated(ctx, payload)
}

func (f *flowRunner) SetUserVar(userVar *UserVar) {
	f.decoratorApp.SetUserVar(userVar)
}

func NewCtx(app *cli.App, input ...string) (*cli.Context, error) {

	fSet := flag.NewFlagSet(app.Name, flag.ContinueOnError)
	for _, flag := range app.Flags {
		flag.Apply(fSet)
	}

	if err := fSet.Parse(input); err != nil {
		return nil, errors.Trace(err)
	}
	if err := normalizeFlags(app.Flags, fSet); err != nil {
		return nil, errors.Trace(err)
	}

	ctx := cli.NewContext(nil, fSet, nil)

	return ctx, nil
}

func normalizeFlags(flags []cli.Flag, set *flag.FlagSet) error {
	visited := make(map[string]bool)
	set.Visit(func(f *flag.Flag) {
		visited[f.Name] = true
	})
	for _, f := range flags {
		parts := strings.Split(f.GetName(), ",")
		if len(parts) == 1 {
			continue
		}
		var ff *flag.Flag
		for _, name := range parts {
			name = strings.Trim(name, " ")
			if visited[name] {
				if ff != nil {
					return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
				}
				ff = set.Lookup(name)
			}
		}
		if ff == nil {
			continue
		}
		for _, name := range parts {
			name = strings.Trim(name, " ")
			if !visited[name] {
				copyFlag(name, ff, set)
			}
		}
	}
	return nil
}

func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
	switch ff.Value.(type) {
	case *cli.StringSlice:
	default:
		set.Set(name, ff.Value.String())
	}
}
<commit_msg>Add comments to explain flow sdk.<commit_after>package flow

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
	"sync"
	"time"

	"encoding/json"

	"github.com/codelingo/lingo/app/util"
	"github.com/common-nighthawk/go-figure"
	"github.com/golang/protobuf/proto"
	"github.com/juju/errors"
	"github.com/urfave/cli"
)

type DecoratedResult struct {

	// Ctx are the flags and args parsed from the decorated fact
	Ctx *cli.Context

	// Payload is the struct returned form the flow server for the matched
	// fact.
	Payload proto.Message
}

type flowRunner struct {
	cliCtx           *cli.Context
	cliApp           *CLIApp
	decoratorApp     *DecoratorApp
	decoratedResultc chan *DecoratedResult
	errc             chan error
}

type CLIApp struct {
	cli.App
	Request func(*cli.Context) (chan proto.Message, <-chan *UserVar, chan error, func(), error)

	// Help data
	Tagline string
}

type DecoratorApp struct {
	cli.App
	ConfirmDecorated func(*cli.Context, proto.Message) (bool, error)
	SetUserVar       func(*UserVar)

	// Help info
	DecoratorUsage   string
	DecoratorExample string
}

// NewFlow creates a flowRunner from a CLIApp.
// For flows that query the flow server it overrides the action with command function, which
// runs the cliApp's Request function and listens on the channels it returns.
// TODO: move away from flowRunner model by explicitly defining the command function as the
// action in the flow.
func NewFlow(cliApp *CLIApp, decoratorApp *DecoratorApp) *flowRunner {

	// setBaseApp overrides Action with the help action. We don't want this
	// and either want nil or the action that was already set on
	// CLIApp.Action.
	action := cliApp.Action
	setBaseApp(cliApp)
	cliApp.Action = action

	fRunner := &flowRunner{
		cliApp:       cliApp,
		decoratorApp: decoratorApp,
		errc:         make(chan error),
	}
	if fRunner.decoratorApp != nil {
		fRunner.decoratorApp.Setup()
	}
	fRunner.setHelp()

	if fRunner.cliApp.Action == nil {

		// If Action is set, decoratedResultc is not used, nor is it closed.
		// Only set it if we're calling the internal flowRunner.action.
		fRunner.decoratedResultc = make(chan *DecoratedResult)
		fRunner.cliApp.Action = fRunner.action
	}
	return fRunner
}

func (f *flowRunner) setHelp() {
	cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
		defer func() {

			// decoratedResultc is nil if the Flow is using it's own action.
			if f.decoratedResultc != nil {
				close(f.decoratedResultc)
			}
		}()
		figure.NewFigure("codelingo", "larry3d", false).Print()
		printHelp(w, CLIAPPHELPTMP, f.cliApp)
		if f.decoratorApp != nil {
			printHelp(w, DECAPPHELPTMP, f.decoratorApp)
		}
		printHelp(w, INFOTMP, f.cliApp)
	}

	f.cliApp.Commands = append(f.cliApp.Commands, cli.Command{
		Name: "_genJSONHelp",
		Action: func(ctx *cli.Context) {

			defer func() {
				// decoratedResultc is nil if the Flow is using it's own action.
				if f.decoratedResultc != nil {
					close(f.decoratedResultc)
				}
			}()

			var cliFlags, decFlags []string
			for _, flag := range f.cliApp.Flags {
				cliFlags = append(cliFlags, flag.String())
			}
			if f.decoratorApp != nil {
				for _, flag := range f.decoratorApp.Flags {
					decFlags = append(decFlags, flag.String())
				}
			}

			helpData := helpData{
				Name:         f.cliApp.Name,
				Tagline:      f.cliApp.Tagline,
				Description:  f.cliApp.Usage,
				LastCompiled: f.cliApp.Compiled.String(),
				Version:      f.cliApp.Version,

				Options: decFlags,
			}

			if f.decoratorApp != nil {
				helpData.Decorator = decHelp{
					Description: f.decoratorApp.Usage,
					// Example: f.decoratorApp.Example,
					Options: decFlags,
				}
			}

			b, err := json.Marshal(helpData)
			if err != nil {
				fmt.Println(err)
				return
			}

			outPath := ctx.Args()[0]
			if err := ioutil.WriteFile(outPath, b, 0644); err != nil {
				fmt.Println(err)
				return
			}
			fmt.Println("Success! JSON Help written to:", outPath)

		},
	})

}

func setBaseApp(cliApp *CLIApp) {

	app := CLIApp{
		App:     *cli.NewApp(),
		Request: cliApp.Request,
	}

	// base settings
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "lingo-file",
			Usage: "the Tenet `FILE` to run rewrite over. If the flag is not set, codelingo.yaml files are read from the branch being rewritten.",
			//	Destination: &language,
		},
	}
	app.Compiled = time.Now()
	app.EnableBashCompletion = true

	// user settings
	app.Name = cliApp.Name
	app.Tagline = cliApp.Tagline
	app.Usage = cliApp.Usage
	app.Flags = append(app.Flags, cliApp.Flags...)
	app.Action = cliApp.Action
	app.Version = cliApp.Version

	*cliApp = app
}

// TODO(waigani) incorrect usage func

// Run runs the CLI app. We assume that cliApp's uses f.command as its action to query
// the flow server and stream back decorated/confirmed results on f.decoratedResultc.
// Special cliApps that don't use the flow server have their own custom actions, in which
// case the chans returned from here will not be closed.
func (f *flowRunner) Run() (chan *DecoratedResult, chan error) {

	go func() {
		defer close(f.errc)
		if err := f.cliApp.Run(os.Args); err != nil {
			f.errc <- err
		}
	}()

	return f.decoratedResultc, f.errc
}

func (f *flowRunner) action(ctx *cli.Context) {
	if err := f.command(ctx); err != nil {
		util.Logger.Debugw(errors.ErrorStack(err))
		util.FatalOSErr(err)
	}

}

func (f *flowRunner) command(ctx *cli.Context) (err error) {
	wg := sync.WaitGroup{}
	wg.Add(1)
	defer func() {
		wg.Wait()
		close(f.decoratedResultc)
	}()
	defer wg.Done()

	resultc, userVarC, errc, cancel, err := f.RunCLI()
	if err != nil {
		return errors.Trace(err)
	}

	// If user is manually confirming results, set a long timeout.
	timeout := time.After(time.Hour * 1)
l:
	for {
		select {
		case err, ok := <-errc:
			if !ok {
				errc = nil
				break
			}

			util.Logger.Debugf("Result error: %s", errors.ErrorStack(err))
			return errors.Trace(err)
		case v, ok := <-userVarC:
			if !ok {
				userVarC = nil
				break
			}

			f.SetUserVar(v)
		case result, ok := <-resultc:
			if !ok {
				resultc = nil
				break
			}

			// TODO(waigani) this is brittle and expects the result struct to have a
			// DecoratorOptions field. We need to refactor the Flow server to return a
			// tuple of [<decorator>, <result>].
			decorator := reflect.Indirect(reflect.ValueOf(result)).FieldByName("DecoratorOptions").String()

			var keep bool
			if ctx.Bool("keep-all") {
				keep = true
			} else {
				keep, err = f.ConfirmDecorated(decorator, result)
				if err != nil {
					cancel()
					return errors.Trace(err)
				}
			}

			if keep {
				wg.Add(1)
				go func(string, proto.Message) {
					defer wg.Done()
					ctx, err := NewCtx(&f.decoratorApp.App, strings.Split(decorator, " ")[1:]...)
					if err != nil {
						cancel()
						f.errc <- err
						util.Logger.Fatalf("error getting decorated context: %q", err)
						return
					}
					util.Logger.Debug("sending result to Flow...")
					f.decoratedResultc <- &DecoratedResult{
						Ctx:     ctx,
						Payload: result,
					}
					util.Logger.Debug("...result sent to Flow")

				}(decorator, result)

			}

		case <-timeout:
			cancel()
			return errors.New("timed out waiting for issue")
		}
		if resultc == nil && errc == nil && userVarC == nil {
			break l
		}
	}

	return
}

func (f *flowRunner) CliCtx() (*cli.Context, error) {
	if f.cliCtx == nil {
		ctx, err := NewCtx(&f.cliApp.App, os.Args[1:]...)
		if err != nil {
			return nil, errors.Trace(err)
		}
		f.cliCtx = ctx
	}
	return f.cliCtx, nil
}

func (f *flowRunner) RunCLI() (chan proto.Message, <-chan *UserVar, chan error, func(), error) {
	ctx, err := f.CliCtx()
	if err != nil {
		return nil, nil, nil, nil, errors.Trace(err)
	}
	if ctx.Bool("debug") {
		err := util.SetDebugLogger()
		if err != nil {
			return nil, nil, nil, nil, errors.Trace(err)
		}
	}

	return f.cliApp.Request(ctx)
}

func (f *flowRunner) ConfirmDecorated(decorator string, payload proto.Message) (bool, error) {
	ctx, err := NewCtx(&f.decoratorApp.App, strings.Split(decorator, " ")...)
	if err != nil {
		return false, errors.Trace(err)
	}

	return f.decoratorApp.ConfirmDecorated(ctx, payload)
}

func (f *flowRunner) SetUserVar(userVar *UserVar) {
	f.decoratorApp.SetUserVar(userVar)
}

func NewCtx(app *cli.App, input ...string) (*cli.Context, error) {

	fSet := flag.NewFlagSet(app.Name, flag.ContinueOnError)
	for _, flag := range app.Flags {
		flag.Apply(fSet)
	}

	if err := fSet.Parse(input); err != nil {
		return nil, errors.Trace(err)
	}
	if err := normalizeFlags(app.Flags, fSet); err != nil {
		return nil, errors.Trace(err)
	}

	ctx := cli.NewContext(nil, fSet, nil)

	return ctx, nil
}

func normalizeFlags(flags []cli.Flag, set *flag.FlagSet) error {
	visited := make(map[string]bool)
	set.Visit(func(f *flag.Flag) {
		visited[f.Name] = true
	})
	for _, f := range flags {
		parts := strings.Split(f.GetName(), ",")
		if len(parts) == 1 {
			continue
		}
		var ff *flag.Flag
		for _, name := range parts {
			name = strings.Trim(name, " ")
			if visited[name] {
				if ff != nil {
					return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
				}
				ff = set.Lookup(name)
			}
		}
		if ff == nil {
			continue
		}
		for _, name := range parts {
			name = strings.Trim(name, " ")
			if !visited[name] {
				copyFlag(name, ff, set)
			}
		}
	}
	return nil
}

func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
	switch ff.Value.(type) {
	case *cli.StringSlice:
	default:
		set.Set(name, ff.Value.String())
	}
}
<|endoftext|>


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

package serial

import "io/ioutil"
import "regexp"
import "syscall"
import "unsafe"

//sys ioctl(fd int, req uint64, data uintptr) (err error)
func ioctl(fd int, req uint64, data uintptr) (err error) {
	_, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data))
	if e1 != 0 {
		err = e1
	}
	return
}

// native syscall wrapper functions

func getExclusiveAccess(handle int) error {
	return ioctl(handle, syscall.TIOCEXCL, 0)
}

func releaseExclusiveAccess(handle int) error {
	return ioctl(handle, syscall.TIOCNXCL, 0)
}

func getTermSettings(handle int) (*syscall.Termios, error) {
	settings := &syscall.Termios{}
	err := ioctl(handle, syscall.TCGETS, uintptr(unsafe.Pointer(settings)))
	return settings, err
}

func setTermSettings(handle int, settings *syscall.Termios) error {
	return ioctl(handle, syscall.TCSETS, uintptr(unsafe.Pointer(settings)))
}

func getErrno(err error) int {
	return int(err.(syscall.Errno))
}

// OS dependent values

const devFolder = "/dev"
const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}"

// opaque type that implements SerialPort interface for linux
type linuxSerialPort struct {
	Handle int
}

func GetPortsList() ([]string, error) {
	files, err := ioutil.ReadDir(devFolder)
	if err != nil {
		return nil, err
	}

	ports := make([]string, 0, len(files))
	for _, f := range files {
		// Skip folders
		if f.IsDir() {
			continue
		}

		// Keep only devices with the correct name
		match, err := regexp.MatchString(regexFilter, f.Name())
		if err != nil {
			return nil, err
		}
		if !match {
			continue
		}

		portName := devFolder + "/" + f.Name()

		// Check if serial port is real or is a placeholder serial port "ttySxx"
		if f.Name()[:4] == "ttyS" {
			port, err := OpenPort(portName, false)
			if err != nil {
				serr, ok := err.(*SerialPortError)
				if ok && serr.Code() == ERROR_INVALID_SERIAL_PORT {
					continue
				}
			} else {
				port.Close()
			}
		}

		// Save serial port in the resulting list
		ports = append(ports, portName)
	}

	return ports, nil
}

func (port *linuxSerialPort) Close() error {
	releaseExclusiveAccess(port.Handle)
	return syscall.Close(port.Handle)
}

func (port *linuxSerialPort) Read(p []byte) (n int, err error) {
	return syscall.Read(port.Handle, p)
}

func (port *linuxSerialPort) Write(p []byte) (n int, err error) {
	return syscall.Write(port.Handle, p)
}

var baudrateMap = map[int]uint32{
	50:      syscall.B50,
	75:      syscall.B75,
	110:     syscall.B110,
	134:     syscall.B134,
	150:     syscall.B150,
	200:     syscall.B200,
	300:     syscall.B300,
	600:     syscall.B600,
	1200:    syscall.B1200,
	1800:    syscall.B1800,
	2400:    syscall.B2400,
	4800:    syscall.B4800,
	9600:    syscall.B9600,
	19200:   syscall.B19200,
	38400:   syscall.B38400,
	57600:   syscall.B57600,
	115200:  syscall.B115200,
	230400:  syscall.B230400,
	460800:  syscall.B460800,
	500000:  syscall.B500000,
	576000:  syscall.B576000,
	921600:  syscall.B921600,
	1000000: syscall.B1000000,
	1152000: syscall.B1152000,
	1500000: syscall.B1500000,
	2000000: syscall.B2000000,
	2500000: syscall.B2500000,
	3000000: syscall.B3000000,
	3500000: syscall.B3500000,
	4000000: syscall.B4000000,
}

func (port *linuxSerialPort) SetSpeed(speed int) error {
	baudrate, ok := baudrateMap[speed]
	if !ok {
		return &SerialPortError{code: ERROR_INVALID_PORT_SPEED}
	}
	settings, err := getTermSettings(port.Handle)
	if err != nil {
		return err
	}
	// revert old baudrate
	var BAUDMASK uint32 = 0
	for _, rate := range baudrateMap {
		BAUDMASK |= rate
	}
	settings.Cflag &= ^uint32(BAUDMASK)
	// set new baudrate
	settings.Cflag |= baudrate
	settings.Ispeed = baudrate
	settings.Ospeed = baudrate
	return setTermSettings(port.Handle, settings)
}

func (port *linuxSerialPort) SetParity(parity Parity) error {
	const FIXED_PARITY_FLAG uint32 = 0 // may be CMSPAR or PAREXT

	settings, err := getTermSettings(port.Handle)
	if err != nil {
		return err
	}
	switch parity {
	case PARITY_NONE:
		settings.Cflag &= ^uint32(syscall.PARENB | syscall.PARODD | FIXED_PARITY_FLAG)
		settings.Iflag &= ^uint32(syscall.INPCK)
	case PARITY_ODD:
		settings.Cflag |= syscall.PARENB | syscall.PARODD
		settings.Cflag &= ^uint32(FIXED_PARITY_FLAG)
		settings.Iflag |= syscall.INPCK
	case PARITY_EVEN:
		settings.Cflag &= ^uint32(syscall.PARODD | FIXED_PARITY_FLAG)
		settings.Cflag |= syscall.PARENB
		settings.Iflag |= syscall.INPCK
	case PARITY_MARK:
		settings.Cflag |= syscall.PARENB | syscall.PARODD | FIXED_PARITY_FLAG
		settings.Iflag |= syscall.INPCK
	case PARITY_SPACE:
		settings.Cflag &= ^uint32(syscall.PARODD)
		settings.Cflag |= syscall.PARENB | FIXED_PARITY_FLAG
		settings.Iflag |= syscall.INPCK
	}
	return setTermSettings(port.Handle, settings)
}

var databitsMap = map[int]uint32{
	5: syscall.CS5,
	6: syscall.CS6,
	7: syscall.CS7,
	8: syscall.CS8,
}

func (port *linuxSerialPort) SetDataBits(bits int) error {
	databits, ok := databitsMap[bits]
	if !ok {
		return &SerialPortError{code: ERROR_INVALID_PORT_DATA_BITS}
	}
	settings, err := getTermSettings(port.Handle)
	if err != nil {
		return err
	}
	settings.Cflag &= ^uint32(syscall.CSIZE)
	settings.Cflag |= databits
	return setTermSettings(port.Handle, settings)
}

func (port *linuxSerialPort) SetStopBits(bits StopBits) error {
	settings, err := getTermSettings(port.Handle)
	if err != nil {
		return err
	}
	switch bits {
	case STOPBITS_ONE:
		settings.Cflag &= ^uint32(syscall.CSTOPB)
	case STOPBITS_ONEPOINTFIVE, STOPBITS_TWO:
		settings.Cflag |= syscall.CSTOPB
	}
	return setTermSettings(port.Handle, settings)
}

func OpenPort(portName string, exclusive bool) (SerialPort, error) {
	handle, err := syscall.Open(portName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY, 0)
	if err != nil {
		switch err {
		case syscall.EBUSY:
			return nil, &SerialPortError{code: ERROR_PORT_BUSY}
		case syscall.EACCES:
			return nil, &SerialPortError{code: ERROR_PERMISSION_DENIED}
		}
		return nil, err
	}

	// Setup serial port with defaults

	settings, err := getTermSettings(handle)
	if err != nil {
		syscall.Close(handle)
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}

	// Set local mode
	settings.Cflag |= syscall.CREAD | syscall.CLOCAL

	// Set raw mode
	settings.Lflag &= ^uint32(syscall.ICANON | syscall.ECHO | syscall.ECHOE | syscall.ECHOK | syscall.ECHONL | syscall.ECHOCTL | syscall.ECHOPRT | syscall.ECHOKE | syscall.ISIG | syscall.IEXTEN)
	settings.Iflag &= ^uint32(syscall.IXON | syscall.IXOFF | syscall.IXANY | syscall.INPCK | syscall.IGNPAR | syscall.PARMRK | syscall.ISTRIP | syscall.IGNBRK | syscall.BRKINT | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IUCLC)
	settings.Oflag &= ^uint32(syscall.OPOST)

	// Block reads until at least one char is available (no timeout)
	settings.Cc[syscall.VMIN] = 1
	settings.Cc[syscall.VTIME] = 0

	err = setTermSettings(handle, settings)
	if err != nil {
		syscall.Close(handle)
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}
	/*
	   settings->c_cflag &= ~CRTSCTS;
	*/
	syscall.SetNonblock(handle, false)

	if exclusive {
		getExclusiveAccess(handle)
	}

	serialPort := &linuxSerialPort{
		Handle: handle,
	}
	return serialPort, nil
}

// vi:ts=2
<commit_msg>linux: refactored native calls as class private<commit_after>//
// Copyright 2014 Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package serial

import "io/ioutil"
import "regexp"
import "syscall"
import "unsafe"

//sys ioctl(fd int, req uint64, data uintptr) (err error)
func ioctl(fd int, req uint64, data uintptr) (err error) {
	_, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data))
	if e1 != 0 {
		err = e1
	}
	return
}

func getErrno(err error) int {
	return int(err.(syscall.Errno))
}

// OS dependent values

const devFolder = "/dev"
const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}"

// opaque type that implements SerialPort interface for linux
type linuxSerialPort struct {
	handle int
}

func GetPortsList() ([]string, error) {
	files, err := ioutil.ReadDir(devFolder)
	if err != nil {
		return nil, err
	}

	ports := make([]string, 0, len(files))
	for _, f := range files {
		// Skip folders
		if f.IsDir() {
			continue
		}

		// Keep only devices with the correct name
		match, err := regexp.MatchString(regexFilter, f.Name())
		if err != nil {
			return nil, err
		}
		if !match {
			continue
		}

		portName := devFolder + "/" + f.Name()

		// Check if serial port is real or is a placeholder serial port "ttySxx"
		if f.Name()[:4] == "ttyS" {
			port, err := OpenPort(portName, false)
			if err != nil {
				serr, ok := err.(*SerialPortError)
				if ok && serr.Code() == ERROR_INVALID_SERIAL_PORT {
					continue
				}
			} else {
				port.Close()
			}
		}

		// Save serial port in the resulting list
		ports = append(ports, portName)
	}

	return ports, nil
}

func (port *linuxSerialPort) Close() error {
	port.releaseExclusiveAccess()
	return syscall.Close(port.handle)
}

func (port *linuxSerialPort) Read(p []byte) (n int, err error) {
	return syscall.Read(port.handle, p)
}

func (port *linuxSerialPort) Write(p []byte) (n int, err error) {
	return syscall.Write(port.handle, p)
}

var baudrateMap = map[int]uint32{
	50:      syscall.B50,
	75:      syscall.B75,
	110:     syscall.B110,
	134:     syscall.B134,
	150:     syscall.B150,
	200:     syscall.B200,
	300:     syscall.B300,
	600:     syscall.B600,
	1200:    syscall.B1200,
	1800:    syscall.B1800,
	2400:    syscall.B2400,
	4800:    syscall.B4800,
	9600:    syscall.B9600,
	19200:   syscall.B19200,
	38400:   syscall.B38400,
	57600:   syscall.B57600,
	115200:  syscall.B115200,
	230400:  syscall.B230400,
	460800:  syscall.B460800,
	500000:  syscall.B500000,
	576000:  syscall.B576000,
	921600:  syscall.B921600,
	1000000: syscall.B1000000,
	1152000: syscall.B1152000,
	1500000: syscall.B1500000,
	2000000: syscall.B2000000,
	2500000: syscall.B2500000,
	3000000: syscall.B3000000,
	3500000: syscall.B3500000,
	4000000: syscall.B4000000,
}

func (port *linuxSerialPort) SetSpeed(speed int) error {
	baudrate, ok := baudrateMap[speed]
	if !ok {
		return &SerialPortError{code: ERROR_INVALID_PORT_SPEED}
	}
	settings, err := port.getTermSettings()
	if err != nil {
		return err
	}
	// revert old baudrate
	var BAUDMASK uint32 = 0
	for _, rate := range baudrateMap {
		BAUDMASK |= rate
	}
	settings.Cflag &= ^uint32(BAUDMASK)
	// set new baudrate
	settings.Cflag |= baudrate
	settings.Ispeed = baudrate
	settings.Ospeed = baudrate
	return port.setTermSettings(settings)
}

func (port *linuxSerialPort) SetParity(parity Parity) error {
	const FIXED_PARITY_FLAG uint32 = 0 // may be CMSPAR or PAREXT

	settings, err := port.getTermSettings()
	if err != nil {
		return err
	}
	switch parity {
	case PARITY_NONE:
		settings.Cflag &= ^uint32(syscall.PARENB | syscall.PARODD | FIXED_PARITY_FLAG)
		settings.Iflag &= ^uint32(syscall.INPCK)
	case PARITY_ODD:
		settings.Cflag |= syscall.PARENB | syscall.PARODD
		settings.Cflag &= ^uint32(FIXED_PARITY_FLAG)
		settings.Iflag |= syscall.INPCK
	case PARITY_EVEN:
		settings.Cflag &= ^uint32(syscall.PARODD | FIXED_PARITY_FLAG)
		settings.Cflag |= syscall.PARENB
		settings.Iflag |= syscall.INPCK
	case PARITY_MARK:
		settings.Cflag |= syscall.PARENB | syscall.PARODD | FIXED_PARITY_FLAG
		settings.Iflag |= syscall.INPCK
	case PARITY_SPACE:
		settings.Cflag &= ^uint32(syscall.PARODD)
		settings.Cflag |= syscall.PARENB | FIXED_PARITY_FLAG
		settings.Iflag |= syscall.INPCK
	}
	return port.setTermSettings(settings)
}

var databitsMap = map[int]uint32{
	5: syscall.CS5,
	6: syscall.CS6,
	7: syscall.CS7,
	8: syscall.CS8,
}

func (port *linuxSerialPort) SetDataBits(bits int) error {
	databits, ok := databitsMap[bits]
	if !ok {
		return &SerialPortError{code: ERROR_INVALID_PORT_DATA_BITS}
	}
	settings, err := port.getTermSettings()
	if err != nil {
		return err
	}
	settings.Cflag &= ^uint32(syscall.CSIZE)
	settings.Cflag |= databits
	return port.setTermSettings(settings)
}

func (port *linuxSerialPort) SetStopBits(bits StopBits) error {
	settings, err := port.getTermSettings()
	if err != nil {
		return err
	}
	switch bits {
	case STOPBITS_ONE:
		settings.Cflag &= ^uint32(syscall.CSTOPB)
	case STOPBITS_ONEPOINTFIVE, STOPBITS_TWO:
		settings.Cflag |= syscall.CSTOPB
	}
	return port.setTermSettings(settings)
}

// native syscall wrapper functions

func (port *linuxSerialPort) getExclusiveAccess() error {
	return ioctl(port.handle, syscall.TIOCEXCL, 0)
}

func (port *linuxSerialPort) releaseExclusiveAccess() error {
	return ioctl(port.handle, syscall.TIOCNXCL, 0)
}

func (port *linuxSerialPort) getTermSettings() (*syscall.Termios, error) {
	settings := &syscall.Termios{}
	err := ioctl(port.handle, syscall.TCGETS, uintptr(unsafe.Pointer(settings)))
	return settings, err
}

func (port *linuxSerialPort) setTermSettings(settings *syscall.Termios) error {
	return ioctl(port.handle, syscall.TCSETS, uintptr(unsafe.Pointer(settings)))
}

func OpenPort(portName string, exclusive bool) (SerialPort, error) {
	h, err := syscall.Open(portName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY, 0)
	if err != nil {
		switch err {
		case syscall.EBUSY:
			return nil, &SerialPortError{code: ERROR_PORT_BUSY}
		case syscall.EACCES:
			return nil, &SerialPortError{code: ERROR_PERMISSION_DENIED}
		}
		return nil, err
	}
	port := &linuxSerialPort{
		handle: h,
	}

	// Setup serial port with defaults

	settings, err := port.getTermSettings()
	if err != nil {
		port.Close()
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}

	// Set local mode
	settings.Cflag |= syscall.CREAD | syscall.CLOCAL

	// Set raw mode
	settings.Lflag &= ^uint32(syscall.ICANON | syscall.ECHO | syscall.ECHOE | syscall.ECHOK | syscall.ECHONL | syscall.ECHOCTL | syscall.ECHOPRT | syscall.ECHOKE | syscall.ISIG | syscall.IEXTEN)
	settings.Iflag &= ^uint32(syscall.IXON | syscall.IXOFF | syscall.IXANY | syscall.INPCK | syscall.IGNPAR | syscall.PARMRK | syscall.ISTRIP | syscall.IGNBRK | syscall.BRKINT | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IUCLC)
	settings.Oflag &= ^uint32(syscall.OPOST)

	// Block reads until at least one char is available (no timeout)
	settings.Cc[syscall.VMIN] = 1
	settings.Cc[syscall.VTIME] = 0

	err = port.setTermSettings(settings)
	if err != nil {
		port.Close()
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}
	/*
	   settings->c_cflag &= ~CRTSCTS;
	*/
	syscall.SetNonblock(h, false)

	if exclusive {
		port.getExclusiveAccess()
	}

	return port, nil
}

// vi:ts=2
<|endoftext|>