<commit_before>package wxpay

import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
)

// AppTrans is abstact of Transaction handler. With AppTrans, we can get prepay id
type AppTrans struct {
	Config *WxConfig
}

// Initialized the AppTrans with specific config
func NewAppTrans(cfg *WxConfig) (*AppTrans, error) {
	if cfg.AppId == "" ||
		cfg.MchId == "" ||
		cfg.AppKey == "" ||
		cfg.NotifyUrl == "" ||
		cfg.QueryOrderUrl == "" ||
		cfg.PlaceOrderUrl == "" ||
		cfg.TradeType == "" {
		return &AppTrans{Config: cfg}, errors.New("config field canot empty string")
	}

	return &AppTrans{Config: cfg}, nil
}

// Submit the order to weixin pay and return the prepay id if success,
// Prepay id is used for app to start a payment
// If fail, error is not nil, check error for more information
func (this *AppTrans) Submit(orderId string, amount float64, desc string, clientIp string) (string, error) {

	odrInXml := this.signedOrderRequestXmlString(orderId, fmt.Sprintf("%.0f", amount), desc, clientIp)
	resp, err := doHttpPost(this.Config.PlaceOrderUrl, []byte(odrInXml))
	if err != nil {
		return "", err
	}

	placeOrderResult, err := ParsePlaceOrderResult(resp)
	if err != nil {
		return "", err
	}

	//Verify the sign of response
	resultInMap := placeOrderResult.ToMap()
	wantSign := Sign(resultInMap, this.Config.AppKey)
	gotSign := resultInMap["sign"]
	if wantSign != gotSign {
		return "", fmt.Errorf("sign not match, want:%s, got:%s", wantSign, gotSign)
	}

	if placeOrderResult.ReturnCode != "SUCCESS" {
		return "", fmt.Errorf("return code:%s, return desc:%s", placeOrderResult.ReturnCode, placeOrderResult.ReturnMsg)
	}

	if placeOrderResult.ResultCode != "SUCCESS" {
		return "", fmt.Errorf("resutl code:%s, result desc:%s", placeOrderResult.ErrCode, placeOrderResult.ErrCodeDesc)
	}

	return placeOrderResult.PrepayId, nil
}

func (this *AppTrans) newQueryXml(transId string) string {
	param := make(map[string]string)
	param["appid"] = this.Config.AppId
	param["mch_id"] = this.Config.MchId
	param["transaction_id"] = transId
	param["nonce_str"] = NewNonceString()

	sign := Sign(param, this.Config.AppKey)
	param["sign"] = sign

	return ToXmlString(param)
}

// Query the order from weixin pay server by transaction id of weixin pay
func (this *AppTrans) Query(transId string) (QueryOrderResult, error) {
	queryOrderResult := QueryOrderResult{}

	queryXml := this.newQueryXml(transId)
	// fmt.Println(queryXml)
	resp, err := doHttpPost(this.Config.QueryOrderUrl, []byte(queryXml))
	if err != nil {
		return queryOrderResult, nil
	}

	queryOrderResult, err = ParseQueryOrderResult(resp)
	if err != nil {
		return queryOrderResult, err
	}

	//verity sign of response
	resultInMap := queryOrderResult.ToMap()
	wantSign := Sign(resultInMap, this.Config.AppKey)
	gotSign := resultInMap["sign"]
	if wantSign != gotSign {
		return queryOrderResult, fmt.Errorf("sign not match, want:%s, got:%s", wantSign, gotSign)
	}

	return queryOrderResult, nil
}

// NewPaymentRequest build the payment request structure for app to start a payment.
// Return stuct of PaymentRequest, please refer to http://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_12&index=2
func (this *AppTrans) NewPaymentRequest(prepayId string) PaymentRequest {
	param := make(map[string]string)
	param["appid"] = this.Config.AppId
	param["partnerid"] = this.Config.MchId
	param["prepayid"] = prepayId
	param["package"] = "Sign=WXPay"
	param["noncestr"] = NewNonceString()
	param["timestamp"] = NewTimestampString()

	sign := Sign(param, this.Config.AppKey)

	payRequest := PaymentRequest{
		AppId:     this.Config.AppId,
		PartnerId: this.Config.MchId,
		PrepayId:  prepayId,
		Package:   "Sign=WXPay",
		NonceStr:  NewNonceString(),
		Timestamp: NewTimestampString(),
		Sign:      sign,
	}

	return payRequest
}

func (this *AppTrans) newOrderRequest(orderId, amount, desc, clientIp string) map[string]string {
	param := make(map[string]string)
	param["appid"] = this.Config.AppId
	param["attach"] = "透传字段" //optional
	param["body"] = desc
	param["mch_id"] = this.Config.MchId
	param["nonce_str"] = NewNonceString()
	param["notify_url"] = this.Config.NotifyUrl
	param["out_trade_no"] = orderId
	param["spbill_create_ip"] = clientIp
	param["total_fee"] = amount
	param["trade_type"] = "APP"

	return param
}

func (this *AppTrans) signedOrderRequestXmlString(orderId, amount, desc, clientIp string) string {
	order := this.newOrderRequest(orderId, amount, desc, clientIp)
	sign := Sign(order, this.Config.AppKey)
	// fmt.Println(sign)

	order["sign"] = sign

	return ToXmlString(order)
}

// doRequest post the order in xml format with a sign
func doHttpPost(targetUrl string, body []byte) ([]byte, error) {
	req, err := http.NewRequest("POST", targetUrl, bytes.NewBuffer([]byte(body)))
	if err != nil {
		return []byte(""), err
	}
	req.Header.Add("Content-type", "application/x-www-form-urlencoded;charset=UTF-8")

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
	}
	client := &http.Client{Transport: tr}

	resp, err := client.Do(req)
	if err != nil {
		return []byte(""), err
	}

	defer resp.Body.Close()
	respData, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte(""), err
	}

	return respData, nil
}
<commit_msg>Return code url<commit_after>package wxpay

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
)

// AppTrans is abstact of Transaction handler. With AppTrans, we can get prepay id
type AppTrans struct {
	Config *WxConfig
}

// Initialized the AppTrans with specific config
func NewAppTrans(cfg *WxConfig) (*AppTrans, error) {
	if cfg.AppId == "" ||
		cfg.MchId == "" ||
		cfg.AppKey == "" ||
		cfg.NotifyUrl == "" ||
		cfg.QueryOrderUrl == "" ||
		cfg.PlaceOrderUrl == "" ||
		cfg.TradeType == "" {
		return &AppTrans{Config: cfg}, errors.New("config field canot empty string")
	}

	return &AppTrans{Config: cfg}, nil
}

// Submit the order to weixin pay and return the prepay id if success,
// Prepay id is used for app to start a payment
// If fail, error is not nil, check error for more information
func (this *AppTrans) Submit(orderId string, amount float64, desc string, clientIp string, trade_type string, openid string) (string, string, error) {

	odrInXml := this.signedOrderRequestXmlString(orderId, fmt.Sprintf("%.0f", amount), desc, clientIp, trade_type, openid)

	resp, err := doHttpPost(this.Config.PlaceOrderUrl, []byte(odrInXml))
	if err != nil {
		return "", "", err
	}

	placeOrderResult, err := ParsePlaceOrderResult(resp)
	if err != nil {
		return "", "", err
	}

	//Verify the sign of response
	resultInMap := placeOrderResult.ToMap()
	wantSign := Sign(resultInMap, this.Config.AppKey)
	gotSign := resultInMap["sign"]
	if wantSign != gotSign {
		return "", "", fmt.Errorf("sign not match, want:%s, got:%s", wantSign, gotSign)
	}

	if placeOrderResult.ReturnCode != "SUCCESS" {
		return "", "", fmt.Errorf("return code:%s, return desc:%s", placeOrderResult.ReturnCode, placeOrderResult.ReturnMsg)
	}

	if placeOrderResult.ResultCode != "SUCCESS" {
		return "", "", fmt.Errorf("resutl code:%s, result desc:%s", placeOrderResult.ErrCode, placeOrderResult.ErrCodeDesc)
	}

	return placeOrderResult.PrepayId, placeOrderResult.CodeUrl, nil
}

func (this *AppTrans) newQueryXml(transId string) string {
	param := make(map[string]string)
	param["appid"] = this.Config.AppId
	param["mch_id"] = this.Config.MchId
	param["transaction_id"] = transId
	param["nonce_str"] = NewNonceString()

	sign := Sign(param, this.Config.AppKey)
	param["sign"] = sign

	return ToXmlString(param)
}

// Query the order from weixin pay server by transaction id of weixin pay
func (this *AppTrans) Query(transId string) (QueryOrderResult, error) {
	queryOrderResult := QueryOrderResult{}

	queryXml := this.newQueryXml(transId)
	// fmt.Println(queryXml)
	resp, err := doHttpPost(this.Config.QueryOrderUrl, []byte(queryXml))
	if err != nil {
		return queryOrderResult, nil
	}

	queryOrderResult, err = ParseQueryOrderResult(resp)
	if err != nil {
		return queryOrderResult, err
	}

	//verity sign of response
	resultInMap := queryOrderResult.ToMap()
	wantSign := Sign(resultInMap, this.Config.AppKey)
	gotSign := resultInMap["sign"]
	if wantSign != gotSign {
		return queryOrderResult, fmt.Errorf("sign not match, want:%s, got:%s", wantSign, gotSign)
	}

	return queryOrderResult, nil
}

// NewPaymentRequest build the payment request structure for app to start a payment.
// Return stuct of PaymentRequest, please refer to http://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_12&index=2
func (this *AppTrans) NewPaymentRequest(prepayId, trade_type string) map[string]string {
	param := make(map[string]string)
	if trade_type == "APP" {
		param["appid"] = this.Config.AppId
		param["package"] = "Sign=WXPay"
		param["partnerid"] = this.Config.MchId
		param["prepayid"] = prepayId
		param["noncestr"] = NewNonceString()
		param["timestamp"] = NewTimestampString()
	} else {
		param["appId"] = this.Config.AppId
		param["timeStamp"] = NewTimestampString()
		param["nonceStr"] = NewNonceString()
		param["package"] = "prepay_id=" + prepayId
		param["signType"] = "MD5"
	}

	sign := Sign(param, this.Config.AppKey)
	param["sign"] = sign

	return param
}

func (this *AppTrans) newOrderRequest(orderId, amount, desc, clientIp, trade_type string, openid string) map[string]string {
	param := make(map[string]string)
	param["appid"] = this.Config.AppId
	param["attach"] = "透传字段" //optional
	param["body"] = desc
	param["mch_id"] = this.Config.MchId
	param["nonce_str"] = NewNonceString()
	param["notify_url"] = this.Config.NotifyUrl
	param["out_trade_no"] = orderId
	param["spbill_create_ip"] = clientIp
	param["total_fee"] = amount
	if trade_type == "APP" {
		param["trade_type"] = "APP"
	} else {
		param["trade_type"] = "JSAPI"
		param["openid"] = openid
	}

	return param
}

func (this *AppTrans) signedOrderRequestXmlString(orderId, amount, desc, clientIp, trade_type, openid string) string {
	order := this.newOrderRequest(orderId, amount, desc, clientIp, trade_type, openid)
	sign := Sign(order, this.Config.AppKey)
	// fmt.Println(sign)

	order["sign"] = sign

	return ToXmlString(order)
}

// doRequest post the order in xml format with a sign
func doHttpPost(targetUrl string, body []byte) ([]byte, error) {
	req, err := http.NewRequest("POST", targetUrl, bytes.NewBuffer([]byte(body)))
	if err != nil {
		return []byte(""), err
	}
	req.Header.Add("Content-type", "application/x-www-form-urlencoded;charset=UTF-8")

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
	}
	client := &http.Client{Transport: tr}

	resp, err := client.Do(req)
	if err != nil {
		return []byte(""), err
	}

	defer resp.Body.Close()
	respData, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte(""), err
	}

	return respData, nil
}

// get请求
func doHttpGet(targetUrl string, params map[string]string) ([]byte, error) {
	u, err := url.Parse(targetUrl)

	if err != nil {
		return []byte(""), err
	}

	q := u.Query()

	for k, v := range params {
		q.Set(k, v)
	}
	u.RawQuery = q.Encode()

	resp, err := http.Get(u.String())
	if err != nil {
		return []byte(""), err
	}

	result, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte(""), err
	}

	resp.Body.Close()

	return result, nil
}

// 根据授权code获取openid  jsapi用
func (this *AppTrans) GetOpenID(param map[string]string) (string, error) {

	res, err := doHttpGet("https://api.weixin.qq.com/sns/oauth2/access_token", param)
	if err != nil {
		return "", err
	}

	// json string to map
	m, err := JsonStrToMap(string(res))

	if err != nil {
		return "", err
	}

	// get openid
	openid := ""
	if s, ok := m["openid"].(string); ok {
		openid = s
	}
	return openid, nil

}

// json转map
func JsonStrToMap(str string) (map[string]interface{}, error) {
	m := make(map[string]interface{})

	err := json.Unmarshal([]byte(str), &m)

	if err != nil {
		return nil, err
	}
	return m, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/astaxie/bat/httplib"
)

var defaultSetting = httplib.BeegoHttpSettings{
	ShowDebug:        true,
	UserAgent:        "bat/" + version,
	ConnectTimeout:   60 * time.Second,
	ReadWriteTimeout: 60 * time.Second,
	Gzip:             true,
	DumpBody:         true,
}

func getHTTP(method string, url string, args []string) (r *httplib.BeegoHttpRequest) {
	r = httplib.NewBeegoRequest(url, method)
	r.Setting(defaultSetting)
	r.Header("Accept-Encoding", "gzip, deflate")
	if *isjson {
		r.Header("Accept", "application/json")
	} else if form || method == "GET" {
		r.Header("Accept", "*/*")
	} else {
		r.Header("Accept", "application/json")
	}
	for i := range args {
		// Headers
		strs := strings.Split(args[i], ":")
		if len(strs) >= 2 {
			if strs[0] == "Host" {
				r.SetHost(strings.Join(strs[1:], ":"))
			}
			r.Header(strs[0], strings.Join(strs[1:], ":"))
			continue
		}
		// files
		strs = strings.SplitN(args[i], "@", 2)
		if len(strs) == 2 {
			if !form {
				log.Fatal("file upload only support in forms style: -f=true")
			}
			r.PostFile(strs[0], strs[1])
			continue
		}
		// Json raws
		strs = strings.SplitN(args[i], ":=", 2)
		if len(strs) == 2 {
			if strings.HasPrefix(strs[1], "@") {
				f, err := os.Open(strings.TrimLeft(strs[1], "@"))
				if err != nil {
					log.Fatal("Read File", strings.TrimLeft(strs[1], "@"), err)
				}
				content, err := ioutil.ReadAll(f)
				if err != nil {
					log.Fatal("ReadAll from File", strings.TrimLeft(strs[1], "@"), err)
				}
				var j interface{}
				err = json.Unmarshal(content, &j)
				if err != nil {
					log.Fatal("Read from File", strings.TrimLeft(strs[1], "@"), "Unmarshal", err)
				}
				jsonmap[strs[0]] = j
				continue
			}
			jsonmap[strs[0]] = toRealType(strs[1])
			continue
		}
		// Params
		strs = strings.SplitN(args[i], "=", 2)
		if len(strs) == 2 {
			if strings.HasPrefix(strs[1], "@") {
				f, err := os.Open(strings.TrimLeft(strs[1], "@"))
				if err != nil {
					log.Fatal("Read File", strings.TrimLeft(strs[1], "@"), err)
				}
				content, err := ioutil.ReadAll(f)
				if err != nil {
					log.Fatal("ReadAll from File", strings.TrimLeft(strs[1], "@"), err)
				}
				strs[1] = string(content)
			}
			if form || method == "GET" {
				r.Param(strs[0], strs[1])
			} else {
				jsonmap[strs[0]] = strs[1]
			}
			continue
		}
	}
	if !form && len(jsonmap) > 0 {
		r.JsonBody(jsonmap)
	}
	return
}

func formatResponseBody(res *http.Response, httpreq *httplib.BeegoHttpRequest, pretty bool) string {
	body, err := httpreq.Bytes()
	if err != nil {
		log.Fatalln("can't get the url", err)
	}
	fmt.Println("")
	match, err := regexp.MatchString(contentJsonRegex, res.Header.Get("Content-Type"))
	if err != nil {
		log.Fatalln("failed to compile regex", err)
	}
	if pretty && match {
		var output bytes.Buffer
		err := json.Indent(&output, body, "", "  ")
		if err != nil {
			log.Fatal("Response Json Indent: ", err)
		}

		return output.String()
	}

	return string(body)
}
<commit_msg>fix when including @ in the value of a property in json body<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/astaxie/bat/httplib"
)

var defaultSetting = httplib.BeegoHttpSettings{
	ShowDebug:        true,
	UserAgent:        "bat/" + version,
	ConnectTimeout:   60 * time.Second,
	ReadWriteTimeout: 60 * time.Second,
	Gzip:             true,
	DumpBody:         true,
}

func getHTTP(method string, url string, args []string) (r *httplib.BeegoHttpRequest) {
	r = httplib.NewBeegoRequest(url, method)
	r.Setting(defaultSetting)
	r.Header("Accept-Encoding", "gzip, deflate")
	if *isjson {
		r.Header("Accept", "application/json")
	} else if form || method == "GET" {
		r.Header("Accept", "*/*")
	} else {
		r.Header("Accept", "application/json")
	}
	for i := range args {
		// Headers
		strs := strings.Split(args[i], ":")
		if len(strs) >= 2 {
			if strs[0] == "Host" {
				r.SetHost(strings.Join(strs[1:], ":"))
			}
			r.Header(strs[0], strings.Join(strs[1:], ":"))
			continue
		}
		// files
		strs = strings.SplitN(args[i], "@", 2)
		if !*isjson && len(strs) == 2 {
			if !form {
				log.Fatal("file upload only support in forms style: -f=true")
			}
			r.PostFile(strs[0], strs[1])
			continue
		}
		// Json raws
		strs = strings.SplitN(args[i], ":=", 2)
		if len(strs) == 2 {
			if strings.HasPrefix(strs[1], "@") {
				f, err := os.Open(strings.TrimLeft(strs[1], "@"))
				if err != nil {
					log.Fatal("Read File", strings.TrimLeft(strs[1], "@"), err)
				}
				content, err := ioutil.ReadAll(f)
				if err != nil {
					log.Fatal("ReadAll from File", strings.TrimLeft(strs[1], "@"), err)
				}
				var j interface{}
				err = json.Unmarshal(content, &j)
				if err != nil {
					log.Fatal("Read from File", strings.TrimLeft(strs[1], "@"), "Unmarshal", err)
				}
				jsonmap[strs[0]] = j
				continue
			}
			jsonmap[strs[0]] = toRealType(strs[1])
			continue
		}
		// Params
		strs = strings.SplitN(args[i], "=", 2)
		if len(strs) == 2 {
			if strings.HasPrefix(strs[1], "@") {
				f, err := os.Open(strings.TrimLeft(strs[1], "@"))
				if err != nil {
					log.Fatal("Read File", strings.TrimLeft(strs[1], "@"), err)
				}
				content, err := ioutil.ReadAll(f)
				if err != nil {
					log.Fatal("ReadAll from File", strings.TrimLeft(strs[1], "@"), err)
				}
				strs[1] = string(content)
			}
			if form || method == "GET" {
				r.Param(strs[0], strs[1])
			} else {
				jsonmap[strs[0]] = strs[1]
			}
			continue
		}
	}
	if !form && len(jsonmap) > 0 {
		r.JsonBody(jsonmap)
	}
	return
}

func formatResponseBody(res *http.Response, httpreq *httplib.BeegoHttpRequest, pretty bool) string {
	body, err := httpreq.Bytes()
	if err != nil {
		log.Fatalln("can't get the url", err)
	}
	fmt.Println("")
	match, err := regexp.MatchString(contentJsonRegex, res.Header.Get("Content-Type"))
	if err != nil {
		log.Fatalln("failed to compile regex", err)
	}
	if pretty && match {
		var output bytes.Buffer
		err := json.Indent(&output, body, "", "  ")
		if err != nil {
			log.Fatal("Response Json Indent: ", err)
		}

		return output.String()
	}

	return string(body)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	invalidContnetSize = errors.New("invalid content size")
)

type HttpWorker struct {
	config *Config
	start  *sync.WaitGroup
	stop   chan bool

	client    *http.Client
	jobs      chan *http.Request
	collector chan *Record
	readBuf   *bytes.Buffer
}

func NewHttpWorker(config *Config, start *sync.WaitGroup, stop chan bool, jobs chan *http.Request, collector chan *Record) *HttpWorker {
	return &HttpWorker{config, start, stop, NewClient(config), jobs, collector, bytes.NewBuffer(make([]byte, 0, config.contentSize+bytes.MinRead))}
}

func (h *HttpWorker) Run() {
	h.start.Done()
	h.start.Wait()

	for job := range h.jobs {

		asyncResult := h.send(job)
		// TODO: use timer.Reset(d) instead of create new timer (Go 1.1)
		timeout := time.NewTimer(time.Duration(MAX_RESPONSE_TIMEOUT) * time.Second)

		select {
		case record := <-asyncResult:
			h.collector <- record

		case <-timeout.C:
			h.collector <- &Record{Error: errors.New("execution timeout")}

		case <-h.stop:
			timeout.Stop()
			return
		}
		timeout.Stop()
	}
}

func (h *HttpWorker) send(request *http.Request) (asyncResult chan *Record) {

	asyncResult = make(chan *Record)
	go func() {
		record := &Record{}
		sw := &StopWatch{}
		sw.Start()

		var contentSize int64

		defer func() {
			if r := recover(); r != nil {
				if Err, ok := r.(error); ok {
					record.Error = Err
				} else {
					record.Error = &ExceptionError{errors.New(fmt.Sprint(r))}
				}

			} else {
				record.contentSize = contentSize
				record.responseTime = sw.Elapsed
			}

			if record.Error != nil {
				TraceException(record.Error)
			}

			asyncResult <- record
		}()

		resp, err := h.client.Do(request)
		if err != nil {
			record.Error = &ConnectError{err}
			return
		} else {
			defer resp.Body.Close()

			if resp.StatusCode < 200 || resp.StatusCode > 300 {
				record.Error = &ResponseError{err}
				return
			}

			defer h.readBuf.Reset()
			contentSize, err := h.readBuf.ReadFrom(resp.Body)
			if err != nil {
				record.Error = &ReceiveError{err}
				return
			}

			expectedContentSize := 0
			headerContentSize := resp.Header.Get("Content-Length")
			if headerContentSize != "" {
				expectedContentSize, _ = strconv.Atoi(headerContentSize)
			} else {
				expectedContentSize = h.config.contentSize
			}

			if h.config.method != "HEAD" && int64(expectedContentSize) != contentSize {
				record.Error = &LengthError{invalidContnetSize}
				return
			}

		}

		sw.Stop()
	}()
	return asyncResult
}

func detectHost(config *Config) error {
	var err error
	defer func() {
		if r := recover(); r != nil {
			TraceException(r)
		}
	}()

	client := NewClient(config)
	reqeust, err := NewHttpRequest(config)
	if err != nil {
		return err
	}

	resp, err := client.Do(reqeust)

	if err != nil {
		return err
	} else {

		defer resp.Body.Close()
		body, _ := ioutil.ReadAll(resp.Body)

		// TODO: place on another context
		config.serverName = resp.Header.Get("Server")
		headerContentSize := resp.Header.Get("Content-Length")

		if headerContentSize != "" {
			config.contentSize, _ = strconv.Atoi(headerContentSize)
		} else {
			config.contentSize = len(body)
		}
	}

	return nil
}

func NewClient(config *Config) *http.Client {

	// skip certification check for self-signed certificates
	tlsconfig := &tls.Config{
		InsecureSkipVerify: true,
	}

	// TODO: timeout control
	// TODO: tcp options
	// TODO: monitor tcp metrics
	transport := &http.Transport{
		DisableCompression: !config.gzip,
		DisableKeepAlives:  !config.keepAlive,
		TLSClientConfig:    tlsconfig,
	}

	client := &http.Client{Transport: transport}
	return client
}

func NewHttpRequest(config *Config) (*http.Request, error) {

	var body io.Reader
	var err error

	if (config.method == "POST" || config.method == "PUT") && config.bodyFile != nil {

		body = bytes.NewReader(config.bodyFile)
	}

	request, err := http.NewRequest(config.method, config.url, body)

	if err != nil {
		return nil, err
	}

	if body != nil && config.contentType == "text/plain" {
		request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	} else {
		request.Header.Set("Content-Type", config.contentType)
	}

	request.Header.Set("User-Agent", config.userAgent)

	if config.keepAlive {
		request.Header.Set("Connection", "keep-alive")
	}

	for _, header := range config.headers {
		pair := strings.Split(header, ":")
		request.Header.Add(pair[0], pair[1])
	}

	for _, cookie := range config.cookies {
		pair := strings.Split(cookie, "=")
		c := &http.Cookie{Name: pair[0], Value: pair[1]}
		request.AddCookie(c)
	}

	if config.basicAuthentication != "" {
		pair := strings.Split(config.basicAuthentication, ":")
		request.SetBasicAuth(pair[0], pair[1])
	}

	return request, err
}

func CopyHttpRequest(config *Config, request *http.Request) *http.Request {

	if config.method != "POST" || config.method != "PUT" {
		return request
	}

	newRequest := *request
	if newRequest.Body != nil {
		newRequest.Body = ioutil.NopCloser(bytes.NewReader(config.bodyFile))
	}
	return &newRequest
}

type LengthError struct {
	err error
}

func (e *LengthError) Error() string {
	return e.err.Error()
}

type ConnectError struct {
	err error
}

func (e *ConnectError) Error() string {
	return e.err.Error()
}

type ReceiveError struct {
	err error
}

func (e *ReceiveError) Error() string {
	return e.err.Error()
}

type ExceptionError struct {
	err error
}

func (e *ExceptionError) Error() string {
	return e.err.Error()
}

type ResponseError struct {
	err error
}

func (e *ResponseError) Error() string {
	return e.err.Error()
}
<commit_msg>fixed received contenet size was zero<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	invalidContnetSize = errors.New("invalid content size")
)

type HttpWorker struct {
	config *Config
	start  *sync.WaitGroup
	stop   chan bool

	client    *http.Client
	jobs      chan *http.Request
	collector chan *Record
	readBuf   *bytes.Buffer
}

func NewHttpWorker(config *Config, start *sync.WaitGroup, stop chan bool, jobs chan *http.Request, collector chan *Record) *HttpWorker {
	return &HttpWorker{config, start, stop, NewClient(config), jobs, collector, bytes.NewBuffer(make([]byte, 0, config.contentSize+bytes.MinRead))}
}

func (h *HttpWorker) Run() {
	h.start.Done()
	h.start.Wait()

	for job := range h.jobs {

		asyncResult := h.send(job)
		// TODO: use timer.Reset(d) instead of create new timer (Go 1.1)
		timeout := time.NewTimer(time.Duration(MAX_RESPONSE_TIMEOUT) * time.Second)

		select {
		case record := <-asyncResult:
			h.collector <- record

		case <-timeout.C:
			h.collector <- &Record{Error: errors.New("execution timeout")}

		case <-h.stop:
			timeout.Stop()
			return
		}
		timeout.Stop()
	}
}

func (h *HttpWorker) send(request *http.Request) (asyncResult chan *Record) {

	asyncResult = make(chan *Record)
	go func() {
		record := &Record{}
		sw := &StopWatch{}
		sw.Start()

		var contentSize int64

		defer func() {
			if r := recover(); r != nil {
				if Err, ok := r.(error); ok {
					record.Error = Err
				} else {
					record.Error = &ExceptionError{errors.New(fmt.Sprint(r))}
				}

			} else {
				record.contentSize = contentSize
				record.responseTime = sw.Elapsed
			}

			if record.Error != nil {
				TraceException(record.Error)
			}

			asyncResult <- record
		}()

		resp, err := h.client.Do(request)
		if err != nil {
			record.Error = &ConnectError{err}
			return
		} else {
			defer resp.Body.Close()

			if resp.StatusCode < 200 || resp.StatusCode > 300 {
				record.Error = &ResponseError{err}
				return
			}

			defer h.readBuf.Reset()
			var err error
			contentSize, err = h.readBuf.ReadFrom(resp.Body)

			if err != nil {
				record.Error = &ReceiveError{err}
				return
			}

			expectedContentSize := 0
			headerContentSize := resp.Header.Get("Content-Length")
			if headerContentSize != "" {
				expectedContentSize, _ = strconv.Atoi(headerContentSize)
			} else {
				expectedContentSize = h.config.contentSize
			}

			if h.config.method != "HEAD" && int64(expectedContentSize) != contentSize {
				record.Error = &LengthError{invalidContnetSize}
				return
			}

		}

		sw.Stop()
	}()
	return asyncResult
}

func detectHost(config *Config) error {
	var err error
	defer func() {
		if r := recover(); r != nil {
			TraceException(r)
		}
	}()

	client := NewClient(config)
	reqeust, err := NewHttpRequest(config)
	if err != nil {
		return err
	}

	resp, err := client.Do(reqeust)

	if err != nil {
		return err
	} else {

		defer resp.Body.Close()
		body, _ := ioutil.ReadAll(resp.Body)

		// TODO: place on another context
		config.serverName = resp.Header.Get("Server")
		headerContentSize := resp.Header.Get("Content-Length")

		if headerContentSize != "" {
			config.contentSize, _ = strconv.Atoi(headerContentSize)
		} else {
			config.contentSize = len(body)
		}
	}

	return nil
}

func NewClient(config *Config) *http.Client {

	// skip certification check for self-signed certificates
	tlsconfig := &tls.Config{
		InsecureSkipVerify: true,
	}

	// TODO: timeout control
	// TODO: tcp options
	// TODO: monitor tcp metrics
	transport := &http.Transport{
		DisableCompression: !config.gzip,
		DisableKeepAlives:  !config.keepAlive,
		TLSClientConfig:    tlsconfig,
	}

	client := &http.Client{Transport: transport}
	return client
}

func NewHttpRequest(config *Config) (*http.Request, error) {

	var body io.Reader
	var err error

	if (config.method == "POST" || config.method == "PUT") && config.bodyFile != nil {

		body = bytes.NewReader(config.bodyFile)
	}

	request, err := http.NewRequest(config.method, config.url, body)

	if err != nil {
		return nil, err
	}

	if body != nil && config.contentType == "text/plain" {
		request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	} else {
		request.Header.Set("Content-Type", config.contentType)
	}

	request.Header.Set("User-Agent", config.userAgent)

	if config.keepAlive {
		request.Header.Set("Connection", "keep-alive")
	}

	for _, header := range config.headers {
		pair := strings.Split(header, ":")
		request.Header.Add(pair[0], pair[1])
	}

	for _, cookie := range config.cookies {
		pair := strings.Split(cookie, "=")
		c := &http.Cookie{Name: pair[0], Value: pair[1]}
		request.AddCookie(c)
	}

	if config.basicAuthentication != "" {
		pair := strings.Split(config.basicAuthentication, ":")
		request.SetBasicAuth(pair[0], pair[1])
	}

	return request, err
}

func CopyHttpRequest(config *Config, request *http.Request) *http.Request {

	if config.method != "POST" || config.method != "PUT" {
		return request
	}

	newRequest := *request
	if newRequest.Body != nil {
		newRequest.Body = ioutil.NopCloser(bytes.NewReader(config.bodyFile))
	}
	return &newRequest
}

type LengthError struct {
	err error
}

func (e *LengthError) Error() string {
	return e.err.Error()
}

type ConnectError struct {
	err error
}

func (e *ConnectError) Error() string {
	return e.err.Error()
}

type ReceiveError struct {
	err error
}

func (e *ReceiveError) Error() string {
	return e.err.Error()
}

type ExceptionError struct {
	err error
}

func (e *ExceptionError) Error() string {
	return e.err.Error()
}

type ResponseError struct {
	err error
}

func (e *ResponseError) Error() string {
	return e.err.Error()
}
<|endoftext|>


<commit_before>/*
	Package hwio implements a simple Arduino-like interface for controlling
	hardware I/O, with configurable backends depending on the device.
*/
package hwio

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"
)

type BitShiftOrder byte

const (
	LSBFIRST BitShiftOrder = iota
	MSBFIRST
)

// Reference to driver we're using
var driver HardwareDriver

// Retrieved from the driver, this is the map of the hardware pins supported by
// the driver and their capabilities
var definedPins HardwarePinMap

// A private type for associating a pin's definition with the current IO mode
// and any other dynamic properties of the pin.
type assignedPin struct {
	pinDef    *PinDef   // definition of pin
	pinIOMode PinIOMode // mode that was assigned to this pin
}

// A map of pin numbers to the assigned dynamic properties of the pin. This is
// set by PinMode when errorChecking is on, and can be used by other functions
// to determine if the request is valid given the assigned properties of the pin.
var assignedPins map[Pin]*assignedPin

// If set to true, functions should test that their constraints are met.
// e.g. test that the pin is capable of doing what is asked. This can be set
// with SetErrorChecking(). Setting to false bypasses checks for performance.
// By default turned on, which is a better default for beginners.
var errorChecking bool = true

// init() attempts to determine from the environment what the driver is. The
// intent is that the consumer of the library would not generally have to worry
// about it, it would just work. If it cannot determine the driver, it doesn't
// set the driver to anything.
func init() {
	determineDriver()
}

func fileExists(name string) bool {
	_, err := os.Stat(name)
	if err != nil {
		return false
	}

	return true
}

// Work out the driver from environment if we can. If we have any problems,
// don't generate an error, just return with the driver not set.
// @todo use reflection to determine all implementors of the driver interface, and
// @todo   call a method on the interface to self-detect. init and
// @todo   constructor of drivers should do no setup in this case, esp of hardware
func determineDriver() {
	uname, e := exec.Command("uname", "-a").Output()
	if e != nil {
		return
	}

	s := string(uname)
	if strings.Contains(s, "beaglebone") {
		if fileExists("/sys/kernel/debug/omap_mux") {
			SetDriver(new(BeagleBoneDriver))
		} else {
			SetDriver(new(BeagleBoneFSDriver))
		}
	} else if strings.Contains(s, "raspberrypi") || strings.Contains(s, "adafruit") {
		SetDriver(new(RaspberryPiDriver))
	} else {
		file, e := os.Open("/etc/rpi-issue") // test for existence (only)
		if e == nil {
			file.Close()
			SetDriver(new(RaspberryPiDriver))
		} else {
			fmt.Printf("Unable to select a suitable driver for this board.\n%s\n", s)
		}
	}
}

// Check if the driver is assigned. If not, return an error to indicate that,
// otherwise return no error.
func assertDriver() error {
	if driver == nil {
		return errors.New("hwio has no configured driver")
	}
	return nil
}

// Set the driver. Also calls Init on the driver, and loads the capabilities
// of the device.
func SetDriver(d HardwareDriver) {
	driver = d
	e := driver.Init()
	if e != nil {
		fmt.Printf("Could not initialise driver: %s", e)
	}
	definedPins = driver.PinMap()
	assignedPins = make(map[Pin]*assignedPin)
}

// Retrieve the current hardware driver.
func GetDriver() HardwareDriver {
	return driver
}

// Returns a map of the hardware pins. This will only work once the driver is
// set.
func GetDefinedPins() HardwarePinMap {
	return definedPins
}

// Returns a Pin given a canonical name for the pin.
// e.g. to get the pin number of P8.13 on a beaglebone,
//     pin := hwio.GetPin("P8.13")
// Order of search is:
// - search hwRefs in the pin map in order.
// This function should not generally be relied on for performance. For max speed, call this
// for each pin you use once on init, and use the returned Pin values thereafter.
// Search is case sensitive at the moment
// @todo GetPin: consider making it case-insensitive on name
// @todo GetPin: consider allowing an int or int as string to identify logical pin directly
func GetPin(cname string) (Pin, error) {
	for pin, pinDef := range definedPins {
		for _, name := range pinDef.hwPinRefs {
			if name == cname {
				return pin, nil
			}
		}
	}
	return Pin(0), errors.New(fmt.Sprintf("Could not find a pin called %s", cname))
}

// Set error checking. This should be called before pin assignments.
func SetErrorChecking(check bool) {
	errorChecking = check
}

// Set the mode of a pin. Analogous to Arduino pin mode.
func PinMode(pin Pin, mode PinIOMode) (e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		pd := definedPins[pin]
		if pd == nil {
			return errors.New(fmt.Sprintf("Pin %d is not defined by the current driver", pin))
		}

		if e = checkPinMode(mode, pd); e != nil {
			return
		}

		// assign this pin
		assignedPins[pin] = &assignedPin{pinDef: pd, pinIOMode: mode}
	}

	return driver.PinMode(pin, mode)
}

func checkPinMode(mode PinIOMode, pd *PinDef) (e error) {
	ok := false
	switch mode {
	case INPUT:
		ok = pd.HasCapability(CAP_INPUT) || pd.HasCapability(CAP_ANALOG_IN)
	case OUTPUT:
		ok = pd.HasCapability(CAP_OUTPUT)
	case INPUT_PULLUP:
		ok = pd.HasCapability(CAP_INPUT_PULLUP)
	case INPUT_PULLDOWN:
		ok = pd.HasCapability(CAP_INPUT_PULLDOWN)
	}
	if ok {
		return nil
	}
	return errors.New(fmt.Sprintf("Pin %d can't be set to mode %s because it does not support that capability", pd.pin, mode.String()))
}

// Write a value to a digital pin
func DigitalWrite(pin Pin, value int) (e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		a := assignedPins[pin]
		if a == nil {
			return errors.New(fmt.Sprintf("DigitalWrite: pin %d mode has not been set", pin))
		}
		if a.pinIOMode != OUTPUT {
			return errors.New(fmt.Sprintf("DigitalWrite: pin %d mode is not set for output", pin))
		}
	}

	return driver.DigitalWrite(pin, value)
}

// Read a value from a digital pin
func DigitalRead(pin Pin) (result int, e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return 0, e
		}

		a := assignedPins[pin]
		if a == nil {
			e = errors.New(fmt.Sprintf("DigitalRead: pin %d mode has not been set", pin))
			return
		}
		if a.pinIOMode != INPUT && a.pinIOMode != INPUT_PULLUP && a.pinIOMode != INPUT_PULLDOWN {
			e = errors.New(fmt.Sprintf("DigitalRead: pin %d mode is not set for input", pin))
			return
		}
	}

	return driver.DigitalRead(pin)
}

// Read an analog value from a pin. The range of values is hardware driver dependent.
func AnalogRead(pin Pin) (result int, e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		a := assignedPins[pin]
		if a == nil {
			e = errors.New(fmt.Sprintf("AnalogRead: pin %d mode has not been set", pin))
			return
		}
		if a.pinIOMode != INPUT && a.pinIOMode != INPUT_PULLUP {
			e = errors.New(fmt.Sprintf("AnalogRead: pin %d mode is not set for input", pin))
			return
		}
	}

	return driver.AnalogRead(pin)
}

// Write an analog value. The interpretation is hardware dependent, but is
// generally implemented using PWM.
func AnalogWrite(pin Pin, value int) (e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		a := assignedPins[pin]
		if a == nil {
			return errors.New(fmt.Sprintf("AnalogWrite: pin %d mode has not been set", pin))
		}
		if a.pinIOMode != OUTPUT {
			return errors.New(fmt.Sprintf("AnalogWrite: pin %d mode is not set for output", pin))
		}
	}

	return driver.AnalogWrite(pin, value)
}

// Delay execution by the specified number of milliseconds. This is a helper
// function for similarity with Arduino. It is implemented using standard go
// time package.
func Delay(duration int) {
	time.Sleep(time.Duration(duration) * time.Millisecond)
}

// Delay execution by the specified number of microseconds. This is a helper
// function for similarity with Arduino. It is implemented using standard go
// time package
func DelayMicroseconds(duration int) {
	time.Sleep(time.Duration(duration) * time.Microsecond)
}

// @todo DebugPinMap: sort
func DebugPinMap() {
	fmt.Println("HardwarePinMap:")
	for key, val := range definedPins {
		fmt.Printf("Pin %d: %s\n", key, val.String())
	}
	fmt.Printf("\n")
}

// The approximate mapping of Arduino shiftOut, this shifts a byte out on the
// data pin, pulsing the clock pin high and then low.
func ShiftOut(dataPin Pin, clockPin Pin, value uint, order BitShiftOrder) error {
	return ShiftOutSize(dataPin, clockPin, value, order, 8)
}

// More generic version of ShiftOut which shifts out n of data from value. The
// value shifted out is always the lowest n bits of the value, but 'order'
// determines whether the msb or lsb from that value are shifted first
func ShiftOutSize(dataPin Pin, clockPin Pin, value uint, order BitShiftOrder, n uint) error {
	bit := uint(0)
	v := value
	mask := uint(1) << (n - 1)
	for i := uint(0); i < n; i++ {
		// get the next bit
		if order == LSBFIRST {
			bit = v & 1
			v = v >> 1
		} else {
			bit = v & mask
			if bit != 0 {
				bit = 1
			}
			v = v << 1
		}
		// write to data pin
		e := DigitalWrite(dataPin, int(bit))
		if e != nil {
			return e
		}
		// pulse clock high and then low
		e = DigitalWrite(clockPin, HIGH)
		if e != nil {
			return e
		}
		DigitalWrite(clockPin, LOW)
	}
	return nil
}

// def toggle(gpio_pin):
//   """ Toggles the state of the given digital pin. """
//   assert (gpio_pin in GPIO), "*Invalid GPIO pin: '%s'" % gpio_pin
//   _xorReg(GPIO[gpio_pin][0]+GPIO_DATAOUT, GPIO[gpio_pin][1])

// def pinState(gpio_pin):
//   """ Returns the state of a digital pin if it is configured as
//       an output. Returns None if it is configuredas an input. """
//   assert (gpio_pin in GPIO), "*Invalid GPIO pin: '%s'" % gpio_pin
//   if (_getReg(GPIO[gpio_pin][0]+GPIO_OE) & GPIO[gpio_pin][1]):
//     return None
//   if (_getReg(GPIO[gpio_pin][0]+GPIO_DATAOUT) & GPIO[gpio_pin][1]):
//     return HIGH
//   return LOW

// @todo Implement other core Arduino function equivalents:
//	AnalogReference
//	Tone
//	NoTone
//	ShiftOut
//	ShiftIn
//	PulseIn
//	Millis
//	Micros
//	RandomSeed
//	Random
//	AttachInterupt
//	DetachInterupt

// This is the interface that hardware drivers implement.
type HardwareDriver interface {
	// Initialise the driver after creation
	Init() (e error)

	// Set mode of a pin
	PinMode(pin Pin, mode PinIOMode) (e error)

	// Write digital output
	DigitalWrite(pin Pin, value int) error

	// Read digital input
	DigitalRead(Pin) (int, error)

	// PWM write
	AnalogWrite(pin Pin, value int) error

	// Analog input. Resolution is device dependent.
	AnalogRead(pin Pin) (int, error)

	// Return the pin map for the driver, listing all supported pins and their capabilities
	PinMap() (pinMap HardwarePinMap)

	// Close the driver before destruction
	Close()
}
<commit_msg>Implemented GetPinWithMode helper <commit_after>/*
	Package hwio implements a simple Arduino-like interface for controlling
	hardware I/O, with configurable backends depending on the device.
*/
package hwio

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"
)

type BitShiftOrder byte

const (
	LSBFIRST BitShiftOrder = iota
	MSBFIRST
)

// Reference to driver we're using
var driver HardwareDriver

// Retrieved from the driver, this is the map of the hardware pins supported by
// the driver and their capabilities
var definedPins HardwarePinMap

// A private type for associating a pin's definition with the current IO mode
// and any other dynamic properties of the pin.
type assignedPin struct {
	pinDef    *PinDef   // definition of pin
	pinIOMode PinIOMode // mode that was assigned to this pin
}

// A map of pin numbers to the assigned dynamic properties of the pin. This is
// set by PinMode when errorChecking is on, and can be used by other functions
// to determine if the request is valid given the assigned properties of the pin.
var assignedPins map[Pin]*assignedPin

// If set to true, functions should test that their constraints are met.
// e.g. test that the pin is capable of doing what is asked. This can be set
// with SetErrorChecking(). Setting to false bypasses checks for performance.
// By default turned on, which is a better default for beginners.
var errorChecking bool = true

// init() attempts to determine from the environment what the driver is. The
// intent is that the consumer of the library would not generally have to worry
// about it, it would just work. If it cannot determine the driver, it doesn't
// set the driver to anything.
func init() {
	determineDriver()
}

func fileExists(name string) bool {
	_, err := os.Stat(name)
	if err != nil {
		return false
	}

	return true
}

// Work out the driver from environment if we can. If we have any problems,
// don't generate an error, just return with the driver not set.
// @todo use reflection to determine all implementors of the driver interface, and
// @todo   call a method on the interface to self-detect. init and
// @todo   constructor of drivers should do no setup in this case, esp of hardware
func determineDriver() {
	uname, e := exec.Command("uname", "-a").Output()
	if e != nil {
		return
	}

	s := string(uname)
	if strings.Contains(s, "beaglebone") {
		if fileExists("/sys/kernel/debug/omap_mux") {
			SetDriver(new(BeagleBoneDriver))
		} else {
			SetDriver(new(BeagleBoneFSDriver))
		}
	} else if strings.Contains(s, "raspberrypi") || strings.Contains(s, "adafruit") {
		SetDriver(new(RaspberryPiDriver))
	} else {
		file, e := os.Open("/etc/rpi-issue") // test for existence (only)
		if e == nil {
			file.Close()
			SetDriver(new(RaspberryPiDriver))
		} else {
			fmt.Printf("Unable to select a suitable driver for this board.\n%s\n", s)
		}
	}
}

// Check if the driver is assigned. If not, return an error to indicate that,
// otherwise return no error.
func assertDriver() error {
	if driver == nil {
		return errors.New("hwio has no configured driver")
	}
	return nil
}

// Set the driver. Also calls Init on the driver, and loads the capabilities
// of the device.
func SetDriver(d HardwareDriver) {
	driver = d
	e := driver.Init()
	if e != nil {
		fmt.Printf("Could not initialise driver: %s", e)
	}
	definedPins = driver.PinMap()
	assignedPins = make(map[Pin]*assignedPin)
}

// Retrieve the current hardware driver.
func GetDriver() HardwareDriver {
	return driver
}

// Returns a map of the hardware pins. This will only work once the driver is
// set.
func GetDefinedPins() HardwarePinMap {
	return definedPins
}

// Returns a Pin given a canonical name for the pin.
// e.g. to get the pin number of P8.13 on a beaglebone,
//     pin := hwio.GetPin("P8.13")
// Order of search is:
// - search hwRefs in the pin map in order.
// This function should not generally be relied on for performance. For max speed, call this
// for each pin you use once on init, and use the returned Pin values thereafter.
// Search is case sensitive at the moment
// @todo GetPin: consider making it case-insensitive on name
// @todo GetPin: consider allowing an int or int as string to identify logical pin directly
func GetPin(cname string) (Pin, error) {
	for pin, pinDef := range definedPins {
		for _, name := range pinDef.hwPinRefs {
			if name == cname {
				return pin, nil
			}
		}
	}
	return Pin(0), errors.New(fmt.Sprintf("Could not find a pin called %s", cname))
}

// Shortcut for calling GetPin and then PinMode.
func GetPinWithMode(cname string, mode PinIOMode) (pin Pin, e error) {
	p, e := GetPin(cname)
	if e != nil {
		return
	}

	e = PinMode(p, mode)
	return p, e
}

// Set error checking. This should be called before pin assignments.
func SetErrorChecking(check bool) {
	errorChecking = check
}

// Set the mode of a pin. Analogous to Arduino pin mode.
func PinMode(pin Pin, mode PinIOMode) (e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		pd := definedPins[pin]
		if pd == nil {
			return errors.New(fmt.Sprintf("Pin %d is not defined by the current driver", pin))
		}

		if e = checkPinMode(mode, pd); e != nil {
			return
		}

		// assign this pin
		assignedPins[pin] = &assignedPin{pinDef: pd, pinIOMode: mode}
	}

	return driver.PinMode(pin, mode)
}

func checkPinMode(mode PinIOMode, pd *PinDef) (e error) {
	ok := false
	switch mode {
	case INPUT:
		ok = pd.HasCapability(CAP_INPUT) || pd.HasCapability(CAP_ANALOG_IN)
	case OUTPUT:
		ok = pd.HasCapability(CAP_OUTPUT)
	case INPUT_PULLUP:
		ok = pd.HasCapability(CAP_INPUT_PULLUP)
	case INPUT_PULLDOWN:
		ok = pd.HasCapability(CAP_INPUT_PULLDOWN)
	}
	if ok {
		return nil
	}
	return errors.New(fmt.Sprintf("Pin %d can't be set to mode %s because it does not support that capability", pd.pin, mode.String()))
}

// Write a value to a digital pin
func DigitalWrite(pin Pin, value int) (e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		a := assignedPins[pin]
		if a == nil {
			return errors.New(fmt.Sprintf("DigitalWrite: pin %d mode has not been set", pin))
		}
		if a.pinIOMode != OUTPUT {
			return errors.New(fmt.Sprintf("DigitalWrite: pin %d mode is not set for output", pin))
		}
	}

	return driver.DigitalWrite(pin, value)
}

// Read a value from a digital pin
func DigitalRead(pin Pin) (result int, e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return 0, e
		}

		a := assignedPins[pin]
		if a == nil {
			e = errors.New(fmt.Sprintf("DigitalRead: pin %d mode has not been set", pin))
			return
		}
		if a.pinIOMode != INPUT && a.pinIOMode != INPUT_PULLUP && a.pinIOMode != INPUT_PULLDOWN {
			e = errors.New(fmt.Sprintf("DigitalRead: pin %d mode is not set for input", pin))
			return
		}
	}

	return driver.DigitalRead(pin)
}

// Read an analog value from a pin. The range of values is hardware driver dependent.
func AnalogRead(pin Pin) (result int, e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		a := assignedPins[pin]
		if a == nil {
			e = errors.New(fmt.Sprintf("AnalogRead: pin %d mode has not been set", pin))
			return
		}
		if a.pinIOMode != INPUT && a.pinIOMode != INPUT_PULLUP {
			e = errors.New(fmt.Sprintf("AnalogRead: pin %d mode is not set for input", pin))
			return
		}
	}

	return driver.AnalogRead(pin)
}

// Write an analog value. The interpretation is hardware dependent, but is
// generally implemented using PWM.
func AnalogWrite(pin Pin, value int) (e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		a := assignedPins[pin]
		if a == nil {
			return errors.New(fmt.Sprintf("AnalogWrite: pin %d mode has not been set", pin))
		}
		if a.pinIOMode != OUTPUT {
			return errors.New(fmt.Sprintf("AnalogWrite: pin %d mode is not set for output", pin))
		}
	}

	return driver.AnalogWrite(pin, value)
}

// Delay execution by the specified number of milliseconds. This is a helper
// function for similarity with Arduino. It is implemented using standard go
// time package.
func Delay(duration int) {
	time.Sleep(time.Duration(duration) * time.Millisecond)
}

// Delay execution by the specified number of microseconds. This is a helper
// function for similarity with Arduino. It is implemented using standard go
// time package
func DelayMicroseconds(duration int) {
	time.Sleep(time.Duration(duration) * time.Microsecond)
}

// @todo DebugPinMap: sort
func DebugPinMap() {
	fmt.Println("HardwarePinMap:")
	for key, val := range definedPins {
		fmt.Printf("Pin %d: %s\n", key, val.String())
	}
	fmt.Printf("\n")
}

// The approximate mapping of Arduino shiftOut, this shifts a byte out on the
// data pin, pulsing the clock pin high and then low.
func ShiftOut(dataPin Pin, clockPin Pin, value uint, order BitShiftOrder) error {
	return ShiftOutSize(dataPin, clockPin, value, order, 8)
}

// More generic version of ShiftOut which shifts out n of data from value. The
// value shifted out is always the lowest n bits of the value, but 'order'
// determines whether the msb or lsb from that value are shifted first
func ShiftOutSize(dataPin Pin, clockPin Pin, value uint, order BitShiftOrder, n uint) error {
	bit := uint(0)
	v := value
	mask := uint(1) << (n - 1)
	for i := uint(0); i < n; i++ {
		// get the next bit
		if order == LSBFIRST {
			bit = v & 1
			v = v >> 1
		} else {
			bit = v & mask
			if bit != 0 {
				bit = 1
			}
			v = v << 1
		}
		// write to data pin
		e := DigitalWrite(dataPin, int(bit))
		if e != nil {
			return e
		}
		// pulse clock high and then low
		e = DigitalWrite(clockPin, HIGH)
		if e != nil {
			return e
		}
		DigitalWrite(clockPin, LOW)
	}
	return nil
}

// def toggle(gpio_pin):
//   """ Toggles the state of the given digital pin. """
//   assert (gpio_pin in GPIO), "*Invalid GPIO pin: '%s'" % gpio_pin
//   _xorReg(GPIO[gpio_pin][0]+GPIO_DATAOUT, GPIO[gpio_pin][1])

// def pinState(gpio_pin):
//   """ Returns the state of a digital pin if it is configured as
//       an output. Returns None if it is configuredas an input. """
//   assert (gpio_pin in GPIO), "*Invalid GPIO pin: '%s'" % gpio_pin
//   if (_getReg(GPIO[gpio_pin][0]+GPIO_OE) & GPIO[gpio_pin][1]):
//     return None
//   if (_getReg(GPIO[gpio_pin][0]+GPIO_DATAOUT) & GPIO[gpio_pin][1]):
//     return HIGH
//   return LOW

// @todo Implement other core Arduino function equivalents:
//	AnalogReference
//	Tone
//	NoTone
//	ShiftOut
//	ShiftIn
//	PulseIn
//	Millis
//	Micros
//	RandomSeed
//	Random
//	AttachInterupt
//	DetachInterupt

// This is the interface that hardware drivers implement.
type HardwareDriver interface {
	// Initialise the driver after creation
	Init() (e error)

	// Set mode of a pin
	PinMode(pin Pin, mode PinIOMode) (e error)

	// Write digital output
	DigitalWrite(pin Pin, value int) error

	// Read digital input
	DigitalRead(Pin) (int, error)

	// PWM write
	AnalogWrite(pin Pin, value int) error

	// Analog input. Resolution is device dependent.
	AnalogRead(pin Pin) (int, error)

	// Return the pin map for the driver, listing all supported pins and their capabilities
	PinMap() (pinMap HardwarePinMap)

	// Close the driver before destruction
	Close()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/JustinBeckwith/go-yelp/yelp"
	"github.com/guregu/null"
	"github.com/line/line-bot-sdk-go/linebot"
)

var o *yelp.AuthOptions

type UrlShortener struct {
	ShortUrl    string
	OriginalUrl string
}

func yelp_init() {
	rand.Seed(time.Now().UnixNano())

	// check environment variables
	o = &yelp.AuthOptions{
		ConsumerKey:       os.Getenv("CONSUMER_KEY"),
		ConsumerSecret:    os.Getenv("CONSUMER_SECRET"),
		AccessToken:       os.Getenv("ACCESS_TOKEN"),
		AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
	}

	if o.ConsumerKey == "" || o.ConsumerSecret == "" || o.AccessToken == "" || o.AccessTokenSecret == "" {
		log.Fatal("Wrong environment setting about yelp-api-keys")
	}
}

func yelp_parse(bot *linebot.Client, token string, loc *linebot.LocationMessage, food string) {
	var err error

	// create a new yelp client with the auth keys
	client := yelp.New(o, nil)

	if loc == nil {
		// make a simple query for food and location
		results, err := client.DoSimpleSearch(food, "台北市通化街")
		if err != nil {
			log.Println(err)
			_, err = bot.ReplyMessage(token, linebot.NewTextMessage("查無資料！")).Do()
		} else {
			yelp_parse_result(bot, token, results)
		}
	} else {
		// Build an advanced set of search criteria that include
		// general options, and coordinate options.
		s := yelp.SearchOptions{
			GeneralOptions: &yelp.GeneralOptions{
				Term: food,
			},
			CoordinateOptions: &yelp.CoordinateOptions{
				Latitude:  null.FloatFrom(loc.Latitude),
				Longitude: null.FloatFrom(loc.Longitude),
			},
		}

		// Perform the search using the search options
		results, err := client.DoSearch(s)
		if err != nil {
			log.Println(err)
			_, err = bot.ReplyMessage(token, linebot.NewTextMessage("查無資料！")).Do()
		} else {
			yelp_parse_result(bot, token, results)
		}
	}
}

func yelp_parse_result(bot *linebot.Client, token string, results yelp.SearchResult) {
//	var msgs []linebot.Message

	for j := 0; j < 3; j++ {
		i := 0
		if results.Total >= 20 {
			i = rand.Intn(20)
		} else if results.Total >= 10 {
			i = rand.Intn(10)
		} else if results.Total > j {
			i = j
		} else if results.Total <= j && results.Total != 0 {
			_, err = bot.ReplyMessage(token, linebot.NewTextMessage("已無更多資料！")).Do()
			break
		}
		urlOrig := UrlShortener{}
		urlOrig.short(results.Businesses[i].MobileURL)
		address := strings.Join(results.Businesses[i].Location.DisplayAddress, ",")
		var largeImageURL = strings.Replace(results.Businesses[i].ImageURL, "ms.jpg", "l.jpg", 1)

		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL), linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl), linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude)) ).Do()
//		msgs = append(msgs, linebot.NewImageMessage(largeImageURL, largeImageURL))
//		msgs = append(msgs, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl))
//		msgs = append(msgs, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude)))
//		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude))).Do()
	}

//	if len(msgs) > 0 {
//		_, err = bot.ReplyMessage(token, msgs).Do()
//	}
}

func getResponseData(urlOrig string) string {
	response, err := http.Get(urlOrig)
	if err != nil {
		log.Println(err)
	}
	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	return string(contents)
}

func isGdShortener(urlOrig string) (string, string) {
	escapedUrl := url.QueryEscape(urlOrig)
	isGdUrl := fmt.Sprintf("http://is.gd/create.php?url=%s&format=simple", escapedUrl)
	return getResponseData(isGdUrl), urlOrig
}

func (u *UrlShortener) short(urlOrig string) *UrlShortener {
	shortUrl, originalUrl := isGdShortener(urlOrig)
	u.ShortUrl = shortUrl
	u.OriginalUrl = originalUrl
	return u
}
<commit_msg>test location<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/JustinBeckwith/go-yelp/yelp"
	"github.com/guregu/null"
	"github.com/line/line-bot-sdk-go/linebot"
)

var o *yelp.AuthOptions

type UrlShortener struct {
	ShortUrl    string
	OriginalUrl string
}

func yelp_init() {
	rand.Seed(time.Now().UnixNano())

	// check environment variables
	o = &yelp.AuthOptions{
		ConsumerKey:       os.Getenv("CONSUMER_KEY"),
		ConsumerSecret:    os.Getenv("CONSUMER_SECRET"),
		AccessToken:       os.Getenv("ACCESS_TOKEN"),
		AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
	}

	if o.ConsumerKey == "" || o.ConsumerSecret == "" || o.AccessToken == "" || o.AccessTokenSecret == "" {
		log.Fatal("Wrong environment setting about yelp-api-keys")
	}
}

func yelp_parse(bot *linebot.Client, token string, loc *linebot.LocationMessage, food string) {
	var err error

	// create a new yelp client with the auth keys
	client := yelp.New(o, nil)

	if loc == nil {
		// make a simple query for food and location
		results, err := client.DoSimpleSearch(food, "台北市通化街")
		if err != nil {
			log.Println(err)
			_, err = bot.ReplyMessage(token, linebot.NewTextMessage("查無資料！")).Do()
		} else {
			yelp_parse_result(bot, token, results)
		}
	} else {
		// Build an advanced set of search criteria that include
		// general options, and coordinate options.
		s := yelp.SearchOptions{
			GeneralOptions: &yelp.GeneralOptions{
				Term: food,
			},
			CoordinateOptions: &yelp.CoordinateOptions{
				Latitude:  null.FloatFrom(loc.Latitude),
				Longitude: null.FloatFrom(loc.Longitude),
			},
		}

		// Perform the search using the search options
		results, err := client.DoSearch(s)
		if err != nil {
			log.Println(err)
			_, err = bot.ReplyMessage(token, linebot.NewTextMessage("查無資料！")).Do()
		} else {
			yelp_parse_result(bot, token, results)
		}
	}
}

func yelp_parse_result(bot *linebot.Client, token string, results yelp.SearchResult) {
	var err error
//	var msgs []linebot.Message

	for j := 0; j < 3; j++ {
		i := 0
		if results.Total >= 20 {
			i = rand.Intn(20)
		} else if results.Total >= 10 {
			i = rand.Intn(10)
		} else if results.Total > j {
			i = j
		} else if results.Total <= j && results.Total != 0 {
			_, err = bot.ReplyMessage(token, linebot.NewTextMessage("已無更多資料！")).Do()
			break
		}
		urlOrig := UrlShortener{}
		urlOrig.short(results.Businesses[i].MobileURL)
		address := strings.Join(results.Businesses[i].Location.DisplayAddress, ",")
		var largeImageURL = strings.Replace(results.Businesses[i].ImageURL, "ms.jpg", "l.jpg", 1)

		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL), linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl), linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude)) ).Do()
//		msgs = append(msgs, linebot.NewImageMessage(largeImageURL, largeImageURL))
//		msgs = append(msgs, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl))
//		msgs = append(msgs, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude)))
//		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude))).Do()
	}

//	if len(msgs) > 0 {
//		_, err = bot.ReplyMessage(token, msgs).Do()
//	}
}

func getResponseData(urlOrig string) string {
	response, err := http.Get(urlOrig)
	if err != nil {
		log.Println(err)
	}
	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	return string(contents)
}

func isGdShortener(urlOrig string) (string, string) {
	escapedUrl := url.QueryEscape(urlOrig)
	isGdUrl := fmt.Sprintf("http://is.gd/create.php?url=%s&format=simple", escapedUrl)
	return getResponseData(isGdUrl), urlOrig
}

func (u *UrlShortener) short(urlOrig string) *UrlShortener {
	shortUrl, originalUrl := isGdShortener(urlOrig)
	u.ShortUrl = shortUrl
	u.OriginalUrl = originalUrl
	return u
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/droundy/goopt"
	"github.com/gin-gonic/gin"
	"net/http"
	"net/url"
)

var host = goopt.String(
	[]string{"-h", "--host"}, "0.0.0.0", "web server host address to bind.")
var port = goopt.Int(
	[]string{"-p", "--port"}, 4040, "web server listening port to bind.")
var yoAPIToken = goopt.String(
	[]string{"-y", "--yo"}, "", "the Yo API token.")

func Parse() {
	goopt.Summary = "Runs YoYo web server."
	goopt.Parse(nil)
	if *yoAPIToken == "" {
		panic("Yo API token required.")
	}
}

func WebServer() *gin.Engine {
	www := gin.Default()
	www.GET("/", func(c *gin.Context) {
		query := c.Req.URL.Query()
		username := query["username"][0]
		form := url.Values{"api_token": {*yoAPIToken}, "username": {username}}
		http.PostForm("http://api.justyo.co/yo", form)
	})
	return www
}

func main() {
	Parse()
	endpoint := fmt.Sprintf("%s:%d", *host, *port)
	WebServer().Run(endpoint)
}
<commit_msg>fix api error<commit_after>package main

import (
	"fmt"
	"github.com/droundy/goopt"
	"github.com/gin-gonic/gin"
	"net/http"
	"net/url"
)

var host = goopt.String(
	[]string{"-h", "--host"}, "0.0.0.0", "web server host address to bind.")
var port = goopt.Int(
	[]string{"-p", "--port"}, 4040, "web server listening port to bind.")
var yoAPIToken = goopt.String(
	[]string{"-y", "--yo"}, "", "the Yo API token.")

func Parse() {
	goopt.Summary = "Runs YoYo web server."
	goopt.Parse(nil)
	if *yoAPIToken == "" {
		panic("Yo API token required.")
	}
}

func WebServer() *gin.Engine {
	www := gin.Default()
	www.GET("/", func(c *gin.Context) {
		query := c.Req.URL.Query()
		username := query["username"][0]
		form := url.Values{"api_token": {*yoAPIToken}, "username": {username}}
		http.PostForm("http://api.justyo.co/yo/", form)
	})
	return www
}

func main() {
	Parse()
	endpoint := fmt.Sprintf("%s:%d", *host, *port)
	WebServer().Run(endpoint)
}
<|endoftext|>


<commit_before>package lib

import (
    "os"
    "fmt"

    . "gopkg.in/check.v1"
)

type OssutilConfigSuite struct{}

var _ = Suite(&OssutilConfigSuite{})

// Run once when the suite starts running
func (s *OssutilConfigSuite) SetUpSuite(c *C) {
    os.Stdout = testLogFile
    os.Stderr = testLogFile
    testLogger.Println("test config started")
    SetUpCredential()
    cm.Init()
}

// Run before each test or benchmark starts running
func (s *OssutilConfigSuite) TearDownSuite(c *C) {
    testLogger.Println("test config completed")
}

// Run after each test or benchmark runs
func (s *OssutilConfigSuite) SetUpTest(c *C) {
    _ = os.Remove(configFile)
}

// Run once after all tests or benchmarks have finished running
func (s *OssutilConfigSuite) TearDownTest(c *C) {
    _ = os.Remove(configFile)
}

// test "config"
func (s *OssutilConfigSuite) TestConfigNonInteractive(c *C) {
    command := "config"
    var args []string
    options := OptionMapType{
        "endpoint": &endpoint,
        "accessKeyID": &accessKeyID,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &stsToken,
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(showElapse, Equals, false)
    c.Assert(err, IsNil)

    f, err := os.Stat(configFile)
    c.Assert(err, IsNil)
    c.Assert(f.Size() > 0, Equals, true)

    opts, err := LoadConfig(configFile)
    c.Assert(err, IsNil)
    c.Assert(len(opts), Equals, 5)
    c.Assert(opts[OptionLanguage], Equals, DefaultLanguage)
    c.Assert(opts[OptionEndpoint], Equals, endpoint)
    c.Assert(opts[OptionAccessKeyID], Equals, accessKeyID)
    c.Assert(opts[OptionAccessKeySecret], Equals, accessKeySecret)
    c.Assert(opts[OptionSTSToken], Equals, stsToken)
}

func (s *OssutilConfigSuite) TestConfigNonInteractiveLanguage(c *C) {
    command := "config"
    var args []string
    for _, language := range []string{DefaultLanguage, EnglishLanguage, LEnglishLanguage} {
        options := OptionMapType{
            "endpoint": &endpoint,
            "stsToken": &stsToken,
            "configFile": &configFile,
            "language": &language,
        }
        showElapse, err := cm.RunCommand(command, args, options)
        c.Assert(showElapse, Equals, false)
        c.Assert(err, IsNil)

        f, err := os.Stat(configFile)
        c.Assert(err, IsNil)
        c.Assert(f.Size() > 0, Equals, true)

        opts, err := LoadConfig(configFile)
        c.Assert(err, IsNil)
        c.Assert(len(opts), Equals, 3)
        c.Assert(opts[OptionEndpoint], Equals, endpoint)
        c.Assert(opts[OptionSTSToken], Equals, stsToken)
        c.Assert(opts[OptionLanguage], Equals, language)
    }
}

func (s *OssutilConfigSuite) TestConfigInteractive(c *C) {
    command := "config"
    var args []string
    options := OptionMapType{
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(showElapse, Equals, false)
    c.Assert(err, IsNil)

    f, err := os.Stat(configFile)
    c.Assert(err, IsNil)
    c.Assert(f.Size() > 0, Equals, true)

    opts, err := LoadConfig(configFile)
    c.Assert(err, IsNil)
    c.Assert(len(opts), Equals, 1)
    c.Assert(opts[OptionLanguage], Equals, DefaultLanguage)
}

func (s *OssutilConfigSuite) TestConfigInteractiveLanguage(c *C) {
    command := "config"
    var args []string
    for _, language := range []string{DefaultLanguage, EnglishLanguage, LEnglishLanguage} {
        options := OptionMapType{
            "configFile": &configFile,
            "language": &language,
        }
        showElapse, err := cm.RunCommand(command, args, options)
        c.Assert(showElapse, Equals, false)
        c.Assert(err, IsNil)
    }

    f, err := os.Stat(configFile)
    c.Assert(err, IsNil)
    c.Assert(f.Size() > 0, Equals, true)

    opts, err := LoadConfig(configFile)
    c.Assert(err, IsNil)
    c.Assert(len(opts), Equals, 1)
}

func (s *OssutilConfigSuite) TestConfigLanguageEN(c *C) {
    command := "config"
    var args []string
    language := "En"
    options := OptionMapType{
        "configFile": &configFile,
        "language": &language,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(showElapse, Equals, false)
    c.Assert(err, IsNil)

    f, err := os.Stat(configFile)
    c.Assert(err, IsNil)
    c.Assert(f.Size() > 0, Equals, true)

    opts, err := LoadConfig(configFile)
    c.Assert(err, IsNil)
    c.Assert(len(opts), Equals, 1)
    c.Assert(opts[OptionLanguage], Equals, language)
}

func (s *OssutilConfigSuite) TestConfigLanguageCH(c *C) {
    command := "config"
    var args []string
    language := "CH"
    options := OptionMapType{
        "configFile": &configFile,
        "language": &language,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(showElapse, Equals, false)
    c.Assert(err, IsNil)

    f, err := os.Stat(configFile)
    c.Assert(err, IsNil)
    c.Assert(f.Size() > 0, Equals, true)

    opts, err := LoadConfig(configFile)
    c.Assert(err, IsNil)
    c.Assert(len(opts), Equals, 1)
    c.Assert(opts[OptionLanguage], Equals, language)
}

// test option empty value
func (s *OssutilConfigSuite) TestConfigOptionEmptyValue(c *C) {
    command := "config"
    var args []string
    endp := ""
    id := ""
    options := OptionMapType{
        "endpoint": &endp,
        "accessKeyID": &id,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &stsToken,
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(showElapse, Equals, false)
    c.Assert(err, IsNil)

    f, err := os.Stat(configFile)
    c.Assert(err, IsNil)
    c.Assert(f.Size() > 0, Equals, true)

    opts, err := LoadConfig(configFile)
    c.Assert(err, IsNil)
    c.Assert(len(opts), Equals, 3)
    c.Assert(opts[OptionEndpoint], IsNil)
    c.Assert(opts[OptionAccessKeyID], IsNil)
    c.Assert(opts[OptionLanguage], Equals, DefaultLanguage)
    c.Assert(opts[OptionAccessKeySecret], Equals, accessKeySecret)
    c.Assert(opts[OptionSTSToken], Equals, stsToken)
}

// test invalid option
func (s *OssutilConfigSuite) TestConfigInvalidOptions(c *C) {
    command := "config"
    var args []string
    opts := []string{"acl", "bigfileThreshold", "checkpointDir", "retryTimes", "routines", "notexist"}
    for _, name := range opts {
        str := "private"
        options := OptionMapType{name: &str}
        showElapse, err := cm.RunCommand(command, args, options)
        c.Assert(showElapse, Equals, false)
        c.Assert(err, Equals, CommandError{command:"config", reason:fmt.Sprintf("the command does not support option: \"%s\"", name)})
    }
}

func (s *OssutilConfigSuite) TestConfigInvalidOption(c *C) {
    command := "config"
    var args []string
    shortFormat := true
    options := OptionMapType{
        "endpoint": &endpoint,
        "shortFormat": &shortFormat,
    }

    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(showElapse, Equals, false)
    c.Assert(err, Equals, CommandError{command:"config", reason:fmt.Sprintf("the command does not support option: \"%s\"", "shortFormat")})
}
<commit_msg>modify config_test to addpt ststoken empty<commit_after>package lib

import (
    "os"
    "fmt"

    . "gopkg.in/check.v1"
)

type OssutilConfigSuite struct{}

var _ = Suite(&OssutilConfigSuite{})

var token = stsToken

// Run once when the suite starts running
func (s *OssutilConfigSuite) SetUpSuite(c *C) {
    os.Stdout = testLogFile
    os.Stderr = testLogFile
    testLogger.Println("test config started")
    SetUpCredential()
    cm.Init()
}

// Run before each test or benchmark starts running
func (s *OssutilConfigSuite) TearDownSuite(c *C) {
    testLogger.Println("test config completed")
}

// Run after each test or benchmark runs
func (s *OssutilConfigSuite) SetUpTest(c *C) {
    if stsToken == "" {
        stsToken = "ststoken"
    }
    _ = os.Remove(configFile)
}

// Run once after all tests or benchmarks have finished running
func (s *OssutilConfigSuite) TearDownTest(c *C) {
    stsToken = token
    _ = os.Remove(configFile)
}

// test "config"
func (s *OssutilConfigSuite) TestConfigNonInteractive(c *C) {
    command := "config"
    var args []string
    options := OptionMapType{
        "endpoint": &endpoint,
        "accessKeyID": &accessKeyID,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &stsToken,
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(showElapse, Equals, false)
    c.Assert(err, IsNil)

    f, err := os.Stat(configFile)
    c.Assert(err, IsNil)
    c.Assert(f.Size() > 0, Equals, true)

    opts, err := LoadConfig(configFile)
    c.Assert(err, IsNil)
    c.Assert(len(opts), Equals, 5)
    c.Assert(opts[OptionLanguage], Equals, DefaultLanguage)
    c.Assert(opts[OptionEndpoint], Equals, endpoint)
    c.Assert(opts[OptionAccessKeyID], Equals, accessKeyID)
    c.Assert(opts[OptionAccessKeySecret], Equals, accessKeySecret)
    c.Assert(opts[OptionSTSToken], Equals, stsToken)
}

func (s *OssutilConfigSuite) TestConfigNonInteractiveLanguage(c *C) {
    command := "config"
    var args []string
    for _, language := range []string{DefaultLanguage, EnglishLanguage, LEnglishLanguage} {
        options := OptionMapType{
            "endpoint": &endpoint,
            "stsToken": &stsToken,
            "configFile": &configFile,
            "language": &language,
        }
        showElapse, err := cm.RunCommand(command, args, options)
        c.Assert(showElapse, Equals, false)
        c.Assert(err, IsNil)

        f, err := os.Stat(configFile)
        c.Assert(err, IsNil)
        c.Assert(f.Size() > 0, Equals, true)

        opts, err := LoadConfig(configFile)
        c.Assert(err, IsNil)
        c.Assert(len(opts), Equals, 3)
        c.Assert(opts[OptionEndpoint], Equals, endpoint)
        c.Assert(opts[OptionSTSToken], Equals, stsToken)
        c.Assert(opts[OptionLanguage], Equals, language)
    }
}

func (s *OssutilConfigSuite) TestConfigInteractive(c *C) {
    command := "config"
    var args []string
    options := OptionMapType{
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(showElapse, Equals, false)
    c.Assert(err, IsNil)

    f, err := os.Stat(configFile)
    c.Assert(err, IsNil)
    c.Assert(f.Size() > 0, Equals, true)

    opts, err := LoadConfig(configFile)
    c.Assert(err, IsNil)
    c.Assert(len(opts), Equals, 1)
    c.Assert(opts[OptionLanguage], Equals, DefaultLanguage)
}

func (s *OssutilConfigSuite) TestConfigInteractiveLanguage(c *C) {
    command := "config"
    var args []string
    for _, language := range []string{DefaultLanguage, EnglishLanguage, LEnglishLanguage} {
        options := OptionMapType{
            "configFile": &configFile,
            "language": &language,
        }
        showElapse, err := cm.RunCommand(command, args, options)
        c.Assert(showElapse, Equals, false)
        c.Assert(err, IsNil)
    }

    f, err := os.Stat(configFile)
    c.Assert(err, IsNil)
    c.Assert(f.Size() > 0, Equals, true)

    opts, err := LoadConfig(configFile)
    c.Assert(err, IsNil)
    c.Assert(len(opts), Equals, 1)
}

func (s *OssutilConfigSuite) TestConfigLanguageEN(c *C) {
    command := "config"
    var args []string
    language := "En"
    options := OptionMapType{
        "configFile": &configFile,
        "language": &language,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(showElapse, Equals, false)
    c.Assert(err, IsNil)

    f, err := os.Stat(configFile)
    c.Assert(err, IsNil)
    c.Assert(f.Size() > 0, Equals, true)

    opts, err := LoadConfig(configFile)
    c.Assert(err, IsNil)
    c.Assert(len(opts), Equals, 1)
    c.Assert(opts[OptionLanguage], Equals, language)
}

func (s *OssutilConfigSuite) TestConfigLanguageCH(c *C) {
    command := "config"
    var args []string
    language := "CH"
    options := OptionMapType{
        "configFile": &configFile,
        "language": &language,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(showElapse, Equals, false)
    c.Assert(err, IsNil)

    f, err := os.Stat(configFile)
    c.Assert(err, IsNil)
    c.Assert(f.Size() > 0, Equals, true)

    opts, err := LoadConfig(configFile)
    c.Assert(err, IsNil)
    c.Assert(len(opts), Equals, 1)
    c.Assert(opts[OptionLanguage], Equals, language)
}

// test option empty value
func (s *OssutilConfigSuite) TestConfigOptionEmptyValue(c *C) {
    command := "config"
    var args []string
    endp := ""
    id := ""
    options := OptionMapType{
        "endpoint": &endp,
        "accessKeyID": &id,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &stsToken,
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(showElapse, Equals, false)
    c.Assert(err, IsNil)

    f, err := os.Stat(configFile)
    c.Assert(err, IsNil)
    c.Assert(f.Size() > 0, Equals, true)

    opts, err := LoadConfig(configFile)
    c.Assert(err, IsNil)
    c.Assert(len(opts), Equals, 3)
    c.Assert(opts[OptionEndpoint], IsNil)
    c.Assert(opts[OptionAccessKeyID], IsNil)
    c.Assert(opts[OptionLanguage], Equals, DefaultLanguage)
    c.Assert(opts[OptionAccessKeySecret], Equals, accessKeySecret)
    c.Assert(opts[OptionSTSToken], Equals, stsToken)
}

// test invalid option
func (s *OssutilConfigSuite) TestConfigInvalidOptions(c *C) {
    command := "config"
    var args []string
    opts := []string{"acl", "bigfileThreshold", "checkpointDir", "retryTimes", "routines", "notexist"}
    for _, name := range opts {
        str := "private"
        options := OptionMapType{name: &str}
        showElapse, err := cm.RunCommand(command, args, options)
        c.Assert(showElapse, Equals, false)
        c.Assert(err, Equals, CommandError{command:"config", reason:fmt.Sprintf("the command does not support option: \"%s\"", name)})
    }
}

func (s *OssutilConfigSuite) TestConfigInvalidOption(c *C) {
    command := "config"
    var args []string
    shortFormat := true
    options := OptionMapType{
        "endpoint": &endpoint,
        "shortFormat": &shortFormat,
    }

    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(showElapse, Equals, false)
    c.Assert(err, Equals, CommandError{command:"config", reason:fmt.Sprintf("the command does not support option: \"%s\"", "shortFormat")})
}
<|endoftext|>


<commit_before>package sqlt

import (
	"context"
)

type (
	SqlDescriber interface {
		GetSql(c context.Context) (string, context.Context, error)
		Release()
	}

	SqlAssembler interface {
		AssembleSql(id string, data interface{}) (SqlDescriber, error)
	}

	RowScanner interface {
		Columns() ([]string, error)
		Scan(dest ...interface{}) error
		Err() error
	}

	RowsHandler interface {
		HandleRow(r RowScanner)
	}

	MultiRowsHandler interface {
		RowsHandler
		AddResultSet()
	}
)
<commit_msg>interface refactor<commit_after>package sqlt

import (
	"context"
)

type (
	SqlDescriber interface {
		GetSql(c context.Context) (string, context.Context, error)
		Release()
	}

	SqlAssembler interface {
		AssembleSql(id string, data interface{}) (SqlDescriber, error)
	}

	RowScanner interface {
		Columns() ([]string, error)
		Scan(dest ...interface{}) error
		Err() error
	}

	MultiRowsHandler interface {
		HandleRow(r RowScanner)
		AddResultSet()
	}
)
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"syscall"
	"unsafe"
)

/*** defines ***/

const KILO_VERSION = "0.0.1"
const (
	ARROW_LEFT  = 1000 + iota
	ARROW_RIGHT = 1000 + iota
	ARROW_UP    = 1000 + iota
	ARROW_DOWN  = 1000 + iota
	DEL_KEY     = 1000 + iota
	HOME_KEY    = 1000 + iota
	END_KEY     = 1000 + iota
	PAGE_UP     = 1000 + iota
	PAGE_DOWN   = 1000 + iota
)

/*** data ***/

type Termios struct {
	Iflag  uint32
	Oflag  uint32
	Cflag  uint32
	Lflag  uint32
	Cc     [20]byte
	Ispeed uint32
	Ospeed uint32
}

type erow struct {
	chars []byte
}

type editorConfig struct {
	cx          int
	cy          int
	rowoff      int
	screenRows  int
	screenCols  int
	numRows     int
	rows        []erow
	origTermios *Termios
}

type WinSize struct {
	Row    uint16
	Col    uint16
	Xpixel uint16
	Ypixel uint16
}

var E editorConfig

/*** terminal ***/

func die(err error) {
	disableRawMode()
	io.WriteString(os.Stdout, "\x1b[2J")
	io.WriteString(os.Stdout, "\x1b[H")
	log.Fatal(err)
}

func TcSetAttr(fd uintptr, termios *Termios) error {
	// TCSETS+1 == TCSETSW, because TCSAFLUSH doesn't exist
	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TCSETS+1), uintptr(unsafe.Pointer(termios))); err != 0 {
		return err
	}
	return nil
}

func TcGetAttr(fd uintptr) *Termios {
	var termios = &Termios{}
	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, syscall.TCGETS, uintptr(unsafe.Pointer(termios))); err != 0 {
		log.Fatalf("Problem getting terminal attributes: %s\n", err)
	}
	return termios
}

func enableRawMode() {
	E.origTermios = TcGetAttr(os.Stdin.Fd())
	var raw Termios
	raw = *E.origTermios
	raw.Iflag &^= syscall.BRKINT | syscall.ICRNL | syscall.INPCK | syscall.ISTRIP | syscall.IXON
	raw.Oflag &^= syscall.OPOST
	raw.Cflag |= syscall.CS8
	raw.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.IEXTEN | syscall.ISIG
	raw.Cc[syscall.VMIN+1] = 0
	raw.Cc[syscall.VTIME+1] = 1
	if e := TcSetAttr(os.Stdin.Fd(), &raw); e != nil {
		log.Fatalf("Problem enabling raw mode: %s\n", e)
	}
}

func disableRawMode() {
	if e := TcSetAttr(os.Stdin.Fd(), E.origTermios); e != nil {
		log.Fatalf("Problem disabling raw mode: %s\n", e)
	}
}

func editorReadKey() int {
	var buffer [1]byte
	var cc int
	var err error
	for cc, err = os.Stdin.Read(buffer[:]); cc != 1; cc, err = os.Stdin.Read(buffer[:]) {
	}
	if err != nil {
		die(err)
	}
	if buffer[0] == '\x1b' {
		var seq [2]byte
		if cc, _ = os.Stdin.Read(seq[:]); cc != 2 {
			return '\x1b'
		}

		if seq[0] == '[' {
			if seq[1] >= '0' && seq[1] <= '9' {
				if cc, err = os.Stdin.Read(buffer[:]); cc != 1 {
					return '\x1b'
				}
				if buffer[0] == '~' {
					switch seq[1] {
					case '1':
						return HOME_KEY
					case '3':
						return DEL_KEY
					case '4':
						return END_KEY
					case '5':
						return PAGE_UP
					case '6':
						return PAGE_DOWN
					case '7':
						return HOME_KEY
					case '8':
						return END_KEY
					}
				}
				// XXX - what happens here?
			} else {
				switch seq[1] {
				case 'A':
					return ARROW_UP
				case 'B':
					return ARROW_DOWN
				case 'C':
					return ARROW_RIGHT
				case 'D':
					return ARROW_LEFT
				case 'H':
					return HOME_KEY
				case 'F':
					return END_KEY
				}
			}
		} else if seq[0] == '0' {
			switch seq[1] {
			case 'H':
				return HOME_KEY
			case 'F':
				return END_KEY
			}
		}

		return '\x1b'
	}
	return int(buffer[0])
}

func getCursorPosition(rows *int, cols *int) int {
	io.WriteString(os.Stdout, "\x1b[6n")
	var buffer [1]byte
	var buf []byte
	var cc int
	for cc, _ = os.Stdin.Read(buffer[:]); cc == 1; cc, _ = os.Stdin.Read(buffer[:]) {
		if buffer[0] == 'R' {
			break
		}
		buf = append(buf, buffer[0])
	}
	if string(buf[0:2]) != "\x1b[" {
		log.Printf("Failed to read rows;cols from tty\n")
		return -1
	}
	if n, e := fmt.Sscanf(string(buf[2:]), "%d;%d", rows, cols); n != 2 || e != nil {
		if e != nil {
			log.Printf("getCursorPosition: fmt.Sscanf() failed: %s\n", e)
		}
		if n != 2 {
			log.Printf("getCursorPosition: got %d items, wanted 2\n", n)
		}
		return -1
	}
	return 0
}

func getWindowSize(rows *int, cols *int) int {
	var w WinSize
	_, _, err := syscall.Syscall(syscall.SYS_IOCTL,
		os.Stdout.Fd(),
		syscall.TIOCGWINSZ,
		uintptr(unsafe.Pointer(&w)),
	)
	if err != 0 { // type syscall.Errno
		io.WriteString(os.Stdout, "\x1b[999C\x1b[999B")
		return getCursorPosition(rows, cols)
	} else {
		*rows = int(w.Row)
		*cols = int(w.Col)
		return 0
	}
	return -1
}

/*** row operations ***/

func editorAppendRow(s []byte) {
	var r erow
	r.chars = s
	E.rows = append(E.rows, r)
	E.numRows++
}

/*** file I/O ***/

func editorOpen(filename string) {
	fd, err := os.Open(filename)
	if err != nil {
		die(err)
	}
	defer fd.Close()
	fp := bufio.NewReader(fd)

	for line, err := fp.ReadBytes('\n'); err == nil; line, err = fp.ReadBytes('\n') {
		// Trim trailing newlines and carriage returns
		for c := line[len(line) - 1]; len(line) > 0 && (c == '\n' || c == '\r'); {
			line = line[:len(line)-1]
			if len(line) > 0 {
				c = line[len(line) - 1]
			}
		}
		editorAppendRow(line)
	}

	if err != nil && err != io.EOF {
		die(err)
	}
}

/*** input ***/

func editorMoveCursor(key int) {
	switch key {
	case ARROW_LEFT:
		if E.cx != 0 {
			E.cx--
		}
	case ARROW_RIGHT:
		if E.cx != E.screenCols-1 {
			E.cx++
		}
	case ARROW_UP:
		if E.cy != 0 {
			E.cy--
		}
	case ARROW_DOWN:
		if E.cy < E.numRows {
			E.cy++
		}
	}
}

func editorProcessKeypress() {
	c := editorReadKey()
	switch c {
	case ('q' & 0x1f):
		io.WriteString(os.Stdout, "\x1b[2J")
		io.WriteString(os.Stdout, "\x1b[H")
		disableRawMode()
		os.Exit(0)
	case HOME_KEY:
		E.cx = 0
	case END_KEY:
		E.cx = E.screenCols - 1
	case PAGE_UP, PAGE_DOWN:
		dir := ARROW_DOWN
		if c == PAGE_UP {
			dir = ARROW_UP
		}
		for times := E.screenRows; times > 0; times-- {
			editorMoveCursor(dir)
		}
	case ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT:
		editorMoveCursor(c)
	}
}

/*** append buffer ***/

type abuf struct {
	buf []byte
}

func (p abuf) String() string {
	return string(p.buf)
}

func (p *abuf) abAppend(s string) {
	p.buf = append(p.buf, []byte(s)...)
}

func (p *abuf) abAppendBytes(b []byte) {
	p.buf = append(p.buf, b...)
}

/*** output ***/

func editorScroll() {
	if E.cy < E.rowoff {
		E.rowoff = E.cy
	}
	if E.cy >= E.rowoff + E.screenRows {
		E.rowoff = E.cy - E.screenRows + 1
	}
}

func editorRefreshScreen() {
	editorScroll()
	var ab abuf
	ab.abAppend("\x1b[25l")
	ab.abAppend("\x1b[H")
	editorDrawRows(&ab)
	ab.abAppend(fmt.Sprintf("\x1b[%d;%dH", (E.cy - E.rowoff) + 1, E.cx+1))
	ab.abAppend("\x1b[25h")
	_, e := io.WriteString(os.Stdout, ab.String())
	if e != nil {
		log.Fatal(e)
	}
}

func editorDrawRows(ab *abuf) {
	for y := 0; y < E.screenRows; y++ {
		filerow := y + E.rowoff
		if filerow >= E.numRows {
			if E.numRows == 0 && y == E.screenRows/3 {
				w := fmt.Sprintf("Kilo editor -- version %s", KILO_VERSION)
				if len(w) > E.screenCols {
					w = w[0:E.screenCols]
				}
				pad := "~ "
				for padding := (E.screenCols - len(w)) / 2; padding > 0; padding-- {
					ab.abAppend(pad)
					pad = " "
				}
				ab.abAppend(w)
			} else {
				ab.abAppend("~")
			}
		} else {
			length := len(E.rows[filerow].chars)
			if length > E.screenCols { length = E.screenCols }
			ab.abAppendBytes(E.rows[filerow].chars[:length])
		}
		ab.abAppend("\x1b[K")
		if y < E.screenRows-1 {
			ab.abAppend("\r\n")
		}
	}
}

/*** init ***/

func initEditor() {
	// Initialization a la C not necessary.
	if getWindowSize(&E.screenRows, &E.screenCols) == -1 {
		die(fmt.Errorf("couldn't get screen size"))
	}
}

func main() {
	enableRawMode()
	defer disableRawMode()
	initEditor()
	if len(os.Args) > 1 {
		editorOpen(os.Args[1])
	}

	for {
		editorRefreshScreen()
		editorProcessKeypress()
	}
}
<commit_msg>Step 71<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"syscall"
	"unsafe"
)

/*** defines ***/

const KILO_VERSION = "0.0.1"
const (
	ARROW_LEFT  = 1000 + iota
	ARROW_RIGHT = 1000 + iota
	ARROW_UP    = 1000 + iota
	ARROW_DOWN  = 1000 + iota
	DEL_KEY     = 1000 + iota
	HOME_KEY    = 1000 + iota
	END_KEY     = 1000 + iota
	PAGE_UP     = 1000 + iota
	PAGE_DOWN   = 1000 + iota
)

/*** data ***/

type Termios struct {
	Iflag  uint32
	Oflag  uint32
	Cflag  uint32
	Lflag  uint32
	Cc     [20]byte
	Ispeed uint32
	Ospeed uint32
}

type erow struct {
	chars []byte
}

type editorConfig struct {
	cx          int
	cy          int
	rowoff      int
	coloff      int
	screenRows  int
	screenCols  int
	numRows     int
	rows        []erow
	origTermios *Termios
}

type WinSize struct {
	Row    uint16
	Col    uint16
	Xpixel uint16
	Ypixel uint16
}

var E editorConfig

/*** terminal ***/

func die(err error) {
	disableRawMode()
	io.WriteString(os.Stdout, "\x1b[2J")
	io.WriteString(os.Stdout, "\x1b[H")
	log.Fatal(err)
}

func TcSetAttr(fd uintptr, termios *Termios) error {
	// TCSETS+1 == TCSETSW, because TCSAFLUSH doesn't exist
	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TCSETS+1), uintptr(unsafe.Pointer(termios))); err != 0 {
		return err
	}
	return nil
}

func TcGetAttr(fd uintptr) *Termios {
	var termios = &Termios{}
	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, syscall.TCGETS, uintptr(unsafe.Pointer(termios))); err != 0 {
		log.Fatalf("Problem getting terminal attributes: %s\n", err)
	}
	return termios
}

func enableRawMode() {
	E.origTermios = TcGetAttr(os.Stdin.Fd())
	var raw Termios
	raw = *E.origTermios
	raw.Iflag &^= syscall.BRKINT | syscall.ICRNL | syscall.INPCK | syscall.ISTRIP | syscall.IXON
	raw.Oflag &^= syscall.OPOST
	raw.Cflag |= syscall.CS8
	raw.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.IEXTEN | syscall.ISIG
	raw.Cc[syscall.VMIN+1] = 0
	raw.Cc[syscall.VTIME+1] = 1
	if e := TcSetAttr(os.Stdin.Fd(), &raw); e != nil {
		log.Fatalf("Problem enabling raw mode: %s\n", e)
	}
}

func disableRawMode() {
	if e := TcSetAttr(os.Stdin.Fd(), E.origTermios); e != nil {
		log.Fatalf("Problem disabling raw mode: %s\n", e)
	}
}

func editorReadKey() int {
	var buffer [1]byte
	var cc int
	var err error
	for cc, err = os.Stdin.Read(buffer[:]); cc != 1; cc, err = os.Stdin.Read(buffer[:]) {
	}
	if err != nil {
		die(err)
	}
	if buffer[0] == '\x1b' {
		var seq [2]byte
		if cc, _ = os.Stdin.Read(seq[:]); cc != 2 {
			return '\x1b'
		}

		if seq[0] == '[' {
			if seq[1] >= '0' && seq[1] <= '9' {
				if cc, err = os.Stdin.Read(buffer[:]); cc != 1 {
					return '\x1b'
				}
				if buffer[0] == '~' {
					switch seq[1] {
					case '1':
						return HOME_KEY
					case '3':
						return DEL_KEY
					case '4':
						return END_KEY
					case '5':
						return PAGE_UP
					case '6':
						return PAGE_DOWN
					case '7':
						return HOME_KEY
					case '8':
						return END_KEY
					}
				}
				// XXX - what happens here?
			} else {
				switch seq[1] {
				case 'A':
					return ARROW_UP
				case 'B':
					return ARROW_DOWN
				case 'C':
					return ARROW_RIGHT
				case 'D':
					return ARROW_LEFT
				case 'H':
					return HOME_KEY
				case 'F':
					return END_KEY
				}
			}
		} else if seq[0] == '0' {
			switch seq[1] {
			case 'H':
				return HOME_KEY
			case 'F':
				return END_KEY
			}
		}

		return '\x1b'
	}
	return int(buffer[0])
}

func getCursorPosition(rows *int, cols *int) int {
	io.WriteString(os.Stdout, "\x1b[6n")
	var buffer [1]byte
	var buf []byte
	var cc int
	for cc, _ = os.Stdin.Read(buffer[:]); cc == 1; cc, _ = os.Stdin.Read(buffer[:]) {
		if buffer[0] == 'R' {
			break
		}
		buf = append(buf, buffer[0])
	}
	if string(buf[0:2]) != "\x1b[" {
		log.Printf("Failed to read rows;cols from tty\n")
		return -1
	}
	if n, e := fmt.Sscanf(string(buf[2:]), "%d;%d", rows, cols); n != 2 || e != nil {
		if e != nil {
			log.Printf("getCursorPosition: fmt.Sscanf() failed: %s\n", e)
		}
		if n != 2 {
			log.Printf("getCursorPosition: got %d items, wanted 2\n", n)
		}
		return -1
	}
	return 0
}

func getWindowSize(rows *int, cols *int) int {
	var w WinSize
	_, _, err := syscall.Syscall(syscall.SYS_IOCTL,
		os.Stdout.Fd(),
		syscall.TIOCGWINSZ,
		uintptr(unsafe.Pointer(&w)),
	)
	if err != 0 { // type syscall.Errno
		io.WriteString(os.Stdout, "\x1b[999C\x1b[999B")
		return getCursorPosition(rows, cols)
	} else {
		*rows = int(w.Row)
		*cols = int(w.Col)
		return 0
	}
	return -1
}

/*** row operations ***/

func editorAppendRow(s []byte) {
	var r erow
	r.chars = s
	E.rows = append(E.rows, r)
	E.numRows++
}

/*** file I/O ***/

func editorOpen(filename string) {
	fd, err := os.Open(filename)
	if err != nil {
		die(err)
	}
	defer fd.Close()
	fp := bufio.NewReader(fd)

	for line, err := fp.ReadBytes('\n'); err == nil; line, err = fp.ReadBytes('\n') {
		// Trim trailing newlines and carriage returns
		for c := line[len(line) - 1]; len(line) > 0 && (c == '\n' || c == '\r'); {
			line = line[:len(line)-1]
			if len(line) > 0 {
				c = line[len(line) - 1]
			}
		}
		editorAppendRow(line)
	}

	if err != nil && err != io.EOF {
		die(err)
	}
}

/*** input ***/

func editorMoveCursor(key int) {
	switch key {
	case ARROW_LEFT:
		if E.cx != 0 {
			E.cx--
		}
	case ARROW_RIGHT:
		if E.cx != E.screenCols-1 {
			E.cx++
		}
	case ARROW_UP:
		if E.cy != 0 {
			E.cy--
		}
	case ARROW_DOWN:
		if E.cy < E.numRows {
			E.cy++
		}
	}
}

func editorProcessKeypress() {
	c := editorReadKey()
	switch c {
	case ('q' & 0x1f):
		io.WriteString(os.Stdout, "\x1b[2J")
		io.WriteString(os.Stdout, "\x1b[H")
		disableRawMode()
		os.Exit(0)
	case HOME_KEY:
		E.cx = 0
	case END_KEY:
		E.cx = E.screenCols - 1
	case PAGE_UP, PAGE_DOWN:
		dir := ARROW_DOWN
		if c == PAGE_UP {
			dir = ARROW_UP
		}
		for times := E.screenRows; times > 0; times-- {
			editorMoveCursor(dir)
		}
	case ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT:
		editorMoveCursor(c)
	}
}

/*** append buffer ***/

type abuf struct {
	buf []byte
}

func (p abuf) String() string {
	return string(p.buf)
}

func (p *abuf) abAppend(s string) {
	p.buf = append(p.buf, []byte(s)...)
}

func (p *abuf) abAppendBytes(b []byte) {
	p.buf = append(p.buf, b...)
}

/*** output ***/

func editorScroll() {
	if E.cy < E.rowoff {
		E.rowoff = E.cy
	}
	if E.cy >= E.rowoff + E.screenRows {
		E.rowoff = E.cy - E.screenRows + 1
	}
}

func editorRefreshScreen() {
	editorScroll()
	var ab abuf
	ab.abAppend("\x1b[25l")
	ab.abAppend("\x1b[H")
	editorDrawRows(&ab)
	ab.abAppend(fmt.Sprintf("\x1b[%d;%dH", (E.cy - E.rowoff) + 1, E.cx+1))
	ab.abAppend("\x1b[25h")
	_, e := io.WriteString(os.Stdout, ab.String())
	if e != nil {
		log.Fatal(e)
	}
}

func editorDrawRows(ab *abuf) {
	for y := 0; y < E.screenRows; y++ {
		filerow := y + E.rowoff
		if filerow >= E.numRows {
			if E.numRows == 0 && y == E.screenRows/3 {
				w := fmt.Sprintf("Kilo editor -- version %s", KILO_VERSION)
				if len(w) > E.screenCols {
					w = w[0:E.screenCols]
				}
				pad := "~ "
				for padding := (E.screenCols - len(w)) / 2; padding > 0; padding-- {
					ab.abAppend(pad)
					pad = " "
				}
				ab.abAppend(w)
			} else {
				ab.abAppend("~")
			}
		} else {
			length := len(E.rows[filerow].chars)
			if length > E.screenCols { length = E.screenCols }
			ab.abAppendBytes(E.rows[filerow].chars[:length])
		}
		ab.abAppend("\x1b[K")
		if y < E.screenRows-1 {
			ab.abAppend("\r\n")
		}
	}
}

/*** init ***/

func initEditor() {
	// Initialization a la C not necessary.
	if getWindowSize(&E.screenRows, &E.screenCols) == -1 {
		die(fmt.Errorf("couldn't get screen size"))
	}
}

func main() {
	enableRawMode()
	defer disableRawMode()
	initEditor()
	if len(os.Args) > 1 {
		editorOpen(os.Args[1])
	}

	for {
		editorRefreshScreen()
		editorProcessKeypress()
	}
}
<|endoftext|>