<commit_before>// Package edit implements a full-righteature line editor.
package edit

import (
	"fmt"
	"os"
	"strings"
	"syscall"
	"time"

	"github.com/xiaq/elvish/edit/tty"
	"github.com/xiaq/elvish/eval"
	"github.com/xiaq/elvish/parse"
)

const (
	CPRWaitTimeout = 10 * time.Millisecond
)

var LackEOL = "\033[7m\u23ce\033[m\n"

type bufferMode int

const (
	modeInsert bufferMode = iota
	modeCommand
	modeCompletion
	modeNavigation
	modeHistory
)

type editorState struct {
	// States used during ReadLine.
	tokens                []parse.Item
	prompt, rprompt, line string
	dot                   int
	tips                  []string
	mode                  bufferMode
	completion            *completion
	completionLines       int
	navigation            *navigation
	history               *historyState
}

type historyState struct {
	items         []string
	current       int
	saved, prefix string
}

// Editor keeps the status of the line editor.
type Editor struct {
	savedTermios *tty.Termios
	file         *os.File
	writer       *writer
	reader       *Reader
	ev           *eval.Evaluator
	sigs         <-chan os.Signal
	editorState
}

// LineRead is the result of ReadLine. Exactly one member is non-zero, making
// it effectively a tagged union.
type LineRead struct {
	Line string
	EOF  bool
	Err  error
}

func (hs *historyState) append(line string) {
	hs.items = append(hs.items, line)
}

func (hs *historyState) prev() bool {
	for i := hs.current - 1; i >= 0; i-- {
		if strings.HasPrefix(hs.items[i], hs.prefix) {
			hs.current = i
			return true
		}
	}
	return false
}

func (hs *historyState) next() bool {
	for i := hs.current + 1; i < len(hs.items); i++ {
		if strings.HasPrefix(hs.items[i], hs.prefix) {
			hs.current = i
			return true
		}
	}
	return false
}

// New creates an Editor.
func New(file *os.File, ev *eval.Evaluator, sigs <-chan os.Signal) *Editor {
	return &Editor{
		file:   file,
		writer: newWriter(file),
		reader: NewReader(file),
		ev:     ev,
		sigs:   sigs,
		editorState: editorState{
			history: &historyState{},
		},
	}
}

func (ed *Editor) beep() {
}

func (ed *Editor) pushTip(more string) {
	ed.tips = append(ed.tips, more)
}

func (ed *Editor) refresh() error {
	// Re-lex the line, unless we are in modeCompletion
	if ed.mode != modeCompletion {
		ed.tokens = nil
		hl := Highlight("<interactive code>", ed.line, ed.ev)
		for token := range hl {
			ed.tokens = append(ed.tokens, token)
		}
	}
	return ed.writer.refresh(&ed.editorState)
}

// TODO Allow modifiable keybindings.
var keyBindings = map[bufferMode]map[Key]string{
	modeCommand: map[Key]string{
		Key{'i', 0}:    "start-insert",
		Key{'h', 0}:    "move-dot-left",
		Key{'l', 0}:    "move-dot-right",
		Key{'D', 0}:    "kill-line-right",
		DefaultBinding: "default-command",
	},
	modeInsert: map[Key]string{
		Key{'[', Ctrl}:    "start-command",
		Key{'U', Ctrl}:    "kill-line-left",
		Key{'K', Ctrl}:    "kill-line-right",
		Key{Backspace, 0}: "kill-rune-left",
		Key{Delete, 0}:    "kill-rune-right",
		Key{Left, 0}:      "move-dot-left",
		Key{Right, 0}:     "move-dot-right",
		Key{Up, 0}:        "move-dot-up",
		Key{Down, 0}:      "move-dot-down",
		Key{Enter, Alt}:   "insert-key",
		Key{Enter, 0}:     "return-line",
		Key{'D', Ctrl}:    "return-eof",
		Key{Tab, 0}:       "start-completion",
		Key{PageUp, 0}:    "start-history",
		Key{'N', Ctrl}:    "start-navigation",
		DefaultBinding:    "default-insert",
	},
	modeCompletion: map[Key]string{
		Key{'[', Ctrl}: "cancel-completion",
		Key{Up, 0}:     "select-cand-up",
		Key{Down, 0}:   "select-cand-down",
		Key{Left, 0}:   "select-cand-left",
		Key{Right, 0}:  "select-cand-right",
		Key{Tab, 0}:    "cycle-cand-right",
		DefaultBinding: "default-completion",
	},
	modeNavigation: map[Key]string{
		Key{Up, 0}:     "select-nav-up",
		Key{Down, 0}:   "select-nav-down",
		Key{Left, 0}:   "ascend-nav",
		Key{Right, 0}:  "descend-nav",
		DefaultBinding: "default-navigation",
	},
	modeHistory: map[Key]string{
		Key{'[', Ctrl}:   "cancel-history",
		Key{PageUp, 0}:   "select-history-prev",
		Key{PageDown, 0}: "select-history-next",
		DefaultBinding:   "default-history",
	},
}

func init() {
	for _, kb := range keyBindings {
		for _, name := range kb {
			if leBuiltins[name] == nil {
				panic("bad keyBindings table: no editor builtin named " + name)
			}
		}
	}
}

// acceptCompletion accepts currently selected completion candidate.
func (ed *Editor) acceptCompletion() {
	c := ed.completion
	if 0 <= c.current && c.current < len(c.candidates) {
		accepted := c.candidates[c.current].text
		ed.line = ed.line[:c.start] + accepted + ed.line[c.end:]
		ed.dot += len(accepted) - (c.end - c.start)
	}
	ed.completion = nil
	ed.mode = modeInsert
}

// acceptHistory accepts currently history.
func (ed *Editor) acceptHistory() {
	ed.line = ed.history.items[ed.history.current]
	ed.dot = len(ed.line)
}

func SetupTerminal(file *os.File) (*tty.Termios, error) {
	fd := int(file.Fd())
	term, err := tty.NewTermiosFromFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't get terminal attribute: %s", err)
	}

	savedTermios := term.Copy()

	term.SetIcanon(false)
	term.SetEcho(false)
	term.SetMin(1)
	term.SetTime(0)

	err = term.ApplyToFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't set up terminal attribute: %s", err)
	}

	// Set autowrap off
	file.WriteString("\033[?7l")

	err = tty.FlushInput(fd)
	if err != nil {
		return nil, fmt.Errorf("can't flush input: %s", err)
	}

	return savedTermios, nil
}

func CleanupTerminal(file *os.File, savedTermios *tty.Termios) error {
	// Set autowrap on
	file.WriteString("\033[?7h")
	fd := int(file.Fd())
	return savedTermios.ApplyToFd(fd)
}

// startsReadLine prepares the terminal for the editor.
func (ed *Editor) startReadLine() error {
	savedTermios, err := SetupTerminal(ed.file)
	if err != nil {
		return err
	}
	ed.savedTermios = savedTermios

	// Query cursor location
	ed.file.WriteString("\033[6n")

	ed.reader.Continue()
	ones := ed.reader.Chan()

	cpr := InvalidPos
FindCPR:
	for {
		select {
		case or := <-ones:
			if or.CPR != InvalidPos {
				cpr = or.CPR
				break FindCPR
			} else {
				// Just discard
			}
		case <-time.After(CPRTimeout):
			break FindCPR
		}
	}

	if cpr == InvalidPos {
		// Unable to get CPR, just rewind to column 1
		ed.file.WriteString("\r")
	} else if cpr.col != 1 {
		// BUG(xiaq) startReadline assumes that column number starts from 0
		ed.file.WriteString(LackEOL)
	}

	return nil
}

// finishReadLine puts the terminal in a state suitable for other programs to
// use.
func (ed *Editor) finishReadLine(lr *LineRead) {
	if lr.EOF == false && lr.Err == nil {
		ed.history.append(lr.Line)
	}

	ed.reader.Stop()

	ed.mode = modeInsert
	ed.tips = nil
	ed.completion = nil
	ed.navigation = nil
	ed.dot = len(ed.line)
	// TODO Perhaps make it optional to NOT clear the rprompt
	ed.rprompt = ""
	ed.refresh() // XXX(xiaq): Ignore possible error
	ed.file.WriteString("\n")

	err := CleanupTerminal(ed.file, ed.savedTermios)

	if err != nil {
		// BUG(xiaq): Error in Editor.finishReadLine may override earlier error
		*lr = LineRead{Err: fmt.Errorf("can't restore terminal attribute: %s", err)}
	}
	ed.savedTermios = nil
}

// ReadLine reads a line interactively.
// TODO(xiaq): ReadLine currently just ignores all signals.
func (ed *Editor) ReadLine(prompt, rprompt func() string) (lr LineRead) {
	err := ed.startReadLine()
	if err != nil {
		return LineRead{Err: err}
	}
	defer ed.finishReadLine(&lr)

Begin:
	ed.line = ""
	ed.mode = modeInsert
	ed.tips = nil
	ed.completion = nil
	ed.navigation = nil
	ed.dot = 0
	ed.writer.oldBuf.cells = nil

	ones := ed.reader.Chan()

MainLoop:
	for {
		ed.prompt = prompt()
		ed.rprompt = rprompt()
		err := ed.refresh()
		if err != nil {
			return LineRead{Err: err}
		}

		ed.tips = nil

		select {
		case sig := <-ed.sigs:
			// TODO(xiaq): Maybe support customizable handling of signals
			switch sig {
			case syscall.SIGINT:
				// Start over
				goto Begin
			case syscall.SIGWINCH:
				continue MainLoop
			}
		case or := <-ones:
			// Alert about error
			err := or.Err
			if err != nil {
				ed.pushTip(err.Error())
				continue
			}

			// Ignore bogus CPR
			if or.CPR != InvalidPos {
				panic("got cpr")
				continue
			}

			k := or.Key
		lookupKey:
			keyBinding, ok := keyBindings[ed.mode]
			if !ok {
				ed.pushTip("No binding for current mode")
				continue
			}

			name, bound := keyBinding[k]
			if !bound {
				name = keyBinding[DefaultBinding]
			}
			ret := leBuiltins[name](ed, k)
			if ret == nil {
				continue
			}
			switch ret.action {
			case noAction:
				continue
			case changeMode:
				ed.mode = ret.newMode
				continue
			case changeModeAndReprocess:
				ed.mode = ret.newMode
				goto lookupKey
			case exitReadLine:
				return ret.readLineReturn
			}
		}
	}
}
<commit_msg>edit: editorState.history is now not a pointer<commit_after>// Package edit implements a full-righteature line editor.
package edit

import (
	"fmt"
	"os"
	"strings"
	"syscall"
	"time"

	"github.com/xiaq/elvish/edit/tty"
	"github.com/xiaq/elvish/eval"
	"github.com/xiaq/elvish/parse"
)

const (
	CPRWaitTimeout = 10 * time.Millisecond
)

var LackEOL = "\033[7m\u23ce\033[m\n"

type bufferMode int

const (
	modeInsert bufferMode = iota
	modeCommand
	modeCompletion
	modeNavigation
	modeHistory
)

type editorState struct {
	// States used during ReadLine.
	tokens                []parse.Item
	prompt, rprompt, line string
	dot                   int
	tips                  []string
	mode                  bufferMode
	completion            *completion
	completionLines       int
	navigation            *navigation
	history               historyState
}

type historyState struct {
	items         []string
	current       int
	saved, prefix string
}

// Editor keeps the status of the line editor.
type Editor struct {
	savedTermios *tty.Termios
	file         *os.File
	writer       *writer
	reader       *Reader
	ev           *eval.Evaluator
	sigs         <-chan os.Signal
	editorState
}

// LineRead is the result of ReadLine. Exactly one member is non-zero, making
// it effectively a tagged union.
type LineRead struct {
	Line string
	EOF  bool
	Err  error
}

func (hs *historyState) append(line string) {
	hs.items = append(hs.items, line)
}

func (hs *historyState) prev() bool {
	for i := hs.current - 1; i >= 0; i-- {
		if strings.HasPrefix(hs.items[i], hs.prefix) {
			hs.current = i
			return true
		}
	}
	return false
}

func (hs *historyState) next() bool {
	for i := hs.current + 1; i < len(hs.items); i++ {
		if strings.HasPrefix(hs.items[i], hs.prefix) {
			hs.current = i
			return true
		}
	}
	return false
}

// New creates an Editor.
func New(file *os.File, ev *eval.Evaluator, sigs <-chan os.Signal) *Editor {
	return &Editor{
		file:   file,
		writer: newWriter(file),
		reader: NewReader(file),
		ev:     ev,
		sigs:   sigs,
	}
}

func (ed *Editor) beep() {
}

func (ed *Editor) pushTip(more string) {
	ed.tips = append(ed.tips, more)
}

func (ed *Editor) refresh() error {
	// Re-lex the line, unless we are in modeCompletion
	if ed.mode != modeCompletion {
		ed.tokens = nil
		hl := Highlight("<interactive code>", ed.line, ed.ev)
		for token := range hl {
			ed.tokens = append(ed.tokens, token)
		}
	}
	return ed.writer.refresh(&ed.editorState)
}

// TODO Allow modifiable keybindings.
var keyBindings = map[bufferMode]map[Key]string{
	modeCommand: map[Key]string{
		Key{'i', 0}:    "start-insert",
		Key{'h', 0}:    "move-dot-left",
		Key{'l', 0}:    "move-dot-right",
		Key{'D', 0}:    "kill-line-right",
		DefaultBinding: "default-command",
	},
	modeInsert: map[Key]string{
		Key{'[', Ctrl}:    "start-command",
		Key{'U', Ctrl}:    "kill-line-left",
		Key{'K', Ctrl}:    "kill-line-right",
		Key{Backspace, 0}: "kill-rune-left",
		Key{Delete, 0}:    "kill-rune-right",
		Key{Left, 0}:      "move-dot-left",
		Key{Right, 0}:     "move-dot-right",
		Key{Up, 0}:        "move-dot-up",
		Key{Down, 0}:      "move-dot-down",
		Key{Enter, Alt}:   "insert-key",
		Key{Enter, 0}:     "return-line",
		Key{'D', Ctrl}:    "return-eof",
		Key{Tab, 0}:       "start-completion",
		Key{PageUp, 0}:    "start-history",
		Key{'N', Ctrl}:    "start-navigation",
		DefaultBinding:    "default-insert",
	},
	modeCompletion: map[Key]string{
		Key{'[', Ctrl}: "cancel-completion",
		Key{Up, 0}:     "select-cand-up",
		Key{Down, 0}:   "select-cand-down",
		Key{Left, 0}:   "select-cand-left",
		Key{Right, 0}:  "select-cand-right",
		Key{Tab, 0}:    "cycle-cand-right",
		DefaultBinding: "default-completion",
	},
	modeNavigation: map[Key]string{
		Key{Up, 0}:     "select-nav-up",
		Key{Down, 0}:   "select-nav-down",
		Key{Left, 0}:   "ascend-nav",
		Key{Right, 0}:  "descend-nav",
		DefaultBinding: "default-navigation",
	},
	modeHistory: map[Key]string{
		Key{'[', Ctrl}:   "cancel-history",
		Key{PageUp, 0}:   "select-history-prev",
		Key{PageDown, 0}: "select-history-next",
		DefaultBinding:   "default-history",
	},
}

func init() {
	for _, kb := range keyBindings {
		for _, name := range kb {
			if leBuiltins[name] == nil {
				panic("bad keyBindings table: no editor builtin named " + name)
			}
		}
	}
}

// acceptCompletion accepts currently selected completion candidate.
func (ed *Editor) acceptCompletion() {
	c := ed.completion
	if 0 <= c.current && c.current < len(c.candidates) {
		accepted := c.candidates[c.current].text
		ed.line = ed.line[:c.start] + accepted + ed.line[c.end:]
		ed.dot += len(accepted) - (c.end - c.start)
	}
	ed.completion = nil
	ed.mode = modeInsert
}

// acceptHistory accepts currently history.
func (ed *Editor) acceptHistory() {
	ed.line = ed.history.items[ed.history.current]
	ed.dot = len(ed.line)
}

func SetupTerminal(file *os.File) (*tty.Termios, error) {
	fd := int(file.Fd())
	term, err := tty.NewTermiosFromFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't get terminal attribute: %s", err)
	}

	savedTermios := term.Copy()

	term.SetIcanon(false)
	term.SetEcho(false)
	term.SetMin(1)
	term.SetTime(0)

	err = term.ApplyToFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't set up terminal attribute: %s", err)
	}

	// Set autowrap off
	file.WriteString("\033[?7l")

	err = tty.FlushInput(fd)
	if err != nil {
		return nil, fmt.Errorf("can't flush input: %s", err)
	}

	return savedTermios, nil
}

func CleanupTerminal(file *os.File, savedTermios *tty.Termios) error {
	// Set autowrap on
	file.WriteString("\033[?7h")
	fd := int(file.Fd())
	return savedTermios.ApplyToFd(fd)
}

// startsReadLine prepares the terminal for the editor.
func (ed *Editor) startReadLine() error {
	savedTermios, err := SetupTerminal(ed.file)
	if err != nil {
		return err
	}
	ed.savedTermios = savedTermios

	// Query cursor location
	ed.file.WriteString("\033[6n")

	ed.reader.Continue()
	ones := ed.reader.Chan()

	cpr := InvalidPos
FindCPR:
	for {
		select {
		case or := <-ones:
			if or.CPR != InvalidPos {
				cpr = or.CPR
				break FindCPR
			} else {
				// Just discard
			}
		case <-time.After(CPRTimeout):
			break FindCPR
		}
	}

	if cpr == InvalidPos {
		// Unable to get CPR, just rewind to column 1
		ed.file.WriteString("\r")
	} else if cpr.col != 1 {
		// BUG(xiaq) startReadline assumes that column number starts from 0
		ed.file.WriteString(LackEOL)
	}

	return nil
}

// finishReadLine puts the terminal in a state suitable for other programs to
// use.
func (ed *Editor) finishReadLine(lr *LineRead) {
	if lr.EOF == false && lr.Err == nil {
		ed.history.append(lr.Line)
	}

	ed.reader.Stop()

	ed.mode = modeInsert
	ed.tips = nil
	ed.completion = nil
	ed.navigation = nil
	ed.dot = len(ed.line)
	// TODO Perhaps make it optional to NOT clear the rprompt
	ed.rprompt = ""
	ed.refresh() // XXX(xiaq): Ignore possible error
	ed.file.WriteString("\n")

	err := CleanupTerminal(ed.file, ed.savedTermios)

	if err != nil {
		// BUG(xiaq): Error in Editor.finishReadLine may override earlier error
		*lr = LineRead{Err: fmt.Errorf("can't restore terminal attribute: %s", err)}
	}
	ed.savedTermios = nil
}

// ReadLine reads a line interactively.
// TODO(xiaq): ReadLine currently just ignores all signals.
func (ed *Editor) ReadLine(prompt, rprompt func() string) (lr LineRead) {
	err := ed.startReadLine()
	if err != nil {
		return LineRead{Err: err}
	}
	defer ed.finishReadLine(&lr)

Begin:
	ed.line = ""
	ed.mode = modeInsert
	ed.tips = nil
	ed.completion = nil
	ed.navigation = nil
	ed.dot = 0
	ed.writer.oldBuf.cells = nil

	ones := ed.reader.Chan()

MainLoop:
	for {
		ed.prompt = prompt()
		ed.rprompt = rprompt()
		err := ed.refresh()
		if err != nil {
			return LineRead{Err: err}
		}

		ed.tips = nil

		select {
		case sig := <-ed.sigs:
			// TODO(xiaq): Maybe support customizable handling of signals
			switch sig {
			case syscall.SIGINT:
				// Start over
				goto Begin
			case syscall.SIGWINCH:
				continue MainLoop
			}
		case or := <-ones:
			// Alert about error
			err := or.Err
			if err != nil {
				ed.pushTip(err.Error())
				continue
			}

			// Ignore bogus CPR
			if or.CPR != InvalidPos {
				panic("got cpr")
				continue
			}

			k := or.Key
		lookupKey:
			keyBinding, ok := keyBindings[ed.mode]
			if !ok {
				ed.pushTip("No binding for current mode")
				continue
			}

			name, bound := keyBinding[k]
			if !bound {
				name = keyBinding[DefaultBinding]
			}
			ret := leBuiltins[name](ed, k)
			if ret == nil {
				continue
			}
			switch ret.action {
			case noAction:
				continue
			case changeMode:
				ed.mode = ret.newMode
				continue
			case changeModeAndReprocess:
				ed.mode = ret.newMode
				goto lookupKey
			case exitReadLine:
				return ret.readLineReturn
			}
		}
	}
}
<|endoftext|>


<commit_before>package crawler

import (
	"math/rand"
	"net/http"
	"golang.org/x/net/html"
	"fmt"
	"net/url"
	"time"
)

type Crawler struct {
	baseUrl *url.URL
	initalDelay int
}

func NewCrawler(baseUrl string) *Crawler {
	crawler := new(Crawler)
	crawler.baseUrl, _ = url.Parse(baseUrl)
	crawler.initalDelay = rand.Intn(10)

	return crawler
}

func (self *Crawler) Crawl() {
	baseUrl := self.baseUrl

	resp, err := http.Get(baseUrl.String())

	if err != nil {
		return
	}

	defer resp.Body.Close()

	tokenizer := html.NewTokenizer(resp.Body)

	toVisit := make([]url.URL, 0)

	for {
		next := tokenizer.Next()

		if next == html.ErrorToken {
			break
		}

		token := tokenizer.Token()

		if token.Data == "a" {
			for _, attr := range token.Attr {
				if attr.Key == "href" {
					u, err := url.Parse(attr.Val)
					if err == nil {
						if !u.IsAbs() {
							if u.String() == "/" {
								continue
							}
							u.Scheme = baseUrl.Scheme
							u.Host = baseUrl.Host
							toVisit = append(toVisit, *u)
						}
					}
					break
				}
			}
		}
	}

	numConplete := 0

	for _, urlToVisit := range toVisit {
		go func(urlToVisit url.URL) {
			time.Sleep(time.Duration(rand.Intn(len(toVisit) * 2)) * time.Second)
			fmt.Println("Visitng: ", urlToVisit.String())
			_, _ = http.Get(urlToVisit.String())
			numConplete++
		}(urlToVisit)
	}

	for numConplete < len(toVisit) {
		time.Sleep(1 * time.Second)
	}
}<commit_msg>Crawler: Add delay before visiting base url<commit_after>package crawler

import (
	"math/rand"
	"net/http"
	"golang.org/x/net/html"
	"fmt"
	"net/url"
	"time"
)

type Crawler struct {
	baseUrl *url.URL
	initalDelay time.Duration
}

func NewCrawler(baseUrl string) *Crawler {
	crawler := new(Crawler)
	crawler.baseUrl, _ = url.Parse(baseUrl)
	crawler.initalDelay = time.Duration(rand.Intn(10)) * time.Second

	return crawler
}

func (self *Crawler) Crawl() {
	fmt.Printf("***** Crawling: %v in %v *****\n", self.baseUrl.String(), self.initalDelay)
	time.Sleep(self.initalDelay)

	resp, err := http.Get(self.baseUrl.String())

	if err != nil {
		return
	}

	defer resp.Body.Close()

	tokenizer := html.NewTokenizer(resp.Body)

	toVisit := make([]url.URL, 0)

	for {
		next := tokenizer.Next()

		if next == html.ErrorToken {
			break
		}

		token := tokenizer.Token()

		if token.Data == "a" {
			for _, attr := range token.Attr {
				if attr.Key == "href" {
					u, err := url.Parse(attr.Val)
					if err == nil {
						if !u.IsAbs() {
							if u.String() == "/" {
								continue
							}
							u.Scheme = self.baseUrl.Scheme
							u.Host = self.baseUrl.Host
							toVisit = append(toVisit, *u)
						}
					}
					break
				}
			}
		}
	}

	numConplete := 0

	for _, urlToVisit := range toVisit {
		go func(urlToVisit url.URL) {
			time.Sleep(time.Duration(rand.Intn(len(toVisit) * 2)) * time.Second)
			fmt.Println("Visiting: ", urlToVisit.String())
			_, _ = http.Get(urlToVisit.String())
			numConplete++
		}(urlToVisit)
	}

	for numConplete < len(toVisit) {
		time.Sleep(1 * time.Second)
	}
}<|endoftext|>


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

// Script takes the arg list, does minimal rewriting, builds it and runs it
package main

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

	"golang.org/x/tools/imports"
)

func main() {
     opts := imports.Options{
     Fragment: true,
     AllErrors: true,
     Comments: true,
     TabIndent: true,
     TabWidth: 8,
     }
	flag.Parse()
	a := ""
	for _, v := range flag.Args() {
	    a = a + v
	    }
	   log.Printf("'%v'", a)
	goCode, err := imports.Process("commandline", []byte(a), &opts)
	if err != nil {
	   log.Fatalf("bad parse: '%v': %v", a, err)
	   }
	   log.Printf("%v", a)

	f, err := TempFile("", "script%s.go")
	if err != nil {
		log.Fatalf("Script: opening TempFile: %v", err)
	}

	if _, err := f.Write([]byte(goCode)); err != nil {
		log.Fatalf("Script: Writing %v: %v", f, err)
	}
	if err := f.Close(); err != nil {
		log.Fatalf("Script: Closing %v: %v", f, err)
	}

	os.Setenv("GOBIN", "/tmp")
	cmd := exec.Command("go", "install", "-x", f.Name())
	//installenvs = append(envs, "GOBIN=/tmp")
	cmd.Dir = "/"

	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	log.Printf("Install %v", f.Name())
	if err = cmd.Run(); err != nil {
		log.Printf("%v\n", err)
	}

	// stupid, but hey ...
	execName := f.Name()
	execName = execName[:len(execName)-3]
	cmd = exec.Command(execName)
	cmd.Dir = "/tmp"

	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	log.Printf("Run %v", f.Name())
	if err := cmd.Run(); err != nil {
		log.Printf("%v\n", err)
	}

}
<commit_msg>A reasonable compromise for now<commit_after>// Copyright 2012 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Script takes the arg list, does minimal rewriting, builds it and runs it
package main

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

	"golang.org/x/tools/imports"
)

func main() {
     opts := imports.Options{
     Fragment: true,
     AllErrors: true,
     Comments: true,
     TabIndent: true,
     TabWidth: 8,
     }
	flag.Parse()
	a := "func main()"
	for _, v := range flag.Args() {
	    a = a + v
	    }
	   log.Printf("'%v'", a)
	goCode, err := imports.Process("commandline", []byte(a), &opts)
	if err != nil {
	   log.Fatalf("bad parse: '%v': %v", a, err)
	   }
	   log.Printf("%v", a)

	f, err := TempFile("", "script%s.go")
	if err != nil {
		log.Fatalf("Script: opening TempFile: %v", err)
	}

	if _, err := f.Write([]byte(goCode)); err != nil {
		log.Fatalf("Script: Writing %v: %v", f, err)
	}
	if err := f.Close(); err != nil {
		log.Fatalf("Script: Closing %v: %v", f, err)
	}

	os.Setenv("GOBIN", "/tmp")
	cmd := exec.Command("go", "install", "-x", f.Name())
	//installenvs = append(envs, "GOBIN=/tmp")
	cmd.Dir = "/"

	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	log.Printf("Install %v", f.Name())
	if err = cmd.Run(); err != nil {
		log.Printf("%v\n", err)
	}

	// stupid, but hey ...
	execName := f.Name()
	execName = execName[:len(execName)-3]
	cmd = exec.Command(execName)
	cmd.Dir = "/tmp"

	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	log.Printf("Run %v", f.Name())
	if err := cmd.Run(); err != nil {
		log.Printf("%v\n", err)
	}

}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package validation

import (
	"fmt"
	"math"
	"net"
	"regexp"
	"strings"
)

const qnameCharFmt string = "[A-Za-z0-9]"
const qnameExtCharFmt string = "[-A-Za-z0-9_.]"
const qualifiedNameFmt string = "(" + qnameCharFmt + qnameExtCharFmt + "*)?" + qnameCharFmt
const qualifiedNameMaxLength int = 63

var qualifiedNameRegexp = regexp.MustCompile("^" + qualifiedNameFmt + "$")

// IsQualifiedName tests whether the value passed is what Kubernetes calls a
// "qualified name".  This is a format used in various places throughout the
// system.  If the value is not valid, a list of error strings is returned.
// Otherwise an empty list (or nil) is returned.
func IsQualifiedName(value string) []string {
	var errs []string
	parts := strings.Split(value, "/")
	var name string
	switch len(parts) {
	case 1:
		name = parts[0]
	case 2:
		var prefix string
		prefix, name = parts[0], parts[1]
		if len(prefix) == 0 {
			errs = append(errs, "prefix part "+EmptyError())
		} else if msgs := IsDNS1123Subdomain(prefix); len(msgs) != 0 {
			errs = append(errs, prefixEach(msgs, "prefix part ")...)
		}
	default:
		return append(errs, RegexError(qualifiedNameFmt, "MyName", "my.name", "123-abc")+
			" with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName'")
	}

	if len(name) == 0 {
		errs = append(errs, "name part "+EmptyError())
	} else if len(name) > qualifiedNameMaxLength {
		errs = append(errs, "name part "+MaxLenError(qualifiedNameMaxLength))
	}
	if !qualifiedNameRegexp.MatchString(name) {
		errs = append(errs, "name part "+RegexError(qualifiedNameFmt, "MyName", "my.name", "123-abc"))
	}
	return errs
}

const labelValueFmt string = "(" + qualifiedNameFmt + ")?"
const LabelValueMaxLength int = 63

var labelValueRegexp = regexp.MustCompile("^" + labelValueFmt + "$")

// IsValidLabelValue tests whether the value passed is a valid label value.  If
// the value is not valid, a list of error strings is returned.  Otherwise an
// empty list (or nil) is returned.
func IsValidLabelValue(value string) []string {
	var errs []string
	if len(value) > LabelValueMaxLength {
		errs = append(errs, MaxLenError(LabelValueMaxLength))
	}
	if !labelValueRegexp.MatchString(value) {
		errs = append(errs, RegexError(labelValueFmt, "MyValue", "my_value", "12345"))
	}
	return errs
}

const dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
const DNS1123LabelMaxLength int = 63

var dns1123LabelRegexp = regexp.MustCompile("^" + dns1123LabelFmt + "$")

// IsDNS1123Label tests for a string that conforms to the definition of a label in
// DNS (RFC 1123).
func IsDNS1123Label(value string) []string {
	var errs []string
	if len(value) > DNS1123LabelMaxLength {
		errs = append(errs, MaxLenError(DNS1123LabelMaxLength))
	}
	if !dns1123LabelRegexp.MatchString(value) {
		errs = append(errs, RegexError(dns1123LabelFmt, "my-name", "123-abc"))
	}
	return errs
}

const dns1123SubdomainFmt string = dns1123LabelFmt + "(\\." + dns1123LabelFmt + ")*"
const DNS1123SubdomainMaxLength int = 253

var dns1123SubdomainRegexp = regexp.MustCompile("^" + dns1123SubdomainFmt + "$")

// IsDNS1123Subdomain tests for a string that conforms to the definition of a
// subdomain in DNS (RFC 1123).
func IsDNS1123Subdomain(value string) []string {
	var errs []string
	if len(value) > DNS1123SubdomainMaxLength {
		errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
	}
	if !dns1123SubdomainRegexp.MatchString(value) {
		errs = append(errs, RegexError(dns1123SubdomainFmt, "example.com"))
	}
	return errs
}

const dns1035LabelFmt string = "[a-z]([-a-z0-9]*[a-z0-9])?"
const DNS1035LabelMaxLength int = 63

var dns1035LabelRegexp = regexp.MustCompile("^" + dns1035LabelFmt + "$")

// IsDNS1035Label tests for a string that conforms to the definition of a label in
// DNS (RFC 1035).
func IsDNS1035Label(value string) []string {
	var errs []string
	if len(value) > DNS1035LabelMaxLength {
		errs = append(errs, MaxLenError(DNS1035LabelMaxLength))
	}
	if !dns1035LabelRegexp.MatchString(value) {
		errs = append(errs, RegexError(dns1035LabelFmt, "my-name", "abc-123"))
	}
	return errs
}

// wildcard definition - RFC 1034 section 4.3.3.
// examples:
// - valid: *.bar.com, *.foo.bar.com
// - invalid: *.*.bar.com, *.foo.*.com, *bar.com, f*.bar.com, *
const wildcardDNF1123SubdomainFmt = "\\*\\." + dns1123SubdomainFmt

// IsWildcardDNS1123Subdomain tests for a string that conforms to the definition of a
// wildcard subdomain in DNS (RFC 1034 section 4.3.3).
func IsWildcardDNS1123Subdomain(value string) []string {
	wildcardDNS1123SubdomainRegexp := regexp.MustCompile("^\\*\\." + dns1123SubdomainFmt + "$")

	var errs []string
	if len(value) > DNS1123SubdomainMaxLength {
		errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
	}
	if !wildcardDNS1123SubdomainRegexp.MatchString(value) {
		errs = append(errs, RegexError(wildcardDNF1123SubdomainFmt, "*.example.com"))
	}
	return errs
}

const cIdentifierFmt string = "[A-Za-z_][A-Za-z0-9_]*"

var cIdentifierRegexp = regexp.MustCompile("^" + cIdentifierFmt + "$")

// IsCIdentifier tests for a string that conforms the definition of an identifier
// in C. This checks the format, but not the length.
func IsCIdentifier(value string) []string {
	if !cIdentifierRegexp.MatchString(value) {
		return []string{RegexError(cIdentifierFmt, "my_name", "MY_NAME", "MyName")}
	}
	return nil
}

// IsValidPortNum tests that the argument is a valid, non-zero port number.
func IsValidPortNum(port int) []string {
	if 1 <= port && port <= 65535 {
		return nil
	}
	return []string{InclusiveRangeError(1, 65535)}
}

// Now in libcontainer UID/GID limits is 0 ~ 1<<31 - 1
// TODO: once we have a type for UID/GID we should make these that type.
const (
	minUserID  = 0
	maxUserID  = math.MaxInt32
	minGroupID = 0
	maxGroupID = math.MaxInt32
)

// IsValidGroupId tests that the argument is a valid Unix GID.
func IsValidGroupId(gid int64) []string {
	if minGroupID <= gid && gid <= maxGroupID {
		return nil
	}
	return []string{InclusiveRangeError(minGroupID, maxGroupID)}
}

// IsValidUserId tests that the argument is a valid Unix UID.
func IsValidUserId(uid int64) []string {
	if minUserID <= uid && uid <= maxUserID {
		return nil
	}
	return []string{InclusiveRangeError(minUserID, maxUserID)}
}

var portNameCharsetRegex = regexp.MustCompile("^[-a-z0-9]+$")
var portNameOneLetterRegexp = regexp.MustCompile("[a-z]")

// IsValidPortName check that the argument is valid syntax. It must be
// non-empty and no more than 15 characters long. It may contain only [-a-z0-9]
// and must contain at least one letter [a-z]. It must not start or end with a
// hyphen, nor contain adjacent hyphens.
//
// Note: We only allow lower-case characters, even though RFC 6335 is case
// insensitive.
func IsValidPortName(port string) []string {
	var errs []string
	if len(port) > 15 {
		errs = append(errs, MaxLenError(15))
	}
	if !portNameCharsetRegex.MatchString(port) {
		errs = append(errs, "must contain only alpha-numeric characters (a-z, 0-9), and hyphens (-)")
	}
	if !portNameOneLetterRegexp.MatchString(port) {
		errs = append(errs, "must contain at least one letter or number (a-z, 0-9)")
	}
	if strings.Contains(port, "--") {
		errs = append(errs, "must not contain consecutive hyphens")
	}
	if len(port) > 0 && (port[0] == '-' || port[len(port)-1] == '-') {
		errs = append(errs, "must not begin or end with a hyphen")
	}
	return errs
}

// IsValidIP tests that the argument is a valid IP address.
func IsValidIP(value string) []string {
	if net.ParseIP(value) == nil {
		return []string{"must be a valid IP address, (e.g. 10.9.8.7)"}
	}
	return nil
}

const percentFmt string = "[0-9]+%"

var percentRegexp = regexp.MustCompile("^" + percentFmt + "$")

func IsValidPercent(percent string) []string {
	if !percentRegexp.MatchString(percent) {
		return []string{RegexError(percentFmt, "1%", "93%")}
	}
	return nil
}

const httpHeaderNameFmt string = "[-A-Za-z0-9]+"

var httpHeaderNameRegexp = regexp.MustCompile("^" + httpHeaderNameFmt + "$")

// IsHTTPHeaderName checks that a string conforms to the Go HTTP library's
// definition of a valid header field name (a stricter subset than RFC7230).
func IsHTTPHeaderName(value string) []string {
	if !httpHeaderNameRegexp.MatchString(value) {
		return []string{RegexError(httpHeaderNameFmt, "X-Header-Name")}
	}
	return nil
}

const configMapKeyFmt = `[-._a-zA-Z0-9]+`

var configMapKeyRegexp = regexp.MustCompile("^" + configMapKeyFmt + "$")

// IsConfigMapKey tests for a string that is a valid key for a ConfigMap or Secret
func IsConfigMapKey(value string) []string {
	var errs []string
	if len(value) > DNS1123SubdomainMaxLength {
		errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
	}
	if !configMapKeyRegexp.MatchString(value) {
		errs = append(errs, RegexError(configMapKeyFmt, "key.name", "KEY_NAME", "key-name"))
	}
	if value == "." {
		errs = append(errs, `must not be '.'`)
	}
	if value == ".." {
		errs = append(errs, `must not be '..'`)
	} else if strings.HasPrefix(value, "..") {
		errs = append(errs, `must not start with '..'`)
	}
	return errs
}

// MaxLenError returns a string explanation of a "string too long" validation
// failure.
func MaxLenError(length int) string {
	return fmt.Sprintf("must be no more than %d characters", length)
}

// RegexError returns a string explanation of a regex validation failure.
func RegexError(fmt string, examples ...string) string {
	s := "must match the regex " + fmt
	if len(examples) == 0 {
		return s
	}
	s += " (e.g. "
	for i := range examples {
		if i > 0 {
			s += " or "
		}
		s += "'" + examples[i] + "'"
	}
	return s + ")"
}

// EmptyError returns a string explanation of a "must not be empty" validation
// failure.
func EmptyError() string {
	return "must be non-empty"
}

func prefixEach(msgs []string, prefix string) []string {
	for i := range msgs {
		msgs[i] = prefix + msgs[i]
	}
	return msgs
}

// InclusiveRangeError returns a string explanation of a numeric "must be
// between" validation failure.
func InclusiveRangeError(lo, hi int) string {
	return fmt.Sprintf(`must be between %d and %d, inclusive`, lo, hi)
}
<commit_msg>use elseif to replace if<commit_after>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package validation

import (
	"fmt"
	"math"
	"net"
	"regexp"
	"strings"
)

const qnameCharFmt string = "[A-Za-z0-9]"
const qnameExtCharFmt string = "[-A-Za-z0-9_.]"
const qualifiedNameFmt string = "(" + qnameCharFmt + qnameExtCharFmt + "*)?" + qnameCharFmt
const qualifiedNameMaxLength int = 63

var qualifiedNameRegexp = regexp.MustCompile("^" + qualifiedNameFmt + "$")

// IsQualifiedName tests whether the value passed is what Kubernetes calls a
// "qualified name".  This is a format used in various places throughout the
// system.  If the value is not valid, a list of error strings is returned.
// Otherwise an empty list (or nil) is returned.
func IsQualifiedName(value string) []string {
	var errs []string
	parts := strings.Split(value, "/")
	var name string
	switch len(parts) {
	case 1:
		name = parts[0]
	case 2:
		var prefix string
		prefix, name = parts[0], parts[1]
		if len(prefix) == 0 {
			errs = append(errs, "prefix part "+EmptyError())
		} else if msgs := IsDNS1123Subdomain(prefix); len(msgs) != 0 {
			errs = append(errs, prefixEach(msgs, "prefix part ")...)
		}
	default:
		return append(errs, RegexError(qualifiedNameFmt, "MyName", "my.name", "123-abc")+
			" with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName'")
	}

	if len(name) == 0 {
		errs = append(errs, "name part "+EmptyError())
	} else if len(name) > qualifiedNameMaxLength {
		errs = append(errs, "name part "+MaxLenError(qualifiedNameMaxLength))
	}
	if !qualifiedNameRegexp.MatchString(name) {
		errs = append(errs, "name part "+RegexError(qualifiedNameFmt, "MyName", "my.name", "123-abc"))
	}
	return errs
}

const labelValueFmt string = "(" + qualifiedNameFmt + ")?"
const LabelValueMaxLength int = 63

var labelValueRegexp = regexp.MustCompile("^" + labelValueFmt + "$")

// IsValidLabelValue tests whether the value passed is a valid label value.  If
// the value is not valid, a list of error strings is returned.  Otherwise an
// empty list (or nil) is returned.
func IsValidLabelValue(value string) []string {
	var errs []string
	if len(value) > LabelValueMaxLength {
		errs = append(errs, MaxLenError(LabelValueMaxLength))
	}
	if !labelValueRegexp.MatchString(value) {
		errs = append(errs, RegexError(labelValueFmt, "MyValue", "my_value", "12345"))
	}
	return errs
}

const dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
const DNS1123LabelMaxLength int = 63

var dns1123LabelRegexp = regexp.MustCompile("^" + dns1123LabelFmt + "$")

// IsDNS1123Label tests for a string that conforms to the definition of a label in
// DNS (RFC 1123).
func IsDNS1123Label(value string) []string {
	var errs []string
	if len(value) > DNS1123LabelMaxLength {
		errs = append(errs, MaxLenError(DNS1123LabelMaxLength))
	}
	if !dns1123LabelRegexp.MatchString(value) {
		errs = append(errs, RegexError(dns1123LabelFmt, "my-name", "123-abc"))
	}
	return errs
}

const dns1123SubdomainFmt string = dns1123LabelFmt + "(\\." + dns1123LabelFmt + ")*"
const DNS1123SubdomainMaxLength int = 253

var dns1123SubdomainRegexp = regexp.MustCompile("^" + dns1123SubdomainFmt + "$")

// IsDNS1123Subdomain tests for a string that conforms to the definition of a
// subdomain in DNS (RFC 1123).
func IsDNS1123Subdomain(value string) []string {
	var errs []string
	if len(value) > DNS1123SubdomainMaxLength {
		errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
	}
	if !dns1123SubdomainRegexp.MatchString(value) {
		errs = append(errs, RegexError(dns1123SubdomainFmt, "example.com"))
	}
	return errs
}

const dns1035LabelFmt string = "[a-z]([-a-z0-9]*[a-z0-9])?"
const DNS1035LabelMaxLength int = 63

var dns1035LabelRegexp = regexp.MustCompile("^" + dns1035LabelFmt + "$")

// IsDNS1035Label tests for a string that conforms to the definition of a label in
// DNS (RFC 1035).
func IsDNS1035Label(value string) []string {
	var errs []string
	if len(value) > DNS1035LabelMaxLength {
		errs = append(errs, MaxLenError(DNS1035LabelMaxLength))
	}
	if !dns1035LabelRegexp.MatchString(value) {
		errs = append(errs, RegexError(dns1035LabelFmt, "my-name", "abc-123"))
	}
	return errs
}

// wildcard definition - RFC 1034 section 4.3.3.
// examples:
// - valid: *.bar.com, *.foo.bar.com
// - invalid: *.*.bar.com, *.foo.*.com, *bar.com, f*.bar.com, *
const wildcardDNF1123SubdomainFmt = "\\*\\." + dns1123SubdomainFmt

// IsWildcardDNS1123Subdomain tests for a string that conforms to the definition of a
// wildcard subdomain in DNS (RFC 1034 section 4.3.3).
func IsWildcardDNS1123Subdomain(value string) []string {
	wildcardDNS1123SubdomainRegexp := regexp.MustCompile("^\\*\\." + dns1123SubdomainFmt + "$")

	var errs []string
	if len(value) > DNS1123SubdomainMaxLength {
		errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
	}
	if !wildcardDNS1123SubdomainRegexp.MatchString(value) {
		errs = append(errs, RegexError(wildcardDNF1123SubdomainFmt, "*.example.com"))
	}
	return errs
}

const cIdentifierFmt string = "[A-Za-z_][A-Za-z0-9_]*"

var cIdentifierRegexp = regexp.MustCompile("^" + cIdentifierFmt + "$")

// IsCIdentifier tests for a string that conforms the definition of an identifier
// in C. This checks the format, but not the length.
func IsCIdentifier(value string) []string {
	if !cIdentifierRegexp.MatchString(value) {
		return []string{RegexError(cIdentifierFmt, "my_name", "MY_NAME", "MyName")}
	}
	return nil
}

// IsValidPortNum tests that the argument is a valid, non-zero port number.
func IsValidPortNum(port int) []string {
	if 1 <= port && port <= 65535 {
		return nil
	}
	return []string{InclusiveRangeError(1, 65535)}
}

// Now in libcontainer UID/GID limits is 0 ~ 1<<31 - 1
// TODO: once we have a type for UID/GID we should make these that type.
const (
	minUserID  = 0
	maxUserID  = math.MaxInt32
	minGroupID = 0
	maxGroupID = math.MaxInt32
)

// IsValidGroupId tests that the argument is a valid Unix GID.
func IsValidGroupId(gid int64) []string {
	if minGroupID <= gid && gid <= maxGroupID {
		return nil
	}
	return []string{InclusiveRangeError(minGroupID, maxGroupID)}
}

// IsValidUserId tests that the argument is a valid Unix UID.
func IsValidUserId(uid int64) []string {
	if minUserID <= uid && uid <= maxUserID {
		return nil
	}
	return []string{InclusiveRangeError(minUserID, maxUserID)}
}

var portNameCharsetRegex = regexp.MustCompile("^[-a-z0-9]+$")
var portNameOneLetterRegexp = regexp.MustCompile("[a-z]")

// IsValidPortName check that the argument is valid syntax. It must be
// non-empty and no more than 15 characters long. It may contain only [-a-z0-9]
// and must contain at least one letter [a-z]. It must not start or end with a
// hyphen, nor contain adjacent hyphens.
//
// Note: We only allow lower-case characters, even though RFC 6335 is case
// insensitive.
func IsValidPortName(port string) []string {
	var errs []string
	if len(port) > 15 {
		errs = append(errs, MaxLenError(15))
	}
	if !portNameCharsetRegex.MatchString(port) {
		errs = append(errs, "must contain only alpha-numeric characters (a-z, 0-9), and hyphens (-)")
	}
	if !portNameOneLetterRegexp.MatchString(port) {
		errs = append(errs, "must contain at least one letter or number (a-z, 0-9)")
	}
	if strings.Contains(port, "--") {
		errs = append(errs, "must not contain consecutive hyphens")
	}
	if len(port) > 0 && (port[0] == '-' || port[len(port)-1] == '-') {
		errs = append(errs, "must not begin or end with a hyphen")
	}
	return errs
}

// IsValidIP tests that the argument is a valid IP address.
func IsValidIP(value string) []string {
	if net.ParseIP(value) == nil {
		return []string{"must be a valid IP address, (e.g. 10.9.8.7)"}
	}
	return nil
}

const percentFmt string = "[0-9]+%"

var percentRegexp = regexp.MustCompile("^" + percentFmt + "$")

func IsValidPercent(percent string) []string {
	if !percentRegexp.MatchString(percent) {
		return []string{RegexError(percentFmt, "1%", "93%")}
	}
	return nil
}

const httpHeaderNameFmt string = "[-A-Za-z0-9]+"

var httpHeaderNameRegexp = regexp.MustCompile("^" + httpHeaderNameFmt + "$")

// IsHTTPHeaderName checks that a string conforms to the Go HTTP library's
// definition of a valid header field name (a stricter subset than RFC7230).
func IsHTTPHeaderName(value string) []string {
	if !httpHeaderNameRegexp.MatchString(value) {
		return []string{RegexError(httpHeaderNameFmt, "X-Header-Name")}
	}
	return nil
}

const configMapKeyFmt = `[-._a-zA-Z0-9]+`

var configMapKeyRegexp = regexp.MustCompile("^" + configMapKeyFmt + "$")

// IsConfigMapKey tests for a string that is a valid key for a ConfigMap or Secret
func IsConfigMapKey(value string) []string {
	var errs []string
	if len(value) > DNS1123SubdomainMaxLength {
		errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
	}
	if !configMapKeyRegexp.MatchString(value) {
		errs = append(errs, RegexError(configMapKeyFmt, "key.name", "KEY_NAME", "key-name"))
	}
	if value == "." {
		errs = append(errs, `must not be '.'`)
	} else if value == ".." {
		errs = append(errs, `must not be '..'`)
	} else if strings.HasPrefix(value, "..") {
		errs = append(errs, `must not start with '..'`)
	}
	return errs
}

// MaxLenError returns a string explanation of a "string too long" validation
// failure.
func MaxLenError(length int) string {
	return fmt.Sprintf("must be no more than %d characters", length)
}

// RegexError returns a string explanation of a regex validation failure.
func RegexError(fmt string, examples ...string) string {
	s := "must match the regex " + fmt
	if len(examples) == 0 {
		return s
	}
	s += " (e.g. "
	for i := range examples {
		if i > 0 {
			s += " or "
		}
		s += "'" + examples[i] + "'"
	}
	return s + ")"
}

// EmptyError returns a string explanation of a "must not be empty" validation
// failure.
func EmptyError() string {
	return "must be non-empty"
}

func prefixEach(msgs []string, prefix string) []string {
	for i := range msgs {
		msgs[i] = prefix + msgs[i]
	}
	return msgs
}

// InclusiveRangeError returns a string explanation of a numeric "must be
// between" validation failure.
func InclusiveRangeError(lo, hi int) string {
	return fmt.Sprintf(`must be between %d and %d, inclusive`, lo, hi)
}
<|endoftext|>


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

package utils

import (
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/goharbor/harbor/src/common/utils/log"
)

// ParseEndpoint parses endpoint to a URL
func ParseEndpoint(endpoint string) (*url.URL, error) {
	endpoint = strings.Trim(endpoint, " ")
	endpoint = strings.TrimRight(endpoint, "/")
	if len(endpoint) == 0 {
		return nil, fmt.Errorf("empty URL")
	}
	i := strings.Index(endpoint, "://")
	if i >= 0 {
		scheme := endpoint[:i]
		if scheme != "http" && scheme != "https" {
			return nil, fmt.Errorf("invalid scheme: %s", scheme)
		}
	} else {
		endpoint = "http://" + endpoint
	}

	return url.ParseRequestURI(endpoint)
}

// ParseRepository splits a repository into two parts: project and rest
func ParseRepository(repository string) (project, rest string) {
	repository = strings.TrimLeft(repository, "/")
	repository = strings.TrimRight(repository, "/")
	if !strings.ContainsRune(repository, '/') {
		rest = repository
		return
	}
	index := strings.Index(repository, "/")
	project = repository[0:index]
	rest = repository[index+1:]
	return
}

// GenerateRandomString generates a random string
func GenerateRandomString() string {
	length := 32
	const chars = "abcdefghijklmnopqrstuvwxyz0123456789"
	l := len(chars)
	result := make([]byte, length)
	_, err := rand.Read(result)
	if err != nil {
		log.Warningf("Error reading random bytes: %v", err)
	}
	for i := 0; i < length; i++ {
		result[i] = chars[int(result[i])%l]
	}
	return string(result)
}

// TestTCPConn tests TCP connection
// timeout: the total time before returning if something is wrong
// with the connection, in second
// interval: the interval time for retring after failure, in second
func TestTCPConn(addr string, timeout, interval int) error {
	success := make(chan int)
	cancel := make(chan int)

	go func() {
		n := 1
		for {
			select {
			case <-cancel:
				break
			default:
				conn, err := net.DialTimeout("tcp", addr, time.Duration(n)*time.Second)
				if err != nil {
					log.Errorf("failed to connect to tcp://%s, retry after %d seconds :%v",
						addr, interval, err)
					n = n * 2
					time.Sleep(time.Duration(interval) * time.Second)
					continue
				}
				if err = conn.Close(); err != nil {
					log.Errorf("failed to close the connection: %v", err)
				}
				success <- 1
				break
			}
		}
	}()

	select {
	case <-success:
		return nil
	case <-time.After(time.Duration(timeout) * time.Second):
		cancel <- 1
		return fmt.Errorf("failed to connect to tcp:%s after %d seconds", addr, timeout)
	}
}

// ParseTimeStamp parse timestamp to time
func ParseTimeStamp(timestamp string) (*time.Time, error) {
	i, err := strconv.ParseInt(timestamp, 10, 64)
	if err != nil {
		return nil, err
	}
	t := time.Unix(i, 0)
	return &t, nil
}

// ConvertMapToStruct is used to fill the specified struct with map.
func ConvertMapToStruct(object interface{}, values interface{}) error {
	if object == nil {
		return errors.New("nil struct is not supported")
	}

	if reflect.TypeOf(object).Kind() != reflect.Ptr {
		return errors.New("object should be referred by pointer")
	}

	bytes, err := json.Marshal(values)
	if err != nil {
		return err
	}

	return json.Unmarshal(bytes, object)
}

// ParseProjectIDOrName parses value to ID(int64) or name(string)
func ParseProjectIDOrName(value interface{}) (int64, string, error) {
	if value == nil {
		return 0, "", errors.New("harborIDOrName is nil")
	}

	var id int64
	var name string
	switch value.(type) {
	case int:
		i := value.(int)
		id = int64(i)
	case int64:
		id = value.(int64)
	case string:
		name = value.(string)
	default:
		return 0, "", fmt.Errorf("unsupported type")
	}
	return id, name, nil
}

// SafeCastString -- cast a object to string saftely
func SafeCastString(value interface{}) string {
	if result, ok := value.(string); ok {
		return result
	}
	return ""
}

// SafeCastInt --
func SafeCastInt(value interface{}) int {
	if result, ok := value.(int); ok {
		return result
	}
	return 0
}

// SafeCastBool --
func SafeCastBool(value interface{}) bool {
	if result, ok := value.(bool); ok {
		return result
	}
	return false
}

// SafeCastFloat64 --
func SafeCastFloat64(value interface{}) float64 {
	if result, ok := value.(float64); ok {
		return result
	}
	return 0
}

// ParseOfftime ...
func ParseOfftime(offtime int64) (hour, minite, second int) {
	offtime = offtime % (3600 * 24)
	hour = int(offtime / 3600)
	offtime = offtime % 3600
	minite = int(offtime / 60)
	second = int(offtime % 60)
	return
}
<commit_msg>Fix `TestTCPConn` break issue.<commit_after>// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import (
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/goharbor/harbor/src/common/utils/log"
)

// ParseEndpoint parses endpoint to a URL
func ParseEndpoint(endpoint string) (*url.URL, error) {
	endpoint = strings.Trim(endpoint, " ")
	endpoint = strings.TrimRight(endpoint, "/")
	if len(endpoint) == 0 {
		return nil, fmt.Errorf("empty URL")
	}
	i := strings.Index(endpoint, "://")
	if i >= 0 {
		scheme := endpoint[:i]
		if scheme != "http" && scheme != "https" {
			return nil, fmt.Errorf("invalid scheme: %s", scheme)
		}
	} else {
		endpoint = "http://" + endpoint
	}

	return url.ParseRequestURI(endpoint)
}

// ParseRepository splits a repository into two parts: project and rest
func ParseRepository(repository string) (project, rest string) {
	repository = strings.TrimLeft(repository, "/")
	repository = strings.TrimRight(repository, "/")
	if !strings.ContainsRune(repository, '/') {
		rest = repository
		return
	}
	index := strings.Index(repository, "/")
	project = repository[0:index]
	rest = repository[index+1:]
	return
}

// GenerateRandomString generates a random string
func GenerateRandomString() string {
	length := 32
	const chars = "abcdefghijklmnopqrstuvwxyz0123456789"
	l := len(chars)
	result := make([]byte, length)
	_, err := rand.Read(result)
	if err != nil {
		log.Warningf("Error reading random bytes: %v", err)
	}
	for i := 0; i < length; i++ {
		result[i] = chars[int(result[i])%l]
	}
	return string(result)
}

// TestTCPConn tests TCP connection
// timeout: the total time before returning if something is wrong
// with the connection, in second
// interval: the interval time for retring after failure, in second
func TestTCPConn(addr string, timeout, interval int) error {
	success := make(chan int)
	cancel := make(chan int)

	go func() {
		n := 1

	loop:
		for {
			select {
			case <-cancel:
				break loop
			default:
				conn, err := net.DialTimeout("tcp", addr, time.Duration(n)*time.Second)
				if err != nil {
					log.Errorf("failed to connect to tcp://%s, retry after %d seconds :%v",
						addr, interval, err)
					n = n * 2
					time.Sleep(time.Duration(interval) * time.Second)
					continue
				}
				if err = conn.Close(); err != nil {
					log.Errorf("failed to close the connection: %v", err)
				}
				success <- 1
				break loop
			}
		}
	}()

	select {
	case <-success:
		return nil
	case <-time.After(time.Duration(timeout) * time.Second):
		cancel <- 1
		return fmt.Errorf("failed to connect to tcp:%s after %d seconds", addr, timeout)
	}
}

// ParseTimeStamp parse timestamp to time
func ParseTimeStamp(timestamp string) (*time.Time, error) {
	i, err := strconv.ParseInt(timestamp, 10, 64)
	if err != nil {
		return nil, err
	}
	t := time.Unix(i, 0)
	return &t, nil
}

// ConvertMapToStruct is used to fill the specified struct with map.
func ConvertMapToStruct(object interface{}, values interface{}) error {
	if object == nil {
		return errors.New("nil struct is not supported")
	}

	if reflect.TypeOf(object).Kind() != reflect.Ptr {
		return errors.New("object should be referred by pointer")
	}

	bytes, err := json.Marshal(values)
	if err != nil {
		return err
	}

	return json.Unmarshal(bytes, object)
}

// ParseProjectIDOrName parses value to ID(int64) or name(string)
func ParseProjectIDOrName(value interface{}) (int64, string, error) {
	if value == nil {
		return 0, "", errors.New("harborIDOrName is nil")
	}

	var id int64
	var name string
	switch value.(type) {
	case int:
		i := value.(int)
		id = int64(i)
	case int64:
		id = value.(int64)
	case string:
		name = value.(string)
	default:
		return 0, "", fmt.Errorf("unsupported type")
	}
	return id, name, nil
}

// SafeCastString -- cast a object to string saftely
func SafeCastString(value interface{}) string {
	if result, ok := value.(string); ok {
		return result
	}
	return ""
}

// SafeCastInt --
func SafeCastInt(value interface{}) int {
	if result, ok := value.(int); ok {
		return result
	}
	return 0
}

// SafeCastBool --
func SafeCastBool(value interface{}) bool {
	if result, ok := value.(bool); ok {
		return result
	}
	return false
}

// SafeCastFloat64 --
func SafeCastFloat64(value interface{}) float64 {
	if result, ok := value.(float64); ok {
		return result
	}
	return 0
}

// ParseOfftime ...
func ParseOfftime(offtime int64) (hour, minite, second int) {
	offtime = offtime % (3600 * 24)
	hour = int(offtime / 3600)
	offtime = offtime % 3600
	minite = int(offtime / 60)
	second = int(offtime % 60)
	return
}
<|endoftext|>


<commit_before>package nuimo

import (
	"encoding/binary"
	"fmt"
	"strings"
	"time"

	"github.com/currantlabs/ble"
	"github.com/currantlabs/ble/examples/lib/gatt"
	"github.com/currantlabs/ble/linux/hci"
	"github.com/currantlabs/ble/linux/hci/cmd"
	"github.com/mgutz/logxi/v1"
)

const SERVICE_BATTERY_STATUS = "180F"
const SERVICE_DEVICE_INFO = "180A"
const SERVICE_LED_MATRIX = "F29B1523CB1940F3BE5C7241ECB82FD1"
const SERVICE_USER_INPUT = "F29B1525CB1940F3BE5C7241ECB82FD2"

const CHAR_BATTERY_LEVEL = "2A19"
const CHAR_DEVICE_INFO = "2A29"
const CHAR_LED_MATRIX = "F29B1524CB1940F3BE5C7241ECB82FD1"
const CHAR_INPUT_FLY = "F29B1526CB1940F3BE5C7241ECB82FD2"
const CHAR_INPUT_SWIPE = "F29B1527CB1940F3BE5C7241ECB82FD2"
const CHAR_INPUT_ROTATE = "F29B1528CB1940F3BE5C7241ECB82FD2"
const CHAR_INPUT_CLICK = "F29B1529CB1940F3BE5C7241ECB82FD2"

const DIR_LEFT = 0
const DIR_RIGHT = 1
const DIR_UP = 2
const DIR_BACKWARDS = 2
const DIR_DOWN = 3
const DIR_TOWARDS = 3
const DIR_UPDOWN = 4

const CLICK_DOWN = 1
const CLICK_UP = 0

var logger = log.New("nuimo")

type Nuimo struct {
	client ble.Client
	events chan Event
	led    *ble.Characteristic
	bttry  *ble.Characteristic
	nfo    *ble.Characteristic
}

type Event struct {
	Key   string
	Value int64
	Raw   []byte
}

func Connect(params ...int) (*Nuimo, error) {

	ch := make(chan Event, 100)
	n := &Nuimo{events: ch}
	err := n.reconnect()

	if err != nil {
		logger.Fatal("%s", err)
	}

	if len(params) == 1 && params[0] > 0 {
		go n.keepConnected(params[0])
	}

	return n, err
}

func discoverDevice() (ble.Client, error) {
	logger.Info("Discover")
	filter := func(a ble.Advertisement) bool {
		return strings.ToUpper(a.LocalName()) == "NUIMO"
	}

	// Set connection parameters. Only supported on Linux platform.
	d := gatt.DefaultDevice()
	if h, ok := d.(*hci.HCI); ok {
		if err := h.Option(hci.OptConnParams(
			cmd.LECreateConnection{
				LEScanInterval:        0x0004,    // 0x0004 - 0x4000; N * 0.625 msec
				LEScanWindow:          0x0004,    // 0x0004 - 0x4000; N * 0.625 msec
				InitiatorFilterPolicy: 0x00,      // White list is not used
				PeerAddressType:       0x00,      // Public Device Address
				PeerAddress:           [6]byte{}, //
				OwnAddressType:        0x00,      // Public Device Address
				ConnIntervalMin:       0x0006,    // 0x0006 - 0x0C80; N * 1.25 msec
				ConnIntervalMax:       0x0006,    // 0x0006 - 0x0C80; N * 1.25 msec
				ConnLatency:           0x0000,    // 0x0000 - 0x01F3; N * 1.25 msec
				SupervisionTimeout:    0x0048,    // 0x000A - 0x0C80; N * 10 msec
				MinimumCELength:       0x0000,    // 0x0000 - 0xFFFF; N * 0.625 msec
				MaximumCELength:       0x0000,    // 0x0000 - 0xFFFF; N * 0.625 msec
			})); err != nil {
			logger.Fatal("can't set advertising param: %s", err)
		}
	}
	return gatt.Discover(gatt.FilterFunc(filter))
}

func (n *Nuimo) reconnect() error {
	logger.Info("Reconnect")
	if n.client != nil {
		n.client.ClearSubscriptions()
		n.client.CancelConnection()
	}
	client, err := discoverDevice()
	if err != nil {
		return err
	}
	n.client = client
	return n.DiscoverServices()
}

func (n *Nuimo) keepConnected(refresh int) {

	for {
		c := make(chan uint8, 1)
		go func() {
			logger.Info("Reading batterie")
			data, err := n.client.ReadCharacteristic(n.bttry)
			if err != nil {
				logger.Fatal("Error", err)
			}
			c <- uint8(data[0])
		}()
		select {
		case data := <-c:
			logger.Info("Batterie level", data)
		case <-time.After(30 * time.Second):
			n.reconnect()
		}
		close(c)
		time.Sleep(10 * time.Second)
	}

}

func (n *Nuimo) Events() <-chan Event {
	return n.events
}

func (n *Nuimo) Display(matrix []byte, brightness uint8, timeout uint8) {

	displayMatrix := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

	for c, dots := range matrix {
		if c > 10 {
			break
		}
		displayMatrix[c] = dots
	}

	displayMatrix[11] = brightness
	displayMatrix[12] = timeout

	n.client.WriteCharacteristic(n.led, displayMatrix, true)
}

func DisplayMatrix(dots ...byte) []byte {
	bytes := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	var b uint8
	var i uint8
	dotCount := uint8(len(dots))

	for b = 0; b < 11; b++ {
		for i = 0; i < 8; i++ {
			dot := (b * 8) + i
			if dot < dotCount && dots[dot] > 0 {
				bytes[b] |= byte(1) << i
			}
		}

	}

	return bytes
}

//  TODO: make sure we only subscribe to the services we need
func (n *Nuimo) DiscoverServices() error {
	p, err := n.client.DiscoverProfile(true)
	if err != nil {
		return fmt.Errorf("can't discover services: %s\n", err)
	}

	for _, s := range p.Services {

		switch {
		case s.UUID.Equal(ble.MustParse(SERVICE_DEVICE_INFO)):
			for _, c := range s.Characteristics {
				switch {
				case c.UUID.Equal(ble.MustParse(CHAR_DEVICE_INFO)):
					n.nfo = c
					logger.Info("Info subscribed")
					n.client.Subscribe(c, false, n.info)
				default:
					logger.Warn("Unknown device char", "uuid", c.UUID.String())
					n.client.Subscribe(c, false, n.unknown)
				}
			}
		case s.UUID.Equal(ble.MustParse(SERVICE_BATTERY_STATUS)):
			for _, c := range s.Characteristics {
				switch {
				case c.UUID.Equal(ble.MustParse(CHAR_BATTERY_LEVEL)):
					logger.Info("Battery subscribed")
					n.bttry = c
					n.client.Subscribe(c, false, n.battery)

				default:
					logger.Warn("Unknown battery char", "uuid", c.UUID.String())
					n.client.Subscribe(c, false, n.unknown)
				}
			}
		case s.UUID.Equal(ble.MustParse(SERVICE_USER_INPUT)):
			for _, c := range s.Characteristics {

				switch {

				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_CLICK)):
					n.client.Subscribe(c, false, n.click)
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_ROTATE)):
					n.client.Subscribe(c, false, n.rotate)
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_SWIPE)):
					n.client.Subscribe(c, false, n.swipe)
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_FLY)):
					n.client.Subscribe(c, false, n.fly)
				default:
					logger.Warn("Unknown input characteristik", "uuid", c.UUID.String())
					n.client.Subscribe(c, false, n.unknown)

				}
			}
		case s.UUID.Equal(ble.MustParse(SERVICE_LED_MATRIX)):
			for _, c := range s.Characteristics {
				logger.Info("Led found")
				n.led = c
			}
		default:
			logger.Warn("Unknown service %s", "uuid", s.UUID.String())
		}
	}
	return nil
}

func (n *Nuimo) Disconnect() error {
	logger.Warn("Nuimo connection closed")
	close(n.events)
	return n.client.CancelConnection()
}

func (n *Nuimo) battery(req []byte) {
	uval, _ := binary.Uvarint(req)
	level := int64(uval)
	n.send(Event{Key: "battery", Raw: req, Value: level})
}
func (n *Nuimo) info(req []byte) {
	logger.Info("Info: " + string(req))
}

func (n *Nuimo) click(req []byte) {
	uval, _ := binary.Uvarint(req)
	dir := int64(uval)
	switch dir {
	case CLICK_DOWN:
		n.send(Event{Key: "press", Raw: req})
	case CLICK_UP:
		n.send(Event{Key: "release", Raw: req})
	}
}

func (n *Nuimo) rotate(req []byte) {
	uval := binary.LittleEndian.Uint16(req)
	val := int64(int16(uval))
	n.send(Event{Key: "rotate", Raw: req, Value: val})
}
func (n *Nuimo) swipe(req []byte) {
	uval, _ := binary.Uvarint(req)
	dir := int64(uval)
	n.send(Event{Key: "swipe", Raw: req, Value: dir})

	switch dir {
	case DIR_LEFT:
		n.send(Event{Key: "swipe_left", Raw: req})
	case DIR_RIGHT:
		n.send(Event{Key: "swipe_right", Raw: req})
	case DIR_UP:
		n.send(Event{Key: "swipe_up", Raw: req})
	case DIR_DOWN:
		n.send(Event{Key: "swipe_down", Raw: req})
	}
}
func (n *Nuimo) fly(req []byte) {
	uval, _ := binary.Uvarint(req[0:1])
	dir := int(uval)
	uval, _ = binary.Uvarint(req[2:])
	distance := int64(uval)

	switch dir {
	case DIR_LEFT:
		n.send(Event{Key: "fly_left", Raw: req, Value: distance})
	case DIR_RIGHT:
		n.send(Event{Key: "fly_right", Raw: req, Value: distance})
	case DIR_BACKWARDS:
		n.send(Event{Key: "fly_backwards", Raw: req, Value: distance})
	case DIR_TOWARDS:
		n.send(Event{Key: "fly_towards", Raw: req, Value: distance})
	case DIR_UPDOWN:
		n.send(Event{Key: "fly_updown", Raw: req, Value: distance})
	}
}
func (n *Nuimo) unknown(req []byte) {
	n.send(Event{Key: "unknown", Raw: req})
}

// make sure missing event sinks don't block the client
func (n *Nuimo) send(e Event) {
	go func() { n.events <- e }()
}
<commit_msg>Skip the fatal error and allow graceful reconnects<commit_after>package nuimo

import (
	"encoding/binary"
	"fmt"
	"strings"
	"time"

	"github.com/currantlabs/ble"
	"github.com/currantlabs/ble/examples/lib/gatt"
	"github.com/currantlabs/ble/linux/hci"
	"github.com/currantlabs/ble/linux/hci/cmd"
	"github.com/mgutz/logxi/v1"
)

const SERVICE_BATTERY_STATUS = "180F"
const SERVICE_DEVICE_INFO = "180A"
const SERVICE_LED_MATRIX = "F29B1523CB1940F3BE5C7241ECB82FD1"
const SERVICE_USER_INPUT = "F29B1525CB1940F3BE5C7241ECB82FD2"

const CHAR_BATTERY_LEVEL = "2A19"
const CHAR_DEVICE_INFO = "2A29"
const CHAR_LED_MATRIX = "F29B1524CB1940F3BE5C7241ECB82FD1"
const CHAR_INPUT_FLY = "F29B1526CB1940F3BE5C7241ECB82FD2"
const CHAR_INPUT_SWIPE = "F29B1527CB1940F3BE5C7241ECB82FD2"
const CHAR_INPUT_ROTATE = "F29B1528CB1940F3BE5C7241ECB82FD2"
const CHAR_INPUT_CLICK = "F29B1529CB1940F3BE5C7241ECB82FD2"

const DIR_LEFT = 0
const DIR_RIGHT = 1
const DIR_UP = 2
const DIR_BACKWARDS = 2
const DIR_DOWN = 3
const DIR_TOWARDS = 3
const DIR_UPDOWN = 4

const CLICK_DOWN = 1
const CLICK_UP = 0

var logger = log.New("nuimo")

type Nuimo struct {
	client ble.Client
	events chan Event
	led    *ble.Characteristic
	bttry  *ble.Characteristic
	nfo    *ble.Characteristic
}

type Event struct {
	Key   string
	Value int64
	Raw   []byte
}

func Connect(params ...int) (*Nuimo, error) {

	ch := make(chan Event, 100)
	n := &Nuimo{events: ch}
	err := n.reconnect()

	if err != nil {
		logger.Fatal("%s", err)
	}

	if len(params) == 1 && params[0] > 0 {
		go n.keepConnected(params[0])
	}

	return n, err
}

func discoverDevice() (ble.Client, error) {
	logger.Info("Discover")
	filter := func(a ble.Advertisement) bool {
		return strings.ToUpper(a.LocalName()) == "NUIMO"
	}

	// Set connection parameters. Only supported on Linux platform.
	d := gatt.DefaultDevice()
	if h, ok := d.(*hci.HCI); ok {
		if err := h.Option(hci.OptConnParams(
			cmd.LECreateConnection{
				LEScanInterval:        0x0004,    // 0x0004 - 0x4000; N * 0.625 msec
				LEScanWindow:          0x0004,    // 0x0004 - 0x4000; N * 0.625 msec
				InitiatorFilterPolicy: 0x00,      // White list is not used
				PeerAddressType:       0x00,      // Public Device Address
				PeerAddress:           [6]byte{}, //
				OwnAddressType:        0x00,      // Public Device Address
				ConnIntervalMin:       0x0006,    // 0x0006 - 0x0C80; N * 1.25 msec
				ConnIntervalMax:       0x0006,    // 0x0006 - 0x0C80; N * 1.25 msec
				ConnLatency:           0x0000,    // 0x0000 - 0x01F3; N * 1.25 msec
				SupervisionTimeout:    0x0048,    // 0x000A - 0x0C80; N * 10 msec
				MinimumCELength:       0x0000,    // 0x0000 - 0xFFFF; N * 0.625 msec
				MaximumCELength:       0x0000,    // 0x0000 - 0xFFFF; N * 0.625 msec
			})); err != nil {
			logger.Fatal("can't set advertising param: %s", err)
		}
	}
	return gatt.Discover(gatt.FilterFunc(filter))
}

func (n *Nuimo) reconnect() error {
	logger.Info("Reconnect")
	if n.client != nil {
		n.client.ClearSubscriptions()
		n.client.CancelConnection()
	}
	client, err := discoverDevice()
	if err != nil {
		return err
	}
	n.client = client
	return n.DiscoverServices()
}

func (n *Nuimo) keepConnected(refresh int) {

	for {
		c := make(chan uint8, 1)
		go func() {
			logger.Info("Reading batterie")
			data, err := n.client.ReadCharacteristic(n.bttry)
			if err != nil {
				logger.Error("Error", err)
				// this will cause a reconnect
				return
			}
			c <- uint8(data[0])

		}()
		select {
		case data := <-c:
			logger.Info("Batterie level", data)
		case <-time.After(30 * time.Second):
			n.reconnect()
		}
		close(c)
		time.Sleep(time.Duration(refresh) * time.Second)
	}

}

func (n *Nuimo) Events() <-chan Event {
	return n.events
}

func (n *Nuimo) Display(matrix []byte, brightness uint8, timeout uint8) {

	displayMatrix := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

	for c, dots := range matrix {
		if c > 10 {
			break
		}
		displayMatrix[c] = dots
	}

	displayMatrix[11] = brightness
	displayMatrix[12] = timeout

	n.client.WriteCharacteristic(n.led, displayMatrix, true)
}

func DisplayMatrix(dots ...byte) []byte {
	bytes := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	var b uint8
	var i uint8
	dotCount := uint8(len(dots))

	for b = 0; b < 11; b++ {
		for i = 0; i < 8; i++ {
			dot := (b * 8) + i
			if dot < dotCount && dots[dot] > 0 {
				bytes[b] |= byte(1) << i
			}
		}

	}

	return bytes
}

//  TODO: make sure we only subscribe to the services we need
func (n *Nuimo) DiscoverServices() error {
	p, err := n.client.DiscoverProfile(true)
	if err != nil {
		return fmt.Errorf("can't discover services: %s\n", err)
	}

	for _, s := range p.Services {

		switch {
		case s.UUID.Equal(ble.MustParse(SERVICE_DEVICE_INFO)):
			for _, c := range s.Characteristics {
				switch {
				case c.UUID.Equal(ble.MustParse(CHAR_DEVICE_INFO)):
					n.nfo = c
					logger.Info("Info subscribed")
					n.client.Subscribe(c, false, n.info)
				default:
					logger.Warn("Unknown device char", "uuid", c.UUID.String())
					n.client.Subscribe(c, false, n.unknown)
				}
			}
		case s.UUID.Equal(ble.MustParse(SERVICE_BATTERY_STATUS)):
			for _, c := range s.Characteristics {
				switch {
				case c.UUID.Equal(ble.MustParse(CHAR_BATTERY_LEVEL)):
					logger.Info("Battery subscribed")
					n.bttry = c
					n.client.Subscribe(c, false, n.battery)

				default:
					logger.Warn("Unknown battery char", "uuid", c.UUID.String())
					n.client.Subscribe(c, false, n.unknown)
				}
			}
		case s.UUID.Equal(ble.MustParse(SERVICE_USER_INPUT)):
			for _, c := range s.Characteristics {

				switch {

				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_CLICK)):
					n.client.Subscribe(c, false, n.click)
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_ROTATE)):
					n.client.Subscribe(c, false, n.rotate)
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_SWIPE)):
					n.client.Subscribe(c, false, n.swipe)
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_FLY)):
					n.client.Subscribe(c, false, n.fly)
				default:
					logger.Warn("Unknown input characteristik", "uuid", c.UUID.String())
					n.client.Subscribe(c, false, n.unknown)

				}
			}
		case s.UUID.Equal(ble.MustParse(SERVICE_LED_MATRIX)):
			for _, c := range s.Characteristics {
				logger.Info("Led found")
				n.led = c
			}
		default:
			logger.Warn("Unknown service %s", "uuid", s.UUID.String())
		}
	}
	return nil
}

func (n *Nuimo) Disconnect() error {
	logger.Warn("Nuimo connection closed")
	close(n.events)
	return n.client.CancelConnection()
}

func (n *Nuimo) battery(req []byte) {
	uval, _ := binary.Uvarint(req)
	level := int64(uval)
	n.send(Event{Key: "battery", Raw: req, Value: level})
}
func (n *Nuimo) info(req []byte) {
	logger.Info("Info: " + string(req))
}

func (n *Nuimo) click(req []byte) {
	uval, _ := binary.Uvarint(req)
	dir := int64(uval)
	switch dir {
	case CLICK_DOWN:
		n.send(Event{Key: "press", Raw: req})
	case CLICK_UP:
		n.send(Event{Key: "release", Raw: req})
	}
}

func (n *Nuimo) rotate(req []byte) {
	uval := binary.LittleEndian.Uint16(req)
	val := int64(int16(uval))
	n.send(Event{Key: "rotate", Raw: req, Value: val})
}
func (n *Nuimo) swipe(req []byte) {
	uval, _ := binary.Uvarint(req)
	dir := int64(uval)
	n.send(Event{Key: "swipe", Raw: req, Value: dir})

	switch dir {
	case DIR_LEFT:
		n.send(Event{Key: "swipe_left", Raw: req})
	case DIR_RIGHT:
		n.send(Event{Key: "swipe_right", Raw: req})
	case DIR_UP:
		n.send(Event{Key: "swipe_up", Raw: req})
	case DIR_DOWN:
		n.send(Event{Key: "swipe_down", Raw: req})
	}
}
func (n *Nuimo) fly(req []byte) {
	uval, _ := binary.Uvarint(req[0:1])
	dir := int(uval)
	uval, _ = binary.Uvarint(req[2:])
	distance := int64(uval)

	switch dir {
	case DIR_LEFT:
		n.send(Event{Key: "fly_left", Raw: req, Value: distance})
	case DIR_RIGHT:
		n.send(Event{Key: "fly_right", Raw: req, Value: distance})
	case DIR_BACKWARDS:
		n.send(Event{Key: "fly_backwards", Raw: req, Value: distance})
	case DIR_TOWARDS:
		n.send(Event{Key: "fly_towards", Raw: req, Value: distance})
	case DIR_UPDOWN:
		n.send(Event{Key: "fly_updown", Raw: req, Value: distance})
	}
}
func (n *Nuimo) unknown(req []byte) {
	n.send(Event{Key: "unknown", Raw: req})
}

// make sure missing event sinks don't block the client
func (n *Nuimo) send(e Event) {
	go func() { n.events <- e }()
}
<|endoftext|>


<commit_before>// A Go OAuth library, mainly created to interact with Twitter.
//
// Does header-based OAuth over HTTP or HTTPS.
package oauth

import (
	"crypto/hmac"
	"encoding/base64"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"sort"
	"strconv"
	"time"
)

// Supported oauth version (currently the only legal value):
const OAUTH_VERSION = "1.0"

// Supported signature methods:
const (
	HMAC_SHA1 = "HMAC-SHA1"
)

// Request types:
const (
	TempCredentialReq = iota
	OwnerAuthorization
	TokenReq
)

type OAuth struct {
	ConsumerKey     string
	ConsumerSecret  string
	SignatureMethod string

	Callback string

	RequestTokenURL string
	OwnerAuthURL    string
	AccessTokenURL  string

	// NOT initialized.
	RequestTokenParams map[string]string

	requestToken  string
	requestSecret string

	userName     string
	userId       uint
	accessToken  string
	accessSecret string
}

// An empty map[string]string.
// Caters to laziness when no params are given.
var None map[string]string

func (o *OAuth) Authorized() bool {
	if o.accessToken != "" && o.accessSecret != "" {
		return true
	}
	return false
}

// Returns the user id, if any.
//
// Does not return any dance errors, because that would just be
// obnoxious.  Check for authorization with Authorized().
func (o *OAuth) UserID() uint {
	return o.userId
}

// Returns the username, if any.
//
// Does not return any dance errors.  Check for authorization with
// Authorized().
func (o *OAuth) UserName() string {
	return o.userName
}

// Initiates the OAuth dance.
func (o *OAuth) GetRequestToken() (err error) {
	oParams := o.params()
	oParams["oauth_callback"] = o.Callback

	allParams := mergeParams(oParams, o.RequestTokenParams)

	resp, err := o.makeRequest("POST", o.RequestTokenURL, allParams, None)
	if err != nil {
		return
	}
	err = o.parseResponse(resp.StatusCode, resp.Body, TempCredentialReq)
	return
}

// Makes an HTTP request, handling all the repetitive OAuth overhead.
func (o *OAuth) makeRequest(method, url string, oParams map[string]string, params map[string]string) (resp *http.Response, err error) {
	escapeParams(oParams)
	escapeParams(params)

	allParams := mergeParams(oParams, params)
	signature, err := o.sign(baseString(method, url, allParams))
	if err != nil {
		return
	}

	oParams["oauth_signature"] = PercentEncode(signature)

	switch method {
	case "POST":
		resp, err = post(addQueryParams(url, params), oParams)
	case "GET":
		resp, err = get(addQueryParams(url, params), oParams)
	default:
		return nil, &implementationError{
			What:  fmt.Sprintf("HTTP method (%s)", method),
			Where: "OAuth\xb7makeRequest()",
		}
	}
	return
}

// The URL the user needs to visit to grant authorization.
// Call after GetRequestToken().
func (o *OAuth) AuthorizationURL() (string, error) {
	if o.requestToken == "" || o.requestSecret == "" {
		return "", &danceError{
			What:  "attempt to get authorization without credentials",
			Where: "OAuth\xb7AuthorizationURL()",
		}
	}

	url := o.OwnerAuthURL + "?oauth_token=" + o.requestToken
	return url, nil
}

// Performs the final step in the dance: getting the access token.
//
// Call this after GetRequestToken() and getting user verification.
func (o *OAuth) GetAccessToken(verifier string) (err error) {
	if o.requestToken == "" || o.requestSecret == "" {
		return &danceError{
			What:  "Temporary credentials not avaiable",
			Where: "OAuth\xb7GetAccessToken()",
		}
	}

	params := o.params()
	params["oauth_token"] = o.requestToken
	params["oauth_verifier"] = verifier
	resp, err := o.makeRequest("POST", o.AccessTokenURL, params, None)
	if err != nil {
		return
	}

	return o.parseResponse(resp.StatusCode, resp.Body, TokenReq)
}

// Parses a response for the OAuth dance and sets the appropriate fields
// in o for the request type.
func (o *OAuth) parseResponse(status int, body io.Reader, requestType int) error {
	//dump, _ := http.DumpResponse(resp, true)
	//fmt.Fprintf(os.Stderr, "%s\n", dump)
	r := bodyString(body)

	if status == 401 {
		return &danceError{
			What:  r,
			Where: fmt.Sprintf("parseResponse(requestType=%d)", requestType),
		}
	}

	params := parseParams(r)

	switch requestType {
	case TempCredentialReq:
		o.requestToken = params["oauth_token"]
		o.requestSecret = params["oauth_token_secret"]
		if confirmed, ok := params["oauth_callback_confirmed"]; !ok ||
			confirmed != "true" {
			return &callbackError{o.Callback}
		}
	case TokenReq:
		o.accessToken = params["oauth_token"]
		o.accessSecret = params["oauth_token_secret"]
		n, _ := strconv.ParseUint(params["user_id"], 10, 0)
		o.userId = uint(n)
		o.userName = params["screen_name"]
	default:
		return &implementationError{
			What:  "requestType=" + strconv.Itoa(requestType),
			Where: "OAuth\xb7parseResponse()",
		}
	}
	return nil
}

func (o *OAuth) params() (p map[string]string) {
	p = make(map[string]string)
	p["oauth_consumer_key"] = o.ConsumerKey
	p["oauth_signature_method"] = o.SignatureMethod
	p["oauth_timestamp"] = timestamp()
	p["oauth_nonce"] = nonce()
	p["oauth_version"] = OAUTH_VERSION
	if o.Authorized() {
		p["oauth_token"] = o.accessToken
	}
	return
}

// The base string used to compute signatures.
//
// Pass in all parameters, (query params, oauth params, post body).
func baseString(method, url string, params map[string]string) string {
	str := method + "&"
	str += PercentEncode(url)

	keys := make([]string, len(params))
	i := 0
	for k, _ := range params {
		keys[i] = k
		i++
	}

	sort.Strings(keys)
	first := true
	for _, k := range keys {
		if first {
			str += "&"
			first = false
		} else {
			str += "%26"
		}
		str += PercentEncode(k) + "%3D"
		str += PercentEncode(params[k])
	}
	return str
}

// For oauth_nonce (if that wasn't obvious).
func nonce() string {
	return strconv.FormatInt(rand.Int63(), 10)
}

// This could probably seem like less of a hack...
func (o *OAuth) signingKey() string {
	key := o.ConsumerSecret + "&"
	if o.accessSecret != "" {
		key += o.accessSecret
	} else if o.requestSecret != "" {
		key += o.requestSecret
	}
	return key
}

func (o *OAuth) sign(request string) (string, error) {
	key := o.signingKey()
	switch o.SignatureMethod {
	case HMAC_SHA1:
		hash := hmac.NewSHA1([]byte(key))
		hash.Write([]byte(request))
		signature := hash.Sum(nil)
		digest := make([]byte, base64.StdEncoding.EncodedLen(len(signature)))
		base64.StdEncoding.Encode(digest, signature)
		return string(digest), nil
	}
	return "", &implementationError{
		What:  fmt.Sprintf("Unknown signature method (%d)", o.SignatureMethod),
		Where: "OAuth\xb7sign",
	}
}

func timestamp() string {
	return strconv.FormatInt(time.Now().Unix(), 10)
}

func (o *OAuth) Post(url string, params map[string]string) (r *http.Response, err error) {
	if !o.Authorized() {
		return nil, &danceError{
			What:  "Not authorized",
			Where: "OAuth\xb7PostParams()",
		}
	}

	oParams := o.params()
	r, err = o.makeRequest("POST", url, oParams, params)
	return
}

func (o *OAuth) Get(url string, params map[string]string) (r *http.Response, err error) {
	if !o.Authorized() {
		return nil, &danceError{
			What:  "Not authorized",
			Where: "OAuth\xb7PostParams()",
		}
	}

	oParams := o.params()
	r, err = o.makeRequest("GET", url, oParams, params)
	return
}
<commit_msg>Weekly update 2012-02-07 -- new SHA1 API<commit_after>// A Go OAuth library, mainly created to interact with Twitter.
//
// Does header-based OAuth over HTTP or HTTPS.
package oauth

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"sort"
	"strconv"
	"time"
)

// Supported oauth version (currently the only legal value):
const OAUTH_VERSION = "1.0"

// Supported signature methods:
const (
	HMAC_SHA1 = "HMAC-SHA1"
)

// Request types:
const (
	TempCredentialReq = iota
	OwnerAuthorization
	TokenReq
)

type OAuth struct {
	ConsumerKey     string
	ConsumerSecret  string
	SignatureMethod string

	Callback string

	RequestTokenURL string
	OwnerAuthURL    string
	AccessTokenURL  string

	// NOT initialized.
	RequestTokenParams map[string]string

	requestToken  string
	requestSecret string

	userName     string
	userId       uint
	accessToken  string
	accessSecret string
}

// An empty map[string]string.
// Caters to laziness when no params are given.
var None map[string]string

func (o *OAuth) Authorized() bool {
	if o.accessToken != "" && o.accessSecret != "" {
		return true
	}
	return false
}

// Returns the user id, if any.
//
// Does not return any dance errors, because that would just be
// obnoxious.  Check for authorization with Authorized().
func (o *OAuth) UserID() uint {
	return o.userId
}

// Returns the username, if any.
//
// Does not return any dance errors.  Check for authorization with
// Authorized().
func (o *OAuth) UserName() string {
	return o.userName
}

// Initiates the OAuth dance.
func (o *OAuth) GetRequestToken() (err error) {
	oParams := o.params()
	oParams["oauth_callback"] = o.Callback

	allParams := mergeParams(oParams, o.RequestTokenParams)

	resp, err := o.makeRequest("POST", o.RequestTokenURL, allParams, None)
	if err != nil {
		return
	}
	err = o.parseResponse(resp.StatusCode, resp.Body, TempCredentialReq)
	return
}

// Makes an HTTP request, handling all the repetitive OAuth overhead.
func (o *OAuth) makeRequest(method, url string, oParams map[string]string, params map[string]string) (resp *http.Response, err error) {
	escapeParams(oParams)
	escapeParams(params)

	allParams := mergeParams(oParams, params)
	signature, err := o.sign(baseString(method, url, allParams))
	if err != nil {
		return
	}

	oParams["oauth_signature"] = PercentEncode(signature)

	switch method {
	case "POST":
		resp, err = post(addQueryParams(url, params), oParams)
	case "GET":
		resp, err = get(addQueryParams(url, params), oParams)
	default:
		return nil, &implementationError{
			What:  fmt.Sprintf("HTTP method (%s)", method),
			Where: "OAuth\xb7makeRequest()",
		}
	}
	return
}

// The URL the user needs to visit to grant authorization.
// Call after GetRequestToken().
func (o *OAuth) AuthorizationURL() (string, error) {
	if o.requestToken == "" || o.requestSecret == "" {
		return "", &danceError{
			What:  "attempt to get authorization without credentials",
			Where: "OAuth\xb7AuthorizationURL()",
		}
	}

	url := o.OwnerAuthURL + "?oauth_token=" + o.requestToken
	return url, nil
}

// Performs the final step in the dance: getting the access token.
//
// Call this after GetRequestToken() and getting user verification.
func (o *OAuth) GetAccessToken(verifier string) (err error) {
	if o.requestToken == "" || o.requestSecret == "" {
		return &danceError{
			What:  "Temporary credentials not avaiable",
			Where: "OAuth\xb7GetAccessToken()",
		}
	}

	params := o.params()
	params["oauth_token"] = o.requestToken
	params["oauth_verifier"] = verifier
	resp, err := o.makeRequest("POST", o.AccessTokenURL, params, None)
	if err != nil {
		return
	}

	return o.parseResponse(resp.StatusCode, resp.Body, TokenReq)
}

// Parses a response for the OAuth dance and sets the appropriate fields
// in o for the request type.
func (o *OAuth) parseResponse(status int, body io.Reader, requestType int) error {
	//dump, _ := http.DumpResponse(resp, true)
	//fmt.Fprintf(os.Stderr, "%s\n", dump)
	r := bodyString(body)

	if status == 401 {
		return &danceError{
			What:  r,
			Where: fmt.Sprintf("parseResponse(requestType=%d)", requestType),
		}
	}

	params := parseParams(r)

	switch requestType {
	case TempCredentialReq:
		o.requestToken = params["oauth_token"]
		o.requestSecret = params["oauth_token_secret"]
		if confirmed, ok := params["oauth_callback_confirmed"]; !ok ||
			confirmed != "true" {
			return &callbackError{o.Callback}
		}
	case TokenReq:
		o.accessToken = params["oauth_token"]
		o.accessSecret = params["oauth_token_secret"]
		n, _ := strconv.ParseUint(params["user_id"], 10, 0)
		o.userId = uint(n)
		o.userName = params["screen_name"]
	default:
		return &implementationError{
			What:  "requestType=" + strconv.Itoa(requestType),
			Where: "OAuth\xb7parseResponse()",
		}
	}
	return nil
}

func (o *OAuth) params() (p map[string]string) {
	p = make(map[string]string)
	p["oauth_consumer_key"] = o.ConsumerKey
	p["oauth_signature_method"] = o.SignatureMethod
	p["oauth_timestamp"] = timestamp()
	p["oauth_nonce"] = nonce()
	p["oauth_version"] = OAUTH_VERSION
	if o.Authorized() {
		p["oauth_token"] = o.accessToken
	}
	return
}

// The base string used to compute signatures.
//
// Pass in all parameters, (query params, oauth params, post body).
func baseString(method, url string, params map[string]string) string {
	str := method + "&"
	str += PercentEncode(url)

	keys := make([]string, len(params))
	i := 0
	for k, _ := range params {
		keys[i] = k
		i++
	}

	sort.Strings(keys)
	first := true
	for _, k := range keys {
		if first {
			str += "&"
			first = false
		} else {
			str += "%26"
		}
		str += PercentEncode(k) + "%3D"
		str += PercentEncode(params[k])
	}
	return str
}

// For oauth_nonce (if that wasn't obvious).
func nonce() string {
	return strconv.FormatInt(rand.Int63(), 10)
}

// This could probably seem like less of a hack...
func (o *OAuth) signingKey() string {
	key := o.ConsumerSecret + "&"
	if o.accessSecret != "" {
		key += o.accessSecret
	} else if o.requestSecret != "" {
		key += o.requestSecret
	}
	return key
}

func (o *OAuth) sign(request string) (string, error) {
	key := o.signingKey()
	switch o.SignatureMethod {
	case HMAC_SHA1:
		hash := hmac.New(sha1.New, []byte(key))
		hash.Write([]byte(request))
		signature := hash.Sum(nil)
		digest := make([]byte, base64.StdEncoding.EncodedLen(len(signature)))
		base64.StdEncoding.Encode(digest, signature)
		return string(digest), nil
	}
	return "", &implementationError{
		What:  fmt.Sprintf("Unknown signature method (%d)", o.SignatureMethod),
		Where: "OAuth\xb7sign",
	}
}

func timestamp() string {
	return strconv.FormatInt(time.Now().Unix(), 10)
}

func (o *OAuth) Post(url string, params map[string]string) (r *http.Response, err error) {
	if !o.Authorized() {
		return nil, &danceError{
			What:  "Not authorized",
			Where: "OAuth\xb7PostParams()",
		}
	}

	oParams := o.params()
	r, err = o.makeRequest("POST", url, oParams, params)
	return
}

func (o *OAuth) Get(url string, params map[string]string) (r *http.Response, err error) {
	if !o.Authorized() {
		return nil, &danceError{
			What:  "Not authorized",
			Where: "OAuth\xb7PostParams()",
		}
	}

	oParams := o.params()
	r, err = o.makeRequest("GET", url, oParams, params)
	return
}
<|endoftext|>