<commit_before>// Copyright 2015 Mitsuhiro Koga Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package gitbucket

import (
	"net/http"
	"time"
)

type WebHookEvent string

const (
	IssuesEvent                   = WebHookEvent("issues")
	IssueCommentEvent             = WebHookEvent("issue_comment")
	PullRequestEvent              = WebHookEvent("pull_request")
	PullRequestReviewCommentEvent = WebHookEvent("pull_request_review_comment")
	PushEvent                     = WebHookEvent("push")
)

func GetWebHookEvent(req *http.Request) WebHookEvent {
	return WebHookEvent(req.Header.Get("X-Github-Event"))
}

type commit struct {
	ID        *string       `json"id"`
	Message   *string       `json:"message"`
	Timestamp *time.Time    `json:"timestamp"`
	Added     []string      `json:"added"`
	Removed   []string      `json:"removed"`
	Modified  []string      `json:"modified"`
	Author    *CommitAuthor `json:"author"`
	Committer *CommitAuthor `json:"committer"`
	URL       *string       `json:"url"`
}

type issue struct {
	Number      *int       `json:"number"`
	Title       *string    `json:"title"`
	User        *User      `json:"user"`
	State       *string    `json:"state"`
	CreatedAt   *time.Time `json:"created_at"`
	UpdatedAt   *time.Time `json:"updated_at"`
	Body        *string    `json:"body"`
	CommentsURL *string    `json:"comments_url"`
	HTMLURL     *string    `json:"html_url"`
}

type path struct {
	Href *string `json:"href"`
}

type links struct {
	Self        *path `json:"self"`
	HTML        *path `json:"html"`
	PullRequest *path `json:"pull_request"`
}

type comment struct {
	ID             *int       `json:"id"`
	Path           *string    `json:"path"`
	CommitID       *string    `json:"commit_id"`
	User           *User      `json:"user"`
	Body           *string    `json:"body"`
	CreatedAt      *time.Time `json:"created_at"`
	UpdatedAt      *time.Time `json:"updated_at"`
	URL            *string    `json:"url"`
	HTMLURL        *string    `json:"html_url"`
	PullRequestURL *string    `json:"pull_request_url"`
	Links          *links     `json:"_links"`
}

// Action is one of "opened", "reopen", "closed"
type WebHookIssuesPayload struct {
	Action     *string     `json:"action"`
	Sender     *User       `json:"sender"`
	Number     *int        `json:"number"`
	Issue      *issue      `json:"issue"`
	Repository *Repository `json:"repository"`
}

// Action is only "created"
type WebHookIssueCommentPayload struct {
	Action     *string       `json:"action"`
	Sender     *User         `json:"sender"`
	Issue      *issue        `json:"issue"`
	Comment    *IssueComment `json:"comment"`
	Repository *Repository   `json:"repository"`
}

// Action is one of "created", "synchronize"
type WebHookPullRequestPayload struct {
	Action      *string      `json:"action"`
	Sender      *User        `json:"sender"`
	Number      *int         `json:"number"`
	PullRequest *PullRequest `json:"pull_request"`
	Repository  *Repository  `json:"repository"`
}

// Action is only "created"
type WebHookPullRequestReviewCommentPayload struct {
	Action      *string      `json:"action"`
	Comment     *comment     `json:"comment"`
	PullRequest *PullRequest `json:"pull_request"`
	Repository  *Repository  `json:"repository"`
	Sender      *User        `json:"sender"`
}

type WebHookPushPayload struct {
	Commits    []commit    `json:"commits"`
	Pusher     *User       `json:"pusher"`
	Ref        *string     `json:"ref"`
	Before     *string     `json:"before"`
	After      *string     `json:"after"`
	Repository *Repository `json:"repository"`
	Compare    *string     `json:"compare"`
	HeadCommit *commit     `json:"head_commit"`
}
<commit_msg>Update a webhook push event<commit_after>// Copyright 2015 Mitsuhiro Koga Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package gitbucket

import (
	"net/http"
	"time"
)

type WebHookEvent string

const (
	IssuesEvent                   = WebHookEvent("issues")
	IssueCommentEvent             = WebHookEvent("issue_comment")
	PullRequestEvent              = WebHookEvent("pull_request")
	PullRequestReviewCommentEvent = WebHookEvent("pull_request_review_comment")
	PushEvent                     = WebHookEvent("push")
)

func GetWebHookEvent(req *http.Request) WebHookEvent {
	return WebHookEvent(req.Header.Get("X-Github-Event"))
}

type commit struct {
	ID        *string       `json"id"`
	Message   *string       `json:"message"`
	Timestamp *time.Time    `json:"timestamp"`
	Added     []string      `json:"added"`
	Removed   []string      `json:"removed"`
	Modified  []string      `json:"modified"`
	Author    *CommitAuthor `json:"author"`
	Committer *CommitAuthor `json:"committer"`
	URL       *string       `json:"url"`
}

type issue struct {
	Number      *int       `json:"number"`
	Title       *string    `json:"title"`
	User        *User      `json:"user"`
	State       *string    `json:"state"`
	CreatedAt   *time.Time `json:"created_at"`
	UpdatedAt   *time.Time `json:"updated_at"`
	Body        *string    `json:"body"`
	CommentsURL *string    `json:"comments_url"`
	HTMLURL     *string    `json:"html_url"`
}

type path struct {
	Href *string `json:"href"`
}

type links struct {
	Self        *path `json:"self"`
	HTML        *path `json:"html"`
	PullRequest *path `json:"pull_request"`
}

type pusher struct {
	Name  *string `json:"name"`
	Email *string `json:"email"`
}

type comment struct {
	ID             *int       `json:"id"`
	Path           *string    `json:"path"`
	CommitID       *string    `json:"commit_id"`
	User           *User      `json:"user"`
	Body           *string    `json:"body"`
	CreatedAt      *time.Time `json:"created_at"`
	UpdatedAt      *time.Time `json:"updated_at"`
	URL            *string    `json:"url"`
	HTMLURL        *string    `json:"html_url"`
	PullRequestURL *string    `json:"pull_request_url"`
	Links          *links     `json:"_links"`
}

// Action is one of "opened", "reopen", "closed"
type WebHookIssuesPayload struct {
	Action     *string     `json:"action"`
	Sender     *User       `json:"sender"`
	Number     *int        `json:"number"`
	Issue      *issue      `json:"issue"`
	Repository *Repository `json:"repository"`
}

// Action is only "created"
type WebHookIssueCommentPayload struct {
	Action     *string       `json:"action"`
	Sender     *User         `json:"sender"`
	Issue      *issue        `json:"issue"`
	Comment    *IssueComment `json:"comment"`
	Repository *Repository   `json:"repository"`
}

// Action is one of "created", "synchronize"
type WebHookPullRequestPayload struct {
	Action      *string      `json:"action"`
	Sender      *User        `json:"sender"`
	Number      *int         `json:"number"`
	PullRequest *PullRequest `json:"pull_request"`
	Repository  *Repository  `json:"repository"`
}

// Action is only "created"
type WebHookPullRequestReviewCommentPayload struct {
	Action      *string      `json:"action"`
	Comment     *comment     `json:"comment"`
	PullRequest *PullRequest `json:"pull_request"`
	Repository  *Repository  `json:"repository"`
	Sender      *User        `json:"sender"`
}

type WebHookPushPayload struct {
	Commits    []commit    `json:"commits"`
	Pusher     *pusher     `json:"pusher"`
	Sender     *User       `json:"sender"`
	Ref        *string     `json:"ref"`
	Before     *string     `json:"before"`
	After      *string     `json:"after"`
	Repository *Repository `json:"repository"`
	Compare    *string     `json:"compare"`
	HeadCommit *commit     `json:"head_commit"`
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/dynport/dgtk/github"
	"github.com/dynport/gocli"
	"github.com/pkg/errors"
)

type Status struct {
	WithURLs bool   `cli:"opt --with-urls"`
	Open     bool   `cli:"opt --open"`
	Branch   string `cli:"opt --branch"`
	Wait     bool   `cli:"opt --wait"`
}

func (r *Status) Run() error {
	var branches []string
	if r.Branch != "" {
		branches = []string{r.Branch}
	} else {
		branches = []string{"master"}
		if cb, err := currentBranch(); err == nil && cb != "master" {
			branches = append([]string{cb}, "master")
		}
	}
	repo, err := githubRepo()
	if err != nil {
		return err
	}
	cl, err := client()
	if err != nil {
		return err
	}

	l := log.New(os.Stderr, "", 0)

	if r.Wait {
		branch := r.Branch
		if branch == "" {
			branch, err = currentBranch()
			if err != nil {
				return err
			}
		}
		var printedURL bool
		for {
			s, err := loadStatus(cl, repo, branch)
			if err != nil {
				l.Printf("error fetching status: %s", err)
			} else {
				if s.State != statusPending {
					fmt.Println(s.State)
					if s.State == statusSuccess {
						return nil
					}
					return fmt.Errorf("not successful (%s)", s.State)
				}
				if !printedURL && len(s.Statuses) > 0 {
					l.Printf("url=%s", s.Statuses[0].TargetURL)
					printedURL = true
				}
			}
			time.Sleep(10 * time.Second)
		}
		return nil
	}

	type status struct {
		Time   time.Time
		Branch string
		URL    string
		URLs   []string
		Status string
		SHA    string
	}

	t := gocli.NewTable()
	all := []*status{}
	failures := 0
	agoFunc := func(t time.Time) string { return strings.Split(time.Since(t).String(), ".")[0] }
	for _, b := range branches {
		st := &status{Branch: b}
		all = append(all, st)
		if s, err := loadStatus(cl, repo, b); err != nil {
			if isNotFound(err) {
				st.Status = "not_found"
			} else {
				return err
			}
		} else {
			st.Status = s.State
			st.SHA = s.SHA
			sm := map[string]int{}
			for _, s := range s.Statuses {
				sm[s.State]++
			}
			if sm[statusFailure] > 0 {
				st.Status = statusFailure
				failures++
			} else if sm[statusPending] > 0 {
				st.Status = statusPending
			} else {
				st.Status = statusSuccess
			}
			t.Add(string(b), colorizeStatus(st.Status))
			if len(s.Statuses) > 0 {
				for _, ss := range s.Statuses {
					if ss.TargetURL != "" {
						st.URLs = append(st.URLs, ss.TargetURL)
					}
					args := []interface{}{"", colorizeStatus(ss.State), truncate(s.SHA, 8, false), ss.Context, agoFunc(ss.CreatedAt)}
					if r.WithURLs {
						args = append(args, ss.TargetURL)
					}
					t.Add(args...)
				}
			}
		}
	}

	if r.Open {
		if len(all) == 0 {
			return fmt.Errorf("no status found")
		}
		s := all[0]
		for _, s := range all {
			l.Printf("url: %s", s.URL)
		}
		url := ""
		if s.URL != "" {
			url = s.URL
		} else if len(s.URLs) == 1 {
			url = s.URLs[0]
		} else {
			return fmt.Errorf("status has no url (yet?). url=%q urls=%#v", s.URL, s.URLs)
		}
		return openUrl(url)
	}
	fmt.Println(t)
	if failures > 0 {
		return errors.Errorf("%d failures", failures)
	}
	return nil
}

func isNotFound(err error) bool {
	return err != nil && strings.Contains(err.Error(), "404 Not Found")
}

const (
	statusSuccess  = "success"
	statusPending  = "pending"
	statusNotFound = "not_found"
	statusFailure  = "failure"
)

func colorizeStatus(in string) string {
	color := gocli.Green
	switch in {
	case statusSuccess:
		color = gocli.Green
	case statusPending, statusNotFound:
		color = gocli.Yellow
	default:
		color = gocli.Red
	}
	return color(in)
}

func loadStatus(cl *github.Client, repo, ref string) (res *statusResponse, err error) {
	u := "https://api.github.com/repos/" + repo + "/commits/" + ref + "/status"
	rsp, err := cl.Get(u)
	if err != nil {
		return nil, err
	}
	defer rsp.Body.Close()
	if rsp.Status[0] != '2' {
		b, _ := ioutil.ReadAll(rsp.Body)
		return nil, fmt.Errorf("got status %s but expected 2x. body=%s", rsp.Status, string(b))
	}
	err = json.NewDecoder(rsp.Body).Decode(&res)
	if err != nil {
		return nil, err
	}
	return res, nil
}

func currentBranch() (string, error) {
	b, err := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD").CombinedOutput()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(b)), nil
}

type statusResponse struct {
	State    string `json:"state"`
	Statuses []*struct {
		State     string    `json:"state,omitempty"`
		URL       string    `json:"url,omitempty"`
		Context   string    `json:"context"`
		TargetURL string    `json:"target_url,omitempty"`
		CreatedAt time.Time `json:"created_at,omitempty"`
		UpdatedAt time.Time `json:"updated_at,omitempty"`
	} `json:"statuses"`
	SHA string `json:"sha"`
}

// to be used to colorize
func dataOn(f *os.File) bool {
	stat, err := f.Stat()
	if err != nil {
		return false
	}
	return (stat.Mode() & os.ModeCharDevice) == 0
}
<commit_msg>gh: always display build urls<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/dynport/dgtk/github"
	"github.com/dynport/gocli"
	"github.com/pkg/errors"
)

type Status struct {
	Open   bool   `cli:"opt --open"`
	Branch string `cli:"opt --branch"`
	Wait   bool   `cli:"opt --wait"`
}

func (r *Status) Run() error {
	var branches []string
	if r.Branch != "" {
		branches = []string{r.Branch}
	} else {
		branches = []string{"master"}
		if cb, err := currentBranch(); err == nil && cb != "master" {
			branches = append([]string{cb}, "master")
		}
	}
	repo, err := githubRepo()
	if err != nil {
		return err
	}
	cl, err := client()
	if err != nil {
		return err
	}

	l := log.New(os.Stderr, "", 0)

	if r.Wait {
		branch := r.Branch
		if branch == "" {
			branch, err = currentBranch()
			if err != nil {
				return err
			}
		}
		var printedURL bool
		for {
			s, err := loadStatus(cl, repo, branch)
			if err != nil {
				l.Printf("error fetching status: %s", err)
			} else {
				if s.State != statusPending {
					fmt.Println(s.State)
					if s.State == statusSuccess {
						return nil
					}
					return fmt.Errorf("not successful (%s)", s.State)
				}
				if !printedURL && len(s.Statuses) > 0 {
					l.Printf("url=%s", s.Statuses[0].TargetURL)
					printedURL = true
				}
			}
			time.Sleep(10 * time.Second)
		}
		return nil
	}

	type status struct {
		Time   time.Time
		Branch string
		URL    string
		URLs   []string
		Status string
		SHA    string
	}

	t := gocli.NewTable()
	all := []*status{}
	failures := 0
	agoFunc := func(t time.Time) string { return strings.Split(time.Since(t).String(), ".")[0] }
	for _, b := range branches {
		st := &status{Branch: b}
		all = append(all, st)
		if s, err := loadStatus(cl, repo, b); err != nil {
			if isNotFound(err) {
				st.Status = "not_found"
			} else {
				return err
			}
		} else {
			st.Status = s.State
			st.SHA = s.SHA
			sm := map[string]int{}
			for _, s := range s.Statuses {
				sm[s.State]++
			}
			if sm[statusFailure] > 0 {
				st.Status = statusFailure
				failures++
			} else if sm[statusPending] > 0 {
				st.Status = statusPending
			} else {
				st.Status = statusSuccess
			}
			t.Add(string(b), colorizeStatus(st.Status))
			if len(s.Statuses) > 0 {
				for _, ss := range s.Statuses {
					if ss.TargetURL != "" {
						st.URLs = append(st.URLs, ss.TargetURL)
					}
					t.Add("", colorizeStatus(ss.State), truncate(s.SHA, 8, false), ss.Context, agoFunc(ss.CreatedAt), ss.TargetURL)
				}
			}
		}
	}

	if r.Open {
		if len(all) == 0 {
			return fmt.Errorf("no status found")
		}
		s := all[0]
		for _, s := range all {
			l.Printf("url: %s", s.URL)
		}
		url := ""
		if s.URL != "" {
			url = s.URL
		} else if len(s.URLs) == 1 {
			url = s.URLs[0]
		} else {
			return fmt.Errorf("status has no url (yet?). url=%q urls=%#v", s.URL, s.URLs)
		}
		return openUrl(url)
	}
	fmt.Println(t)
	if failures > 0 {
		return errors.Errorf("%d failures", failures)
	}
	return nil
}

func isNotFound(err error) bool {
	return err != nil && strings.Contains(err.Error(), "404 Not Found")
}

const (
	statusSuccess  = "success"
	statusPending  = "pending"
	statusNotFound = "not_found"
	statusFailure  = "failure"
)

func colorizeStatus(in string) string {
	color := gocli.Green
	switch in {
	case statusSuccess:
		color = gocli.Green
	case statusPending, statusNotFound:
		color = gocli.Yellow
	default:
		color = gocli.Red
	}
	return color(in)
}

func loadStatus(cl *github.Client, repo, ref string) (res *statusResponse, err error) {
	u := "https://api.github.com/repos/" + repo + "/commits/" + ref + "/status"
	rsp, err := cl.Get(u)
	if err != nil {
		return nil, err
	}
	defer rsp.Body.Close()
	if rsp.Status[0] != '2' {
		b, _ := ioutil.ReadAll(rsp.Body)
		return nil, fmt.Errorf("got status %s but expected 2x. body=%s", rsp.Status, string(b))
	}
	err = json.NewDecoder(rsp.Body).Decode(&res)
	if err != nil {
		return nil, err
	}
	return res, nil
}

func currentBranch() (string, error) {
	b, err := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD").CombinedOutput()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(b)), nil
}

type statusResponse struct {
	State    string `json:"state"`
	Statuses []*struct {
		State     string    `json:"state,omitempty"`
		URL       string    `json:"url,omitempty"`
		Context   string    `json:"context"`
		TargetURL string    `json:"target_url,omitempty"`
		CreatedAt time.Time `json:"created_at,omitempty"`
		UpdatedAt time.Time `json:"updated_at,omitempty"`
	} `json:"statuses"`
	SHA string `json:"sha"`
}

// to be used to colorize
func dataOn(f *os.File) bool {
	stat, err := f.Stat()
	if err != nil {
		return false
	}
	return (stat.Mode() & os.ModeCharDevice) == 0
}
<|endoftext|>


<commit_before>package ledger

import (
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"
)

type Entry struct {
	Date        string // "Y-m-d"
	Description string
	Change      int // in cents
}

func FormatLedger(currency string, locale string, entries []Entry) (output string, e error) {
	if !isValidCurrency(currency) {
		return "", errors.New("invalid currency")
	}
	if !isValidLocale(locale) {
		return "", errors.New("invalid locale")
	}
	if !isValidDate(entries) {
		return "", errors.New("invalid date")
	}

	entriesCopy := append([]Entry{}, entries...)

	m1 := map[bool]int{true: 0, false: 1}
	m2 := map[bool]int{true: -1, false: 1}
	es := entriesCopy
	for len(es) > 1 {
		first, rest := es[0], es[1:]
		success := false
		for !success {
			success = true
			for i, e := range rest {
				if (m1[e.Date == first.Date]*m2[e.Date < first.Date]*4 +
					m1[e.Description == first.Description]*m2[e.Description < first.Description]*2 +
					m1[e.Change == first.Change]*m2[e.Change < first.Change]*1) < 0 {
					es[0], es[i+1] = es[i+1], es[0]
					success = false
				}
			}
		}
		es = es[1:]
	}

	output += header(locale)
	for _, entry := range entriesCopy {
		output += formatEntry(locale, currency, entry)
	}
	return output, nil
}

func formatEntry(locale string, currency string, entry Entry) (formatted string) {
	date := formatDate(locale, entry.Date)
	description := formatDescription(entry.Description)
	change := formatChange(locale, currency, entry.Change)
	if isNegative(entry.Change) {
		return fmt.Sprintf("%s | %s | %13s\n", date, description, change)
	} else {
		return fmt.Sprintf("%s | %s | %12s\n", date, description, change)
	}
}

func getCurrencySymbol(currency string) (symbol string) {
	if currency == "EUR" {
		return "€"
	} else if currency == "USD" {
		return "$"
	}
	panic("invalid currency")
}

func isNegative(cents int) bool {
	return cents < 0
}

func formatChange(locale string, currency string, cents int) (change string) {
	isNegative := isNegative(cents)
	absoluteValueCents := int(math.Abs(float64(cents)))
	if locale == "nl-NL" {
		change += getCurrencySymbol(currency)
		change += " "
		centsStr := strconv.Itoa(absoluteValueCents)
		switch len(centsStr) {
		case 1:
			centsStr = "00" + centsStr
		case 2:
			centsStr = "0" + centsStr
		}
		rest := centsStr[:len(centsStr)-2]
		var parts []string
		for len(rest) > 3 {
			parts = append(parts, rest[len(rest)-3:])
			rest = rest[:len(rest)-3]
		}
		if len(rest) > 0 {
			parts = append(parts, rest)
		}
		for i := len(parts) - 1; i >= 0; i-- {
			change += parts[i] + "."
		}
		change = change[:len(change)-1]
		change += ","
		change += centsStr[len(centsStr)-2:]
		if isNegative {
			change = fmt.Sprintf("%s-", change)
		}
	} else if locale == "en-US" {
		change += getCurrencySymbol(currency)
		centsStr := strconv.Itoa(absoluteValueCents)
		switch len(centsStr) {
		case 1:
			centsStr = "00" + centsStr
		case 2:
			centsStr = "0" + centsStr
		}
		rest := centsStr[:len(centsStr)-2]
		var parts []string
		for len(rest) > 3 {
			parts = append(parts, rest[len(rest)-3:])
			rest = rest[:len(rest)-3]
		}
		if len(rest) > 0 {
			parts = append(parts, rest)
		}
		for i := len(parts) - 1; i >= 0; i-- {
			change += parts[i] + ","
		}
		change = change[:len(change)-1]
		change += "."
		change += centsStr[len(centsStr)-2:]
		if isNegative {
			change = fmt.Sprintf("(%s)", change)
		}
	}
	return change
}

func isValidCurrency(currency string) bool {
	return currency == "USD" || currency == "EUR"
}

func isValidLocale(locale string) bool {
	return locale == "en-US" || locale == "nl-NL"
}

func isValidDate(entries []Entry) bool {
	for _, entry := range entries {
		if len(entry.Date) != 10 {
			return false
		}
		_, d2, _, d4, _ := entry.Date[0:4], entry.Date[4], entry.Date[5:7], entry.Date[7], entry.Date[8:10]
		if d2 != '-' || d4 != '-' {
			return false
		}
	}
	return true
}

func formatDescription(description string) string {
	if len(description) > 25 {
		return description[:22] + "..."
	}
	return description + strings.Repeat(" ", 25-len(description))
}

func formatDate(locale string, date string) string {
	year, month, day := date[0:4], date[5:7], date[8:10]

	if locale == "nl-NL" {
		return fmt.Sprintf("%v-%v-%v", day, month, year)
	} else if locale == "en-US" {
		return fmt.Sprintf("%v/%v/%v", month, day, year)
	}
	panic("invalid locale")
}

func header(locale string) (output string) {
	if locale == "nl-NL" {
		return fmt.Sprintf("%-10s | %-25s | %s\n", "Datum", "Omschrijving", "Verandering")
	} else if locale == "en-US" {
		return fmt.Sprintf("%-10s | %-25s | %s\n", "Date", "Description", "Change")
	}
	panic("invalid locale")
}
<commit_msg>Left pad with 0s<commit_after>package ledger

import (
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"
)

type Entry struct {
	Date        string // "Y-m-d"
	Description string
	Change      int // in cents
}

func FormatLedger(currency string, locale string, entries []Entry) (output string, e error) {
	if !isValidCurrency(currency) {
		return "", errors.New("invalid currency")
	}
	if !isValidLocale(locale) {
		return "", errors.New("invalid locale")
	}
	if !isValidDate(entries) {
		return "", errors.New("invalid date")
	}

	entriesCopy := append([]Entry{}, entries...)

	m1 := map[bool]int{true: 0, false: 1}
	m2 := map[bool]int{true: -1, false: 1}
	es := entriesCopy
	for len(es) > 1 {
		first, rest := es[0], es[1:]
		success := false
		for !success {
			success = true
			for i, e := range rest {
				if (m1[e.Date == first.Date]*m2[e.Date < first.Date]*4 +
					m1[e.Description == first.Description]*m2[e.Description < first.Description]*2 +
					m1[e.Change == first.Change]*m2[e.Change < first.Change]*1) < 0 {
					es[0], es[i+1] = es[i+1], es[0]
					success = false
				}
			}
		}
		es = es[1:]
	}

	output += header(locale)
	for _, entry := range entriesCopy {
		output += formatEntry(locale, currency, entry)
	}
	return output, nil
}

func formatEntry(locale string, currency string, entry Entry) (formatted string) {
	date := formatDate(locale, entry.Date)
	description := formatDescription(entry.Description)
	change := formatChange(locale, currency, entry.Change)
	if isNegative(entry.Change) {
		return fmt.Sprintf("%s | %s | %13s\n", date, description, change)
	} else {
		return fmt.Sprintf("%s | %s | %12s\n", date, description, change)
	}
}

func getCurrencySymbol(currency string) (symbol string) {
	if currency == "EUR" {
		return "€"
	} else if currency == "USD" {
		return "$"
	}
	panic("invalid currency")
}

func isNegative(cents int) bool {
	return cents < 0
}

func formatChange(locale string, currency string, cents int) (change string) {
	isNegative := isNegative(cents)
	absoluteValueCents := int(math.Abs(float64(cents)))
	if locale == "nl-NL" {
		change += getCurrencySymbol(currency)
		change += " "
		centsStr := fmt.Sprintf("%03s", strconv.Itoa(absoluteValueCents))
		rest := centsStr[:len(centsStr)-2]
		var parts []string
		for len(rest) > 3 {
			parts = append(parts, rest[len(rest)-3:])
			rest = rest[:len(rest)-3]
		}
		if len(rest) > 0 {
			parts = append(parts, rest)
		}
		for i := len(parts) - 1; i >= 0; i-- {
			change += parts[i] + "."
		}
		change = change[:len(change)-1]
		change += ","
		change += centsStr[len(centsStr)-2:]
		if isNegative {
			change = fmt.Sprintf("%s-", change)
		}
	} else if locale == "en-US" {
		change += getCurrencySymbol(currency)
		centsStr := fmt.Sprintf("%03s", strconv.Itoa(absoluteValueCents))
		rest := centsStr[:len(centsStr)-2]
		var parts []string
		for len(rest) > 3 {
			parts = append(parts, rest[len(rest)-3:])
			rest = rest[:len(rest)-3]
		}
		if len(rest) > 0 {
			parts = append(parts, rest)
		}
		for i := len(parts) - 1; i >= 0; i-- {
			change += parts[i] + ","
		}
		change = change[:len(change)-1]
		change += "."
		change += centsStr[len(centsStr)-2:]
		if isNegative {
			change = fmt.Sprintf("(%s)", change)
		}
	}
	return change
}

func isValidCurrency(currency string) bool {
	return currency == "USD" || currency == "EUR"
}

func isValidLocale(locale string) bool {
	return locale == "en-US" || locale == "nl-NL"
}

func isValidDate(entries []Entry) bool {
	for _, entry := range entries {
		if len(entry.Date) != 10 {
			return false
		}
		_, d2, _, d4, _ := entry.Date[0:4], entry.Date[4], entry.Date[5:7], entry.Date[7], entry.Date[8:10]
		if d2 != '-' || d4 != '-' {
			return false
		}
	}
	return true
}

func formatDescription(description string) string {
	if len(description) > 25 {
		return description[:22] + "..."
	}
	return description + strings.Repeat(" ", 25-len(description))
}

func formatDate(locale string, date string) string {
	year, month, day := date[0:4], date[5:7], date[8:10]

	if locale == "nl-NL" {
		return fmt.Sprintf("%v-%v-%v", day, month, year)
	} else if locale == "en-US" {
		return fmt.Sprintf("%v/%v/%v", month, day, year)
	}
	panic("invalid locale")
}

func header(locale string) (output string) {
	if locale == "nl-NL" {
		return fmt.Sprintf("%-10s | %-25s | %s\n", "Datum", "Omschrijving", "Verandering")
	} else if locale == "en-US" {
		return fmt.Sprintf("%-10s | %-25s | %s\n", "Date", "Description", "Change")
	}
	panic("invalid locale")
}
<|endoftext|>


<commit_before>/*
Package arrow provides C-style date formating and parsing, along with other date goodies.

See the github project page at http://github.com/bmuller/arrow for more info.
*/
package arrow

import (
	"strconv"
	"strings"
	"time"
)

type Arrow struct {
	time.Time
}

// Like time's constants, but with Day and Week
const (
	Nanosecond  time.Duration = 1
	Microsecond               = 1000 * Nanosecond
	Millisecond               = 1000 * Microsecond
	Second                    = 1000 * Millisecond
	Minute                    = 60 * Second
	Hour                      = 60 * Minute
	Day                       = 24 * Hour
	Week                      = 7 * Day
)

func New(t time.Time) Arrow {
	return Arrow{t}
}

func UTC() Arrow {
	return New(time.Now().UTC())
}

func Now() Arrow {
	return New(time.Now())
}

func Yesterday() Arrow {
	return Now().Yesterday()
}

func Tomorrow() Arrow {
	return Now().Tomorrow()
}

func NextMinute() Arrow {
	return Now().AddMinutes(1).AtBeginningOfMinute()
}

func NextHour() Arrow {
	return Now().AddHours(1).AtBeginningOfHour()
}

func NextDay() Arrow {
	return Now().AddDays(1).AtBeginningOfDay()
}

func SleepUntil(t Arrow) {
	time.Sleep(t.Sub(Now()))
}

// Get the current time in the given timezone.
// The timezone parameter should correspond to a file in the IANA Time Zone database,
// such as "America/New_York".  "UTC" and "Local" are also acceptable.  If the timezone
// given isn't valid, then no change to the timezone is made.
func InTimezone(timezone string) Arrow {
	return Now().InTimezone(timezone)
}

func (a Arrow) Yesterday() Arrow {
	return a.AddDays(-1)
}

func (a Arrow) Tomorrow() Arrow {
	return a.AddDays(1)
}

func (a Arrow) UTC() Arrow {
	return New(a.Time.UTC())
}

func (a Arrow) Sub(b Arrow) time.Duration {
	return a.Time.Sub(b.Time)
}

// Add any duration parseable by time.ParseDuration
func (a Arrow) AddDuration(duration string) Arrow {
	if pduration, err := time.ParseDuration(duration); err == nil {
		return New(a.Add(pduration))
	}
	return a
}

// The timezone parameter should correspond to a file in the IANA Time Zone database,
// such as "America/New_York".  "UTC" and "Local" are also acceptable.  If the timezone
// given isn't valid, then no change to the timezone is made.
func (a Arrow) InTimezone(timezone string) Arrow {
	if location, err := time.LoadLocation(timezone); err == nil {
		return New(a.In(location))
	}
	return a
}

func (a Arrow) AddDays(days int) Arrow {
	return New(a.AddDate(0, 0, days))
}

func (a Arrow) AddHours(hours int) Arrow {
	year, month, day := a.Time.Date()
	hour, min, sec := a.Time.Clock()
	d := time.Date(year, month, day, hour+hours, min, sec, a.Nanosecond(), a.Location())
	return New(d)
}

func (a Arrow) AddMinutes(minutes int) Arrow {
	year, month, day := a.Time.Date()
	hour, min, sec := a.Time.Clock()
	d := time.Date(year, month, day, hour, min+minutes, sec, a.Nanosecond(), a.Location())
	return New(d)
}

func (a Arrow) AddSeconds(seconds int) Arrow {
	year, month, day := a.Time.Date()
	hour, min, sec := a.Time.Clock()
	d := time.Date(year, month, day, hour, min, sec+seconds, a.Nanosecond(), a.Location())
	return New(d)
}

func (a Arrow) AtBeginningOfMinute() Arrow {
	return New(a.Truncate(Minute))
}

func (a Arrow) AtBeginningOfHour() Arrow {
	return New(a.Truncate(Hour))
}

func (a Arrow) AtBeginningOfDay() Arrow {
	d := time.Duration(-a.Hour()) * Hour
	return New(a.AtBeginningOfHour().Add(d))
}

func (a Arrow) AtBeginningOfWeek() Arrow {
	days := time.Duration(-1*int(a.Weekday())) * Day
	return New(a.AtBeginningOfDay().Add(days))
}

func (a Arrow) AtBeginningOfMonth() Arrow {
	days := time.Duration(-1*int(a.Day())+1) * Day
	return New(a.AtBeginningOfDay().Add(days))
}

func (a Arrow) AtBeginningOfYear() Arrow {
	days := time.Duration(-1*int(a.YearDay())+1) * Day
	return New(a.AtBeginningOfDay().Add(days))
}

// Add any durations parseable by time.ParseDuration
func (a Arrow) AddDurations(durations ...string) Arrow {
	for _, duration := range durations {
		a = a.AddDuration(duration)
	}
	return a
}

func formatConvert(format string) string {
	// create mapping from strftime to time in Go
	strftimeMapping := map[string]string{
		"%a": "Mon",
		"%A": "Monday",
		"%b": "Jan",
		"%B": "January",
		"%c": "", // locale not supported
		"%C": "06",
		"%d": "02",
		"%D": "01/02/06",
		"%e": "_2",
		"%E": "", // modifiers not supported
		"%F": "2006-01-02",
		"%G": "%G", // special case, see below
		"%g": "%g", // special case, see below
		"%h": "Jan",
		"%H": "15",
		"%I": "03",
		"%j": "%j", // special case, see below
		"%k": "%k", // special case, see below
		"%l": "_3",
		"%m": "01",
		"%M": "04",
		"%n": "\n",
		"%O": "", // modifiers not supported
		"%p": "PM",
		"%P": "pm",
		"%r": "03:04:05 PM",
		"%R": "15:04",
		"%s": "%s", // special case, see below
		"%S": "05",
		"%t": "\t",
		"%T": "15:04:05",
		"%u": "%u", // special case, see below
		"%U": "%U", // special case, see below
		"%V": "%V", // special case, see below
		"%w": "%w", // special case, see below
		"%W": "%W", // special case, see below
		"%x": "%x", // locale not supported
		"%X": "%X", // locale not supported
		"%y": "06",
		"%Y": "2006",
		"%z": "-0700",
		"%Z": "MST",
		"%+": "Mon Jan _2 15:04:05 MST 2006",
		"%%": "%%", // special case, see below
	}

	for fmt, conv := range strftimeMapping {
		format = strings.Replace(format, fmt, conv, -1)
	}

	return format
}

// Parse the time using the same format string types as strftime
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func CParse(layout, value string) (Arrow, error) {
	t, e := time.Parse(formatConvert(layout), value)
	return New(t), e
}

// Parse the time using the same format string types as strftime,
// within the given location.
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func CParseInLocation(layout, value string, loc *time.Location) (Arrow, error) {
	t, e := time.ParseInLocation(formatConvert(layout), value, loc)
	return New(t), e
}

// Format the time using the same format string types as strftime.
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func (a Arrow) CFormat(format string) string {
	format = a.Format(formatConvert(format))

	year, week := a.ISOWeek()
	yearday := a.YearDay()
	weekday := a.Weekday()
	syear := strconv.Itoa(year)
	sweek := strconv.Itoa(week)
	syearday := strconv.Itoa(yearday)
	sweekday := strconv.Itoa(int(weekday))

	if a.Year() > 999 {
		format = strings.Replace(format, "%G", syear, -1)
		format = strings.Replace(format, "%g", syear[2:4], -1)
	}

	format = strings.Replace(format, "%j", syearday, -1)
	if a.Hour() < 10 {
		shour := " " + strconv.Itoa(a.Hour())
		format = strings.Replace(format, "%k", shour, -1)
	}
	format = strings.Replace(format, "%s", strconv.FormatInt(a.Unix(), 10), -1)

	if weekday == 0 {
		format = strings.Replace(format, "%u", "7", -1)
	} else {
		format = strings.Replace(format, "%u", sweekday, -1)
	}

	format = strings.Replace(format, "%U", weekNumber(a, time.Sunday), -1)
	format = strings.Replace(format, "%U", sweek, -1)
	format = strings.Replace(format, "%w", sweekday, -1)
	format = strings.Replace(format, "%W", weekNumber(a, time.Monday), -1)
	return strings.Replace(format, "%%", "%", -1)
}

// Used for %U and %W:
// %U: The week number of the current year as a decimal number, range
// 00 to 53, starting with the first Sunday as the first day of week 01.
//
// %W: The week number of the current year as a decimal number, range
// 00 to 53, starting with the first Monday as the first day of week 01.
func weekNumber(a Arrow, firstday time.Weekday) string {
	dayone := a.AtBeginningOfYear()
	for dayone.Weekday() != time.Sunday {
		dayone = dayone.AddDays(1)
	}
	week := int(a.Sub(dayone.AddDays(-7)) / Week)
	return strconv.Itoa(week)
}
<commit_msg>can now Add duration and get back arrow<commit_after>/*
Package arrow provides C-style date formating and parsing, along with other date goodies.

See the github project page at http://github.com/bmuller/arrow for more info.
*/
package arrow

import (
	"strconv"
	"strings"
	"time"
)

type Arrow struct {
	time.Time
}

// Like time's constants, but with Day and Week
const (
	Nanosecond  time.Duration = 1
	Microsecond               = 1000 * Nanosecond
	Millisecond               = 1000 * Microsecond
	Second                    = 1000 * Millisecond
	Minute                    = 60 * Second
	Hour                      = 60 * Minute
	Day                       = 24 * Hour
	Week                      = 7 * Day
)

func New(t time.Time) Arrow {
	return Arrow{t}
}

func UTC() Arrow {
	return New(time.Now().UTC())
}

func Now() Arrow {
	return New(time.Now())
}

func Yesterday() Arrow {
	return Now().Yesterday()
}

func Tomorrow() Arrow {
	return Now().Tomorrow()
}

func NextMinute() Arrow {
	return Now().AddMinutes(1).AtBeginningOfMinute()
}

func NextHour() Arrow {
	return Now().AddHours(1).AtBeginningOfHour()
}

func NextDay() Arrow {
	return Now().AddDays(1).AtBeginningOfDay()
}

func SleepUntil(t Arrow) {
	time.Sleep(t.Sub(Now()))
}

// Get the current time in the given timezone.
// The timezone parameter should correspond to a file in the IANA Time Zone database,
// such as "America/New_York".  "UTC" and "Local" are also acceptable.  If the timezone
// given isn't valid, then no change to the timezone is made.
func InTimezone(timezone string) Arrow {
	return Now().InTimezone(timezone)
}

func (a Arrow) Yesterday() Arrow {
	return a.AddDays(-1)
}

func (a Arrow) Tomorrow() Arrow {
	return a.AddDays(1)
}

func (a Arrow) UTC() Arrow {
	return New(a.Time.UTC())
}

func (a Arrow) Sub(b Arrow) time.Duration {
	return a.Time.Sub(b.Time)
}

// Add any duration parseable by time.ParseDuration
func (a Arrow) AddDuration(duration string) Arrow {
	if pduration, err := time.ParseDuration(duration); err == nil {
		return a.Add(pduration)
	}
	return a
}

func (a Arrow) Add(d time.Duration) Arrow {
	return New(a.Time.Add(d))
}

// The timezone parameter should correspond to a file in the IANA Time Zone database,
// such as "America/New_York".  "UTC" and "Local" are also acceptable.  If the timezone
// given isn't valid, then no change to the timezone is made.
func (a Arrow) InTimezone(timezone string) Arrow {
	if location, err := time.LoadLocation(timezone); err == nil {
		return New(a.In(location))
	}
	return a
}

func (a Arrow) AddDays(days int) Arrow {
	return New(a.AddDate(0, 0, days))
}

func (a Arrow) AddHours(hours int) Arrow {
	year, month, day := a.Time.Date()
	hour, min, sec := a.Time.Clock()
	d := time.Date(year, month, day, hour+hours, min, sec, a.Nanosecond(), a.Location())
	return New(d)
}

func (a Arrow) AddMinutes(minutes int) Arrow {
	year, month, day := a.Time.Date()
	hour, min, sec := a.Time.Clock()
	d := time.Date(year, month, day, hour, min+minutes, sec, a.Nanosecond(), a.Location())
	return New(d)
}

func (a Arrow) AddSeconds(seconds int) Arrow {
	year, month, day := a.Time.Date()
	hour, min, sec := a.Time.Clock()
	d := time.Date(year, month, day, hour, min, sec+seconds, a.Nanosecond(), a.Location())
	return New(d)
}

func (a Arrow) AtBeginningOfMinute() Arrow {
	return New(a.Truncate(Minute))
}

func (a Arrow) AtBeginningOfHour() Arrow {
	return New(a.Truncate(Hour))
}

func (a Arrow) AtBeginningOfDay() Arrow {
	d := time.Duration(-a.Hour()) * Hour
	return a.AtBeginningOfHour().Add(d)
}

func (a Arrow) AtBeginningOfWeek() Arrow {
	days := time.Duration(-1*int(a.Weekday())) * Day
	return a.AtBeginningOfDay().Add(days)
}

func (a Arrow) AtBeginningOfMonth() Arrow {
	days := time.Duration(-1*int(a.Day())+1) * Day
	return a.AtBeginningOfDay().Add(days)
}

func (a Arrow) AtBeginningOfYear() Arrow {
	days := time.Duration(-1*int(a.YearDay())+1) * Day
	return a.AtBeginningOfDay().Add(days)
}

// Add any durations parseable by time.ParseDuration
func (a Arrow) AddDurations(durations ...string) Arrow {
	for _, duration := range durations {
		a = a.AddDuration(duration)
	}
	return a
}

func formatConvert(format string) string {
	// create mapping from strftime to time in Go
	strftimeMapping := map[string]string{
		"%a": "Mon",
		"%A": "Monday",
		"%b": "Jan",
		"%B": "January",
		"%c": "", // locale not supported
		"%C": "06",
		"%d": "02",
		"%D": "01/02/06",
		"%e": "_2",
		"%E": "", // modifiers not supported
		"%F": "2006-01-02",
		"%G": "%G", // special case, see below
		"%g": "%g", // special case, see below
		"%h": "Jan",
		"%H": "15",
		"%I": "03",
		"%j": "%j", // special case, see below
		"%k": "%k", // special case, see below
		"%l": "_3",
		"%m": "01",
		"%M": "04",
		"%n": "\n",
		"%O": "", // modifiers not supported
		"%p": "PM",
		"%P": "pm",
		"%r": "03:04:05 PM",
		"%R": "15:04",
		"%s": "%s", // special case, see below
		"%S": "05",
		"%t": "\t",
		"%T": "15:04:05",
		"%u": "%u", // special case, see below
		"%U": "%U", // special case, see below
		"%V": "%V", // special case, see below
		"%w": "%w", // special case, see below
		"%W": "%W", // special case, see below
		"%x": "%x", // locale not supported
		"%X": "%X", // locale not supported
		"%y": "06",
		"%Y": "2006",
		"%z": "-0700",
		"%Z": "MST",
		"%+": "Mon Jan _2 15:04:05 MST 2006",
		"%%": "%%", // special case, see below
	}

	for fmt, conv := range strftimeMapping {
		format = strings.Replace(format, fmt, conv, -1)
	}

	return format
}

// Parse the time using the same format string types as strftime
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func CParse(layout, value string) (Arrow, error) {
	t, e := time.Parse(formatConvert(layout), value)
	return New(t), e
}

// Parse the time using the same format string types as strftime,
// within the given location.
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func CParseInLocation(layout, value string, loc *time.Location) (Arrow, error) {
	t, e := time.ParseInLocation(formatConvert(layout), value, loc)
	return New(t), e
}

// Format the time using the same format string types as strftime.
// See http://man7.org/linux/man-pages/man3/strftime.3.html for more info.
func (a Arrow) CFormat(format string) string {
	format = a.Format(formatConvert(format))

	year, week := a.ISOWeek()
	yearday := a.YearDay()
	weekday := a.Weekday()
	syear := strconv.Itoa(year)
	sweek := strconv.Itoa(week)
	syearday := strconv.Itoa(yearday)
	sweekday := strconv.Itoa(int(weekday))

	if a.Year() > 999 {
		format = strings.Replace(format, "%G", syear, -1)
		format = strings.Replace(format, "%g", syear[2:4], -1)
	}

	format = strings.Replace(format, "%j", syearday, -1)
	if a.Hour() < 10 {
		shour := " " + strconv.Itoa(a.Hour())
		format = strings.Replace(format, "%k", shour, -1)
	}
	format = strings.Replace(format, "%s", strconv.FormatInt(a.Unix(), 10), -1)

	if weekday == 0 {
		format = strings.Replace(format, "%u", "7", -1)
	} else {
		format = strings.Replace(format, "%u", sweekday, -1)
	}

	format = strings.Replace(format, "%U", weekNumber(a, time.Sunday), -1)
	format = strings.Replace(format, "%U", sweek, -1)
	format = strings.Replace(format, "%w", sweekday, -1)
	format = strings.Replace(format, "%W", weekNumber(a, time.Monday), -1)
	return strings.Replace(format, "%%", "%", -1)
}

// Used for %U and %W:
// %U: The week number of the current year as a decimal number, range
// 00 to 53, starting with the first Sunday as the first day of week 01.
//
// %W: The week number of the current year as a decimal number, range
// 00 to 53, starting with the first Monday as the first day of week 01.
func weekNumber(a Arrow, firstday time.Weekday) string {
	dayone := a.AtBeginningOfYear()
	for dayone.Weekday() != time.Sunday {
		dayone = dayone.AddDays(1)
	}
	week := int(a.Sub(dayone.AddDays(-7)) / Week)
	return strconv.Itoa(week)
}
<|endoftext|>


<commit_before>package driver

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/docker/go-plugins-helpers/ipam"
	"github.com/vishvananda/netlink"
	"net"
	"sync"
)

type Driver struct {
	ipam.Ipam
	ncCh  chan<- *neighCheck       // Channel to request a neighbor check
	ncUch chan<- *neighUseNotifier // Channel to close if neighbor becomes in use
	sgCh  chan<- *addrSuggest      // Channel to request a suggested IP
	quit  <-chan struct{}
}

func NewDriver(quit <-chan struct{}, wg sync.WaitGroup) (*Driver, error) {
	log.Debugf("NewDriver")

	ncCh := make(chan *neighCheck)
	ncUch := make(chan *neighUseNotifier)
	sgCh := make(chan *addrSuggest)

	wg.Add(1)
	go func() {
		defer wg.Done()
		defer close(sgCh)
		defer close(ncUch)
		defer close(ncCh)
		_ = <-quit
	}()

	d := &Driver{
		ncCh:  ncCh,
		ncUch: ncUch,
		sgCh:  sgCh,
		quit:  quit,
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		checkNeigh(ncCh, ncUch, quit, wg)
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		genSuggestions(sgCh, ncCh, ncUch, quit, wg)
	}()
	return d, nil
}

func (d *Driver) GetCapabilities() (*ipam.CapabilitiesResponse, error) {
	log.Debugf("GetCapabilities")
	return &ipam.CapabilitiesResponse{
		RequiresMACAddress: false,
	}, nil
}

func (d *Driver) GetDefaultAddressSpaces() (*ipam.AddressSpacesResponse, error) {
	log.Debugf("GetDefaultAddressSpaces")
	return &ipam.AddressSpacesResponse{
		LocalDefaultAddressSpace:  "arp-ipam-default",
		GlobalDefaultAddressSpace: "arp-ipam-default",
	}, nil
}

func (d *Driver) RequestPool(r *ipam.RequestPoolRequest) (*ipam.RequestPoolResponse, error) {
	log.Debugf("RequestPool: %v", r)
	if r.Pool == "" {
		log.Errorf("Automatic pool assignment not supported")
		return nil, fmt.Errorf("Automatic pool assignment not supported")
	}
	if r.V6 {
		log.Errorf("Automatic V6 pool assignment not supported.")
		return nil, fmt.Errorf("Automatic V6 pool assignment not supported.")
	}
	if r.SubPool != "" {
		log.Errorf("SubPool not supported.")
		return nil, fmt.Errorf("SubPool not supported.")
	}
	n, err := netlink.ParseIPNet(r.Pool)
	if err != nil {
		log.Errorf("Error parsing pool: %v", err)
		return nil, err
	}

	if err := verifyLocalNet(n); err != nil {
		return nil, err
	}

	addrs, err := netlink.AddrList(nil, netlink.FAMILY_ALL)
	if err != nil {
		log.Errorf("Error getting local addresses: %v", err)
		return nil, err
	}
	innet := false
	for _, addr := range addrs {
		if n.Contains(addr.IP) {
			innet = true
			break
		}
	}
	if !innet {
		log.Errorf("Pool is not a local network: %v", n)
		return nil, fmt.Errorf("Pool is not a local network")
	}

	return &ipam.RequestPoolResponse{
		PoolID: r.Pool,
		Pool:   r.Pool,
	}, nil
}

func verifyLocalNet(n *net.IPNet) error {
	addrs, err := netlink.AddrList(nil, netlink.FAMILY_ALL)
	if err != nil {
		log.Errorf("Error getting local addresses: %v", err)
		return err
	}
	innet := false
	for _, addr := range addrs {
		if n.Contains(addr.IP) {
			innet = true
			break
		}
	}
	if !innet {
		log.Errorf("Pool is not a local network: %v", n)
		return fmt.Errorf("Pool is not a local network")
	}
	return nil
}

func (d *Driver) ReleasePool(r *ipam.ReleasePoolRequest) error {
	log.Debugf("ReleasePool: %v", r)
	return nil
}

func (d *Driver) RequestAddress(r *ipam.RequestAddressRequest) (*ipam.RequestAddressResponse, error) {
	log.Debugf("RequestAddress: %v", r)

	n, err := netlink.ParseIPNet(r.PoolID)
	if err != nil {
		log.Errorf("Unable to parse PoolID: %v", r.PoolID)
		log.Errorf("err: %v", err)
		return nil, err
	}

	if err := verifyLocalNet(n); err != nil {
		return nil, err
	}

	res := &ipam.RequestAddressResponse{}

	if r.Address != "" {
		log.Debugf("Specific Address Requested: %v", r.Address)

		addr := net.ParseIP(r.Address)
		if addr == nil {
			log.Errorf("Unable to parse address: %v", r.Address)
			return nil, fmt.Errorf("Unable to parse address: %v", r.Address)
		}

		if r.Options["RequestAddressType"] == "com.docker.network.gateway" {
			log.Debugf("Gateway requested, approving")
			ret_addr := net.IPNet{IP: addr, Mask: n.Mask}
			res.Address = ret_addr.String()
			return res, nil
		}

		if tryAddress(&addr, d.ncCh) {
			log.Errorf("Address already in use: %v", addr)
			return nil, fmt.Errorf("Address already in use: %v", addr)
		}
		ret_addr := net.IPNet{IP: addr, Mask: n.Mask}
		res.Address = ret_addr.String()
		return res, nil
	}

	log.Debugf("Random Address Requested in network %v", n)

	ch := make(chan *net.IPNet)
	defer close(ch)
	ech := make(chan error)
	defer close(ech)
	d.sgCh <- &addrSuggest{
		ipn: n,
		ch:  ch,
		ech: ech,
	}
	select {
	case _ = <-d.quit:
		return nil, fmt.Errorf("Shutting down")
	case err := <-ech:
		log.Errorf("Error getting random address")
		return nil, err
	case ret_addr := <-ch:
		res.Address = ret_addr.String()
	}
	return res, nil
}

func tryAddress(ip *net.IP, ncCh chan<- *neighCheck) bool {
	ch := make(chan bool)
	defer close(ch)
	nc := &neighCheck{
		ip: ip,
		ch: ch,
	}
	ncCh <- nc
	res := <-ch
	return res
}

func (d *Driver) ReleaseAddress(r *ipam.ReleaseAddressRequest) error {
	log.Debugf("ReleaseAddress: %v", r)
	ip := net.ParseIP(r.Address)
	log.Debugf("Deleting entry from arp table for %v", ip)
	neighs, err := netlink.NeighList(0, netlink.FAMILY_ALL)
	if err != nil {
		log.WithError(err).Error("Failed to get arp table")
		return err
	}
	for _, n := range neighs {
		if ip.Equal(n.IP) {
			err := netlink.NeighDel(&n)
			if err != nil {
				log.WithError(err).Error("Failed to delete arp entry for %v", ip)
			}
			return err
		}
	}
	return nil
}
<commit_msg>probe specific addresses<commit_after>package driver

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/docker/go-plugins-helpers/ipam"
	"github.com/vishvananda/netlink"
	"net"
	"sync"
)

type Driver struct {
	ipam.Ipam
	ncCh  chan<- *neighCheck       // Channel to request a neighbor check
	ncUch chan<- *neighUseNotifier // Channel to close if neighbor becomes in use
	sgCh  chan<- *addrSuggest      // Channel to request a suggested IP
	quit  <-chan struct{}
}

func NewDriver(quit <-chan struct{}, wg sync.WaitGroup) (*Driver, error) {
	log.Debugf("NewDriver")

	ncCh := make(chan *neighCheck)
	ncUch := make(chan *neighUseNotifier)
	sgCh := make(chan *addrSuggest)

	wg.Add(1)
	go func() {
		defer wg.Done()
		defer close(sgCh)
		defer close(ncUch)
		defer close(ncCh)
		_ = <-quit
	}()

	d := &Driver{
		ncCh:  ncCh,
		ncUch: ncUch,
		sgCh:  sgCh,
		quit:  quit,
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		checkNeigh(ncCh, ncUch, quit, wg)
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		genSuggestions(sgCh, ncCh, ncUch, quit, wg)
	}()
	return d, nil
}

func (d *Driver) GetCapabilities() (*ipam.CapabilitiesResponse, error) {
	log.Debugf("GetCapabilities")
	return &ipam.CapabilitiesResponse{
		RequiresMACAddress: false,
	}, nil
}

func (d *Driver) GetDefaultAddressSpaces() (*ipam.AddressSpacesResponse, error) {
	log.Debugf("GetDefaultAddressSpaces")
	return &ipam.AddressSpacesResponse{
		LocalDefaultAddressSpace:  "arp-ipam-default",
		GlobalDefaultAddressSpace: "arp-ipam-default",
	}, nil
}

func (d *Driver) RequestPool(r *ipam.RequestPoolRequest) (*ipam.RequestPoolResponse, error) {
	log.Debugf("RequestPool: %v", r)
	if r.Pool == "" {
		log.Errorf("Automatic pool assignment not supported")
		return nil, fmt.Errorf("Automatic pool assignment not supported")
	}
	if r.V6 {
		log.Errorf("Automatic V6 pool assignment not supported.")
		return nil, fmt.Errorf("Automatic V6 pool assignment not supported.")
	}
	if r.SubPool != "" {
		log.Errorf("SubPool not supported.")
		return nil, fmt.Errorf("SubPool not supported.")
	}
	n, err := netlink.ParseIPNet(r.Pool)
	if err != nil {
		log.Errorf("Error parsing pool: %v", err)
		return nil, err
	}

	if err := verifyLocalNet(n); err != nil {
		return nil, err
	}

	addrs, err := netlink.AddrList(nil, netlink.FAMILY_ALL)
	if err != nil {
		log.Errorf("Error getting local addresses: %v", err)
		return nil, err
	}
	innet := false
	for _, addr := range addrs {
		if n.Contains(addr.IP) {
			innet = true
			break
		}
	}
	if !innet {
		log.Errorf("Pool is not a local network: %v", n)
		return nil, fmt.Errorf("Pool is not a local network")
	}

	return &ipam.RequestPoolResponse{
		PoolID: r.Pool,
		Pool:   r.Pool,
	}, nil
}

func verifyLocalNet(n *net.IPNet) error {
	addrs, err := netlink.AddrList(nil, netlink.FAMILY_ALL)
	if err != nil {
		log.Errorf("Error getting local addresses: %v", err)
		return err
	}
	innet := false
	for _, addr := range addrs {
		if n.Contains(addr.IP) {
			innet = true
			break
		}
	}
	if !innet {
		log.Errorf("Pool is not a local network: %v", n)
		return fmt.Errorf("Pool is not a local network")
	}
	return nil
}

func (d *Driver) ReleasePool(r *ipam.ReleasePoolRequest) error {
	log.Debugf("ReleasePool: %v", r)
	return nil
}

func (d *Driver) RequestAddress(r *ipam.RequestAddressRequest) (*ipam.RequestAddressResponse, error) {
	log.Debugf("RequestAddress: %v", r)

	n, err := netlink.ParseIPNet(r.PoolID)
	if err != nil {
		log.Errorf("Unable to parse PoolID: %v", r.PoolID)
		log.Errorf("err: %v", err)
		return nil, err
	}

	if err := verifyLocalNet(n); err != nil {
		return nil, err
	}

	res := &ipam.RequestAddressResponse{}

	if r.Address != "" {
		log.Debugf("Specific Address Requested: %v", r.Address)
		probe(&n.IP)

		addr := net.ParseIP(r.Address)
		if addr == nil {
			log.Errorf("Unable to parse address: %v", r.Address)
			return nil, fmt.Errorf("Unable to parse address: %v", r.Address)
		}

		if r.Options["RequestAddressType"] == "com.docker.network.gateway" {
			log.Debugf("Gateway requested, approving")
			ret_addr := net.IPNet{IP: addr, Mask: n.Mask}
			res.Address = ret_addr.String()
			return res, nil
		}

		if tryAddress(&addr, d.ncCh) {
			log.Errorf("Address already in use: %v", addr)
			return nil, fmt.Errorf("Address already in use: %v", addr)
		}
		ret_addr := net.IPNet{IP: addr, Mask: n.Mask}
		res.Address = ret_addr.String()
		return res, nil
	}

	log.Debugf("Random Address Requested in network %v", n)

	ch := make(chan *net.IPNet)
	defer close(ch)
	ech := make(chan error)
	defer close(ech)
	d.sgCh <- &addrSuggest{
		ipn: n,
		ch:  ch,
		ech: ech,
	}
	select {
	case _ = <-d.quit:
		return nil, fmt.Errorf("Shutting down")
	case err := <-ech:
		log.Errorf("Error getting random address")
		return nil, err
	case ret_addr := <-ch:
		res.Address = ret_addr.String()
	}
	return res, nil
}

func tryAddress(ip *net.IP, ncCh chan<- *neighCheck) bool {
	ch := make(chan bool)
	defer close(ch)
	nc := &neighCheck{
		ip: ip,
		ch: ch,
	}
	ncCh <- nc
	res := <-ch
	return res
}

func (d *Driver) ReleaseAddress(r *ipam.ReleaseAddressRequest) error {
	log.Debugf("ReleaseAddress: %v", r)
	ip := net.ParseIP(r.Address)
	log.Debugf("Deleting entry from arp table for %v", ip)
	neighs, err := netlink.NeighList(0, netlink.FAMILY_ALL)
	if err != nil {
		log.WithError(err).Error("Failed to get arp table")
		return err
	}
	for _, n := range neighs {
		if ip.Equal(n.IP) {
			err := netlink.NeighDel(&n)
			if err != nil {
				log.WithError(err).Error("Failed to delete arp entry for %v", ip)
			}
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package libkb

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"
	"sync"

	"github.com/blang/semver"
)

type GpgCLI struct {
	Contextified
	path    string
	options []string
	version string
	tty     string

	mutex *sync.Mutex

	logUI LogUI
}

func NewGpgCLI(g *GlobalContext, logUI LogUI) *GpgCLI {
	if logUI == nil {
		logUI = g.Log
	}
	return &GpgCLI{
		Contextified: NewContextified(g),
		mutex:        new(sync.Mutex),
		logUI:        logUI,
	}
}

func (g *GpgCLI) SetTTY(t string) {
	g.tty = t
}

func (g *GpgCLI) Configure() (err error) {

	g.mutex.Lock()
	defer g.mutex.Unlock()

	prog := G.Env.GetGpg()
	opts := G.Env.GetGpgOptions()

	if len(prog) > 0 {
		err = canExec(prog)
	} else {
		prog, err = exec.LookPath("gpg2")
		if err != nil {
			prog, err = exec.LookPath("gpg")
		}
	}
	if err != nil {
		return err
	}

	g.logUI.Debug("| configured GPG w/ path: %s", prog)

	g.path = prog
	g.options = opts

	return
}

// CanExec returns true if a gpg executable exists.
func (g *GpgCLI) CanExec() (bool, error) {
	err := g.Configure()
	if IsExecError(err) {
		return false, nil
	}
	if err != nil {
		return false, err
	}
	return true, nil
}

// Path returns the path of the gpg executable.
// Path is only available if CanExec() is true.
func (g *GpgCLI) Path() string {
	canExec, err := g.CanExec()
	if err == nil && canExec {
		return g.path
	}
	return ""
}

func (g *GpgCLI) ImportKey(secret bool, fp PGPFingerprint) (*PGPKeyBundle, error) {
	g.outputVersion()
	var cmd string
	var which string
	if secret {
		cmd = "--export-secret-key"
		which = "secret "
	} else {
		cmd = "--export"
	}

	arg := RunGpg2Arg{
		Arguments: []string{"--armor", cmd, fp.String()},
		Stdout:    true,
	}

	res := g.Run2(arg)
	if res.Err != nil {
		return nil, res.Err
	}

	buf := new(bytes.Buffer)
	buf.ReadFrom(res.Stdout)
	armored := buf.String()

	// Convert to posix style on windows
	armored = PosixLineEndings(armored)

	if err := res.Wait(); err != nil {
		return nil, err
	}

	if len(armored) == 0 {
		return nil, NoKeyError{fmt.Sprintf("No %skey found for fingerprint %s", which, fp)}
	}

	bundle, w, err := ReadOneKeyFromString(armored)
	w.Warn(g.G())
	if err != nil {
		return nil, err
	}

	// For secret keys, *also* import the key in public mode, and then grab the
	// ArmoredPublicKey from that. That's because the public import goes out of
	// its way to preserve the exact armored string from GPG.
	if secret {
		publicBundle, err := g.ImportKey(false, fp)
		if err != nil {
			return nil, err
		}
		bundle.ArmoredPublicKey = publicBundle.ArmoredPublicKey

		// It's a bug that gpg --export-secret-keys doesn't grep subkey revocations.
		// No matter, we have both in-memory, so we can copy it over here
		bundle.CopySubkeyRevocations(publicBundle.Entity)
	}

	return bundle, nil
}

func (g *GpgCLI) ExportKey(k PGPKeyBundle, private bool) (err error) {
	g.outputVersion()
	arg := RunGpg2Arg{
		Arguments: []string{"--import"},
		Stdin:     true,
	}

	res := g.Run2(arg)
	if res.Err != nil {
		return res.Err
	}

	e1 := k.EncodeToStream(res.Stdin, private)
	e2 := res.Stdin.Close()
	e3 := res.Wait()
	return PickFirstError(e1, e2, e3)
}

func (g *GpgCLI) Sign(fp PGPFingerprint, payload []byte) (string, error) {
	g.outputVersion()
	arg := RunGpg2Arg{
		Arguments: []string{"--armor", "--sign", "-u", fp.String()},
		Stdout:    true,
		Stdin:     true,
	}

	res := g.Run2(arg)
	if res.Err != nil {
		return "", res.Err
	}

	res.Stdin.Write(payload)
	res.Stdin.Close()

	buf := new(bytes.Buffer)
	buf.ReadFrom(res.Stdout)
	armored := buf.String()

	// Convert to posix style on windows
	armored = PosixLineEndings(armored)

	if err := res.Wait(); err != nil {
		return "", err
	}

	return armored, nil
}

func (g *GpgCLI) Version() (string, error) {
	if len(g.version) > 0 {
		return g.version, nil
	}

	args := append(g.options, "--version")
	out, err := exec.Command(g.path, args...).Output()
	if err != nil {
		return "", err
	}
	g.version = string(out)
	return g.version, nil
}

func (g *GpgCLI) outputVersion() {
	v, err := g.Version()
	if err != nil {
		g.logUI.Debug("error getting GPG version: %s", err)
		return
	}
	g.logUI.Debug("GPG version:\n%s", v)
}

func (g *GpgCLI) SemanticVersion() (*semver.Version, error) {
	out, err := g.Version()
	if err != nil {
		return nil, err
	}
	lines := strings.Split(out, "\n")
	if len(lines) == 0 {
		return nil, errors.New("empty gpg version")
	}
	parts := strings.Fields(lines[0])
	if len(parts) != 3 {
		return nil, errors.New("unhandled gpg version output")
	}
	return semver.New(parts[2])
}

func (g *GpgCLI) VersionAtLeast(s string) (bool, error) {
	min, err := semver.New(s)
	if err != nil {
		return false, err
	}
	cur, err := g.SemanticVersion()
	if err != nil {
		return false, err
	}
	return cur.GTE(*min), nil
}

type RunGpg2Arg struct {
	Arguments []string
	Stdin     bool
	Stderr    bool
	Stdout    bool
}

type RunGpg2Res struct {
	Stdin  io.WriteCloser
	Stdout io.ReadCloser
	Stderr io.ReadCloser
	Wait   func() error
	Err    error
}

func (g *GpgCLI) Run2(arg RunGpg2Arg) (res RunGpg2Res) {
	if g.path == "" {
		res.Err = errors.New("no gpg path set")
		return
	}

	cmd := g.MakeCmd(arg.Arguments)

	if arg.Stdin {
		if res.Stdin, res.Err = cmd.StdinPipe(); res.Err != nil {
			return
		}
	}

	var stdout, stderr io.ReadCloser

	if stdout, res.Err = cmd.StdoutPipe(); res.Err != nil {
		return
	}
	if stderr, res.Err = cmd.StderrPipe(); res.Err != nil {
		return
	}

	if res.Err = cmd.Start(); res.Err != nil {
		return
	}

	waited := false
	out := 0
	ch := make(chan error)
	var fep FirstErrorPicker

	res.Wait = func() error {
		for out > 0 {
			fep.Push(<-ch)
			out--
		}
		if !waited {
			waited = true
			err := cmd.Wait()
			if err != nil {
				fep.Push(ErrorToGpgError(err))
			}
			return fep.Error()
		}
		return nil
	}

	if !arg.Stdout {
		out++
		go func() {
			ch <- DrainPipe(stdout, func(s string) { g.logUI.Debug(s) })
		}()
	} else {
		res.Stdout = stdout
	}

	if !arg.Stderr {
		out++
		go func() {
			ch <- DrainPipe(stderr, func(s string) { g.logUI.Debug(s) })
		}()
	} else {
		res.Stderr = stderr
	}

	return
}

func (g *GpgCLI) MakeCmd(args []string) *exec.Cmd {
	var nargs []string
	if g.options != nil {
		nargs = make([]string, len(g.options))
		copy(nargs, g.options)
		nargs = append(nargs, args...)
	} else {
		nargs = args
	}
	if g.G().Service {
		nargs = append([]string{"--no-tty"}, nargs...)
	}
	g.logUI.Debug("| running Gpg: %s %v", g.path, nargs)
	ret := exec.Command(g.path, nargs...)
	if g.tty != "" {
		ret.Env = append(os.Environ(), "GPG_TTY="+g.tty)
	}
	return ret
}
<commit_msg>Include gpg output in error to figure out win issue<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package libkb

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"
	"sync"

	"github.com/blang/semver"
)

type GpgCLI struct {
	Contextified
	path    string
	options []string
	version string
	tty     string

	mutex *sync.Mutex

	logUI LogUI
}

func NewGpgCLI(g *GlobalContext, logUI LogUI) *GpgCLI {
	if logUI == nil {
		logUI = g.Log
	}
	return &GpgCLI{
		Contextified: NewContextified(g),
		mutex:        new(sync.Mutex),
		logUI:        logUI,
	}
}

func (g *GpgCLI) SetTTY(t string) {
	g.tty = t
}

func (g *GpgCLI) Configure() (err error) {

	g.mutex.Lock()
	defer g.mutex.Unlock()

	prog := G.Env.GetGpg()
	opts := G.Env.GetGpgOptions()

	if len(prog) > 0 {
		err = canExec(prog)
	} else {
		prog, err = exec.LookPath("gpg2")
		if err != nil {
			prog, err = exec.LookPath("gpg")
		}
	}
	if err != nil {
		return err
	}

	g.logUI.Debug("| configured GPG w/ path: %s", prog)

	g.path = prog
	g.options = opts

	return
}

// CanExec returns true if a gpg executable exists.
func (g *GpgCLI) CanExec() (bool, error) {
	err := g.Configure()
	if IsExecError(err) {
		return false, nil
	}
	if err != nil {
		return false, err
	}
	return true, nil
}

// Path returns the path of the gpg executable.
// Path is only available if CanExec() is true.
func (g *GpgCLI) Path() string {
	canExec, err := g.CanExec()
	if err == nil && canExec {
		return g.path
	}
	return ""
}

func (g *GpgCLI) ImportKey(secret bool, fp PGPFingerprint) (*PGPKeyBundle, error) {
	g.outputVersion()
	var cmd string
	var which string
	if secret {
		cmd = "--export-secret-key"
		which = "secret "
	} else {
		cmd = "--export"
	}

	arg := RunGpg2Arg{
		Arguments: []string{"--armor", cmd, fp.String()},
		Stdout:    true,
	}

	res := g.Run2(arg)
	if res.Err != nil {
		return nil, res.Err
	}

	buf := new(bytes.Buffer)
	buf.ReadFrom(res.Stdout)
	armored := buf.String()

	// Convert to posix style on windows
	armored = PosixLineEndings(armored)

	if err := res.Wait(); err != nil {
		return nil, err
	}

	if len(armored) == 0 {
		return nil, NoKeyError{fmt.Sprintf("No %skey found for fingerprint %s", which, fp)}
	}

	bundle, w, err := ReadOneKeyFromString(armored)
	w.Warn(g.G())
	if err != nil {
		return nil, err
	}

	// For secret keys, *also* import the key in public mode, and then grab the
	// ArmoredPublicKey from that. That's because the public import goes out of
	// its way to preserve the exact armored string from GPG.
	if secret {
		publicBundle, err := g.ImportKey(false, fp)
		if err != nil {
			return nil, err
		}
		bundle.ArmoredPublicKey = publicBundle.ArmoredPublicKey

		// It's a bug that gpg --export-secret-keys doesn't grep subkey revocations.
		// No matter, we have both in-memory, so we can copy it over here
		bundle.CopySubkeyRevocations(publicBundle.Entity)
	}

	return bundle, nil
}

func (g *GpgCLI) ExportKey(k PGPKeyBundle, private bool) (err error) {
	g.outputVersion()
	arg := RunGpg2Arg{
		Arguments: []string{"--import"},
		Stdin:     true,
	}

	res := g.Run2(arg)
	if res.Err != nil {
		return res.Err
	}

	e1 := k.EncodeToStream(res.Stdin, private)
	e2 := res.Stdin.Close()
	e3 := res.Wait()
	return PickFirstError(e1, e2, e3)
}

func (g *GpgCLI) Sign(fp PGPFingerprint, payload []byte) (string, error) {
	g.outputVersion()
	arg := RunGpg2Arg{
		Arguments: []string{"--armor", "--sign", "-u", fp.String()},
		Stdout:    true,
		Stdin:     true,
	}

	res := g.Run2(arg)
	if res.Err != nil {
		return "", res.Err
	}

	res.Stdin.Write(payload)
	res.Stdin.Close()

	buf := new(bytes.Buffer)
	buf.ReadFrom(res.Stdout)
	armored := buf.String()

	// Convert to posix style on windows
	armored = PosixLineEndings(armored)

	if err := res.Wait(); err != nil {
		return "", err
	}

	return armored, nil
}

func (g *GpgCLI) Version() (string, error) {
	if len(g.version) > 0 {
		return g.version, nil
	}

	args := append(g.options, "--version")
	out, err := exec.Command(g.path, args...).Output()
	if err != nil {
		return "", err
	}
	g.version = string(out)
	return g.version, nil
}

func (g *GpgCLI) outputVersion() {
	v, err := g.Version()
	if err != nil {
		g.logUI.Debug("error getting GPG version: %s", err)
		return
	}
	g.logUI.Debug("GPG version:\n%s", v)
}

func (g *GpgCLI) SemanticVersion() (*semver.Version, error) {
	out, err := g.Version()
	if err != nil {
		return nil, err
	}
	lines := strings.Split(out, "\n")
	if len(lines) == 0 {
		return nil, errors.New("empty gpg version")
	}
	parts := strings.Fields(lines[0])
	if len(parts) != 3 {
		return nil, fmt.Errorf("unhandled gpg version output %q full: %q", lines[0], lines)
	}
	return semver.New(parts[2])
}

func (g *GpgCLI) VersionAtLeast(s string) (bool, error) {
	min, err := semver.New(s)
	if err != nil {
		return false, err
	}
	cur, err := g.SemanticVersion()
	if err != nil {
		return false, err
	}
	return cur.GTE(*min), nil
}

type RunGpg2Arg struct {
	Arguments []string
	Stdin     bool
	Stderr    bool
	Stdout    bool
}

type RunGpg2Res struct {
	Stdin  io.WriteCloser
	Stdout io.ReadCloser
	Stderr io.ReadCloser
	Wait   func() error
	Err    error
}

func (g *GpgCLI) Run2(arg RunGpg2Arg) (res RunGpg2Res) {
	if g.path == "" {
		res.Err = errors.New("no gpg path set")
		return
	}

	cmd := g.MakeCmd(arg.Arguments)

	if arg.Stdin {
		if res.Stdin, res.Err = cmd.StdinPipe(); res.Err != nil {
			return
		}
	}

	var stdout, stderr io.ReadCloser

	if stdout, res.Err = cmd.StdoutPipe(); res.Err != nil {
		return
	}
	if stderr, res.Err = cmd.StderrPipe(); res.Err != nil {
		return
	}

	if res.Err = cmd.Start(); res.Err != nil {
		return
	}

	waited := false
	out := 0
	ch := make(chan error)
	var fep FirstErrorPicker

	res.Wait = func() error {
		for out > 0 {
			fep.Push(<-ch)
			out--
		}
		if !waited {
			waited = true
			err := cmd.Wait()
			if err != nil {
				fep.Push(ErrorToGpgError(err))
			}
			return fep.Error()
		}
		return nil
	}

	if !arg.Stdout {
		out++
		go func() {
			ch <- DrainPipe(stdout, func(s string) { g.logUI.Debug(s) })
		}()
	} else {
		res.Stdout = stdout
	}

	if !arg.Stderr {
		out++
		go func() {
			ch <- DrainPipe(stderr, func(s string) { g.logUI.Debug(s) })
		}()
	} else {
		res.Stderr = stderr
	}

	return
}

func (g *GpgCLI) MakeCmd(args []string) *exec.Cmd {
	var nargs []string
	if g.options != nil {
		nargs = make([]string, len(g.options))
		copy(nargs, g.options)
		nargs = append(nargs, args...)
	} else {
		nargs = args
	}
	if g.G().Service {
		nargs = append([]string{"--no-tty"}, nargs...)
	}
	g.logUI.Debug("| running Gpg: %s %v", g.path, nargs)
	ret := exec.Command(g.path, nargs...)
	if g.tty != "" {
		ret.Env = append(os.Environ(), "GPG_TTY="+g.tty)
	}
	return ret
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/hidu/go-speed"
	"github.com/hidu/goutils/log_util"
	"io"
	"io/ioutil"
	glog "log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var version = "0.1.3 20161114"

var conc = flag.Uint("c", 10, "Concurrent Num [conc]")
var timeout = flag.Int64("t", 10000, "Timeout,ms")
var method = flag.String("method", "GET", "HTTP Method")
var noResp = flag.Bool("nr", false, "No Respose (default false)")
var ua = flag.String("ua", "url_call_conc/"+version, "User-Agent")
var logPath = flag.String("log", "", "log file prex,default stderr")
var start = flag.Int("start", 0, "start item num")

var complex = flag.Bool("complex", false, `Complex Input (default false)`)

var v = flag.Bool("version", false, "Version:"+version)

var idx uint64

var jobs chan *http.Request

func init() {
	usage := flag.Usage
	flag.Usage = func() {
		usage()
		fmt.Println("\ncat urllist.txt|url_call_conc -c 100")
		fmt.Println("\n use dynamic conf [ ./url_call_conc.conf ] to change runtime params :")
		fmt.Println(`{"conc":10}`)
		fmt.Println("\n site: https://github.com/hidu/tool")
		fmt.Println(" version:", version)
	}
}

var speedData *speed.Speed

var timeOut time.Duration
var log *glog.Logger

var rw sync.RWMutex

type UrlCallConcConf struct {
	FromFile bool   `json:"from_file"`
	ConcMax  uint   `json:"conc"`
	Start    uint64 `json:"start"`
	//    QpsMax  uint `json:"qps"`
}

var confCur *UrlCallConcConf
var confLast *UrlCallConcConf

func (c *UrlCallConcConf) String() string {
	bf, _ := json.Marshal(c)
	return string(bf)
}

func getConf() *UrlCallConcConf {
	conf := &UrlCallConcConf{
		ConcMax: *conc,
		Start:   uint64(*start),
	}
	fname := "url_call_conc.conf"
	bs, err := ioutil.ReadFile(fname)
	if err != nil {
		//log.Println("[wf] read_conf failed", fname, "skip,", err.Error())
	} else {
		err := json.Unmarshal(bs, &conf)
		if err != nil {
			log.Println("[wf] parse_conf ", fname, "failed,skip,", err.Error())
		} else {
			conf.FromFile = true
		}
	}
	log.Println("[trace] now_conf is:", conf)
	return conf
}

func startWorkers() {
	if confCur.ConcMax > confLast.ConcMax {
		log.Println("[trace] startWorkers,last_conc=", confLast.ConcMax, "cur_conc=", confCur.ConcMax)
		for i := confLast.ConcMax; i < confCur.ConcMax; i++ {
			go urlCallWorker(jobs, i)
		}
	}
}

var workerRunning int64 = 0

func main() {
	flag.Parse()
	if *v {
		fmt.Println(version)
		return
	}

	if *ua == "mac" {
		*ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.1103.21"
	}

	log = glog.New(os.Stderr, "", glog.LstdFlags)
	if *logPath != "" {
		log_util.SetLogFile(log, *logPath, log_util.LOG_TYPE_HOUR)
	}
	startTime := time.Now()
	timeOut = time.Duration(*timeout) * time.Millisecond

	speedData = speed.NewSpeed("call", 5, func(msg string, sp *speed.Speed) {
		n := atomic.LoadInt64(&workerRunning)
		log.Println("speed", msg, "running_workers=", n)
	})

	confCur = getConf()
	confLast = &UrlCallConcConf{}

	var urlStr = strings.TrimSpace(flag.Arg(0))

	jobs = make(chan *http.Request, 1024)

	startWorkers()

	ticker := time.NewTicker(30 * time.Second)

	go func() {
		for range ticker.C {
			(func() {
				confTmp := getConf()
				if confTmp.String() != confCur.String() {
					rw.Lock()
					defer rw.Unlock()
					confLast = confCur
					confCur = confTmp
					startWorkers()
				}
			})()
		}
	}()

	if urlStr == "" {
		if *complex {
			parseComplexStdIn()
		} else {
			parseSimpleStdIn()
		}
	} else {
		req, err := http.NewRequest(strings.ToUpper(*method), urlStr, nil)
		if err != nil {
			log.Println(err)
		} else {
			req.Header.Set("User-Agent", *ua)
			jobs <- req
		}
	}
	close(jobs)

	time.Sleep(timeOut + 500*time.Millisecond)
	var num int64 = 1
	for num > 0 {
		num = atomic.LoadInt64(&workerRunning)
		log.Println("[trace] e_running_worker_total=", workerRunning, "waiting...")
		time.Sleep(1 * time.Second)
	}
	speedData.Stop()

	timeUsed := time.Now().Sub(startTime)

	log.Println("[info]done total:", idx, "used:", timeUsed)
}

func parseSimpleStdIn() {
	var urlStr string
	buf := bufio.NewReaderSize(os.Stdin, 81920)
	for {
		line, err := buf.ReadBytes('\n')
		if len(line) > 0 {
			urlStr = strings.TrimSpace(string(line))
			if urlStr == "" {
				continue
			}
			req, err := http.NewRequest(strings.ToUpper(*method), urlStr, nil)
			if err != nil {
				log.Println("[wf]", urlStr, "err:", err)
				continue
			}
			req.Header.Set("User-Agent", *ua)
			jobs <- req
		}
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Println("[wf]", string(line), "err:", err)
		}
	}
}

func parseComplexStdIn() {
	buf := bufio.NewReaderSize(os.Stdin, 81920)
	for {
		headLen, err := buf.ReadString('|')
		if err == io.EOF {
			break
		}
		hl, err := strconv.Atoi(headLen[0 : len(headLen)-1])
		if err != nil {
			log.Fatalln("[fat] read head length faild:", headLen)
		}
		headBf := make([]byte, hl)
		buf.Read(headBf)

		var hdObj *Head
		err = json.Unmarshal(headBf, &hdObj)
		if err != nil {
			log.Fatalln("[fat] parse head faild:", headLen)
		}

		bodyLen, err := buf.ReadString('|')
		if err != nil {
			log.Fatalln("[fat] read body length faild:", err)
		}
		bl, err := strconv.Atoi(bodyLen[0 : len(bodyLen)-1])
		if err != nil {
			log.Fatalln("[fat] parse body length faild:", headLen)
		}

		var body io.Reader

		if bl > 0 {
			bodyBf := make([]byte, bl)
			buf.Read(bodyBf)
			body = bytes.NewReader(bodyBf)
		}
		if hdObj.Method == "" {
			hdObj.Method = "GET"
		}
		req, err := http.NewRequest(strings.ToUpper(hdObj.Method), hdObj.Url, body)
		if err != nil {
			log.Fatalln("[wf]", hdObj, "err:", err)
		}
		req.Header.Set("User-Agent", *ua)
		if hdObj.Header != nil {
			for k, v := range hdObj.Header {
				req.Header.Add(k, v)
			}
		}

		jobs <- req
	}
}

type Head struct {
	Url    string            `json:"url"`
	Method string            `json:"method"`
	Header map[string]string `json:"header"`
}

func urlCallWorker(jobs <-chan *http.Request, workerId uint) {
	_runningNum := atomic.AddInt64(&workerRunning, 1)
	defer func() {
		num := atomic.AddInt64(&workerRunning, -1)
		log.Println("[trace] running_worker_total=", num)
	}()

	log.Println("[trace] urlCallWorker_start,worker_id=", workerId, "running_worker_total=", _runningNum)
	client := &http.Client{
		Timeout: timeOut,
	}
	var respStr string
	var sucNum int
	lr := &logRequest{
		buf: new(bytes.Buffer),
	}
	isSkip := false
	for req := range jobs {
		id := atomic.AddUint64(&idx, 1)
		urlStr := req.URL.String()
		lr.reset()

		lr.addNotice("id", id)
		lr.addNotice("worker_id", workerId)
		lr.addNotice("method", req.Method)
		lr.addNotice("url", urlStr)

		(func() {
			rw.RLock()
			defer rw.RUnlock()
			isSkip = confCur.Start >= id
		})()
		if isSkip {
			lr.print("[skip]")
			continue
		}

		startTime := time.Now()
		resp, err := client.Do(req)
		timeUsed := time.Now().Sub(startTime)

		lr.addNotice("used_ms", float64(timeUsed.Nanoseconds())/1e6)

		lr.addNotice("client_err", err)

		if resp != nil && resp.Body != nil {
			defer resp.Body.Close()
		}

		if err == nil {
			if resp.StatusCode == 200 {
				sucNum = 1
			} else {
				sucNum = 0
			}
			bd, r_err := ioutil.ReadAll(resp.Body)
			lr.addNotice("http_code", resp.StatusCode)
			lr.addNotice("resp_len", len(bd))
			lr.addNotice("resp_err", r_err)
			respStr = string(bd)
			if !*noResp {
				lr.addNotice("resp_body", respStr)
			}
			lr.print("info")
		} else {
			lr.addNotice("http_code", 0)
			lr.addNotice("resp_len", 0)
			lr.addNotice("resp_err", err)
			lr.print("wf")
		}
		speedData.Inc(1, len(respStr), sucNum)

		isOutRange := false
		(func() {
			rw.RLock()
			defer rw.RUnlock()
			isOutRange = workerId >= confCur.ConcMax
		})()
		if isOutRange {
			log.Println("[trace] urlCallWorker_close,workerId=", workerId)
			break
		}
	}
}

type logRequest struct {
	buf *bytes.Buffer
}

func (lr *logRequest) reset() {
	lr.buf.Reset()
}

func (lr *logRequest) addNotice(key string, val interface{}) {
	lr.buf.WriteString(key)
	lr.buf.WriteString("=")
	if val == nil {
		lr.buf.WriteString("nil")
	} else {
		lr.buf.WriteString(url.QueryEscape(fmt.Sprintf("%v", val)))
	}
	lr.buf.WriteString(" ")
}

func (lr *logRequest) print(ty string) {
	log.Printf("[%s] %s \n", ty, lr.buf.String())
}
<commit_msg>fix url_call_conc bug<commit_after>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/hidu/go-speed"
	"github.com/hidu/goutils/log_util"
	"io"
	"io/ioutil"
	glog "log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

//import _ "net/http/pprof"

var version = "0.1.4 20161115"

var conc = flag.Uint("c", 10, "Concurrent Num [conc]")
var timeout = flag.Int64("t", 10000, "Timeout,ms")
var method = flag.String("method", "GET", "HTTP Method")
var noResp = flag.Bool("nr", false, "No Respose (default false)")
var ua = flag.String("ua", "url_call_conc/"+version, "User-Agent")
var logPath = flag.String("log", "", "log file prex,default stderr")
var start = flag.Int("start", 0, "start item num")

var complex = flag.Bool("complex", false, `Complex Input (default false)`)

var v = flag.Bool("version", false, "Version:"+version)

var idx uint64

var jobs chan *http.Request

func init() {
	usage := flag.Usage
	flag.Usage = func() {
		usage()
		fmt.Println("\ncat urllist.txt|url_call_conc -c 100")
		fmt.Println("\n use dynamic conf [ ./url_call_conc.conf ] to change runtime params :")
		fmt.Println(`{"conc":10}`)
		fmt.Println("\n site: https://github.com/hidu/tool")
		fmt.Println(" version:", version)
	}
}

var speedData *speed.Speed

var timeOut time.Duration
var log *glog.Logger

var rw sync.RWMutex

type UrlCallConcConf struct {
	FromFile bool   `json:"from_file"`
	ConcMax  uint   `json:"conc"`
	Start    uint64 `json:"start"`
	//    QpsMax  uint `json:"qps"`
}

var confCur *UrlCallConcConf
var confLast *UrlCallConcConf

func (c *UrlCallConcConf) String() string {
	bf, _ := json.Marshal(c)
	return string(bf)
}

func getConf() *UrlCallConcConf {
	conf := &UrlCallConcConf{
		ConcMax: *conc,
		Start:   uint64(*start),
	}
	fname := "url_call_conc.conf"
	bs, err := ioutil.ReadFile(fname)
	if err != nil {
		//log.Println("[wf] read_conf failed", fname, "skip,", err.Error())
	} else {
		err := json.Unmarshal(bs, &conf)
		if err != nil {
			log.Println("[wf] parse_conf ", fname, "failed,skip,", err.Error())
		} else {
			conf.FromFile = true
		}
	}
	log.Println("[trace] now_conf is:", conf)
	return conf
}

func startWorkers() {
	if confCur.ConcMax > confLast.ConcMax {
		log.Println("[trace] startWorkers,last_conc=", confLast.ConcMax, "cur_conc=", confCur.ConcMax)
		for i := confLast.ConcMax; i < confCur.ConcMax; i++ {
			go urlCallWorker(jobs, i)
		}
	}
}

var workerRunning int64 = 0

func main() {
	flag.Parse()
	if *v {
		fmt.Println(version)
		return
	}

	if *ua == "mac" {
		*ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.1103.21"
	}

	//	go func() {
	//		http.ListenAndServe("localhost:6060", nil)
	//	}()

	log = glog.New(os.Stderr, "", glog.LstdFlags)
	if *logPath != "" {
		log_util.SetLogFile(log, *logPath, log_util.LOG_TYPE_HOUR)
	}
	startTime := time.Now()
	timeOut = time.Duration(*timeout) * time.Millisecond

	speedData = speed.NewSpeed("call", 5, func(msg string, sp *speed.Speed) {
		n := atomic.LoadInt64(&workerRunning)
		log.Println("speed", msg, "running_workers=", n)
	})

	confCur = getConf()
	confLast = &UrlCallConcConf{}

	var urlStr = strings.TrimSpace(flag.Arg(0))

	jobs = make(chan *http.Request, 1024)

	startWorkers()

	ticker := time.NewTicker(30 * time.Second)

	go func() {
		for range ticker.C {
			(func() {
				confTmp := getConf()
				if confTmp.String() != confCur.String() {
					rw.Lock()
					defer rw.Unlock()
					confLast = confCur
					confCur = confTmp
					startWorkers()
				}
			})()
		}
	}()

	if urlStr == "" {
		if *complex {
			parseComplexStdIn()
		} else {
			parseSimpleStdIn()
		}
	} else {
		req, err := http.NewRequest(strings.ToUpper(*method), urlStr, nil)
		if err != nil {
			log.Println(err)
		} else {
			req.Header.Set("User-Agent", *ua)
			jobs <- req
		}
	}
	close(jobs)

	time.Sleep(timeOut + 500*time.Millisecond)
	var num int64 = 1
	for num > 0 {
		num = atomic.LoadInt64(&workerRunning)
		log.Println("[trace] e_running_worker_total=", workerRunning, "waiting...")
		time.Sleep(1 * time.Second)
	}
	speedData.Stop()

	timeUsed := time.Now().Sub(startTime)

	log.Println("[info]done total:", idx, "used:", timeUsed)
}

func parseSimpleStdIn() {
	var urlStr string
	buf := bufio.NewReaderSize(os.Stdin, 81920)
	for {
		line, err := buf.ReadBytes('\n')
		if len(line) > 0 {
			urlStr = strings.TrimSpace(string(line))
			if urlStr == "" {
				continue
			}
			req, err := http.NewRequest(strings.ToUpper(*method), urlStr, nil)
			if err != nil {
				log.Println("[wf]", urlStr, "err:", err)
				continue
			}
			req.Header.Set("User-Agent", *ua)
			jobs <- req
		}
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Println("[wf]", string(line), "err:", err)
		}
	}
}

func parseComplexStdIn() {
	buf := bufio.NewReaderSize(os.Stdin, 81920)
	for {
		headLen, err := buf.ReadString('|')
		if err == io.EOF {
			break
		}
		hl, err := strconv.Atoi(headLen[0 : len(headLen)-1])
		if err != nil {
			log.Fatalln("[fat] read head length faild:", headLen)
		}
		headBf := make([]byte, hl)
		buf.Read(headBf)

		var hdObj *Head
		err = json.Unmarshal(headBf, &hdObj)
		if err != nil {
			log.Fatalln("[fat] parse head faild:", headLen)
		}

		bodyLen, err := buf.ReadString('|')
		if err != nil {
			log.Fatalln("[fat] read body length faild:", err)
		}
		bl, err := strconv.Atoi(bodyLen[0 : len(bodyLen)-1])
		if err != nil {
			log.Fatalln("[fat] parse body length faild:", headLen)
		}

		var body io.Reader

		if bl > 0 {
			bodyBf := make([]byte, bl)
			buf.Read(bodyBf)
			body = bytes.NewReader(bodyBf)
		}
		if hdObj.Method == "" {
			hdObj.Method = "GET"
		}
		req, err := http.NewRequest(strings.ToUpper(hdObj.Method), hdObj.Url, body)
		if err != nil {
			log.Fatalln("[wf]", hdObj, "err:", err)
		}
		req.Header.Set("User-Agent", *ua)
		if hdObj.Header != nil {
			for k, v := range hdObj.Header {
				req.Header.Add(k, v)
			}
		}

		jobs <- req
	}
}

type Head struct {
	Url    string            `json:"url"`
	Method string            `json:"method"`
	Header map[string]string `json:"header"`
}

func urlCallWorker(jobs <-chan *http.Request, workerId uint) {
	_runningNum := atomic.AddInt64(&workerRunning, 1)
	defer func() {
		num := atomic.AddInt64(&workerRunning, -1)
		log.Println("[trace] this worker closed,running_worker_total=", num)
	}()

	log.Println("[trace] urlCallWorker_start,worker_id=", workerId, "running_worker_total=", _runningNum)
	client := &http.Client{
		Timeout: timeOut,
	}
	var respStr string
	var sucNum int
	lr := &logRequest{
		buf: new(bytes.Buffer),
	}
	isSkip := false
	isOutRange := false
	var errOutOfRange = fmt.Errorf("urlCallWorker_OutOfRange")

	var dealRequest = func(req *http.Request) error {
		id := atomic.AddUint64(&idx, 1)
		urlStr := req.URL.String()
		lr.reset()

		lr.addNotice("id", id)
		lr.addNotice("worker_id", workerId)
		lr.addNotice("method", req.Method)
		lr.addNotice("url", urlStr)

		(func() {
			rw.RLock()
			defer rw.RUnlock()
			isSkip = confCur.Start >= id
			isOutRange = workerId >= confCur.ConcMax
		})()

		if isSkip {
			lr.print("[skip]")
			return nil
		}

		startTime := time.Now()
		resp, err := client.Do(req)
		timeUsed := time.Now().Sub(startTime)

		if resp != nil && resp.Body != nil {
			defer resp.Body.Close()
		}

		lr.addNotice("used_ms", float64(timeUsed.Nanoseconds())/1e6)

		lr.addNotice("client_err", err)

		if err == nil {
			if resp.StatusCode == 200 {
				sucNum = 1
			} else {
				sucNum = 0
			}
			bd, r_err := ioutil.ReadAll(resp.Body)
			lr.addNotice("http_code", resp.StatusCode)
			lr.addNotice("resp_len", len(bd))
			lr.addNotice("resp_err", r_err)
			respStr = string(bd)
			if !*noResp {
				lr.addNotice("resp_body", respStr)
			}
			lr.print("info")
		} else {
			lr.addNotice("http_code", 0)
			lr.addNotice("resp_len", 0)
			lr.addNotice("resp_err", err)
			lr.print("wf")
		}

		speedData.Inc(1, len(respStr), sucNum)

		if isOutRange {
			return errOutOfRange
		}
		return nil
	}

	for req := range jobs {
		err := dealRequest(req)

		if err == errOutOfRange {
			log.Println("[trace]", err.Error(), "close_worker,workerId=", workerId)
			break
		}
	}
}

type logRequest struct {
	buf *bytes.Buffer
}

func (lr *logRequest) reset() {
	lr.buf.Reset()
}

func (lr *logRequest) addNotice(key string, val interface{}) {
	lr.buf.WriteString(key)
	lr.buf.WriteString("=")
	if val == nil {
		lr.buf.WriteString("nil")
	} else {
		lr.buf.WriteString(url.QueryEscape(fmt.Sprintf("%v", val)))
	}
	lr.buf.WriteString(" ")
}

func (lr *logRequest) print(ty string) {
	log.Printf("[%s] %s \n", ty, lr.buf.String())
}
<|endoftext|>


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

// Package static exports a map of static file content that supports the godoc
// user interface. The map should be used with the mapfs package, see
// code.google.com/p/godoc/vfs/mapfs.
package static
<commit_msg>go.tools/godoc/static: fix import path doc typo<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package static exports a map of static file content that supports the godoc
// user interface. The map should be used with the mapfs package, see
// code.google.com/p/go.tools/godoc/vfs/mapfs.
package static
<|endoftext|>


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

// +build appengine

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"net/http"

	"appengine"
	"appengine/urlfetch"
)

const runUrl = "http://golang.org/compile"

func init() {
	http.HandleFunc("/", rootHandler)
	http.HandleFunc("/compile", compileHandler)
	err := serveScripts("js", "playground.js")
	if err != nil {
		panic(err)
	}
	if err := initTour("."); err != nil {
		panic(err)
	}
}

func rootHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	if err := renderTour(w); err != nil {
		c.Criticalf("template render: %v", err)
	}
}

func compileHandler(w http.ResponseWriter, r *http.Request) {
	if err := passThru(w, r); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, "Compile server error.")
	}
}

func passThru(w io.Writer, req *http.Request) error {
	c := appengine.NewContext(req)
	client := urlfetch.Client(c)
	defer req.Body.Close()
	r, err := client.Post(runUrl, req.Header.Get("Content-type"), req.Body)
	if err != nil {
		c.Errorf("making POST request:", err)
		return err
	}
	defer r.Body.Close()
	if _, err := io.Copy(w, r.Body); err != nil {
		c.Errorf("copying response Body:", err)
		return err
	}
	return nil
}

// prepContent returns a Reader that produces the content from the given
// Reader, but strips the prefix "#appengine: " from each line. It also drops
// any non-blank like that follows a series of 1 or more lines with the prefix.
func prepContent(in io.Reader) io.Reader {
	var prefix = []byte("#appengine: ")
	out, w := io.Pipe()
	go func() {
		r := bufio.NewReader(in)
		drop := false
		for {
			b, err := r.ReadBytes('\n')
			if err != nil && err != io.EOF {
				w.CloseWithError(err)
				return
			}
			if bytes.HasPrefix(b, prefix) {
				b = b[len(prefix):]
				drop = true
			} else if drop {
				if len(b) > 1 {
					b = nil
				}
				drop = false
			}
			if len(b) > 0 {
				w.Write(b)
			}
			if err == io.EOF {
				w.Close()
				return
			}
		}
	}()
	return out
}

// socketAddr returns the WebSocket handler address.
// The App Engine version does not provide a WebSocket handler.
func socketAddr() string { return "" }
<commit_msg>go-tour: use playground package from go.talks for /compile handler<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build appengine

package main

import (
	"bufio"
	"bytes"
	"io"
	"net/http"

	"appengine"

	_ "code.google.com/p/go.talks/pkg/playground"
)

const runUrl = "http://golang.org/compile"

func init() {
	http.HandleFunc("/", rootHandler)
	err := serveScripts("js", "playground.js")
	if err != nil {
		panic(err)
	}
	if err := initTour("."); err != nil {
		panic(err)
	}
}

func rootHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	if err := renderTour(w); err != nil {
		c.Criticalf("template render: %v", err)
	}
}

// prepContent returns a Reader that produces the content from the given
// Reader, but strips the prefix "#appengine: " from each line. It also drops
// any non-blank like that follows a series of 1 or more lines with the prefix.
func prepContent(in io.Reader) io.Reader {
	var prefix = []byte("#appengine: ")
	out, w := io.Pipe()
	go func() {
		r := bufio.NewReader(in)
		drop := false
		for {
			b, err := r.ReadBytes('\n')
			if err != nil && err != io.EOF {
				w.CloseWithError(err)
				return
			}
			if bytes.HasPrefix(b, prefix) {
				b = b[len(prefix):]
				drop = true
			} else if drop {
				if len(b) > 1 {
					b = nil
				}
				drop = false
			}
			if len(b) > 0 {
				w.Write(b)
			}
			if err == io.EOF {
				w.Close()
				return
			}
		}
	}()
	return out
}

// socketAddr returns the WebSocket handler address.
// The App Engine version does not provide a WebSocket handler.
func socketAddr() string { return "" }
<|endoftext|>


<commit_before>package handlers

import (
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"github.com/nelsonleduc/calmanbot/cache"
	"github.com/nelsonleduc/calmanbot/handlers/models"
	"github.com/nelsonleduc/calmanbot/service"
)

const currentCalmanBotVersion string = "v2.16.0"

type builtinDescription struct {
	trigger     string
	description string
}

type builtInParams struct {
	bot     models.Bot
	cache   cache.QueryCache
	repo    models.Repo
	service service.Service
}

type builtin struct {
	builtinDescription
	featureChecker func(service.Service) bool
	handler        func([]string, builtInParams) string
}

// Descriptions
var helpDescription = builtinDescription{
	"(help)",
	"See this",
}
var topDescription = builtinDescription{
	"(top)",
	"List top 10 liked posts",
}
var showDescription = builtinDescription{
	"show (10|[1-9])(?:$| )",
	"Repost nth top post",
}

var versionDescription = builtinDescription{
	"(version)",
	"Display current version",
}

var builtins []builtin

func init() {
	builtins = []builtin{
		{
			helpDescription,
			noFeatureCheck,
			responseForHelp,
		},
		{
			topDescription,
			leaderBoardFeatureCheck,
			responseForLeaderboard,
		},
		{
			showDescription,
			leaderBoardFeatureCheck,
			responseForShow,
		},
		{
			versionDescription,
			noFeatureCheck,
			responseForVersion,
		},
	}
}

// Handlers
func responseForLeaderboard(matched []string, params builtInParams) string {
	entries := params.cache.LeaderboardEntries(params.bot.GroupID, 10)
	leaderboardAccumulatr := "Top posts:"
	for _, e := range entries {
		leaderboardAccumulatr += "\n" + strconv.Itoa(e.LikeCount) + "    " + e.Query
	}

	return leaderboardAccumulatr
}

func responseForShow(matched []string, params builtInParams) string {
	entries := params.cache.LeaderboardEntries(params.bot.GroupID, 10)
	num, error := strconv.Atoi(matched[1])
	num--
	if len(entries) <= num || error != nil {
		return "There is nothing to display"
	}

	return entries[num].Result
}

func responseForVersion(matched []string, params builtInParams) string {
	return "I'm currently running " + currentCalmanBotVersion
}

func responseForHelp(matched []string, params builtInParams) string {
	_, e := params.service.ServiceTriggerWrangler()
	actions, _ := params.repo.FetchActions(true, e == nil)
	sort.Sort(models.ByPriority(actions))
	botName := params.bot.SanitizedBotNames()[0]

	helpAccumulator := "```Commands:"
	longest := 0
	for _, a := range actions {
		if a.Description == nil || a.Pattern == nil {
			continue
		}

		if len(*a.Pattern) > longest {
			longest = len(*a.Pattern)
		}
	}
	for _, b := range builtins {
		if !b.featureChecker(params.service) {
			continue
		}
		length := len("@" + botName + " !" + b.trigger)
		if length > longest {
			longest = length
		}
	}
	paddingFmt := fmt.Sprintf("%%-%ds", longest+2)

	for _, a := range actions {
		if a.Description == nil || a.Pattern == nil {
			continue
		}
		printablePattern := *a.Pattern
		printablePattern = strings.Replace(printablePattern, "{_botname_}", botName, -1)
		re := regexp.MustCompile(`^\[(.)\]`)
		matched := re.FindStringSubmatch(printablePattern)
		thing := ""
		if len(matched) > 1 && matched[1] != "" {
			thing = matched[1]
		}
		printablePattern = re.ReplaceAllLiteralString(printablePattern, thing)

		helpAccumulator += "\n" + fmt.Sprintf(paddingFmt, printablePattern) + "\n\t" + *a.Description
	}
	for _, b := range builtins {
		if !b.featureChecker(params.service) {
			continue
		}
		printablePattern := "@" + botName + " !" + b.trigger
		helpAccumulator += "\n" + fmt.Sprintf(paddingFmt, printablePattern) + "\n\t" + b.description
	}

	return helpAccumulator + "```"
}

// Feature checker
func noFeatureCheck(s service.Service) bool {
	return true
}

func leaderBoardFeatureCheck(s service.Service) bool {
	return s.SupportsBuiltInFeature(service.BuiltInFeatureLeaderboard)
}
<commit_msg>Update version to v2.16.1<commit_after>package handlers

import (
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"github.com/nelsonleduc/calmanbot/cache"
	"github.com/nelsonleduc/calmanbot/handlers/models"
	"github.com/nelsonleduc/calmanbot/service"
)

const currentCalmanBotVersion string = "v2.16.1"

type builtinDescription struct {
	trigger     string
	description string
}

type builtInParams struct {
	bot     models.Bot
	cache   cache.QueryCache
	repo    models.Repo
	service service.Service
}

type builtin struct {
	builtinDescription
	featureChecker func(service.Service) bool
	handler        func([]string, builtInParams) string
}

// Descriptions
var helpDescription = builtinDescription{
	"(help)",
	"See this",
}
var topDescription = builtinDescription{
	"(top)",
	"List top 10 liked posts",
}
var showDescription = builtinDescription{
	"show (10|[1-9])(?:$| )",
	"Repost nth top post",
}

var versionDescription = builtinDescription{
	"(version)",
	"Display current version",
}

var builtins []builtin

func init() {
	builtins = []builtin{
		{
			helpDescription,
			noFeatureCheck,
			responseForHelp,
		},
		{
			topDescription,
			leaderBoardFeatureCheck,
			responseForLeaderboard,
		},
		{
			showDescription,
			leaderBoardFeatureCheck,
			responseForShow,
		},
		{
			versionDescription,
			noFeatureCheck,
			responseForVersion,
		},
	}
}

// Handlers
func responseForLeaderboard(matched []string, params builtInParams) string {
	entries := params.cache.LeaderboardEntries(params.bot.GroupID, 10)
	leaderboardAccumulatr := "Top posts:"
	for _, e := range entries {
		leaderboardAccumulatr += "\n" + strconv.Itoa(e.LikeCount) + "    " + e.Query
	}

	return leaderboardAccumulatr
}

func responseForShow(matched []string, params builtInParams) string {
	entries := params.cache.LeaderboardEntries(params.bot.GroupID, 10)
	num, error := strconv.Atoi(matched[1])
	num--
	if len(entries) <= num || error != nil {
		return "There is nothing to display"
	}

	return entries[num].Result
}

func responseForVersion(matched []string, params builtInParams) string {
	return "I'm currently running " + currentCalmanBotVersion
}

func responseForHelp(matched []string, params builtInParams) string {
	_, e := params.service.ServiceTriggerWrangler()
	actions, _ := params.repo.FetchActions(true, e == nil)
	sort.Sort(models.ByPriority(actions))
	botName := params.bot.SanitizedBotNames()[0]

	helpAccumulator := "```Commands:"
	longest := 0
	for _, a := range actions {
		if a.Description == nil || a.Pattern == nil {
			continue
		}

		if len(*a.Pattern) > longest {
			longest = len(*a.Pattern)
		}
	}
	for _, b := range builtins {
		if !b.featureChecker(params.service) {
			continue
		}
		length := len("@" + botName + " !" + b.trigger)
		if length > longest {
			longest = length
		}
	}
	paddingFmt := fmt.Sprintf("%%-%ds", longest+2)

	for _, a := range actions {
		if a.Description == nil || a.Pattern == nil {
			continue
		}
		printablePattern := *a.Pattern
		printablePattern = strings.Replace(printablePattern, "{_botname_}", botName, -1)
		re := regexp.MustCompile(`^\[(.)\]`)
		matched := re.FindStringSubmatch(printablePattern)
		thing := ""
		if len(matched) > 1 && matched[1] != "" {
			thing = matched[1]
		}
		printablePattern = re.ReplaceAllLiteralString(printablePattern, thing)

		helpAccumulator += "\n" + fmt.Sprintf(paddingFmt, printablePattern) + "\n\t" + *a.Description
	}
	for _, b := range builtins {
		if !b.featureChecker(params.service) {
			continue
		}
		printablePattern := "@" + botName + " !" + b.trigger
		helpAccumulator += "\n" + fmt.Sprintf(paddingFmt, printablePattern) + "\n\t" + b.description
	}

	return helpAccumulator + "```"
}

// Feature checker
func noFeatureCheck(s service.Service) bool {
	return true
}

func leaderBoardFeatureCheck(s service.Service) bool {
	return s.SupportsBuiltInFeature(service.BuiltInFeatureLeaderboard)
}
<|endoftext|>