<commit_before>package cddb

import (
	"fmt"
	"log"
	"strconv"
)

type QueryCmd struct {
	discID       string
	trackCount   int
	offsets      []int
	totalSeconds int
	language     string
	country      string
}

type ReadCmd struct {
	category string
	discID   string
	language string
	country  string
}

var syntaxError error = fmt.Errorf("%v", cddbStatus(500, "Command syntax error", true))

func logSyntaxError(cmdArray []string) {
	log.Println("syntax error:")
	log.Println(cmdArray)
}

func createQueryCmd(cmdArray []string) (queryCmd QueryCmd, err error) {
	if len(cmdArray) < 4 {
		logSyntaxError(cmdArray)
		return QueryCmd{}, syntaxError
	}

	queryCmd.discID = cmdArray[0]
	queryCmd.trackCount, err = strconv.Atoi(cmdArray[1])
	if err != nil {
		logSyntaxError(cmdArray)
		return QueryCmd{}, syntaxError
	}

	if len(cmdArray[2:len(cmdArray)-1]) != queryCmd.trackCount {
		logSyntaxError(cmdArray)
		return QueryCmd{}, syntaxError
	}

	for i := 0; i < queryCmd.trackCount; i++ {
		offset, err := strconv.Atoi(cmdArray[i+2])
		if err != nil {
			logSyntaxError(cmdArray)
			return QueryCmd{}, syntaxError
		}
		queryCmd.offsets = append(queryCmd.offsets, offset)
	}

	queryCmd.totalSeconds, err = strconv.Atoi(cmdArray[len(cmdArray)-1])
	if err != nil {
		logSyntaxError(cmdArray)
		return QueryCmd{}, syntaxError
	}

	queryCmd.offsets = append(queryCmd.offsets, queryCmd.totalSeconds*75)

	if queryCmd.offsets[0] == 0 {
		for i := range queryCmd.offsets {
			queryCmd.offsets[i] += 150
		}
	}

	return queryCmd, nil
}

func createReadCmd(cmdArray []string) (readCmd ReadCmd, err error) {
	if len(cmdArray) != 2 {
		logSyntaxError(cmdArray)
		return ReadCmd{}, syntaxError
	}

	readCmd.category = cmdArray[0]
	readCmd.discID = cmdArray[1]

	return readCmd, nil
}
<commit_msg>properly allocate slice<commit_after>package cddb

import (
	"fmt"
	"log"
	"strconv"
)

type QueryCmd struct {
	discID       string
	trackCount   int
	offsets      []int
	totalSeconds int
	language     string
	country      string
}

type ReadCmd struct {
	category string
	discID   string
	language string
	country  string
}

var syntaxError error = fmt.Errorf("%v", cddbStatus(500, "Command syntax error", true))

func logSyntaxError(cmdArray []string) {
	log.Println("syntax error:")
	log.Println(cmdArray)
}

func createQueryCmd(cmdArray []string) (queryCmd QueryCmd, err error) {
	if len(cmdArray) < 4 {
		logSyntaxError(cmdArray)
		return QueryCmd{}, syntaxError
	}

	queryCmd.discID = cmdArray[0]
	queryCmd.trackCount, err = strconv.Atoi(cmdArray[1])
	if err != nil {
		logSyntaxError(cmdArray)
		return QueryCmd{}, syntaxError
	}

	if len(cmdArray[2:len(cmdArray)-1]) != queryCmd.trackCount {
		logSyntaxError(cmdArray)
		return QueryCmd{}, syntaxError
	}

	queryCmd.offsets = make([]int, queryCmd.trackCount+1)

	for i := 0; i < queryCmd.trackCount; i++ {
		offset, err := strconv.Atoi(cmdArray[i+2])
		if err != nil {
			logSyntaxError(cmdArray)
			return QueryCmd{}, syntaxError
		}
		queryCmd.offsets[i] = offset
	}

	queryCmd.totalSeconds, err = strconv.Atoi(cmdArray[len(cmdArray)-1])
	if err != nil {
		logSyntaxError(cmdArray)
		return QueryCmd{}, syntaxError
	}

	queryCmd.offsets[len(queryCmd.offsets)-1] = queryCmd.totalSeconds * 75

	if queryCmd.offsets[0] == 0 {
		for i := range queryCmd.offsets {
			queryCmd.offsets[i] += 150
		}
	}

	return queryCmd, nil
}

func createReadCmd(cmdArray []string) (readCmd ReadCmd, err error) {
	if len(cmdArray) != 2 {
		logSyntaxError(cmdArray)
		return ReadCmd{}, syntaxError
	}

	readCmd.category = cmdArray[0]
	readCmd.discID = cmdArray[1]

	return readCmd, nil
}
<|endoftext|>


<commit_before>package spf

import (
	"net"
	"sync"

	"github.com/miekg/dns"
)

// NewMiekgDNSResolver returns new instance of Resolver
func NewMiekgDNSResolver(addr string) Resolver {
	return &MiekgDNSResolver{
		client:     new(dns.Client),
		serverAddr: addr,
	}
}

// MiekgDNSResolver implements Resolver using github.com/miekg/dns
type MiekgDNSResolver struct {
	client     *dns.Client
	serverAddr string
}

// If the DNS lookup returns a server failure (RCODE 2) or some other
// error (RCODE other than 0 or 3), or if the lookup times out, then
// check_host() terminates immediately with the result "temperror".
func (r *MiekgDNSResolver) exchange(req *dns.Msg) (*dns.Msg, error) {
	res, _, err := r.client.Exchange(req, r.serverAddr)
	if err != nil {
		return nil, ErrDNSTemperror
	}
	if res.Rcode == dns.RcodeNameError {
		return nil, ErrDNSPermerror
	}
	if res.Rcode != dns.RcodeSuccess {
		return nil, ErrDNSTemperror
	}
	return res, nil
}

// LookupTXT returns the DNS TXT records for the given domain name.
func (r *MiekgDNSResolver) LookupTXT(name string) ([]string, error) {
	req := new(dns.Msg)
	req.SetQuestion(name, dns.TypeTXT)

	res, err := r.exchange(req)
	if err != nil {
		return nil, err
	}

	txts := make([]string, 0, len(res.Answer))
	for _, a := range res.Answer {
		if r, ok := a.(*dns.TXT); ok {
			txts = append(txts, r.Txt...)
		}
	}
	return txts, nil
}

// Exists is used for a DNS A RR lookup (even when the
// connection type is IPv6).  If any A record is returned, this
// mechanism matches.
func (r *MiekgDNSResolver) Exists(name string) (bool, error) {
	req := new(dns.Msg)
	req.SetQuestion(name, dns.TypeA)

	res, err := r.exchange(req)
	if err != nil {
		return false, err
	}

	return len(res.Answer) > 0, nil
}

func matchIP(rrs []dns.RR, matcher IPMatcherFunc) (bool, error) {
	for _, rr := range rrs {
		var ip net.IP
		switch a := rr.(type) {
		case *dns.A:
			ip = a.A
		case *dns.AAAA:
			ip = a.AAAA
		}
		if m, e := matcher(ip); m || e != nil {
			return m, e
		}
	}
	return false, nil
}

// MatchIP provides an address lookup, which should be done on the name
// using the type of lookup (A or AAAA).
// Then IPMatcherFunc used to compare checked IP to the returned address(es).
// If any address matches, the mechanism matches
func (r *MiekgDNSResolver) MatchIP(name string, matcher IPMatcherFunc) (bool, error) {
	var wg sync.WaitGroup
	hits := make(chan hit)

	for _, qType := range []uint16{dns.TypeA, dns.TypeAAAA} {
		wg.Add(1)
		go func(qType uint16) {
			defer wg.Done()

			req := new(dns.Msg)
			req.SetQuestion(name, qType)
			res, err := r.exchange(req)
			if err != nil {
				hits <- hit{false, err}
				return
			}

			if m, e := matchIP(res.Answer, matcher); m || e != nil {
				hits <- hit{m, e}
				return
			}
		}(qType)
	}

	go func() {
		wg.Wait()
		close(hits)
	}()

	for h := range hits {
		if h.found || h.err != nil {
			return h.found, h.err
		}
	}

	return false, nil
}

// MatchMX is similar to MatchIP but first performs an MX lookup on the
// name.  Then it performs an address lookup on each MX name returned.
// Then IPMatcherFunc used to compare checked IP to the returned address(es).
// If any address matches, the mechanism matches
func (r *MiekgDNSResolver) MatchMX(name string, matcher IPMatcherFunc) (bool, error) {
	req := new(dns.Msg)
	req.SetQuestion(name, dns.TypeMX)

	res, err := r.exchange(req)
	if err != nil {
		return false, err
	}

	var wg sync.WaitGroup
	hits := make(chan hit)

	for _, rr := range res.Answer {
		mx, ok := rr.(*dns.MX)
		if !ok {
			continue
		}
		wg.Add(1)
		go func(name string) {
			found, err := r.MatchIP(name, matcher)
			hits <- hit{found, err}
			wg.Done()
		}(mx.Mx)
	}

	go func() {
		wg.Wait()
		close(hits)
	}()

	for h := range hits {
		if h.found || h.err != nil {
			return h.found, h.err
		}
	}

	return false, nil
}
<commit_msg>Add mutex to MiekgDNSResolver<commit_after>package spf

import (
	"net"
	"sync"

	"github.com/miekg/dns"
)

// NewMiekgDNSResolver returns new instance of Resolver
func NewMiekgDNSResolver(addr string) Resolver {
	return &MiekgDNSResolver{
		client:     new(dns.Client),
		serverAddr: addr,
	}
}

// MiekgDNSResolver implements Resolver using github.com/miekg/dns
type MiekgDNSResolver struct {
	mu         sync.Mutex
	client     *dns.Client
	serverAddr string
}

// If the DNS lookup returns a server failure (RCODE 2) or some other
// error (RCODE other than 0 or 3), or if the lookup times out, then
// check_host() terminates immediately with the result "temperror".
func (r *MiekgDNSResolver) exchange(req *dns.Msg) (*dns.Msg, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	res, _, err := r.client.Exchange(req, r.serverAddr)
	if err != nil {
		return nil, ErrDNSTemperror
	}
	if res.Rcode == dns.RcodeNameError {
		return nil, ErrDNSPermerror
	}
	if res.Rcode != dns.RcodeSuccess {
		return nil, ErrDNSTemperror
	}
	return res, nil
}

// LookupTXT returns the DNS TXT records for the given domain name.
func (r *MiekgDNSResolver) LookupTXT(name string) ([]string, error) {
	req := new(dns.Msg)
	req.SetQuestion(name, dns.TypeTXT)

	res, err := r.exchange(req)
	if err != nil {
		return nil, err
	}

	txts := make([]string, 0, len(res.Answer))
	for _, a := range res.Answer {
		if r, ok := a.(*dns.TXT); ok {
			txts = append(txts, r.Txt...)
		}
	}
	return txts, nil
}

// Exists is used for a DNS A RR lookup (even when the
// connection type is IPv6).  If any A record is returned, this
// mechanism matches.
func (r *MiekgDNSResolver) Exists(name string) (bool, error) {
	req := new(dns.Msg)
	req.SetQuestion(name, dns.TypeA)

	res, err := r.exchange(req)
	if err != nil {
		return false, err
	}

	return len(res.Answer) > 0, nil
}

func matchIP(rrs []dns.RR, matcher IPMatcherFunc) (bool, error) {
	for _, rr := range rrs {
		var ip net.IP
		switch a := rr.(type) {
		case *dns.A:
			ip = a.A
		case *dns.AAAA:
			ip = a.AAAA
		}
		if m, e := matcher(ip); m || e != nil {
			return m, e
		}
	}
	return false, nil
}

// MatchIP provides an address lookup, which should be done on the name
// using the type of lookup (A or AAAA).
// Then IPMatcherFunc used to compare checked IP to the returned address(es).
// If any address matches, the mechanism matches
func (r *MiekgDNSResolver) MatchIP(name string, matcher IPMatcherFunc) (bool, error) {
	var wg sync.WaitGroup
	hits := make(chan hit)

	for _, qType := range []uint16{dns.TypeA, dns.TypeAAAA} {
		wg.Add(1)
		go func(qType uint16) {
			defer wg.Done()

			req := new(dns.Msg)
			req.SetQuestion(name, qType)
			res, err := r.exchange(req)
			if err != nil {
				hits <- hit{false, err}
				return
			}

			if m, e := matchIP(res.Answer, matcher); m || e != nil {
				hits <- hit{m, e}
				return
			}
		}(qType)
	}

	go func() {
		wg.Wait()
		close(hits)
	}()

	for h := range hits {
		if h.found || h.err != nil {
			return h.found, h.err
		}
	}

	return false, nil
}

// MatchMX is similar to MatchIP but first performs an MX lookup on the
// name.  Then it performs an address lookup on each MX name returned.
// Then IPMatcherFunc used to compare checked IP to the returned address(es).
// If any address matches, the mechanism matches
func (r *MiekgDNSResolver) MatchMX(name string, matcher IPMatcherFunc) (bool, error) {
	req := new(dns.Msg)
	req.SetQuestion(name, dns.TypeMX)

	res, err := r.exchange(req)
	if err != nil {
		return false, err
	}

	var wg sync.WaitGroup
	hits := make(chan hit)

	for _, rr := range res.Answer {
		mx, ok := rr.(*dns.MX)
		if !ok {
			continue
		}
		wg.Add(1)
		go func(name string) {
			found, err := r.MatchIP(name, matcher)
			hits <- hit{found, err}
			wg.Done()
		}(mx.Mx)
	}

	go func() {
		wg.Wait()
		close(hits)
	}()

	for h := range hits {
		if h.found || h.err != nil {
			return h.found, h.err
		}
	}

	return false, nil
}
<|endoftext|>


<commit_before>package standard

import (
	"io"
	"io/ioutil"
	"mime/multipart"
	"net"
	"net/http"
	"strings"

	"github.com/webx-top/echo"
	"github.com/webx-top/echo/engine"
)

var defaultMaxRequestBodySize int64 = 32 << 20 // 32 MB

type Request struct {
	config  *engine.Config
	request *http.Request
	url     engine.URL
	header  engine.Header
	value   *Value
	realIP  string
}

func NewRequest(r *http.Request) *Request {
	req := &Request{
		request: r,
		url:     &URL{url: r.URL},
		header:  &Header{r.Header},
	}
	req.value = NewValue(req)
	return req
}

func (r *Request) Host() string {
	return r.request.Host
}

func (r *Request) URL() engine.URL {
	return r.url
}

func (r *Request) Header() engine.Header {
	return r.header
}

func (r *Request) Proto() string {
	return r.request.Proto
}

//
// func ProtoMajor() int {
// 	return r.request.ProtoMajor()
// }
//
// func ProtoMinor() int {
// 	return r.request.ProtoMinor()
// }

func (r *Request) RemoteAddress() string {
	return r.request.RemoteAddr
}

// RealIP implements `engine.Request#RealIP` function.
func (r *Request) RealIP() string {
	if len(r.realIP) > 0 {
		return r.realIP
	}
	r.realIP = r.RemoteAddress()
	if ip := r.header.Get(echo.HeaderXForwardedFor); len(ip) > 0 {
		ip = strings.TrimSpace(strings.SplitN(ip, ",", 2)[0])
		r.realIP = ip
	} else if ip := r.header.Get(echo.HeaderXRealIP); len(ip) > 0 {
		r.realIP = ip
	} else {
		r.realIP, _, _ = net.SplitHostPort(r.realIP)
	}
	return r.realIP
}

func (r *Request) Method() string {
	return r.request.Method
}

func (r *Request) SetMethod(method string) {
	r.request.Method = method
}

func (r *Request) URI() string {
	return r.request.RequestURI
}

// SetURI implements `engine.Request#SetURI` function.
func (r *Request) SetURI(uri string) {
	r.request.RequestURI = uri
}

func (r *Request) Body() io.ReadCloser {
	return r.request.Body
}

// SetBody implements `engine.Request#SetBody` function.
func (r *Request) SetBody(reader io.Reader) {
	if readCloser, ok := reader.(io.ReadCloser); ok {
		r.request.Body = readCloser
	} else {
		r.request.Body = ioutil.NopCloser(reader)
	}
}

func (r *Request) FormValue(name string) string {
	return r.request.FormValue(name)
}

func (r *Request) Form() engine.URLValuer {
	return r.value
}

func (r *Request) PostForm() engine.URLValuer {
	return r.value.postArgs
}

func (r *Request) MultipartForm() *multipart.Form {
	if r.request.MultipartForm == nil {
		maxMemory := defaultMaxRequestBodySize
		if r.config != nil && r.config.MaxRequestBodySize != 0 {
			maxMemory = int64(r.config.MaxRequestBodySize)
		}
		r.request.ParseMultipartForm(maxMemory)
	}
	return r.request.MultipartForm
}

func (r *Request) IsTLS() bool {
	return r.request.TLS != nil
}

func (r *Request) Cookie(key string) string {
	if cookie, err := r.request.Cookie(key); err == nil {
		return cookie.Value
	}
	return ``
}

func (r *Request) Referer() string {
	return r.request.Referer()
}

func (r *Request) UserAgent() string {
	return r.request.UserAgent()
}

func (r *Request) Object() interface{} {
	return r.request
}

func (r *Request) reset(req *http.Request, h engine.Header, u engine.URL) {
	r.request = req
	r.header = h
	r.url = u
	r.value = NewValue(r)
	r.realIP = ``
}

func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
	file, fileHeader, err := r.request.FormFile(key)
	if err != nil {
		return nil, nil, err
	}
	return file, fileHeader, err
}

// Size implements `engine.Request#ContentLength` function.
func (r *Request) Size() int64 {
	return r.request.ContentLength
}

func (r *Request) Scheme() string {
	if r.IsTLS() {
		return `https`
	}
	return `http`
}

func (r *Request) BasicAuth() (username, password string, ok bool) {
	return r.request.BasicAuth()
}

// SetHost implements `engine.Request#SetHost` function.
func (r *Request) SetHost(host string) {
	r.request.Host = host
}

func (r *Request) StdRequest() *http.Request {
	return r.request
}
<commit_msg>update<commit_after>package standard

import (
	"io"
	"io/ioutil"
	"mime/multipart"
	"net"
	"net/http"
	"strings"

	"github.com/webx-top/echo"
	"github.com/webx-top/echo/engine"
)

var defaultMaxRequestBodySize int64 = 32 << 20 // 32 MB

type Request struct {
	config  *engine.Config
	request *http.Request
	url     engine.URL
	header  engine.Header
	value   *Value
	realIP  string
}

func NewRequest(r *http.Request) *Request {
	req := &Request{
		request: r,
		url:     &URL{url: r.URL},
		header:  &Header{r.Header},
	}
	req.value = NewValue(req)
	return req
}

func (r *Request) Host() string {
	return r.request.Host
}

func (r *Request) URL() engine.URL {
	return r.url
}

func (r *Request) Header() engine.Header {
	return r.header
}

func (r *Request) Proto() string {
	return r.request.Proto
}

//
// func ProtoMajor() int {
// 	return r.request.ProtoMajor()
// }
//
// func ProtoMinor() int {
// 	return r.request.ProtoMinor()
// }

func (r *Request) RemoteAddress() string {
	return r.request.RemoteAddr
}

// RealIP implements `engine.Request#RealIP` function.
func (r *Request) RealIP() string {
	if len(r.realIP) > 0 {
		return r.realIP
	}
	r.realIP = r.RemoteAddress()
	if ip := r.header.Get(echo.HeaderXForwardedFor); len(ip) > 0 {
		ip = strings.TrimSpace(strings.SplitN(ip, ",", 2)[0])
		r.realIP = ip
	} else if ip := r.header.Get(echo.HeaderXRealIP); len(ip) > 0 {
		r.realIP = ip
	} else {
		r.realIP, _, _ = net.SplitHostPort(r.realIP)
	}
	return r.realIP
}

func (r *Request) Method() string {
	return r.request.Method
}

func (r *Request) SetMethod(method string) {
	r.request.Method = method
}

func (r *Request) URI() string {
	return r.request.RequestURI
}

// SetURI implements `engine.Request#SetURI` function.
func (r *Request) SetURI(uri string) {
	r.request.RequestURI = uri
}

func (r *Request) Body() io.ReadCloser {
	return r.request.Body
}

// SetBody implements `engine.Request#SetBody` function.
func (r *Request) SetBody(reader io.Reader) {
	if readCloser, ok := reader.(io.ReadCloser); ok {
		r.request.Body = readCloser
	} else {
		r.request.Body = ioutil.NopCloser(reader)
	}
}

func (r *Request) FormValue(name string) string {
	r.MultipartForm()
	return r.request.FormValue(name)
}

func (r *Request) Form() engine.URLValuer {
	return r.value
}

func (r *Request) PostForm() engine.URLValuer {
	return r.value.postArgs
}

func (r *Request) MultipartForm() *multipart.Form {
	if r.request.MultipartForm == nil {
		maxMemory := defaultMaxRequestBodySize
		if r.config != nil && r.config.MaxRequestBodySize != 0 {
			maxMemory = int64(r.config.MaxRequestBodySize)
		}
		r.request.ParseMultipartForm(maxMemory)
	}
	return r.request.MultipartForm
}

func (r *Request) IsTLS() bool {
	return r.request.TLS != nil
}

func (r *Request) Cookie(key string) string {
	if cookie, err := r.request.Cookie(key); err == nil {
		return cookie.Value
	}
	return ``
}

func (r *Request) Referer() string {
	return r.request.Referer()
}

func (r *Request) UserAgent() string {
	return r.request.UserAgent()
}

func (r *Request) Object() interface{} {
	return r.request
}

func (r *Request) reset(req *http.Request, h engine.Header, u engine.URL) {
	r.request = req
	r.header = h
	r.url = u
	r.value = NewValue(r)
	r.realIP = ``
}

func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
	r.MultipartForm()
	file, fileHeader, err := r.request.FormFile(key)
	if err != nil {
		return nil, nil, err
	}
	return file, fileHeader, err
}

// Size implements `engine.Request#ContentLength` function.
func (r *Request) Size() int64 {
	return r.request.ContentLength
}

func (r *Request) Scheme() string {
	if r.IsTLS() {
		return `https`
	}
	return `http`
}

func (r *Request) BasicAuth() (username, password string, ok bool) {
	return r.request.BasicAuth()
}

// SetHost implements `engine.Request#SetHost` function.
func (r *Request) SetHost(host string) {
	r.request.Host = host
}

func (r *Request) StdRequest() *http.Request {
	return r.request
}
<|endoftext|>


<commit_before>// Package clusterversionoperator contains utilities for exercising the cluster-version operator.
package clusterversionoperator

import (
	"context"
	"fmt"
	"regexp"
	"strings"
	"time"

	configv1 "github.com/openshift/api/config/v1"
	configv1client "github.com/openshift/client-go/config/clientset/versioned"

	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	restclient "k8s.io/client-go/rest"
	"k8s.io/kubernetes/test/e2e/framework"

	exutil "github.com/openshift/origin/test/extended/util"
)

// AdminAckTest contains artifacts used during test
type AdminAckTest struct {
	Oc     *exutil.CLI
	Config *restclient.Config
	Poll   time.Duration
}

const adminAckGateFmt string = "^ack-[4-5][.]([0-9]{1,})-[^-]"

var adminAckGateRegexp = regexp.MustCompile(adminAckGateFmt)

// Test simply returns successfully if admin ack functionality is not part of the baseline being tested. Otherwise,
// for each configured admin ack gate, test verifies the gate name format and that it contains a description. If
// valid and the gate is applicable to the OCP version under test, test checks the value of the admin ack gate.
// If the gate has been ack'ed the test verifies that the Upgradeable condition does not complain about the ack. Test
// then clears the ack and verifies that the Upgradeable condition complains about the ack. Test then sets the ack
// and verifies that the Upgradeable condition no longer complains about the ack.
func (t *AdminAckTest) Test(ctx context.Context) {
	if t.Poll == 0 {
		t.test(ctx, nil)
		return
	}

	exercisedGates := map[string]struct{}{}
	if err := wait.PollImmediateUntilWithContext(ctx, t.Poll, func(ctx context.Context) (bool, error) {
		t.test(ctx, exercisedGates)
		return false, nil
	}); err == nil || err == wait.ErrWaitTimeout {
		return
	} else {
		framework.Fail(err.Error())
	}
}

func (t *AdminAckTest) test(ctx context.Context, exercisedGates map[string]struct{}) {
	exists := struct{}{}

	gateCm, err := getAdminGatesConfigMap(ctx, t.Oc)
	if err != nil {
		framework.Fail(err.Error())
	}
	// Check if this release has admin ack functionality.
	if gateCm == nil || (gateCm != nil && len(gateCm.Data) == 0) {
		framework.Logf("Skipping admin ack test. Admin ack is not in this baseline or contains no gates.")
		return
	}
	ackCm, err := getAdminAcksConfigMap(ctx, t.Oc)
	if err != nil {
		framework.Fail(err.Error())
	}
	currentVersion := getCurrentVersion(ctx, t.Config)
	var msg string
	for k, v := range gateCm.Data {
		if exercisedGates != nil {
			if _, ok := exercisedGates[k]; ok {
				continue
			}
		}
		ackVersion := adminAckGateRegexp.FindString(k)
		if ackVersion == "" {
			framework.Failf("Configmap openshift-config-managed/admin-gates gate %s has invalid format; must comply with %q.", k, adminAckGateFmt)
		}
		if v == "" {
			framework.Failf("Configmap openshift-config-managed/admin-gates gate %s does not contain description.", k)
		}
		if !gateApplicableToCurrentVersion(ackVersion, currentVersion) {
			continue
		}
		if ackCm.Data[k] == "true" {
			if upgradeableExplicitlyFalse(ctx, t.Config) {
				if adminAckRequiredWithMessage(ctx, t.Config, v) {
					framework.Failf("Gate %s has been ack'ed but Upgradeable is "+
						"false with reason AdminAckRequired and message %q.", k, v)
				}
				framework.Logf("Gate %s has been ack'ed. Upgradeable is "+
					"false but not due to this gate which would set reason AdminAckRequired with message %s. %s", k, v, getUpgradeable(ctx, t.Config))
			}
			// Clear admin ack configmap gate ack
			if err := setAdminGate(ctx, k, "", t.Oc); err != nil {
				framework.Fail(err.Error())
			}
		}
		if err := waitForAdminAckRequired(ctx, t.Config, msg); err != nil {
			framework.Fail(err.Error())
		}
		// Update admin ack configmap with ack
		if err := setAdminGate(ctx, k, "true", t.Oc); err != nil {
			framework.Fail(err.Error())
		}
		if err = waitForAdminAckNotRequired(ctx, t.Config, msg); err != nil {
			framework.Fail(err.Error())
		}
		if exercisedGates != nil {
			exercisedGates[k] = exists
		}
	}
	framework.Logf("Admin Ack verified")
}

// getClusterVersion returns the ClusterVersion object.
func getClusterVersion(ctx context.Context, config *restclient.Config) *configv1.ClusterVersion {
	c, err := configv1client.NewForConfig(config)
	if err != nil {
		framework.Failf("Error getting config, err=%v", err)
	}
	cv, err := c.ConfigV1().ClusterVersions().Get(ctx, "version", metav1.GetOptions{})
	if err != nil {
		framework.Failf("Error getting custer version, err=%v", err)
	}
	return cv
}

// getCurrentVersion determines and returns the cluster's current version by iterating through the
// provided update history until it finds the first version with update State of Completed. If a
// Completed version is not found the version of the oldest history entry, which is the originally
// installed version, is returned. If history is empty the empty string is returned.
func getCurrentVersion(ctx context.Context, config *restclient.Config) string {
	cv := getClusterVersion(ctx, config)
	for _, h := range cv.Status.History {
		if h.State == configv1.CompletedUpdate {
			return h.Version
		}
	}
	// Empty history should only occur if method is called early in startup before history is populated.
	if len(cv.Status.History) != 0 {
		return cv.Status.History[len(cv.Status.History)-1].Version
	}
	return ""
}

// getEffectiveMinor attempts to do a simple parse of the version provided.  If it does not parse, the value is considered
// an empty string, which works for a comparison for equivalence.
func getEffectiveMinor(version string) string {
	splits := strings.Split(version, ".")
	if len(splits) < 2 {
		return ""
	}
	return splits[1]
}

func gateApplicableToCurrentVersion(gateAckVersion string, currentVersion string) bool {
	parts := strings.Split(gateAckVersion, "-")
	ackMinor := getEffectiveMinor(parts[1])
	cvMinor := getEffectiveMinor(currentVersion)
	if ackMinor == cvMinor {
		return true
	}
	return false
}

func getAdminGatesConfigMap(ctx context.Context, oc *exutil.CLI) (*corev1.ConfigMap, error) {
	cm, err := oc.AdminKubeClient().CoreV1().ConfigMaps("openshift-config-managed").Get(ctx, "admin-gates", metav1.GetOptions{})
	if err != nil {
		if !apierrors.IsNotFound(err) {
			return nil, fmt.Errorf("Error accessing configmap openshift-config-managed/admin-gates: %w", err)
		} else {
			return nil, nil
		}
	}
	return cm, nil
}

func getAdminAcksConfigMap(ctx context.Context, oc *exutil.CLI) (*corev1.ConfigMap, error) {
	cm, err := oc.AdminKubeClient().CoreV1().ConfigMaps("openshift-config").Get(ctx, "admin-acks", metav1.GetOptions{})
	if err != nil {
		return nil, fmt.Errorf("Error accessing configmap openshift-config/admin-acks: %w", err)
	}
	return cm, nil
}

// adminAckRequiredWithMessage returns true if Upgradeable condition reason contains AdminAckRequired
// and message contains given message.
func adminAckRequiredWithMessage(ctx context.Context, config *restclient.Config, message string) bool {
	clusterVersion := getClusterVersion(ctx, config)
	cond := getUpgradeableStatusCondition(clusterVersion.Status.Conditions)
	if cond != nil && strings.Contains(cond.Reason, "AdminAckRequired") && strings.Contains(cond.Message, message) {
		return true
	}
	return false
}

// upgradeableExplicitlyFalse returns true if the Upgradeable condition status is set to false.
func upgradeableExplicitlyFalse(ctx context.Context, config *restclient.Config) bool {
	clusterVersion := getClusterVersion(ctx, config)
	cond := getUpgradeableStatusCondition(clusterVersion.Status.Conditions)
	if cond != nil && cond.Status == configv1.ConditionFalse {
		return true
	}
	return false
}

// setAdminGate gets the admin ack configmap and then updates it with given gate name and given value.
func setAdminGate(ctx context.Context, gateName string, gateValue string, oc *exutil.CLI) error {
	ackCm, err := getAdminAcksConfigMap(ctx, oc)
	if err != nil {
		return err
	}
	ackCm.Data[gateName] = gateValue
	if _, err := oc.AdminKubeClient().CoreV1().ConfigMaps("openshift-config").Update(ctx, ackCm, metav1.UpdateOptions{}); err != nil {
		return fmt.Errorf("Unable to update configmap openshift-config/admin-acks: %w", err)
	}
	return nil
}

func waitForAdminAckRequired(ctx context.Context, config *restclient.Config, message string) error {
	framework.Logf("Waiting for Upgradeable to be AdminAckRequired for %q ...", message)
	if err := wait.PollImmediate(10*time.Second, 3*time.Minute, func() (bool, error) {
		if adminAckRequiredWithMessage(ctx, config, message) {
			return true, nil
		}
		return false, nil
	}); err != nil {
		return fmt.Errorf("Error while waiting for Upgradeable to go AdminAckRequired with message %q: %w\n%s", message, err, getUpgradeable(ctx, config))
	}
	return nil
}

func waitForAdminAckNotRequired(ctx context.Context, config *restclient.Config, message string) error {
	framework.Logf("Waiting for Upgradeable to not be AdminAckRequired for %q ...", message)
	if err := wait.PollImmediate(10*time.Second, 3*time.Minute, func() (bool, error) {
		if !adminAckRequiredWithMessage(ctx, config, message) {
			return true, nil
		}
		return false, nil
	}); err != nil {
		return fmt.Errorf("Error while waiting for Upgradeable to not be AdminAckRequired with message %q: %w\n%s", message, err, getUpgradeable(ctx, config))
	}
	return nil
}

func getUpgradeableStatusCondition(conditions []configv1.ClusterOperatorStatusCondition) *configv1.ClusterOperatorStatusCondition {
	for _, condition := range conditions {
		if condition.Type == configv1.OperatorUpgradeable {
			return &condition
		}
	}
	return nil
}

func getUpgradeable(ctx context.Context, config *restclient.Config) string {
	clusterVersion := getClusterVersion(ctx, config)
	cond := getUpgradeableStatusCondition(clusterVersion.Status.Conditions)
	if cond != nil {
		return fmt.Sprintf("Upgradeable: Status=%s, Reason=%s, Message=%q.", cond.Status, cond.Reason, cond.Message)
	}
	return "Upgradeable nil"
}
<commit_msg>Bug 2026806: clusterversionoperator/adminack.go: Check for nil cm map<commit_after>// Package clusterversionoperator contains utilities for exercising the cluster-version operator.
package clusterversionoperator

import (
	"context"
	"fmt"
	"regexp"
	"strings"
	"time"

	configv1 "github.com/openshift/api/config/v1"
	configv1client "github.com/openshift/client-go/config/clientset/versioned"

	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	restclient "k8s.io/client-go/rest"
	"k8s.io/kubernetes/test/e2e/framework"

	exutil "github.com/openshift/origin/test/extended/util"
)

// AdminAckTest contains artifacts used during test
type AdminAckTest struct {
	Oc     *exutil.CLI
	Config *restclient.Config
	Poll   time.Duration
}

const adminAckGateFmt string = "^ack-[4-5][.]([0-9]{1,})-[^-]"

var adminAckGateRegexp = regexp.MustCompile(adminAckGateFmt)

// Test simply returns successfully if admin ack functionality is not part of the baseline being tested. Otherwise,
// for each configured admin ack gate, test verifies the gate name format and that it contains a description. If
// valid and the gate is applicable to the OCP version under test, test checks the value of the admin ack gate.
// If the gate has been ack'ed the test verifies that the Upgradeable condition does not complain about the ack. Test
// then clears the ack and verifies that the Upgradeable condition complains about the ack. Test then sets the ack
// and verifies that the Upgradeable condition no longer complains about the ack.
func (t *AdminAckTest) Test(ctx context.Context) {
	if t.Poll == 0 {
		t.test(ctx, nil)
		return
	}

	exercisedGates := map[string]struct{}{}
	if err := wait.PollImmediateUntilWithContext(ctx, t.Poll, func(ctx context.Context) (bool, error) {
		t.test(ctx, exercisedGates)
		return false, nil
	}); err == nil || err == wait.ErrWaitTimeout {
		return
	} else {
		framework.Fail(err.Error())
	}
}

func (t *AdminAckTest) test(ctx context.Context, exercisedGates map[string]struct{}) {
	exists := struct{}{}

	gateCm, err := getAdminGatesConfigMap(ctx, t.Oc)
	if err != nil {
		framework.Fail(err.Error())
	}
	// Check if this release has admin ack functionality.
	if gateCm == nil || (gateCm != nil && len(gateCm.Data) == 0) {
		framework.Logf("Skipping admin ack test. Admin ack is not in this baseline or contains no gates.")
		return
	}
	ackCm, err := getAdminAcksConfigMap(ctx, t.Oc)
	if err != nil {
		framework.Fail(err.Error())
	}
	currentVersion := getCurrentVersion(ctx, t.Config)
	var msg string
	for k, v := range gateCm.Data {
		if exercisedGates != nil {
			if _, ok := exercisedGates[k]; ok {
				continue
			}
		}
		ackVersion := adminAckGateRegexp.FindString(k)
		if ackVersion == "" {
			framework.Failf("Configmap openshift-config-managed/admin-gates gate %s has invalid format; must comply with %q.", k, adminAckGateFmt)
		}
		if v == "" {
			framework.Failf("Configmap openshift-config-managed/admin-gates gate %s does not contain description.", k)
		}
		if !gateApplicableToCurrentVersion(ackVersion, currentVersion) {
			continue
		}
		if ackCm.Data[k] == "true" {
			if upgradeableExplicitlyFalse(ctx, t.Config) {
				if adminAckRequiredWithMessage(ctx, t.Config, v) {
					framework.Failf("Gate %s has been ack'ed but Upgradeable is "+
						"false with reason AdminAckRequired and message %q.", k, v)
				}
				framework.Logf("Gate %s has been ack'ed. Upgradeable is "+
					"false but not due to this gate which would set reason AdminAckRequired with message %s. %s", k, v, getUpgradeable(ctx, t.Config))
			}
			// Clear admin ack configmap gate ack
			if err := setAdminGate(ctx, k, "", t.Oc); err != nil {
				framework.Fail(err.Error())
			}
		}
		if err := waitForAdminAckRequired(ctx, t.Config, msg); err != nil {
			framework.Fail(err.Error())
		}
		// Update admin ack configmap with ack
		if err := setAdminGate(ctx, k, "true", t.Oc); err != nil {
			framework.Fail(err.Error())
		}
		if err = waitForAdminAckNotRequired(ctx, t.Config, msg); err != nil {
			framework.Fail(err.Error())
		}
		if exercisedGates != nil {
			exercisedGates[k] = exists
		}
	}
	framework.Logf("Admin Ack verified")
}

// getClusterVersion returns the ClusterVersion object.
func getClusterVersion(ctx context.Context, config *restclient.Config) *configv1.ClusterVersion {
	c, err := configv1client.NewForConfig(config)
	if err != nil {
		framework.Failf("Error getting config, err=%v", err)
	}
	cv, err := c.ConfigV1().ClusterVersions().Get(ctx, "version", metav1.GetOptions{})
	if err != nil {
		framework.Failf("Error getting custer version, err=%v", err)
	}
	return cv
}

// getCurrentVersion determines and returns the cluster's current version by iterating through the
// provided update history until it finds the first version with update State of Completed. If a
// Completed version is not found the version of the oldest history entry, which is the originally
// installed version, is returned. If history is empty the empty string is returned.
func getCurrentVersion(ctx context.Context, config *restclient.Config) string {
	cv := getClusterVersion(ctx, config)
	for _, h := range cv.Status.History {
		if h.State == configv1.CompletedUpdate {
			return h.Version
		}
	}
	// Empty history should only occur if method is called early in startup before history is populated.
	if len(cv.Status.History) != 0 {
		return cv.Status.History[len(cv.Status.History)-1].Version
	}
	return ""
}

// getEffectiveMinor attempts to do a simple parse of the version provided.  If it does not parse, the value is considered
// an empty string, which works for a comparison for equivalence.
func getEffectiveMinor(version string) string {
	splits := strings.Split(version, ".")
	if len(splits) < 2 {
		return ""
	}
	return splits[1]
}

func gateApplicableToCurrentVersion(gateAckVersion string, currentVersion string) bool {
	parts := strings.Split(gateAckVersion, "-")
	ackMinor := getEffectiveMinor(parts[1])
	cvMinor := getEffectiveMinor(currentVersion)
	if ackMinor == cvMinor {
		return true
	}
	return false
}

func getAdminGatesConfigMap(ctx context.Context, oc *exutil.CLI) (*corev1.ConfigMap, error) {
	cm, err := oc.AdminKubeClient().CoreV1().ConfigMaps("openshift-config-managed").Get(ctx, "admin-gates", metav1.GetOptions{})
	if err != nil {
		if !apierrors.IsNotFound(err) {
			return nil, fmt.Errorf("Error accessing configmap openshift-config-managed/admin-gates: %w", err)
		} else {
			return nil, nil
		}
	}
	return cm, nil
}

func getAdminAcksConfigMap(ctx context.Context, oc *exutil.CLI) (*corev1.ConfigMap, error) {
	cm, err := oc.AdminKubeClient().CoreV1().ConfigMaps("openshift-config").Get(ctx, "admin-acks", metav1.GetOptions{})
	if err != nil {
		return nil, fmt.Errorf("Error accessing configmap openshift-config/admin-acks: %w", err)
	}
	return cm, nil
}

// adminAckRequiredWithMessage returns true if Upgradeable condition reason contains AdminAckRequired
// and message contains given message.
func adminAckRequiredWithMessage(ctx context.Context, config *restclient.Config, message string) bool {
	clusterVersion := getClusterVersion(ctx, config)
	cond := getUpgradeableStatusCondition(clusterVersion.Status.Conditions)
	if cond != nil && strings.Contains(cond.Reason, "AdminAckRequired") && strings.Contains(cond.Message, message) {
		return true
	}
	return false
}

// upgradeableExplicitlyFalse returns true if the Upgradeable condition status is set to false.
func upgradeableExplicitlyFalse(ctx context.Context, config *restclient.Config) bool {
	clusterVersion := getClusterVersion(ctx, config)
	cond := getUpgradeableStatusCondition(clusterVersion.Status.Conditions)
	if cond != nil && cond.Status == configv1.ConditionFalse {
		return true
	}
	return false
}

// setAdminGate gets the admin ack configmap and then updates it with given gate name and given value.
func setAdminGate(ctx context.Context, gateName string, gateValue string, oc *exutil.CLI) error {
	ackCm, err := getAdminAcksConfigMap(ctx, oc)
	if err != nil {
		return err
	}
	if ackCm.Data == nil {
		ackCm.Data = make(map[string]string)
	}
	ackCm.Data[gateName] = gateValue
	if _, err := oc.AdminKubeClient().CoreV1().ConfigMaps("openshift-config").Update(ctx, ackCm, metav1.UpdateOptions{}); err != nil {
		return fmt.Errorf("Unable to update configmap openshift-config/admin-acks: %w", err)
	}
	return nil
}

func waitForAdminAckRequired(ctx context.Context, config *restclient.Config, message string) error {
	framework.Logf("Waiting for Upgradeable to be AdminAckRequired for %q ...", message)
	if err := wait.PollImmediate(10*time.Second, 3*time.Minute, func() (bool, error) {
		if adminAckRequiredWithMessage(ctx, config, message) {
			return true, nil
		}
		return false, nil
	}); err != nil {
		return fmt.Errorf("Error while waiting for Upgradeable to go AdminAckRequired with message %q: %w\n%s", message, err, getUpgradeable(ctx, config))
	}
	return nil
}

func waitForAdminAckNotRequired(ctx context.Context, config *restclient.Config, message string) error {
	framework.Logf("Waiting for Upgradeable to not be AdminAckRequired for %q ...", message)
	if err := wait.PollImmediate(10*time.Second, 3*time.Minute, func() (bool, error) {
		if !adminAckRequiredWithMessage(ctx, config, message) {
			return true, nil
		}
		return false, nil
	}); err != nil {
		return fmt.Errorf("Error while waiting for Upgradeable to not be AdminAckRequired with message %q: %w\n%s", message, err, getUpgradeable(ctx, config))
	}
	return nil
}

func getUpgradeableStatusCondition(conditions []configv1.ClusterOperatorStatusCondition) *configv1.ClusterOperatorStatusCondition {
	for _, condition := range conditions {
		if condition.Type == configv1.OperatorUpgradeable {
			return &condition
		}
	}
	return nil
}

func getUpgradeable(ctx context.Context, config *restclient.Config) string {
	clusterVersion := getClusterVersion(ctx, config)
	cond := getUpgradeableStatusCondition(clusterVersion.Status.Conditions)
	if cond != nil {
		return fmt.Sprintf("Upgradeable: Status=%s, Reason=%s, Message=%q.", cond.Status, cond.Reason, cond.Message)
	}
	return "Upgradeable nil"
}
<|endoftext|>


<commit_before>// Copyright 2018 The Terraformer 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 terraformutils

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"regexp"
	"strings"

	"github.com/hashicorp/hcl/hcl/ast"
	hclPrinter "github.com/hashicorp/hcl/hcl/printer"
	hclParser "github.com/hashicorp/hcl/json/parser"
)

// Copy code from https://github.com/kubernetes/kops project with few changes for support many provider and heredoc

const safeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"

var unsafeChars = regexp.MustCompile(`[^0-9A-Za-z_\-]`)

// sanitizer fixes up an invalid HCL AST, as produced by the HCL parser for JSON
type astSanitizer struct{}

// output prints creates b printable HCL output and returns it.
func (v *astSanitizer) visit(n interface{}) {
	switch t := n.(type) {
	case *ast.File:
		v.visit(t.Node)
	case *ast.ObjectList:
		var index int
		for {
			if index == len(t.Items) {
				break
			}
			v.visit(t.Items[index])
			index++
		}
	case *ast.ObjectKey:
	case *ast.ObjectItem:
		v.visitObjectItem(t)
	case *ast.LiteralType:
	case *ast.ListType:
	case *ast.ObjectType:
		v.visit(t.List)
	default:
		fmt.Printf(" unknown type: %T\n", n)
	}
}

func (v *astSanitizer) visitObjectItem(o *ast.ObjectItem) {
	for i, k := range o.Keys {
		if i == 0 {
			text := k.Token.Text
			if text != "" && text[0] == '"' && text[len(text)-1] == '"' {
				v := text[1 : len(text)-1]
				safe := true
				for _, c := range v {
					if !strings.ContainsRune(safeChars, c) {
						safe = false
						break
					}
				}
				if safe {
					k.Token.Text = v
				}
			}
		}
	}
	switch t := o.Val.(type) {
	case *ast.LiteralType: // heredoc support
		if strings.HasPrefix(t.Token.Text, `"<<`) {
			t.Token.Text = t.Token.Text[1:]
			t.Token.Text = t.Token.Text[:len(t.Token.Text)-1]
			t.Token.Text = strings.ReplaceAll(t.Token.Text, `\n`, "\n")
			t.Token.Text = strings.ReplaceAll(t.Token.Text, `\t`, "")
			t.Token.Type = 10
			// check if text json for Unquote and Indent
			jsonTest := t.Token.Text
			lines := strings.Split(jsonTest, "\n")
			jsonTest = strings.Join(lines[1:len(lines)-1], "\n")
			jsonTest = strings.ReplaceAll(jsonTest, "\\\"", "\"")
			// it's json we convert to heredoc back
			var tmp interface{} = map[string]interface{}{}
			err := json.Unmarshal([]byte(jsonTest), &tmp)
			if err != nil {
				tmp = make([]interface{}, 0)
				err = json.Unmarshal([]byte(jsonTest), &tmp)
			}
			if err == nil {
				dataJSONBytes, err := json.MarshalIndent(tmp, "", "  ")
				if err == nil {
					jsonData := strings.Split(string(dataJSONBytes), "\n")
					// first line for heredoc
					jsonData = append([]string{lines[0]}, jsonData...)
					// last line for heredoc
					jsonData = append(jsonData, lines[len(lines)-1])
					hereDoc := strings.Join(jsonData, "\n")
					t.Token.Text = hereDoc
				}
			}
		}
	default:
	}

	// A hack so that Assign.IsValid is true, so that the printer will output =
	o.Assign.Line = 1

	v.visit(o.Val)
}

func Print(data interface{}, mapsObjects map[string]struct{}, format string) ([]byte, error) {
	switch format {
	case "hcl":
		return hclPrint(data, mapsObjects)
	case "json":
		return jsonPrint(data)
	}
	return []byte{}, errors.New("error: unknown output format")
}

func hclPrint(data interface{}, mapsObjects map[string]struct{}) ([]byte, error) {
	dataBytesJSON, err := jsonPrint(data)
	if err != nil {
		return dataBytesJSON, err
	}
	dataJSON := string(dataBytesJSON)
	nodes, err := hclParser.Parse([]byte(dataJSON))
	if err != nil {
		log.Println(dataJSON)
		return []byte{}, fmt.Errorf("error parsing terraform json: %v", err)
	}
	var sanitizer astSanitizer
	sanitizer.visit(nodes)

	var b bytes.Buffer
	err = hclPrinter.Fprint(&b, nodes)
	if err != nil {
		return nil, fmt.Errorf("error writing HCL: %v", err)
	}
	s := b.String()

	// Remove extra whitespace...
	s = strings.ReplaceAll(s, "\n\n", "\n")

	// ...but leave whitespace between resources
	s = strings.ReplaceAll(s, "}\nresource", "}\n\nresource")

	// Apply Terraform style (alignment etc.)
	formatted, err := hclPrinter.Format([]byte(s))
	if err != nil {
		return nil, err
	}
	// hack for support terraform 0.12
	formatted = terraform12Adjustments(formatted, mapsObjects)
	// hack for support terraform 0.13
	formatted = terraform13Adjustments(formatted)
	if err != nil {
		log.Println("Invalid HCL follows:")
		for i, line := range strings.Split(s, "\n") {
			fmt.Printf("%4d|\t%s\n", i+1, line)
		}
		return nil, fmt.Errorf("error formatting HCL: %v", err)
	}

	return formatted, nil
}

func terraform12Adjustments(formatted []byte, mapsObjects map[string]struct{}) []byte {
	singletonListFix := regexp.MustCompile(`^\s*\w+ = {`)
	singletonListFixEnd := regexp.MustCompile(`^\s*}`)

	s := string(formatted)
	old := " = {"
	newEquals := " {"
	lines := strings.Split(s, "\n")
	prefix := make([]string, 0)
	for i, line := range lines {
		if singletonListFixEnd.MatchString(line) && len(prefix) > 0 {
			prefix = prefix[:len(prefix)-1]
			continue
		}
		if !singletonListFix.MatchString(line) {
			continue
		}
		key := strings.Trim(strings.Split(line, old)[0], " ")
		prefix = append(prefix, key)
		if _, exist := mapsObjects[strings.Join(prefix, ".")]; exist {
			continue
		}
		lines[i] = strings.ReplaceAll(line, old, newEquals)
	}
	s = strings.Join(lines, "\n")
	return []byte(s)
}

func terraform13Adjustments(formatted []byte) []byte {
	s := string(formatted)
	requiredProvidersRe := regexp.MustCompile("required_providers \".*\" {")
	oldRequiredProviders := "\"required_providers\""
	newRequiredProviders := "required_providers"
	lines := strings.Split(s, "\n")
	for i, line := range lines {
		if requiredProvidersRe.MatchString(line) {
			parts := strings.Split(strings.TrimSpace(line), " ")
			provider := strings.ReplaceAll(parts[1], "\"", "")
			lines[i] = "\t" + newRequiredProviders + " {"
			lines[i+1] = "\t\t" + provider + " = {\n\t" + lines[i+1] + "\n\t\t}"
		}
		lines[i] = strings.Replace(lines[i], oldRequiredProviders, newRequiredProviders, 1)
	}
	s = strings.Join(lines, "\n")
	return []byte(s)
}

func escapeRune(s string) string {
	return fmt.Sprintf("-%04X-", s)
}

// Sanitize name for terraform style
func TfSanitize(name string) string {
	name = unsafeChars.ReplaceAllStringFunc(name, escapeRune)
	name = "tfer--" + name
	return name
}

// Print hcl file from TerraformResource + provider
func HclPrintResource(resources []Resource, providerData map[string]interface{}, output string) ([]byte, error) {
	resourcesByType := map[string]map[string]interface{}{}
	mapsObjects := map[string]struct{}{}
	indexRe := regexp.MustCompile(`\.[0-9]+`)
	for _, res := range resources {
		r := resourcesByType[res.InstanceInfo.Type]
		if r == nil {
			r = make(map[string]interface{})
			resourcesByType[res.InstanceInfo.Type] = r
		}

		if r[res.ResourceName] != nil {
			log.Println(resources)
			log.Printf("[ERR]: duplicate resource found: %s.%s", res.InstanceInfo.Type, res.ResourceName)
			continue
		}

		r[res.ResourceName] = res.Item

		for k := range res.InstanceState.Attributes {
			if strings.HasSuffix(k, ".%") {
				key := strings.TrimSuffix(k, ".%")
				mapsObjects[indexRe.ReplaceAllString(key, "")] = struct{}{}
			}
		}
	}

	data := map[string]interface{}{}
	if len(resourcesByType) > 0 {
		data["resource"] = resourcesByType
	}
	if len(providerData) > 0 {
		data["provider"] = providerData
	}
	var err error

	hclBytes, err := Print(data, mapsObjects, output)
	if err != nil {
		return []byte{}, err
	}
	return hclBytes, nil
}
<commit_msg>make HCL output reproducible by sorting the AST nodes (#1101)<commit_after>// Copyright 2018 The Terraformer 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 terraformutils

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"regexp"
	"sort"
	"strings"

	"github.com/hashicorp/hcl/hcl/ast"
	hclPrinter "github.com/hashicorp/hcl/hcl/printer"
	hclParser "github.com/hashicorp/hcl/json/parser"
)

// Copy code from https://github.com/kubernetes/kops project with few changes for support many provider and heredoc

const safeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"

var unsafeChars = regexp.MustCompile(`[^0-9A-Za-z_\-]`)

// make HCL output reproducible by sorting the AST nodes
func sortHclTree(tree interface{}) {
	switch t := tree.(type) {
	case []*ast.ObjectItem:
		sort.Slice(t, func(i, j int) bool {
			var bI, bJ bytes.Buffer
			_, _ = hclPrinter.Fprint(&bI, t[i]), hclPrinter.Fprint(&bJ, t[j])
			return bI.String() < bJ.String()
		})
	case []ast.Node:
		sort.Slice(t, func(i, j int) bool {
			var bI, bJ bytes.Buffer
			_, _ = hclPrinter.Fprint(&bI, t[i]), hclPrinter.Fprint(&bJ, t[j])
			return bI.String() < bJ.String()
		})
	default:
	}
}

// sanitizer fixes up an invalid HCL AST, as produced by the HCL parser for JSON
type astSanitizer struct{}

// output prints creates b printable HCL output and returns it.
func (v *astSanitizer) visit(n interface{}) {
	switch t := n.(type) {
	case *ast.File:
		v.visit(t.Node)
	case *ast.ObjectList:
		var index int
		sortHclTree(t.Items)
		for {
			if index == len(t.Items) {
				break
			}
			v.visit(t.Items[index])
			index++
		}
	case *ast.ObjectKey:
	case *ast.ObjectItem:
		v.visitObjectItem(t)
	case *ast.LiteralType:
	case *ast.ListType:
		sortHclTree(t.List)
	case *ast.ObjectType:
		sortHclTree(t.List)
		v.visit(t.List)
	default:
		fmt.Printf(" unknown type: %T\n", n)
	}
}

func (v *astSanitizer) visitObjectItem(o *ast.ObjectItem) {
	for i, k := range o.Keys {
		if i == 0 {
			text := k.Token.Text
			if text != "" && text[0] == '"' && text[len(text)-1] == '"' {
				v := text[1 : len(text)-1]
				safe := true
				for _, c := range v {
					if !strings.ContainsRune(safeChars, c) {
						safe = false
						break
					}
				}
				if safe {
					k.Token.Text = v
				}
			}
		}
	}
	switch t := o.Val.(type) {
	case *ast.LiteralType: // heredoc support
		if strings.HasPrefix(t.Token.Text, `"<<`) {
			t.Token.Text = t.Token.Text[1:]
			t.Token.Text = t.Token.Text[:len(t.Token.Text)-1]
			t.Token.Text = strings.ReplaceAll(t.Token.Text, `\n`, "\n")
			t.Token.Text = strings.ReplaceAll(t.Token.Text, `\t`, "")
			t.Token.Type = 10
			// check if text json for Unquote and Indent
			jsonTest := t.Token.Text
			lines := strings.Split(jsonTest, "\n")
			jsonTest = strings.Join(lines[1:len(lines)-1], "\n")
			jsonTest = strings.ReplaceAll(jsonTest, "\\\"", "\"")
			// it's json we convert to heredoc back
			var tmp interface{} = map[string]interface{}{}
			err := json.Unmarshal([]byte(jsonTest), &tmp)
			if err != nil {
				tmp = make([]interface{}, 0)
				err = json.Unmarshal([]byte(jsonTest), &tmp)
			}
			if err == nil {
				dataJSONBytes, err := json.MarshalIndent(tmp, "", "  ")
				if err == nil {
					jsonData := strings.Split(string(dataJSONBytes), "\n")
					// first line for heredoc
					jsonData = append([]string{lines[0]}, jsonData...)
					// last line for heredoc
					jsonData = append(jsonData, lines[len(lines)-1])
					hereDoc := strings.Join(jsonData, "\n")
					t.Token.Text = hereDoc
				}
			}
		}
	case *ast.ListType:
		sortHclTree(t.List)
	default:
	}

	// A hack so that Assign.IsValid is true, so that the printer will output =
	o.Assign.Line = 1

	v.visit(o.Val)
}

func Print(data interface{}, mapsObjects map[string]struct{}, format string) ([]byte, error) {
	switch format {
	case "hcl":
		return hclPrint(data, mapsObjects)
	case "json":
		return jsonPrint(data)
	}
	return []byte{}, errors.New("error: unknown output format")
}

func hclPrint(data interface{}, mapsObjects map[string]struct{}) ([]byte, error) {
	dataBytesJSON, err := jsonPrint(data)
	if err != nil {
		return dataBytesJSON, err
	}
	dataJSON := string(dataBytesJSON)
	nodes, err := hclParser.Parse([]byte(dataJSON))
	if err != nil {
		log.Println(dataJSON)
		return []byte{}, fmt.Errorf("error parsing terraform json: %v", err)
	}
	var sanitizer astSanitizer
	sanitizer.visit(nodes)

	var b bytes.Buffer
	err = hclPrinter.Fprint(&b, nodes)
	if err != nil {
		return nil, fmt.Errorf("error writing HCL: %v", err)
	}
	s := b.String()

	// Remove extra whitespace...
	s = strings.ReplaceAll(s, "\n\n", "\n")

	// ...but leave whitespace between resources
	s = strings.ReplaceAll(s, "}\nresource", "}\n\nresource")

	// Apply Terraform style (alignment etc.)
	formatted, err := hclPrinter.Format([]byte(s))
	if err != nil {
		return nil, err
	}
	// hack for support terraform 0.12
	formatted = terraform12Adjustments(formatted, mapsObjects)
	// hack for support terraform 0.13
	formatted = terraform13Adjustments(formatted)
	if err != nil {
		log.Println("Invalid HCL follows:")
		for i, line := range strings.Split(s, "\n") {
			fmt.Printf("%4d|\t%s\n", i+1, line)
		}
		return nil, fmt.Errorf("error formatting HCL: %v", err)
	}

	return formatted, nil
}

func terraform12Adjustments(formatted []byte, mapsObjects map[string]struct{}) []byte {
	singletonListFix := regexp.MustCompile(`^\s*\w+ = {`)
	singletonListFixEnd := regexp.MustCompile(`^\s*}`)

	s := string(formatted)
	old := " = {"
	newEquals := " {"
	lines := strings.Split(s, "\n")
	prefix := make([]string, 0)
	for i, line := range lines {
		if singletonListFixEnd.MatchString(line) && len(prefix) > 0 {
			prefix = prefix[:len(prefix)-1]
			continue
		}
		if !singletonListFix.MatchString(line) {
			continue
		}
		key := strings.Trim(strings.Split(line, old)[0], " ")
		prefix = append(prefix, key)
		if _, exist := mapsObjects[strings.Join(prefix, ".")]; exist {
			continue
		}
		lines[i] = strings.ReplaceAll(line, old, newEquals)
	}
	s = strings.Join(lines, "\n")
	return []byte(s)
}

func terraform13Adjustments(formatted []byte) []byte {
	s := string(formatted)
	requiredProvidersRe := regexp.MustCompile("required_providers \".*\" {")
	oldRequiredProviders := "\"required_providers\""
	newRequiredProviders := "required_providers"
	lines := strings.Split(s, "\n")
	for i, line := range lines {
		if requiredProvidersRe.MatchString(line) {
			parts := strings.Split(strings.TrimSpace(line), " ")
			provider := strings.ReplaceAll(parts[1], "\"", "")
			lines[i] = "\t" + newRequiredProviders + " {"
			lines[i+1] = "\t\t" + provider + " = {\n\t" + lines[i+1] + "\n\t\t}"
		}
		lines[i] = strings.Replace(lines[i], oldRequiredProviders, newRequiredProviders, 1)
	}
	s = strings.Join(lines, "\n")
	return []byte(s)
}

func escapeRune(s string) string {
	return fmt.Sprintf("-%04X-", s)
}

// Sanitize name for terraform style
func TfSanitize(name string) string {
	name = unsafeChars.ReplaceAllStringFunc(name, escapeRune)
	name = "tfer--" + name
	return name
}

// Print hcl file from TerraformResource + provider
func HclPrintResource(resources []Resource, providerData map[string]interface{}, output string) ([]byte, error) {
	resourcesByType := map[string]map[string]interface{}{}
	mapsObjects := map[string]struct{}{}
	indexRe := regexp.MustCompile(`\.[0-9]+`)
	for _, res := range resources {
		r := resourcesByType[res.InstanceInfo.Type]
		if r == nil {
			r = make(map[string]interface{})
			resourcesByType[res.InstanceInfo.Type] = r
		}

		if r[res.ResourceName] != nil {
			log.Println(resources)
			log.Printf("[ERR]: duplicate resource found: %s.%s", res.InstanceInfo.Type, res.ResourceName)
			continue
		}

		r[res.ResourceName] = res.Item

		for k := range res.InstanceState.Attributes {
			if strings.HasSuffix(k, ".%") {
				key := strings.TrimSuffix(k, ".%")
				mapsObjects[indexRe.ReplaceAllString(key, "")] = struct{}{}
			}
		}
	}

	data := map[string]interface{}{}
	if len(resourcesByType) > 0 {
		data["resource"] = resourcesByType
	}
	if len(providerData) > 0 {
		data["provider"] = providerData
	}
	var err error

	hclBytes, err := Print(data, mapsObjects, output)
	if err != nil {
		return []byte{}, err
	}
	return hclBytes, nil
}
<|endoftext|>


<commit_before>package richtext

import (
	"image"
	"time"
	"unicode/utf8"

	"github.com/aarzilli/nucular"
	"github.com/aarzilli/nucular/clipboard"
	"github.com/aarzilli/nucular/label"
	"github.com/aarzilli/nucular/rect"

	"golang.org/x/image/math/fixed"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/mouse"
)

func (rtxt *RichText) handleClick(w *nucular.Window, r rect.Rect, in *nucular.Input, styleSel styleSel, line *line, chunkIdx int, hovering *bool, linkClick *int32) {
	if rtxt.flags&Selectable == 0 && !styleSel.isLink && rtxt.flags&Clipboard == 0 && styleSel.Tooltip == nil && rtxt.flags&Keyboard == 0 {
		return
	}

	if rtxt.flags&Clipboard != 0 && r.W > 0 && r.H > 0 {
		fn := styleSel.ContextMenu
		if fn == nil {
			fn = func(w *nucular.Window) {
				w.Row(20).Dynamic(1)
				if w.MenuItem(label.TA("Copy", "LC")) {
					clipboard.Set(rtxt.Get(rtxt.Sel))
					w.Close()
				}
			}
		}
		if w := w.ContextualOpen(0, image.Point{}, r, fn); w != nil {
			rtxt.focused = true
		}
	}

	oldSel := rtxt.Sel

	if rtxt.down {
		rtxt.focused = true
		if in.Mouse.HoveringRect(r) {
			if !in.Mouse.Down(mouse.ButtonLeft) {
				if rtxt.isClick && styleSel.isLink && in.Mouse.HoveringRect(r) {
					if styleSel.link != nil {
						styleSel.link()
					} else if linkClick != nil {
						*linkClick = line.coordToIndex(in.Mouse.Pos, chunkIdx, rtxt.adv)
					}
				}
				rtxt.down = false
				rtxt.isClick = false
			} else {
				q := line.coordToIndex(in.Mouse.Pos, chunkIdx, rtxt.adv)
				if q < rtxt.dragStart {
					rtxt.Sel.S = q
					rtxt.Sel.E = rtxt.dragStart
				} else {
					rtxt.Sel.S = rtxt.dragStart
					rtxt.Sel.E = q
				}
				rtxt.expandSelection()
				if q != rtxt.dragStart {
					rtxt.isClick = false
				}
			}
		}
	} else {
		if in.Mouse.Down(mouse.ButtonLeft) && in.Mouse.HoveringRect(r) {
			rtxt.focused = true
			q := line.coordToIndex(in.Mouse.Pos, chunkIdx, rtxt.adv)
			if time.Since(rtxt.lastClickTime) < 200*time.Millisecond && q == rtxt.dragStart {
				rtxt.clickCount++
			} else {
				rtxt.clickCount = 1
			}
			if rtxt.clickCount > 3 {
				rtxt.clickCount = 3
			}
			rtxt.lastClickTime = time.Now()
			rtxt.dragStart = q
			rtxt.Sel.S = rtxt.dragStart
			rtxt.Sel.E = rtxt.Sel.S
			rtxt.expandSelection()
			rtxt.down = true
			rtxt.isClick = true
		}
		if (styleSel.isLink || styleSel.Tooltip != nil) && hovering != nil && in.Mouse.HoveringRect(r) {
			*hovering = true
		}
	}

	if rtxt.flags&Selectable == 0 {
		rtxt.Sel = oldSel
	}
	return
}

func (rtxt *RichText) expandSelection() {
	switch rtxt.clickCount {
	case 2:
		sline := rtxt.findLine(rtxt.Sel.S)
		eline := rtxt.findLine(rtxt.Sel.E)

		var citer citer
		for citer.Init(sline, rtxt.Sel.S); citer.Valid(); citer.Prev() {
			if citer.Char() == ' ' {
				citer.Next()
				break
			}

		}
		rtxt.Sel.S = citer.off

		for citer.Init(eline, rtxt.Sel.E); citer.Valid(); citer.Next() {
			if citer.Char() == ' ' {
				break
			}
		}
		rtxt.Sel.E = citer.off
	case 3:
		sline := rtxt.findLine(rtxt.Sel.S)
		eline := rtxt.findLine(rtxt.Sel.E)
		if len(sline.off) > 0 {
			rtxt.Sel.S = sline.off[0]
			rtxt.Sel.E = eline.endoff()
		}
	}
}

func (rtxt *RichText) findLine(q int32) line {
	for _, line := range rtxt.lines {
		if len(line.off) <= 0 {
			continue
		}
		if line.sel().contains(q) {
			return line
		}
	}
	return rtxt.lines[len(rtxt.lines)-1]
}

func (line line) coordToIndex(p image.Point, chunkIdx int, adv []fixed.Int26_6) int32 {
	advance, runeoff := line.chunkAdvance(chunkIdx)
	if len(line.chunks) == 0 {
		return line.off[0]
	}
	chunk := line.chunks[chunkIdx]

	x := advance + line.leftMargin + line.p.X

	w := fixed.I(0)

	off := line.off[chunkIdx]
	for chunk.len() > 0 {
		w += adv[runeoff]

		if x+w.Ceil() > p.X {
			return off
		}

		var rsz int
		if chunk.b != nil {
			_, rsz = utf8.DecodeRune(chunk.b)
			chunk.b = chunk.b[rsz:]
		} else {
			_, rsz = utf8.DecodeRuneInString(chunk.s)
			chunk.s = chunk.s[rsz:]
		}
		off += int32(rsz)
		runeoff++
	}

	return off
}

type citer struct {
	valid bool
	off   int32
	line  line
	i, j  int
}

func (citer *citer) Init(line line, off int32) {
	citer.valid = true
	citer.off = off
	citer.line = line
	found := false
	for i := range citer.line.chunks {
		if citer.line.off[i] <= off && off < citer.line.off[i]+citer.line.chunks[i].len() {
			citer.i = i
			citer.j = int(off - citer.line.off[i])
			found = true
			break
		}
	}
	if !found {
		citer.i = len(citer.line.chunks)
		citer.j = 0
		citer.valid = false
	}
	if len(citer.line.chunks) <= 0 {
		citer.valid = false
	}
}

func (citer *citer) Valid() bool {
	if !citer.valid {
		return false
	}
	if citer.i < 0 || citer.i >= len(citer.line.chunks) {
		return false
	}
	chunk := citer.line.chunks[citer.i]
	if citer.j < 0 {
		return false
	}
	if chunk.b != nil {
		return citer.j < len(chunk.b)
	}
	return citer.j < len(chunk.s)
}

func (citer *citer) Char() byte {
	chunk := citer.line.chunks[citer.i]
	if chunk.b != nil {
		return chunk.b[citer.j]
	}
	return chunk.s[citer.j]
}

func (citer *citer) Prev() {
	citer.j--
	citer.off--
	if citer.j < 0 {
		citer.i--
		if citer.i < 0 {
			citer.i = 0
			citer.off++
			citer.valid = false
		} else {
			chunk := citer.line.chunks[citer.i]
			if chunk.b != nil {
				citer.j = len(chunk.b) - 1
			} else {
				citer.j = len(chunk.s) - 1
			}
		}
	}
}

func (citer *citer) Next() {
	citer.j++
	citer.off++
	for citer.j >= int(citer.line.chunks[citer.i].len()) {
		citer.j = 0
		citer.i++
		if citer.i >= len(citer.line.chunks) {
			citer.valid = false
			return
		}
	}
}

func (rtxt *RichText) handleKeyboard(in *nucular.Input) (arrowKey, pageKey int) {
	if !rtxt.focused || rtxt.flags&Keyboard == 0 {
		return
	}

	for _, k := range in.Keyboard.Keys {
		switch {
		case k.Modifiers == key.ModControl && k.Code == key.CodeC:
			if rtxt.flags&Clipboard != 0 {
				clipboard.Set(rtxt.Get(rtxt.Sel))
			}
		case k.Code == key.CodeUpArrow:
			return -1, 0
		case k.Code == key.CodeDownArrow:
			return +1, 0
		case k.Code == key.CodePageDown:
			return 0, +1
		case k.Code == key.CodePageUp:
			return 0, -1
		}
	}

	return 0, 0
}
<commit_msg>richtext: fix crash with inconsistent selection (again)<commit_after>package richtext

import (
	"image"
	"time"
	"unicode/utf8"

	"github.com/aarzilli/nucular"
	"github.com/aarzilli/nucular/clipboard"
	"github.com/aarzilli/nucular/label"
	"github.com/aarzilli/nucular/rect"

	"golang.org/x/image/math/fixed"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/mouse"
)

func (rtxt *RichText) handleClick(w *nucular.Window, r rect.Rect, in *nucular.Input, styleSel styleSel, line *line, chunkIdx int, hovering *bool, linkClick *int32) {
	defer func() {
		if rtxt.Sel.S > rtxt.Sel.E {
			rtxt.Sel.E = rtxt.Sel.S
		}
	}()
	if rtxt.flags&Selectable == 0 && !styleSel.isLink && rtxt.flags&Clipboard == 0 && styleSel.Tooltip == nil && rtxt.flags&Keyboard == 0 {
		return
	}

	if rtxt.flags&Clipboard != 0 && r.W > 0 && r.H > 0 {
		fn := styleSel.ContextMenu
		if fn == nil {
			fn = func(w *nucular.Window) {
				w.Row(20).Dynamic(1)
				if w.MenuItem(label.TA("Copy", "LC")) {
					clipboard.Set(rtxt.Get(rtxt.Sel))
					w.Close()
				}
			}
		}
		if w := w.ContextualOpen(0, image.Point{}, r, fn); w != nil {
			rtxt.focused = true
		}
	}

	oldSel := rtxt.Sel

	if rtxt.down {
		rtxt.focused = true
		if in.Mouse.HoveringRect(r) {
			if !in.Mouse.Down(mouse.ButtonLeft) {
				if rtxt.isClick && styleSel.isLink && in.Mouse.HoveringRect(r) {
					if styleSel.link != nil {
						styleSel.link()
					} else if linkClick != nil {
						*linkClick = line.coordToIndex(in.Mouse.Pos, chunkIdx, rtxt.adv)
					}
				}
				rtxt.down = false
				rtxt.isClick = false
			} else {
				q := line.coordToIndex(in.Mouse.Pos, chunkIdx, rtxt.adv)
				if q < rtxt.dragStart {
					rtxt.Sel.S = q
					rtxt.Sel.E = rtxt.dragStart
				} else {
					rtxt.Sel.S = rtxt.dragStart
					rtxt.Sel.E = q
				}
				rtxt.expandSelection()
				if q != rtxt.dragStart {
					rtxt.isClick = false
				}
			}
		}
	} else {
		if in.Mouse.Down(mouse.ButtonLeft) && in.Mouse.HoveringRect(r) {
			rtxt.focused = true
			q := line.coordToIndex(in.Mouse.Pos, chunkIdx, rtxt.adv)
			if time.Since(rtxt.lastClickTime) < 200*time.Millisecond && q == rtxt.dragStart {
				rtxt.clickCount++
			} else {
				rtxt.clickCount = 1
			}
			if rtxt.clickCount > 3 {
				rtxt.clickCount = 3
			}
			rtxt.lastClickTime = time.Now()
			rtxt.dragStart = q
			rtxt.Sel.S = rtxt.dragStart
			rtxt.Sel.E = rtxt.Sel.S
			rtxt.expandSelection()
			rtxt.down = true
			rtxt.isClick = true
		}
		if (styleSel.isLink || styleSel.Tooltip != nil) && hovering != nil && in.Mouse.HoveringRect(r) {
			*hovering = true
		}
	}

	if rtxt.flags&Selectable == 0 {
		rtxt.Sel = oldSel
	}
	return
}

func (rtxt *RichText) expandSelection() {
	switch rtxt.clickCount {
	case 2:
		sline := rtxt.findLine(rtxt.Sel.S)
		eline := rtxt.findLine(rtxt.Sel.E)

		var citer citer
		for citer.Init(sline, rtxt.Sel.S); citer.Valid(); citer.Prev() {
			if citer.Char() == ' ' {
				citer.Next()
				break
			}

		}
		rtxt.Sel.S = citer.off

		for citer.Init(eline, rtxt.Sel.E); citer.Valid(); citer.Next() {
			if citer.Char() == ' ' {
				break
			}
		}
		rtxt.Sel.E = citer.off
	case 3:
		sline := rtxt.findLine(rtxt.Sel.S)
		eline := rtxt.findLine(rtxt.Sel.E)
		if len(sline.off) > 0 {
			rtxt.Sel.S = sline.off[0]
			rtxt.Sel.E = eline.endoff()
		}
	}
}

func (rtxt *RichText) findLine(q int32) line {
	for _, line := range rtxt.lines {
		if len(line.off) <= 0 {
			continue
		}
		if line.sel().contains(q) {
			return line
		}
	}
	return rtxt.lines[len(rtxt.lines)-1]
}

func (line line) coordToIndex(p image.Point, chunkIdx int, adv []fixed.Int26_6) int32 {
	advance, runeoff := line.chunkAdvance(chunkIdx)
	if len(line.chunks) == 0 {
		return line.off[0]
	}
	chunk := line.chunks[chunkIdx]

	x := advance + line.leftMargin + line.p.X

	w := fixed.I(0)

	off := line.off[chunkIdx]
	for chunk.len() > 0 {
		w += adv[runeoff]

		if x+w.Ceil() > p.X {
			return off
		}

		var rsz int
		if chunk.b != nil {
			_, rsz = utf8.DecodeRune(chunk.b)
			chunk.b = chunk.b[rsz:]
		} else {
			_, rsz = utf8.DecodeRuneInString(chunk.s)
			chunk.s = chunk.s[rsz:]
		}
		off += int32(rsz)
		runeoff++
	}

	return off
}

type citer struct {
	valid bool
	off   int32
	line  line
	i, j  int
}

func (citer *citer) Init(line line, off int32) {
	citer.valid = true
	citer.off = off
	citer.line = line
	found := false
	for i := range citer.line.chunks {
		if citer.line.off[i] <= off && off < citer.line.off[i]+citer.line.chunks[i].len() {
			citer.i = i
			citer.j = int(off - citer.line.off[i])
			found = true
			break
		}
	}
	if !found {
		citer.i = len(citer.line.chunks)
		citer.j = 0
		citer.valid = false
	}
	if len(citer.line.chunks) <= 0 {
		citer.valid = false
	}
}

func (citer *citer) Valid() bool {
	if !citer.valid {
		return false
	}
	if citer.i < 0 || citer.i >= len(citer.line.chunks) {
		return false
	}
	chunk := citer.line.chunks[citer.i]
	if citer.j < 0 {
		return false
	}
	if chunk.b != nil {
		return citer.j < len(chunk.b)
	}
	return citer.j < len(chunk.s)
}

func (citer *citer) Char() byte {
	chunk := citer.line.chunks[citer.i]
	if chunk.b != nil {
		return chunk.b[citer.j]
	}
	return chunk.s[citer.j]
}

func (citer *citer) Prev() {
	citer.j--
	citer.off--
	if citer.j < 0 {
		citer.i--
		if citer.i < 0 {
			citer.i = 0
			citer.off++
			citer.valid = false
		} else {
			chunk := citer.line.chunks[citer.i]
			if chunk.b != nil {
				citer.j = len(chunk.b) - 1
			} else {
				citer.j = len(chunk.s) - 1
			}
		}
	}
}

func (citer *citer) Next() {
	citer.j++
	citer.off++
	for citer.j >= int(citer.line.chunks[citer.i].len()) {
		citer.j = 0
		citer.i++
		if citer.i >= len(citer.line.chunks) {
			citer.valid = false
			return
		}
	}
}

func (rtxt *RichText) handleKeyboard(in *nucular.Input) (arrowKey, pageKey int) {
	if !rtxt.focused || rtxt.flags&Keyboard == 0 {
		return
	}

	for _, k := range in.Keyboard.Keys {
		switch {
		case k.Modifiers == key.ModControl && k.Code == key.CodeC:
			if rtxt.flags&Clipboard != 0 {
				clipboard.Set(rtxt.Get(rtxt.Sel))
			}
		case k.Code == key.CodeUpArrow:
			return -1, 0
		case k.Code == key.CodeDownArrow:
			return +1, 0
		case k.Code == key.CodePageDown:
			return 0, +1
		case k.Code == key.CodePageUp:
			return 0, -1
		}
	}

	return 0, 0
}
<|endoftext|>


<commit_before>package logrus

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"time"
)

var (

	// qualified package name, cached at first use
	logrusPackage string

	// Positions in the call stack when tracing to report the calling method
	minimumCallerDepth int

	// Used for caller information initialisation
	callerInitOnce sync.Once
)

const (
	maximumCallerDepth int = 25
	knownLogrusFrames  int = 4
)

func init() {
	// start at the bottom of the stack before the package-name cache is primed
	minimumCallerDepth = 1
}

// Defines the key when adding errors using WithError.
var ErrorKey = "error"

// An entry is the final or intermediate Logrus logging entry. It contains all
// the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
// Info, Warn, Error, Fatal or Panic is called on it. These objects can be
// reused and passed around as much as you wish to avoid field duplication.
type Entry struct {
	Logger *Logger

	// Contains all the fields set by the user.
	Data Fields

	// Time at which the log entry was created
	Time time.Time

	// Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
	// This field will be set on entry firing and the value will be equal to the one in Logger struct field.
	Level Level

	// Calling method, with package name
	Caller *runtime.Frame

	// Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
	Message string

	// When formatter is called in entry.log(), a Buffer may be set to entry
	Buffer *bytes.Buffer

	// Contains the context set by the user. Useful for hook processing etc.
	Context context.Context

	// err may contain a field formatting error
	err string
}

func NewEntry(logger *Logger) *Entry {
	return &Entry{
		Logger: logger,
		// Default is three fields, plus one optional.  Give a little extra room.
		Data: make(Fields, 6),
	}
}

func (entry *Entry) Dup() *Entry {
	data := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		data[k] = v
	}
	return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
}

// Returns the bytes representation of this entry from the formatter.
func (entry *Entry) Bytes() ([]byte, error) {
	return entry.bytes_nolock()
}

func (entry *Entry) bytes_nolock() ([]byte, error) {
	return entry.Logger.Formatter.Format(entry)
}

// Returns the string representation from the reader and ultimately the
// formatter.
func (entry *Entry) String() (string, error) {
	serialized, err := entry.Bytes()
	if err != nil {
		return "", err
	}
	str := string(serialized)
	return str, nil
}

// Add an error as single field (using the key defined in ErrorKey) to the Entry.
func (entry *Entry) WithError(err error) *Entry {
	return entry.WithField(ErrorKey, err)
}

// Add a context to the Entry.
func (entry *Entry) WithContext(ctx context.Context) *Entry {
	dataCopy := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		dataCopy[k] = v
	}
	return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
}

// Add a single field to the Entry.
func (entry *Entry) WithField(key string, value interface{}) *Entry {
	return entry.WithFields(Fields{key: value})
}

// Add a map of fields to the Entry.
func (entry *Entry) WithFields(fields Fields) *Entry {
	data := make(Fields, len(entry.Data)+len(fields))
	for k, v := range entry.Data {
		data[k] = v
	}
	fieldErr := entry.err
	for k, v := range fields {
		isErrField := false
		if t := reflect.TypeOf(v); t != nil {
			switch {
			case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
				isErrField = true
			}
		}
		if isErrField {
			tmp := fmt.Sprintf("can not add field %q", k)
			if fieldErr != "" {
				fieldErr = entry.err + ", " + tmp
			} else {
				fieldErr = tmp
			}
		} else {
			data[k] = v
		}
	}
	return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
}

// Overrides the time of the Entry.
func (entry *Entry) WithTime(t time.Time) *Entry {
	dataCopy := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		dataCopy[k] = v
	}
	return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
}

// getPackageName reduces a fully qualified function name to the package name
// There really ought to be to be a better way...
func getPackageName(f string) string {
	for {
		lastPeriod := strings.LastIndex(f, ".")
		lastSlash := strings.LastIndex(f, "/")
		if lastPeriod > lastSlash {
			f = f[:lastPeriod]
		} else {
			break
		}
	}

	return f
}

// getCaller retrieves the name of the first non-logrus calling function
func getCaller() *runtime.Frame {
	// cache this package's fully-qualified name
	callerInitOnce.Do(func() {
		pcs := make([]uintptr, maximumCallerDepth)
		_ = runtime.Callers(0, pcs)

		// dynamic get the package name and the minimum caller depth
		for i := 0; i < maximumCallerDepth; i++ {
			funcName := runtime.FuncForPC(pcs[i]).Name()
			if strings.Contains(funcName, "getCaller") {
				logrusPackage = getPackageName(funcName)
				break
			}
		}

		minimumCallerDepth = knownLogrusFrames
	})

	// Restrict the lookback frames to avoid runaway lookups
	pcs := make([]uintptr, maximumCallerDepth)
	depth := runtime.Callers(minimumCallerDepth, pcs)
	frames := runtime.CallersFrames(pcs[:depth])

	for f, again := frames.Next(); again; f, again = frames.Next() {
		pkg := getPackageName(f.Function)

		// If the caller isn't part of this package, we're done
		if pkg != logrusPackage {
			return &f //nolint:scopelint
		}
	}

	// if we got here, we failed to find the caller's context
	return nil
}

func (entry Entry) HasCaller() (has bool) {
	return entry.Logger != nil &&
		entry.Logger.ReportCaller &&
		entry.Caller != nil
}

// This function is not declared with a pointer value because otherwise
// race conditions will occur when using multiple goroutines
func (entry *Entry) log(level Level, msg string) {
	var buffer *bytes.Buffer

	newEntry := entry.Dup()

	if newEntry.Time.IsZero() {
		newEntry.Time = time.Now()
	}

	newEntry.Level = level
	newEntry.Message = msg

	newEntry.Logger.mu.Lock()
	reportCaller := newEntry.Logger.ReportCaller
	newEntry.Logger.mu.Unlock()

	if reportCaller {
		newEntry.Caller = getCaller()
	}

	newEntry.fireHooks()

	buffer = getBuffer()
	defer func() {
		newEntry.Buffer = nil
		putBuffer(buffer)
	}()
	buffer.Reset()
	newEntry.Buffer = buffer

	newEntry.write()

	newEntry.Buffer = nil

	// To avoid Entry#log() returning a value that only would make sense for
	// panic() to use in Entry#Panic(), we avoid the allocation by checking
	// directly here.
	if level <= PanicLevel {
		panic(newEntry)
	}
}

func (entry *Entry) fireHooks() {
	err := entry.Logger.Hooks.Fire(entry.Level, entry)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
	}
}

func (entry *Entry) write() {
	serialized, err := entry.Logger.Formatter.Format(entry)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
		return
	}
	func() {
		entry.Logger.mu.Lock()
		defer entry.Logger.mu.Unlock()
		if _, err := entry.Logger.Out.Write(serialized); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
		}
	}()
}

func (entry *Entry) Log(level Level, args ...interface{}) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.log(level, fmt.Sprint(args...))
	}
}

func (entry *Entry) Trace(args ...interface{}) {
	entry.Log(TraceLevel, args...)
}

func (entry *Entry) Debug(args ...interface{}) {
	entry.Log(DebugLevel, args...)
}

func (entry *Entry) Print(args ...interface{}) {
	entry.Info(args...)
}

func (entry *Entry) Info(args ...interface{}) {
	entry.Log(InfoLevel, args...)
}

func (entry *Entry) Warn(args ...interface{}) {
	entry.Log(WarnLevel, args...)
}

func (entry *Entry) Warning(args ...interface{}) {
	entry.Warn(args...)
}

func (entry *Entry) Error(args ...interface{}) {
	entry.Log(ErrorLevel, args...)
}

func (entry *Entry) Fatal(args ...interface{}) {
	entry.Log(FatalLevel, args...)
	entry.Logger.Exit(1)
}

func (entry *Entry) Panic(args ...interface{}) {
	entry.Log(PanicLevel, args...)
}

// Entry Printf family functions

func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.Log(level, fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Tracef(format string, args ...interface{}) {
	entry.Logf(TraceLevel, format, args...)
}

func (entry *Entry) Debugf(format string, args ...interface{}) {
	entry.Logf(DebugLevel, format, args...)
}

func (entry *Entry) Infof(format string, args ...interface{}) {
	entry.Logf(InfoLevel, format, args...)
}

func (entry *Entry) Printf(format string, args ...interface{}) {
	entry.Infof(format, args...)
}

func (entry *Entry) Warnf(format string, args ...interface{}) {
	entry.Logf(WarnLevel, format, args...)
}

func (entry *Entry) Warningf(format string, args ...interface{}) {
	entry.Warnf(format, args...)
}

func (entry *Entry) Errorf(format string, args ...interface{}) {
	entry.Logf(ErrorLevel, format, args...)
}

func (entry *Entry) Fatalf(format string, args ...interface{}) {
	entry.Logf(FatalLevel, format, args...)
	entry.Logger.Exit(1)
}

func (entry *Entry) Panicf(format string, args ...interface{}) {
	entry.Logf(PanicLevel, format, args...)
}

// Entry Println family functions

func (entry *Entry) Logln(level Level, args ...interface{}) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.Log(level, entry.sprintlnn(args...))
	}
}

func (entry *Entry) Traceln(args ...interface{}) {
	entry.Logln(TraceLevel, args...)
}

func (entry *Entry) Debugln(args ...interface{}) {
	entry.Logln(DebugLevel, args...)
}

func (entry *Entry) Infoln(args ...interface{}) {
	entry.Logln(InfoLevel, args...)
}

func (entry *Entry) Println(args ...interface{}) {
	entry.Infoln(args...)
}

func (entry *Entry) Warnln(args ...interface{}) {
	entry.Logln(WarnLevel, args...)
}

func (entry *Entry) Warningln(args ...interface{}) {
	entry.Warnln(args...)
}

func (entry *Entry) Errorln(args ...interface{}) {
	entry.Logln(ErrorLevel, args...)
}

func (entry *Entry) Fatalln(args ...interface{}) {
	entry.Logln(FatalLevel, args...)
	entry.Logger.Exit(1)
}

func (entry *Entry) Panicln(args ...interface{}) {
	entry.Logln(PanicLevel, args...)
}

// Sprintlnn => Sprint no newline. This is to get the behavior of how
// fmt.Sprintln where spaces are always added between operands, regardless of
// their type. Instead of vendoring the Sprintln implementation to spare a
// string allocation, we do the simplest thing.
func (entry *Entry) sprintlnn(args ...interface{}) string {
	msg := fmt.Sprintln(args...)
	return msg[:len(msg)-1]
}
<commit_msg>code and comments clean up<commit_after>package logrus

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"time"
)

var (

	// qualified package name, cached at first use
	logrusPackage string

	// Positions in the call stack when tracing to report the calling method
	minimumCallerDepth int

	// Used for caller information initialisation
	callerInitOnce sync.Once
)

const (
	maximumCallerDepth int = 25
	knownLogrusFrames  int = 4
)

func init() {
	// start at the bottom of the stack before the package-name cache is primed
	minimumCallerDepth = 1
}

// Defines the key when adding errors using WithError.
var ErrorKey = "error"

// An entry is the final or intermediate Logrus logging entry. It contains all
// the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
// Info, Warn, Error, Fatal or Panic is called on it. These objects can be
// reused and passed around as much as you wish to avoid field duplication.
type Entry struct {
	Logger *Logger

	// Contains all the fields set by the user.
	Data Fields

	// Time at which the log entry was created
	Time time.Time

	// Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
	// This field will be set on entry firing and the value will be equal to the one in Logger struct field.
	Level Level

	// Calling method, with package name
	Caller *runtime.Frame

	// Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
	Message string

	// When formatter is called in entry.log(), a Buffer may be set to entry
	Buffer *bytes.Buffer

	// Contains the context set by the user. Useful for hook processing etc.
	Context context.Context

	// err may contain a field formatting error
	err string
}

func NewEntry(logger *Logger) *Entry {
	return &Entry{
		Logger: logger,
		// Default is three fields, plus one optional.  Give a little extra room.
		Data: make(Fields, 6),
	}
}

func (entry *Entry) Dup() *Entry {
	data := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		data[k] = v
	}
	return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
}

// Returns the bytes representation of this entry from the formatter.
func (entry *Entry) Bytes() ([]byte, error) {
	return entry.Logger.Formatter.Format(entry)
}

// Returns the string representation from the reader and ultimately the
// formatter.
func (entry *Entry) String() (string, error) {
	serialized, err := entry.Bytes()
	if err != nil {
		return "", err
	}
	str := string(serialized)
	return str, nil
}

// Add an error as single field (using the key defined in ErrorKey) to the Entry.
func (entry *Entry) WithError(err error) *Entry {
	return entry.WithField(ErrorKey, err)
}

// Add a context to the Entry.
func (entry *Entry) WithContext(ctx context.Context) *Entry {
	dataCopy := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		dataCopy[k] = v
	}
	return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
}

// Add a single field to the Entry.
func (entry *Entry) WithField(key string, value interface{}) *Entry {
	return entry.WithFields(Fields{key: value})
}

// Add a map of fields to the Entry.
func (entry *Entry) WithFields(fields Fields) *Entry {
	data := make(Fields, len(entry.Data)+len(fields))
	for k, v := range entry.Data {
		data[k] = v
	}
	fieldErr := entry.err
	for k, v := range fields {
		isErrField := false
		if t := reflect.TypeOf(v); t != nil {
			switch {
			case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
				isErrField = true
			}
		}
		if isErrField {
			tmp := fmt.Sprintf("can not add field %q", k)
			if fieldErr != "" {
				fieldErr = entry.err + ", " + tmp
			} else {
				fieldErr = tmp
			}
		} else {
			data[k] = v
		}
	}
	return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
}

// Overrides the time of the Entry.
func (entry *Entry) WithTime(t time.Time) *Entry {
	dataCopy := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		dataCopy[k] = v
	}
	return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
}

// getPackageName reduces a fully qualified function name to the package name
// There really ought to be to be a better way...
func getPackageName(f string) string {
	for {
		lastPeriod := strings.LastIndex(f, ".")
		lastSlash := strings.LastIndex(f, "/")
		if lastPeriod > lastSlash {
			f = f[:lastPeriod]
		} else {
			break
		}
	}

	return f
}

// getCaller retrieves the name of the first non-logrus calling function
func getCaller() *runtime.Frame {
	// cache this package's fully-qualified name
	callerInitOnce.Do(func() {
		pcs := make([]uintptr, maximumCallerDepth)
		_ = runtime.Callers(0, pcs)

		// dynamic get the package name and the minimum caller depth
		for i := 0; i < maximumCallerDepth; i++ {
			funcName := runtime.FuncForPC(pcs[i]).Name()
			if strings.Contains(funcName, "getCaller") {
				logrusPackage = getPackageName(funcName)
				break
			}
		}

		minimumCallerDepth = knownLogrusFrames
	})

	// Restrict the lookback frames to avoid runaway lookups
	pcs := make([]uintptr, maximumCallerDepth)
	depth := runtime.Callers(minimumCallerDepth, pcs)
	frames := runtime.CallersFrames(pcs[:depth])

	for f, again := frames.Next(); again; f, again = frames.Next() {
		pkg := getPackageName(f.Function)

		// If the caller isn't part of this package, we're done
		if pkg != logrusPackage {
			return &f //nolint:scopelint
		}
	}

	// if we got here, we failed to find the caller's context
	return nil
}

func (entry Entry) HasCaller() (has bool) {
	return entry.Logger != nil &&
		entry.Logger.ReportCaller &&
		entry.Caller != nil
}

func (entry *Entry) log(level Level, msg string) {
	var buffer *bytes.Buffer

	newEntry := entry.Dup()

	if newEntry.Time.IsZero() {
		newEntry.Time = time.Now()
	}

	newEntry.Level = level
	newEntry.Message = msg

	newEntry.Logger.mu.Lock()
	reportCaller := newEntry.Logger.ReportCaller
	newEntry.Logger.mu.Unlock()

	if reportCaller {
		newEntry.Caller = getCaller()
	}

	newEntry.fireHooks()

	buffer = getBuffer()
	defer func() {
		newEntry.Buffer = nil
		putBuffer(buffer)
	}()
	buffer.Reset()
	newEntry.Buffer = buffer

	newEntry.write()

	newEntry.Buffer = nil

	// To avoid Entry#log() returning a value that only would make sense for
	// panic() to use in Entry#Panic(), we avoid the allocation by checking
	// directly here.
	if level <= PanicLevel {
		panic(newEntry)
	}
}

func (entry *Entry) fireHooks() {
	err := entry.Logger.Hooks.Fire(entry.Level, entry)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
	}
}

func (entry *Entry) write() {
	serialized, err := entry.Logger.Formatter.Format(entry)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
		return
	}
	entry.Logger.mu.Lock()
	defer entry.Logger.mu.Unlock()
	if _, err := entry.Logger.Out.Write(serialized); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
	}
}

func (entry *Entry) Log(level Level, args ...interface{}) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.log(level, fmt.Sprint(args...))
	}
}

func (entry *Entry) Trace(args ...interface{}) {
	entry.Log(TraceLevel, args...)
}

func (entry *Entry) Debug(args ...interface{}) {
	entry.Log(DebugLevel, args...)
}

func (entry *Entry) Print(args ...interface{}) {
	entry.Info(args...)
}

func (entry *Entry) Info(args ...interface{}) {
	entry.Log(InfoLevel, args...)
}

func (entry *Entry) Warn(args ...interface{}) {
	entry.Log(WarnLevel, args...)
}

func (entry *Entry) Warning(args ...interface{}) {
	entry.Warn(args...)
}

func (entry *Entry) Error(args ...interface{}) {
	entry.Log(ErrorLevel, args...)
}

func (entry *Entry) Fatal(args ...interface{}) {
	entry.Log(FatalLevel, args...)
	entry.Logger.Exit(1)
}

func (entry *Entry) Panic(args ...interface{}) {
	entry.Log(PanicLevel, args...)
}

// Entry Printf family functions

func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.Log(level, fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Tracef(format string, args ...interface{}) {
	entry.Logf(TraceLevel, format, args...)
}

func (entry *Entry) Debugf(format string, args ...interface{}) {
	entry.Logf(DebugLevel, format, args...)
}

func (entry *Entry) Infof(format string, args ...interface{}) {
	entry.Logf(InfoLevel, format, args...)
}

func (entry *Entry) Printf(format string, args ...interface{}) {
	entry.Infof(format, args...)
}

func (entry *Entry) Warnf(format string, args ...interface{}) {
	entry.Logf(WarnLevel, format, args...)
}

func (entry *Entry) Warningf(format string, args ...interface{}) {
	entry.Warnf(format, args...)
}

func (entry *Entry) Errorf(format string, args ...interface{}) {
	entry.Logf(ErrorLevel, format, args...)
}

func (entry *Entry) Fatalf(format string, args ...interface{}) {
	entry.Logf(FatalLevel, format, args...)
	entry.Logger.Exit(1)
}

func (entry *Entry) Panicf(format string, args ...interface{}) {
	entry.Logf(PanicLevel, format, args...)
}

// Entry Println family functions

func (entry *Entry) Logln(level Level, args ...interface{}) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.Log(level, entry.sprintlnn(args...))
	}
}

func (entry *Entry) Traceln(args ...interface{}) {
	entry.Logln(TraceLevel, args...)
}

func (entry *Entry) Debugln(args ...interface{}) {
	entry.Logln(DebugLevel, args...)
}

func (entry *Entry) Infoln(args ...interface{}) {
	entry.Logln(InfoLevel, args...)
}

func (entry *Entry) Println(args ...interface{}) {
	entry.Infoln(args...)
}

func (entry *Entry) Warnln(args ...interface{}) {
	entry.Logln(WarnLevel, args...)
}

func (entry *Entry) Warningln(args ...interface{}) {
	entry.Warnln(args...)
}

func (entry *Entry) Errorln(args ...interface{}) {
	entry.Logln(ErrorLevel, args...)
}

func (entry *Entry) Fatalln(args ...interface{}) {
	entry.Logln(FatalLevel, args...)
	entry.Logger.Exit(1)
}

func (entry *Entry) Panicln(args ...interface{}) {
	entry.Logln(PanicLevel, args...)
}

// Sprintlnn => Sprint no newline. This is to get the behavior of how
// fmt.Sprintln where spaces are always added between operands, regardless of
// their type. Instead of vendoring the Sprintln implementation to spare a
// string allocation, we do the simplest thing.
func (entry *Entry) sprintlnn(args ...interface{}) string {
	msg := fmt.Sprintln(args...)
	return msg[:len(msg)-1]
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
)

func FileList(d string) []os.FileInfo {
	dorf, _ := ioutil.ReadDir(d)
	var fileinfos []os.FileInfo
	for i := 0; i < len(dorf); i++ {
		if dorf[i].IsDir() {
			dir := d + "/" + dorf[i].Name()
			infos := FileList(dir)
			for k := 0; k < len(infos); k++ {
				fileinfos = append(fileinfos, infos[k])
			}
		} else {
			fileinfos = append(fileinfos, dorf[i])
		}
	}
	return fileinfos
}

func main() {
	var dir string

	flag.Parse();
	dirs := flag.Args()

	if len(dirs) == 0 {
		dir = "./"
	} else {
		dir = dirs[0]
	}

	files := FileList(dir)
	count := len(files)

	var size int64
	for i := 0; i < len(files); i++ {
		size += files[i].Size()
	}
	fmt.Printf("%d files, %d bytes", count, size)
}
<commit_msg>du.go multi dir & use range<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
)

func FileList(d string) []os.FileInfo {
	dorf, _ := ioutil.ReadDir(d)
	var fileinfos []os.FileInfo
	for i := 0; i < len(dorf); i++ {
		if dorf[i].IsDir() {
			dir := d + "/" + dorf[i].Name()
			infos := FileList(dir)
			for _, info := range infos {
				fileinfos = append(fileinfos, info)
			}
		} else {
			fileinfos = append(fileinfos, dorf[i])
		}
	}
	return fileinfos
}

func main() {
	flag.Parse();
	dirs := flag.Args()

	// 指定がない場合はカレントディレクトリ配下に対して実行
	if len(dirs) == 0 {
		dirs = append(dirs, "./")
	}

	var size int64 // os.Fileinfo.Size() の返り値はint64なのでそのまま使う
	var count int

	for _, dir := range dirs {
		files := FileList(dir)
		count += len(files)
		for _, file := range files {
			size += file.Size()
		}
	}
	fmt.Printf("%d files, %d bytes", count, size)
}
<|endoftext|>


<commit_before>package api2go

import "strconv"

//HTTPError is used for errors
type HTTPError struct {
	err    error
	msg    string
	status int
	Errors map[string]Error `json:"errors,omitempty"`
}

//Error can be used for all kind of application errors
//e.g. you would use it to define form errors or any
//other semantical application problems
//for more information see http://jsonapi.org/format/#errors
type Error struct {
	ID     string `json:"id,omitempty"`
	Href   string `json:"href,omitempty"`
	Status string `json:"status,omitempty"`
	Code   string `json:"code,omitempty"`
	Title  string `json:"title,omitempty"`
	Detail string `json:"detail,omitempty"`
	Path   string `json:"path,omitempty"`
}

// NewHTTPError creates a new error with message and status code.
// `err` will be logged (but never sent to a client), `msg` will be sent and `status` is the http status code.
// `err` can be nil.
func NewHTTPError(err error, msg string, status int) HTTPError {
	return HTTPError{err: err, msg: msg, status: status, Errors: make(map[string]Error)}
}

//Error returns a nice string represenation including the status
func (e HTTPError) Error() string {
	msg := "http error (" + strconv.Itoa(e.status) + "): " + e.msg
	if e.err != nil {
		msg += ", " + e.err.Error()
	}

	return msg
}
<commit_msg>Include more information in fmt.Stringer<commit_after>package api2go

import "fmt"

//HTTPError is used for errors
type HTTPError struct {
	err    error
	msg    string
	status int
	Errors map[string]Error `json:"errors,omitempty"`
}

//Error can be used for all kind of application errors
//e.g. you would use it to define form errors or any
//other semantical application problems
//for more information see http://jsonapi.org/format/#errors
type Error struct {
	ID     string `json:"id,omitempty"`
	Href   string `json:"href,omitempty"`
	Status string `json:"status,omitempty"`
	Code   string `json:"code,omitempty"`
	Title  string `json:"title,omitempty"`
	Detail string `json:"detail,omitempty"`
	Path   string `json:"path,omitempty"`
}

// NewHTTPError creates a new error with message and status code.
// `err` will be logged (but never sent to a client), `msg` will be sent and `status` is the http status code.
// `err` can be nil.
func NewHTTPError(err error, msg string, status int) HTTPError {
	return HTTPError{err: err, msg: msg, status: status, Errors: make(map[string]Error)}
}

//Error returns a nice string represenation including the status
func (e HTTPError) Error() string {
	msg := fmt.Sprintf("http error (%d) %s and %d more errors", e.status, e.msg, len(e.Errors))
	if e.err != nil {
		msg += ", " + e.err.Error()
	}

	return msg
}
<|endoftext|>


<commit_before>package cfclient

//go:generate go run gen_error.go

import (
	"fmt"
)

type CloudFoundryErrors struct {
	Errors []CloudFoundryError `json:"errors"`
}

func (cfErrs CloudFoundryErrors) Error() string {
	err := ""

	for _, cfErr := range cfErrs.Errors {
		err += fmt.Sprintf("%s\n", cfErr)
	}

	return err
}

type CloudFoundryError struct {
	Code        int    `json:"code"`
	ErrorCode   string `json:"error_code"`
	Description string `json:"description"`
}

func (cfErr CloudFoundryError) Error() string {
	return fmt.Sprintf("cfclient: error (%d): %s", cfErr.Code, cfErr.ErrorCode)
}

type CloudFoundryHTTPError struct {
	StatusCode int
	Status     string
	Body       []byte
}

func (e CloudFoundryHTTPError) Error() string {
	return fmt.Sprintf("cfclient: HTTP error (%d): %s", e.StatusCode, e.Status)
}
<commit_msg>Update errors to include detail<commit_after>package cfclient

//go:generate go run gen_error.go

import (
	"fmt"
)

type CloudFoundryErrors struct {
	Errors []CloudFoundryError `json:"errors"`
}

func (cfErrs CloudFoundryErrors) Error() string {
	err := ""

	for _, cfErr := range cfErrs.Errors {
		err += fmt.Sprintf("%s\n", cfErr)
	}

	return err
}

type CloudFoundryError struct {
	Code        int    `json:"code"`
	ErrorCode   string `json:"error_code"`
	Description string `json:"description"`
}

func (cfErr CloudFoundryError) Error() string {
	return fmt.Sprintf("cfclient: error (%d): %s", cfErr.Code, cfErr.ErrorCode)
}

type CloudFoundryHTTPError struct {
	StatusCode int
	Status     string
	Body       []byte
}

func (e CloudFoundryHTTPError) Error() string {
	return fmt.Sprintf("cfclient error %s(%d): %s", cfErr.Title, cfErr.Code, cfErr.Detail)
}
<|endoftext|>