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

// SIG(0)
//
// From RFC 2931:
//
//     SIG(0) provides protection for DNS transactions and requests ....
//     ... protection for glue records, DNS requests, protection for message headers
//     on requests and responses, and protection of the overall integrity of a response.
//
// It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared
// secret approach in TSIG.
package dns

import (
	"crypto"
	"crypto/dsa"
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/rsa"
	"fmt"
	"math/big"
	"strings"
	"time"
)

// Sign signs a dns.Msg. It fills the signature with the appropriate data.
// The SIG record should have the SignerName, KeyTag, Algorithm, Inception
// and Expiration set.
func (rr *SIG) Sign(k PrivateKey, m *Msg) ([]byte, error) {
	if k == nil {
		return nil, ErrPrivKey
	}
	if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
		return nil, ErrKey
	}
	rr.Header().Rrtype = TypeSIG
	rr.Header().Class = ClassANY
	rr.Header().Ttl = 0
	rr.Header().Name = "."
	rr.OrigTtl = 0
	rr.TypeCovered = 0
	rr.Labels = 0

	buflen := m.Len() + rr.len()
	switch k := k.(type) {
	case *rsa.PrivateKey:
		buflen += len(k.N.Bytes())
	case *dsa.PrivateKey:
		buflen += 40
	case *ecdsa.PrivateKey:
		buflen += 96
	default:
		return nil, ErrPrivKey
	}
	buf := make([]byte, m.Len()+rr.len()+buflen)
	mbuf, err := m.PackBuffer(buf)
	if err != nil {
		return nil, err
	}
	if &buf[0] != &mbuf[0] {
		return nil, ErrBuf
	}
	off, err := PackRR(rr, buf, len(mbuf), nil, false)
	if err != nil {
		return nil, err
	}
	buf = buf[:off:cap(buf)]
	var hash crypto.Hash
	switch rr.Algorithm {
	case DSA, RSASHA1:
		hash = crypto.SHA1
	case RSASHA256, ECDSAP256SHA256:
		hash = crypto.SHA256
	case ECDSAP384SHA384:
		hash = crypto.SHA384
	case RSASHA512:
		hash = crypto.SHA512
	default:
		return nil, ErrAlg
	}
	hasher := hash.New()
	// Write SIG rdata
	hasher.Write(buf[len(mbuf)+1+2+2+4+2:])
	// Write message
	hasher.Write(buf[:len(mbuf)])
	hashed := hasher.Sum(nil)

	var sig []byte
	switch p := k.(type) {
	case *dsa.PrivateKey:
		t := byte((len(p.PublicKey.Y.Bytes()) - 64) / 8)
		r1, s1, err := dsa.Sign(rand.Reader, p, hashed)
		if err != nil {
			return nil, err
		}
		sig = make([]byte, 0, 1+len(r1.Bytes())+len(s1.Bytes()))
		sig = append(sig, t)
		sig = append(sig, r1.Bytes()...)
		sig = append(sig, s1.Bytes()...)
	case *rsa.PrivateKey:
		sig, err = rsa.SignPKCS1v15(rand.Reader, p, hash, hashed)
		if err != nil {
			return nil, err
		}
	case *ecdsa.PrivateKey:
		r1, s1, err := ecdsa.Sign(rand.Reader, p, hashed)
		if err != nil {
			return nil, err
		}
		sig = r1.Bytes()
		sig = append(sig, s1.Bytes()...)
	default:
		return nil, ErrAlg
	}
	rr.Signature = unpackBase64(sig)
	buf = append(buf, sig...)
	if len(buf) > int(^uint16(0)) {
		return nil, ErrBuf
	}
	// Adjust sig data length
	rdoff := len(mbuf) + 1 + 2 + 2 + 4
	rdlen, _ := unpackUint16(buf, rdoff)
	rdlen += uint16(len(sig))
	buf[rdoff], buf[rdoff+1] = packUint16(rdlen)
	// Adjust additional count
	adc, _ := unpackUint16(buf, 10)
	adc += 1
	buf[10], buf[11] = packUint16(adc)
	return buf, nil
}

// Verify validates the message buf using the key k.
// It's assumed that buf is a valid message from which rr was unpacked.
func (rr *SIG) Verify(k *KEY, buf []byte) error {
	if k == nil {
		return ErrKey
	}
	if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
		return ErrKey
	}

	var hash crypto.Hash
	switch rr.Algorithm {
	case DSA, RSASHA1:
		hash = crypto.SHA1
	case RSASHA256, ECDSAP256SHA256:
		hash = crypto.SHA256
	case ECDSAP384SHA384:
		hash = crypto.SHA384
	case RSASHA512:
		hash = crypto.SHA512
	default:
		return ErrAlg
	}
	hasher := hash.New()

	buflen := len(buf)
	qdc, _ := unpackUint16(buf, 4)
	anc, _ := unpackUint16(buf, 6)
	auc, _ := unpackUint16(buf, 8)
	adc, offset := unpackUint16(buf, 10)
	var err error
	for i := uint16(0); i < qdc && offset < buflen; i++ {
		// decode a name
		_, offset, err = UnpackDomainName(buf, offset)
		if err != nil {
			return err
		}
		// skip past Type and Class
		offset += 2 + 2
	}
	for i := uint16(1); i < anc+auc+adc && offset < buflen; i++ {
		// decode a name
		_, offset, err = UnpackDomainName(buf, offset)
		if err != nil {
			return err
		}
		// skip past Type, Class and TTL
		offset += 2 + 2 + 4
		var rdlen uint16
		rdlen, offset = unpackUint16(buf, offset)
		offset += int(rdlen)
	}
	// offset should be just prior to SIG
	bodyend := offset
	// Owner name SHOULD be root
	_, offset, err = UnpackDomainName(buf, offset)
	if err != nil {
		return err
	}
	// Skip Type, Class, TTL, RDLen
	offset += 2 + 2 + 4 + 2
	sigstart := offset
	offset += 2 + 1 + 1 + 4 // skip Type Covered, Algorithm, Labels, Original TTL
	// TODO: This should be moved out and used elsewhere
	unpackUint32 := func(buf []byte, off int) (uint32, int) {
		r := uint32(buf[off])<<24 | uint32(buf[off+1])<<16 | uint32(buf[off+2])<<8 | uint32(buf[off+3])
		return r, off + 4
	}
	var expire, incept uint32
	expire, offset = unpackUint32(buf, offset)
	incept, offset = unpackUint32(buf, offset)
	now := uint32(time.Now().Unix())
	if now < incept || now > expire {
		return ErrTime
	}
	offset += 2 // skip key tag
	var signername string
	signername, offset, err = UnpackDomainName(buf, offset)
	if err != nil {
		return err
	}
	// If key has come from the DNS name compression might
	// have mangled the case of the name
	if strings.ToLower(signername) != strings.ToLower(k.Header().Name) {
		return fmt.Errorf("Signer name doesn't match key name")
	}
	sigend := offset
	hasher.Write(buf[sigstart:sigend])
	hasher.Write(buf[:10])
	hasher.Write([]byte{
		byte((adc - 1) << 8),
		byte(adc - 1),
	})
	hasher.Write(buf[12:bodyend])

	hashed := hasher.Sum(nil)
	sig := buf[sigend:]
	switch k.Algorithm {
	case DSA:
		pk := k.publicKeyDSA()
		sig = sig[1:]
		r := big.NewInt(0)
		r.SetBytes(sig[:len(sig)/2])
		s := big.NewInt(0)
		s.SetBytes(sig[len(sig)/2:])
		if pk != nil {
			if dsa.Verify(pk, hashed, r, s) {
				return nil
			}
			return ErrSig
		}
	case RSASHA1, RSASHA256, RSASHA512:
		pk := k.publicKeyRSA()
		if pk != nil {
			return rsa.VerifyPKCS1v15(pk, hash, hashed, sig)
		}
	case ECDSAP256SHA256, ECDSAP384SHA384:
		pk := k.publicKeyCurve()
		r := big.NewInt(0)
		r.SetBytes(sig[:len(sig)/2])
		s := big.NewInt(0)
		s.SetBytes(sig[len(sig)/2:])
		if pk != nil {
			if ecdsa.Verify(pk, hashed, r, s) {
				return nil
			}
			return ErrSig
		}
	}
	return ErrKeyAlg
}
<commit_msg>There is a copyright file now<commit_after>// SIG(0)
//
// From RFC 2931:
//
//     SIG(0) provides protection for DNS transactions and requests ....
//     ... protection for glue records, DNS requests, protection for message headers
//     on requests and responses, and protection of the overall integrity of a response.
//
// It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared
// secret approach in TSIG.
package dns

import (
	"crypto"
	"crypto/dsa"
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/rsa"
	"fmt"
	"math/big"
	"strings"
	"time"
)

// Sign signs a dns.Msg. It fills the signature with the appropriate data.
// The SIG record should have the SignerName, KeyTag, Algorithm, Inception
// and Expiration set.
func (rr *SIG) Sign(k PrivateKey, m *Msg) ([]byte, error) {
	if k == nil {
		return nil, ErrPrivKey
	}
	if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
		return nil, ErrKey
	}
	rr.Header().Rrtype = TypeSIG
	rr.Header().Class = ClassANY
	rr.Header().Ttl = 0
	rr.Header().Name = "."
	rr.OrigTtl = 0
	rr.TypeCovered = 0
	rr.Labels = 0

	buflen := m.Len() + rr.len()
	switch k := k.(type) {
	case *rsa.PrivateKey:
		buflen += len(k.N.Bytes())
	case *dsa.PrivateKey:
		buflen += 40
	case *ecdsa.PrivateKey:
		buflen += 96
	default:
		return nil, ErrPrivKey
	}
	buf := make([]byte, m.Len()+rr.len()+buflen)
	mbuf, err := m.PackBuffer(buf)
	if err != nil {
		return nil, err
	}
	if &buf[0] != &mbuf[0] {
		return nil, ErrBuf
	}
	off, err := PackRR(rr, buf, len(mbuf), nil, false)
	if err != nil {
		return nil, err
	}
	buf = buf[:off:cap(buf)]
	var hash crypto.Hash
	switch rr.Algorithm {
	case DSA, RSASHA1:
		hash = crypto.SHA1
	case RSASHA256, ECDSAP256SHA256:
		hash = crypto.SHA256
	case ECDSAP384SHA384:
		hash = crypto.SHA384
	case RSASHA512:
		hash = crypto.SHA512
	default:
		return nil, ErrAlg
	}
	hasher := hash.New()
	// Write SIG rdata
	hasher.Write(buf[len(mbuf)+1+2+2+4+2:])
	// Write message
	hasher.Write(buf[:len(mbuf)])
	hashed := hasher.Sum(nil)

	var sig []byte
	switch p := k.(type) {
	case *dsa.PrivateKey:
		t := byte((len(p.PublicKey.Y.Bytes()) - 64) / 8)
		r1, s1, err := dsa.Sign(rand.Reader, p, hashed)
		if err != nil {
			return nil, err
		}
		sig = make([]byte, 0, 1+len(r1.Bytes())+len(s1.Bytes()))
		sig = append(sig, t)
		sig = append(sig, r1.Bytes()...)
		sig = append(sig, s1.Bytes()...)
	case *rsa.PrivateKey:
		sig, err = rsa.SignPKCS1v15(rand.Reader, p, hash, hashed)
		if err != nil {
			return nil, err
		}
	case *ecdsa.PrivateKey:
		r1, s1, err := ecdsa.Sign(rand.Reader, p, hashed)
		if err != nil {
			return nil, err
		}
		sig = r1.Bytes()
		sig = append(sig, s1.Bytes()...)
	default:
		return nil, ErrAlg
	}
	rr.Signature = unpackBase64(sig)
	buf = append(buf, sig...)
	if len(buf) > int(^uint16(0)) {
		return nil, ErrBuf
	}
	// Adjust sig data length
	rdoff := len(mbuf) + 1 + 2 + 2 + 4
	rdlen, _ := unpackUint16(buf, rdoff)
	rdlen += uint16(len(sig))
	buf[rdoff], buf[rdoff+1] = packUint16(rdlen)
	// Adjust additional count
	adc, _ := unpackUint16(buf, 10)
	adc += 1
	buf[10], buf[11] = packUint16(adc)
	return buf, nil
}

// Verify validates the message buf using the key k.
// It's assumed that buf is a valid message from which rr was unpacked.
func (rr *SIG) Verify(k *KEY, buf []byte) error {
	if k == nil {
		return ErrKey
	}
	if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
		return ErrKey
	}

	var hash crypto.Hash
	switch rr.Algorithm {
	case DSA, RSASHA1:
		hash = crypto.SHA1
	case RSASHA256, ECDSAP256SHA256:
		hash = crypto.SHA256
	case ECDSAP384SHA384:
		hash = crypto.SHA384
	case RSASHA512:
		hash = crypto.SHA512
	default:
		return ErrAlg
	}
	hasher := hash.New()

	buflen := len(buf)
	qdc, _ := unpackUint16(buf, 4)
	anc, _ := unpackUint16(buf, 6)
	auc, _ := unpackUint16(buf, 8)
	adc, offset := unpackUint16(buf, 10)
	var err error
	for i := uint16(0); i < qdc && offset < buflen; i++ {
		// decode a name
		_, offset, err = UnpackDomainName(buf, offset)
		if err != nil {
			return err
		}
		// skip past Type and Class
		offset += 2 + 2
	}
	for i := uint16(1); i < anc+auc+adc && offset < buflen; i++ {
		// decode a name
		_, offset, err = UnpackDomainName(buf, offset)
		if err != nil {
			return err
		}
		// skip past Type, Class and TTL
		offset += 2 + 2 + 4
		var rdlen uint16
		rdlen, offset = unpackUint16(buf, offset)
		offset += int(rdlen)
	}
	// offset should be just prior to SIG
	bodyend := offset
	// Owner name SHOULD be root
	_, offset, err = UnpackDomainName(buf, offset)
	if err != nil {
		return err
	}
	// Skip Type, Class, TTL, RDLen
	offset += 2 + 2 + 4 + 2
	sigstart := offset
	offset += 2 + 1 + 1 + 4 // skip Type Covered, Algorithm, Labels, Original TTL
	// TODO: This should be moved out and used elsewhere
	unpackUint32 := func(buf []byte, off int) (uint32, int) {
		r := uint32(buf[off])<<24 | uint32(buf[off+1])<<16 | uint32(buf[off+2])<<8 | uint32(buf[off+3])
		return r, off + 4
	}
	var expire, incept uint32
	expire, offset = unpackUint32(buf, offset)
	incept, offset = unpackUint32(buf, offset)
	now := uint32(time.Now().Unix())
	if now < incept || now > expire {
		return ErrTime
	}
	offset += 2 // skip key tag
	var signername string
	signername, offset, err = UnpackDomainName(buf, offset)
	if err != nil {
		return err
	}
	// If key has come from the DNS name compression might
	// have mangled the case of the name
	if strings.ToLower(signername) != strings.ToLower(k.Header().Name) {
		return fmt.Errorf("Signer name doesn't match key name")
	}
	sigend := offset
	hasher.Write(buf[sigstart:sigend])
	hasher.Write(buf[:10])
	hasher.Write([]byte{
		byte((adc - 1) << 8),
		byte(adc - 1),
	})
	hasher.Write(buf[12:bodyend])

	hashed := hasher.Sum(nil)
	sig := buf[sigend:]
	switch k.Algorithm {
	case DSA:
		pk := k.publicKeyDSA()
		sig = sig[1:]
		r := big.NewInt(0)
		r.SetBytes(sig[:len(sig)/2])
		s := big.NewInt(0)
		s.SetBytes(sig[len(sig)/2:])
		if pk != nil {
			if dsa.Verify(pk, hashed, r, s) {
				return nil
			}
			return ErrSig
		}
	case RSASHA1, RSASHA256, RSASHA512:
		pk := k.publicKeyRSA()
		if pk != nil {
			return rsa.VerifyPKCS1v15(pk, hash, hashed, sig)
		}
	case ECDSAP256SHA256, ECDSAP384SHA384:
		pk := k.publicKeyCurve()
		r := big.NewInt(0)
		r.SetBytes(sig[:len(sig)/2])
		s := big.NewInt(0)
		s.SetBytes(sig[len(sig)/2:])
		if pk != nil {
			if ecdsa.Verify(pk, hashed, r, s) {
				return nil
			}
			return ErrSig
		}
	}
	return ErrKeyAlg
}
<|endoftext|>


<commit_before>package sigi

import (
	"log"
	"reflect"
	"runtime/debug"
	"sync"
)

var (
	handlers map[string][]Handler = map[string][]Handler{}

	mutex sync.Mutex
)

type Handler interface{}

// Connect signal with handler
func Connect(signal string, handler Handler) {
	if kind := reflect.TypeOf(handler).Kind(); kind != reflect.Func {
		log.Printf("Handler type is '%s' must be func", kind)
		return
	}

	mutex.Lock()
	defer mutex.Unlock()
	if _, exists := handlers[signal]; exists {
		handlers[signal] = append(handlers[signal], handler)
	} else {
		handlers[signal] = []Handler{handler}
	}
}

// Disconnect signal from handler
func Disconnect(signal string, handler Handler) {
	mutex.Lock()
	defer mutex.Unlock()
	if signalHandlers, exists := handlers[signal]; exists {
		handlerValue := reflect.ValueOf(handler)
		for i := range signalHandlers {
			if handlerValue == reflect.ValueOf(signalHandlers[i]) {
				handlers[signal] = append(signalHandlers[:i], signalHandlers[i+1:]...)
				return
			}
		}
	}
}

// Emit signal
func Emit(signal string, args ...interface{}) {
	signalHandlers, exists := handlers[signal]
	if !exists {
		log.Printf("No handlers for signal '%s'\n", signal)
		return
	}

	for _, handler := range signalHandlers {
		callHandler(handler, args...)
	}
}

func callHandler(handler Handler, args ...interface{}) {
	defer func() {
		if err := recover(); err != nil {
			log.Println(err)
			log.Println(string(debug.Stack()))
		}
	}()

	in := make([]reflect.Value, len(args))
	for i := range args {
		in[i] = reflect.ValueOf(args[i])
	}

	reflect.ValueOf(handler).Call(in)
}
<commit_msg>Using RWMutex.<commit_after>package sigi

import (
	"log"
	"reflect"
	"runtime/debug"
	"sync"
)

var (
	handlers map[string][]Handler = map[string][]Handler{}

	mutex sync.RWMutex
)

type Handler interface{}

// Connect signal with handler
func Connect(signal string, handler Handler) {
	if kind := reflect.TypeOf(handler).Kind(); kind != reflect.Func {
		log.Printf("Handler type is '%s' must be func", kind)
		return
	}

	mutex.Lock()
	defer mutex.Unlock()
	if _, exists := handlers[signal]; exists {
		handlers[signal] = append(handlers[signal], handler)
	} else {
		handlers[signal] = []Handler{handler}
	}
}

// Disconnect signal from handler
func Disconnect(signal string, handler Handler) {
	mutex.Lock()
	defer mutex.Unlock()
	if signalHandlers, exists := handlers[signal]; exists {
		handlerValue := reflect.ValueOf(handler)
		for i := range signalHandlers {
			if handlerValue == reflect.ValueOf(signalHandlers[i]) {
				handlers[signal] = append(signalHandlers[:i], signalHandlers[i+1:]...)
				return
			}
		}
	}
}

// Emit signal
func Emit(signal string, args ...interface{}) {
	mutex.RLock()
	defer mutex.RUnlock()
	signalHandlers, exists := handlers[signal]
	if !exists {
		log.Printf("No handlers for signal '%s'\n", signal)
		return
	}

	for _, handler := range signalHandlers {
		callHandler(handler, args...)
	}
}

func callHandler(handler Handler, args ...interface{}) {
	defer func() {
		if err := recover(); err != nil {
			log.Println(err)
			log.Println(string(debug.Stack()))
		}
	}()

	in := make([]reflect.Value, len(args))
	for i := range args {
		in[i] = reflect.ValueOf(args[i])
	}

	reflect.ValueOf(handler).Call(in)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"text/tabwriter"
)

func main() {
	flag.Parse()

	args := flag.Args()
	if len(args) == 0 {
		args = append(args, `.`)
	}

	for _, n := range args {
		add(n)
	}

	for _, f := range files {
		c, err := ioutil.ReadFile(f)
		if err != nil {
			fmt.Fprintf(os.Stderr, "  ! %s\n", f)
		}
		cs := string(c)
		handleFile(f, cs)
	}
	printInfo()
}

type Matcher interface{
	Match(string) bool
}

type Language interface{
	Name() string
	Matcher
	Update(string, *Stats)
}

type lName string

func (l lName) Name() string {
	return string(l)
}

type lMatch func(string) bool

func (m lMatch) Match(fname string) bool {
	return m(fname)
}

func mExt(exts ...string) lMatch {
	return func(fname string) bool {
		for _, ext := range exts {
			if ext == path.Ext(fname) {
				return true
			}
		}
		return false
	}
}

func mName(names ...string) lMatch {
	return func(fname string) bool {
		for _, name := range names {
			if name == path.Base(fname) {
				return true
			}
		}
		return false
	}
}

type Stats struct{
	FileCount    int
	TotalLines   int
	CodeLines    int
	BlankLines   int
	CommentLines int
}

var info = map[string]*Stats{}

var languages = []Language{
	LineLanguage{"C", mExt(".c")},
	LineLanguage{"C++", mExt(".cc", ".cpp", ".cxx")},
	LineLanguage{"Go", mExt(".go")},
	LineLanguage{"Haskell", mExt(".hs", ".lhs")},
	LineLanguage{"Python", mExt(".py")},
	LineLanguage{"Lisp", mExt(".lsp")},
	LineLanguage{"Make", mName("makefile", "Makefile", "MAKEFILE")},
	LineLanguage{"HTML", mExt(".htm", ".html", ".xhtml")},
}

func handleFile(fname, content string) {
	var l Language
	ok := false
	for _, lang := range languages {
		if lang.Match(fname) {
			ok = true
			l = lang
			break
		}
	}
	if !ok {
		return // ignore this file
	}
	i, ok := info[l.Name()]
	if !ok {
		i = &Stats{}
		info[l.Name()] = i
	}
	c, err := ioutil.ReadFile(fname)
	if err != nil {
		fmt.Fprintf(os.Stderr, "  ! %s\n", fname)
		return
	}
	l.Update(string(c), i)
}

func printInfo() {
	w := tabwriter.NewWriter(os.Stdout, 2, 8, 2, ' ', tabwriter.AlignRight)
	fmt.Fprintln(w, "Language\tFiles\tLines\t")
	for n, i := range info {
		fmt.Fprintf(w, "%s\t%d\t%d\t\n", n, i.FileCount, i.TotalLines)
	}
	w.Flush()
}

var files []string

func add(n string) {
	fi, err := os.Stat(n)
	if err != nil {
		goto invalid
	}
	if fi.IsDir() {
		fs, err := ioutil.ReadDir(n)
		if err != nil {
			goto invalid
		}
		for _, f := range fs {
			if f.Name()[0] != '.' {
				add(path.Join(n, f.Name()))
			}
		}
		return
	}
	if fi.Mode() & os.ModeType == 0 {
		files = append(files, n)
		return
	}

	println(fi.Mode())

invalid:
	fmt.Fprintf(os.Stderr, "  ! %s\n", n)
}
<commit_msg>Adding more extensions<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"text/tabwriter"
)

func main() {
	flag.Parse()

	args := flag.Args()
	if len(args) == 0 {
		args = append(args, `.`)
	}

	for _, n := range args {
		add(n)
	}

	for _, f := range files {
		c, err := ioutil.ReadFile(f)
		if err != nil {
			fmt.Fprintf(os.Stderr, "  ! %s\n", f)
		}
		cs := string(c)
		handleFile(f, cs)
	}
	printInfo()
}

type Matcher interface{
	Match(string) bool
}

type Language interface{
	Name() string
	Matcher
	Update(string, *Stats)
}

type lName string

func (l lName) Name() string {
	return string(l)
}

type lMatch func(string) bool

func (m lMatch) Match(fname string) bool {
	return m(fname)
}

func mExt(exts ...string) lMatch {
	return func(fname string) bool {
		for _, ext := range exts {
			if ext == path.Ext(fname) {
				return true
			}
		}
		return false
	}
}

func mName(names ...string) lMatch {
	return func(fname string) bool {
		for _, name := range names {
			if name == path.Base(fname) {
				return true
			}
		}
		return false
	}
}

type Stats struct{
	FileCount    int
	TotalLines   int
	CodeLines    int
	BlankLines   int
	CommentLines int
}

var info = map[string]*Stats{}

var languages = []Language{
	LineLanguage{"C", mExt(".c", ".h")},
	LineLanguage{"C++", mExt(".cc", ".cpp", ".cxx", ".hh", ".hpp", ".hxx")},
	LineLanguage{"Go", mExt(".go")},
	LineLanguage{"Haskell", mExt(".hs", ".lhs")},
	LineLanguage{"Python", mExt(".py")},
	LineLanguage{"Lisp", mExt(".lsp")},
	LineLanguage{"Make", mName("makefile", "Makefile", "MAKEFILE")},
	LineLanguage{"HTML", mExt(".htm", ".html", ".xhtml")},
}

func handleFile(fname, content string) {
	var l Language
	ok := false
	for _, lang := range languages {
		if lang.Match(fname) {
			ok = true
			l = lang
			break
		}
	}
	if !ok {
		return // ignore this file
	}
	i, ok := info[l.Name()]
	if !ok {
		i = &Stats{}
		info[l.Name()] = i
	}
	c, err := ioutil.ReadFile(fname)
	if err != nil {
		fmt.Fprintf(os.Stderr, "  ! %s\n", fname)
		return
	}
	l.Update(string(c), i)
}

func printInfo() {
	w := tabwriter.NewWriter(os.Stdout, 2, 8, 2, ' ', tabwriter.AlignRight)
	fmt.Fprintln(w, "Language\tFiles\tLines\t")
	for n, i := range info {
		fmt.Fprintf(w, "%s\t%d\t%d\t\n", n, i.FileCount, i.TotalLines)
	}
	w.Flush()
}

var files []string

func add(n string) {
	fi, err := os.Stat(n)
	if err != nil {
		goto invalid
	}
	if fi.IsDir() {
		fs, err := ioutil.ReadDir(n)
		if err != nil {
			goto invalid
		}
		for _, f := range fs {
			if f.Name()[0] != '.' {
				add(path.Join(n, f.Name()))
			}
		}
		return
	}
	if fi.Mode() & os.ModeType == 0 {
		files = append(files, n)
		return
	}

	println(fi.Mode())

invalid:
	fmt.Fprintf(os.Stderr, "  ! %s\n", n)
}
<|endoftext|>


<commit_before>// Copyright 2013 by Dobrosław Żybort. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package slug

import (
	"github.com/fiam/gounidecode/unidecode"
	"regexp"
	"strings"
)

var (
	// Custom substitution map
	CustomSub map[string]string
	// Custom rune substitution map
	CustomRuneSub map[rune]string
)

//=============================================================================

// Make returns slug generated from provided string. Will use "en" as language
// substitution.
func Make(s string) (slug string) {
	return MakeLang(s, "en")
}

// MakeLang returns slug generated from provided string and will use provided
// language for chars substitution.
func MakeLang(s string, lang string) (slug string) {
	slug = strings.TrimSpace(s)

	// Select substitution language
	switch lang {
	case "de":
		slug = SubstituteRune(slug, deSub)
	case "en":
		slug = SubstituteRune(slug, enSub)
	case "pl":
		slug = SubstituteRune(slug, plSub)
	default: // fallback to "en" if lang not found
		slug = SubstituteRune(slug, enSub)
	}

	slug = SubstituteRune(slug, defaultSub)

	// Custom substitutions
	slug = SubstituteRune(slug, CustomRuneSub)
	slug = Substitute(slug, CustomSub)

	slug = unidecode.Unidecode(slug)

	slug = strings.ToLower(slug)

	slug = regexp.MustCompile("[^a-z0-9-_]").ReplaceAllString(slug, "-")
	slug = regexp.MustCompile("-+").ReplaceAllString(slug, "-")
	slug = strings.Trim(slug, "-")
	return slug
}

// Substitute returns string with superseded all substrings from
// provided substitution map.
func Substitute(s string, sub map[string]string) (buf string) {
	buf = s
	for key, val := range sub {
		buf = strings.Replace(s, key, val, -1)
	}
	return
}

// SubstituteRune substitutes string chars with provided rune
// substitution map.
func SubstituteRune(s string, sub map[rune]string) (buf string) {
	for _, c := range s {
		if d, ok := sub[c]; ok {
			buf += d
		} else {
			buf += string(c)
		}
	}
	return
}
<commit_msg>Add: smart slug MaxLength<commit_after>// Copyright 2013 by Dobrosław Żybort. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package slug

import (
	"github.com/fiam/gounidecode/unidecode"
	"regexp"
	"strings"
)

var (
	// Custom substitution map
	CustomSub map[string]string
	// Custom rune substitution map
	CustomRuneSub map[rune]string

	// Maximum slug length. It's smart so it will cat slug after full word.
	// By default slugs aren't shortened.
	// If MaxLength is smaller than length of the first word, then returned
	// slug will contain substring from the first word truncated after
	// MaxLength.
	MaxLength int
)

//=============================================================================

// Make returns slug generated from provided string. Will use "en" as language
// substitution.
func Make(s string) (slug string) {
	return MakeLang(s, "en")
}

// MakeLang returns slug generated from provided string and will use provided
// language for chars substitution.
func MakeLang(s string, lang string) (slug string) {
	slug = strings.TrimSpace(s)

	// Select substitution language
	switch lang {
	case "de":
		slug = SubstituteRune(slug, deSub)
	case "en":
		slug = SubstituteRune(slug, enSub)
	case "pl":
		slug = SubstituteRune(slug, plSub)
	default: // fallback to "en" if lang not found
		slug = SubstituteRune(slug, enSub)
	}

	slug = SubstituteRune(slug, defaultSub)

	// Custom substitutions
	slug = SubstituteRune(slug, CustomRuneSub)
	slug = Substitute(slug, CustomSub)

	slug = unidecode.Unidecode(slug)

	slug = strings.ToLower(slug)

	slug = regexp.MustCompile("[^a-z0-9-_]").ReplaceAllString(slug, "-")
	slug = regexp.MustCompile("-+").ReplaceAllString(slug, "-")
	slug = strings.Trim(slug, "-")

	if MaxLength > 0 {
		slug = smartTruncate(slug)
	}

	return slug
}

// Substitute returns string with superseded all substrings from
// provided substitution map.
func Substitute(s string, sub map[string]string) (buf string) {
	buf = s
	for key, val := range sub {
		buf = strings.Replace(s, key, val, -1)
	}
	return
}

// SubstituteRune substitutes string chars with provided rune
// substitution map.
func SubstituteRune(s string, sub map[rune]string) (buf string) {
	for _, c := range s {
		if d, ok := sub[c]; ok {
			buf += d
		} else {
			buf += string(c)
		}
	}
	return
}

func smartTruncate(text string) string {
	if len(text) < MaxLength {
		return text
	}

	var truncated string
	words := strings.SplitAfter(text, "-")
	// If MaxLength is smaller than length of the first word return word
	// truncated after MaxLength.
	if len(words[0]) > MaxLength {
		return words[0][:MaxLength]
	}
	for _, word := range words {
		if len(truncated)+len(word)-1 <= MaxLength {
			truncated = truncated + word
		} else {
			break
		}
	}
	return strings.Trim(truncated, "-")
}
<|endoftext|>


<commit_before>// Package smaz is a pure Go implementation of the smaz algorithm
// (https://github.com/antirez/smaz) for compressing small strings.
package smaz

import (
	"errors"

	"github.com/kjk/smaz/trie"
)

var codeStrings = []string{" ",
	"the", "e", "t", "a", "of", "o", "and", "i", "n", "s", "e ", "r", " th",
	" t", "in", "he", "th", "h", "he ", "to", "\r\n", "l", "s ", "d", " a", "an",
	"er", "c", " o", "d ", "on", " of", "re", "of ", "t ", ", ", "is", "u", "at",
	"   ", "n ", "or", "which", "f", "m", "as", "it", "that", "\n", "was", "en",
	"  ", " w", "es", " an", " i", "\r", "f ", "g", "p", "nd", " s", "nd ", "ed ",
	"w", "ed", "http://", "for", "te", "ing", "y ", "The", " c", "ti", "r ", "his",
	"st", " in", "ar", "nt", ",", " to", "y", "ng", " h", "with", "le", "al", "to ",
	"b", "ou", "be", "were", " b", "se", "o ", "ent", "ha", "ng ", "their", "\"",
	"hi", "from", " f", "in ", "de", "ion", "me", "v", ".", "ve", "all", "re ",
	"ri", "ro", "is ", "co", "f t", "are", "ea", ". ", "her", " m", "er ", " p",
	"es ", "by", "they", "di", "ra", "ic", "not", "s, ", "d t", "at ", "ce", "la",
	"h ", "ne", "as ", "tio", "on ", "n t", "io", "we", " a ", "om", ", a", "s o",
	"ur", "li", "ll", "ch", "had", "this", "e t", "g ", "e\r\n", " wh", "ere",
	" co", "e o", "a ", "us", " d", "ss", "\n\r\n", "\r\n\r", "=\"", " be", " e",
	"s a", "ma", "one", "t t", "or ", "but", "el", "so", "l ", "e s", "s,", "no",
	"ter", " wa", "iv", "ho", "e a", " r", "hat", "s t", "ns", "ch ", "wh", "tr",
	"ut", "/", "have", "ly ", "ta", " ha", " on", "tha", "-", " l", "ati", "en ",
	"pe", " re", "there", "ass", "si", " fo", "wa", "ec", "our", "who", "its", "z",
	"fo", "rs", ">", "ot", "un", "<", "im", "th ", "nc", "ate", "><", "ver", "ad",
	" we", "ly", "ee", " n", "id", " cl", "ac", "il", "</", "rt", " wi", "div",
	"e, ", " it", "whi", " ma", "ge", "x", "e c", "men", ".com",
}

var codes = make([][]byte, len(codeStrings))
var codeTrie = trie.New()

func init() {
	for i, code := range codeStrings {
		codes[i] = []byte(code)
		codeTrie.Put([]byte(code), i)
	}
}
func loadCustomTable(table []string) {
	for i, code := range table§ {
		codes[i] = []byte(code)
		codeTrie.Put([]byte(code), i)
	}
}

func appendSrc(dst, src []byte) []byte {
	// We can write a max of 255 continuous verbatim characters, because the
	// length of the continous verbatim section is represented by a single byte.
	for len(src) > 0 {
		left := len(src)
		if left == 1 {
			// 254 is code for a single verbatim byte
			dst = append(dst, byte(254))
			return append(dst, src[0])
		}
		toCopy := left
		if left > 255 {
			toCopy = 255
		}
		// 255 is code for a verbatim string. It is followed by a byte
		// containing the length of the string.
		dst = append(dst, byte(255))
		dst = append(dst, byte(toCopy))
		dst = append(dst, src[:toCopy]...)
		src = src[toCopy:]
	}
	return dst
}

// Encode returns the encoded form of src. The returned slice may be a sub-slice
// of dst if dst was large enough to hold the entire encoded block. Otherwise,
// a newly allocated slice will be returned. It is valid to pass a nil dst.
func Encode(dst, src []byte) []byte {
	dst = dst[:0]
	root := codeTrie.Root()
	currPos := 0
	nLeft := len(src)
	tmp := src
	code := 0
	prefixLen := 0
	for currPos < nLeft {
		node := root
		for i, c := range tmp {
			next := node.Walk(c)
			if next == nil {
				break
			}
			node = next
			if node.Terminal() {
				prefixLen = i + 1
				code = node.Val()
			}
		}
		if prefixLen == 0 {
			currPos++
			tmp = tmp[1:]
			continue
		}
		dst = appendSrc(dst, src[:currPos])
		dst = append(dst, byte(code))
		src = src[currPos+prefixLen:]
		currPos = 0
		nLeft = len(src)
		tmp = src
		prefixLen = 0
	}
	dst = appendSrc(dst, src)
	return dst
}

// ErrCorrupt reports that the input is invalid.
var ErrCorrupt = errors.New("smaz: corrupt input")

// Decode returns the decoded form of src. The returned slice may be a sub-slice
// of dst if dst was large enough to hold the entire decoded block. Otherwise,
// a newly allocated slice will be returned. It is valid to pass a nil dst.
func Decode(dst, src []byte) ([]byte, error) {
	if cap(dst) < len(src) {
		dst = make([]byte, 0, len(src))
	}
	for len(src) > 0 {
		n := int(src[0])
		switch n {
		case 254: // Verbatim byte
			if len(src) < 2 {
				return nil, ErrCorrupt
			}
			dst = append(dst, src[1])
			src = src[2:]
		case 255: // Verbatim string
			if len(src) < 2 {
				return nil, ErrCorrupt
			}
			n = int(src[1])
			if len(src) < n+2 {
				return nil, ErrCorrupt
			}
			dst = append(dst, src[2:n+2]...)
			src = src[n+2:]
		default: // Look up encoded value
			d := codes[n]
			dst = append(dst, d...)
			src = src[1:]
		}
	}

	return dst, nil
}
<commit_msg>Update smaz.go<commit_after>// Package smaz is a pure Go implementation of the smaz algorithm
// (https://github.com/antirez/smaz) for compressing small strings.
package smaz

import (
	"errors"

	"github.com/kjk/smaz/trie"
)

var codeStrings = []string{" ",
	"the", "e", "t", "a", "of", "o", "and", "i", "n", "s", "e ", "r", " th",
	" t", "in", "he", "th", "h", "he ", "to", "\r\n", "l", "s ", "d", " a", "an",
	"er", "c", " o", "d ", "on", " of", "re", "of ", "t ", ", ", "is", "u", "at",
	"   ", "n ", "or", "which", "f", "m", "as", "it", "that", "\n", "was", "en",
	"  ", " w", "es", " an", " i", "\r", "f ", "g", "p", "nd", " s", "nd ", "ed ",
	"w", "ed", "http://", "for", "te", "ing", "y ", "The", " c", "ti", "r ", "his",
	"st", " in", "ar", "nt", ",", " to", "y", "ng", " h", "with", "le", "al", "to ",
	"b", "ou", "be", "were", " b", "se", "o ", "ent", "ha", "ng ", "their", "\"",
	"hi", "from", " f", "in ", "de", "ion", "me", "v", ".", "ve", "all", "re ",
	"ri", "ro", "is ", "co", "f t", "are", "ea", ". ", "her", " m", "er ", " p",
	"es ", "by", "they", "di", "ra", "ic", "not", "s, ", "d t", "at ", "ce", "la",
	"h ", "ne", "as ", "tio", "on ", "n t", "io", "we", " a ", "om", ", a", "s o",
	"ur", "li", "ll", "ch", "had", "this", "e t", "g ", "e\r\n", " wh", "ere",
	" co", "e o", "a ", "us", " d", "ss", "\n\r\n", "\r\n\r", "=\"", " be", " e",
	"s a", "ma", "one", "t t", "or ", "but", "el", "so", "l ", "e s", "s,", "no",
	"ter", " wa", "iv", "ho", "e a", " r", "hat", "s t", "ns", "ch ", "wh", "tr",
	"ut", "/", "have", "ly ", "ta", " ha", " on", "tha", "-", " l", "ati", "en ",
	"pe", " re", "there", "ass", "si", " fo", "wa", "ec", "our", "who", "its", "z",
	"fo", "rs", ">", "ot", "un", "<", "im", "th ", "nc", "ate", "><", "ver", "ad",
	" we", "ly", "ee", " n", "id", " cl", "ac", "il", "</", "rt", " wi", "div",
	"e, ", " it", "whi", " ma", "ge", "x", "e c", "men", ".com",
}

var codes = make([][]byte, len(codeStrings))
var codeTrie = trie.New()

func init() {
	for i, code := range codeStrings {
		codes[i] = []byte(code)
		codeTrie.Put([]byte(code), i)
	}
}
func LoadCustomTable(table []string) {
	codeTrie = trie.New()
	for i, code := range table{
		codes[i] = []byte(code)
		codeTrie.Put([]byte(code), i)
	}
}

func appendSrc(dst, src []byte) []byte {
	// We can write a max of 255 continuous verbatim characters, because the
	// length of the continous verbatim section is represented by a single byte.
	for len(src) > 0 {
		left := len(src)
		if left == 1 {
			// 254 is code for a single verbatim byte
			dst = append(dst, byte(254))
			return append(dst, src[0])
		}
		toCopy := left
		if left > 255 {
			toCopy = 255
		}
		// 255 is code for a verbatim string. It is followed by a byte
		// containing the length of the string.
		dst = append(dst, byte(255))
		dst = append(dst, byte(toCopy))
		dst = append(dst, src[:toCopy]...)
		src = src[toCopy:]
	}
	return dst
}

// Encode returns the encoded form of src. The returned slice may be a sub-slice
// of dst if dst was large enough to hold the entire encoded block. Otherwise,
// a newly allocated slice will be returned. It is valid to pass a nil dst.
func Encode(dst, src []byte) []byte {
	dst = dst[:0]
	root := codeTrie.Root()
	currPos := 0
	nLeft := len(src)
	tmp := src
	code := 0
	prefixLen := 0
	for currPos < nLeft {
		node := root
		for i, c := range tmp {
			next := node.Walk(c)
			if next == nil {
				break
			}
			node = next
			if node.Terminal() {
				prefixLen = i + 1
				code = node.Val()
			}
		}
		if prefixLen == 0 {
			currPos++
			tmp = tmp[1:]
			continue
		}
		dst = appendSrc(dst, src[:currPos])
		dst = append(dst, byte(code))
		src = src[currPos+prefixLen:]
		currPos = 0
		nLeft = len(src)
		tmp = src
		prefixLen = 0
	}
	dst = appendSrc(dst, src)
	return dst
}

// ErrCorrupt reports that the input is invalid.
var ErrCorrupt = errors.New("smaz: corrupt input")

// Decode returns the decoded form of src. The returned slice may be a sub-slice
// of dst if dst was large enough to hold the entire decoded block. Otherwise,
// a newly allocated slice will be returned. It is valid to pass a nil dst.
func Decode(dst, src []byte) ([]byte, error) {
	if cap(dst) < len(src) {
		dst = make([]byte, 0, len(src))
	}
	for len(src) > 0 {
		n := int(src[0])
		switch n {
		case 254: // Verbatim byte
			if len(src) < 2 {
				return nil, ErrCorrupt
			}
			dst = append(dst, src[1])
			src = src[2:]
		case 255: // Verbatim string
			if len(src) < 2 {
				return nil, ErrCorrupt
			}
			n = int(src[1])
			if len(src) < n+2 {
				return nil, ErrCorrupt
			}
			dst = append(dst, src[2:n+2]...)
			src = src[n+2:]
		default: // Look up encoded value
			d := codes[n]
			dst = append(dst, d...)
			src = src[1:]
		}
	}

	return dst, nil
}
<|endoftext|>


<commit_before>// Copyright 2012-present Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

import "errors"

// -- Sorter --

// Sorter is an interface for sorting strategies, e.g. ScoreSort or FieldSort.
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html.
type Sorter interface {
	Source() (interface{}, error)
}

// -- SortInfo --

// SortInfo contains information about sorting a field.
type SortInfo struct {
	Sorter
	Field          string
	Ascending      bool
	Missing        interface{}
	IgnoreUnmapped *bool
	SortMode       string
	NestedFilter   Query
	NestedPath     string
}

func (info SortInfo) Source() (interface{}, error) {
	prop := make(map[string]interface{})
	if info.Ascending {
		prop["order"] = "asc"
	} else {
		prop["order"] = "desc"
	}
	if info.Missing != nil {
		prop["missing"] = info.Missing
	}
	if info.IgnoreUnmapped != nil {
		prop["ignore_unmapped"] = *info.IgnoreUnmapped
	}
	if info.SortMode != "" {
		prop["mode"] = info.SortMode
	}
	if info.NestedFilter != nil {
		src, err := info.NestedFilter.Source()
		if err != nil {
			return nil, err
		}
		prop["nested_filter"] = src
	}
	if info.NestedPath != "" {
		prop["nested_path"] = info.NestedPath
	}
	source := make(map[string]interface{})
	source[info.Field] = prop
	return source, nil
}

// -- SortByDoc --

// SortByDoc sorts by the "_doc" field, as described in
// https://www.elastic.co/guide/en/elasticsearch/reference/master/search-request-scroll.html.
//
// Example:
//   ss := elastic.NewSearchSource()
//   ss = ss.SortBy(elastic.SortByDoc{})
type SortByDoc struct {
	Sorter
}

// Source returns the JSON-serializable data.
func (s SortByDoc) Source() (interface{}, error) {
	return "_doc", nil
}

// -- ScoreSort --

// ScoreSort sorts by relevancy score.
type ScoreSort struct {
	Sorter
	ascending bool
}

// NewScoreSort creates a new ScoreSort.
func NewScoreSort() ScoreSort {
	return ScoreSort{ascending: false} // Descending by default!
}

// Order defines whether sorting ascending (default) or descending.
func (s ScoreSort) Order(ascending bool) ScoreSort {
	s.ascending = ascending
	return s
}

// Asc sets ascending sort order.
func (s ScoreSort) Asc() ScoreSort {
	s.ascending = true
	return s
}

// Desc sets descending sort order.
func (s ScoreSort) Desc() ScoreSort {
	s.ascending = false
	return s
}

// Source returns the JSON-serializable data.
func (s ScoreSort) Source() (interface{}, error) {
	source := make(map[string]interface{})
	x := make(map[string]interface{})
	source["_score"] = x
	if s.ascending {
		x["reverse"] = true
	}
	return source, nil
}

// -- FieldSort --

// FieldSort sorts by a given field.
type FieldSort struct {
	Sorter
	fieldName      string
	ascending      bool
	missing        interface{}
	ignoreUnmapped *bool
	unmappedType   *string
	sortMode       *string
	nestedFilter   Query
	nestedPath     *string
}

// NewFieldSort creates a new FieldSort.
func NewFieldSort(fieldName string) FieldSort {
	return FieldSort{
		fieldName: fieldName,
		ascending: true,
	}
}

// FieldName specifies the name of the field to be used for sorting.
func (s FieldSort) FieldName(fieldName string) FieldSort {
	s.fieldName = fieldName
	return s
}

// Order defines whether sorting ascending (default) or descending.
func (s FieldSort) Order(ascending bool) FieldSort {
	s.ascending = ascending
	return s
}

// Asc sets ascending sort order.
func (s FieldSort) Asc() FieldSort {
	s.ascending = true
	return s
}

// Desc sets descending sort order.
func (s FieldSort) Desc() FieldSort {
	s.ascending = false
	return s
}

// Missing sets the value to be used when a field is missing in a document.
// You can also use "_last" or "_first" to sort missing last or first
// respectively.
func (s FieldSort) Missing(missing interface{}) FieldSort {
	s.missing = missing
	return s
}

// IgnoreUnmapped specifies what happens if the field does not exist in
// the index. Set it to true to ignore, or set it to false to not ignore (default).
func (s FieldSort) IgnoreUnmapped(ignoreUnmapped bool) FieldSort {
	s.ignoreUnmapped = &ignoreUnmapped
	return s
}

// UnmappedType sets the type to use when the current field is not mapped
// in an index.
func (s FieldSort) UnmappedType(typ string) FieldSort {
	s.unmappedType = &typ
	return s
}

// SortMode specifies what values to pick in case a document contains
// multiple values for the targeted sort field. Possible values are:
// min, max, sum, and avg.
func (s FieldSort) SortMode(sortMode string) FieldSort {
	s.sortMode = &sortMode
	return s
}

// NestedFilter sets a filter that nested objects should match with
// in order to be taken into account for sorting.
func (s FieldSort) NestedFilter(nestedFilter Query) FieldSort {
	s.nestedFilter = nestedFilter
	return s
}

// NestedPath is used if sorting occurs on a field that is inside a
// nested object.
func (s FieldSort) NestedPath(nestedPath string) FieldSort {
	s.nestedPath = &nestedPath
	return s
}

// Source returns the JSON-serializable data.
func (s FieldSort) Source() (interface{}, error) {
	source := make(map[string]interface{})
	x := make(map[string]interface{})
	source[s.fieldName] = x
	if s.ascending {
		x["order"] = "asc"
	} else {
		x["order"] = "desc"
	}
	if s.missing != nil {
		x["missing"] = s.missing
	}
	if s.ignoreUnmapped != nil {
		x["ignore_unmapped"] = *s.ignoreUnmapped
	}
	if s.unmappedType != nil {
		x["unmapped_type"] = *s.unmappedType
	}
	if s.sortMode != nil {
		x["mode"] = *s.sortMode
	}
	if s.nestedFilter != nil {
		src, err := s.nestedFilter.Source()
		if err != nil {
			return nil, err
		}
		x["nested_filter"] = src
	}
	if s.nestedPath != nil {
		x["nested_path"] = *s.nestedPath
	}
	return source, nil
}

// -- GeoDistanceSort --

// GeoDistanceSort allows for sorting by geographic distance.
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_geo_distance_sorting.
type GeoDistanceSort struct {
	Sorter
	fieldName    string
	points       []*GeoPoint
	geohashes    []string
	geoDistance  *string
	unit         string
	ascending    bool
	sortMode     *string
	nestedFilter Query
	nestedPath   *string
}

// NewGeoDistanceSort creates a new sorter for geo distances.
func NewGeoDistanceSort(fieldName string) GeoDistanceSort {
	return GeoDistanceSort{
		fieldName: fieldName,
		points:    make([]*GeoPoint, 0),
		geohashes: make([]string, 0),
		ascending: true,
	}
}

// FieldName specifies the name of the (geo) field to use for sorting.
func (s GeoDistanceSort) FieldName(fieldName string) GeoDistanceSort {
	s.fieldName = fieldName
	return s
}

// Order defines whether sorting ascending (default) or descending.
func (s GeoDistanceSort) Order(ascending bool) GeoDistanceSort {
	s.ascending = ascending
	return s
}

// Asc sets ascending sort order.
func (s GeoDistanceSort) Asc() GeoDistanceSort {
	s.ascending = true
	return s
}

// Desc sets descending sort order.
func (s GeoDistanceSort) Desc() GeoDistanceSort {
	s.ascending = false
	return s
}

// Point specifies a point to create the range distance aggregations from.
func (s GeoDistanceSort) Point(lat, lon float64) GeoDistanceSort {
	s.points = append(s.points, GeoPointFromLatLon(lat, lon))
	return s
}

// Points specifies the geo point(s) to create the range distance aggregations from.
func (s GeoDistanceSort) Points(points ...*GeoPoint) GeoDistanceSort {
	s.points = append(s.points, points...)
	return s
}

// GeoHashes specifies the geo point to create the range distance aggregations from.
func (s GeoDistanceSort) GeoHashes(geohashes ...string) GeoDistanceSort {
	s.geohashes = append(s.geohashes, geohashes...)
	return s
}

// GeoDistance represents how to compute the distance.
// It can be sloppy_arc (default), arc, or plane.
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_geo_distance_sorting.
func (s GeoDistanceSort) GeoDistance(geoDistance string) GeoDistanceSort {
	s.geoDistance = &geoDistance
	return s
}

// Unit specifies the distance unit to use. It defaults to km.
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/common-options.html#distance-units
// for details.
func (s GeoDistanceSort) Unit(unit string) GeoDistanceSort {
	s.unit = unit
	return s
}

// SortMode specifies what values to pick in case a document contains
// multiple values for the targeted sort field. Possible values are:
// min, max, sum, and avg.
func (s GeoDistanceSort) SortMode(sortMode string) GeoDistanceSort {
	s.sortMode = &sortMode
	return s
}

// NestedFilter sets a filter that nested objects should match with
// in order to be taken into account for sorting.
func (s GeoDistanceSort) NestedFilter(nestedFilter Query) GeoDistanceSort {
	s.nestedFilter = nestedFilter
	return s
}

// NestedPath is used if sorting occurs on a field that is inside a
// nested object.
func (s GeoDistanceSort) NestedPath(nestedPath string) GeoDistanceSort {
	s.nestedPath = &nestedPath
	return s
}

// Source returns the JSON-serializable data.
func (s GeoDistanceSort) Source() (interface{}, error) {
	source := make(map[string]interface{})
	x := make(map[string]interface{})
	source["_geo_distance"] = x

	// Points
	var ptarr []interface{}
	for _, pt := range s.points {
		ptarr = append(ptarr, pt.Source())
	}
	for _, geohash := range s.geohashes {
		ptarr = append(ptarr, geohash)
	}
	x[s.fieldName] = ptarr

	if s.unit != "" {
		x["unit"] = s.unit
	}
	if s.geoDistance != nil {
		x["distance_type"] = *s.geoDistance
	}

	if !s.ascending {
		x["reverse"] = true
	}
	if s.sortMode != nil {
		x["mode"] = *s.sortMode
	}
	if s.nestedFilter != nil {
		src, err := s.nestedFilter.Source()
		if err != nil {
			return nil, err
		}
		x["nested_filter"] = src
	}
	if s.nestedPath != nil {
		x["nested_path"] = *s.nestedPath
	}
	return source, nil
}

// -- ScriptSort --

// ScriptSort sorts by a custom script. See
// http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-scripting.html#modules-scripting
// for details about scripting.
type ScriptSort struct {
	Sorter
	script       *Script
	typ          string
	ascending    bool
	sortMode     *string
	nestedFilter Query
	nestedPath   *string
}

// NewScriptSort creates and initializes a new ScriptSort.
// You must provide a script and a type, e.g. "string" or "number".
func NewScriptSort(script *Script, typ string) ScriptSort {
	return ScriptSort{
		script:    script,
		typ:       typ,
		ascending: true,
	}
}

// Type sets the script type, which can be either "string" or "number".
func (s ScriptSort) Type(typ string) ScriptSort {
	s.typ = typ
	return s
}

// Order defines whether sorting ascending (default) or descending.
func (s ScriptSort) Order(ascending bool) ScriptSort {
	s.ascending = ascending
	return s
}

// Asc sets ascending sort order.
func (s ScriptSort) Asc() ScriptSort {
	s.ascending = true
	return s
}

// Desc sets descending sort order.
func (s ScriptSort) Desc() ScriptSort {
	s.ascending = false
	return s
}

// SortMode specifies what values to pick in case a document contains
// multiple values for the targeted sort field. Possible values are:
// min or max.
func (s ScriptSort) SortMode(sortMode string) ScriptSort {
	s.sortMode = &sortMode
	return s
}

// NestedFilter sets a filter that nested objects should match with
// in order to be taken into account for sorting.
func (s ScriptSort) NestedFilter(nestedFilter Query) ScriptSort {
	s.nestedFilter = nestedFilter
	return s
}

// NestedPath is used if sorting occurs on a field that is inside a
// nested object.
func (s ScriptSort) NestedPath(nestedPath string) ScriptSort {
	s.nestedPath = &nestedPath
	return s
}

// Source returns the JSON-serializable data.
func (s ScriptSort) Source() (interface{}, error) {
	if s.script == nil {
		return nil, errors.New("ScriptSort expected a script")
	}
	source := make(map[string]interface{})
	x := make(map[string]interface{})
	source["_script"] = x

	src, err := s.script.Source()
	if err != nil {
		return nil, err
	}
	x["script"] = src

	x["type"] = s.typ

	if !s.ascending {
		x["reverse"] = true
	}
	if s.sortMode != nil {
		x["mode"] = *s.sortMode
	}
	if s.nestedFilter != nil {
		src, err := s.nestedFilter.Source()
		if err != nil {
			return nil, err
		}
		x["nested_filter"] = src
	}
	if s.nestedPath != nil {
		x["nested_path"] = *s.nestedPath
	}
	return source, nil
}
<commit_msg>Convert Sorters to pointer receivers<commit_after>// Copyright 2012-present Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

import "errors"

// -- Sorter --

// Sorter is an interface for sorting strategies, e.g. ScoreSort or FieldSort.
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html.
type Sorter interface {
	Source() (interface{}, error)
}

// -- SortInfo --

// SortInfo contains information about sorting a field.
type SortInfo struct {
	Sorter
	Field          string
	Ascending      bool
	Missing        interface{}
	IgnoreUnmapped *bool
	SortMode       string
	NestedFilter   Query
	NestedPath     string
}

func (info SortInfo) Source() (interface{}, error) {
	prop := make(map[string]interface{})
	if info.Ascending {
		prop["order"] = "asc"
	} else {
		prop["order"] = "desc"
	}
	if info.Missing != nil {
		prop["missing"] = info.Missing
	}
	if info.IgnoreUnmapped != nil {
		prop["ignore_unmapped"] = *info.IgnoreUnmapped
	}
	if info.SortMode != "" {
		prop["mode"] = info.SortMode
	}
	if info.NestedFilter != nil {
		src, err := info.NestedFilter.Source()
		if err != nil {
			return nil, err
		}
		prop["nested_filter"] = src
	}
	if info.NestedPath != "" {
		prop["nested_path"] = info.NestedPath
	}
	source := make(map[string]interface{})
	source[info.Field] = prop
	return source, nil
}

// -- SortByDoc --

// SortByDoc sorts by the "_doc" field, as described in
// https://www.elastic.co/guide/en/elasticsearch/reference/master/search-request-scroll.html.
//
// Example:
//   ss := elastic.NewSearchSource()
//   ss = ss.SortBy(elastic.SortByDoc{})
type SortByDoc struct {
	Sorter
}

// Source returns the JSON-serializable data.
func (s SortByDoc) Source() (interface{}, error) {
	return "_doc", nil
}

// -- ScoreSort --

// ScoreSort sorts by relevancy score.
type ScoreSort struct {
	Sorter
	ascending bool
}

// NewScoreSort creates a new ScoreSort.
func NewScoreSort() *ScoreSort {
	return &ScoreSort{ascending: false} // Descending by default!
}

// Order defines whether sorting ascending (default) or descending.
func (s *ScoreSort) Order(ascending bool) *ScoreSort {
	s.ascending = ascending
	return s
}

// Asc sets ascending sort order.
func (s *ScoreSort) Asc() *ScoreSort {
	s.ascending = true
	return s
}

// Desc sets descending sort order.
func (s *ScoreSort) Desc() *ScoreSort {
	s.ascending = false
	return s
}

// Source returns the JSON-serializable data.
func (s *ScoreSort) Source() (interface{}, error) {
	source := make(map[string]interface{})
	x := make(map[string]interface{})
	source["_score"] = x
	if s.ascending {
		x["reverse"] = true
	}
	return source, nil
}

// -- FieldSort --

// FieldSort sorts by a given field.
type FieldSort struct {
	Sorter
	fieldName      string
	ascending      bool
	missing        interface{}
	ignoreUnmapped *bool
	unmappedType   *string
	sortMode       *string
	nestedFilter   Query
	nestedPath     *string
}

// NewFieldSort creates a new FieldSort.
func NewFieldSort(fieldName string) *FieldSort {
	return &FieldSort{
		fieldName: fieldName,
		ascending: true,
	}
}

// FieldName specifies the name of the field to be used for sorting.
func (s *FieldSort) FieldName(fieldName string) *FieldSort {
	s.fieldName = fieldName
	return s
}

// Order defines whether sorting ascending (default) or descending.
func (s *FieldSort) Order(ascending bool) *FieldSort {
	s.ascending = ascending
	return s
}

// Asc sets ascending sort order.
func (s *FieldSort) Asc() *FieldSort {
	s.ascending = true
	return s
}

// Desc sets descending sort order.
func (s *FieldSort) Desc() *FieldSort {
	s.ascending = false
	return s
}

// Missing sets the value to be used when a field is missing in a document.
// You can also use "_last" or "_first" to sort missing last or first
// respectively.
func (s *FieldSort) Missing(missing interface{}) *FieldSort {
	s.missing = missing
	return s
}

// IgnoreUnmapped specifies what happens if the field does not exist in
// the index. Set it to true to ignore, or set it to false to not ignore (default).
func (s *FieldSort) IgnoreUnmapped(ignoreUnmapped bool) *FieldSort {
	s.ignoreUnmapped = &ignoreUnmapped
	return s
}

// UnmappedType sets the type to use when the current field is not mapped
// in an index.
func (s *FieldSort) UnmappedType(typ string) *FieldSort {
	s.unmappedType = &typ
	return s
}

// SortMode specifies what values to pick in case a document contains
// multiple values for the targeted sort field. Possible values are:
// min, max, sum, and avg.
func (s *FieldSort) SortMode(sortMode string) *FieldSort {
	s.sortMode = &sortMode
	return s
}

// NestedFilter sets a filter that nested objects should match with
// in order to be taken into account for sorting.
func (s *FieldSort) NestedFilter(nestedFilter Query) *FieldSort {
	s.nestedFilter = nestedFilter
	return s
}

// NestedPath is used if sorting occurs on a field that is inside a
// nested object.
func (s *FieldSort) NestedPath(nestedPath string) *FieldSort {
	s.nestedPath = &nestedPath
	return s
}

// Source returns the JSON-serializable data.
func (s *FieldSort) Source() (interface{}, error) {
	source := make(map[string]interface{})
	x := make(map[string]interface{})
	source[s.fieldName] = x
	if s.ascending {
		x["order"] = "asc"
	} else {
		x["order"] = "desc"
	}
	if s.missing != nil {
		x["missing"] = s.missing
	}
	if s.ignoreUnmapped != nil {
		x["ignore_unmapped"] = *s.ignoreUnmapped
	}
	if s.unmappedType != nil {
		x["unmapped_type"] = *s.unmappedType
	}
	if s.sortMode != nil {
		x["mode"] = *s.sortMode
	}
	if s.nestedFilter != nil {
		src, err := s.nestedFilter.Source()
		if err != nil {
			return nil, err
		}
		x["nested_filter"] = src
	}
	if s.nestedPath != nil {
		x["nested_path"] = *s.nestedPath
	}
	return source, nil
}

// -- GeoDistanceSort --

// GeoDistanceSort allows for sorting by geographic distance.
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_geo_distance_sorting.
type GeoDistanceSort struct {
	Sorter
	fieldName    string
	points       []*GeoPoint
	geohashes    []string
	geoDistance  *string
	unit         string
	ascending    bool
	sortMode     *string
	nestedFilter Query
	nestedPath   *string
}

// NewGeoDistanceSort creates a new sorter for geo distances.
func NewGeoDistanceSort(fieldName string) *GeoDistanceSort {
	return &GeoDistanceSort{
		fieldName: fieldName,
		points:    make([]*GeoPoint, 0),
		geohashes: make([]string, 0),
		ascending: true,
	}
}

// FieldName specifies the name of the (geo) field to use for sorting.
func (s *GeoDistanceSort) FieldName(fieldName string) *GeoDistanceSort {
	s.fieldName = fieldName
	return s
}

// Order defines whether sorting ascending (default) or descending.
func (s *GeoDistanceSort) Order(ascending bool) *GeoDistanceSort {
	s.ascending = ascending
	return s
}

// Asc sets ascending sort order.
func (s *GeoDistanceSort) Asc() *GeoDistanceSort {
	s.ascending = true
	return s
}

// Desc sets descending sort order.
func (s *GeoDistanceSort) Desc() *GeoDistanceSort {
	s.ascending = false
	return s
}

// Point specifies a point to create the range distance aggregations from.
func (s *GeoDistanceSort) Point(lat, lon float64) *GeoDistanceSort {
	s.points = append(s.points, GeoPointFromLatLon(lat, lon))
	return s
}

// Points specifies the geo point(s) to create the range distance aggregations from.
func (s *GeoDistanceSort) Points(points ...*GeoPoint) *GeoDistanceSort {
	s.points = append(s.points, points...)
	return s
}

// GeoHashes specifies the geo point to create the range distance aggregations from.
func (s *GeoDistanceSort) GeoHashes(geohashes ...string) *GeoDistanceSort {
	s.geohashes = append(s.geohashes, geohashes...)
	return s
}

// GeoDistance represents how to compute the distance.
// It can be sloppy_arc (default), arc, or plane.
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html#_geo_distance_sorting.
func (s *GeoDistanceSort) GeoDistance(geoDistance string) *GeoDistanceSort {
	s.geoDistance = &geoDistance
	return s
}

// Unit specifies the distance unit to use. It defaults to km.
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/common-options.html#distance-units
// for details.
func (s *GeoDistanceSort) Unit(unit string) *GeoDistanceSort {
	s.unit = unit
	return s
}

// SortMode specifies what values to pick in case a document contains
// multiple values for the targeted sort field. Possible values are:
// min, max, sum, and avg.
func (s *GeoDistanceSort) SortMode(sortMode string) *GeoDistanceSort {
	s.sortMode = &sortMode
	return s
}

// NestedFilter sets a filter that nested objects should match with
// in order to be taken into account for sorting.
func (s *GeoDistanceSort) NestedFilter(nestedFilter Query) *GeoDistanceSort {
	s.nestedFilter = nestedFilter
	return s
}

// NestedPath is used if sorting occurs on a field that is inside a
// nested object.
func (s *GeoDistanceSort) NestedPath(nestedPath string) *GeoDistanceSort {
	s.nestedPath = &nestedPath
	return s
}

// Source returns the JSON-serializable data.
func (s *GeoDistanceSort) Source() (interface{}, error) {
	source := make(map[string]interface{})
	x := make(map[string]interface{})
	source["_geo_distance"] = x

	// Points
	var ptarr []interface{}
	for _, pt := range s.points {
		ptarr = append(ptarr, pt.Source())
	}
	for _, geohash := range s.geohashes {
		ptarr = append(ptarr, geohash)
	}
	x[s.fieldName] = ptarr

	if s.unit != "" {
		x["unit"] = s.unit
	}
	if s.geoDistance != nil {
		x["distance_type"] = *s.geoDistance
	}

	if !s.ascending {
		x["reverse"] = true
	}
	if s.sortMode != nil {
		x["mode"] = *s.sortMode
	}
	if s.nestedFilter != nil {
		src, err := s.nestedFilter.Source()
		if err != nil {
			return nil, err
		}
		x["nested_filter"] = src
	}
	if s.nestedPath != nil {
		x["nested_path"] = *s.nestedPath
	}
	return source, nil
}

// -- ScriptSort --

// ScriptSort sorts by a custom script. See
// http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-scripting.html#modules-scripting
// for details about scripting.
type ScriptSort struct {
	Sorter
	script       *Script
	typ          string
	ascending    bool
	sortMode     *string
	nestedFilter Query
	nestedPath   *string
}

// NewScriptSort creates and initializes a new ScriptSort.
// You must provide a script and a type, e.g. "string" or "number".
func NewScriptSort(script *Script, typ string) *ScriptSort {
	return &ScriptSort{
		script:    script,
		typ:       typ,
		ascending: true,
	}
}

// Type sets the script type, which can be either "string" or "number".
func (s *ScriptSort) Type(typ string) *ScriptSort {
	s.typ = typ
	return s
}

// Order defines whether sorting ascending (default) or descending.
func (s *ScriptSort) Order(ascending bool) *ScriptSort {
	s.ascending = ascending
	return s
}

// Asc sets ascending sort order.
func (s *ScriptSort) Asc() *ScriptSort {
	s.ascending = true
	return s
}

// Desc sets descending sort order.
func (s *ScriptSort) Desc() *ScriptSort {
	s.ascending = false
	return s
}

// SortMode specifies what values to pick in case a document contains
// multiple values for the targeted sort field. Possible values are:
// min or max.
func (s *ScriptSort) SortMode(sortMode string) *ScriptSort {
	s.sortMode = &sortMode
	return s
}

// NestedFilter sets a filter that nested objects should match with
// in order to be taken into account for sorting.
func (s *ScriptSort) NestedFilter(nestedFilter Query) *ScriptSort {
	s.nestedFilter = nestedFilter
	return s
}

// NestedPath is used if sorting occurs on a field that is inside a
// nested object.
func (s *ScriptSort) NestedPath(nestedPath string) *ScriptSort {
	s.nestedPath = &nestedPath
	return s
}

// Source returns the JSON-serializable data.
func (s *ScriptSort) Source() (interface{}, error) {
	if s.script == nil {
		return nil, errors.New("ScriptSort expected a script")
	}
	source := make(map[string]interface{})
	x := make(map[string]interface{})
	source["_script"] = x

	src, err := s.script.Source()
	if err != nil {
		return nil, err
	}
	x["script"] = src

	x["type"] = s.typ

	if !s.ascending {
		x["reverse"] = true
	}
	if s.sortMode != nil {
		x["mode"] = *s.sortMode
	}
	if s.nestedFilter != nil {
		src, err := s.nestedFilter.Source()
		if err != nil {
			return nil, err
		}
		x["nested_filter"] = src
	}
	if s.nestedPath != nil {
		x["nested_path"] = *s.nestedPath
	}
	return source, nil
}
<|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package cmd

import (
	"os"
	"path/filepath"

	"github.com/spf13/cobra"
	"github.com/webx-top/echo"
	"github.com/webx-top/echo/defaults"
	"github.com/webx-top/echo/middleware/language"

	"github.com/admpub/log"
	"github.com/admpub/nging/v4/application/cmd/bootconfig"
	"github.com/admpub/nging/v4/application/handler/setup"
	"github.com/admpub/nging/v4/application/library/config"
	"github.com/admpub/nging/v4/application/library/config/subconfig/sdb"
)

// 静默安装
var InitDBConfig = &sdb.DB{
	Type:     `mysql`, // mysql / sqlite
	User:     `root`,
	Database: `nging`,
	Host:     `127.0.0.1:3306`,
}

var InitInstallConfig = &struct {
	Charset    string
	AdminUser  string
	AdminPass  string
	AdminEmail string
	Language   string // en / zh-cn
}{
	Charset:   sdb.MySQLDefaultCharset,
	AdminUser: `admin`,
	Language:  `zh-cn`,
}

var initCmd = &cobra.Command{
	Use:     "init",
	Short:   "Silent install",
	Example: filepath.Base(os.Args[0]) + " init [options]",
	RunE:    initRunE,
}

func BuildTranslator(c language.Config) echo.Translator {
	c.SetFSFunc(bootconfig.LangFSFunc)
	i18n := language.NewI18n(&c)
	tr := &language.Translate{}
	tr.Reset(InitInstallConfig.Language, i18n)
	return tr
}

func initRunE(cmd *cobra.Command, args []string) error {
	conf, err := config.InitConfig()
	if err != nil {
		return err
	}
	conf.AsDefault()
	ctx := defaults.NewMockContext()

	// 启用多语言支持
	ctx.SetTranslator(BuildTranslator(config.DefaultConfig.Language))

	ctx.Request().SetMethod(echo.POST)
	ctx.Request().Form().Set(`type`, InitDBConfig.Type)
	ctx.Request().Form().Set(`user`, InitDBConfig.User)
	ctx.Request().Form().Set(`host`, InitDBConfig.Host)
	ctx.Request().Form().Set(`password`, InitDBConfig.Password)
	ctx.Request().Form().Set(`database`, InitDBConfig.Database)
	ctx.Request().Form().Set(`prefix`, InitDBConfig.Prefix)
	ctx.Request().Form().Set(`charset`, InitInstallConfig.Charset)
	ctx.Request().Form().Set(`adminUser`, InitInstallConfig.AdminUser)
	ctx.Request().Form().Set(`adminPass`, InitInstallConfig.AdminPass)
	ctx.Request().Form().Set(`adminEmail`, InitInstallConfig.AdminEmail)
	err = setup.Setup(ctx)
	if err == nil {
		log.Okay(`Congratulations, this program has been installed successfully`)
	}
	return err
}

func init() {
	rootCmd.AddCommand(initCmd)

	initCmd.Flags().StringVar(&InitDBConfig.Type, "type", InitDBConfig.Type, "database type")
	initCmd.Flags().StringVar(&InitDBConfig.User, "user", InitDBConfig.User, "database user")
	initCmd.Flags().StringVar(&InitDBConfig.Host, "host", InitDBConfig.Host, "database host")
	initCmd.Flags().StringVar(&InitDBConfig.Password, "password", InitDBConfig.Password, "database password")
	initCmd.Flags().StringVar(&InitDBConfig.Database, "database", InitDBConfig.Database, "database name")
	initCmd.Flags().StringVar(&InitDBConfig.Prefix, "prefix", InitDBConfig.Prefix, "database table prefix")
	initCmd.Flags().StringVar(&InitInstallConfig.Charset, "charset", InitInstallConfig.Charset, "database table charset")
	initCmd.Flags().StringVar(&InitInstallConfig.AdminUser, "adminUser", InitInstallConfig.AdminUser, "administrator name")
	initCmd.Flags().StringVar(&InitInstallConfig.AdminPass, "adminPass", InitInstallConfig.AdminPass, "administrator password")
	initCmd.Flags().StringVar(&InitInstallConfig.AdminEmail, "adminEmail", InitInstallConfig.AdminEmail, "administrator e-mail")
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package cmd

import (
	"os"
	"path/filepath"

	"github.com/spf13/cobra"
	"github.com/webx-top/echo"
	"github.com/webx-top/echo/defaults"
	"github.com/webx-top/echo/middleware/language"

	"github.com/admpub/log"
	"github.com/admpub/nging/v4/application/cmd/bootconfig"
	"github.com/admpub/nging/v4/application/handler/setup"
	"github.com/admpub/nging/v4/application/library/config"
	"github.com/admpub/nging/v4/application/library/config/subconfig/sdb"
	"github.com/admpub/once"
)

// 静默安装
var InitDBConfig = &sdb.DB{
	Type:     `mysql`, // mysql / sqlite
	User:     `root`,
	Database: `nging`,
	Host:     `127.0.0.1:3306`,
}

var InitInstallConfig = &struct {
	Charset    string
	AdminUser  string
	AdminPass  string
	AdminEmail string
	Language   string // en / zh-cn
}{
	Charset:   sdb.MySQLDefaultCharset,
	AdminUser: `admin`,
	Language:  `zh-cn`,
}

var initCmd = &cobra.Command{
	Use:     "init",
	Short:   "Silent install",
	Example: filepath.Base(os.Args[0]) + " init [options]",
	RunE:    initRunE,
}

var translate *language.Translate
var translock once.Once

func initTranslate() {
	translate = BuildTranslator(config.DefaultConfig.Language)
}

func GetTranslator() *language.Translate {
	translock.Do(initTranslate)
	return translate
}

func ResetTranslator() {
	translock.Reset()
}

func BuildTranslator(c language.Config) *language.Translate {
	c.SetFSFunc(bootconfig.LangFSFunc)
	i18n := language.NewI18n(&c)
	tr := &language.Translate{}
	tr.Reset(InitInstallConfig.Language, i18n)
	return tr
}

func initRunE(cmd *cobra.Command, args []string) error {
	conf, err := config.InitConfig()
	if err != nil {
		return err
	}
	conf.AsDefault()
	ctx := defaults.NewMockContext()
	ctx.SetAuto(true)

	// 启用多语言支持
	ctx.SetTranslator(GetTranslator())

	ctx.Request().Header().Set(echo.HeaderAccept, echo.MIMETextPlain)
	ctx.Request().SetMethod(echo.POST)
	ctx.Request().Form().Set(`type`, InitDBConfig.Type)
	ctx.Request().Form().Set(`user`, InitDBConfig.User)
	ctx.Request().Form().Set(`host`, InitDBConfig.Host)
	ctx.Request().Form().Set(`password`, InitDBConfig.Password)
	ctx.Request().Form().Set(`database`, InitDBConfig.Database)
	ctx.Request().Form().Set(`prefix`, InitDBConfig.Prefix)
	ctx.Request().Form().Set(`charset`, InitInstallConfig.Charset)
	ctx.Request().Form().Set(`adminUser`, InitInstallConfig.AdminUser)
	ctx.Request().Form().Set(`adminPass`, InitInstallConfig.AdminPass)
	ctx.Request().Form().Set(`adminEmail`, InitInstallConfig.AdminEmail)
	return ctx.Render(`index`, nil)
	err = setup.Setup(ctx)
	if err == nil {
		log.Okay(`Congratulations, this program has been installed successfully`)
	}
	return err
}

func init() {
	rootCmd.AddCommand(initCmd)

	initCmd.Flags().StringVar(&InitDBConfig.Type, "type", InitDBConfig.Type, "database type")
	initCmd.Flags().StringVar(&InitDBConfig.User, "user", InitDBConfig.User, "database user")
	initCmd.Flags().StringVar(&InitDBConfig.Host, "host", InitDBConfig.Host, "database host")
	initCmd.Flags().StringVar(&InitDBConfig.Password, "password", InitDBConfig.Password, "database password")
	initCmd.Flags().StringVar(&InitDBConfig.Database, "database", InitDBConfig.Database, "database name")
	initCmd.Flags().StringVar(&InitDBConfig.Prefix, "prefix", InitDBConfig.Prefix, "database table prefix")
	initCmd.Flags().StringVar(&InitInstallConfig.Charset, "charset", InitInstallConfig.Charset, "database table charset")
	initCmd.Flags().StringVar(&InitInstallConfig.AdminUser, "adminUser", InitInstallConfig.AdminUser, "administrator name")
	initCmd.Flags().StringVar(&InitInstallConfig.AdminPass, "adminPass", InitInstallConfig.AdminPass, "administrator password")
	initCmd.Flags().StringVar(&InitInstallConfig.AdminEmail, "adminEmail", InitInstallConfig.AdminEmail, "administrator e-mail")
}
<|endoftext|>


<commit_before>package rollbar

import (
	"errors"
	"fmt"
	"os"
	"testing"
)

type CustomError struct {
	s string
}

func (e *CustomError) Error() string {
	return e.s
}

func testErrorStack(s string) {
	testErrorStack2(s)
}

func testErrorStack2(s string) {
	Error("error", errors.New(s))
}

func testErrorStackWithSkip(s string) {
	testErrorStackWithSkip2(s)
}

func testErrorStackWithSkip2(s string) {
	ErrorWithStackSkip("error", errors.New(s), 2)
}

func TestErrorClass(t *testing.T) {
	errors := map[string]error{
		"errors.errorString{508e076d}": fmt.Errorf("Something is broken!"),
		"rollbar.CustomError":          &CustomError{"Terrible mistakes were made."},
	}

	for expected, err := range errors {
		if errorClass(err) != expected {
			t.Error("Got:", errorClass(err), "Expected:", expected)
		}
	}
}

func TestEverything(t *testing.T) {
	Token = os.Getenv("TOKEN")
	Environment = "test"

	Error("critical", errors.New("Normal critical error"))
	Error("error", &CustomError{"This is a custom error"})

	testErrorStack("This error should have a nice stacktrace")
	testErrorStackWithSkip("This error should have a skipped stacktrace")

	done := make(chan bool)
	go func() {
		testErrorStack("I'm in a goroutine")
		done <- true
	}()
	<-done

	Message("error", "This is an error message")
	Message("info", "And this is an info message")

	// If you don't see the message sent on line 65 in Rollbar, that means this
	// is broken:
	Wait()
}
<commit_msg>Fix test.<commit_after>package rollbar

import (
	"errors"
	"fmt"
	"os"
	"testing"
)

type CustomError struct {
	s string
}

func (e *CustomError) Error() string {
	return e.s
}

func testErrorStack(s string) {
	testErrorStack2(s)
}

func testErrorStack2(s string) {
	Error("error", errors.New(s))
}

func testErrorStackWithSkip(s string) {
	testErrorStackWithSkip2(s)
}

func testErrorStackWithSkip2(s string) {
	ErrorWithStackSkip("error", errors.New(s), 2)
}

func TestErrorClass(t *testing.T) {
	errors := map[string]error{
		"{508e076d}":          fmt.Errorf("Something is broken!"),
		"rollbar.CustomError": &CustomError{"Terrible mistakes were made."},
	}

	for expected, err := range errors {
		if errorClass(err) != expected {
			t.Error("Got:", errorClass(err), "Expected:", expected)
		}
	}
}

func TestEverything(t *testing.T) {
	Token = os.Getenv("TOKEN")
	Environment = "test"

	Error("critical", errors.New("Normal critical error"))
	Error("error", &CustomError{"This is a custom error"})

	testErrorStack("This error should have a nice stacktrace")
	testErrorStackWithSkip("This error should have a skipped stacktrace")

	done := make(chan bool)
	go func() {
		testErrorStack("I'm in a goroutine")
		done <- true
	}()
	<-done

	Message("error", "This is an error message")
	Message("info", "And this is an info message")

	// If you don't see the message sent on line 65 in Rollbar, that means this
	// is broken:
	Wait()
}
<|endoftext|>


<commit_before>package grpcpool

import (
    "github.com/silenceper/pool"
    "sync"
    "time"
    "google.golang.org/grpc"
    "errors"
)

var (
    Pool GRPCPool
)

var (
    ErrInvalidConn = errors.New("invalid connection")
)

func init()  {
    Pool = GRPCPool{
        make(map[string]pool.Pool),
        sync.RWMutex{},
    }
}

type GRPCPool struct {
    // map key格式 ip:port
    conns map[string]pool.Pool
    sync.RWMutex
}

func (p *GRPCPool) Get(addr string) (*grpc.ClientConn, error)  {
    p.RLock()
    p.RUnlock()
    pool, ok := p.conns[addr]
    if !ok {
        err := p.newCommonPool(addr)
        if err != nil {
            return nil, err
        }
    }

    pool = p.conns[addr]
    conn, err := pool.Get()
    if err != nil {
        return nil, err
    }

    return conn.(*grpc.ClientConn), nil
}

func (p *GRPCPool) Put(addr string, conn *grpc.ClientConn) error {
    p.RLock()
    defer p.RUnlock()
    pool, ok := p.conns[addr]
    if ok {
        return pool.Put(conn)
    }

    return ErrInvalidConn
}

// 释放连接池
func (p *GRPCPool) Release(addr string) {
    p.Lock()
    defer p.Unlock()
    pool, ok := p.conns[addr]
    if !ok {
        return
    }
    pool.Release()
    delete(p.conns, addr)
}

// 释放所有连接池
func (p *GRPCPool) ReleaseAll()  {
    p.Lock()
    defer p.Unlock()
    for _, pool := range(p.conns) {
        pool.Release()
    }
}

// 初始化底层连接池
func (p *GRPCPool) newCommonPool(addr string) (error) {
    p.Lock()
    defer p.Unlock()
    commonPool, ok := p.conns[addr]
    if ok {
        return nil
    }
    poolConfig := &pool.PoolConfig{
        InitialCap: 1,
        MaxCap: 30,
        Factory: func() (interface{}, error) {
            return grpc.Dial(addr, grpc.WithInsecure())
        },
        Close: func(v interface{}) error {
            conn, ok := v.(*grpc.ClientConn)
            if ok && conn != nil {
                return conn.Close()
            }
            return ErrInvalidConn
        },
        IdleTimeout: 3 * time.Minute,
    }

    commonPool, err := pool.NewChannelPool(poolConfig)
    if err != nil {
        return err
    }

    p.conns[addr] = commonPool

    return nil
}<commit_msg>fix: 修复map加锁无效<commit_after>package grpcpool

import (
    "github.com/silenceper/pool"
    "sync"
    "time"
    "google.golang.org/grpc"
    "errors"
)

var (
    Pool GRPCPool
)

var (
    ErrInvalidConn = errors.New("invalid connection")
)

func init()  {
    Pool = GRPCPool{
        make(map[string]pool.Pool),
        sync.RWMutex{},
    }
}

type GRPCPool struct {
    // map key格式 ip:port
    conns map[string]pool.Pool
    sync.RWMutex
}

func (p *GRPCPool) Get(addr string) (*grpc.ClientConn, error)  {
    p.RLock()
    defer p.RUnlock()
    pool, ok := p.conns[addr]
    if !ok {
        err := p.newCommonPool(addr)
        if err != nil {
            return nil, err
        }
    }

    pool = p.conns[addr]
    conn, err := pool.Get()
    if err != nil {
        return nil, err
    }

    return conn.(*grpc.ClientConn), nil
}

func (p *GRPCPool) Put(addr string, conn *grpc.ClientConn) error {
    p.RLock()
    defer p.RUnlock()
    pool, ok := p.conns[addr]
    if ok {
        return pool.Put(conn)
    }

    return ErrInvalidConn
}

// 释放连接池
func (p *GRPCPool) Release(addr string) {
    p.Lock()
    defer p.Unlock()
    pool, ok := p.conns[addr]
    if !ok {
        return
    }
    pool.Release()
    delete(p.conns, addr)
}

// 释放所有连接池
func (p *GRPCPool) ReleaseAll()  {
    p.Lock()
    defer p.Unlock()
    for _, pool := range(p.conns) {
        pool.Release()
    }
}

// 初始化底层连接池
func (p *GRPCPool) newCommonPool(addr string) (error) {
    p.Lock()
    defer p.Unlock()
    commonPool, ok := p.conns[addr]
    if ok {
        return nil
    }
    poolConfig := &pool.PoolConfig{
        InitialCap: 1,
        MaxCap: 30,
        Factory: func() (interface{}, error) {
            return grpc.Dial(addr, grpc.WithInsecure())
        },
        Close: func(v interface{}) error {
            conn, ok := v.(*grpc.ClientConn)
            if ok && conn != nil {
                return conn.Close()
            }
            return ErrInvalidConn
        },
        IdleTimeout: 3 * time.Minute,
    }

    commonPool, err := pool.NewChannelPool(poolConfig)
    if err != nil {
        return err
    }

    p.conns[addr] = commonPool

    return nil
}<|endoftext|>