<commit_before>package peco

import (
	"fmt"
	"os/exec"
	"regexp"
	"sort"
	"strings"
)

// Match defines the interface for matches. Note that to make drawing easier,
// we have a DidMatch and NoMatch types instead of using []Match and []string.
type Match interface {
	Buffer() string // Raw buffer, may contain null
	Line() string   // Line to be displayed
	Output() string // Output string to be displayed after peco is done
	Indices() [][]int
}

type MatchString struct {
	buf    string
	sepLoc int
}

func NewMatchString(v string, enableSep bool) *MatchString {
	m := &MatchString{
		v,
		-1,
	}
	if !enableSep {
		return m
	}

	// XXX This may be silly, but we're avoiding using strings.IndexByte()
	// here because it doesn't exist on go1.1. Let's remove support for
	// 1.1 when 1.4 comes out (or something)
	for i := 0; i < len(m.buf); i++ {
		if m.buf[i] == '\000' {
			m.sepLoc = i
		}
	}
	return m
}

func (m MatchString) Buffer() string {
	return m.buf
}

func (m MatchString) Line() string {
	if i := m.sepLoc; i > -1 {
		return m.buf[:i]
	}
	return m.buf
}

func (m MatchString) Output() string {
	if i := m.sepLoc; i > -1 {
		return m.buf[i+1:]
	}
	return m.buf
}

// NoMatch is actually an alias to a regular string. It implements the
// Match interface, but just returns the underlying string with no matches
type NoMatch struct {
	*MatchString
}

func NewNoMatch(v string, enableSep bool) *NoMatch {
	return &NoMatch{NewMatchString(v, enableSep)}
}

func (m NoMatch) Indices() [][]int {
	return nil
}

// DidMatch contains the actual match, and the indices to the matches
// in the line
type DidMatch struct {
	*MatchString
	matches [][]int
}

func NewDidMatch(v string, enableSep bool, m [][]int) *DidMatch {
	return &DidMatch{NewMatchString(v, enableSep), m}
}

func (d DidMatch) Indices() [][]int {
	return d.matches
}

// Matcher interface defines the API for things that want to
// match against the buffer
type Matcher interface {
	// Match takes in three parameters.
	//
	// The first chan is the channel where cancel requests are sent.
	// If you receive a request here, you should stop running your query.
	//
	// The second is the query. Do what you want with it
	//
	// The third is the buffer in which to match the query against.
	Match(chan struct{}, string, []Match) []Match
	String() string
}

const (
	IgnoreCaseMatch    = "IgnoreCase"
	CaseSensitiveMatch = "CaseSensitive"
	RegexpMatch        = "Regexp"
)

type RegexpMatcher struct {
	enableSep bool
	flags     []string
	quotemeta bool
}

type CaseSensitiveMatcher struct {
	*RegexpMatcher
}

type IgnoreCaseMatcher struct {
	*RegexpMatcher
}

type CustomMatcher struct {
	enableSep bool
	name      string
	args      []string
}

func NewCaseSensitiveMatcher(enableSep bool) *CaseSensitiveMatcher {
	m := &CaseSensitiveMatcher{NewRegexpMatcher(enableSep)}
	m.quotemeta = true
	return m
}

func NewIgnoreCaseMatcher(enableSep bool) *IgnoreCaseMatcher {
	m := &IgnoreCaseMatcher{NewRegexpMatcher(enableSep)}
	m.flags = []string{"i"}
	m.quotemeta = true
	return m
}

func NewRegexpMatcher(enableSep bool) *RegexpMatcher {
	return &RegexpMatcher{
		enableSep,
		[]string{},
		false,
	}
}

func NewCustomMatcher(enableSep bool, name string, args []string) *CustomMatcher {
	return &CustomMatcher{enableSep, name, args}
}

func regexpFor(q string, flags []string, quotemeta bool) (*regexp.Regexp, error) {
	reTxt := q
	if quotemeta {
		reTxt = regexp.QuoteMeta(q)
	}

	if flags != nil && len(flags) > 0 {
		reTxt = fmt.Sprintf("(?%s)%s", strings.Join(flags, ""), reTxt)
	}

	re, err := regexp.Compile(reTxt)
	if err != nil {
		return nil, err
	}
	return re, nil
}

func (m *RegexpMatcher) QueryToRegexps(query string) ([]*regexp.Regexp, error) {
	queries := strings.Split(strings.TrimSpace(query), " ")
	regexps := make([]*regexp.Regexp, 0)

	for _, q := range queries {
		re, err := regexpFor(q, m.flags, m.quotemeta)
		if err != nil {
			return nil, err
		}
		regexps = append(regexps, re)
	}

	return regexps, nil
}

func (m *RegexpMatcher) String() string {
	return "Regexp"
}

func (m *CaseSensitiveMatcher) String() string {
	return "CaseSensitive"
}

func (m *IgnoreCaseMatcher) String() string {
	return "IgnoreCase"
}

func (m *CustomMatcher) String() string {
	return m.name
}

// sort related stuff
type byStart [][]int

func (m byStart) Len() int {
	return len(m)
}

func (m byStart) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

func (m byStart) Less(i, j int) bool {
	return m[i][0] < m[j][0]
}

func (m *RegexpMatcher) Match(quit chan struct{}, q string, buffer []Match) []Match {
	results := []Match{}
	regexps, err := m.QueryToRegexps(q)
	if err != nil {
		return results
	}

	// The actual matching is done in a separate goroutine
	iter := make(chan Match, len(buffer))
	go func() {
		// This protects us from panics, caused when we cancel the
		// query and forcefully close the channel (and thereby
		// causing a "close of a closed channel"
		defer func() { recover() }()

		// This must be here to make sure the channel is properly
		// closed in normal cases
		defer close(iter)

		// Iterate through the lines, and do the match.
		// Upon success, send it through the channel
		for _, match := range buffer {
			ms := m.MatchAllRegexps(regexps, match.Line())
			if ms == nil {
				continue
			}
			iter <- NewDidMatch(match.Buffer(), m.enableSep, ms)
		}
		iter <- nil
	}()

MATCH:
	for {
		select {
		case <-quit:
			// If we recieved a cancel request, we immediately bail out.
			// It's a little dirty, but we focefully terminate the other
			// goroutine by closing the channel, and invoking a panic in the
			// goroutine above

			// There's a possibility that the match fails early and the
			// cancel happens after iter has been closed. It's totally okay
			// for us to try to close iter, but trying to detect if the
			// channel can be closed safely synchronously is really hard
			// so we punt it by letting the close() happen at a separate
			// goroutine, protected by a defer recover()
			go func() {
				defer func() { recover() }()
				close(iter)
			}()
			break MATCH
		case match := <-iter:
			// Receive elements from the goroutine performing the match
			if match == nil {
				break MATCH
			}

			results = append(results, match)
		}
	}
	return results
}

func (m *RegexpMatcher) MatchAllRegexps(regexps []*regexp.Regexp, line string) [][]int {
	matches := make([][]int, 0)

	allMatched := true
Match:
	for _, re := range regexps {
		match := re.FindAllStringSubmatchIndex(line, -1)
		if match == nil {
			allMatched = false
			break Match
		}

		for _, ma := range match {
			start, end := ma[0], ma[1]
			for _, m := range matches {
				if start >= m[0] && start < m[1] {
					continue Match
				}

				if start < m[0] && end >= m[0] {
					continue Match
				}
			}
			matches = append(matches, ma)
		}
	}

	if !allMatched {
		return nil
	}

	sort.Sort(byStart(matches))

	return matches
}

func (m *CustomMatcher) Match(quit chan struct{}, q string, buffer []Match) []Match {
	if len(m.args) < 1 {
		return []Match{}
	}

	results := []Match{}
	if q == "" {
		for _, match := range buffer {
			results = append(results, NewDidMatch(match.Buffer(), m.enableSep, nil))
		}
		return results
	}

	// Receive elements from the goroutine performing the match
	lines := []Match{}
	matcherInput := ""
	for _, match := range buffer {
		matcherInput += match.Line() + "\n"
		lines = append(lines, match)
	}
	args := []string{}
	for _, arg := range m.args {
		if arg == "$QUERY" {
			arg = q
		}
		args = append(args, arg)
	}
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = strings.NewReader(matcherInput)

	// See RegexpMatcher.Match() for explanation of constructs
	iter := make(chan Match, len(buffer))
	go func() {
		defer func() { recover() }()
		defer func() {
			close(iter)
			if p := cmd.Process; p != nil {
				p.Kill()
			}
		}()
		b, err := cmd.Output()
		if err != nil {
			iter <- nil
		}
		for _, line := range strings.Split(string(b), "\n") {
			if len(line) > 0 {
				iter <- NewDidMatch(line, m.enableSep, nil)
			}
		}
		iter <- nil
	}()
MATCH:
	for {
		select {
		case <-quit:
			go func() {
				defer func() { recover() }()
				close(iter)
			}()
			break MATCH
		case match := <-iter:
			if match == nil {
				break MATCH
			}
			results = append(results, match)
		}
	}

	return results
}
<commit_msg>move the close to AFTER the p.Kill()<commit_after>package peco

import (
	"fmt"
	"os/exec"
	"regexp"
	"sort"
	"strings"
)

// Match defines the interface for matches. Note that to make drawing easier,
// we have a DidMatch and NoMatch types instead of using []Match and []string.
type Match interface {
	Buffer() string // Raw buffer, may contain null
	Line() string   // Line to be displayed
	Output() string // Output string to be displayed after peco is done
	Indices() [][]int
}

type MatchString struct {
	buf    string
	sepLoc int
}

func NewMatchString(v string, enableSep bool) *MatchString {
	m := &MatchString{
		v,
		-1,
	}
	if !enableSep {
		return m
	}

	// XXX This may be silly, but we're avoiding using strings.IndexByte()
	// here because it doesn't exist on go1.1. Let's remove support for
	// 1.1 when 1.4 comes out (or something)
	for i := 0; i < len(m.buf); i++ {
		if m.buf[i] == '\000' {
			m.sepLoc = i
		}
	}
	return m
}

func (m MatchString) Buffer() string {
	return m.buf
}

func (m MatchString) Line() string {
	if i := m.sepLoc; i > -1 {
		return m.buf[:i]
	}
	return m.buf
}

func (m MatchString) Output() string {
	if i := m.sepLoc; i > -1 {
		return m.buf[i+1:]
	}
	return m.buf
}

// NoMatch is actually an alias to a regular string. It implements the
// Match interface, but just returns the underlying string with no matches
type NoMatch struct {
	*MatchString
}

func NewNoMatch(v string, enableSep bool) *NoMatch {
	return &NoMatch{NewMatchString(v, enableSep)}
}

func (m NoMatch) Indices() [][]int {
	return nil
}

// DidMatch contains the actual match, and the indices to the matches
// in the line
type DidMatch struct {
	*MatchString
	matches [][]int
}

func NewDidMatch(v string, enableSep bool, m [][]int) *DidMatch {
	return &DidMatch{NewMatchString(v, enableSep), m}
}

func (d DidMatch) Indices() [][]int {
	return d.matches
}

// Matcher interface defines the API for things that want to
// match against the buffer
type Matcher interface {
	// Match takes in three parameters.
	//
	// The first chan is the channel where cancel requests are sent.
	// If you receive a request here, you should stop running your query.
	//
	// The second is the query. Do what you want with it
	//
	// The third is the buffer in which to match the query against.
	Match(chan struct{}, string, []Match) []Match
	String() string
}

const (
	IgnoreCaseMatch    = "IgnoreCase"
	CaseSensitiveMatch = "CaseSensitive"
	RegexpMatch        = "Regexp"
)

type RegexpMatcher struct {
	enableSep bool
	flags     []string
	quotemeta bool
}

type CaseSensitiveMatcher struct {
	*RegexpMatcher
}

type IgnoreCaseMatcher struct {
	*RegexpMatcher
}

type CustomMatcher struct {
	enableSep bool
	name      string
	args      []string
}

func NewCaseSensitiveMatcher(enableSep bool) *CaseSensitiveMatcher {
	m := &CaseSensitiveMatcher{NewRegexpMatcher(enableSep)}
	m.quotemeta = true
	return m
}

func NewIgnoreCaseMatcher(enableSep bool) *IgnoreCaseMatcher {
	m := &IgnoreCaseMatcher{NewRegexpMatcher(enableSep)}
	m.flags = []string{"i"}
	m.quotemeta = true
	return m
}

func NewRegexpMatcher(enableSep bool) *RegexpMatcher {
	return &RegexpMatcher{
		enableSep,
		[]string{},
		false,
	}
}

func NewCustomMatcher(enableSep bool, name string, args []string) *CustomMatcher {
	return &CustomMatcher{enableSep, name, args}
}

func regexpFor(q string, flags []string, quotemeta bool) (*regexp.Regexp, error) {
	reTxt := q
	if quotemeta {
		reTxt = regexp.QuoteMeta(q)
	}

	if flags != nil && len(flags) > 0 {
		reTxt = fmt.Sprintf("(?%s)%s", strings.Join(flags, ""), reTxt)
	}

	re, err := regexp.Compile(reTxt)
	if err != nil {
		return nil, err
	}
	return re, nil
}

func (m *RegexpMatcher) QueryToRegexps(query string) ([]*regexp.Regexp, error) {
	queries := strings.Split(strings.TrimSpace(query), " ")
	regexps := make([]*regexp.Regexp, 0)

	for _, q := range queries {
		re, err := regexpFor(q, m.flags, m.quotemeta)
		if err != nil {
			return nil, err
		}
		regexps = append(regexps, re)
	}

	return regexps, nil
}

func (m *RegexpMatcher) String() string {
	return "Regexp"
}

func (m *CaseSensitiveMatcher) String() string {
	return "CaseSensitive"
}

func (m *IgnoreCaseMatcher) String() string {
	return "IgnoreCase"
}

func (m *CustomMatcher) String() string {
	return m.name
}

// sort related stuff
type byStart [][]int

func (m byStart) Len() int {
	return len(m)
}

func (m byStart) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

func (m byStart) Less(i, j int) bool {
	return m[i][0] < m[j][0]
}

func (m *RegexpMatcher) Match(quit chan struct{}, q string, buffer []Match) []Match {
	results := []Match{}
	regexps, err := m.QueryToRegexps(q)
	if err != nil {
		return results
	}

	// The actual matching is done in a separate goroutine
	iter := make(chan Match, len(buffer))
	go func() {
		// This protects us from panics, caused when we cancel the
		// query and forcefully close the channel (and thereby
		// causing a "close of a closed channel"
		defer func() { recover() }()

		// This must be here to make sure the channel is properly
		// closed in normal cases
		defer close(iter)

		// Iterate through the lines, and do the match.
		// Upon success, send it through the channel
		for _, match := range buffer {
			ms := m.MatchAllRegexps(regexps, match.Line())
			if ms == nil {
				continue
			}
			iter <- NewDidMatch(match.Buffer(), m.enableSep, ms)
		}
		iter <- nil
	}()

MATCH:
	for {
		select {
		case <-quit:
			// If we recieved a cancel request, we immediately bail out.
			// It's a little dirty, but we focefully terminate the other
			// goroutine by closing the channel, and invoking a panic in the
			// goroutine above

			// There's a possibility that the match fails early and the
			// cancel happens after iter has been closed. It's totally okay
			// for us to try to close iter, but trying to detect if the
			// channel can be closed safely synchronously is really hard
			// so we punt it by letting the close() happen at a separate
			// goroutine, protected by a defer recover()
			go func() {
				defer func() { recover() }()
				close(iter)
			}()
			break MATCH
		case match := <-iter:
			// Receive elements from the goroutine performing the match
			if match == nil {
				break MATCH
			}

			results = append(results, match)
		}
	}
	return results
}

func (m *RegexpMatcher) MatchAllRegexps(regexps []*regexp.Regexp, line string) [][]int {
	matches := make([][]int, 0)

	allMatched := true
Match:
	for _, re := range regexps {
		match := re.FindAllStringSubmatchIndex(line, -1)
		if match == nil {
			allMatched = false
			break Match
		}

		for _, ma := range match {
			start, end := ma[0], ma[1]
			for _, m := range matches {
				if start >= m[0] && start < m[1] {
					continue Match
				}

				if start < m[0] && end >= m[0] {
					continue Match
				}
			}
			matches = append(matches, ma)
		}
	}

	if !allMatched {
		return nil
	}

	sort.Sort(byStart(matches))

	return matches
}

func (m *CustomMatcher) Match(quit chan struct{}, q string, buffer []Match) []Match {
	if len(m.args) < 1 {
		return []Match{}
	}

	results := []Match{}
	if q == "" {
		for _, match := range buffer {
			results = append(results, NewDidMatch(match.Buffer(), m.enableSep, nil))
		}
		return results
	}

	// Receive elements from the goroutine performing the match
	lines := []Match{}
	matcherInput := ""
	for _, match := range buffer {
		matcherInput += match.Line() + "\n"
		lines = append(lines, match)
	}
	args := []string{}
	for _, arg := range m.args {
		if arg == "$QUERY" {
			arg = q
		}
		args = append(args, arg)
	}
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = strings.NewReader(matcherInput)

	// See RegexpMatcher.Match() for explanation of constructs
	iter := make(chan Match, len(buffer))
	go func() {
		defer func() { recover() }()
		defer func() {
			if p := cmd.Process; p != nil {
				p.Kill()
			}
			close(iter)
		}()
		b, err := cmd.Output()
		if err != nil {
			iter <- nil
		}
		for _, line := range strings.Split(string(b), "\n") {
			if len(line) > 0 {
				iter <- NewDidMatch(line, m.enableSep, nil)
			}
		}
		iter <- nil
	}()
MATCH:
	for {
		select {
		case <-quit:
			go func() {
				defer func() { recover() }()
				close(iter)
			}()
			break MATCH
		case match := <-iter:
			if match == nil {
				break MATCH
			}
			results = append(results, match)
		}
	}

	return results
}
<|endoftext|>


<commit_before>package peco

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

// Match defines the interface for matches. Note that to make drawing easier,
// we have a DidMatch and NoMatch types instead of using []Match and []string.
type Match interface {
	Buffer() string // Raw buffer, may contain null
	Line() string // Line to be displayed
	Output() string // Output string to be displayed after peco is done
	Indices() [][]int
}

type MatchString struct {
	buf string
	sepLoc int
}

func NewMatchString(v string, enableSep bool) *MatchString {
	m := &MatchString{
		v,
		-1,
	}
	if !enableSep {
		return m
	}

	// XXX This may be silly, but we're avoiding using strings.IndexByte()
	// here because it doesn't exist on go1.1. Let's remove support for
	// 1.1 when 1.4 comes out (or something)
	for i := 0; i < len(m.buf); i++ {
		if m.buf[i] == '\000' {
			m.sepLoc = i
		}
	}
	return m
}

func (m MatchString) Buffer() string {
	return m.buf
}

func (m MatchString) Line() string {
	if i := m.sepLoc; i > -1 {
		return m.buf[:i]
	}
	return m.buf
}

func (m MatchString) Output() string {
	if i := m.sepLoc; i > -1 {
		return m.buf[i:]
	}
	return m.buf
}

// NoMatch is actually an alias to a regular string. It implements the
// Match interface, but just returns the underlying string with no matches
type NoMatch struct {
	*MatchString
}

func NewNoMatch(v string, enableSep bool) *NoMatch {
	return &NoMatch{NewMatchString(v, enableSep)}
}

func (m NoMatch) Indices() [][]int {
	return nil
}

// DidMatch contains the actual match, and the indices to the matches
// in the line
type DidMatch struct {
	*MatchString
	matches [][]int
}

func NewDidMatch(v string, enableSep bool, m [][]int) *DidMatch {
	return &DidMatch{NewMatchString(v, enableSep), m}
}

func (d DidMatch) Indices() [][]int {
	return d.matches
}

// Matcher interface defines the API for things that want to
// match against the buffer
type Matcher interface {
	Match(string, []Match) []Match
	String() string
}

const (
	IgnoreCaseMatch    = "IgnoreCase"
	CaseSensitiveMatch = "CaseSensitive"
	RegexpMatch        = "Regexp"
)

type RegexpMatcher struct {
	enableSep bool
	flags     []string
	quotemeta bool
}

type CaseSensitiveMatcher struct {
	*RegexpMatcher
}

type IgnoreCaseMatcher struct {
	*RegexpMatcher
}

func NewCaseSensitiveMatcher(enableSep bool) *CaseSensitiveMatcher {
	m := &CaseSensitiveMatcher{NewRegexpMatcher(enableSep)}
	m.quotemeta = true
	return m
}

func NewIgnoreCaseMatcher(enableSep bool) *IgnoreCaseMatcher {
	m := &IgnoreCaseMatcher{NewRegexpMatcher(enableSep)}
	m.flags = []string{"i"}
	m.quotemeta = true
	return m
}

func NewRegexpMatcher(enableSep bool) *RegexpMatcher {
	return &RegexpMatcher{
		enableSep,
		[]string{},
		false,
	}
}

func regexpFor(q string, flags []string, quotemeta bool) (*regexp.Regexp, error) {
	reTxt := q
	if quotemeta {
		reTxt = regexp.QuoteMeta(q)
	}

	if flags != nil && len(flags) > 0 {
		reTxt = fmt.Sprintf("(?%s)%s", strings.Join(flags, ""), reTxt)
	}

	re, err := regexp.Compile(reTxt)
	if err != nil {
		return nil, err
	}
	return re, nil
}

func (m *RegexpMatcher) QueryToRegexps(query string) ([]*regexp.Regexp, error) {
	queries := strings.Split(strings.TrimSpace(query), " ")
	regexps := make([]*regexp.Regexp, 0)

	for _, q := range queries {
		re, err := regexpFor(q, m.flags, m.quotemeta)
		if err != nil {
			return nil, err
		}
		regexps = append(regexps, re)
	}

	return regexps, nil
}

func (m *RegexpMatcher) String() string {
	return "Regexp"
}

func (m *CaseSensitiveMatcher) String() string {
	return "CaseSensitive"
}

func (m *IgnoreCaseMatcher) String() string {
	return "IgnoreCase"
}

// sort related stuff
type byStart [][]int

func (m byStart) Len() int {
	return len(m)
}

func (m byStart) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

func (m byStart) Less(i, j int) bool {
	return m[i][0] < m[j][0]
}

func (m *RegexpMatcher) Match(q string, buffer []Match) []Match {
	results := []Match{}
	regexps, err := m.QueryToRegexps(q)
	if err != nil {
		return results
	}

	for _, line := range buffer {
		ms := m.MatchAllRegexps(regexps, line.Line())
		if ms == nil {
			continue
		}
		results = append(results, NewDidMatch(line.Buffer(), m.enableSep, ms))
	}
	return results
}

func (m *RegexpMatcher) MatchAllRegexps(regexps []*regexp.Regexp, line string) [][]int {
	matches := make([][]int, 0)

	allMatched := true
Match:
	for _, re := range regexps {
		match := re.FindAllStringSubmatchIndex(line, -1)
		if match == nil {
			allMatched = false
			break Match
		}

		for _, ma := range match {
			start, end := ma[0], ma[1]
			for _, m := range matches {
				if start >= m[0] && start < m[1] {
					continue Match
				}

				if start < m[0] && end >= m[0] {
					continue Match
				}
			}
			matches = append(matches, ma)
		}
	}

	if !allMatched {
		return nil
	}

	sort.Sort(byStart(matches))

	return matches
}
<commit_msg>Fix off-by-one<commit_after>package peco

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

// Match defines the interface for matches. Note that to make drawing easier,
// we have a DidMatch and NoMatch types instead of using []Match and []string.
type Match interface {
	Buffer() string // Raw buffer, may contain null
	Line() string // Line to be displayed
	Output() string // Output string to be displayed after peco is done
	Indices() [][]int
}

type MatchString struct {
	buf string
	sepLoc int
}

func NewMatchString(v string, enableSep bool) *MatchString {
	m := &MatchString{
		v,
		-1,
	}
	if !enableSep {
		return m
	}

	// XXX This may be silly, but we're avoiding using strings.IndexByte()
	// here because it doesn't exist on go1.1. Let's remove support for
	// 1.1 when 1.4 comes out (or something)
	for i := 0; i < len(m.buf); i++ {
		if m.buf[i] == '\000' {
			m.sepLoc = i
		}
	}
	return m
}

func (m MatchString) Buffer() string {
	return m.buf
}

func (m MatchString) Line() string {
	if i := m.sepLoc; i > -1 {
		return m.buf[:i]
	}
	return m.buf
}

func (m MatchString) Output() string {
	if i := m.sepLoc; i > -1 {
		return m.buf[i+1:]
	}
	return m.buf
}

// NoMatch is actually an alias to a regular string. It implements the
// Match interface, but just returns the underlying string with no matches
type NoMatch struct {
	*MatchString
}

func NewNoMatch(v string, enableSep bool) *NoMatch {
	return &NoMatch{NewMatchString(v, enableSep)}
}

func (m NoMatch) Indices() [][]int {
	return nil
}

// DidMatch contains the actual match, and the indices to the matches
// in the line
type DidMatch struct {
	*MatchString
	matches [][]int
}

func NewDidMatch(v string, enableSep bool, m [][]int) *DidMatch {
	return &DidMatch{NewMatchString(v, enableSep), m}
}

func (d DidMatch) Indices() [][]int {
	return d.matches
}

// Matcher interface defines the API for things that want to
// match against the buffer
type Matcher interface {
	Match(string, []Match) []Match
	String() string
}

const (
	IgnoreCaseMatch    = "IgnoreCase"
	CaseSensitiveMatch = "CaseSensitive"
	RegexpMatch        = "Regexp"
)

type RegexpMatcher struct {
	enableSep bool
	flags     []string
	quotemeta bool
}

type CaseSensitiveMatcher struct {
	*RegexpMatcher
}

type IgnoreCaseMatcher struct {
	*RegexpMatcher
}

func NewCaseSensitiveMatcher(enableSep bool) *CaseSensitiveMatcher {
	m := &CaseSensitiveMatcher{NewRegexpMatcher(enableSep)}
	m.quotemeta = true
	return m
}

func NewIgnoreCaseMatcher(enableSep bool) *IgnoreCaseMatcher {
	m := &IgnoreCaseMatcher{NewRegexpMatcher(enableSep)}
	m.flags = []string{"i"}
	m.quotemeta = true
	return m
}

func NewRegexpMatcher(enableSep bool) *RegexpMatcher {
	return &RegexpMatcher{
		enableSep,
		[]string{},
		false,
	}
}

func regexpFor(q string, flags []string, quotemeta bool) (*regexp.Regexp, error) {
	reTxt := q
	if quotemeta {
		reTxt = regexp.QuoteMeta(q)
	}

	if flags != nil && len(flags) > 0 {
		reTxt = fmt.Sprintf("(?%s)%s", strings.Join(flags, ""), reTxt)
	}

	re, err := regexp.Compile(reTxt)
	if err != nil {
		return nil, err
	}
	return re, nil
}

func (m *RegexpMatcher) QueryToRegexps(query string) ([]*regexp.Regexp, error) {
	queries := strings.Split(strings.TrimSpace(query), " ")
	regexps := make([]*regexp.Regexp, 0)

	for _, q := range queries {
		re, err := regexpFor(q, m.flags, m.quotemeta)
		if err != nil {
			return nil, err
		}
		regexps = append(regexps, re)
	}

	return regexps, nil
}

func (m *RegexpMatcher) String() string {
	return "Regexp"
}

func (m *CaseSensitiveMatcher) String() string {
	return "CaseSensitive"
}

func (m *IgnoreCaseMatcher) String() string {
	return "IgnoreCase"
}

// sort related stuff
type byStart [][]int

func (m byStart) Len() int {
	return len(m)
}

func (m byStart) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

func (m byStart) Less(i, j int) bool {
	return m[i][0] < m[j][0]
}

func (m *RegexpMatcher) Match(q string, buffer []Match) []Match {
	results := []Match{}
	regexps, err := m.QueryToRegexps(q)
	if err != nil {
		return results
	}

	for _, line := range buffer {
		ms := m.MatchAllRegexps(regexps, line.Line())
		if ms == nil {
			continue
		}
		results = append(results, NewDidMatch(line.Buffer(), m.enableSep, ms))
	}
	return results
}

func (m *RegexpMatcher) MatchAllRegexps(regexps []*regexp.Regexp, line string) [][]int {
	matches := make([][]int, 0)

	allMatched := true
Match:
	for _, re := range regexps {
		match := re.FindAllStringSubmatchIndex(line, -1)
		if match == nil {
			allMatched = false
			break Match
		}

		for _, ma := range match {
			start, end := ma[0], ma[1]
			for _, m := range matches {
				if start >= m[0] && start < m[1] {
					continue Match
				}

				if start < m[0] && end >= m[0] {
					continue Match
				}
			}
			matches = append(matches, ma)
		}
	}

	if !allMatched {
		return nil
	}

	sort.Sort(byStart(matches))

	return matches
}
<|endoftext|>


<commit_before>package peco

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

const (
	CaseSensitiveMatch = iota
	IgnoreCaseMatch
)

type StraightMatcher struct {
	flags []string
}

type CaseSensitiveMatcher struct {
	StraightMatcher
}

type IgnoreCaseMatcher struct {
	StraightMatcher
}

func NewCaseSensitiveMatcher() *CaseSensitiveMatcher {
	return &CaseSensitiveMatcher{StraightMatcher{nil}}
}

func NewIgnoreCaseMatcher() *IgnoreCaseMatcher {
	return &IgnoreCaseMatcher{StraightMatcher{[]string{"i"}}}
}

func regexpFor(q string, flags []string) (*regexp.Regexp, error) {
	var reTxt string
	if flags == nil || len(flags) <= 0 {
		reTxt = fmt.Sprintf("%s", regexp.QuoteMeta(q))
	} else {
		reTxt = fmt.Sprintf("(?%s)%s", strings.Join(flags, ""), regexp.QuoteMeta(q))
	}
	re, err := regexp.Compile(reTxt)
	if err != nil {
		return nil, err
	}
	return re, nil
}

func (m *StraightMatcher) QueryToRegexps(query string) ([]*regexp.Regexp, error) {
	queries := strings.Split(strings.TrimSpace(query), " ")
	regexps := make([]*regexp.Regexp, 0)

	for _, q := range queries {
		re, err := regexpFor(q, m.flags)
		if err != nil {
			return nil, err
		}
		regexps = append(regexps, re)
	}

	return regexps, nil
}

func (m *CaseSensitiveMatcher) String() string {
	return "CaseSentive"
}

func (m *IgnoreCaseMatcher) String() string {
	return "IgnoreCase"
}

// sort related stuff
type byStart [][]int

func (m byStart) Len() int {
	return len(m)
}

func (m byStart) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

func (m byStart) Less(i, j int) bool {
	return m[i][0] < m[j][0]
}

func (m *StraightMatcher) Match(q string, buffer []Match) []Match {
	results := []Match{}
	regexps, err := m.QueryToRegexps(q)
	if err != nil {
		return []Match{}
	}

	for _, line := range buffer {
		ms := m.MatchAllRegexps(regexps, line.line)
		if ms == nil {
			continue
		}
		results = append(results, Match{line.line, ms})
	}
	return results
}

func (m *StraightMatcher) MatchAllRegexps(regexps []*regexp.Regexp, line string) [][]int {
	matches := make([][]int, 0)

	allMatched := true
Match:
	for _, re := range regexps {
		match := re.FindAllStringSubmatchIndex(line, -1)
		if match == nil {
			allMatched = false
			break Match
		}

		for _, ma := range match {
			start, end := ma[0], ma[1]
			for _, m := range matches {
				if start >= m[0] && start < m[1] {
					continue Match
				}

				if start < m[0] && end >= m[0] {
					continue Match
				}
			}
			matches = append(matches, ma)
		}
	}

	if !allMatched {
		return nil
	}

	sort.Sort(byStart(matches))

	return matches
}
<commit_msg>Fix string representation<commit_after>package peco

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

const (
	CaseSensitiveMatch = iota
	IgnoreCaseMatch
)

type StraightMatcher struct {
	flags []string
}

type CaseSensitiveMatcher struct {
	StraightMatcher
}

type IgnoreCaseMatcher struct {
	StraightMatcher
}

func NewCaseSensitiveMatcher() *CaseSensitiveMatcher {
	return &CaseSensitiveMatcher{StraightMatcher{nil}}
}

func NewIgnoreCaseMatcher() *IgnoreCaseMatcher {
	return &IgnoreCaseMatcher{StraightMatcher{[]string{"i"}}}
}

func regexpFor(q string, flags []string) (*regexp.Regexp, error) {
	var reTxt string
	if flags == nil || len(flags) <= 0 {
		reTxt = fmt.Sprintf("%s", regexp.QuoteMeta(q))
	} else {
		reTxt = fmt.Sprintf("(?%s)%s", strings.Join(flags, ""), regexp.QuoteMeta(q))
	}
	re, err := regexp.Compile(reTxt)
	if err != nil {
		return nil, err
	}
	return re, nil
}

func (m *StraightMatcher) QueryToRegexps(query string) ([]*regexp.Regexp, error) {
	queries := strings.Split(strings.TrimSpace(query), " ")
	regexps := make([]*regexp.Regexp, 0)

	for _, q := range queries {
		re, err := regexpFor(q, m.flags)
		if err != nil {
			return nil, err
		}
		regexps = append(regexps, re)
	}

	return regexps, nil
}

func (m *CaseSensitiveMatcher) String() string {
	return "CaseSensitive"
}

func (m *IgnoreCaseMatcher) String() string {
	return "IgnoreCase"
}

// sort related stuff
type byStart [][]int

func (m byStart) Len() int {
	return len(m)
}

func (m byStart) Swap(i, j int) {
	m[i], m[j] = m[j], m[i]
}

func (m byStart) Less(i, j int) bool {
	return m[i][0] < m[j][0]
}

func (m *StraightMatcher) Match(q string, buffer []Match) []Match {
	results := []Match{}
	regexps, err := m.QueryToRegexps(q)
	if err != nil {
		return []Match{}
	}

	for _, line := range buffer {
		ms := m.MatchAllRegexps(regexps, line.line)
		if ms == nil {
			continue
		}
		results = append(results, Match{line.line, ms})
	}
	return results
}

func (m *StraightMatcher) MatchAllRegexps(regexps []*regexp.Regexp, line string) [][]int {
	matches := make([][]int, 0)

	allMatched := true
Match:
	for _, re := range regexps {
		match := re.FindAllStringSubmatchIndex(line, -1)
		if match == nil {
			allMatched = false
			break Match
		}

		for _, ma := range match {
			start, end := ma[0], ma[1]
			for _, m := range matches {
				if start >= m[0] && start < m[1] {
					continue Match
				}

				if start < m[0] && end >= m[0] {
					continue Match
				}
			}
			matches = append(matches, ma)
		}
	}

	if !allMatched {
		return nil
	}

	sort.Sort(byStart(matches))

	return matches
}
<|endoftext|>


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

package rpmutils

import (
	"encoding/binary"
	"fmt"
	"io"

	"gerrit-pdt.unx.sas.com/dt/go-rpmutils.git/cpio"
)

type Rpm struct {
	Header *RpmHeader
	f      io.Reader
}

type RpmHeader struct {
	sigHeader *rpmHeader
	genHeader *rpmHeader
	isSource  bool
}

func ReadRpm(f io.Reader) (*Rpm, error) {
	hdr, err := ReadHeader(f)
	if err != nil {
		return nil, err
	}
	return &Rpm{
		Header: hdr,
		f:      f,
	}, nil
}

func (rpm *Rpm) ExpandPayload(dest string) error {
	pld, err := uncompressRpmPayloadReader(rpm.f, rpm.Header)
	if err != nil {
		return err
	}
	return cpio.Extract(pld, dest)
}

func (rpm *Rpm) PayloadReader() (*cpio.Reader, error) {
	pld, err := uncompressRpmPayloadReader(rpm.f, rpm.Header)
	if err != nil {
		return nil, err
	}
	return cpio.NewReader(pld), nil
}

func ReadHeader(f io.Reader) (*RpmHeader, error) {
	sigHeader, err := readSignatureHeader(f)
	if err != nil {
		return nil, err
	}

	sha1 := "" // need to read this from the sig header.

	genHeader, err := readHeader(f, sha1, sigHeader.isSource, false)
	if err != nil {
		return nil, err
	}

	return &RpmHeader{
		sigHeader: sigHeader,
		genHeader: genHeader,
		isSource:  sigHeader.isSource,
	}, nil
}

func readSignatureHeader(f io.Reader) (*rpmHeader, error) {
	// Read signature header
	lead := make([]byte, 96)
	s, err := f.Read(lead)
	if s != 96 {
		return nil, fmt.Errorf("short sig header, got %d bytes, expected 96", s)
	}
	if err != nil {
		return nil, err
	}

	// Check file magic
	magic := binary.BigEndian.Uint32(lead[0:4])
	if magic&0xffffffff != 0xedabeedb {
		return nil, fmt.Errorf("file is not an RPM")
	}

	// Check source flag
	isSource := binary.BigEndian.Uint16(lead[6:8]) == 1

	// Return signature header
	return readHeader(f, "", isSource, true)
}

func (hdr *RpmHeader) HasTag(tag int) bool {
	h, t := hdr.getHeader(tag)
	return h.HasTag(t)
}

func (hdr *RpmHeader) Get(tag int) (interface{}, error) {
	h, t := hdr.getHeader(tag)
	return h.Get(t)
}

func (hdr *RpmHeader) GetString(tag int) (string, error) {
	vals, err := hdr.GetStrings(tag)
	if err != nil {
		return "", err
	}
	if len(vals) != 1 {
		return "", fmt.Errorf("incorrect number of values")
	}
	return vals[0], nil
}

func (hdr *RpmHeader) GetStrings(tag int) ([]string, error) {
	h, t := hdr.getHeader(tag)
	return h.GetStrings(t)
}

func (hdr *RpmHeader) GetInts(tag int) ([]int, error) {
	h, t := hdr.getHeader(tag)
	return h.GetInts(t)
}

func (hdr *RpmHeader) GetBytes(tag int) ([]byte, error) {
	h, t := hdr.getHeader(tag)
	return h.GetBytes(t)
}

func (hdr *RpmHeader) getHeader(tag int) (*rpmHeader, int) {
	if tag > _SIGHEADER_TAG_BASE {
		return hdr.sigHeader, tag - _SIGHEADER_TAG_BASE
	}
	if tag < _GENERAL_TAG_BASE {
		return hdr.sigHeader, tag
	}
	return hdr.genHeader, tag
}

func (hdr *RpmHeader) GetNEVRA() (*NEVRA, error) {
	return hdr.genHeader.GetNEVRA()
}

func (hdr *RpmHeader) GetFiles() ([]FileInfo, error) {
	return hdr.genHeader.GetFiles()
}
<commit_msg>rehome to new github location<commit_after>/*
 * Copyright (c) SAS Institute, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package rpmutils

import (
	"encoding/binary"
	"fmt"
	"io"

	"github.com/sassoftware/go-rpmutils/cpio"
)

type Rpm struct {
	Header *RpmHeader
	f      io.Reader
}

type RpmHeader struct {
	sigHeader *rpmHeader
	genHeader *rpmHeader
	isSource  bool
}

func ReadRpm(f io.Reader) (*Rpm, error) {
	hdr, err := ReadHeader(f)
	if err != nil {
		return nil, err
	}
	return &Rpm{
		Header: hdr,
		f:      f,
	}, nil
}

func (rpm *Rpm) ExpandPayload(dest string) error {
	pld, err := uncompressRpmPayloadReader(rpm.f, rpm.Header)
	if err != nil {
		return err
	}
	return cpio.Extract(pld, dest)
}

func (rpm *Rpm) PayloadReader() (*cpio.Reader, error) {
	pld, err := uncompressRpmPayloadReader(rpm.f, rpm.Header)
	if err != nil {
		return nil, err
	}
	return cpio.NewReader(pld), nil
}

func ReadHeader(f io.Reader) (*RpmHeader, error) {
	sigHeader, err := readSignatureHeader(f)
	if err != nil {
		return nil, err
	}

	sha1 := "" // need to read this from the sig header.

	genHeader, err := readHeader(f, sha1, sigHeader.isSource, false)
	if err != nil {
		return nil, err
	}

	return &RpmHeader{
		sigHeader: sigHeader,
		genHeader: genHeader,
		isSource:  sigHeader.isSource,
	}, nil
}

func readSignatureHeader(f io.Reader) (*rpmHeader, error) {
	// Read signature header
	lead := make([]byte, 96)
	s, err := f.Read(lead)
	if s != 96 {
		return nil, fmt.Errorf("short sig header, got %d bytes, expected 96", s)
	}
	if err != nil {
		return nil, err
	}

	// Check file magic
	magic := binary.BigEndian.Uint32(lead[0:4])
	if magic&0xffffffff != 0xedabeedb {
		return nil, fmt.Errorf("file is not an RPM")
	}

	// Check source flag
	isSource := binary.BigEndian.Uint16(lead[6:8]) == 1

	// Return signature header
	return readHeader(f, "", isSource, true)
}

func (hdr *RpmHeader) HasTag(tag int) bool {
	h, t := hdr.getHeader(tag)
	return h.HasTag(t)
}

func (hdr *RpmHeader) Get(tag int) (interface{}, error) {
	h, t := hdr.getHeader(tag)
	return h.Get(t)
}

func (hdr *RpmHeader) GetString(tag int) (string, error) {
	vals, err := hdr.GetStrings(tag)
	if err != nil {
		return "", err
	}
	if len(vals) != 1 {
		return "", fmt.Errorf("incorrect number of values")
	}
	return vals[0], nil
}

func (hdr *RpmHeader) GetStrings(tag int) ([]string, error) {
	h, t := hdr.getHeader(tag)
	return h.GetStrings(t)
}

func (hdr *RpmHeader) GetInts(tag int) ([]int, error) {
	h, t := hdr.getHeader(tag)
	return h.GetInts(t)
}

func (hdr *RpmHeader) GetBytes(tag int) ([]byte, error) {
	h, t := hdr.getHeader(tag)
	return h.GetBytes(t)
}

func (hdr *RpmHeader) getHeader(tag int) (*rpmHeader, int) {
	if tag > _SIGHEADER_TAG_BASE {
		return hdr.sigHeader, tag - _SIGHEADER_TAG_BASE
	}
	if tag < _GENERAL_TAG_BASE {
		return hdr.sigHeader, tag
	}
	return hdr.genHeader, tag
}

func (hdr *RpmHeader) GetNEVRA() (*NEVRA, error) {
	return hdr.genHeader.GetNEVRA()
}

func (hdr *RpmHeader) GetFiles() ([]FileInfo, error) {
	return hdr.genHeader.GetFiles()
}
<|endoftext|>


<commit_before>/*
 This package implements the leftpad function, inspired by the NPM (JS)
 package of the same name.

 Two functions are defined:

 import "leftpad"

 // pad with spaces
 str, err := LeftPad(s, n)

 // pad with specified character
 str, err := func LeftPadStr(s, n, c)

*/
package leftpad

import (
	"fmt"
	"strings"
)

func doLeftPad(s string, n int, r rune) (string, error) {
	if n < 0 {
		return "", fmt.Errorf("invalid length %d", n)
	}

	toAdd := n - len(s)
	if toAdd <= 0 {
		return s, nil
	}

	return strings.Repeat(string(r), toAdd) + s, nil
}

// LeftPad left-pads s with spaces, to length n.
// If n is smaller than s, LeftPad is a no-op.
func LeftPad(s string, n int) (string, error) {
	return doLeftPad(s, n, ' ')
}

// LeftPadStr left-pads s with the rune r, to length n.
// If n is smaller than s, LeftPadStr is a no-op.
func LeftPadStr(s string, n int, r rune) (string, error) {
	return doLeftPad(s, n, r)
}
<commit_msg>Remove unnecessary intermediate function<commit_after>/*
 This package implements the leftpad function, inspired by the NPM (JS)
 package of the same name.

 Two functions are defined:

 import "leftpad"

 // pad with spaces
 str, err := LeftPad(s, n)

 // pad with specified character
 str, err := func LeftPadStr(s, n, c)

*/
package leftpad

import (
	"fmt"
	"strings"
)

// LeftPad left-pads s with spaces, to length n.
// If n is smaller than s, LeftPad is a no-op.
func LeftPad(s string, n int) (string, error) {
	return LeftPadStr(s, n, ' ')
}

// LeftPadStr left-pads s with the rune r, to length n.
// If n is smaller than s, LeftPadStr is a no-op.
func LeftPadStr(s string, n int, r rune) (string, error) {
	if n < 0 {
		return "", fmt.Errorf("invalid length %d", n)
	}

	toAdd := n - len(s)
	if toAdd <= 0 {
		return s, nil
	}

	return strings.Repeat(string(r), toAdd) + s, nil
}
<|endoftext|>


<commit_before>package util

import (
	"encoding/xml"
)

type TestCase struct {
	XMLName   xml.Name `xml:"testcase"`
	ClassName string   `xml:"classname,attr"`
	Name      string   `xml:"name,attr"`
	Time      float64  `xml:"time,attr"`
	Failure   string   `xml:"failure,omitempty"`
	Skipped   string   `xml:"skipped,omitempty"`
}

type TestSuite struct {
	XMLName  xml.Name `xml:"testsuite"`
	Failures int      `xml:"failures,attr"`
	Tests    int      `xml:"tests,attr"`
	Time     float64  `xml:"time,attr"`
	Cases    []TestCase
}
<commit_msg>Fix boilerplate for kubetest/util/<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package util

import (
	"encoding/xml"
)

type TestCase struct {
	XMLName   xml.Name `xml:"testcase"`
	ClassName string   `xml:"classname,attr"`
	Name      string   `xml:"name,attr"`
	Time      float64  `xml:"time,attr"`
	Failure   string   `xml:"failure,omitempty"`
	Skipped   string   `xml:"skipped,omitempty"`
}

type TestSuite struct {
	XMLName  xml.Name `xml:"testsuite"`
	Failures int      `xml:"failures,attr"`
	Tests    int      `xml:"tests,attr"`
	Time     float64  `xml:"time,attr"`
	Cases    []TestCase
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
)

type LaTeXOut struct {
}

func (lo LaTeXOut) output(s []string, rpmInfo map[string]*RpmInfo, groupSet map[string]bool, nodes map[string]*Node) error {
	fmt.Println("\\documentclass[11pt,landscape]{article}")
	fmt.Println("")
	fmt.Println("\\usepackage[landscape,paperwidth=10in,paperheight=8.5in]{geometry}")
	fmt.Println("\\usepackage{longtable,microtype,savetrees}")
	fmt.Println("\\usepackage[hyphens]{url}")
	fmt.Println("\\usepackage{seqsplit}")

	fmt.Println("")
	fmt.Println("\\oddsidemargin -.5cm")
	fmt.Println("\\evensidemargin -.5cm")
	fmt.Println("")
	fmt.Println("\\newcommand\\foo[2]{%")
	fmt.Println("\\begin{minipage}{#1}")
	fmt.Println("\\seqsplit{#2}")
	fmt.Println("\\end{minipage}")
	fmt.Println("}")
	fmt.Println("")
	fmt.Println("")
	fmt.Println("\\begin{document}")
	fmt.Println("\\thispagestyle{empty}")
	fmt.Println("\\pagestyle{empty}")
	//fmt.Println("\\tableofcontents")
	//fmt.Println("\\newpage")
	//fmt.Println("\\begin{landscape}")
	fmt.Println("\\renewcommand*{\arraystretch}{1.4}")
	fmt.Println("\\begin{longtable}{|p{2cm}|p{1.4cm}|p{4cm}|p{5cm}|p{4cm}|p{3cm}|}")
	fmt.Println("\\hline")
	fmt.Println("\\textbf{Name}& \\textbf{Version}& \\textbf{Summary}& \\textbf{Description}& \\textbf{URL}& \\textbf{Install Time}\\\\")
	fmt.Println("\\hline")
	fmt.Println("\\endfirsthead")
	fmt.Println("\\hline")
	fmt.Println("\\textbf{Name}& \\textbf{Version}& \\textbf{Summary}& \\textbf{Description}& \\textbf{URL}& \\textbf{Install Time}\\\\")
	fmt.Println("\\hline")
	fmt.Println("\\endhead")

	//fmt.Println("\\begin{enumerate}")
	for r := range s {
		//fmt.Println("\\section{" + escapeLatex(rpmInfo[s[r]].Name) + "}")
		//fmt.Println("\\item{" + escapeLatex(rpmInfo[s[r]].Name) + "}")
		//fmt.Println("\\begin{itemize}")
		//for k,v := range rpmInfo[s[r]].Tags{

		//	v = strings.Replace(v, "\n", " ", -1)
		//fmt.Println("\\item {\\bf" + escapeLatex("  " + k + ": ") + "}" + escapeLatex(v))
		//fmt.Println("\\newline")
		//	fmt.Println("\\hline")
		fmt.Println(escapeLatex(rpmInfo[s[r]].Tags["name"]) + "&")
		fmt.Println("\\foo{1.4cm}{" + escapeLatex(rpmInfo[s[r]].Tags["version"]) + "}&")
		fmt.Println(escapeLatex(rpmInfo[s[r]].Tags["summary"]) + "&")
		fmt.Println(escapeLatex(rpmInfo[s[r]].Tags["description"]) + "&")
		fmt.Println("\\url{" + escapeLatex(rpmInfo[s[r]].Tags["url"]) + "}&")
		fmt.Println(escapeLatex(rpmInfo[s[r]].Tags["installtime"]))
		fmt.Println("\\\\ \\hline")
	}
	//fmt.Println("\\end{itemize}")
	//fmt.Println("\\end{section}")
	fmt.Println("\\end{longtable}")
	//fmt.Println("\\end{landscape}")
	fmt.Println("\\end{document}")

	return nil
}

func escapeLatex(v string) string {
	v = strings.Replace(v, "\\", "\\textbackslash{}", -1)
	v = strings.Replace(v, "_", "\\_", -1)
	v = strings.Replace(v, "$", "\\$", -1)

	v = strings.Replace(v, "#", "\\#", -1)
	v = strings.Replace(v, "%", "\\%", -1)
	v = strings.Replace(v, "^", "\\^{}", -1)
	v = strings.Replace(v, "&", "\\&", -1)

	v = strings.Replace(v, "{", "\\{", -1)

	v = strings.Replace(v, "}", "\\}", -1)
	v = strings.Replace(v, "~", "\\~{}", -1)

	return v
}
<commit_msg>Moved latex output from url-package to hyperref-package<commit_after>package main

import (
	"fmt"
	"strings"
)

type LaTeXOut struct {
}

func (lo LaTeXOut) output(s []string, rpmInfo map[string]*RpmInfo, groupSet map[string]bool, nodes map[string]*Node) error {
	fmt.Println("\\documentclass[11pt,landscape]{article}")
	fmt.Println("")
	fmt.Println("\\usepackage[landscape,paperwidth=10in,paperheight=8.5in]{geometry}")
	fmt.Println("\\usepackage{longtable,microtype,savetrees}")
	//fmt.Println("\\usepackage[hyphens]{url}")
	fmt.Println("\\usepackage{hyperref}")
	fmt.Println("\\usepackage{seqsplit}")

	fmt.Println("")
	fmt.Println("\\oddsidemargin -.5cm")
	fmt.Println("\\evensidemargin -.5cm")
	fmt.Println("")
	fmt.Println("\\newcommand\\foo[2]{%")
	fmt.Println("\\begin{minipage}{#1}")
	fmt.Println("\\seqsplit{#2}")
	fmt.Println("\\end{minipage}")
	fmt.Println("}")
	fmt.Println("")
	fmt.Println("")
	fmt.Println("\\begin{document}")
	fmt.Println("\\thispagestyle{empty}")
	fmt.Println("\\pagestyle{empty}")
	//fmt.Println("\\tableofcontents")
	//fmt.Println("\\newpage")
	//fmt.Println("\\begin{landscape}")
	fmt.Println("\\renewcommand*{\\arraystretch}{1.4}")
	fmt.Println("\\begin{longtable}{|p{2cm}|p{1.4cm}|p{4cm}|p{5cm}|p{4cm}|p{3cm}|}")
	fmt.Println("\\hline")
	fmt.Println("\\textbf{Name}& \\textbf{Version}& \\textbf{Summary}& \\textbf{Description}& \\textbf{URL}& \\textbf{Install Time}\\\\")
	fmt.Println("\\hline")
	fmt.Println("\\endfirsthead")
	fmt.Println("\\hline")
	fmt.Println("\\textbf{Name}& \\textbf{Version}& \\textbf{Summary}& \\textbf{Description}& \\textbf{URL}& \\textbf{Install Time}\\\\")
	fmt.Println("\\hline")
	fmt.Println("\\endhead")

	//fmt.Println("\\begin{enumerate}")
	for r := range s {
		//fmt.Println("\\section{" + escapeLatex(rpmInfo[s[r]].Name) + "}")
		//fmt.Println("\\item{" + escapeLatex(rpmInfo[s[r]].Name) + "}")
		//fmt.Println("\\begin{itemize}")
		//for k,v := range rpmInfo[s[r]].Tags{

		//	v = strings.Replace(v, "\n", " ", -1)
		//fmt.Println("\\item {\\bf" + escapeLatex("  " + k + ": ") + "}" + escapeLatex(v))
		//fmt.Println("\\newline")
		//	fmt.Println("\\hline")
		fmt.Println(escapeLatex(rpmInfo[s[r]].Tags["name"]) + "&")
		fmt.Println("\\foo{1.4cm}{" + escapeLatex(rpmInfo[s[r]].Tags["version"]) + "}&")
		fmt.Println(escapeLatex(rpmInfo[s[r]].Tags["summary"]) + "&")
		fmt.Println(escapeLatex(rpmInfo[s[r]].Tags["description"]) + "&")
		fmt.Println("\\small \\url{" + escapeLatex(rpmInfo[s[r]].Tags["url"]) + "}&")
		fmt.Println(escapeLatex(rpmInfo[s[r]].Tags["installtime"]))
		fmt.Println("\\\\ \\hline")
	}
	//fmt.Println("\\end{itemize}")
	//fmt.Println("\\end{section}")
	fmt.Println("\\end{longtable}")
	//fmt.Println("\\end{landscape}")
	fmt.Println("\\end{document}")

	return nil
}

func escapeLatex(v string) string {
	v = strings.Replace(v, "\\", "\\textbackslash{}", -1)
	v = strings.Replace(v, "_", "\\_", -1)
	v = strings.Replace(v, "$", "\\$", -1)

	v = strings.Replace(v, "#", "\\#", -1)
	v = strings.Replace(v, "%", "\\%", -1)
	v = strings.Replace(v, "^", "\\^{}", -1)
	v = strings.Replace(v, "&", "\\&", -1)

	v = strings.Replace(v, "{", "\\{", -1)

	v = strings.Replace(v, "}", "\\}", -1)
	v = strings.Replace(v, "~", "\\~{}", -1)

	return v
}
<|endoftext|>


<commit_before>package backend

import (
	"context"
	"fmt"
	"time"

	gw "github.com/cvmfs/gateway/internal/gateway"
)

// LeaseReturn is the response type of lease queries, handed
// back to the HTTP frontend
type LeaseReturn struct {
	KeyID     string `json:"key_id,omitempty"`
	LeasePath string `json:"path,omitempty"`
	Expires   string `json:"expires,omitempty"`
}

// NewLease for the specified path, using keyID
func (s *Services) NewLease(ctx context.Context, keyID, leasePath string, protocolVersion int) (string, error) {
	t0 := time.Now()

	outcome := "success"
	defer logAction(ctx, "new_lease", &outcome, t0)

	repoName, subPath, err := gw.SplitLeasePath(leasePath)
	if err != nil {
		outcome = err.Error()
		return "", err
	}

	// Check if keyID is allowed to request a lease in the repository
	// at the specified subpath
	if err := s.Access.Check(keyID, subPath, repoName); err != nil {
		outcome = err.Error()
		return "", err
	}

	// Generate a new token for the lease
	token, err := NewLeaseToken(leasePath, s.Config.MaxLeaseTime)
	if err != nil {
		outcome = err.Error()
		return "", err
	}

	if err := s.Leases.NewLease(ctx, keyID, leasePath, protocolVersion, *token); err != nil {
		outcome = err.Error()
		return "", err
	}

	if err := s.StatsMgr.CreateLease(leasePath); err != nil {
		outcome = err.Error()
		return "", err
	}

	outcome = fmt.Sprintf("success: %v", token.TokenStr)
	return token.TokenStr, err
}

// GetLeases returns all active and valid leases
func (s *Services) GetLeases(ctx context.Context) (map[string]LeaseReturn, error) {
	t0 := time.Now()

	outcome := "success"
	defer logAction(ctx, "get_leases", &outcome, t0)

	leases, err := s.Leases.GetLeases(ctx)
	if err != nil {
		outcome = err.Error()
		return nil, err
	}
	ret := make(map[string]LeaseReturn)
	for k, v := range leases {
		if err := CheckToken(v.Token.TokenStr, v.Token.Secret); err == nil {
			ret[k] = LeaseReturn{KeyID: v.KeyID, Expires: v.Token.Expiration.String()}
		}
	}
	return ret, nil
}

// GetLease returns the lease associated with a token
func (s *Services) GetLease(ctx context.Context, tokenStr string) (*LeaseReturn, error) {
	t0 := time.Now()

	outcome := "success"
	defer logAction(ctx, "get_lease", &outcome, t0)

	leasePath, lease, err := s.Leases.GetLease(ctx, tokenStr)
	if err != nil {
		outcome = err.Error()
		return nil, err
	}

	if err := CheckToken(tokenStr, lease.Token.Secret); err != nil {
		outcome = err.Error()
		return nil, err
	}

	ret := &LeaseReturn{
		KeyID:     lease.KeyID,
		LeasePath: leasePath,
		Expires:   lease.Token.Expiration.String(),
	}
	return ret, nil
}

// CancelLeases cancels all the active leases below a repository path
func (s *Services) CancelLeases(ctx context.Context, repoPath string) error {
	t0 := time.Now()

	outcome := "success"
	defer logAction(ctx, "cancel_lease", &outcome, t0)

	if err := s.Leases.CancelLeases(ctx, repoPath); err != nil {
		outcome = err.Error()
		return err
	}

	return nil
}

// CancelLease associated with the token (transaction rollback)
func (s *Services) CancelLease(ctx context.Context, tokenStr string) error {
	t0 := time.Now()

	outcome := "success"
	defer logAction(ctx, "cancel_lease", &outcome, t0)

	leasePath, lease, err := s.Leases.GetLease(ctx, tokenStr)
	if err != nil {
		outcome = err.Error()
		return err
	}

	if err := CheckToken(tokenStr, lease.Token.Secret); err != nil {
		// Allow an expired token to be used to cancel a lease
		if _, ok := err.(ExpiredTokenError); !ok {
			outcome = err.Error()
			return err
		}
	}

	if err := s.Leases.CancelLease(ctx, tokenStr); err != nil {
		outcome = err.Error()
		return err
	}

	if _, err := s.StatsMgr.PopLease(leasePath); err != nil {
		outcome = err.Error()
		return err
	}

	return nil
}

// CommitLease associated with the token (transaction commit)
func (s *Services) CommitLease(ctx context.Context, tokenStr, oldRootHash, newRootHash string, tag gw.RepositoryTag) (uint64, error) {
	t0 := time.Now()

	outcome := "success"
	defer logAction(ctx, "commit_lease", &outcome, t0)

	leasePath, lease, err := s.Leases.GetLease(ctx, tokenStr)
	if err != nil {
		outcome = err.Error()
		return 0, err
	}

	if err := CheckToken(tokenStr, lease.Token.Secret); err != nil {
		outcome = err.Error()
		return 0, err
	}

	repository, _, err := gw.SplitLeasePath(leasePath)
	if err != nil {
		outcome = err.Error()
		return 0, err
	}
	var finalRev uint64
	if err := s.Leases.WithLock(ctx, repository, func() error {
		var err error
		finalRev, err = s.Pool.CommitLease(ctx, leasePath, oldRootHash, newRootHash, tag)
		return err
	}); err != nil {
		outcome = err.Error()
		return 0, err
	}

	go func() {
		plotsErr := s.StatsMgr.UploadStatsPlots(repository)
		if plotsErr != nil {
			gw.LogC(ctx, "actions", gw.LogError).Msgf(plotsErr.Error())
		}
	}()

	if err := s.Leases.CancelLease(ctx, tokenStr); err != nil {
		outcome = err.Error()
		return finalRev, err
	}

	return finalRev, nil
}
<commit_msg>clean up reminers of statistics in case of expired lease<commit_after>package backend

import (
	"context"
	"fmt"
	"time"

	gw "github.com/cvmfs/gateway/internal/gateway"
)

// LeaseReturn is the response type of lease queries, handed
// back to the HTTP frontend
type LeaseReturn struct {
	KeyID     string `json:"key_id,omitempty"`
	LeasePath string `json:"path,omitempty"`
	Expires   string `json:"expires,omitempty"`
}

// NewLease for the specified path, using keyID
func (s *Services) NewLease(ctx context.Context, keyID, leasePath string, protocolVersion int) (string, error) {
	t0 := time.Now()

	outcome := "success"
	defer logAction(ctx, "new_lease", &outcome, t0)

	repoName, subPath, err := gw.SplitLeasePath(leasePath)
	if err != nil {
		outcome = err.Error()
		return "", err
	}

	// Check if keyID is allowed to request a lease in the repository
	// at the specified subpath
	if err := s.Access.Check(keyID, subPath, repoName); err != nil {
		outcome = err.Error()
		return "", err
	}

	// Generate a new token for the lease
	token, err := NewLeaseToken(leasePath, s.Config.MaxLeaseTime)
	if err != nil {
		outcome = err.Error()
		return "", err
	}

	if err := s.Leases.NewLease(ctx, keyID, leasePath, protocolVersion, *token); err != nil {
		outcome = err.Error()
		return "", err
	}

	// the StatsMgr does not handle the case in which a lease expires.
	// However, if a lease expires, we should not upload it's statistics.
	// If the LeaseMgr successfully create a new lease,
	// then, the lease path must be free.
	// We remove it, no matter what.
	// We don't check the error because it return an error if the lease does not exist, the standard case.
	s.StatsMgr.PopLease(leasePath)

	if err := s.StatsMgr.CreateLease(leasePath); err != nil {
		outcome = err.Error()
		return "", err
	}

	outcome = fmt.Sprintf("success: %v", token.TokenStr)
	return token.TokenStr, err
}

// GetLeases returns all active and valid leases
func (s *Services) GetLeases(ctx context.Context) (map[string]LeaseReturn, error) {
	t0 := time.Now()

	outcome := "success"
	defer logAction(ctx, "get_leases", &outcome, t0)

	leases, err := s.Leases.GetLeases(ctx)
	if err != nil {
		outcome = err.Error()
		return nil, err
	}
	ret := make(map[string]LeaseReturn)
	for k, v := range leases {
		if err := CheckToken(v.Token.TokenStr, v.Token.Secret); err == nil {
			ret[k] = LeaseReturn{KeyID: v.KeyID, Expires: v.Token.Expiration.String()}
		}
	}
	return ret, nil
}

// GetLease returns the lease associated with a token
func (s *Services) GetLease(ctx context.Context, tokenStr string) (*LeaseReturn, error) {
	t0 := time.Now()

	outcome := "success"
	defer logAction(ctx, "get_lease", &outcome, t0)

	leasePath, lease, err := s.Leases.GetLease(ctx, tokenStr)
	if err != nil {
		outcome = err.Error()
		return nil, err
	}

	if err := CheckToken(tokenStr, lease.Token.Secret); err != nil {
		outcome = err.Error()
		return nil, err
	}

	ret := &LeaseReturn{
		KeyID:     lease.KeyID,
		LeasePath: leasePath,
		Expires:   lease.Token.Expiration.String(),
	}
	return ret, nil
}

// CancelLeases cancels all the active leases below a repository path
func (s *Services) CancelLeases(ctx context.Context, repoPath string) error {
	t0 := time.Now()

	outcome := "success"
	defer logAction(ctx, "cancel_lease", &outcome, t0)

	if err := s.Leases.CancelLeases(ctx, repoPath); err != nil {
		outcome = err.Error()
		return err
	}

	return nil
}

// CancelLease associated with the token (transaction rollback)
func (s *Services) CancelLease(ctx context.Context, tokenStr string) error {
	t0 := time.Now()

	outcome := "success"
	defer logAction(ctx, "cancel_lease", &outcome, t0)

	leasePath, lease, err := s.Leases.GetLease(ctx, tokenStr)
	if err != nil {
		outcome = err.Error()
		return err
	}

	if err := CheckToken(tokenStr, lease.Token.Secret); err != nil {
		// Allow an expired token to be used to cancel a lease
		if _, ok := err.(ExpiredTokenError); !ok {
			outcome = err.Error()
			return err
		}
	}

	if err := s.Leases.CancelLease(ctx, tokenStr); err != nil {
		outcome = err.Error()
		return err
	}

	if _, err := s.StatsMgr.PopLease(leasePath); err != nil {
		outcome = err.Error()
		return err
	}

	return nil
}

// CommitLease associated with the token (transaction commit)
func (s *Services) CommitLease(ctx context.Context, tokenStr, oldRootHash, newRootHash string, tag gw.RepositoryTag) (uint64, error) {
	t0 := time.Now()

	outcome := "success"
	defer logAction(ctx, "commit_lease", &outcome, t0)

	leasePath, lease, err := s.Leases.GetLease(ctx, tokenStr)
	if err != nil {
		outcome = err.Error()
		return 0, err
	}

	if err := CheckToken(tokenStr, lease.Token.Secret); err != nil {
		outcome = err.Error()
		return 0, err
	}

	repository, _, err := gw.SplitLeasePath(leasePath)
	if err != nil {
		outcome = err.Error()
		return 0, err
	}
	var finalRev uint64
	if err := s.Leases.WithLock(ctx, repository, func() error {
		var err error
		finalRev, err = s.Pool.CommitLease(ctx, leasePath, oldRootHash, newRootHash, tag)
		return err
	}); err != nil {
		outcome = err.Error()
		return 0, err
	}

	go func() {
		plotsErr := s.StatsMgr.UploadStatsPlots(repository)
		if plotsErr != nil {
			gw.LogC(ctx, "actions", gw.LogError).Msgf(plotsErr.Error())
		}
	}()

	if err := s.Leases.CancelLease(ctx, tokenStr); err != nil {
		outcome = err.Error()
		return finalRev, err
	}

	return finalRev, nil
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"io"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/mitchellh/cli"
)

type MonitorCommand struct {
	Meta
}

func (c *MonitorCommand) Help() string {
	helpText := `
Usage: nomad monitor [options]

	Stream log messages of a nomad agent. The monitor command lets you
	listen for log levels that may be filtered out of the Nomad agent. For
	example your agent may only be logging at INFO level, but with the monitor
	command you can set -log-level DEBUG

General Options:

	` + generalOptionsUsage() + `

Monitor Specific Options:

  -log-level <level>
    Sets the log level to monitor (default: INFO)

  -node-id <node-id>
    Sets the specific node to monitor

  -json
    Sets log output to JSON format
  `
	return strings.TrimSpace(helpText)
}

func (c *MonitorCommand) Synopsis() string {
	return "stream logs from a Nomad agent"
}

func (c *MonitorCommand) Name() string { return "monitor" }

func (c *MonitorCommand) Run(args []string) int {
	c.Ui = &cli.PrefixedUi{
		OutputPrefix: "    ",
		InfoPrefix:   "    ",
		ErrorPrefix:  "==> ",
		Ui:           c.Ui,
	}

	var logLevel string
	var nodeID string
	var serverID string
	var logJSON bool

	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	flags.StringVar(&logLevel, "log-level", "", "")
	flags.StringVar(&nodeID, "node-id", "", "")
	flags.StringVar(&serverID, "server-id", "", "")
	flags.BoolVar(&logJSON, "json", false, "")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()
	if l := len(args); l != 0 {
		c.Ui.Error("This command takes no arguments")
		c.Ui.Error(commandErrorText(c))
		return 1
	}

	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		c.Ui.Error(commandErrorText(c))
		return 1
	}

	params := map[string]string{
		"log_level": logLevel,
		"node_id":   nodeID,
		"server_id": serverID,
		"log_json":  strconv.FormatBool(logJSON),
	}

	query := &api.QueryOptions{
		Params: params,
	}

	eventDoneCh := make(chan struct{})
	frames, errCh := client.Agent().Monitor(eventDoneCh, query)
	select {
	case err := <-errCh:
		c.Ui.Error(fmt.Sprintf("Error starting monitor: %s", err))
		c.Ui.Error(commandErrorText(c))
		return 1
	default:
	}

	// Create a reader
	var r io.ReadCloser
	frameReader := api.NewFrameReader(frames, errCh, eventDoneCh)
	frameReader.SetUnblockTime(500 * time.Millisecond)
	r = frameReader

	defer r.Close()

	signalCh := make(chan os.Signal, 1)
	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)

	go func() {
		<-signalCh
		// End the streaming
		r.Close()
	}()

	_, err = io.Copy(os.Stdout, r)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("error monitoring logs: %s", err))
		return 1
	}

	return 0
}
<commit_msg>Allows a node uuid prefix to be passed in<commit_after>package command

import (
	"fmt"
	"io"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/mitchellh/cli"
)

type MonitorCommand struct {
	Meta
}

func (c *MonitorCommand) Help() string {
	helpText := `
Usage: nomad monitor [options]

	Stream log messages of a nomad agent. The monitor command lets you
	listen for log levels that may be filtered out of the Nomad agent. For
	example your agent may only be logging at INFO level, but with the monitor
	command you can set -log-level DEBUG

General Options:

	` + generalOptionsUsage() + `

Monitor Specific Options:

  -log-level <level>
    Sets the log level to monitor (default: INFO)

  -node-id <node-id>
    Sets the specific node to monitor

  -json
    Sets log output to JSON format
  `
	return strings.TrimSpace(helpText)
}

func (c *MonitorCommand) Synopsis() string {
	return "stream logs from a Nomad agent"
}

func (c *MonitorCommand) Name() string { return "monitor" }

func (c *MonitorCommand) Run(args []string) int {
	c.Ui = &cli.PrefixedUi{
		OutputPrefix: "    ",
		InfoPrefix:   "    ",
		ErrorPrefix:  "==> ",
		Ui:           c.Ui,
	}

	var logLevel string
	var nodeID string
	var serverID string
	var logJSON bool

	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	flags.StringVar(&logLevel, "log-level", "", "")
	flags.StringVar(&nodeID, "node-id", "", "")
	flags.StringVar(&serverID, "server-id", "", "")
	flags.BoolVar(&logJSON, "json", false, "")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()
	if l := len(args); l != 0 {
		c.Ui.Error("This command takes no arguments")
		c.Ui.Error(commandErrorText(c))
		return 1
	}

	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		c.Ui.Error(commandErrorText(c))
		return 1
	}

	// Query the node info and lookup prefix
	if len(nodeID) == 1 {
		c.Ui.Error(fmt.Sprintf("Node identifier must contain at least two characters."))
		return 1
	}

	if nodeID != "" {
		nodeID = sanitizeUUIDPrefix(nodeID)
		nodes, _, err := client.Nodes().PrefixList(nodeID)
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Error querying node: %v", err))
			return 1
		}

		if len(nodes) > 1 {
			out := formatNodeStubList(nodes, false)
			c.Ui.Output(fmt.Sprintf("Prefix matched multiple nodes\n\n%s", out))
			return 1
		}
		nodeID = nodes[0].ID
	}

	params := map[string]string{
		"log_level": logLevel,
		"node_id":   nodeID,
		"server_id": serverID,
		"log_json":  strconv.FormatBool(logJSON),
	}

	query := &api.QueryOptions{
		Params: params,
	}

	eventDoneCh := make(chan struct{})
	frames, errCh := client.Agent().Monitor(eventDoneCh, query)
	select {
	case err := <-errCh:
		c.Ui.Error(fmt.Sprintf("Error starting monitor: %s", err))
		c.Ui.Error(commandErrorText(c))
		return 1
	default:
	}

	// Create a reader
	var r io.ReadCloser
	frameReader := api.NewFrameReader(frames, errCh, eventDoneCh)
	frameReader.SetUnblockTime(500 * time.Millisecond)
	r = frameReader

	defer r.Close()

	signalCh := make(chan os.Signal, 1)
	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)

	go func() {
		<-signalCh
		// End the streaming
		r.Close()
	}()

	_, err = io.Copy(os.Stdout, r)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("error monitoring logs: %s", err))
		return 1
	}

	return 0
}
<|endoftext|>


<commit_before>package commands

import (
	"io/ioutil"
	"os"
	"strings"

	"github.com/github/git-lfs/git"
	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx"
	"github.com/github/git-lfs/vendor/_nuts/github.com/spf13/cobra"
)

var (
	pushCmd = &cobra.Command{
		Use:   "push",
		Short: "Push files to the Git LFS server",
		Run:   pushCommand,
	}
	pushDryRun       = false
	pushDeleteBranch = "(delete)"
	pushObjectIDs    = false
	useStdin         = false

	// shares some global vars and functions with commmands_pre_push.go
)

func uploadsBetweenRefs(left string, right string) *lfs.TransferQueue {
	// Just use scanner here
	pointers, err := lfs.ScanRefs(left, right)
	if err != nil {
		Panic(err, "Error scanning for Git LFS files")
	}

	uploadQueue := lfs.NewUploadQueue(lfs.Config.ConcurrentTransfers(), len(pointers))

	for i, pointer := range pointers {
		if pushDryRun {
			Print("push %s", pointer.Name)
			continue
		}
		tracerx.Printf("checking_asset: %s %s %d/%d", pointer.Oid, pointer.Name, i+1, len(pointers))

		u, wErr := lfs.NewUploadable(pointer.Oid, pointer.Name)
		if wErr != nil {
			if Debugging || wErr.Panic {
				Panic(wErr.Err, wErr.Error())
			} else {
				Exit(wErr.Error())
			}
		}
		uploadQueue.Add(u)
	}

	return uploadQueue
}

func uploadsWithObjectIDs(oids []string) *lfs.TransferQueue {
	uploadQueue := lfs.NewUploadQueue(lfs.Config.ConcurrentTransfers(), len(oids))

	for _, oid := range oids {
		u, wErr := lfs.NewUploadable(oid, "")
		if wErr != nil {
			if Debugging || wErr.Panic {
				Panic(wErr.Err, wErr.Error())
			} else {
				Exit(wErr.Error())
			}
		}
		uploadQueue.Add(u)
	}

	return uploadQueue
}

// pushCommand pushes local objects to a Git LFS server.  It takes two
// arguments:
//
//   `<remote> <remote ref>`
//
// Both a remote name ("origin") or a remote URL are accepted.
//
// pushCommand calculates the git objects to send by looking comparing the range
// of commits between the local and remote git servers.
func pushCommand(cmd *cobra.Command, args []string) {
	var left, right string
	var uploadQueue *lfs.TransferQueue

	if len(args) == 0 {
		Print("Specify a remote and a remote branch name (`git lfs push origin master`)")
		os.Exit(1)
	}

	lfs.Config.CurrentRemote = args[0]

	if useStdin {
		requireStdin("Run this command from the Git pre-push hook, or leave the --stdin flag off.")

		// called from a pre-push hook!  Update the existing pre-push hook if it's
		// one that git-lfs set.
		lfs.InstallHooks(false)

		refsData, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			Panic(err, "Error reading refs on stdin")
		}

		if len(refsData) == 0 {
			return
		}

		left, right = decodeRefs(string(refsData))
		if left == pushDeleteBranch {
			return
		}

		uploadQueue = uploadsBetweenRefs(left, right)
	} else if pushObjectIDs {
		if len(args) < 2 {
			Print("Usage: git lfs push --objectid <remote> <lfs-object-id> [lfs-object-id] ...")
			return
		}

		uploadQueue = uploadsWithObjectIDs(args[1:])
	} else {
		var remoteArg, refArg string

		if len(args) < 1 {
			Print("Usage: git lfs push --dry-run <remote> [ref]")
			return
		}

		remoteArg = args[0]
		if len(args) == 2 {
			refArg = args[1]
		}

		localRef, err := git.CurrentRef()
		if err != nil {
			Panic(err, "Error getting local ref")
		}
		left = localRef

		remoteRef, err := git.LsRemote(remoteArg, refArg)
		if err != nil {
			Panic(err, "Error getting remote ref")
		}

		if remoteRef != "" {
			right = "^" + strings.Split(remoteRef, "\t")[0]
		}

		uploadQueue = uploadsBetweenRefs(left, right)
	}

	if !pushDryRun {
		uploadQueue.Process()
		for _, err := range uploadQueue.Errors() {
			if Debugging || err.Panic {
				LoggedError(err.Err, err.Error())
			} else {
				Error(err.Error())
			}
		}

		if len(uploadQueue.Errors()) > 0 {
			os.Exit(2)
		}
	}
}

func init() {
	pushCmd.Flags().BoolVarP(&pushDryRun, "dry-run", "d", false, "Do everything except actually send the updates")
	pushCmd.Flags().BoolVarP(&useStdin, "stdin", "s", false, "Take refs on stdin (for pre-push hook)")
	pushCmd.Flags().BoolVarP(&pushObjectIDs, "object-id", "o", false, "Push LFS object ID(s)")
	RootCmd.AddCommand(pushCmd)
}
<commit_msg>ラララララ ラー ウウウ フフフ<commit_after>package commands

import (
	"io/ioutil"
	"os"
	"strings"

	"github.com/github/git-lfs/git"
	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx"
	"github.com/github/git-lfs/vendor/_nuts/github.com/spf13/cobra"
)

var (
	pushCmd = &cobra.Command{
		Use:   "push",
		Short: "Push files to the Git LFS server",
		Run:   pushCommand,
	}
	pushDryRun       = false
	pushDeleteBranch = "(delete)"
	pushObjectIDs    = false
	useStdin         = false

	// shares some global vars and functions with commmands_pre_push.go
)

func uploadsBetweenRefs(left string, right string) *lfs.TransferQueue {
	// Just use scanner here
	pointers, err := lfs.ScanRefs(left, right)
	if err != nil {
		Panic(err, "Error scanning for Git LFS files")
	}

	uploadQueue := lfs.NewUploadQueue(lfs.Config.ConcurrentTransfers(), len(pointers))

	for i, pointer := range pointers {
		if pushDryRun {
			Print("push %s", pointer.Name)
			continue
		}
		tracerx.Printf("checking_asset: %s %s %d/%d", pointer.Oid, pointer.Name, i+1, len(pointers))

		u, wErr := lfs.NewUploadable(pointer.Oid, pointer.Name)
		if wErr != nil {
			if Debugging || wErr.Panic {
				Panic(wErr.Err, wErr.Error())
			} else {
				Exit(wErr.Error())
			}
		}
		uploadQueue.Add(u)
	}

	return uploadQueue
}

func uploadsWithObjectIDs(oids []string) *lfs.TransferQueue {
	uploadQueue := lfs.NewUploadQueue(lfs.Config.ConcurrentTransfers(), len(oids))

	for _, oid := range oids {
		u, wErr := lfs.NewUploadable(oid, "")
		if wErr != nil {
			if Debugging || wErr.Panic {
				Panic(wErr.Err, wErr.Error())
			} else {
				Exit(wErr.Error())
			}
		}
		uploadQueue.Add(u)
	}

	return uploadQueue
}

// pushCommand pushes local objects to a Git LFS server.  It takes two
// arguments:
//
//   `<remote> <remote ref>`
//
// Both a remote name ("origin") or a remote URL are accepted.
//
// pushCommand calculates the git objects to send by looking comparing the range
// of commits between the local and remote git servers.
func pushCommand(cmd *cobra.Command, args []string) {
	var left, right string
	var uploadQueue *lfs.TransferQueue

	if len(args) == 0 {
		Print("Specify a remote and a remote branch name (`git lfs push origin master`)")
		os.Exit(1)
	}

	lfs.Config.CurrentRemote = args[0]

	if useStdin {
		requireStdin("Run this command from the Git pre-push hook, or leave the --stdin flag off.")

		// called from a pre-push hook!  Update the existing pre-push hook if it's
		// one that git-lfs set.
		lfs.InstallHooks(false)

		refsData, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			Panic(err, "Error reading refs on stdin")
		}

		if len(refsData) == 0 {
			return
		}

		left, right = decodeRefs(string(refsData))
		if left == pushDeleteBranch {
			return
		}

		uploadQueue = uploadsBetweenRefs(left, right)
	} else if pushObjectIDs {
		if len(args) < 2 {
			Print("Usage: git lfs push --object-id <remote> <lfs-object-id> [lfs-object-id] ...")
			return
		}

		uploadQueue = uploadsWithObjectIDs(args[1:])
	} else {
		var remoteArg, refArg string

		if len(args) < 1 {
			Print("Usage: git lfs push --dry-run <remote> [ref]")
			return
		}

		remoteArg = args[0]
		if len(args) == 2 {
			refArg = args[1]
		}

		localRef, err := git.CurrentRef()
		if err != nil {
			Panic(err, "Error getting local ref")
		}
		left = localRef

		remoteRef, err := git.LsRemote(remoteArg, refArg)
		if err != nil {
			Panic(err, "Error getting remote ref")
		}

		if remoteRef != "" {
			right = "^" + strings.Split(remoteRef, "\t")[0]
		}

		uploadQueue = uploadsBetweenRefs(left, right)
	}

	if !pushDryRun {
		uploadQueue.Process()
		for _, err := range uploadQueue.Errors() {
			if Debugging || err.Panic {
				LoggedError(err.Err, err.Error())
			} else {
				Error(err.Error())
			}
		}

		if len(uploadQueue.Errors()) > 0 {
			os.Exit(2)
		}
	}
}

func init() {
	pushCmd.Flags().BoolVarP(&pushDryRun, "dry-run", "d", false, "Do everything except actually send the updates")
	pushCmd.Flags().BoolVarP(&useStdin, "stdin", "s", false, "Take refs on stdin (for pre-push hook)")
	pushCmd.Flags().BoolVarP(&pushObjectIDs, "object-id", "o", false, "Push LFS object ID(s)")
	RootCmd.AddCommand(pushCmd)
}
<|endoftext|>


<commit_before>// Package encrypt is an interface to manage encrypted storage backends.
// It presents an unencrypted interface to callers by storing bytes in the
// provided Child client, encrypting the bytes written to it, and decrypting
// them again when requested.
//
// File objects are encrypted with an RSA public key provided in the config.
// If an RSA private key is provided, GetFile and ListFiles will perform the
// reverse operation.
//
// Chunk objects are encrypted with 256-bit AES-GCM using an AES key stored in
// the shade.File struct and a random 96-bit nonce stored with each shade.Chunk
// struct.
//
// The sha256sum of each Chunk is AES encrypted with the same key as the
// contents and a nonce which is stored in the corresponding shade.File struct.
// Unlike the Chunk, the nonce cannot be stored appended to the sha256sum, because
// it must be known in advance to retrieve the chunk.
// Nb: It is important not to reuse a nonce with the same key, thus callers must
// reset the Nonce in a shade.Chunk when updating the Sha256sum value.
//
// The sha256sum of File objects are not encrypted.  The struct contains
// sufficient internal randomness (Nonces of shade.Chunk objects, mtime, etc)
// that the sum does not leak information about the contents of the file.
package encrypt

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"

	"github.com/asjoyner/shade"
	"github.com/asjoyner/shade/drive"
)

func init() {
	drive.RegisterProvider("encrypt", NewClient)
}

// NewClient performs sanity checking and returns a Drive client.
func NewClient(c drive.Config) (drive.Client, error) {
	d := &Drive{config: c}
	var err error
	// Decode and verify RSA pub/priv keys
	if len(c.RsaPrivateKey) > 0 {
		b, _ := pem.Decode([]byte(c.RsaPrivateKey))
		if b == nil {
			return nil, fmt.Errorf("parsing PEM encoded private key from config: %s", c.RsaPrivateKey)
		}
		key, err := x509.ParsePKCS1PrivateKey(b.Bytes)
		if err != nil {
			return nil, fmt.Errorf("parsing PKCS1 private key from config: %s", err)
		}
		d.privkey = key
		d.pubkey = &key.PublicKey
	} else if len(c.RsaPublicKey) > 0 {
		b, _ := pem.Decode([]byte(c.RsaPublicKey))
		if b == nil {
			return nil, fmt.Errorf("parsing PEM encoded public key from config: %s", c.RsaPublicKey)
		}
		pubkey, err := x509.ParsePKIXPublicKey(b.Bytes)
		if err != nil {
			return nil, fmt.Errorf("failed to parse DER encoded public key from config: %s", err)
		}
		rsapubkey, ok := pubkey.(*rsa.PublicKey)
		if !ok {
			return nil, fmt.Errorf("DER encoded public key in config must be an RSA key: %s", err)
		}
		d.pubkey = rsapubkey
	} else {
		return nil, fmt.Errorf("encrypt requires that you specify either a public or private key")
	}

	// Initialize the child client
	if len(c.Children) == 0 {
		return nil, errors.New("no clients provided")
	}
	if len(c.Children) > 1 {
		return nil, errors.New("only one encrypted child is supported, you probably want a drive/cache in your config")
	}
	child, err := drive.NewClient(c.Children[0])
	if err != nil {
		return nil, fmt.Errorf("initing encrypted client %q: %s", c.Provider, err)
	}
	d.client = child
	if child.GetConfig().Write {
		d.config.Write = true
	}
	return d, nil
}

// Drive protects the contents of a single child drive.Client.  It can return a
// config which describes only its name.
//
// If any of its clients are not Local(), it reports itself as not Local() by
// returning false.  If any of its clients are Persistent(), it requires writes
// to at least one of those backends to succeed, and reports itself as
// Persistent().
type Drive struct {
	config  drive.Config
	client  drive.Client
	pubkey  *rsa.PublicKey
	privkey *rsa.PrivateKey
}

// encryptedObj is used to store shade.File objects in the child client.
type encryptedObj struct {
	Key   []byte // the symmetric key, an AES 256 key
	Bytes []byte // the provided shdae.File object
}

// ListFiles retrieves all of the File objects known to the child
// client.  The return is a list of sha256sums of the file object.  The keys
// may be passed to GetFile() to retrieve the corresponding shade.File.
func (s *Drive) ListFiles() ([][]byte, error) {
	return s.client.ListFiles()
}

// PutFile encrypts and writes the metadata describing a new file.
// It uses the following process:
//  - generates a new 256-bit AES encryption key
//  - uses the new key to Encrypt() the provided File's bytes
//  - RSA encrypts the AES key (but not the sha256sum of the File's bytes)
//  - bundles the encrypted key and encrypted bytes as an encryptedObj
//  - marshals the encryptedObj as JSON and store it in the child client, at
//    the value of the sha256sum of the plaintext
func (s *Drive) PutFile(sha256sum, f []byte) error {
	if s.config.Write == false {
		return errors.New("no clients configured to write")
	}
	key := shade.NewSymmetricKey()
	rng := rand.Reader
	encryptedKey, err := rsa.EncryptOAEP(sha256.New(), rng, s.pubkey, key[:], nil)
	if err != nil {
		return fmt.Errorf("could not encrypt key: %s", err)
	}
	encryptedBytes, err := Encrypt(f, key)
	if err != nil {
		return fmt.Errorf("encrypting file %x: %s", sha256sum, err)
	}
	// TODO: consider making this more efficient by avoiding JSON and using a
	// fixed-size prefix to store the encryptedKey, ala gcm.Seal and gcm.Open.
	jm, err := json.Marshal(encryptedObj{Key: encryptedKey, Bytes: encryptedBytes})
	if err != nil {
		return fmt.Errorf("could not marshal json: %s", err)
	}
	if err := s.client.PutFile(sha256sum, jm); err != nil {
		return fmt.Errorf("writing encrypted file: %x", sha256sum)
	}
	return nil
}

// GetFile retrieves the file object described by the sha256sum, decrypts it,
// and returns it to the caller.  It reverses the process described in
// PutFile.
func (s *Drive) GetFile(sha256sum []byte) ([]byte, error) {
	jm, err := s.client.GetFile(sha256sum)
	if err != nil {
		return nil, fmt.Errorf("reading encrypted file %x: %s", sha256sum, err)
	}
	eo := &encryptedObj{}
	// TODO: consider making this more efficient by avoiding JSON and using a
	// fixed-size prefix to store the encryptedKey, ala gcm.Seal and gcm.Open.
	if err := json.Unmarshal(jm, eo); err != nil {
		return nil, fmt.Errorf("failed to unmarshal: %v", err)
	}
	rng := rand.Reader

	keySlice, err := rsa.DecryptOAEP(sha256.New(), rng, s.privkey, eo.Key, nil)
	if err != nil {
		return nil, fmt.Errorf("could not decrypt key: %s", err)
	}
	key := &[32]byte{}
	copy(key[:], keySlice)
	plaintext, err := Decrypt(eo.Bytes, key)
	if err != nil {
		return nil, fmt.Errorf("could not decrypt contents %s", err)
	}
	if err != nil {
		return nil, fmt.Errorf("decrypting encrypted file %x: %s", sha256sum, err)
	}
	return plaintext, nil
}

// PutChunk writes a chunk associated with a SHA-256 sum.  It uses the following process:
//  - From the provided shade.File struct, retrieve:
//    - the AES key of the File
//    - the Nonce of the associated shade.Chunk struct
//  - encrypt the sha256sum with the provided Key and Nonce
//  - encrypt the bytes with the provided Key and a unique Nonce
//  - store the encrypted bytes at the encrypted sum in the child client
func (s *Drive) PutChunk(sha256sum []byte, chunkBytes []byte, f *shade.File) error {
	if s.config.Write == false {
		return errors.New("no clients configured to write")
	}
	if f == nil {
		return errors.New("no file provided")
	}
	if f.AesKey == nil {
		return errors.New("no AES encryption key for file")
	}
	encBytes, err := Encrypt(chunkBytes, f.AesKey)
	if err != nil {
		return fmt.Errorf("encrypting file: %x", sha256sum)
	}
	encryptedSum, err := GetEncryptedSum(sha256sum, f)
	if err != nil {
		return fmt.Errorf("encrypting sha256sum %x: %s", sha256sum, err)
	}

	if err := s.client.PutChunk(encryptedSum, encBytes, f); err != nil {
		return fmt.Errorf("writing encrypted file %x: %s", sha256sum, err)
	}
	return nil
}

// GetChunk retrieves and decrypts the chunk with a given SHA-256 sum.
// It reverses the process of PutChunk, in particular, leveraging the stored
// Nonce to be able to find the encrypted sha256sum in the child client.
func (s *Drive) GetChunk(sha256sum []byte, f *shade.File) ([]byte, error) {
	encryptedSum, err := GetEncryptedSum(sha256sum, f)
	if err != nil {
		return nil, fmt.Errorf("encrypting sha256sum %x: %s", sha256sum, err)
	}

	encBytes, err := s.client.GetChunk(encryptedSum, f)
	if err != nil {
		return nil, err
	}
	chunkBytes, err := Decrypt(encBytes, f.AesKey)
	if err != nil {
		return nil, fmt.Errorf("decrypting file %x: %s", sha256sum, err)
	}
	return chunkBytes, nil
}

// GetEncryptedSum calculates the encrypted sha256sum that a chunk will be
// stored at, for a given chunk in a given file.  It is used both by PutChunk
// to store the chunk, and later by GetChunk to find it again.
func GetEncryptedSum(sha256sum []byte, f *shade.File) (encryptedSum []byte, err error) {
	var nonce []byte
	// Find the chunk's Nonce
	for _, chunk := range f.Chunks {
		if bytes.Equal(chunk.Sha256, sha256sum) {
			if chunk.Nonce == nil {
				return nil, fmt.Errorf("no Nonce in Chunk: %x", sha256sum)
			}
			nonce = chunk.Nonce
		}
	}
	if nonce == nil {
		return nil, fmt.Errorf("no corresponding Chunk in File: %x", sha256sum)
	}
	return encryptUnsafe(sha256sum, f.AesKey, nonce)
}

// Encrypt encrypts data using 256-bit AES-GCM.  This both hides the content of
// the data and provides a check that it hasn't been altered. Output takes the
// form nonce|ciphertext|tag where '|' indicates concatenation.
func Encrypt(plaintext []byte, key *[32]byte) (ciphertext []byte, err error) {
	return encryptUnsafe(plaintext, key, shade.NewNonce())
}

// encryptUnsafe is the internal implementation of Encrypt().  It  allows you
// to specify the key AND the nonce.  Use with caution: you must not encrypt
// two different messages with the same key and nonce!
func encryptUnsafe(plaintext []byte, key *[32]byte, nonce []byte) (ciphertext []byte, err error) {
	if key == nil {
		return nil, fmt.Errorf("no key provided")
	}
	block, err := aes.NewCipher(key[:])
	if err != nil {
		return nil, err
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}

	if len(nonce) != gcm.NonceSize() {
		return nil, fmt.Errorf("Invalid nonce size, want: %d got %d", gcm.NonceSize(), len(nonce))
	}

	return gcm.Seal(nonce, nonce, plaintext, nil), nil
}

// Decrypt decrypts data using 256-bit AES-GCM.  This both hides the content of
// the data and provides a check that it hasn't been altered. Expects input
// form nonce|ciphertext|tag where '|' indicates concatenation.
func Decrypt(ciphertext []byte, key *[32]byte) (plaintext []byte, err error) {
	block, err := aes.NewCipher(key[:])
	if err != nil {
		return nil, err
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}

	if len(ciphertext) < gcm.NonceSize() {
		return nil, errors.New("malformed ciphertext")
	}

	return gcm.Open(nil,
		ciphertext[:gcm.NonceSize()],
		ciphertext[gcm.NonceSize():],
		nil,
	)
}

// GetConfig returns the config used to initialize this client.
func (s *Drive) GetConfig() drive.Config {
	return s.config
}

// Local returns true only if the configured backend is local to this machine.
func (s *Drive) Local() bool {
	return s.client.Local()
}

// Persistent returns true if the configured storage backend is Persistent().
func (s *Drive) Persistent() bool {
	return s.client.Persistent()
}
<commit_msg>Always check file pointer != nil before use.<commit_after>// Package encrypt is an interface to manage encrypted storage backends.
// It presents an unencrypted interface to callers by storing bytes in the
// provided Child client, encrypting the bytes written to it, and decrypting
// them again when requested.
//
// File objects are encrypted with an RSA public key provided in the config.
// If an RSA private key is provided, GetFile and ListFiles will perform the
// reverse operation.
//
// Chunk objects are encrypted with 256-bit AES-GCM using an AES key stored in
// the shade.File struct and a random 96-bit nonce stored with each shade.Chunk
// struct.
//
// The sha256sum of each Chunk is AES encrypted with the same key as the
// contents and a nonce which is stored in the corresponding shade.File struct.
// Unlike the Chunk, the nonce cannot be stored appended to the sha256sum, because
// it must be known in advance to retrieve the chunk.
// Nb: It is important not to reuse a nonce with the same key, thus callers must
// reset the Nonce in a shade.Chunk when updating the Sha256sum value.
//
// The sha256sum of File objects are not encrypted.  The struct contains
// sufficient internal randomness (Nonces of shade.Chunk objects, mtime, etc)
// that the sum does not leak information about the contents of the file.
package encrypt

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"

	"github.com/asjoyner/shade"
	"github.com/asjoyner/shade/drive"
)

func init() {
	drive.RegisterProvider("encrypt", NewClient)
}

// NewClient performs sanity checking and returns a Drive client.
func NewClient(c drive.Config) (drive.Client, error) {
	d := &Drive{config: c}
	var err error
	// Decode and verify RSA pub/priv keys
	if len(c.RsaPrivateKey) > 0 {
		b, _ := pem.Decode([]byte(c.RsaPrivateKey))
		if b == nil {
			return nil, fmt.Errorf("parsing PEM encoded private key from config: %s", c.RsaPrivateKey)
		}
		key, err := x509.ParsePKCS1PrivateKey(b.Bytes)
		if err != nil {
			return nil, fmt.Errorf("parsing PKCS1 private key from config: %s", err)
		}
		d.privkey = key
		d.pubkey = &key.PublicKey
	} else if len(c.RsaPublicKey) > 0 {
		b, _ := pem.Decode([]byte(c.RsaPublicKey))
		if b == nil {
			return nil, fmt.Errorf("parsing PEM encoded public key from config: %s", c.RsaPublicKey)
		}
		pubkey, err := x509.ParsePKIXPublicKey(b.Bytes)
		if err != nil {
			return nil, fmt.Errorf("failed to parse DER encoded public key from config: %s", err)
		}
		rsapubkey, ok := pubkey.(*rsa.PublicKey)
		if !ok {
			return nil, fmt.Errorf("DER encoded public key in config must be an RSA key: %s", err)
		}
		d.pubkey = rsapubkey
	} else {
		return nil, fmt.Errorf("encrypt requires that you specify either a public or private key")
	}

	// Initialize the child client
	if len(c.Children) == 0 {
		return nil, errors.New("no clients provided")
	}
	if len(c.Children) > 1 {
		return nil, errors.New("only one encrypted child is supported, you probably want a drive/cache in your config")
	}
	child, err := drive.NewClient(c.Children[0])
	if err != nil {
		return nil, fmt.Errorf("initing encrypted client %q: %s", c.Provider, err)
	}
	d.client = child
	if child.GetConfig().Write {
		d.config.Write = true
	}
	return d, nil
}

// Drive protects the contents of a single child drive.Client.  It can return a
// config which describes only its name.
//
// If any of its clients are not Local(), it reports itself as not Local() by
// returning false.  If any of its clients are Persistent(), it requires writes
// to at least one of those backends to succeed, and reports itself as
// Persistent().
type Drive struct {
	config  drive.Config
	client  drive.Client
	pubkey  *rsa.PublicKey
	privkey *rsa.PrivateKey
}

// encryptedObj is used to store shade.File objects in the child client.
type encryptedObj struct {
	Key   []byte // the symmetric key, an AES 256 key
	Bytes []byte // the provided shdae.File object
}

// ListFiles retrieves all of the File objects known to the child
// client.  The return is a list of sha256sums of the file object.  The keys
// may be passed to GetFile() to retrieve the corresponding shade.File.
func (s *Drive) ListFiles() ([][]byte, error) {
	return s.client.ListFiles()
}

// PutFile encrypts and writes the metadata describing a new file.
// It uses the following process:
//  - generates a new 256-bit AES encryption key
//  - uses the new key to Encrypt() the provided File's bytes
//  - RSA encrypts the AES key (but not the sha256sum of the File's bytes)
//  - bundles the encrypted key and encrypted bytes as an encryptedObj
//  - marshals the encryptedObj as JSON and store it in the child client, at
//    the value of the sha256sum of the plaintext
func (s *Drive) PutFile(sha256sum, f []byte) error {
	if s.config.Write == false {
		return errors.New("no clients configured to write")
	}
	key := shade.NewSymmetricKey()
	rng := rand.Reader
	encryptedKey, err := rsa.EncryptOAEP(sha256.New(), rng, s.pubkey, key[:], nil)
	if err != nil {
		return fmt.Errorf("could not encrypt key: %s", err)
	}
	encryptedBytes, err := Encrypt(f, key)
	if err != nil {
		return fmt.Errorf("encrypting file %x: %s", sha256sum, err)
	}
	// TODO: consider making this more efficient by avoiding JSON and using a
	// fixed-size prefix to store the encryptedKey, ala gcm.Seal and gcm.Open.
	jm, err := json.Marshal(encryptedObj{Key: encryptedKey, Bytes: encryptedBytes})
	if err != nil {
		return fmt.Errorf("could not marshal json: %s", err)
	}
	if err := s.client.PutFile(sha256sum, jm); err != nil {
		return fmt.Errorf("writing encrypted file: %x", sha256sum)
	}
	return nil
}

// GetFile retrieves the file object described by the sha256sum, decrypts it,
// and returns it to the caller.  It reverses the process described in
// PutFile.
func (s *Drive) GetFile(sha256sum []byte) ([]byte, error) {
	jm, err := s.client.GetFile(sha256sum)
	if err != nil {
		return nil, fmt.Errorf("reading encrypted file %x: %s", sha256sum, err)
	}
	eo := &encryptedObj{}
	// TODO: consider making this more efficient by avoiding JSON and using a
	// fixed-size prefix to store the encryptedKey, ala gcm.Seal and gcm.Open.
	if err := json.Unmarshal(jm, eo); err != nil {
		return nil, fmt.Errorf("failed to unmarshal: %v", err)
	}
	rng := rand.Reader

	keySlice, err := rsa.DecryptOAEP(sha256.New(), rng, s.privkey, eo.Key, nil)
	if err != nil {
		return nil, fmt.Errorf("could not decrypt key: %s", err)
	}
	key := &[32]byte{}
	copy(key[:], keySlice)
	plaintext, err := Decrypt(eo.Bytes, key)
	if err != nil {
		return nil, fmt.Errorf("could not decrypt contents %s", err)
	}
	if err != nil {
		return nil, fmt.Errorf("decrypting encrypted file %x: %s", sha256sum, err)
	}
	return plaintext, nil
}

// PutChunk writes a chunk associated with a SHA-256 sum.  It uses the following process:
//  - From the provided shade.File struct, retrieve:
//    - the AES key of the File
//    - the Nonce of the associated shade.Chunk struct
//  - encrypt the sha256sum with the provided Key and Nonce
//  - encrypt the bytes with the provided Key and a unique Nonce
//  - store the encrypted bytes at the encrypted sum in the child client
func (s *Drive) PutChunk(sha256sum []byte, chunkBytes []byte, f *shade.File) error {
	if f == nil {
		return errors.New("provide a file pointer to Put an encrypted chunk")
	}
	if s.config.Write == false {
		return errors.New("no clients configured to write")
	}
	if f.AesKey == nil {
		return errors.New("no AES encryption key for file")
	}
	encBytes, err := Encrypt(chunkBytes, f.AesKey)
	if err != nil {
		return fmt.Errorf("encrypting file: %x", sha256sum)
	}
	encryptedSum, err := GetEncryptedSum(sha256sum, f)
	if err != nil {
		return fmt.Errorf("encrypting sha256sum %x: %s", sha256sum, err)
	}

	if err := s.client.PutChunk(encryptedSum, encBytes, f); err != nil {
		return fmt.Errorf("writing encrypted file %x: %s", sha256sum, err)
	}
	return nil
}

// GetChunk retrieves and decrypts the chunk with a given SHA-256 sum.
// It reverses the process of PutChunk, in particular, leveraging the stored
// Nonce to be able to find the encrypted sha256sum in the child client.
func (s *Drive) GetChunk(sha256sum []byte, f *shade.File) ([]byte, error) {
	if f == nil {
		return nil, errors.New("provide a file pointer to Get an encrypted chunk")
	}
	encryptedSum, err := GetEncryptedSum(sha256sum, f)
	if err != nil {
		return nil, fmt.Errorf("encrypting sha256sum %x: %s", sha256sum, err)
	}

	encBytes, err := s.client.GetChunk(encryptedSum, f)
	if err != nil {
		return nil, err
	}
	chunkBytes, err := Decrypt(encBytes, f.AesKey)
	if err != nil {
		return nil, fmt.Errorf("decrypting file %x: %s", sha256sum, err)
	}
	return chunkBytes, nil
}

// GetEncryptedSum calculates the encrypted sha256sum that a chunk will be
// stored at, for a given chunk in a given file.  It is used both by PutChunk
// to store the chunk, and later by GetChunk to find it again.
func GetEncryptedSum(sha256sum []byte, f *shade.File) (encryptedSum []byte, err error) {
	if f == nil {
		return nil, errors.New("provide a file pointer to Get an encrypted chunk")
	}
	var nonce []byte
	// Find the chunk's Nonce
	for _, chunk := range f.Chunks {
		if bytes.Equal(chunk.Sha256, sha256sum) {
			if chunk.Nonce == nil {
				return nil, fmt.Errorf("no Nonce in Chunk: %x", sha256sum)
			}
			nonce = chunk.Nonce
		}
	}
	if nonce == nil {
		return nil, fmt.Errorf("no corresponding Chunk in File: %x", sha256sum)
	}
	return encryptUnsafe(sha256sum, f.AesKey, nonce)
}

// Encrypt encrypts data using 256-bit AES-GCM.  This both hides the content of
// the data and provides a check that it hasn't been altered. Output takes the
// form nonce|ciphertext|tag where '|' indicates concatenation.
func Encrypt(plaintext []byte, key *[32]byte) (ciphertext []byte, err error) {
	return encryptUnsafe(plaintext, key, shade.NewNonce())
}

// encryptUnsafe is the internal implementation of Encrypt().  It  allows you
// to specify the key AND the nonce.  Use with caution: you must not encrypt
// two different messages with the same key and nonce!
func encryptUnsafe(plaintext []byte, key *[32]byte, nonce []byte) (ciphertext []byte, err error) {
	if key == nil {
		return nil, fmt.Errorf("no key provided")
	}
	block, err := aes.NewCipher(key[:])
	if err != nil {
		return nil, err
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}

	if len(nonce) != gcm.NonceSize() {
		return nil, fmt.Errorf("Invalid nonce size, want: %d got %d", gcm.NonceSize(), len(nonce))
	}

	return gcm.Seal(nonce, nonce, plaintext, nil), nil
}

// Decrypt decrypts data using 256-bit AES-GCM.  This both hides the content of
// the data and provides a check that it hasn't been altered. Expects input
// form nonce|ciphertext|tag where '|' indicates concatenation.
func Decrypt(ciphertext []byte, key *[32]byte) (plaintext []byte, err error) {
	block, err := aes.NewCipher(key[:])
	if err != nil {
		return nil, err
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}

	if len(ciphertext) < gcm.NonceSize() {
		return nil, errors.New("malformed ciphertext")
	}

	return gcm.Open(nil,
		ciphertext[:gcm.NonceSize()],
		ciphertext[gcm.NonceSize():],
		nil,
	)
}

// GetConfig returns the config used to initialize this client.
func (s *Drive) GetConfig() drive.Config {
	return s.config
}

// Local returns true only if the configured backend is local to this machine.
func (s *Drive) Local() bool {
	return s.client.Local()
}

// Persistent returns true if the configured storage backend is Persistent().
func (s *Drive) Persistent() bool {
	return s.client.Persistent()
}
<|endoftext|>