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

/*
The gltext package offers a set of text rendering utilities for OpenGL
programs. It deals with TrueType and Bitmap (raster) fonts.

Text can be rendered in predefined directions (Left-to-right, right-to-left and
top-to-bottom). This allows for correct display of text for various languages.

This package supports the full set of unicode characters, provided the loaded
font does as well.

This packages uses freetype-go (code.google.com/p/freetype-go) which is licensed
under GPLv2 e FTL licenses. You can choose which one is a better fit for your
use case but FTL requires you to give some form of credit to Freetype.org

You can read the GPLv2 (https://code.google.com/p/freetype-go/source/browse/licenses/gpl.txt)
and FTL (https://code.google.com/p/freetype-go/source/browse/licenses/ftl.txt)
licenses for more information about the requirements.
*/
package gltext
<commit_msg>Removed unused file.<commit_after><|endoftext|>


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

// Package date provides functionality for working with dates. Subpackages support
// clock-face time, spans of time and ranges of dates.
//
// This package introduces a light-weight Date type that is storage-efficient
// and covenient for calendrical calculations and date parsing and formatting
// (including years outside the [0,9999] interval).
//
// Credits
//
// This package follows very closely the design of package time
// (http://golang.org/pkg/time/) in the standard library, many of the Date
// methods are implemented using the corresponding methods of the time.Time
// type, and much of the documentation is copied directly from that package.
//
// References
//
// https://golang.org/src/time/time.go
//
// https://en.wikipedia.org/wiki/Gregorian_calendar
//
// https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
//
// https://en.wikipedia.org/wiki/Astronomical_year_numbering
//
// https://en.wikipedia.org/wiki/ISO_8601
//
// https://tools.ietf.org/html/rfc822
//
// https://tools.ietf.org/html/rfc850
//
// https://tools.ietf.org/html/rfc1123
//
// https://tools.ietf.org/html/rfc3339
//
package date
<commit_msg>Documentation<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package date provides functionality for working with dates. Subpackages support
// clock-face time, spans of time, ranges of dates, and periods (as years, months,
// weeks and days).
//
// This package introduces a light-weight Date type that is storage-efficient
// and covenient for calendrical calculations and date parsing and formatting
// (including years outside the [0,9999] interval).
//
// Credits
//
// This package follows very closely the design of package time
// (http://golang.org/pkg/time/) in the standard library, many of the Date
// methods are implemented using the corresponding methods of the time.Time
// type, and much of the documentation is copied directly from that package.
//
// References
//
// https://golang.org/src/time/time.go
//
// https://en.wikipedia.org/wiki/Gregorian_calendar
//
// https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
//
// https://en.wikipedia.org/wiki/Astronomical_year_numbering
//
// https://en.wikipedia.org/wiki/ISO_8601
//
// https://tools.ietf.org/html/rfc822
//
// https://tools.ietf.org/html/rfc850
//
// https://tools.ietf.org/html/rfc1123
//
// https://tools.ietf.org/html/rfc3339
//
package date
<|endoftext|>


<commit_before>//go:generate go get github.com/blynn/nex
//go:generate go install github.com/blynn/nex
//go:generate ./bin/nex -o lexer.nn.go lexer.nn
//go:generate go fmt lexer.nn.go
//go:generate sed -i .tmp s:Lexer:lexer:g lexer.nn.go
//go:generate sed -i .tmp s:Newlexer:newLexer:g lexer.nn.go
//go:generate go tool yacc -o parser.y.go parser.y
//go:generate sed -i .tmp -f fixparser.sed parser.y.go

package edn

import (
	"errors"
	"fmt"
	"github.com/csm/go-edn/types"
	"io"
	"strings"
)

// ParseString is like ParseReader except it takes a string.
// See ParseReader for more details.
func ParseString(string string) (val types.Value, err error) {
	val, err = ParseReader(strings.NewReader(string))
	return
}

// ParseReader parses EDN from an io.Reader.
//
// Data is returned as a Value in the first return value.
// The second return value is nil on successful parses, and
// an error on unsuccessful parses (e.g. syntax error).
func ParseReader(reader io.Reader) (val types.Value, err error) {
	defer func() {
		// Nex's parser calls panic() on a lexing error
		if r := recover(); r != nil {
			if err == nil {
				err = errors.New(fmt.Sprintf("Error: %v", r))
			}
		}
	}()

	lexer := newLexer(reader)
	result := new(yySymType)
	if yyParse(lexer, result) == 0 {
		//fmt.Printf("result: v:%T:%+v\n", result.v, result.v)
		val = result.v
	} else {
		err = errors.New("Error: could not parse provided EDN")
	}

	return
}

// DumpString accepts any EDN value and will return the EDN string
// representation.
func DumpString(value types.Value) string {
	return value.String()
}
<commit_msg>go run?<commit_after>//go:generate go get github.com/blynn/nex
//go:generate go install github.com/blynn/nex
//go:generate go run src/github.com/blynn/nex/nex.go -o lexer.nn.go lexer.nn
//go:generate go fmt lexer.nn.go
//go:generate sed -i .tmp s:Lexer:lexer:g lexer.nn.go
//go:generate sed -i .tmp s:Newlexer:newLexer:g lexer.nn.go
//go:generate go tool yacc -o parser.y.go parser.y
//go:generate sed -i .tmp -f fixparser.sed parser.y.go

package edn

import (
	"errors"
	"fmt"
	"github.com/csm/go-edn/types"
	"io"
	"strings"
)

// ParseString is like ParseReader except it takes a string.
// See ParseReader for more details.
func ParseString(string string) (val types.Value, err error) {
	val, err = ParseReader(strings.NewReader(string))
	return
}

// ParseReader parses EDN from an io.Reader.
//
// Data is returned as a Value in the first return value.
// The second return value is nil on successful parses, and
// an error on unsuccessful parses (e.g. syntax error).
func ParseReader(reader io.Reader) (val types.Value, err error) {
	defer func() {
		// Nex's parser calls panic() on a lexing error
		if r := recover(); r != nil {
			if err == nil {
				err = errors.New(fmt.Sprintf("Error: %v", r))
			}
		}
	}()

	lexer := newLexer(reader)
	result := new(yySymType)
	if yyParse(lexer, result) == 0 {
		//fmt.Printf("result: v:%T:%+v\n", result.v, result.v)
		val = result.v
	} else {
		err = errors.New("Error: could not parse provided EDN")
	}

	return
}

// DumpString accepts any EDN value and will return the EDN string
// representation.
func DumpString(value types.Value) string {
	return value.String()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha1"
	"encoding/gob"
	"errors"
	"fmt"
	"golang.org/x/crypto/pbkdf2"
	"io"
	"io/ioutil"
	"os"
	"path"
)

func makeEncryptionStream(key []byte, out io.Writer) (*cipher.StreamWriter, error) {
	if len(key) != 32 {
		return nil, errors.New("Incorrect key length")
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	var iv [aes.BlockSize]byte
	if _, err := io.ReadFull(rand.Reader, iv[:]); err != nil {
		return nil, err
	}
	cfb := cipher.NewCFBEncrypter(block, iv[:])
	n, err := out.Write(iv[:])
	if err != nil {
		return nil, err
	}
	if n != len(iv) {
		return nil, io.ErrShortWrite
	}
	return &cipher.StreamWriter{S: cfb, W: out}, nil
}

func makeDecryptionStream(key []byte, in io.Reader) (*cipher.StreamReader, error) {
	if len(key) != 32 {
		return nil, errors.New("Incorrect key length")
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	var iv [aes.BlockSize]byte
	if _, err := io.ReadFull(in, iv[:]); err != nil {
		return nil, err
	}
	cfb := cipher.NewCFBDecrypter(block, iv[:])
	return &cipher.StreamReader{S: cfb, R: in}, nil
}

func encryptBytes(key, text []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	b := []byte(text)
	ciphertext := make([]byte, aes.BlockSize+len(b))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, err
	}
	cfb := cipher.NewCFBEncrypter(block, iv)
	cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
	return ciphertext, nil
}

func decryptBytes(key, text []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	if len(text) < aes.BlockSize {
		return nil, errors.New("ciphertext too short")
	}
	iv := text[:aes.BlockSize]
	text = text[aes.BlockSize:]
	cfb := cipher.NewCFBDecrypter(block, iv)
	cfb.XORKeyStream(text, text)
	return text, nil
}

type EncConfig struct {
	Magic          string
	EncryptionType int
	Salt           []byte
	Check          []byte
	EncryptionKey  []byte
}

const (
	ENC_CONFIG_MAGIC = "PTVBKCFG"
	ENC_CONFIG       = "vecbackup-enc-config"
	NO_ENCRYPTION    = iota
	SYMMETRIC_ENCRYPTION
)

func CheckEncConfig(ec *EncConfig) error {
	if ec.Magic != ENC_CONFIG_MAGIC {
		return errors.New("Invalid enc config: missing magic string")
	}
	if ec.EncryptionType != NO_ENCRYPTION && ec.EncryptionType != SYMMETRIC_ENCRYPTION {
		return errors.New(fmt.Sprintf("Unsupposed encryption method: %d", ec.EncryptionType))
	}
	if ec.EncryptionType == SYMMETRIC_ENCRYPTION {
		if len(ec.Salt) == 0 {
			return errors.New("Invalid enc config: Missing salt")
		}
		if len(ec.EncryptionKey) == 0 {
			return errors.New("Invalid enc config: Missing encrypted key")
		}
	}
	return nil
}

func ReadEncConfig(fp string) (*EncConfig, error) {
	in, err := os.Open(fp)
	if err != nil {
		return nil, err
	}
	defer in.Close()
	dec := gob.NewDecoder(in)
	ec := &EncConfig{}
	dec.Decode(ec)
	err = CheckEncConfig(ec)
	if err != nil {
		return nil, err
	}
	return ec, nil
}

func WriteEncConfig(bkDir string, ec *EncConfig) error {
	err := CheckEncConfig(ec)
	if err != nil {
		return err
	}
	fp := path.Join(bkDir, ENC_CONFIG)
	_, err = os.Stat(fp)
	if !os.IsNotExist(err) {
		return errors.New(fmt.Sprintf("Enc config file already exists: %s", fp))
	}
	out, err := os.Create(fp)
	if err != nil {
		return err
	}
	enc := gob.NewEncoder(out)
	err = enc.Encode(ec)
	if err != nil {
		out.Close()
		return err
	}
	return out.Close()
}

func WriteNewEncConfig(pwFile, bkDir string) error {
	if pwFile == "" {
		return WriteEncConfig(bkDir, &EncConfig{Magic: ENC_CONFIG_MAGIC, EncryptionType: NO_ENCRYPTION})
	}
	pw, err := ioutil.ReadFile(pwFile)
	if err != nil {
		return errors.New(fmt.Sprintf("Cannot read password file: %s", pwFile))
	}
	ec := EncConfig{Magic: ENC_CONFIG_MAGIC, EncryptionType: SYMMETRIC_ENCRYPTION, Salt: make([]byte, 32)}
	if _, err := io.ReadFull(rand.Reader, ec.Salt); err != nil {
		return err
	}
	key2 := pbkdf2.Key(pw, ec.Salt, 10000, 32, sha1.New)
	var check [32]byte
	if _, err := io.ReadFull(rand.Reader, check[:16]); err != nil {
		return err
	}
	copy(check[16:32], check[:16])
	ec.Check, err = encryptBytes(key2, check[:])
	if err != nil {
		return err
	}
	var rawKey [32]byte
	if _, err := io.ReadFull(rand.Reader, rawKey[:]); err != nil {
		return err
	}
	ec.EncryptionKey, err = encryptBytes(key2, rawKey[:])
	if err != nil {
		return err
	}
	return WriteEncConfig(bkDir, &ec)
}

func GetKey(pwFile, bkDir string) ([]byte, error) {
	ec, err := ReadEncConfig(path.Join(bkDir, ENC_CONFIG))
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Cannot read enc config file: %s", err))
	}
	if ec.EncryptionType == NO_ENCRYPTION {
		if pwFile != "" {
			return nil, errors.New("Backup is not encrypted")
		}
		return nil, nil
	}
	if pwFile == "" {
		return nil, errors.New("Backup is encrypted")
	}
	pw, err := ioutil.ReadFile(pwFile)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Cannot read pw file: %s", err))
	}
	key2 := pbkdf2.Key(pw, ec.Salt, 10000, 32, sha1.New)
	check, err := decryptBytes(key2, ec.Check)
	if len(check) != 32 || bytes.Compare(check[:16], check[16:32]) != 0 {
		return nil, errors.New("Wrong password")
	}
	key, err := decryptBytes(key2, ec.EncryptionKey)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Cannot decrypt enc key: %s", err))
	}
	return append([]byte(nil), key[:]...), nil
}
<commit_msg>Cleanup: created const PBKDF_ROUNDS.<commit_after>package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha1"
	"encoding/gob"
	"errors"
	"fmt"
	"golang.org/x/crypto/pbkdf2"
	"io"
	"io/ioutil"
	"os"
	"path"
)

const PBKDF_ROUNDS = 10000

func makeEncryptionStream(key []byte, out io.Writer) (*cipher.StreamWriter, error) {
	if len(key) != 32 {
		return nil, errors.New("Incorrect key length")
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	var iv [aes.BlockSize]byte
	if _, err := io.ReadFull(rand.Reader, iv[:]); err != nil {
		return nil, err
	}
	cfb := cipher.NewCFBEncrypter(block, iv[:])
	n, err := out.Write(iv[:])
	if err != nil {
		return nil, err
	}
	if n != len(iv) {
		return nil, io.ErrShortWrite
	}
	return &cipher.StreamWriter{S: cfb, W: out}, nil
}

func makeDecryptionStream(key []byte, in io.Reader) (*cipher.StreamReader, error) {
	if len(key) != 32 {
		return nil, errors.New("Incorrect key length")
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	var iv [aes.BlockSize]byte
	if _, err := io.ReadFull(in, iv[:]); err != nil {
		return nil, err
	}
	cfb := cipher.NewCFBDecrypter(block, iv[:])
	return &cipher.StreamReader{S: cfb, R: in}, nil
}

func encryptBytes(key, text []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	b := []byte(text)
	ciphertext := make([]byte, aes.BlockSize+len(b))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, err
	}
	cfb := cipher.NewCFBEncrypter(block, iv)
	cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
	return ciphertext, nil
}

func decryptBytes(key, text []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	if len(text) < aes.BlockSize {
		return nil, errors.New("ciphertext too short")
	}
	iv := text[:aes.BlockSize]
	text = text[aes.BlockSize:]
	cfb := cipher.NewCFBDecrypter(block, iv)
	cfb.XORKeyStream(text, text)
	return text, nil
}

type EncConfig struct {
	Magic          string
	EncryptionType int
	Salt           []byte
	Check          []byte
	EncryptionKey  []byte
}

const (
	ENC_CONFIG_MAGIC = "PTVBKCFG"
	ENC_CONFIG       = "vecbackup-enc-config"
	NO_ENCRYPTION    = iota
	SYMMETRIC_ENCRYPTION
)

func CheckEncConfig(ec *EncConfig) error {
	if ec.Magic != ENC_CONFIG_MAGIC {
		return errors.New("Invalid enc config: missing magic string")
	}
	if ec.EncryptionType != NO_ENCRYPTION && ec.EncryptionType != SYMMETRIC_ENCRYPTION {
		return errors.New(fmt.Sprintf("Unsupposed encryption method: %d", ec.EncryptionType))
	}
	if ec.EncryptionType == SYMMETRIC_ENCRYPTION {
		if len(ec.Salt) == 0 {
			return errors.New("Invalid enc config: Missing salt")
		}
		if len(ec.EncryptionKey) == 0 {
			return errors.New("Invalid enc config: Missing encrypted key")
		}
	}
	return nil
}

func ReadEncConfig(fp string) (*EncConfig, error) {
	in, err := os.Open(fp)
	if err != nil {
		return nil, err
	}
	defer in.Close()
	dec := gob.NewDecoder(in)
	ec := &EncConfig{}
	dec.Decode(ec)
	err = CheckEncConfig(ec)
	if err != nil {
		return nil, err
	}
	return ec, nil
}

func WriteEncConfig(bkDir string, ec *EncConfig) error {
	err := CheckEncConfig(ec)
	if err != nil {
		return err
	}
	fp := path.Join(bkDir, ENC_CONFIG)
	_, err = os.Stat(fp)
	if !os.IsNotExist(err) {
		return errors.New(fmt.Sprintf("Enc config file already exists: %s", fp))
	}
	out, err := os.Create(fp)
	if err != nil {
		return err
	}
	enc := gob.NewEncoder(out)
	err = enc.Encode(ec)
	if err != nil {
		out.Close()
		return err
	}
	return out.Close()
}

func WriteNewEncConfig(pwFile, bkDir string) error {
	if pwFile == "" {
		return WriteEncConfig(bkDir, &EncConfig{Magic: ENC_CONFIG_MAGIC, EncryptionType: NO_ENCRYPTION})
	}
	pw, err := ioutil.ReadFile(pwFile)
	if err != nil {
		return errors.New(fmt.Sprintf("Cannot read password file: %s", pwFile))
	}
	ec := EncConfig{Magic: ENC_CONFIG_MAGIC, EncryptionType: SYMMETRIC_ENCRYPTION, Salt: make([]byte, 32)}
	if _, err := io.ReadFull(rand.Reader, ec.Salt); err != nil {
		return err
	}
	key2 := pbkdf2.Key(pw, ec.Salt, PBKDF_ROUNDS, 32, sha1.New)
	var check [32]byte
	if _, err := io.ReadFull(rand.Reader, check[:16]); err != nil {
		return err
	}
	copy(check[16:32], check[:16])
	ec.Check, err = encryptBytes(key2, check[:])
	if err != nil {
		return err
	}
	var rawKey [32]byte
	if _, err := io.ReadFull(rand.Reader, rawKey[:]); err != nil {
		return err
	}
	ec.EncryptionKey, err = encryptBytes(key2, rawKey[:])
	if err != nil {
		return err
	}
	return WriteEncConfig(bkDir, &ec)
}

func GetKey(pwFile, bkDir string) ([]byte, error) {
	ec, err := ReadEncConfig(path.Join(bkDir, ENC_CONFIG))
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Cannot read enc config file: %s", err))
	}
	if ec.EncryptionType == NO_ENCRYPTION {
		if pwFile != "" {
			return nil, errors.New("Backup is not encrypted")
		}
		return nil, nil
	}
	if pwFile == "" {
		return nil, errors.New("Backup is encrypted")
	}
	pw, err := ioutil.ReadFile(pwFile)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Cannot read pw file: %s", err))
	}
	key2 := pbkdf2.Key(pw, ec.Salt, PBKDF_ROUNDS, 32, sha1.New)
	check, err := decryptBytes(key2, ec.Check)
	if len(check) != 32 || bytes.Compare(check[:16], check[16:32]) != 0 {
		return nil, errors.New("Wrong password")
	}
	key, err := decryptBytes(key2, ec.EncryptionKey)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Cannot decrypt enc key: %s", err))
	}
	return append([]byte(nil), key[:]...), nil
}
<|endoftext|>


<commit_before>package env

import (
	"errors"
	"fmt"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"
)

var (
	// ErrNotAStructPtr is returned if you pass something that is not a pointer to a
	// Struct to Parse
	ErrNotAStructPtr = errors.New("Expected a pointer to a Struct")
	// ErrUnsupportedType if the struct field type is not supported by env
	ErrUnsupportedType = errors.New("Type is not supported")
	// ErrUnsupportedSliceType if the slice element type is not supported by env
	ErrUnsupportedSliceType = errors.New("Unsupported slice type")
	// Friendly names for reflect types
	sliceOfInts      = reflect.TypeOf([]int(nil))
	sliceOfInt64s    = reflect.TypeOf([]int64(nil))
	sliceOfUint64s   = reflect.TypeOf([]uint64(nil))
	sliceOfStrings   = reflect.TypeOf([]string(nil))
	sliceOfBools     = reflect.TypeOf([]bool(nil))
	sliceOfFloat32s  = reflect.TypeOf([]float32(nil))
	sliceOfFloat64s  = reflect.TypeOf([]float64(nil))
	sliceOfDurations = reflect.TypeOf([]time.Duration(nil))
)

// CustomParsers is a friendly name for the type that `ParseWithFuncs()` accepts
type CustomParsers map[reflect.Type]ParserFunc

// ParserFunc defines the signature of a function that can be used within `CustomParsers`
type ParserFunc func(v string) (interface{}, error)

// Parse parses a struct containing `env` tags and loads its values from
// environment variables.
func Parse(v interface{}) error {
	ptrRef := reflect.ValueOf(v)
	if ptrRef.Kind() != reflect.Ptr {
		return ErrNotAStructPtr
	}
	ref := ptrRef.Elem()
	if ref.Kind() != reflect.Struct {
		return ErrNotAStructPtr
	}
	return doParse(ref, make(map[reflect.Type]ParserFunc, 0))
}

// ParseWithFuncs is the same as `Parse` except it also allows the user to pass
// in custom parsers.
func ParseWithFuncs(v interface{}, funcMap CustomParsers) error {
	ptrRef := reflect.ValueOf(v)
	if ptrRef.Kind() != reflect.Ptr {
		return ErrNotAStructPtr
	}
	ref := ptrRef.Elem()
	if ref.Kind() != reflect.Struct {
		return ErrNotAStructPtr
	}
	return doParse(ref, funcMap)
}

func doParse(ref reflect.Value, funcMap CustomParsers) error {
	refType := ref.Type()
	var errorList []string

	for i := 0; i < refType.NumField(); i++ {
		refField := ref.Field(i)
		if reflect.Ptr == refField.Kind() && !refField.IsNil() && refField.CanSet() {
			err := Parse(refField.Interface())
			if nil != err {
				return err
			}
			continue
		}
		refTypeField := refType.Field(i)
		value, err := get(refTypeField)
		if err != nil {
			errorList = append(errorList, err.Error())
			continue
		}
		if value == "" {
			continue
		}
		if err := set(refField, refTypeField, value, funcMap); err != nil {
			errorList = append(errorList, err.Error())
			continue
		}
	}
	if len(errorList) == 0 {
		return nil
	}
	return errors.New(strings.Join(errorList, ". "))
}

func get(field reflect.StructField) (string, error) {
	var (
		val string
		err error
	)

	key, opts := parseKeyForOption(field.Tag.Get("env"))

	defaultValue := field.Tag.Get("envDefault")
	val = getOr(key, defaultValue)

	if len(opts) > 0 {
		for _, opt := range opts {
			// The only option supported is "required".
			switch opt {
			case "":
				break
			case "required":
				val, err = getRequired(key)
			default:
				err = errors.New("Env tag option " + opt + " not supported.")
			}
		}
	}

	return val, err
}

// split the env tag's key into the expected key and desired option, if any.
func parseKeyForOption(key string) (string, []string) {
	opts := strings.Split(key, ",")
	return opts[0], opts[1:]
}

func getRequired(key string) (string, error) {
	if value, ok := os.LookupEnv(key); ok {
		return value, nil
	}
	// We do not use fmt.Errorf to avoid another import.
	return "", errors.New("Required environment variable " + key + " is not set")
}

func getOr(key, defaultValue string) string {
	value, ok := os.LookupEnv(key)
	if ok {
		return value
	}
	return defaultValue
}

func set(field reflect.Value, refType reflect.StructField, value string, funcMap CustomParsers) error {
	switch field.Kind() {
	case reflect.Slice:
		separator := refType.Tag.Get("envSeparator")
		return handleSlice(field, value, separator)
	case reflect.String:
		field.SetString(value)
	case reflect.Bool:
		bvalue, err := strconv.ParseBool(value)
		if err != nil {
			return err
		}
		field.SetBool(bvalue)
	case reflect.Int:
		intValue, err := strconv.ParseInt(value, 10, 32)
		if err != nil {
			return err
		}
		field.SetInt(intValue)
	case reflect.Uint:
		uintValue, err := strconv.ParseUint(value, 10, 32)
		if err != nil {
			return err
		}
		field.SetUint(uintValue)
	case reflect.Float32:
		v, err := strconv.ParseFloat(value, 32)
		if err != nil {
			return err
		}
		field.SetFloat(v)
	case reflect.Float64:
		v, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(v))
	case reflect.Int64:
		if refType.Type.String() == "time.Duration" {
			dValue, err := time.ParseDuration(value)
			if err != nil {
				return err
			}
			field.Set(reflect.ValueOf(dValue))
		} else {
			intValue, err := strconv.ParseInt(value, 10, 64)
			if err != nil {
				return err
			}
			field.SetInt(intValue)
		}
	case reflect.Uint64:
		uintValue, err := strconv.ParseUint(value, 10, 64)
		if err != nil {
			return err
		}
		field.SetUint(uintValue)
	case reflect.Struct:
		return handleStruct(field, refType, value, funcMap)
	default:
		return ErrUnsupportedType
	}
	return nil
}

func handleStruct(field reflect.Value, refType reflect.StructField, value string, funcMap CustomParsers) error {
	// Does the custom parser func map contain this type?
	parserFunc, ok := funcMap[field.Type()]
	if !ok {
		// Map does not contain a custom parser for this type
		return ErrUnsupportedType
	}

	// Call on the custom parser func
	data, err := parserFunc(value)
	if err != nil {
		return fmt.Errorf("Custom parser error: %v", err)
	}

	// Set the field to the data returned by the customer parser func
	rv := reflect.ValueOf(data)
	field.Set(rv)

	return nil
}

func handleSlice(field reflect.Value, value, separator string) error {
	if separator == "" {
		separator = ","
	}

	splitData := strings.Split(value, separator)

	switch field.Type() {
	case sliceOfStrings:
		field.Set(reflect.ValueOf(splitData))
	case sliceOfInts:
		intData, err := parseInts(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(intData))
	case sliceOfInt64s:
		int64Data, err := parseInt64s(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(int64Data))
	case sliceOfUint64s:
		uint64Data, err := parseUint64s(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(uint64Data))
	case sliceOfFloat32s:
		data, err := parseFloat32s(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(data))
	case sliceOfFloat64s:
		data, err := parseFloat64s(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(data))
	case sliceOfBools:
		boolData, err := parseBools(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(boolData))
	case sliceOfDurations:
		durationData, err := parseDurations(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(durationData))
	default:
		return ErrUnsupportedSliceType
	}
	return nil
}

func parseInts(data []string) ([]int, error) {
	intSlice := make([]int, 0, len(data))

	for _, v := range data {
		intValue, err := strconv.ParseInt(v, 10, 32)
		if err != nil {
			return nil, err
		}
		intSlice = append(intSlice, int(intValue))
	}
	return intSlice, nil
}

func parseInt64s(data []string) ([]int64, error) {
	intSlice := make([]int64, 0, len(data))

	for _, v := range data {
		intValue, err := strconv.ParseInt(v, 10, 64)
		if err != nil {
			return nil, err
		}
		intSlice = append(intSlice, int64(intValue))
	}
	return intSlice, nil
}

func parseUint64s(data []string) ([]uint64, error) {
	var uintSlice []uint64

	for _, v := range data {
		uintValue, err := strconv.ParseUint(v, 10, 64)
		if err != nil {
			return nil, err
		}
		uintSlice = append(uintSlice, uint64(uintValue))
	}
	return uintSlice, nil
}

func parseFloat32s(data []string) ([]float32, error) {
	float32Slice := make([]float32, 0, len(data))

	for _, v := range data {
		data, err := strconv.ParseFloat(v, 32)
		if err != nil {
			return nil, err
		}
		float32Slice = append(float32Slice, float32(data))
	}
	return float32Slice, nil
}

func parseFloat64s(data []string) ([]float64, error) {
	float64Slice := make([]float64, 0, len(data))

	for _, v := range data {
		data, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return nil, err
		}
		float64Slice = append(float64Slice, float64(data))
	}
	return float64Slice, nil
}

func parseBools(data []string) ([]bool, error) {
	boolSlice := make([]bool, 0, len(data))

	for _, v := range data {
		bvalue, err := strconv.ParseBool(v)
		if err != nil {
			return nil, err
		}

		boolSlice = append(boolSlice, bvalue)
	}
	return boolSlice, nil
}

func parseDurations(data []string) ([]time.Duration, error) {
	durationSlice := make([]time.Duration, 0, len(data))

	for _, v := range data {
		dvalue, err := time.ParseDuration(v)
		if err != nil {
			return nil, err
		}

		durationSlice = append(durationSlice, dvalue)
	}
	return durationSlice, nil
}
<commit_msg>adds a general-purpose per-field callback<commit_after>package env

import (
	"errors"
	"fmt"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"
)

var (
	// ErrNotAStructPtr is returned if you pass something that is not a pointer to a
	// Struct to Parse
	ErrNotAStructPtr = errors.New("Expected a pointer to a Struct")
	// ErrUnsupportedType if the struct field type is not supported by env
	ErrUnsupportedType = errors.New("Type is not supported")
	// ErrUnsupportedSliceType if the slice element type is not supported by env
	ErrUnsupportedSliceType = errors.New("Unsupported slice type")
	// OnEnvVarSet is an optional convenience callback, such as for logging purposes.
	// If not nil, it's called after successfully setting the given field from the given value.
	OnEnvVarSet func(reflect.StructField, string)
	// Friendly names for reflect types
	sliceOfInts      = reflect.TypeOf([]int(nil))
	sliceOfInt64s    = reflect.TypeOf([]int64(nil))
	sliceOfUint64s   = reflect.TypeOf([]uint64(nil))
	sliceOfStrings   = reflect.TypeOf([]string(nil))
	sliceOfBools     = reflect.TypeOf([]bool(nil))
	sliceOfFloat32s  = reflect.TypeOf([]float32(nil))
	sliceOfFloat64s  = reflect.TypeOf([]float64(nil))
	sliceOfDurations = reflect.TypeOf([]time.Duration(nil))
)

// CustomParsers is a friendly name for the type that `ParseWithFuncs()` accepts
type CustomParsers map[reflect.Type]ParserFunc

// ParserFunc defines the signature of a function that can be used within `CustomParsers`
type ParserFunc func(v string) (interface{}, error)

// Parse parses a struct containing `env` tags and loads its values from
// environment variables.
func Parse(v interface{}) error {
	ptrRef := reflect.ValueOf(v)
	if ptrRef.Kind() != reflect.Ptr {
		return ErrNotAStructPtr
	}
	ref := ptrRef.Elem()
	if ref.Kind() != reflect.Struct {
		return ErrNotAStructPtr
	}
	return doParse(ref, make(map[reflect.Type]ParserFunc, 0))
}

// ParseWithFuncs is the same as `Parse` except it also allows the user to pass
// in custom parsers.
func ParseWithFuncs(v interface{}, funcMap CustomParsers) error {
	ptrRef := reflect.ValueOf(v)
	if ptrRef.Kind() != reflect.Ptr {
		return ErrNotAStructPtr
	}
	ref := ptrRef.Elem()
	if ref.Kind() != reflect.Struct {
		return ErrNotAStructPtr
	}
	return doParse(ref, funcMap)
}

func doParse(ref reflect.Value, funcMap CustomParsers) error {
	refType := ref.Type()
	var errorList []string

	for i := 0; i < refType.NumField(); i++ {
		refField := ref.Field(i)
		if reflect.Ptr == refField.Kind() && !refField.IsNil() && refField.CanSet() {
			err := Parse(refField.Interface())
			if nil != err {
				return err
			}
			continue
		}
		refTypeField := refType.Field(i)
		value, err := get(refTypeField)
		if err != nil {
			errorList = append(errorList, err.Error())
			continue
		}
		if value == "" {
			continue
		}
		if err := set(refField, refTypeField, value, funcMap); err != nil {
			errorList = append(errorList, err.Error())
			continue
		}
		if OnEnvVarSet != nil {
			OnEnvVarSet(refTypeField, value)
		}
	}
	if len(errorList) == 0 {
		return nil
	}
	return errors.New(strings.Join(errorList, ". "))
}

func get(field reflect.StructField) (string, error) {
	var (
		val string
		err error
	)

	key, opts := parseKeyForOption(field.Tag.Get("env"))

	defaultValue := field.Tag.Get("envDefault")
	val = getOr(key, defaultValue)

	if len(opts) > 0 {
		for _, opt := range opts {
			// The only option supported is "required".
			switch opt {
			case "":
				break
			case "required":
				val, err = getRequired(key)
			default:
				err = errors.New("Env tag option " + opt + " not supported.")
			}
		}
	}

	return val, err
}

// split the env tag's key into the expected key and desired option, if any.
func parseKeyForOption(key string) (string, []string) {
	opts := strings.Split(key, ",")
	return opts[0], opts[1:]
}

func getRequired(key string) (string, error) {
	if value, ok := os.LookupEnv(key); ok {
		return value, nil
	}
	// We do not use fmt.Errorf to avoid another import.
	return "", errors.New("Required environment variable " + key + " is not set")
}

func getOr(key, defaultValue string) string {
	value, ok := os.LookupEnv(key)
	if ok {
		return value
	}
	return defaultValue
}

func set(field reflect.Value, refType reflect.StructField, value string, funcMap CustomParsers) error {
	switch field.Kind() {
	case reflect.Slice:
		separator := refType.Tag.Get("envSeparator")
		return handleSlice(field, value, separator)
	case reflect.String:
		field.SetString(value)
	case reflect.Bool:
		bvalue, err := strconv.ParseBool(value)
		if err != nil {
			return err
		}
		field.SetBool(bvalue)
	case reflect.Int:
		intValue, err := strconv.ParseInt(value, 10, 32)
		if err != nil {
			return err
		}
		field.SetInt(intValue)
	case reflect.Uint:
		uintValue, err := strconv.ParseUint(value, 10, 32)
		if err != nil {
			return err
		}
		field.SetUint(uintValue)
	case reflect.Float32:
		v, err := strconv.ParseFloat(value, 32)
		if err != nil {
			return err
		}
		field.SetFloat(v)
	case reflect.Float64:
		v, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(v))
	case reflect.Int64:
		if refType.Type.String() == "time.Duration" {
			dValue, err := time.ParseDuration(value)
			if err != nil {
				return err
			}
			field.Set(reflect.ValueOf(dValue))
		} else {
			intValue, err := strconv.ParseInt(value, 10, 64)
			if err != nil {
				return err
			}
			field.SetInt(intValue)
		}
	case reflect.Uint64:
		uintValue, err := strconv.ParseUint(value, 10, 64)
		if err != nil {
			return err
		}
		field.SetUint(uintValue)
	case reflect.Struct:
		return handleStruct(field, refType, value, funcMap)
	default:
		return ErrUnsupportedType
	}
	return nil
}

func handleStruct(field reflect.Value, refType reflect.StructField, value string, funcMap CustomParsers) error {
	// Does the custom parser func map contain this type?
	parserFunc, ok := funcMap[field.Type()]
	if !ok {
		// Map does not contain a custom parser for this type
		return ErrUnsupportedType
	}

	// Call on the custom parser func
	data, err := parserFunc(value)
	if err != nil {
		return fmt.Errorf("Custom parser error: %v", err)
	}

	// Set the field to the data returned by the customer parser func
	rv := reflect.ValueOf(data)
	field.Set(rv)

	return nil
}

func handleSlice(field reflect.Value, value, separator string) error {
	if separator == "" {
		separator = ","
	}

	splitData := strings.Split(value, separator)

	switch field.Type() {
	case sliceOfStrings:
		field.Set(reflect.ValueOf(splitData))
	case sliceOfInts:
		intData, err := parseInts(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(intData))
	case sliceOfInt64s:
		int64Data, err := parseInt64s(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(int64Data))
	case sliceOfUint64s:
		uint64Data, err := parseUint64s(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(uint64Data))
	case sliceOfFloat32s:
		data, err := parseFloat32s(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(data))
	case sliceOfFloat64s:
		data, err := parseFloat64s(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(data))
	case sliceOfBools:
		boolData, err := parseBools(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(boolData))
	case sliceOfDurations:
		durationData, err := parseDurations(splitData)
		if err != nil {
			return err
		}
		field.Set(reflect.ValueOf(durationData))
	default:
		return ErrUnsupportedSliceType
	}
	return nil
}

func parseInts(data []string) ([]int, error) {
	intSlice := make([]int, 0, len(data))

	for _, v := range data {
		intValue, err := strconv.ParseInt(v, 10, 32)
		if err != nil {
			return nil, err
		}
		intSlice = append(intSlice, int(intValue))
	}
	return intSlice, nil
}

func parseInt64s(data []string) ([]int64, error) {
	intSlice := make([]int64, 0, len(data))

	for _, v := range data {
		intValue, err := strconv.ParseInt(v, 10, 64)
		if err != nil {
			return nil, err
		}
		intSlice = append(intSlice, int64(intValue))
	}
	return intSlice, nil
}

func parseUint64s(data []string) ([]uint64, error) {
	var uintSlice []uint64

	for _, v := range data {
		uintValue, err := strconv.ParseUint(v, 10, 64)
		if err != nil {
			return nil, err
		}
		uintSlice = append(uintSlice, uint64(uintValue))
	}
	return uintSlice, nil
}

func parseFloat32s(data []string) ([]float32, error) {
	float32Slice := make([]float32, 0, len(data))

	for _, v := range data {
		data, err := strconv.ParseFloat(v, 32)
		if err != nil {
			return nil, err
		}
		float32Slice = append(float32Slice, float32(data))
	}
	return float32Slice, nil
}

func parseFloat64s(data []string) ([]float64, error) {
	float64Slice := make([]float64, 0, len(data))

	for _, v := range data {
		data, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return nil, err
		}
		float64Slice = append(float64Slice, float64(data))
	}
	return float64Slice, nil
}

func parseBools(data []string) ([]bool, error) {
	boolSlice := make([]bool, 0, len(data))

	for _, v := range data {
		bvalue, err := strconv.ParseBool(v)
		if err != nil {
			return nil, err
		}

		boolSlice = append(boolSlice, bvalue)
	}
	return boolSlice, nil
}

func parseDurations(data []string) ([]time.Duration, error) {
	durationSlice := make([]time.Duration, 0, len(data))

	for _, v := range data {
		dvalue, err := time.ParseDuration(v)
		if err != nil {
			return nil, err
		}

		durationSlice = append(durationSlice, dvalue)
	}
	return durationSlice, nil
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
)

var Verbose bool

type Environ map[string]string
type Config struct {
	Unversioned []string
	Profiles    map[string]Environ
}

func die(err error, args ...interface{}) {
	if err != nil {
		if args != nil {
			panic(fmt.Errorf("%v: %v", err, args))
		} else {
			panic(err)
		}
	}
}

func main() {
	verb := os.Getenv("VERBOSE")
	if verb != "" {
		var err error
		Verbose, err = strconv.ParseBool(verb)
		if err != nil {
			fmt.Printf("warning: unable to parse VERBOSE=%s as bool\n", verb)
		}
	}

	var profile = flag.String("profile", "dev", "profile")
	var output = flag.String("output", "", "output file")
	var input = flag.String("input", "", "input file")
	var newline = flag.String("newline", "\n", "string to use for newline")
	flag.Parse()

	var err error
	var in *os.File
	if *input != "" {
		in, err = os.Open(*input)
		defer in.Close()
	} else {
		in = os.Stdin
	}

	var out *os.File
	if *output != "" {
		out, err := os.Create(*output)
		die(err)
		defer out.Close()
	} else {
		out = os.Stdout
	}

	bytes, err := ioutil.ReadAll(in)
	die(err)

	var config Config
	err = json.Unmarshal(bytes, &config)
	die(err)

	current, ok := config.Profiles[*profile]
	if !ok {
		panic("no such profile: " + *profile)
	}

	out.WriteString(fmt.Sprintf("PROFILE=%s%s", *profile, *newline))

	combined := make(map[string]string)

	for k, v := range config.Profiles["default"] {
		combined[k] = v
	}
	for k, v := range current {
		combined[k] = v
	}

	for k, v := range combined {
		out.WriteString(fmt.Sprintf("%s=%s%s", k, v, *newline))
	}

	out.WriteString(fmt.Sprintf("HASH=%x%s", hash(config.Unversioned), *newline))
}

func versioned(path string, excludes []string) bool {
	for _, ex := range excludes {
		m, err := filepath.Match(ex, path)
		die(err)
		if m {
			return false
		}
	}
	return true
}

func hash(unversioned []string) []byte {
	standard, err := shell("git ls-files --exclude-standard")
	die(err)
	others, err := shell("git ls-files --exclude-standard --others")
	die(err)

	files := append(standard, others...)

	h := md5.New()
	for _, file := range files {
		if strings.TrimSpace(file) == "" {
			continue
		}
		if !versioned(file, unversioned) {
			if Verbose {
				fmt.Printf("skipping %s\n", file)
			}
			continue
		}
		if Verbose {
			fmt.Printf("hashing %s\n", file)
		}
		h.Write([]byte(file))
		info, err := os.Lstat(file)
		if err != nil {
			h.Write([]byte("error"))
			h.Write([]byte(err.Error()))
		} else {
			if info.Mode()&os.ModeSymlink != 0 {
				target, err := os.Readlink(file)
				die(err)
				h.Write([]byte("link"))
				h.Write([]byte(target))
			} else if !info.IsDir() {
				h.Write([]byte("file"))
				f, err := os.Open(file)
				die(err, file)
				_, err = io.Copy(h, f)
				f.Close()
				die(err)
			}
		}
	}

	return h.Sum(nil)
}

func shell(command string) ([]string, error) {
	cmd := exec.Command("sh", "-c", command)
	out, err := cmd.CombinedOutput()
	str := string(out)
	lines := strings.Split(str, "\n")
	return lines, err
}
<commit_msg>env.go: +build ignore<commit_after>// +build ignore

package main

import (
	"crypto/md5"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
)

var Verbose bool

type Environ map[string]string
type Config struct {
	Unversioned []string
	Profiles    map[string]Environ
}

func die(err error, args ...interface{}) {
	if err != nil {
		if args != nil {
			panic(fmt.Errorf("%v: %v", err, args))
		} else {
			panic(err)
		}
	}
}

func main() {
	verb := os.Getenv("VERBOSE")
	if verb != "" {
		var err error
		Verbose, err = strconv.ParseBool(verb)
		if err != nil {
			fmt.Printf("warning: unable to parse VERBOSE=%s as bool\n", verb)
		}
	}

	var profile = flag.String("profile", "dev", "profile")
	var output = flag.String("output", "", "output file")
	var input = flag.String("input", "", "input file")
	var newline = flag.String("newline", "\n", "string to use for newline")
	flag.Parse()

	var err error
	var in *os.File
	if *input != "" {
		in, err = os.Open(*input)
		defer in.Close()
	} else {
		in = os.Stdin
	}

	var out *os.File
	if *output != "" {
		out, err := os.Create(*output)
		die(err)
		defer out.Close()
	} else {
		out = os.Stdout
	}

	bytes, err := ioutil.ReadAll(in)
	die(err)

	var config Config
	err = json.Unmarshal(bytes, &config)
	die(err)

	current, ok := config.Profiles[*profile]
	if !ok {
		panic("no such profile: " + *profile)
	}

	out.WriteString(fmt.Sprintf("PROFILE=%s%s", *profile, *newline))

	combined := make(map[string]string)

	for k, v := range config.Profiles["default"] {
		combined[k] = v
	}
	for k, v := range current {
		combined[k] = v
	}

	for k, v := range combined {
		out.WriteString(fmt.Sprintf("%s=%s%s", k, v, *newline))
	}

	out.WriteString(fmt.Sprintf("HASH=%x%s", hash(config.Unversioned), *newline))
}

func versioned(path string, excludes []string) bool {
	for _, ex := range excludes {
		m, err := filepath.Match(ex, path)
		die(err)
		if m {
			return false
		}
	}
	return true
}

func hash(unversioned []string) []byte {
	standard, err := shell("git ls-files --exclude-standard")
	die(err)
	others, err := shell("git ls-files --exclude-standard --others")
	die(err)

	files := append(standard, others...)

	h := md5.New()
	for _, file := range files {
		if strings.TrimSpace(file) == "" {
			continue
		}
		if !versioned(file, unversioned) {
			if Verbose {
				fmt.Printf("skipping %s\n", file)
			}
			continue
		}
		if Verbose {
			fmt.Printf("hashing %s\n", file)
		}
		h.Write([]byte(file))
		info, err := os.Lstat(file)
		if err != nil {
			h.Write([]byte("error"))
			h.Write([]byte(err.Error()))
		} else {
			if info.Mode()&os.ModeSymlink != 0 {
				target, err := os.Readlink(file)
				die(err)
				h.Write([]byte("link"))
				h.Write([]byte(target))
			} else if !info.IsDir() {
				h.Write([]byte("file"))
				f, err := os.Open(file)
				die(err, file)
				_, err = io.Copy(h, f)
				f.Close()
				die(err)
			}
		}
	}

	return h.Sum(nil)
}

func shell(command string) ([]string, error) {
	cmd := exec.Command("sh", "-c", command)
	out, err := cmd.CombinedOutput()
	str := string(out)
	lines := strings.Split(str, "\n")
	return lines, err
}
<|endoftext|>


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

// Package fit provides functions to fit data.
package fit

import (
	"github.com/gonum/diff/fd"
)

// Func1D describes a 1D function to fit some data.
type Func1D struct {
	// F is the function to minimize.
	// ps is the slice of parameters to optimize during the fit.
	F func(x float64, ps []float64) float64

	// N is the number of parameters to optimize during the fit.
	// If N is 0, Ps must not be nil.
	N int

	// Ps is the initial values for the parameters.
	// If Ps is nil, the set of initial parameters values is a slice of
	// length N filled with zeros.
	Ps []float64

	X   []float64
	Y   []float64
	Err []float64

	sig2 []float64 // inverse of squares of measurement errors along Y.

	fct  func(ps []float64) float64 // cost function (objective function)
	grad func(grad, ps []float64)
}

func (f *Func1D) init() {

	f.sig2 = make([]float64, len(f.Y))
	switch {
	default:
		for i := range f.Y {
			f.sig2[i] = 1
		}
	case f.Err != nil:
		for i, v := range f.Err {
			f.sig2[i] = 1 / (v * v)
		}
	}

	if f.Ps == nil {
		f.Ps = make([]float64, f.N)
	}

	if len(f.Ps) == 0 {
		panic("fit: invalid number of initial parameters")
	}

	if len(f.X) != len(f.Y) {
		panic("fit: mismatch length")
	}

	if len(f.sig2) != len(f.Y) {
		panic("fit: mismatch length")
	}

	f.fct = func(ps []float64) float64 {
		var chi2 float64
		for i := range f.X {
			res := f.F(f.X[i], ps) - f.Y[i]
			chi2 += res * res * f.sig2[i]
		}
		return 0.5 * chi2
	}

	f.grad = func(grad, ps []float64) {
		fd.Gradient(grad, f.fct, ps, nil)
	}
}
<commit_msg>fit: prepare for vanity import path<commit_after>// Copyright 2017 The go-hep Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package fit provides functions to fit data.
package fit // import "github.com/go-hep/fit"

import (
	"github.com/gonum/diff/fd"
)

// Func1D describes a 1D function to fit some data.
type Func1D struct {
	// F is the function to minimize.
	// ps is the slice of parameters to optimize during the fit.
	F func(x float64, ps []float64) float64

	// N is the number of parameters to optimize during the fit.
	// If N is 0, Ps must not be nil.
	N int

	// Ps is the initial values for the parameters.
	// If Ps is nil, the set of initial parameters values is a slice of
	// length N filled with zeros.
	Ps []float64

	X   []float64
	Y   []float64
	Err []float64

	sig2 []float64 // inverse of squares of measurement errors along Y.

	fct  func(ps []float64) float64 // cost function (objective function)
	grad func(grad, ps []float64)
}

func (f *Func1D) init() {

	f.sig2 = make([]float64, len(f.Y))
	switch {
	default:
		for i := range f.Y {
			f.sig2[i] = 1
		}
	case f.Err != nil:
		for i, v := range f.Err {
			f.sig2[i] = 1 / (v * v)
		}
	}

	if f.Ps == nil {
		f.Ps = make([]float64, f.N)
	}

	if len(f.Ps) == 0 {
		panic("fit: invalid number of initial parameters")
	}

	if len(f.X) != len(f.Y) {
		panic("fit: mismatch length")
	}

	if len(f.sig2) != len(f.Y) {
		panic("fit: mismatch length")
	}

	f.fct = func(ps []float64) float64 {
		var chi2 float64
		for i := range f.X {
			res := f.F(f.X[i], ps) - f.Y[i]
			chi2 += res * res * f.sig2[i]
		}
		return 0.5 * chi2
	}

	f.grad = func(grad, ps []float64) {
		fd.Gradient(grad, f.fct, ps, nil)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/fatih/color"
	"github.com/urfave/cli"
	"net"
	"os"
	"io"
	"os/signal"
	"syscall"
)

func getLocalAddrs() ([]net.IP, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return nil, err
	}
	var list []net.IP
	for _, addr := range addrs {
		v := addr.(*net.IPNet)
		if v.IP.To4() != nil {
			list = append(list, v.IP)
		}
	}
	return list, nil
}

func fwd(src net.Conn, remote string, proto string) {
	dst, err := net.Dial(proto, remote)
	errHandler(err)
	go func() {
		_, err = io.Copy(src, dst)
		errHandler(err)
	}()
	go func() {
		_, err = io.Copy(dst, src)
		errHandler(err)
	}()
}

func errHandler(err error) {
	if err != nil {
		color.Set(color.FgRed)
		fmt.Fprintf(os.Stderr, "[Error] %s\n", err.Error())
		color.Unset()
		os.Exit(1)
	}
}

func tcpStart(from string, to string) {
	proto := "tcp"

	localAddress, err := net.ResolveTCPAddr(proto, from)
	errHandler(err)

	remoteAddress, err := net.ResolveTCPAddr(proto, to)
	errHandler(err)

	listener, err := net.ListenTCP(proto, localAddress)
	errHandler(err)

	defer listener.Close()

	fmt.Printf("Forwarding %s traffic from '%v' to '%v'\n", proto, localAddress, remoteAddress)
	color.Set(color.FgYellow)
	fmt.Println("<CTRL+C> to exit")
	fmt.Println()
	color.Unset()

	for {
		src, err := listener.Accept()
		errHandler(err)
		fmt.Printf("New connection established from '%v'\n", src.RemoteAddr())
		go fwd(src, to, proto)
	}
}

func udpStart(from string, to string) {
	proto := "udp"

	localAddress, err := net.ResolveUDPAddr(proto, from)
	errHandler(err)

	remoteAddress, err := net.ResolveUDPAddr(proto, to)
	errHandler(err)

	listener, err := net.ListenUDP(proto, localAddress)
	errHandler(err)
	defer listener.Close()

	dst, err := net.DialUDP(proto, nil, remoteAddress)
	errHandler(err)
	defer dst.Close()

	fmt.Printf("Forwarding %s traffic from '%v' to '%v'\n", proto, localAddress, remoteAddress)
	color.Set(color.FgYellow)
	fmt.Println("<CTRL+C> to exit")
	fmt.Println()
	color.Unset()

	buf := make([]byte, 512)
	for {
		rnum, err := listener.Read(buf[0:])
		errHandler(err)

		_, err = dst.Write(buf[:rnum])
		errHandler(err)

		fmt.Printf("%d bytes forwared\n", rnum)
	}
}

func ctrlc() {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		sig := <-sigs
		color.Set(color.FgGreen)
		fmt.Println("\nExecution stopped by", sig)
		color.Unset()
		os.Exit(0)
	}()
}

func main() {
	app := cli.NewApp()
	app.Name = "fwd"
	app.Version = "0.1.1"
	app.Usage = "The little forwarder that could"
	app.UsageText = "fwd --from localhost:2222 --to 192.168.1.254:22"
	app.Copyright = "MIT License"
	app.Authors = []cli.Author{
		cli.Author{
			Name:  "Joel Bastos",
			Email: "kintoandar@gmail.com",
		},
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "from, f",
			Value:  "127.0.0.1:8000",
			EnvVar: "FWD_FROM",
			Usage:  "source HOST:PORT",
		},
		cli.StringFlag{
			Name:   "to, t",
			EnvVar: "FWD_TO",
			Usage:  "destination HOST:PORT",
		},
		cli.BoolFlag{
			Name:  "list, l",
			Usage: "list local addresses",
		},
		cli.BoolFlag{
			Name:  "udp, u",
			Usage: "enable udp forwarding (tcp by default)",
		},
	}
	app.Action = func(c *cli.Context) error {
		defer color.Unset()
		color.Set(color.FgGreen)
		if c.Bool("list") {
			list, err := getLocalAddrs()
			errHandler(err)
			fmt.Println("Available local addresses:")
			color.Unset()
			for _, ip := range list {
				fmt.Println(ip)
			}
			return nil
		} else if c.String("to") == "" {
			color.Unset()
			cli.ShowAppHelp(c)
			return nil
		} else {
			ctrlc()
			if c.Bool("udp") {
				udpStart(c.String("from"), c.String("to"))

			} else {
				tcpStart(c.String("from"), c.String("to"))
			}
			return nil
		}
	}
	app.Run(os.Args)
}
<commit_msg>Fix issue #1<commit_after>package main

import (
	"fmt"
	"github.com/fatih/color"
	"github.com/urfave/cli"
	"net"
	"os"
	"io"
	"os/signal"
	"syscall"
)

func getLocalAddrs() ([]net.IP, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return nil, err
	}
	var list []net.IP
	for _, addr := range addrs {
		v := addr.(*net.IPNet)
		if v.IP.To4() != nil {
			list = append(list, v.IP)
		}
	}
	return list, nil
}

func fwd(src net.Conn, remote string, proto string) {
	dst, err := net.Dial(proto, remote)
	errHandler(err)
	go func() {
		_, err = io.Copy(src, dst)
		errPrinter(err)
	}()
	go func() {
		_, err = io.Copy(dst, src)
		errPrinter(err)
	}()
}

func errHandler(err error) {
	if err != nil {
		color.Set(color.FgRed)
		fmt.Fprintf(os.Stderr, "[Error] %s\n", err.Error())
		color.Unset()
		os.Exit(1)
	}
}

// TODO: merge error handling functions
func errPrinter(err error) {
	if err != nil {
		color.Set(color.FgRed)
		fmt.Fprintf(os.Stderr, "[Error] %s\n", err.Error())
		color.Unset()
	}
}

func tcpStart(from string, to string) {
	proto := "tcp"

	localAddress, err := net.ResolveTCPAddr(proto, from)
	errHandler(err)

	remoteAddress, err := net.ResolveTCPAddr(proto, to)
	errHandler(err)

	listener, err := net.ListenTCP(proto, localAddress)
	errHandler(err)

	defer listener.Close()

	fmt.Printf("Forwarding %s traffic from '%v' to '%v'\n", proto, localAddress, remoteAddress)
	color.Set(color.FgYellow)
	fmt.Println("<CTRL+C> to exit")
	fmt.Println()
	color.Unset()

	for {
		src, err := listener.Accept()
		errHandler(err)
		fmt.Printf("New connection established from '%v'\n", src.RemoteAddr())
		go fwd(src, to, proto)
	}
}

func udpStart(from string, to string) {
	proto := "udp"

	localAddress, err := net.ResolveUDPAddr(proto, from)
	errHandler(err)

	remoteAddress, err := net.ResolveUDPAddr(proto, to)
	errHandler(err)

	listener, err := net.ListenUDP(proto, localAddress)
	errHandler(err)
	defer listener.Close()

	dst, err := net.DialUDP(proto, nil, remoteAddress)
	errHandler(err)
	defer dst.Close()

	fmt.Printf("Forwarding %s traffic from '%v' to '%v'\n", proto, localAddress, remoteAddress)
	color.Set(color.FgYellow)
	fmt.Println("<CTRL+C> to exit")
	fmt.Println()
	color.Unset()

	buf := make([]byte, 512)
	for {
		rnum, err := listener.Read(buf[0:])
		errHandler(err)

		_, err = dst.Write(buf[:rnum])
		errHandler(err)

		fmt.Printf("%d bytes forwared\n", rnum)
	}
}

func ctrlc() {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		sig := <-sigs
		color.Set(color.FgGreen)
		fmt.Println("\nExecution stopped by", sig)
		color.Unset()
		os.Exit(0)
	}()
}

func main() {
	app := cli.NewApp()
	app.Name = "fwd"
	app.Version = "0.1.1"
	app.Usage = "The little forwarder that could"
	app.UsageText = "fwd --from localhost:2222 --to 192.168.1.254:22"
	app.Copyright = "MIT License"
	app.Authors = []cli.Author{
		cli.Author{
			Name:  "Joel Bastos",
			Email: "kintoandar@gmail.com",
		},
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "from, f",
			Value:  "127.0.0.1:8000",
			EnvVar: "FWD_FROM",
			Usage:  "source HOST:PORT",
		},
		cli.StringFlag{
			Name:   "to, t",
			EnvVar: "FWD_TO",
			Usage:  "destination HOST:PORT",
		},
		cli.BoolFlag{
			Name:  "list, l",
			Usage: "list local addresses",
		},
		cli.BoolFlag{
			Name:  "udp, u",
			Usage: "enable udp forwarding (tcp by default)",
		},
	}
	app.Action = func(c *cli.Context) error {
		defer color.Unset()
		color.Set(color.FgGreen)
		if c.Bool("list") {
			list, err := getLocalAddrs()
			errHandler(err)
			fmt.Println("Available local addresses:")
			color.Unset()
			for _, ip := range list {
				fmt.Println(ip)
			}
			return nil
		} else if c.String("to") == "" {
			color.Unset()
			cli.ShowAppHelp(c)
			return nil
		} else {
			ctrlc()
			if c.Bool("udp") {
				udpStart(c.String("from"), c.String("to"))

			} else {
				tcpStart(c.String("from"), c.String("to"))
			}
			return nil
		}
	}
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os/exec"
	"regexp"
)

type Matcher struct {
	re *regexp.Regexp
}

func NewMatcher(expr string) (m *Matcher, err error) {
	m = &Matcher{}
	m.re, err = regexp.Compile(expr)
	if err != nil {
		return nil, err
	}
	return m, nil
}

func (m *Matcher) MatchString(s string) bool {
	return m.re.MatchString(s)
}

type Processor struct {
	cmd *exec.Cmd
}

func NewProcessor(name string, arg ...string) (p *Processor, err error) {
	if _, err = exec.LookPath(name); err != nil {
		return nil, err
	}
	p = &Processor{}
	p.cmd = exec.Command(name, arg...)
	return p, nil
}

func (p *Processor) Process(a []string) error {
	in, err := p.cmd.StdinPipe()
	if err != nil {
		return err
	}
	defer in.Close()

	out, err := p.cmd.StdoutPipe()
	if err != nil {
		return err
	}
	defer out.Close()

	if err = p.cmd.Start(); err != nil {
		return err
	}
	for _, s := range a {
		fmt.Fprintln(in, s)
	}
	if err = in.Close(); err != nil {
		return err
	}

	b := bufio.NewScanner(out)
	for i := 0; i < len(a) && b.Scan(); i++ {
		a[i] = b.Text()
	}
	return b.Err()
}

type Option struct {
	IsHelp  bool
	Pattern string
	Command string
	Arg     []string
	Files   []string
}

func ParseOption(args []string) (opt *Option, err error) {
	opt = &Option{}
	f := flag.NewFlagSet("gdo", flag.ContinueOnError)
	f.SetOutput(ioutil.Discard)

	f.BoolVar(&opt.IsHelp, "h", false, "")
	f.BoolVar(&opt.IsHelp, "help", false, "")
	if err = f.Parse(args); err != nil {
		return nil, err
	}
	switch f.NArg() {
	case 0:
		return nil, fmt.Errorf("no specify PATTERN and COMMAND")
	case 1:
		return nil, fmt.Errorf("no specify COMMAND")
	}
	opt.Pattern = f.Arg(0)
	opt.Command = f.Arg(1)

	var finishArg bool
	for _, arg := range f.Args()[2:] {
		switch {
		case finishArg:
			opt.Files = append(opt.Files, arg)
		case arg == "--":
			finishArg = true
		default:
			opt.Arg = append(opt.Arg, arg)
		}
	}
	return opt, nil
}

type Lines struct {
	lines          []string
	matchedLines   []string
	matchedIndexes map[int]bool
}

func NewLines() *Lines {
	return &Lines{
		lines:          []string{},
		matchedLines:   []string{},
		matchedIndexes: make(map[int]bool),
	}
}

func (l *Lines) LoadLines(r io.Reader, m *Matcher) error {
	b := bufio.NewScanner(r)
	for i := 0; b.Scan(); i++ {
		line := b.Text()
		if m.MatchString(line) {
			l.matchedLines = append(l.matchedLines, line)
			l.matchedIndexes[i] = true
		}
		l.lines = append(l.lines, line)
	}
	return b.Err()
}

func (l *Lines) Flush(out io.Writer, p *Processor) error {
	if err := p.Process(l.matchedLines); err != nil {
		return err
	}
	mi := 0
	for li := 0; li < len(l.lines); li++ {
		if l.matchedIndexes[li] {
			fmt.Fprintln(out, l.matchedLines[mi])
			mi++
		} else {
			fmt.Fprintln(out, l.lines[li])
		}
	}
	return nil
}
<commit_msg>Add fields to Lines<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os/exec"
	"regexp"
)

type Matcher struct {
	re *regexp.Regexp
}

func NewMatcher(expr string) (m *Matcher, err error) {
	m = &Matcher{}
	m.re, err = regexp.Compile(expr)
	if err != nil {
		return nil, err
	}
	return m, nil
}

func (m *Matcher) MatchString(s string) bool {
	return m.re.MatchString(s)
}

type Processor struct {
	cmd *exec.Cmd
}

func NewProcessor(name string, arg ...string) (p *Processor, err error) {
	if _, err = exec.LookPath(name); err != nil {
		return nil, err
	}
	p = &Processor{}
	p.cmd = exec.Command(name, arg...)
	return p, nil
}

func (p *Processor) Process(a []string) error {
	in, err := p.cmd.StdinPipe()
	if err != nil {
		return err
	}
	defer in.Close()

	out, err := p.cmd.StdoutPipe()
	if err != nil {
		return err
	}
	defer out.Close()

	if err = p.cmd.Start(); err != nil {
		return err
	}
	for _, s := range a {
		fmt.Fprintln(in, s)
	}
	if err = in.Close(); err != nil {
		return err
	}

	b := bufio.NewScanner(out)
	for i := 0; i < len(a) && b.Scan(); i++ {
		a[i] = b.Text()
	}
	return b.Err()
}

type Option struct {
	IsHelp  bool
	Pattern string
	Command string
	Arg     []string
	Files   []string
}

func ParseOption(args []string) (opt *Option, err error) {
	opt = &Option{}
	f := flag.NewFlagSet("gdo", flag.ContinueOnError)
	f.SetOutput(ioutil.Discard)

	f.BoolVar(&opt.IsHelp, "h", false, "")
	f.BoolVar(&opt.IsHelp, "help", false, "")
	if err = f.Parse(args); err != nil {
		return nil, err
	}
	switch f.NArg() {
	case 0:
		return nil, fmt.Errorf("no specify PATTERN and COMMAND")
	case 1:
		return nil, fmt.Errorf("no specify COMMAND")
	}
	opt.Pattern = f.Arg(0)
	opt.Command = f.Arg(1)

	var finishArg bool
	for _, arg := range f.Args()[2:] {
		switch {
		case finishArg:
			opt.Files = append(opt.Files, arg)
		case arg == "--":
			finishArg = true
		default:
			opt.Arg = append(opt.Arg, arg)
		}
	}
	return opt, nil
}

type Lines struct {
	matcher        *Matcher
	processor      *Processor
	lines          []string
	matchedLines   []string
	matchedIndexes map[int]bool
}

func NewLines() *Lines {
	return &Lines{
		lines:          []string{},
		matchedLines:   []string{},
		matchedIndexes: make(map[int]bool),
	}
}

func (l *Lines) LoadLines(r io.Reader, m *Matcher) error {
	b := bufio.NewScanner(r)
	for i := 0; b.Scan(); i++ {
		line := b.Text()
		if m.MatchString(line) {
			l.matchedLines = append(l.matchedLines, line)
			l.matchedIndexes[i] = true
		}
		l.lines = append(l.lines, line)
	}
	return b.Err()
}

func (l *Lines) Flush(out io.Writer, p *Processor) error {
	if err := p.Process(l.matchedLines); err != nil {
		return err
	}
	mi := 0
	for li := 0; li < len(l.lines); li++ {
		if l.matchedIndexes[li] {
			fmt.Fprintln(out, l.matchedLines[mi])
			mi++
		} else {
			fmt.Fprintln(out, l.lines[li])
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// +build ignore

package main

import (
	"html/template"
	"log"
	"os"
)

var versioninfoTpl = template.Must(template.New("").Parse(`{
	"FixedFileInfo":
	{
		"FileFlagsMask": "3f",
		"FileFlags ": "00",
		"FileOS": "040004",
		"FileType": "01",
		"FileSubType": "00"
	},
	"StringFileInfo":
	{
		"Comments": "",
		"CompanyName": "",
		"FileDescription": "Refresh icons on Desktop, Start Menu and Taskbar",
		"FileVersion": "{{ .Version }}",
		"InternalName": "",
		"LegalCopyright": "https://github.com/{{ .Repository }}",
		"LegalTrademarks": "",
		"OriginalFilename": "IconsRefresh.exe",
		"PrivateBuild": "",
		"ProductName": "IconsRefresh",
		"ProductVersion": "{{ .Version }}",
		"SpecialBuild": ""
	},
	"VarFileInfo":
	{
		"Translation": {
			"LangID": "0409",
			"CharsetID": "04B0"
		}
	}
}`))

func main() {
	var version, repository string
	var ok bool

	if version, ok = os.LookupEnv("VERSION"); !ok {
		version = "0.0.0.0"
	}
	if repository, ok = os.LookupEnv("GITHUB_REPOSITORY"); !ok {
		repository = "crazy-max/IconsRefresh"
	}

	f, err := os.Create("versioninfo.json")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	err = versioninfoTpl.Execute(f, struct {
		Repository string
		Version    string
	}{
		Repository: repository,
		Version:    version,
	})
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Fix workflow<commit_after>// +build ignore

package main

import (
	"fmt"
	"html/template"
	"log"
	"os"
)

var versioninfoTpl = template.Must(template.New("").Parse(`{
	"FixedFileInfo":
	{
		"FileFlagsMask": "3f",
		"FileFlags ": "00",
		"FileOS": "040004",
		"FileType": "01",
		"FileSubType": "00"
	},
	"StringFileInfo":
	{
		"Comments": "",
		"CompanyName": "",
		"FileDescription": "Refresh icons on Desktop, Start Menu and Taskbar",
		"FileVersion": "{{ .Version }}.0",
		"InternalName": "",
		"LegalCopyright": "https://github.com/{{ .Repository }}",
		"LegalTrademarks": "",
		"OriginalFilename": "IconsRefresh.exe",
		"PrivateBuild": "",
		"ProductName": "IconsRefresh",
		"ProductVersion": "{{ .Version }}.0",
		"SpecialBuild": ""
	},
	"VarFileInfo":
	{
		"Translation": {
			"LangID": "0409",
			"CharsetID": "04B0"
		}
	}
}`))

func main() {
	var version, repository string
	var ok bool

	if version, ok = os.LookupEnv("VERSION"); !ok {
		version = "0.0.0.0"
	}
	fmt.Println("gen.version:", version)
	if repository, ok = os.LookupEnv("GITHUB_REPOSITORY"); !ok {
		repository = "crazy-max/IconsRefresh"
	}
	fmt.Println("gen.repository:", repository)

	f, err := os.Create("versioninfo.json")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	err = versioninfoTpl.Execute(f, struct {
		Repository string
		Version    string
	}{
		Repository: repository,
		Version:    version,
	})
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package ghg

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"

	"github.com/mholt/archiver"
	"github.com/mitchellh/ioprogress"
	"github.com/octokit/go-octokit/octokit"
	"github.com/pkg/errors"
)

func getOctCli(token string) *octokit.Client {
	var auth octokit.AuthMethod
	if token != "" {
		auth = octokit.TokenAuth{AccessToken: token}
	}
	return octokit.NewClient(auth)
}

type ghg struct {
	binDir  string
	target  string
	client  *octokit.Client
	upgrade bool
}

func (gh *ghg) getBinDir() string {
	if gh.binDir != "" {
		return gh.binDir
	}
	return "."
}

var releaseByTagURL = octokit.Hyperlink("repos/{owner}/{repo}/releases/tags/{tag}")
var archiveReg = regexp.MustCompile(`\.(?:zip|tgz|tar\.gz)$`)

func (gh *ghg) get() error {
	owner, repo, tag, err := getOwnerRepoAndTag(gh.target)
	if err != nil {
		return errors.Wrap(err, "failed to resolve target")
	}
	log.Printf("fetch the GitHub release for %s\n", gh.target)
	var url *url.URL
	if tag == "" {
		url, err = octokit.ReleasesLatestURL.Expand(octokit.M{"owner": owner, "repo": repo})
	} else {
		url, err = releaseByTagURL.Expand(octokit.M{"owner": owner, "repo": repo, "tag": tag})
	}
	if err != nil {
		return errors.Wrap(err, "failed to build GitHub URL")
	}
	release, r := gh.client.Releases(url).Latest()
	if r.HasError() {
		return errors.Wrap(r.Err, "failed to fetch a release")
	}
	tag = release.TagName
	goarch := runtime.GOARCH
	goos := runtime.GOOS
	var urls []string
	for _, asset := range release.Assets {
		name := asset.Name
		if strings.Contains(name, goarch) && strings.Contains(name, goos) && archiveReg.MatchString(name) {
			urls = append(urls, fmt.Sprintf("https://github.com/%s/%s/releases/download/%s/%s", owner, repo, tag, name))
		}
	}
	if len(urls) < 1 {
		return fmt.Errorf("no assets available")
	}
	log.Printf("install %s/%s version: %s", owner, repo, tag)
	for _, url := range urls {
		err := gh.install(url)
		if err != nil {
			return err
		}
	}
	return nil
}

func (gh *ghg) install(url string) error {
	log.Printf("download %s\n", url)
	archivePath, err := download(url)
	if err != nil {
		return errors.Wrap(err, "failed to download")
	}
	tmpdir := filepath.Dir(archivePath)
	defer os.RemoveAll(tmpdir)

	workDir := filepath.Join(tmpdir, "work")
	os.MkdirAll(workDir, 0755)

	log.Printf("extract %s\n", path.Base(url))
	err = extract(archivePath, workDir)
	if err != nil {
		return errors.Wrap(err, "failed to extract")
	}

	bin := gh.getBinDir()
	os.MkdirAll(bin, 0755)

	err = gh.pickupExecutable(workDir)
	if err != nil {
		return errors.Wrap(err, "failed to pickup")
	}
	return nil
}

func download(url string) (fpath string, err error) {
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		err = errors.Wrap(err, "failed to create request")
		return
	}
	req.Header.Set("User-Agent", fmt.Sprintf("ghg/%s", version))
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		err = errors.Wrap(err, "failed to create request")
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		err = fmt.Errorf("http response not OK. code: %d, url: %s", resp.StatusCode, url)
		return
	}
	archiveBase := path.Base(url)
	tempdir, err := ioutil.TempDir("", "ghg-")
	if err != nil {
		err = errors.Wrap(err, "failed to create tempdir")
		return
	}
	defer func() {
		if err != nil {
			os.RemoveAll(tempdir)
		}
	}()
	fpath = filepath.Join(tempdir, archiveBase)
	f, err := os.Create(filepath.Join(tempdir, archiveBase))
	if err != nil {
		err = errors.Wrap(err, "failed to open file")
		return
	}
	defer f.Close()
	progressR := progbar(resp.Body, resp.ContentLength)
	_, err = io.Copy(f, progressR)
	if err != nil {
		err = errors.Wrap(err, "failed to read response")
		return
	}
	return fpath, nil
}

func progbar(r io.Reader, size int64) io.Reader {
	bar := ioprogress.DrawTextFormatBar(40)
	f := func(progress, total int64) string {
		return fmt.Sprintf(
			"%s %s",
			bar(progress, total),
			ioprogress.DrawTextFormatBytes(progress, total))
	}
	return &ioprogress.Reader{
		Reader:   r,
		Size:     size,
		DrawFunc: ioprogress.DrawTerminalf(os.Stderr, f),
	}
}

func extract(src, dest string) error {
	base := filepath.Base(src)
	if strings.HasSuffix(base, ".zip") {
		return archiver.Zip.Open(src, dest)
	}
	if strings.HasSuffix(base, ".tar.gz") || strings.HasSuffix(base, ".tgz") {
		return archiver.TarGz.Open(src, dest)
	}
	return fmt.Errorf("failed to extract file: %s", src)
}

var targetReg = regexp.MustCompile(`^(?:([^/]+)/)?([^@]+)(?:@(.+))?$`)

func getOwnerRepoAndTag(target string) (owner, repo, tag string, err error) {
	matches := targetReg.FindStringSubmatch(target)
	if len(matches) != 4 {
		err = fmt.Errorf("failed to get owner, repo and tag")
		return
	}
	owner = matches[1]
	repo = matches[2]
	tag = matches[3]
	if owner == "" {
		owner = repo
	}
	return
}

var executableReg = regexp.MustCompile(`^[a-z][-_a-zA-Z0-9]+(?:\.exe)?$`)

func (gh *ghg) pickupExecutable(src string) error {
	bindir := gh.getBinDir()
	return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() {
			return err
		}
		if name := info.Name(); (info.Mode()&0111) != 0 && executableReg.MatchString(name) {
			dest := filepath.Join(bindir, name)
			if exists(dest) {
				if !gh.upgrade {
					log.Printf("%s already exists. skip installing. You can use -u flag for overwrite it", dest)
					return nil
				}
				log.Printf("%s exists. overwrite it", dest)
			}
			log.Printf("install %s\n", name)
			err := os.Rename(path, dest)
			if err != nil {
				return copyExecutable(path, dest)
			}
		}
		return nil
	})
}

func exists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func copyExecutable(srcName string, destName string) error {
	src, err := os.Open(srcName)
	if err != nil {
		return err
	}
	defer src.Close()

	dest, err := os.Create(destName)
	if err != nil {
		return err
	}
	defer dest.Close()

	_, err = io.Copy(dest, src)
	if err != nil {
		return err
	}

	fileInfo, err := os.Stat(srcName)
	if err != nil {
		return err
	}

	return os.Chmod(destName, fileInfo.Mode())
}
<commit_msg>handle naked binary<commit_after>package ghg

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"

	"github.com/mholt/archiver"
	"github.com/mitchellh/ioprogress"
	"github.com/octokit/go-octokit/octokit"
	"github.com/pkg/errors"
)

func getOctCli(token string) *octokit.Client {
	var auth octokit.AuthMethod
	if token != "" {
		auth = octokit.TokenAuth{AccessToken: token}
	}
	return octokit.NewClient(auth)
}

type ghg struct {
	binDir  string
	target  string
	client  *octokit.Client
	upgrade bool
}

func (gh *ghg) getBinDir() string {
	if gh.binDir != "" {
		return gh.binDir
	}
	return "."
}

var releaseByTagURL = octokit.Hyperlink("repos/{owner}/{repo}/releases/tags/{tag}")

func (gh *ghg) get() error {
	owner, repo, tag, err := getOwnerRepoAndTag(gh.target)
	if err != nil {
		return errors.Wrap(err, "failed to resolve target")
	}
	log.Printf("fetch the GitHub release for %s\n", gh.target)
	var url *url.URL
	if tag == "" {
		url, err = octokit.ReleasesLatestURL.Expand(octokit.M{"owner": owner, "repo": repo})
	} else {
		url, err = releaseByTagURL.Expand(octokit.M{"owner": owner, "repo": repo, "tag": tag})
	}
	if err != nil {
		return errors.Wrap(err, "failed to build GitHub URL")
	}
	release, r := gh.client.Releases(url).Latest()
	if r.HasError() {
		return errors.Wrap(r.Err, "failed to fetch a release")
	}
	tag = release.TagName
	goarch := runtime.GOARCH
	goos := runtime.GOOS
	var urls []string
	for _, asset := range release.Assets {
		name := asset.Name
		if strings.Contains(name, goarch) && strings.Contains(name, goos) {
			urls = append(urls, fmt.Sprintf("https://github.com/%s/%s/releases/download/%s/%s", owner, repo, tag, name))
		}
	}
	if len(urls) < 1 {
		return fmt.Errorf("no assets available")
	}
	log.Printf("install %s/%s version: %s", owner, repo, tag)
	for _, url := range urls {
		err := gh.install(url)
		if err != nil {
			return err
		}
	}
	return nil
}

var archiveReg = regexp.MustCompile(`\.(?:zip|tgz|tar\.gz)$`)

func (gh *ghg) install(url string) error {
	log.Printf("download %s\n", url)
	archivePath, err := download(url)
	if err != nil {
		return errors.Wrap(err, "failed to download")
	}
	tmpdir := filepath.Dir(archivePath)
	defer os.RemoveAll(tmpdir)

	if archiveReg.MatchString(url) {
		workDir := filepath.Join(tmpdir, "work")
		os.MkdirAll(workDir, 0755)

		log.Printf("extract %s\n", path.Base(url))
		err = extract(archivePath, workDir)
		if err != nil {
			return errors.Wrap(err, "failed to extract")
		}

		bin := gh.getBinDir()
		os.MkdirAll(bin, 0755)

		err = gh.pickupExecutable(workDir)
		if err != nil {
			return errors.Wrap(err, "failed to pickup")
		}
	} else {
		_, repo, _, _ := getOwnerRepoAndTag(gh.target)
		name := lcs(repo, filepath.Base(archivePath))
		name = strings.Trim(name, "-_")
		if name == "" {
			name = repo
		}
		dest := filepath.Join(gh.getBinDir(), name)
		if exists(dest) {
			if !gh.upgrade {
				log.Printf("%s already exists. skip installing. You can use -u flag for overwrite it", dest)
				return nil
			}
			log.Printf("%s exists. overwrite it", dest)
		}
		log.Printf("install %s\n", name)
		err := os.Rename(archivePath, dest)
		if err != nil {
			return copyExecutable(archivePath, dest)
		}
	}
	return nil
}

func download(url string) (fpath string, err error) {
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		err = errors.Wrap(err, "failed to create request")
		return
	}
	req.Header.Set("User-Agent", fmt.Sprintf("ghg/%s", version))
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		err = errors.Wrap(err, "failed to create request")
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		err = fmt.Errorf("http response not OK. code: %d, url: %s", resp.StatusCode, url)
		return
	}
	archiveBase := path.Base(url)
	tempdir, err := ioutil.TempDir("", "ghg-")
	if err != nil {
		err = errors.Wrap(err, "failed to create tempdir")
		return
	}
	defer func() {
		if err != nil {
			os.RemoveAll(tempdir)
		}
	}()
	fpath = filepath.Join(tempdir, archiveBase)
	f, err := os.Create(filepath.Join(tempdir, archiveBase))
	if err != nil {
		err = errors.Wrap(err, "failed to open file")
		return
	}
	defer f.Close()
	progressR := progbar(resp.Body, resp.ContentLength)
	_, err = io.Copy(f, progressR)
	if err != nil {
		err = errors.Wrap(err, "failed to read response")
		return
	}
	return fpath, nil
}

func progbar(r io.Reader, size int64) io.Reader {
	bar := ioprogress.DrawTextFormatBar(40)
	f := func(progress, total int64) string {
		return fmt.Sprintf(
			"%s %s",
			bar(progress, total),
			ioprogress.DrawTextFormatBytes(progress, total))
	}
	return &ioprogress.Reader{
		Reader:   r,
		Size:     size,
		DrawFunc: ioprogress.DrawTerminalf(os.Stderr, f),
	}
}

func extract(src, dest string) error {
	base := filepath.Base(src)
	if strings.HasSuffix(base, ".zip") {
		return archiver.Zip.Open(src, dest)
	}
	if strings.HasSuffix(base, ".tar.gz") || strings.HasSuffix(base, ".tgz") {
		return archiver.TarGz.Open(src, dest)
	}
	return fmt.Errorf("failed to extract file: %s", src)
}

var targetReg = regexp.MustCompile(`^(?:([^/]+)/)?([^@]+)(?:@(.+))?$`)

func getOwnerRepoAndTag(target string) (owner, repo, tag string, err error) {
	matches := targetReg.FindStringSubmatch(target)
	if len(matches) != 4 {
		err = fmt.Errorf("failed to get owner, repo and tag")
		return
	}
	owner = matches[1]
	repo = matches[2]
	tag = matches[3]
	if owner == "" {
		owner = repo
	}
	return
}

var executableReg = regexp.MustCompile(`^[a-z][-_a-zA-Z0-9]+(?:\.exe)?$`)

func (gh *ghg) pickupExecutable(src string) error {
	bindir := gh.getBinDir()
	return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() {
			return err
		}
		if name := info.Name(); (info.Mode()&0111) != 0 && executableReg.MatchString(name) {
			dest := filepath.Join(bindir, name)
			if exists(dest) {
				if !gh.upgrade {
					log.Printf("%s already exists. skip installing. You can use -u flag for overwrite it", dest)
					return nil
				}
				log.Printf("%s exists. overwrite it", dest)
			}
			log.Printf("install %s\n", name)
			err := os.Rename(path, dest)
			if err != nil {
				return copyExecutable(path, dest)
			}
		}
		return nil
	})
}

func exists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func copyExecutable(srcName string, destName string) error {
	src, err := os.Open(srcName)
	if err != nil {
		return err
	}
	defer src.Close()

	dest, err := os.Create(destName)
	if err != nil {
		return err
	}
	defer dest.Close()

	_, err = io.Copy(dest, src)
	if err != nil {
		return err
	}

	fileInfo, err := os.Stat(srcName)
	if err != nil {
		return err
	}

	return os.Chmod(destName, fileInfo.Mode())
}

func lcs(a, b string) string {
	arunes := []rune(a)
	brunes := []rune(b)
	aLen := len(arunes)
	bLen := len(brunes)
	lengths := make([][]int, aLen+1)
	for i := 0; i <= aLen; i++ {
		lengths[i] = make([]int, bLen+1)
	}
	// row 0 and column 0 are initialized to 0 already

	for i := 0; i < aLen; i++ {
		for j := 0; j < bLen; j++ {
			if arunes[i] == brunes[j] {
				lengths[i+1][j+1] = lengths[i][j] + 1
			} else if lengths[i+1][j] > lengths[i][j+1] {
				lengths[i+1][j+1] = lengths[i+1][j]
			} else {
				lengths[i+1][j+1] = lengths[i][j+1]
			}
		}
	}

	// read the substring out from the matrix
	s := make([]rune, 0, lengths[aLen][bLen])
	for x, y := aLen, bLen; x != 0 && y != 0; {
		if lengths[x][y] == lengths[x-1][y] {
			x--
		} else if lengths[x][y] == lengths[x][y-1] {
			y--
		} else {
			s = append(s, arunes[x-1])
			x--
			y--
		}
	}
	// reverse string
	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
		s[i], s[j] = s[j], s[i]
	}
	return string(s)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"runtime"

	"github.com/fatih/color"
)

func main() {

	// test flags
	var (
		compile            bool
		codegen            bool
		optimize           bool
		optimizeStandalone bool

		clean bool

		invertFlags bool
	)
	test := flag.NewFlagSet("test", flag.ExitOnError)
	test.BoolVar(&clean, "clean", false,
		"Clean out the output directories before running tests")

	test.BoolVar(&codegen, "codegen", false,
		"Generate asm but DON'T optimize")
	test.BoolVar(&compile, "compile", false,
		"Generate asm and optimize")
	test.BoolVar(&optimize, "optimize", false,
		"Optimize asm from -codegen.\n"+
			"\tDifferent to -compile:\n"+
			"\tCode is read back in, after being written to a file")
	test.BoolVar(&optimizeStandalone, "optimizeStandalone", false,
		"Optimize asm written explicitly for testing")

	test.BoolVar(&invertFlags, "invert", false,
		"Inverts all flags, making them subtractive instead of additive")

	// view flags
	var (
		diff  bool
		run   bool
		asm   bool
		build bool
	)
	view := flag.NewFlagSet("view", flag.ExitOnError)
	view.BoolVar(&diff, "diff", false,
		"view as a diff, instead of result and expectation separately")
	view.BoolVar(&run, "run", false,
		"compare results of the run phase of testing")
	view.BoolVar(&asm, "asm", false,
		"compare asm generated by building phase of testing")
	view.BoolVar(&build, "build", false,
		"compare results of the build phase of testing")

	// accept flags
	var all bool
	accept := flag.NewFlagSet("accept", flag.ExitOnError)
	accept.BoolVar(&all, "all", false,
		"accept every test result run in the last testing round")

	if len(os.Args) == 1 {
		helpMessage()
		os.Exit(0)
	}

	command := os.Args[1]
	switch command {
	case "test":
		test.Parse(os.Args[2:])
		testCommand(
			clean,
			codegen, optimize,
			compile,
			optimizeStandalone,
			invertFlags)
	case "view":
		view.Parse(os.Args[2:])
	case "create":
		// do creation tasks
	case "accept":
		accept.Parse(os.Args[2:])
	case "init":
		initDirs()
	case "help":
		fallthrough
	case "-help":
		fallthrough
	case "--help":
		helpMessage()
		os.Exit(0)
	default:
		color.Red("invalid command " + command)
		helpMessage()
		os.Exit(1)
	}

	os.Exit(0)
}
func createCommand(testName string) {

}

func testCommand(clean, codegen, optimize, compile, optimizeStandalone, invertFlags bool) {
	if invertFlags {
		compile = !compile
		codegen = !codegen
		optimize = !optimize
		optimizeStandalone = !optimizeStandalone

		clean = !clean
	}

	cores := runtime.NumCPU()
	runtime.GOMAXPROCS(cores + 1)

	if clean {
		fmt.Print("CLEANING... ")
		cleanDirs()
		fmt.Println(" done")
	}
	if codegen {
		color.Cyan("GENERATING CODE...")
		batchCodeGen(cores)
		color.Yellow("RUNNING...")
		batchRunUnoptimized(cores)
	}
	if optimize {
		color.Cyan("OPTIMIZING...")
		batchOptimize(cores)
		color.Yellow("RUNNING...")
		batchRunOptimized(cores)
	}
	if compile {
		color.Cyan("COMPILING...")
		batchCompile(cores)
		color.Yellow("RUNNING...")
		batchRunCompiled(cores)
	}
	if optimizeStandalone {
		color.Cyan("OPTIMIZING STANDALONE ASM...")
		batchOptimizeStandalone(cores)
		color.YellowString("RUNNING...")
		batchRunOptimizedStandalone(cores)
	}
}
<commit_msg>change format of feedback and fix bug in optimizeStandalone feedback<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"runtime"

	"github.com/fatih/color"
)

func main() {

	// test flags
	var (
		compile            bool
		codegen            bool
		optimize           bool
		optimizeStandalone bool

		clean bool

		invertFlags bool
	)
	test := flag.NewFlagSet("test", flag.ExitOnError)
	test.BoolVar(&clean, "clean", false,
		"Clean out the output directories before running tests")

	test.BoolVar(&codegen, "codegen", false,
		"Generate asm but DON'T optimize")
	test.BoolVar(&compile, "compile", false,
		"Generate asm and optimize")
	test.BoolVar(&optimize, "optimize", false,
		"Optimize asm from -codegen.\n"+
			"\tDifferent to -compile:\n"+
			"\tCode is read back in, after being written to a file")
	test.BoolVar(&optimizeStandalone, "optimizeStandalone", false,
		"Optimize asm written explicitly for testing")

	test.BoolVar(&invertFlags, "invert", false,
		"Inverts all flags, making them subtractive instead of additive")

	// view flags
	var (
		diff  bool
		run   bool
		asm   bool
		build bool
	)
	view := flag.NewFlagSet("view", flag.ExitOnError)
	view.BoolVar(&diff, "diff", false,
		"view as a diff, instead of result and expectation separately")
	view.BoolVar(&run, "run", false,
		"compare results of the run phase of testing")
	view.BoolVar(&asm, "asm", false,
		"compare asm generated by building phase of testing")
	view.BoolVar(&build, "build", false,
		"compare results of the build phase of testing")

	// accept flags
	var all bool
	accept := flag.NewFlagSet("accept", flag.ExitOnError)
	accept.BoolVar(&all, "all", false,
		"accept every test result run in the last testing round")

	if len(os.Args) == 1 {
		helpMessage()
		os.Exit(0)
	}

	command := os.Args[1]
	switch command {
	case "test":
		test.Parse(os.Args[2:])
		testCommand(
			clean,
			codegen, optimize,
			compile,
			optimizeStandalone,
			invertFlags)
	case "view":
		view.Parse(os.Args[2:])
	case "create":
		// do creation tasks
	case "accept":
		accept.Parse(os.Args[2:])
	case "init":
		initDirs()
	case "help":
		fallthrough
	case "-help":
		fallthrough
	case "--help":
		helpMessage()
		os.Exit(0)
	default:
		color.Red("invalid command " + command)
		helpMessage()
		os.Exit(1)
	}

	os.Exit(0)
}
func createCommand(testName string) {

}

func testCommand(clean, codegen, optimize, compile, optimizeStandalone, invertFlags bool) {
	if invertFlags {
		compile = !compile
		codegen = !codegen
		optimize = !optimize
		optimizeStandalone = !optimizeStandalone

		clean = !clean
	}

	cores := runtime.NumCPU()
	runtime.GOMAXPROCS(cores + 1)

	if clean {
		fmt.Print("CLEANING...")
		cleanDirs()
		fmt.Println(" done")
	}
	if codegen {
		color.Cyan("GENERATING CODE...")
		color.Yellow("building...")
		batchCodeGen(cores)
		color.Yellow("running...")
		batchRunUnoptimized(cores)
	}
	if optimize {
		color.Cyan("OPTIMIZING...")
		color.Yellow("building...")
		batchOptimize(cores)
		color.Yellow("running...")
		batchRunOptimized(cores)
	}
	if compile {
		color.Cyan("COMPILING...")
		color.Yellow("building...")
		batchCompile(cores)
		color.Yellow("running...")
		batchRunCompiled(cores)
	}
	if optimizeStandalone {
		color.Cyan("OPTIMIZING STANDALONE ASM...")
		color.Yellow("building...")
		batchOptimizeStandalone(cores)
		color.Yellow("running...")
		batchRunOptimizedStandalone(cores)
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// 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
//
//     https://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 main

import (
	"context"
	"crypto/tls"
	"log"
	"strconv"
	"strings"
	"sync"
	"time"

	irc "github.com/fluffle/goirc/client"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
)

const (
	pingFrequencySecs          = 60
	connectionTimeoutSecs      = 30
	nickservWaitSecs           = 10
	ircConnectMaxBackoffSecs   = 300
	ircConnectBackoffResetSecs = 1800
)

var (
	ircConnectedGauge = promauto.NewGauge(prometheus.GaugeOpts{
		Name: "irc_connected",
		Help: "Whether the IRC connection is established",
	})
	ircSentMsgs = promauto.NewCounterVec(prometheus.CounterOpts{
		Name: "irc_sent_msgs",
		Help: "Number of IRC messages sent"},
		[]string{"ircchannel"},
	)
	ircSendMsgErrors = promauto.NewCounterVec(prometheus.CounterOpts{
		Name: "irc_send_msg_errors",
		Help: "Errors while sending IRC messages"},
		[]string{"ircchannel", "error"},
	)
)

func loggerHandler(_ *irc.Conn, line *irc.Line) {
	log.Printf("Received: '%s'", line.Raw)
}

type IRCNotifier struct {
	// Nick stores the nickname specified in the config, because irc.Client
	// might change its copy.
	Nick         string
	NickPassword string
	Client       *irc.Conn
	AlertMsgs    chan AlertMsg

	stopCtx context.Context
	stopWg  *sync.WaitGroup

	// irc.Conn has a Connected() method that can tell us wether the TCP
	// connection is up, and thus if we should trigger connect/disconnect.
	// We need to track the session establishment also at a higher level to
	// understand when the server has accepted us and thus when we can join
	// channels, send notices, etc.
	sessionUp         bool
	sessionUpSignal   chan bool
	sessionDownSignal chan bool

	channelReconciler *ChannelReconciler

	UsePrivmsg bool

	NickservDelayWait time.Duration
	BackoffCounter    Delayer
}

func NewIRCNotifier(stopCtx context.Context, stopWg *sync.WaitGroup, config *Config, alertMsgs chan AlertMsg, delayerMaker DelayerMaker) (*IRCNotifier, error) {

	ircConfig := irc.NewConfig(config.IRCNick)
	ircConfig.Me.Ident = config.IRCNick
	ircConfig.Me.Name = config.IRCRealName
	ircConfig.Server = strings.Join(
		[]string{config.IRCHost, strconv.Itoa(config.IRCPort)}, ":")
	ircConfig.Pass = config.IRCHostPass
	ircConfig.SSL = config.IRCUseSSL
	ircConfig.SSLConfig = &tls.Config{
		ServerName:         config.IRCHost,
		InsecureSkipVerify: !config.IRCVerifySSL,
	}
	ircConfig.PingFreq = pingFrequencySecs * time.Second
	ircConfig.Timeout = connectionTimeoutSecs * time.Second
	ircConfig.NewNick = func(n string) string { return n + "^" }

	client := irc.Client(ircConfig)

	backoffCounter := delayerMaker.NewDelayer(
		ircConnectMaxBackoffSecs, ircConnectBackoffResetSecs,
		time.Second)

	channelReconciler := NewChannelReconciler(config, client, delayerMaker)

	notifier := &IRCNotifier{
		Nick:              config.IRCNick,
		NickPassword:      config.IRCNickPass,
		Client:            client,
		AlertMsgs:         alertMsgs,
		stopCtx:           stopCtx,
		stopWg:            stopWg,
		sessionUpSignal:   make(chan bool),
		sessionDownSignal: make(chan bool),
		channelReconciler: channelReconciler,
		UsePrivmsg:        config.UsePrivmsg,
		NickservDelayWait: nickservWaitSecs * time.Second,
		BackoffCounter:    backoffCounter,
	}

	notifier.registerHandlers()

	return notifier, nil
}

func (n *IRCNotifier) registerHandlers() {
	n.Client.HandleFunc(irc.CONNECTED,
		func(*irc.Conn, *irc.Line) {
			log.Printf("Session established")
			n.sessionUpSignal <- true
		})

	n.Client.HandleFunc(irc.DISCONNECTED,
		func(*irc.Conn, *irc.Line) {
			log.Printf("Disconnected from IRC")
			n.sessionDownSignal <- false
		})

	for _, event := range []string{irc.NOTICE, "433"} {
		n.Client.HandleFunc(event, loggerHandler)
	}
}

func (n *IRCNotifier) MaybeIdentifyNick() {
	if n.NickPassword == "" {
		return
	}

	// Very lazy/optimistic, but this is good enough for my irssi config,
	// so it should work here as well.
	currentNick := n.Client.Me().Nick
	if currentNick != n.Nick {
		log.Printf("My nick is '%s', sending GHOST to NickServ to get '%s'",
			currentNick, n.Nick)
		n.Client.Privmsgf("NickServ", "GHOST %s %s", n.Nick,
			n.NickPassword)
		time.Sleep(n.NickservDelayWait)

		log.Printf("Changing nick to '%s'", n.Nick)
		n.Client.Nick(n.Nick)
	}
	log.Printf("Sending IDENTIFY to NickServ")
	n.Client.Privmsgf("NickServ", "IDENTIFY %s", n.NickPassword)
	time.Sleep(n.NickservDelayWait)
}

func (n *IRCNotifier) MaybeSendAlertMsg(alertMsg *AlertMsg) {
	if !n.sessionUp {
		log.Printf("Cannot send alert to %s : IRC not connected",
			alertMsg.Channel)
		ircSendMsgErrors.WithLabelValues(alertMsg.Channel, "not_connected").Inc()
		return
	}
	n.channelReconciler.JoinChannel(&IRCChannel{Name: alertMsg.Channel})

	if n.UsePrivmsg {
		n.Client.Privmsg(alertMsg.Channel, alertMsg.Alert)
	} else {
		n.Client.Notice(alertMsg.Channel, alertMsg.Alert)
	}
	ircSentMsgs.WithLabelValues(alertMsg.Channel).Inc()
}

func (n *IRCNotifier) ShutdownPhase() {
	if n.Client.Connected() {
		log.Printf("IRC client connected, quitting")
		n.Client.Quit("see ya")

		if n.sessionUp {
			log.Printf("Session is up, wait for IRC disconnect to complete")
			select {
			case <-n.sessionDownSignal:
			case <-time.After(n.Client.Config().Timeout):
				log.Printf("Timeout while waiting for IRC disconnect to complete, stopping anyway")
			}
		}
	}
}

func (n *IRCNotifier) ConnectedPhase() {
	select {
	case alertMsg := <-n.AlertMsgs:
		n.MaybeSendAlertMsg(&alertMsg)
	case <-n.sessionDownSignal:
		n.sessionUp = false
		n.channelReconciler.CleanupChannels()
		n.Client.Quit("see ya")
		ircConnectedGauge.Set(0)
	case <-n.stopCtx.Done():
		log.Printf("IRC routine asked to terminate")
	}
}

func (n *IRCNotifier) SetupPhase() {
	if !n.Client.Connected() {
		log.Printf("Connecting to IRC %s", n.Client.Config().Server)
		if ok := n.BackoffCounter.DelayContext(n.stopCtx); !ok {
			return
		}
		if err := n.Client.ConnectContext(n.stopCtx); err != nil {
			log.Printf("Could not connect to IRC: %s", err)
			return
		}
		log.Printf("Connected to IRC server, waiting to establish session")
	}
	select {
	case <-n.sessionUpSignal:
		n.sessionUp = true
		n.MaybeIdentifyNick()
		n.channelReconciler.JoinChannels()
		ircConnectedGauge.Set(1)
	case <-n.sessionDownSignal:
		log.Printf("Receiving a session down before the session is up, this is odd")
	case <-n.stopCtx.Done():
		log.Printf("IRC routine asked to terminate")
	}
}

func (n *IRCNotifier) Run() {
	defer n.stopWg.Done()

	for n.stopCtx.Err() != context.Canceled {
		if !n.sessionUp {
			n.SetupPhase()
		} else {
			n.ConnectedPhase()
		}
	}
	n.ShutdownPhase()
}
<commit_msg>refactor: separate func for goirc config creation<commit_after>// Copyright 2018 Google LLC
//
// 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
//
//     https://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 main

import (
	"context"
	"crypto/tls"
	"log"
	"strconv"
	"strings"
	"sync"
	"time"

	irc "github.com/fluffle/goirc/client"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
)

const (
	pingFrequencySecs          = 60
	connectionTimeoutSecs      = 30
	nickservWaitSecs           = 10
	ircConnectMaxBackoffSecs   = 300
	ircConnectBackoffResetSecs = 1800
)

var (
	ircConnectedGauge = promauto.NewGauge(prometheus.GaugeOpts{
		Name: "irc_connected",
		Help: "Whether the IRC connection is established",
	})
	ircSentMsgs = promauto.NewCounterVec(prometheus.CounterOpts{
		Name: "irc_sent_msgs",
		Help: "Number of IRC messages sent"},
		[]string{"ircchannel"},
	)
	ircSendMsgErrors = promauto.NewCounterVec(prometheus.CounterOpts{
		Name: "irc_send_msg_errors",
		Help: "Errors while sending IRC messages"},
		[]string{"ircchannel", "error"},
	)
)

func loggerHandler(_ *irc.Conn, line *irc.Line) {
	log.Printf("Received: '%s'", line.Raw)
}

func makeGOIRCConfig(config *Config) *irc.Config {
	ircConfig := irc.NewConfig(config.IRCNick)
	ircConfig.Me.Ident = config.IRCNick
	ircConfig.Me.Name = config.IRCRealName
	ircConfig.Server = strings.Join(
		[]string{config.IRCHost, strconv.Itoa(config.IRCPort)}, ":")
	ircConfig.Pass = config.IRCHostPass
	ircConfig.SSL = config.IRCUseSSL
	ircConfig.SSLConfig = &tls.Config{
		ServerName:         config.IRCHost,
		InsecureSkipVerify: !config.IRCVerifySSL,
	}
	ircConfig.PingFreq = pingFrequencySecs * time.Second
	ircConfig.Timeout = connectionTimeoutSecs * time.Second
	ircConfig.NewNick = func(n string) string { return n + "^" }

	return ircConfig
}

type IRCNotifier struct {
	// Nick stores the nickname specified in the config, because irc.Client
	// might change its copy.
	Nick         string
	NickPassword string
	Client       *irc.Conn
	AlertMsgs    chan AlertMsg

	stopCtx context.Context
	stopWg  *sync.WaitGroup

	// irc.Conn has a Connected() method that can tell us wether the TCP
	// connection is up, and thus if we should trigger connect/disconnect.
	// We need to track the session establishment also at a higher level to
	// understand when the server has accepted us and thus when we can join
	// channels, send notices, etc.
	sessionUp         bool
	sessionUpSignal   chan bool
	sessionDownSignal chan bool

	channelReconciler *ChannelReconciler

	UsePrivmsg bool

	NickservDelayWait time.Duration
	BackoffCounter    Delayer
}

func NewIRCNotifier(stopCtx context.Context, stopWg *sync.WaitGroup, config *Config, alertMsgs chan AlertMsg, delayerMaker DelayerMaker) (*IRCNotifier, error) {

	ircConfig := makeGOIRCConfig(config)

	client := irc.Client(ircConfig)

	backoffCounter := delayerMaker.NewDelayer(
		ircConnectMaxBackoffSecs, ircConnectBackoffResetSecs,
		time.Second)

	channelReconciler := NewChannelReconciler(config, client, delayerMaker)

	notifier := &IRCNotifier{
		Nick:              config.IRCNick,
		NickPassword:      config.IRCNickPass,
		Client:            client,
		AlertMsgs:         alertMsgs,
		stopCtx:           stopCtx,
		stopWg:            stopWg,
		sessionUpSignal:   make(chan bool),
		sessionDownSignal: make(chan bool),
		channelReconciler: channelReconciler,
		UsePrivmsg:        config.UsePrivmsg,
		NickservDelayWait: nickservWaitSecs * time.Second,
		BackoffCounter:    backoffCounter,
	}

	notifier.registerHandlers()

	return notifier, nil
}

func (n *IRCNotifier) registerHandlers() {
	n.Client.HandleFunc(irc.CONNECTED,
		func(*irc.Conn, *irc.Line) {
			log.Printf("Session established")
			n.sessionUpSignal <- true
		})

	n.Client.HandleFunc(irc.DISCONNECTED,
		func(*irc.Conn, *irc.Line) {
			log.Printf("Disconnected from IRC")
			n.sessionDownSignal <- false
		})

	for _, event := range []string{irc.NOTICE, "433"} {
		n.Client.HandleFunc(event, loggerHandler)
	}
}

func (n *IRCNotifier) MaybeIdentifyNick() {
	if n.NickPassword == "" {
		return
	}

	// Very lazy/optimistic, but this is good enough for my irssi config,
	// so it should work here as well.
	currentNick := n.Client.Me().Nick
	if currentNick != n.Nick {
		log.Printf("My nick is '%s', sending GHOST to NickServ to get '%s'",
			currentNick, n.Nick)
		n.Client.Privmsgf("NickServ", "GHOST %s %s", n.Nick,
			n.NickPassword)
		time.Sleep(n.NickservDelayWait)

		log.Printf("Changing nick to '%s'", n.Nick)
		n.Client.Nick(n.Nick)
	}
	log.Printf("Sending IDENTIFY to NickServ")
	n.Client.Privmsgf("NickServ", "IDENTIFY %s", n.NickPassword)
	time.Sleep(n.NickservDelayWait)
}

func (n *IRCNotifier) MaybeSendAlertMsg(alertMsg *AlertMsg) {
	if !n.sessionUp {
		log.Printf("Cannot send alert to %s : IRC not connected",
			alertMsg.Channel)
		ircSendMsgErrors.WithLabelValues(alertMsg.Channel, "not_connected").Inc()
		return
	}
	n.channelReconciler.JoinChannel(&IRCChannel{Name: alertMsg.Channel})

	if n.UsePrivmsg {
		n.Client.Privmsg(alertMsg.Channel, alertMsg.Alert)
	} else {
		n.Client.Notice(alertMsg.Channel, alertMsg.Alert)
	}
	ircSentMsgs.WithLabelValues(alertMsg.Channel).Inc()
}

func (n *IRCNotifier) ShutdownPhase() {
	if n.Client.Connected() {
		log.Printf("IRC client connected, quitting")
		n.Client.Quit("see ya")

		if n.sessionUp {
			log.Printf("Session is up, wait for IRC disconnect to complete")
			select {
			case <-n.sessionDownSignal:
			case <-time.After(n.Client.Config().Timeout):
				log.Printf("Timeout while waiting for IRC disconnect to complete, stopping anyway")
			}
		}
	}
}

func (n *IRCNotifier) ConnectedPhase() {
	select {
	case alertMsg := <-n.AlertMsgs:
		n.MaybeSendAlertMsg(&alertMsg)
	case <-n.sessionDownSignal:
		n.sessionUp = false
		n.channelReconciler.CleanupChannels()
		n.Client.Quit("see ya")
		ircConnectedGauge.Set(0)
	case <-n.stopCtx.Done():
		log.Printf("IRC routine asked to terminate")
	}
}

func (n *IRCNotifier) SetupPhase() {
	if !n.Client.Connected() {
		log.Printf("Connecting to IRC %s", n.Client.Config().Server)
		if ok := n.BackoffCounter.DelayContext(n.stopCtx); !ok {
			return
		}
		if err := n.Client.ConnectContext(n.stopCtx); err != nil {
			log.Printf("Could not connect to IRC: %s", err)
			return
		}
		log.Printf("Connected to IRC server, waiting to establish session")
	}
	select {
	case <-n.sessionUpSignal:
		n.sessionUp = true
		n.MaybeIdentifyNick()
		n.channelReconciler.JoinChannels()
		ircConnectedGauge.Set(1)
	case <-n.sessionDownSignal:
		log.Printf("Receiving a session down before the session is up, this is odd")
	case <-n.stopCtx.Done():
		log.Printf("IRC routine asked to terminate")
	}
}

func (n *IRCNotifier) Run() {
	defer n.stopWg.Done()

	for n.stopCtx.Err() != context.Canceled {
		if !n.sessionUp {
			n.SetupPhase()
		} else {
			n.ConnectedPhase()
		}
	}
	n.ShutdownPhase()
}
<|endoftext|>