<commit_before>package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"

	"github.com/raff/govaluate"
)

var (
	VERSION     = "0.12.0"
	SPACES      = regexp.MustCompile("\\s+")
	INVALID_POS = errors.New("invalid position")

	OK              = 0
	MATCH_FOUND     = 100
	MATCH_NOT_FOUND = 101

	SET = struct{}{}

	gitCommit, buildDate string
)

type Pos struct {
	Start, End *int
	Value      *string
}

func (p Pos) String() (result string) {
	if p.Start != nil {
		result = strconv.Itoa(*p.Start)
	} else {
		result += "FIRST"
	}

	result += " TO "

	if p.End != nil {
		result += strconv.Itoa(*p.End)
	} else {
		result += "LAST"
	}

	return
}

func (p *Pos) Set(s string) error {
	p.Start = nil
	p.End = nil

	parts := strings.Split(s, ":")
	if len(parts) < 1 || len(parts) > 2 {
		return INVALID_POS
	}

	if len(parts[0]) > 0 {
		v, err := strconv.Atoi(parts[0])
		if err != nil {
			p.Value = &s
			return nil
		}

		p.Start = &v
	}

	if len(parts) == 1 {
		// not a slice
		// note: same pointer (to distinguish from *p.End == *p.Start that returns an empty slice)
		p.End = p.Start
	} else if len(parts[1]) > 0 {
		v, err := strconv.Atoi(parts[1])
		if err != nil {
			return err
		}

		p.End = &v
	}

	return nil
}

func Slice(source []string, p Pos) []string {
	if p.Value != nil {
		return []string{*p.Value}
	}

	var start, end int

	if p.Start == nil {
		start = 0
	} else if *p.Start >= len(source) {
		return source[0:0]
	} else if *p.Start < 0 {
		start = len(source) + *p.Start

		if start < 0 {
			start = 0
		}
	} else {
		start = *p.Start
	}

	if p.End == p.Start {
		// this should return source[start]
		end = start + 1
	} else if p.End == nil || *p.End >= len(source) {
		return source[start:]
	} else if *p.End < 0 {
		end = len(source) + *p.End
	} else {
		end = *p.End
	}

	if end < start {
		end = start
	}

	return source[start:end]
}

func Quote(a []string) []string {
	q := make([]string, len(a))
	for i, s := range a {
		q[i] = fmt.Sprintf("%q", s)
	}

	return q
}

func Unquote(a []string) []string {
	q := make([]string, len(a))
	for i, s := range a {
		q[i] = strings.Trim(s, `"'`)
	}

	return q
}

func Print(format string, a []string) {
	printable := make([]interface{}, len(a))

	for i, v := range a {
		printable[i] = v
	}

	fmt.Printf(format, printable...)
}

func MustEvaluate(expr string) *govaluate.EvaluableExpression {
	ee, err := govaluate.NewEvaluableExpressionWithFunctions(expr, funcs)
	if err != nil {
		log.Fatalf("%q: %v", expr, err)
	}

	return ee
}

type Context struct {
	vars   map[string]interface{}
	fields []string
}

func (p *Context) Get(name string) (interface{}, error) {

	if strings.HasPrefix(name, "$") {
		n, err := strconv.Atoi(name[1:])
		if err != nil {
			return nil, err
		}

		if n < len(p.fields) {
			return p.fields[n], nil
		}

		return nil, fmt.Errorf("No field %q", name)
	}

	if value, ok := p.vars[name]; ok {
		return value, nil
	}

	return nil, fmt.Errorf("No variable %q", name)
}

func (p *Context) Set(name string, value interface{}) error {

	if strings.HasPrefix(name, "$") {
		return fmt.Errorf("Cannot override field %q", name)
	}

	p.vars[name] = value
	return nil
}

func toFloat(arg interface{}) (float64, error) {
	switch v := arg.(type) {
	case string:
		f, err := strconv.ParseFloat(v, 64)
		return f, err
	case bool:
		if v {
			return 1.0, nil
		} else {
			return 0.0, nil
		}
	default:
		return v.(float64), nil
	}
}

var funcs = map[string]govaluate.ExpressionFunction{
	"num": func(arguments ...interface{}) (interface{}, error) {
		if len(arguments) != 1 {
			return nil, fmt.Errorf("- one parameter expected, got %d", len(arguments))
		}

		return toFloat(arguments[0])
	},

	"int": func(arguments ...interface{}) (interface{}, error) {
		if len(arguments) != 1 {
			return nil, fmt.Errorf("- one parameter expected, got %d", len(arguments))
		}

		f, err := toFloat(arguments[0])
		return float64(int(f)), err
	},

	"len": func(arguments ...interface{}) (interface{}, error) {
		if len(arguments) != 1 {
			return nil, fmt.Errorf("- one parameter expected, got %d", len(arguments))
		}

		if s, ok := arguments[0].(string); ok {
			return float64(len(s)), nil
		}

		return nil, fmt.Errorf("- input should be a string")
	},
}

func main() {
	version := flag.Bool("version", false, "print version and exit")
	quote := flag.Bool("quote", false, "quote returned fields")
	unquote := flag.Bool("unquote", false, "quote returned fields")
	ifs := flag.String("ifs", " ", "input field separator")
	ire := flag.String("ifs-re", "", "input field separator (as regular expression)")
	ofs := flag.String("ofs", " ", "output field separator")
	re := flag.String("re", "", "regular expression for parsing input")
	grep := flag.String("grep", "", "output only lines that match the regular expression")
	format := flag.String("printf", "", "output is formatted according to specified format")
	matches := flag.String("matches", "", "return status code 100 if any line matches the specified pattern, 101 otherwise")
	after := flag.String("after", "", "process fields in line after specified tag")
	afterline := flag.String("after-line", "", "process lines after lines that matches")
	afterlinen := flag.Int("after-linen", 0, "process lines after n lines")
	printline := flag.Bool("line", false, "print line numbers")
	debug := flag.Bool("debug", false, "print debug info")
	exprbegin := flag.String("begin", "", "expression to be executed before processing lines")
	exprend := flag.String("end", "", "expression to be executed after processing lines")
	exprline := flag.String("expr", "", "expression to be executed for each line")
	uniq := flag.Bool("uniq", false, "print only unique lines")

	flag.Parse()

	if *version {
		extra := ""
		if gitCommit != "" {
			extra = fmt.Sprintf(" (%.4v %v)", gitCommit, buildDate)
		}

		fmt.Printf("%s version %s%v\n", path.Base(os.Args[0]), VERSION, extra)
		return
	}

	pos := make([]Pos, len(flag.Args()))

	for i, arg := range flag.Args() {
		pos[i].Set(arg)
	}

	if len(*format) > 0 && !strings.HasSuffix(*format, "\n") {
		*format += "\n"
	}

	var split_re, split_pattern, match_pattern, grep_pattern *regexp.Regexp
	var expr_begin, expr_end, expr_line *govaluate.EvaluableExpression

	status_code := OK

	if len(*matches) > 0 {
		match_pattern = regexp.MustCompile(*matches)
		status_code = MATCH_NOT_FOUND
	}

	if len(*grep) > 0 {
		grep_pattern = regexp.MustCompile(*grep)
	}

	if len(*re) > 0 {
		split_pattern = regexp.MustCompile(*re)
	}

	if len(*ire) > 0 {
		split_re = regexp.MustCompile(*ire)
	}

	if len(*exprbegin) > 0 {
		expr_begin = MustEvaluate(*exprbegin)
	}
	if len(*exprline) > 0 {
		expr_line = MustEvaluate(*exprline)
	}
	if len(*exprend) > 0 {
		expr_end = MustEvaluate(*exprend)
	}

	scanner := bufio.NewScanner(os.Stdin)
	len_after := len(*after)
	len_afterline := len(*afterline)
	lineno := 0
	uniques := map[string]struct{}{}

	expr_context := Context{vars: map[string]interface{}{}}

	if expr_begin != nil {
		_, err := expr_begin.Eval(&expr_context)
		if err != nil {
			log.Println("error in begin", err)
		}
		// else, should we print the result ?
	}

	for scanner.Scan() {
		if scanner.Err() != nil {
			log.Fatal(scanner.Err())
		}

		line := scanner.Text()

		lineno += 1

		if *afterlinen >= lineno {
			continue
		}

		if len_afterline > 0 {
			if strings.Contains(line, *afterline) {
				len_afterline = 0
			}

			continue
		}

		if len_after > 0 {
			i := strings.Index(line, *after)
			if i < 0 {
				continue // no match
			}

			line = line[i+len_after:]
		}

		expr_context.fields = []string{line} // $0 is the full line

		if grep_pattern != nil {
			if matches := grep_pattern.FindStringSubmatch(line); matches != nil {
				expr_context.fields = matches
			} else {
				continue
			}
		} else if split_pattern != nil {
			if matches := split_pattern.FindStringSubmatch(line); matches != nil {
				expr_context.fields = matches
			}
		} else if split_re != nil {
			// split line according to input regular expression
			expr_context.fields = append(expr_context.fields, split_re.Split(line, -1)...)
		} else if *ifs == " " {
			// split line on spaces (compact multiple spaces)
			expr_context.fields = append(expr_context.fields, SPACES.Split(strings.TrimSpace(line), -1)...)
		} else {
			// split line according to input field separator
			expr_context.fields = append(expr_context.fields, strings.Split(line, *ifs)...)
		}

		if *debug {
			log.Printf("input fields: %q\n", expr_context.fields)
			if len(pos) > 0 {
				log.Printf("output fields: %q\n", pos)
			}
		}

		var result []string

		// do some processing
		if len(pos) > 0 {
			result = make([]string, 0)

			for _, p := range pos {
				result = append(result, Slice(expr_context.fields, p)...)
			}
		} else {
			result = expr_context.fields[1:]
		}

		if *unquote {
			result = Unquote(result)
		}

		if *quote {
			result = Quote(result)
		}

		if *printline {
			fmt.Printf("%d: ", lineno)
		}

		if *uniq {
			l := strings.Join(result, " ")
			if _, ok := uniques[l]; ok {
				continue
			}

			uniques[l] = SET
		}

		if len(*format) > 0 {
			Print(*format, result)
		} else {
			// join the result according to output field separator
			fmt.Println(strings.Join(result, *ofs))
		}

		if match_pattern != nil && match_pattern.MatchString(line) {
			status_code = MATCH_FOUND
		}

		if expr_line != nil {
			_, err := expr_line.Eval(&expr_context)
			if err != nil {
				log.Println("error in expr", err)
			}
			// else, should we print the result ?
		}
	}

	if expr_end != nil {
		res, err := expr_end.Eval(&expr_context)
		if err != nil {
			log.Println("error in end", err)
		} else {
			fmt.Println(res)
		}
	}

	os.Exit(status_code)
}
<commit_msg>Added --test to test the value of an expression ( i.e. $1 == 'some string' )<commit_after>package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"

	"github.com/raff/govaluate"
)

var (
	VERSION     = "0.12.0"
	SPACES      = regexp.MustCompile("\\s+")
	INVALID_POS = errors.New("invalid position")

	OK              = 0
	MATCH_FOUND     = 100
	MATCH_NOT_FOUND = 101

	SET = struct{}{}

	gitCommit, buildDate string
)

type Pos struct {
	Start, End *int
	Value      *string
}

func (p Pos) String() (result string) {
	if p.Start != nil {
		result = strconv.Itoa(*p.Start)
	} else {
		result += "FIRST"
	}

	result += " TO "

	if p.End != nil {
		result += strconv.Itoa(*p.End)
	} else {
		result += "LAST"
	}

	return
}

func (p *Pos) Set(s string) error {
	p.Start = nil
	p.End = nil

	parts := strings.Split(s, ":")
	if len(parts) < 1 || len(parts) > 2 {
		return INVALID_POS
	}

	if len(parts[0]) > 0 {
		v, err := strconv.Atoi(parts[0])
		if err != nil {
			p.Value = &s
			return nil
		}

		p.Start = &v
	}

	if len(parts) == 1 {
		// not a slice
		// note: same pointer (to distinguish from *p.End == *p.Start that returns an empty slice)
		p.End = p.Start
	} else if len(parts[1]) > 0 {
		v, err := strconv.Atoi(parts[1])
		if err != nil {
			return err
		}

		p.End = &v
	}

	return nil
}

func Slice(source []string, p Pos) []string {
	if p.Value != nil {
		return []string{*p.Value}
	}

	var start, end int

	if p.Start == nil {
		start = 0
	} else if *p.Start >= len(source) {
		return source[0:0]
	} else if *p.Start < 0 {
		start = len(source) + *p.Start

		if start < 0 {
			start = 0
		}
	} else {
		start = *p.Start
	}

	if p.End == p.Start {
		// this should return source[start]
		end = start + 1
	} else if p.End == nil || *p.End >= len(source) {
		return source[start:]
	} else if *p.End < 0 {
		end = len(source) + *p.End
	} else {
		end = *p.End
	}

	if end < start {
		end = start
	}

	return source[start:end]
}

func Quote(a []string) []string {
	q := make([]string, len(a))
	for i, s := range a {
		q[i] = fmt.Sprintf("%q", s)
	}

	return q
}

func Unquote(a []string) []string {
	q := make([]string, len(a))
	for i, s := range a {
		q[i] = strings.Trim(s, `"'`)
	}

	return q
}

func Print(format string, a []string) {
	printable := make([]interface{}, len(a))

	for i, v := range a {
		printable[i] = v
	}

	fmt.Printf(format, printable...)
}

func MustEvaluate(expr string) *govaluate.EvaluableExpression {
	ee, err := govaluate.NewEvaluableExpressionWithFunctions(expr, funcs)
	if err != nil {
		log.Fatalf("%q: %v", expr, err)
	}

	return ee
}

type Context struct {
	vars   map[string]interface{}
	fields []string
}

func (p *Context) Get(name string) (interface{}, error) {

	if strings.HasPrefix(name, "$") {
		n, err := strconv.Atoi(name[1:])
		if err != nil {
			return nil, err
		}

		if n < len(p.fields) {
			return p.fields[n], nil
		}

		return nil, fmt.Errorf("No field %q", name)
	}

	if value, ok := p.vars[name]; ok {
		return value, nil
	}

	return nil, fmt.Errorf("No variable %q", name)
}

func (p *Context) Set(name string, value interface{}) error {

	if strings.HasPrefix(name, "$") {
		return fmt.Errorf("Cannot override field %q", name)
	}

	p.vars[name] = value
	return nil
}

func toFloat(arg interface{}) (float64, error) {
	switch v := arg.(type) {
	case string:
		f, err := strconv.ParseFloat(v, 64)
		return f, err
	case bool:
		if v {
			return 1.0, nil
		} else {
			return 0.0, nil
		}
	default:
		return v.(float64), nil
	}
}

var funcs = map[string]govaluate.ExpressionFunction{
	"num": func(arguments ...interface{}) (interface{}, error) {
		if len(arguments) != 1 {
			return nil, fmt.Errorf("- one parameter expected, got %d", len(arguments))
		}

		return toFloat(arguments[0])
	},

	"int": func(arguments ...interface{}) (interface{}, error) {
		if len(arguments) != 1 {
			return nil, fmt.Errorf("- one parameter expected, got %d", len(arguments))
		}

		f, err := toFloat(arguments[0])
		return float64(int(f)), err
	},

	"len": func(arguments ...interface{}) (interface{}, error) {
		if len(arguments) != 1 {
			return nil, fmt.Errorf("- one parameter expected, got %d", len(arguments))
		}

		if s, ok := arguments[0].(string); ok {
			return float64(len(s)), nil
		}

		return nil, fmt.Errorf("- input should be a string")
	},
}

func main() {
	version := flag.Bool("version", false, "print version and exit")
	quote := flag.Bool("quote", false, "quote returned fields")
	unquote := flag.Bool("unquote", false, "quote returned fields")
	ifs := flag.String("ifs", " ", "input field separator")
	ire := flag.String("ifs-re", "", "input field separator (as regular expression)")
	ofs := flag.String("ofs", " ", "output field separator")
	re := flag.String("re", "", "regular expression for parsing input")
	grep := flag.String("grep", "", "output only lines that match the regular expression")
	format := flag.String("printf", "", "output is formatted according to specified format")
	matches := flag.String("matches", "", "return status code 100 if any line matches the specified pattern, 101 otherwise")
	after := flag.String("after", "", "process fields in line after specified tag")
	afterline := flag.String("after-line", "", "process lines after lines that matches")
	afterlinen := flag.Int("after-linen", 0, "process lines after n lines")
	printline := flag.Bool("line", false, "print line numbers")
	debug := flag.Bool("debug", false, "print debug info")
	exprbegin := flag.String("begin", "", "expression to be executed before processing lines")
	exprend := flag.String("end", "", "expression to be executed after processing lines")
	exprline := flag.String("expr", "", "expression to be executed for each line")
	exprtest := flag.String("test", "", "test expression (skip line if false)")
	uniq := flag.Bool("uniq", false, "print only unique lines")

	flag.Parse()

	if *version {
		extra := ""
		if gitCommit != "" {
			extra = fmt.Sprintf(" (%.4v %v)", gitCommit, buildDate)
		}

		fmt.Printf("%s version %s%v\n", path.Base(os.Args[0]), VERSION, extra)
		return
	}

	pos := make([]Pos, len(flag.Args()))

	for i, arg := range flag.Args() {
		pos[i].Set(arg)
	}

	if len(*format) > 0 && !strings.HasSuffix(*format, "\n") {
		*format += "\n"
	}

	var split_re, split_pattern, match_pattern, grep_pattern *regexp.Regexp
	var expr_begin, expr_end, expr_line, expr_test *govaluate.EvaluableExpression

	status_code := OK

	if len(*matches) > 0 {
		match_pattern = regexp.MustCompile(*matches)
		status_code = MATCH_NOT_FOUND
	}

	if len(*grep) > 0 {
		grep_pattern = regexp.MustCompile(*grep)
	}

	if len(*re) > 0 {
		split_pattern = regexp.MustCompile(*re)
	}

	if len(*ire) > 0 {
		split_re = regexp.MustCompile(*ire)
	}

	if len(*exprbegin) > 0 {
		expr_begin = MustEvaluate(*exprbegin)
	}
	if len(*exprline) > 0 {
		expr_line = MustEvaluate(*exprline)
	}
	if len(*exprend) > 0 {
		expr_end = MustEvaluate(*exprend)
	}
	if len(*exprtest) > 0 {
		expr_test = MustEvaluate(*exprtest)
	}

	scanner := bufio.NewScanner(os.Stdin)
	len_after := len(*after)
	len_afterline := len(*afterline)
	lineno := 0
	uniques := map[string]struct{}{}

	expr_context := Context{vars: map[string]interface{}{}}

	if expr_begin != nil {
		_, err := expr_begin.Eval(&expr_context)
		if err != nil {
			log.Println("error in begin", err)
		}
		// else, should we print the result ?
	}

	for scanner.Scan() {
		if scanner.Err() != nil {
			log.Fatal(scanner.Err())
		}

		line := scanner.Text()

		lineno += 1

		if *afterlinen >= lineno {
			continue
		}

		if len_afterline > 0 {
			if strings.Contains(line, *afterline) {
				len_afterline = 0
			}

			continue
		}

		if len_after > 0 {
			i := strings.Index(line, *after)
			if i < 0 {
				continue // no match
			}

			line = line[i+len_after:]
		}

		expr_context.fields = []string{line} // $0 is the full line

		if grep_pattern != nil {
			if matches := grep_pattern.FindStringSubmatch(line); matches != nil {
				expr_context.fields = matches
			} else {
				continue
			}
		} else if split_pattern != nil {
			if matches := split_pattern.FindStringSubmatch(line); matches != nil {
				expr_context.fields = matches
			}
		} else if split_re != nil {
			// split line according to input regular expression
			expr_context.fields = append(expr_context.fields, split_re.Split(line, -1)...)
		} else if *ifs == " " {
			// split line on spaces (compact multiple spaces)
			expr_context.fields = append(expr_context.fields, SPACES.Split(strings.TrimSpace(line), -1)...)
		} else {
			// split line according to input field separator
			expr_context.fields = append(expr_context.fields, strings.Split(line, *ifs)...)
		}

		if *debug {
			log.Printf("input fields: %q\n", expr_context.fields)
			if len(pos) > 0 {
				log.Printf("output fields: %q\n", pos)
			}
		}

		var result []string

		// do some processing
		if len(pos) > 0 {
			result = make([]string, 0)

			for _, p := range pos {
				result = append(result, Slice(expr_context.fields, p)...)
			}
		} else {
			result = expr_context.fields[1:]
		}

		if *unquote {
			result = Unquote(result)
		}

		if *quote {
			result = Quote(result)
		}

		if *printline {
			fmt.Printf("%d: ", lineno)
		}

		if expr_test != nil {
			test, err := expr_test.Eval(&expr_context)
			if err != nil {
				log.Println("error in expr", err)
			} else if !test.(bool) {
				continue
			}
		}

		if *uniq {
			l := strings.Join(result, " ")
			if _, ok := uniques[l]; ok {
				continue
			}

			uniques[l] = SET
		}

		if len(*format) > 0 {
			Print(*format, result)
		} else {
			// join the result according to output field separator
			fmt.Println(strings.Join(result, *ofs))
		}

		if match_pattern != nil && match_pattern.MatchString(line) {
			status_code = MATCH_FOUND
		}

		if expr_line != nil {
			_, err := expr_line.Eval(&expr_context)
			if err != nil {
				log.Println("error in expr", err)
			}
			// else, should we print the result ?
		}
	}

	if expr_end != nil {
		res, err := expr_end.Eval(&expr_context)
		if err != nil {
			log.Println("error in end", err)
		} else {
			fmt.Println(res)
		}
	}

	os.Exit(status_code)
}
<|endoftext|>


<commit_before>// Package ohmyglob provides a minimal glob matching utility.
package ohmyglob

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"

	log "github.com/cihub/seelog"
)

var (
	// Logger is used to log trace-level info; logging is completely disabled by default but can be changed by replacing
	// this with a configured logger
	Logger log.LoggerInterface
	// Runes that, in addition to the separator, mean something when they appear in the glob (includes Escaper)
	expanders = []rune{'?', '*', Escaper}
	// Map built from expanders
	expandersMap map[rune]bool
	// Escaper is the character used to escape a meaningful character
	Escaper = '\\'
)

func init() {
	expandersMap = make(map[rune]bool, len(expanders))
	for _, r := range expanders {
		expandersMap[r] = true
	}

	if Logger == nil {
		var err error
		Logger, err = log.LoggerFromWriterWithMinLevel(os.Stderr, log.CriticalLvl) // seelog bug means we can't use log.Off
		if err != nil {
			panic(err)
		}
	}
}

type processedToken struct {
	contents  *bytes.Buffer
	tokenType tc
}

type parserState struct {
	options *Options
	// The regex-escaped separator character
	escapedSeparator string
	processedTokens  []processedToken
}

// GlobMatcher is the basic interface of a Glob or GlobSet. It provides a Regexp-style interface for checking matches.
type GlobMatcher interface {
	// Match reports whether the Glob matches the byte slice b
	Match(b []byte) bool
	// MatchReader reports whether the Glob matches the text read by the RuneReader
	MatchReader(r io.RuneReader) bool
	// MatchString reports whether the Glob matches the string s
	MatchString(s string) bool
}

// Glob is a single glob pattern; implements GlobMatcher. A Glob is immutable.
type Glob interface {
	GlobMatcher
	// String returns the pattern that was used to create the Glob
	String() string
	// IsNegative returns whether the pattern was negated (prefixed with !)
	IsNegative() bool
}

// Glob is a glob pattern that has been compiled into a regular expression.
type globImpl struct {
	*regexp.Regexp
	// The separator from options, escaped for appending to the regexBuffer (only available during parsing)
	// The input pattern
	globPattern string
	// State only available during parsing
	parserState *parserState
	// Set to true if the pattern was negated
	negated bool
}

// Options modify the behaviour of Glob parsing
type Options struct {
	// The character used to split path components
	Separator rune
	// Set to false to allow any prefix before the glob match
	MatchAtStart bool
	// Set to false to allow any suffix after the glob match
	MatchAtEnd bool
}

// DefaultOptions are a default set of Options that uses a forward slash as a separator, and require a full match
var DefaultOptions = &Options{
	Separator:    '/',
	MatchAtStart: true,
	MatchAtEnd:   true,
}

func (g *globImpl) String() string {
	return g.globPattern
}

func (g *globImpl) IsNegative() bool {
	return g.negated
}

func popLastToken(state *parserState) *processedToken {
	state.processedTokens = state.processedTokens[:len(state.processedTokens)-1]
	if len(state.processedTokens) > 0 {
		return &state.processedTokens[len(state.processedTokens)-1]
	}
	return &processedToken{}
}

// Compile parses the given glob pattern and convertes it to a Glob. If no options are given, the DefaultOptions are
// used.
func Compile(pattern string, options *Options) (Glob, error) {
	pattern = strings.TrimSpace(pattern)
	if options == nil {
		options = DefaultOptions
	} else {
		// Check that the separator is not an expander
		for _, expander := range expanders {
			if options.Separator == expander {
				return nil, fmt.Errorf("'%s' is not allowed as a separator", string(options.Separator))
			}
		}
	}

	state := &parserState{
		options:          options,
		escapedSeparator: escapeRegexComponent(string(options.Separator)),
		processedTokens:  make([]processedToken, 0, 10),
	}
	glob := &globImpl{
		Regexp:      nil,
		globPattern: pattern,
		negated:     false,
		parserState: state,
	}

	regexBuf := new(bytes.Buffer)
	if options.MatchAtStart {
		regexBuf.WriteRune('^')
	}

	var err error
	// Transform into a regular expression pattern
	// 1. Parse negation prefixes
	pattern, err = parseNegation(pattern, glob)
	if err != nil {
		return nil, err
	}

	// 2. Tokenise and convert!
	tokeniser := newGlobTokeniser(strings.NewReader(pattern), options)
	lastProcessedToken := &processedToken{}
	for tokeniser.Scan() {
		if err = tokeniser.Err(); err != nil {
			return nil, err
		}

		token, tokenType := tokeniser.Token()
		t := processedToken{
			contents:  nil,
			tokenType: tokenType,
		}

		// Special cases
		if tokenType == tcGlobStar && tokeniser.Peek() {
			// If this is a globstar and the next token is a separator, consume it (the globstar pattern itself includes
			// a separator)
			if err = tokeniser.PeekErr(); err != nil {
				return nil, err
			}
			_, peekedType := tokeniser.PeekToken()
			if peekedType == tcSeparator {
				tokeniser.Scan()
			}
		}
		if tokenType == tcGlobStar && lastProcessedToken.tokenType == tcGlobStar {
			// If the last token was a globstar and this is too, remove the last. We don't remove this globstar because
			// it may now be the last in the pattern, which is special
			lastProcessedToken = popLastToken(state)
		}
		if tokenType == tcGlobStar && lastProcessedToken.tokenType == tcSeparator && !tokeniser.Peek() {
			// If this is the last token, and it's a globstar, remove a preceeding separator
			lastProcessedToken = popLastToken(state)
		}

		t.contents, err = processToken(token, tokenType, glob, tokeniser)
		if err != nil {
			return nil, err
		}

		lastProcessedToken = &t
		state.processedTokens = append(state.processedTokens, t)
	}

	fmt.Print(lastProcessedToken)

	for _, t := range state.processedTokens {
		t.contents.WriteTo(regexBuf)
	}

	if options.MatchAtEnd {
		regexBuf.WriteRune('$')
	}

	regexString := regexBuf.String()
	Logger.Infof("[ohmyglob:Glob] Compiled \"%s\" to regex `%s` (negated: %v)", pattern, regexString, glob.negated)
	re, err := regexp.Compile(regexString)
	if err != nil {
		return nil, err
	}

	glob.parserState = nil
	glob.Regexp = re

	return glob, nil
}

func parseNegation(pattern string, glob *globImpl) (string, error) {
	if pattern == "" {
		return pattern, nil
	}

	negations := 0
	for _, char := range pattern {
		if char == '!' {
			glob.negated = !glob.negated
			negations++
		}
	}

	return pattern[negations:], nil
}

func processToken(token string, tokenType tc, glob *globImpl, tokeniser *globTokeniser) (*bytes.Buffer, error) {
	state := glob.parserState
	buf := new(bytes.Buffer)

	switch tokenType {
	case tcGlobStar:
		// Globstars also take care of surrounding separators; separator components before and after a globstar are
		// suppressed
		isLast := !tokeniser.Peek()
		buf.WriteString("(?:")
		if isLast {
			buf.WriteString(state.escapedSeparator)
		}
		buf.WriteString(".+")
		if !isLast {
			buf.WriteString(state.escapedSeparator)
		}
		buf.WriteString(")?")
	case tcStar:
		buf.WriteString("[^")
		buf.WriteString(state.escapedSeparator)
		buf.WriteString("]*")
	case tcAny:
		buf.WriteString("[^")
		buf.WriteString(state.escapedSeparator)
		buf.WriteString("]")
	case tcSeparator:
		buf.WriteString(state.escapedSeparator)
	case tcLiteral:
		buf.WriteString(escapeRegexComponent(token))
	}

	return buf, nil
}
<commit_msg>Remove terrible debugging print<commit_after>// Package ohmyglob provides a minimal glob matching utility.
package ohmyglob

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"

	log "github.com/cihub/seelog"
)

var (
	// Logger is used to log trace-level info; logging is completely disabled by default but can be changed by replacing
	// this with a configured logger
	Logger log.LoggerInterface
	// Runes that, in addition to the separator, mean something when they appear in the glob (includes Escaper)
	expanders = []rune{'?', '*', Escaper}
	// Map built from expanders
	expandersMap map[rune]bool
	// Escaper is the character used to escape a meaningful character
	Escaper = '\\'
)

func init() {
	expandersMap = make(map[rune]bool, len(expanders))
	for _, r := range expanders {
		expandersMap[r] = true
	}

	if Logger == nil {
		var err error
		Logger, err = log.LoggerFromWriterWithMinLevel(os.Stderr, log.CriticalLvl) // seelog bug means we can't use log.Off
		if err != nil {
			panic(err)
		}
	}
}

type processedToken struct {
	contents  *bytes.Buffer
	tokenType tc
}

type parserState struct {
	options *Options
	// The regex-escaped separator character
	escapedSeparator string
	processedTokens  []processedToken
}

// GlobMatcher is the basic interface of a Glob or GlobSet. It provides a Regexp-style interface for checking matches.
type GlobMatcher interface {
	// Match reports whether the Glob matches the byte slice b
	Match(b []byte) bool
	// MatchReader reports whether the Glob matches the text read by the RuneReader
	MatchReader(r io.RuneReader) bool
	// MatchString reports whether the Glob matches the string s
	MatchString(s string) bool
}

// Glob is a single glob pattern; implements GlobMatcher. A Glob is immutable.
type Glob interface {
	GlobMatcher
	// String returns the pattern that was used to create the Glob
	String() string
	// IsNegative returns whether the pattern was negated (prefixed with !)
	IsNegative() bool
}

// Glob is a glob pattern that has been compiled into a regular expression.
type globImpl struct {
	*regexp.Regexp
	// The separator from options, escaped for appending to the regexBuffer (only available during parsing)
	// The input pattern
	globPattern string
	// State only available during parsing
	parserState *parserState
	// Set to true if the pattern was negated
	negated bool
}

// Options modify the behaviour of Glob parsing
type Options struct {
	// The character used to split path components
	Separator rune
	// Set to false to allow any prefix before the glob match
	MatchAtStart bool
	// Set to false to allow any suffix after the glob match
	MatchAtEnd bool
}

// DefaultOptions are a default set of Options that uses a forward slash as a separator, and require a full match
var DefaultOptions = &Options{
	Separator:    '/',
	MatchAtStart: true,
	MatchAtEnd:   true,
}

func (g *globImpl) String() string {
	return g.globPattern
}

func (g *globImpl) IsNegative() bool {
	return g.negated
}

func popLastToken(state *parserState) *processedToken {
	state.processedTokens = state.processedTokens[:len(state.processedTokens)-1]
	if len(state.processedTokens) > 0 {
		return &state.processedTokens[len(state.processedTokens)-1]
	}
	return &processedToken{}
}

// Compile parses the given glob pattern and convertes it to a Glob. If no options are given, the DefaultOptions are
// used.
func Compile(pattern string, options *Options) (Glob, error) {
	pattern = strings.TrimSpace(pattern)
	if options == nil {
		options = DefaultOptions
	} else {
		// Check that the separator is not an expander
		for _, expander := range expanders {
			if options.Separator == expander {
				return nil, fmt.Errorf("'%s' is not allowed as a separator", string(options.Separator))
			}
		}
	}

	state := &parserState{
		options:          options,
		escapedSeparator: escapeRegexComponent(string(options.Separator)),
		processedTokens:  make([]processedToken, 0, 10),
	}
	glob := &globImpl{
		Regexp:      nil,
		globPattern: pattern,
		negated:     false,
		parserState: state,
	}

	regexBuf := new(bytes.Buffer)
	if options.MatchAtStart {
		regexBuf.WriteRune('^')
	}

	var err error
	// Transform into a regular expression pattern
	// 1. Parse negation prefixes
	pattern, err = parseNegation(pattern, glob)
	if err != nil {
		return nil, err
	}

	// 2. Tokenise and convert!
	tokeniser := newGlobTokeniser(strings.NewReader(pattern), options)
	lastProcessedToken := &processedToken{}
	for tokeniser.Scan() {
		if err = tokeniser.Err(); err != nil {
			return nil, err
		}

		token, tokenType := tokeniser.Token()
		t := processedToken{
			contents:  nil,
			tokenType: tokenType,
		}

		// Special cases
		if tokenType == tcGlobStar && tokeniser.Peek() {
			// If this is a globstar and the next token is a separator, consume it (the globstar pattern itself includes
			// a separator)
			if err = tokeniser.PeekErr(); err != nil {
				return nil, err
			}
			_, peekedType := tokeniser.PeekToken()
			if peekedType == tcSeparator {
				tokeniser.Scan()
			}
		}
		if tokenType == tcGlobStar && lastProcessedToken.tokenType == tcGlobStar {
			// If the last token was a globstar and this is too, remove the last. We don't remove this globstar because
			// it may now be the last in the pattern, which is special
			lastProcessedToken = popLastToken(state)
		}
		if tokenType == tcGlobStar && lastProcessedToken.tokenType == tcSeparator && !tokeniser.Peek() {
			// If this is the last token, and it's a globstar, remove a preceeding separator
			lastProcessedToken = popLastToken(state)
		}

		t.contents, err = processToken(token, tokenType, glob, tokeniser)
		if err != nil {
			return nil, err
		}

		lastProcessedToken = &t
		state.processedTokens = append(state.processedTokens, t)
	}

	for _, t := range state.processedTokens {
		t.contents.WriteTo(regexBuf)
	}

	if options.MatchAtEnd {
		regexBuf.WriteRune('$')
	}

	regexString := regexBuf.String()
	Logger.Infof("[ohmyglob:Glob] Compiled \"%s\" to regex `%s` (negated: %v)", pattern, regexString, glob.negated)
	re, err := regexp.Compile(regexString)
	if err != nil {
		return nil, err
	}

	glob.parserState = nil
	glob.Regexp = re

	return glob, nil
}

func parseNegation(pattern string, glob *globImpl) (string, error) {
	if pattern == "" {
		return pattern, nil
	}

	negations := 0
	for _, char := range pattern {
		if char == '!' {
			glob.negated = !glob.negated
			negations++
		}
	}

	return pattern[negations:], nil
}

func processToken(token string, tokenType tc, glob *globImpl, tokeniser *globTokeniser) (*bytes.Buffer, error) {
	state := glob.parserState
	buf := new(bytes.Buffer)

	switch tokenType {
	case tcGlobStar:
		// Globstars also take care of surrounding separators; separator components before and after a globstar are
		// suppressed
		isLast := !tokeniser.Peek()
		buf.WriteString("(?:")
		if isLast {
			buf.WriteString(state.escapedSeparator)
		}
		buf.WriteString(".+")
		if !isLast {
			buf.WriteString(state.escapedSeparator)
		}
		buf.WriteString(")?")
	case tcStar:
		buf.WriteString("[^")
		buf.WriteString(state.escapedSeparator)
		buf.WriteString("]*")
	case tcAny:
		buf.WriteString("[^")
		buf.WriteString(state.escapedSeparator)
		buf.WriteString("]")
	case tcSeparator:
		buf.WriteString(state.escapedSeparator)
	case tcLiteral:
		buf.WriteString(escapeRegexComponent(token))
	}

	return buf, nil
}
<|endoftext|>


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

package client

import (
	"crypto"
	"errors"
	"fmt"
	"runtime"
	"sync"

	"github.com/google/trillian"
	"github.com/google/trillian/crypto/keys/der"
	"github.com/google/trillian/merkle"
	"github.com/google/trillian/merkle/hashers"
	"github.com/google/trillian/trees"
	"github.com/google/trillian/types"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	tcrypto "github.com/google/trillian/crypto"
)

// MapVerifier allows verification of output from Trillian Maps; it is safe
// for concurrent use (as its contents are fixed after construction).
type MapVerifier struct {
	MapID int64
	// Hasher is the hash strategy used to compute nodes in the Merkle tree.
	Hasher hashers.MapHasher
	// PubKey verifies the signature on the digest of MapRoot.
	PubKey crypto.PublicKey
	// SigHash computes the digest of MapRoot for signing.
	SigHash crypto.Hash
}

// NewMapVerifierFromTree creates a new MapVerifier using the information
// from a Trillian Tree object.
func NewMapVerifierFromTree(config *trillian.Tree) (*MapVerifier, error) {
	if config == nil {
		return nil, errors.New("client: NewMapVerifierFromTree(): nil config")
	}
	if got, want := config.TreeType, trillian.TreeType_MAP; got != want {
		return nil, fmt.Errorf("client: NewMapVerifierFromTree(): TreeType: %v, want %v", got, want)
	}

	mapHasher, err := hashers.NewMapHasher(config.HashStrategy)
	if err != nil {
		return nil, fmt.Errorf("failed creating MapHasher: %v", err)
	}

	mapPubKey, err := der.UnmarshalPublicKey(config.PublicKey.GetDer())
	if err != nil {
		return nil, fmt.Errorf("failed parsing Map public key: %v", err)
	}

	sigHash, err := trees.Hash(config)
	if err != nil {
		return nil, fmt.Errorf("client: NewMapVerifierFromTree(): Failed parsing Map signature hash: %v", err)
	}

	return &MapVerifier{
		MapID:   config.TreeId,
		Hasher:  mapHasher,
		PubKey:  mapPubKey,
		SigHash: sigHash,
	}, nil
}

// VerifyMapLeafInclusion verifies a MapLeafInclusion object against a signed map root.
func (m *MapVerifier) VerifyMapLeafInclusion(smr *trillian.SignedMapRoot, leafProof *trillian.MapLeafInclusion) error {
	root, err := m.VerifySignedMapRoot(smr)
	if err != nil {
		return err
	}
	return m.VerifyMapLeafInclusionHash(root.RootHash, leafProof)
}

// VerifyMapLeafInclusionHash verifies a MapLeafInclusion object against a root hash.
func (m *MapVerifier) VerifyMapLeafInclusionHash(rootHash []byte, leafProof *trillian.MapLeafInclusion) error {
	return merkle.VerifyMapInclusionProof(m.MapID, leafProof.GetLeaf(), rootHash, leafProof.GetInclusion(), m.Hasher)
}

// VerifySignedMapRoot verifies the signature on a SignedMapRoot.
func (m *MapVerifier) VerifySignedMapRoot(smr *trillian.SignedMapRoot) (*types.MapRootV1, error) {
	return tcrypto.VerifySignedMapRoot(m.PubKey, m.SigHash, smr)
}

// VerifyMapLeavesResponse verifies the responses of GetMapLeaves and GetMapLeavesByRevision.
// To accept any map revision, pass -1 as revision.
func (m *MapVerifier) VerifyMapLeavesResponse(indexes [][]byte, revision int64, resp *trillian.GetMapLeavesResponse) ([]*trillian.MapLeaf, error) {
	if got, want := len(resp.MapLeafInclusion), len(indexes); got != want {
		return nil, status.Errorf(codes.Internal, "got %v leaves, want %v", got, want)
	}
	mapRoot, err := m.VerifySignedMapRoot(resp.GetMapRoot())
	if err != nil {
		return nil, status.Errorf(codes.Internal, "VerifySignedMapRoot(%v): %v", m.MapID, err)
	}
	if revision != -1 && int64(mapRoot.Revision) != revision {
		return nil, status.Errorf(codes.Internal, "got map revision %v, want %v", mapRoot.Revision, revision)
	}

	proofs := make(chan *trillian.MapLeafInclusion)
	done := make(chan struct{})
	defer close(done)
	go func() {
		defer close(proofs)
		for _, p := range resp.MapLeafInclusion {
			select {
			case proofs <- p:
			case <-done:
				return
			}
		}
	}()
	errors := make(chan error)
	go func() {
		defer close(errors)
		var wg sync.WaitGroup
		defer wg.Wait()
		for w := 0; w < runtime.NumCPU(); w++ {
			wg.Add(1)
			go func() {
				defer wg.Done()
				for p := range proofs {
					select {
					case errors <- m.VerifyMapLeafInclusionHash(mapRoot.RootHash, p):
					case <-done:
						return
					}
				}
			}()
		}
	}()
	for err := range errors {
		if err != nil {
			return nil, status.Errorf(status.Code(err), "map: VerifyMapLeafInclusion(): %v", err)
		}
	}

	leaves := make([]*trillian.MapLeaf, 0, len(resp.MapLeafInclusion))
	for _, i := range resp.MapLeafInclusion {
		leaves = append(leaves, i.Leaf)
	}
	return leaves, nil
}
<commit_msg>Use errgroup in VerifyMapLeavesResponse (#1665)<commit_after>// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package client

import (
	"crypto"
	"errors"
	"fmt"

	"github.com/google/trillian"
	"github.com/google/trillian/crypto/keys/der"
	"github.com/google/trillian/merkle"
	"github.com/google/trillian/merkle/hashers"
	"github.com/google/trillian/trees"
	"github.com/google/trillian/types"
	"golang.org/x/sync/errgroup"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	tcrypto "github.com/google/trillian/crypto"
)

// MapVerifier allows verification of output from Trillian Maps; it is safe
// for concurrent use (as its contents are fixed after construction).
type MapVerifier struct {
	MapID int64
	// Hasher is the hash strategy used to compute nodes in the Merkle tree.
	Hasher hashers.MapHasher
	// PubKey verifies the signature on the digest of MapRoot.
	PubKey crypto.PublicKey
	// SigHash computes the digest of MapRoot for signing.
	SigHash crypto.Hash
}

// NewMapVerifierFromTree creates a new MapVerifier using the information
// from a Trillian Tree object.
func NewMapVerifierFromTree(config *trillian.Tree) (*MapVerifier, error) {
	if config == nil {
		return nil, errors.New("client: NewMapVerifierFromTree(): nil config")
	}
	if got, want := config.TreeType, trillian.TreeType_MAP; got != want {
		return nil, fmt.Errorf("client: NewMapVerifierFromTree(): TreeType: %v, want %v", got, want)
	}

	mapHasher, err := hashers.NewMapHasher(config.HashStrategy)
	if err != nil {
		return nil, fmt.Errorf("failed creating MapHasher: %v", err)
	}

	mapPubKey, err := der.UnmarshalPublicKey(config.PublicKey.GetDer())
	if err != nil {
		return nil, fmt.Errorf("failed parsing Map public key: %v", err)
	}

	sigHash, err := trees.Hash(config)
	if err != nil {
		return nil, fmt.Errorf("client: NewMapVerifierFromTree(): Failed parsing Map signature hash: %v", err)
	}

	return &MapVerifier{
		MapID:   config.TreeId,
		Hasher:  mapHasher,
		PubKey:  mapPubKey,
		SigHash: sigHash,
	}, nil
}

// VerifyMapLeafInclusion verifies a MapLeafInclusion object against a signed map root.
func (m *MapVerifier) VerifyMapLeafInclusion(smr *trillian.SignedMapRoot, leafProof *trillian.MapLeafInclusion) error {
	root, err := m.VerifySignedMapRoot(smr)
	if err != nil {
		return err
	}
	return m.VerifyMapLeafInclusionHash(root.RootHash, leafProof)
}

// VerifyMapLeafInclusionHash verifies a MapLeafInclusion object against a root hash.
func (m *MapVerifier) VerifyMapLeafInclusionHash(rootHash []byte, leafProof *trillian.MapLeafInclusion) error {
	return merkle.VerifyMapInclusionProof(m.MapID, leafProof.GetLeaf(), rootHash, leafProof.GetInclusion(), m.Hasher)
}

// VerifySignedMapRoot verifies the signature on a SignedMapRoot.
func (m *MapVerifier) VerifySignedMapRoot(smr *trillian.SignedMapRoot) (*types.MapRootV1, error) {
	return tcrypto.VerifySignedMapRoot(m.PubKey, m.SigHash, smr)
}

// VerifyMapLeavesResponse verifies the responses of GetMapLeaves and GetMapLeavesByRevision.
// To accept any map revision, pass -1 as revision.
func (m *MapVerifier) VerifyMapLeavesResponse(indexes [][]byte, revision int64, resp *trillian.GetMapLeavesResponse) ([]*trillian.MapLeaf, error) {
	if got, want := len(resp.MapLeafInclusion), len(indexes); got != want {
		return nil, status.Errorf(codes.Internal, "got %v leaves, want %v", got, want)
	}
	mapRoot, err := m.VerifySignedMapRoot(resp.GetMapRoot())
	if err != nil {
		return nil, status.Errorf(codes.Internal, "VerifySignedMapRoot(%v): %v", m.MapID, err)
	}
	if revision != -1 && int64(mapRoot.Revision) != revision {
		return nil, status.Errorf(codes.Internal, "got map revision %v, want %v", mapRoot.Revision, revision)
	}

	var g errgroup.Group
	for _, p := range resp.MapLeafInclusion {
		p := p
		g.Go(func() error {
			return m.VerifyMapLeafInclusionHash(mapRoot.RootHash, p)
		})
	}
	if err := g.Wait(); err != nil {
		return nil, status.Errorf(status.Code(err), "map: VerifyMapLeafInclusion(): %v", err)
	}

	leaves := make([]*trillian.MapLeaf, 0, len(resp.MapLeafInclusion))
	for _, i := range resp.MapLeafInclusion {
		leaves = append(leaves, i.Leaf)
	}
	return leaves, nil
}
<|endoftext|>


<commit_before>/*
 * Author: Emmanuel Odeke <odeke@ualberta.ca>
 *  Trie hashmap implementation
*/

package goht

import (
    "fmt"
    "math"
)

const (
    Base = 10
)

type Trie struct {
    children [] *Trie
    Value interface{}
    EOS bool
}

func New(v interface{}) *Trie {
   return new(Trie).Init(v)
}

func (t *Trie) Init(v interface{}) *Trie {
    t.EOS = false
    t.children = make([] *Trie, Base, Base)
    t.Value = v
    return t
}

func (t *Trie) SetTValue(v interface {}) (err error) {
    err = nil
    if t == nil {
        err = fmt.Errorf("Cannot set value of a null node")
    } else {
        t.Value = v
        t.EOS = true
    }

    return
}

func (t *Trie) accessOp (h int, isPop bool) (retr interface{}) {
    // Handles 'Get' and 'Pop' operations. This is toggled by the param 'isPop'
    trav := t
    retr = nil

    powerCount := math.Ceil(math.Log10(float64(h)))
    for {
        if trav == nil || trav.children == nil {
            return
        } else if powerCount <= 0 {
            break
        } else {
            trav = trav.children[h % 10]
            h /= 10
            powerCount--
        }
    }

    if trav.EOS == true {
        retr = trav.Value
        if isPop == true {
            trav.EOS = false
            trav.Value = nil
        }
    }

    return
}

func (t *Trie) Pop(h int) (retr interface{}) {
    retr = nil
    if t == nil {
        return
    } else {
        retr = t.accessOp(h, true)
    }

    return
}

func (t *Trie) Get(h int) (retr interface{}) {
    retr = nil
    if t == nil {
        return
    } else {
        retr = t.accessOp(h, false)
    }

    return
}

func (t *Trie) Put(h int, v interface{}) (err error) {
    err = nil
    if t == nil || h < 0 {
        t = new(Trie).Init(v)
        return
    }

    trav := t
    radixCount := math.Ceil(math.Log10(float64(h)))

    for {
        if radixCount <= 0 {
            break
        }

        mod := h % Base
        if trav.children[mod] == nil {
            trav.children[mod] = New(nil)
        }

        trav = trav.children[mod]

        h /= Base
        radixCount--
    }

    if trav == nil {
        err = fmt.Errorf("Trav should not be nil")
    } else {
        err = trav.SetTValue(v)
    }

    return
}
<commit_msg>Using const value ref of Base<commit_after>/*
 * Author: Emmanuel Odeke <odeke@ualberta.ca>
 *  Trie hashmap implementation
*/

package goht

import (
    "fmt"
    "math"
)

const (
    Base = 10
)

type Trie struct {
    children [] *Trie
    Value interface{}
    EOS bool
}

func New(v interface{}) *Trie {
   return new(Trie).Init(v)
}

func (t *Trie) Init(v interface{}) *Trie {
    t.EOS = false
    t.children = make([] *Trie, Base, Base)
    t.Value = v
    return t
}

func (t *Trie) SetTValue(v interface {}) (err error) {
    err = nil
    if t == nil {
        err = fmt.Errorf("Cannot set value of a null node")
    } else {
        t.Value = v
        t.EOS = true
    }

    return
}

func (t *Trie) accessOp (h int, isPop bool) (retr interface{}) {
    // Handles 'Get' and 'Pop' operations. This is toggled by the param 'isPop'
    trav := t
    retr = nil

    powerCount := math.Ceil(math.Log10(float64(h)))
    for {
        if trav == nil || trav.children == nil {
            return
        } else if powerCount <= 0 {
            break
        } else {
            trav = trav.children[h % Base]
            h /= Base
            powerCount--
        }
    }

    if trav.EOS == true {
        retr = trav.Value
        if isPop == true {
            trav.EOS = false
            trav.Value = nil
        }
    }

    return
}

func (t *Trie) Pop(h int) (retr interface{}) {
    retr = nil
    if t == nil {
        return
    } else {
        retr = t.accessOp(h, true)
    }

    return
}

func (t *Trie) Get(h int) (retr interface{}) {
    retr = nil
    if t == nil {
        return
    } else {
        retr = t.accessOp(h, false)
    }

    return
}

func (t *Trie) Put(h int, v interface{}) (err error) {
    err = nil
    if t == nil || h < 0 {
        t = new(Trie).Init(v)
        return
    }

    trav := t
    radixCount := math.Ceil(math.Log10(float64(h)))

    for {
        if radixCount <= 0 {
            break
        }

        mod := h % Base
        if trav.children[mod] == nil {
            trav.children[mod] = New(nil)
        }

        trav = trav.children[mod]

        h /= Base
        radixCount--
    }

    if trav == nil {
        err = fmt.Errorf("Trav should not be nil")
    } else {
        err = trav.SetTValue(v)
    }

    return
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"sort"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
)

const (
	port    = ":8001"
	dump    = "gone.gob"
	logf    = "gone.log"
	unknown = "unknown"
)

var (
	tracks = make(Tracker)
	tmpl   = template.Must(template.ParseFiles("index.html"))
	zzz    bool
)

type Tracker map[Window]*Track

type Track struct {
	Seen  time.Time
	Spent time.Duration
}

type Window struct {
	Class string
	Name  string
}

type Xorg struct {
	X           *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
}

func (t Track) String() string {
	return fmt.Sprint(t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.X, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.X, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		return x.root
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) string {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		return unknown
	}
	if string(name.Value) == "" {
		name, err = x.property(w, x.nameAtom)
		if err != nil || string(name.Value) == "" {
			return unknown
		}
	}
	return string(name.Value)
}

func (x Xorg) class(w xproto.Window) string {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		return unknown
	}
	i := bytes.IndexByte(class.Value, 0)
	if i == -1 || string(class.Value[:i]) == "" {
		return unknown
	}
	return string(class.Value[:i])
}

func (x Xorg) winName() (Window, bool) {
	windowId := x.active()
	/* skip invalid window id */
	if windowId == 0 {
		return Window{}, false
	}
	x.spy(windowId)
	return Window{
		Class: x.class(windowId),
		Name:  x.name(windowId),
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.X, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) update(t Tracker) (prev *Track) {
	if win, ok := x.winName(); ok {
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Seen = time.Now()
		prev = t[win]
	}
	return
}

func connect() Xorg {
	var x Xorg
	var err error

	x.X, err = xgb.NewConn()
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.X)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.X)
	x.root = setup.DefaultScreen(x.X).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.X, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")

	x.spy(x.root)

	return x
}

func (t Tracker) collect() {
	x := connect()
	defer x.X.Close()

	prev := x.update(t)
	for {
		ev, everr := x.X.WaitForEvent()
		if everr != nil {
			log.Println("wait for event:", everr)
			continue
		}
		switch event := ev.(type) {
		case xproto.PropertyNotifyEvent:
			if prev != nil {
				prev.Spent += time.Since(prev.Seen)
			}
			prev = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				fmt.Println("away from keyboard")
				prev = nil
				zzz = true
			default:
				fmt.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	w, err := os.OpenFile(logf, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer w.Close()
	for k, v := range t {
		if time.Since(v.Seen) > d {
			log.Println("removing", k)
			log.SetOutput(w)
			log.Println("%s %s %s %s",
				v.Seen, v.Spent, k.Class, k.Name)
			log.SetOutput(os.Stderr)
			delete(t, k)
		}
	}
}

func (t Tracker) load(fname string) {
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
}

func (t Tracker) store(fname string) {
	dump, err := os.Create(fname + ".tmp")
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
	}
	os.Rename(fname+".tmp", fname)
}

func (t Tracker) reset() {
	for k := range t {
		delete(t, k)
	}
}

type Index struct {
	Title   string
	Records Records
	Classes Records
	Total   time.Duration
	Zzz     bool
}

type Records []Record

type Record struct {
	Class string
	Name  string
	Spent time.Duration
	Seen  time.Time
	Odd   bool `json:"-"`
}

func (r Records) Len() int           { return len(r) }
func (r Records) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
func (r Records) Less(i, j int) bool { return r[i].Spent < r[j].Spent }

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var idx Index
	idx.Title = "Gone Time Tracker"
	idx.Zzz = zzz
	class := r.URL.Path[1:]

	classtotal := make(map[string]time.Duration)

	for k, v := range tracks {
		classtotal[k.Class] += v.Spent
		idx.Total += v.Spent
		if class != "" && class != k.Class {
			continue
		}
		idx.Records = append(idx.Records, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: v.Spent})
	}
	for k, v := range classtotal {
		idx.Classes = append(idx.Classes, Record{Class: k, Spent: v})
	}
	sort.Sort(sort.Reverse(idx.Classes))
	sort.Sort(sort.Reverse(idx.Records))
	for j := range idx.Records {
		idx.Records[j].Odd = j%2 == 0
	}
	err := tmpl.Execute(w, idx)
	if err != nil {
		log.Println(err)
	}
}

func dumpHandler(w http.ResponseWriter, r *http.Request) {
	var rec Records

	for k, v := range tracks {
		rec = append(rec, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: v.Spent,
			Seen:  v.Seen})
	}

	data, err := json.MarshalIndent(rec, "", "\t")
	if err != nil {
		log.Println("dump:", err)
	}
	w.Write(data)
}

func resetHandler(w http.ResponseWriter, r *http.Request) {
	tracks.reset()
	http.Redirect(w, r, "/", http.StatusFound)
}

func main() {
	tracks.load(dump)
	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(dump)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/gone.json", dumpHandler)
	http.HandleFunc("/reset", resetHandler)
	http.ListenAndServe(port, nil)
}
<commit_msg>Synchronize accesses to the tracker map<commit_after>package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"sort"
	"sync"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
)

const (
	port    = ":8001"
	dump    = "gone.gob"
	logf    = "gone.log"
	unknown = "unknown"
)

var (
	tracks = make(Tracker)
	tmpl   = template.Must(template.ParseFiles("index.html"))
	zzz    bool
	m      sync.Mutex
)

type Tracker map[Window]*Track

type Track struct {
	Seen  time.Time
	Spent time.Duration
}

type Window struct {
	Class string
	Name  string
}

type Xorg struct {
	X           *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
}

func (t Track) String() string {
	return fmt.Sprint(t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.X, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.X, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		return x.root
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) string {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		return unknown
	}
	if string(name.Value) == "" {
		name, err = x.property(w, x.nameAtom)
		if err != nil || string(name.Value) == "" {
			return unknown
		}
	}
	return string(name.Value)
}

func (x Xorg) class(w xproto.Window) string {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		return unknown
	}
	i := bytes.IndexByte(class.Value, 0)
	if i == -1 || string(class.Value[:i]) == "" {
		return unknown
	}
	return string(class.Value[:i])
}

func (x Xorg) winName() (Window, bool) {
	windowId := x.active()
	/* skip invalid window id */
	if windowId == 0 {
		return Window{}, false
	}
	x.spy(windowId)
	return Window{
		Class: x.class(windowId),
		Name:  x.name(windowId),
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.X, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) update(t Tracker) (prev *Track) {
	if win, ok := x.winName(); ok {
		m.Lock()
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Seen = time.Now()
		prev = t[win]
		m.Unlock()
	}
	return
}

func connect() Xorg {
	var x Xorg
	var err error

	x.X, err = xgb.NewConn()
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.X)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.X)
	x.root = setup.DefaultScreen(x.X).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.X, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")

	x.spy(x.root)

	return x
}

func (t Tracker) collect() {
	x := connect()
	defer x.X.Close()

	prev := x.update(t)
	for {
		ev, everr := x.X.WaitForEvent()
		if everr != nil {
			log.Println("wait for event:", everr)
			continue
		}
		switch event := ev.(type) {
		case xproto.PropertyNotifyEvent:
			if prev != nil {
				m.Lock()
				prev.Spent += time.Since(prev.Seen)
				m.Unlock()
			}
			prev = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				fmt.Println("away from keyboard")
				prev = nil
				zzz = true
			default:
				fmt.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	w, err := os.OpenFile(logf, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer w.Close()
	m.Lock()
	for k, v := range t {
		if time.Since(v.Seen) > d {
			log.Println("removing", k)
			log.SetOutput(w)
			log.Println("%s %s %s %s",
				v.Seen, v.Spent, k.Class, k.Name)
			log.SetOutput(os.Stderr)
			delete(t, k)
		}
	}
	m.Unlock()
}

func (t Tracker) load(fname string) {
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	m.Lock()
	err = dec.Decode(&t)
	m.Unlock()
	if err != nil {
		log.Println(err)
	}
}

func (t Tracker) store(fname string) {
	dump, err := os.Create(fname + ".tmp")
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	m.Lock()
	err = enc.Encode(t)
	m.Unlock()
	if err != nil {
		log.Println(err)
	}
	os.Rename(fname+".tmp", fname)
}

func (t Tracker) reset() {
	m.Lock()
	for k := range t {
		delete(t, k)
	}
	m.Unlock()
}

type Index struct {
	Title   string
	Records Records
	Classes Records
	Total   time.Duration
	Zzz     bool
}

type Records []Record

type Record struct {
	Class string
	Name  string
	Spent time.Duration
	Seen  time.Time
	Odd   bool `json:"-"`
}

func (r Records) Len() int           { return len(r) }
func (r Records) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
func (r Records) Less(i, j int) bool { return r[i].Spent < r[j].Spent }

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var idx Index
	idx.Title = "Gone Time Tracker"
	idx.Zzz = zzz
	class := r.URL.Path[1:]

	classtotal := make(map[string]time.Duration)

	m.Lock()
	for k, v := range tracks {
		classtotal[k.Class] += v.Spent
		idx.Total += v.Spent
		if class != "" && class != k.Class {
			continue
		}
		idx.Records = append(idx.Records, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: v.Spent})
	}
	m.Unlock()
	for k, v := range classtotal {
		idx.Classes = append(idx.Classes, Record{Class: k, Spent: v})
	}
	sort.Sort(sort.Reverse(idx.Classes))
	sort.Sort(sort.Reverse(idx.Records))
	for j := range idx.Records {
		idx.Records[j].Odd = j%2 == 0
	}
	err := tmpl.Execute(w, idx)
	if err != nil {
		log.Println(err)
	}
}

func dumpHandler(w http.ResponseWriter, r *http.Request) {
	var rec Records

	m.Lock()
	for k, v := range tracks {
		rec = append(rec, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: v.Spent,
			Seen:  v.Seen})
	}
	m.Unlock()

	data, err := json.MarshalIndent(rec, "", "\t")
	if err != nil {
		log.Println("dump:", err)
	}
	w.Write(data)
}

func resetHandler(w http.ResponseWriter, r *http.Request) {
	tracks.reset()
	http.Redirect(w, r, "/", http.StatusFound)
}

func main() {
	tracks.load(dump)
	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(dump)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/gone.json", dumpHandler)
	http.HandleFunc("/reset", resetHandler)
	http.ListenAndServe(port, nil)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"sort"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
)

const (
	port    = ":8001"
	file    = "dump.gob"
	unknown = "unknown"
)

var (
	tracks = make(Tracker)
	tmpl   = template.Must(template.ParseFiles("index.html"))
	zzz    bool
)

type Tracker map[Window]*Track

type Track struct {
	Start time.Time
	Spent time.Duration
}

type Window struct {
	Class string
	Name  string
}

type Xorg struct {
	X           *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
}

func (t Track) String() string {
	return fmt.Sprint(t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.X, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.X, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		return x.root
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) string {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		return unknown
	}
	if string(name.Value) == "" {
		name, err = x.property(w, x.nameAtom)
		if err != nil || string(name.Value) == "" {
			return unknown
		}
	}
	return string(name.Value)
}

func (x Xorg) class(w xproto.Window) string {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		return unknown
	}
	i := bytes.IndexByte(class.Value, 0)
	if i == -1 || string(class.Value[:i]) == "" {
		return unknown
	}
	return string(class.Value[:i])
}

func (x Xorg) winName() (Window, bool) {
	windowId := x.active()
	/* skip invalid window id */
	if windowId == 0 {
		return Window{}, false
	}
	x.spy(windowId)
	return Window{
		Class: x.class(windowId),
		Name:  x.name(windowId),
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.X, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) update(t Tracker) (prev *Track) {
	if win, ok := x.winName(); ok {
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Start = time.Now()
		prev = t[win]
	}
	return
}

func connect() Xorg {
	var x Xorg
	var err error

	x.X, err = xgb.NewConn()
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.X)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.X)
	x.root = setup.DefaultScreen(x.X).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.X, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")

	x.spy(x.root)

	return x
}

func (t Tracker) collect() {
	x := connect()
	defer x.X.Close()

	prev := x.update(t)
	for {
		ev, everr := x.X.WaitForEvent()
		if everr != nil {
			log.Println("wait for event:", everr)
			continue
		}
		switch event := ev.(type) {
		case xproto.PropertyNotifyEvent:
			if prev != nil {
				prev.Spent += time.Since(prev.Start)
			}
			prev = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				fmt.Println("away from keyboard")
				prev = nil
				zzz = true
			default:
				fmt.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	for k, v := range t {
		if time.Since(v.Start) > d {
			log.Println("removing", k)
			delete(t, k)
		}
	}
}

func (t Tracker) load(fname string) {
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
}

func (t Tracker) store(fname string) {
	dump, err := os.Create(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
	}
}

type Index struct {
	Title   string
	Records Records
	Classes Records
	Total   time.Duration
	Zzz     bool
}

type Records []Record

type Record struct {
	Class    string
	Name     string
	Spent    time.Duration
	LastSeen time.Time
	Odd      bool `json:"-"`
}

func (r Records) Len() int           { return len(r) }
func (r Records) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
func (r Records) Less(i, j int) bool { return r[i].Spent < r[j].Spent }

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var idx Index
	idx.Title = "Gone Time Tracker"
	idx.Zzz = zzz
	class := r.URL.Path[1:]

	classtotal := make(map[string]time.Duration)

	for k, v := range tracks {
		classtotal[k.Class] += v.Spent
		idx.Total += v.Spent
		if class != "" && class != k.Class {
			continue
		}
		idx.Records = append(idx.Records, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: v.Spent})
	}
	for k, v := range classtotal {
		idx.Classes = append(idx.Classes, Record{Class: k, Spent: v})
	}
	sort.Sort(sort.Reverse(idx.Classes))
	sort.Sort(sort.Reverse(idx.Records))
	for j := range idx.Records {
		idx.Records[j].Odd = j%2 == 0
	}
	err := tmpl.Execute(w, idx)
	if err != nil {
		log.Println(err)
	}
}

func dumpHandler(w http.ResponseWriter, r *http.Request) {
	var rec Records

	for k, v := range tracks {
		rec = append(rec, Record{
			Class:    k.Class,
			Name:     k.Name,
			Spent:    v.Spent,
			LastSeen: v.Start})
	}

	data, err := json.MarshalIndent(rec, "", "\t")
	if err != nil {
		log.Println("dump:", err)
	}
	w.Write(data)
}

func main() {
	tracks.load(file)
	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(file)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/dump.json", dumpHandler)
	http.ListenAndServe(port, nil)
}
<commit_msg>rename Start to Seen<commit_after>package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"sort"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
)

const (
	port    = ":8001"
	file    = "dump.gob"
	unknown = "unknown"
)

var (
	tracks = make(Tracker)
	tmpl   = template.Must(template.ParseFiles("index.html"))
	zzz    bool
)

type Tracker map[Window]*Track

type Track struct {
	Seen  time.Time
	Spent time.Duration
}

type Window struct {
	Class string
	Name  string
}

type Xorg struct {
	X           *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
}

func (t Track) String() string {
	return fmt.Sprint(t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.X, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.X, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		return x.root
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) string {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		return unknown
	}
	if string(name.Value) == "" {
		name, err = x.property(w, x.nameAtom)
		if err != nil || string(name.Value) == "" {
			return unknown
		}
	}
	return string(name.Value)
}

func (x Xorg) class(w xproto.Window) string {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		return unknown
	}
	i := bytes.IndexByte(class.Value, 0)
	if i == -1 || string(class.Value[:i]) == "" {
		return unknown
	}
	return string(class.Value[:i])
}

func (x Xorg) winName() (Window, bool) {
	windowId := x.active()
	/* skip invalid window id */
	if windowId == 0 {
		return Window{}, false
	}
	x.spy(windowId)
	return Window{
		Class: x.class(windowId),
		Name:  x.name(windowId),
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.X, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) update(t Tracker) (prev *Track) {
	if win, ok := x.winName(); ok {
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Seen = time.Now()
		prev = t[win]
	}
	return
}

func connect() Xorg {
	var x Xorg
	var err error

	x.X, err = xgb.NewConn()
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.X)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.X)
	x.root = setup.DefaultScreen(x.X).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.X, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")

	x.spy(x.root)

	return x
}

func (t Tracker) collect() {
	x := connect()
	defer x.X.Close()

	prev := x.update(t)
	for {
		ev, everr := x.X.WaitForEvent()
		if everr != nil {
			log.Println("wait for event:", everr)
			continue
		}
		switch event := ev.(type) {
		case xproto.PropertyNotifyEvent:
			if prev != nil {
				prev.Spent += time.Since(prev.Seen)
			}
			prev = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				fmt.Println("away from keyboard")
				prev = nil
				zzz = true
			default:
				fmt.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	for k, v := range t {
		if time.Since(v.Seen) > d {
			log.Println("removing", k)
			delete(t, k)
		}
	}
}

func (t Tracker) load(fname string) {
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
}

func (t Tracker) store(fname string) {
	dump, err := os.Create(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
	}
}

type Index struct {
	Title   string
	Records Records
	Classes Records
	Total   time.Duration
	Zzz     bool
}

type Records []Record

type Record struct {
	Class    string
	Name     string
	Spent    time.Duration
	LastSeen time.Time
	Odd      bool `json:"-"`
}

func (r Records) Len() int           { return len(r) }
func (r Records) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
func (r Records) Less(i, j int) bool { return r[i].Spent < r[j].Spent }

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var idx Index
	idx.Title = "Gone Time Tracker"
	idx.Zzz = zzz
	class := r.URL.Path[1:]

	classtotal := make(map[string]time.Duration)

	for k, v := range tracks {
		classtotal[k.Class] += v.Spent
		idx.Total += v.Spent
		if class != "" && class != k.Class {
			continue
		}
		idx.Records = append(idx.Records, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: v.Spent})
	}
	for k, v := range classtotal {
		idx.Classes = append(idx.Classes, Record{Class: k, Spent: v})
	}
	sort.Sort(sort.Reverse(idx.Classes))
	sort.Sort(sort.Reverse(idx.Records))
	for j := range idx.Records {
		idx.Records[j].Odd = j%2 == 0
	}
	err := tmpl.Execute(w, idx)
	if err != nil {
		log.Println(err)
	}
}

func dumpHandler(w http.ResponseWriter, r *http.Request) {
	var rec Records

	for k, v := range tracks {
		rec = append(rec, Record{
			Class: k.Class,
			Name:  k.Name,
			Spent: v.Spent,
			Seen:  v.Seen})
	}

	data, err := json.MarshalIndent(rec, "", "\t")
	if err != nil {
		log.Println("dump:", err)
	}
	w.Write(data)
}

func main() {
	tracks.load(file)
	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(file)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/dump.json", dumpHandler)
	http.ListenAndServe(port, nil)
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Matt Jibson <matt.jibson@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package goon

import (
	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"bytes"
	"encoding/gob"
	"errors"
	"net/http"
	"reflect"
)

var (
	// LogErrors issues appengine.Context.Errorf on any error.
	LogErrors bool = true
)

// Goon holds the app engine context and request memory cache.
type Goon struct {
	context       appengine.Context
	cache         map[string]*Entity
	inTransaction bool
	toSet         map[string]*Entity
	toDelete      []string
}

func memkey(k *datastore.Key) string {
	return k.String()
}

func NewGoon(r *http.Request) *Goon {
	return FromContext(appengine.NewContext(r))
}

func FromContext(c appengine.Context) *Goon {
	return &Goon{
		context: c,
		cache:   make(map[string]*Entity),
	}
}

func (g *Goon) error(err error) {
	if LogErrors {
		g.context.Errorf(err.Error())
	}
}

// RunInTransaction runs f in a transaction. It calls f with a transaction
// context tg that f should use for all App Engine operations. Neither cache nor
// memcache are used or set during a transaction.
//
// Otherwise similar to appengine/datastore.RunInTransaction:
// https://developers.google.com/appengine/docs/go/datastore/reference#RunInTransaction
func (g *Goon) RunInTransaction(f func(tg *Goon) error, opts *datastore.TransactionOptions) error {
	var ng *Goon
	err := datastore.RunInTransaction(g.context, func(tc appengine.Context) error {
		ng = &Goon{
			context:       tc,
			inTransaction: true,
			toSet:         make(map[string]*Entity),
		}
		return f(ng)
	}, opts)

	if err == nil {
		for k, v := range ng.toSet {
			g.cache[k] = v
		}

		for _, k := range ng.toDelete {
			delete(g.cache, k)
		}
	} else {
		g.error(err)
	}

	return err
}

// Put stores Entity e.
// If e has an incomplete key, it is updated.
func (g *Goon) Put(e *Entity) error {
	return g.PutMulti([]*Entity{e})
}

// PutMany is a wrapper around PutMulti.
func (g *Goon) PutMany(es ...*Entity) error {
	return g.PutMulti(es)
}

const putMultiLimit = 500

// PutMulti stores a sequence of Entities.
// Any entity with an incomplete key will be updated.
func (g *Goon) PutMulti(es []*Entity) error {
	var memkeys []string
	keys := make([]*datastore.Key, len(es))
	src := make([]interface{}, len(es))

	for i, e := range es {
		if !e.Key.Incomplete() {
			memkeys = append(memkeys, e.memkey())
		}

		keys[i] = e.Key
		src[i] = e.Src
	}

	memcache.DeleteMulti(g.context, memkeys)

	for i := 0; i <= len(src)/putMultiLimit; i++ {
		lo := i * putMultiLimit
		hi := (i + 1) * putMultiLimit
		if hi > len(src) {
			hi = len(src)
		}
		rkeys, err := datastore.PutMulti(g.context, keys[lo:hi], src[lo:hi])
		if err != nil {
			g.error(err)
			return err
		}

		for i, e := range es[lo:hi] {
			es[lo+i].setKey(rkeys[i])

			if g.inTransaction {
				g.toSet[e.memkey()] = e
			}
		}
	}

	if !g.inTransaction {
		g.putMemoryMulti(es)
	}

	return nil
}

func (g *Goon) putMemoryMulti(es []*Entity) {
	for _, e := range es {
		g.putMemory(e)
	}
}

func (g *Goon) putMemory(e *Entity) {
	g.cache[e.memkey()] = e
}

func (g *Goon) putMemcache(es []*Entity) error {
	items := make([]*memcache.Item, len(es))

	for i, e := range es {
		gob, err := e.gob()
		if err != nil {
			g.error(err)
			return err
		}

		items[i] = &memcache.Item{
			Key:   e.memkey(),
			Value: gob,
		}
	}

	err := memcache.SetMulti(g.context, items)

	if err != nil {
		g.error(err)
		return err
	}

	g.putMemoryMulti(es)
	return nil
}

// Kind returns the Kind name of src, which must be a struct.
// If src has a field named _goon with a tag "kind", that is used.
// Otherwise, reflection is used to determine the type name of src.
// In the case of an error, the empty string is returned.
//
// For example, to overwrite the default "Group" kind name:
//   type Group struct {
//     _goon interface{} `kind:"something_else"`
//     Name  string
//   }
func Kind(src interface{}) string {
	v := reflect.ValueOf(src)
	v = reflect.Indirect(v)
	t := v.Type()
	k := t.Kind()

	if k == reflect.Struct {
		if f, present := t.FieldByName("_goon"); present {
			name := f.Tag.Get("kind")
			if name != "" {
				return name
			}
		}

		return t.Name()
	}
	return ""
}

// GetById fetches an entity of kind src by id.
// Refer to appengine/datastore.NewKey regarding key specification.
func (g *Goon) GetById(src interface{}, stringID string, intID int64, parent *datastore.Key) (*Entity, error) {
	return g.GetByIdKind(src, Kind(src), stringID, intID, parent)
}

// GetByIdKind fetches an entity of specified kind by id.
// Refer to appengine/datastore.NewKey regarding key specification.
func (g *Goon) GetByIdKind(src interface{}, kind, stringID string, intID int64, parent *datastore.Key) (*Entity, error) {
	key := datastore.NewKey(g.context, kind, stringID, intID, parent)
	return g.Get(src, key)
}

// Get fetches an entity of kind src by key.
func (g *Goon) Get(src interface{}, key *datastore.Key) (*Entity, error) {
	e := NewEntity(key, src)
	es := []*Entity{e}
	err := g.GetMulti(es)
	if err != nil {
		return nil, err
	}
	return es[0], nil
}

// Get fetches a sequency of Entities, whose keys must already be valid.
// Entities with no correspending key have their NotFound field set to true.
func (g *Goon) GetMulti(es []*Entity) error {
	for _, e := range es {
		t := reflect.TypeOf(e.Src)
		pt := reflect.Indirect(reflect.ValueOf(e.Src)).Type()
		if t.Kind() != reflect.Ptr || pt.Kind() != reflect.Struct {
			return errors.New("goon: expected *struct (ptr to struct), got struct")
		}
	}

	var dskeys []*datastore.Key
	var dst []interface{}
	var dixs []int

	if !g.inTransaction {
		var memkeys []string
		var mixs []int

		for i, e := range es {
			m := e.memkey()
			if s, present := g.cache[m]; present {
				es[i] = s
			} else {
				memkeys = append(memkeys, m)
				mixs = append(mixs, i)
			}
		}

		memvalues, err := memcache.GetMulti(g.context, memkeys)
		if err != nil {
			g.error(err)
			return err
		}

		for i, m := range memkeys {
			e := es[mixs[i]]
			if s, present := memvalues[m]; present {
				err := fromGob(e, s.Value)
				if err != nil {
					return err
				}

				g.putMemory(e)
			} else {
				dskeys = append(dskeys, e.Key)
				dst = append(dst, e.Src)
				dixs = append(dixs, mixs[i])
			}
		}
	} else {
		dskeys = make([]*datastore.Key, len(es))
		dst = make([]interface{}, len(es))
		dixs = make([]int, len(es))

		for i, e := range es {
			dskeys[i] = e.Key
			dst[i] = e.Src
			dixs[i] = i
		}
	}

	var merr appengine.MultiError
	err := datastore.GetMulti(g.context, dskeys, dst)
	if err != nil {
		switch err.(type) {
		case appengine.MultiError:
			merr = err.(appengine.MultiError)
		default:
			g.error(err)
			return err
		}
	}
	var mes []*Entity

	multiErr, any := make(appengine.MultiError, len(es)), false
	for i, idx := range dixs {
		e := es[idx]
		if merr != nil {
			if merr[i] == datastore.ErrNoSuchEntity {
				e.NotFound = true
			} else {
				multiErr[i] = merr[i]
				any = true
			}
		}
		mes = append(mes, e)
	}

	if len(mes) > 0 && !g.inTransaction {
		err = g.putMemcache(mes)
		if err != nil {
			return err
		}
	}

	if any {
		return multiErr
	}

	return nil
}

func fromGob(e *Entity, b []byte) error {
	var buf bytes.Buffer
	_, _ = buf.Write(b)
	gob.Register(e.Src)
	dec := gob.NewDecoder(&buf)
	t := Entity{}
	err := dec.Decode(&t)
	if err != nil {
		return err
	}

	e.NotFound = t.NotFound
	ev := reflect.Indirect(reflect.ValueOf(e.Src))

	v := reflect.Indirect(reflect.ValueOf(t.Src))
	for i := 0; i < v.NumField(); i++ {
		f := v.Field(i)
		if f.CanSet() {
			ev.Field(i).Set(f)
		}
	}

	return nil
}

// Delete deletes the entity for the given key.
func (g *Goon) Delete(key *datastore.Key) error {
	keys := []*datastore.Key{key}
	return g.DeleteMulti(keys)
}

// DeleteMulti is a batch version of Delete.
func (g *Goon) DeleteMulti(keys []*datastore.Key) error {
	memkeys := make([]string, len(keys))
	for i, k := range keys {
		mk := memkey(k)
		memkeys[i] = mk

		if g.inTransaction {
			g.toDelete = append(g.toDelete, mk)
		} else {
			delete(g.cache, mk)
		}
	}

	memcache.DeleteMulti(g.context, memkeys)

	return datastore.DeleteMulti(g.context, keys)
}
<commit_msg>Fixed a common race condition regarding memcache usage.<commit_after>/*
 * Copyright (c) 2012 Matt Jibson <matt.jibson@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package goon

import (
	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"bytes"
	"encoding/gob"
	"errors"
	"net/http"
	"reflect"
)

var (
	// LogErrors issues appengine.Context.Errorf on any error.
	LogErrors bool = true
)

// Goon holds the app engine context and request memory cache.
type Goon struct {
	context       appengine.Context
	cache         map[string]*Entity
	inTransaction bool
	toSet         map[string]*Entity
	toDelete      []string
}

func memkey(k *datastore.Key) string {
	return k.String()
}

func NewGoon(r *http.Request) *Goon {
	return FromContext(appengine.NewContext(r))
}

func FromContext(c appengine.Context) *Goon {
	return &Goon{
		context: c,
		cache:   make(map[string]*Entity),
	}
}

func (g *Goon) error(err error) {
	if LogErrors {
		g.context.Errorf(err.Error())
	}
}

// RunInTransaction runs f in a transaction. It calls f with a transaction
// context tg that f should use for all App Engine operations. Neither cache nor
// memcache are used or set during a transaction.
//
// Otherwise similar to appengine/datastore.RunInTransaction:
// https://developers.google.com/appengine/docs/go/datastore/reference#RunInTransaction
func (g *Goon) RunInTransaction(f func(tg *Goon) error, opts *datastore.TransactionOptions) error {
	var ng *Goon
	err := datastore.RunInTransaction(g.context, func(tc appengine.Context) error {
		ng = &Goon{
			context:       tc,
			inTransaction: true,
			toSet:         make(map[string]*Entity),
		}
		return f(ng)
	}, opts)

	if err == nil {
		for k, v := range ng.toSet {
			g.cache[k] = v
		}

		for _, k := range ng.toDelete {
			delete(g.cache, k)
		}
	} else {
		g.error(err)
	}

	return err
}

// Put stores Entity e.
// If e has an incomplete key, it is updated.
func (g *Goon) Put(e *Entity) error {
	return g.PutMulti([]*Entity{e})
}

// PutMany is a wrapper around PutMulti.
func (g *Goon) PutMany(es ...*Entity) error {
	return g.PutMulti(es)
}

const putMultiLimit = 500

// PutMulti stores a sequence of Entities.
// Any entity with an incomplete key will be updated.
func (g *Goon) PutMulti(es []*Entity) error {
	var memkeys []string
	keys := make([]*datastore.Key, len(es))
	src := make([]interface{}, len(es))

	for i, e := range es {
		if !e.Key.Incomplete() {
			memkeys = append(memkeys, e.memkey())
		}

		keys[i] = e.Key
		src[i] = e.Src
	}

	// Memcache needs to be updated after the datastore to prevent a common race condition
	defer memcache.DeleteMulti(g.context, memkeys)

	for i := 0; i <= len(src)/putMultiLimit; i++ {
		lo := i * putMultiLimit
		hi := (i + 1) * putMultiLimit
		if hi > len(src) {
			hi = len(src)
		}
		rkeys, err := datastore.PutMulti(g.context, keys[lo:hi], src[lo:hi])
		if err != nil {
			g.error(err)
			return err
		}

		for i, e := range es[lo:hi] {
			es[lo+i].setKey(rkeys[i])

			if g.inTransaction {
				g.toSet[e.memkey()] = e
			}
		}
	}

	if !g.inTransaction {
		g.putMemoryMulti(es)
	}

	return nil
}

func (g *Goon) putMemoryMulti(es []*Entity) {
	for _, e := range es {
		g.putMemory(e)
	}
}

func (g *Goon) putMemory(e *Entity) {
	g.cache[e.memkey()] = e
}

func (g *Goon) putMemcache(es []*Entity) error {
	items := make([]*memcache.Item, len(es))

	for i, e := range es {
		gob, err := e.gob()
		if err != nil {
			g.error(err)
			return err
		}

		items[i] = &memcache.Item{
			Key:   e.memkey(),
			Value: gob,
		}
	}

	err := memcache.SetMulti(g.context, items)

	if err != nil {
		g.error(err)
		return err
	}

	g.putMemoryMulti(es)
	return nil
}

// Kind returns the Kind name of src, which must be a struct.
// If src has a field named _goon with a tag "kind", that is used.
// Otherwise, reflection is used to determine the type name of src.
// In the case of an error, the empty string is returned.
//
// For example, to overwrite the default "Group" kind name:
//   type Group struct {
//     _goon interface{} `kind:"something_else"`
//     Name  string
//   }
func Kind(src interface{}) string {
	v := reflect.ValueOf(src)
	v = reflect.Indirect(v)
	t := v.Type()
	k := t.Kind()

	if k == reflect.Struct {
		if f, present := t.FieldByName("_goon"); present {
			name := f.Tag.Get("kind")
			if name != "" {
				return name
			}
		}

		return t.Name()
	}
	return ""
}

// GetById fetches an entity of kind src by id.
// Refer to appengine/datastore.NewKey regarding key specification.
func (g *Goon) GetById(src interface{}, stringID string, intID int64, parent *datastore.Key) (*Entity, error) {
	return g.GetByIdKind(src, Kind(src), stringID, intID, parent)
}

// GetByIdKind fetches an entity of specified kind by id.
// Refer to appengine/datastore.NewKey regarding key specification.
func (g *Goon) GetByIdKind(src interface{}, kind, stringID string, intID int64, parent *datastore.Key) (*Entity, error) {
	key := datastore.NewKey(g.context, kind, stringID, intID, parent)
	return g.Get(src, key)
}

// Get fetches an entity of kind src by key.
func (g *Goon) Get(src interface{}, key *datastore.Key) (*Entity, error) {
	e := NewEntity(key, src)
	es := []*Entity{e}
	err := g.GetMulti(es)
	if err != nil {
		return nil, err
	}
	return es[0], nil
}

// Get fetches a sequency of Entities, whose keys must already be valid.
// Entities with no correspending key have their NotFound field set to true.
func (g *Goon) GetMulti(es []*Entity) error {
	for _, e := range es {
		t := reflect.TypeOf(e.Src)
		pt := reflect.Indirect(reflect.ValueOf(e.Src)).Type()
		if t.Kind() != reflect.Ptr || pt.Kind() != reflect.Struct {
			return errors.New("goon: expected *struct (ptr to struct), got struct")
		}
	}

	var dskeys []*datastore.Key
	var dst []interface{}
	var dixs []int

	if !g.inTransaction {
		var memkeys []string
		var mixs []int

		for i, e := range es {
			m := e.memkey()
			if s, present := g.cache[m]; present {
				es[i] = s
			} else {
				memkeys = append(memkeys, m)
				mixs = append(mixs, i)
			}
		}

		memvalues, err := memcache.GetMulti(g.context, memkeys)
		if err != nil {
			g.error(err)
			return err
		}

		for i, m := range memkeys {
			e := es[mixs[i]]
			if s, present := memvalues[m]; present {
				err := fromGob(e, s.Value)
				if err != nil {
					return err
				}

				g.putMemory(e)
			} else {
				dskeys = append(dskeys, e.Key)
				dst = append(dst, e.Src)
				dixs = append(dixs, mixs[i])
			}
		}
	} else {
		dskeys = make([]*datastore.Key, len(es))
		dst = make([]interface{}, len(es))
		dixs = make([]int, len(es))

		for i, e := range es {
			dskeys[i] = e.Key
			dst[i] = e.Src
			dixs[i] = i
		}
	}

	var merr appengine.MultiError
	err := datastore.GetMulti(g.context, dskeys, dst)
	if err != nil {
		switch err.(type) {
		case appengine.MultiError:
			merr = err.(appengine.MultiError)
		default:
			g.error(err)
			return err
		}
	}
	var mes []*Entity

	multiErr, any := make(appengine.MultiError, len(es)), false
	for i, idx := range dixs {
		e := es[idx]
		if merr != nil {
			if merr[i] == datastore.ErrNoSuchEntity {
				e.NotFound = true
			} else {
				multiErr[i] = merr[i]
				any = true
			}
		}
		mes = append(mes, e)
	}

	if len(mes) > 0 && !g.inTransaction {
		err = g.putMemcache(mes)
		if err != nil {
			return err
		}
	}

	if any {
		return multiErr
	}

	return nil
}

func fromGob(e *Entity, b []byte) error {
	var buf bytes.Buffer
	_, _ = buf.Write(b)
	gob.Register(e.Src)
	dec := gob.NewDecoder(&buf)
	t := Entity{}
	err := dec.Decode(&t)
	if err != nil {
		return err
	}

	e.NotFound = t.NotFound
	ev := reflect.Indirect(reflect.ValueOf(e.Src))

	v := reflect.Indirect(reflect.ValueOf(t.Src))
	for i := 0; i < v.NumField(); i++ {
		f := v.Field(i)
		if f.CanSet() {
			ev.Field(i).Set(f)
		}
	}

	return nil
}

// Delete deletes the entity for the given key.
func (g *Goon) Delete(key *datastore.Key) error {
	keys := []*datastore.Key{key}
	return g.DeleteMulti(keys)
}

// DeleteMulti is a batch version of Delete.
func (g *Goon) DeleteMulti(keys []*datastore.Key) error {
	memkeys := make([]string, len(keys))
	for i, k := range keys {
		mk := memkey(k)
		memkeys[i] = mk

		if g.inTransaction {
			g.toDelete = append(g.toDelete, mk)
		} else {
			delete(g.cache, mk)
		}
	}

	// Memcache needs to be updated after the datastore to prevent a common race condition
	defer memcache.DeleteMulti(g.context, memkeys)

	return datastore.DeleteMulti(g.context, keys)
}
<|endoftext|>


<commit_before>package cmd

import (
	"os"

	"github.com/spf13/cobra"
)

var completionBashCmd = &cobra.Command{
	Use:   "bash",
	Short: "Outputs fritzctl shell completion for the given shell (bash)",
	Long: `Outputs fritzctl shell completion for the given shell (bash)
This depends on the bash-completion binary. Example installation instructions:
OS X:
	$ brew install bash-completion
	$ source $(brew --prefix)/etc/bash_completion
	$ fritzctl completion bash > ~/.fritzctl-completion
	$ source ~/.fritzctl-completion
Ubuntu:
	$ apt-get install bash-completion
	$ source /etc/bash-completion
	$ source <(fritzctl completion bash)
Additionally, you may want to output completion to a file and source in your .bashrc`,
	Example: "fritzctl completion bash",
	RunE:    completionBash,
}

func init() {
	completionCmd.AddCommand(completionBashCmd)
}

func completionBash(cmd *cobra.Command, args []string) error {
	RootCmd.GenBashCompletion(os.Stdout)
	return nil
}
<commit_msg>Issue #48: polish help for bash completion; use return value of GenBash... for command return.<commit_after>package cmd

import (
	"os"

	"github.com/spf13/cobra"
)

var completionBashCmd = &cobra.Command{
	Use:   "bash",
	Short: "Outputs fritzctl shell completion for the given shell (bash)",
	Long: `Outputs fritzctl shell completion for the given shell (bash) to stdout.
Usage depends on the bash-completion binary. Example installation instructions:
OS X:
	$ brew install bash-completion
	$ source $(brew --prefix)/etc/bash_completion
	$ fritzctl completion bash > ~/.fritzctl-completion
	$ source ~/.fritzctl-completion
Ubuntu:
	$ apt-get install bash-completion
	$ source /etc/bash-completion
	$ source <(fritzctl completion bash)
Additionally, you may want to output completion to a file and source in your .bashrc`,
	Example: "fritzctl completion bash",
	RunE:    completionBash,
}

func init() {
	completionCmd.AddCommand(completionBashCmd)
}

func completionBash(cmd *cobra.Command, args []string) error {
	return RootCmd.GenBashCompletion(os.Stdout)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	// "regexp"
)

var out io.Writer

func init() {
	out = os.Stdout
}

// Does the grepping
func grep(r io.Reader, pmms []PolarizedMultiMatcher, opt *grepOpt) (int, error) {
	buf := bufio.NewReader(r)
	n := 1
	count := 0
	for {
		b, _, err := buf.ReadLine()
		if err != nil {
			if err == io.EOF {
				return count, nil
			}
			return 0, err
		}
		line    := string(b)
		matched := true
		for _, pmm := range pmms {
			if pmm.matcher.MatchString(line) != pmm.polar {
				matched = false
				break
			}
		}
		if matched {
			count++
			if opt.optFilename {
				fmt.Fprintf(out, "%s:", opt.filename)
			}
			if opt.optNumber {
				fmt.Fprintf(out, "%d:", n)
			}
			fmt.Fprintln(out, line)
			break
		}
		n++
	}
	return count, nil
}

<commit_msg>Fix that it finds only one per one file<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	// "regexp"
)

var out io.Writer

func init() {
	out = os.Stdout
}

// Does the grepping
func grep(r io.Reader, pmms []PolarizedMultiMatcher, opt *grepOpt) (int, error) {
	buf := bufio.NewReader(r)
	n := 1
	count := 0
	for {
		b, _, err := buf.ReadLine()
		if err != nil {
			if err == io.EOF {
				return count, nil
			}
			return 0, err
		}
		line    := string(b)
		matched := true
		for _, pmm := range pmms {
			if pmm.matcher.MatchString(line) != pmm.polar {
				matched = false
				break
			}
		}
		if matched {
			count++
			if opt.optFilename {
				fmt.Fprintf(out, "%s:", opt.filename)
			}
			if opt.optNumber {
				fmt.Fprintf(out, "%d:", n)
			}
			fmt.Fprintln(out, line)
		}
		n++
	}
	return count, nil
}

<|endoftext|>