<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

package hawk

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"text/scanner"
)

type Algorithm string

func (a *Algorithm) Scan(src interface{}) error {
	*a = "sha256"
	return nil
}

const (
	SHA256Algorithm  Algorithm = "sha256"
	DefaultAlgorithm           = SHA256Algorithm
)

type Authenticator struct {
	credentialsStore CredentialsStore
	replayChecker    ReplayChecker
}

func NewAuthenticator(credentialsStore CredentialsStore, replayChecker ReplayChecker) *Authenticator {
	return &Authenticator{
		credentialsStore: credentialsStore,
		replayChecker:    replayChecker,
	}
}

type Key struct {
	Identifier string
	Secret     []byte
	Algorithm  Algorithm
}

var MalformedParametersErr = errors.New("Malformed Parameters")
var MalformedCredentialsErr = errors.New("Malformed Credentials")

//
type Parameters struct {
	Id        string
	Timestamp int64
	Nonce     string
	Ext       string
	Mac       []byte
	Hash      []byte
}

func parseString(s string) string {
	return strings.Trim(s, `"`) // TODO: We really need to find out how strings in Hawk are encoded, maybe we need to deal with things like \t ?
}

func parseParameters(src string) (Parameters, error) {
	items := make(map[string]string)

	s := scanner.Scanner{Mode: scanner.ScanIdents | scanner.ScanChars | scanner.ScanStrings}
	s.Init(strings.NewReader(src))

	for {
		tok := s.Scan()
		if tok != scanner.Ident {
			return Parameters{}, MalformedParametersErr
		}
		name := s.TokenText()

		tok = s.Scan()
		if tok != '=' {
			return Parameters{}, MalformedParametersErr
		}

		tok = s.Scan()
		if tok != scanner.String {
			return Parameters{}, MalformedParametersErr
		}
		value := s.TokenText()

		items[name] = parseString(value)

		tok = s.Scan()
		if tok == scanner.EOF {
			break
		}
		if tok != ',' {
			return Parameters{}, MalformedParametersErr
		}
	}

	// Now parse the items and setup a Parameters struct

	timestamp, err := strconv.ParseInt(items["ts"], 10, 64)
	if err != nil {
		return Parameters{}, err
	}

	decodedHash, err := base64.StdEncoding.DecodeString(items["hash"])
	if err != nil {
		return Parameters{}, err
	}

	decodedMac, err := base64.StdEncoding.DecodeString(items["mac"])
	if err != nil {
		return Parameters{}, err
	}

	return Parameters{
		Id:        items["id"],
		Timestamp: timestamp,
		Nonce:     items["nonce"],
		Ext:       items["ext"],
		Hash:      decodedHash,
		Mac:       decodedMac,
	}, nil
}

func validateParameters(parameters Parameters) error {
	if parameters.Id == "" {
		return MalformedParametersErr
	}
	if parameters.Timestamp == 0 {
		return MalformedParametersErr
	}
	if parameters.Nonce == "" {
		return MalformedParametersErr
	}
	if len(parameters.Mac) == 0 {
		return MalformedParametersErr
	}
	return nil
}

// Make sure the Credentials return a valid key
func validateCredentials(credentials Credentials) error {
	if credentials.Key().Identifier == "" || len(credentials.Key().Identifier) == 0 {
		return MalformedCredentialsErr
	}
	if credentials.Key().Algorithm != DefaultAlgorithm {
		return MalformedCredentialsErr
	}
	return nil
}

func GetRequestPath(r *http.Request) string {
	path := r.URL.Path
	if len(r.URL.RawQuery) != 0 {
		path = path + "?" + r.URL.RawQuery
	}
	if len(r.URL.Fragment) != 0 {
		path = path + "#" + r.URL.Fragment
	}
	return path
}

// TODO: Make sure the following two do the right thing when behind a proxy

func GetRequestHost(r *http.Request) string {
	hostPort := strings.Split(r.Host, ":")
	return hostPort[0]
}

func GetRequestPort(r *http.Request) int {
	host := r.Host
	if len(r.Header["X-Forwarded-Host"]) != 0 {
		host = r.Header["X-Forwarded-Host"][0]
	}
	hostPort := strings.Split(host, ":")
	if len(hostPort) == 2 {
		port, _ := strconv.Atoi(hostPort[1])
		return port
	} else {
		switch r.URL.Scheme {
		case "http":
			return 80
		case "https":
			return 443
		}
	}
	return 0
}

func GetRequestContentType(r *http.Request) string {
	return r.Header.Get("Content-Type")
}

func CalculatePayloadHash(r *http.Request) ([]byte, error) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}

	r.Body = NewClosingBytesReader(body)

	hash := sha256.New()
	hash.Write([]byte("hawk.1.payload\n"))
	hash.Write([]byte(GetRequestContentType(r)))
	hash.Write([]byte("\n"))
	hash.Write(body)
	hash.Write([]byte("\n"))
	return hash.Sum(nil), nil
}

func CalculateRequestSignature(r *http.Request, parameters Parameters, credentials Credentials) ([]byte, error) {
	var encodedPayloadHash string
	if len(parameters.Hash) != 0 {
		payloadHash, err := CalculatePayloadHash(r)
		if err != nil {
			return nil, err
		}
		encodedPayloadHash = base64.StdEncoding.EncodeToString(payloadHash)
	}

	parts := []string{
		"hawk.1.header",
		strconv.FormatInt(parameters.Timestamp, 10),
		parameters.Nonce,
		r.Method,
		GetRequestPath(r),
		GetRequestHost(r),
		strconv.Itoa(GetRequestPort(r)),
		encodedPayloadHash,
		parameters.Ext,
	}

	requestHeader := strings.Join(parts, "\n") + "\n"

	mac := hmac.New(sha256.New, credentials.Key().Secret)
	mac.Write([]byte(requestHeader))
	return mac.Sum(nil), nil
}

// CreateRequestHeader makes a Hawk authentication string that can be
// added to an http.Request
func CreateRequestHeader(r *http.Request, c Credentials, id string, timestamp int64, nonce string, ext string) (string, error) {

	p := Parameters{
		Id:        id,
		Timestamp: timestamp,
		Nonce:     nonce,
		Ext:       ext,
	}

	var mac, hash string

	if r.Body != nil {
		hashB, err := CalculatePayloadHash(r)
		if err != nil {
			return "", err
		}
		p.Hash = hashB
		hash = base64.StdEncoding.EncodeToString(hashB)
	}

	macB, err := CalculateRequestSignature(r, p, c)
	if err != nil {
		return "", err
	}

	mac = base64.StdEncoding.EncodeToString(macB)

	header := fmt.Sprintf(`Hawk id="%s", ts="%d", nonce="%s", ext="%s", mac="%s"`,
		id,
		timestamp,
		nonce,
		ext,
		mac,
	)

	if hash != "" {
		header = fmt.Sprintf(`%s, hash="%s"`, header, hash)
	}

	return header, nil
}

func (a *Authenticator) Authenticate(w http.ResponseWriter, r *http.Request) (Credentials, bool) {
	// Grab the Authorization Header

	authorization := r.Header.Get("Authorization")
	if len(authorization) == 0 {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return nil, false
	}

	tokens := strings.SplitN(authorization, " ", 2)
	if len(tokens) != 2 {
		http.Error(w, "Unsupported authorization method", http.StatusUnauthorized)
		return nil, false
	}
	if tokens[0] != "Hawk" {
		http.Error(w, "Unsupported authorization method", http.StatusUnauthorized)
		return nil, false
	}

	// Parse and validate the Hawk parameters

	parameters, err := parseParameters(tokens[1])
	if err != nil {
		http.Error(w, "Unable to parse Hawk parameters", http.StatusUnauthorized)
		return nil, false
	}

	if err = validateParameters(parameters); err != nil {
		http.Error(w, "Invalid Hawk parameters: "+err.Error(), http.StatusUnauthorized)
		return nil, false
	}

	// TODO: Check if this request has expired

	// Check if we have seen this request before

	requestId := fmt.Sprintf("%s:%d:%s", parameters.Timestamp, parameters.Id, parameters.Nonce)

	seenBefore, err := a.replayChecker.Check(requestId)
	if err != nil {
		// TODO: Unable to check means server error. Is there a better strategy?
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return nil, false
	}

	if seenBefore {
		http.Error(w, "Request has been seen before", http.StatusUnauthorized)
		return nil, false
	}

	if err := a.replayChecker.Remember(requestId); err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return nil, false
	}

	// Find the user and keys

	credentials, err := a.credentialsStore.CredentialsForKeyIdentifier(parameters.Id)
	if err != nil {
		http.Error(w, "Internal server error:"+err.Error(), http.StatusInternalServerError)
		return nil, false
	}
	if credentials == nil {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return nil, false
	}

	if err := validateCredentials(credentials); err != nil {
		http.Error(w, "Invalid credentials: "+err.Error(), http.StatusUnauthorized)
		return nil, false
	}

	// Check the Hawk request signature

	mac, err := CalculateRequestSignature(r, parameters, credentials)
	if err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return nil, false
	}

	if !bytes.Equal(mac, parameters.Mac) {
		http.Error(w, "Signature Mismatch", http.StatusUnauthorized)
		return nil, false
	}

	// Return the credentials and parsed artifacts

	return credentials, true
}
<commit_msg>add debugging<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

package hawk

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"text/scanner"

	. "github.com/mostlygeek/go-debug"
)

var debug = Debug("hawk")

type Algorithm string

func (a *Algorithm) Scan(src interface{}) error {
	*a = "sha256"
	return nil
}

const (
	SHA256Algorithm  Algorithm = "sha256"
	DefaultAlgorithm           = SHA256Algorithm
)

type Authenticator struct {
	credentialsStore CredentialsStore
	replayChecker    ReplayChecker
}

func NewAuthenticator(credentialsStore CredentialsStore, replayChecker ReplayChecker) *Authenticator {
	return &Authenticator{
		credentialsStore: credentialsStore,
		replayChecker:    replayChecker,
	}
}

type Key struct {
	Identifier string
	Secret     []byte
	Algorithm  Algorithm
}

var MalformedParametersErr = errors.New("Malformed Parameters")
var MalformedCredentialsErr = errors.New("Malformed Credentials")

//
type Parameters struct {
	Id        string
	Timestamp int64
	Nonce     string
	Ext       string
	Mac       []byte
	Hash      []byte
}

func parseString(s string) string {
	return strings.Trim(s, `"`) // TODO: We really need to find out how strings in Hawk are encoded, maybe we need to deal with things like \t ?
}

func parseParameters(src string) (Parameters, error) {
	items := make(map[string]string)

	s := scanner.Scanner{Mode: scanner.ScanIdents | scanner.ScanChars | scanner.ScanStrings}
	s.Init(strings.NewReader(src))

	for {
		tok := s.Scan()
		if tok != scanner.Ident {
			return Parameters{}, MalformedParametersErr
		}
		name := s.TokenText()

		tok = s.Scan()
		if tok != '=' {
			return Parameters{}, MalformedParametersErr
		}

		tok = s.Scan()
		if tok != scanner.String {
			return Parameters{}, MalformedParametersErr
		}
		value := s.TokenText()

		items[name] = parseString(value)

		tok = s.Scan()
		if tok == scanner.EOF {
			break
		}
		if tok != ',' {
			return Parameters{}, MalformedParametersErr
		}
	}

	// Now parse the items and setup a Parameters struct

	timestamp, err := strconv.ParseInt(items["ts"], 10, 64)
	if err != nil {
		return Parameters{}, err
	}

	decodedHash, err := base64.StdEncoding.DecodeString(items["hash"])
	if err != nil {
		return Parameters{}, err
	}

	decodedMac, err := base64.StdEncoding.DecodeString(items["mac"])
	if err != nil {
		return Parameters{}, err
	}

	return Parameters{
		Id:        items["id"],
		Timestamp: timestamp,
		Nonce:     items["nonce"],
		Ext:       items["ext"],
		Hash:      decodedHash,
		Mac:       decodedMac,
	}, nil
}

func validateParameters(parameters Parameters) error {
	if parameters.Id == "" {
		return MalformedParametersErr
	}
	if parameters.Timestamp == 0 {
		return MalformedParametersErr
	}
	if parameters.Nonce == "" {
		return MalformedParametersErr
	}
	if len(parameters.Mac) == 0 {
		return MalformedParametersErr
	}
	return nil
}

// Make sure the Credentials return a valid key
func validateCredentials(credentials Credentials) error {
	if credentials.Key().Identifier == "" || len(credentials.Key().Identifier) == 0 {
		return MalformedCredentialsErr
	}
	if credentials.Key().Algorithm != DefaultAlgorithm {
		return MalformedCredentialsErr
	}
	return nil
}

func GetRequestPath(r *http.Request) string {
	path := r.URL.Path
	if len(r.URL.RawQuery) != 0 {
		path = path + "?" + r.URL.RawQuery
	}
	if len(r.URL.Fragment) != 0 {
		path = path + "#" + r.URL.Fragment
	}
	return path
}

// TODO: Make sure the following two do the right thing when behind a proxy

func GetRequestHost(r *http.Request) string {
	hostPort := strings.Split(r.Host, ":")
	return hostPort[0]
}

func GetRequestPort(r *http.Request) int {
	host := r.Host
	if len(r.Header["X-Forwarded-Host"]) != 0 {
		host = r.Header["X-Forwarded-Host"][0]
	}
	hostPort := strings.Split(host, ":")
	if len(hostPort) == 2 {
		port, _ := strconv.Atoi(hostPort[1])
		return port
	} else {
		switch r.URL.Scheme {
		case "http":
			return 80
		case "https":
			return 443
		}
	}
	return 0
}

func GetRequestContentType(r *http.Request) string {
	return r.Header.Get("Content-Type")
}

func CalculatePayloadHash(r *http.Request) ([]byte, error) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}

	r.Body = NewClosingBytesReader(body)

	hash := sha256.New()
	hash.Write([]byte("hawk.1.payload\n"))
	hash.Write([]byte(GetRequestContentType(r)))
	hash.Write([]byte("\n"))
	hash.Write(body)
	hash.Write([]byte("\n"))
	return hash.Sum(nil), nil
}

func CalculateRequestSignature(r *http.Request, parameters Parameters, credentials Credentials) ([]byte, error) {
	var encodedPayloadHash string
	if len(parameters.Hash) != 0 {
		payloadHash, err := CalculatePayloadHash(r)
		if err != nil {
			return nil, err
		}
		encodedPayloadHash = base64.StdEncoding.EncodeToString(payloadHash)
	}

	parts := []string{
		"hawk.1.header",
		strconv.FormatInt(parameters.Timestamp, 10),
		parameters.Nonce,
		r.Method,
		GetRequestPath(r),
		GetRequestHost(r),
		strconv.Itoa(GetRequestPort(r)),
		encodedPayloadHash,
		parameters.Ext,
	}

	requestHeader := strings.Join(parts, "\n") + "\n"

	mac := hmac.New(sha256.New, credentials.Key().Secret)
	mac.Write([]byte(requestHeader))
	return mac.Sum(nil), nil
}

// CreateRequestHeader makes a Hawk authentication string that can be
// added to an http.Request
func CreateRequestHeader(r *http.Request, c Credentials, id string, timestamp int64, nonce string, ext string) (string, error) {

	p := Parameters{
		Id:        id,
		Timestamp: timestamp,
		Nonce:     nonce,
		Ext:       ext,
	}

	var mac, hash string

	if r.Body != nil {
		hashB, err := CalculatePayloadHash(r)
		if err != nil {
			return "", err
		}
		p.Hash = hashB
		hash = base64.StdEncoding.EncodeToString(hashB)
	}

	macB, err := CalculateRequestSignature(r, p, c)
	if err != nil {
		return "", err
	}

	mac = base64.StdEncoding.EncodeToString(macB)

	header := fmt.Sprintf(`Hawk id="%s", ts="%d", nonce="%s", ext="%s", mac="%s"`,
		id,
		timestamp,
		nonce,
		ext,
		mac,
	)

	if hash != "" {
		header = fmt.Sprintf(`%s, hash="%s"`, header, hash)
	}

	return header, nil
}

func (a *Authenticator) Authenticate(w http.ResponseWriter, r *http.Request) (Credentials, bool) {
	// Grab the Authorization Header

	authorization := r.Header.Get("Authorization")
	if len(authorization) == 0 {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return nil, false
	}

	tokens := strings.SplitN(authorization, " ", 2)
	if len(tokens) != 2 {
		http.Error(w, "Unsupported authorization method", http.StatusUnauthorized)
		return nil, false
	}
	if tokens[0] != "Hawk" {
		http.Error(w, "Unsupported authorization method", http.StatusUnauthorized)
		return nil, false
	}

	// Parse and validate the Hawk parameters

	parameters, err := parseParameters(tokens[1])
	if err != nil {
		http.Error(w, "Unable to parse Hawk parameters", http.StatusUnauthorized)
		return nil, false
	}

	if err = validateParameters(parameters); err != nil {
		http.Error(w, "Invalid Hawk parameters: "+err.Error(), http.StatusUnauthorized)
		return nil, false
	}

	// TODO: Check if this request has expired

	// Check if we have seen this request before

	requestId := fmt.Sprintf("%s:%d:%s", parameters.Timestamp, parameters.Id, parameters.Nonce)

	seenBefore, err := a.replayChecker.Check(requestId)
	if err != nil {
		// TODO: Unable to check means server error. Is there a better strategy?
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return nil, false
	}

	if seenBefore {
		http.Error(w, "Request has been seen before", http.StatusUnauthorized)
		return nil, false
	}

	if err := a.replayChecker.Remember(requestId); err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return nil, false
	}

	// Find the user and keys

	credentials, err := a.credentialsStore.CredentialsForKeyIdentifier(parameters.Id)
	if err != nil {
		http.Error(w, "Internal server error:"+err.Error(), http.StatusInternalServerError)
		return nil, false
	}
	if credentials == nil {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return nil, false
	}

	if err := validateCredentials(credentials); err != nil {
		http.Error(w, "Invalid credentials: "+err.Error(), http.StatusUnauthorized)
		return nil, false
	}

	// Check the Hawk request signature

	mac, err := CalculateRequestSignature(r, parameters, credentials)
	if err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return nil, false
	}

	if !bytes.Equal(mac, parameters.Mac) {
		debug(`Signature Mismatch: %s != %s
  - Id: "%s"
  - TS: "%d"
  - Nonce: "%s"
  - Ext: "%s"`,
			base64.StdEncoding.EncodeToString(mac),
			base64.StdEncoding.EncodeToString(parameters.Mac),
			parameters.Id,
			parameters.Timestamp,
			parameters.Nonce,
			parameters.Ext,
		)
		http.Error(w, "Signature Mismatch", http.StatusUnauthorized)
		return nil, false
	}

	// Return the credentials and parsed artifacts

	return credentials, true
}
<|endoftext|>


<commit_before>/*
   Copyright 2016-2017 gtalent2@gmail.com

   This Source Code Form is subject to the terms of the Mozilla Public
   License, v. 2.0. If a copy of the MPL was not distributed with this
   file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package main

import (
	"flag"
	"log"
)

const (
	CMD_SERVE       = "serve"
	CMD_SPINDOWNALL = "spindownall"
)

type cmdOptions struct {
	config string
	cmd    string
}

func parseCmdOptions() cmdOptions {
	var o cmdOptions
	flag.StringVar(&o.cmd, "cmd", CMD_SERVE, "Mode to run command in ("+CMD_SERVE+","+CMD_SPINDOWNALL+")")
	flag.StringVar(&o.config, "config", "dospin.json", "Path to the dospin config file")
	flag.Parse()
	return o
}

func spindownAll(opts cmdOptions) {
	// load settings file
	settings, err := loadSettings(opts.config)
	if err != nil {
		log.Fatal(err)
	}

	// spin down servers
	for name, _ := range settings.Servers {
		dh := NewDropletHandler(settings)
		dh.Spindown(name)
	}
}

func runServer(opts cmdOptions) {
	log.Println("Loading config:", opts.config)
	settings, err := loadSettings(opts.config)
	if err != nil {
		log.Fatal(err)
	}

	for name, sv := range settings.Servers {
		dh := NewDropletHandler(settings)
		sm := NewServerManager(name, dh, settings)

		// start the ServerManager
		go sm.Serve()

		// assign this ServerManager all appropriate ports
		for _, port := range sv.Ports {
			log.Println("Setting up port", port)
			sm.setupListener(port)
		}
	}

	done := make(chan interface{})
	<-done
}

func main() {
	opts := parseCmdOptions()
	switch opts.cmd {
	case CMD_SPINDOWNALL:
		spindownAll(opts)
	case CMD_SERVE:
		runServer(opts)
	default:
		println("Invalid cmd: " + opts.cmd)
	}
}
<commit_msg>Add ability to set log file in command line options<commit_after>/*
   Copyright 2016-2017 gtalent2@gmail.com

   This Source Code Form is subject to the terms of the Mozilla Public
   License, v. 2.0. If a copy of the MPL was not distributed with this
   file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package main

import (
	"flag"
	"log"
	"os"
)

const (
	CMD_SERVE       = "serve"
	CMD_SPINDOWNALL = "spindownall"
)

type cmdOptions struct {
	config  string
	logFile string
	cmd     string
}

func parseCmdOptions() cmdOptions {
	var o cmdOptions
	flag.StringVar(&o.cmd, "cmd", CMD_SERVE, "Mode to run command in ("+CMD_SERVE+","+CMD_SPINDOWNALL+")")
	flag.StringVar(&o.config, "config", "dospin.json", "Path to the dospin config file")
	flag.StringVar(&o.logFile, "logFile", "stdout", "Path to the dospin log file")
	flag.Parse()
	return o
}

func spindownAll(opts cmdOptions) {
	// load settings file
	settings, err := loadSettings(opts.config)
	if err != nil {
		log.Fatal(err)
	}

	// spin down servers
	for name, _ := range settings.Servers {
		dh := NewDropletHandler(settings)
		dh.Spindown(name)
	}
}

func runServer(opts cmdOptions) {
	if opts.logFile != "stdout" {
		logFile, err := os.OpenFile(opts.logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664)
		if err == nil {
			log.SetOutput(logFile)
		} else {
			log.Print("Could not open log file: ", err)

		}
	}
	log.Println("Loading config:", opts.config)
	settings, err := loadSettings(opts.config)
	if err != nil {
		log.Fatal(err)
	}

	for name, sv := range settings.Servers {
		dh := NewDropletHandler(settings)
		sm := NewServerManager(name, dh, settings)

		// start the ServerManager
		go sm.Serve()

		// assign this ServerManager all appropriate ports
		for _, port := range sv.Ports {
			log.Println("Setting up port", port)
			sm.setupListener(port)
		}
	}

	done := make(chan interface{})
	<-done
}

func main() {
	opts := parseCmdOptions()
	switch opts.cmd {
	case CMD_SPINDOWNALL:
		spindownAll(opts)
	case CMD_SERVE:
		runServer(opts)
	default:
		println("Invalid cmd: " + opts.cmd)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"os/exec"
	"regexp"

	"github.com/mitchellh/packer/builder/docker"
)

type Driver interface {
	docker.Driver

	BuildImage(*bytes.Buffer) (string, error)
}

type DockerDriver struct {
	*docker.DockerDriver
}

// Build an image from a compiled Dockerfile
// Return the ID for the new image
func (d *DockerDriver) BuildImage(df *bytes.Buffer) (string, error) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer

	c := exec.Command("docker", "build", "--force-rm=true", "--no-cache=true", "--quiet=true", "-")
	c.Stdin = df
	c.Stdout = &stdout
	c.Stderr = &stderr

	if err := c.Start(); err != nil {
		return "", err
	}

	if err := c.Wait(); err != nil {
		err = fmt.Errorf("Error building image: %s\nStderr: %s", err, stderr.String())
		return "", err
	}

	p := regexp.MustCompile("Successfully built ([a-f0-9]+)")
	m := p.FindStringSubmatch(stdout.String())
	if m == nil {
		err := fmt.Errorf("Error parsing `docker build` output: %s", stdout.String())

		return "", err
	}
	id := m[len(m)-1]

	return id, nil
}
<commit_msg>Docker 1.10 compatibility<commit_after>package main

import (
	"bytes"
	"fmt"
	"os/exec"
	"regexp"

	"github.com/mitchellh/packer/builder/docker"
)

type Driver interface {
	docker.Driver

	BuildImage(*bytes.Buffer) (string, error)
}

type DockerDriver struct {
	*docker.DockerDriver
}

// Build an image from a compiled Dockerfile
// Return the ID for the new image
func (d *DockerDriver) BuildImage(df *bytes.Buffer) (string, error) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer

	c := exec.Command("docker", "build", "--force-rm=true", "--no-cache=true", "--quiet=true", "-")
	c.Stdin = df
	c.Stdout = &stdout
	c.Stderr = &stderr

	if err := c.Start(); err != nil {
		return "", err
	}

	if err := c.Wait(); err != nil {
		err = fmt.Errorf("Error building image: %s\nStderr: %s", err, stderr.String())
		return "", err
	}

	p := regexp.MustCompile("sha256:([a-f0-9]+)")
	m := p.FindStringSubmatch(stdout.String())
	if m == nil {
		err := fmt.Errorf("Error parsing `docker build` output: %s", stdout.String())

		return "", err
	}
	id := m[len(m)-1]

	return id, nil
}
<|endoftext|>


<commit_before>package rounding

import (
	"math/big"
)

// Wrapper around QuoRem that returns the remainder
func remQuo(x, y, q, r *big.Int) *big.Int {
	q.QuoRem(x, y, r)
	return r
}

// Finite returns true if x has a finite decimal representation.
// x is finite if the Denom can be represented as (2**x) * (5**y).
func Finite(x *big.Rat) bool {
	d := new(big.Int).Set(x.Denom())
	i := new(big.Int)
	m := new(big.Int)

	for {
		switch {
		case d.Cmp(big1) == 0:
			return true
		case remQuo(d, big2, i, m).Sign() == 0:
			d.Set(i)
		case remQuo(d, big5, i, m).Sign() == 0:
			d.Set(i)
		default:
			return false
		}
	}
}

// FinitePrec returns the precision of the finite decimal representation of x.
// WARNING: Running this on a value that does not have a finite decimal
// representation will result in an infinite loop. Always check with Finite()
// first if you are unsure.
func FinitePrec(x *big.Rat) int {
	d := x.Denom()
	n := new(big.Int).Set(x.Num())
	m := new(big.Int)

	var i int
	for m.Mod(n, d).Sign() != 0 {
		i++
		n.Mul(n, big10)
	}
	return i
}

// FiniteString returns the equivalent of x.FloatString(FinitePrec(x)).
// WARNING: Running this on a value that does not have a finite decimal
// representation will result in an infinite loop. Always check with Finite()
// first if you are unsure.
func FiniteString(x *big.Rat) string {
	return x.FloatString(FinitePrec(x))
}

// FiniteString returns the equivalent of x.FloatString(max(FinitePrec(x), prec)).
// WARNING: Running this on a value that does not have a finite decimal
// representation will result in an infinite loop. Always check with Finite()
// first if you are unsure.
func FiniteStringMin(x *big.Rat, prec int) string {
	p := FinitePrec(x)
	if p < prec {
		p = prec
	}
	return x.FloatString(p)
}
<commit_msg>doc fix<commit_after>package rounding

import (
	"math/big"
)

// Wrapper around QuoRem that returns the remainder
func remQuo(x, y, q, r *big.Int) *big.Int {
	q.QuoRem(x, y, r)
	return r
}

// Finite returns true if x has a finite decimal representation.
// x is finite if the Denom can be represented as (2**x) * (5**y).
func Finite(x *big.Rat) bool {
	d := new(big.Int).Set(x.Denom())
	i := new(big.Int)
	m := new(big.Int)

	for {
		switch {
		case d.Cmp(big1) == 0:
			return true
		case remQuo(d, big2, i, m).Sign() == 0:
			d.Set(i)
		case remQuo(d, big5, i, m).Sign() == 0:
			d.Set(i)
		default:
			return false
		}
	}
}

// FinitePrec returns the precision of the finite decimal representation of x.
// WARNING: Running this on a value that does not have a finite decimal
// representation will result in an infinite loop. Always check with Finite()
// first if you are unsure.
func FinitePrec(x *big.Rat) int {
	d := x.Denom()
	n := new(big.Int).Set(x.Num())
	m := new(big.Int)

	var i int
	for m.Mod(n, d).Sign() != 0 {
		i++
		n.Mul(n, big10)
	}
	return i
}

// FiniteString returns the equivalent of x.FloatString(FinitePrec(x)).
// WARNING: Running this on a value that does not have a finite decimal
// representation will result in an infinite loop. Always check with Finite()
// first if you are unsure.
func FiniteString(x *big.Rat) string {
	return x.FloatString(FinitePrec(x))
}

// FiniteStringMin returns the equivalent of x.FloatString(max(FinitePrec(x), prec)).
// WARNING: Running this on a value that does not have a finite decimal
// representation will result in an infinite loop. Always check with Finite()
// first if you are unsure.
func FiniteStringMin(x *big.Rat, prec int) string {
	p := FinitePrec(x)
	if p < prec {
		p = prec
	}
	return x.FloatString(p)
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.

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

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

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

package govmomi

import (
	"github.com/vmware/govmomi/vim25/methods"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"
)

type Folder struct {
	types.ManagedObjectReference

	c *Client
}

func NewFolder(c *Client, ref types.ManagedObjectReference) *Folder {
	return &Folder{
		ManagedObjectReference: ref,
		c: c,
	}
}

func (f Folder) Reference() types.ManagedObjectReference {
	return f.ManagedObjectReference
}

func (f Folder) Children() ([]Reference, error) {
	var mf mo.Folder

	err := f.c.Properties(f.Reference(), []string{"childEntity"}, &mf)
	if err != nil {
		return nil, err
	}

	var rs []Reference

	for _, e := range mf.ChildEntity {
		if r := NewReference(f.c, e); r != nil {
			rs = append(rs, r)
		}
	}

	return rs, nil
}

func (f Folder) CreateDatacenter(datacenter string) (*Datacenter, error) {
	req := types.CreateDatacenter{
		This: f.Reference(),
		Name: datacenter,
	}

	// response will be nil if this is an ESX host that does not belong to a vCenter
	res, err := methods.CreateDatacenter(f.c, &req)
	if res != nil {
		return NewDatacenter(f.c, res.Returnval), err
	}

	return nil, err
}

func (f Folder) CreateVM(config types.VirtualMachineConfigSpec, pool *ResourcePool, host *HostSystem) (*Task, error) {
	req := types.CreateVM_Task{
		This:   f.Reference(),
		Config: config,
		Pool:   pool.Reference(),
	}

	if host != nil {
		ref := host.Reference()
		req.Host = &ref
	}

	res, err := methods.CreateVM_Task(f.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(f.c, res.Returnval), nil
}
<commit_msg>Adding the RegisterVM task.<commit_after>/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.

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

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

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

package govmomi

import (
	"github.com/vmware/govmomi/vim25/methods"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"
)

type Folder struct {
	types.ManagedObjectReference

	c *Client
}

func NewFolder(c *Client, ref types.ManagedObjectReference) *Folder {
	return &Folder{
		ManagedObjectReference: ref,
		c: c,
	}
}

func (f Folder) Reference() types.ManagedObjectReference {
	return f.ManagedObjectReference
}

func (f Folder) Children() ([]Reference, error) {
	var mf mo.Folder

	err := f.c.Properties(f.Reference(), []string{"childEntity"}, &mf)
	if err != nil {
		return nil, err
	}

	var rs []Reference

	for _, e := range mf.ChildEntity {
		if r := NewReference(f.c, e); r != nil {
			rs = append(rs, r)
		}
	}

	return rs, nil
}

func (f Folder) CreateDatacenter(datacenter string) (*Datacenter, error) {
	req := types.CreateDatacenter{
		This: f.Reference(),
		Name: datacenter,
	}

	// response will be nil if this is an ESX host that does not belong to a vCenter
	res, err := methods.CreateDatacenter(f.c, &req)
	if res != nil {
		return NewDatacenter(f.c, res.Returnval), err
	}

	return nil, err
}

func (f Folder) CreateVM(config types.VirtualMachineConfigSpec, pool *ResourcePool, host *HostSystem) (*Task, error) {
	req := types.CreateVM_Task{
		This:   f.Reference(),
		Config: config,
		Pool:   pool.Reference(),
	}

	if host != nil {
		ref := host.Reference()
		req.Host = &ref
	}

	res, err := methods.CreateVM_Task(f.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(f.c, res.Returnval), nil
}

func (f Folder) RegisterVM(path string, name string, asTemplate bool, pool *ResourcePool, host *HostSystem) (*Task, error) {
	req := types.RegisterVM_Task{
		This:       f.Reference(),
		Path:       path,
		AsTemplate: asTemplate,
	}

	if name != "" {
		req.Name = name
	}

	if host != nil {
		ref := host.Reference()
		req.Host = &ref
	}

	if pool != nil {
		ref := pool.Reference()
		req.Pool = &ref
	}

	res, err := methods.RegisterVM_Task(f.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(f.c, res.Returnval), nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/url"
	"time"

	"github.com/elastic/libbeat/beat"
	"github.com/elastic/libbeat/cfgfile"
	"github.com/elastic/libbeat/logp"

	"github.com/mrkschan/nginxbeat/collector"
	"github.com/mrkschan/nginxbeat/publisher"
)

const selector = "nginxbeat"

// Nginxbeat implements Beater interface and sends Nginx status using libbeat.
type Nginxbeat struct {
	// NbConfig holds configurations of Nginxbeat parsed by libbeat.
	NbConfig ConfigSettings

	done chan uint

	urls   []*url.URL
	format string
	period time.Duration
}

// Config Nginxbeat according to nginxbeat.yml.
func (nb *Nginxbeat) Config(b *beat.Beat) error {
	err := cfgfile.Read(&nb.NbConfig, "")
	if err != nil {
		logp.Err("Error reading configuration file: %v", err)
		return err
	}

	var urlConfig []string
	if nb.NbConfig.Input.URLs != nil {
		urlConfig = nb.NbConfig.Input.URLs
	} else {
		urlConfig = []string{"http://127.0.0.1/status"}
	}

	nb.urls = make([]*url.URL, len(urlConfig))
	for i := 0; i < len(urlConfig); i++ {
		u, err := url.Parse(urlConfig[i])
		if err != nil {
			logp.Err("Invalid Nginx status page: %v", err)
			return err
		}
		nb.urls[i] = u
	}

	var f string
	if nb.NbConfig.Input.Format != "" {
		f = nb.NbConfig.Input.Format
	} else {
		f = "stub"
	}
	if f != "stub" && f != "plus" {
		err = fmt.Errorf("%s is an unsupported format", f)
	}
	if err != nil {
		logp.Err("Invalid Nginx status format: %v", err)
		return err
	}
	nb.format = f

	if nb.NbConfig.Input.Period != nil {
		nb.period = time.Duration(*nb.NbConfig.Input.Period) * time.Second
	} else {
		nb.period = 10 * time.Second
	}

	logp.Debug(selector, "Init nginxbeat")
	logp.Debug(selector, "Watch %v", nb.urls)
	logp.Debug(selector, "Format %v", nb.format)
	logp.Debug(selector, "Period %v", nb.period)

	return nil
}

// Setup Nginxbeat.
func (nb *Nginxbeat) Setup(b *beat.Beat) error {
	nb.done = make(chan uint)

	return nil
}

// Run Nginxbeat.
func (nb *Nginxbeat) Run(b *beat.Beat) error {
	logp.Debug(selector, "Run nginxbeat")

	for _, u := range nb.urls {
		go func() {
			var c collector.Collector
			var p publisher.Publisher

			switch nb.format {
			case "stub":
				c = collector.NewStubCollector()
				p = publisher.NewStubPublisher(b.Events)
			case "plus":
				c = collector.NewPlusCollector()
				p = publisher.NewPlusPublisher(b.Events)
			}

			ticker := time.NewTicker(nb.period)
			defer ticker.Stop()

			for {
				select {
				case <-nb.done:
					goto GotoFinish
				case <-ticker.C:
				}

				start := time.Now()

				s, err := c.Collect(*u)
				if err != nil {
					logp.Err("Fail to read Nginx status: %v", err)
					goto GotoNext
				}
				p.Publish(s)

			GotoNext:
				end := time.Now()
				duration := end.Sub(start)
				if duration.Nanoseconds() > nb.period.Nanoseconds() {
					logp.Warn("Ignoring tick(s) due to processing taking longer than one period")
				}
			}

		GotoFinish:
		}()
	}

	<-nb.done
	return nil
}

// Cleanup Nginxbeat.
func (nb *Nginxbeat) Cleanup(b *beat.Beat) error {
	return nil
}

// Stop Nginxbeat.
func (nb *Nginxbeat) Stop() {
	logp.Debug(selector, "Stop nginxbeat")
	close(nb.done)
}
<commit_msg>Use function closure to pass URLs into go-routine to prevent wrong reference<commit_after>package main

import (
	"fmt"
	"net/url"
	"time"

	"github.com/elastic/libbeat/beat"
	"github.com/elastic/libbeat/cfgfile"
	"github.com/elastic/libbeat/logp"

	"github.com/mrkschan/nginxbeat/collector"
	"github.com/mrkschan/nginxbeat/publisher"
)

const selector = "nginxbeat"

// Nginxbeat implements Beater interface and sends Nginx status using libbeat.
type Nginxbeat struct {
	// NbConfig holds configurations of Nginxbeat parsed by libbeat.
	NbConfig ConfigSettings

	done chan uint

	urls   []*url.URL
	format string
	period time.Duration
}

// Config Nginxbeat according to nginxbeat.yml.
func (nb *Nginxbeat) Config(b *beat.Beat) error {
	err := cfgfile.Read(&nb.NbConfig, "")
	if err != nil {
		logp.Err("Error reading configuration file: %v", err)
		return err
	}

	var urlConfig []string
	if nb.NbConfig.Input.URLs != nil {
		urlConfig = nb.NbConfig.Input.URLs
	} else {
		urlConfig = []string{"http://127.0.0.1/status"}
	}

	nb.urls = make([]*url.URL, len(urlConfig))
	for i := 0; i < len(urlConfig); i++ {
		u, err := url.Parse(urlConfig[i])
		if err != nil {
			logp.Err("Invalid Nginx status page: %v", err)
			return err
		}
		nb.urls[i] = u
	}

	var f string
	if nb.NbConfig.Input.Format != "" {
		f = nb.NbConfig.Input.Format
	} else {
		f = "stub"
	}
	if f != "stub" && f != "plus" {
		err = fmt.Errorf("%s is an unsupported format", f)
	}
	if err != nil {
		logp.Err("Invalid Nginx status format: %v", err)
		return err
	}
	nb.format = f

	if nb.NbConfig.Input.Period != nil {
		nb.period = time.Duration(*nb.NbConfig.Input.Period) * time.Second
	} else {
		nb.period = 10 * time.Second
	}

	logp.Debug(selector, "Init nginxbeat")
	logp.Debug(selector, "Watch %v", nb.urls)
	logp.Debug(selector, "Format %v", nb.format)
	logp.Debug(selector, "Period %v", nb.period)

	return nil
}

// Setup Nginxbeat.
func (nb *Nginxbeat) Setup(b *beat.Beat) error {
	nb.done = make(chan uint)

	return nil
}

// Run Nginxbeat.
func (nb *Nginxbeat) Run(b *beat.Beat) error {
	logp.Debug(selector, "Run nginxbeat")

	for _, u := range nb.urls {
		go func(u *url.URL) {
			var c collector.Collector
			var p publisher.Publisher

			switch nb.format {
			case "stub":
				c = collector.NewStubCollector()
				p = publisher.NewStubPublisher(b.Events)
			case "plus":
				c = collector.NewPlusCollector()
				p = publisher.NewPlusPublisher(b.Events)
			}

			ticker := time.NewTicker(nb.period)
			defer ticker.Stop()

			for {
				select {
				case <-nb.done:
					goto GotoFinish
				case <-ticker.C:
				}

				start := time.Now()

				s, err := c.Collect(*u)
				if err != nil {
					logp.Err("Fail to read Nginx status: %v", err)
					goto GotoNext
				}
				p.Publish(s)

			GotoNext:
				end := time.Now()
				duration := end.Sub(start)
				if duration.Nanoseconds() > nb.period.Nanoseconds() {
					logp.Warn("Ignoring tick(s) due to processing taking longer than one period")
				}
			}

		GotoFinish:
		}(u)
	}

	<-nb.done
	return nil
}

// Cleanup Nginxbeat.
func (nb *Nginxbeat) Cleanup(b *beat.Beat) error {
	return nil
}

// Stop Nginxbeat.
func (nb *Nginxbeat) Stop() {
	logp.Debug(selector, "Stop nginxbeat")
	close(nb.done)
}
<|endoftext|>


<commit_before>package libvirt

import (
	"fmt"
	"log"
	"sync"

	uri "github.com/dmacvicar/terraform-provider-libvirt/libvirt/uri"
	libvirt "github.com/digitalocean/go-libvirt"
	"github.com/hashicorp/terraform-plugin-sdk/helper/mutexkv"
)

// Config struct for the libvirt-provider
type Config struct {
	URI string
}

// Client libvirt
type Client struct {
	libvirt     *libvirt.Libvirt
	poolMutexKV *mutexkv.MutexKV
	// define only one network at a time
	// https://gitlab.com/libvirt/libvirt/-/issues/78
	networkMutex sync.Mutex
}

// Client libvirt, generate libvirt client given URI
func (c *Config) Client() (*Client, error) {

	u, err := uri.Parse(c.URI)
	if err != nil {
		return nil, err
	}

	conn, err := u.DialTransport()
	if err != nil {
		return nil, fmt.Errorf("failed to dial libvirt: %w", err)
	}
	log.Printf("[INFO] Set up libvirt transport: %v\n", conn)

	l := libvirt.New(conn)
	if err := l.ConnectToURI(libvirt.ConnectURI(u.RemoteName())); err != nil {
		return nil, fmt.Errorf("failed to connect: %w", err)
	}

	v, err := l.ConnectGetLibVersion()
	if err != nil {
		return nil, fmt.Errorf("failed to retrieve libvirt version: %w", err)
	}
	log.Printf("[INFO] libvirt client libvirt version: %v\n", v)

	client := &Client{
		libvirt:     l,
		poolMutexKV: mutexkv.NewMutexKV(),
	}

	return client, nil
}
<commit_msg>go fmt<commit_after>package libvirt

import (
	"fmt"
	"log"
	"sync"

	libvirt "github.com/digitalocean/go-libvirt"
	uri "github.com/dmacvicar/terraform-provider-libvirt/libvirt/uri"
	"github.com/hashicorp/terraform-plugin-sdk/helper/mutexkv"
)

// Config struct for the libvirt-provider
type Config struct {
	URI string
}

// Client libvirt
type Client struct {
	libvirt     *libvirt.Libvirt
	poolMutexKV *mutexkv.MutexKV
	// define only one network at a time
	// https://gitlab.com/libvirt/libvirt/-/issues/78
	networkMutex sync.Mutex
}

// Client libvirt, generate libvirt client given URI
func (c *Config) Client() (*Client, error) {

	u, err := uri.Parse(c.URI)
	if err != nil {
		return nil, err
	}

	conn, err := u.DialTransport()
	if err != nil {
		return nil, fmt.Errorf("failed to dial libvirt: %w", err)
	}
	log.Printf("[INFO] Set up libvirt transport: %v\n", conn)

	l := libvirt.New(conn)
	if err := l.ConnectToURI(libvirt.ConnectURI(u.RemoteName())); err != nil {
		return nil, fmt.Errorf("failed to connect: %w", err)
	}

	v, err := l.ConnectGetLibVersion()
	if err != nil {
		return nil, fmt.Errorf("failed to retrieve libvirt version: %w", err)
	}
	log.Printf("[INFO] libvirt client libvirt version: %v\n", v)

	client := &Client{
		libvirt:     l,
		poolMutexKV: mutexkv.NewMutexKV(),
	}

	return client, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package seelog

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"
)

// FormatterSymbol is a special symbol used in config files to mark special format aliases.
const (
	FormatterSymbol = '%'
)
const (
	formatterSymbolString   = "%"
	formatterParameterStart = '('
	formatterParameterEnd   = ')'
)

// These are the time and date formats that are used when %Date or %Time format aliases are used.
const (
	DateDefaultFormat = "2006-01-02"
	TimeFormat        = "15:04:05"
)

var DefaultMsgFormat = "%Ns [%Level] %Msg%n"

var defaultformatter *formatter
var msgonlyformatter *formatter

func init() {
	var err error
	defaultformatter, err = newFormatter(DefaultMsgFormat)
	if err != nil {
		fmt.Println("Error during defaultformatter creation: " + err.Error())
	}
	msgonlyformatter, err = newFormatter("%Msg")
	if err != nil {
		fmt.Println("Error during msgonlyformatter creation: " + err.Error())
	}
}

// FormatterFunc represents one formatter object that starts with '%' sign in the 'format' attribute
// of the 'format' config item. These special symbols are replaced with context values or special
// strings when message is written to byte receiver.
//
// Check https://github.com/cihub/seelog/wiki/Formatting for details.
// Full list (with descriptions) of formatters: https://github.com/cihub/seelog/wiki/Format-reference
//
// FormatterFunc takes raw log message, level, log context and returns a string, number (of any type) or any object
// that can be evaluated as string.
type FormatterFunc func(message string, level LogLevel, context LogContextInterface) interface{}

// FormatterFuncCreator is a factory of FormatterFunc objects. It is used to generate parameterized
// formatters (such as %Date or %EscM) and custom user formatters.
type FormatterFuncCreator func(param string) FormatterFunc

var formatterFuncs = map[string]FormatterFunc{
	"Level":     formatterLevel,
	"Lev":       formatterLev,
	"LEVEL":     formatterLEVEL,
	"LEV":       formatterLEV,
	"l":         formatterl,
	"Msg":       formatterMsg,
	"FullPath":  formatterFullPath,
	"File":      formatterFile,
	"RelFile":   formatterRelFile,
	"Func":      FormatterFunction,
	"FuncShort": FormatterFunctionShort,
	"Line":      formatterLine,
	"Time":      formatterTime,
	"Ns":        formatterNs,
	"n":         formattern,
	"t":         formattert,
}

var formatterFuncsParameterized = map[string]FormatterFuncCreator{
	"Date": createDateTimeFormatterFunc,
	"EscM": createANSIEscapeFunc,
}

func errorAliasReserved(name string) error {
	return fmt.Errorf("Cannot use '%s' as custom formatter name. Name is reserved.", name)
}

// RegisterCustomFormatter registers a new custom formatter factory with a given name. If returned error is nil,
// then this name (prepended by '%' symbol) can be used in 'format' attributes in configuration and
// it will be treated like the standard parameterized formatter identifiers.
//
// RegisterCustomFormatter needs to be called before creating a logger for it to take effect. The general recommendation
// is to call it once in 'init' func of your application or any initializer func.
//
// For usage examples, check https://github.com/cihub/seelog/wiki/Custom-formatters.
//
// Name must only consist of letters (unicode.IsLetter).
//
// Name must not be one of the already registered standard formatter names
// (https://github.com/cihub/seelog/wiki/Format-reference) and previously registered
// custom format names. To avoid any potential name conflicts (in future releases), it is recommended
// to start your custom formatter name with a namespace (e.g. 'MyCompanySomething') or a 'Custom' keyword.
func RegisterCustomFormatter(name string, creator FormatterFuncCreator) error {
	if _, ok := formatterFuncs[name]; ok {
		return errorAliasReserved(name)
	}
	if _, ok := formatterFuncsParameterized[name]; ok {
		return errorAliasReserved(name)
	}
	formatterFuncsParameterized[name] = creator
	return nil
}

// formatter is used to write messages in a specific format, inserting such additional data
// as log level, date/time, etc.
type formatter struct {
	fmtStringOriginal string
	fmtString         string
	formatterFuncs    []FormatterFunc
}

// newFormatter creates a new formatter using a format string
func newFormatter(formatString string) (*formatter, error) {
	newformatter := new(formatter)
	newformatter.fmtStringOriginal = formatString

	err := newformatter.buildFormatterFuncs()
	if err != nil {
		return nil, err
	}

	return newformatter, nil
}

func (formatter *formatter) buildFormatterFuncs() error {
	formatter.formatterFuncs = make([]FormatterFunc, 0)
	var fmtString string
	for i := 0; i < len(formatter.fmtStringOriginal); i++ {
		char := formatter.fmtStringOriginal[i]
		if char != FormatterSymbol {
			fmtString += string(char)
			continue
		}

		isEndOfStr := i == len(formatter.fmtStringOriginal)-1
		if isEndOfStr {
			return errors.New(fmt.Sprintf("Format error: %v - last symbol", formatterSymbolString))
		}

		isDoubledFormatterSymbol := formatter.fmtStringOriginal[i+1] == FormatterSymbol
		if isDoubledFormatterSymbol {
			fmtString += formatterSymbolString
			i++
			continue
		}

		function, nextI, err := formatter.extractFormatterFunc(i + 1)
		if err != nil {
			return err
		}

		fmtString += "%v"
		i = nextI
		formatter.formatterFuncs = append(formatter.formatterFuncs, function)
	}

	formatter.fmtString = fmtString
	return nil
}

func (formatter *formatter) extractFormatterFunc(index int) (FormatterFunc, int, error) {
	letterSequence := formatter.extractLetterSequence(index)
	if len(letterSequence) == 0 {
		return nil, 0, errors.New(fmt.Sprintf("Format error: lack of formatter after %v. At %v", formatterSymbolString, index))
	}

	function, formatterLength, ok := formatter.findFormatterFunc(letterSequence)
	if ok {
		return function, index + formatterLength - 1, nil
	}

	function, formatterLength, ok = formatter.findFormatterFuncParametrized(letterSequence, index)
	if ok {
		return function, index + formatterLength - 1, nil
	}

	return nil, 0, errors.New("Format error: unrecognized formatter at " + strconv.Itoa(index) + ": " + letterSequence)
}

func (formatter *formatter) extractLetterSequence(index int) string {
	letters := ""

	bytesToParse := []byte(formatter.fmtStringOriginal[index:])
	runeCount := utf8.RuneCount(bytesToParse)
	for i := 0; i < runeCount; i++ {
		rune, runeSize := utf8.DecodeRune(bytesToParse)
		bytesToParse = bytesToParse[runeSize:]

		if unicode.IsLetter(rune) {
			letters += string(rune)
		} else {
			break
		}
	}
	return letters
}

func (formatter *formatter) findFormatterFunc(letters string) (FormatterFunc, int, bool) {
	currentVerb := letters
	for i := 0; i < len(letters); i++ {
		function, ok := formatterFuncs[currentVerb]
		if ok {
			return function, len(currentVerb), ok
		}
		currentVerb = currentVerb[:len(currentVerb)-1]
	}

	return nil, 0, false
}

func (formatter *formatter) findFormatterFuncParametrized(letters string, lettersStartIndex int) (FormatterFunc, int, bool) {
	currentVerb := letters
	for i := 0; i < len(letters); i++ {
		functionCreator, ok := formatterFuncsParameterized[currentVerb]
		if ok {
			paramter := ""
			parameterLen := 0
			isVerbEqualsLetters := i == 0 // if not, then letter goes after formatter, and formatter is parameterless
			if isVerbEqualsLetters {
				userParamter := ""
				userParamter, parameterLen, ok = formatter.findparameter(lettersStartIndex + len(currentVerb))
				if ok {
					paramter = userParamter
				}
			}

			return functionCreator(paramter), len(currentVerb) + parameterLen, true
		}

		currentVerb = currentVerb[:len(currentVerb)-1]
	}

	return nil, 0, false
}

func (formatter *formatter) findparameter(startIndex int) (string, int, bool) {
	if len(formatter.fmtStringOriginal) == startIndex || formatter.fmtStringOriginal[startIndex] != formatterParameterStart {
		return "", 0, false
	}

	endIndex := strings.Index(formatter.fmtStringOriginal[startIndex:], string(formatterParameterEnd)) + startIndex
	if endIndex == -1 {
		return "", 0, false
	}

	length := endIndex - startIndex + 1

	return formatter.fmtStringOriginal[startIndex+1 : endIndex], length, true
}

// Format processes a message with special formatters, log level, and context. Returns formatted string
// with all formatter identifiers changed to appropriate values.
func (formatter *formatter) Format(message string, level LogLevel, context LogContextInterface) string {
	if len(formatter.formatterFuncs) == 0 {
		return formatter.fmtString
	}

	params := make([]interface{}, len(formatter.formatterFuncs))
	for i, function := range formatter.formatterFuncs {
		params[i] = function(message, level, context)
	}

	return fmt.Sprintf(formatter.fmtString, params...)
}

func (formatter *formatter) String() string {
	return formatter.fmtStringOriginal
}

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

const (
	wrongLogLevel   = "WRONG_LOGLEVEL"
	wrongEscapeCode = "WRONG_ESCAPE"
)

var levelToString = map[LogLevel]string{
	TraceLvl:    "Trace",
	DebugLvl:    "Debug",
	InfoLvl:     "Info",
	WarnLvl:     "Warn",
	ErrorLvl:    "Error",
	CriticalLvl: "Critical",
	Off:         "Off",
}

var levelToShortString = map[LogLevel]string{
	TraceLvl:    "Trc",
	DebugLvl:    "Dbg",
	InfoLvl:     "Inf",
	WarnLvl:     "Wrn",
	ErrorLvl:    "Err",
	CriticalLvl: "Crt",
	Off:         "Off",
}

var levelToShortestString = map[LogLevel]string{
	TraceLvl:    "t",
	DebugLvl:    "d",
	InfoLvl:     "i",
	WarnLvl:     "w",
	ErrorLvl:    "e",
	CriticalLvl: "c",
	Off:         "o",
}

func formatterLevel(message string, level LogLevel, context LogContextInterface) interface{} {
	levelStr, ok := levelToString[level]
	if !ok {
		return wrongLogLevel
	}
	return levelStr
}

func formatterLev(message string, level LogLevel, context LogContextInterface) interface{} {
	levelStr, ok := levelToShortString[level]
	if !ok {
		return wrongLogLevel
	}
	return levelStr
}

func formatterLEVEL(message string, level LogLevel, context LogContextInterface) interface{} {
	return strings.ToTitle(formatterLevel(message, level, context).(string))
}

func formatterLEV(message string, level LogLevel, context LogContextInterface) interface{} {
	return strings.ToTitle(formatterLev(message, level, context).(string))
}

func formatterl(message string, level LogLevel, context LogContextInterface) interface{} {
	levelStr, ok := levelToShortestString[level]
	if !ok {
		return wrongLogLevel
	}
	return levelStr
}

func formatterMsg(message string, level LogLevel, context LogContextInterface) interface{} {
	return message
}

func formatterFullPath(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.FullPath()
}

func formatterFile(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.FileName()
}

func formatterRelFile(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.ShortPath()
}

func FormatterFunction(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.Func()
}

func FormatterFunctionShort(message string, level LogLevel, context LogContextInterface) interface{} {
	f := context.Func()
	spl := strings.Split(f, ".")
	return spl[len(spl)-1]
}

func formatterLine(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.Line()
}

func formatterTime(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.CallTime().Format(TimeFormat)
}

func formatterNs(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.CallTime().UnixNano()
}

func formattern(message string, level LogLevel, context LogContextInterface) interface{} {
	return "\n"
}

func formattert(message string, level LogLevel, context LogContextInterface) interface{} {
	return "\t"
}

func createDateTimeFormatterFunc(dateTimeFormat string) FormatterFunc {
	format := dateTimeFormat
	if format == "" {
		format = DateDefaultFormat
	}
	return func(message string, level LogLevel, context LogContextInterface) interface{} {
		return time.Now().Format(format)
	}
}

func createANSIEscapeFunc(escapeCodeString string) FormatterFunc {
	return func(message string, level LogLevel, context LogContextInterface) interface{} {
		if len(escapeCodeString) == 0 {
			return wrongEscapeCode
		}

		return fmt.Sprintf("%c[%sm", 0x1B, escapeCodeString)
	}
}
<commit_msg>Add support for UTC date/time formats.<commit_after>// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package seelog

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"
)

// FormatterSymbol is a special symbol used in config files to mark special format aliases.
const (
	FormatterSymbol = '%'
)
const (
	formatterSymbolString   = "%"
	formatterParameterStart = '('
	formatterParameterEnd   = ')'
)

// These are the time and date formats that are used when %Date or %Time format aliases are used.
const (
	DateDefaultFormat = "2006-01-02"
	TimeFormat        = "15:04:05"
)

var DefaultMsgFormat = "%Ns [%Level] %Msg%n"

var defaultformatter *formatter
var msgonlyformatter *formatter

func init() {
	var err error
	defaultformatter, err = newFormatter(DefaultMsgFormat)
	if err != nil {
		fmt.Println("Error during defaultformatter creation: " + err.Error())
	}
	msgonlyformatter, err = newFormatter("%Msg")
	if err != nil {
		fmt.Println("Error during msgonlyformatter creation: " + err.Error())
	}
}

// FormatterFunc represents one formatter object that starts with '%' sign in the 'format' attribute
// of the 'format' config item. These special symbols are replaced with context values or special
// strings when message is written to byte receiver.
//
// Check https://github.com/cihub/seelog/wiki/Formatting for details.
// Full list (with descriptions) of formatters: https://github.com/cihub/seelog/wiki/Format-reference
//
// FormatterFunc takes raw log message, level, log context and returns a string, number (of any type) or any object
// that can be evaluated as string.
type FormatterFunc func(message string, level LogLevel, context LogContextInterface) interface{}

// FormatterFuncCreator is a factory of FormatterFunc objects. It is used to generate parameterized
// formatters (such as %Date or %EscM) and custom user formatters.
type FormatterFuncCreator func(param string) FormatterFunc

var formatterFuncs = map[string]FormatterFunc{
	"Level":     formatterLevel,
	"Lev":       formatterLev,
	"LEVEL":     formatterLEVEL,
	"LEV":       formatterLEV,
	"l":         formatterl,
	"Msg":       formatterMsg,
	"FullPath":  formatterFullPath,
	"File":      formatterFile,
	"RelFile":   formatterRelFile,
	"Func":      FormatterFunction,
	"FuncShort": FormatterFunctionShort,
	"Line":      formatterLine,
	"Time":      formatterTime,
	"UTCTime":   formatterUTCTime,
	"Ns":        formatterNs,
	"UTCNs":     formatterUTCNs,
	"n":         formattern,
	"t":         formattert,
}

var formatterFuncsParameterized = map[string]FormatterFuncCreator{
	"Date":    createDateTimeFormatterFunc,
	"UTCDate": createUTCDateTimeFormatterFunc,
	"EscM":    createANSIEscapeFunc,
}

func errorAliasReserved(name string) error {
	return fmt.Errorf("Cannot use '%s' as custom formatter name. Name is reserved.", name)
}

// RegisterCustomFormatter registers a new custom formatter factory with a given name. If returned error is nil,
// then this name (prepended by '%' symbol) can be used in 'format' attributes in configuration and
// it will be treated like the standard parameterized formatter identifiers.
//
// RegisterCustomFormatter needs to be called before creating a logger for it to take effect. The general recommendation
// is to call it once in 'init' func of your application or any initializer func.
//
// For usage examples, check https://github.com/cihub/seelog/wiki/Custom-formatters.
//
// Name must only consist of letters (unicode.IsLetter).
//
// Name must not be one of the already registered standard formatter names
// (https://github.com/cihub/seelog/wiki/Format-reference) and previously registered
// custom format names. To avoid any potential name conflicts (in future releases), it is recommended
// to start your custom formatter name with a namespace (e.g. 'MyCompanySomething') or a 'Custom' keyword.
func RegisterCustomFormatter(name string, creator FormatterFuncCreator) error {
	if _, ok := formatterFuncs[name]; ok {
		return errorAliasReserved(name)
	}
	if _, ok := formatterFuncsParameterized[name]; ok {
		return errorAliasReserved(name)
	}
	formatterFuncsParameterized[name] = creator
	return nil
}

// formatter is used to write messages in a specific format, inserting such additional data
// as log level, date/time, etc.
type formatter struct {
	fmtStringOriginal string
	fmtString         string
	formatterFuncs    []FormatterFunc
}

// newFormatter creates a new formatter using a format string
func newFormatter(formatString string) (*formatter, error) {
	newformatter := new(formatter)
	newformatter.fmtStringOriginal = formatString

	err := newformatter.buildFormatterFuncs()
	if err != nil {
		return nil, err
	}

	return newformatter, nil
}

func (formatter *formatter) buildFormatterFuncs() error {
	formatter.formatterFuncs = make([]FormatterFunc, 0)
	var fmtString string
	for i := 0; i < len(formatter.fmtStringOriginal); i++ {
		char := formatter.fmtStringOriginal[i]
		if char != FormatterSymbol {
			fmtString += string(char)
			continue
		}

		isEndOfStr := i == len(formatter.fmtStringOriginal)-1
		if isEndOfStr {
			return errors.New(fmt.Sprintf("Format error: %v - last symbol", formatterSymbolString))
		}

		isDoubledFormatterSymbol := formatter.fmtStringOriginal[i+1] == FormatterSymbol
		if isDoubledFormatterSymbol {
			fmtString += formatterSymbolString
			i++
			continue
		}

		function, nextI, err := formatter.extractFormatterFunc(i + 1)
		if err != nil {
			return err
		}

		fmtString += "%v"
		i = nextI
		formatter.formatterFuncs = append(formatter.formatterFuncs, function)
	}

	formatter.fmtString = fmtString
	return nil
}

func (formatter *formatter) extractFormatterFunc(index int) (FormatterFunc, int, error) {
	letterSequence := formatter.extractLetterSequence(index)
	if len(letterSequence) == 0 {
		return nil, 0, errors.New(fmt.Sprintf("Format error: lack of formatter after %v. At %v", formatterSymbolString, index))
	}

	function, formatterLength, ok := formatter.findFormatterFunc(letterSequence)
	if ok {
		return function, index + formatterLength - 1, nil
	}

	function, formatterLength, ok = formatter.findFormatterFuncParametrized(letterSequence, index)
	if ok {
		return function, index + formatterLength - 1, nil
	}

	return nil, 0, errors.New("Format error: unrecognized formatter at " + strconv.Itoa(index) + ": " + letterSequence)
}

func (formatter *formatter) extractLetterSequence(index int) string {
	letters := ""

	bytesToParse := []byte(formatter.fmtStringOriginal[index:])
	runeCount := utf8.RuneCount(bytesToParse)
	for i := 0; i < runeCount; i++ {
		rune, runeSize := utf8.DecodeRune(bytesToParse)
		bytesToParse = bytesToParse[runeSize:]

		if unicode.IsLetter(rune) {
			letters += string(rune)
		} else {
			break
		}
	}
	return letters
}

func (formatter *formatter) findFormatterFunc(letters string) (FormatterFunc, int, bool) {
	currentVerb := letters
	for i := 0; i < len(letters); i++ {
		function, ok := formatterFuncs[currentVerb]
		if ok {
			return function, len(currentVerb), ok
		}
		currentVerb = currentVerb[:len(currentVerb)-1]
	}

	return nil, 0, false
}

func (formatter *formatter) findFormatterFuncParametrized(letters string, lettersStartIndex int) (FormatterFunc, int, bool) {
	currentVerb := letters
	for i := 0; i < len(letters); i++ {
		functionCreator, ok := formatterFuncsParameterized[currentVerb]
		if ok {
			paramter := ""
			parameterLen := 0
			isVerbEqualsLetters := i == 0 // if not, then letter goes after formatter, and formatter is parameterless
			if isVerbEqualsLetters {
				userParamter := ""
				userParamter, parameterLen, ok = formatter.findparameter(lettersStartIndex + len(currentVerb))
				if ok {
					paramter = userParamter
				}
			}

			return functionCreator(paramter), len(currentVerb) + parameterLen, true
		}

		currentVerb = currentVerb[:len(currentVerb)-1]
	}

	return nil, 0, false
}

func (formatter *formatter) findparameter(startIndex int) (string, int, bool) {
	if len(formatter.fmtStringOriginal) == startIndex || formatter.fmtStringOriginal[startIndex] != formatterParameterStart {
		return "", 0, false
	}

	endIndex := strings.Index(formatter.fmtStringOriginal[startIndex:], string(formatterParameterEnd)) + startIndex
	if endIndex == -1 {
		return "", 0, false
	}

	length := endIndex - startIndex + 1

	return formatter.fmtStringOriginal[startIndex+1 : endIndex], length, true
}

// Format processes a message with special formatters, log level, and context. Returns formatted string
// with all formatter identifiers changed to appropriate values.
func (formatter *formatter) Format(message string, level LogLevel, context LogContextInterface) string {
	if len(formatter.formatterFuncs) == 0 {
		return formatter.fmtString
	}

	params := make([]interface{}, len(formatter.formatterFuncs))
	for i, function := range formatter.formatterFuncs {
		params[i] = function(message, level, context)
	}

	return fmt.Sprintf(formatter.fmtString, params...)
}

func (formatter *formatter) String() string {
	return formatter.fmtStringOriginal
}

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

const (
	wrongLogLevel   = "WRONG_LOGLEVEL"
	wrongEscapeCode = "WRONG_ESCAPE"
)

var levelToString = map[LogLevel]string{
	TraceLvl:    "Trace",
	DebugLvl:    "Debug",
	InfoLvl:     "Info",
	WarnLvl:     "Warn",
	ErrorLvl:    "Error",
	CriticalLvl: "Critical",
	Off:         "Off",
}

var levelToShortString = map[LogLevel]string{
	TraceLvl:    "Trc",
	DebugLvl:    "Dbg",
	InfoLvl:     "Inf",
	WarnLvl:     "Wrn",
	ErrorLvl:    "Err",
	CriticalLvl: "Crt",
	Off:         "Off",
}

var levelToShortestString = map[LogLevel]string{
	TraceLvl:    "t",
	DebugLvl:    "d",
	InfoLvl:     "i",
	WarnLvl:     "w",
	ErrorLvl:    "e",
	CriticalLvl: "c",
	Off:         "o",
}

func formatterLevel(message string, level LogLevel, context LogContextInterface) interface{} {
	levelStr, ok := levelToString[level]
	if !ok {
		return wrongLogLevel
	}
	return levelStr
}

func formatterLev(message string, level LogLevel, context LogContextInterface) interface{} {
	levelStr, ok := levelToShortString[level]
	if !ok {
		return wrongLogLevel
	}
	return levelStr
}

func formatterLEVEL(message string, level LogLevel, context LogContextInterface) interface{} {
	return strings.ToTitle(formatterLevel(message, level, context).(string))
}

func formatterLEV(message string, level LogLevel, context LogContextInterface) interface{} {
	return strings.ToTitle(formatterLev(message, level, context).(string))
}

func formatterl(message string, level LogLevel, context LogContextInterface) interface{} {
	levelStr, ok := levelToShortestString[level]
	if !ok {
		return wrongLogLevel
	}
	return levelStr
}

func formatterMsg(message string, level LogLevel, context LogContextInterface) interface{} {
	return message
}

func formatterFullPath(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.FullPath()
}

func formatterFile(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.FileName()
}

func formatterRelFile(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.ShortPath()
}

func FormatterFunction(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.Func()
}

func FormatterFunctionShort(message string, level LogLevel, context LogContextInterface) interface{} {
	f := context.Func()
	spl := strings.Split(f, ".")
	return spl[len(spl)-1]
}

func formatterLine(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.Line()
}

func formatterTime(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.CallTime().Format(TimeFormat)
}

func formatterUTCTime(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.CallTime().UTC().Format(TimeFormat)
}

func formatterNs(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.CallTime().UnixNano()
}

func formatterUTCNs(message string, level LogLevel, context LogContextInterface) interface{} {
	return context.CallTime().UTC().UnixNano()
}

func formattern(message string, level LogLevel, context LogContextInterface) interface{} {
	return "\n"
}

func formattert(message string, level LogLevel, context LogContextInterface) interface{} {
	return "\t"
}

func createDateTimeFormatterFunc(dateTimeFormat string) FormatterFunc {
	format := dateTimeFormat
	if format == "" {
		format = DateDefaultFormat
	}
	return func(message string, level LogLevel, context LogContextInterface) interface{} {
		return time.Now().Format(format)
	}
}

func createUTCDateTimeFormatterFunc(dateTimeFormat string) FormatterFunc {
	format := dateTimeFormat
	if format == "" {
		format = DateDefaultFormat
	}
	return func(message string, level LogLevel, context LogContextInterface) interface{} {
		return time.Now().UTC().Format(format)
	}
}

func createANSIEscapeFunc(escapeCodeString string) FormatterFunc {
	return func(message string, level LogLevel, context LogContextInterface) interface{} {
		if len(escapeCodeString) == 0 {
			return wrongEscapeCode
		}

		return fmt.Sprintf("%c[%sm", 0x1B, escapeCodeString)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/fatih/color"
	"strconv"
	"time"
)

func ColorList() []color.Attribute {
	return []color.Attribute{
		color.FgHiRed,
		color.FgHiGreen,
		color.FgHiYellow,
		color.FgHiBlue,
		color.FgHiMagenta,
		color.FgHiCyan,
	}
}

func GenerateColorHash(keys []string, colorList []color.Attribute) map[string]color.Attribute {
	colorHash := map[string]color.Attribute{}
	colorNum := 0
	for _, key := range keys {
		var colorAttribute color.Attribute
		value, ok := colorHash[key]
		if ok {
			colorAttribute = value
		} else {
			colorAttribute = colorList[colorNum]
			colorHash[key] = colorAttribute
			colorNum = colorNum + 1
			if colorNum == len(colorList) {
				colorNum = 0
			}
		}
	}
	return colorHash
}

func IdFormat(id int) string {
	return color.BlueString(strconv.Itoa(id))
}

func PriorityFormat(priority int) string {
	priorityColor := color.New(color.Bold)
	switch priority {
	case 4:
		priorityColor.Add(color.FgWhite).Add(color.BgRed)
	case 3:
		priorityColor.Add(color.FgHiRed).Add(color.BgBlack)
	case 2:
		priorityColor.Add(color.FgHiYellow).Add(color.BgBlack)
	default:
		priorityColor.Add(color.FgBlue).Add(color.BgBlack)
	}
	return priorityColor.SprintFunc()("p" + strconv.Itoa(priority))
}

func ProjectFormat(projectName string, projectColorHash map[string]color.Attribute) string {
	return color.New(projectColorHash[projectName]).SprintFunc()(projectName)
}

func dueDateString(dueDate time.Time, allDay bool) string {
	if (dueDate == time.Time{}) {
		return ""
	}
	dueDate = dueDate.Local()
	if !allDay {
		return dueDate.Format(ShortDateTimeFormat)
	}
	return dueDate.Format(ShortDateFormat)
}

func DueDateFormat(dueDate time.Time, allDay bool) string {
	dueDateString := dueDateString(dueDate, allDay)
	duration := time.Since(dueDate)
	dueDateColor := color.New(color.Bold)
	if duration > 0 {
		dueDateColor.Add(color.FgWhite).Add(color.BgRed)
	} else if duration > -1*time.Hour {
		dueDateColor.Add(color.FgHiRed).Add(color.BgBlack)
	} else if duration > -24*time.Hour {
		dueDateColor.Add(color.FgHiYellow).Add(color.BgBlack)
	} else {
		dueDateColor.Add(color.FgHiBlue).Add(color.BgBlack)
	}
	return dueDateColor.SprintFunc()(dueDateString)
}
<commit_msg>Modify project format<commit_after>package main

import (
	"github.com/fatih/color"
	"strconv"
	"time"
)

func ColorList() []color.Attribute {
	return []color.Attribute{
		color.FgHiRed,
		color.FgHiGreen,
		color.FgHiYellow,
		color.FgHiBlue,
		color.FgHiMagenta,
		color.FgHiCyan,
	}
}

func GenerateColorHash(keys []string, colorList []color.Attribute) map[string]color.Attribute {
	colorHash := map[string]color.Attribute{}
	colorNum := 0
	for _, key := range keys {
		var colorAttribute color.Attribute
		value, ok := colorHash[key]
		if ok {
			colorAttribute = value
		} else {
			colorAttribute = colorList[colorNum]
			colorHash[key] = colorAttribute
			colorNum = colorNum + 1
			if colorNum == len(colorList) {
				colorNum = 0
			}
		}
	}
	return colorHash
}

func IdFormat(id int) string {
	return color.BlueString(strconv.Itoa(id))
}

func PriorityFormat(priority int) string {
	priorityColor := color.New(color.Bold)
	switch priority {
	case 4:
		priorityColor.Add(color.FgWhite).Add(color.BgRed)
	case 3:
		priorityColor.Add(color.FgHiRed).Add(color.BgBlack)
	case 2:
		priorityColor.Add(color.FgHiYellow).Add(color.BgBlack)
	default:
		priorityColor.Add(color.FgBlue).Add(color.BgBlack)
	}
	return priorityColor.SprintFunc()("p" + strconv.Itoa(priority))
}

func ProjectFormat(projectName string, projectColorHash map[string]color.Attribute) string {
	return color.New(projectColorHash[projectName]).SprintFunc()("#" + projectName)
}

func dueDateString(dueDate time.Time, allDay bool) string {
	if (dueDate == time.Time{}) {
		return ""
	}
	dueDate = dueDate.Local()
	if !allDay {
		return dueDate.Format(ShortDateTimeFormat)
	}
	return dueDate.Format(ShortDateFormat)
}

func DueDateFormat(dueDate time.Time, allDay bool) string {
	dueDateString := dueDateString(dueDate, allDay)
	duration := time.Since(dueDate)
	dueDateColor := color.New(color.Bold)
	if duration > 0 {
		dueDateColor.Add(color.FgWhite).Add(color.BgRed)
	} else if duration > -1*time.Hour {
		dueDateColor.Add(color.FgHiRed).Add(color.BgBlack)
	} else if duration > -24*time.Hour {
		dueDateColor.Add(color.FgHiYellow).Add(color.BgBlack)
	} else {
		dueDateColor.Add(color.FgHiBlue).Add(color.BgBlack)
	}
	return dueDateColor.SprintFunc()(dueDateString)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Gravitational, Inc.

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

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

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

package utils

import (
	"crypto/x509"
	"fmt"
	"io"
	"io/ioutil"
	"log/syslog"
	"os"
	"strconv"

	"github.com/gravitational/teleport"

	log "github.com/Sirupsen/logrus"
	logrusSyslog "github.com/Sirupsen/logrus/hooks/syslog"
	"github.com/gravitational/kingpin"
	"github.com/gravitational/trace"
)

// InitLoggerCLI tools by default log into syslog, not stderr
func InitLoggerCLI() {
	log.SetLevel(log.WarnLevel)
	// clear existing hooks:
	log.StandardLogger().Hooks = make(log.LevelHooks)
	log.SetFormatter(&trace.TextFormatter{})

	hook, err := logrusSyslog.NewSyslogHook("", "", syslog.LOG_WARNING, "")
	if err != nil {
		// syslog not available
		log.Warn("syslog not available. reverting to stderr")
	} else {
		// ... and disable stderr:
		log.AddHook(hook)
		log.SetOutput(ioutil.Discard)
	}
}

// InitLoggerDebug configures the logger to dump everything to stderr
func InitLoggerDebug() {
	InitDebugLogger(log.DebugLevel)
}

// InitLoggerVerbose is a less chatty version of debug logger above
func InitLoggerVerbose() {
	InitDebugLogger(log.InfoLevel)
}

func InitDebugLogger(level log.Level) {
	// clear existing hooks:
	log.StandardLogger().Hooks = make(log.LevelHooks)
	log.SetFormatter(&trace.TextFormatter{})
	log.SetOutput(os.Stderr)
	log.SetLevel(level)
}

// InitLoggerForTests inits logger to discard ouput in tests unless
// DEBUG is set to "1"
func InitLoggerForTests() {
	val, _ := strconv.ParseBool(os.Getenv(teleport.VerboseLogsEnvVar))
	if val {
		InitLoggerDebug()
		return
	}
	log.SetLevel(log.WarnLevel)
	log.StandardLogger().Hooks = make(log.LevelHooks)
	log.SetOutput(ioutil.Discard)
}

// FatalError is for CLI front-ends: it detects gravitational/trace debugging
// information, sends it to the logger, strips it off and prints a clean message to stderr
func FatalError(err error) {
	fmt.Fprintln(os.Stderr, UserMessageFromError(err))
	os.Exit(1)
}

// UserMessageFromError returns user friendly error message from error
func UserMessageFromError(err error) string {

	// untrusted cert?
	switch trace.Unwrap(err).(interface{}).(type) {
	case x509.UnknownAuthorityError, x509.CertificateInvalidError:
		return "WARNING:\n The proxy you are connecting to uses the self-signed HTTPS certificate.\n" +
			" Try --insecure flag if you know what you're doing.\n"
	}
	if log.GetLevel() == log.DebugLevel {
		return trace.DebugReport(err)
	}
	return err.Error()
}

// Consolef prints the same message to a 'ui console' (if defined) and also to
// the logger with INFO priority
func Consolef(w io.Writer, msg string, params ...interface{}) {
	msg = fmt.Sprintf(msg, params...)
	if w != nil {
		fmt.Fprintln(w, msg)
	}
	log.Info(msg)
}

// InitCLIParser configures kingpin command line args parser with
// some defaults common for all Teleport CLI tools
func InitCLIParser(appName, appHelp string) (app *kingpin.Application) {
	app = kingpin.New(appName, appHelp)

	// hide "--help" flag
	app.HelpFlag.Hidden()
	app.HelpFlag.NoEnvar()

	// set our own help template
	return app.UsageTemplate(defaultUsageTemplate)
}

// Usage template with compactly formatted commands.
var defaultUsageTemplate = `{{define "FormatCommand"}}\
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
{{end}}\

{{define "FormatCommands"}}\
{{range .FlattenedCommands}}\
{{if not .Hidden}}\
  {{.FullCommand | printf "%-12s" }}{{if .Default}} (Default){{end}} {{ .Help }}
{{end}}\
{{end}}\
{{end}}\

{{define "FormatUsage"}}\
{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
{{if .Help}}
{{.Help|Wrap 0}}\
{{end}}\

{{end}}\

{{if .Context.SelectedCommand}}\
usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
{{else}}\
Usage: {{.App.Name}}{{template "FormatUsage" .App}}
{{end}}\
{{if .Context.Flags}}\
Flags:
{{.Context.Flags|FlagsToTwoColumnsCompact|FormatTwoColumns}}
{{end}}\
{{if .Context.Args}}\
Args:
{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
{{end}}\
{{if .Context.SelectedCommand}}\

{{ if .Context.SelectedCommand.Commands}}\
Commands:
{{if .Context.SelectedCommand.Commands}}\
{{template "FormatCommands" .Context.SelectedCommand}}
{{end}}\
{{end}}\

{{else if .App.Commands}}\
Commands:
{{template "FormatCommands" .App}}
Try '{{.App.Name}} help [command]' to get help for a given command.
{{end}}\

{{ if .Context.SelectedCommand }}\
{{ range .Context.SelectedCommand.Aliases}}\
{{ . }}
{{end}}\
{{end}}
`
<commit_msg>Nicer HTTPS error message on mismatched proxy hostname<commit_after>/*
Copyright 2016 Gravitational, Inc.

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

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

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

package utils

import (
	"crypto/x509"
	"fmt"
	"io"
	"io/ioutil"
	"log/syslog"
	"os"
	"strconv"

	"github.com/gravitational/teleport"

	log "github.com/Sirupsen/logrus"
	logrusSyslog "github.com/Sirupsen/logrus/hooks/syslog"
	"github.com/gravitational/kingpin"
	"github.com/gravitational/trace"
)

// InitLoggerCLI tools by default log into syslog, not stderr
func InitLoggerCLI() {
	log.SetLevel(log.WarnLevel)
	// clear existing hooks:
	log.StandardLogger().Hooks = make(log.LevelHooks)
	log.SetFormatter(&trace.TextFormatter{})

	hook, err := logrusSyslog.NewSyslogHook("", "", syslog.LOG_WARNING, "")
	if err != nil {
		// syslog not available
		log.Warn("syslog not available. reverting to stderr")
	} else {
		// ... and disable stderr:
		log.AddHook(hook)
		log.SetOutput(ioutil.Discard)
	}
}

// InitLoggerDebug configures the logger to dump everything to stderr
func InitLoggerDebug() {
	InitDebugLogger(log.DebugLevel)
}

// InitLoggerVerbose is a less chatty version of debug logger above
func InitLoggerVerbose() {
	InitDebugLogger(log.InfoLevel)
}

func InitDebugLogger(level log.Level) {
	// clear existing hooks:
	log.StandardLogger().Hooks = make(log.LevelHooks)
	log.SetFormatter(&trace.TextFormatter{})
	log.SetOutput(os.Stderr)
	log.SetLevel(level)
}

// InitLoggerForTests inits logger to discard ouput in tests unless
// DEBUG is set to "1"
func InitLoggerForTests() {
	val, _ := strconv.ParseBool(os.Getenv(teleport.VerboseLogsEnvVar))
	if val {
		InitLoggerDebug()
		return
	}
	log.SetLevel(log.WarnLevel)
	log.StandardLogger().Hooks = make(log.LevelHooks)
	log.SetOutput(ioutil.Discard)
}

// FatalError is for CLI front-ends: it detects gravitational/trace debugging
// information, sends it to the logger, strips it off and prints a clean message to stderr
func FatalError(err error) {
	fmt.Fprintln(os.Stderr, UserMessageFromError(err))
	os.Exit(1)
}

// UserMessageFromError returns user friendly error message from error
func UserMessageFromError(err error) string {
	// untrusted cert?
	switch innerError := trace.Unwrap(err).(interface{}).(type) {
	case x509.HostnameError:
		return fmt.Sprintf("Cannot establish https connection to %s:\n%s\n%s\n",
			innerError.Host,
			innerError.Error(),
			"try a different hostname for --proxy or specify --insecure flag if you know what you're doing.")
	case x509.UnknownAuthorityError, x509.CertificateInvalidError:
		return "WARNING:\n The proxy you are connecting to uses the self-signed HTTPS certificate.\n" +
			" Try --insecure flag if you know what you're doing.\n"
	}
	if log.GetLevel() == log.DebugLevel {
		return trace.DebugReport(err)
	}
	return err.Error()
}

// Consolef prints the same message to a 'ui console' (if defined) and also to
// the logger with INFO priority
func Consolef(w io.Writer, msg string, params ...interface{}) {
	msg = fmt.Sprintf(msg, params...)
	if w != nil {
		fmt.Fprintln(w, msg)
	}
	log.Info(msg)
}

// InitCLIParser configures kingpin command line args parser with
// some defaults common for all Teleport CLI tools
func InitCLIParser(appName, appHelp string) (app *kingpin.Application) {
	app = kingpin.New(appName, appHelp)

	// hide "--help" flag
	app.HelpFlag.Hidden()
	app.HelpFlag.NoEnvar()

	// set our own help template
	return app.UsageTemplate(defaultUsageTemplate)
}

// Usage template with compactly formatted commands.
var defaultUsageTemplate = `{{define "FormatCommand"}}\
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
{{end}}\

{{define "FormatCommands"}}\
{{range .FlattenedCommands}}\
{{if not .Hidden}}\
  {{.FullCommand | printf "%-12s" }}{{if .Default}} (Default){{end}} {{ .Help }}
{{end}}\
{{end}}\
{{end}}\

{{define "FormatUsage"}}\
{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
{{if .Help}}
{{.Help|Wrap 0}}\
{{end}}\

{{end}}\

{{if .Context.SelectedCommand}}\
usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
{{else}}\
Usage: {{.App.Name}}{{template "FormatUsage" .App}}
{{end}}\
{{if .Context.Flags}}\
Flags:
{{.Context.Flags|FlagsToTwoColumnsCompact|FormatTwoColumns}}
{{end}}\
{{if .Context.Args}}\
Args:
{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
{{end}}\
{{if .Context.SelectedCommand}}\

{{ if .Context.SelectedCommand.Commands}}\
Commands:
{{if .Context.SelectedCommand.Commands}}\
{{template "FormatCommands" .Context.SelectedCommand}}
{{end}}\
{{end}}\

{{else if .App.Commands}}\
Commands:
{{template "FormatCommands" .App}}
Try '{{.App.Name}} help [command]' to get help for a given command.
{{end}}\

{{ if .Context.SelectedCommand }}\
{{ range .Context.SelectedCommand.Aliases}}\
{{ . }}
{{end}}\
{{end}}
`
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"runtime"
	"strconv"
	"strings"
)

type experiment struct {
	Header   []string
	Segments []segment
}

var numWorkers = flag.Int("n", 4, "the number of processors availble to the program")
var f = flag.String("f", "rharr-c453.head", "the path to the file to be parsed")

type segment struct {
	index int
	Header,
	Footer,
	lines []string
	I,
	sigmaI,
	iOverSigmaI float32
}

// take in a scanner, and return a segment of the text file you are looking for
func chomp(scanner *bufio.Reader, lookFor string) ([]string, bool) {
	found := false
	var lines []string
	for !found {
		line, err := scanner.ReadString('\n')
		if err != nil {
			found = true
		}
		if strings.Contains(line, lookFor) {
			found = true
		}
		lines = append(lines, line)
		if line == "" {

			return lines, true
		}
	}

	return lines, false
}
func parse(line string) map[string]interface{} {
	b := strings.Split(line, " ")
	hold := make(map[string]interface{})

	hold["h"], _ = strconv.Atoi(b[0])
	hold["k"], _ = strconv.Atoi(b[1])
	hold["l"], _ = strconv.Atoi(b[2])
	hold["I"], _ = strconv.ParseFloat(b[3], 32)
	hold["sigmaI"], _ = strconv.ParseFloat(b[5], 32)

	return hold
}

// type listener creates an experiment, and reconsititues the santatized data
type listener struct {
	exp          *experiment
	fileLocation string
}

func (l listener) rebuild() {
	err := os.Rename(l.fileLocation, l.fileLocation+".old")
	if err != nil {
		fmt.Println("could not rename file")
	}
	file, err := os.Open(l.fileLocation)
	if err != nil {
		fmt.Println("could not open " + l.fileLocation)

	}
	writer := bufio.NewWriter(file)
	for i := 0; i < len(l.exp.Segments); i++ {
		for x := 0; x < len(l.exp.Segments[i].lines); x++ {
			writer.WriteString(l.exp.Segments[i].lines[x])
		}

	}
	file.Close()
}

// listen for segments and place them in order
func (l listener) listen(doneChan chan bool, segChan chan segment) {
	done := false
	for !done {
		select {
		case hold := <-segChan:
			l.exp.Segments[hold.index] = hold
		case done = <-doneChan:
			close(doneChan)
			close(segChan)
			l.rebuild()
		}
	}
}

func main() {
	flag.Parse()
	runtime.GOMAXPROCS(*numWorkers)

	fileLocation := *f
	fmt.Println(fileLocation)
	file, err := os.Open(fileLocation)
	if err != nil {
		fmt.Println("File ", fileLocation, " does not exist.")
	}
	scanner := bufio.NewReader(file)
	exp := new(experiment)
	exp.Header, _ = chomp(scanner, "Reflections measured after indexing")
	fmt.Println(exp.Header)
}
<commit_msg>updated headParse.go<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"runtime"
	"strconv"
	"strings"
)

type experiment struct {
	Header   []string
	Segments []segment
}

var numWorkers = flag.Int("n", 4, "the number of processors availble to the program")
var f = flag.String("f", "rharr-c453.head", "the path to the file to be parsed")

type segment struct {
	index int
	lines []string
	flags,
	tags []int
	data [][]float64
	I,
	sigmaI,
	iOverSigmaI float64
}

func (s segment) stdDev() float64 {
	total := 0.0
	for i := 0; i < len(s.data); i++ {
		total += s.data[i][3]
	}
	return total / float64(len(s.data))
}

// take in a scanner, and return a segment of the text file you are looking for
func chomp(scanner *bufio.Reader, lookFor string) ([]string, bool) {
	found := false
	var lines []string
	for !found {
		line, err := scanner.ReadString('\n')
		if err != nil {
			found = true
		}
		if strings.Contains(line, lookFor) {
			found = true
		}
		lines = append(lines, line)
		if line == "" {

			return lines, true
		}
	}

	return lines, false
}

// parse out
func parse(seg segment) [][]float64 {
	seg.data = make([][]float64, len(seg.lines))
	xBound := len(seg.lines)
	var tags []int
	for x := 0; x < xBound; x++ {

		line := seg.lines[x]
		if strings.Contains(line, " - ") {
			tags = append(tags, x)
			seg.data[x] = make([]float64, 5)
			b := strings.Split(line, " ")
			for y := 0; y < 5; y++ {

				hold, err := strconv.ParseFloat(b[y], 64)
				seg.data[x][y] = hold
				if err != nil {
					fmt.Println(err)
				}
			}
		}

	}
	return seg.data
}

// type listener creates an experiment, and reconsititues the santatized data
type listener struct {
	exp          *experiment
	fileLocation string
}

func (l listener) rebuild() {
	err := os.Rename(l.fileLocation, l.fileLocation+".old")
	if err != nil {
		fmt.Println("could not rename file")
	}
	file, err := os.Open(l.fileLocation)
	if err != nil {
		fmt.Println("could not open " + l.fileLocation)

	}
	writer := bufio.NewWriter(file)
	for i := 0; i < len(l.exp.Segments); i++ {
		for x := 0; x < len(l.exp.Segments[i].lines); x++ {
			writer.WriteString(l.exp.Segments[i].lines[x])
		}

	}
	file.Close()
}

// listen for segments and place them in order
func (l listener) listen(doneChan chan bool, segChan chan segment) {
	done := false
	for !done {
		select {
		case hold := <-segChan:
			l.exp.Segments[hold.index] = hold
		case done = <-doneChan:
			close(doneChan)
			close(segChan)
			l.rebuild()
		}
	}
}

func main() {
	flag.Parse()
	runtime.GOMAXPROCS(*numWorkers)

	fileLocation := *f
	fmt.Println(fileLocation)
	file, err := os.Open(fileLocation)
	if err != nil {
		fmt.Println("File ", fileLocation, " does not exist.")
	}
	scanner := bufio.NewReader(file)
	exp := new(experiment)
	list := new(listener)
	list.exp = exp
	list.fileLocation = fileLocation
	exp.Header, _ = chomp(scanner, "Reflections measured after indexing")

	// collect lines from file and turn them into segments
	done := false
	i := 0
	var seg segment
	for !done {
		seg.lines, done = chomp(scanner, "Reflections measured after indexing")
		seg.index = i
		seg.data = parse(seg)
		fmt.Println(seg.data)
		fmt.Println(seg.stdDev())
		exp.Segments = append(exp.Segments, seg)
		i++

	}

}
<|endoftext|>