<commit_before>// editor.go

// part of the pat package
package editor

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
)

var LogS func(string)

// One per file.
type Editor struct {
	file []byte
	dot  [][]int
}

func (ed *Editor) nthLine(n int) (int, bool) {
	var newlines, index int
	var r byte
	for index, r = range ed.file {
		if newlines == n {
			return index, true
		}
		if r == '\n' {
			newlines++
		}
	}
	return index, false
}

func (ed *Editor) Highlights() [][]int {
	return ed.dot
}

func apply(f func([]int) [][]int, ints [][]int) [][]int {
	outScopes := make([][]int, 0)
	for _, scope := range ints {
		outScopes = append(outScopes, f(scope)...)
	}
	return outScopes
}

func (ed *Editor) multiLineSelect(l1, l2 int) error {
	index1, finished := ed.nthLine(l1)
	if !finished {
		return errors.New(fmt.Sprintf("No such line %d.", l1))
	}
	index2, _ := ed.nthLine(l2)
	ed.dot = [][]int{[]int{index1, index2}}
	return nil
}

func (ed *Editor) Command(name string, args []string) error {
	switch name {
	case "line":
		n, err := strconv.Atoi(args[0])
		if err != nil {
			return err
		}
		ed.multiLineSelect(n, n+1)
	case ",":
		var err error
		var n1, n2 int
		if args[0] == "" {
			n1 = 0
		} else {
			n1, err = strconv.Atoi(args[0])
			if err != nil {
				return err
			}
		}
		if args[1] == "" || args[1] == "$" {
			n2 = -1
		} else {
			n2, err = strconv.Atoi(args[1])
			if err != nil {
				return err
			}
		}
		ed.multiLineSelect(n1, n2)
	case "x":
		re, err := regexp.Compile(args[0])
		if err != nil {
			return err
		}
		ed.dot = apply(func(s []int) [][]int { return ed.xCommand(s, re) }, ed.dot)
	case "a":
		ed.dot = ed.insertCommand(ed.dot, []byte(args[0]), false)
	case "c":
		ed.dot = ed.cCommand(ed.dot, []byte(args[0]))
	case "i":
		ed.dot = ed.insertCommand(ed.dot, []byte(args[0]), true)
	case "d":
		LogS(fmt.Sprint(ed.dot))
		ed.dot = ed.dCommand(ed.dot)
		LogS(fmt.Sprint(ed.dot))
	case "g":
		re, err := regexp.Compile(args[0])
		if err != nil {
			return err
		}
		ed.dot = apply(func(s []int) [][]int { return ed.matchCommand(s, re, true) }, ed.dot)
	case "y":
		re, err := regexp.Compile(args[0])
		if err != nil {
			return err
		}
		ed.dot = apply(func(s []int) [][]int { return ed.matchCommand(s, re, false) }, ed.dot)
	}

	return nil
}

func (ed *Editor) String() string {
	return string(ed.file)
}

func NewEditor(file []byte) *Editor {
	return &Editor{file, [][]int{[]int{0, len(file)}}}
}

func (ed *Editor) xCommand(scope []int, re *regexp.Regexp) [][]int {
	scopes := re.FindAllIndex(ed.file[scope[0]:scope[1]], -1)
	for _, s := range scopes {
		s[0] += scope[0]
		s[1] += scope[0]
	}
	return scopes
}

func (ed *Editor) matchCommand(scope []int, re *regexp.Regexp, keep bool) [][]int {
	// Strange xor simplification
	if re.Match(ed.file[scope[0]:scope[1]]) == keep {
		return [][]int{scope}
	}
	return [][]int{}
}

// Assumes increasing indicies
// func (ed *Editor) insertCommand(scopes [][]int, addition []byte, beginning bool) [][]int {
// 	var offset, off, index int
// 	outScopes := make([][]int, 0)
// 	for _, scope := range scopes {
// 		off = len(addition)
// 		if beginning {
// 			index = scope[0] + offset
// 		} else {
// 			index = scope[1] + offset
// 		}
//
// 		ed.file = ed.file[:len(ed.file)+off+1]
// 		ed.file[len(ed.file)-1] = 0
// 		copy(ed.file[index+off:], ed.file[index:])
// 		copy(ed.file[index:index+off], addition)
//
// 		outScopes = append(outScopes, []int{scope[0] + offset, index + off})
//
// 		offset += off
// 	}
// 	return outScopes
// }

func (ed *Editor) insertCommand(scopes [][]int, addition []byte, beginning bool) [][]int {
	// No places do do anything, don't.
	if len(scopes) == 0 {
		return [][]int{}
	}

	var finalSum int
	addLength := len(addition)
	for range scopes {
		finalSum += addLength
	}
	new_file := make([]byte, len(ed.file)+finalSum)

	outscopes := make([][]int, len(scopes))
	var outscopeI int

	var startOrEnd int
	if beginning {
		startOrEnd = 0
	} else {
		startOrEnd = 1
	}

	var j, scopeIndex int
	currentscope := scopes[scopeIndex]
	for k := range ed.file {
		// Iterate over the scopes as we go.
		if k > currentscope[startOrEnd] {
			scopeIndex++
			if len(scopes) > scopeIndex {
				currentscope = scopes[scopeIndex]
			}
		}

		// add the insertion
		if k == currentscope[startOrEnd] {
			copy(new_file[j:j+addLength], addition)
			j += addLength

			if beginning {
				outscopes[outscopeI] = []int{j - addLength, currentscope[1] + addLength}
			} else {
				outscopes[outscopeI] = []int{currentscope[0], j}
			}
			outscopeI++
			// LogS(fmt.Sprint(outscopes))
		}

		// continue copying
		new_file[j] = ed.file[k]
		j++
	}
	ed.file = new_file
	return outscopes
}

// fast because doesn't allocate memory
func (ed *Editor) dCommand(scopes [][]int) [][]int {
	var offset, off, index int
	outScopes := make([][]int, 0)
	for _, scope := range scopes {
		off = scope[1] - scope[0]
		index = scope[0] - offset
		ed.file = append(ed.file[:index], ed.file[index+off:]...)
		offset += off
		outScopes = append(outScopes, []int{index, index})
	}
	return outScopes
}

func (ed *Editor) cCommand(scopes [][]int, addition []byte) [][]int {
	// No places do do anything, don't.
	if len(scopes) == 0 {
		return [][]int{}
	}

	var finalSum int
	addLength := len(addition)
	for _, scope := range scopes {
		finalSum += addLength - (scope[1] - scope[0])
	}
	new_file := make([]byte, len(ed.file)+finalSum)

	outscopes := make([][]int, len(scopes))
	var outscopeI int

	var j, scopeIndex int
	currentscope := scopes[scopeIndex]
	for k := 0; k < len(ed.file); k++ {
		scopediff := currentscope[1] - currentscope[0]

		// Iterate over the scopes as we go.
		if k > currentscope[1] {
			scopeIndex++
			if len(scopes) > scopeIndex {
				currentscope = scopes[scopeIndex]
			}
		}

		// add the insertion
		if k == currentscope[0] {
			copy(new_file[j:j+addLength], addition)
			j += addLength

			outscopes[outscopeI] = []int{currentscope[0], j}
			outscopeI++
			// Ignore those characters...
			k += scopediff
		}

		// continue copying
		if len(ed.file) > k && len(new_file) > j {
			new_file[j] = ed.file[k]
		}
		j++
	}
	ed.file = new_file
	return outscopes
}
<commit_msg>sensible selections with 'a' and 'i'<commit_after>// editor.go

// part of the pat package
package editor

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
)

var LogS func(string)

// One per file.
type Editor struct {
	file []byte
	dot  [][]int
}

func (ed *Editor) nthLine(n int) (int, bool) {
	var newlines, index int
	var r byte
	for index, r = range ed.file {
		if newlines == n {
			return index, true
		}
		if r == '\n' {
			newlines++
		}
	}
	return index, false
}

func (ed *Editor) Highlights() [][]int {
	return ed.dot
}

func apply(f func([]int) [][]int, ints [][]int) [][]int {
	outScopes := make([][]int, 0)
	for _, scope := range ints {
		outScopes = append(outScopes, f(scope)...)
	}
	return outScopes
}

func (ed *Editor) multiLineSelect(l1, l2 int) error {
	index1, finished := ed.nthLine(l1)
	if !finished {
		return errors.New(fmt.Sprintf("No such line %d.", l1))
	}
	index2, _ := ed.nthLine(l2)
	ed.dot = [][]int{[]int{index1, index2}}
	return nil
}

func (ed *Editor) Command(name string, args []string) error {
	switch name {
	case "line":
		n, err := strconv.Atoi(args[0])
		if err != nil {
			return err
		}
		ed.multiLineSelect(n, n+1)
	case ",":
		var err error
		var n1, n2 int
		if args[0] == "" {
			n1 = 0
		} else {
			n1, err = strconv.Atoi(args[0])
			if err != nil {
				return err
			}
		}
		if args[1] == "" || args[1] == "$" {
			n2 = -1
		} else {
			n2, err = strconv.Atoi(args[1])
			if err != nil {
				return err
			}
		}
		ed.multiLineSelect(n1, n2)
	case "x":
		re, err := regexp.Compile(args[0])
		if err != nil {
			return err
		}
		ed.dot = apply(func(s []int) [][]int { return ed.xCommand(s, re) }, ed.dot)
	case "a":
		ed.dot = ed.insertCommand(ed.dot, []byte(args[0]), false)
	case "c":
		ed.dot = ed.cCommand(ed.dot, []byte(args[0]))
	case "i":
		ed.dot = ed.insertCommand(ed.dot, []byte(args[0]), true)
	case "d":
		LogS(fmt.Sprint(ed.dot))
		ed.dot = ed.dCommand(ed.dot)
		LogS(fmt.Sprint(ed.dot))
	case "g":
		re, err := regexp.Compile(args[0])
		if err != nil {
			return err
		}
		ed.dot = apply(func(s []int) [][]int { return ed.matchCommand(s, re, true) }, ed.dot)
	case "y":
		re, err := regexp.Compile(args[0])
		if err != nil {
			return err
		}
		ed.dot = apply(func(s []int) [][]int { return ed.matchCommand(s, re, false) }, ed.dot)
	}

	return nil
}

func (ed *Editor) String() string {
	return string(ed.file)
}

func NewEditor(file []byte) *Editor {
	return &Editor{file, [][]int{[]int{0, len(file)}}}
}

func (ed *Editor) xCommand(scope []int, re *regexp.Regexp) [][]int {
	scopes := re.FindAllIndex(ed.file[scope[0]:scope[1]], -1)
	for _, s := range scopes {
		s[0] += scope[0]
		s[1] += scope[0]
	}
	return scopes
}

func (ed *Editor) matchCommand(scope []int, re *regexp.Regexp, keep bool) [][]int {
	// Strange xor simplification
	if re.Match(ed.file[scope[0]:scope[1]]) == keep {
		return [][]int{scope}
	}
	return [][]int{}
}

// Assumes increasing indicies
// func (ed *Editor) insertCommand(scopes [][]int, addition []byte, beginning bool) [][]int {
// 	var offset, off, index int
// 	outScopes := make([][]int, 0)
// 	for _, scope := range scopes {
// 		off = len(addition)
// 		if beginning {
// 			index = scope[0] + offset
// 		} else {
// 			index = scope[1] + offset
// 		}
//
// 		ed.file = ed.file[:len(ed.file)+off+1]
// 		ed.file[len(ed.file)-1] = 0
// 		copy(ed.file[index+off:], ed.file[index:])
// 		copy(ed.file[index:index+off], addition)
//
// 		outScopes = append(outScopes, []int{scope[0] + offset, index + off})
//
// 		offset += off
// 	}
// 	return outScopes
// }

func (ed *Editor) insertCommand(scopes [][]int, addition []byte, beginning bool) [][]int {
	// No places do do anything, don't.
	if len(scopes) == 0 {
		return [][]int{}
	}

	var finalSum int
	addLength := len(addition)
	for range scopes {
		finalSum += addLength
	}
	new_file := make([]byte, len(ed.file)+finalSum)

	outscopes := make([][]int, len(scopes))
	var outscopeI int

	var startOrEnd int
	if beginning {
		startOrEnd = 0
	} else {
		startOrEnd = 1
	}

	var j, scopeIndex int
	currentscope := scopes[scopeIndex]
	for k := range ed.file {
		// Iterate over the scopes as we go.
		if k > currentscope[startOrEnd] {
			scopeIndex++
			if len(scopes) > scopeIndex {
				currentscope = scopes[scopeIndex]
			}
		}

		// add the insertion
		if k == currentscope[startOrEnd] {
			copy(new_file[j:j+addLength], addition)
			j += addLength

			if beginning {
				outscopes[outscopeI] = []int{j - 1, j + addLength - 1}
			} else {
				LogS(fmt.Sprint(currentscope[0], j))
				outscopes[outscopeI] = []int{j - 1, j + addLength - 1}
			}
			outscopeI++
			// LogS(fmt.Sprint(outscopes))
		}

		// continue copying
		new_file[j] = ed.file[k]
		j++
	}
	ed.file = new_file
	return outscopes
}

// fast because doesn't allocate memory
func (ed *Editor) dCommand(scopes [][]int) [][]int {
	var offset, off, index int
	outScopes := make([][]int, 0)
	for _, scope := range scopes {
		off = scope[1] - scope[0]
		index = scope[0] - offset
		ed.file = append(ed.file[:index], ed.file[index+off:]...)
		offset += off
		outScopes = append(outScopes, []int{index, index})
	}
	return outScopes
}

func (ed *Editor) cCommand(scopes [][]int, addition []byte) [][]int {
	// No places do do anything, don't.
	if len(scopes) == 0 {
		return [][]int{}
	}

	var finalSum int
	addLength := len(addition)
	for _, scope := range scopes {
		finalSum += addLength - (scope[1] - scope[0])
	}
	new_file := make([]byte, len(ed.file)+finalSum)

	outscopes := make([][]int, len(scopes))
	var outscopeI int

	var j, scopeIndex int
	currentscope := scopes[scopeIndex]
	for k := 0; k < len(ed.file); k++ {
		scopediff := currentscope[1] - currentscope[0]

		// Iterate over the scopes as we go.
		if k > currentscope[1] {
			scopeIndex++
			if len(scopes) > scopeIndex {
				currentscope = scopes[scopeIndex]
			}
		}

		// add the insertion
		if k == currentscope[0] {
			copy(new_file[j:j+addLength], addition)
			j += addLength

			outscopes[outscopeI] = []int{currentscope[0], j}
			outscopeI++
			// Ignore those characters...
			k += scopediff
		}

		// continue copying
		if len(ed.file) > k && len(new_file) > j {
			new_file[j] = ed.file[k]
		}
		j++
	}
	ed.file = new_file
	return outscopes
}
<|endoftext|>


<commit_before>package edcore

import (
	"strings"
	"unicode/utf8"

	"github.com/elves/elvish/edit/highlight"
	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/util"
)

type placeholderRenderer string

func (lp placeholderRenderer) Render(b *ui.Buffer) {
	b.WriteString(util.TrimWcwidth(string(lp), b.Width), "")
}

type listingRenderer struct {
	lines []ui.Styled
}

func (ls listingRenderer) Render(b *ui.Buffer) {
	for i, line := range ls.lines {
		if i > 0 {
			b.Newline()
		}
		b.WriteString(util.ForceWcwidth(line.Text, b.Width), line.Styles.String())
	}
}

type listingWithScrollBarRenderer struct {
	listingRenderer
	n, low, high, height int
}

func (ls listingWithScrollBarRenderer) Render(b *ui.Buffer) {
	b1 := ui.Render(ls.listingRenderer, b.Width-1)
	b.ExtendRight(b1, 0)

	scrollbar := renderScrollbar(ls.n, ls.low, ls.high, ls.height)
	b.ExtendRight(scrollbar, b.Width-1)
}

type navRenderer struct {
	maxHeight                      int
	fwParent, fwCurrent, fwPreview int
	parent, current, preview       ui.Renderer
}

func makeNavRenderer(h int, w1, w2, w3 int, r1, r2, r3 ui.Renderer) ui.Renderer {
	return &navRenderer{h, w1, w2, w3, r1, r2, r3}
}

const navColMargin = 1

func (nr *navRenderer) Render(b *ui.Buffer) {
	wParent, wCurrent, wPreview := getNavWidths(b.Width-navColMargin*2,
		nr.fwCurrent, nr.fwPreview)

	bParent := ui.Render(nr.parent, wParent)
	b.ExtendRight(bParent, 0)

	bCurrent := ui.Render(nr.current, wCurrent)
	b.ExtendRight(bCurrent, wParent+navColMargin)

	if wPreview > 0 {
		bPreview := ui.Render(nr.preview, wPreview)
		b.ExtendRight(bPreview, wParent+wCurrent+2*navColMargin)
	}
}

// linesRenderer renders lines with a uniform style.
type linesRenderer struct {
	lines []string
	style string
}

func (nr linesRenderer) Render(b *ui.Buffer) {
	b.WriteString(strings.Join(nr.lines, "\n"), "")
}

// cmdlineRenderer renders the command line, including the prompt, the user's
// input and the rprompt.
type cmdlineRenderer struct {
	prompt  []*ui.Styled
	line    string
	styling *highlight.Styling
	dot     int
	rprompt []*ui.Styled

	hasRepl   bool
	replBegin int
	replEnd   int
	replText  string
}

func newCmdlineRenderer(p []*ui.Styled, l string, s *highlight.Styling, d int, rp []*ui.Styled) *cmdlineRenderer {
	return &cmdlineRenderer{prompt: p, line: l, styling: s, dot: d, rprompt: rp}
}

func (clr *cmdlineRenderer) setRepl(b, e int, t string) {
	clr.hasRepl = true
	clr.replBegin, clr.replEnd, clr.replText = b, e, t
}

func (clr *cmdlineRenderer) Render(b *ui.Buffer) {
	b.EagerWrap = true

	b.WriteStyleds(clr.prompt)

	// If the prompt takes less than half of a line, set the indent.
	if len(b.Lines) == 1 && b.Col*2 < b.Width {
		b.Indent = b.Col
	}

	// i keeps track of number of bytes written.
	i := 0

	applier := clr.styling.Apply()

	// nowAt is called at every rune boundary.
	nowAt := func(i int) {
		applier.At(i)
		// Replacement should be written before setting b.Dot. This way, if the
		// replacement starts right at the dot, the cursor is correctly placed
		// after the replacement.
		if clr.hasRepl && i == clr.replBegin {
			b.WriteString(clr.replText, styleForReplacement.String())
		}
		if i == clr.dot {
			b.Dot = b.Cursor()
		}
	}
	nowAt(0)

	for _, r := range clr.line {
		if clr.hasRepl && clr.replBegin <= i && i < clr.replEnd {
			// Do nothing. This part is replaced by the replacement.
		} else {
			b.Write(r, applier.Get())
		}
		i += utf8.RuneLen(r)

		nowAt(i)
	}

	// Write rprompt
	if len(clr.rprompt) > 0 {
		padding := b.Width - b.Col
		for _, s := range clr.rprompt {
			padding -= util.Wcswidth(s.Text)
		}
		if padding >= 1 {
			b.EagerWrap = false
			b.WriteSpaces(padding, "")
			b.WriteStyleds(clr.rprompt)
		}
	}
}

var logEditorRender = false

// editorRenderer renders the entire editor.
type editorRenderer struct {
	*editorState
	height  int
	bufNoti *ui.Buffer
}

func (er *editorRenderer) Render(buf *ui.Buffer) {
	height, width, es := er.height, buf.Width, er.editorState

	var bufNoti, bufLine, bufMode, bufTips, bufListing *ui.Buffer
	// butNoti
	if len(es.notifications) > 0 {
		bufNoti = ui.Render(linesRenderer{es.notifications, ""}, width)
		es.notifications = nil
	}

	// bufLine
	clr := newCmdlineRenderer(es.promptContent, es.buffer, es.styling, es.dot, es.rpromptContent)
	if repl, ok := es.mode.(replacementer); ok {
		clr.setRepl(repl.Replacement())
	}
	bufLine = ui.Render(clr, width)

	// bufMode
	bufMode = ui.Render(es.mode.ModeLine(), width)

	// bufTips
	// TODO tips is assumed to contain no newlines.
	if len(es.tips) > 0 {
		bufTips = ui.Render(linesRenderer{es.tips, styleForTip.String()}, width)
	}

	hListing := 0
	// Trim lines and determine the maximum height for bufListing
	// TODO come up with a UI to tell the user that something is not shown.
	switch {
	case height >= ui.BuffersHeight(bufNoti, bufLine, bufMode, bufTips):
		hListing = height - ui.BuffersHeight(bufLine, bufMode, bufTips)
	case height >= ui.BuffersHeight(bufNoti, bufLine, bufTips):
		bufMode = nil
	case height >= ui.BuffersHeight(bufNoti, bufLine):
		bufMode = nil
		if bufTips != nil {
			bufTips.TrimToLines(0, height-ui.BuffersHeight(bufNoti, bufLine))
		}
	case height >= ui.BuffersHeight(bufLine):
		bufTips, bufMode = nil, nil
		if bufNoti != nil {
			n := len(bufNoti.Lines)
			bufNoti.TrimToLines(n-(height-ui.BuffersHeight(bufLine)), n)
		}
	case height >= 1:
		bufNoti, bufTips, bufMode = nil, nil, nil
		// Determine a window of bufLine that has $height lines around the line
		// where the dot is currently on.
		low := bufLine.Dot.Line - height/2
		high := low + height
		if low < 0 {
			low = 0
			high = low + height
		} else if high > len(bufLine.Lines) {
			high = len(bufLine.Lines)
			low = high - height
		}
		bufLine.TrimToLines(low, high)
	default:
		// Broken terminal. Still try to render one line of bufLine.
		bufNoti, bufTips, bufMode = nil, nil, nil
		dotLine := bufLine.Dot.Line
		bufLine.TrimToLines(dotLine, dotLine+1)
	}

	// bufListing.
	if hListing > 0 {
		switch mode := es.mode.(type) {
		case listRenderer:
			bufListing = mode.ListRender(width, hListing)
		case lister:
			bufListing = ui.Render(mode.List(hListing), width)
		}
		// XXX When in completion mode, we re-render the mode line, since the
		// scrollbar in the mode line depends on completion.lastShown which is
		// only known after the listing has been rendered. Since rendering the
		// scrollbar never adds additional lines to bufMode, we may do this
		// without recalculating the layout.
		if _, ok := es.mode.(redrawModeLiner); ok {
			bufMode = ui.Render(es.mode.ModeLine(), width)
		}
	}

	if logEditorRender {
		logger.Printf("bufLine %d, bufMode %d, bufTips %d, bufListing %d",
			ui.BuffersHeight(bufLine), ui.BuffersHeight(bufMode), ui.BuffersHeight(bufTips), ui.BuffersHeight(bufListing))
	}

	// XXX
	buf.Lines = nil
	// Combine buffers (reusing bufLine)
	buf.Extend(bufLine, true)
	cursorOnModeLine := false
	if coml, ok := es.mode.(cursorOnModeLiner); ok {
		cursorOnModeLine = coml.CursorOnModeLine()
	}
	buf.Extend(bufMode, cursorOnModeLine)
	buf.Extend(bufTips, false)
	buf.Extend(bufListing, false)

	er.bufNoti = bufNoti
}
<commit_msg>edit/edcore: More log message.<commit_after>package edcore

import (
	"strings"
	"unicode/utf8"

	"github.com/elves/elvish/edit/highlight"
	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/util"
)

type placeholderRenderer string

func (lp placeholderRenderer) Render(b *ui.Buffer) {
	b.WriteString(util.TrimWcwidth(string(lp), b.Width), "")
}

type listingRenderer struct {
	lines []ui.Styled
}

func (ls listingRenderer) Render(b *ui.Buffer) {
	for i, line := range ls.lines {
		if i > 0 {
			b.Newline()
		}
		b.WriteString(util.ForceWcwidth(line.Text, b.Width), line.Styles.String())
	}
}

type listingWithScrollBarRenderer struct {
	listingRenderer
	n, low, high, height int
}

func (ls listingWithScrollBarRenderer) Render(b *ui.Buffer) {
	b1 := ui.Render(ls.listingRenderer, b.Width-1)
	b.ExtendRight(b1, 0)

	scrollbar := renderScrollbar(ls.n, ls.low, ls.high, ls.height)
	b.ExtendRight(scrollbar, b.Width-1)
}

type navRenderer struct {
	maxHeight                      int
	fwParent, fwCurrent, fwPreview int
	parent, current, preview       ui.Renderer
}

func makeNavRenderer(h int, w1, w2, w3 int, r1, r2, r3 ui.Renderer) ui.Renderer {
	return &navRenderer{h, w1, w2, w3, r1, r2, r3}
}

const navColMargin = 1

func (nr *navRenderer) Render(b *ui.Buffer) {
	wParent, wCurrent, wPreview := getNavWidths(b.Width-navColMargin*2,
		nr.fwCurrent, nr.fwPreview)

	bParent := ui.Render(nr.parent, wParent)
	b.ExtendRight(bParent, 0)

	bCurrent := ui.Render(nr.current, wCurrent)
	b.ExtendRight(bCurrent, wParent+navColMargin)

	if wPreview > 0 {
		bPreview := ui.Render(nr.preview, wPreview)
		b.ExtendRight(bPreview, wParent+wCurrent+2*navColMargin)
	}
}

// linesRenderer renders lines with a uniform style.
type linesRenderer struct {
	lines []string
	style string
}

func (nr linesRenderer) Render(b *ui.Buffer) {
	b.WriteString(strings.Join(nr.lines, "\n"), "")
}

// cmdlineRenderer renders the command line, including the prompt, the user's
// input and the rprompt.
type cmdlineRenderer struct {
	prompt  []*ui.Styled
	line    string
	styling *highlight.Styling
	dot     int
	rprompt []*ui.Styled

	hasRepl   bool
	replBegin int
	replEnd   int
	replText  string
}

func newCmdlineRenderer(p []*ui.Styled, l string, s *highlight.Styling, d int, rp []*ui.Styled) *cmdlineRenderer {
	return &cmdlineRenderer{prompt: p, line: l, styling: s, dot: d, rprompt: rp}
}

func (clr *cmdlineRenderer) setRepl(b, e int, t string) {
	clr.hasRepl = true
	clr.replBegin, clr.replEnd, clr.replText = b, e, t
}

func (clr *cmdlineRenderer) Render(b *ui.Buffer) {
	b.EagerWrap = true

	b.WriteStyleds(clr.prompt)

	// If the prompt takes less than half of a line, set the indent.
	if len(b.Lines) == 1 && b.Col*2 < b.Width {
		b.Indent = b.Col
	}

	// i keeps track of number of bytes written.
	i := 0

	applier := clr.styling.Apply()

	// nowAt is called at every rune boundary.
	nowAt := func(i int) {
		applier.At(i)
		// Replacement should be written before setting b.Dot. This way, if the
		// replacement starts right at the dot, the cursor is correctly placed
		// after the replacement.
		if clr.hasRepl && i == clr.replBegin {
			b.WriteString(clr.replText, styleForReplacement.String())
		}
		if i == clr.dot {
			b.Dot = b.Cursor()
		}
	}
	nowAt(0)

	for _, r := range clr.line {
		if clr.hasRepl && clr.replBegin <= i && i < clr.replEnd {
			// Do nothing. This part is replaced by the replacement.
		} else {
			b.Write(r, applier.Get())
		}
		i += utf8.RuneLen(r)

		nowAt(i)
	}

	// Write rprompt
	if len(clr.rprompt) > 0 {
		padding := b.Width - b.Col
		for _, s := range clr.rprompt {
			padding -= util.Wcswidth(s.Text)
		}
		if padding >= 1 {
			b.EagerWrap = false
			b.WriteSpaces(padding, "")
			b.WriteStyleds(clr.rprompt)
		}
	}
}

var logEditorRender = false

// editorRenderer renders the entire editor.
type editorRenderer struct {
	*editorState
	height  int
	bufNoti *ui.Buffer
}

func (er *editorRenderer) Render(buf *ui.Buffer) {
	height, width, es := er.height, buf.Width, er.editorState

	var bufNoti, bufLine, bufMode, bufTips, bufListing *ui.Buffer
	// butNoti
	if len(es.notifications) > 0 {
		bufNoti = ui.Render(linesRenderer{es.notifications, ""}, width)
		es.notifications = nil
	}

	// bufLine
	clr := newCmdlineRenderer(es.promptContent, es.buffer, es.styling, es.dot, es.rpromptContent)
	if repl, ok := es.mode.(replacementer); ok {
		clr.setRepl(repl.Replacement())
	}
	bufLine = ui.Render(clr, width)

	// bufMode
	bufMode = ui.Render(es.mode.ModeLine(), width)

	// bufTips
	// TODO tips is assumed to contain no newlines.
	if len(es.tips) > 0 {
		bufTips = ui.Render(linesRenderer{es.tips, styleForTip.String()}, width)
	}

	hListing := 0
	// Trim lines and determine the maximum height for bufListing
	// TODO come up with a UI to tell the user that something is not shown.
	switch {
	case height >= ui.BuffersHeight(bufNoti, bufLine, bufMode, bufTips):
		hListing = height - ui.BuffersHeight(bufLine, bufMode, bufTips)
	case height >= ui.BuffersHeight(bufNoti, bufLine, bufTips):
		bufMode = nil
	case height >= ui.BuffersHeight(bufNoti, bufLine):
		bufMode = nil
		if bufTips != nil {
			bufTips.TrimToLines(0, height-ui.BuffersHeight(bufNoti, bufLine))
		}
	case height >= ui.BuffersHeight(bufLine):
		bufTips, bufMode = nil, nil
		if bufNoti != nil {
			n := len(bufNoti.Lines)
			bufNoti.TrimToLines(n-(height-ui.BuffersHeight(bufLine)), n)
		}
	case height >= 1:
		bufNoti, bufTips, bufMode = nil, nil, nil
		// Determine a window of bufLine that has $height lines around the line
		// where the dot is currently on.
		low := bufLine.Dot.Line - height/2
		high := low + height
		if low < 0 {
			low = 0
			high = low + height
		} else if high > len(bufLine.Lines) {
			high = len(bufLine.Lines)
			low = high - height
		}
		bufLine.TrimToLines(low, high)
	default:
		// Broken terminal. Still try to render one line of bufLine.
		bufNoti, bufTips, bufMode = nil, nil, nil
		dotLine := bufLine.Dot.Line
		bufLine.TrimToLines(dotLine, dotLine+1)
	}

	// bufListing.
	if hListing > 0 {
		switch mode := es.mode.(type) {
		case listRenderer:
			bufListing = mode.ListRender(width, hListing)
		case lister:
			bufListing = ui.Render(mode.List(hListing), width)
		}
		// XXX When in completion mode, we re-render the mode line, since the
		// scrollbar in the mode line depends on completion.lastShown which is
		// only known after the listing has been rendered. Since rendering the
		// scrollbar never adds additional lines to bufMode, we may do this
		// without recalculating the layout.
		if _, ok := es.mode.(redrawModeLiner); ok {
			bufMode = ui.Render(es.mode.ModeLine(), width)
		}
	}

	if logEditorRender {
		logger.Printf("bufNoti %d, bufLine %d, bufMode %d, bufTips %d, "+
			"hListing %d, bufListing %d",
			ui.BuffersHeight(bufNoti), ui.BuffersHeight(bufLine),
			ui.BuffersHeight(bufMode), ui.BuffersHeight(bufTips),
			hListing, ui.BuffersHeight(bufListing))
	}

	// XXX
	buf.Lines = nil
	// Combine buffers (reusing bufLine)
	buf.Extend(bufLine, true)
	cursorOnModeLine := false
	if coml, ok := es.mode.(cursorOnModeLiner); ok {
		cursorOnModeLine = coml.CursorOnModeLine()
	}
	buf.Extend(bufMode, cursorOnModeLine)
	buf.Extend(bufTips, false)
	buf.Extend(bufListing, false)

	er.bufNoti = bufNoti
}
<|endoftext|>


<commit_before>package sirius

import "time"

type Extension struct {
	Id        int
	Name      string
	CreatedAt time.Time
	Users     []User `pg:"many2many:user_extensions"`
}
<commit_msg>Remove unused model<commit_after><|endoftext|>


<commit_before>// Package q contains a list of Matchers used to compare struct fields with values
package q

import (
	"go/token"
	"reflect"
)

// A Matcher is used to test against a record to see if it matches.
type Matcher interface {
	// Match is used to test the criteria against a structure.
	Match(interface{}) (bool, error)
}

// A ValueMatcher is used to test against a reflect.Value.
type ValueMatcher interface {
	// MatchValue tests if the given reflect.Value matches.
	// It is useful when the reflect.Value of an object already exists.
	MatchValue(*reflect.Value) (bool, error)
}

type cmp struct {
	value interface{}
	token token.Token
}

func (c *cmp) MatchField(v interface{}) (bool, error) {
	return compare(v, c.value, c.token), nil
}

type trueMatcher struct{}

func (*trueMatcher) Match(i interface{}) (bool, error) {
	return true, nil
}

func (*trueMatcher) MatchValue(v *reflect.Value) (bool, error) {
	return true, nil
}

type or struct {
	children []Matcher
}

func (c *or) Match(i interface{}) (bool, error) {
	v := reflect.Indirect(reflect.ValueOf(i))
	return c.MatchValue(&v)
}

func (c *or) MatchValue(v *reflect.Value) (bool, error) {
	for _, matcher := range c.children {
		if vm, ok := matcher.(ValueMatcher); ok {
			ok, err := vm.MatchValue(v)
			if err != nil {
				return false, err
			}
			if ok {
				return true, nil
			}
			continue
		}

		ok, err := matcher.Match(v.Interface())
		if err != nil {
			return false, err
		}
		if ok {
			return true, nil
		}
	}

	return false, nil
}

type and struct {
	children []Matcher
}

func (c *and) Match(i interface{}) (bool, error) {
	v := reflect.Indirect(reflect.ValueOf(i))
	return c.MatchValue(&v)
}

func (c *and) MatchValue(v *reflect.Value) (bool, error) {
	for _, matcher := range c.children {
		if vm, ok := matcher.(ValueMatcher); ok {
			ok, err := vm.MatchValue(v)
			if err != nil {
				return false, err
			}
			if !ok {
				return false, nil
			}
			continue
		}

		ok, err := matcher.Match(v.Interface())
		if err != nil {
			return false, err
		}
		if !ok {
			return false, nil
		}
	}

	return true, nil
}

type strictEq struct {
	field string
	value interface{}
}

func (s *strictEq) MatchField(v interface{}) (bool, error) {
	return reflect.DeepEqual(v, s.value), nil
}

type in struct {
	list interface{}
}

func (i *in) MatchField(v interface{}) (bool, error) {
	ref := reflect.ValueOf(i.list)
	if ref.Kind() != reflect.Slice {
		return false, nil
	}

	c := cmp{
		token: token.EQL,
	}

	for i := 0; i < ref.Len(); i++ {
		c.value = ref.Index(i).Interface()
		ok, err := c.MatchField(v)
		if err != nil {
			return false, err
		}
		if ok {
			return true, nil
		}
	}

	return false, nil
}

type not struct {
	children []Matcher
}

func (n *not) Match(i interface{}) (bool, error) {
	v := reflect.Indirect(reflect.ValueOf(i))
	return n.MatchValue(&v)
}

func (n *not) MatchValue(v *reflect.Value) (bool, error) {
	var err error

	for _, matcher := range n.children {
		vm, ok := matcher.(ValueMatcher)
		if ok {
			ok, err = vm.MatchValue(v)
		} else {
			ok, err = matcher.Match(v.Interface())
		}
		if err != nil {
			return false, err
		}
		if ok {
			return false, nil
		}
	}

	return true, nil
}

// Eq matcher, checks if the given field is equal to the given value
func Eq(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &cmp{value: v, token: token.EQL})
}

// StrictEq matcher, checks if the given field is deeply equal to the given value
func StrictEq(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &strictEq{value: v})
}

// Gt matcher, checks if the given field is greater than the given value
func Gt(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &cmp{value: v, token: token.GTR})
}

// Gte matcher, checks if the given field is greater than or equal to the given value
func Gte(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &cmp{value: v, token: token.GEQ})
}

// Lt matcher, checks if the given field is lesser than the given value
func Lt(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &cmp{value: v, token: token.LSS})
}

// Lte matcher, checks if the given field is lesser than or equal to the given value
func Lte(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &cmp{value: v, token: token.LEQ})
}

// In matcher, checks if the given field matches one of the value of the given slice.
// v must be a slice.
func In(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &in{list: v})
}

// True matcher, always returns true
func True() Matcher { return &trueMatcher{} }

// Or matcher, checks if at least one of the given matchers matches the record
func Or(matchers ...Matcher) Matcher { return &or{children: matchers} }

// And matcher, checks if all of the given matchers matches the record
func And(matchers ...Matcher) Matcher { return &and{children: matchers} }

// Not matcher, checks if all of the given matchers return false
func Not(matchers ...Matcher) Matcher { return &not{children: matchers} }
<commit_msg>fields matching (EqF, LtF ...)<commit_after>// Package q contains a list of Matchers used to compare struct fields with values
package q

import (
	"go/token"
	"reflect"
)

// A Matcher is used to test against a record to see if it matches.
type Matcher interface {
	// Match is used to test the criteria against a structure.
	Match(interface{}) (bool, error)
}

// A ValueMatcher is used to test against a reflect.Value.
type ValueMatcher interface {
	// MatchValue tests if the given reflect.Value matches.
	// It is useful when the reflect.Value of an object already exists.
	MatchValue(*reflect.Value) (bool, error)
}

type cmp struct {
	value interface{}
	token token.Token
}

func (c *cmp) MatchField(v interface{}) (bool, error) {
	return compare(v, c.value, c.token), nil
}

type trueMatcher struct{}

func (*trueMatcher) Match(i interface{}) (bool, error) {
	return true, nil
}

func (*trueMatcher) MatchValue(v *reflect.Value) (bool, error) {
	return true, nil
}

type or struct {
	children []Matcher
}

func (c *or) Match(i interface{}) (bool, error) {
	v := reflect.Indirect(reflect.ValueOf(i))
	return c.MatchValue(&v)
}

func (c *or) MatchValue(v *reflect.Value) (bool, error) {
	for _, matcher := range c.children {
		if vm, ok := matcher.(ValueMatcher); ok {
			ok, err := vm.MatchValue(v)
			if err != nil {
				return false, err
			}
			if ok {
				return true, nil
			}
			continue
		}

		ok, err := matcher.Match(v.Interface())
		if err != nil {
			return false, err
		}
		if ok {
			return true, nil
		}
	}

	return false, nil
}

type and struct {
	children []Matcher
}

func (c *and) Match(i interface{}) (bool, error) {
	v := reflect.Indirect(reflect.ValueOf(i))
	return c.MatchValue(&v)
}

func (c *and) MatchValue(v *reflect.Value) (bool, error) {
	for _, matcher := range c.children {
		if vm, ok := matcher.(ValueMatcher); ok {
			ok, err := vm.MatchValue(v)
			if err != nil {
				return false, err
			}
			if !ok {
				return false, nil
			}
			continue
		}

		ok, err := matcher.Match(v.Interface())
		if err != nil {
			return false, err
		}
		if !ok {
			return false, nil
		}
	}

	return true, nil
}

type strictEq struct {
	field string
	value interface{}
}

func (s *strictEq) MatchField(v interface{}) (bool, error) {
	return reflect.DeepEqual(v, s.value), nil
}

type in struct {
	list interface{}
}

func (i *in) MatchField(v interface{}) (bool, error) {
	ref := reflect.ValueOf(i.list)
	if ref.Kind() != reflect.Slice {
		return false, nil
	}

	c := cmp{
		token: token.EQL,
	}

	for i := 0; i < ref.Len(); i++ {
		c.value = ref.Index(i).Interface()
		ok, err := c.MatchField(v)
		if err != nil {
			return false, err
		}
		if ok {
			return true, nil
		}
	}

	return false, nil
}

type not struct {
	children []Matcher
}

func (n *not) Match(i interface{}) (bool, error) {
	v := reflect.Indirect(reflect.ValueOf(i))
	return n.MatchValue(&v)
}

func (n *not) MatchValue(v *reflect.Value) (bool, error) {
	var err error

	for _, matcher := range n.children {
		vm, ok := matcher.(ValueMatcher)
		if ok {
			ok, err = vm.MatchValue(v)
		} else {
			ok, err = matcher.Match(v.Interface())
		}
		if err != nil {
			return false, err
		}
		if ok {
			return false, nil
		}
	}

	return true, nil
}

// Eq matcher, checks if the given field is equal to the given value
func Eq(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &cmp{value: v, token: token.EQL})
}

// EqF matcher, checks if the given field is equal to the given field
func EqF(field1, field2 string) Matcher {
	return NewField2FieldMatcher(field1, field2, token.EQL)
}

// StrictEq matcher, checks if the given field is deeply equal to the given value
func StrictEq(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &strictEq{value: v})
}

// Gt matcher, checks if the given field is greater than the given value
func Gt(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &cmp{value: v, token: token.GTR})
}

// GtF matcher, checks if the given field is greater than the given field
func GtF(field1, field2 string) Matcher {
	return NewField2FieldMatcher(field1, field2, token.GTR)
}

// Gte matcher, checks if the given field is greater than or equal to the given value
func Gte(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &cmp{value: v, token: token.GEQ})
}

// GteF matcher, checks if the given field is greater than or equal to the given field
func GteF(field1, field2 string) Matcher {
	return NewField2FieldMatcher(field1, field2, token.GEQ)
}

// Lt matcher, checks if the given field is lesser than the given value
func Lt(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &cmp{value: v, token: token.LSS})
}

// LtF matcher, checks if the given field is lesser than the given field
func LtF(field1, field2 string) Matcher {
	return NewField2FieldMatcher(field1, field2, token.LSS)
}

// Lte matcher, checks if the given field is lesser than or equal to the given value
func Lte(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &cmp{value: v, token: token.LEQ})
}

// LteF matcher, checks if the given field is lesser than or equal to the given field
func LteF(field1, field2 string) Matcher {
	return NewField2FieldMatcher(field1, field2, token.LEQ)
}

// In matcher, checks if the given field matches one of the value of the given slice.
// v must be a slice.
func In(field string, v interface{}) Matcher {
	return NewFieldMatcher(field, &in{list: v})
}

// True matcher, always returns true
func True() Matcher { return &trueMatcher{} }

// Or matcher, checks if at least one of the given matchers matches the record
func Or(matchers ...Matcher) Matcher { return &or{children: matchers} }

// And matcher, checks if all of the given matchers matches the record
func And(matchers ...Matcher) Matcher { return &and{children: matchers} }

// Not matcher, checks if all of the given matchers return false
func Not(matchers ...Matcher) Matcher { return &not{children: matchers} }
<|endoftext|>


<commit_before>package shp

import (
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"os"
	"path/filepath"
	"strings"
)

// Reader provides a interface for reading Shapefiles. Calls
// to the Next method will iterate through the objects in the
// Shapefile. After a call to Next the object will be available
// through the Shape method.
type Reader struct {
	GeometryType ShapeType
	bbox         Box
	err          error

	shp        readSeekCloser
	shape      Shape
	num        int32
	filename   string
	filelength int64

	dbf             readSeekCloser
	dbfFields       []Field
	dbfNumRecords   int32
	dbfHeaderLength int16
	dbfRecordLength int16
}

type readSeekCloser interface {
	io.Reader
	io.Seeker
	io.Closer
}

// Open opens a Shapefile for reading.
func Open(filename string) (*Reader, error) {
	ext := filepath.Ext(filename)
	if strings.ToLower(ext) != ".shp" {
		return nil, fmt.Errorf("Invalid file extension: %s", filename)
	}
	shp, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	s := &Reader{filename: strings.TrimSuffix(filename, ext), shp: shp}
	s.readHeaders()
	return s, nil
}

// BBox returns the bounding box of the shapefile.
func (r *Reader) BBox() Box {
	return r.bbox
}

// Read and parse headers in the Shapefile. This will
// fill out GeometryType, filelength and bbox.
func (r *Reader) readHeaders() {
	// don't trust the the filelength in the header
	r.filelength, _ = r.shp.Seek(0, io.SeekEnd)

	var filelength int32
	r.shp.Seek(24, 0)
	// file length
	binary.Read(r.shp, binary.BigEndian, &filelength)
	r.shp.Seek(32, 0)
	binary.Read(r.shp, binary.LittleEndian, &r.GeometryType)
	r.bbox.MinX = readFloat64(r.shp)
	r.bbox.MinY = readFloat64(r.shp)
	r.bbox.MaxX = readFloat64(r.shp)
	r.bbox.MaxY = readFloat64(r.shp)
	r.shp.Seek(100, 0)
}

func readFloat64(r io.Reader) float64 {
	var bits uint64
	binary.Read(r, binary.LittleEndian, &bits)
	return math.Float64frombits(bits)
}

// Close closes the Shapefile.
func (r *Reader) Close() error {
	if r.err == nil {
		r.err = r.shp.Close()
		if r.dbf != nil {
			r.dbf.Close()
		}
	}
	return r.err
}

// Shape returns the most recent feature that was read by
// a call to Next. It returns two values, the int is the
// object index starting from zero in the shapefile which
// can be used as row in ReadAttribute, and the Shape is the object.
func (r *Reader) Shape() (int, Shape) {
	return int(r.num) - 1, r.shape
}

// Attribute returns value of the n-th attribute of the most recent feature
// that was read by a call to Next.
func (r *Reader) Attribute(n int) string {
	return r.ReadAttribute(int(r.num)-1, n)
}

// newShape creates a new shape with a given type.
func newShape(shapetype ShapeType) (Shape, error) {
	switch shapetype {
	case NULL:
		return new(Null), nil
	case POINT:
		return new(Point), nil
	case POLYLINE:
		return new(PolyLine), nil
	case POLYGON:
		return new(Polygon), nil
	case MULTIPOINT:
		return new(MultiPoint), nil
	case POINTZ:
		return new(PointZ), nil
	case POLYLINEZ:
		return new(PolyLineZ), nil
	case POLYGONZ:
		return new(PolygonZ), nil
	case MULTIPOINTZ:
		return new(MultiPointZ), nil
	case POINTM:
		return new(PointM), nil
	case POLYLINEM:
		return new(PolyLineM), nil
	case POLYGONM:
		return new(PolygonM), nil
	case MULTIPOINTM:
		return new(MultiPointM), nil
	case MULTIPATCH:
		return new(MultiPatch), nil
	default:
		return nil, fmt.Errorf("Unsupported shape type: %v", shapetype)
	}
}

// Next reads in the next Shape in the Shapefile, which
// will then be available through the Shape method. It
// returns false when the reader has reached the end of the
// file or encounters an error.
func (r *Reader) Next() bool {
	cur, _ := r.shp.Seek(0, io.SeekCurrent)
	if cur >= r.filelength {
		return false
	}

	var size int32
	var shapetype ShapeType
	er := &errReader{Reader: r.shp}
	binary.Read(er, binary.BigEndian, &r.num)
	binary.Read(er, binary.BigEndian, &size)
	binary.Read(er, binary.LittleEndian, &shapetype)
	if er.e != nil {
		if er.e != io.EOF {
			r.err = fmt.Errorf("Error when reading metadata of next shape: %v", er.e)
		} else {
			r.err = io.EOF
		}
		return false
	}

	var err error
	r.shape, err = newShape(shapetype)
	if err != nil {
		r.err = fmt.Errorf("Error decoding shape type: %v", err)
		return false
	}
	r.shape.read(er)
	if er.e != nil {
		r.err = fmt.Errorf("Error while reading next shape: %v", er.e)
		return false
	}

	// move to next object
	r.shp.Seek(int64(size)*2+cur+8, 0)
	return true
}

// Opens DBF file using r.filename + "dbf". This method
// will parse the header and fill out all dbf* values int
// the f object.
func (r *Reader) openDbf() (err error) {
	if r.dbf != nil {
		return
	}

	r.dbf, err = os.Open(r.filename + ".dbf")
	if err != nil {
		return
	}

	// read header
	r.dbf.Seek(4, io.SeekStart)
	binary.Read(r.dbf, binary.LittleEndian, &r.dbfNumRecords)
	binary.Read(r.dbf, binary.LittleEndian, &r.dbfHeaderLength)
	binary.Read(r.dbf, binary.LittleEndian, &r.dbfRecordLength)

	r.dbf.Seek(20, io.SeekCurrent) // skip padding
	numFields := int(math.Floor(float64(r.dbfHeaderLength-33) / 32.0))
	r.dbfFields = make([]Field, numFields)
	binary.Read(r.dbf, binary.LittleEndian, &r.dbfFields)
	return
}

// Fields returns a slice of Fields that are present in the
// DBF table.
func (r *Reader) Fields() []Field {
	r.openDbf() // make sure we have dbf file to read from
	return r.dbfFields
}

// Err returns the last non-EOF error encountered.
func (r *Reader) Err() error {
	if r.err == io.EOF {
		return nil
	}
	return r.err
}

// AttributeCount returns number of records in the DBF table.
func (r *Reader) AttributeCount() int {
	r.openDbf() // make sure we have a dbf file to read from
	return int(r.dbfNumRecords)
}

// ReadAttribute returns the attribute value at row for field in
// the DBF table as a string. Both values starts at 0.
func (r *Reader) ReadAttribute(row int, field int) string {
	r.openDbf() // make sure we have a dbf file to read from
	seekTo := 1 + int64(r.dbfHeaderLength) + (int64(row) * int64(r.dbfRecordLength))
	for n := 0; n < field; n++ {
		seekTo += int64(r.dbfFields[n].Size)
	}
	r.dbf.Seek(seekTo, io.SeekStart)
	buf := make([]byte, r.dbfFields[field].Size)
	r.dbf.Read(buf)
	return strings.Trim(string(buf[:]), " ")
}
<commit_msg>Check for errors while reading header<commit_after>package shp

import (
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"os"
	"path/filepath"
	"strings"
)

// Reader provides a interface for reading Shapefiles. Calls
// to the Next method will iterate through the objects in the
// Shapefile. After a call to Next the object will be available
// through the Shape method.
type Reader struct {
	GeometryType ShapeType
	bbox         Box
	err          error

	shp        readSeekCloser
	shape      Shape
	num        int32
	filename   string
	filelength int64

	dbf             readSeekCloser
	dbfFields       []Field
	dbfNumRecords   int32
	dbfHeaderLength int16
	dbfRecordLength int16
}

type readSeekCloser interface {
	io.Reader
	io.Seeker
	io.Closer
}

// Open opens a Shapefile for reading.
func Open(filename string) (*Reader, error) {
	ext := filepath.Ext(filename)
	if strings.ToLower(ext) != ".shp" {
		return nil, fmt.Errorf("Invalid file extension: %s", filename)
	}
	shp, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	s := &Reader{filename: strings.TrimSuffix(filename, ext), shp: shp}
	return s, s.readHeaders()
}

// BBox returns the bounding box of the shapefile.
func (r *Reader) BBox() Box {
	return r.bbox
}

// Read and parse headers in the Shapefile. This will
// fill out GeometryType, filelength and bbox.
func (r *Reader) readHeaders() error {
	er := &errReader{Reader: r.shp}
	// don't trust the the filelength in the header
	r.filelength, _ = r.shp.Seek(0, io.SeekEnd)

	var filelength int32
	r.shp.Seek(24, 0)
	// file length
	binary.Read(r.shp, binary.BigEndian, &filelength)
	r.shp.Seek(32, 0)
	binary.Read(er, binary.LittleEndian, &r.GeometryType)
	r.bbox.MinX = readFloat64(er)
	r.bbox.MinY = readFloat64(er)
	r.bbox.MaxX = readFloat64(er)
	r.bbox.MaxY = readFloat64(er)
	r.shp.Seek(100, 0)
	return er.e
}

func readFloat64(r io.Reader) float64 {
	var bits uint64
	binary.Read(r, binary.LittleEndian, &bits)
	return math.Float64frombits(bits)
}

// Close closes the Shapefile.
func (r *Reader) Close() error {
	if r.err == nil {
		r.err = r.shp.Close()
		if r.dbf != nil {
			r.dbf.Close()
		}
	}
	return r.err
}

// Shape returns the most recent feature that was read by
// a call to Next. It returns two values, the int is the
// object index starting from zero in the shapefile which
// can be used as row in ReadAttribute, and the Shape is the object.
func (r *Reader) Shape() (int, Shape) {
	return int(r.num) - 1, r.shape
}

// Attribute returns value of the n-th attribute of the most recent feature
// that was read by a call to Next.
func (r *Reader) Attribute(n int) string {
	return r.ReadAttribute(int(r.num)-1, n)
}

// newShape creates a new shape with a given type.
func newShape(shapetype ShapeType) (Shape, error) {
	switch shapetype {
	case NULL:
		return new(Null), nil
	case POINT:
		return new(Point), nil
	case POLYLINE:
		return new(PolyLine), nil
	case POLYGON:
		return new(Polygon), nil
	case MULTIPOINT:
		return new(MultiPoint), nil
	case POINTZ:
		return new(PointZ), nil
	case POLYLINEZ:
		return new(PolyLineZ), nil
	case POLYGONZ:
		return new(PolygonZ), nil
	case MULTIPOINTZ:
		return new(MultiPointZ), nil
	case POINTM:
		return new(PointM), nil
	case POLYLINEM:
		return new(PolyLineM), nil
	case POLYGONM:
		return new(PolygonM), nil
	case MULTIPOINTM:
		return new(MultiPointM), nil
	case MULTIPATCH:
		return new(MultiPatch), nil
	default:
		return nil, fmt.Errorf("Unsupported shape type: %v", shapetype)
	}
}

// Next reads in the next Shape in the Shapefile, which
// will then be available through the Shape method. It
// returns false when the reader has reached the end of the
// file or encounters an error.
func (r *Reader) Next() bool {
	cur, _ := r.shp.Seek(0, io.SeekCurrent)
	if cur >= r.filelength {
		return false
	}

	var size int32
	var shapetype ShapeType
	er := &errReader{Reader: r.shp}
	binary.Read(er, binary.BigEndian, &r.num)
	binary.Read(er, binary.BigEndian, &size)
	binary.Read(er, binary.LittleEndian, &shapetype)
	if er.e != nil {
		if er.e != io.EOF {
			r.err = fmt.Errorf("Error when reading metadata of next shape: %v", er.e)
		} else {
			r.err = io.EOF
		}
		return false
	}

	var err error
	r.shape, err = newShape(shapetype)
	if err != nil {
		r.err = fmt.Errorf("Error decoding shape type: %v", err)
		return false
	}
	r.shape.read(er)
	if er.e != nil {
		r.err = fmt.Errorf("Error while reading next shape: %v", er.e)
		return false
	}

	// move to next object
	r.shp.Seek(int64(size)*2+cur+8, 0)
	return true
}

// Opens DBF file using r.filename + "dbf". This method
// will parse the header and fill out all dbf* values int
// the f object.
func (r *Reader) openDbf() (err error) {
	if r.dbf != nil {
		return
	}

	r.dbf, err = os.Open(r.filename + ".dbf")
	if err != nil {
		return
	}

	// read header
	r.dbf.Seek(4, io.SeekStart)
	binary.Read(r.dbf, binary.LittleEndian, &r.dbfNumRecords)
	binary.Read(r.dbf, binary.LittleEndian, &r.dbfHeaderLength)
	binary.Read(r.dbf, binary.LittleEndian, &r.dbfRecordLength)

	r.dbf.Seek(20, io.SeekCurrent) // skip padding
	numFields := int(math.Floor(float64(r.dbfHeaderLength-33) / 32.0))
	r.dbfFields = make([]Field, numFields)
	binary.Read(r.dbf, binary.LittleEndian, &r.dbfFields)
	return
}

// Fields returns a slice of Fields that are present in the
// DBF table.
func (r *Reader) Fields() []Field {
	r.openDbf() // make sure we have dbf file to read from
	return r.dbfFields
}

// Err returns the last non-EOF error encountered.
func (r *Reader) Err() error {
	if r.err == io.EOF {
		return nil
	}
	return r.err
}

// AttributeCount returns number of records in the DBF table.
func (r *Reader) AttributeCount() int {
	r.openDbf() // make sure we have a dbf file to read from
	return int(r.dbfNumRecords)
}

// ReadAttribute returns the attribute value at row for field in
// the DBF table as a string. Both values starts at 0.
func (r *Reader) ReadAttribute(row int, field int) string {
	r.openDbf() // make sure we have a dbf file to read from
	seekTo := 1 + int64(r.dbfHeaderLength) + (int64(row) * int64(r.dbfRecordLength))
	for n := 0; n < field; n++ {
		seekTo += int64(r.dbfFields[n].Size)
	}
	r.dbf.Seek(seekTo, io.SeekStart)
	buf := make([]byte, r.dbfFields[field].Size)
	r.dbf.Read(buf)
	return strings.Trim(string(buf[:]), " ")
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package filestorage

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"launchpad.net/juju-core/environs/storage"
	coreerrors "launchpad.net/juju-core/errors"
	"launchpad.net/juju-core/utils"
)

// fileStorageReader implements StorageReader backed
// by the local filesystem.
type fileStorageReader struct {
	path string
}

// NewFileStorageReader returns a new storage reader for
// a directory inside the local file system.
func NewFileStorageReader(path string) (reader storage.StorageReader, err error) {
	var p string
	if p, err = utils.NormalizePath(path); err != nil {
		return nil, err
	}
	if p, err = filepath.Abs(p); err != nil {
		return nil, err
	}
	fi, err := os.Stat(p)
	if err != nil {
		return nil, err
	}
	if !fi.Mode().IsDir() {
		return nil, fmt.Errorf("specified source path is not a directory: %s", path)
	}
	return &fileStorageReader{p}, nil
}

func (f *fileStorageReader) fullPath(name string) string {
	return filepath.Join(f.path, name)
}

// Get implements storage.StorageReader.Get.
func (f *fileStorageReader) Get(name string) (io.ReadCloser, error) {
	filename := f.fullPath(name)
	fi, err := os.Stat(filename)
	if err != nil {
		if os.IsNotExist(err) {
			err = coreerrors.NewNotFoundError(err, "")
		}
		return nil, err
	} else if fi.IsDir() {
		return nil, coreerrors.NotFoundf("no such file with name %q", name)
	}
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	return file, nil
}

// List implements storage.StorageReader.List.
func (f *fileStorageReader) List(prefix string) ([]string, error) {
	var names []string
	if prefix == ".tmp" {
		// We don't expose our staging directory
		return names, nil
	}
	prefix = filepath.Join(f.path, prefix)
	dir := filepath.Dir(prefix)
	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() && strings.HasPrefix(path, prefix) {
			names = append(names, path[len(f.path)+1:])
		}
		return nil
	})
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	sort.Strings(names)
	return names, nil
}

// URL implements storage.StorageReader.URL.
func (f *fileStorageReader) URL(name string) (string, error) {
	return "file://" + filepath.Join(f.path, name), nil
}

// ConsistencyStrategy implements storage.StorageReader.ConsistencyStrategy.
func (f *fileStorageReader) DefaultConsistencyStrategy() utils.AttemptStrategy {
	return utils.AttemptStrategy{}
}

// ShouldRetry is specified in the StorageReader interface.
func (f *fileStorageReader) ShouldRetry(err error) bool {
	return false
}

type fileStorageWriter struct {
	fileStorageReader
}

// NewFileStorageWriter returns a new read/write storag for
// a directory inside the local file system.
func NewFileStorageWriter(path string) (storage.Storage, error) {
	reader, err := NewFileStorageReader(path)
	if err != nil {
		return nil, err
	}
	return &fileStorageWriter{*reader.(*fileStorageReader)}, nil
}

func (f *fileStorageWriter) Put(name string, r io.Reader, length int64) error {
	fullpath := f.fullPath(name)
	dir := filepath.Dir(fullpath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	tmpdir := filepath.Join(f.path, ".tmp")
	if err := os.MkdirAll(tmpdir, 0755); err != nil {
		return err
	}
	defer os.Remove(tmpdir)
	// Write to a temporary file first, and then move (atomically).
	file, err := ioutil.TempFile(tmpdir, "juju-filestorage-")
	if err != nil {
		return err
	}
	_, err = io.CopyN(file, r, length)
	file.Close()
	if err != nil {
		os.Remove(file.Name())
		return err
	}
	return utils.ReplaceFile(file.Name(), fullpath)
}

func (f *fileStorageWriter) Remove(name string) error {
	fullpath := f.fullPath(name)
	err := os.Remove(fullpath)
	if os.IsNotExist(err) {
		err = nil
	}
	return err
}

func (f *fileStorageWriter) RemoveAll() error {
	return storage.RemoveAll(f)
}
<commit_msg>make go fmt happy<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package filestorage

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"launchpad.net/juju-core/environs/storage"
	coreerrors "launchpad.net/juju-core/errors"
	"launchpad.net/juju-core/utils"
)

// fileStorageReader implements StorageReader backed
// by the local filesystem.
type fileStorageReader struct {
	path string
}

// NewFileStorageReader returns a new storage reader for
// a directory inside the local file system.
func NewFileStorageReader(path string) (reader storage.StorageReader, err error) {
	var p string
	if p, err = utils.NormalizePath(path); err != nil {
		return nil, err
	}
	if p, err = filepath.Abs(p); err != nil {
		return nil, err
	}
	fi, err := os.Stat(p)
	if err != nil {
		return nil, err
	}
	if !fi.Mode().IsDir() {
		return nil, fmt.Errorf("specified source path is not a directory: %s", path)
	}
	return &fileStorageReader{p}, nil
}

func (f *fileStorageReader) fullPath(name string) string {
	return filepath.Join(f.path, name)
}

// Get implements storage.StorageReader.Get.
func (f *fileStorageReader) Get(name string) (io.ReadCloser, error) {
	filename := f.fullPath(name)
	fi, err := os.Stat(filename)
	if err != nil {
		if os.IsNotExist(err) {
			err = coreerrors.NewNotFoundError(err, "")
		}
		return nil, err
	} else if fi.IsDir() {
		return nil, coreerrors.NotFoundf("no such file with name %q", name)
	}
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	return file, nil
}

// List implements storage.StorageReader.List.
func (f *fileStorageReader) List(prefix string) ([]string, error) {
	var names []string
	if prefix == ".tmp" {
		// We don't expose our staging directory
		return names, nil
	}
	prefix = filepath.Join(f.path, prefix)
	dir := filepath.Dir(prefix)
	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() && strings.HasPrefix(path, prefix) {
			names = append(names, path[len(f.path)+1:])
		}
		return nil
	})
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	sort.Strings(names)
	return names, nil
}

// URL implements storage.StorageReader.URL.
func (f *fileStorageReader) URL(name string) (string, error) {
	return "file://" + filepath.Join(f.path, name), nil
}

// ConsistencyStrategy implements storage.StorageReader.ConsistencyStrategy.
func (f *fileStorageReader) DefaultConsistencyStrategy() utils.AttemptStrategy {
	return utils.AttemptStrategy{}
}

// ShouldRetry is specified in the StorageReader interface.
func (f *fileStorageReader) ShouldRetry(err error) bool {
	return false
}

type fileStorageWriter struct {
	fileStorageReader
}

// NewFileStorageWriter returns a new read/write storag for
// a directory inside the local file system.
func NewFileStorageWriter(path string) (storage.Storage, error) {
	reader, err := NewFileStorageReader(path)
	if err != nil {
		return nil, err
	}
	return &fileStorageWriter{*reader.(*fileStorageReader)}, nil
}

func (f *fileStorageWriter) Put(name string, r io.Reader, length int64) error {
	fullpath := f.fullPath(name)
	dir := filepath.Dir(fullpath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	tmpdir := filepath.Join(f.path, ".tmp")
	if err := os.MkdirAll(tmpdir, 0755); err != nil {
		return err
	}
	defer os.Remove(tmpdir)
	// Write to a temporary file first, and then move (atomically).
	file, err := ioutil.TempFile(tmpdir, "juju-filestorage-")
	if err != nil {
		return err
	}
	_, err = io.CopyN(file, r, length)
	file.Close()
	if err != nil {
		os.Remove(file.Name())
		return err
	}
	return utils.ReplaceFile(file.Name(), fullpath)
}

func (f *fileStorageWriter) Remove(name string) error {
	fullpath := f.fullPath(name)
	err := os.Remove(fullpath)
	if os.IsNotExist(err) {
		err = nil
	}
	return err
}

func (f *fileStorageWriter) RemoveAll() error {
	return storage.RemoveAll(f)
}
<|endoftext|>


<commit_before>package draw

import (
	"fmt"
	"image"
	"image/color"
	"log"
	"os"
	"strings"

	"github.com/llgcode/draw2d"
	"github.com/llgcode/draw2d/draw2dimg"
	"github.com/riston/slack-hangman"
)

const (
	Width  = 350
	Height = 350
	Offset = 25.0
)

var DefaultColor, FirstColor, SecondColor color.RGBA
var RedColor, GreenColor color.RGBA

func init() {
	// #444444
	DefaultColor = color.RGBA{0x44, 0x44, 0x44, 0xff}
	// #0A63BB
	FirstColor = color.RGBA{0x0a, 0x63, 0xbb, 0xff}
	// #6D083E
	SecondColor = color.RGBA{0x6d, 0x08, 0x3e, 0xff}
	// #FF0000
	RedColor = color.RGBA{0xff, 0x0, 0x0, 0xff}
	// #00FF00
	GreenColor = color.RGBA{0x00, 0xff, 0x0, 0xff}
}

func DrawWrongGuesses(gc *draw2dimg.GraphicContext, guess, word string) {
	gc.Save()
	gc.SetFillColor(color.Black)
	gc.SetFontSize(20)
	gc.FillStringAt("Guess:", 240, 50)

	for index, char := range guess {
		if strings.ContainsRune(word, char) {
			gc.SetFillColor(GreenColor)
		} else {
			gc.SetFillColor(RedColor)
		}
		gc.SetFontSize(16)
		xOffset := index % 2 * 35
		yOffset := index * 20
		gc.FillStringAt(fmt.Sprintf("%c", char), float64(240+xOffset), float64(80+yOffset))
	}
	gc.Restore()
}

func DrawHangmanFrame(gc *draw2dimg.GraphicContext, imagePath string, wrongGuess []rune) {
	guesses := len(wrongGuess)
	if guesses > hangman.Steps {
		log.Println("Too many guesses")
		return
	}
	file := fmt.Sprintf("%s/frame%d.png", imagePath, guesses)
	source, err := draw2dimg.LoadFromPngFile(file)
	if err != nil {
		log.Println("Error on loading png", err)
	}

	gc.Save()
	gc.Translate(30, 30)
	gc.Scale(0.7, 0.7)
	gc.DrawImage(source)
	gc.Restore()
}

func DrawState(gc *draw2dimg.GraphicContext, state hangman.State) {
	var fontColor color.RGBA
	var message string

	switch state {
	case hangman.GameOverState:
		fontColor = color.RGBA{0xFF, 0x00, 0x00, 0xFF}
		message = "GameOver"
	case hangman.WinState:
		fontColor = color.RGBA{0x00, 0xFF, 0x00, 0xFF}
		message = "Awesome"
	}

	gc.Save()
	gc.SetFillColor(fontColor)
	gc.SetFontSize(60)
	// Convert to radians
	gc.Rotate(40 * 3.14 / 180)
	gc.FillStringAt(message, 55, 50)
	gc.Restore()
}

func Draw(game *hangman.Hangman) image.Image {

	// Initialize the graphic context on an RGBA image
	dest := image.NewRGBA(image.Rect(0, 0, Width, Height))
	gc := draw2dimg.NewGraphicContext(dest)

	imagePath := os.Getenv("IMAGE_PATH")
	if imagePath == "" {
		log.Fatalln("No IMAGE_PATH has been set")
	}

	fontPath := os.Getenv("FONT_PATH")
	if fontPath == "" {
		log.Fatalln("No FONT_PATH has been set")
	}

	// Draw letters
	draw2d.SetFontFolder(fontPath)

	gc.SetFontData(draw2d.FontData{
		Name:   "luxi",
		Family: draw2d.FontFamilySans,
		Style:  draw2d.FontStyleNormal,
	})

	wrongGuesses := game.GetWrongGuesses()

	DrawHangmanFrame(gc, imagePath, wrongGuesses)
	DrawState(gc, game.State)

	// Set some properties
	gc.SetFillColor(color.Transparent)
	gc.SetStrokeColor(color.RGBA{0x44, 0x44, 0x44, 0xff})
	gc.SetLineWidth(2)

	DrawWrongGuesses(gc, game.Guess, game.Word)

	// Show the current word
	gc.Save()
	gc.SetFillColor(color.Black)
	gc.SetFontSize(25)
	gc.FillStringAt(fmt.Sprintf("%s [%d]", game.Current, len(game.Current)), 30, 320)
	gc.Restore()

	return dest
}
<commit_msg>Show the full word when the game is over<commit_after>package draw

import (
	"fmt"
	"image"
	"image/color"
	"log"
	"os"
	"strings"

	"github.com/llgcode/draw2d"
	"github.com/llgcode/draw2d/draw2dimg"
	"github.com/riston/slack-hangman"
)

const (
	Width  = 350
	Height = 350
	Offset = 25.0
)

var DefaultColor, FirstColor, SecondColor color.RGBA
var RedColor, GreenColor color.RGBA

func init() {
	// #444444
	DefaultColor = color.RGBA{0x44, 0x44, 0x44, 0xff}
	// #0A63BB
	FirstColor = color.RGBA{0x0a, 0x63, 0xbb, 0xff}
	// #6D083E
	SecondColor = color.RGBA{0x6d, 0x08, 0x3e, 0xff}
	// #FF0000
	RedColor = color.RGBA{0xff, 0x0, 0x0, 0xff}
	// #00FF00
	GreenColor = color.RGBA{0x00, 0xff, 0x0, 0xff}
}

func DrawWrongGuesses(gc *draw2dimg.GraphicContext, guess, word string) {
	gc.Save()
	gc.SetFillColor(color.Black)
	gc.SetFontSize(20)
	gc.FillStringAt("Guess:", 240, 50)

	for index, char := range guess {
		if strings.ContainsRune(word, char) {
			gc.SetFillColor(GreenColor)
		} else {
			gc.SetFillColor(RedColor)
		}
		gc.SetFontSize(16)
		xOffset := index % 2 * 35
		yOffset := index * 20
		gc.FillStringAt(fmt.Sprintf("%c", char), float64(240+xOffset), float64(80+yOffset))
	}
	gc.Restore()
}

func DrawHangmanFrame(gc *draw2dimg.GraphicContext, imagePath string, wrongGuess []rune) {
	guesses := len(wrongGuess)
	if guesses > hangman.Steps {
		log.Println("Too many guesses")
		return
	}
	file := fmt.Sprintf("%s/frame%d.png", imagePath, guesses)
	source, err := draw2dimg.LoadFromPngFile(file)
	if err != nil {
		log.Println("Error on loading png", err)
	}

	gc.Save()
	gc.Translate(30, 30)
	gc.Scale(0.7, 0.7)
	gc.DrawImage(source)
	gc.Restore()
}

func DrawState(gc *draw2dimg.GraphicContext, state hangman.State) {
	var fontColor color.RGBA
	var message string

	switch state {
	case hangman.GameOverState:
		fontColor = color.RGBA{0xFF, 0x00, 0x00, 0xFF}
		message = "GameOver"
	case hangman.WinState:
		fontColor = color.RGBA{0x00, 0xFF, 0x00, 0xFF}
		message = "Awesome"
	}

	gc.Save()
	gc.SetFillColor(fontColor)
	gc.SetFontSize(60)
	// Convert to radians
	gc.Rotate(40 * 3.14 / 180)
	gc.FillStringAt(message, 55, 50)
	gc.Restore()
}

func Draw(game *hangman.Hangman) image.Image {

	// Initialize the graphic context on an RGBA image
	dest := image.NewRGBA(image.Rect(0, 0, Width, Height))
	gc := draw2dimg.NewGraphicContext(dest)

	imagePath := os.Getenv("IMAGE_PATH")
	if imagePath == "" {
		log.Fatalln("No IMAGE_PATH has been set")
	}

	fontPath := os.Getenv("FONT_PATH")
	if fontPath == "" {
		log.Fatalln("No FONT_PATH has been set")
	}

	// Draw letters
	draw2d.SetFontFolder(fontPath)

	gc.SetFontData(draw2d.FontData{
		Name:   "luxi",
		Family: draw2d.FontFamilySans,
		Style:  draw2d.FontStyleNormal,
	})

	wrongGuesses := game.GetWrongGuesses()

	DrawHangmanFrame(gc, imagePath, wrongGuesses)
	DrawState(gc, game.State)

	// Set some properties
	gc.SetFillColor(color.Transparent)
	gc.SetStrokeColor(color.RGBA{0x44, 0x44, 0x44, 0xff})
	gc.SetLineWidth(2)

	DrawWrongGuesses(gc, game.Guess, game.Word)

	if game.State == hangman.GameOverState {
		gc.Save()
		gc.SetFillColor(GreenColor)
		gc.SetFontSize(25)
		gc.FillStringAt(fmt.Sprintf("%s", game.Word), 30, 320)
		gc.Restore()
	}

	// Show the current word
	gc.Save()
	gc.SetFillColor(color.Black)
	gc.SetFontSize(25)
	gc.FillStringAt(fmt.Sprintf("%s [%d]", game.Current, len(game.Current)), 30, 320)
	gc.Restore()

	return dest
}
<|endoftext|>


<commit_before>/*
 * A Go package to handles access to a an Sql database as a Dataset
 *
 * Copyright (C) 2016 Lawrence Woodman <lwoodman@vlifesystems.com>
 *
 * Licensed under an MIT licence.  Please see LICENCE.md for details.
 */

// Package dsql handles access to an SQL database as a Dataset
package dsql

import (
	"database/sql"
	"fmt"
	"github.com/lawrencewoodman/ddataset"
	"github.com/lawrencewoodman/dlit"
)

// DSQL represents a SQL database Dataset
type DSQL struct {
	dbHandler  DBHandler
	openConn   int
	fieldNames []string
}

// DSQLConn represents a connection to a DSQL Dataset
type DSQLConn struct {
	dataset       *DSQL
	rows          *sql.Rows
	row           []sql.NullString
	rowPtrs       []interface{}
	currentRecord ddataset.Record
	err           error
}

// DBHandler handles basic access to an Sql database
type DBHandler interface {
	// Open opens the database
	Open() error
	// Rows returns the rows for the database with each row having
	// the same number and same order of fields as those passed
	// to New.  However, the fields don't have to have the same names.
	Rows() (*sql.Rows, error)
	// Close closes the database
	Close() error
}

// New creates a new DSQL Dataset
func New(dbHandler DBHandler, fieldNames []string) ddataset.Dataset {
	return &DSQL{
		dbHandler:  dbHandler,
		openConn:   0,
		fieldNames: fieldNames,
	}
}

// Open creates a connection to the Dataset
func (s *DSQL) Open() (ddataset.Conn, error) {
	if err := s.dbHandler.Open(); err != nil {
		return nil, err
	}
	rows, err := s.dbHandler.Rows()
	if err != nil {
		s.dbHandler.Close()
		return nil, err
	}
	columns, err := rows.Columns()
	if err != nil {
		s.dbHandler.Close()
		return nil, err
	}
	numColumns := len(columns)
	if err := checkTableValid(s.fieldNames, numColumns); err != nil {
		s.dbHandler.Close()
		return nil, err
	}
	row := make([]sql.NullString, numColumns)
	rowPtrs := make([]interface{}, numColumns)
	for i := range s.fieldNames {
		rowPtrs[i] = &row[i]
	}

	return &DSQLConn{
		dataset:       s,
		rows:          rows,
		row:           row,
		rowPtrs:       rowPtrs,
		currentRecord: make(ddataset.Record, numColumns),
		err:           nil,
	}, nil
}

// GetFieldNames returns the field names used by the Dataset
func (s *DSQL) GetFieldNames() []string {
	return s.fieldNames
}

// Next returns whether there is a Record to be Read
func (sc *DSQLConn) Next() bool {
	if sc.err != nil {
		return false
	}
	if sc.rows.Next() {
		if err := sc.rows.Scan(sc.rowPtrs...); err != nil {
			sc.Close()
			sc.err = err
			return false
		}
		if err := sc.makeRowCurrentRecord(); err != nil {
			sc.Close()
			sc.err = err
			return false
		}
		return true
	}
	if err := sc.rows.Err(); err != nil {
		sc.Close()
		sc.err = err
		return false
	}
	return false
}

// Err returns any errors from the connection
func (sc *DSQLConn) Err() error {
	return sc.err
}

// Read returns the current Record
func (sc *DSQLConn) Read() ddataset.Record {
	return sc.currentRecord
}

// Close closes the connection
func (sc *DSQLConn) Close() error {
	return sc.dataset.dbHandler.Close()
}

func (sc *DSQLConn) makeRowCurrentRecord() error {
	var l *dlit.Literal
	var err error
	for i, v := range sc.row {
		if v.Valid {
			l = dlit.NewString(v.String)
		} else {
			l, err = dlit.New(nil)
			if err != nil {
				sc.Close()
				return err
			}
		}
		sc.currentRecord[sc.dataset.fieldNames[i]] = l
	}
	return nil
}

func checkTableValid(fieldNames []string, numColumns int) error {
	if len(fieldNames) < numColumns {
		return fmt.Errorf(
			"number of field names doesn't match number of columns in table",
		)
	}
	return nil
}
<commit_msg>Make checkTableValid more exact<commit_after>/*
 * A Go package to handles access to a an Sql database as a Dataset
 *
 * Copyright (C) 2016 Lawrence Woodman <lwoodman@vlifesystems.com>
 *
 * Licensed under an MIT licence.  Please see LICENCE.md for details.
 */

// Package dsql handles access to an SQL database as a Dataset
package dsql

import (
	"database/sql"
	"fmt"
	"github.com/lawrencewoodman/ddataset"
	"github.com/lawrencewoodman/dlit"
)

// DSQL represents a SQL database Dataset
type DSQL struct {
	dbHandler  DBHandler
	openConn   int
	fieldNames []string
}

// DSQLConn represents a connection to a DSQL Dataset
type DSQLConn struct {
	dataset       *DSQL
	rows          *sql.Rows
	row           []sql.NullString
	rowPtrs       []interface{}
	currentRecord ddataset.Record
	err           error
}

// DBHandler handles basic access to an Sql database
type DBHandler interface {
	// Open opens the database
	Open() error
	// Rows returns the rows for the database with each row having
	// the same number and same order of fields as those passed
	// to New.  However, the fields don't have to have the same names.
	Rows() (*sql.Rows, error)
	// Close closes the database
	Close() error
}

// New creates a new DSQL Dataset
func New(dbHandler DBHandler, fieldNames []string) ddataset.Dataset {
	return &DSQL{
		dbHandler:  dbHandler,
		openConn:   0,
		fieldNames: fieldNames,
	}
}

// Open creates a connection to the Dataset
func (s *DSQL) Open() (ddataset.Conn, error) {
	if err := s.dbHandler.Open(); err != nil {
		return nil, err
	}
	rows, err := s.dbHandler.Rows()
	if err != nil {
		s.dbHandler.Close()
		return nil, err
	}
	columns, err := rows.Columns()
	if err != nil {
		s.dbHandler.Close()
		return nil, err
	}
	numColumns := len(columns)
	if err := checkTableValid(s.fieldNames, numColumns); err != nil {
		s.dbHandler.Close()
		return nil, err
	}
	row := make([]sql.NullString, numColumns)
	rowPtrs := make([]interface{}, numColumns)
	for i := range s.fieldNames {
		rowPtrs[i] = &row[i]
	}

	return &DSQLConn{
		dataset:       s,
		rows:          rows,
		row:           row,
		rowPtrs:       rowPtrs,
		currentRecord: make(ddataset.Record, numColumns),
		err:           nil,
	}, nil
}

// GetFieldNames returns the field names used by the Dataset
func (s *DSQL) GetFieldNames() []string {
	return s.fieldNames
}

// Next returns whether there is a Record to be Read
func (sc *DSQLConn) Next() bool {
	if sc.err != nil {
		return false
	}
	if sc.rows.Next() {
		if err := sc.rows.Scan(sc.rowPtrs...); err != nil {
			sc.Close()
			sc.err = err
			return false
		}
		if err := sc.makeRowCurrentRecord(); err != nil {
			sc.Close()
			sc.err = err
			return false
		}
		return true
	}
	if err := sc.rows.Err(); err != nil {
		sc.Close()
		sc.err = err
		return false
	}
	return false
}

// Err returns any errors from the connection
func (sc *DSQLConn) Err() error {
	return sc.err
}

// Read returns the current Record
func (sc *DSQLConn) Read() ddataset.Record {
	return sc.currentRecord
}

// Close closes the connection
func (sc *DSQLConn) Close() error {
	return sc.dataset.dbHandler.Close()
}

func (sc *DSQLConn) makeRowCurrentRecord() error {
	var l *dlit.Literal
	var err error
	for i, v := range sc.row {
		if v.Valid {
			l = dlit.NewString(v.String)
		} else {
			l, err = dlit.New(nil)
			if err != nil {
				sc.Close()
				return err
			}
		}
		sc.currentRecord[sc.dataset.fieldNames[i]] = l
	}
	return nil
}

func checkTableValid(fieldNames []string, numColumns int) error {
	if len(fieldNames) != numColumns {
		return fmt.Errorf(
			"number of field names doesn't match number of columns in table",
		)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 The etcd 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 command

import (
	"context"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"os/signal"

	"go.etcd.io/etcd/client"

	"github.com/urfave/cli"
)

// NewExecWatchCommand returns the CLI command for "exec-watch".
func NewExecWatchCommand() cli.Command {
	return cli.Command{
		Name:      "exec-watch",
		Usage:     "watch a key for changes and exec an executable",
		ArgsUsage: "<key> <command> [args...]",
		Flags: []cli.Flag{
			cli.IntFlag{Name: "after-index", Value: 0, Usage: "watch after the given index"},
			cli.BoolFlag{Name: "recursive, r", Usage: "watch all values for key and child keys"},
		},
		Action: func(c *cli.Context) error {
			execWatchCommandFunc(c, mustNewKeyAPI(c))
			return nil
		},
	}
}

// execWatchCommandFunc executes the "exec-watch" command.
func execWatchCommandFunc(c *cli.Context, ki client.KeysAPI) {
	args := c.Args()
	argslen := len(args)

	if argslen < 2 {
		handleError(c, ExitBadArgs, errors.New("key and command to exec required"))
	}

	var (
		key     string
		cmdArgs []string
	)

	foundSep := false
	for i := range args {
		if args[i] == "--" && i != 0 {
			foundSep = true
			break
		}
	}

	if foundSep {
		key = args[0]
		cmdArgs = args[2:]
	} else {
		// If no flag is parsed, the order of key and cmdArgs will be switched and
		// args will not contain `--`.
		key = args[argslen-1]
		cmdArgs = args[:argslen-1]
	}

	index := 0
	if c.Int("after-index") != 0 {
		index = c.Int("after-index")
	}

	recursive := c.Bool("recursive")

	sigch := make(chan os.Signal, 1)
	signal.Notify(sigch, os.Interrupt)

	go func() {
		<-sigch
		os.Exit(0)
	}()

	w := ki.Watcher(key, &client.WatcherOptions{AfterIndex: uint64(index), Recursive: recursive})

	for {
		resp, err := w.Next(context.TODO())
		if err != nil {
			handleError(c, ExitServerError, err)
		}
		if resp.Node.Dir {
			fmt.Fprintf(os.Stderr, "Ignored dir %s change\n", resp.Node.Key)
			continue
		}

		cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
		cmd.Env = environResponse(resp, os.Environ())

		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		go func() {
			err := cmd.Start()
			if err != nil {
				fmt.Fprintf(os.Stderr, err.Error())
				os.Exit(1)
			}
			cmd.Wait()
		}()
	}
}

func environResponse(resp *client.Response, env []string) []string {
	env = append(env, "ETCD_WATCH_ACTION="+resp.Action)
	env = append(env, "ETCD_WATCH_MODIFIED_INDEX="+fmt.Sprintf("%d", resp.Node.ModifiedIndex))
	env = append(env, "ETCD_WATCH_KEY="+resp.Node.Key)
	env = append(env, "ETCD_WATCH_VALUE="+resp.Node.Value)
	return env
}
<commit_msg>etcdctl: use appropriate type conversion<commit_after>// Copyright 2015 The etcd 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 command

import (
	"context"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"os/signal"

	"go.etcd.io/etcd/client"

	"github.com/urfave/cli"
)

// NewExecWatchCommand returns the CLI command for "exec-watch".
func NewExecWatchCommand() cli.Command {
	return cli.Command{
		Name:      "exec-watch",
		Usage:     "watch a key for changes and exec an executable",
		ArgsUsage: "<key> <command> [args...]",
		Flags: []cli.Flag{
			cli.IntFlag{Name: "after-index", Value: 0, Usage: "watch after the given index"},
			cli.BoolFlag{Name: "recursive, r", Usage: "watch all values for key and child keys"},
		},
		Action: func(c *cli.Context) error {
			execWatchCommandFunc(c, mustNewKeyAPI(c))
			return nil
		},
	}
}

// execWatchCommandFunc executes the "exec-watch" command.
func execWatchCommandFunc(c *cli.Context, ki client.KeysAPI) {
	args := c.Args()
	argslen := len(args)

	if argslen < 2 {
		handleError(c, ExitBadArgs, errors.New("key and command to exec required"))
	}

	var (
		key     string
		cmdArgs []string
	)

	foundSep := false
	for i := range args {
		if args[i] == "--" && i != 0 {
			foundSep = true
			break
		}
	}

	if foundSep {
		key = args[0]
		cmdArgs = args[2:]
	} else {
		// If no flag is parsed, the order of key and cmdArgs will be switched and
		// args will not contain `--`.
		key = args[argslen-1]
		cmdArgs = args[:argslen-1]
	}

	index := c.Uint64("after-index")

	recursive := c.Bool("recursive")

	sigch := make(chan os.Signal, 1)
	signal.Notify(sigch, os.Interrupt)

	go func() {
		<-sigch
		os.Exit(0)
	}()

	w := ki.Watcher(key, &client.WatcherOptions{AfterIndex: uint64(index), Recursive: recursive})

	for {
		resp, err := w.Next(context.TODO())
		if err != nil {
			handleError(c, ExitServerError, err)
		}
		if resp.Node.Dir {
			fmt.Fprintf(os.Stderr, "Ignored dir %s change\n", resp.Node.Key)
			continue
		}

		cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
		cmd.Env = environResponse(resp, os.Environ())

		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		go func() {
			err := cmd.Start()
			if err != nil {
				fmt.Fprintf(os.Stderr, err.Error())
				os.Exit(1)
			}
			cmd.Wait()
		}()
	}
}

func environResponse(resp *client.Response, env []string) []string {
	env = append(env, "ETCD_WATCH_ACTION="+resp.Action)
	env = append(env, "ETCD_WATCH_MODIFIED_INDEX="+fmt.Sprintf("%d", resp.Node.ModifiedIndex))
	env = append(env, "ETCD_WATCH_KEY="+resp.Node.Key)
	env = append(env, "ETCD_WATCH_VALUE="+resp.Node.Value)
	return env
}
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC. 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 ws

import (
	"bytes"
	"fmt"
	"testing"
)

const (
	dbFp    = "/tmp/wdb.db"
	undefFp = ""
)

func TestRoundTrip(t *testing.T) {
	for _, test := range []struct {
		desc string
		data []byte
	}{
		{
			desc: "initial checkpoint test",
			data: []byte("some checkpoint"),
		}, {
			desc: "check over-write of checkpoint",
			data: []byte("some more checkpoint"),
		},
	} {
		t.Run(test.desc, func(t *testing.T) {

			store, err := NewStorage(dbFp)
			if err != nil {
				t.Error("failed to create storage", err)
			}
			want := test.data
			if err := store.StoreCP(test.data); err != nil {
				t.Error("failed to store into Witness Store", err)
			}
			got, err := store.RetrieveCP()
			if err != nil {
				t.Error("failed to retrieve from Witness Store", err)
			}
			if !bytes.Equal(got, want) {
				t.Errorf("got '%s' want '%s'", got, want)
			}

		})
	}
}

func TestFailedStorage(t *testing.T) {
	for _, test := range []struct {
		desc      string
		wantError string
	}{
		{
			desc:      "Handle Storage failure",
			wantError: "failed to open file: open : no such file or directory",
		},
	} {
		t.Run(test.desc, func(t *testing.T) {
			_, err := NewStorage(undefFp)
			if err == nil {
				t.Error("Unexpected success in storage creation", err)
			}
			fmt.Printf("Received Error = %s", err.Error())
			if err.Error() != test.wantError {
				t.Error("Unexpected error message received", err)
			}
		})
	}
}
<commit_msg>Fix flakey ft_witness test<commit_after>// Copyright 2021 Google LLC. 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 ws

import (
	"bytes"
	"fmt"
	"path/filepath"
	"testing"
)

const undefFp = ""

func TestRoundTrip(t *testing.T) {
	for _, test := range []struct {
		desc string
		data []byte
	}{
		{
			desc: "initial checkpoint test",
			data: []byte("some checkpoint"),
		}, {
			desc: "check over-write of checkpoint",
			data: []byte("some more checkpoint"),
		},
	} {
		dbFP := filepath.Join(t.TempDir(), "db")
		t.Run(test.desc, func(t *testing.T) {
			store, err := NewStorage(dbFP)
			if err != nil {
				t.Error("failed to create storage", err)
			}
			want := test.data
			if err := store.StoreCP(test.data); err != nil {
				t.Error("failed to store into Witness Store", err)
			}
			got, err := store.RetrieveCP()
			if err != nil {
				t.Error("failed to retrieve from Witness Store", err)
			}
			if !bytes.Equal(got, want) {
				t.Errorf("got '%s' want '%s'", got, want)
			}

		})
	}
}

func TestFailedStorage(t *testing.T) {
	for _, test := range []struct {
		desc      string
		wantError string
	}{
		{
			desc:      "Handle Storage failure",
			wantError: "failed to open file: open : no such file or directory",
		},
	} {
		t.Run(test.desc, func(t *testing.T) {
			_, err := NewStorage(undefFp)
			if err == nil {
				t.Error("Unexpected success in storage creation", err)
			}
			fmt.Printf("Received Error = %s", err.Error())
			if err.Error() != test.wantError {
				t.Error("Unexpected error message received", err)
			}
		})
	}
}
<|endoftext|>


<commit_before>// +build ignore

// Enumerate unique keys from key/values found in the whois responses.
// To use: go run enumerate.go

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"regexp"

	"github.com/domainr/whois"
	"github.com/domainr/whoistest"
)

var (
	keys = make(map[string]bool)
)

func main() {
	flag.Parse()
	if err := main1(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func main1() error {
	fns, err := whoistest.ResponseFiles()
	if err != nil {
		return err
	}
	for _, fn := range fns {
		res, err := whois.ReadMIMEFile(fn)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error reading response file %s: %s\n", fn, err)
			continue
		}
		if res.MediaType != "text/plain" {
			continue
		}
		scan(res)
	}

	fmt.Fprintf(os.Stderr, "\n%d unique keys parsed:\n", len(keys))
	for k, _ := range keys {
		fmt.Fprintf(os.Stderr, "%s\n", k)
	}

	return nil
}

var (
	reEmptyLine     = regexp.MustCompile(`^\s*$`)
	reKey           = regexp.MustCompile(`^\s*([^\:]*\S)\s*\:\s*$`)
	reKeyValue      = regexp.MustCompile(`^\s*([^\:]*\S)\s*\:\s*(.*\S)\s*$`)
	reAltKey        = regexp.MustCompile(`^\s*\[([^\]]+)\]\s*$`)
	reAltKeyValue   = regexp.MustCompile(`^\s*\[([^\]]+)\]\s*(.*\S)\s*$`)
	reIndentedValue = regexp.MustCompile(`^      \s+(.*\S)\s*$`)
	deNotice        = `^% .*$`
	jpNotice        = `^\[ .+ \]$`
	krNotice        = `^# .*$`
	updated         = `^>>>.+<<<$`
	reNotice        = regexp.MustCompile(
		deNotice + "|" + jpNotice + "|" + krNotice + "|" + updated)
)

func scan(res *whois.Response) {
	r, err := res.Reader()
	if err != nil {
		return
	}
	line := 0
	s := bufio.NewScanner(r)
	for s.Scan() {
		line++
		text := s.Text()

		if reEmptyLine.MatchString(text) {
			fmt.Printf("% 4d  EMPTY\n", line)
			continue
		}

		if m := reNotice.FindStringSubmatch(text); m != nil {
			fmt.Printf("% 4d  %- 20s  %s\n", line, "NOTICE", m[0])
			continue
		}

		if m := reAltKeyValue.FindStringSubmatch(text); m != nil {
			keys[m[1]] = true
			fmt.Printf("% 4d  %- 20s  %- 40s %s\n", line, "ALT_KEY_VALUE", m[1], m[2])
			continue
		}

		if m := reAltKey.FindStringSubmatch(text); m != nil {
			keys[m[1]] = true
			fmt.Printf("% 4d  %- 20s  %s\n", line, "ALT_KEY", m[1])
			continue
		}

		if m := reKeyValue.FindStringSubmatch(text); m != nil {
			keys[m[1]] = true
			fmt.Printf("% 4d  %- 20s  %- 40s %s\n", line, "KEY_VALUE", m[1], m[2])
			continue
		}

		if m := reKey.FindStringSubmatch(text); m != nil {
			keys[m[1]] = true
			fmt.Printf("% 4d  %- 20s  %s\n", line, "KEY", m[1])
			continue
		}

		if m := reIndentedValue.FindStringSubmatch(text); m != nil {
			fmt.Printf("% 4d  %- 20s  %- 40s %s\n", line, "INDENTED_VALUE", "", m[1])
			continue
		}

		fmt.Fprintf(os.Stderr, "% 4d  %- 20s  %s\n", line, "UNKNOWN", text)
	}

	fmt.Printf("\n")
}
<commit_msg>Color output.<commit_after>// +build ignore

// Enumerate unique keys from key/values found in the whois responses.
// To use: go run enumerate.go

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"regexp"
	"sort"
	"strings"

	"github.com/domainr/whois"
	"github.com/domainr/whoistest"
	"github.com/wsxiaoys/terminal/color"
)

var (
	keys = make(map[string]string)
)

func main() {
	flag.Parse()
	if err := main1(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func main1() error {
	fns, err := whoistest.ResponseFiles()
	if err != nil {
		return err
	}
	for _, fn := range fns {
		res, err := whois.ReadMIMEFile(fn)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error reading response file %s: %s\n", fn, err)
			continue
		}
		if res.MediaType != "text/plain" {
			continue
		}
		scan(res)
	}

	sorted := make([]string, 0, len(keys))
	for k, _ := range keys {
		sorted = append(sorted, k)
	}
	sort.Strings(sorted)

	color.Printf("\n@{|w}%d unique keys parsed:\n", len(keys))
	for _, k := range sorted {
		color.Printf("@{|c}%- 40s  @{|.}%s\n", k, keys[k])
	}

	return nil
}

var (
	reEmptyLine     = regexp.MustCompile(`^\s*$`)
	reKey           = regexp.MustCompile(`^\s*([^\:]*\S)\s*\:\s*$`)
	reKeyValue      = regexp.MustCompile(`^\s*([^\:]*\S)\s*\:\s*(.*\S)\s*$`)
	reAltKey        = regexp.MustCompile(`^\s*\[([^\]]+)\]\s*$`)
	reAltKeyValue   = regexp.MustCompile(`^\s*\[([^\]]+)\]\s*(.*\S)\s*$`)
	reIndentedValue = regexp.MustCompile(`^      \s+(.*\S)\s*$`)
	deNotice        = `^% .*$`
	jpNotice        = `^\[ .+ \]$`
	krNotice        = `^# .*$`
	updated         = `^>>>.+<<<$`
	reNotice        = regexp.MustCompile(
		deNotice + "|" + jpNotice + "|" + krNotice + "|" + updated)
)

func scan(res *whois.Response) {
	r, err := res.Reader()
	if err != nil {
		return
	}
	line := 0
	s := bufio.NewScanner(r)
	for s.Scan() {
		line++
		color.Printf("@{|.}% 4d  ", line)

		// Get next line
		text := s.Text()

		// Notices and empty lines
		if reEmptyLine.MatchString(text) {
			color.Printf("@{|w}EMPTY\n")
			continue
		}
		if m := reNotice.FindStringSubmatch(text); m != nil {
			color.Printf("@{|w}%- 10s  %s\n", "NOTICE", m[0])
			continue
		}

		// Keys and values
		if m := reAltKeyValue.FindStringSubmatch(text); m != nil {
			addKey(m[1], res.Host)
			color.Printf("@{|w}%- 10s  @{c}%- 40s @{w}%s\n", "ALT_KEY_VALUE", m[1], m[2])
			continue
		}
		if m := reAltKey.FindStringSubmatch(text); m != nil {
			addKey(m[1], res.Host)
			color.Printf("@{|w}%- 10s  @{c}%s\n", "ALT_KEY", m[1])
			continue
		}
		if m := reKeyValue.FindStringSubmatch(text); m != nil {
			addKey(m[1], res.Host)
			color.Printf("@{|w}%- 10s  @{c}%- 40s @{w}%s\n", "KEY_VALUE", m[1], m[2])
			continue
		}
		if m := reKey.FindStringSubmatch(text); m != nil {
			addKey(m[1], res.Host)
			color.Printf("@{|w}%- 10s  @{c}%s\n", "KEY", m[1])
			continue
		}
		if m := reIndentedValue.FindStringSubmatch(text); m != nil {
			color.Printf("@{|w}%- 10s  @{c}%- 40s @{w}%s\n", "INDENTED_VALUE", "", m[1])
			continue
		}

		// Unknown
		color.Printf("@{|.}%- 10s  @{|.}%s\n", "UNKNOWN", text)
	}

	fmt.Printf("\n")
}

func addKey(k, host string) {
	if _, ok := keys[k]; !ok {
		keys[k] = host
	} else if !strings.Contains(keys[k], host) {
		keys[k] = keys[k] + "  " + host
	}
}
<|endoftext|>


<commit_before>// +build ignore

// Enumerate unique keys from key/values found in the whois responses.
// To use: go run enumerate.go

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"

	"github.com/domainr/whois"
	"github.com/domainr/whoistest"
)

func main() {
	flag.Parse()
	if err := main1(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func main1() error {
	fns, err := whoistest.ResponseFiles()
	if err != nil {
		return err
	}
	for _, fn := range fns {
		res, err := whois.ReadMIMEFile(fn)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error reading response file %s: %s\n", fn, err)
			continue
		}
		if res.MediaType != "text/plain" {
			continue
		}

		fmt.Printf("File:  %s\n", fn)
		fmt.Printf("Query: %s\n", res.Query)
		fmt.Printf("Host:  %s\n", res.Host)
		fmt.Printf("\n")
		scan(res)
		fmt.Printf("\n\n\n")
	}
	return nil
}

func scan(res *whois.Response) {
	r, err := res.Reader()
	if err != nil {
		return
	}
	line := 0
	s := bufio.NewScanner(r)
	for s.Scan() {
		line++
		fmt.Printf("% 4d %s\n", line, s.Text())
	}
}
<commit_msg>Identify bracketed and colon-delimited key/value elements.<commit_after>// +build ignore

// Enumerate unique keys from key/values found in the whois responses.
// To use: go run enumerate.go

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"regexp"

	"github.com/domainr/whois"
	"github.com/domainr/whoistest"
)

func main() {
	flag.Parse()
	if err := main1(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func main1() error {
	fns, err := whoistest.ResponseFiles()
	if err != nil {
		return err
	}
	for _, fn := range fns {
		res, err := whois.ReadMIMEFile(fn)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error reading response file %s: %s\n", fn, err)
			continue
		}
		if res.MediaType != "text/plain" {
			continue
		}
		scan(res)
	}
	return nil
}

var colonElement = regexp.MustCompile(`^\s*([^\:]*\S)\s*\:\s*(.*\S)\s*$`)
var bracketElement = regexp.MustCompile(`^\s*\[\s*([^\]]*\S)\s*\]\s*(.*\S)\s*$`)

func scan(res *whois.Response) {
	r, err := res.Reader()
	if err != nil {
		return
	}
	line := 0
	s := bufio.NewScanner(r)
	for s.Scan() {
		line++
		// fmt.Printf("% 4d %s\n", line, s.Text())
		text := s.Text()

		if m := colonElement.FindStringSubmatch(text); m != nil {
			fmt.Printf("COLON ELEMENT:   %s: %s\n", m[1], m[2])
			continue
		}

		if m := bracketElement.FindStringSubmatch(text); m != nil {
			fmt.Printf("BRACKET ELEMENT: %s: %s\n", m[1], m[2])
			continue
		}
	}
	fmt.Printf("\n")
}
<|endoftext|>


<commit_before>// Copyright 2017 Tomas Machalek <tomas.machalek@gmail.com>
//
// 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 elpush

import (
	"fmt"
	"testing"

	"github.com/czcorpus/klogproc/logs"
)

// cnkr.Action + cnkr.Corpus + cnkr.Datetime + cnkr.IPAddress + cnkr.Type + cnkr.UserAgent + cnkr.UserID
func createRecord() *CNKRecord {
	return &CNKRecord{
		ID:          "abcdef",
		Type:        "kontext",
		Action:      "view",
		Corpus:      "syn2015",
		Datetime:    "2017-02-11T11:02:31.880",
		IPAddress:   "195.113.53.66",
		IsAnonymous: true,
		IsQuery:     false,
		Limited:     false,
		ProcTime:    0.712,
		QueryType:   "cql",
		Type2:       "kontext",
		UserAgent:   "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:51.0) Gecko/20100101 Firefox/51.0",
		UserID:      100,
		GeoIP: GeoDataRecord{
			CountryCode2: "CZ",
			CountryName:  "Czech Republic",
			IP:           "195.113.53.66",
			Latitude:     49.4,
			Longitude:    17.674,
			Location:     [2]float32{17.6742, 49.3996},
		},
	}
}

func TestCreateID(t *testing.T) {
	rec := createRecord()
	fmt.Println("HASH: ", createID(rec))
	if createID(rec) != "2452d6c39ddd4dfcba2df61e1115511e547c09af" {
		t.Error("Hash match error")
	}
}

func TestImportCorpname(t *testing.T) {
	p := make(map[string]string)
	p["corpname"] = "omezeni/foobar7;x=10"
	r := &logs.LogRecord{Params: p}
	c := importCorpname(r)
	if c.Corpname != "foobar7" || c.limited != true {
		t.Error("Failed import corpname: ", c)
	}
}
<commit_msg>Fix line endings<commit_after>// Copyright 2017 Tomas Machalek <tomas.machalek@gmail.com>
//
// 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 elpush

import (
	"fmt"
	"testing"

	"github.com/czcorpus/klogproc/logs"
)

// cnkr.Action + cnkr.Corpus + cnkr.Datetime + cnkr.IPAddress + cnkr.Type + cnkr.UserAgent + cnkr.UserID
func createRecord() *CNKRecord {
	return &CNKRecord{
		ID:          "abcdef",
		Type:        "kontext",
		Action:      "view",
		Corpus:      "syn2015",
		Datetime:    "2017-02-11T11:02:31.880",
		IPAddress:   "195.113.53.66",
		IsAnonymous: true,
		IsQuery:     false,
		Limited:     false,
		ProcTime:    0.712,
		QueryType:   "cql",
		Type2:       "kontext",
		UserAgent:   "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:51.0) Gecko/20100101 Firefox/51.0",
		UserID:      100,
		GeoIP: GeoDataRecord{
			CountryCode2: "CZ",
			CountryName:  "Czech Republic",
			IP:           "195.113.53.66",
			Latitude:     49.4,
			Longitude:    17.674,
			Location:     [2]float32{17.6742, 49.3996},
		},
	}
}

func TestCreateID(t *testing.T) {
	rec := createRecord()
	fmt.Println("HASH: ", createID(rec))
	if createID(rec) != "2452d6c39ddd4dfcba2df61e1115511e547c09af" {
		t.Error("Hash match error")
	}
}

func TestImportCorpname(t *testing.T) {
	p := make(map[string]string)
	p["corpname"] = "omezeni/foobar7;x=10"
	r := &logs.LogRecord{Params: p}
	c := importCorpname(r)
	if c.Corpname != "foobar7" || c.limited != true {
		t.Error("Failed import corpname: ", c)
	}
}
<|endoftext|>


<commit_before>package model

import (
	"encoding/json"
	"time"
)

type StopVisitScheduleType string

const (
	STOP_VISIT_SCHEDULE_AIMED    StopVisitScheduleType = "aimed"
	STOP_VISIT_SCHEDULE_EXPECTED StopVisitScheduleType = "expected"
	STOP_VISIT_SCHEDULE_ACTUAL   StopVisitScheduleType = "actual"
)

type StopVisitSchedule struct {
	kind          StopVisitScheduleType
	departureTime time.Time
	arrivalTime   time.Time
}

func (schedule *StopVisitSchedule) Kind() StopVisitScheduleType {
	return schedule.kind
}

func (schedule *StopVisitSchedule) DepartureTime() time.Time {
	return schedule.departureTime
}

func (schedule *StopVisitSchedule) ArrivalTime() time.Time {
	return schedule.arrivalTime
}

func (schedule *StopVisitSchedule) MarshalJSON() ([]byte, error) {
	return json.Marshal(map[string]interface{}{
		"Kind":          schedule.kind,
		"DepartureTime": schedule.departureTime,
		"ArrivalTime":   schedule.arrivalTime,
	})
}

type StopVisitSchedules map[StopVisitScheduleType]*StopVisitSchedule

func NewStopVisitSchedules() StopVisitSchedules {
	schedules := make(StopVisitSchedules)
	return schedules
}

func (schedules StopVisitSchedules) SetSchedule(kind StopVisitScheduleType, departureTime time.Time, arrivalTime time.Time) {
	_, ok := schedules[kind]
	if !ok {
		schedules[kind] = &StopVisitSchedule{}
	}
	schedules[kind] = &StopVisitSchedule{
		kind:          kind,
		departureTime: departureTime,
		arrivalTime:   arrivalTime,
	}
}
<commit_msg>Don't send zero values for schedules in json. Refs #2477.<commit_after>package model

import (
	"encoding/json"
	"time"
)

type StopVisitScheduleType string

const (
	STOP_VISIT_SCHEDULE_AIMED    StopVisitScheduleType = "aimed"
	STOP_VISIT_SCHEDULE_EXPECTED StopVisitScheduleType = "expected"
	STOP_VISIT_SCHEDULE_ACTUAL   StopVisitScheduleType = "actual"
)

type StopVisitSchedule struct {
	kind          StopVisitScheduleType
	departureTime time.Time
	arrivalTime   time.Time
}

func (schedule *StopVisitSchedule) Kind() StopVisitScheduleType {
	return schedule.kind
}

func (schedule *StopVisitSchedule) DepartureTime() time.Time {
	return schedule.departureTime
}

func (schedule *StopVisitSchedule) ArrivalTime() time.Time {
	return schedule.arrivalTime
}

func (schedule *StopVisitSchedule) MarshalJSON() ([]byte, error) {
	jsonSchedule := map[string]interface{}{
		"Kind": schedule.kind,
	}
	if !schedule.departureTime.IsZero() {
		jsonSchedule["DepartureTime"] = schedule.departureTime
	}
	if !schedule.arrivalTime.IsZero() {
		jsonSchedule["ArrivalTime"] = schedule.arrivalTime
	}
	return json.Marshal(jsonSchedule)
}

type StopVisitSchedules map[StopVisitScheduleType]*StopVisitSchedule

func NewStopVisitSchedules() StopVisitSchedules {
	schedules := make(StopVisitSchedules)
	return schedules
}

func (schedules StopVisitSchedules) SetSchedule(kind StopVisitScheduleType, departureTime time.Time, arrivalTime time.Time) {
	_, ok := schedules[kind]
	if !ok {
		schedules[kind] = &StopVisitSchedule{}
	}
	schedules[kind] = &StopVisitSchedule{
		kind:          kind,
		departureTime: departureTime,
		arrivalTime:   arrivalTime,
	}
}
<|endoftext|>