<commit_before>package goriot

import (
	"fmt"
)

//Game represents a League of Legends game
type Game struct {
	ChampionID    int      `json:"championId"`
	CreateDate    int64    `json:"createDate"`
	FellowPlayers []Player `json:"fellowPlayers"`
	GameID        int64    `json:"gameId"`
	GameMode      string   `json:"gameMode"`
	GameType      string   `json:"gameType"`
	Invalid       bool     `json:"invalid"`
	Level         int      `json:"level"`
	MapID         int      `json:"mapId"`
	Spell1        int      `json:"spell1"`
	Spell2        int      `json:"spell2"`
	Statistics    GameStat `json:"stats"`
	SubType       string   `json:"subType"`
	TeamID        int      `json:"teamId"`
}

//Player represents a League of Legends player that was in the requested game
type Player struct {
	ChampionID int   `json:"championId"`
	SummonerID int64 `json:"summonerId"`
	TeamID     int   `json:"teamId"`
}

//GameStat represents a stat for the assosiated Game
type GameStat struct {
	Assists                         int  `json:"assists"`
	BarracksKilled                  int  `json:"barracksKilled"`
	ChampionsKilled                 int  `json:"championsKilled"`
	combatPlayerScore               int  `json:"combatPlayerScore"`
	ConsumablesPurchased            int  `json:"consumablesPurchased"`
	DamageDealtPlayer               int  `json:"damageDealtPlayer"`
	DoubleKills                     int  `json:"doubleKills"`
	FirstBlood                      int  `json:"firstBlood"`
	Gold                            int  `json:"gold"`
	GoldEarned                      int  `json:"goldEarned"`
	GoldSpent                       int  `json:"goldSpent"`
	Item0                           int  `json:"item0"`
	Item1                           int  `json:"item1"`
	Item2                           int  `json:"item2"`
	Item3                           int  `json:"item3"`
	Item4                           int  `json:"item4"`
	Item5                           int  `json:"item5"`
	Item6                           int  `json:"item6"`
	ItemsPurchased                  int  `json:"itemsPurchased"`
	KillingSprees                   int  `json:"killingSprees"`
	LargestCriticalStrike           int  `json:"largestCriticalStrike"`
	LargestKillingSpree             int  `json:"largestKillingSpree"`
	LargestMultiKill                int  `json:"largestMultiKill"`
	LegendaryItemsCreated           int  `json:"legendaryItemsCreated"`
	Level                           int  `json:"level"`
	MagicDamageDealtPlayer          int  `json:"magicDamageDealtPlayer"`
	MagicDamageDealtToChampions     int  `json:"magicDamageDealtToChampions"`
	MagicDamageTaken                int  `json:"magicDamageTaken"`
	MinionsDenied                   int  `json:"minionsDenied"`
	MinionsKilled                   int  `json:"minionsKilled"`
	NeutralMinionsKilled            int  `json:"neutralMinionsKilled"`
	NeutralMinionsKilledEnemyJungle int  `json:"neutralMinionsKilledEnemyJungle"`
	NeutralMinionsKilledYourJungle  int  `json:"neutralMinionsKilledYourJungle"`
	NexusKilled                     bool `json:"nexusKilled"`
	NodeCapture                     int  `json:"nodeCapture"`
	NodeCaptureAssist               int  `json:"nodeCaptureAssist"`
	NodeNeutralize                  int  `json:"nodeNeutralize"`
	NodeNeutralizeAssist            int  `json:"nodeNeutralizeAssist"`
	NumDeaths                       int  `json:"numDeaths"`
	NumItemsBought                  int  `json:"numItemsBought"`
	ObjectivePlayerSocre            int  `json:"objectivePlayerScore"`
	PentaKills                      int  `json:"pentaKills"`
	PhysicalDamageDealtPlayer       int  `json:"physicalDamageDealtPlayer"`
	PysicalDamageDealtToChampions   int  `json:"physicalDmageDealtToChampions"`
	PhysicalDamageTaken             int  `json:"physicalDamageTaken"`
	QuadraKills                     int  `json:"quadraKills"`
	SightWardsBought                int  `json:"sightWardsBought"`
	Spell1Cast                      int  `json:"spell1Cast"`
	Spell2Cast                      int  `json:"spell2Cast"`
	Spell3Cast                      int  `json:"spell3Cast"`
	Spell4Cast                      int  `json:"spell4Cast"`
	SummonSpell1Cast                int  `json:"summonSpell1Cast"`
	SummonSpell2Cast                int  `json:"summonSpell2Cast"`
	SuperMonsterKilled              int  `json:"superMonsterKilled"`
	Team                            int  `json:"team"`
	TeamObjective                   int  `json:"teamObjective"`
	TimePlayed                      int  `json:"timePlayed"`
	TotalDamageDealt                int  `json:"totalDamageDealt"`
	TotalDamageDealtToChampions     int  `json:"totalDamageDealtToChampions"`
	TotalDamageTaken                int  `json:"totalDamageTaken"`
	TotalHeal                       int  `json:"totalHeal"`
	TotalPlayerScore                int  `json:"totalPlayerScore"`
	TotalScoreRank                  int  `json:"totalScoreRank"`
	TotalTimeCrowdControlDealt      int  `json:"totalTimeCrowdControlDealt"`
	TotalUnitsHealed                int  `json:"totalUnitsHealed"`
	TripleKills                     int  `json:"tripleKills"`
	TrueDamageDealtPlayer           int  `json:"trueDamageDealtPlayer"`
	TrueDamageDealtToChampions      int  `json:"trueDamageDealtToChampions"`
	TrueDamageTaken                 int  `json:"trueDamageTaken"`
	TurretsKilled                   int  `json:"turretsKilled"`
	UnrealKills                     int  `json:"unrealKills"`
	VictoryPointTotal               int  `json:"victoryPointTotal"`
	VisionWardsBought               int  `json:"visionWardsBought"`
	WardKilled                      int  `json:"wardKilled"`
	WardPlaced                      int  `json:"wardPlaced"`
	Win                             bool `json:"win"`
}

type gamesList struct {
	Games []Game `json:"games"`
}

//RecentGameBySummoner retrieves the current list of recent games from the Riot Games API.
//It returns an array of Game(s) and any errors that occured from the server
//The global API key must be set before use
func RecentGameBySummoner(region string, summonerID int64) (games []Game, err error) {
	var gameslist gamesList
	if !IsKeySet() {
		return games, ErrAPIKeyNotSet
	}
	args := "api_key=" + apikey
	url := fmt.Sprintf("%v/lol/%v/v1.3/game/by-summoner/%d/recent?%v", BaseURL, region, summonerID, args)
	err = requestAndUnmarshal(url, &gameslist)
	if err != nil {
		return
	}

	return gameslist.Games, err
}
<commit_msg>Fixed typos in GameStat struct<commit_after>package goriot

import (
	"fmt"
)

//Game represents a League of Legends game
type Game struct {
	ChampionID    int      `json:"championId"`
	CreateDate    int64    `json:"createDate"`
	FellowPlayers []Player `json:"fellowPlayers"`
	GameID        int64    `json:"gameId"`
	GameMode      string   `json:"gameMode"`
	GameType      string   `json:"gameType"`
	Invalid       bool     `json:"invalid"`
	Level         int      `json:"level"`
	MapID         int      `json:"mapId"`
	Spell1        int      `json:"spell1"`
	Spell2        int      `json:"spell2"`
	Statistics    GameStat `json:"stats"`
	SubType       string   `json:"subType"`
	TeamID        int      `json:"teamId"`
}

//Player represents a League of Legends player that was in the requested game
type Player struct {
	ChampionID int   `json:"championId"`
	SummonerID int64 `json:"summonerId"`
	TeamID     int   `json:"teamId"`
}

//GameStat represents a stat for the assosiated Game
type GameStat struct {
	Assists                         int  `json:"assists"`
	BarracksKilled                  int  `json:"barracksKilled"`
	ChampionsKilled                 int  `json:"championsKilled"`
	CombatPlayerScore               int  `json:"combatPlayerScore"`
	ConsumablesPurchased            int  `json:"consumablesPurchased"`
	DamageDealtPlayer               int  `json:"damageDealtPlayer"`
	DoubleKills                     int  `json:"doubleKills"`
	FirstBlood                      int  `json:"firstBlood"`
	Gold                            int  `json:"gold"`
	GoldEarned                      int  `json:"goldEarned"`
	GoldSpent                       int  `json:"goldSpent"`
	Item0                           int  `json:"item0"`
	Item1                           int  `json:"item1"`
	Item2                           int  `json:"item2"`
	Item3                           int  `json:"item3"`
	Item4                           int  `json:"item4"`
	Item5                           int  `json:"item5"`
	Item6                           int  `json:"item6"`
	ItemsPurchased                  int  `json:"itemsPurchased"`
	KillingSprees                   int  `json:"killingSprees"`
	LargestCriticalStrike           int  `json:"largestCriticalStrike"`
	LargestKillingSpree             int  `json:"largestKillingSpree"`
	LargestMultiKill                int  `json:"largestMultiKill"`
	LegendaryItemsCreated           int  `json:"legendaryItemsCreated"`
	Level                           int  `json:"level"`
	MagicDamageDealtPlayer          int  `json:"magicDamageDealtPlayer"`
	MagicDamageDealtToChampions     int  `json:"magicDamageDealtToChampions"`
	MagicDamageTaken                int  `json:"magicDamageTaken"`
	MinionsDenied                   int  `json:"minionsDenied"`
	MinionsKilled                   int  `json:"minionsKilled"`
	NeutralMinionsKilled            int  `json:"neutralMinionsKilled"`
	NeutralMinionsKilledEnemyJungle int  `json:"neutralMinionsKilledEnemyJungle"`
	NeutralMinionsKilledYourJungle  int  `json:"neutralMinionsKilledYourJungle"`
	NexusKilled                     bool `json:"nexusKilled"`
	NodeCapture                     int  `json:"nodeCapture"`
	NodeCaptureAssist               int  `json:"nodeCaptureAssist"`
	NodeNeutralize                  int  `json:"nodeNeutralize"`
	NodeNeutralizeAssist            int  `json:"nodeNeutralizeAssist"`
	NumDeaths                       int  `json:"numDeaths"`
	NumItemsBought                  int  `json:"numItemsBought"`
	ObjectivePlayerScore            int  `json:"objectivePlayerScore"`
	PentaKills                      int  `json:"pentaKills"`
	PhysicalDamageDealtPlayer       int  `json:"physicalDamageDealtPlayer"`
	PhysicalDamageDealtToChampions  int  `json:"physicalDamageDealtToChampions"`
	PhysicalDamageTaken             int  `json:"physicalDamageTaken"`
	QuadraKills                     int  `json:"quadraKills"`
	SightWardsBought                int  `json:"sightWardsBought"`
	Spell1Cast                      int  `json:"spell1Cast"`
	Spell2Cast                      int  `json:"spell2Cast"`
	Spell3Cast                      int  `json:"spell3Cast"`
	Spell4Cast                      int  `json:"spell4Cast"`
	SummonSpell1Cast                int  `json:"summonSpell1Cast"`
	SummonSpell2Cast                int  `json:"summonSpell2Cast"`
	SuperMonsterKilled              int  `json:"superMonsterKilled"`
	Team                            int  `json:"team"`
	TeamObjective                   int  `json:"teamObjective"`
	TimePlayed                      int  `json:"timePlayed"`
	TotalDamageDealt                int  `json:"totalDamageDealt"`
	TotalDamageDealtToChampions     int  `json:"totalDamageDealtToChampions"`
	TotalDamageTaken                int  `json:"totalDamageTaken"`
	TotalHeal                       int  `json:"totalHeal"`
	TotalPlayerScore                int  `json:"totalPlayerScore"`
	TotalScoreRank                  int  `json:"totalScoreRank"`
	TotalTimeCrowdControlDealt      int  `json:"totalTimeCrowdControlDealt"`
	TotalUnitsHealed                int  `json:"totalUnitsHealed"`
	TripleKills                     int  `json:"tripleKills"`
	TrueDamageDealtPlayer           int  `json:"trueDamageDealtPlayer"`
	TrueDamageDealtToChampions      int  `json:"trueDamageDealtToChampions"`
	TrueDamageTaken                 int  `json:"trueDamageTaken"`
	TurretsKilled                   int  `json:"turretsKilled"`
	UnrealKills                     int  `json:"unrealKills"`
	VictoryPointTotal               int  `json:"victoryPointTotal"`
	VisionWardsBought               int  `json:"visionWardsBought"`
	WardKilled                      int  `json:"wardKilled"`
	WardPlaced                      int  `json:"wardPlaced"`
	Win                             bool `json:"win"`
}

type gamesList struct {
	Games []Game `json:"games"`
}

//RecentGameBySummoner retrieves the current list of recent games from the Riot Games API.
//It returns an array of Game(s) and any errors that occured from the server
//The global API key must be set before use
func RecentGameBySummoner(region string, summonerID int64) (games []Game, err error) {
	var gameslist gamesList
	if !IsKeySet() {
		return games, ErrAPIKeyNotSet
	}
	args := "api_key=" + apikey
	url := fmt.Sprintf("%v/lol/%v/v1.3/game/by-summoner/%d/recent?%v", BaseURL, region, summonerID, args)
	err = requestAndUnmarshal(url, &gameslist)
	if err != nil {
		return
	}

	return gameslist.Games, err
}
<|endoftext|>


<commit_before>package lendingclub

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"

	"github.com/shopspring/decimal"
)

const (
	accountsResource      = "/accounts/%d"
	summaryEndpoint       = "/summary"
	availableCashEndpoint = "/availablecash"
	addFundsEndpoint      = "/funds/add"
)

type Time struct {
	time.Time
}

const timeFormat = "2006-01-02T15:04:05.999-0700"

func (lct *Time) UnmarshalJSON(b []byte) error {
	if b[0] == '"' && b[len(b)-1] == '"' {
		b = b[1 : len(b)-1]
	}

	t, err := time.Parse(timeFormat, string(b))
	if err != nil {
		return err
	}
	*lct = Time{Time: t}

	return nil
}

func (lct Time) MarshalJSON() ([]byte, error) {
	ts := fmt.Sprintf("%q", lct.Format(timeFormat))
	return []byte(ts), nil
}

type AccountsResource struct {
	client   *Client
	endpoint string
}

func (c *Client) Accounts(investorID int) *AccountsResource {
	return &AccountsResource{
		client:   c,
		endpoint: fmt.Sprintf(lendingClubAPI+accountsResource, investorID),
	}
}

type AvailableCash struct {
	InvestorID    int
	AvailableCash decimal.Decimal
}

func (ar *AccountsResource) AvailableCash() (*AvailableCash, error) {
	req, err := ar.client.newRequest("GET", ar.endpoint+availableCashEndpoint, nil)
	if err != nil {
		return nil, err
	}

	res, err := ar.client.Do(req)
	if err != nil {
		return nil, err
	}

	var ac AvailableCash
	if err := ar.client.processResponse(res, &ac); err != nil {
		return nil, err
	}

	return &ac, nil
}

type Summary struct {
	AvailableCash        decimal.Decimal
	InvestorID           int
	AccruedInterest      decimal.Decimal
	OutstandingPrincipal decimal.Decimal
	AccountTotal         decimal.Decimal
	TotalNotes           int
	TotalPortfolios      int
	InFundingBalance     decimal.Decimal
	ReceivedInterest     decimal.Decimal
	ReceivedPrincipal    decimal.Decimal
	ReceivedLateFees     decimal.Decimal
}

func (ar *AccountsResource) Summary() (*Summary, error) {
	req, err := ar.client.newRequest("GET", ar.endpoint+summaryEndpoint, nil)
	if err != nil {
		return nil, err
	}

	res, err := ar.client.Do(req)
	if err != nil {
		return nil, err
	}

	var sum Summary
	if err := ar.client.processResponse(res, &sum); err != nil {
		return nil, err
	}

	return &sum, nil
}

type FundsPayload struct {
	Amount            decimal.Decimal `json:"amount"`
	TransferFrequency string          `json:"transferFrequency"`
	StartDate         *Time           `json:"startDate,omitempty"`
	EndDate           *Time           `json:"endDate,omitempty"`
}

type FundsResponse struct {
	FundsPayload
	InvestorID                 int  `json:"investorId"`
	EstimatedFundsTransferDate Time `json:"estimatedFundsTransferDate"`
}

func (ar *AccountsResource) AddFunds(fundTransfer *FundsPayload) (*FundsResponse, error) {
	payload, err := json.Marshal(fundTransfer)
	if err != nil {
		return nil, err
	}

	req, err := ar.client.newRequest("POST", ar.endpoint+addFundsEndpoint, bytes.NewBuffer(payload))
	if err != nil {
		return nil, err
	}

	res, err := ar.client.Do(req)
	if err != nil {
		return nil, err
	}

	var fr FundsResponse
	if err := ar.client.processResponse(res, &fr); err != nil {
		return nil, err
	}

	return &fr, nil
}
<commit_msg>FundsResponse -> Deposit for better wording<commit_after>package lendingclub

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"

	"github.com/shopspring/decimal"
)

const (
	accountsResource      = "/accounts/%d"
	summaryEndpoint       = "/summary"
	availableCashEndpoint = "/availablecash"
	addFundsEndpoint      = "/funds/add"
)

type Time struct {
	time.Time
}

const timeFormat = "2006-01-02T15:04:05.999-0700"

func (lct *Time) UnmarshalJSON(b []byte) error {
	if b[0] == '"' && b[len(b)-1] == '"' {
		b = b[1 : len(b)-1]
	}

	t, err := time.Parse(timeFormat, string(b))
	if err != nil {
		return err
	}
	*lct = Time{Time: t}

	return nil
}

func (lct Time) MarshalJSON() ([]byte, error) {
	ts := fmt.Sprintf("%q", lct.Format(timeFormat))
	return []byte(ts), nil
}

type AccountsResource struct {
	client   *Client
	endpoint string
}

func (c *Client) Accounts(investorID int) *AccountsResource {
	return &AccountsResource{
		client:   c,
		endpoint: fmt.Sprintf(lendingClubAPI+accountsResource, investorID),
	}
}

type AvailableCash struct {
	InvestorID    int
	AvailableCash decimal.Decimal
}

func (ar *AccountsResource) AvailableCash() (*AvailableCash, error) {
	req, err := ar.client.newRequest("GET", ar.endpoint+availableCashEndpoint, nil)
	if err != nil {
		return nil, err
	}

	res, err := ar.client.Do(req)
	if err != nil {
		return nil, err
	}

	var ac AvailableCash
	if err := ar.client.processResponse(res, &ac); err != nil {
		return nil, err
	}

	return &ac, nil
}

type Summary struct {
	AvailableCash        decimal.Decimal
	InvestorID           int
	AccruedInterest      decimal.Decimal
	OutstandingPrincipal decimal.Decimal
	AccountTotal         decimal.Decimal
	TotalNotes           int
	TotalPortfolios      int
	InFundingBalance     decimal.Decimal
	ReceivedInterest     decimal.Decimal
	ReceivedPrincipal    decimal.Decimal
	ReceivedLateFees     decimal.Decimal
}

func (ar *AccountsResource) Summary() (*Summary, error) {
	req, err := ar.client.newRequest("GET", ar.endpoint+summaryEndpoint, nil)
	if err != nil {
		return nil, err
	}

	res, err := ar.client.Do(req)
	if err != nil {
		return nil, err
	}

	var sum Summary
	if err := ar.client.processResponse(res, &sum); err != nil {
		return nil, err
	}

	return &sum, nil
}

type FundsPayload struct {
	Amount            decimal.Decimal `json:"amount"`
	TransferFrequency string          `json:"transferFrequency"`
	StartDate         *Time           `json:"startDate,omitempty"`
	EndDate           *Time           `json:"endDate,omitempty"`
}

type Deposit struct {
	FundsPayload
	InvestorID                 int  `json:"investorId"`
	EstimatedFundsTransferDate Time `json:"estimatedFundsTransferDate"`
}

func (ar *AccountsResource) AddFunds(fundTransfer *FundsPayload) (*Deposit, error) {
	payload, err := json.Marshal(fundTransfer)
	if err != nil {
		return nil, err
	}

	req, err := ar.client.newRequest("POST", ar.endpoint+addFundsEndpoint, bytes.NewBuffer(payload))
	if err != nil {
		return nil, err
	}

	res, err := ar.client.Do(req)
	if err != nil {
		return nil, err
	}

	var deposit Deposit
	if err := ar.client.processResponse(res, &deposit); err != nil {
		return nil, err
	}

	return &deposit, nil
}
<|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)
	}

	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 && len(glob.parserState.processedTokens) > 0 {
			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>More efficient negation parsing<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)
	reader := strings.NewReader(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
	err = parseNegation(reader, glob)
	if err != nil {
		return nil, err
	}

	// 2. Tokenise and convert!
	tokeniser := newGlobTokeniser(reader, 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(r io.RuneScanner, glob *globImpl) error {
	for {
		char, _, err := r.ReadRune()

		if err != nil {
			return err
		} else if char == '!' {
			glob.negated = !glob.negated
		} else {
			r.UnreadRune()
			return 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 && len(glob.parserState.processedTokens) > 0 {
			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>package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
)

func main() {
	http.HandleFunc("/", handler)
	port := os.Getenv("PORT")

	if port == "" {
		port = "8080"
	}

	socket := fmt.Sprintf(":%v", port)
	log.Println(fmt.Sprintf("Listening on %v...", socket))
	log.Fatal(http.ListenAndServe(socket, nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
	host, err := os.Hostname()

	if err != nil {
		fmt.Fprintln(w, err)
	}

	fmt.Fprintln(w, fmt.Sprintf("%v\n", host))

	addrs, err := net.InterfaceAddrs()

	if err != nil {
		fmt.Fprintln(w, err)
	}

	for _, a := range addrs {
		fmt.Fprintln(w, a)
	}

	log.Println(fmt.Sprintf("%s %s %s %d", r.Method, r.URL, r.Proto, 200))
}
<commit_msg>make log format key=value<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"time"
)

func main() {
	http.HandleFunc("/", handler)
	port := os.Getenv("PORT")

	if port == "" {
		port = "8080"
	}

	socket := fmt.Sprintf(":%v", port)
	log.Println(fmt.Sprintf("Listening on %v...", socket))
	log.Fatal(http.ListenAndServe(socket, nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
	host, err := os.Hostname()

	if err != nil {
		fmt.Fprintln(w, err)
	}

	fmt.Fprintln(w, fmt.Sprintf("%v\n", host))

	addrs, err := net.InterfaceAddrs()

	if err != nil {
		fmt.Fprintln(w, err)
	}

	for _, a := range addrs {
		fmt.Fprintln(w, a)
	}

	fmt.Println(fmt.Sprintf("timestamp=%s method=%s route=%s protocol=%s status_code=%d", time.Now().Format("2006-01-02T15:04:05"), r.Method, r.URL, r.Proto, 200))
}
<|endoftext|>


<commit_before>// Gone Time Tracker -or- Where has my time gone?
package main

import (
	"encoding/gob"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
	"github.com/mewkiz/pkg/goutil"
)

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

var (
	goneDir string
	tracks  Tracker
	tmpl    *template.Template
	zzz     bool
	m       sync.Mutex
	logger  *log.Logger
)

func init() {
	goneDir, err := goutil.SrcDir("github.com/dim13/gone")
	if err != nil {
		log.Fatal("init: ", err)
	}
	tmpl = template.Must(template.ParseFiles(filepath.Join(goneDir, "index.html")))
}

type Tracker map[Window]*Track

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

type Window struct {
	Class string
	Name  string
}

func (t Track) String() string {
	return fmt.Sprintf("%s %s", t.Seen.Format("2006/01/02 15:04:05"), t.Spent)
}

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

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

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

	current := 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 current != nil {
				m.Lock()
				current.Spent += time.Since(current.Seen)
				m.Unlock()
			}
			current = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				log.Println("away from keyboard")
				current = nil
				zzz = true
			default:
				log.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	m.Lock()
	for k, v := range t {
		if time.Since(v.Seen) > d {
			logger.Println(v, k)
			delete(t, k)
		}
	}
	m.Unlock()
}

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

func (t Tracker) store(fname string) {
	tmp := fname + ".tmp"
	dump, err := os.Create(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.Remove(tmp)
		return
	}
	os.Rename(tmp, fname)
}

func main() {
	logfile, err := os.OpenFile(filepath.Join(goneDir, logf), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer logfile.Close()
	logger = log.New(logfile, "", log.LstdFlags)

	dumpPath := filepath.Join(goneDir, dump)
	tracks = load(dumpPath)

	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(dumpPath)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/gone.json", dumpHandler)
	http.HandleFunc("/reset", resetHandler)
	err = http.ListenAndServe(port, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>make interface more consistent<commit_after>// Gone Time Tracker -or- Where has my time gone?
package main

import (
	"encoding/gob"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
	"github.com/mewkiz/pkg/goutil"
)

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

var (
	goneDir string
	tracks  Tracker
	tmpl    *template.Template
	zzz     bool
	m       sync.Mutex
	logger  *log.Logger
)

func init() {
	goneDir, err := goutil.SrcDir("github.com/dim13/gone")
	if err != nil {
		log.Fatal("init: ", err)
	}
	tmpl = template.Must(template.ParseFiles(filepath.Join(goneDir, "index.html")))
}

type Tracker map[Window]*Track

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

type Window struct {
	Class string
	Name  string
}

func (t Track) String() string {
	return fmt.Sprintf("%s %s", t.Seen.Format("2006/01/02 15:04:05"), t.Spent)
}

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

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

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

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

func (t Tracker) cleanup(d time.Duration) {
	m.Lock()
	for k, v := range t {
		if time.Since(v.Seen) > d {
			logger.Println(v, k)
			delete(t, k)
		}
	}
	m.Unlock()
}

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

func (t Tracker) store(fname string) {
	tmp := fname + ".tmp"
	dump, err := os.Create(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.Remove(tmp)
		return
	}
	os.Rename(tmp, fname)
}

func main() {
	logfile, err := os.OpenFile(filepath.Join(goneDir, logf), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer logfile.Close()
	logger = log.New(logfile, "", log.LstdFlags)

	dumpPath := filepath.Join(goneDir, dump)
	tracks = load(dumpPath)

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


<commit_before>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    = "127.0.0.1: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.Sprintf("%s %s", t.Seen.Format("2006/01/02 15:04:05"), 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) (current *Track) {
	if win, ok := x.winName(); ok {
		m.Lock()
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Seen = time.Now()
		current = 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()

	current := 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 current != nil {
				m.Lock()
				current.Spent += time.Since(current.Seen)
				m.Unlock()
			}
			current = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				fmt.Println("away from keyboard")
				current = nil
				zzz = true
			default:
				fmt.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

type logger struct {
	*os.File
}

func openLog(fname string) logger {
	f, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	return logger{f}
}

func (l logger) logDelete(w Window, t Track) {
	log.Println("removing", w.Name)
	log.SetOutput(l)
	log.Println(t, w)
	log.SetOutput(os.Stderr)
}

func (t Tracker) cleanup(d time.Duration) {
	f := openLog(logf)
	defer f.Close()
	m.Lock()
	for k, v := range t {
		if time.Since(v.Seen) > d {
			f.logDelete(k, *v)
			delete(t, k)
		}
	}
	m.Unlock()
}

func (t Tracker) reset() {
	f := openLog(logf)
	defer f.Close()
	m.Lock()
	for k, v := range t {
		f.logDelete(k, *v)
		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)
}

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)
	err := http.ListenAndServe(port, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>switch to propper logging interface<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    = "127.0.0.1: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
	logger *log.Logger
)

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.Sprintf("%s %s", t.Seen.Format("2006/01/02 15:04:05"), 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) (current *Track) {
	if win, ok := x.winName(); ok {
		m.Lock()
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Seen = time.Now()
		current = 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()

	current := 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 current != nil {
				m.Lock()
				current.Spent += time.Since(current.Seen)
				m.Unlock()
			}
			current = x.update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				fmt.Println("away from keyboard")
				current = nil
				zzz = true
			default:
				fmt.Println("back to keyboard")
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	m.Lock()
	for k, v := range t {
		if time.Since(v.Seen) > d {
			logger.Println(v, k)
			delete(t, k)
		}
	}
	m.Unlock()
}

func (t Tracker) reset() {
	m.Lock()
	for k, v := range t {
		logger.Println(v, k)
		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)
}

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() {
	logfile, err := os.OpenFile(logf, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	defer logfile.Close()
	logger = log.New(logfile, "", log.LstdFlags)

	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)
	err = http.ListenAndServe(port, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"sort"
	"strings"
)

type helper struct {
	topics map[string]*fctCmd
}

func NewHelper() *helper {
	h := new(helper)
	h.topics = make(map[string]*fctCmd)
	return h
}

func (h *helper) Add(s string, c *fctCmd) {
	h.topics[s] = c
}

func (h *helper) All() {
	keys := make([]string, 0)
	for k := range h.topics {
		keys = append(keys, k)
	}

	sort.Strings(keys)

	for _, v := range keys {
		fmt.Printf("%s\n\t%s\n\n", h.topics[v].helpMsg, h.topics[v].description)
	}
}

func (h *helper) Execute(args []string) {
	if len(args) < 1 {
		fmt.Println("factom-cli help [subcommand]")
		return
	}
	if len(args) < 2 {
		h.All()
		return
	}

	topic := strings.Join(args[1:], " ")

	c, ok := h.topics[topic]
	if !ok {
		if c, ok = h.topics[args[1]]; !ok {
			fmt.Println("No help for:", topic)
			return
		}
	}
	fmt.Printf("%s\n\t%s\n", c.helpMsg, c.description)
}

var help = NewHelper()
<commit_msg>added flag usage to help message<commit_after>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"sort"
	"strings"
)

type helper struct {
	topics map[string]*fctCmd
}

func NewHelper() *helper {
	h := new(helper)
	h.topics = make(map[string]*fctCmd)
	return h
}

func (h *helper) Add(s string, c *fctCmd) {
	h.topics[s] = c
}

func (h *helper) All() {
	flag.Usage()

	fmt.Println()
	keys := make([]string, 0)
	for k := range h.topics {
		keys = append(keys, k)
	}

	sort.Strings(keys)

	for _, v := range keys {
		fmt.Printf("%s\n\t%s\n\n", h.topics[v].helpMsg, h.topics[v].description)
	}
}

func (h *helper) Execute(args []string) {
	if len(args) < 1 {
		fmt.Println("factom-cli help [subcommand]")
		return
	}
	if len(args) < 2 {
		h.All()
		return
	}

	topic := strings.Join(args[1:], " ")

	c, ok := h.topics[topic]
	if !ok {
		if c, ok = h.topics[args[1]]; !ok {
			fmt.Println("No help for:", topic)
			return
		}
	}
	fmt.Printf("%s\n\t%s\n", c.helpMsg, c.description)
}

var help = NewHelper()
<|endoftext|>


<commit_before>package gocropy

import (
	"encoding/xml"
	"fmt"
	"image"
	_ "image/jpeg"
	_ "image/png"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strconv"
)

type HOCRTitle struct {
	Title string `xml:"title,attr"`
}

type HOCRClass struct {
	Class string `xml:"class,attr"`
}

type HOCRSpan struct {
	HOCRClass
	HOCRTitle
	Data  string     `xml:",chardata"`
	Token []HOCRSpan `xml:"span"`
}

type HOCRDiv struct {
	HOCRClass
	HOCRTitle
	Spans []HOCRSpan `xml:"span"`
}

type HOCRBody struct {
	Div HOCRDiv `xml:"div"`
}

type HOCRMeta struct {
	HttpEquiv string `xml:"http-equiv,attr,omitempty"`
	Name      string `xml:"name,attr,omitempty"`
	Content   string `xml:"content,attr"`
}

type HOCRHead struct {
	Title string     `xml:"title"`
	Metas []HOCRMeta `xml:"meta"`
}

type HOCR struct {
	XMLName xml.Name
	Head    HOCRHead `xml:"head"`
	Body    HOCRBody `xml:"body"`
	File    string
}

func (hocr *HOCR) ReadImageFileBbox() (*Bbox, error) {
	basedir, _ := path.Split(hocr.File)
	file := hocr.Body.Div.GetFile()
	in, err := os.Open(path.Join(basedir, file))
	if err != nil {
		return nil, err
	}
	defer in.Close()
	img, _, err := image.DecodeConfig(in)
	if err != nil {
		return nil, err
	}
	if img.ColorModel == nil {
		return nil, fmt.Errorf("Could not read image (missing decoder?)")
	}
	bbox := Bbox{0, 0, int64(img.Width), int64(img.Height)}
	hocr.Body.Div.SetBbox(bbox)
	return &bbox, nil
}

var fileRegex = regexp.MustCompile("file\\s+(.*)")

func (title HOCRTitle) GetFile() string {
	m := fileRegex.FindStringSubmatch(title.Title)
	if m != nil {
		return m[1]
	} else {
		return ""
	}
}

func (title *HOCRTitle) SetFile(file string) {
	if len(title.Title) > 0 {
		title.Title = fmt.Sprintf("%s; file %s", title.Title, file)
	} else {
		title.Title = fmt.Sprintf("file %s", file)
	}
}

var bboxRegex = regexp.MustCompile("bbox\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)")

func (title HOCRTitle) GetBbox() Bbox {
	var bbox = Bbox{-1, -1, -1, -1}
	m := bboxRegex.FindStringSubmatch(title.Title)
	if m != nil {
		bbox.Left, _ = strconv.ParseInt(m[1], 0, 64)
		bbox.Top, _ = strconv.ParseInt(m[2], 0, 64)
		bbox.Right, _ = strconv.ParseInt(m[3], 0, 64)
		bbox.Bottom, _ = strconv.ParseInt(m[4], 0, 64)
	}
	return bbox
}

func (title *HOCRTitle) SetBbox(bbox Bbox) {
	if len(title.Title) > 0 {
		title.Title = fmt.Sprintf("%s; %v", title.Title, bbox)
	} else {
		title.Title = fmt.Sprintf("%v", bbox)
	}
}

func ReadHOCR(file string) (*HOCR, error) {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	hocr := HOCR{File: file}
	err = xml.Unmarshal(data, &hocr)
	if err != nil {
		return nil, err
	}
	return &hocr, nil
}

func MustReadHOCR(file string) *HOCR {
	hocr, err := ReadHOCR(file)
	if err != nil {
		panic(err)
	}
	return hocr
}
<commit_msg>remove `File` the xml output<commit_after>package gocropy

import (
	"encoding/xml"
	"fmt"
	"image"
	_ "image/jpeg"
	_ "image/png"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strconv"
)

type HOCRTitle struct {
	Title string `xml:"title,attr"`
}

type HOCRClass struct {
	Class string `xml:"class,attr"`
}

type HOCRSpan struct {
	HOCRClass
	HOCRTitle
	Data  string     `xml:",chardata"`
	Token []HOCRSpan `xml:"span"`
}

type HOCRDiv struct {
	HOCRClass
	HOCRTitle
	Spans []HOCRSpan `xml:"span"`
}

type HOCRBody struct {
	Div HOCRDiv `xml:"div"`
}

type HOCRMeta struct {
	HttpEquiv string `xml:"http-equiv,attr,omitempty"`
	Name      string `xml:"name,attr,omitempty"`
	Content   string `xml:"content,attr"`
}

type HOCRHead struct {
	Title string     `xml:"title"`
	Metas []HOCRMeta `xml:"meta"`
}

type HOCR struct {
	XMLName xml.Name
	Head    HOCRHead `xml:"head"`
	Body    HOCRBody `xml:"body"`
	File    string   `xml:"-"`
}

func (hocr *HOCR) ReadImageFileBbox() (*Bbox, error) {
	basedir, _ := path.Split(hocr.File)
	file := hocr.Body.Div.GetFile()
	in, err := os.Open(path.Join(basedir, file))
	if err != nil {
		return nil, err
	}
	defer in.Close()
	img, _, err := image.DecodeConfig(in)
	if err != nil {
		return nil, err
	}
	if img.ColorModel == nil {
		return nil, fmt.Errorf("Could not read image (missing decoder?)")
	}
	bbox := Bbox{0, 0, int64(img.Width), int64(img.Height)}
	hocr.Body.Div.SetBbox(bbox)
	return &bbox, nil
}

var fileRegex = regexp.MustCompile("file\\s+(.*)")

func (title HOCRTitle) GetFile() string {
	m := fileRegex.FindStringSubmatch(title.Title)
	if m != nil {
		return m[1]
	} else {
		return ""
	}
}

func (title *HOCRTitle) SetFile(file string) {
	if len(title.Title) > 0 {
		title.Title = fmt.Sprintf("%s; file %s", title.Title, file)
	} else {
		title.Title = fmt.Sprintf("file %s", file)
	}
}

var bboxRegex = regexp.MustCompile("bbox\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)")

func (title HOCRTitle) GetBbox() Bbox {
	var bbox = Bbox{-1, -1, -1, -1}
	m := bboxRegex.FindStringSubmatch(title.Title)
	if m != nil {
		bbox.Left, _ = strconv.ParseInt(m[1], 0, 64)
		bbox.Top, _ = strconv.ParseInt(m[2], 0, 64)
		bbox.Right, _ = strconv.ParseInt(m[3], 0, 64)
		bbox.Bottom, _ = strconv.ParseInt(m[4], 0, 64)
	}
	return bbox
}

func (title *HOCRTitle) SetBbox(bbox Bbox) {
	if len(title.Title) > 0 {
		title.Title = fmt.Sprintf("%s; %v", title.Title, bbox)
	} else {
		title.Title = fmt.Sprintf("%v", bbox)
	}
}

func ReadHOCR(file string) (*HOCR, error) {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	hocr := HOCR{File: file}
	err = xml.Unmarshal(data, &hocr)
	if err != nil {
		return nil, err
	}
	return &hocr, nil
}

func MustReadHOCR(file string) *HOCR {
	hocr, err := ReadHOCR(file)
	if err != nil {
		panic(err)
	}
	return hocr
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package vix

/*
#include "vix.h"
#include "helper.h"
*/
import "C"

import "unsafe"

type Host struct {
	Provider Provider
	handle   C.VixHandle
}

// Destroys the state for a particular host instance
//
// Call this function to disconnect the host. After you call this function the
// Host object is no longer valid and you should not longer use it.
// Similarly, you should not use any other object instances obtained from the
// Host object while it was connected.
//
// Since VMware Server 1.0
func (h *Host) Disconnect() {
	if &h.handle != nil && h.handle != C.VIX_INVALID_HANDLE {
		C.VixHost_Disconnect(h.handle)
		h.handle = C.VIX_INVALID_HANDLE
	}
}

//export go_callback_char
func go_callback_char(callbackPtr unsafe.Pointer, item *C.char) {
	callback := *(*func(*C.char))(callbackPtr)
	callback(item)
}

// This function finds Vix objects. For example, when used to find all
// running virtual machines, Host.FindItems() returns a series of virtual
// machine file path names.
func (h *Host) FindItems(options SearchType) ([]string, error) {
	var jobHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var err C.VixError = C.VIX_OK
	var items []string

	callback := func(item *C.char) {
		items = append(items, C.GoString(item))
	}

	jobHandle = C.VixHost_FindItems(h.handle,
		C.VixFindItemType(options), //searchType
		C.VIX_INVALID_HANDLE,       //searchCriteria
		-1,                         //timeout
		(*C.VixEventProc)(C.find_items_callback), //callbackProc
		unsafe.Pointer(&callback))                //clientData

	defer C.Vix_ReleaseHandle(jobHandle)

	err = C.vix_job_wait(jobHandle)
	if C.VIX_OK != err {
		return nil, &VixError{
			Operation: "host.FindItems",
			Code:      int(err & 0xFFFF),
			Text:      C.GoString(C.Vix_GetErrorText(err, nil)),
		}
	}

	return items, nil
}

// This function opens a virtual machine on the host
// and returns a VM instance.
//
// Parameters:
//
//   VmxFile: The path name of the virtual machine configuration file on the
//   local host.
//
//   Password: If VM is encrypted, this is the password for VIX to be able to
//   open it.
//
// Remarks:
//
//   * This function opens a virtual machine on the host instance
//     The virtual machine is identified by vmxFile, which is a path name to the
//     configuration file (.VMX file) for that virtual machine.
//
//   * The format of the path name depends on the host operating system.
//     For example, a path name for a Windows host requires backslash as a
//     directory separator, whereas a Linux host requires a forward slash. If the
//     path name includes backslash characters, you need to precede each one with
//     an escape character. For VMware Server 2.x, the path contains a preceeding
//     data store, for example [storage1] vm/vm.vmx.
//
//   * For VMware Server hosts, a virtual machine must be registered before you
//     can open it. You can register a virtual machine by opening it with the
//     VMware Server Console, through the vmware-cmd command with the register
//     parameter, or with Host.RegisterVM().
//
//   * For vSphere, the virtual machine opened may not be the one desired if more
//     than one Datacenter contains VmxFile.
//
//   * To open an encrypted virtual machine, pass its correspondent password.
//
// Since VMware Workstation 7.0
func (h *Host) OpenVm(vmxFile, password string) (*VM, error) {
	var jobHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var propertyHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var vmHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var err C.VixError = C.VIX_OK

	defer C.Vix_ReleaseHandle(propertyHandle)
	defer C.Vix_ReleaseHandle(jobHandle)

	if password != "" {
		cpassword := C.CString(password)
		defer C.free(unsafe.Pointer(cpassword))

		err = C.alloc_vm_pwd_proplist(h.handle,
			&propertyHandle,
			cpassword)

		if C.VIX_OK != err {
			return nil, &VixError{
				Operation: "host.OpenVM",
				Code:      int(err & 0xFFFF),
				Text:      C.GoString(C.Vix_GetErrorText(err, nil)),
			}
		}
	}

	cVmxFile := C.CString(vmxFile)
	defer C.free(unsafe.Pointer(cVmxFile))

	jobHandle = C.VixHost_OpenVM(h.handle,
		cVmxFile,
		C.VIX_VMOPEN_NORMAL,
		propertyHandle,
		nil, // callbackProc
		nil) // clientData

	err = C.get_vix_handle(jobHandle,
		C.VIX_PROPERTY_JOB_RESULT_HANDLE,
		&vmHandle,
		C.VIX_PROPERTY_NONE)

	if C.VIX_OK != err {
		return nil, &VixError{
			Operation: "host.OpenVM.get_vix_handle",
			Code:      int(err & 0xFFFF),
			Text:      C.GoString(C.Vix_GetErrorText(err, nil)),
		}
	}

	return NewVirtualMachine(vmHandle, vmxFile)
}

// This function adds a virtual machine to the host's inventory.
//
// Parameters:
//
//   vmxFile: The path name of the .vmx file on the host.
//
// Remarks:
//
//   * This function registers the virtual machine identified by vmxFile, which
//     is a storage path to the configuration file (.vmx) for that virtual machine.
//     You can register a virtual machine regardless of its power state.
//
//   * The format of the path name depends on the host operating system.
//     If the path name includes backslash characters, you need to precede each
//     one with an escape character. Path to storage [standard] or [storage1] may
//     vary.
//
//   * For VMware Server 1.x, supply the full path name instead of storage path,
//     and specify provider VMWARE_SERVER to connect.
//
//   * This function has no effect on Workstation or Player, which lack a virtual
//     machine inventory.
//
//   * It is not a Vix error to register an already-registered virtual machine,
//     although the VMware Server UI shows an error icon in the Task pane.
//     Trying to register a non-existent virtual machine results in error 2000,
//     VIX_E_NOT_FOUND.
//
// Since VMware Server 1.0
func (h *Host) RegisterVm(vmxFile string) error {
	var jobHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var err C.VixError = C.VIX_OK

	cVmxFile := C.CString(vmxFile)
	defer C.free(unsafe.Pointer(cVmxFile))

	jobHandle = C.VixHost_RegisterVM(h.handle,
		cVmxFile,
		nil, // callbackProc
		nil) // clientData

	defer C.Vix_ReleaseHandle(jobHandle)

	err = C.vix_job_wait(jobHandle)
	if C.VIX_OK != err {
		return &VixError{
			Operation: "host.RegisterVM",
			Code:      int(err & 0xFFFF),
			Text:      C.GoString(C.Vix_GetErrorText(err, nil)),
		}
	}

	return nil
}

// This function removes a virtual machine from the host's inventory.
//
// Parameters:
//
//   VmxFile: The path name of the .vmx file on the host.
//
// Remarks:
//
//   * This function unregisters the virtual machine identified by vmxFile,
//     which is a storage path to the configuration file (.vmx) for that virtual
//     machine. A virtual machine must be powered off to unregister it.
//   * The format of the storage path depends on the host operating system.
//     If the storage path includes backslash characters, you need to precede each
//     one with an escape character. Path to storage [standard] or [storage1] may
//     vary.
//   * For VMware Server 1.x, supply the full path name instead of storage path,
//     and specify VMWARE_SERVER provider to connect.
//   * This function has no effect on Workstation or Player, which lack a virtual
//     machine inventory.
//   * It is not a Vix error to unregister an already-unregistered virtual machine,
//     nor is it a Vix error to unregister a non-existent virtual machine.
//
// Since VMware Server 1.0
func (h *Host) UnregisterVm(vmxFile string) error {
	var jobHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var err C.VixError = C.VIX_OK

	cVmxFile := C.CString(vmxFile)
	defer C.free(unsafe.Pointer(cVmxFile))

	jobHandle = C.VixHost_UnregisterVM(h.handle,
		cVmxFile,
		nil, // callbackProc
		nil) // clientData

	defer C.Vix_ReleaseHandle(jobHandle)

	err = C.vix_job_wait(jobHandle)
	if C.VIX_OK != err {
		return &VixError{
			Operation: "host.UnregisterVM",
			Code:      int(err & 0xFFFF),
			Text:      C.GoString(C.Vix_GetErrorText(err, nil)),
		}
	}

	return nil
}

// Copies a file or directory from the local system (where the Vix client is
// running) to the guest operating system.
//
// Parameters:
//
//   src: The path name of a file on a file system available to the Vix client.
//   guest: Guest instance where the file is going to be copied to
//   dest: The path name of a file on a file system available to the guest.
//
// Remarks:
//
//   * The virtual machine must be running while the file is copied from the Vix
//     client machine to the guest operating system.
//
//   * Existing files of the same name are overwritten, and folder contents are
//     merged.
//
//   * The copy operation requires VMware Tools to be installed and running in
//     the guest operating system.
//
//   * You must call VM.LoginInGuest() before calling this function in order
//     to get a Guest instance.
//
//   * The format of the file name depends on the guest or local operating system.
//     For example, a path name for a Microsoft Windows guest or host requires
//     backslash as a directory separator, whereas a Linux guest or host requires
//     a forward slash. If the path name includes backslash characters,
//     you need to precede each one with an escape character.
//
//   * Only absolute paths should be used for files in the guest; the resolution
//     of relative paths is not specified.
//
//   * If any file fails to be copied, Vix aborts the operation, does not attempt
//     to copy the remaining files, and returns an error.
//
//   * In order to copy a file to a mapped network drive in a Windows guest
//     operating system, it is necessary to call VixVM_LoginInGuest() with the
//     LOGIN_IN_GUEST_REQUIRE_INTERACTIVE_ENVIRONMENT flag set.
//     Using the interactive session option incurs an overhead in file transfer
//     speed.
//
// Since VMware Server 1.0
// Minimum Supported Guest OS: Microsoft Windows NT Series, Linux
//
func (h *Host) CopyFileToGuest(src string, guest *Guest, dest string) error {
	var jobHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var err C.VixError = C.VIX_OK

	csrc := C.CString(src)
	cdest := C.CString(dest)
	defer C.free(unsafe.Pointer(csrc))
	defer C.free(unsafe.Pointer(cdest))

	jobHandle = C.VixVM_CopyFileFromHostToGuest(
		guest.handle,         // VM handle
		csrc,                 // src name
		cdest,                // dest name
		C.int(0),             // options
		C.VIX_INVALID_HANDLE, // propertyListHandle
		nil,                  // callbackProc
		nil)                  // clientData

	defer C.Vix_ReleaseHandle(jobHandle)

	err = C.vix_job_wait(jobHandle)
	if C.VIX_OK != err {
		return &VixError{
			Operation: "host.CopyFileToGuest",
			Code:      int(err & 0xFFFF),
			Text:      C.GoString(C.Vix_GetErrorText(err, nil)),
		}
	}

	return nil
}
<commit_msg>When hardware resources are insufficient, VIX cancels operations<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package vix

/*
#include "vix.h"
#include "helper.h"
*/
import "C"

import "unsafe"

type Host struct {
	Provider Provider
	handle   C.VixHandle
}

// Destroys the state for a particular host instance
//
// Call this function to disconnect the host. After you call this function the
// Host object is no longer valid and you should not longer use it.
// Similarly, you should not use any other object instances obtained from the
// Host object while it was connected.
//
// Since VMware Server 1.0
func (h *Host) Disconnect() {
	// TODO(c4milo): Return an error to the user given that this error
	// may be thrown due to insufficient hardware resources
	if h.handle == C.VIX_E_CANCELLED {
		return
	}

	if &h.handle != nil &&
		h.handle != C.VIX_INVALID_HANDLE {

		C.VixHost_Disconnect(h.handle)
		h.handle = C.VIX_INVALID_HANDLE
	}
}

//export go_callback_char
func go_callback_char(callbackPtr unsafe.Pointer, item *C.char) {
	callback := *(*func(*C.char))(callbackPtr)
	callback(item)
}

// This function finds Vix objects. For example, when used to find all
// running virtual machines, Host.FindItems() returns a series of virtual
// machine file path names.
func (h *Host) FindItems(options SearchType) ([]string, error) {
	var jobHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var err C.VixError = C.VIX_OK
	var items []string

	callback := func(item *C.char) {
		items = append(items, C.GoString(item))
	}

	jobHandle = C.VixHost_FindItems(h.handle,
		C.VixFindItemType(options), //searchType
		C.VIX_INVALID_HANDLE,       //searchCriteria
		-1,                         //timeout
		(*C.VixEventProc)(C.find_items_callback), //callbackProc
		unsafe.Pointer(&callback))                //clientData

	defer C.Vix_ReleaseHandle(jobHandle)

	err = C.vix_job_wait(jobHandle)
	if C.VIX_OK != err {
		return nil, &VixError{
			Operation: "host.FindItems",
			Code:      int(err & 0xFFFF),
			Text:      C.GoString(C.Vix_GetErrorText(err, nil)),
		}
	}

	return items, nil
}

// This function opens a virtual machine on the host
// and returns a VM instance.
//
// Parameters:
//
//   VmxFile: The path name of the virtual machine configuration file on the
//   local host.
//
//   Password: If VM is encrypted, this is the password for VIX to be able to
//   open it.
//
// Remarks:
//
//   * This function opens a virtual machine on the host instance
//     The virtual machine is identified by vmxFile, which is a path name to the
//     configuration file (.VMX file) for that virtual machine.
//
//   * The format of the path name depends on the host operating system.
//     For example, a path name for a Windows host requires backslash as a
//     directory separator, whereas a Linux host requires a forward slash. If the
//     path name includes backslash characters, you need to precede each one with
//     an escape character. For VMware Server 2.x, the path contains a preceeding
//     data store, for example [storage1] vm/vm.vmx.
//
//   * For VMware Server hosts, a virtual machine must be registered before you
//     can open it. You can register a virtual machine by opening it with the
//     VMware Server Console, through the vmware-cmd command with the register
//     parameter, or with Host.RegisterVM().
//
//   * For vSphere, the virtual machine opened may not be the one desired if more
//     than one Datacenter contains VmxFile.
//
//   * To open an encrypted virtual machine, pass its correspondent password.
//
// Since VMware Workstation 7.0
func (h *Host) OpenVm(vmxFile, password string) (*VM, error) {
	var jobHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var propertyHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var vmHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var err C.VixError = C.VIX_OK

	defer C.Vix_ReleaseHandle(propertyHandle)
	defer C.Vix_ReleaseHandle(jobHandle)

	if password != "" {
		cpassword := C.CString(password)
		defer C.free(unsafe.Pointer(cpassword))

		err = C.alloc_vm_pwd_proplist(h.handle,
			&propertyHandle,
			cpassword)

		if C.VIX_OK != err {
			return nil, &VixError{
				Operation: "host.OpenVM",
				Code:      int(err & 0xFFFF),
				Text:      C.GoString(C.Vix_GetErrorText(err, nil)),
			}
		}
	}

	cVmxFile := C.CString(vmxFile)
	defer C.free(unsafe.Pointer(cVmxFile))

	jobHandle = C.VixHost_OpenVM(h.handle,
		cVmxFile,
		C.VIX_VMOPEN_NORMAL,
		propertyHandle,
		nil, // callbackProc
		nil) // clientData

	err = C.get_vix_handle(jobHandle,
		C.VIX_PROPERTY_JOB_RESULT_HANDLE,
		&vmHandle,
		C.VIX_PROPERTY_NONE)

	if C.VIX_OK != err {
		return nil, &VixError{
			Operation: "host.OpenVM.get_vix_handle",
			Code:      int(err & 0xFFFF),
			Text:      C.GoString(C.Vix_GetErrorText(err, nil)),
		}
	}

	return NewVirtualMachine(vmHandle, vmxFile)
}

// This function adds a virtual machine to the host's inventory.
//
// Parameters:
//
//   vmxFile: The path name of the .vmx file on the host.
//
// Remarks:
//
//   * This function registers the virtual machine identified by vmxFile, which
//     is a storage path to the configuration file (.vmx) for that virtual machine.
//     You can register a virtual machine regardless of its power state.
//
//   * The format of the path name depends on the host operating system.
//     If the path name includes backslash characters, you need to precede each
//     one with an escape character. Path to storage [standard] or [storage1] may
//     vary.
//
//   * For VMware Server 1.x, supply the full path name instead of storage path,
//     and specify provider VMWARE_SERVER to connect.
//
//   * This function has no effect on Workstation or Player, which lack a virtual
//     machine inventory.
//
//   * It is not a Vix error to register an already-registered virtual machine,
//     although the VMware Server UI shows an error icon in the Task pane.
//     Trying to register a non-existent virtual machine results in error 2000,
//     VIX_E_NOT_FOUND.
//
// Since VMware Server 1.0
func (h *Host) RegisterVm(vmxFile string) error {
	var jobHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var err C.VixError = C.VIX_OK

	cVmxFile := C.CString(vmxFile)
	defer C.free(unsafe.Pointer(cVmxFile))

	jobHandle = C.VixHost_RegisterVM(h.handle,
		cVmxFile,
		nil, // callbackProc
		nil) // clientData

	defer C.Vix_ReleaseHandle(jobHandle)

	err = C.vix_job_wait(jobHandle)
	if C.VIX_OK != err {
		return &VixError{
			Operation: "host.RegisterVM",
			Code:      int(err & 0xFFFF),
			Text:      C.GoString(C.Vix_GetErrorText(err, nil)),
		}
	}

	return nil
}

// This function removes a virtual machine from the host's inventory.
//
// Parameters:
//
//   VmxFile: The path name of the .vmx file on the host.
//
// Remarks:
//
//   * This function unregisters the virtual machine identified by vmxFile,
//     which is a storage path to the configuration file (.vmx) for that virtual
//     machine. A virtual machine must be powered off to unregister it.
//   * The format of the storage path depends on the host operating system.
//     If the storage path includes backslash characters, you need to precede each
//     one with an escape character. Path to storage [standard] or [storage1] may
//     vary.
//   * For VMware Server 1.x, supply the full path name instead of storage path,
//     and specify VMWARE_SERVER provider to connect.
//   * This function has no effect on Workstation or Player, which lack a virtual
//     machine inventory.
//   * It is not a Vix error to unregister an already-unregistered virtual machine,
//     nor is it a Vix error to unregister a non-existent virtual machine.
//
// Since VMware Server 1.0
func (h *Host) UnregisterVm(vmxFile string) error {
	var jobHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var err C.VixError = C.VIX_OK

	cVmxFile := C.CString(vmxFile)
	defer C.free(unsafe.Pointer(cVmxFile))

	jobHandle = C.VixHost_UnregisterVM(h.handle,
		cVmxFile,
		nil, // callbackProc
		nil) // clientData

	defer C.Vix_ReleaseHandle(jobHandle)

	err = C.vix_job_wait(jobHandle)
	if C.VIX_OK != err {
		return &VixError{
			Operation: "host.UnregisterVM",
			Code:      int(err & 0xFFFF),
			Text:      C.GoString(C.Vix_GetErrorText(err, nil)),
		}
	}

	return nil
}

// Copies a file or directory from the local system (where the Vix client is
// running) to the guest operating system.
//
// Parameters:
//
//   src: The path name of a file on a file system available to the Vix client.
//   guest: Guest instance where the file is going to be copied to
//   dest: The path name of a file on a file system available to the guest.
//
// Remarks:
//
//   * The virtual machine must be running while the file is copied from the Vix
//     client machine to the guest operating system.
//
//   * Existing files of the same name are overwritten, and folder contents are
//     merged.
//
//   * The copy operation requires VMware Tools to be installed and running in
//     the guest operating system.
//
//   * You must call VM.LoginInGuest() before calling this function in order
//     to get a Guest instance.
//
//   * The format of the file name depends on the guest or local operating system.
//     For example, a path name for a Microsoft Windows guest or host requires
//     backslash as a directory separator, whereas a Linux guest or host requires
//     a forward slash. If the path name includes backslash characters,
//     you need to precede each one with an escape character.
//
//   * Only absolute paths should be used for files in the guest; the resolution
//     of relative paths is not specified.
//
//   * If any file fails to be copied, Vix aborts the operation, does not attempt
//     to copy the remaining files, and returns an error.
//
//   * In order to copy a file to a mapped network drive in a Windows guest
//     operating system, it is necessary to call VixVM_LoginInGuest() with the
//     LOGIN_IN_GUEST_REQUIRE_INTERACTIVE_ENVIRONMENT flag set.
//     Using the interactive session option incurs an overhead in file transfer
//     speed.
//
// Since VMware Server 1.0
// Minimum Supported Guest OS: Microsoft Windows NT Series, Linux
//
func (h *Host) CopyFileToGuest(src string, guest *Guest, dest string) error {
	var jobHandle C.VixHandle = C.VIX_INVALID_HANDLE
	var err C.VixError = C.VIX_OK

	csrc := C.CString(src)
	cdest := C.CString(dest)
	defer C.free(unsafe.Pointer(csrc))
	defer C.free(unsafe.Pointer(cdest))

	jobHandle = C.VixVM_CopyFileFromHostToGuest(
		guest.handle,         // VM handle
		csrc,                 // src name
		cdest,                // dest name
		C.int(0),             // options
		C.VIX_INVALID_HANDLE, // propertyListHandle
		nil,                  // callbackProc
		nil)                  // clientData

	defer C.Vix_ReleaseHandle(jobHandle)

	err = C.vix_job_wait(jobHandle)
	if C.VIX_OK != err {
		return &VixError{
			Operation: "host.CopyFileToGuest",
			Code:      int(err & 0xFFFF),
			Text:      C.GoString(C.Vix_GetErrorText(err, nil)),
		}
	}

	return nil
}
<|endoftext|>